"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.

    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 }