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