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