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