/***************************************************************/ /* */ /* UTILS.C */ /* */ /* Useful utility functions. */ /* */ /* This file is part of REMIND. */ /* Copyright (C) 1992-2026 by Dianne Skoll */ /* SPDX-License-Identifier: GPL-2.0-only */ /* */ /***************************************************************/ static char const DontEscapeMe[] = "1234567890_-=+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@.,/"; #include "config.h" #include "err.h" #include #include #include #ifdef HAVE_STRINGS_H #include #endif #include #include #include #include #include #include "types.h" #include "globals.h" #include "protos.h" static int HaveZoneinfoDir = -1; #define ZONE_DIR "/usr/share/zoneinfo" static int have_zoneinfo_dir(void) { struct stat sb; if (HaveZoneinfoDir < 0) { if (stat(ZONE_DIR, &sb) < 0) { HaveZoneinfoDir = 0; } else if ((sb.st_mode & S_IFMT) != S_IFDIR) { HaveZoneinfoDir = 0; } else { HaveZoneinfoDir = 1; } } return HaveZoneinfoDir; } /* Call this instead of system() so if called ignores return code, we don't get a compiler warning. Also redirect stdin to /dev/null */ int system1(char const *cmd) { int stdin_dup = dup(STDIN_FILENO); int devnull; int r; if (stdin_dup >= 0) { devnull = open("/dev/null", O_RDONLY); if (devnull >= 0) { if (dup2(devnull, STDIN_FILENO) >= 0) { set_cloexec(stdin_dup); } (void) close(devnull); } else { (void) close(stdin_dup); stdin_dup = -1; } } r = system(cmd); if (stdin_dup >= 0) { (void) dup2(stdin_dup, STDIN_FILENO); (void) close(stdin_dup); } return r; } /***************************************************************/ /* */ /* system_to_stderr */ /* */ /* Run system(...) but with stdout redirected to stderr */ /* */ /***************************************************************/ int system_to_stderr(char const *cmd) { int stdout_dup = dup(STDOUT_FILENO); int r; if (stdout_dup < 0) { perror("dup"); return -1; } /* Duplicate STDERR onto STDOUT */ if (dup2(STDERR_FILENO, STDOUT_FILENO) < 0) { (void) close(stdout_dup); return -1; } /* Set close-on-exec flag on stdout_dup */ set_cloexec(stdout_dup); r = system1(cmd); /* Restore original stdout */ /* If this dup2 fails... there's not a whole lot we can do. */ (void) dup2(stdout_dup, STDOUT_FILENO); if (STDOUT_FILENO != stdout_dup) { (void) close(stdout_dup); } return r; } /***************************************************************/ /* */ /* StrnCpy */ /* */ /* Just like strncpy EXCEPT we ALWAYS copy the trailing 0. */ /* */ /***************************************************************/ char *StrnCpy(char *dest, char const *source, int n) { char *odest = dest; if (n <= 0) { *dest = 0; return dest; } while (n-- && (*dest++ = *source++)) ; if (*(dest-1)) *dest = 0; return odest; } /***************************************************************/ /* */ /* DateOK */ /* */ /* Return 1 if the date is OK, 0 otherwise. */ /* */ /***************************************************************/ int DateOK(int y, int m, int d) { if (d < 1 || m < 0 || y < BASE || m > 11 || y > BASE + YR_RANGE || d > DaysInMonth(m, y) ) return 0; return 1; } void strtolower(char *s) { while (*s) { *s = tolower(*s); s++; } } /* Functions designed to defeat gcc optimizer */ int _private_mul_overflow(int a, int b) { double aa = (double) a; double bb = (double) b; if (aa*bb > (double) INT_MAX || aa*bb < (double) INT_MIN) { return 1; } return 0; } int _private_add_overflow(int a, int b) { double aa = (double) a; double bb = (double) b; if (aa+bb < (double) INT_MIN) return 1; if (aa+bb > (double) INT_MAX) return 1; return 0; } int _private_sub_overflow(int a, int b) { double aa = (double) a; double bb = (double) b; if (aa-bb < (double) INT_MIN) return 1; if (aa-bb > (double) INT_MAX) return 1; return 0; } int ShellEscape(char const *in, DynamicBuffer *out) { unsigned char const *i = (unsigned char const *) in; while(*i) { /* Don't escape chars with high bit set. That will mangle UTF-8 */ if (! (*i & 0x80) ) { if (!strchr(DontEscapeMe, *i)) { if (DBufPutc(out, '\\') != OK) return E_NO_MEM; } } if (DBufPutc(out, *i++) != OK) return E_NO_MEM; } return OK; } /* Call-stack for printing errors from user-defined functions */ typedef struct cs_s { struct cs_s *next; char const *filename; char const *func; int lineno; int lineno_start; } cs; static cs *callstack = NULL; static cs *freecs = NULL; static void destroy_cs(cs *entry) { entry->next = freecs; freecs = entry; } int push_call(char const *filename, char const *func, int lineno, int lineno_start) { cs *entry; if (freecs) { entry = freecs; freecs = freecs->next; } else { entry = NEW(cs); if (!entry) { return E_NO_MEM; } } entry->next = NULL; entry->filename = filename; entry->func = func; entry->lineno = lineno; entry->lineno_start = lineno_start; entry->next = callstack; callstack = entry; return OK; } void clear_callstack(void) { cs *entry = callstack; cs *next; while(entry) { next = entry->next; destroy_cs(entry); entry = next; } callstack = NULL; } static void print_callstack_aux(FILE *fp, cs *entry) { int i = 0; char const *in = tr("In"); cs const *prev = NULL; while(entry) { if (prev) { in = tr("Called from"); } if (!prev || strcmp(prev->func, entry->func) || strcmp(prev->filename, entry->filename) || prev->lineno != entry->lineno) { if (prev) { fprintf(fp, "\n"); } fprintf(fp, " "); fprintf(fp, tr("%s(%s): [#%d] %s function `%s'"), entry->filename, line_range(entry->lineno_start, entry->lineno), i, in, entry->func); } prev = entry; entry = entry->next; i++; if (i > 10) { break; } } if (entry) { (void) fprintf(fp, "\n ["); (void) fprintf(fp, "%s", tr("remaining call frames omitted")); (void) fprintf(fp, "]"); } } int print_callstack(FILE *fp) { print_callstack_aux(fp, callstack); if (callstack) return 1; return 0; } void pop_call(void) { cs *entry = callstack; if (entry) { callstack = entry->next; destroy_cs(entry); } } char const * line_range(int lineno_start, int lineno) { static char buf[128]; if (lineno_start == lineno) { snprintf(buf, sizeof(buf), "%d", lineno); } else { snprintf(buf, sizeof(buf), "%d:%d", lineno_start, lineno); } return buf; } int warning_level(char const *which) { if (!WarningLevel) return 1; return strcmp(WarningLevel, which) >= 0; } void warn_if_timezone_bad(char const *tz) { DynamicBuffer zfile; struct stat sb; int r; if (!tz) { return; } if (!*tz) { return; } if (*tz == '!') { return; } if (!have_zoneinfo_dir()) { return; } DBufInit(&zfile); DBufPuts(&zfile, ZONE_DIR); DBufPuts(&zfile, "/"); DBufPuts(&zfile, tz); r = stat(DBufValue(&zfile), &sb); DBufFree(&zfile); if (r < 0) { if (warning_level("06.01.05")) { Wprint(tr("No time zone file found for TZ `%s'... is it valid?"), tz); } } }