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