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