Annotation of src/lib/libc/gen/getcap.c, Revision 1.51
1.51 ! christos 1: /* $NetBSD: getcap.c,v 1.50 2011/10/15 23:00:01 christos Exp $ */
1.9 cgd 2:
1.1 cgd 3: /*-
1.9 cgd 4: * Copyright (c) 1992, 1993
5: * The Regents of the University of California. All rights reserved.
1.1 cgd 6: *
7: * This code is derived from software contributed to Berkeley by
8: * Casey Leedom of Lawrence Livermore National Laboratory.
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
1.38 agc 18: * 3. Neither the name of the University nor the names of its contributors
1.1 cgd 19: * may be used to endorse or promote products derived from this software
20: * without specific prior written permission.
21: *
22: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32: * SUCH DAMAGE.
33: */
34:
1.39 lukem 35: #if HAVE_NBTOOL_CONFIG_H
36: #include "nbtool_config.h"
1.37 uwe 37: #endif
38:
1.13 christos 39: #include <sys/cdefs.h>
1.1 cgd 40: #if defined(LIBC_SCCS) && !defined(lint)
1.9 cgd 41: #if 0
42: static char sccsid[] = "@(#)getcap.c 8.3 (Berkeley) 3/25/94";
43: #else
1.51 ! christos 44: __RCSID("$NetBSD: getcap.c,v 1.50 2011/10/15 23:00:01 christos Exp $");
1.9 cgd 45: #endif
1.1 cgd 46: #endif /* LIBC_SCCS and not lint */
47:
1.41 christos 48: #ifndef SMALL
1.14 jtc 49: #include "namespace.h"
1.41 christos 50: #endif
1.1 cgd 51: #include <sys/types.h>
1.36 tron 52: #include <sys/param.h>
1.30 lukem 53:
54: #include <assert.h>
1.51 ! christos 55: #include <stddef.h>
1.1 cgd 56: #include <ctype.h>
1.41 christos 57: #ifndef SMALL
1.1 cgd 58: #include <db.h>
1.41 christos 59: #endif
1.1 cgd 60: #include <errno.h>
61: #include <fcntl.h>
62: #include <limits.h>
63: #include <stdio.h>
64: #include <stdlib.h>
65: #include <string.h>
66: #include <unistd.h>
1.14 jtc 67:
1.49 joerg 68: #if defined(__weak_alias) && !defined(SMALL)
1.32 mycroft 69: __weak_alias(cgetcap,_cgetcap)
70: __weak_alias(cgetclose,_cgetclose)
71: __weak_alias(cgetent,_cgetent)
72: __weak_alias(cgetfirst,_cgetfirst)
73: __weak_alias(cgetmatch,_cgetmatch)
74: __weak_alias(cgetnext,_cgetnext)
75: __weak_alias(cgetnum,_cgetnum)
76: __weak_alias(cgetset,_cgetset)
77: __weak_alias(cgetstr,_cgetstr)
78: __weak_alias(cgetustr,_cgetustr)
1.48 christos 79: __weak_alias(csetexpandtc,_csetexpandtc)
1.14 jtc 80: #endif
1.1 cgd 81:
82: #define BFRAG 1024
83: #define BSIZE 1024
84: #define ESC ('[' & 037) /* ASCII ESC */
85: #define MAX_RECURSION 32 /* maximum getent recursion */
86: #define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */
87:
88: #define RECOK (char)0
89: #define TCERR (char)1
90: #define SHADOW (char)2
91:
92: static size_t topreclen; /* toprec length */
93: static char *toprec; /* Additional record specified by cgetset() */
94: static int gottoprec; /* Flag indicating retrieval of toprecord */
1.48 christos 95: static int expandtc = 1; /* flag to expand tc= or not */
1.1 cgd 96:
1.41 christos 97: #ifndef SMALL
1.40 christos 98: static int cdbget(DB *, char **, const char *);
1.41 christos 99: #endif
1.40 christos 100: static int getent(char **, size_t *, const char * const *, int,
101: const char *, int, char *);
102: static int nfcmp(char *, char *);
1.1 cgd 103:
104: /*
105: * Cgetset() allows the addition of a user specified buffer to be added
106: * to the database array, in effect "pushing" the buffer on top of the
107: * virtual database. 0 is returned on success, -1 on failure.
108: */
109: int
1.40 christos 110: cgetset(const char *ent)
1.1 cgd 111: {
1.27 abs 112: const char *source, *check;
113: char *dest;
114:
1.1 cgd 115: if (ent == NULL) {
1.47 jnemeth 116: if (toprec != NULL)
1.1 cgd 117: free(toprec);
118: toprec = NULL;
119: topreclen = 0;
1.47 jnemeth 120: return 0;
1.1 cgd 121: }
122: topreclen = strlen(ent);
1.47 jnemeth 123: if ((toprec = malloc(topreclen + 1)) == NULL) {
1.1 cgd 124: errno = ENOMEM;
1.47 jnemeth 125: return -1;
1.1 cgd 126: }
127: gottoprec = 0;
1.27 abs 128:
1.47 jnemeth 129: source = ent;
130: dest = toprec;
131: while (*source != '\0') { /* Strip whitespace */
1.27 abs 132: *dest++ = *source++; /* Do not check first field */
133: while (*source == ':') {
1.47 jnemeth 134: check = source + 1;
1.29 abs 135: while (*check && (isspace((unsigned char)*check) ||
136: (*check=='\\' && isspace((unsigned char)check[1]))))
1.27 abs 137: ++check;
1.47 jnemeth 138: if (*check == ':')
139: source = check;
1.27 abs 140: else
141: break;
142:
143: }
144: }
1.47 jnemeth 145: *dest = 0;
1.27 abs 146:
1.47 jnemeth 147: return 0;
1.1 cgd 148: }
149:
150: /*
151: * Cgetcap searches the capability record buf for the capability cap with
152: * type `type'. A pointer to the value of cap is returned on success, NULL
153: * if the requested capability couldn't be found.
154: *
155: * Specifying a type of ':' means that nothing should follow cap (:cap:).
156: * In this case a pointer to the terminating ':' or NUL will be returned if
157: * cap is found.
158: *
159: * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
160: * return NULL.
161: */
162: char *
163: cgetcap(buf, cap, type)
1.21 mycroft 164: char *buf;
165: const char *cap;
1.1 cgd 166: int type;
167: {
1.21 mycroft 168: char *bp;
169: const char *cp;
1.1 cgd 170:
1.30 lukem 171: _DIAGASSERT(buf != NULL);
172: _DIAGASSERT(cap != NULL);
173:
1.1 cgd 174: bp = buf;
175: for (;;) {
176: /*
177: * Skip past the current capability field - it's either the
178: * name field if this is the first time through the loop, or
179: * the remainder of a field whose name failed to match cap.
180: */
181: for (;;)
182: if (*bp == '\0')
1.47 jnemeth 183: return NULL;
184: else if (*bp++ == ':')
185: break;
1.1 cgd 186:
187: /*
188: * Try to match (cap, type) in buf.
189: */
190: for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
191: continue;
192: if (*cp != '\0')
193: continue;
194: if (*bp == '@')
1.47 jnemeth 195: return NULL;
1.1 cgd 196: if (type == ':') {
197: if (*bp != '\0' && *bp != ':')
198: continue;
1.47 jnemeth 199: return bp;
1.1 cgd 200: }
201: if (*bp != type)
202: continue;
203: bp++;
1.47 jnemeth 204: return *bp == '@' ? NULL : bp;
1.1 cgd 205: }
206: /* NOTREACHED */
207: }
208:
209: /*
210: * Cgetent extracts the capability record name from the NULL terminated file
211: * array db_array and returns a pointer to a malloc'd copy of it in buf.
212: * Buf must be retained through all subsequent calls to cgetcap, cgetnum,
213: * cgetflag, and cgetstr, but may then be free'd. 0 is returned on success,
214: * -1 if the requested record couldn't be found, -2 if a system error was
215: * encountered (couldn't open/read a file, etc.), and -3 if a potential
216: * reference loop is detected.
217: */
1.45 christos 218: /* coverity[+alloc : arg-*0] */
1.1 cgd 219: int
1.40 christos 220: cgetent(char **buf, const char * const *db_array, const char *name)
1.1 cgd 221: {
1.18 thorpej 222: size_t dummy;
1.1 cgd 223:
1.30 lukem 224: _DIAGASSERT(buf != NULL);
225: _DIAGASSERT(db_array != NULL);
226: _DIAGASSERT(name != NULL);
227:
1.47 jnemeth 228: return getent(buf, &dummy, db_array, -1, name, 0, NULL);
1.1 cgd 229: }
230:
1.48 christos 231: void
232: csetexpandtc(int etc)
233: {
234: expandtc = etc;
235: }
236:
1.1 cgd 237: /*
238: * Getent implements the functions of cgetent. If fd is non-negative,
239: * *db_array has already been opened and fd is the open file descriptor. We
240: * do this to save time and avoid using up file descriptors for tc=
241: * recursions.
242: *
243: * Getent returns the same success/failure codes as cgetent. On success, a
244: * pointer to a malloc'ed capability record with all tc= capabilities fully
245: * expanded and its length (not including trailing ASCII NUL) are left in
246: * *cap and *len.
247: *
248: * Basic algorithm:
249: * + Allocate memory incrementally as needed in chunks of size BFRAG
250: * for capability buffer.
251: * + Recurse for each tc=name and interpolate result. Stop when all
252: * names interpolated, a name can't be found, or depth exceeds
253: * MAX_RECURSION.
254: */
1.45 christos 255: /* coverity[+alloc : arg-*0] */
1.1 cgd 256: static int
1.40 christos 257: getent(char **cap, size_t *len, const char * const *db_array, int fd,
258: const char *name, int depth, char *nfield)
1.1 cgd 259: {
1.41 christos 260: #ifndef SMALL
1.1 cgd 261: DB *capdbp;
1.41 christos 262: char pbuf[MAXPATHLEN];
263: char *cbuf;
264: int retval;
265: size_t clen;
266: #endif
267: char *record, *newrecord;
1.47 jnemeth 268: char *r_end, *rp; /* pacify gcc */
1.40 christos 269: const char * const *db_p;
1.47 jnemeth 270: int myfd, eof, foundit;
1.1 cgd 271: int tc_not_resolved;
272:
1.30 lukem 273: _DIAGASSERT(cap != NULL);
274: _DIAGASSERT(len != NULL);
275: _DIAGASSERT(db_array != NULL);
276: /* fd may be -1 */
277: _DIAGASSERT(name != NULL);
278: /* nfield may be NULL */
279:
1.47 jnemeth 280: myfd = 0;
281: rp = NULL;
282:
1.1 cgd 283: /*
284: * Return with ``loop detected'' error if we've recursed more than
285: * MAX_RECURSION times.
286: */
287: if (depth > MAX_RECURSION)
1.47 jnemeth 288: return -3;
1.1 cgd 289:
290: /*
291: * Check if we have a top record from cgetset().
292: */
293: if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) {
1.47 jnemeth 294: if ((record = malloc(topreclen + BFRAG)) == NULL) {
1.1 cgd 295: errno = ENOMEM;
1.47 jnemeth 296: return -2;
1.1 cgd 297: }
1.11 mrg 298: (void)strcpy(record, toprec); /* XXX: strcpy is safe */
1.1 cgd 299: db_p = db_array;
300: rp = record + topreclen + 1;
301: r_end = rp + BFRAG;
302: goto tc_exp;
303: }
304: /*
305: * Allocate first chunk of memory.
306: */
307: if ((record = malloc(BFRAG)) == NULL) {
308: errno = ENOMEM;
1.47 jnemeth 309: return -2;
1.1 cgd 310: }
311: r_end = record + BFRAG;
312: foundit = 0;
313: /*
314: * Loop through database array until finding the record.
315: */
316:
317: for (db_p = db_array; *db_p != NULL; db_p++) {
318: eof = 0;
319:
320: /*
321: * Open database if not already open.
322: */
323:
324: if (fd >= 0) {
1.15 kleink 325: (void)lseek(fd, (off_t)0, SEEK_SET);
1.1 cgd 326: } else {
1.41 christos 327: #ifndef SMALL
1.1 cgd 328: (void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p);
1.48 christos 329: if (expandtc &&
330: (capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0))
1.1 cgd 331: != NULL) {
332: free(record);
333: retval = cdbget(capdbp, &record, name);
1.8 cgd 334: if (retval < 0) {
335: /* no record available */
336: (void)capdbp->close(capdbp);
1.47 jnemeth 337: return retval;
1.8 cgd 338: }
339: /* save the data; close frees it */
1.7 cgd 340: clen = strlen(record);
1.46 jnemeth 341: if ((cbuf = malloc(clen + 1)) == NULL) {
342: (void)capdbp->close(capdbp);
343: errno = ENOMEM;
1.47 jnemeth 344: return -2;
1.46 jnemeth 345: }
1.23 perry 346: memmove(cbuf, record, clen + 1);
1.7 cgd 347: if (capdbp->close(capdbp) < 0) {
1.30 lukem 348: int serrno = errno;
349:
1.7 cgd 350: free(cbuf);
1.30 lukem 351: errno = serrno;
1.47 jnemeth 352: return -2;
1.7 cgd 353: }
354: *len = clen;
355: *cap = cbuf;
1.47 jnemeth 356: return retval;
1.41 christos 357: } else
358: #endif
359: {
1.1 cgd 360: fd = open(*db_p, O_RDONLY, 0);
361: if (fd < 0) {
362: /* No error on unfound file. */
1.10 mycroft 363: continue;
1.1 cgd 364: }
365: myfd = 1;
366: }
367: }
368: /*
369: * Find the requested capability record ...
370: */
371: {
372: char buf[BUFSIZ];
1.20 mycroft 373: char *b_end, *bp, *cp;
374: int c, slash;
1.1 cgd 375:
376: /*
377: * Loop invariants:
378: * There is always room for one more character in record.
379: * R_end always points just past end of record.
380: * Rp always points just past last character in record.
381: * B_end always points just past last character in buf.
382: * Bp always points at next character in buf.
1.20 mycroft 383: * Cp remembers where the last colon was.
1.1 cgd 384: */
385: b_end = buf;
386: bp = buf;
1.47 jnemeth 387: cp = NULL;
1.20 mycroft 388: slash = 0;
1.1 cgd 389: for (;;) {
390: /*
391: * Read in a line implementing (\, newline)
392: * line continuation.
393: */
394: rp = record;
395: for (;;) {
396: if (bp >= b_end) {
1.51 ! christos 397: ssize_t n;
1.1 cgd 398:
399: n = read(fd, buf, sizeof(buf));
400: if (n <= 0) {
401: if (myfd)
402: (void)close(fd);
403: if (n < 0) {
1.30 lukem 404: int serrno = errno;
405:
1.1 cgd 406: free(record);
1.30 lukem 407: errno = serrno;
1.47 jnemeth 408: return -2;
1.1 cgd 409: } else {
410: fd = -1;
411: eof = 1;
412: break;
413: }
414: }
415: b_end = buf+n;
416: bp = buf;
417: }
418:
419: c = *bp++;
420: if (c == '\n') {
1.20 mycroft 421: if (slash) {
422: slash = 0;
1.1 cgd 423: rp--;
424: continue;
425: } else
426: break;
427: }
1.20 mycroft 428: if (slash) {
429: slash = 0;
430: cp = 0;
431: }
432: if (c == ':') {
433: /*
434: * If the field was `empty' (i.e.
435: * contained only white space), back up
436: * to the colon (eliminating the
437: * field).
438: */
1.47 jnemeth 439: if (cp != NULL)
1.20 mycroft 440: rp = cp;
441: else
442: cp = rp;
443: } else if (c == '\\') {
444: slash = 1;
445: } else if (c != ' ' && c != '\t') {
446: /*
447: * Forget where the colon was, as this
448: * is not an empty field.
449: */
450: cp = 0;
451: }
1.1 cgd 452: *rp++ = c;
453:
454: /*
455: * Enforce loop invariant: if no room
456: * left in record buffer, try to get
457: * some more.
458: */
459: if (rp >= r_end) {
1.51 ! christos 460: ptrdiff_t pos;
1.1 cgd 461: size_t newsize;
462:
463: pos = rp - record;
464: newsize = r_end - record + BFRAG;
1.33 itojun 465: newrecord = realloc(record, newsize);
466: if (newrecord == NULL) {
467: free(record);
1.1 cgd 468: if (myfd)
469: (void)close(fd);
1.30 lukem 470: errno = ENOMEM;
1.47 jnemeth 471: return -2;
1.1 cgd 472: }
1.33 itojun 473: record = newrecord;
1.1 cgd 474: r_end = record + newsize;
475: rp = record + pos;
476: }
477: }
1.20 mycroft 478: /* Eliminate any white space after the last colon. */
479: if (cp)
480: rp = cp + 1;
481: /* Loop invariant lets us do this. */
1.1 cgd 482: *rp++ = '\0';
483:
484: /*
485: * If encountered eof check next file.
486: */
487: if (eof)
488: break;
489:
490: /*
491: * Toss blank lines and comments.
492: */
493: if (*record == '\0' || *record == '#')
494: continue;
495:
496: /*
497: * See if this is the record we want ...
498: */
1.47 jnemeth 499: if (cgetmatch(record, name) == 0)
1.1 cgd 500: if (nfield == NULL || !nfcmp(nfield, record)) {
501: foundit = 1;
502: break; /* found it! */
503: }
504: }
1.47 jnemeth 505: }
1.1 cgd 506: if (foundit)
507: break;
508: }
509:
510: if (!foundit)
1.47 jnemeth 511: return -1;
1.1 cgd 512:
513: /*
514: * Got the capability record, but now we have to expand all tc=name
515: * references in it ...
516: */
1.48 christos 517: tc_exp:
518: tc_not_resolved = 0;
519: if (expandtc) {
1.16 perry 520: char *newicap, *s;
1.17 perry 521: size_t ilen, newilen;
1.51 ! christos 522: int iret;
! 523: ptrdiff_t diff, tclen;
1.1 cgd 524: char *icap, *scan, *tc, *tcstart, *tcend;
525:
526: /*
527: * Loop invariants:
528: * There is room for one more character in record.
529: * R_end points just past end of record.
530: * Rp points just past last character in record.
531: * Scan points at remainder of record that needs to be
532: * scanned for tc=name constructs.
533: */
534: scan = record;
535: for (;;) {
536: if ((tc = cgetcap(scan, "tc", '=')) == NULL)
537: break;
538:
539: /*
540: * Find end of tc=name and stomp on the trailing `:'
541: * (if present) so we can use it to call ourselves.
542: */
543: s = tc;
544: for (;;)
545: if (*s == '\0')
546: break;
547: else
548: if (*s++ == ':') {
549: *(s - 1) = '\0';
550: break;
551: }
552: tcstart = tc - 3;
553: tclen = s - tcstart;
554: tcend = s;
555:
556: iret = getent(&icap, &ilen, db_p, fd, tc, depth+1,
557: NULL);
558: newicap = icap; /* Put into a register. */
559: newilen = ilen;
560: if (iret != 0) {
561: /* an error */
562: if (iret < -1) {
563: if (myfd)
564: (void)close(fd);
565: free(record);
1.47 jnemeth 566: return iret;
1.1 cgd 567: }
568: if (iret == 1)
569: tc_not_resolved = 1;
570: /* couldn't resolve tc */
571: if (iret == -1) {
572: *(s - 1) = ':';
573: scan = s - 1;
574: tc_not_resolved = 1;
575: continue;
576:
577: }
578: }
579: /* not interested in name field of tc'ed record */
580: s = newicap;
581: for (;;)
582: if (*s == '\0')
583: break;
1.47 jnemeth 584: else if (*s++ == ':')
585: break;
1.1 cgd 586: newilen -= s - newicap;
587: newicap = s;
588:
589: /* make sure interpolated record is `:'-terminated */
590: s += newilen;
1.47 jnemeth 591: if (*(s - 1) != ':') {
1.1 cgd 592: *s = ':'; /* overwrite NUL with : */
593: newilen++;
594: }
595:
596: /*
597: * Make sure there's enough room to insert the
598: * new record.
599: */
600: diff = newilen - tclen;
601: if (diff >= r_end - rp) {
1.51 ! christos 602: ptrdiff_t pos, tcpos, tcposend;
1.1 cgd 603: size_t newsize;
604:
605: pos = rp - record;
606: newsize = r_end - record + diff + BFRAG;
607: tcpos = tcstart - record;
608: tcposend = tcend - record;
1.33 itojun 609: newrecord = realloc(record, newsize);
610: if (newrecord == NULL) {
611: free(record);
1.1 cgd 612: if (myfd)
613: (void)close(fd);
614: free(icap);
1.30 lukem 615: errno = ENOMEM;
1.47 jnemeth 616: return -2;
1.1 cgd 617: }
1.33 itojun 618: record = newrecord;
1.1 cgd 619: r_end = record + newsize;
620: rp = record + pos;
621: tcstart = record + tcpos;
622: tcend = record + tcposend;
623: }
624:
625: /*
626: * Insert tc'ed record into our record.
627: */
628: s = tcstart + newilen;
1.23 perry 629: memmove(s, tcend, (size_t)(rp - tcend));
630: memmove(tcstart, newicap, newilen);
1.1 cgd 631: rp += diff;
632: free(icap);
633:
634: /*
635: * Start scan on `:' so next cgetcap works properly
636: * (cgetcap always skips first field).
637: */
1.47 jnemeth 638: scan = s - 1;
1.1 cgd 639: }
640:
641: }
642: /*
643: * Close file (if we opened it), give back any extra memory, and
644: * return capability, length and success.
645: */
646: if (myfd)
647: (void)close(fd);
648: *len = rp - record - 1; /* don't count NUL */
1.33 itojun 649: if (r_end > rp) {
650: if ((newrecord =
1.1 cgd 651: realloc(record, (size_t)(rp - record))) == NULL) {
1.33 itojun 652: free(record);
1.1 cgd 653: errno = ENOMEM;
1.47 jnemeth 654: return -2;
1.1 cgd 655: }
1.33 itojun 656: record = newrecord;
657: }
1.1 cgd 658:
659: *cap = record;
660: if (tc_not_resolved)
1.47 jnemeth 661: return 1;
662: return 0;
1.1 cgd 663: }
664:
1.41 christos 665: #ifndef SMALL
1.1 cgd 666: static int
1.40 christos 667: cdbget(DB *capdbp, char **bp, const char *name)
1.1 cgd 668: {
1.25 christos 669: DBT key;
1.24 christos 670: DBT data;
1.1 cgd 671:
1.30 lukem 672: _DIAGASSERT(capdbp != NULL);
673: _DIAGASSERT(bp != NULL);
674: _DIAGASSERT(name != NULL);
675:
1.42 christos 676: key.data = __UNCONST(name);
1.1 cgd 677: key.size = strlen(name);
678:
679: for (;;) {
680: /* Get the reference. */
681: switch(capdbp->get(capdbp, &key, &data, 0)) {
682: case -1:
1.47 jnemeth 683: return -2;
1.1 cgd 684: case 1:
1.47 jnemeth 685: return -1;
1.1 cgd 686: }
687:
688: /* If not an index to another record, leave. */
689: if (((char *)data.data)[0] != SHADOW)
690: break;
691:
692: key.data = (char *)data.data + 1;
693: key.size = data.size - 1;
694: }
695:
696: *bp = (char *)data.data + 1;
1.47 jnemeth 697: return ((char *)(data.data))[0] == TCERR ? 1 : 0;
1.1 cgd 698: }
1.41 christos 699: #endif
1.1 cgd 700:
701: /*
702: * Cgetmatch will return 0 if name is one of the names of the capability
703: * record buf, -1 if not.
704: */
705: int
1.40 christos 706: cgetmatch(const char *buf, const char *name)
1.1 cgd 707: {
1.21 mycroft 708: const char *np, *bp;
1.1 cgd 709:
1.30 lukem 710: _DIAGASSERT(buf != NULL);
711: _DIAGASSERT(name != NULL);
712:
1.1 cgd 713: /*
714: * Start search at beginning of record.
715: */
716: bp = buf;
717: for (;;) {
718: /*
719: * Try to match a record name.
720: */
721: np = name;
722: for (;;)
1.26 christos 723: if (*np == '\0') {
1.1 cgd 724: if (*bp == '|' || *bp == ':' || *bp == '\0')
1.47 jnemeth 725: return 0;
1.1 cgd 726: else
727: break;
1.47 jnemeth 728: } else if (*bp++ != *np++)
729: break;
1.1 cgd 730:
731: /*
732: * Match failed, skip to next name in record.
733: */
1.34 mrg 734: if (bp > buf)
735: bp--; /* a '|' or ':' may have stopped the match */
736: else
1.47 jnemeth 737: return -1;
1.1 cgd 738: for (;;)
739: if (*bp == '\0' || *bp == ':')
1.47 jnemeth 740: return -1; /* match failed totally */
741: else if (*bp++ == '|')
742: break; /* found next name */
1.1 cgd 743: }
744: }
745:
746: int
1.40 christos 747: cgetfirst(char **buf, const char * const *db_array)
1.1 cgd 748: {
1.30 lukem 749:
750: _DIAGASSERT(buf != NULL);
751: _DIAGASSERT(db_array != NULL);
752:
1.1 cgd 753: (void)cgetclose();
1.47 jnemeth 754: return cgetnext(buf, db_array);
1.1 cgd 755: }
756:
757: static FILE *pfp;
758: static int slash;
1.40 christos 759: static const char * const *dbp;
1.1 cgd 760:
761: int
1.40 christos 762: cgetclose(void)
1.1 cgd 763: {
764: if (pfp != NULL) {
765: (void)fclose(pfp);
766: pfp = NULL;
767: }
768: dbp = NULL;
769: gottoprec = 0;
770: slash = 0;
1.47 jnemeth 771: return 0;
1.1 cgd 772: }
773:
774: /*
775: * Cgetnext() gets either the first or next entry in the logical database
776: * specified by db_array. It returns 0 upon completion of the database, 1
777: * upon returning an entry with more remaining, and -1 if an error occurs.
778: */
1.45 christos 779: /* coverity[+alloc : arg-*0] */
1.1 cgd 780: int
1.40 christos 781: cgetnext(char **bp, const char * const *db_array)
1.1 cgd 782: {
1.43 christos 783: size_t len = 0;
1.17 perry 784: int status, done;
1.1 cgd 785: char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE];
1.18 thorpej 786: size_t dummy;
1.1 cgd 787:
1.30 lukem 788: _DIAGASSERT(bp != NULL);
789: _DIAGASSERT(db_array != NULL);
790:
1.1 cgd 791: if (dbp == NULL)
792: dbp = db_array;
793:
1.50 christos 794: if (pfp == NULL && (pfp = fopen(*dbp, "re")) == NULL) {
1.1 cgd 795: (void)cgetclose();
1.47 jnemeth 796: return -1;
1.1 cgd 797: }
1.47 jnemeth 798: for (;;) {
799: if (toprec != NULL && !gottoprec) {
1.1 cgd 800: gottoprec = 1;
801: line = toprec;
802: } else {
1.6 cgd 803: line = fgetln(pfp, &len);
1.44 christos 804: if (line == NULL) {
805: if (pfp == NULL)
806: return -1;
1.1 cgd 807: if (ferror(pfp)) {
808: (void)cgetclose();
1.47 jnemeth 809: return -1;
1.1 cgd 810: } else {
1.19 tv 811: (void)fclose(pfp);
812: pfp = NULL;
1.1 cgd 813: if (*++dbp == NULL) {
814: (void)cgetclose();
1.47 jnemeth 815: return 0;
1.1 cgd 816: } else if ((pfp =
1.50 christos 817: fopen(*dbp, "re")) == NULL) {
1.1 cgd 818: (void)cgetclose();
1.47 jnemeth 819: return -1;
1.1 cgd 820: } else
821: continue;
822: }
1.5 cgd 823: } else
824: line[len - 1] = '\0';
825: if (len == 1) {
1.1 cgd 826: slash = 0;
827: continue;
828: }
1.26 christos 829: if (isspace((unsigned char)*line) ||
1.1 cgd 830: *line == ':' || *line == '#' || slash) {
1.5 cgd 831: if (line[len - 2] == '\\')
1.1 cgd 832: slash = 1;
833: else
834: slash = 0;
835: continue;
836: }
1.5 cgd 837: if (line[len - 2] == '\\')
1.1 cgd 838: slash = 1;
839: else
840: slash = 0;
841: }
842:
843:
844: /*
845: * Line points to a name line.
846: */
1.35 groo 847: if (len > sizeof(nbuf))
848: return -1;
1.1 cgd 849: done = 0;
850: np = nbuf;
851: for (;;) {
852: for (cp = line; *cp != '\0'; cp++) {
853: if (*cp == ':') {
854: *np++ = ':';
855: done = 1;
856: break;
857: }
858: if (*cp == '\\')
859: break;
860: *np++ = *cp;
861: }
862: if (done) {
863: *np = '\0';
864: break;
865: } else { /* name field extends beyond the line */
1.6 cgd 866: line = fgetln(pfp, &len);
1.1 cgd 867: if (line == NULL && pfp) {
868: if (ferror(pfp)) {
869: (void)cgetclose();
1.47 jnemeth 870: return -1;
1.1 cgd 871: }
1.19 tv 872: (void)fclose(pfp);
873: pfp = NULL;
874: *np = '\0';
875: break;
1.5 cgd 876: } else
877: line[len - 1] = '\0';
1.1 cgd 878: }
879: }
1.35 groo 880: if (len > sizeof(buf))
881: return -1;
1.1 cgd 882: rp = buf;
1.47 jnemeth 883: for (cp = nbuf; *cp != '\0'; cp++)
1.1 cgd 884: if (*cp == '|' || *cp == ':')
885: break;
886: else
887: *rp++ = *cp;
888:
889: *rp = '\0';
890: /*
891: * XXX
892: * Last argument of getent here should be nbuf if we want true
893: * sequential access in the case of duplicates.
894: * With NULL, getent will return the first entry found
895: * rather than the duplicate entry record. This is a
896: * matter of semantics that should be resolved.
897: */
898: status = getent(bp, &dummy, db_array, -1, buf, 0, NULL);
899: if (status == -2 || status == -3)
900: (void)cgetclose();
901:
1.47 jnemeth 902: return status + 1;
1.1 cgd 903: }
904: /* NOTREACHED */
905: }
906:
907: /*
908: * Cgetstr retrieves the value of the string capability cap from the
909: * capability record pointed to by buf. A pointer to a decoded, NUL
910: * terminated, malloc'd copy of the string is returned in the char *
911: * pointed to by str. The length of the string not including the trailing
912: * NUL is returned on success, -1 if the requested string capability
913: * couldn't be found, -2 if a system error was encountered (storage
914: * allocation failure).
915: */
916: int
1.40 christos 917: cgetstr(char *buf, const char *cap, char **str)
1.1 cgd 918: {
1.16 perry 919: u_int m_room;
1.21 mycroft 920: const char *bp;
921: char *mp;
1.51 ! christos 922: ptrdiff_t len;
1.33 itojun 923: char *mem, *newmem;
1.1 cgd 924:
1.30 lukem 925: _DIAGASSERT(buf != NULL);
926: _DIAGASSERT(cap != NULL);
927: _DIAGASSERT(str != NULL);
928:
1.1 cgd 929: /*
930: * Find string capability cap
931: */
932: bp = cgetcap(buf, cap, '=');
933: if (bp == NULL)
1.47 jnemeth 934: return -1;
1.1 cgd 935:
936: /*
937: * Conversion / storage allocation loop ... Allocate memory in
938: * chunks SFRAG in size.
939: */
940: if ((mem = malloc(SFRAG)) == NULL) {
941: errno = ENOMEM;
1.47 jnemeth 942: return -2; /* couldn't even allocate the first fragment */
1.1 cgd 943: }
944: m_room = SFRAG;
945: mp = mem;
946:
947: while (*bp != ':' && *bp != '\0') {
948: /*
949: * Loop invariants:
950: * There is always room for one more character in mem.
951: * Mp always points just past last character in mem.
952: * Bp always points at next character in buf.
953: */
954: if (*bp == '^') {
955: bp++;
956: if (*bp == ':' || *bp == '\0')
957: break; /* drop unfinished escape */
958: *mp++ = *bp++ & 037;
959: } else if (*bp == '\\') {
960: bp++;
961: if (*bp == ':' || *bp == '\0')
962: break; /* drop unfinished escape */
963: if ('0' <= *bp && *bp <= '7') {
1.16 perry 964: int n, i;
1.1 cgd 965:
966: n = 0;
967: i = 3; /* maximum of three octal digits */
968: do {
969: n = n * 8 + (*bp++ - '0');
970: } while (--i && '0' <= *bp && *bp <= '7');
971: *mp++ = n;
972: }
973: else switch (*bp++) {
974: case 'b': case 'B':
975: *mp++ = '\b';
976: break;
977: case 't': case 'T':
978: *mp++ = '\t';
979: break;
980: case 'n': case 'N':
981: *mp++ = '\n';
982: break;
983: case 'f': case 'F':
984: *mp++ = '\f';
985: break;
986: case 'r': case 'R':
987: *mp++ = '\r';
988: break;
989: case 'e': case 'E':
990: *mp++ = ESC;
991: break;
992: case 'c': case 'C':
993: *mp++ = ':';
994: break;
995: default:
996: /*
997: * Catches '\', '^', and
998: * everything else.
999: */
1000: *mp++ = *(bp-1);
1001: break;
1002: }
1003: } else
1004: *mp++ = *bp++;
1005: m_room--;
1006:
1007: /*
1008: * Enforce loop invariant: if no room left in current
1009: * buffer, try to get some more.
1010: */
1011: if (m_room == 0) {
1012: size_t size = mp - mem;
1013:
1.33 itojun 1014: if ((newmem = realloc(mem, size + SFRAG)) == NULL) {
1015: free(mem);
1.47 jnemeth 1016: return -2;
1.33 itojun 1017: }
1018: mem = newmem;
1.1 cgd 1019: m_room = SFRAG;
1020: mp = mem + size;
1021: }
1022: }
1023: *mp++ = '\0'; /* loop invariant let's us do this */
1024: m_room--;
1025: len = mp - mem - 1;
1026:
1027: /*
1028: * Give back any extra memory and return value and success.
1029: */
1.33 itojun 1030: if (m_room != 0) {
1031: if ((newmem = realloc(mem, (size_t)(mp - mem))) == NULL) {
1032: free(mem);
1.47 jnemeth 1033: return -2;
1.33 itojun 1034: }
1035: mem = newmem;
1036: }
1.1 cgd 1037: *str = mem;
1.51 ! christos 1038: _DIAGASSERT(__type_fit(int, len));
! 1039: return (int)len;
1.1 cgd 1040: }
1041:
1042: /*
1043: * Cgetustr retrieves the value of the string capability cap from the
1044: * capability record pointed to by buf. The difference between cgetustr()
1045: * and cgetstr() is that cgetustr does not decode escapes but rather treats
1046: * all characters literally. A pointer to a NUL terminated malloc'd
1047: * copy of the string is returned in the char pointed to by str. The
1048: * length of the string not including the trailing NUL is returned on success,
1049: * -1 if the requested string capability couldn't be found, -2 if a system
1050: * error was encountered (storage allocation failure).
1051: */
1052: int
1.40 christos 1053: cgetustr(char *buf, const char *cap, char **str)
1.1 cgd 1054: {
1.16 perry 1055: u_int m_room;
1.21 mycroft 1056: const char *bp;
1057: char *mp;
1.51 ! christos 1058: size_t len;
1.33 itojun 1059: char *mem, *newmem;
1.1 cgd 1060:
1.30 lukem 1061: _DIAGASSERT(buf != NULL);
1062: _DIAGASSERT(cap != NULL);
1063: _DIAGASSERT(str != NULL);
1064:
1.1 cgd 1065: /*
1066: * Find string capability cap
1067: */
1068: if ((bp = cgetcap(buf, cap, '=')) == NULL)
1.47 jnemeth 1069: return -1;
1.1 cgd 1070:
1071: /*
1072: * Conversion / storage allocation loop ... Allocate memory in
1073: * chunks SFRAG in size.
1074: */
1075: if ((mem = malloc(SFRAG)) == NULL) {
1076: errno = ENOMEM;
1.47 jnemeth 1077: return -2; /* couldn't even allocate the first fragment */
1.1 cgd 1078: }
1079: m_room = SFRAG;
1080: mp = mem;
1081:
1082: while (*bp != ':' && *bp != '\0') {
1083: /*
1084: * Loop invariants:
1085: * There is always room for one more character in mem.
1086: * Mp always points just past last character in mem.
1087: * Bp always points at next character in buf.
1088: */
1089: *mp++ = *bp++;
1090: m_room--;
1091:
1092: /*
1093: * Enforce loop invariant: if no room left in current
1094: * buffer, try to get some more.
1095: */
1096: if (m_room == 0) {
1097: size_t size = mp - mem;
1098:
1.33 itojun 1099: if ((newmem = realloc(mem, size + SFRAG)) == NULL) {
1100: free(mem);
1.47 jnemeth 1101: return -2;
1.33 itojun 1102: }
1103: mem = newmem;
1.1 cgd 1104: m_room = SFRAG;
1105: mp = mem + size;
1106: }
1107: }
1108: *mp++ = '\0'; /* loop invariant let's us do this */
1109: m_room--;
1110: len = mp - mem - 1;
1111:
1112: /*
1113: * Give back any extra memory and return value and success.
1114: */
1.33 itojun 1115: if (m_room != 0) {
1116: if ((newmem = realloc(mem, (size_t)(mp - mem))) == NULL) {
1117: free(mem);
1.47 jnemeth 1118: return -2;
1.33 itojun 1119: }
1120: mem = newmem;
1121: }
1.1 cgd 1122: *str = mem;
1.51 ! christos 1123: _DIAGASSERT(__type_fit(int, len));
! 1124: return (int)len;
1.1 cgd 1125: }
1126:
1127: /*
1128: * Cgetnum retrieves the value of the numeric capability cap from the
1129: * capability record pointed to by buf. The numeric value is returned in
1130: * the long pointed to by num. 0 is returned on success, -1 if the requested
1131: * numeric capability couldn't be found.
1132: */
1133: int
1.40 christos 1134: cgetnum(char *buf, const char *cap, long *num)
1.1 cgd 1135: {
1.16 perry 1136: long n;
1137: int base, digit;
1.21 mycroft 1138: const char *bp;
1.1 cgd 1139:
1.30 lukem 1140: _DIAGASSERT(buf != NULL);
1141: _DIAGASSERT(cap != NULL);
1142: _DIAGASSERT(num != NULL);
1143:
1.1 cgd 1144: /*
1145: * Find numeric capability cap
1146: */
1147: bp = cgetcap(buf, cap, '#');
1148: if (bp == NULL)
1.47 jnemeth 1149: return -1;
1.1 cgd 1150:
1151: /*
1152: * Look at value and determine numeric base:
1153: * 0x... or 0X... hexadecimal,
1154: * else 0... octal,
1155: * else decimal.
1156: */
1157: if (*bp == '0') {
1158: bp++;
1159: if (*bp == 'x' || *bp == 'X') {
1160: bp++;
1161: base = 16;
1162: } else
1163: base = 8;
1164: } else
1165: base = 10;
1166:
1167: /*
1168: * Conversion loop ...
1169: */
1170: n = 0;
1171: for (;;) {
1172: if ('0' <= *bp && *bp <= '9')
1173: digit = *bp - '0';
1174: else if ('a' <= *bp && *bp <= 'f')
1175: digit = 10 + *bp - 'a';
1176: else if ('A' <= *bp && *bp <= 'F')
1177: digit = 10 + *bp - 'A';
1178: else
1179: break;
1180:
1181: if (digit >= base)
1182: break;
1183:
1184: n = n * base + digit;
1185: bp++;
1186: }
1187:
1188: /*
1189: * Return value and success.
1190: */
1191: *num = n;
1.47 jnemeth 1192: return 0;
1.1 cgd 1193: }
1194:
1195:
1196: /*
1197: * Compare name field of record.
1198: */
1199: static int
1.40 christos 1200: nfcmp(char *nf, char *rec)
1.1 cgd 1201: {
1202: char *cp, tmp;
1203: int ret;
1.30 lukem 1204:
1205: _DIAGASSERT(nf != NULL);
1206: _DIAGASSERT(rec != NULL);
1207:
1.1 cgd 1208: for (cp = rec; *cp != ':'; cp++)
1.47 jnemeth 1209: continue;
1.1 cgd 1210:
1211: tmp = *(cp + 1);
1212: *(cp + 1) = '\0';
1213: ret = strcmp(nf, rec);
1214: *(cp + 1) = tmp;
1215:
1.47 jnemeth 1216: return ret;
1.1 cgd 1217: }
CVSweb <webmaster@jp.NetBSD.org>