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