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