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