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