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