|
|
1.17 ! perry 1: /* $NetBSD: getcap.c,v 1.16 1998/02/03 18:23:43 perry 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.17 ! perry 44: __RCSID("$NetBSD: getcap.c,v 1.16 1998/02/03 18:23:43 perry 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;
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.16 perry 301: char *b_end, *bp;
302: int c;
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.
311: */
312: b_end = buf;
313: bp = buf;
314: for (;;) {
315:
316: /*
317: * Read in a line implementing (\, newline)
318: * line continuation.
319: */
320: rp = record;
321: for (;;) {
322: if (bp >= b_end) {
323: int n;
324:
325: n = read(fd, buf, sizeof(buf));
326: if (n <= 0) {
327: if (myfd)
328: (void)close(fd);
329: if (n < 0) {
330: free(record);
331: return (-2);
332: } else {
333: fd = -1;
334: eof = 1;
335: break;
336: }
337: }
338: b_end = buf+n;
339: bp = buf;
340: }
341:
342: c = *bp++;
343: if (c == '\n') {
344: if (rp > record && *(rp-1) == '\\') {
345: rp--;
346: continue;
347: } else
348: break;
349: }
350: *rp++ = c;
351:
352: /*
353: * Enforce loop invariant: if no room
354: * left in record buffer, try to get
355: * some more.
356: */
357: if (rp >= r_end) {
358: u_int pos;
359: size_t newsize;
360:
361: pos = rp - record;
362: newsize = r_end - record + BFRAG;
363: record = realloc(record, newsize);
364: if (record == NULL) {
365: errno = ENOMEM;
366: if (myfd)
367: (void)close(fd);
368: return (-2);
369: }
370: r_end = record + newsize;
371: rp = record + pos;
372: }
373: }
374: /* loop invariant let's us do this */
375: *rp++ = '\0';
376:
377: /*
378: * If encountered eof check next file.
379: */
380: if (eof)
381: break;
382:
383: /*
384: * Toss blank lines and comments.
385: */
386: if (*record == '\0' || *record == '#')
387: continue;
388:
389: /*
390: * See if this is the record we want ...
391: */
392: if (cgetmatch(record, name) == 0) {
393: if (nfield == NULL || !nfcmp(nfield, record)) {
394: foundit = 1;
395: break; /* found it! */
396: }
397: }
398: }
399: }
400: if (foundit)
401: break;
402: }
403:
404: if (!foundit)
405: return (-1);
406:
407: /*
408: * Got the capability record, but now we have to expand all tc=name
409: * references in it ...
410: */
411: tc_exp: {
1.16 perry 412: char *newicap, *s;
1.17 ! perry 413: size_t ilen, newilen;
1.1 cgd 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;
1.17 ! perry 520: bcopy(tcend, s, (size_t)(rp - tcend));
1.1 cgd 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;
1.17 ! perry 666: int status, done;
1.1 cgd 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: done = 0;
724: np = nbuf;
725: for (;;) {
726: for (cp = line; *cp != '\0'; cp++) {
727: if (*cp == ':') {
728: *np++ = ':';
729: done = 1;
730: break;
731: }
732: if (*cp == '\\')
733: break;
734: *np++ = *cp;
735: }
736: if (done) {
737: *np = '\0';
738: break;
739: } else { /* name field extends beyond the line */
1.6 cgd 740: line = fgetln(pfp, &len);
1.1 cgd 741: if (line == NULL && pfp) {
742: (void)fclose(pfp);
743: if (ferror(pfp)) {
744: (void)cgetclose();
745: return (-1);
746: }
1.5 cgd 747: } else
748: line[len - 1] = '\0';
1.1 cgd 749: }
750: }
751: rp = buf;
1.12 pk 752: for(cp = nbuf; *cp != '\0'; cp++)
1.1 cgd 753: if (*cp == '|' || *cp == ':')
754: break;
755: else
756: *rp++ = *cp;
757:
758: *rp = '\0';
759: /*
760: * XXX
761: * Last argument of getent here should be nbuf if we want true
762: * sequential access in the case of duplicates.
763: * With NULL, getent will return the first entry found
764: * rather than the duplicate entry record. This is a
765: * matter of semantics that should be resolved.
766: */
767: status = getent(bp, &dummy, db_array, -1, buf, 0, NULL);
768: if (status == -2 || status == -3)
769: (void)cgetclose();
770:
771: return (status + 1);
772: }
773: /* NOTREACHED */
774: }
775:
776: /*
777: * Cgetstr retrieves the value of the string capability cap from the
778: * capability record pointed to by buf. A pointer to a decoded, NUL
779: * terminated, malloc'd copy of the string is returned in the char *
780: * pointed to by str. The length of the string not including the trailing
781: * NUL is returned on success, -1 if the requested string capability
782: * couldn't be found, -2 if a system error was encountered (storage
783: * allocation failure).
784: */
785: int
786: cgetstr(buf, cap, str)
787: char *buf, *cap;
788: char **str;
789: {
1.16 perry 790: u_int m_room;
791: char *bp, *mp;
1.1 cgd 792: int len;
793: char *mem;
794:
795: /*
796: * Find string capability cap
797: */
798: bp = cgetcap(buf, cap, '=');
799: if (bp == NULL)
800: return (-1);
801:
802: /*
803: * Conversion / storage allocation loop ... Allocate memory in
804: * chunks SFRAG in size.
805: */
806: if ((mem = malloc(SFRAG)) == NULL) {
807: errno = ENOMEM;
808: return (-2); /* couldn't even allocate the first fragment */
809: }
810: m_room = SFRAG;
811: mp = mem;
812:
813: while (*bp != ':' && *bp != '\0') {
814: /*
815: * Loop invariants:
816: * There is always room for one more character in mem.
817: * Mp always points just past last character in mem.
818: * Bp always points at next character in buf.
819: */
820: if (*bp == '^') {
821: bp++;
822: if (*bp == ':' || *bp == '\0')
823: break; /* drop unfinished escape */
824: *mp++ = *bp++ & 037;
825: } else if (*bp == '\\') {
826: bp++;
827: if (*bp == ':' || *bp == '\0')
828: break; /* drop unfinished escape */
829: if ('0' <= *bp && *bp <= '7') {
1.16 perry 830: int n, i;
1.1 cgd 831:
832: n = 0;
833: i = 3; /* maximum of three octal digits */
834: do {
835: n = n * 8 + (*bp++ - '0');
836: } while (--i && '0' <= *bp && *bp <= '7');
837: *mp++ = n;
838: }
839: else switch (*bp++) {
840: case 'b': case 'B':
841: *mp++ = '\b';
842: break;
843: case 't': case 'T':
844: *mp++ = '\t';
845: break;
846: case 'n': case 'N':
847: *mp++ = '\n';
848: break;
849: case 'f': case 'F':
850: *mp++ = '\f';
851: break;
852: case 'r': case 'R':
853: *mp++ = '\r';
854: break;
855: case 'e': case 'E':
856: *mp++ = ESC;
857: break;
858: case 'c': case 'C':
859: *mp++ = ':';
860: break;
861: default:
862: /*
863: * Catches '\', '^', and
864: * everything else.
865: */
866: *mp++ = *(bp-1);
867: break;
868: }
869: } else
870: *mp++ = *bp++;
871: m_room--;
872:
873: /*
874: * Enforce loop invariant: if no room left in current
875: * buffer, try to get some more.
876: */
877: if (m_room == 0) {
878: size_t size = mp - mem;
879:
880: if ((mem = realloc(mem, size + SFRAG)) == NULL)
881: return (-2);
882: m_room = SFRAG;
883: mp = mem + size;
884: }
885: }
886: *mp++ = '\0'; /* loop invariant let's us do this */
887: m_room--;
888: len = mp - mem - 1;
889:
890: /*
891: * Give back any extra memory and return value and success.
892: */
893: if (m_room != 0)
894: if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL)
895: return (-2);
896: *str = mem;
897: return (len);
898: }
899:
900: /*
901: * Cgetustr retrieves the value of the string capability cap from the
902: * capability record pointed to by buf. The difference between cgetustr()
903: * and cgetstr() is that cgetustr does not decode escapes but rather treats
904: * all characters literally. A pointer to a NUL terminated malloc'd
905: * copy of the string is returned in the char pointed to by str. The
906: * length of the string not including the trailing NUL is returned on success,
907: * -1 if the requested string capability couldn't be found, -2 if a system
908: * error was encountered (storage allocation failure).
909: */
910: int
911: cgetustr(buf, cap, str)
912: char *buf, *cap, **str;
913: {
1.16 perry 914: u_int m_room;
915: char *bp, *mp;
1.1 cgd 916: int len;
917: char *mem;
918:
919: /*
920: * Find string capability cap
921: */
922: if ((bp = cgetcap(buf, cap, '=')) == NULL)
923: return (-1);
924:
925: /*
926: * Conversion / storage allocation loop ... Allocate memory in
927: * chunks SFRAG in size.
928: */
929: if ((mem = malloc(SFRAG)) == NULL) {
930: errno = ENOMEM;
931: return (-2); /* couldn't even allocate the first fragment */
932: }
933: m_room = SFRAG;
934: mp = mem;
935:
936: while (*bp != ':' && *bp != '\0') {
937: /*
938: * Loop invariants:
939: * There is always room for one more character in mem.
940: * Mp always points just past last character in mem.
941: * Bp always points at next character in buf.
942: */
943: *mp++ = *bp++;
944: m_room--;
945:
946: /*
947: * Enforce loop invariant: if no room left in current
948: * buffer, try to get some more.
949: */
950: if (m_room == 0) {
951: size_t size = mp - mem;
952:
953: if ((mem = realloc(mem, size + SFRAG)) == NULL)
954: return (-2);
955: m_room = SFRAG;
956: mp = mem + size;
957: }
958: }
959: *mp++ = '\0'; /* loop invariant let's us do this */
960: m_room--;
961: len = mp - mem - 1;
962:
963: /*
964: * Give back any extra memory and return value and success.
965: */
966: if (m_room != 0)
967: if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL)
968: return (-2);
969: *str = mem;
970: return (len);
971: }
972:
973: /*
974: * Cgetnum retrieves the value of the numeric capability cap from the
975: * capability record pointed to by buf. The numeric value is returned in
976: * the long pointed to by num. 0 is returned on success, -1 if the requested
977: * numeric capability couldn't be found.
978: */
979: int
980: cgetnum(buf, cap, num)
981: char *buf, *cap;
982: long *num;
983: {
1.16 perry 984: long n;
985: int base, digit;
986: char *bp;
1.1 cgd 987:
988: /*
989: * Find numeric capability cap
990: */
991: bp = cgetcap(buf, cap, '#');
992: if (bp == NULL)
993: return (-1);
994:
995: /*
996: * Look at value and determine numeric base:
997: * 0x... or 0X... hexadecimal,
998: * else 0... octal,
999: * else decimal.
1000: */
1001: if (*bp == '0') {
1002: bp++;
1003: if (*bp == 'x' || *bp == 'X') {
1004: bp++;
1005: base = 16;
1006: } else
1007: base = 8;
1008: } else
1009: base = 10;
1010:
1011: /*
1012: * Conversion loop ...
1013: */
1014: n = 0;
1015: for (;;) {
1016: if ('0' <= *bp && *bp <= '9')
1017: digit = *bp - '0';
1018: else if ('a' <= *bp && *bp <= 'f')
1019: digit = 10 + *bp - 'a';
1020: else if ('A' <= *bp && *bp <= 'F')
1021: digit = 10 + *bp - 'A';
1022: else
1023: break;
1024:
1025: if (digit >= base)
1026: break;
1027:
1028: n = n * base + digit;
1029: bp++;
1030: }
1031:
1032: /*
1033: * Return value and success.
1034: */
1035: *num = n;
1036: return (0);
1037: }
1038:
1039:
1040: /*
1041: * Compare name field of record.
1042: */
1043: static int
1044: nfcmp(nf, rec)
1045: char *nf, *rec;
1046: {
1047: char *cp, tmp;
1048: int ret;
1049:
1050: for (cp = rec; *cp != ':'; cp++)
1051: ;
1052:
1053: tmp = *(cp + 1);
1054: *(cp + 1) = '\0';
1055: ret = strcmp(nf, rec);
1056: *(cp + 1) = tmp;
1057:
1058: return (ret);
1059: }