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