/*** analog 4.13 http://www.analog.cx/ ***/ /*** This program is copyright (c) Stephen R. E. Turner 1995 - 2000 except as *** stated otherwise. Distribution, usage and modification of this program is *** subject to the conditions of the Licence which you should have received *** with it. This program comes with no warranty, expressed or implied. ***/ /*** utils.c; lots of little functions to do odd little things ***/ #include "anlghea3.h" void my_exit(int status) { extern FILE *errfile; #ifndef NODNS extern FILE *dnslock; extern char *dnslockfile; #endif #ifdef MAC_EVENTS MacFini(); #endif #ifdef WIN32 Win32Cleanup(); #endif #ifndef NODNS if (dnslock != NULL) { fclose(dnslock); if (remove(dnslockfile) != 0) warn('F', TRUE, "Trouble deleting DNS lock file %s", dnslockfile); else debug('F', "Deleting DNS lock file %s", dnslockfile); } #endif if (errfile != stderr) fclose(errfile); exit(status); } void sighandler(int sig) { extern FILE *errfile; extern logical anywarns; extern char *commandname; fprintf(errfile, "%s: Received ", commandname); if (sig == SIGTERM) fprintf(errfile, "termination"); else if (sig == SIGINT) fprintf(errfile, "interrupt"); #ifdef SIGHUP else if (sig == SIGHUP) fprintf(errfile, "HUP"); #endif #ifdef SIGQUIT else if (sig == SIGQUIT) fprintf(errfile, "quit"); #endif fprintf(errfile, " signal: exiting\n"); if (errfile != stderr) { fprintf(stderr, "Received "); if (sig == SIGTERM) fprintf(stderr, "termination"); else if (sig == SIGINT) fprintf(stderr, "interrupt"); #ifdef SIGHUP else if (sig == SIGHUP) fprintf(stderr, "HUP"); #endif #ifdef SIGQUIT else if (sig == SIGQUIT) fprintf(stderr, "quit"); #endif fprintf(stderr, " signal: exiting\n"); } anywarns = TRUE; my_exit(EXIT_SIGNAL); } logical substrcaseeq(char *s, char *t) { /* match the beginning of s case insensitively against lower case t */ for ( ; TOLOWER(*s) == *t && *t != '\0'; s++, t++) ; /* *s != '\0' covered by above two cases */ if (*t == '\0') return(TRUE); else return(FALSE); } char *strtoupper(char *n) { /* convert a string to upper case UP TO the first line break */ char *c; for (c = n; *c != '\0' && *c != '\n' && *c != '\r'; c++) if (ISLOWER(*c)) /* needed to stop (known u.c.) const char *n crashing */ *c = TOUPPER(*c); return(n); } void strtolowerx(char *n) { /* only used in next 2 fns. */ for ( ; *n != '\0' && *n != '?'; n++) *n = TOLOWER(*n); } void toloweralias(Alias *ap, logical both) { AliasTo *tp; char *errstr; int erroffset; for ( ; ap != NULL; TO_NEXT(ap)) { if (headcasematch(ap->from, "REGEXP:")) ap->pattern = pcre_compile(ap->from + 7, PCRE_CASELESS | PCRE_DOTALL, (const char **)(&errstr), &erroffset, NULL); /* if original regcomp succeeded, this one should too */ else if (!headcasematch(ap->from, "REGEXPI:")) /* nothing to do for REGEXPI: */ strtolowerx(ap->from); if (both) { for (tp = ap->to; tp != NULL; TO_NEXT(tp)) strtolowerx(tp->string); } } } void tolowerinc(Include *ip) { /* see comments in previous function */ char *errstr; int erroffset; for ( ; ip != NULL; TO_NEXT(ip)) { if (headcasematch(ip->name, "REGEXP:")) ip->pattern = pcre_compile(ip->name + 7, PCRE_CASELESS | PCRE_DOTALL, (const char **)(&errstr), &erroffset, NULL); else if (!headcasematch(ip->name, "REGEXPI:")) strtolowerx((char *)(ip->name)); } } void reversealias(Alias **head) { /* aliases are first collated in reverse order */ Alias *ap, *lastap = NULL, *nextap; for (ap = *head; ap != NULL; ap = nextap) { nextap = ap->next; ap->next = lastap; lastap = ap; } *head = lastap; } logical strcaseeq(char *s, char *t) { for ( ; TOUPPER(*s) == TOUPPER(*t) && *s != '\0'; s++, t++) ; if (TOUPPER(*s) != TOUPPER(*t)) return(FALSE); else return(TRUE); } #ifdef NEED_STRCMP int my_strcmp(char *s, char *t) { for ( ; *s == *t && *s != '\0'; s++, t++) ; if (*s < *t) return(-1); else if (*s > *t) return(1); else return(0); } #endif unsigned int chrn(char *s, char c) { /* no. of occurrences of c in s */ unsigned int n = 0; for ( ; *s != '\0'; s++) n += (*s == c); return(n); } unsigned int chrdistn(char *s, char c) {/* ditto but consec c's count only 1 */ unsigned int n = 0; for ( ; *s != '\0'; s++) n += (*s == c && *(s + 1) != c); return(n); } unsigned int log10i(unsigned long i) { unsigned int j; for (j = 0; i >= 10; i /= 10) j++; return(j); } unsigned int log10x(double d) { unsigned int j; for (j = 0; d >= 10; d /= 10) j++; return(j); } unsigned int findbmult(double d) { unsigned int j; if (d < 1024) return(0); /* see also in calcsizes() */ for (j = 0; d >= 1000.0; d /= 1024.0) j++; return(j); } unsigned long arraymaxl(unsigned long *x, unsigned int size) { unsigned long y; unsigned int i; y = 0; for (i = 0; i < size; i++) y = MAX(y, x[i]); return(y); } double arraymaxd(double *x, unsigned int size) { double y; /* assuming doubles in x all positive */ unsigned int i; y = 0.0; for (i = 0; i < size; i++) y = MAX(y, x[i]); return(y); } size_t arraymaxlen(char **s, unsigned int size, choice outstyle) { unsigned int i; size_t y = 0; for (i = 0; i < size; i++) y = MAX(htmlstrlen(s[i], outstyle), y); return(y); } void prettyprintf(FILE *outf, unsigned int pagewidth, char *s, va_list ap) { /* NB Calling function must call mprintf with pagewidth = 0 after message. */ char m[511]; /* longer than any message we want to produce */ extern unsigned int ppcol; static logical spacedone = FALSE; char *m1, *m2; if (pagewidth == 0) { putc('\n', outf); ppcol = 0; spacedone = FALSE; return; } vsprintf(m, s, ap); for (m1 = m, m2 = m; *m1 != '\0'; m2++) { if (*m2 == ' ' || *m2 == '\0' || *m2 == '\n') { if ((m2 - m1) + ppcol > pagewidth && (*m1 == ' ' || spacedone)) { fputs("\n ", outf); if (*m1 == ' ') m1++; spacedone = FALSE; ppcol = 1; } while (m1 < m2) { putc(*m1, outf); spacedone = (*(m1++) == ' '); ppcol++; } if (*m1 == '\n') { fputs("\n ", outf); m1++; spacedone = FALSE; ppcol = 2; } } } } void mprintf(FILE *outf, unsigned int pagewidth, char *s, ...) { /* wrapper to prettyprintf(): see note there */ va_list ap; va_start(ap, s); prettyprintf(outf, pagewidth, s, ap); va_end(ap); } void print_progversion(FILE *f) { extern char *commandname; static logical done = FALSE; if (!done) { fprintf(f, "%s: analog version %s\n", commandname, VERSION); done = TRUE; } } void warn(char c, choice domess, char *s, ...) { extern FILE *errfile; extern char *warn_args; extern logical anywarns, errmess; extern unsigned int errwidth; extern char *commandname; va_list ap; if (strchr(warn_args, c) != NULL) { if (errwidth == 0) errwidth = UINT_MAX; print_progversion(errfile); va_start(ap, s); if (domess == CONTINUATION) mprintf(errfile, errwidth, "%s: ...cont..: ", commandname, c); else mprintf(errfile, errwidth, "%s: Warning %c: ", commandname, c); prettyprintf(errfile, errwidth, s, ap); mprintf(errfile, 0, NULL); if (!errmess && domess != 0) { fputs(" (For help on all errors and warnings,", errfile); fputs(" see docs/errors.html)\n", errfile); errmess = TRUE; } va_end(ap); anywarns = TRUE; } } void error(char *s, ...) { extern FILE *errfile; extern logical anywarns, errmess; extern unsigned int errwidth; extern char *commandname; va_list ap; if (errwidth == 0) errwidth = UINT_MAX; print_progversion(errfile); va_start(ap, s); mprintf(errfile, errwidth, "%s: Fatal error: ", commandname); prettyprintf(errfile, errwidth, s, ap); mprintf(errfile, errwidth, ": exiting"); mprintf(errfile, 0, NULL); if (!errmess) { fputs(" (For help on all errors and warnings,", errfile); fputs(" see docs/errors.html)\n", errfile); errmess = TRUE; } if (errfile != stderr) { mprintf(stderr, STDERRWIDTH, "%s: Fatal error: ", commandname); prettyprintf(stderr, STDERRWIDTH, s, ap); mprintf(stderr, STDERRWIDTH, ": exiting"); mprintf(stderr, 0, NULL); } va_end(ap); anywarns = TRUE; my_exit(EXIT_FAILURE); } void debug(char c, char *s, ...) { extern FILE *errfile; extern char *debug_args; extern logical anywarns; va_list ap; if (strchr(debug_args, c) != NULL) { print_progversion(errfile); va_start(ap, s); fprintf(errfile, "%c: ", c); vfprintf(errfile, s, ap); fputc('\n', errfile); va_end(ap); if (c != 'D') anywarns = TRUE; } } char *delimit(char *s) { /* This function delimits an argument s so that it could appear in a config command. It malloc's as necessary: only used in warning messages */ char *ans; size_t len; if (*s != '\'' && *s != '"' && *s != '(' && strchr(s, ' ') == NULL && strchr(s, '#') == NULL) return(s); len = strlen(s); ans = (char *)xmalloc(len + 3); strcpy(ans + 1, s); if (strchr(s, '"') == NULL) { *ans = '"'; ans[len + 1] = '"'; } else if (strchr(s, ')') == NULL) { *ans = '('; ans[len + 1] = ')'; } else { *ans = '\''; ans[len + 1] = '\''; } ans[len + 2] = '\0'; return(ans); } void *xmalloc(size_t size) { /* the same as malloc, only checks for out of memory */ void *answer; if ((answer = malloc(size)) == NULL) error("Ran out of memory: cannot continue"); /* Enable this for debugging: memset(answer, 35, size); */ return(answer); } void *xrealloc(void *ptr, size_t size) { if (ptr == NULL) /* Some broken systems don't allow realloc(NULL, ...) */ ptr = malloc(size); else ptr = realloc(ptr, size); if (ptr == NULL) error("Ran out of memory: cannot continue"); return(ptr); } void *submalloc(Memman *m, size_t size) { Mmlist *l; /* so memmans are initialised with curr_pos, first = NULL & alignment set */ size = ((size + m->alignment - 1) / m->alignment) * m->alignment; if (m->curr_pos != NULL && (size_t)((char *)(m->block_end) - (char *)(m->next_pos)) >= size) m->curr_pos = m->next_pos; else { m->curr_pos = xmalloc(BLOCKSIZE); m->block_end = (void *)((char *)(m->curr_pos) + BLOCKSIZE); if (m->first == NULL) { /* initialisation only */ m->first = (Mmlist *)xmalloc(sizeof(Mmlist)); m->first->pos = m->curr_pos; m->first->next = NULL; m->last = m->first; } else { l = (Mmlist *)xmalloc(sizeof(Mmlist)); l->pos = m->curr_pos; l->next = NULL; m->last->next = l; TO_NEXT(m->last); } } m->next_pos = (void *)((char *)(m->curr_pos) + size); return(m->curr_pos); } void freemm(Memman *m) { Mmlist *p; for (p = m->first; p != NULL; TO_NEXT(p)) free(p->pos); } /* The next function is derived from the FreeBSD 2.2.6 source, and comes with the following copyright notice: * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Chris Torek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef NEED_MEMMOVE /* * sizeof(word) MUST BE A POWER OF TWO * SO THAT wmask BELOW IS ALL ONES */ typedef int word; /* "word" used for optimal copy speed */ #define wsize sizeof(word) #define wmask (wsize - 1) void *memmove(void *dst0, const void *src0, size_t length) { char *dst = dst0; const char *src = src0; size_t t; if (length == 0 || dst == src) /* nothing to do */ goto done; /* * Macros: loop-t-times; and loop-t-times, t>0 */ #define TLOOP(s) if (t) TLOOP1(s) #define TLOOP1(s) do { s; } while (--t) if ((unsigned long)dst < (unsigned long)src) { /* * Copy forward. */ t = (int)src; /* only need low bits */ if ((t | (int)dst) & wmask) { /* * Try to align operands. This cannot be done * unless the low bits match. */ if ((t ^ (int)dst) & wmask || length < wsize) t = length; else t = wsize - (t & wmask); length -= t; TLOOP1(*dst++ = *src++); } /* * Copy whole words, then mop up any trailing bytes. */ t = length / wsize; TLOOP(*(word *)dst = *(word *)src; src += wsize; dst += wsize); t = length & wmask; TLOOP(*dst++ = *src++); } else { /* * Copy backwards. Otherwise essentially the same. * Alignment works as before, except that it takes * (t&wmask) bytes to align, not wsize-(t&wmask). */ src += length; dst += length; t = (int)src; if ((t | (int)dst) & wmask) { if ((t ^ (int)dst) & wmask || length <= wsize) t = length; else t &= wmask; length -= t; TLOOP1(*--dst = *--src); } t = length / wsize; TLOOP(src -= wsize; dst -= wsize; *(word *)dst = *(word *)src); t = length & wmask; TLOOP(*--dst = *--src); } done: return (dst0); } #endif #define SETPMATCH(ptr1, ptr2) \ if (nmatch > 0) { \ pmatch[0] = ptr1 - whole; \ pmatch[1] = ptr2 - whole; \ } logical wildmatch(char *s, char *p, char *whole, int nmatch, ptrdiff_t *pmatch) { /* match string s against pattern p */ char *ss, *pp; /* First match head portions */ while ((*s == *p || *p == '?') && *s != '\0' && *p != '*') { s++; /* *p != '\0' is covered by (*s == *p) && (*s != '\0') */ p++; } /* Unless we've reached a pattern *, we've finished now */ if (*p != '*') { if (*s == '\0' && *p == '\0') return(TRUE); else return(FALSE); } /* Otherwise, match the tail portions. This is purely for speed reasons, so that patterns with only one star don't have to go through the loop below. */ ss = strchr(s, '\0'); pp = strchr(p, '\0'); while ((*ss == *pp || *pp == '?') && ss >= s && *pp != '*') { ss--; /* pp != p is covered because *p == '*' */ pp--; } /* Again, we might have finished now */ if (pp == p) { SETPMATCH(s, ss + 1); return(TRUE); } else if (*pp != '*') return(FALSE); for (p++; *p == '*' && p != pp; p++) ; if (p == pp) { /* there was really only one *, disguised as several */ SETPMATCH(s, ss + 1); return(TRUE); } if (ss < s) return(FALSE); /* this leaves only the difficult case, where p contains >= 2 *'s. Try and match ANY tail substring of s against the bit of the pattern after the first * (or consec. *'s) (reducing *'s in p, guaranteeing termination). (p was incremented above). We choose to match tail portion afresh each time (cheap), rather than insert null bytes into (poss. const) s and p */ for (ss = s; *ss != '\0'; ss++) { /* reuse ss */ if (wildmatch(ss, p, whole, nmatch - 2, pmatch + 2)) { SETPMATCH(s, ss); return(TRUE); } } return(FALSE); } logical matchq(char *s, void *p, logical is_regex, ptrdiff_t *pmatch) { if (is_regex) return(pcre_exec((pcre *)p, NULL, s, (int)strlen(s), 0, 0, (int *)pmatch, (pmatch == NULL)?0:PMATCH_SIZE) >= 0); else return(wildmatch(s, (char *)p, s, (pmatch == NULL)?0:PMATCH_SIZE, pmatch)); } logical genwildmatch(char *s, char *s2, char *p) { /* Exactly the same as wildmatch, except with doubly delimited strings, and we don't need to know the matched portions. */ char *ss, *pp; while ((*s == *p || *p == '?') && s < s2 && *p != '*' && *p != '\0') { s++; p++; } if (*p != '*') { if (s == s2 && *p == '\0') return(TRUE); else return(FALSE); } ss = s2; pp = strchr(p, '\0'); while ((*ss == *pp || *pp == '?' || ss == s2) && ss >= s && *pp != '*') { ss--; pp--; } if (pp == p) return(TRUE); else if (*pp != '*') return(FALSE); for (p++; *p == '*' && p != pp; p++) ; if (p == pp) return(TRUE); if (ss < s) return(FALSE); for ( ; s < s2; s++) { if (genwildmatch(s, s2, p)) return(TRUE); } return(FALSE); } logical headmatch(char *s, char *p) { /* does s start with p? */ for ( ; *p != '\0'; s++, p++) { if (*s != *p) return(FALSE); } return(TRUE); } logical headcasematch(char *s, char *p) { /* same, only case insensitive */ for ( ; *p != '\0'; s++, p++) { if (TOLOWER(*s) != TOLOWER(*p)) return(FALSE); } return(TRUE); } logical included(char *name, logical ispage, Include *listhead) { logical is_regex; Include *lp, *lastlp; if (listhead == NULL) return(TRUE); for (lp = listhead; lp != NULL; TO_NEXT(lp)) { is_regex = IS_REGEXT(lp->type); if (!is_regex && STREQ((char *)(lp->name), "pages")) { /* 'pages' is made case insensitive at config time */ if (ispage % 16) return(IS_INC(lp->type)); } else if (matchq(name, is_regex?((void *)(lp->pattern)):((void *)(lp->name)), is_regex, NULL)) return(IS_INC(lp->type)); lastlp = lp; } /* not listed: return opposite of first inc/exc */ return(!IS_INC(lastlp->type)); } logical pageq(char *name, Include *ispagehead, choice type) { char *c; logical ans; if (type == ITEM_REFERRER) return(TRUE); if (type != ITEM_FILE) return(FALSE); if ((c = strchr(name, '?')) != NULL) { *c = '\0'; ans = (choice)included(name, FALSE, ispagehead); *c = '?'; } else ans = (choice)included(name, FALSE, ispagehead); return(ans); }