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