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