155868Sbostic /*- 255868Sbostic * Copyright (c) 1992 The Regents of the University of California. 355868Sbostic * All rights reserved. 455868Sbostic * 555868Sbostic * This code is derived from software contributed to Berkeley by 655868Sbostic * Casey Leedom of Lawrence Livermore National Laboratory. 755868Sbostic * 855875Selan * Redistribution and use in source and binary forms, with or without 955875Selan * modification, are permitted provided that the following conditions 1055875Selan * are met: 1155875Selan * 1. Redistributions of source code must retain the above copyright 1255875Selan * notice, this list of conditions and the following disclaimer. 1355875Selan * 2. Redistributions in binary form must reproduce the above copyright 1455875Selan * notice, this list of conditions and the following disclaimer in the 1555875Selan * documentation and/or other materials provided with the distribution. 1655875Selan * 3. All advertising materials mentioning features or use of this software 1755875Selan * must display the following acknowledgement: 1855875Selan * This product includes software developed by the University of 1955875Selan * California, Berkeley and its contributors. 2055875Selan * 4. Neither the name of the University nor the names of its contributors 2155875Selan * may be used to endorse or promote products derived from this software 2255875Selan * without specific prior written permission. 2355875Selan * 2455875Selan * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2555875Selan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2655875Selan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2755875Selan * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2855875Selan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2955875Selan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3055875Selan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3155875Selan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3255875Selan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3355875Selan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3455875Selan * SUCH DAMAGE. 3555868Sbostic */ 3655868Sbostic 3755868Sbostic #if defined(LIBC_SCCS) && !defined(lint) 3855875Selan static char sccsid[] = "@(#)getcap.c 5.1 (Berkeley) 8/6/92"; 3955868Sbostic #endif /* LIBC_SCCS and not lint */ 4055868Sbostic 4155868Sbostic #include <sys/types.h> 4255868Sbostic 4355868Sbostic #include <ctype.h> 4455915Selan #include <db.h> 4555868Sbostic #include <errno.h> 4655868Sbostic #include <errno.h> 4755868Sbostic #include <fcntl.h> 4855915Selan #include <limits.h> 4955868Sbostic #include <stdio.h> 5055868Sbostic #include <stdlib.h> 5155868Sbostic #include <string.h> 5255868Sbostic #include <unistd.h> 5355868Sbostic 5455868Sbostic #define BFRAG 1024 5555868Sbostic #define BSIZE 80 5655868Sbostic #define ESC ('[' & 037) /* ASCII ESC */ 5755868Sbostic #define MAX_RECURSION 32 /* maximum getent recursion */ 5855868Sbostic #define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */ 5955868Sbostic 6055915Selan #define REFERENCE (char)0 6155915Selan #define RECORD (char)1 6255915Selan 6355868Sbostic static size_t topreclen; /* toprec length */ 6455868Sbostic static char *toprec; /* Additional record specified by cgetset() */ 6555875Selan static int gottoprec; /* Flag indicating retrieval of toprecord */ 6655868Sbostic 6755915Selan static int cdbget __P((DB *, char **, char *)); 6855915Selan static int getent __P((char **, u_int *, char **, int, char *, int)); 6955868Sbostic 7055868Sbostic /* 7155868Sbostic * Cgetset() allows the addition of a user specified buffer to be added 7255868Sbostic * to the database array, in effect "pushing" the buffer on top of the 7355868Sbostic * virtual database. 0 is returned on success, -1 on failure. 7455868Sbostic */ 7555868Sbostic int 7655868Sbostic cgetset(ent) 7755868Sbostic char *ent; 7855868Sbostic { 7955868Sbostic if (ent == NULL) { 8055868Sbostic if (toprec) 8155868Sbostic free(toprec); 8255868Sbostic toprec = NULL; 8355868Sbostic topreclen = 0; 8455868Sbostic return (0); 8555868Sbostic } 8655868Sbostic topreclen = strlen(ent); 8755868Sbostic if ((toprec = malloc (topreclen + 1)) == NULL) { 8855868Sbostic errno = ENOMEM; 8955868Sbostic return (-1); 9055868Sbostic } 9155876Selan gottoprec = 0; 9255868Sbostic (void)strcpy(toprec, ent); 9355868Sbostic return (0); 9455868Sbostic } 9555868Sbostic 9655868Sbostic /* 9755868Sbostic * Cgetcap searches the capability record buf for the capability cap with 9855868Sbostic * type `type'. A pointer to the value of cap is returned on success, NULL 9955868Sbostic * if the requested capability couldn't be found. 10055868Sbostic * 10155868Sbostic * Specifying a type of ':' means that nothing should follow cap (:cap:). 10255868Sbostic * In this case a pointer to the terminating ':' or NUL will be returned if 10355868Sbostic * cap is found. 10455868Sbostic * 10555868Sbostic * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator) 10655868Sbostic * return NULL. 10755868Sbostic */ 10855868Sbostic char * 10955868Sbostic cgetcap(buf, cap, type) 11055868Sbostic char *buf, *cap; 11155868Sbostic int type; 11255868Sbostic { 11355868Sbostic register char *bp, *cp; 11455868Sbostic 11555868Sbostic bp = buf; 11655868Sbostic for (;;) { 11755868Sbostic /* 11855868Sbostic * Skip past the current capability field - it's either the 11955868Sbostic * name field if this is the first time through the loop, or 12055868Sbostic * the remainder of a field whose name failed to match cap. 12155868Sbostic */ 12255868Sbostic for (;;) 12355868Sbostic if (*bp == '\0') 12455868Sbostic return (NULL); 12555868Sbostic else 12655868Sbostic if (*bp++ == ':') 12755868Sbostic break; 12855868Sbostic 12955868Sbostic /* 13055868Sbostic * Try to match (cap, type) in buf. 13155868Sbostic */ 13255868Sbostic for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++) 13355868Sbostic continue; 13455868Sbostic if (*cp != '\0') 13555868Sbostic continue; 13655868Sbostic if (*bp == '@') 13755868Sbostic return (NULL); 13855868Sbostic if (type == ':') { 13955868Sbostic if (*bp != '\0' && *bp != ':') 14055868Sbostic continue; 14155868Sbostic return(bp); 14255868Sbostic } 14355868Sbostic if (*bp != type) 14455868Sbostic continue; 14555868Sbostic bp++; 14655868Sbostic return (*bp == '@' ? NULL : bp); 14755868Sbostic } 14855868Sbostic /* NOTREACHED */ 14955868Sbostic } 15055868Sbostic 15155868Sbostic /* 15255868Sbostic * Cgetent extracts the capability record name from the NULL terminated file 15355868Sbostic * array db_array and returns a pointer to a malloc'd copy of it in buf. 15455868Sbostic * Buf must be retained through all subsequent calls to cgetcap, cgetnum, 15555868Sbostic * cgetflag, and cgetstr, but may then be free'd. 0 is returned on success, 15655868Sbostic * -1 if the requested record couldn't be found, -2 if a system error was 15755868Sbostic * encountered (couldn't open/read a file, etc.), and -3 if a potential 15855868Sbostic * reference loop is detected. 15955868Sbostic */ 16055868Sbostic int 16155868Sbostic cgetent(buf, db_array, name) 16255868Sbostic char **buf, **db_array, *name; 16355868Sbostic { 16455868Sbostic u_int dummy; 16555868Sbostic 16655868Sbostic return (getent(buf, &dummy, db_array, -1, name, 0)); 16755868Sbostic } 16855868Sbostic 16955868Sbostic /* 17055868Sbostic * Getent implements the functions of cgetent. If fd is non-negative, 17155868Sbostic * *db_array has already been opened and fd is the open file descriptor. We 17255868Sbostic * do this to save time and avoid using up file descriptors for tc= 17355868Sbostic * recursions. 17455868Sbostic * 17555868Sbostic * Getent returns the same success/failure codes as cgetent. On success, a 17655868Sbostic * pointer to a malloc'ed capability record with all tc= capabilities fully 17755868Sbostic * expanded and its length (not including trailing ASCII NUL) are left in 17855868Sbostic * *cap and *len. 17955868Sbostic * 18055868Sbostic * Basic algorithm: 18155868Sbostic * + Allocate memory incrementally as needed in chunks of size BFRAG 18255868Sbostic * for capability buffer. 18355868Sbostic * + Recurse for each tc=name and interpolate result. Stop when all 18455868Sbostic * names interpolated, a name can't be found, or depth exceeds 18555868Sbostic * MAX_RECURSION. 18655868Sbostic */ 18755868Sbostic static int 18855868Sbostic getent(cap, len, db_array, fd, name, depth) 18955868Sbostic char **cap, **db_array, *name; 19055868Sbostic u_int *len; 19155868Sbostic int fd, depth; 19255868Sbostic { 19355915Selan DB *capdbp; 19455915Selan DBT key, data; 19555868Sbostic register char *r_end, *rp, **db_p; 19655915Selan int myfd, eof, foundit, retval; 19755868Sbostic char *record; 19855915Selan int tc_not_resolved; 19955915Selan char pbuf[_POSIX_PATH_MAX]; 20055915Selan 20155868Sbostic /* 20255868Sbostic * Return with ``loop detected'' error if we've recursed more than 20355868Sbostic * MAX_RECURSION times. 20455868Sbostic */ 20555868Sbostic if (depth > MAX_RECURSION) 20655868Sbostic return (-3); 20755868Sbostic 20855868Sbostic /* 20955868Sbostic * Check if we have a top record from cgetset(). 21055868Sbostic */ 21155876Selan if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) { 21255868Sbostic if ((record = malloc (topreclen + BFRAG)) == NULL) { 21355868Sbostic errno = ENOMEM; 21455868Sbostic return (-2); 21555868Sbostic } 21655868Sbostic (void)strcpy(record, toprec); 21755868Sbostic myfd = 0; 21855868Sbostic db_p = db_array; 21955868Sbostic rp = record + topreclen + 1; 22055868Sbostic r_end = rp + BFRAG; 22155868Sbostic goto tc_exp; 22255868Sbostic } 22355868Sbostic /* 22455868Sbostic * Allocate first chunk of memory. 22555868Sbostic */ 22655868Sbostic if ((record = malloc(BFRAG)) == NULL) { 22755868Sbostic errno = ENOMEM; 22855868Sbostic return (-2); 22955868Sbostic } 23055868Sbostic r_end = record + BFRAG; 23155868Sbostic foundit = 0; 23255868Sbostic /* 23355868Sbostic * Loop through database array until finding the record. 23455868Sbostic */ 23555868Sbostic 23655868Sbostic for (db_p = db_array; *db_p != NULL; db_p++) { 23755868Sbostic eof = 0; 23855868Sbostic 23955868Sbostic /* 24055868Sbostic * Open database if not already open. 24155868Sbostic */ 24255915Selan 24355868Sbostic if (fd >= 0) { 24455868Sbostic (void)lseek(fd, 0L, L_SET); 24555868Sbostic myfd = 0; 24655868Sbostic } else { 24755915Selan sprintf(pbuf, "%s.db", *db_p); 24855915Selan if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0)) 24955915Selan != NULL) { 25055868Sbostic free(record); 25155915Selan retval = cdbget(capdbp, &record, name); 25255915Selan if (capdbp->close(capdbp) < 0) 25355915Selan return (-2); 25455915Selan if (retval < 0) 25555915Selan return (retval); 25655915Selan rp = record + strlen(record) + 1; 25755915Selan myfd = 0; 25855915Selan goto tc_exp; 25955915Selan } else { 26055915Selan fd = open(*db_p, O_RDONLY, 0); 26155915Selan if (fd < 0) { 26255915Selan free(record); 26355915Selan return (-2); 26455915Selan } 26555915Selan myfd = 1; 26655868Sbostic } 26755868Sbostic } 26855868Sbostic /* 26955868Sbostic * Find the requested capability record ... 27055868Sbostic */ 27155868Sbostic { 27255868Sbostic char buf[BUFSIZ]; 27355868Sbostic register char *b_end, *bp; 27455868Sbostic register int c; 27555868Sbostic 27655868Sbostic /* 27755868Sbostic * Loop invariants: 27855868Sbostic * There is always room for one more character in record. 27955868Sbostic * R_end always points just past end of record. 28055868Sbostic * Rp always points just past last character in record. 28155868Sbostic * B_end always points just past last character in buf. 28255868Sbostic * Bp always points at next character in buf. 28355868Sbostic */ 28455868Sbostic b_end = buf; 28555868Sbostic bp = buf; 28655868Sbostic for (;;) { 28755868Sbostic 28855868Sbostic /* 28955868Sbostic * Read in a line implementing (\, newline) 29055868Sbostic * line continuation. 29155868Sbostic */ 29255868Sbostic rp = record; 29355868Sbostic for (;;) { 29455868Sbostic if (bp >= b_end) { 29555868Sbostic int n; 29655868Sbostic 29755868Sbostic n = read(fd, buf, sizeof(buf)); 29855868Sbostic if (n <= 0) { 29955868Sbostic if (myfd) 30055868Sbostic (void)close(fd); 30155868Sbostic if (n < 0) { 30255868Sbostic free(record); 30355868Sbostic return (-2); 30455868Sbostic } else { 30555868Sbostic fd = -1; 30655868Sbostic eof = 1; 30755868Sbostic break; 30855868Sbostic } 30955868Sbostic } 31055868Sbostic b_end = buf+n; 31155868Sbostic bp = buf; 31255868Sbostic } 31355868Sbostic 31455868Sbostic c = *bp++; 31555868Sbostic if (c == '\n') { 31655868Sbostic if (rp > record && *(rp-1) == '\\') { 31755868Sbostic rp--; 31855868Sbostic continue; 31955868Sbostic } else 32055868Sbostic break; 32155868Sbostic } 32255868Sbostic *rp++ = c; 32355868Sbostic 32455868Sbostic /* 32555868Sbostic * Enforce loop invariant: if no room 32655868Sbostic * left in record buffer, try to get 32755868Sbostic * some more. 32855868Sbostic */ 32955868Sbostic if (rp >= r_end) { 33055875Selan u_int pos; 33155875Selan size_t newsize; 33255868Sbostic 33355868Sbostic pos = rp - record; 33455868Sbostic newsize = r_end - record + BFRAG; 33555868Sbostic record = realloc(record, newsize); 33655868Sbostic if (record == NULL) { 33755868Sbostic errno = ENOMEM; 33855868Sbostic if (myfd) 33955868Sbostic (void)close(fd); 34055868Sbostic return (-2); 34155868Sbostic } 34255868Sbostic r_end = record + newsize; 34355868Sbostic rp = record + pos; 34455868Sbostic } 34555868Sbostic } 34655868Sbostic /* loop invariant let's us do this */ 34755868Sbostic *rp++ = '\0'; 34855868Sbostic 34955868Sbostic /* 35055868Sbostic * If encountered eof check next file. 35155868Sbostic */ 35255868Sbostic if (eof) 35355868Sbostic break; 35455868Sbostic 35555868Sbostic /* 35655868Sbostic * Toss blank lines and comments. 35755868Sbostic */ 35855868Sbostic if (*record == '\0' || *record == '#') 35955868Sbostic continue; 36055868Sbostic 36155868Sbostic /* 36255868Sbostic * See if this is the record we want ... 36355868Sbostic */ 36455868Sbostic if (cgetmatch(record, name) == 0) { 36555868Sbostic foundit = 1; 36655868Sbostic break; /* found it! */ 36755868Sbostic } 36855868Sbostic } 36955868Sbostic } 37055868Sbostic if (foundit) 37155868Sbostic break; 37255868Sbostic } 37355868Sbostic 37455868Sbostic if (!foundit) 37555868Sbostic return (-1); 37655876Selan 37755868Sbostic /* 37855868Sbostic * Got the capability record, but now we have to expand all tc=name 37955868Sbostic * references in it ... 38055868Sbostic */ 38155868Sbostic tc_exp: { 38255868Sbostic register char *newicap, *s; 38355868Sbostic register int newilen; 38455868Sbostic u_int ilen; 38555868Sbostic int diff, iret, tclen; 38655868Sbostic char *icap, *scan, *tc, *tcstart, *tcend; 38755868Sbostic 38855868Sbostic /* 38955868Sbostic * Loop invariants: 39055868Sbostic * There is room for one more character in record. 39155868Sbostic * R_end points just past end of record. 39255868Sbostic * Rp points just past last character in record. 39355868Sbostic * Scan points at remainder of record that needs to be 39455868Sbostic * scanned for tc=name constructs. 39555868Sbostic */ 39655868Sbostic scan = record; 39755915Selan tc_not_resolved = 0; 39855868Sbostic for (;;) { 39955868Sbostic if ((tc = cgetcap(scan, "tc", '=')) == NULL) 40055868Sbostic break; 40155868Sbostic 40255868Sbostic /* 40355868Sbostic * Find end of tc=name and stomp on the trailing `:' 40455868Sbostic * (if present) so we can use it to call ourselves. 40555868Sbostic */ 40655868Sbostic s = tc; 40755868Sbostic for (;;) 40855868Sbostic if (*s == '\0') 40955868Sbostic break; 41055868Sbostic else 41155868Sbostic if (*s++ == ':') { 412*56184Selan *(s - 1) = '\0'; 41355868Sbostic break; 41455868Sbostic } 41555868Sbostic tcstart = tc - 3; 41655868Sbostic tclen = s - tcstart; 41755868Sbostic tcend = s; 41855868Sbostic 41955868Sbostic iret = getent(&icap, &ilen, db_p, fd, tc, depth+1); 42055868Sbostic newicap = icap; /* Put into a register. */ 42155868Sbostic newilen = ilen; 42255868Sbostic if (iret != 0) { 42355915Selan /* couldn't resolve tc */ 42455915Selan if (iret == 1) 42555915Selan tc_not_resolved = 1; 42655915Selan if (iret == -1) { 427*56184Selan *(s - 1) = ':'; 42855915Selan scan = s - 1; 42955915Selan tc_not_resolved = 1; 43055915Selan continue; 43155915Selan 43255915Selan } 43355915Selan /* an error */ 43455868Sbostic if (myfd) 43555868Sbostic (void)close(fd); 43655868Sbostic free(record); 43755868Sbostic return (iret); 43855868Sbostic } 43955868Sbostic 44055868Sbostic /* not interested in name field of tc'ed record */ 44155868Sbostic s = newicap; 44255868Sbostic for (;;) 44355868Sbostic if (*s == '\0') 44455868Sbostic break; 44555868Sbostic else 44655868Sbostic if (*s++ == ':') 44755868Sbostic break; 44855868Sbostic newilen -= s - newicap; 44955868Sbostic newicap = s; 45055868Sbostic 45155868Sbostic /* make sure interpolated record is `:'-terminated */ 45255868Sbostic s += newilen; 45355868Sbostic if (*(s-1) != ':') { 45455868Sbostic *s = ':'; /* overwrite NUL with : */ 45555868Sbostic newilen++; 45655868Sbostic } 45755868Sbostic 45855868Sbostic /* 45955868Sbostic * Make sure there's enough room to insert the 46055868Sbostic * new record. 46155868Sbostic */ 46255868Sbostic diff = newilen - tclen; 46355868Sbostic if (diff >= r_end - rp) { 46455875Selan u_int pos, tcpos, tcposend; 46555875Selan size_t newsize; 46655868Sbostic 46755868Sbostic pos = rp - record; 46855868Sbostic newsize = r_end - record + diff + BFRAG; 46955868Sbostic tcpos = tcstart - record; 47055868Sbostic tcposend = tcend - record; 47155868Sbostic record = realloc(record, newsize); 47255868Sbostic if (record == NULL) { 47355868Sbostic errno = ENOMEM; 47455868Sbostic if (myfd) 47555868Sbostic (void)close(fd); 47655868Sbostic free(icap); 47755868Sbostic return (-2); 47855868Sbostic } 47955868Sbostic r_end = record + newsize; 48055868Sbostic rp = record + pos; 48155868Sbostic tcstart = record + tcpos; 48255868Sbostic tcend = record + tcposend; 48355868Sbostic } 48455868Sbostic 48555868Sbostic /* 48655868Sbostic * Insert tc'ed record into our record. 48755868Sbostic */ 48855868Sbostic s = tcstart + newilen; 48955868Sbostic bcopy(tcend, s, rp - tcend); 49055868Sbostic bcopy(newicap, tcstart, newilen); 49155868Sbostic rp += diff; 49255868Sbostic free(icap); 49355868Sbostic 49455868Sbostic /* 49555868Sbostic * Start scan on `:' so next cgetcap works properly 49655868Sbostic * (cgetcap always skips first field). 49755868Sbostic */ 49855868Sbostic scan = s-1; 49955868Sbostic } 50055868Sbostic } 50155868Sbostic 50255868Sbostic /* 50355868Sbostic * Close file (if we opened it), give back any extra memory, and 50455868Sbostic * return capability, length and success. 50555868Sbostic */ 50655868Sbostic if (myfd) 50755868Sbostic (void)close(fd); 50855868Sbostic *len = rp - record - 1; /* don't count NUL */ 50955868Sbostic if (r_end > rp) 510*56184Selan if ((record = 511*56184Selan realloc(record, (size_t)(rp - record))) == NULL) { 512*56184Selan errno = ENOMEM; 513*56184Selan return (-2); 514*56184Selan } 515*56184Selan 51655868Sbostic *cap = record; 51755915Selan if (tc_not_resolved) 51855915Selan return (1); 51955868Sbostic return (0); 52055868Sbostic } 52155868Sbostic 52255915Selan static int 52355915Selan cdbget(capdbp, bp, name) 52455915Selan DB *capdbp; 52555915Selan char **bp, *name; 52655915Selan { 52755915Selan DBT key, data; 52855915Selan char *buf; 52955915Selan int st; 53055915Selan 53155915Selan key.data = name; 53255915Selan key.size = strlen(name) + 1; 53355915Selan 53455915Selan if ((st = capdbp->get(capdbp, &key, &data, 0)) < 0) 53555915Selan return(-2); 53655915Selan if (st == 1) 53755915Selan return(-1); 53855915Selan 53955915Selan if (((char *)(data.data))[0] == RECORD) { 54055915Selan *bp = &((char *)(data.data))[1]; 54155915Selan return(0); 54255915Selan } 54355915Selan if ((buf = malloc(data.size - 1)) == NULL) 54455915Selan return(-2); 54555915Selan 54655915Selan strcpy(buf, &((char *)(data.data))[1]); 54755915Selan 54855915Selan key.data = buf; 54955915Selan key.size = data.size - 1; 55055915Selan 55155915Selan if (capdbp->get(capdbp, &key, &data, 0) < 0) { 55255915Selan free(buf); 55355915Selan return(-2); 55455915Selan } 55555915Selan free(buf); 55655915Selan *bp = &((char *)(data.data))[1]; 55755915Selan return(0); 55855915Selan } 55955915Selan 56055915Selan 56155868Sbostic /* 56255868Sbostic * Cgetmatch will return 0 if name is one of the names of the capability 56355868Sbostic * record buf, -1 if not. 56455868Sbostic */ 56555868Sbostic int 56655868Sbostic cgetmatch(buf, name) 56755868Sbostic char *buf, *name; 56855868Sbostic { 56955868Sbostic register char *np, *bp; 57055868Sbostic 57155868Sbostic /* 57255868Sbostic * Start search at beginning of record. 57355868Sbostic */ 57455868Sbostic bp = buf; 57555868Sbostic for (;;) { 57655868Sbostic /* 57755868Sbostic * Try to match a record name. 57855868Sbostic */ 57955868Sbostic np = name; 58055868Sbostic for (;;) 58155868Sbostic if (*np == '\0') 58255868Sbostic if (*bp == '|' || *bp == ':' || *bp == '\0') 58355868Sbostic return (0); 58455868Sbostic else 58555868Sbostic break; 58655868Sbostic else 58755868Sbostic if (*bp++ != *np++) 58855868Sbostic break; 58955868Sbostic 59055868Sbostic /* 59155868Sbostic * Match failed, skip to next name in record. 59255868Sbostic */ 59355868Sbostic bp--; /* a '|' or ':' may have stopped the match */ 59455868Sbostic for (;;) 59555868Sbostic if (*bp == '\0' || *bp == ':') 59655868Sbostic return (-1); /* match failed totally */ 59755868Sbostic else 59855868Sbostic if (*bp++ == '|') 59955868Sbostic break; /* found next name */ 60055868Sbostic } 60155868Sbostic } 60255868Sbostic 60355876Selan 60455876Selan 60555876Selan 60655876Selan 60755868Sbostic int 60855868Sbostic cgetfirst(buf, db_array) 60955868Sbostic char **buf, **db_array; 61055868Sbostic { 61155868Sbostic (void)cgetclose(); 61255868Sbostic return (cgetnext(buf, db_array)); 61355868Sbostic } 61455868Sbostic 61555868Sbostic static FILE *pfp; 61655868Sbostic static int slash; 61755868Sbostic static char **dbp; 61855868Sbostic 61955868Sbostic int 62055868Sbostic cgetclose() 62155868Sbostic { 62255868Sbostic if (pfp != NULL) { 62355868Sbostic (void)fclose(pfp); 62455868Sbostic pfp = NULL; 62555868Sbostic } 62655868Sbostic dbp = NULL; 62755875Selan gottoprec = 0; 62855868Sbostic slash = 0; 62955875Selan return(0); 63055868Sbostic } 63155868Sbostic 63255868Sbostic /* 63355868Sbostic * Cgetnext() gets either the first or next entry in the logical database 63455868Sbostic * specified by db_array. It returns 0 upon completion of the database, 1 63555868Sbostic * upon returning an entry with more remaining, and -1 if an error occurs. 63655868Sbostic */ 63755868Sbostic int 63855868Sbostic cgetnext(bp, db_array) 63955868Sbostic register char **bp; 64055868Sbostic char **db_array; 64155868Sbostic { 64255868Sbostic size_t len; 64355868Sbostic int status; 64455868Sbostic char *cp, *line, *rp, buf[BSIZE]; 64555876Selan u_int dummy; 64655868Sbostic 64755876Selan if (dbp == NULL) 64855868Sbostic dbp = db_array; 64955876Selan 65055875Selan if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) { 65155875Selan (void)cgetclose(); 65255868Sbostic return (-1); 65355875Selan } 65455868Sbostic for(;;) { 65555876Selan if (toprec && !gottoprec) { 65655876Selan gottoprec = 1; 65755876Selan line = toprec; 65855876Selan } else { 65955876Selan line = fgetline(pfp, &len); 66055876Selan if (line == NULL && pfp) { 66155876Selan (void)fclose(pfp); 66255876Selan if (ferror(pfp)) { 66355875Selan (void)cgetclose(); 66455868Sbostic return (-1); 66555876Selan } else { 66655876Selan dbp++; 66755876Selan if (*dbp == NULL) { 66855876Selan (void)cgetclose(); 66955876Selan return (0); 67055876Selan } else if ((pfp = fopen(*dbp, "r")) == 67155876Selan NULL) { 67255876Selan (void)cgetclose(); 67355876Selan return (-1); 67455876Selan } else 67555876Selan continue; 67655876Selan } 67755868Sbostic } 67855876Selan if (isspace(*line) || *line == ':' || *line == '#' 67955876Selan || len == 0 || slash) { 68055876Selan if (len > 0 && line[len - 1] == '\\') 68155876Selan slash = 1; 68255876Selan else 68355876Selan slash = 0; 68455876Selan continue; 68555876Selan } 68655868Sbostic if (len > 0 && line[len - 1] == '\\') 68755868Sbostic slash = 1; 68855868Sbostic else 68955868Sbostic slash = 0; 69055876Selan } 69155868Sbostic /* line points to a name line */ 69255868Sbostic 69355868Sbostic rp = buf; 69455868Sbostic for(cp = line; *cp != NULL; cp++) 69555868Sbostic if (*cp == '|' || *cp == ':') 69655868Sbostic break; 69755868Sbostic else 69855868Sbostic *rp++ = *cp; 69955868Sbostic 70055868Sbostic *rp = '\0'; 70155876Selan status = getent(bp, &dummy, db_array, -1, buf, 0); 70255915Selan if (status == -2 || status == -3) 70355875Selan (void)cgetclose(); 70455915Selan 70555915Selan return (status + 1); 70655868Sbostic } 70755868Sbostic /* NOTREACHED */ 70855868Sbostic } 70955868Sbostic 71055868Sbostic /* 71155868Sbostic * Cgetstr retrieves the value of the string capability cap from the 71255868Sbostic * capability record pointed to by buf. A pointer to a decoded, NUL 71355868Sbostic * terminated, malloc'd copy of the string is returned in the char * 71455868Sbostic * pointed to by str. The length of the string not including the trailing 71555868Sbostic * NUL is returned on success, -1 if the requested string capability 71655868Sbostic * couldn't be found, -2 if a system error was encountered (storage 71755868Sbostic * allocation failure). 71855868Sbostic */ 71955868Sbostic int 72055868Sbostic cgetstr(buf, cap, str) 72155868Sbostic char *buf, *cap; 72255868Sbostic char **str; 72355868Sbostic { 72455868Sbostic register u_int m_room; 72555868Sbostic register char *bp, *mp; 72655868Sbostic int len; 72755868Sbostic char *mem; 72855868Sbostic 72955868Sbostic /* 73055868Sbostic * Find string capability cap 73155868Sbostic */ 73255868Sbostic bp = cgetcap(buf, cap, '='); 73355868Sbostic if (bp == NULL) 73455868Sbostic return (-1); 73555868Sbostic 73655868Sbostic /* 73755868Sbostic * Conversion / storage allocation loop ... Allocate memory in 73855868Sbostic * chunks SFRAG in size. 73955868Sbostic */ 74055868Sbostic if ((mem = malloc(SFRAG)) == NULL) { 74155868Sbostic errno = ENOMEM; 74255868Sbostic return (-2); /* couldn't even allocate the first fragment */ 74355868Sbostic } 74455868Sbostic m_room = SFRAG; 74555868Sbostic mp = mem; 74655868Sbostic 74755868Sbostic while (*bp != ':' && *bp != '\0') { 74855868Sbostic /* 74955868Sbostic * Loop invariants: 75055868Sbostic * There is always room for one more character in mem. 75155868Sbostic * Mp always points just past last character in mem. 75255868Sbostic * Bp always points at next character in buf. 75355868Sbostic */ 75455868Sbostic if (*bp == '^') { 75555868Sbostic bp++; 75655868Sbostic if (*bp == ':' || *bp == '\0') 75755868Sbostic break; /* drop unfinished escape */ 75855868Sbostic *mp++ = *bp++ & 037; 75955868Sbostic } else if (*bp == '\\') { 76055868Sbostic bp++; 76155868Sbostic if (*bp == ':' || *bp == '\0') 76255868Sbostic break; /* drop unfinished escape */ 76355868Sbostic if ('0' <= *bp && *bp <= '7') { 76455868Sbostic register int n, i; 76555868Sbostic 76655868Sbostic n = 0; 76755868Sbostic i = 3; /* maximum of three octal digits */ 76855868Sbostic do { 76955868Sbostic n = n * 8 + (*bp++ - '0'); 77055868Sbostic } while (--i && '0' <= *bp && *bp <= '7'); 77155868Sbostic *mp++ = n; 77255868Sbostic } 77355868Sbostic else switch (*bp++) { 77455868Sbostic case 'b': case 'B': 77555868Sbostic *mp++ = '\b'; 77655868Sbostic break; 77755868Sbostic case 't': case 'T': 77855868Sbostic *mp++ = '\t'; 77955868Sbostic break; 78055868Sbostic case 'n': case 'N': 78155868Sbostic *mp++ = '\n'; 78255868Sbostic break; 78355868Sbostic case 'f': case 'F': 78455868Sbostic *mp++ = '\f'; 78555868Sbostic break; 78655868Sbostic case 'r': case 'R': 78755868Sbostic *mp++ = '\r'; 78855868Sbostic break; 78955868Sbostic case 'e': case 'E': 79055868Sbostic *mp++ = ESC; 79155868Sbostic break; 79255868Sbostic case 'c': case 'C': 79355868Sbostic *mp++ = ':'; 79455868Sbostic break; 79555868Sbostic default: 79655868Sbostic /* 79755868Sbostic * Catches '\', '^', and 79855868Sbostic * everything else. 79955868Sbostic */ 80055868Sbostic *mp++ = *(bp-1); 80155868Sbostic break; 80255868Sbostic } 80355868Sbostic } else 80455868Sbostic *mp++ = *bp++; 80555868Sbostic m_room--; 80655868Sbostic 80755868Sbostic /* 80855868Sbostic * Enforce loop invariant: if no room left in current 80955868Sbostic * buffer, try to get some more. 81055868Sbostic */ 81155868Sbostic if (m_room == 0) { 81255875Selan size_t size = mp - mem; 81355868Sbostic 81455868Sbostic if ((mem = realloc(mem, size + SFRAG)) == NULL) 81555868Sbostic return (-2); 81655868Sbostic m_room = SFRAG; 81755868Sbostic mp = mem + size; 81855868Sbostic } 81955868Sbostic } 82055868Sbostic *mp++ = '\0'; /* loop invariant let's us do this */ 82155868Sbostic m_room--; 82255868Sbostic len = mp - mem - 1; 82355868Sbostic 82455868Sbostic /* 82555868Sbostic * Give back any extra memory and return value and success. 82655868Sbostic */ 82755868Sbostic if (m_room != 0) 828*56184Selan if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 829*56184Selan return (-2); 83055868Sbostic *str = mem; 83155868Sbostic return (len); 83255868Sbostic } 83355868Sbostic 83455868Sbostic /* 83555868Sbostic * Cgetustr retrieves the value of the string capability cap from the 83655868Sbostic * capability record pointed to by buf. The difference between cgetustr() 83755868Sbostic * and cgetstr() is that cgetustr does not decode escapes but rather treats 83855868Sbostic * all characters literally. A pointer to a NUL terminated malloc'd 83955868Sbostic * copy of the string is returned in the char pointed to by str. The 84055868Sbostic * length of the string not including the trailing NUL is returned on success, 84155868Sbostic * -1 if the requested string capability couldn't be found, -2 if a system 84255868Sbostic * error was encountered (storage allocation failure). 84355868Sbostic */ 84455868Sbostic int 84555868Sbostic cgetustr(buf, cap, str) 84655868Sbostic char *buf, *cap, **str; 84755868Sbostic { 84855868Sbostic register u_int m_room; 84955868Sbostic register char *bp, *mp; 85055868Sbostic int len; 85155868Sbostic char *mem; 85255868Sbostic 85355868Sbostic /* 85455868Sbostic * Find string capability cap 85555868Sbostic */ 85655868Sbostic if ((bp = cgetcap(buf, cap, '=')) == NULL) 85755868Sbostic return (-1); 85855868Sbostic 85955868Sbostic /* 86055868Sbostic * Conversion / storage allocation loop ... Allocate memory in 86155868Sbostic * chunks SFRAG in size. 86255868Sbostic */ 86355868Sbostic if ((mem = malloc(SFRAG)) == NULL) { 86455868Sbostic errno = ENOMEM; 86555868Sbostic return (-2); /* couldn't even allocate the first fragment */ 86655868Sbostic } 86755868Sbostic m_room = SFRAG; 86855868Sbostic mp = mem; 86955868Sbostic 87055868Sbostic while (*bp != ':' && *bp != '\0') { 87155868Sbostic /* 87255868Sbostic * Loop invariants: 87355868Sbostic * There is always room for one more character in mem. 87455868Sbostic * Mp always points just past last character in mem. 87555868Sbostic * Bp always points at next character in buf. 87655868Sbostic */ 87755868Sbostic *mp++ = *bp++; 87855868Sbostic m_room--; 87955868Sbostic 88055868Sbostic /* 88155868Sbostic * Enforce loop invariant: if no room left in current 88255868Sbostic * buffer, try to get some more. 88355868Sbostic */ 88455868Sbostic if (m_room == 0) { 88555875Selan size_t size = mp - mem; 88655868Sbostic 88755868Sbostic if ((mem = realloc(mem, size + SFRAG)) == NULL) 88855868Sbostic return (-2); 88955868Sbostic m_room = SFRAG; 89055868Sbostic mp = mem + size; 89155868Sbostic } 89255868Sbostic } 89355868Sbostic *mp++ = '\0'; /* loop invariant let's us do this */ 89455868Sbostic m_room--; 89555868Sbostic len = mp - mem - 1; 89655868Sbostic 89755868Sbostic /* 89855868Sbostic * Give back any extra memory and return value and success. 89955868Sbostic */ 90055868Sbostic if (m_room != 0) 901*56184Selan if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 902*56184Selan return (-2); 90355868Sbostic *str = mem; 90455868Sbostic return (len); 90555868Sbostic } 90655868Sbostic 90755868Sbostic /* 90855868Sbostic * Cgetnum retrieves the value of the numeric capability cap from the 90955868Sbostic * capability record pointed to by buf. The numeric value is returned in 91055868Sbostic * the long pointed to by num. 0 is returned on success, -1 if the requested 91155868Sbostic * numeric capability couldn't be found. 91255868Sbostic */ 91355868Sbostic int 91455868Sbostic cgetnum(buf, cap, num) 91555868Sbostic char *buf, *cap; 91655868Sbostic long *num; 91755868Sbostic { 91855868Sbostic register long n; 91955868Sbostic register int base, digit; 92055868Sbostic register char *bp; 92155868Sbostic 92255868Sbostic /* 92355868Sbostic * Find numeric capability cap 92455868Sbostic */ 92555868Sbostic bp = cgetcap(buf, cap, '#'); 92655868Sbostic if (bp == NULL) 92755868Sbostic return (-1); 92855868Sbostic 92955868Sbostic /* 93055868Sbostic * Look at value and determine numeric base: 93155868Sbostic * 0x... or 0X... hexadecimal, 93255868Sbostic * else 0... octal, 93355868Sbostic * else decimal. 93455868Sbostic */ 93555868Sbostic if (*bp == '0') { 93655868Sbostic bp++; 93755868Sbostic if (*bp == 'x' || *bp == 'X') { 93855868Sbostic bp++; 93955868Sbostic base = 16; 94055868Sbostic } else 94155868Sbostic base = 8; 94255868Sbostic } else 94355868Sbostic base = 10; 94455868Sbostic 94555868Sbostic /* 94655868Sbostic * Conversion loop ... 94755868Sbostic */ 94855868Sbostic n = 0; 94955868Sbostic for (;;) { 95055868Sbostic if ('0' <= *bp && *bp <= '9') 95155868Sbostic digit = *bp - '0'; 95255868Sbostic else 95355868Sbostic if ( ('a' <= *bp && *bp <= 'f') 95455868Sbostic || ('A' <= *bp && *bp <= 'F')) 95555868Sbostic digit = 10 + *bp - 'a'; 95655868Sbostic else 95755868Sbostic break; 95855868Sbostic 95955868Sbostic if (digit >= base) 96055868Sbostic break; 96155868Sbostic 96255868Sbostic n = n * base + digit; 96355868Sbostic bp++; 96455868Sbostic } 96555868Sbostic 96655868Sbostic /* 96755868Sbostic * Return value and success. 96855868Sbostic */ 96955868Sbostic *num = n; 97055868Sbostic return (0); 97155868Sbostic } 972