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