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