getcap.c revision 108312
11573Srgrimes/*-
21573Srgrimes * Copyright (c) 1992, 1993
31573Srgrimes *	The Regents of the University of California.  All rights reserved.
41573Srgrimes *
51573Srgrimes * This code is derived from software contributed to Berkeley by
61573Srgrimes * Casey Leedom of Lawrence Livermore National Laboratory.
71573Srgrimes *
81573Srgrimes * Redistribution and use in source and binary forms, with or without
91573Srgrimes * modification, are permitted provided that the following conditions
101573Srgrimes * are met:
111573Srgrimes * 1. Redistributions of source code must retain the above copyright
121573Srgrimes *    notice, this list of conditions and the following disclaimer.
131573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141573Srgrimes *    notice, this list of conditions and the following disclaimer in the
151573Srgrimes *    documentation and/or other materials provided with the distribution.
161573Srgrimes * 3. All advertising materials mentioning features or use of this software
171573Srgrimes *    must display the following acknowledgement:
181573Srgrimes *	This product includes software developed by the University of
191573Srgrimes *	California, Berkeley and its contributors.
201573Srgrimes * 4. Neither the name of the University nor the names of its contributors
211573Srgrimes *    may be used to endorse or promote products derived from this software
221573Srgrimes *    without specific prior written permission.
231573Srgrimes *
241573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341573Srgrimes * SUCH DAMAGE.
351573Srgrimes */
361573Srgrimes
371573Srgrimes#if defined(LIBC_SCCS) && !defined(lint)
381573Srgrimesstatic char sccsid[] = "@(#)getcap.c	8.3 (Berkeley) 3/25/94";
391573Srgrimes#endif /* LIBC_SCCS and not lint */
4090039Sobrien#include <sys/cdefs.h>
4190039Sobrien__FBSDID("$FreeBSD: head/lib/libc/gen/getcap.c 108312 2002-12-27 08:43:40Z alfred $");
421573Srgrimes
4371579Sdeischen#include "namespace.h"
441573Srgrimes#include <sys/types.h>
451573Srgrimes
461573Srgrimes#include <ctype.h>
478870Srgrimes#include <errno.h>
481573Srgrimes#include <fcntl.h>
491573Srgrimes#include <limits.h>
501573Srgrimes#include <stdio.h>
511573Srgrimes#include <stdlib.h>
521573Srgrimes#include <string.h>
531573Srgrimes#include <unistd.h>
5471579Sdeischen#include "un-namespace.h"
551573Srgrimes
5671579Sdeischen#include <db.h>
5771579Sdeischen
581573Srgrimes#define	BFRAG		1024
591573Srgrimes#define	BSIZE		1024
601573Srgrimes#define	ESC		('[' & 037)	/* ASCII ESC */
611573Srgrimes#define	MAX_RECURSION	32		/* maximum getent recursion */
621573Srgrimes#define	SFRAG		100		/* cgetstr mallocs in SFRAG chunks */
631573Srgrimes
641573Srgrimes#define RECOK	(char)0
651573Srgrimes#define TCERR	(char)1
661573Srgrimes#define	SHADOW	(char)2
671573Srgrimes
681573Srgrimesstatic size_t	 topreclen;	/* toprec length */
691573Srgrimesstatic char	*toprec;	/* Additional record specified by cgetset() */
701573Srgrimesstatic int	 gottoprec;	/* Flag indicating retrieval of toprecord */
711573Srgrimes
72108312Salfredstatic int	cdbget(DB *, char **, const char *);
7392941Sobrienstatic int 	getent(char **, u_int *, char **, int, const char *, int, char *);
7490039Sobrienstatic int	nfcmp(char *, char *);
751573Srgrimes
761573Srgrimes/*
771573Srgrimes * Cgetset() allows the addition of a user specified buffer to be added
781573Srgrimes * to the database array, in effect "pushing" the buffer on top of the
791573Srgrimes * virtual database. 0 is returned on success, -1 on failure.
801573Srgrimes */
811573Srgrimesint
8292925Simpcgetset(const char *ent)
831573Srgrimes{
841573Srgrimes	if (ent == NULL) {
851573Srgrimes		if (toprec)
861573Srgrimes			free(toprec);
871573Srgrimes                toprec = NULL;
881573Srgrimes                topreclen = 0;
891573Srgrimes                return (0);
901573Srgrimes        }
911573Srgrimes        topreclen = strlen(ent);
921573Srgrimes        if ((toprec = malloc (topreclen + 1)) == NULL) {
931573Srgrimes		errno = ENOMEM;
941573Srgrimes                return (-1);
951573Srgrimes	}
961573Srgrimes	gottoprec = 0;
971573Srgrimes        (void)strcpy(toprec, ent);
981573Srgrimes        return (0);
991573Srgrimes}
1001573Srgrimes
1011573Srgrimes/*
1021573Srgrimes * Cgetcap searches the capability record buf for the capability cap with
1031573Srgrimes * type `type'.  A pointer to the value of cap is returned on success, NULL
1041573Srgrimes * if the requested capability couldn't be found.
1051573Srgrimes *
1061573Srgrimes * Specifying a type of ':' means that nothing should follow cap (:cap:).
1071573Srgrimes * In this case a pointer to the terminating ':' or NUL will be returned if
1081573Srgrimes * cap is found.
1091573Srgrimes *
1101573Srgrimes * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
1111573Srgrimes * return NULL.
1121573Srgrimes */
1131573Srgrimeschar *
11492925Simpcgetcap(char *buf, const char *cap, int type)
1151573Srgrimes{
11692941Sobrien	char *bp;
11792941Sobrien	const char *cp;
1181573Srgrimes
1191573Srgrimes	bp = buf;
1201573Srgrimes	for (;;) {
1211573Srgrimes		/*
1221573Srgrimes		 * Skip past the current capability field - it's either the
1231573Srgrimes		 * name field if this is the first time through the loop, or
1241573Srgrimes		 * the remainder of a field whose name failed to match cap.
1251573Srgrimes		 */
1261573Srgrimes		for (;;)
1271573Srgrimes			if (*bp == '\0')
1281573Srgrimes				return (NULL);
1291573Srgrimes			else
1301573Srgrimes				if (*bp++ == ':')
1311573Srgrimes					break;
1321573Srgrimes
1331573Srgrimes		/*
1341573Srgrimes		 * Try to match (cap, type) in buf.
1351573Srgrimes		 */
1361573Srgrimes		for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
1371573Srgrimes			continue;
1381573Srgrimes		if (*cp != '\0')
1391573Srgrimes			continue;
1401573Srgrimes		if (*bp == '@')
1411573Srgrimes			return (NULL);
1421573Srgrimes		if (type == ':') {
1431573Srgrimes			if (*bp != '\0' && *bp != ':')
1441573Srgrimes				continue;
1451573Srgrimes			return(bp);
1461573Srgrimes		}
1471573Srgrimes		if (*bp != type)
1481573Srgrimes			continue;
1491573Srgrimes		bp++;
1501573Srgrimes		return (*bp == '@' ? NULL : bp);
1511573Srgrimes	}
1521573Srgrimes	/* NOTREACHED */
1531573Srgrimes}
1541573Srgrimes
1551573Srgrimes/*
1561573Srgrimes * Cgetent extracts the capability record name from the NULL terminated file
1571573Srgrimes * array db_array and returns a pointer to a malloc'd copy of it in buf.
1581573Srgrimes * Buf must be retained through all subsequent calls to cgetcap, cgetnum,
1591573Srgrimes * cgetflag, and cgetstr, but may then be free'd.  0 is returned on success,
1601573Srgrimes * -1 if the requested record couldn't be found, -2 if a system error was
1611573Srgrimes * encountered (couldn't open/read a file, etc.), and -3 if a potential
1621573Srgrimes * reference loop is detected.
1631573Srgrimes */
1641573Srgrimesint
16592925Simpcgetent(char **buf, char **db_array, const char *name)
1661573Srgrimes{
1671573Srgrimes	u_int dummy;
1681573Srgrimes
1691573Srgrimes	return (getent(buf, &dummy, db_array, -1, name, 0, NULL));
1701573Srgrimes}
1711573Srgrimes
1721573Srgrimes/*
1731573Srgrimes * Getent implements the functions of cgetent.  If fd is non-negative,
1741573Srgrimes * *db_array has already been opened and fd is the open file descriptor.  We
1751573Srgrimes * do this to save time and avoid using up file descriptors for tc=
1761573Srgrimes * recursions.
1771573Srgrimes *
1781573Srgrimes * Getent returns the same success/failure codes as cgetent.  On success, a
1791573Srgrimes * pointer to a malloc'ed capability record with all tc= capabilities fully
1801573Srgrimes * expanded and its length (not including trailing ASCII NUL) are left in
1811573Srgrimes * *cap and *len.
1821573Srgrimes *
1831573Srgrimes * Basic algorithm:
1841573Srgrimes *	+ Allocate memory incrementally as needed in chunks of size BFRAG
1851573Srgrimes *	  for capability buffer.
1861573Srgrimes *	+ Recurse for each tc=name and interpolate result.  Stop when all
1871573Srgrimes *	  names interpolated, a name can't be found, or depth exceeds
1881573Srgrimes *	  MAX_RECURSION.
1891573Srgrimes */
1901573Srgrimesstatic int
19192941Sobriengetent(char **cap, u_int *len, char **db_array, int fd, const char *name,
19292941Sobrien    int depth, char *nfield)
1931573Srgrimes{
1941573Srgrimes	DB *capdbp;
19590039Sobrien	char *r_end, *rp, **db_p;
1961573Srgrimes	int myfd, eof, foundit, retval, clen;
1971573Srgrimes	char *record, *cbuf;
1981573Srgrimes	int tc_not_resolved;
1991573Srgrimes	char pbuf[_POSIX_PATH_MAX];
2008870Srgrimes
2011573Srgrimes	/*
2021573Srgrimes	 * Return with ``loop detected'' error if we've recursed more than
2031573Srgrimes	 * MAX_RECURSION times.
2041573Srgrimes	 */
2051573Srgrimes	if (depth > MAX_RECURSION)
2061573Srgrimes		return (-3);
2071573Srgrimes
2081573Srgrimes	/*
2091573Srgrimes	 * Check if we have a top record from cgetset().
2101573Srgrimes         */
2111573Srgrimes	if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) {
2121573Srgrimes		if ((record = malloc (topreclen + BFRAG)) == NULL) {
2131573Srgrimes			errno = ENOMEM;
2141573Srgrimes			return (-2);
2151573Srgrimes		}
2161573Srgrimes		(void)strcpy(record, toprec);
2171573Srgrimes		myfd = 0;
2181573Srgrimes		db_p = db_array;
2191573Srgrimes		rp = record + topreclen + 1;
2201573Srgrimes		r_end = rp + BFRAG;
2211573Srgrimes		goto tc_exp;
2221573Srgrimes	}
2231573Srgrimes	/*
2241573Srgrimes	 * Allocate first chunk of memory.
2251573Srgrimes	 */
2261573Srgrimes	if ((record = malloc(BFRAG)) == NULL) {
2271573Srgrimes		errno = ENOMEM;
2281573Srgrimes		return (-2);
2291573Srgrimes	}
2301573Srgrimes	r_end = record + BFRAG;
2311573Srgrimes	foundit = 0;
2321573Srgrimes	/*
2331573Srgrimes	 * Loop through database array until finding the record.
2341573Srgrimes	 */
2351573Srgrimes
2361573Srgrimes	for (db_p = db_array; *db_p != NULL; db_p++) {
2371573Srgrimes		eof = 0;
2381573Srgrimes
2391573Srgrimes		/*
2401573Srgrimes		 * Open database if not already open.
2411573Srgrimes		 */
2421573Srgrimes
2431573Srgrimes		if (fd >= 0) {
24430713Sjdp			(void)lseek(fd, (off_t)0, SEEK_SET);
2451573Srgrimes			myfd = 0;
2461573Srgrimes		} else {
2471573Srgrimes			(void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p);
2481573Srgrimes			if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0))
2491573Srgrimes			     != NULL) {
2501573Srgrimes				free(record);
2511573Srgrimes				retval = cdbget(capdbp, &record, name);
2521573Srgrimes				if (retval < 0) {
2531573Srgrimes					/* no record available */
2541573Srgrimes					(void)capdbp->close(capdbp);
2551573Srgrimes					return (retval);
2561573Srgrimes				}
2571573Srgrimes				/* save the data; close frees it */
2581573Srgrimes				clen = strlen(record);
2591573Srgrimes				cbuf = malloc(clen + 1);
2601573Srgrimes				memcpy(cbuf, record, clen + 1);
2611573Srgrimes				if (capdbp->close(capdbp) < 0) {
2621573Srgrimes					free(cbuf);
2631573Srgrimes					return (-2);
2641573Srgrimes				}
2651573Srgrimes				*len = clen;
2661573Srgrimes				*cap = cbuf;
2671573Srgrimes				return (retval);
2681573Srgrimes			} else {
26956698Sjasone				fd = _open(*db_p, O_RDONLY, 0);
27044921Simp				if (fd < 0)
27144921Simp					continue;
2721573Srgrimes				myfd = 1;
2731573Srgrimes			}
2741573Srgrimes		}
2751573Srgrimes		/*
2761573Srgrimes		 * Find the requested capability record ...
2771573Srgrimes		 */
2781573Srgrimes		{
2791573Srgrimes		char buf[BUFSIZ];
28090039Sobrien		char *b_end, *bp;
28190039Sobrien		int c;
2821573Srgrimes
2831573Srgrimes		/*
2841573Srgrimes		 * Loop invariants:
2851573Srgrimes		 *	There is always room for one more character in record.
2861573Srgrimes		 *	R_end always points just past end of record.
2871573Srgrimes		 *	Rp always points just past last character in record.
2881573Srgrimes		 *	B_end always points just past last character in buf.
2891573Srgrimes		 *	Bp always points at next character in buf.
2901573Srgrimes		 */
2911573Srgrimes		b_end = buf;
2921573Srgrimes		bp = buf;
2931573Srgrimes		for (;;) {
2941573Srgrimes
2951573Srgrimes			/*
2961573Srgrimes			 * Read in a line implementing (\, newline)
2971573Srgrimes			 * line continuation.
2981573Srgrimes			 */
2991573Srgrimes			rp = record;
3001573Srgrimes			for (;;) {
3011573Srgrimes				if (bp >= b_end) {
3021573Srgrimes					int n;
3038870Srgrimes
30456698Sjasone					n = _read(fd, buf, sizeof(buf));
3051573Srgrimes					if (n <= 0) {
3061573Srgrimes						if (myfd)
30756698Sjasone							(void)_close(fd);
3081573Srgrimes						if (n < 0) {
3091573Srgrimes							free(record);
3101573Srgrimes							return (-2);
3111573Srgrimes						} else {
3121573Srgrimes							fd = -1;
3131573Srgrimes							eof = 1;
3141573Srgrimes							break;
3151573Srgrimes						}
3161573Srgrimes					}
3171573Srgrimes					b_end = buf+n;
3181573Srgrimes					bp = buf;
3191573Srgrimes				}
3208870Srgrimes
3211573Srgrimes				c = *bp++;
3221573Srgrimes				if (c == '\n') {
3231573Srgrimes					if (rp > record && *(rp-1) == '\\') {
3241573Srgrimes						rp--;
3251573Srgrimes						continue;
3261573Srgrimes					} else
3271573Srgrimes						break;
3281573Srgrimes				}
3291573Srgrimes				*rp++ = c;
3301573Srgrimes
3311573Srgrimes				/*
3328870Srgrimes				 * Enforce loop invariant: if no room
3331573Srgrimes				 * left in record buffer, try to get
3341573Srgrimes				 * some more.
3351573Srgrimes				 */
3361573Srgrimes				if (rp >= r_end) {
3371573Srgrimes					u_int pos;
3381573Srgrimes					size_t newsize;
3391573Srgrimes
3401573Srgrimes					pos = rp - record;
3411573Srgrimes					newsize = r_end - record + BFRAG;
34239327Simp					record = reallocf(record, newsize);
3431573Srgrimes					if (record == NULL) {
3441573Srgrimes						errno = ENOMEM;
3451573Srgrimes						if (myfd)
34656698Sjasone							(void)_close(fd);
3471573Srgrimes						return (-2);
3481573Srgrimes					}
3491573Srgrimes					r_end = record + newsize;
3501573Srgrimes					rp = record + pos;
3511573Srgrimes				}
3521573Srgrimes			}
3531573Srgrimes				/* loop invariant let's us do this */
3541573Srgrimes			*rp++ = '\0';
3551573Srgrimes
3561573Srgrimes			/*
3571573Srgrimes			 * If encountered eof check next file.
3581573Srgrimes			 */
3591573Srgrimes			if (eof)
3601573Srgrimes				break;
3618870Srgrimes
3621573Srgrimes			/*
3631573Srgrimes			 * Toss blank lines and comments.
3641573Srgrimes			 */
3651573Srgrimes			if (*record == '\0' || *record == '#')
3661573Srgrimes				continue;
3678870Srgrimes
3681573Srgrimes			/*
3691573Srgrimes			 * See if this is the record we want ...
3701573Srgrimes			 */
3711573Srgrimes			if (cgetmatch(record, name) == 0) {
3721573Srgrimes				if (nfield == NULL || !nfcmp(nfield, record)) {
3731573Srgrimes					foundit = 1;
3741573Srgrimes					break;	/* found it! */
3751573Srgrimes				}
3761573Srgrimes			}
3771573Srgrimes		}
3781573Srgrimes	}
3791573Srgrimes		if (foundit)
3801573Srgrimes			break;
3811573Srgrimes	}
3821573Srgrimes
38360747Shoek	if (!foundit) {
38460747Shoek		free(record);
3851573Srgrimes		return (-1);
38660747Shoek	}
3871573Srgrimes
3881573Srgrimes	/*
3891573Srgrimes	 * Got the capability record, but now we have to expand all tc=name
3901573Srgrimes	 * references in it ...
3911573Srgrimes	 */
3921573Srgrimestc_exp:	{
39390039Sobrien		char *newicap, *s;
39490039Sobrien		int newilen;
3951573Srgrimes		u_int ilen;
3961573Srgrimes		int diff, iret, tclen;
3971573Srgrimes		char *icap, *scan, *tc, *tcstart, *tcend;
3981573Srgrimes
3991573Srgrimes		/*
4001573Srgrimes		 * Loop invariants:
4011573Srgrimes		 *	There is room for one more character in record.
4021573Srgrimes		 *	R_end points just past end of record.
4031573Srgrimes		 *	Rp points just past last character in record.
4041573Srgrimes		 *	Scan points at remainder of record that needs to be
4051573Srgrimes		 *	scanned for tc=name constructs.
4061573Srgrimes		 */
4071573Srgrimes		scan = record;
4081573Srgrimes		tc_not_resolved = 0;
4091573Srgrimes		for (;;) {
4101573Srgrimes			if ((tc = cgetcap(scan, "tc", '=')) == NULL)
4111573Srgrimes				break;
4121573Srgrimes
4131573Srgrimes			/*
4141573Srgrimes			 * Find end of tc=name and stomp on the trailing `:'
4151573Srgrimes			 * (if present) so we can use it to call ourselves.
4161573Srgrimes			 */
4171573Srgrimes			s = tc;
4181573Srgrimes			for (;;)
4191573Srgrimes				if (*s == '\0')
4201573Srgrimes					break;
4211573Srgrimes				else
4221573Srgrimes					if (*s++ == ':') {
4231573Srgrimes						*(s - 1) = '\0';
4241573Srgrimes						break;
4251573Srgrimes					}
4261573Srgrimes			tcstart = tc - 3;
4271573Srgrimes			tclen = s - tcstart;
4281573Srgrimes			tcend = s;
4291573Srgrimes
4308870Srgrimes			iret = getent(&icap, &ilen, db_p, fd, tc, depth+1,
4311573Srgrimes				      NULL);
4321573Srgrimes			newicap = icap;		/* Put into a register. */
4331573Srgrimes			newilen = ilen;
4341573Srgrimes			if (iret != 0) {
4351573Srgrimes				/* an error */
4361573Srgrimes				if (iret < -1) {
4371573Srgrimes					if (myfd)
43856698Sjasone						(void)_close(fd);
4391573Srgrimes					free(record);
4401573Srgrimes					return (iret);
4411573Srgrimes				}
4421573Srgrimes				if (iret == 1)
4431573Srgrimes					tc_not_resolved = 1;
4441573Srgrimes				/* couldn't resolve tc */
4451573Srgrimes				if (iret == -1) {
4468870Srgrimes					*(s - 1) = ':';
4471573Srgrimes					scan = s - 1;
4481573Srgrimes					tc_not_resolved = 1;
4491573Srgrimes					continue;
4508870Srgrimes
4511573Srgrimes				}
4521573Srgrimes			}
4531573Srgrimes			/* not interested in name field of tc'ed record */
4541573Srgrimes			s = newicap;
4551573Srgrimes			for (;;)
4561573Srgrimes				if (*s == '\0')
4571573Srgrimes					break;
4581573Srgrimes				else
4591573Srgrimes					if (*s++ == ':')
4601573Srgrimes						break;
4611573Srgrimes			newilen -= s - newicap;
4621573Srgrimes			newicap = s;
4631573Srgrimes
4641573Srgrimes			/* make sure interpolated record is `:'-terminated */
4651573Srgrimes			s += newilen;
4661573Srgrimes			if (*(s-1) != ':') {
4671573Srgrimes				*s = ':';	/* overwrite NUL with : */
4681573Srgrimes				newilen++;
4691573Srgrimes			}
4701573Srgrimes
4711573Srgrimes			/*
4721573Srgrimes			 * Make sure there's enough room to insert the
4731573Srgrimes			 * new record.
4741573Srgrimes			 */
4751573Srgrimes			diff = newilen - tclen;
4761573Srgrimes			if (diff >= r_end - rp) {
4771573Srgrimes				u_int pos, tcpos, tcposend;
4781573Srgrimes				size_t newsize;
4791573Srgrimes
4801573Srgrimes				pos = rp - record;
4811573Srgrimes				newsize = r_end - record + diff + BFRAG;
4821573Srgrimes				tcpos = tcstart - record;
4831573Srgrimes				tcposend = tcend - record;
48439327Simp				record = reallocf(record, newsize);
4851573Srgrimes				if (record == NULL) {
4861573Srgrimes					errno = ENOMEM;
4871573Srgrimes					if (myfd)
48856698Sjasone						(void)_close(fd);
4891573Srgrimes					free(icap);
4901573Srgrimes					return (-2);
4911573Srgrimes				}
4921573Srgrimes				r_end = record + newsize;
4931573Srgrimes				rp = record + pos;
4941573Srgrimes				tcstart = record + tcpos;
4951573Srgrimes				tcend = record + tcposend;
4961573Srgrimes			}
4971573Srgrimes
4981573Srgrimes			/*
4991573Srgrimes			 * Insert tc'ed record into our record.
5001573Srgrimes			 */
5011573Srgrimes			s = tcstart + newilen;
5021573Srgrimes			bcopy(tcend, s, rp - tcend);
5031573Srgrimes			bcopy(newicap, tcstart, newilen);
5041573Srgrimes			rp += diff;
5051573Srgrimes			free(icap);
5061573Srgrimes
5071573Srgrimes			/*
5081573Srgrimes			 * Start scan on `:' so next cgetcap works properly
5091573Srgrimes			 * (cgetcap always skips first field).
5101573Srgrimes			 */
5111573Srgrimes			scan = s-1;
5121573Srgrimes		}
5138870Srgrimes
5141573Srgrimes	}
5151573Srgrimes	/*
5161573Srgrimes	 * Close file (if we opened it), give back any extra memory, and
5171573Srgrimes	 * return capability, length and success.
5181573Srgrimes	 */
5191573Srgrimes	if (myfd)
52056698Sjasone		(void)_close(fd);
5211573Srgrimes	*len = rp - record - 1;	/* don't count NUL */
5221573Srgrimes	if (r_end > rp)
5238870Srgrimes		if ((record =
52439327Simp		     reallocf(record, (size_t)(rp - record))) == NULL) {
5251573Srgrimes			errno = ENOMEM;
5261573Srgrimes			return (-2);
5271573Srgrimes		}
5288870Srgrimes
5291573Srgrimes	*cap = record;
5301573Srgrimes	if (tc_not_resolved)
5311573Srgrimes		return (1);
5321573Srgrimes	return (0);
5338870Srgrimes}
5341573Srgrimes
5351573Srgrimesstatic int
536108312Salfredcdbget(DB *capdbp, char **bp, const char *name)
5371573Srgrimes{
5381573Srgrimes	DBT key, data;
539108312Salfred	char *namebuf;
5401573Srgrimes
541108312Salfred	namebuf = strdup(name);
542108312Salfred	if (namebuf == NULL)
543108312Salfred		return (-2);
544108312Salfred	key.data = namebuf;
545108312Salfred	key.size = strlen(namebuf);
5461573Srgrimes
5471573Srgrimes	for (;;) {
5481573Srgrimes		/* Get the reference. */
5491573Srgrimes		switch(capdbp->get(capdbp, &key, &data, 0)) {
5501573Srgrimes		case -1:
551108312Salfred			free(namebuf);
5521573Srgrimes			return (-2);
5531573Srgrimes		case 1:
554108312Salfred			free(namebuf);
5551573Srgrimes			return (-1);
5561573Srgrimes		}
5571573Srgrimes
5581573Srgrimes		/* If not an index to another record, leave. */
5591573Srgrimes		if (((char *)data.data)[0] != SHADOW)
5601573Srgrimes			break;
5611573Srgrimes
5621573Srgrimes		key.data = (char *)data.data + 1;
5631573Srgrimes		key.size = data.size - 1;
5641573Srgrimes	}
5658870Srgrimes
5661573Srgrimes	*bp = (char *)data.data + 1;
567108312Salfred	free(namebuf);
5681573Srgrimes	return (((char *)(data.data))[0] == TCERR ? 1 : 0);
5691573Srgrimes}
5701573Srgrimes
5711573Srgrimes/*
5721573Srgrimes * Cgetmatch will return 0 if name is one of the names of the capability
5731573Srgrimes * record buf, -1 if not.
5741573Srgrimes */
5751573Srgrimesint
57692925Simpcgetmatch(const char *buf, const char *name)
5771573Srgrimes{
578108312Salfred	const char *np, *bp;
5791573Srgrimes
5801573Srgrimes	/*
5811573Srgrimes	 * Start search at beginning of record.
5821573Srgrimes	 */
5831573Srgrimes	bp = buf;
5841573Srgrimes	for (;;) {
5851573Srgrimes		/*
5861573Srgrimes		 * Try to match a record name.
5871573Srgrimes		 */
5881573Srgrimes		np = name;
5891573Srgrimes		for (;;)
5901573Srgrimes			if (*np == '\0')
5911573Srgrimes				if (*bp == '|' || *bp == ':' || *bp == '\0')
5921573Srgrimes					return (0);
5931573Srgrimes				else
5941573Srgrimes					break;
5951573Srgrimes			else
5961573Srgrimes				if (*bp++ != *np++)
5971573Srgrimes					break;
5981573Srgrimes
5991573Srgrimes		/*
6001573Srgrimes		 * Match failed, skip to next name in record.
6011573Srgrimes		 */
6021573Srgrimes		bp--;	/* a '|' or ':' may have stopped the match */
6031573Srgrimes		for (;;)
6041573Srgrimes			if (*bp == '\0' || *bp == ':')
6051573Srgrimes				return (-1);	/* match failed totally */
6061573Srgrimes			else
6071573Srgrimes				if (*bp++ == '|')
6081573Srgrimes					break;	/* found next name */
6091573Srgrimes	}
6101573Srgrimes}
6111573Srgrimes
6121573Srgrimes
6131573Srgrimes
6141573Srgrimes
6151573Srgrimes
6161573Srgrimesint
61792925Simpcgetfirst(char **buf, char **db_array)
6181573Srgrimes{
6191573Srgrimes	(void)cgetclose();
6201573Srgrimes	return (cgetnext(buf, db_array));
6211573Srgrimes}
6221573Srgrimes
6231573Srgrimesstatic FILE *pfp;
6241573Srgrimesstatic int slash;
6251573Srgrimesstatic char **dbp;
6261573Srgrimes
6271573Srgrimesint
62892925Simpcgetclose(void)
6291573Srgrimes{
6301573Srgrimes	if (pfp != NULL) {
6311573Srgrimes		(void)fclose(pfp);
6321573Srgrimes		pfp = NULL;
6331573Srgrimes	}
6341573Srgrimes	dbp = NULL;
6351573Srgrimes	gottoprec = 0;
6361573Srgrimes	slash = 0;
6371573Srgrimes	return(0);
6381573Srgrimes}
6391573Srgrimes
6401573Srgrimes/*
6418870Srgrimes * Cgetnext() gets either the first or next entry in the logical database
6421573Srgrimes * specified by db_array.  It returns 0 upon completion of the database, 1
6431573Srgrimes * upon returning an entry with more remaining, and -1 if an error occurs.
6441573Srgrimes */
6451573Srgrimesint
64692925Simpcgetnext(char **bp, char **db_array)
6471573Srgrimes{
6481573Srgrimes	size_t len;
64969502Sgad	int done, hadreaderr, i, savederrno, status;
6501573Srgrimes	char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE];
6511573Srgrimes	u_int dummy;
6521573Srgrimes
6531573Srgrimes	if (dbp == NULL)
6541573Srgrimes		dbp = db_array;
6551573Srgrimes
6561573Srgrimes	if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) {
6571573Srgrimes		(void)cgetclose();
6581573Srgrimes		return (-1);
6591573Srgrimes	}
6601573Srgrimes	for(;;) {
6611573Srgrimes		if (toprec && !gottoprec) {
6621573Srgrimes			gottoprec = 1;
6631573Srgrimes			line = toprec;
6641573Srgrimes		} else {
6651573Srgrimes			line = fgetln(pfp, &len);
6661573Srgrimes			if (line == NULL && pfp) {
66769502Sgad				hadreaderr = ferror(pfp);
66869502Sgad				if (hadreaderr)
66969502Sgad					savederrno = errno;
67069502Sgad				fclose(pfp);
67169502Sgad				pfp = NULL;
67269502Sgad				if (hadreaderr) {
67369502Sgad					cgetclose();
67469502Sgad					errno = savederrno;
6751573Srgrimes					return (-1);
6761573Srgrimes				} else {
6771573Srgrimes					if (*++dbp == NULL) {
6781573Srgrimes						(void)cgetclose();
6791573Srgrimes						return (0);
6801573Srgrimes					} else if ((pfp =
6811573Srgrimes					    fopen(*dbp, "r")) == NULL) {
6821573Srgrimes						(void)cgetclose();
6831573Srgrimes						return (-1);
6841573Srgrimes					} else
6851573Srgrimes						continue;
6861573Srgrimes				}
6871573Srgrimes			} else
6881573Srgrimes				line[len - 1] = '\0';
6891573Srgrimes			if (len == 1) {
6901573Srgrimes				slash = 0;
6911573Srgrimes				continue;
6921573Srgrimes			}
69352856Sache			if (isspace((unsigned char)*line) ||
6941573Srgrimes			    *line == ':' || *line == '#' || slash) {
6951573Srgrimes				if (line[len - 2] == '\\')
6961573Srgrimes					slash = 1;
6971573Srgrimes				else
6981573Srgrimes					slash = 0;
6991573Srgrimes				continue;
7001573Srgrimes			}
7011573Srgrimes			if (line[len - 2] == '\\')
7021573Srgrimes				slash = 1;
7031573Srgrimes			else
7041573Srgrimes				slash = 0;
7058870Srgrimes		}
7061573Srgrimes
7071573Srgrimes
7088870Srgrimes		/*
7091573Srgrimes		 * Line points to a name line.
7101573Srgrimes		 */
7111573Srgrimes		i = 0;
7121573Srgrimes		done = 0;
7131573Srgrimes		np = nbuf;
7141573Srgrimes		for (;;) {
7151573Srgrimes			for (cp = line; *cp != '\0'; cp++) {
7161573Srgrimes				if (*cp == ':') {
7171573Srgrimes					*np++ = ':';
7181573Srgrimes					done = 1;
7191573Srgrimes					break;
7201573Srgrimes				}
7211573Srgrimes				if (*cp == '\\')
7221573Srgrimes					break;
7231573Srgrimes				*np++ = *cp;
7241573Srgrimes			}
7251573Srgrimes			if (done) {
7261573Srgrimes				*np = '\0';
7271573Srgrimes				break;
7281573Srgrimes			} else { /* name field extends beyond the line */
7291573Srgrimes				line = fgetln(pfp, &len);
7301573Srgrimes				if (line == NULL && pfp) {
73169502Sgad					/* Name extends beyond the EOF! */
73269502Sgad					hadreaderr = ferror(pfp);
73369502Sgad					if (hadreaderr)
73469502Sgad						savederrno = errno;
73569502Sgad					fclose(pfp);
73669502Sgad					pfp = NULL;
73769502Sgad					if (hadreaderr) {
73869502Sgad						cgetclose();
73969502Sgad						errno = savederrno;
7401573Srgrimes						return (-1);
74169502Sgad					} else {
74269502Sgad						cgetclose();
74369502Sgad						return (-1);
7441573Srgrimes					}
7451573Srgrimes				} else
7461573Srgrimes					line[len - 1] = '\0';
7471573Srgrimes			}
7481573Srgrimes		}
7491573Srgrimes		rp = buf;
75029574Sphk		for(cp = nbuf; *cp != '\0'; cp++)
7511573Srgrimes			if (*cp == '|' || *cp == ':')
7521573Srgrimes				break;
7531573Srgrimes			else
7541573Srgrimes				*rp++ = *cp;
7551573Srgrimes
7561573Srgrimes		*rp = '\0';
7578870Srgrimes		/*
7588870Srgrimes		 * XXX
7591573Srgrimes		 * Last argument of getent here should be nbuf if we want true
7608870Srgrimes		 * sequential access in the case of duplicates.
7611573Srgrimes		 * With NULL, getent will return the first entry found
7628870Srgrimes		 * rather than the duplicate entry record.  This is a
7631573Srgrimes		 * matter of semantics that should be resolved.
7641573Srgrimes		 */
7651573Srgrimes		status = getent(bp, &dummy, db_array, -1, buf, 0, NULL);
7661573Srgrimes		if (status == -2 || status == -3)
7671573Srgrimes			(void)cgetclose();
7681573Srgrimes
7691573Srgrimes		return (status + 1);
7701573Srgrimes	}
7711573Srgrimes	/* NOTREACHED */
7721573Srgrimes}
7731573Srgrimes
7741573Srgrimes/*
7751573Srgrimes * Cgetstr retrieves the value of the string capability cap from the
7761573Srgrimes * capability record pointed to by buf.  A pointer to a decoded, NUL
7771573Srgrimes * terminated, malloc'd copy of the string is returned in the char *
7781573Srgrimes * pointed to by str.  The length of the string not including the trailing
7791573Srgrimes * NUL is returned on success, -1 if the requested string capability
7801573Srgrimes * couldn't be found, -2 if a system error was encountered (storage
7811573Srgrimes * allocation failure).
7821573Srgrimes */
7831573Srgrimesint
78492925Simpcgetstr(char *buf, const char *cap, char **str)
7851573Srgrimes{
78690039Sobrien	u_int m_room;
78790039Sobrien	char *bp, *mp;
7881573Srgrimes	int len;
7891573Srgrimes	char *mem;
7901573Srgrimes
7911573Srgrimes	/*
7921573Srgrimes	 * Find string capability cap
7931573Srgrimes	 */
7941573Srgrimes	bp = cgetcap(buf, cap, '=');
7951573Srgrimes	if (bp == NULL)
7961573Srgrimes		return (-1);
7971573Srgrimes
7981573Srgrimes	/*
7991573Srgrimes	 * Conversion / storage allocation loop ...  Allocate memory in
8001573Srgrimes	 * chunks SFRAG in size.
8011573Srgrimes	 */
8021573Srgrimes	if ((mem = malloc(SFRAG)) == NULL) {
8031573Srgrimes		errno = ENOMEM;
8041573Srgrimes		return (-2);	/* couldn't even allocate the first fragment */
8051573Srgrimes	}
8061573Srgrimes	m_room = SFRAG;
8071573Srgrimes	mp = mem;
8081573Srgrimes
8091573Srgrimes	while (*bp != ':' && *bp != '\0') {
8101573Srgrimes		/*
8111573Srgrimes		 * Loop invariants:
8121573Srgrimes		 *	There is always room for one more character in mem.
8131573Srgrimes		 *	Mp always points just past last character in mem.
8141573Srgrimes		 *	Bp always points at next character in buf.
8151573Srgrimes		 */
8161573Srgrimes		if (*bp == '^') {
8171573Srgrimes			bp++;
8181573Srgrimes			if (*bp == ':' || *bp == '\0')
8191573Srgrimes				break;	/* drop unfinished escape */
8208522Sache			if (*bp == '?') {
8218522Sache				*mp++ = '\177';
8228522Sache				bp++;
8238522Sache			} else
8248522Sache				*mp++ = *bp++ & 037;
8251573Srgrimes		} else if (*bp == '\\') {
8261573Srgrimes			bp++;
8271573Srgrimes			if (*bp == ':' || *bp == '\0')
8281573Srgrimes				break;	/* drop unfinished escape */
8291573Srgrimes			if ('0' <= *bp && *bp <= '7') {
83090039Sobrien				int n, i;
8311573Srgrimes
8321573Srgrimes				n = 0;
8331573Srgrimes				i = 3;	/* maximum of three octal digits */
8341573Srgrimes				do {
8351573Srgrimes					n = n * 8 + (*bp++ - '0');
8361573Srgrimes				} while (--i && '0' <= *bp && *bp <= '7');
8371573Srgrimes				*mp++ = n;
8381573Srgrimes			}
8391573Srgrimes			else switch (*bp++) {
8401573Srgrimes				case 'b': case 'B':
8411573Srgrimes					*mp++ = '\b';
8421573Srgrimes					break;
8431573Srgrimes				case 't': case 'T':
8441573Srgrimes					*mp++ = '\t';
8451573Srgrimes					break;
8461573Srgrimes				case 'n': case 'N':
8471573Srgrimes					*mp++ = '\n';
8481573Srgrimes					break;
8491573Srgrimes				case 'f': case 'F':
8501573Srgrimes					*mp++ = '\f';
8511573Srgrimes					break;
8521573Srgrimes				case 'r': case 'R':
8531573Srgrimes					*mp++ = '\r';
8541573Srgrimes					break;
8551573Srgrimes				case 'e': case 'E':
8561573Srgrimes					*mp++ = ESC;
8571573Srgrimes					break;
8581573Srgrimes				case 'c': case 'C':
8591573Srgrimes					*mp++ = ':';
8601573Srgrimes					break;
8611573Srgrimes				default:
8621573Srgrimes					/*
8631573Srgrimes					 * Catches '\', '^', and
8641573Srgrimes					 *  everything else.
8651573Srgrimes					 */
8661573Srgrimes					*mp++ = *(bp-1);
8671573Srgrimes					break;
8681573Srgrimes			}
8691573Srgrimes		} else
8701573Srgrimes			*mp++ = *bp++;
8711573Srgrimes		m_room--;
8721573Srgrimes
8731573Srgrimes		/*
8741573Srgrimes		 * Enforce loop invariant: if no room left in current
8751573Srgrimes		 * buffer, try to get some more.
8761573Srgrimes		 */
8771573Srgrimes		if (m_room == 0) {
8781573Srgrimes			size_t size = mp - mem;
8791573Srgrimes
88039327Simp			if ((mem = reallocf(mem, size + SFRAG)) == NULL)
8811573Srgrimes				return (-2);
8821573Srgrimes			m_room = SFRAG;
8831573Srgrimes			mp = mem + size;
8841573Srgrimes		}
8851573Srgrimes	}
8861573Srgrimes	*mp++ = '\0';	/* loop invariant let's us do this */
8871573Srgrimes	m_room--;
8881573Srgrimes	len = mp - mem - 1;
8891573Srgrimes
8901573Srgrimes	/*
8911573Srgrimes	 * Give back any extra memory and return value and success.
8921573Srgrimes	 */
8931573Srgrimes	if (m_room != 0)
89439327Simp		if ((mem = reallocf(mem, (size_t)(mp - mem))) == NULL)
8951573Srgrimes			return (-2);
8961573Srgrimes	*str = mem;
8971573Srgrimes	return (len);
8981573Srgrimes}
8991573Srgrimes
9001573Srgrimes/*
9011573Srgrimes * Cgetustr retrieves the value of the string capability cap from the
9021573Srgrimes * capability record pointed to by buf.  The difference between cgetustr()
9031573Srgrimes * and cgetstr() is that cgetustr does not decode escapes but rather treats
9048870Srgrimes * all characters literally.  A pointer to a  NUL terminated malloc'd
9058870Srgrimes * copy of the string is returned in the char pointed to by str.  The
9061573Srgrimes * length of the string not including the trailing NUL is returned on success,
9078870Srgrimes * -1 if the requested string capability couldn't be found, -2 if a system
9081573Srgrimes * error was encountered (storage allocation failure).
9091573Srgrimes */
9101573Srgrimesint
91192925Simpcgetustr(char *buf, const char *cap, char **str)
9121573Srgrimes{
91390039Sobrien	u_int m_room;
91490039Sobrien	char *bp, *mp;
9151573Srgrimes	int len;
9161573Srgrimes	char *mem;
9171573Srgrimes
9181573Srgrimes	/*
9191573Srgrimes	 * Find string capability cap
9201573Srgrimes	 */
9211573Srgrimes	if ((bp = cgetcap(buf, cap, '=')) == NULL)
9221573Srgrimes		return (-1);
9231573Srgrimes
9241573Srgrimes	/*
9251573Srgrimes	 * Conversion / storage allocation loop ...  Allocate memory in
9261573Srgrimes	 * chunks SFRAG in size.
9271573Srgrimes	 */
9281573Srgrimes	if ((mem = malloc(SFRAG)) == NULL) {
9291573Srgrimes		errno = ENOMEM;
9301573Srgrimes		return (-2);	/* couldn't even allocate the first fragment */
9311573Srgrimes	}
9321573Srgrimes	m_room = SFRAG;
9331573Srgrimes	mp = mem;
9341573Srgrimes
9351573Srgrimes	while (*bp != ':' && *bp != '\0') {
9361573Srgrimes		/*
9371573Srgrimes		 * Loop invariants:
9381573Srgrimes		 *	There is always room for one more character in mem.
9391573Srgrimes		 *	Mp always points just past last character in mem.
9401573Srgrimes		 *	Bp always points at next character in buf.
9411573Srgrimes		 */
9421573Srgrimes		*mp++ = *bp++;
9431573Srgrimes		m_room--;
9441573Srgrimes
9451573Srgrimes		/*
9461573Srgrimes		 * Enforce loop invariant: if no room left in current
9471573Srgrimes		 * buffer, try to get some more.
9481573Srgrimes		 */
9491573Srgrimes		if (m_room == 0) {
9501573Srgrimes			size_t size = mp - mem;
9511573Srgrimes
95239327Simp			if ((mem = reallocf(mem, size + SFRAG)) == NULL)
9531573Srgrimes				return (-2);
9541573Srgrimes			m_room = SFRAG;
9551573Srgrimes			mp = mem + size;
9561573Srgrimes		}
9571573Srgrimes	}
9581573Srgrimes	*mp++ = '\0';	/* loop invariant let's us do this */
9591573Srgrimes	m_room--;
9601573Srgrimes	len = mp - mem - 1;
9611573Srgrimes
9621573Srgrimes	/*
9631573Srgrimes	 * Give back any extra memory and return value and success.
9641573Srgrimes	 */
9651573Srgrimes	if (m_room != 0)
96639327Simp		if ((mem = reallocf(mem, (size_t)(mp - mem))) == NULL)
9671573Srgrimes			return (-2);
9681573Srgrimes	*str = mem;
9691573Srgrimes	return (len);
9701573Srgrimes}
9711573Srgrimes
9721573Srgrimes/*
9731573Srgrimes * Cgetnum retrieves the value of the numeric capability cap from the
9741573Srgrimes * capability record pointed to by buf.  The numeric value is returned in
9751573Srgrimes * the long pointed to by num.  0 is returned on success, -1 if the requested
9761573Srgrimes * numeric capability couldn't be found.
9771573Srgrimes */
9781573Srgrimesint
97992925Simpcgetnum(char *buf, const char *cap, long *num)
9801573Srgrimes{
98190039Sobrien	long n;
98290039Sobrien	int base, digit;
98390039Sobrien	char *bp;
9841573Srgrimes
9851573Srgrimes	/*
9861573Srgrimes	 * Find numeric capability cap
9871573Srgrimes	 */
9881573Srgrimes	bp = cgetcap(buf, cap, '#');
9891573Srgrimes	if (bp == NULL)
9901573Srgrimes		return (-1);
9911573Srgrimes
9921573Srgrimes	/*
9931573Srgrimes	 * Look at value and determine numeric base:
9941573Srgrimes	 *	0x... or 0X...	hexadecimal,
9951573Srgrimes	 * else	0...		octal,
9961573Srgrimes	 * else			decimal.
9971573Srgrimes	 */
9981573Srgrimes	if (*bp == '0') {
9991573Srgrimes		bp++;
10001573Srgrimes		if (*bp == 'x' || *bp == 'X') {
10011573Srgrimes			bp++;
10021573Srgrimes			base = 16;
10031573Srgrimes		} else
10041573Srgrimes			base = 8;
10051573Srgrimes	} else
10061573Srgrimes		base = 10;
10071573Srgrimes
10081573Srgrimes	/*
10091573Srgrimes	 * Conversion loop ...
10101573Srgrimes	 */
10111573Srgrimes	n = 0;
10121573Srgrimes	for (;;) {
10131573Srgrimes		if ('0' <= *bp && *bp <= '9')
10141573Srgrimes			digit = *bp - '0';
10151573Srgrimes		else if ('a' <= *bp && *bp <= 'f')
10161573Srgrimes			digit = 10 + *bp - 'a';
10171573Srgrimes		else if ('A' <= *bp && *bp <= 'F')
10181573Srgrimes			digit = 10 + *bp - 'A';
10191573Srgrimes		else
10201573Srgrimes			break;
10211573Srgrimes
10221573Srgrimes		if (digit >= base)
10231573Srgrimes			break;
10241573Srgrimes
10251573Srgrimes		n = n * base + digit;
10261573Srgrimes		bp++;
10271573Srgrimes	}
10281573Srgrimes
10291573Srgrimes	/*
10301573Srgrimes	 * Return value and success.
10311573Srgrimes	 */
10321573Srgrimes	*num = n;
10331573Srgrimes	return (0);
10341573Srgrimes}
10351573Srgrimes
10361573Srgrimes
10371573Srgrimes/*
10381573Srgrimes * Compare name field of record.
10391573Srgrimes */
10401573Srgrimesstatic int
104192925Simpnfcmp(char *nf, char *rec)
10421573Srgrimes{
10431573Srgrimes	char *cp, tmp;
10441573Srgrimes	int ret;
10458870Srgrimes
10461573Srgrimes	for (cp = rec; *cp != ':'; cp++)
10471573Srgrimes		;
10488870Srgrimes
10491573Srgrimes	tmp = *(cp + 1);
10501573Srgrimes	*(cp + 1) = '\0';
10511573Srgrimes	ret = strcmp(nf, rec);
10521573Srgrimes	*(cp + 1) = tmp;
10531573Srgrimes
10541573Srgrimes	return (ret);
10551573Srgrimes}
1056