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