|
|
1.16 ! perry 1: /* $NetBSD: getcap.c,v 1.15 1997/08/25 19:31:45 kleink 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.16 ! perry 44: __RCSID("$NetBSD: getcap.c,v 1.15 1997/08/25 19:31:45 kleink 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 *));
88: static int getent __P((char **, u_int *, char **, int, char *, int, char *));
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: {
185: u_int dummy;
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;
211: u_int *len;
212: int fd, depth;
213: {
214: DB *capdbp;
1.16 ! perry 215: char *r_end, *rp = NULL, **db_p; /* pacify gcc */
1.13 christos 216: int myfd = 0, eof, foundit, retval, clen;
1.7 cgd 217: char *record, *cbuf;
1.1 cgd 218: int tc_not_resolved;
219: char pbuf[_POSIX_PATH_MAX];
220:
221: /*
222: * Return with ``loop detected'' error if we've recursed more than
223: * MAX_RECURSION times.
224: */
225: if (depth > MAX_RECURSION)
226: return (-3);
227:
228: /*
229: * Check if we have a top record from cgetset().
230: */
231: if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) {
232: if ((record = malloc (topreclen + BFRAG)) == NULL) {
233: errno = ENOMEM;
234: return (-2);
235: }
1.11 mrg 236: (void)strcpy(record, toprec); /* XXX: strcpy is safe */
1.1 cgd 237: db_p = db_array;
238: rp = record + topreclen + 1;
239: r_end = rp + BFRAG;
240: goto tc_exp;
241: }
242: /*
243: * Allocate first chunk of memory.
244: */
245: if ((record = malloc(BFRAG)) == NULL) {
246: errno = ENOMEM;
247: return (-2);
248: }
249: r_end = record + BFRAG;
250: foundit = 0;
251: /*
252: * Loop through database array until finding the record.
253: */
254:
255: for (db_p = db_array; *db_p != NULL; db_p++) {
256: eof = 0;
257:
258: /*
259: * Open database if not already open.
260: */
261:
262: if (fd >= 0) {
1.15 kleink 263: (void)lseek(fd, (off_t)0, SEEK_SET);
1.1 cgd 264: } else {
265: (void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p);
266: if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0))
267: != NULL) {
268: free(record);
269: retval = cdbget(capdbp, &record, name);
1.8 cgd 270: if (retval < 0) {
271: /* no record available */
272: (void)capdbp->close(capdbp);
1.9 cgd 273: return (retval);
1.8 cgd 274: }
275: /* save the data; close frees it */
1.7 cgd 276: clen = strlen(record);
1.8 cgd 277: cbuf = malloc(clen + 1);
278: memcpy(cbuf, record, clen + 1);
1.7 cgd 279: if (capdbp->close(capdbp) < 0) {
280: free(cbuf);
281: return (-2);
282: }
283: *len = clen;
284: *cap = cbuf;
1.1 cgd 285: return (retval);
286: } else {
287: fd = open(*db_p, O_RDONLY, 0);
288: if (fd < 0) {
289: /* No error on unfound file. */
1.10 mycroft 290: continue;
1.1 cgd 291: }
292: myfd = 1;
293: }
294: }
295: /*
296: * Find the requested capability record ...
297: */
298: {
299: char buf[BUFSIZ];
1.16 ! perry 300: char *b_end, *bp;
! 301: int c;
1.1 cgd 302:
303: /*
304: * Loop invariants:
305: * There is always room for one more character in record.
306: * R_end always points just past end of record.
307: * Rp always points just past last character in record.
308: * B_end always points just past last character in buf.
309: * Bp always points at next character in buf.
310: */
311: b_end = buf;
312: bp = buf;
313: for (;;) {
314:
315: /*
316: * Read in a line implementing (\, newline)
317: * line continuation.
318: */
319: rp = record;
320: for (;;) {
321: if (bp >= b_end) {
322: int n;
323:
324: n = read(fd, buf, sizeof(buf));
325: if (n <= 0) {
326: if (myfd)
327: (void)close(fd);
328: if (n < 0) {
329: free(record);
330: return (-2);
331: } else {
332: fd = -1;
333: eof = 1;
334: break;
335: }
336: }
337: b_end = buf+n;
338: bp = buf;
339: }
340:
341: c = *bp++;
342: if (c == '\n') {
343: if (rp > record && *(rp-1) == '\\') {
344: rp--;
345: continue;
346: } else
347: break;
348: }
349: *rp++ = c;
350:
351: /*
352: * Enforce loop invariant: if no room
353: * left in record buffer, try to get
354: * some more.
355: */
356: if (rp >= r_end) {
357: u_int pos;
358: size_t newsize;
359:
360: pos = rp - record;
361: newsize = r_end - record + BFRAG;
362: record = realloc(record, newsize);
363: if (record == NULL) {
364: errno = ENOMEM;
365: if (myfd)
366: (void)close(fd);
367: return (-2);
368: }
369: r_end = record + newsize;
370: rp = record + pos;
371: }
372: }
373: /* loop invariant let's us do this */
374: *rp++ = '\0';
375:
376: /*
377: * If encountered eof check next file.
378: */
379: if (eof)
380: break;
381:
382: /*
383: * Toss blank lines and comments.
384: */
385: if (*record == '\0' || *record == '#')
386: continue;
387:
388: /*
389: * See if this is the record we want ...
390: */
391: if (cgetmatch(record, name) == 0) {
392: if (nfield == NULL || !nfcmp(nfield, record)) {
393: foundit = 1;
394: break; /* found it! */
395: }
396: }
397: }
398: }
399: if (foundit)
400: break;
401: }
402:
403: if (!foundit)
404: return (-1);
405:
406: /*
407: * Got the capability record, but now we have to expand all tc=name
408: * references in it ...
409: */
410: tc_exp: {
1.16 ! perry 411: char *newicap, *s;
! 412: int newilen;
1.1 cgd 413: u_int ilen;
414: int diff, iret, tclen;
415: char *icap, *scan, *tc, *tcstart, *tcend;
416:
417: /*
418: * Loop invariants:
419: * There is room for one more character in record.
420: * R_end points just past end of record.
421: * Rp points just past last character in record.
422: * Scan points at remainder of record that needs to be
423: * scanned for tc=name constructs.
424: */
425: scan = record;
426: tc_not_resolved = 0;
427: for (;;) {
428: if ((tc = cgetcap(scan, "tc", '=')) == NULL)
429: break;
430:
431: /*
432: * Find end of tc=name and stomp on the trailing `:'
433: * (if present) so we can use it to call ourselves.
434: */
435: s = tc;
436: for (;;)
437: if (*s == '\0')
438: break;
439: else
440: if (*s++ == ':') {
441: *(s - 1) = '\0';
442: break;
443: }
444: tcstart = tc - 3;
445: tclen = s - tcstart;
446: tcend = s;
447:
448: iret = getent(&icap, &ilen, db_p, fd, tc, depth+1,
449: NULL);
450: newicap = icap; /* Put into a register. */
451: newilen = ilen;
452: if (iret != 0) {
453: /* an error */
454: if (iret < -1) {
455: if (myfd)
456: (void)close(fd);
457: free(record);
458: return (iret);
459: }
460: if (iret == 1)
461: tc_not_resolved = 1;
462: /* couldn't resolve tc */
463: if (iret == -1) {
464: *(s - 1) = ':';
465: scan = s - 1;
466: tc_not_resolved = 1;
467: continue;
468:
469: }
470: }
471: /* not interested in name field of tc'ed record */
472: s = newicap;
473: for (;;)
474: if (*s == '\0')
475: break;
476: else
477: if (*s++ == ':')
478: break;
479: newilen -= s - newicap;
480: newicap = s;
481:
482: /* make sure interpolated record is `:'-terminated */
483: s += newilen;
484: if (*(s-1) != ':') {
485: *s = ':'; /* overwrite NUL with : */
486: newilen++;
487: }
488:
489: /*
490: * Make sure there's enough room to insert the
491: * new record.
492: */
493: diff = newilen - tclen;
494: if (diff >= r_end - rp) {
495: u_int pos, tcpos, tcposend;
496: size_t newsize;
497:
498: pos = rp - record;
499: newsize = r_end - record + diff + BFRAG;
500: tcpos = tcstart - record;
501: tcposend = tcend - record;
502: record = realloc(record, newsize);
503: if (record == NULL) {
504: errno = ENOMEM;
505: if (myfd)
506: (void)close(fd);
507: free(icap);
508: return (-2);
509: }
510: r_end = record + newsize;
511: rp = record + pos;
512: tcstart = record + tcpos;
513: tcend = record + tcposend;
514: }
515:
516: /*
517: * Insert tc'ed record into our record.
518: */
519: s = tcstart + newilen;
520: bcopy(tcend, s, rp - tcend);
521: bcopy(newicap, tcstart, newilen);
522: rp += diff;
523: free(icap);
524:
525: /*
526: * Start scan on `:' so next cgetcap works properly
527: * (cgetcap always skips first field).
528: */
529: scan = s-1;
530: }
531:
532: }
533: /*
534: * Close file (if we opened it), give back any extra memory, and
535: * return capability, length and success.
536: */
537: if (myfd)
538: (void)close(fd);
539: *len = rp - record - 1; /* don't count NUL */
540: if (r_end > rp)
541: if ((record =
542: realloc(record, (size_t)(rp - record))) == NULL) {
543: errno = ENOMEM;
544: return (-2);
545: }
546:
547: *cap = record;
548: if (tc_not_resolved)
549: return (1);
550: return (0);
551: }
552:
553: static int
554: cdbget(capdbp, bp, name)
555: DB *capdbp;
556: char **bp, *name;
557: {
558: DBT key, data;
559:
560: key.data = name;
561: key.size = strlen(name);
562:
563: for (;;) {
564: /* Get the reference. */
565: switch(capdbp->get(capdbp, &key, &data, 0)) {
566: case -1:
567: return (-2);
568: case 1:
569: return (-1);
570: }
571:
572: /* If not an index to another record, leave. */
573: if (((char *)data.data)[0] != SHADOW)
574: break;
575:
576: key.data = (char *)data.data + 1;
577: key.size = data.size - 1;
578: }
579:
580: *bp = (char *)data.data + 1;
581: return (((char *)(data.data))[0] == TCERR ? 1 : 0);
582: }
583:
584: /*
585: * Cgetmatch will return 0 if name is one of the names of the capability
586: * record buf, -1 if not.
587: */
588: int
589: cgetmatch(buf, name)
590: char *buf, *name;
591: {
1.16 ! perry 592: char *np, *bp;
1.1 cgd 593:
594: /*
595: * Start search at beginning of record.
596: */
597: bp = buf;
598: for (;;) {
599: /*
600: * Try to match a record name.
601: */
602: np = name;
603: for (;;)
604: if (*np == '\0')
605: if (*bp == '|' || *bp == ':' || *bp == '\0')
606: return (0);
607: else
608: break;
609: else
610: if (*bp++ != *np++)
611: break;
612:
613: /*
614: * Match failed, skip to next name in record.
615: */
616: bp--; /* a '|' or ':' may have stopped the match */
617: for (;;)
618: if (*bp == '\0' || *bp == ':')
619: return (-1); /* match failed totally */
620: else
621: if (*bp++ == '|')
622: break; /* found next name */
623: }
624: }
625:
626:
627:
628:
629:
630: int
631: cgetfirst(buf, db_array)
632: char **buf, **db_array;
633: {
634: (void)cgetclose();
635: return (cgetnext(buf, db_array));
636: }
637:
638: static FILE *pfp;
639: static int slash;
640: static char **dbp;
641:
642: int
643: cgetclose()
644: {
645: if (pfp != NULL) {
646: (void)fclose(pfp);
647: pfp = NULL;
648: }
649: dbp = NULL;
650: gottoprec = 0;
651: slash = 0;
652: return(0);
653: }
654:
655: /*
656: * Cgetnext() gets either the first or next entry in the logical database
657: * specified by db_array. It returns 0 upon completion of the database, 1
658: * upon returning an entry with more remaining, and -1 if an error occurs.
659: */
660: int
661: cgetnext(bp, db_array)
1.16 ! perry 662: char **bp;
1.1 cgd 663: char **db_array;
664: {
665: size_t len;
666: int status, i, done;
667: char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE];
668: u_int dummy;
669:
670: if (dbp == NULL)
671: dbp = db_array;
672:
673: if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) {
674: (void)cgetclose();
675: return (-1);
676: }
677: for(;;) {
678: if (toprec && !gottoprec) {
679: gottoprec = 1;
680: line = toprec;
681: } else {
1.6 cgd 682: line = fgetln(pfp, &len);
1.1 cgd 683: if (line == NULL && pfp) {
684: (void)fclose(pfp);
685: if (ferror(pfp)) {
686: (void)cgetclose();
687: return (-1);
688: } else {
689: if (*++dbp == NULL) {
690: (void)cgetclose();
691: return (0);
692: } else if ((pfp =
693: fopen(*dbp, "r")) == NULL) {
694: (void)cgetclose();
695: return (-1);
696: } else
697: continue;
698: }
1.5 cgd 699: } else
700: line[len - 1] = '\0';
701: if (len == 1) {
1.1 cgd 702: slash = 0;
703: continue;
704: }
705: if (isspace(*line) ||
706: *line == ':' || *line == '#' || slash) {
1.5 cgd 707: if (line[len - 2] == '\\')
1.1 cgd 708: slash = 1;
709: else
710: slash = 0;
711: continue;
712: }
1.5 cgd 713: if (line[len - 2] == '\\')
1.1 cgd 714: slash = 1;
715: else
716: slash = 0;
717: }
718:
719:
720: /*
721: * Line points to a name line.
722: */
723: i = 0;
724: done = 0;
725: np = nbuf;
726: for (;;) {
727: for (cp = line; *cp != '\0'; cp++) {
728: if (*cp == ':') {
729: *np++ = ':';
730: done = 1;
731: break;
732: }
733: if (*cp == '\\')
734: break;
735: *np++ = *cp;
736: }
737: if (done) {
738: *np = '\0';
739: break;
740: } else { /* name field extends beyond the line */
1.6 cgd 741: line = fgetln(pfp, &len);
1.1 cgd 742: if (line == NULL && pfp) {
743: (void)fclose(pfp);
744: if (ferror(pfp)) {
745: (void)cgetclose();
746: return (-1);
747: }
1.5 cgd 748: } else
749: line[len - 1] = '\0';
1.1 cgd 750: }
751: }
752: rp = buf;
1.12 pk 753: for(cp = nbuf; *cp != '\0'; cp++)
1.1 cgd 754: if (*cp == '|' || *cp == ':')
755: break;
756: else
757: *rp++ = *cp;
758:
759: *rp = '\0';
760: /*
761: * XXX
762: * Last argument of getent here should be nbuf if we want true
763: * sequential access in the case of duplicates.
764: * With NULL, getent will return the first entry found
765: * rather than the duplicate entry record. This is a
766: * matter of semantics that should be resolved.
767: */
768: status = getent(bp, &dummy, db_array, -1, buf, 0, NULL);
769: if (status == -2 || status == -3)
770: (void)cgetclose();
771:
772: return (status + 1);
773: }
774: /* NOTREACHED */
775: }
776:
777: /*
778: * Cgetstr retrieves the value of the string capability cap from the
779: * capability record pointed to by buf. A pointer to a decoded, NUL
780: * terminated, malloc'd copy of the string is returned in the char *
781: * pointed to by str. The length of the string not including the trailing
782: * NUL is returned on success, -1 if the requested string capability
783: * couldn't be found, -2 if a system error was encountered (storage
784: * allocation failure).
785: */
786: int
787: cgetstr(buf, cap, str)
788: char *buf, *cap;
789: char **str;
790: {
1.16 ! perry 791: u_int m_room;
! 792: char *bp, *mp;
1.1 cgd 793: int len;
794: char *mem;
795:
796: /*
797: * Find string capability cap
798: */
799: bp = cgetcap(buf, cap, '=');
800: if (bp == NULL)
801: return (-1);
802:
803: /*
804: * Conversion / storage allocation loop ... Allocate memory in
805: * chunks SFRAG in size.
806: */
807: if ((mem = malloc(SFRAG)) == NULL) {
808: errno = ENOMEM;
809: return (-2); /* couldn't even allocate the first fragment */
810: }
811: m_room = SFRAG;
812: mp = mem;
813:
814: while (*bp != ':' && *bp != '\0') {
815: /*
816: * Loop invariants:
817: * There is always room for one more character in mem.
818: * Mp always points just past last character in mem.
819: * Bp always points at next character in buf.
820: */
821: if (*bp == '^') {
822: bp++;
823: if (*bp == ':' || *bp == '\0')
824: break; /* drop unfinished escape */
825: *mp++ = *bp++ & 037;
826: } else if (*bp == '\\') {
827: bp++;
828: if (*bp == ':' || *bp == '\0')
829: break; /* drop unfinished escape */
830: if ('0' <= *bp && *bp <= '7') {
1.16 ! perry 831: int n, i;
1.1 cgd 832:
833: n = 0;
834: i = 3; /* maximum of three octal digits */
835: do {
836: n = n * 8 + (*bp++ - '0');
837: } while (--i && '0' <= *bp && *bp <= '7');
838: *mp++ = n;
839: }
840: else switch (*bp++) {
841: case 'b': case 'B':
842: *mp++ = '\b';
843: break;
844: case 't': case 'T':
845: *mp++ = '\t';
846: break;
847: case 'n': case 'N':
848: *mp++ = '\n';
849: break;
850: case 'f': case 'F':
851: *mp++ = '\f';
852: break;
853: case 'r': case 'R':
854: *mp++ = '\r';
855: break;
856: case 'e': case 'E':
857: *mp++ = ESC;
858: break;
859: case 'c': case 'C':
860: *mp++ = ':';
861: break;
862: default:
863: /*
864: * Catches '\', '^', and
865: * everything else.
866: */
867: *mp++ = *(bp-1);
868: break;
869: }
870: } else
871: *mp++ = *bp++;
872: m_room--;
873:
874: /*
875: * Enforce loop invariant: if no room left in current
876: * buffer, try to get some more.
877: */
878: if (m_room == 0) {
879: size_t size = mp - mem;
880:
881: if ((mem = realloc(mem, size + SFRAG)) == NULL)
882: return (-2);
883: m_room = SFRAG;
884: mp = mem + size;
885: }
886: }
887: *mp++ = '\0'; /* loop invariant let's us do this */
888: m_room--;
889: len = mp - mem - 1;
890:
891: /*
892: * Give back any extra memory and return value and success.
893: */
894: if (m_room != 0)
895: if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL)
896: return (-2);
897: *str = mem;
898: return (len);
899: }
900:
901: /*
902: * Cgetustr retrieves the value of the string capability cap from the
903: * capability record pointed to by buf. The difference between cgetustr()
904: * and cgetstr() is that cgetustr does not decode escapes but rather treats
905: * all characters literally. A pointer to a NUL terminated malloc'd
906: * copy of the string is returned in the char pointed to by str. The
907: * length of the string not including the trailing NUL is returned on success,
908: * -1 if the requested string capability couldn't be found, -2 if a system
909: * error was encountered (storage allocation failure).
910: */
911: int
912: cgetustr(buf, cap, str)
913: char *buf, *cap, **str;
914: {
1.16 ! perry 915: u_int m_room;
! 916: char *bp, *mp;
1.1 cgd 917: int len;
918: char *mem;
919:
920: /*
921: * Find string capability cap
922: */
923: if ((bp = cgetcap(buf, cap, '=')) == NULL)
924: return (-1);
925:
926: /*
927: * Conversion / storage allocation loop ... Allocate memory in
928: * chunks SFRAG in size.
929: */
930: if ((mem = malloc(SFRAG)) == NULL) {
931: errno = ENOMEM;
932: return (-2); /* couldn't even allocate the first fragment */
933: }
934: m_room = SFRAG;
935: mp = mem;
936:
937: while (*bp != ':' && *bp != '\0') {
938: /*
939: * Loop invariants:
940: * There is always room for one more character in mem.
941: * Mp always points just past last character in mem.
942: * Bp always points at next character in buf.
943: */
944: *mp++ = *bp++;
945: m_room--;
946:
947: /*
948: * Enforce loop invariant: if no room left in current
949: * buffer, try to get some more.
950: */
951: if (m_room == 0) {
952: size_t size = mp - mem;
953:
954: if ((mem = realloc(mem, size + SFRAG)) == NULL)
955: return (-2);
956: m_room = SFRAG;
957: mp = mem + size;
958: }
959: }
960: *mp++ = '\0'; /* loop invariant let's us do this */
961: m_room--;
962: len = mp - mem - 1;
963:
964: /*
965: * Give back any extra memory and return value and success.
966: */
967: if (m_room != 0)
968: if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL)
969: return (-2);
970: *str = mem;
971: return (len);
972: }
973:
974: /*
975: * Cgetnum retrieves the value of the numeric capability cap from the
976: * capability record pointed to by buf. The numeric value is returned in
977: * the long pointed to by num. 0 is returned on success, -1 if the requested
978: * numeric capability couldn't be found.
979: */
980: int
981: cgetnum(buf, cap, num)
982: char *buf, *cap;
983: long *num;
984: {
1.16 ! perry 985: long n;
! 986: int base, digit;
! 987: char *bp;
1.1 cgd 988:
989: /*
990: * Find numeric capability cap
991: */
992: bp = cgetcap(buf, cap, '#');
993: if (bp == NULL)
994: return (-1);
995:
996: /*
997: * Look at value and determine numeric base:
998: * 0x... or 0X... hexadecimal,
999: * else 0... octal,
1000: * else decimal.
1001: */
1002: if (*bp == '0') {
1003: bp++;
1004: if (*bp == 'x' || *bp == 'X') {
1005: bp++;
1006: base = 16;
1007: } else
1008: base = 8;
1009: } else
1010: base = 10;
1011:
1012: /*
1013: * Conversion loop ...
1014: */
1015: n = 0;
1016: for (;;) {
1017: if ('0' <= *bp && *bp <= '9')
1018: digit = *bp - '0';
1019: else if ('a' <= *bp && *bp <= 'f')
1020: digit = 10 + *bp - 'a';
1021: else if ('A' <= *bp && *bp <= 'F')
1022: digit = 10 + *bp - 'A';
1023: else
1024: break;
1025:
1026: if (digit >= base)
1027: break;
1028:
1029: n = n * base + digit;
1030: bp++;
1031: }
1032:
1033: /*
1034: * Return value and success.
1035: */
1036: *num = n;
1037: return (0);
1038: }
1039:
1040:
1041: /*
1042: * Compare name field of record.
1043: */
1044: static int
1045: nfcmp(nf, rec)
1046: char *nf, *rec;
1047: {
1048: char *cp, tmp;
1049: int ret;
1050:
1051: for (cp = rec; *cp != ':'; cp++)
1052: ;
1053:
1054: tmp = *(cp + 1);
1055: *(cp + 1) = '\0';
1056: ret = strcmp(nf, rec);
1057: *(cp + 1) = tmp;
1058:
1059: return (ret);
1060: }