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