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