summaryrefslogtreecommitdiff
path: root/lib/libc/gen/getcap.c
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
commitd6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch)
treeece253b876159b39c620e62b6c9b1174642e070e /lib/libc/gen/getcap.c
initial import of NetBSD tree
Diffstat (limited to 'lib/libc/gen/getcap.c')
-rw-r--r--lib/libc/gen/getcap.c1051
1 files changed, 1051 insertions, 0 deletions
diff --git a/lib/libc/gen/getcap.c b/lib/libc/gen/getcap.c
new file mode 100644
index 00000000000..3f34ab3452a
--- /dev/null
+++ b/lib/libc/gen/getcap.c
@@ -0,0 +1,1051 @@
+/* $NetBSD: getcap.c,v 1.10 1995/08/24 05:26:35 mycroft Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Casey Leedom of Lawrence Livermore National Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)getcap.c 8.3 (Berkeley) 3/25/94";
+#else
+static char rcsid[] = "$NetBSD: getcap.c,v 1.10 1995/08/24 05:26:35 mycroft Exp $";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <db.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define BFRAG 1024
+#define BSIZE 1024
+#define ESC ('[' & 037) /* ASCII ESC */
+#define MAX_RECURSION 32 /* maximum getent recursion */
+#define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */
+
+#define RECOK (char)0
+#define TCERR (char)1
+#define SHADOW (char)2
+
+static size_t topreclen; /* toprec length */
+static char *toprec; /* Additional record specified by cgetset() */
+static int gottoprec; /* Flag indicating retrieval of toprecord */
+
+static int cdbget __P((DB *, char **, char *));
+static int getent __P((char **, u_int *, char **, int, char *, int, char *));
+static int nfcmp __P((char *, char *));
+
+/*
+ * Cgetset() allows the addition of a user specified buffer to be added
+ * to the database array, in effect "pushing" the buffer on top of the
+ * virtual database. 0 is returned on success, -1 on failure.
+ */
+int
+cgetset(ent)
+ char *ent;
+{
+ if (ent == NULL) {
+ if (toprec)
+ free(toprec);
+ toprec = NULL;
+ topreclen = 0;
+ return (0);
+ }
+ topreclen = strlen(ent);
+ if ((toprec = malloc (topreclen + 1)) == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ gottoprec = 0;
+ (void)strcpy(toprec, ent);
+ return (0);
+}
+
+/*
+ * Cgetcap searches the capability record buf for the capability cap with
+ * type `type'. A pointer to the value of cap is returned on success, NULL
+ * if the requested capability couldn't be found.
+ *
+ * Specifying a type of ':' means that nothing should follow cap (:cap:).
+ * In this case a pointer to the terminating ':' or NUL will be returned if
+ * cap is found.
+ *
+ * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
+ * return NULL.
+ */
+char *
+cgetcap(buf, cap, type)
+ char *buf, *cap;
+ int type;
+{
+ register char *bp, *cp;
+
+ bp = buf;
+ for (;;) {
+ /*
+ * Skip past the current capability field - it's either the
+ * name field if this is the first time through the loop, or
+ * the remainder of a field whose name failed to match cap.
+ */
+ for (;;)
+ if (*bp == '\0')
+ return (NULL);
+ else
+ if (*bp++ == ':')
+ break;
+
+ /*
+ * Try to match (cap, type) in buf.
+ */
+ for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
+ continue;
+ if (*cp != '\0')
+ continue;
+ if (*bp == '@')
+ return (NULL);
+ if (type == ':') {
+ if (*bp != '\0' && *bp != ':')
+ continue;
+ return(bp);
+ }
+ if (*bp != type)
+ continue;
+ bp++;
+ return (*bp == '@' ? NULL : bp);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Cgetent extracts the capability record name from the NULL terminated file
+ * array db_array and returns a pointer to a malloc'd copy of it in buf.
+ * Buf must be retained through all subsequent calls to cgetcap, cgetnum,
+ * cgetflag, and cgetstr, but may then be free'd. 0 is returned on success,
+ * -1 if the requested record couldn't be found, -2 if a system error was
+ * encountered (couldn't open/read a file, etc.), and -3 if a potential
+ * reference loop is detected.
+ */
+int
+cgetent(buf, db_array, name)
+ char **buf, **db_array, *name;
+{
+ u_int dummy;
+
+ return (getent(buf, &dummy, db_array, -1, name, 0, NULL));
+}
+
+/*
+ * Getent implements the functions of cgetent. If fd is non-negative,
+ * *db_array has already been opened and fd is the open file descriptor. We
+ * do this to save time and avoid using up file descriptors for tc=
+ * recursions.
+ *
+ * Getent returns the same success/failure codes as cgetent. On success, a
+ * pointer to a malloc'ed capability record with all tc= capabilities fully
+ * expanded and its length (not including trailing ASCII NUL) are left in
+ * *cap and *len.
+ *
+ * Basic algorithm:
+ * + Allocate memory incrementally as needed in chunks of size BFRAG
+ * for capability buffer.
+ * + Recurse for each tc=name and interpolate result. Stop when all
+ * names interpolated, a name can't be found, or depth exceeds
+ * MAX_RECURSION.
+ */
+static int
+getent(cap, len, db_array, fd, name, depth, nfield)
+ char **cap, **db_array, *name, *nfield;
+ u_int *len;
+ int fd, depth;
+{
+ DB *capdbp;
+ DBT key, data;
+ register char *r_end, *rp, **db_p;
+ int myfd, eof, foundit, retval, clen;
+ char *record, *cbuf;
+ int tc_not_resolved;
+ char pbuf[_POSIX_PATH_MAX];
+
+ /*
+ * Return with ``loop detected'' error if we've recursed more than
+ * MAX_RECURSION times.
+ */
+ if (depth > MAX_RECURSION)
+ return (-3);
+
+ /*
+ * Check if we have a top record from cgetset().
+ */
+ if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) {
+ if ((record = malloc (topreclen + BFRAG)) == NULL) {
+ errno = ENOMEM;
+ return (-2);
+ }
+ (void)strcpy(record, toprec);
+ myfd = 0;
+ db_p = db_array;
+ rp = record + topreclen + 1;
+ r_end = rp + BFRAG;
+ goto tc_exp;
+ }
+ /*
+ * Allocate first chunk of memory.
+ */
+ if ((record = malloc(BFRAG)) == NULL) {
+ errno = ENOMEM;
+ return (-2);
+ }
+ r_end = record + BFRAG;
+ foundit = 0;
+ /*
+ * Loop through database array until finding the record.
+ */
+
+ for (db_p = db_array; *db_p != NULL; db_p++) {
+ eof = 0;
+
+ /*
+ * Open database if not already open.
+ */
+
+ if (fd >= 0) {
+ (void)lseek(fd, (off_t)0, L_SET);
+ myfd = 0;
+ } else {
+ (void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p);
+ if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0))
+ != NULL) {
+ free(record);
+ retval = cdbget(capdbp, &record, name);
+ if (retval < 0) {
+ /* no record available */
+ (void)capdbp->close(capdbp);
+ return (retval);
+ }
+ /* save the data; close frees it */
+ clen = strlen(record);
+ cbuf = malloc(clen + 1);
+ memcpy(cbuf, record, clen + 1);
+ if (capdbp->close(capdbp) < 0) {
+ free(cbuf);
+ return (-2);
+ }
+ *len = clen;
+ *cap = cbuf;
+ return (retval);
+ } else {
+ fd = open(*db_p, O_RDONLY, 0);
+ if (fd < 0) {
+ /* No error on unfound file. */
+ continue;
+ }
+ myfd = 1;
+ }
+ }
+ /*
+ * Find the requested capability record ...
+ */
+ {
+ char buf[BUFSIZ];
+ register char *b_end, *bp;
+ register int c;
+
+ /*
+ * Loop invariants:
+ * There is always room for one more character in record.
+ * R_end always points just past end of record.
+ * Rp always points just past last character in record.
+ * B_end always points just past last character in buf.
+ * Bp always points at next character in buf.
+ */
+ b_end = buf;
+ bp = buf;
+ for (;;) {
+
+ /*
+ * Read in a line implementing (\, newline)
+ * line continuation.
+ */
+ rp = record;
+ for (;;) {
+ if (bp >= b_end) {
+ int n;
+
+ n = read(fd, buf, sizeof(buf));
+ if (n <= 0) {
+ if (myfd)
+ (void)close(fd);
+ if (n < 0) {
+ free(record);
+ return (-2);
+ } else {
+ fd = -1;
+ eof = 1;
+ break;
+ }
+ }
+ b_end = buf+n;
+ bp = buf;
+ }
+
+ c = *bp++;
+ if (c == '\n') {
+ if (rp > record && *(rp-1) == '\\') {
+ rp--;
+ continue;
+ } else
+ break;
+ }
+ *rp++ = c;
+
+ /*
+ * Enforce loop invariant: if no room
+ * left in record buffer, try to get
+ * some more.
+ */
+ if (rp >= r_end) {
+ u_int pos;
+ size_t newsize;
+
+ pos = rp - record;
+ newsize = r_end - record + BFRAG;
+ record = realloc(record, newsize);
+ if (record == NULL) {
+ errno = ENOMEM;
+ if (myfd)
+ (void)close(fd);
+ return (-2);
+ }
+ r_end = record + newsize;
+ rp = record + pos;
+ }
+ }
+ /* loop invariant let's us do this */
+ *rp++ = '\0';
+
+ /*
+ * If encountered eof check next file.
+ */
+ if (eof)
+ break;
+
+ /*
+ * Toss blank lines and comments.
+ */
+ if (*record == '\0' || *record == '#')
+ continue;
+
+ /*
+ * See if this is the record we want ...
+ */
+ if (cgetmatch(record, name) == 0) {
+ if (nfield == NULL || !nfcmp(nfield, record)) {
+ foundit = 1;
+ break; /* found it! */
+ }
+ }
+ }
+ }
+ if (foundit)
+ break;
+ }
+
+ if (!foundit)
+ return (-1);
+
+ /*
+ * Got the capability record, but now we have to expand all tc=name
+ * references in it ...
+ */
+tc_exp: {
+ register char *newicap, *s;
+ register int newilen;
+ u_int ilen;
+ int diff, iret, tclen;
+ char *icap, *scan, *tc, *tcstart, *tcend;
+
+ /*
+ * Loop invariants:
+ * There is room for one more character in record.
+ * R_end points just past end of record.
+ * Rp points just past last character in record.
+ * Scan points at remainder of record that needs to be
+ * scanned for tc=name constructs.
+ */
+ scan = record;
+ tc_not_resolved = 0;
+ for (;;) {
+ if ((tc = cgetcap(scan, "tc", '=')) == NULL)
+ break;
+
+ /*
+ * Find end of tc=name and stomp on the trailing `:'
+ * (if present) so we can use it to call ourselves.
+ */
+ s = tc;
+ for (;;)
+ if (*s == '\0')
+ break;
+ else
+ if (*s++ == ':') {
+ *(s - 1) = '\0';
+ break;
+ }
+ tcstart = tc - 3;
+ tclen = s - tcstart;
+ tcend = s;
+
+ iret = getent(&icap, &ilen, db_p, fd, tc, depth+1,
+ NULL);
+ newicap = icap; /* Put into a register. */
+ newilen = ilen;
+ if (iret != 0) {
+ /* an error */
+ if (iret < -1) {
+ if (myfd)
+ (void)close(fd);
+ free(record);
+ return (iret);
+ }
+ if (iret == 1)
+ tc_not_resolved = 1;
+ /* couldn't resolve tc */
+ if (iret == -1) {
+ *(s - 1) = ':';
+ scan = s - 1;
+ tc_not_resolved = 1;
+ continue;
+
+ }
+ }
+ /* not interested in name field of tc'ed record */
+ s = newicap;
+ for (;;)
+ if (*s == '\0')
+ break;
+ else
+ if (*s++ == ':')
+ break;
+ newilen -= s - newicap;
+ newicap = s;
+
+ /* make sure interpolated record is `:'-terminated */
+ s += newilen;
+ if (*(s-1) != ':') {
+ *s = ':'; /* overwrite NUL with : */
+ newilen++;
+ }
+
+ /*
+ * Make sure there's enough room to insert the
+ * new record.
+ */
+ diff = newilen - tclen;
+ if (diff >= r_end - rp) {
+ u_int pos, tcpos, tcposend;
+ size_t newsize;
+
+ pos = rp - record;
+ newsize = r_end - record + diff + BFRAG;
+ tcpos = tcstart - record;
+ tcposend = tcend - record;
+ record = realloc(record, newsize);
+ if (record == NULL) {
+ errno = ENOMEM;
+ if (myfd)
+ (void)close(fd);
+ free(icap);
+ return (-2);
+ }
+ r_end = record + newsize;
+ rp = record + pos;
+ tcstart = record + tcpos;
+ tcend = record + tcposend;
+ }
+
+ /*
+ * Insert tc'ed record into our record.
+ */
+ s = tcstart + newilen;
+ bcopy(tcend, s, rp - tcend);
+ bcopy(newicap, tcstart, newilen);
+ rp += diff;
+ free(icap);
+
+ /*
+ * Start scan on `:' so next cgetcap works properly
+ * (cgetcap always skips first field).
+ */
+ scan = s-1;
+ }
+
+ }
+ /*
+ * Close file (if we opened it), give back any extra memory, and
+ * return capability, length and success.
+ */
+ if (myfd)
+ (void)close(fd);
+ *len = rp - record - 1; /* don't count NUL */
+ if (r_end > rp)
+ if ((record =
+ realloc(record, (size_t)(rp - record))) == NULL) {
+ errno = ENOMEM;
+ return (-2);
+ }
+
+ *cap = record;
+ if (tc_not_resolved)
+ return (1);
+ return (0);
+}
+
+static int
+cdbget(capdbp, bp, name)
+ DB *capdbp;
+ char **bp, *name;
+{
+ DBT key, data;
+ char *buf;
+ int st;
+
+ key.data = name;
+ key.size = strlen(name);
+
+ for (;;) {
+ /* Get the reference. */
+ switch(capdbp->get(capdbp, &key, &data, 0)) {
+ case -1:
+ return (-2);
+ case 1:
+ return (-1);
+ }
+
+ /* If not an index to another record, leave. */
+ if (((char *)data.data)[0] != SHADOW)
+ break;
+
+ key.data = (char *)data.data + 1;
+ key.size = data.size - 1;
+ }
+
+ *bp = (char *)data.data + 1;
+ return (((char *)(data.data))[0] == TCERR ? 1 : 0);
+}
+
+/*
+ * Cgetmatch will return 0 if name is one of the names of the capability
+ * record buf, -1 if not.
+ */
+int
+cgetmatch(buf, name)
+ char *buf, *name;
+{
+ register char *np, *bp;
+
+ /*
+ * Start search at beginning of record.
+ */
+ bp = buf;
+ for (;;) {
+ /*
+ * Try to match a record name.
+ */
+ np = name;
+ for (;;)
+ if (*np == '\0')
+ if (*bp == '|' || *bp == ':' || *bp == '\0')
+ return (0);
+ else
+ break;
+ else
+ if (*bp++ != *np++)
+ break;
+
+ /*
+ * Match failed, skip to next name in record.
+ */
+ bp--; /* a '|' or ':' may have stopped the match */
+ for (;;)
+ if (*bp == '\0' || *bp == ':')
+ return (-1); /* match failed totally */
+ else
+ if (*bp++ == '|')
+ break; /* found next name */
+ }
+}
+
+
+
+
+
+int
+cgetfirst(buf, db_array)
+ char **buf, **db_array;
+{
+ (void)cgetclose();
+ return (cgetnext(buf, db_array));
+}
+
+static FILE *pfp;
+static int slash;
+static char **dbp;
+
+int
+cgetclose()
+{
+ if (pfp != NULL) {
+ (void)fclose(pfp);
+ pfp = NULL;
+ }
+ dbp = NULL;
+ gottoprec = 0;
+ slash = 0;
+ return(0);
+}
+
+/*
+ * Cgetnext() gets either the first or next entry in the logical database
+ * specified by db_array. It returns 0 upon completion of the database, 1
+ * upon returning an entry with more remaining, and -1 if an error occurs.
+ */
+int
+cgetnext(bp, db_array)
+ register char **bp;
+ char **db_array;
+{
+ size_t len;
+ int status, i, done;
+ char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE];
+ u_int dummy;
+
+ if (dbp == NULL)
+ dbp = db_array;
+
+ if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) {
+ (void)cgetclose();
+ return (-1);
+ }
+ for(;;) {
+ if (toprec && !gottoprec) {
+ gottoprec = 1;
+ line = toprec;
+ } else {
+ line = fgetln(pfp, &len);
+ if (line == NULL && pfp) {
+ (void)fclose(pfp);
+ if (ferror(pfp)) {
+ (void)cgetclose();
+ return (-1);
+ } else {
+ if (*++dbp == NULL) {
+ (void)cgetclose();
+ return (0);
+ } else if ((pfp =
+ fopen(*dbp, "r")) == NULL) {
+ (void)cgetclose();
+ return (-1);
+ } else
+ continue;
+ }
+ } else
+ line[len - 1] = '\0';
+ if (len == 1) {
+ slash = 0;
+ continue;
+ }
+ if (isspace(*line) ||
+ *line == ':' || *line == '#' || slash) {
+ if (line[len - 2] == '\\')
+ slash = 1;
+ else
+ slash = 0;
+ continue;
+ }
+ if (line[len - 2] == '\\')
+ slash = 1;
+ else
+ slash = 0;
+ }
+
+
+ /*
+ * Line points to a name line.
+ */
+ i = 0;
+ done = 0;
+ np = nbuf;
+ for (;;) {
+ for (cp = line; *cp != '\0'; cp++) {
+ if (*cp == ':') {
+ *np++ = ':';
+ done = 1;
+ break;
+ }
+ if (*cp == '\\')
+ break;
+ *np++ = *cp;
+ }
+ if (done) {
+ *np = '\0';
+ break;
+ } else { /* name field extends beyond the line */
+ line = fgetln(pfp, &len);
+ if (line == NULL && pfp) {
+ (void)fclose(pfp);
+ if (ferror(pfp)) {
+ (void)cgetclose();
+ return (-1);
+ }
+ } else
+ line[len - 1] = '\0';
+ }
+ }
+ rp = buf;
+ for(cp = nbuf; *cp != NULL; cp++)
+ if (*cp == '|' || *cp == ':')
+ break;
+ else
+ *rp++ = *cp;
+
+ *rp = '\0';
+ /*
+ * XXX
+ * Last argument of getent here should be nbuf if we want true
+ * sequential access in the case of duplicates.
+ * With NULL, getent will return the first entry found
+ * rather than the duplicate entry record. This is a
+ * matter of semantics that should be resolved.
+ */
+ status = getent(bp, &dummy, db_array, -1, buf, 0, NULL);
+ if (status == -2 || status == -3)
+ (void)cgetclose();
+
+ return (status + 1);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Cgetstr retrieves the value of the string capability cap from the
+ * capability record pointed to by buf. A pointer to a decoded, NUL
+ * terminated, malloc'd copy of the string is returned in the char *
+ * pointed to by str. The length of the string not including the trailing
+ * NUL is returned on success, -1 if the requested string capability
+ * couldn't be found, -2 if a system error was encountered (storage
+ * allocation failure).
+ */
+int
+cgetstr(buf, cap, str)
+ char *buf, *cap;
+ char **str;
+{
+ register u_int m_room;
+ register char *bp, *mp;
+ int len;
+ char *mem;
+
+ /*
+ * Find string capability cap
+ */
+ bp = cgetcap(buf, cap, '=');
+ if (bp == NULL)
+ return (-1);
+
+ /*
+ * Conversion / storage allocation loop ... Allocate memory in
+ * chunks SFRAG in size.
+ */
+ if ((mem = malloc(SFRAG)) == NULL) {
+ errno = ENOMEM;
+ return (-2); /* couldn't even allocate the first fragment */
+ }
+ m_room = SFRAG;
+ mp = mem;
+
+ while (*bp != ':' && *bp != '\0') {
+ /*
+ * Loop invariants:
+ * There is always room for one more character in mem.
+ * Mp always points just past last character in mem.
+ * Bp always points at next character in buf.
+ */
+ if (*bp == '^') {
+ bp++;
+ if (*bp == ':' || *bp == '\0')
+ break; /* drop unfinished escape */
+ *mp++ = *bp++ & 037;
+ } else if (*bp == '\\') {
+ bp++;
+ if (*bp == ':' || *bp == '\0')
+ break; /* drop unfinished escape */
+ if ('0' <= *bp && *bp <= '7') {
+ register int n, i;
+
+ n = 0;
+ i = 3; /* maximum of three octal digits */
+ do {
+ n = n * 8 + (*bp++ - '0');
+ } while (--i && '0' <= *bp && *bp <= '7');
+ *mp++ = n;
+ }
+ else switch (*bp++) {
+ case 'b': case 'B':
+ *mp++ = '\b';
+ break;
+ case 't': case 'T':
+ *mp++ = '\t';
+ break;
+ case 'n': case 'N':
+ *mp++ = '\n';
+ break;
+ case 'f': case 'F':
+ *mp++ = '\f';
+ break;
+ case 'r': case 'R':
+ *mp++ = '\r';
+ break;
+ case 'e': case 'E':
+ *mp++ = ESC;
+ break;
+ case 'c': case 'C':
+ *mp++ = ':';
+ break;
+ default:
+ /*
+ * Catches '\', '^', and
+ * everything else.
+ */
+ *mp++ = *(bp-1);
+ break;
+ }
+ } else
+ *mp++ = *bp++;
+ m_room--;
+
+ /*
+ * Enforce loop invariant: if no room left in current
+ * buffer, try to get some more.
+ */
+ if (m_room == 0) {
+ size_t size = mp - mem;
+
+ if ((mem = realloc(mem, size + SFRAG)) == NULL)
+ return (-2);
+ m_room = SFRAG;
+ mp = mem + size;
+ }
+ }
+ *mp++ = '\0'; /* loop invariant let's us do this */
+ m_room--;
+ len = mp - mem - 1;
+
+ /*
+ * Give back any extra memory and return value and success.
+ */
+ if (m_room != 0)
+ if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL)
+ return (-2);
+ *str = mem;
+ return (len);
+}
+
+/*
+ * Cgetustr retrieves the value of the string capability cap from the
+ * capability record pointed to by buf. The difference between cgetustr()
+ * and cgetstr() is that cgetustr does not decode escapes but rather treats
+ * all characters literally. A pointer to a NUL terminated malloc'd
+ * copy of the string is returned in the char pointed to by str. The
+ * length of the string not including the trailing NUL is returned on success,
+ * -1 if the requested string capability couldn't be found, -2 if a system
+ * error was encountered (storage allocation failure).
+ */
+int
+cgetustr(buf, cap, str)
+ char *buf, *cap, **str;
+{
+ register u_int m_room;
+ register char *bp, *mp;
+ int len;
+ char *mem;
+
+ /*
+ * Find string capability cap
+ */
+ if ((bp = cgetcap(buf, cap, '=')) == NULL)
+ return (-1);
+
+ /*
+ * Conversion / storage allocation loop ... Allocate memory in
+ * chunks SFRAG in size.
+ */
+ if ((mem = malloc(SFRAG)) == NULL) {
+ errno = ENOMEM;
+ return (-2); /* couldn't even allocate the first fragment */
+ }
+ m_room = SFRAG;
+ mp = mem;
+
+ while (*bp != ':' && *bp != '\0') {
+ /*
+ * Loop invariants:
+ * There is always room for one more character in mem.
+ * Mp always points just past last character in mem.
+ * Bp always points at next character in buf.
+ */
+ *mp++ = *bp++;
+ m_room--;
+
+ /*
+ * Enforce loop invariant: if no room left in current
+ * buffer, try to get some more.
+ */
+ if (m_room == 0) {
+ size_t size = mp - mem;
+
+ if ((mem = realloc(mem, size + SFRAG)) == NULL)
+ return (-2);
+ m_room = SFRAG;
+ mp = mem + size;
+ }
+ }
+ *mp++ = '\0'; /* loop invariant let's us do this */
+ m_room--;
+ len = mp - mem - 1;
+
+ /*
+ * Give back any extra memory and return value and success.
+ */
+ if (m_room != 0)
+ if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL)
+ return (-2);
+ *str = mem;
+ return (len);
+}
+
+/*
+ * Cgetnum retrieves the value of the numeric capability cap from the
+ * capability record pointed to by buf. The numeric value is returned in
+ * the long pointed to by num. 0 is returned on success, -1 if the requested
+ * numeric capability couldn't be found.
+ */
+int
+cgetnum(buf, cap, num)
+ char *buf, *cap;
+ long *num;
+{
+ register long n;
+ register int base, digit;
+ register char *bp;
+
+ /*
+ * Find numeric capability cap
+ */
+ bp = cgetcap(buf, cap, '#');
+ if (bp == NULL)
+ return (-1);
+
+ /*
+ * Look at value and determine numeric base:
+ * 0x... or 0X... hexadecimal,
+ * else 0... octal,
+ * else decimal.
+ */
+ if (*bp == '0') {
+ bp++;
+ if (*bp == 'x' || *bp == 'X') {
+ bp++;
+ base = 16;
+ } else
+ base = 8;
+ } else
+ base = 10;
+
+ /*
+ * Conversion loop ...
+ */
+ n = 0;
+ for (;;) {
+ if ('0' <= *bp && *bp <= '9')
+ digit = *bp - '0';
+ else if ('a' <= *bp && *bp <= 'f')
+ digit = 10 + *bp - 'a';
+ else if ('A' <= *bp && *bp <= 'F')
+ digit = 10 + *bp - 'A';
+ else
+ break;
+
+ if (digit >= base)
+ break;
+
+ n = n * base + digit;
+ bp++;
+ }
+
+ /*
+ * Return value and success.
+ */
+ *num = n;
+ return (0);
+}
+
+
+/*
+ * Compare name field of record.
+ */
+static int
+nfcmp(nf, rec)
+ char *nf, *rec;
+{
+ char *cp, tmp;
+ int ret;
+
+ for (cp = rec; *cp != ':'; cp++)
+ ;
+
+ tmp = *(cp + 1);
+ *(cp + 1) = '\0';
+ ret = strcmp(nf, rec);
+ *(cp + 1) = tmp;
+
+ return (ret);
+}