Home | History | Annotate | Line # | Download | only in gen
getcap.c revision 1.27
      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