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