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