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