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