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