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