xref: /netbsd-src/lib/libc/gen/getcap.c (revision 6f4b1613b1a98f5d635505851dfbbaa57ca7af47)
1*6f4b1613Sshm /*	$NetBSD: getcap.c,v 1.58 2023/09/21 13:46:12 shm Exp $	*/
26e6a4e85Scgd 
38d582663Scgd /*-
46e6a4e85Scgd  * Copyright (c) 1992, 1993
56e6a4e85Scgd  *	The Regents of the University of California.  All rights reserved.
68d582663Scgd  *
78d582663Scgd  * This code is derived from software contributed to Berkeley by
88d582663Scgd  * Casey Leedom of Lawrence Livermore National Laboratory.
98d582663Scgd  *
108d582663Scgd  * Redistribution and use in source and binary forms, with or without
118d582663Scgd  * modification, are permitted provided that the following conditions
128d582663Scgd  * are met:
138d582663Scgd  * 1. Redistributions of source code must retain the above copyright
148d582663Scgd  *    notice, this list of conditions and the following disclaimer.
158d582663Scgd  * 2. Redistributions in binary form must reproduce the above copyright
168d582663Scgd  *    notice, this list of conditions and the following disclaimer in the
178d582663Scgd  *    documentation and/or other materials provided with the distribution.
18eb7c1594Sagc  * 3. Neither the name of the University nor the names of its contributors
198d582663Scgd  *    may be used to endorse or promote products derived from this software
208d582663Scgd  *    without specific prior written permission.
218d582663Scgd  *
228d582663Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
238d582663Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
248d582663Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
258d582663Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
268d582663Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
278d582663Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
288d582663Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
298d582663Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
308d582663Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
318d582663Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
328d582663Scgd  * SUCH DAMAGE.
338d582663Scgd  */
348d582663Scgd 
35171d6532Slukem #if HAVE_NBTOOL_CONFIG_H
36171d6532Slukem #include "nbtool_config.h"
374eb408dcSuwe #endif
384eb408dcSuwe 
39d177cac3Schristos #include <sys/cdefs.h>
408d582663Scgd #if defined(LIBC_SCCS) && !defined(lint)
416e6a4e85Scgd #if 0
426e6a4e85Scgd static char sccsid[] = "@(#)getcap.c	8.3 (Berkeley) 3/25/94";
436e6a4e85Scgd #else
44*6f4b1613Sshm __RCSID("$NetBSD: getcap.c,v 1.58 2023/09/21 13:46:12 shm Exp $");
456e6a4e85Scgd #endif
468d582663Scgd #endif /* LIBC_SCCS and not lint */
478d582663Scgd 
486a05c304Smanu #ifndef LIBHACK
4943fa6fe3Sjtc #include "namespace.h"
500b1d0cb3Schristos #endif
518d582663Scgd #include <sys/types.h>
522f9d1834Stron #include <sys/param.h>
53b48252f3Slukem 
54b48252f3Slukem #include <assert.h>
55c5e820caSchristos #include <stddef.h>
568d582663Scgd #include <ctype.h>
570b1d0cb3Schristos #ifndef SMALL
588d582663Scgd #include <db.h>
590b1d0cb3Schristos #endif
608d582663Scgd #include <errno.h>
618d582663Scgd #include <fcntl.h>
628d582663Scgd #include <limits.h>
638d582663Scgd #include <stdio.h>
648d582663Scgd #include <stdlib.h>
658d582663Scgd #include <string.h>
668d582663Scgd #include <unistd.h>
678d582663Scgd 
686a05c304Smanu #if defined(__weak_alias) && !defined(LIBHACK)
6960549036Smycroft __weak_alias(cgetcap,_cgetcap)
7060549036Smycroft __weak_alias(cgetclose,_cgetclose)
7160549036Smycroft __weak_alias(cgetent,_cgetent)
7260549036Smycroft __weak_alias(cgetfirst,_cgetfirst)
7360549036Smycroft __weak_alias(cgetmatch,_cgetmatch)
7460549036Smycroft __weak_alias(cgetnext,_cgetnext)
7560549036Smycroft __weak_alias(cgetnum,_cgetnum)
7660549036Smycroft __weak_alias(cgetset,_cgetset)
7760549036Smycroft __weak_alias(cgetstr,_cgetstr)
7860549036Smycroft __weak_alias(cgetustr,_cgetustr)
790d149bc5Schristos __weak_alias(csetexpandtc,_csetexpandtc)
8043fa6fe3Sjtc #endif
8143fa6fe3Sjtc 
828d582663Scgd #define	BFRAG		1024
838d582663Scgd #define	BSIZE		1024
848d582663Scgd #define	ESC		('[' & 037)	/* ASCII ESC */
858d582663Scgd #define	MAX_RECURSION	32		/* maximum getent recursion */
868d582663Scgd #define	SFRAG		100		/* cgetstr mallocs in SFRAG chunks */
878d582663Scgd 
888d582663Scgd #define RECOK	(char)0
898d582663Scgd #define TCERR	(char)1
908d582663Scgd #define	SHADOW	(char)2
918d582663Scgd 
928d582663Scgd static size_t	 topreclen;	/* toprec length */
938d582663Scgd static char	*toprec;	/* Additional record specified by cgetset() */
948d582663Scgd static int	 gottoprec;	/* Flag indicating retrieval of toprecord */
950d149bc5Schristos static int	 expandtc = 1;	/* flag to expand tc= or not */
968d582663Scgd 
970b1d0cb3Schristos #ifndef SMALL
982bab6168Schristos static int	cdbget(DB *, char **, const char *);
990b1d0cb3Schristos #endif
1002bab6168Schristos static int 	getent(char **, size_t *, const char * const *, int,
1012bab6168Schristos     const char *, int, char *);
1022bab6168Schristos static int	nfcmp(char *, char *);
1038d582663Scgd 
1048d582663Scgd /*
1058d582663Scgd  * Cgetset() allows the addition of a user specified buffer to be added
1068d582663Scgd  * to the database array, in effect "pushing" the buffer on top of the
1078d582663Scgd  * virtual database. 0 is returned on success, -1 on failure.
1088d582663Scgd  */
1098d582663Scgd int
cgetset(const char * ent)1102bab6168Schristos cgetset(const char *ent)
1118d582663Scgd {
1126c70403fSabs 	const char *source, *check;
1136c70403fSabs 	char *dest;
1146c70403fSabs 
1158d582663Scgd 	if (ent == NULL) {
116787fd082Sjnemeth 		if (toprec != NULL)
1178d582663Scgd 			free(toprec);
1188d582663Scgd                 toprec = NULL;
1198d582663Scgd                 topreclen = 0;
120787fd082Sjnemeth                 return 0;
1218d582663Scgd         }
1228d582663Scgd         topreclen = strlen(ent);
1238d582663Scgd         if ((toprec = malloc(topreclen + 1)) == NULL) {
1248d582663Scgd 		errno = ENOMEM;
125787fd082Sjnemeth                 return -1;
1268d582663Scgd 	}
1278d582663Scgd 	gottoprec = 0;
1286c70403fSabs 
1296c70403fSabs 	source = ent;
1306c70403fSabs 	dest = toprec;
131787fd082Sjnemeth 	while (*source != '\0') { /* Strip whitespace */
1326c70403fSabs 		*dest++ = *source++; /* Do not check first field */
1336c70403fSabs 		while (*source == ':') {
1346c70403fSabs 			check = source + 1;
135ac83f64eSabs 			while (*check && (isspace((unsigned char)*check) ||
136ac83f64eSabs 			    (*check=='\\' && isspace((unsigned char)check[1]))))
1376c70403fSabs 				++check;
1386c70403fSabs 			if (*check == ':')
1396c70403fSabs 				source = check;
1406c70403fSabs 			else
1416c70403fSabs 				break;
1426c70403fSabs 
1436c70403fSabs 		}
1446c70403fSabs 	}
1456c70403fSabs 	*dest = 0;
1466c70403fSabs 
147787fd082Sjnemeth         return 0;
1488d582663Scgd }
1498d582663Scgd 
1508d582663Scgd /*
1518d582663Scgd  * Cgetcap searches the capability record buf for the capability cap with
1528d582663Scgd  * type `type'.  A pointer to the value of cap is returned on success, NULL
1538d582663Scgd  * if the requested capability couldn't be found.
1548d582663Scgd  *
1558d582663Scgd  * Specifying a type of ':' means that nothing should follow cap (:cap:).
1568d582663Scgd  * In this case a pointer to the terminating ':' or NUL will be returned if
1578d582663Scgd  * cap is found.
1588d582663Scgd  *
1598d582663Scgd  * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
1608d582663Scgd  * return NULL.
1618d582663Scgd  */
1628d582663Scgd char *
cgetcap(char * buf,const char * cap,int type)16302682f7dSjoerg cgetcap(char *buf, const char *cap, int type)
1648d582663Scgd {
165f9f7e98dSmycroft 	char *bp;
166f9f7e98dSmycroft 	const char *cp;
1678d582663Scgd 
168b48252f3Slukem 	_DIAGASSERT(buf != NULL);
169b48252f3Slukem 	_DIAGASSERT(cap != NULL);
170b48252f3Slukem 
1718d582663Scgd 	bp = buf;
1728d582663Scgd 	for (;;) {
1738d582663Scgd 		/*
1748d582663Scgd 		 * Skip past the current capability field - it's either the
1758d582663Scgd 		 * name field if this is the first time through the loop, or
1768d582663Scgd 		 * the remainder of a field whose name failed to match cap.
1778d582663Scgd 		 */
1788d582663Scgd 		for (;;)
1798d582663Scgd 			if (*bp == '\0')
180787fd082Sjnemeth 				return NULL;
181787fd082Sjnemeth 			else if (*bp++ == ':')
1828d582663Scgd 				break;
1838d582663Scgd 
1848d582663Scgd 		/*
1858d582663Scgd 		 * Try to match (cap, type) in buf.
1868d582663Scgd 		 */
1878d582663Scgd 		for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
1888d582663Scgd 			continue;
1898d582663Scgd 		if (*cp != '\0')
1908d582663Scgd 			continue;
1918d582663Scgd 		if (*bp == '@')
192787fd082Sjnemeth 			return NULL;
1938d582663Scgd 		if (type == ':') {
1948d582663Scgd 			if (*bp != '\0' && *bp != ':')
1958d582663Scgd 				continue;
196787fd082Sjnemeth 			return bp;
1978d582663Scgd 		}
1988d582663Scgd 		if (*bp != type)
1998d582663Scgd 			continue;
2008d582663Scgd 		bp++;
201787fd082Sjnemeth 		return *bp == '@' ? NULL : bp;
2028d582663Scgd 	}
2038d582663Scgd 	/* NOTREACHED */
2048d582663Scgd }
2058d582663Scgd 
2068d582663Scgd /*
2078d582663Scgd  * Cgetent extracts the capability record name from the NULL terminated file
2088d582663Scgd  * array db_array and returns a pointer to a malloc'd copy of it in buf.
2098d582663Scgd  * Buf must be retained through all subsequent calls to cgetcap, cgetnum,
2108d582663Scgd  * cgetflag, and cgetstr, but may then be free'd.  0 is returned on success,
2118d582663Scgd  * -1 if the requested record couldn't be found, -2 if a system error was
2128d582663Scgd  * encountered (couldn't open/read a file, etc.), and -3 if a potential
2138d582663Scgd  * reference loop is detected.
2148d582663Scgd  */
215e9427c25Schristos /* coverity[+alloc : arg-*0] */
2168d582663Scgd int
cgetent(char ** buf,const char * const * db_array,const char * name)2172bab6168Schristos cgetent(char **buf, const char * const *db_array, const char *name)
2188d582663Scgd {
219669c5beaSthorpej 	size_t dummy;
2208d582663Scgd 
221b48252f3Slukem 	_DIAGASSERT(buf != NULL);
222b48252f3Slukem 	_DIAGASSERT(db_array != NULL);
223b48252f3Slukem 	_DIAGASSERT(name != NULL);
224b48252f3Slukem 
225787fd082Sjnemeth 	return getent(buf, &dummy, db_array, -1, name, 0, NULL);
2268d582663Scgd }
2278d582663Scgd 
2280d149bc5Schristos void
csetexpandtc(int etc)2290d149bc5Schristos csetexpandtc(int etc)
2300d149bc5Schristos {
2310d149bc5Schristos 	expandtc = etc;
2320d149bc5Schristos }
2330d149bc5Schristos 
2348d582663Scgd /*
2358d582663Scgd  * Getent implements the functions of cgetent.  If fd is non-negative,
2368d582663Scgd  * *db_array has already been opened and fd is the open file descriptor.  We
2378d582663Scgd  * do this to save time and avoid using up file descriptors for tc=
2388d582663Scgd  * recursions.
2398d582663Scgd  *
2408d582663Scgd  * Getent returns the same success/failure codes as cgetent.  On success, a
2418d582663Scgd  * pointer to a malloc'ed capability record with all tc= capabilities fully
2428d582663Scgd  * expanded and its length (not including trailing ASCII NUL) are left in
2438d582663Scgd  * *cap and *len.
2448d582663Scgd  *
2458d582663Scgd  * Basic algorithm:
2468d582663Scgd  *	+ Allocate memory incrementally as needed in chunks of size BFRAG
2478d582663Scgd  *	  for capability buffer.
2488d582663Scgd  *	+ Recurse for each tc=name and interpolate result.  Stop when all
2498d582663Scgd  *	  names interpolated, a name can't be found, or depth exceeds
2508d582663Scgd  *	  MAX_RECURSION.
2518d582663Scgd  */
252e9427c25Schristos /* coverity[+alloc : arg-*0] */
2538d582663Scgd static int
getent(char ** cap,size_t * len,const char * const * db_array,int fd,const char * name,int depth,char * nfield)2542bab6168Schristos getent(char **cap, size_t *len, const char * const *db_array, int fd,
2552bab6168Schristos     const char *name, int depth, char *nfield)
2568d582663Scgd {
2570b1d0cb3Schristos 	char *record, *newrecord;
258787fd082Sjnemeth 	char *r_end, *rp;	/* pacify gcc */
2592bab6168Schristos 	const char * const *db_p;
260787fd082Sjnemeth 	int myfd, eof, foundit;
2618d582663Scgd 	int tc_not_resolved;
2628d582663Scgd 
263b48252f3Slukem 	_DIAGASSERT(cap != NULL);
264b48252f3Slukem 	_DIAGASSERT(len != NULL);
265b48252f3Slukem 	_DIAGASSERT(db_array != NULL);
266b48252f3Slukem 	/* fd may be -1 */
267b48252f3Slukem 	_DIAGASSERT(name != NULL);
268b48252f3Slukem 	/* nfield may be NULL */
269b48252f3Slukem 
270787fd082Sjnemeth 	myfd = 0;
271787fd082Sjnemeth 	rp = NULL;
272787fd082Sjnemeth 
2738d582663Scgd 	/*
2748d582663Scgd 	 * Return with ``loop detected'' error if we've recursed more than
2758d582663Scgd 	 * MAX_RECURSION times.
2768d582663Scgd 	 */
2778d582663Scgd 	if (depth > MAX_RECURSION)
278787fd082Sjnemeth 		return -3;
2798d582663Scgd 
2808d582663Scgd 	/*
2818d582663Scgd 	 * Check if we have a top record from cgetset().
2828d582663Scgd          */
2838d582663Scgd 	if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) {
2848d582663Scgd 		if ((record = malloc(topreclen + BFRAG)) == NULL) {
2858d582663Scgd 			errno = ENOMEM;
286787fd082Sjnemeth 			return -2;
2878d582663Scgd 		}
2889cd5492cSmrg 		(void)strcpy(record, toprec);	/* XXX: strcpy is safe */
2898d582663Scgd 		db_p = db_array;
2908d582663Scgd 		rp = record + topreclen + 1;
2918d582663Scgd 		r_end = rp + BFRAG;
2928d582663Scgd 		goto tc_exp;
2938d582663Scgd 	}
2948d582663Scgd 	/*
2958d582663Scgd 	 * Allocate first chunk of memory.
2968d582663Scgd 	 */
2978d582663Scgd 	if ((record = malloc(BFRAG)) == NULL) {
2988d582663Scgd 		errno = ENOMEM;
299787fd082Sjnemeth 		return -2;
3008d582663Scgd 	}
3018d582663Scgd 	r_end = record + BFRAG;
3028d582663Scgd 	foundit = 0;
3038d582663Scgd 	/*
3048d582663Scgd 	 * Loop through database array until finding the record.
3058d582663Scgd 	 */
3068d582663Scgd 
3078d582663Scgd 	for (db_p = db_array; *db_p != NULL; db_p++) {
3088d582663Scgd 		eof = 0;
3098d582663Scgd 
3108d582663Scgd 		/*
3118d582663Scgd 		 * Open database if not already open.
3128d582663Scgd 		 */
3138d582663Scgd 
3148d582663Scgd 		if (fd >= 0) {
31566105c37Skleink 			(void)lseek(fd, (off_t)0, SEEK_SET);
3168d582663Scgd 		} else {
3170b1d0cb3Schristos #ifndef SMALL
318321c9aefSchristos 			DB *capdbp;
319321c9aefSchristos 			char pbuf[MAXPATHLEN];
320321c9aefSchristos 			char *cbuf;
321321c9aefSchristos 			int retval;
322321c9aefSchristos 			size_t clen;
323321c9aefSchristos 
3248d582663Scgd 			(void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p);
3259a513d96Schristos 			if ((capdbp = dbopen(pbuf, O_RDONLY | O_CLOEXEC, 0,
3269a513d96Schristos 			    DB_HASH, 0)) != NULL) {
3278d582663Scgd 				free(record);
3288d582663Scgd 				retval = cdbget(capdbp, &record, name);
329eac55eb1Scgd 				if (retval < 0) {
330eac55eb1Scgd 					/* no record available */
331eac55eb1Scgd 					(void)capdbp->close(capdbp);
332787fd082Sjnemeth 					return retval;
3336fbe299bScgd 				}
334eac55eb1Scgd 				/* save the data; close frees it */
335eac55eb1Scgd 				clen = strlen(record);
336d7635ff0Sjnemeth 				if ((cbuf = malloc(clen + 1)) == NULL) {
337d7635ff0Sjnemeth 					(void)capdbp->close(capdbp);
338d7635ff0Sjnemeth 					errno = ENOMEM;
339787fd082Sjnemeth 					return -2;
340d7635ff0Sjnemeth 				}
34198b9f211Sperry 				memmove(cbuf, record, clen + 1);
3426fbe299bScgd 				if (capdbp->close(capdbp) < 0) {
343b48252f3Slukem 					int serrno = errno;
344b48252f3Slukem 
3456fbe299bScgd 					free(cbuf);
346b48252f3Slukem 					errno = serrno;
347787fd082Sjnemeth 					return -2;
3486fbe299bScgd 				}
3496fbe299bScgd 				*len = clen;
3506fbe299bScgd 				*cap = cbuf;
351787fd082Sjnemeth 				return retval;
3520b1d0cb3Schristos 			} else
3530b1d0cb3Schristos #endif
3540b1d0cb3Schristos 			{
3559a513d96Schristos 				fd = open(*db_p, O_RDONLY | O_CLOEXEC, 0);
3568d582663Scgd 				if (fd < 0) {
3578d582663Scgd 					/* No error on unfound file. */
3588d582663Scgd 					continue;
3598d582663Scgd 				}
3608d582663Scgd 				myfd = 1;
3618d582663Scgd 			}
3628d582663Scgd 		}
3638d582663Scgd 		/*
3648d582663Scgd 		 * Find the requested capability record ...
3658d582663Scgd 		 */
3668d582663Scgd 		{
3678d582663Scgd 		char buf[BUFSIZ];
368b3b504b5Smycroft 		char *b_end, *bp, *cp;
369b3b504b5Smycroft 		int c, slash;
3708d582663Scgd 
3718d582663Scgd 		/*
3728d582663Scgd 		 * Loop invariants:
3738d582663Scgd 		 *	There is always room for one more character in record.
3748d582663Scgd 		 *	R_end always points just past end of record.
3758d582663Scgd 		 *	Rp always points just past last character in record.
3768d582663Scgd 		 *	B_end always points just past last character in buf.
3778d582663Scgd 		 *	Bp always points at next character in buf.
378b3b504b5Smycroft 		 *	Cp remembers where the last colon was.
3798d582663Scgd 		 */
3808d582663Scgd 		b_end = buf;
3818d582663Scgd 		bp = buf;
382787fd082Sjnemeth 		cp = NULL;
383b3b504b5Smycroft 		slash = 0;
3848d582663Scgd 		for (;;) {
3858d582663Scgd 			/*
3868d582663Scgd 			 * Read in a line implementing (\, newline)
3878d582663Scgd 			 * line continuation.
3888d582663Scgd 			 */
3898d582663Scgd 			rp = record;
3908d582663Scgd 			for (;;) {
3918d582663Scgd 				if (bp >= b_end) {
392c5e820caSchristos 					ssize_t n;
3938d582663Scgd 
3948d582663Scgd 					n = read(fd, buf, sizeof(buf));
3958d582663Scgd 					if (n <= 0) {
3968d582663Scgd 						if (myfd)
3978d582663Scgd 							(void)close(fd);
3988d582663Scgd 						if (n < 0) {
399b48252f3Slukem 							int serrno = errno;
400b48252f3Slukem 
4018d582663Scgd 							free(record);
402b48252f3Slukem 							errno = serrno;
403787fd082Sjnemeth 							return -2;
4048d582663Scgd 						} else {
4058d582663Scgd 							fd = -1;
4068d582663Scgd 							eof = 1;
4078d582663Scgd 							break;
4088d582663Scgd 						}
4098d582663Scgd 					}
4108d582663Scgd 					b_end = buf+n;
4118d582663Scgd 					bp = buf;
4128d582663Scgd 				}
4138d582663Scgd 
4148d582663Scgd 				c = *bp++;
4158d582663Scgd 				if (c == '\n') {
416b3b504b5Smycroft 					if (slash) {
417b3b504b5Smycroft 						slash = 0;
4188d582663Scgd 						rp--;
4198d582663Scgd 						continue;
4208d582663Scgd 					} else
4218d582663Scgd 						break;
4228d582663Scgd 				}
423b3b504b5Smycroft 				if (slash) {
424b3b504b5Smycroft 					slash = 0;
425b3b504b5Smycroft 					cp = 0;
426b3b504b5Smycroft 				}
427b3b504b5Smycroft 				if (c == ':') {
428b3b504b5Smycroft 					/*
429b3b504b5Smycroft 					 * If the field was `empty' (i.e.
430b3b504b5Smycroft 					 * contained only white space), back up
431b3b504b5Smycroft 					 * to the colon (eliminating the
432b3b504b5Smycroft 					 * field).
433b3b504b5Smycroft 					 */
434787fd082Sjnemeth 					if (cp != NULL)
435b3b504b5Smycroft 						rp = cp;
436b3b504b5Smycroft 					else
437b3b504b5Smycroft 						cp = rp;
438b3b504b5Smycroft 				} else if (c == '\\') {
439b3b504b5Smycroft 					slash = 1;
440b3b504b5Smycroft 				} else if (c != ' ' && c != '\t') {
441b3b504b5Smycroft 					/*
442b3b504b5Smycroft 					 * Forget where the colon was, as this
443b3b504b5Smycroft 					 * is not an empty field.
444b3b504b5Smycroft 					 */
445b3b504b5Smycroft 					cp = 0;
446b3b504b5Smycroft 				}
4478d582663Scgd 				*rp++ = c;
4488d582663Scgd 
4498d582663Scgd 				/*
4508d582663Scgd 				 * Enforce loop invariant: if no room
4518d582663Scgd 				 * left in record buffer, try to get
4528d582663Scgd 				 * some more.
4538d582663Scgd 				 */
4548d582663Scgd 				if (rp >= r_end) {
455c5e820caSchristos 					ptrdiff_t pos;
4568d582663Scgd 					size_t newsize;
4578d582663Scgd 
4588d582663Scgd 					pos = rp - record;
4598d582663Scgd 					newsize = r_end - record + BFRAG;
46074d0ceb9Sitojun 					newrecord = realloc(record, newsize);
46174d0ceb9Sitojun 					if (newrecord == NULL) {
46274d0ceb9Sitojun 						free(record);
4638d582663Scgd 						if (myfd)
4648d582663Scgd 							(void)close(fd);
465b48252f3Slukem 						errno = ENOMEM;
466787fd082Sjnemeth 						return -2;
4678d582663Scgd 					}
46874d0ceb9Sitojun 					record = newrecord;
4698d582663Scgd 					r_end = record + newsize;
4708d582663Scgd 					rp = record + pos;
4718d582663Scgd 				}
4728d582663Scgd 			}
473b3b504b5Smycroft 			/* Eliminate any white space after the last colon. */
474b3b504b5Smycroft 			if (cp)
475b3b504b5Smycroft 				rp = cp + 1;
476b3b504b5Smycroft 			/* Loop invariant lets us do this. */
4778d582663Scgd 			*rp++ = '\0';
4788d582663Scgd 
4798d582663Scgd 			/*
4808d582663Scgd 			 * If encountered eof check next file.
4818d582663Scgd 			 */
4828d582663Scgd 			if (eof)
4838d582663Scgd 				break;
4848d582663Scgd 
4858d582663Scgd 			/*
4868d582663Scgd 			 * Toss blank lines and comments.
4878d582663Scgd 			 */
4888d582663Scgd 			if (*record == '\0' || *record == '#')
4898d582663Scgd 				continue;
4908d582663Scgd 
4918d582663Scgd 			/*
4928d582663Scgd 			 * See if this is the record we want ...
4938d582663Scgd 			 */
494787fd082Sjnemeth 			if (cgetmatch(record, name) == 0)
4958d582663Scgd 				if (nfield == NULL || !nfcmp(nfield, record)) {
4968d582663Scgd 					foundit = 1;
4978d582663Scgd 					break;	/* found it! */
4988d582663Scgd 				}
4998d582663Scgd 		}
5008d582663Scgd 		}
5018d582663Scgd 		if (foundit)
5028d582663Scgd 			break;
5038d582663Scgd 	}
5048d582663Scgd 
505*6f4b1613Sshm 	if (!foundit) {
506*6f4b1613Sshm 		free(record);
507787fd082Sjnemeth 		return -1;
508*6f4b1613Sshm 	}
5098d582663Scgd 
5108d582663Scgd 	/*
5118d582663Scgd 	 * Got the capability record, but now we have to expand all tc=name
5128d582663Scgd 	 * references in it ...
5138d582663Scgd 	 */
5140d149bc5Schristos tc_exp:
5150d149bc5Schristos 	tc_not_resolved = 0;
5160d149bc5Schristos 	if (expandtc) {
5174146d586Sperry 		char *newicap, *s;
5181ea84adfSperry 		size_t ilen, newilen;
519c5e820caSchristos 		int iret;
520c5e820caSchristos 		ptrdiff_t diff, tclen;
5218d582663Scgd 		char *icap, *scan, *tc, *tcstart, *tcend;
5228d582663Scgd 
5238d582663Scgd 		/*
5248d582663Scgd 		 * Loop invariants:
5258d582663Scgd 		 *	There is room for one more character in record.
5268d582663Scgd 		 *	R_end points just past end of record.
5278d582663Scgd 		 *	Rp points just past last character in record.
5288d582663Scgd 		 *	Scan points at remainder of record that needs to be
5298d582663Scgd 		 *	scanned for tc=name constructs.
5308d582663Scgd 		 */
5318d582663Scgd 		scan = record;
5328d582663Scgd 		for (;;) {
5338d582663Scgd 			if ((tc = cgetcap(scan, "tc", '=')) == NULL)
5348d582663Scgd 				break;
5358d582663Scgd 
5368d582663Scgd 			/*
5378d582663Scgd 			 * Find end of tc=name and stomp on the trailing `:'
5388d582663Scgd 			 * (if present) so we can use it to call ourselves.
5398d582663Scgd 			 */
5408d582663Scgd 			s = tc;
5418d582663Scgd 			for (;;)
5428d582663Scgd 				if (*s == '\0')
5438d582663Scgd 					break;
5448d582663Scgd 				else
5458d582663Scgd 					if (*s++ == ':') {
5468d582663Scgd 						*(s - 1) = '\0';
5478d582663Scgd 						break;
5488d582663Scgd 					}
5498d582663Scgd 			tcstart = tc - 3;
5508d582663Scgd 			tclen = s - tcstart;
5518d582663Scgd 			tcend = s;
5528d582663Scgd 
5538d582663Scgd 			iret = getent(&icap, &ilen, db_p, fd, tc, depth+1,
5548d582663Scgd 				      NULL);
5558d582663Scgd 			newicap = icap;		/* Put into a register. */
5568d582663Scgd 			newilen = ilen;
5578d582663Scgd 			if (iret != 0) {
5588d582663Scgd 				/* an error */
5598d582663Scgd 				if (iret < -1) {
5608d582663Scgd 					if (myfd)
5618d582663Scgd 						(void)close(fd);
5628d582663Scgd 					free(record);
563787fd082Sjnemeth 					return iret;
5648d582663Scgd 				}
5658d582663Scgd 				if (iret == 1)
5668d582663Scgd 					tc_not_resolved = 1;
5678d582663Scgd 				/* couldn't resolve tc */
5688d582663Scgd 				if (iret == -1) {
5698d582663Scgd 					*(s - 1) = ':';
5708d582663Scgd 					scan = s - 1;
5718d582663Scgd 					tc_not_resolved = 1;
5728d582663Scgd 					continue;
5738d582663Scgd 
5748d582663Scgd 				}
5758d582663Scgd 			}
5768d582663Scgd 			/* not interested in name field of tc'ed record */
5778d582663Scgd 			s = newicap;
5788d582663Scgd 			for (;;)
5798d582663Scgd 				if (*s == '\0')
5808d582663Scgd 					break;
581787fd082Sjnemeth 				else if (*s++ == ':')
5828d582663Scgd 					break;
5838d582663Scgd 			newilen -= s - newicap;
5848d582663Scgd 			newicap = s;
5858d582663Scgd 
5868d582663Scgd 			/* make sure interpolated record is `:'-terminated */
5878d582663Scgd 			s += newilen;
5888d582663Scgd 			if (*(s - 1) != ':') {
5898d582663Scgd 				*s = ':';	/* overwrite NUL with : */
5908d582663Scgd 				newilen++;
5918d582663Scgd 			}
5928d582663Scgd 
5938d582663Scgd 			/*
5948d582663Scgd 			 * Make sure there's enough room to insert the
5958d582663Scgd 			 * new record.
5968d582663Scgd 			 */
5978d582663Scgd 			diff = newilen - tclen;
5988d582663Scgd 			if (diff >= r_end - rp) {
599c5e820caSchristos 				ptrdiff_t pos, tcpos, tcposend;
6008d582663Scgd 				size_t newsize;
6018d582663Scgd 
6028d582663Scgd 				pos = rp - record;
6038d582663Scgd 				newsize = r_end - record + diff + BFRAG;
6048d582663Scgd 				tcpos = tcstart - record;
6058d582663Scgd 				tcposend = tcend - record;
60674d0ceb9Sitojun 				newrecord = realloc(record, newsize);
60774d0ceb9Sitojun 				if (newrecord == NULL) {
60874d0ceb9Sitojun 					free(record);
6098d582663Scgd 					if (myfd)
6108d582663Scgd 						(void)close(fd);
6118d582663Scgd 					free(icap);
612b48252f3Slukem 					errno = ENOMEM;
613787fd082Sjnemeth 					return -2;
6148d582663Scgd 				}
61574d0ceb9Sitojun 				record = newrecord;
6168d582663Scgd 				r_end = record + newsize;
6178d582663Scgd 				rp = record + pos;
6188d582663Scgd 				tcstart = record + tcpos;
6198d582663Scgd 				tcend = record + tcposend;
6208d582663Scgd 			}
6218d582663Scgd 
6228d582663Scgd 			/*
6238d582663Scgd 			 * Insert tc'ed record into our record.
6248d582663Scgd 			 */
6258d582663Scgd 			s = tcstart + newilen;
62698b9f211Sperry 			memmove(s, tcend,  (size_t)(rp - tcend));
62798b9f211Sperry 			memmove(tcstart, newicap, newilen);
6288d582663Scgd 			rp += diff;
6298d582663Scgd 			free(icap);
6308d582663Scgd 
6318d582663Scgd 			/*
6328d582663Scgd 			 * Start scan on `:' so next cgetcap works properly
6338d582663Scgd 			 * (cgetcap always skips first field).
6348d582663Scgd 			 */
6358d582663Scgd 			scan = s - 1;
6368d582663Scgd 		}
6378d582663Scgd 
6388d582663Scgd 	}
6398d582663Scgd 	/*
6408d582663Scgd 	 * Close file (if we opened it), give back any extra memory, and
6418d582663Scgd 	 * return capability, length and success.
6428d582663Scgd 	 */
6438d582663Scgd 	if (myfd)
6448d582663Scgd 		(void)close(fd);
6458d582663Scgd 	*len = rp - record - 1;	/* don't count NUL */
64674d0ceb9Sitojun 	if (r_end > rp) {
64774d0ceb9Sitojun 		if ((newrecord =
6488d582663Scgd 		     realloc(record, (size_t)(rp - record))) == NULL) {
64974d0ceb9Sitojun 			free(record);
6508d582663Scgd 			errno = ENOMEM;
651787fd082Sjnemeth 			return -2;
6528d582663Scgd 		}
65374d0ceb9Sitojun 		record = newrecord;
65474d0ceb9Sitojun 	}
6558d582663Scgd 
6568d582663Scgd 	*cap = record;
6578d582663Scgd 	if (tc_not_resolved)
658787fd082Sjnemeth 		return 1;
659787fd082Sjnemeth 	return 0;
6608d582663Scgd }
6618d582663Scgd 
6620b1d0cb3Schristos #ifndef SMALL
6638d582663Scgd static int
cdbget(DB * capdbp,char ** bp,const char * name)6642bab6168Schristos cdbget(DB *capdbp, char **bp, const char *name)
6658d582663Scgd {
666920a51d9Schristos 	DBT key;
667ce52ab49Schristos 	DBT data;
6688d582663Scgd 
669b48252f3Slukem 	_DIAGASSERT(capdbp != NULL);
670b48252f3Slukem 	_DIAGASSERT(bp != NULL);
671b48252f3Slukem 	_DIAGASSERT(name != NULL);
672b48252f3Slukem 
67303256c6eSchristos 	key.data = __UNCONST(name);
6748d582663Scgd 	key.size = strlen(name);
6758d582663Scgd 
6768d582663Scgd 	for (;;) {
6778d582663Scgd 		/* Get the reference. */
6788d582663Scgd 		switch(capdbp->get(capdbp, &key, &data, 0)) {
6798d582663Scgd 		case -1:
680787fd082Sjnemeth 			return -2;
6818d582663Scgd 		case 1:
682787fd082Sjnemeth 			return -1;
6838d582663Scgd 		}
6848d582663Scgd 
6858d582663Scgd 		/* If not an index to another record, leave. */
6868d582663Scgd 		if (((char *)data.data)[0] != SHADOW)
6878d582663Scgd 			break;
6888d582663Scgd 
6898d582663Scgd 		key.data = (char *)data.data + 1;
6908d582663Scgd 		key.size = data.size - 1;
6918d582663Scgd 	}
6928d582663Scgd 
6938d582663Scgd 	*bp = (char *)data.data + 1;
694787fd082Sjnemeth 	return ((char *)(data.data))[0] == TCERR ? 1 : 0;
6958d582663Scgd }
6960b1d0cb3Schristos #endif
6978d582663Scgd 
6988d582663Scgd /*
6998d582663Scgd  * Cgetmatch will return 0 if name is one of the names of the capability
7008d582663Scgd  * record buf, -1 if not.
7018d582663Scgd  */
7028d582663Scgd int
cgetmatch(const char * buf,const char * name)7032bab6168Schristos cgetmatch(const char *buf, const char *name)
7048d582663Scgd {
705f9f7e98dSmycroft 	const char *np, *bp;
7068d582663Scgd 
707b48252f3Slukem 	_DIAGASSERT(buf != NULL);
708b48252f3Slukem 	_DIAGASSERT(name != NULL);
709b48252f3Slukem 
7108d582663Scgd 	/*
7118d582663Scgd 	 * Start search at beginning of record.
7128d582663Scgd 	 */
7138d582663Scgd 	bp = buf;
7148d582663Scgd 	for (;;) {
7158d582663Scgd 		/*
7168d582663Scgd 		 * Try to match a record name.
7178d582663Scgd 		 */
7188d582663Scgd 		np = name;
7198d582663Scgd 		for (;;)
7203eeda4afSchristos 			if (*np == '\0') {
7218d582663Scgd 				if (*bp == '|' || *bp == ':' || *bp == '\0')
722787fd082Sjnemeth 					return 0;
7238d582663Scgd 				else
7248d582663Scgd 					break;
725787fd082Sjnemeth 			} else if (*bp++ != *np++)
7268d582663Scgd 				break;
7278d582663Scgd 
7288d582663Scgd 		/*
7298d582663Scgd 		 * Match failed, skip to next name in record.
7308d582663Scgd 		 */
7315812b2feSmrg 		if (bp > buf)
7328d582663Scgd 			bp--;	/* a '|' or ':' may have stopped the match */
7335812b2feSmrg 		else
734787fd082Sjnemeth 			return -1;
7358d582663Scgd 		for (;;)
7368d582663Scgd 			if (*bp == '\0' || *bp == ':')
737787fd082Sjnemeth 				return -1;	/* match failed totally */
738787fd082Sjnemeth 			else if (*bp++ == '|')
7398d582663Scgd 				break;		/* found next name */
7408d582663Scgd 	}
7418d582663Scgd }
7428d582663Scgd 
7438d582663Scgd int
cgetfirst(char ** buf,const char * const * db_array)7442bab6168Schristos cgetfirst(char **buf, const char * const *db_array)
7458d582663Scgd {
746b48252f3Slukem 
747b48252f3Slukem 	_DIAGASSERT(buf != NULL);
748b48252f3Slukem 	_DIAGASSERT(db_array != NULL);
749b48252f3Slukem 
7508d582663Scgd 	(void)cgetclose();
751787fd082Sjnemeth 	return cgetnext(buf, db_array);
7528d582663Scgd }
7538d582663Scgd 
7548d582663Scgd static FILE *pfp;
7558d582663Scgd static int slash;
7562bab6168Schristos static const char * const *dbp;
7578d582663Scgd 
7588d582663Scgd int
cgetclose(void)7592bab6168Schristos cgetclose(void)
7608d582663Scgd {
7618d582663Scgd 	if (pfp != NULL) {
7628d582663Scgd 		(void)fclose(pfp);
7638d582663Scgd 		pfp = NULL;
7648d582663Scgd 	}
7658d582663Scgd 	dbp = NULL;
7668d582663Scgd 	gottoprec = 0;
7678d582663Scgd 	slash = 0;
768787fd082Sjnemeth 	return 0;
7698d582663Scgd }
7708d582663Scgd 
7718d582663Scgd /*
7728d582663Scgd  * Cgetnext() gets either the first or next entry in the logical database
7738d582663Scgd  * specified by db_array.  It returns 0 upon completion of the database, 1
7748d582663Scgd  * upon returning an entry with more remaining, and -1 if an error occurs.
7758d582663Scgd  */
776e9427c25Schristos /* coverity[+alloc : arg-*0] */
7778d582663Scgd int
cgetnext(char ** bp,const char * const * db_array)7782bab6168Schristos cgetnext(char **bp, const char * const *db_array)
7798d582663Scgd {
78010a8cb0eSchristos 	size_t len = 0;
7811ea84adfSperry 	int status, done;
7828d582663Scgd 	char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE];
783669c5beaSthorpej 	size_t dummy;
7848d582663Scgd 
785b48252f3Slukem 	_DIAGASSERT(bp != NULL);
786b48252f3Slukem 	_DIAGASSERT(db_array != NULL);
787b48252f3Slukem 
7888d582663Scgd 	if (dbp == NULL)
7898d582663Scgd 		dbp = db_array;
7908d582663Scgd 
7919292cfb2Schristos 	if (pfp == NULL && (pfp = fopen(*dbp, "re")) == NULL) {
7928d582663Scgd 		(void)cgetclose();
793787fd082Sjnemeth 		return -1;
7948d582663Scgd 	}
7958d582663Scgd 	for (;;) {
796787fd082Sjnemeth 		if (toprec != NULL && !gottoprec) {
7978d582663Scgd 			gottoprec = 1;
7988d582663Scgd 			line = toprec;
7998d582663Scgd 		} else {
800f45b975fScgd 			line = fgetln(pfp, &len);
801dfd5a336Schristos 			if (line == NULL) {
802dfd5a336Schristos 				if (pfp == NULL)
803dfd5a336Schristos 					return -1;
8048d582663Scgd 				if (ferror(pfp)) {
8058d582663Scgd 					(void)cgetclose();
806787fd082Sjnemeth 					return -1;
8078d582663Scgd 				} else {
808902d175aStv 					(void)fclose(pfp);
809902d175aStv 					pfp = NULL;
8108d582663Scgd 					if (*++dbp == NULL) {
8118d582663Scgd 						(void)cgetclose();
812787fd082Sjnemeth 						return 0;
8138d582663Scgd 					} else if ((pfp =
8149292cfb2Schristos 					    fopen(*dbp, "re")) == NULL) {
8158d582663Scgd 						(void)cgetclose();
816787fd082Sjnemeth 						return -1;
8178d582663Scgd 					} else
8188d582663Scgd 						continue;
8198d582663Scgd 				}
8206039a60bScgd 			} else
8216039a60bScgd 				line[len - 1] = '\0';
8226039a60bScgd 			if (len == 1) {
8238d582663Scgd 				slash = 0;
8248d582663Scgd 				continue;
8258d582663Scgd 			}
8263eeda4afSchristos 			if (isspace((unsigned char)*line) ||
8278d582663Scgd 			    *line == ':' || *line == '#' || slash) {
8286039a60bScgd 				if (line[len - 2] == '\\')
8298d582663Scgd 					slash = 1;
8308d582663Scgd 				else
8318d582663Scgd 					slash = 0;
8328d582663Scgd 				continue;
8338d582663Scgd 			}
8346039a60bScgd 			if (line[len - 2] == '\\')
8358d582663Scgd 				slash = 1;
8368d582663Scgd 			else
8378d582663Scgd 				slash = 0;
8388d582663Scgd 		}
8398d582663Scgd 
8408d582663Scgd 
8418d582663Scgd 		/*
8428d582663Scgd 		 * Line points to a name line.
8438d582663Scgd 		 */
844f74c26e4Sgroo 		if (len > sizeof(nbuf))
845f74c26e4Sgroo 			return -1;
8468d582663Scgd 		done = 0;
8478d582663Scgd 		np = nbuf;
8488d582663Scgd 		for (;;) {
8498d582663Scgd 			for (cp = line; *cp != '\0'; cp++) {
8508d582663Scgd 				if (*cp == ':') {
8518d582663Scgd 					*np++ = ':';
8528d582663Scgd 					done = 1;
8538d582663Scgd 					break;
8548d582663Scgd 				}
8558d582663Scgd 				if (*cp == '\\')
8568d582663Scgd 					break;
8578d582663Scgd 				*np++ = *cp;
8588d582663Scgd 			}
8598d582663Scgd 			if (done) {
8608d582663Scgd 				*np = '\0';
8618d582663Scgd 				break;
8628d582663Scgd 			} else { /* name field extends beyond the line */
863f45b975fScgd 				line = fgetln(pfp, &len);
8648d582663Scgd 				if (line == NULL && pfp) {
8658d582663Scgd 					if (ferror(pfp)) {
8668d582663Scgd 						(void)cgetclose();
867787fd082Sjnemeth 						return -1;
8688d582663Scgd 					}
869902d175aStv 					(void)fclose(pfp);
870902d175aStv 					pfp = NULL;
871902d175aStv 					*np = '\0';
872902d175aStv 					break;
8736039a60bScgd 				} else
8746039a60bScgd 					line[len - 1] = '\0';
8758d582663Scgd 			}
8768d582663Scgd 		}
877f74c26e4Sgroo 		if (len > sizeof(buf))
878f74c26e4Sgroo 			return -1;
8798d582663Scgd 		rp = buf;
88058efb9d2Spk 		for (cp = nbuf; *cp != '\0'; cp++)
8818d582663Scgd 			if (*cp == '|' || *cp == ':')
8828d582663Scgd 				break;
8838d582663Scgd 			else
8848d582663Scgd 				*rp++ = *cp;
8858d582663Scgd 
8868d582663Scgd 		*rp = '\0';
8878d582663Scgd 		/*
8888d582663Scgd 		 * XXX
8898d582663Scgd 		 * Last argument of getent here should be nbuf if we want true
8908d582663Scgd 		 * sequential access in the case of duplicates.
8918d582663Scgd 		 * With NULL, getent will return the first entry found
8928d582663Scgd 		 * rather than the duplicate entry record.  This is a
8938d582663Scgd 		 * matter of semantics that should be resolved.
8948d582663Scgd 		 */
8958d582663Scgd 		status = getent(bp, &dummy, db_array, -1, buf, 0, NULL);
8968d582663Scgd 		if (status == -2 || status == -3)
8978d582663Scgd 			(void)cgetclose();
8988d582663Scgd 
899787fd082Sjnemeth 		return status + 1;
9008d582663Scgd 	}
9018d582663Scgd 	/* NOTREACHED */
9028d582663Scgd }
9038d582663Scgd 
9048d582663Scgd /*
9058d582663Scgd  * Cgetstr retrieves the value of the string capability cap from the
9068d582663Scgd  * capability record pointed to by buf.  A pointer to a decoded, NUL
9078d582663Scgd  * terminated, malloc'd copy of the string is returned in the char *
9088d582663Scgd  * pointed to by str.  The length of the string not including the trailing
9098d582663Scgd  * NUL is returned on success, -1 if the requested string capability
9108d582663Scgd  * couldn't be found, -2 if a system error was encountered (storage
9118d582663Scgd  * allocation failure).
9128d582663Scgd  */
9138d582663Scgd int
cgetstr(char * buf,const char * cap,char ** str)9142bab6168Schristos cgetstr(char *buf, const char *cap, char **str)
9158d582663Scgd {
9164146d586Sperry 	u_int m_room;
917f9f7e98dSmycroft 	const char *bp;
918f9f7e98dSmycroft 	char *mp;
919c5e820caSchristos 	ptrdiff_t len;
92074d0ceb9Sitojun 	char *mem, *newmem;
9218d582663Scgd 
922b48252f3Slukem 	_DIAGASSERT(buf != NULL);
923b48252f3Slukem 	_DIAGASSERT(cap != NULL);
924b48252f3Slukem 	_DIAGASSERT(str != NULL);
925b48252f3Slukem 
9268d582663Scgd 	/*
9278d582663Scgd 	 * Find string capability cap
9288d582663Scgd 	 */
9298d582663Scgd 	bp = cgetcap(buf, cap, '=');
9308d582663Scgd 	if (bp == NULL)
931787fd082Sjnemeth 		return -1;
9328d582663Scgd 
9338d582663Scgd 	/*
9348d582663Scgd 	 * Conversion / storage allocation loop ...  Allocate memory in
9358d582663Scgd 	 * chunks SFRAG in size.
9368d582663Scgd 	 */
9378d582663Scgd 	if ((mem = malloc(SFRAG)) == NULL) {
9388d582663Scgd 		errno = ENOMEM;
939787fd082Sjnemeth 		return -2;	/* couldn't even allocate the first fragment */
9408d582663Scgd 	}
9418d582663Scgd 	m_room = SFRAG;
9428d582663Scgd 	mp = mem;
9438d582663Scgd 
9448d582663Scgd 	while (*bp != ':' && *bp != '\0') {
9458d582663Scgd 		/*
9468d582663Scgd 		 * Loop invariants:
9478d582663Scgd 		 *	There is always room for one more character in mem.
9488d582663Scgd 		 *	Mp always points just past last character in mem.
9498d582663Scgd 		 *	Bp always points at next character in buf.
9508d582663Scgd 		 */
9518d582663Scgd 		if (*bp == '^') {
9528d582663Scgd 			bp++;
9538d582663Scgd 			if (*bp == ':' || *bp == '\0')
9548d582663Scgd 				break;	/* drop unfinished escape */
9558d582663Scgd 			*mp++ = *bp++ & 037;
9568d582663Scgd 		} else if (*bp == '\\') {
9578d582663Scgd 			bp++;
9588d582663Scgd 			if (*bp == ':' || *bp == '\0')
9598d582663Scgd 				break;	/* drop unfinished escape */
9608d582663Scgd 			if ('0' <= *bp && *bp <= '7') {
9614146d586Sperry 				int n, i;
9628d582663Scgd 
9638d582663Scgd 				n = 0;
9648d582663Scgd 				i = 3;	/* maximum of three octal digits */
9658d582663Scgd 				do {
9668d582663Scgd 					n = n * 8 + (*bp++ - '0');
9678d582663Scgd 				} while (--i && '0' <= *bp && *bp <= '7');
9688d582663Scgd 				*mp++ = n;
9698d582663Scgd 			}
9708d582663Scgd 			else switch (*bp++) {
9718d582663Scgd 				case 'b': case 'B':
9728d582663Scgd 					*mp++ = '\b';
9738d582663Scgd 					break;
9748d582663Scgd 				case 't': case 'T':
9758d582663Scgd 					*mp++ = '\t';
9768d582663Scgd 					break;
9778d582663Scgd 				case 'n': case 'N':
9788d582663Scgd 					*mp++ = '\n';
9798d582663Scgd 					break;
9808d582663Scgd 				case 'f': case 'F':
9818d582663Scgd 					*mp++ = '\f';
9828d582663Scgd 					break;
9838d582663Scgd 				case 'r': case 'R':
9848d582663Scgd 					*mp++ = '\r';
9858d582663Scgd 					break;
9868d582663Scgd 				case 'e': case 'E':
9878d582663Scgd 					*mp++ = ESC;
9888d582663Scgd 					break;
9898d582663Scgd 				case 'c': case 'C':
9908d582663Scgd 					*mp++ = ':';
9918d582663Scgd 					break;
9928d582663Scgd 				default:
9938d582663Scgd 					/*
9948d582663Scgd 					 * Catches '\', '^', and
9958d582663Scgd 					 *  everything else.
9968d582663Scgd 					 */
9978d582663Scgd 					*mp++ = *(bp-1);
9988d582663Scgd 					break;
9998d582663Scgd 			}
10008d582663Scgd 		} else
10018d582663Scgd 			*mp++ = *bp++;
10028d582663Scgd 		m_room--;
10038d582663Scgd 
10048d582663Scgd 		/*
10058d582663Scgd 		 * Enforce loop invariant: if no room left in current
10068d582663Scgd 		 * buffer, try to get some more.
10078d582663Scgd 		 */
10088d582663Scgd 		if (m_room == 0) {
10098d582663Scgd 			size_t size = mp - mem;
10108d582663Scgd 
101174d0ceb9Sitojun 			if ((newmem = realloc(mem, size + SFRAG)) == NULL) {
101274d0ceb9Sitojun 				free(mem);
1013787fd082Sjnemeth 				return -2;
101474d0ceb9Sitojun 			}
101574d0ceb9Sitojun 			mem = newmem;
10168d582663Scgd 			m_room = SFRAG;
10178d582663Scgd 			mp = mem + size;
10188d582663Scgd 		}
10198d582663Scgd 	}
10208d582663Scgd 	*mp++ = '\0';	/* loop invariant let's us do this */
10218d582663Scgd 	m_room--;
10228d582663Scgd 	len = mp - mem - 1;
10238d582663Scgd 
10248d582663Scgd 	/*
10258d582663Scgd 	 * Give back any extra memory and return value and success.
10268d582663Scgd 	 */
102774d0ceb9Sitojun 	if (m_room != 0) {
102874d0ceb9Sitojun 		if ((newmem = realloc(mem, (size_t)(mp - mem))) == NULL) {
102974d0ceb9Sitojun 			free(mem);
1030787fd082Sjnemeth 			return -2;
103174d0ceb9Sitojun 		}
103274d0ceb9Sitojun 		mem = newmem;
103374d0ceb9Sitojun 	}
10348d582663Scgd 	*str = mem;
1035c5e820caSchristos 	_DIAGASSERT(__type_fit(int, len));
1036c5e820caSchristos 	return (int)len;
10378d582663Scgd }
10388d582663Scgd 
10398d582663Scgd /*
10408d582663Scgd  * Cgetustr retrieves the value of the string capability cap from the
10418d582663Scgd  * capability record pointed to by buf.  The difference between cgetustr()
10428d582663Scgd  * and cgetstr() is that cgetustr does not decode escapes but rather treats
10438d582663Scgd  * all characters literally.  A pointer to a  NUL terminated malloc'd
10448d582663Scgd  * copy of the string is returned in the char pointed to by str.  The
10458d582663Scgd  * length of the string not including the trailing NUL is returned on success,
10468d582663Scgd  * -1 if the requested string capability couldn't be found, -2 if a system
10478d582663Scgd  * error was encountered (storage allocation failure).
10488d582663Scgd  */
10498d582663Scgd int
cgetustr(char * buf,const char * cap,char ** str)10502bab6168Schristos cgetustr(char *buf, const char *cap, char **str)
10518d582663Scgd {
10524146d586Sperry 	u_int m_room;
1053f9f7e98dSmycroft 	const char *bp;
1054f9f7e98dSmycroft 	char *mp;
1055c5e820caSchristos 	size_t len;
105674d0ceb9Sitojun 	char *mem, *newmem;
10578d582663Scgd 
1058b48252f3Slukem 	_DIAGASSERT(buf != NULL);
1059b48252f3Slukem 	_DIAGASSERT(cap != NULL);
1060b48252f3Slukem 	_DIAGASSERT(str != NULL);
1061b48252f3Slukem 
10628d582663Scgd 	/*
10638d582663Scgd 	 * Find string capability cap
10648d582663Scgd 	 */
10658d582663Scgd 	if ((bp = cgetcap(buf, cap, '=')) == NULL)
1066787fd082Sjnemeth 		return -1;
10678d582663Scgd 
10688d582663Scgd 	/*
10698d582663Scgd 	 * Conversion / storage allocation loop ...  Allocate memory in
10708d582663Scgd 	 * chunks SFRAG in size.
10718d582663Scgd 	 */
10728d582663Scgd 	if ((mem = malloc(SFRAG)) == NULL) {
10738d582663Scgd 		errno = ENOMEM;
1074787fd082Sjnemeth 		return -2;	/* couldn't even allocate the first fragment */
10758d582663Scgd 	}
10768d582663Scgd 	m_room = SFRAG;
10778d582663Scgd 	mp = mem;
10788d582663Scgd 
10798d582663Scgd 	while (*bp != ':' && *bp != '\0') {
10808d582663Scgd 		/*
10818d582663Scgd 		 * Loop invariants:
10828d582663Scgd 		 *	There is always room for one more character in mem.
10838d582663Scgd 		 *	Mp always points just past last character in mem.
10848d582663Scgd 		 *	Bp always points at next character in buf.
10858d582663Scgd 		 */
10868d582663Scgd 		*mp++ = *bp++;
10878d582663Scgd 		m_room--;
10888d582663Scgd 
10898d582663Scgd 		/*
10908d582663Scgd 		 * Enforce loop invariant: if no room left in current
10918d582663Scgd 		 * buffer, try to get some more.
10928d582663Scgd 		 */
10938d582663Scgd 		if (m_room == 0) {
10948d582663Scgd 			size_t size = mp - mem;
10958d582663Scgd 
109674d0ceb9Sitojun 			if ((newmem = realloc(mem, size + SFRAG)) == NULL) {
109774d0ceb9Sitojun 				free(mem);
1098787fd082Sjnemeth 				return -2;
109974d0ceb9Sitojun 			}
110074d0ceb9Sitojun 			mem = newmem;
11018d582663Scgd 			m_room = SFRAG;
11028d582663Scgd 			mp = mem + size;
11038d582663Scgd 		}
11048d582663Scgd 	}
11058d582663Scgd 	*mp++ = '\0';	/* loop invariant let's us do this */
11068d582663Scgd 	m_room--;
11078d582663Scgd 	len = mp - mem - 1;
11088d582663Scgd 
11098d582663Scgd 	/*
11108d582663Scgd 	 * Give back any extra memory and return value and success.
11118d582663Scgd 	 */
111274d0ceb9Sitojun 	if (m_room != 0) {
111374d0ceb9Sitojun 		if ((newmem = realloc(mem, (size_t)(mp - mem))) == NULL) {
111474d0ceb9Sitojun 			free(mem);
1115787fd082Sjnemeth 			return -2;
111674d0ceb9Sitojun 		}
111774d0ceb9Sitojun 		mem = newmem;
111874d0ceb9Sitojun 	}
11198d582663Scgd 	*str = mem;
1120c5e820caSchristos 	_DIAGASSERT(__type_fit(int, len));
1121c5e820caSchristos 	return (int)len;
11228d582663Scgd }
11238d582663Scgd 
11248d582663Scgd /*
11258d582663Scgd  * Cgetnum retrieves the value of the numeric capability cap from the
11268d582663Scgd  * capability record pointed to by buf.  The numeric value is returned in
11278d582663Scgd  * the long pointed to by num.  0 is returned on success, -1 if the requested
11288d582663Scgd  * numeric capability couldn't be found.
11298d582663Scgd  */
11308d582663Scgd int
cgetnum(char * buf,const char * cap,long * num)11312bab6168Schristos cgetnum(char *buf, const char *cap, long *num)
11328d582663Scgd {
11334146d586Sperry 	long n;
11344146d586Sperry 	int base, digit;
1135f9f7e98dSmycroft 	const char *bp;
11368d582663Scgd 
1137b48252f3Slukem 	_DIAGASSERT(buf != NULL);
1138b48252f3Slukem 	_DIAGASSERT(cap != NULL);
1139b48252f3Slukem 	_DIAGASSERT(num != NULL);
1140b48252f3Slukem 
11418d582663Scgd 	/*
11428d582663Scgd 	 * Find numeric capability cap
11438d582663Scgd 	 */
11448d582663Scgd 	bp = cgetcap(buf, cap, '#');
11458d582663Scgd 	if (bp == NULL)
1146787fd082Sjnemeth 		return -1;
11478d582663Scgd 
11488d582663Scgd 	/*
11498d582663Scgd 	 * Look at value and determine numeric base:
11508d582663Scgd 	 *	0x... or 0X...	hexadecimal,
11518d582663Scgd 	 * else	0...		octal,
11528d582663Scgd 	 * else			decimal.
11538d582663Scgd 	 */
11548d582663Scgd 	if (*bp == '0') {
11558d582663Scgd 		bp++;
11568d582663Scgd 		if (*bp == 'x' || *bp == 'X') {
11578d582663Scgd 			bp++;
11588d582663Scgd 			base = 16;
11598d582663Scgd 		} else
11608d582663Scgd 			base = 8;
11618d582663Scgd 	} else
11628d582663Scgd 		base = 10;
11638d582663Scgd 
11648d582663Scgd 	/*
11658d582663Scgd 	 * Conversion loop ...
11668d582663Scgd 	 */
11678d582663Scgd 	n = 0;
11688d582663Scgd 	for (;;) {
11698d582663Scgd 		if ('0' <= *bp && *bp <= '9')
11708d582663Scgd 			digit = *bp - '0';
11718d582663Scgd 		else if ('a' <= *bp && *bp <= 'f')
11728d582663Scgd 			digit = 10 + *bp - 'a';
11738d582663Scgd 		else if ('A' <= *bp && *bp <= 'F')
11748d582663Scgd 			digit = 10 + *bp - 'A';
11758d582663Scgd 		else
11768d582663Scgd 			break;
11778d582663Scgd 
11788d582663Scgd 		if (digit >= base)
11798d582663Scgd 			break;
11808d582663Scgd 
11818d582663Scgd 		n = n * base + digit;
11828d582663Scgd 		bp++;
11838d582663Scgd 	}
11848d582663Scgd 
11858d582663Scgd 	/*
11868d582663Scgd 	 * Return value and success.
11878d582663Scgd 	 */
11888d582663Scgd 	*num = n;
1189787fd082Sjnemeth 	return 0;
11908d582663Scgd }
11918d582663Scgd 
11928d582663Scgd 
11938d582663Scgd /*
11948d582663Scgd  * Compare name field of record.
11958d582663Scgd  */
11968d582663Scgd static int
nfcmp(char * nf,char * rec)11972bab6168Schristos nfcmp(char *nf, char *rec)
11988d582663Scgd {
11998d582663Scgd 	char *cp, tmp;
12008d582663Scgd 	int ret;
12018d582663Scgd 
1202b48252f3Slukem 	_DIAGASSERT(nf != NULL);
1203b48252f3Slukem 	_DIAGASSERT(rec != NULL);
1204b48252f3Slukem 
12058d582663Scgd 	for (cp = rec; *cp != ':'; cp++)
1206787fd082Sjnemeth 		continue;
12078d582663Scgd 
12088d582663Scgd 	tmp = *(cp + 1);
12098d582663Scgd 	*(cp + 1) = '\0';
12108d582663Scgd 	ret = strcmp(nf, rec);
12118d582663Scgd 	*(cp + 1) = tmp;
12128d582663Scgd 
1213787fd082Sjnemeth 	return ret;
12148d582663Scgd }
1215