"Fossies" - the Fresh Open Source Software Archive

Member "leafnode-1.12.0/miscutil.c" (28 Dec 2021, 17927 Bytes) of package /linux/misc/leafnode-1.12.0.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "miscutil.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.11.12_vs_1.12.0.

    1 /*
    2 libutil -- miscellaneous stuff
    3 
    4 Written by Arnt Gulbrandsen <agulbra@troll.no> and copyright 1995
    5 Troll Tech AS, Postboks 6133 Etterstad, 0602 Oslo, Norway, fax +47
    6 22646949.
    7 Modified by Cornelius Krasel <krasel@wpxx02.toxi.uni-wuerzburg.de>
    8 and Randolf Skerka <Randolf.Skerka@gmx.de>.
    9 Copyright of the modifications 1997.
   10 Modified by Kent Robotti <robotti@erols.com>. Copyright of the
   11 modifications 1998.
   12 Modified by Markus Enzenberger <enz@cip.physik.uni-muenchen.de>.
   13 Copyright of the modifications 1998.
   14 Modified by Cornelius Krasel <krasel@wpxx02.toxi.uni-wuerzburg.de>.
   15 Copyright of the modifications 1998, 1999.
   16 Modified and copyright of the modifications 2002 by Ralf Wildenhues
   17 <ralf.wildenhues@gmx.de>.
   18 Modified by Matthias Andree <matthias.andree@gmx.de>.
   19 Copyright of the modifications 1999 - 2021.
   20 
   21 See file COPYING for restrictions on the use of this software.
   22 */
   23 
   24 #include "leafnode.h"
   25 #include "validatefqdn.h"
   26 #include "strlcpy.h"
   27 #include "mastring.h"
   28 #include "ln_log.h"
   29 
   30 #include <fcntl.h>
   31 #include <sys/uio.h>
   32 #include <sys/param.h>
   33 #include <ctype.h>
   34 #include <errno.h>
   35 #include <limits.h>
   36 #include <stdlib.h>
   37 #include <netdb.h>
   38 #include <stdio.h>
   39 #include <string.h>
   40 #include <syslog.h>
   41 #include <pwd.h>
   42 #include <sys/stat.h>
   43 #include <sys/types.h>
   44 #include <unistd.h>
   45 #include "system.h"
   46 #include <signal.h>
   47 #include <stdarg.h>
   48 
   49 static void whoami(void);
   50 
   51 static void suicide(void) {
   52     /* just in case */
   53     fflush(stdout);
   54     fflush(stderr);
   55     raise(SIGKILL);
   56 }
   57 
   58 char fqdn[FQDNLEN + 1] = "";
   59 
   60 static const mode_t default_umask = 0002;
   61 
   62 /* xoverutil global vars */
   63 struct xoverinfo *xoverinfo;
   64 unsigned long xfirst, xlast;
   65 
   66 /* kludge around C89 const not being a compile-time constant */
   67 enum { hashsize = 1000 };
   68 
   69 static int
   70 createmsgiddir(void) {
   71     mastr *dir = mastr_new(1024);
   72     mastr *mid = mastr_new(1024);
   73     DIR *d;
   74     int rc = 0;
   75     int havedir[hashsize] = {0};
   76 
   77     mastr_vcat(dir, spooldir, "/message.id", NULL);
   78     d = opendir(mastr_str(dir));
   79     if (d) {
   80     struct dirent *de;
   81     unsigned long u;
   82     const char *t;
   83     char *e;
   84 
   85     /* read directory - should be faster than stat */
   86     while(errno = 0, de = readdir(d)) {
   87         t = de->d_name;
   88         if (isdigit((unsigned char)*t)) {
   89         u = strtoul(t, &e, 10);
   90         if (e > t && u < hashsize)
   91             havedir[u] = 1;
   92         }
   93     }
   94 
   95     if (errno)
   96         ln_log(LNLOG_SERR, LNLOG_CTOP, "error reading directory %s: %m",
   97             mastr_str(dir));
   98 
   99     closedir(d);
  100 
  101     /* create missing */
  102     for(u = 0; u < hashsize; u++) {
  103         char b[4];
  104 
  105         snprintf(b, sizeof(b), "%03lu", u);
  106         mastr_clear(mid);
  107         if (!havedir[u]) {
  108         mastr_vcat(mid, spooldir, "/message.id/", b, NULL);
  109         if (mkdir(mastr_str(mid), 02755)) {
  110             ln_log(LNLOG_SERR, LNLOG_CTOP, "error creating directory %s: %m",
  111                 mastr_str(mid));
  112             break;
  113         }
  114         }
  115     }
  116     } else {
  117     ln_log(LNLOG_SERR, LNLOG_CTOP, "cannot read %s: %m", mastr_str(dir));
  118     rc = -1;
  119     }
  120 
  121     mastr_delete(mid);
  122     mastr_delete(dir);
  123     return rc;
  124 }
  125 
  126 static struct { const char* name; mode_t mode; } dirs[] = {
  127     {"", 04755 },
  128     {"interesting.groups", 02775 },
  129     {"leaf.node", 0755 },
  130     {"failed.postings", 02775 },
  131     {"message.id", 0755 },
  132     {"out.going", 0755 },
  133     {"temp.files", 0755 },
  134 };
  135 
  136 static const int dirs_count = sizeof(dirs)/sizeof(dirs[0]);
  137 
  138 /*
  139  * initialize all global variables
  140  */
  141 /*@-globstate@*/
  142 int
  143 initvars(char *progname)
  144 {
  145 #ifndef TESTMODE
  146     struct passwd *pw;
  147 #endif
  148     char s[SIZE_s+1];
  149     int i;
  150     char *t;
  151 
  152     active = NULL;
  153     xoverinfo = NULL;
  154     xfirst = 0;
  155     xlast = 0;
  156 
  157     /* config.c stuff does not have to be initialized */
  158 
  159     expire_base = NULL;
  160     servers = NULL;
  161 
  162     t = getenv("LN_DEBUG");
  163     if (t)
  164     debugmode = atoi(t);
  165 
  166     (void)umask(default_umask);
  167     if (strlen(spooldir) != strspn(spooldir, PORTFILENAMECSET "/")) {
  168     /* verrecke! */
  169     syslog(LOG_CRIT, "Fatal: spooldir contains illegal characters. "
  170            "Recompile leafnode with a proper spooldir setting.");
  171     suicide();
  172     }
  173 
  174 #ifndef TESTMODE
  175     pw = getpwnam(NEWS_USER);
  176     if (!pw) {
  177     fprintf(stderr, "no such user: %s\n", NEWS_USER);
  178     return FALSE;
  179     }
  180 #endif
  181 
  182     /* These directories should exist anyway */
  183     for (i = 0 ; i < dirs_count ; i++) {
  184     xsnprintf(s, SIZE_s, "%s/%s", spooldir, dirs[i].name);
  185     if ((mkdir(s, dirs[i].mode) && errno != EEXIST)
  186         || chmod(s, dirs[i].mode)
  187 #ifndef TESTMODE
  188     || chown(s, pw->pw_uid, pw->pw_gid)
  189 #endif
  190        ) {
  191         int e = errno;
  192         struct stat st;
  193         if (stat(s, &st)
  194 #ifndef TESTMODE
  195             || st.st_uid != pw->pw_uid
  196 #endif
  197            ) {
  198         fprintf(stderr, "Warning: cannot create %s with proper ownership: %s\nMake sure you run this program as user root or %s.\n", s, strerror(e),
  199             NEWS_USER);
  200         syslog(LOG_WARNING, "Warning: cannot create %s with proper ownership: %s", s, strerror(e));
  201         suicide();
  202         }
  203     }
  204     }
  205 
  206     whoami();
  207 
  208 #ifndef TESTMODE
  209     if (progname) {
  210     int e;
  211 
  212 #ifdef HAVE_SETGID
  213     e = setgid(pw->pw_gid);
  214 #else
  215     e = setregid(pw->pw_gid, pw->pw_gid);
  216 #endif
  217 
  218     if (e) {
  219         syslog(LOG_ERR, "%s: setting group id to %ld failed: %s",
  220             progname, (long)pw->pw_gid, strerror(errno));
  221         fprintf(stderr, "%s: setting group id to %ld failed: %s\n",
  222             progname, (long)pw->pw_gid, strerror(errno));
  223     }
  224 
  225 #ifdef HAVE_SETUID
  226     e = setuid(pw->pw_uid);
  227 #else
  228     e = setreuid(pw->pw_uid, pw->pw_uid);
  229 #endif
  230     if (e) {
  231         syslog(LOG_ERR, "%s: setting user id to %ld failed: %s. (Real uid is %ld, effective %ld)",
  232             progname, (long)pw->pw_uid, strerror(errno), (long)getuid(), (long)geteuid());
  233         fprintf(stderr, "%s: setting user id to %ld failed: %s. (Real uid is %ld, effective %ld)\n",
  234             progname, (long)pw->pw_uid, strerror(errno), (long)getuid(), (long)geteuid());
  235     }
  236 
  237     /* extra sanity check - read back the new values */
  238     if (getuid() != pw->pw_uid || getgid() != pw->pw_gid) {
  239         syslog(LOG_ERR, "%s: must be run as %s or root", progname, NEWS_USER);
  240         fprintf(stderr, "%s: must be run as %s or root\n", progname, NEWS_USER);
  241         endpwent();
  242         return FALSE;
  243     }
  244     }
  245 #endif              /* not TESTMODE */
  246     endpwent();
  247 
  248     if (chdir(spooldir) || (i = open(".", O_RDONLY)) < 0) {
  249     int e = errno;
  250     syslog(LOG_CRIT, "Fatal: cannot change to or open spooldir: %m");
  251     fprintf(stderr, "Fatal: cannot change or open spooldir: %s\n",
  252         strerror(e));
  253     suicide();
  254     }
  255     (void)close(i);
  256 
  257     /* create missing message.id directories */
  258     if (createmsgiddir())
  259     return FALSE;
  260 
  261     return TRUE;
  262 }
  263 
  264 /*@=globstate@*/
  265 
  266 /*
  267  * check whether "groupname" is represented in interesting.groups without
  268  * touching the file
  269  */
  270 int
  271 isinteresting(const char *groupname)
  272 {
  273     DIR *d;
  274     struct dirent *de;
  275     char s[SIZE_s+1];
  276 
  277     xsnprintf(s, SIZE_s, "%s/interesting.groups", spooldir);
  278     d = opendir(s);
  279     if (!d) {
  280     syslog(LOG_ERR, "Unable to open directory %s: %m", s);
  281     printf("Unable to open directory %s\n", s);
  282     return FALSE;
  283     }
  284 
  285     while ((de = readdir(d)) != NULL) {
  286     if (strcasecmp(de->d_name, groupname) == 0) {
  287         closedir(d);
  288         return TRUE;
  289     }
  290     }
  291     closedir(d);
  292     return FALSE;
  293 }
  294 
  295 void
  296 overrun(void)
  297 /* report buffer overrun */
  298 {
  299     syslog(LOG_CRIT, "buffer size too small, cannot continue, aborting program");
  300     abort();
  301     kill(getpid(), SIGKILL);    /* really die! */
  302 }
  303 
  304 /** construct file name for message.id/NNN/ in spooldir from message ID */
  305 const char *
  306 lookup(const char *msgid /** if LOOKUP_FREE, release static resources */)
  307 {
  308     static char *name = NULL;
  309     static size_t namelen = 0;
  310     unsigned int r;
  311     size_t i;
  312 
  313     if (msgid == LOOKUP_FREE) {
  314     namelen = 0;
  315     if (name)
  316         free(name);
  317     return NULL;
  318     }
  319 
  320     if (!msgid || !*msgid)
  321     return NULL;
  322 
  323     i = strlen(msgid) + strlen(spooldir) + 30;
  324 
  325     if (!name) {
  326     name = (char *)critmalloc(i, "lookup");
  327     namelen = i;
  328     } else if (i > namelen) {
  329     free(name);
  330     name = (char *)critmalloc(i, "lookup");
  331     namelen = i;
  332     }
  333 
  334     strcpy(name, spooldir); /* RATS: ignore */
  335     strcat(name, "/message.id/000/");   /* RATS: ignore */
  336     i = strlen(name);
  337     strcat(name, msgid);    /* RATS: ignore */
  338 
  339     r = 0;
  340     do {
  341     if (name[i] == '/')
  342         name[i] = '@';
  343     else if (name[i] == '>')
  344         name[i + 1] = '\0';
  345     r += (int)(name[i]);
  346     r += ++i;
  347     } while (name[i]);
  348 
  349     i = strlen(spooldir) + 14;  /* to the last digit */
  350     r = (r % 999) + 1;
  351     name[i--] = '0' + (char)(r % 10);
  352     r /= 10;
  353     name[i--] = '0' + (char)(r % 10);
  354     r /= 10;
  355     name[i] = '0' + (char)(r);
  356     return name;
  357 }
  358 
  359 #define LM_SIZE 65536
  360 
  361 static int
  362 makedir(char *d)
  363 {
  364     char *p;
  365     char *q;
  366 
  367     if (verbose > 3)
  368     printf("makedir(%s)\n", d);
  369     if (!d || *d != '/' || chdir("/"))
  370     return 0;
  371     q = d;
  372     do {
  373     *q = '/';
  374     p = q;
  375     q = strchr(++p, '/');
  376     if (q)
  377         *q = '\0';
  378     if (!chdir(p))
  379         continue;       /* ok, I do use it sometimes :) */
  380     if (errno == ENOENT)
  381         if (mkdir(p, 0775)) {
  382         syslog(LOG_ERR, "mkdir %s: %m", d);
  383         exit(1);
  384         }
  385     if (chdir(p)) {
  386         syslog(LOG_ERR, "chdir %s: %m", d);
  387         exit(1);
  388     }
  389     } while (q);
  390     return 1;
  391 }
  392 
  393 /* prefix numeric group name components with a minus */
  394 static int migrate(const char *name) {
  395     char *p = critstrdup(name, "dogroup"), *q, *t = NULL;
  396 
  397     /* shortcut: don't call into chdir() excessively */
  398     for(q = strtok(p, "."); q; q = strtok(NULL, ".")) {
  399     if (strspn(q, "0123456789") == strlen(q)) break;
  400     }
  401     if (!q) {
  402     free(p);
  403     return 0;
  404     }
  405 
  406     if (chdir(spooldir)) goto barf;
  407 
  408     for(q = strtok(p, "."); q; q = strtok(NULL, ".")) {
  409     t = critmalloc(strlen(q)+2, "dogroup");
  410     t[0] = '-';
  411     strcpy(t+1, q);
  412     if (strspn(q, "0123456789") == strlen(q)) {
  413         struct stat st;
  414         if (0 == chdir(t)) {
  415         free(t);
  416         continue;
  417         }
  418         if (0 == stat(q, &st) && S_ISDIR(st.st_mode)) {
  419         if (rename(q, t)) {
  420             ln_log(LNLOG_SERR, LNLOG_CTOP, "cannot rename %s to %s: %m",
  421                 q, t);
  422             goto barf;
  423         }
  424         if (0 == chdir(t)) {
  425             free(t);
  426             continue;
  427         }
  428         }
  429         goto barf;
  430     }
  431     if (chdir(q)) {
  432         goto barf;
  433     }
  434     free(t);
  435     }
  436     free(p);
  437     return 0;
  438 barf:
  439     free(p);
  440     free(t);
  441     return -1;
  442 }
  443 
  444 /* chdir to the directory of the argument if it's a valid group */
  445 int
  446 chdirgroup(const char *group, int creatdir)
  447 {
  448     char *p;
  449     const char *c;
  450 
  451     if (group && *group) {
  452     int dots = 0;
  453     char *nam, *q;
  454     mastr *d = mastr_new(1024);
  455 
  456     migrate(group);
  457     mastr_vcat(d, spooldir, "/", group, NULL);
  458     p = mastr_modifyable_str(d) + strlen(spooldir) + 1;
  459     while (*p) {
  460         if (*p == '.') {
  461         *p = '/';
  462         } else
  463         *p = tolower((unsigned char)*p);
  464         p++;
  465     }
  466     for (c = mastr_str(d);*c;c++) {
  467         if (*c == '/' && c[1] && strspn(c+1, "0123456789") == strcspn(c+1, "/"))
  468         dots++;
  469     }
  470     nam = critmalloc(mastr_len(d) + dots + 1, "chdirgroup");
  471     for (c = mastr_str(d), q=nam;*c;c++) {
  472         *(q++) = *c;
  473         if (*c == '/' && c[1] && strspn(c+1, "0123456789") == strcspn(c+1, "/"))
  474         *(q++) = '-';
  475     }
  476     *q = 0;
  477     mastr_delete(d);
  478 
  479     if (!chdir(nam)) {
  480         free(nam);
  481         return 1;       /* chdir successful */
  482     }
  483     if (creatdir) {
  484         int r = makedir(nam);
  485         free(nam);
  486         return r;
  487     }
  488     free(nam);
  489     }
  490     return 0;
  491 }
  492 
  493 #ifdef HAVE_IPV6
  494 /* get the fully qualified domain name of this box into fqdn */
  495 /* IPv6-capable version by Boris Manojlovic <boris@steki.net> */
  496 static void
  497 whoami(void)
  498 {
  499     struct addrinfo hints, *res;
  500     int debugqual = 0;
  501     char *x;
  502 
  503     if ((x = getenv("LN_DEBUG_QUALIFICATION")) != NULL
  504     && *x)
  505     debugqual = 1;
  506     memset(&hints, 0, sizeof hints);
  507     hints.ai_flags = AI_CANONNAME;
  508 
  509     if (!gethostname(fqdn, sizeof(fqdn)) && !getaddrinfo(fqdn, NULL, &hints, &res)) {
  510     if (res->ai_canonname != NULL) {
  511             xstrlcpy(fqdn,res->ai_canonname, sizeof(fqdn));
  512             if (debugqual) syslog(LOG_DEBUG, "canonical hostname: %s", fqdn);
  513         }
  514         freeaddrinfo(res);
  515     }
  516 }
  517 #else
  518 /* get the fully qualified domain name of this box into fqdn */
  519 /* legacy version, uses legacy (IPv4) gethostby*() interfaces */
  520 static void
  521 whoami(void)
  522 {
  523     struct hostent *he;
  524     int debugqual = 0;
  525     char *x;
  526 
  527     if ((x = getenv("LN_DEBUG_QUALIFICATION")) != NULL
  528     && *x)
  529     debugqual = 1;
  530 
  531     if (!gethostname(fqdn, sizeof(fqdn)) && (he = gethostbyname(fqdn)) != NULL) {
  532     xstrlcpy(fqdn, he->h_name, sizeof(fqdn));
  533     if (debugqual) syslog(LOG_DEBUG, "canonical hostname: %s", fqdn);
  534     if (!is_validfqdn(fqdn)) {
  535         char **alias;
  536         alias = he->h_aliases;
  537         while (alias && *alias) {
  538         if (debugqual) {
  539             syslog(LOG_DEBUG, "alias for my hostname: %s", *alias);
  540         }
  541         if (is_validfqdn(*alias)) {
  542             xstrlcpy(fqdn, *alias, sizeof(fqdn));
  543             break;
  544         } else {
  545             alias++;
  546         }
  547         }
  548     }
  549     endhostent();
  550     }
  551 }
  552 #endif
  553 
  554 /*
  555  * prepend string "newentry" to stringlist "list".
  556  */
  557 void
  558 prependtolist(struct stringlist **list, /*@unique@*/ const char *newentry)
  559 {
  560 
  561     struct stringlist *ptr;
  562 
  563     ptr = (struct stringlist *)critmalloc(sizeof(struct stringlist) +
  564                       strlen(newentry),
  565                       "Allocating space in stringlist");
  566     strcpy(ptr->string, newentry);  /* RATS: ignore */
  567     ptr->next = *list;
  568     *list = ptr;
  569 }
  570 
  571 /*
  572  * find a string in a stringlist
  573  * return pointer to string if found, NULL otherwise
  574  */
  575 char *
  576 findinlist(struct stringlist *haystack, char *needle)
  577 {
  578     struct stringlist *a;
  579 
  580     a = haystack;
  581     while (a && *a->string) {
  582     if (strncmp(needle, a->string, strlen(needle)) == 0)
  583         return a->string;
  584     a = a->next;
  585     }
  586     return NULL;
  587 }
  588 
  589 /*
  590  * find a string in a stringlist
  591  * return pointer to string if found, NULL otherwise
  592  */
  593 struct stringlist **
  594 lfindinlist(struct stringlist **haystack, char *needle, size_t len)
  595 {
  596     struct stringlist **a;
  597 
  598     a = haystack;
  599     while (a && *a && *(*a)->string) {
  600     if (strncmp(needle, (*a)->string, len) == 0)
  601         return a;
  602     a = &(*a)->next;
  603     }
  604     return NULL;
  605 }
  606 
  607 void replaceinlist(struct stringlist **haystack, char *needle, size_t len)
  608 {
  609     struct stringlist **f = lfindinlist(haystack, needle, len);
  610     struct stringlist *n;
  611     if (!f) prependtolist(haystack, needle);
  612     else {
  613     n = (*f)->next;
  614         free(*f);
  615         *f = (struct stringlist *)critmalloc(sizeof(struct stringlist) + 
  616             strlen(needle), "Allocating space in stringlist");
  617     strcpy((*f)->string, needle); /* RATS: ignore */
  618     (*f)->next = n;
  619     }
  620 }
  621 
  622 /*
  623  * free a list
  624  */
  625 void
  626 freelist( /*@only@*/ struct stringlist *list)
  627 {
  628     struct stringlist *a;
  629 
  630     while (list) {
  631     a = list->next;
  632     free(list);
  633     list = a;
  634     }
  635 }
  636 
  637 /* next few routines implement a mapping from message-id to article
  638    number, and clearing the entire space */
  639 
  640 struct msgidtree {
  641     struct msgidtree *left;
  642     struct msgidtree *right;
  643     char msgid[1]; /* RATS: ignore */
  644 };
  645 
  646 static struct msgidtree *head;  /* starts as NULL */
  647 
  648 static int
  649 comparemsgid(const char *id1, const char *id2)
  650 {
  651     int c;
  652 
  653     /* comparing only by msgid is uncool because the tree becomes
  654        very unbalanced */
  655     c = strcmp(strchr(id1, '@'), strchr(id2, '@'));
  656     if (!c)
  657     c = strcmp(id1, id2);
  658     return c;
  659 }
  660 
  661 void
  662 insertmsgid( /*@unique@*/ const char *msgid)
  663 {
  664     struct msgidtree **a;
  665     int c;
  666 
  667     if (strchr(msgid, '@') == 0)
  668     return;
  669 
  670     a = &head;
  671     while (a) {
  672     if (*a) {
  673         c = comparemsgid((*a)->msgid, msgid);
  674         if (c < 0)
  675         a = &((*a)->left);
  676         else if (c > 0)
  677         a = &((*a)->right);
  678         else {
  679         return;
  680         }
  681     } else {
  682         *a = (struct msgidtree *)
  683         critmalloc(sizeof(struct msgidtree) + strlen(msgid),
  684                "Building expiry database");
  685         (*a)->left = NULL;
  686         (*a)->right = NULL;
  687         strcpy((*a)->msgid, msgid); /* RATS: ignore */
  688         return;
  689     }
  690     }
  691 }
  692 
  693 /* returns 0 if not found, 1 otherwise */
  694 int
  695 findmsgid(const char *msgid)
  696 {
  697     struct msgidtree *a;
  698     int c;
  699 
  700     /* domain part differs more than local-part, so try it first */
  701 
  702     if (NULL == strchr(msgid, '@'))
  703     return 0;
  704 
  705     a = head;
  706     while (a) {
  707     c = comparemsgid(a->msgid, msgid);
  708     if (c < 0)
  709         a = a->left;
  710     else if (c > 0)
  711         a = a->right;
  712     else
  713         return 1;
  714     }
  715     return 0;
  716 }
  717 
  718 static void
  719 begone( /*@null@*//*@only@*/ struct msgidtree *m)
  720 {
  721     if (m) {
  722     begone(m->right);
  723     begone(m->left);
  724     free((char *)m);
  725     }
  726 }
  727 
  728 void
  729 clearidtree(void)
  730 {
  731     begone(head);
  732     head = NULL;
  733 }
  734 
  735 static int
  736 xtraverseidtree(struct msgidtree *m, tmihook h)
  737 {
  738     int e = 0;
  739     if (!m) return 0;
  740     e |= xtraverseidtree(m->left, h);
  741     e |= h(m->msgid);
  742     e |= xtraverseidtree(m->right, h);
  743     return e;
  744 }
  745 
  746 int
  747 traverseidtree(tmihook h) {
  748     return xtraverseidtree(head, h);
  749 }
  750 
  751 /*@dependent@*/ const char *
  752 rfctime(void)
  753 {
  754     static char date[128]; /* RATS: ignore */
  755     const char *months[] = { "Jan", "Feb", "Mar", "Apr",
  756     "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  757     };
  758     const char *days[] = { "Sun", "Mon", "Tue", "Wed",
  759     "Thu", "Fri", "Sat"
  760     };
  761     time_t now;
  762     struct tm gm;
  763 
  764     now = time(NULL);
  765 #ifdef HAVE_STRUCT_TM_TM_GMTOFF
  766     {
  767     char sign;
  768     long off;
  769 
  770     gm = *(localtime(&now));
  771     /* fiddle a bit to make sure we don't get negative a%b results:
  772      * make sure operands are non-negative */
  773     off = gm.tm_gmtoff/60;
  774     sign = off < 0 ? '-' : '+';
  775     off = labs(off);
  776     xsnprintf(date, sizeof(date), "%3s, %d %3s %4d %02d:%02d:%02d %c%02ld%02ld",
  777         days[gm.tm_wday], gm.tm_mday, months[gm.tm_mon],
  778         gm.tm_year + 1900, gm.tm_hour, gm.tm_min, gm.tm_sec,
  779         sign, off / 60, off % 60);
  780     }
  781 #else
  782     gm = *(gmtime(&now));
  783     xsnprintf(date, sizeof(date), "%3s, %d %3s %4d %02d:%02d:%02d -0000",
  784           days[gm.tm_wday], gm.tm_mday, months[gm.tm_mon],
  785           gm.tm_year + 1900, gm.tm_hour, gm.tm_min, gm.tm_sec);
  786 #endif
  787 
  788     return (date);
  789 }
  790 
  791 int
  792 ngmatch(const char *pattern, const char *str)
  793 {
  794     return !wildmat(str, pattern);
  795 }
  796 
  797 int
  798 xsnprintf(char *str, size_t n, const char *format, ...)
  799 {
  800     int r;
  801     va_list ap;
  802 
  803     va_start(ap, format);
  804     r = vsnprintf(str, n, format, ap);
  805     va_end(ap);
  806 
  807     if ((size_t) r >= n || r < 0)
  808     overrun();
  809     return r;
  810 }
  811 
  812 size_t xstrlcpy(char *dst, const char *src, size_t size) 
  813 {
  814     size_t s;
  815     s = strlcpy(dst, src, size);
  816     if (s >= size)
  817     overrun();
  818     return s;
  819 }