xref: /netbsd-src/lib/libc/gen/getcap.c (revision 89c5a767f8fc7a4633b2d409966e2becbb98ff92)
1 /*	$NetBSD: getcap.c,v 1.32 2000/01/22 22:19:10 mycroft 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.32 2000/01/22 22:19:10 mycroft 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;
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 					record = realloc(record, newsize);
439 					if (record == NULL) {
440 						if (myfd)
441 							(void)close(fd);
442 						errno = ENOMEM;
443 						return (-2);
444 					}
445 					r_end = record + newsize;
446 					rp = record + pos;
447 				}
448 			}
449 			/* Eliminate any white space after the last colon. */
450 			if (cp)
451 				rp = cp + 1;
452 			/* Loop invariant lets us do this. */
453 			*rp++ = '\0';
454 
455 			/*
456 			 * If encountered eof check next file.
457 			 */
458 			if (eof)
459 				break;
460 
461 			/*
462 			 * Toss blank lines and comments.
463 			 */
464 			if (*record == '\0' || *record == '#')
465 				continue;
466 
467 			/*
468 			 * See if this is the record we want ...
469 			 */
470 			if (cgetmatch(record, name) == 0) {
471 				if (nfield == NULL || !nfcmp(nfield, record)) {
472 					foundit = 1;
473 					break;	/* found it! */
474 				}
475 			}
476 		}
477 	}
478 		if (foundit)
479 			break;
480 	}
481 
482 	if (!foundit)
483 		return (-1);
484 
485 	/*
486 	 * Got the capability record, but now we have to expand all tc=name
487 	 * references in it ...
488 	 */
489 tc_exp:	{
490 		char *newicap, *s;
491 		size_t ilen, newilen;
492 		int diff, iret, tclen;
493 		char *icap, *scan, *tc, *tcstart, *tcend;
494 
495 		/*
496 		 * Loop invariants:
497 		 *	There is room for one more character in record.
498 		 *	R_end points just past end of record.
499 		 *	Rp points just past last character in record.
500 		 *	Scan points at remainder of record that needs to be
501 		 *	scanned for tc=name constructs.
502 		 */
503 		scan = record;
504 		tc_not_resolved = 0;
505 		for (;;) {
506 			if ((tc = cgetcap(scan, "tc", '=')) == NULL)
507 				break;
508 
509 			/*
510 			 * Find end of tc=name and stomp on the trailing `:'
511 			 * (if present) so we can use it to call ourselves.
512 			 */
513 			s = tc;
514 			for (;;)
515 				if (*s == '\0')
516 					break;
517 				else
518 					if (*s++ == ':') {
519 						*(s - 1) = '\0';
520 						break;
521 					}
522 			tcstart = tc - 3;
523 			tclen = s - tcstart;
524 			tcend = s;
525 
526 			iret = getent(&icap, &ilen, db_p, fd, tc, depth+1,
527 				      NULL);
528 			newicap = icap;		/* Put into a register. */
529 			newilen = ilen;
530 			if (iret != 0) {
531 				/* an error */
532 				if (iret < -1) {
533 					if (myfd)
534 						(void)close(fd);
535 					free(record);
536 					return (iret);
537 				}
538 				if (iret == 1)
539 					tc_not_resolved = 1;
540 				/* couldn't resolve tc */
541 				if (iret == -1) {
542 					*(s - 1) = ':';
543 					scan = s - 1;
544 					tc_not_resolved = 1;
545 					continue;
546 
547 				}
548 			}
549 			/* not interested in name field of tc'ed record */
550 			s = newicap;
551 			for (;;)
552 				if (*s == '\0')
553 					break;
554 				else
555 					if (*s++ == ':')
556 						break;
557 			newilen -= s - newicap;
558 			newicap = s;
559 
560 			/* make sure interpolated record is `:'-terminated */
561 			s += newilen;
562 			if (*(s-1) != ':') {
563 				*s = ':';	/* overwrite NUL with : */
564 				newilen++;
565 			}
566 
567 			/*
568 			 * Make sure there's enough room to insert the
569 			 * new record.
570 			 */
571 			diff = newilen - tclen;
572 			if (diff >= r_end - rp) {
573 				u_int pos, tcpos, tcposend;
574 				size_t newsize;
575 
576 				pos = rp - record;
577 				newsize = r_end - record + diff + BFRAG;
578 				tcpos = tcstart - record;
579 				tcposend = tcend - record;
580 				record = realloc(record, newsize);
581 				if (record == NULL) {
582 					if (myfd)
583 						(void)close(fd);
584 					free(icap);
585 					errno = ENOMEM;
586 					return (-2);
587 				}
588 				r_end = record + newsize;
589 				rp = record + pos;
590 				tcstart = record + tcpos;
591 				tcend = record + tcposend;
592 			}
593 
594 			/*
595 			 * Insert tc'ed record into our record.
596 			 */
597 			s = tcstart + newilen;
598 			memmove(s, tcend,  (size_t)(rp - tcend));
599 			memmove(tcstart, newicap, newilen);
600 			rp += diff;
601 			free(icap);
602 
603 			/*
604 			 * Start scan on `:' so next cgetcap works properly
605 			 * (cgetcap always skips first field).
606 			 */
607 			scan = s-1;
608 		}
609 
610 	}
611 	/*
612 	 * Close file (if we opened it), give back any extra memory, and
613 	 * return capability, length and success.
614 	 */
615 	if (myfd)
616 		(void)close(fd);
617 	*len = rp - record - 1;	/* don't count NUL */
618 	if (r_end > rp)
619 		if ((record =
620 		     realloc(record, (size_t)(rp - record))) == NULL) {
621 			errno = ENOMEM;
622 			return (-2);
623 		}
624 
625 	*cap = record;
626 	if (tc_not_resolved)
627 		return (1);
628 	return (0);
629 }
630 
631 static int
632 cdbget(capdbp, bp, name)
633 	DB *capdbp;
634 	char **bp;
635 	const char *name;
636 {
637 	DBT key;
638 	DBT data;
639 
640 	_DIAGASSERT(capdbp != NULL);
641 	_DIAGASSERT(bp != NULL);
642 	_DIAGASSERT(name != NULL);
643 
644 	/* LINTED key is not modified */
645 	key.data = (char *)name;
646 	key.size = strlen(name);
647 
648 	for (;;) {
649 		/* Get the reference. */
650 		switch(capdbp->get(capdbp, &key, &data, 0)) {
651 		case -1:
652 			return (-2);
653 		case 1:
654 			return (-1);
655 		}
656 
657 		/* If not an index to another record, leave. */
658 		if (((char *)data.data)[0] != SHADOW)
659 			break;
660 
661 		key.data = (char *)data.data + 1;
662 		key.size = data.size - 1;
663 	}
664 
665 	*bp = (char *)data.data + 1;
666 	return (((char *)(data.data))[0] == TCERR ? 1 : 0);
667 }
668 
669 /*
670  * Cgetmatch will return 0 if name is one of the names of the capability
671  * record buf, -1 if not.
672  */
673 int
674 cgetmatch(buf, name)
675 	const char *buf, *name;
676 {
677 	const char *np, *bp;
678 
679 	_DIAGASSERT(buf != NULL);
680 	_DIAGASSERT(name != NULL);
681 
682 	/*
683 	 * Start search at beginning of record.
684 	 */
685 	bp = buf;
686 	for (;;) {
687 		/*
688 		 * Try to match a record name.
689 		 */
690 		np = name;
691 		for (;;)
692 			if (*np == '\0') {
693 				if (*bp == '|' || *bp == ':' || *bp == '\0')
694 					return (0);
695 				else
696 					break;
697 			} else
698 				if (*bp++ != *np++)
699 					break;
700 
701 		/*
702 		 * Match failed, skip to next name in record.
703 		 */
704 		bp--;	/* a '|' or ':' may have stopped the match */
705 		for (;;)
706 			if (*bp == '\0' || *bp == ':')
707 				return (-1);	/* match failed totally */
708 			else
709 				if (*bp++ == '|')
710 					break;	/* found next name */
711 	}
712 }
713 
714 int
715 cgetfirst(buf, db_array)
716 	char **buf, **db_array;
717 {
718 
719 	_DIAGASSERT(buf != NULL);
720 	_DIAGASSERT(db_array != NULL);
721 
722 	(void)cgetclose();
723 	return (cgetnext(buf, db_array));
724 }
725 
726 static FILE *pfp;
727 static int slash;
728 static char **dbp;
729 
730 int
731 cgetclose()
732 {
733 	if (pfp != NULL) {
734 		(void)fclose(pfp);
735 		pfp = NULL;
736 	}
737 	dbp = NULL;
738 	gottoprec = 0;
739 	slash = 0;
740 	return(0);
741 }
742 
743 /*
744  * Cgetnext() gets either the first or next entry in the logical database
745  * specified by db_array.  It returns 0 upon completion of the database, 1
746  * upon returning an entry with more remaining, and -1 if an error occurs.
747  */
748 int
749 cgetnext(bp, db_array)
750         char **bp;
751 	char **db_array;
752 {
753 	size_t len;
754 	int status, done;
755 	char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE];
756 	size_t dummy;
757 
758 	_DIAGASSERT(bp != NULL);
759 	_DIAGASSERT(db_array != NULL);
760 
761 	if (dbp == NULL)
762 		dbp = db_array;
763 
764 	if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) {
765 		(void)cgetclose();
766 		return (-1);
767 	}
768 	for(;;) {
769 		if (toprec && !gottoprec) {
770 			gottoprec = 1;
771 			line = toprec;
772 		} else {
773 			line = fgetln(pfp, &len);
774 			if (line == NULL && pfp) {
775 				if (ferror(pfp)) {
776 					(void)cgetclose();
777 					return (-1);
778 				} else {
779 					(void)fclose(pfp);
780 					pfp = NULL;
781 					if (*++dbp == NULL) {
782 						(void)cgetclose();
783 						return (0);
784 					} else if ((pfp =
785 					    fopen(*dbp, "r")) == NULL) {
786 						(void)cgetclose();
787 						return (-1);
788 					} else
789 						continue;
790 				}
791 			} else
792 				line[len - 1] = '\0';
793 			if (len == 1) {
794 				slash = 0;
795 				continue;
796 			}
797 			if (isspace((unsigned char)*line) ||
798 			    *line == ':' || *line == '#' || slash) {
799 				if (line[len - 2] == '\\')
800 					slash = 1;
801 				else
802 					slash = 0;
803 				continue;
804 			}
805 			if (line[len - 2] == '\\')
806 				slash = 1;
807 			else
808 				slash = 0;
809 		}
810 
811 
812 		/*
813 		 * Line points to a name line.
814 		 */
815 		done = 0;
816 		np = nbuf;
817 		for (;;) {
818 			for (cp = line; *cp != '\0'; cp++) {
819 				if (*cp == ':') {
820 					*np++ = ':';
821 					done = 1;
822 					break;
823 				}
824 				if (*cp == '\\')
825 					break;
826 				*np++ = *cp;
827 			}
828 			if (done) {
829 				*np = '\0';
830 				break;
831 			} else { /* name field extends beyond the line */
832 				line = fgetln(pfp, &len);
833 				if (line == NULL && pfp) {
834 					if (ferror(pfp)) {
835 						(void)cgetclose();
836 						return (-1);
837 					}
838 					(void)fclose(pfp);
839 					pfp = NULL;
840 					*np = '\0';
841 					break;
842 				} else
843 					line[len - 1] = '\0';
844 			}
845 		}
846 		rp = buf;
847 		for(cp = nbuf; *cp != '\0'; cp++)
848 			if (*cp == '|' || *cp == ':')
849 				break;
850 			else
851 				*rp++ = *cp;
852 
853 		*rp = '\0';
854 		/*
855 		 * XXX
856 		 * Last argument of getent here should be nbuf if we want true
857 		 * sequential access in the case of duplicates.
858 		 * With NULL, getent will return the first entry found
859 		 * rather than the duplicate entry record.  This is a
860 		 * matter of semantics that should be resolved.
861 		 */
862 		status = getent(bp, &dummy, db_array, -1, buf, 0, NULL);
863 		if (status == -2 || status == -3)
864 			(void)cgetclose();
865 
866 		return (status + 1);
867 	}
868 	/* NOTREACHED */
869 }
870 
871 /*
872  * Cgetstr retrieves the value of the string capability cap from the
873  * capability record pointed to by buf.  A pointer to a decoded, NUL
874  * terminated, malloc'd copy of the string is returned in the char *
875  * pointed to by str.  The length of the string not including the trailing
876  * NUL is returned on success, -1 if the requested string capability
877  * couldn't be found, -2 if a system error was encountered (storage
878  * allocation failure).
879  */
880 int
881 cgetstr(buf, cap, str)
882 	char *buf;
883 	const char *cap;
884 	char **str;
885 {
886 	u_int m_room;
887 	const char *bp;
888 	char *mp;
889 	int len;
890 	char *mem;
891 
892 	_DIAGASSERT(buf != NULL);
893 	_DIAGASSERT(cap != NULL);
894 	_DIAGASSERT(str != NULL);
895 
896 	/*
897 	 * Find string capability cap
898 	 */
899 	bp = cgetcap(buf, cap, '=');
900 	if (bp == NULL)
901 		return (-1);
902 
903 	/*
904 	 * Conversion / storage allocation loop ...  Allocate memory in
905 	 * chunks SFRAG in size.
906 	 */
907 	if ((mem = malloc(SFRAG)) == NULL) {
908 		errno = ENOMEM;
909 		return (-2);	/* couldn't even allocate the first fragment */
910 	}
911 	m_room = SFRAG;
912 	mp = mem;
913 
914 	while (*bp != ':' && *bp != '\0') {
915 		/*
916 		 * Loop invariants:
917 		 *	There is always room for one more character in mem.
918 		 *	Mp always points just past last character in mem.
919 		 *	Bp always points at next character in buf.
920 		 */
921 		if (*bp == '^') {
922 			bp++;
923 			if (*bp == ':' || *bp == '\0')
924 				break;	/* drop unfinished escape */
925 			*mp++ = *bp++ & 037;
926 		} else if (*bp == '\\') {
927 			bp++;
928 			if (*bp == ':' || *bp == '\0')
929 				break;	/* drop unfinished escape */
930 			if ('0' <= *bp && *bp <= '7') {
931 				int n, i;
932 
933 				n = 0;
934 				i = 3;	/* maximum of three octal digits */
935 				do {
936 					n = n * 8 + (*bp++ - '0');
937 				} while (--i && '0' <= *bp && *bp <= '7');
938 				*mp++ = n;
939 			}
940 			else switch (*bp++) {
941 				case 'b': case 'B':
942 					*mp++ = '\b';
943 					break;
944 				case 't': case 'T':
945 					*mp++ = '\t';
946 					break;
947 				case 'n': case 'N':
948 					*mp++ = '\n';
949 					break;
950 				case 'f': case 'F':
951 					*mp++ = '\f';
952 					break;
953 				case 'r': case 'R':
954 					*mp++ = '\r';
955 					break;
956 				case 'e': case 'E':
957 					*mp++ = ESC;
958 					break;
959 				case 'c': case 'C':
960 					*mp++ = ':';
961 					break;
962 				default:
963 					/*
964 					 * Catches '\', '^', and
965 					 *  everything else.
966 					 */
967 					*mp++ = *(bp-1);
968 					break;
969 			}
970 		} else
971 			*mp++ = *bp++;
972 		m_room--;
973 
974 		/*
975 		 * Enforce loop invariant: if no room left in current
976 		 * buffer, try to get some more.
977 		 */
978 		if (m_room == 0) {
979 			size_t size = mp - mem;
980 
981 			if ((mem = realloc(mem, size + SFRAG)) == NULL)
982 				return (-2);
983 			m_room = SFRAG;
984 			mp = mem + size;
985 		}
986 	}
987 	*mp++ = '\0';	/* loop invariant let's us do this */
988 	m_room--;
989 	len = mp - mem - 1;
990 
991 	/*
992 	 * Give back any extra memory and return value and success.
993 	 */
994 	if (m_room != 0)
995 		if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL)
996 			return (-2);
997 	*str = mem;
998 	return (len);
999 }
1000 
1001 /*
1002  * Cgetustr retrieves the value of the string capability cap from the
1003  * capability record pointed to by buf.  The difference between cgetustr()
1004  * and cgetstr() is that cgetustr does not decode escapes but rather treats
1005  * all characters literally.  A pointer to a  NUL terminated malloc'd
1006  * copy of the string is returned in the char pointed to by str.  The
1007  * length of the string not including the trailing NUL is returned on success,
1008  * -1 if the requested string capability couldn't be found, -2 if a system
1009  * error was encountered (storage allocation failure).
1010  */
1011 int
1012 cgetustr(buf, cap, str)
1013 	char *buf;
1014 	const char *cap;
1015 	char **str;
1016 {
1017 	u_int m_room;
1018 	const char *bp;
1019 	char *mp;
1020 	int len;
1021 	char *mem;
1022 
1023 	_DIAGASSERT(buf != NULL);
1024 	_DIAGASSERT(cap != NULL);
1025 	_DIAGASSERT(str != NULL);
1026 
1027 	/*
1028 	 * Find string capability cap
1029 	 */
1030 	if ((bp = cgetcap(buf, cap, '=')) == NULL)
1031 		return (-1);
1032 
1033 	/*
1034 	 * Conversion / storage allocation loop ...  Allocate memory in
1035 	 * chunks SFRAG in size.
1036 	 */
1037 	if ((mem = malloc(SFRAG)) == NULL) {
1038 		errno = ENOMEM;
1039 		return (-2);	/* couldn't even allocate the first fragment */
1040 	}
1041 	m_room = SFRAG;
1042 	mp = mem;
1043 
1044 	while (*bp != ':' && *bp != '\0') {
1045 		/*
1046 		 * Loop invariants:
1047 		 *	There is always room for one more character in mem.
1048 		 *	Mp always points just past last character in mem.
1049 		 *	Bp always points at next character in buf.
1050 		 */
1051 		*mp++ = *bp++;
1052 		m_room--;
1053 
1054 		/*
1055 		 * Enforce loop invariant: if no room left in current
1056 		 * buffer, try to get some more.
1057 		 */
1058 		if (m_room == 0) {
1059 			size_t size = mp - mem;
1060 
1061 			if ((mem = realloc(mem, size + SFRAG)) == NULL)
1062 				return (-2);
1063 			m_room = SFRAG;
1064 			mp = mem + size;
1065 		}
1066 	}
1067 	*mp++ = '\0';	/* loop invariant let's us do this */
1068 	m_room--;
1069 	len = mp - mem - 1;
1070 
1071 	/*
1072 	 * Give back any extra memory and return value and success.
1073 	 */
1074 	if (m_room != 0)
1075 		if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL)
1076 			return (-2);
1077 	*str = mem;
1078 	return (len);
1079 }
1080 
1081 /*
1082  * Cgetnum retrieves the value of the numeric capability cap from the
1083  * capability record pointed to by buf.  The numeric value is returned in
1084  * the long pointed to by num.  0 is returned on success, -1 if the requested
1085  * numeric capability couldn't be found.
1086  */
1087 int
1088 cgetnum(buf, cap, num)
1089 	char *buf;
1090 	const char *cap;
1091 	long *num;
1092 {
1093 	long n;
1094 	int base, digit;
1095 	const char *bp;
1096 
1097 	_DIAGASSERT(buf != NULL);
1098 	_DIAGASSERT(cap != NULL);
1099 	_DIAGASSERT(num != NULL);
1100 
1101 	/*
1102 	 * Find numeric capability cap
1103 	 */
1104 	bp = cgetcap(buf, cap, '#');
1105 	if (bp == NULL)
1106 		return (-1);
1107 
1108 	/*
1109 	 * Look at value and determine numeric base:
1110 	 *	0x... or 0X...	hexadecimal,
1111 	 * else	0...		octal,
1112 	 * else			decimal.
1113 	 */
1114 	if (*bp == '0') {
1115 		bp++;
1116 		if (*bp == 'x' || *bp == 'X') {
1117 			bp++;
1118 			base = 16;
1119 		} else
1120 			base = 8;
1121 	} else
1122 		base = 10;
1123 
1124 	/*
1125 	 * Conversion loop ...
1126 	 */
1127 	n = 0;
1128 	for (;;) {
1129 		if ('0' <= *bp && *bp <= '9')
1130 			digit = *bp - '0';
1131 		else if ('a' <= *bp && *bp <= 'f')
1132 			digit = 10 + *bp - 'a';
1133 		else if ('A' <= *bp && *bp <= 'F')
1134 			digit = 10 + *bp - 'A';
1135 		else
1136 			break;
1137 
1138 		if (digit >= base)
1139 			break;
1140 
1141 		n = n * base + digit;
1142 		bp++;
1143 	}
1144 
1145 	/*
1146 	 * Return value and success.
1147 	 */
1148 	*num = n;
1149 	return (0);
1150 }
1151 
1152 
1153 /*
1154  * Compare name field of record.
1155  */
1156 static int
1157 nfcmp(nf, rec)
1158 	char *nf, *rec;
1159 {
1160 	char *cp, tmp;
1161 	int ret;
1162 
1163 	_DIAGASSERT(nf != NULL);
1164 	_DIAGASSERT(rec != NULL);
1165 
1166 	for (cp = rec; *cp != ':'; cp++)
1167 		;
1168 
1169 	tmp = *(cp + 1);
1170 	*(cp + 1) = '\0';
1171 	ret = strcmp(nf, rec);
1172 	*(cp + 1) = tmp;
1173 
1174 	return (ret);
1175 }
1176