xref: /netbsd-src/lib/libc/gen/getcap.c (revision 1ca5c1b28139779176bd5c13ad7c5f25c0bcd5f8)
1 /*	$NetBSD: getcap.c,v 1.34 2001/06/25 15:34:08 mrg 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.34 2001/06/25 15:34:08 mrg 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 		done = 0;
826 		np = nbuf;
827 		for (;;) {
828 			for (cp = line; *cp != '\0'; cp++) {
829 				if (*cp == ':') {
830 					*np++ = ':';
831 					done = 1;
832 					break;
833 				}
834 				if (*cp == '\\')
835 					break;
836 				*np++ = *cp;
837 			}
838 			if (done) {
839 				*np = '\0';
840 				break;
841 			} else { /* name field extends beyond the line */
842 				line = fgetln(pfp, &len);
843 				if (line == NULL && pfp) {
844 					if (ferror(pfp)) {
845 						(void)cgetclose();
846 						return (-1);
847 					}
848 					(void)fclose(pfp);
849 					pfp = NULL;
850 					*np = '\0';
851 					break;
852 				} else
853 					line[len - 1] = '\0';
854 			}
855 		}
856 		rp = buf;
857 		for(cp = nbuf; *cp != '\0'; cp++)
858 			if (*cp == '|' || *cp == ':')
859 				break;
860 			else
861 				*rp++ = *cp;
862 
863 		*rp = '\0';
864 		/*
865 		 * XXX
866 		 * Last argument of getent here should be nbuf if we want true
867 		 * sequential access in the case of duplicates.
868 		 * With NULL, getent will return the first entry found
869 		 * rather than the duplicate entry record.  This is a
870 		 * matter of semantics that should be resolved.
871 		 */
872 		status = getent(bp, &dummy, db_array, -1, buf, 0, NULL);
873 		if (status == -2 || status == -3)
874 			(void)cgetclose();
875 
876 		return (status + 1);
877 	}
878 	/* NOTREACHED */
879 }
880 
881 /*
882  * Cgetstr retrieves the value of the string capability cap from the
883  * capability record pointed to by buf.  A pointer to a decoded, NUL
884  * terminated, malloc'd copy of the string is returned in the char *
885  * pointed to by str.  The length of the string not including the trailing
886  * NUL is returned on success, -1 if the requested string capability
887  * couldn't be found, -2 if a system error was encountered (storage
888  * allocation failure).
889  */
890 int
891 cgetstr(buf, cap, str)
892 	char *buf;
893 	const char *cap;
894 	char **str;
895 {
896 	u_int m_room;
897 	const char *bp;
898 	char *mp;
899 	int len;
900 	char *mem, *newmem;
901 
902 	_DIAGASSERT(buf != NULL);
903 	_DIAGASSERT(cap != NULL);
904 	_DIAGASSERT(str != NULL);
905 
906 	/*
907 	 * Find string capability cap
908 	 */
909 	bp = cgetcap(buf, cap, '=');
910 	if (bp == NULL)
911 		return (-1);
912 
913 	/*
914 	 * Conversion / storage allocation loop ...  Allocate memory in
915 	 * chunks SFRAG in size.
916 	 */
917 	if ((mem = malloc(SFRAG)) == NULL) {
918 		errno = ENOMEM;
919 		return (-2);	/* couldn't even allocate the first fragment */
920 	}
921 	m_room = SFRAG;
922 	mp = mem;
923 
924 	while (*bp != ':' && *bp != '\0') {
925 		/*
926 		 * Loop invariants:
927 		 *	There is always room for one more character in mem.
928 		 *	Mp always points just past last character in mem.
929 		 *	Bp always points at next character in buf.
930 		 */
931 		if (*bp == '^') {
932 			bp++;
933 			if (*bp == ':' || *bp == '\0')
934 				break;	/* drop unfinished escape */
935 			*mp++ = *bp++ & 037;
936 		} else if (*bp == '\\') {
937 			bp++;
938 			if (*bp == ':' || *bp == '\0')
939 				break;	/* drop unfinished escape */
940 			if ('0' <= *bp && *bp <= '7') {
941 				int n, i;
942 
943 				n = 0;
944 				i = 3;	/* maximum of three octal digits */
945 				do {
946 					n = n * 8 + (*bp++ - '0');
947 				} while (--i && '0' <= *bp && *bp <= '7');
948 				*mp++ = n;
949 			}
950 			else switch (*bp++) {
951 				case 'b': case 'B':
952 					*mp++ = '\b';
953 					break;
954 				case 't': case 'T':
955 					*mp++ = '\t';
956 					break;
957 				case 'n': case 'N':
958 					*mp++ = '\n';
959 					break;
960 				case 'f': case 'F':
961 					*mp++ = '\f';
962 					break;
963 				case 'r': case 'R':
964 					*mp++ = '\r';
965 					break;
966 				case 'e': case 'E':
967 					*mp++ = ESC;
968 					break;
969 				case 'c': case 'C':
970 					*mp++ = ':';
971 					break;
972 				default:
973 					/*
974 					 * Catches '\', '^', and
975 					 *  everything else.
976 					 */
977 					*mp++ = *(bp-1);
978 					break;
979 			}
980 		} else
981 			*mp++ = *bp++;
982 		m_room--;
983 
984 		/*
985 		 * Enforce loop invariant: if no room left in current
986 		 * buffer, try to get some more.
987 		 */
988 		if (m_room == 0) {
989 			size_t size = mp - mem;
990 
991 			if ((newmem = realloc(mem, size + SFRAG)) == NULL) {
992 				free(mem);
993 				return (-2);
994 			}
995 			mem = newmem;
996 			m_room = SFRAG;
997 			mp = mem + size;
998 		}
999 	}
1000 	*mp++ = '\0';	/* loop invariant let's us do this */
1001 	m_room--;
1002 	len = mp - mem - 1;
1003 
1004 	/*
1005 	 * Give back any extra memory and return value and success.
1006 	 */
1007 	if (m_room != 0) {
1008 		if ((newmem = realloc(mem, (size_t)(mp - mem))) == NULL) {
1009 			free(mem);
1010 			return (-2);
1011 		}
1012 		mem = newmem;
1013 	}
1014 	*str = mem;
1015 	return (len);
1016 }
1017 
1018 /*
1019  * Cgetustr retrieves the value of the string capability cap from the
1020  * capability record pointed to by buf.  The difference between cgetustr()
1021  * and cgetstr() is that cgetustr does not decode escapes but rather treats
1022  * all characters literally.  A pointer to a  NUL terminated malloc'd
1023  * copy of the string is returned in the char pointed to by str.  The
1024  * length of the string not including the trailing NUL is returned on success,
1025  * -1 if the requested string capability couldn't be found, -2 if a system
1026  * error was encountered (storage allocation failure).
1027  */
1028 int
1029 cgetustr(buf, cap, str)
1030 	char *buf;
1031 	const char *cap;
1032 	char **str;
1033 {
1034 	u_int m_room;
1035 	const char *bp;
1036 	char *mp;
1037 	int len;
1038 	char *mem, *newmem;
1039 
1040 	_DIAGASSERT(buf != NULL);
1041 	_DIAGASSERT(cap != NULL);
1042 	_DIAGASSERT(str != NULL);
1043 
1044 	/*
1045 	 * Find string capability cap
1046 	 */
1047 	if ((bp = cgetcap(buf, cap, '=')) == NULL)
1048 		return (-1);
1049 
1050 	/*
1051 	 * Conversion / storage allocation loop ...  Allocate memory in
1052 	 * chunks SFRAG in size.
1053 	 */
1054 	if ((mem = malloc(SFRAG)) == NULL) {
1055 		errno = ENOMEM;
1056 		return (-2);	/* couldn't even allocate the first fragment */
1057 	}
1058 	m_room = SFRAG;
1059 	mp = mem;
1060 
1061 	while (*bp != ':' && *bp != '\0') {
1062 		/*
1063 		 * Loop invariants:
1064 		 *	There is always room for one more character in mem.
1065 		 *	Mp always points just past last character in mem.
1066 		 *	Bp always points at next character in buf.
1067 		 */
1068 		*mp++ = *bp++;
1069 		m_room--;
1070 
1071 		/*
1072 		 * Enforce loop invariant: if no room left in current
1073 		 * buffer, try to get some more.
1074 		 */
1075 		if (m_room == 0) {
1076 			size_t size = mp - mem;
1077 
1078 			if ((newmem = realloc(mem, size + SFRAG)) == NULL) {
1079 				free(mem);
1080 				return (-2);
1081 			}
1082 			mem = newmem;
1083 			m_room = SFRAG;
1084 			mp = mem + size;
1085 		}
1086 	}
1087 	*mp++ = '\0';	/* loop invariant let's us do this */
1088 	m_room--;
1089 	len = mp - mem - 1;
1090 
1091 	/*
1092 	 * Give back any extra memory and return value and success.
1093 	 */
1094 	if (m_room != 0) {
1095 		if ((newmem = realloc(mem, (size_t)(mp - mem))) == NULL) {
1096 			free(mem);
1097 			return (-2);
1098 		}
1099 		mem = newmem;
1100 	}
1101 	*str = mem;
1102 	return (len);
1103 }
1104 
1105 /*
1106  * Cgetnum retrieves the value of the numeric capability cap from the
1107  * capability record pointed to by buf.  The numeric value is returned in
1108  * the long pointed to by num.  0 is returned on success, -1 if the requested
1109  * numeric capability couldn't be found.
1110  */
1111 int
1112 cgetnum(buf, cap, num)
1113 	char *buf;
1114 	const char *cap;
1115 	long *num;
1116 {
1117 	long n;
1118 	int base, digit;
1119 	const char *bp;
1120 
1121 	_DIAGASSERT(buf != NULL);
1122 	_DIAGASSERT(cap != NULL);
1123 	_DIAGASSERT(num != NULL);
1124 
1125 	/*
1126 	 * Find numeric capability cap
1127 	 */
1128 	bp = cgetcap(buf, cap, '#');
1129 	if (bp == NULL)
1130 		return (-1);
1131 
1132 	/*
1133 	 * Look at value and determine numeric base:
1134 	 *	0x... or 0X...	hexadecimal,
1135 	 * else	0...		octal,
1136 	 * else			decimal.
1137 	 */
1138 	if (*bp == '0') {
1139 		bp++;
1140 		if (*bp == 'x' || *bp == 'X') {
1141 			bp++;
1142 			base = 16;
1143 		} else
1144 			base = 8;
1145 	} else
1146 		base = 10;
1147 
1148 	/*
1149 	 * Conversion loop ...
1150 	 */
1151 	n = 0;
1152 	for (;;) {
1153 		if ('0' <= *bp && *bp <= '9')
1154 			digit = *bp - '0';
1155 		else if ('a' <= *bp && *bp <= 'f')
1156 			digit = 10 + *bp - 'a';
1157 		else if ('A' <= *bp && *bp <= 'F')
1158 			digit = 10 + *bp - 'A';
1159 		else
1160 			break;
1161 
1162 		if (digit >= base)
1163 			break;
1164 
1165 		n = n * base + digit;
1166 		bp++;
1167 	}
1168 
1169 	/*
1170 	 * Return value and success.
1171 	 */
1172 	*num = n;
1173 	return (0);
1174 }
1175 
1176 
1177 /*
1178  * Compare name field of record.
1179  */
1180 static int
1181 nfcmp(nf, rec)
1182 	char *nf, *rec;
1183 {
1184 	char *cp, tmp;
1185 	int ret;
1186 
1187 	_DIAGASSERT(nf != NULL);
1188 	_DIAGASSERT(rec != NULL);
1189 
1190 	for (cp = rec; *cp != ':'; cp++)
1191 		;
1192 
1193 	tmp = *(cp + 1);
1194 	*(cp + 1) = '\0';
1195 	ret = strcmp(nf, rec);
1196 	*(cp + 1) = tmp;
1197 
1198 	return (ret);
1199 }
1200