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 55*56197Selan #define BSIZE 1024 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 *)); 68*56197Selan static int getent __P((char **, u_int *, char **, int, char *, int, char *)); 69*56197Selan static int nfcmp __P((char *, char *)); 7055868Sbostic 7155868Sbostic /* 7255868Sbostic * Cgetset() allows the addition of a user specified buffer to be added 7355868Sbostic * to the database array, in effect "pushing" the buffer on top of the 7455868Sbostic * virtual database. 0 is returned on success, -1 on failure. 7555868Sbostic */ 7655868Sbostic int 7755868Sbostic cgetset(ent) 7855868Sbostic char *ent; 7955868Sbostic { 8055868Sbostic if (ent == NULL) { 8155868Sbostic if (toprec) 8255868Sbostic free(toprec); 8355868Sbostic toprec = NULL; 8455868Sbostic topreclen = 0; 8555868Sbostic return (0); 8655868Sbostic } 8755868Sbostic topreclen = strlen(ent); 8855868Sbostic if ((toprec = malloc (topreclen + 1)) == NULL) { 8955868Sbostic errno = ENOMEM; 9055868Sbostic return (-1); 9155868Sbostic } 9255876Selan gottoprec = 0; 9355868Sbostic (void)strcpy(toprec, ent); 9455868Sbostic return (0); 9555868Sbostic } 9655868Sbostic 9755868Sbostic /* 9855868Sbostic * Cgetcap searches the capability record buf for the capability cap with 9955868Sbostic * type `type'. A pointer to the value of cap is returned on success, NULL 10055868Sbostic * if the requested capability couldn't be found. 10155868Sbostic * 10255868Sbostic * Specifying a type of ':' means that nothing should follow cap (:cap:). 10355868Sbostic * In this case a pointer to the terminating ':' or NUL will be returned if 10455868Sbostic * cap is found. 10555868Sbostic * 10655868Sbostic * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator) 10755868Sbostic * return NULL. 10855868Sbostic */ 10955868Sbostic char * 11055868Sbostic cgetcap(buf, cap, type) 11155868Sbostic char *buf, *cap; 11255868Sbostic int type; 11355868Sbostic { 11455868Sbostic register char *bp, *cp; 11555868Sbostic 11655868Sbostic bp = buf; 11755868Sbostic for (;;) { 11855868Sbostic /* 11955868Sbostic * Skip past the current capability field - it's either the 12055868Sbostic * name field if this is the first time through the loop, or 12155868Sbostic * the remainder of a field whose name failed to match cap. 12255868Sbostic */ 12355868Sbostic for (;;) 12455868Sbostic if (*bp == '\0') 12555868Sbostic return (NULL); 12655868Sbostic else 12755868Sbostic if (*bp++ == ':') 12855868Sbostic break; 12955868Sbostic 13055868Sbostic /* 13155868Sbostic * Try to match (cap, type) in buf. 13255868Sbostic */ 13355868Sbostic for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++) 13455868Sbostic continue; 13555868Sbostic if (*cp != '\0') 13655868Sbostic continue; 13755868Sbostic if (*bp == '@') 13855868Sbostic return (NULL); 13955868Sbostic if (type == ':') { 14055868Sbostic if (*bp != '\0' && *bp != ':') 14155868Sbostic continue; 14255868Sbostic return(bp); 14355868Sbostic } 14455868Sbostic if (*bp != type) 14555868Sbostic continue; 14655868Sbostic bp++; 14755868Sbostic return (*bp == '@' ? NULL : bp); 14855868Sbostic } 14955868Sbostic /* NOTREACHED */ 15055868Sbostic } 15155868Sbostic 15255868Sbostic /* 15355868Sbostic * Cgetent extracts the capability record name from the NULL terminated file 15455868Sbostic * array db_array and returns a pointer to a malloc'd copy of it in buf. 15555868Sbostic * Buf must be retained through all subsequent calls to cgetcap, cgetnum, 15655868Sbostic * cgetflag, and cgetstr, but may then be free'd. 0 is returned on success, 15755868Sbostic * -1 if the requested record couldn't be found, -2 if a system error was 15855868Sbostic * encountered (couldn't open/read a file, etc.), and -3 if a potential 15955868Sbostic * reference loop is detected. 16055868Sbostic */ 16155868Sbostic int 16255868Sbostic cgetent(buf, db_array, name) 16355868Sbostic char **buf, **db_array, *name; 16455868Sbostic { 16555868Sbostic u_int dummy; 16655868Sbostic 167*56197Selan return (getent(buf, &dummy, db_array, -1, name, 0, NULL)); 16855868Sbostic } 16955868Sbostic 17055868Sbostic /* 17155868Sbostic * Getent implements the functions of cgetent. If fd is non-negative, 17255868Sbostic * *db_array has already been opened and fd is the open file descriptor. We 17355868Sbostic * do this to save time and avoid using up file descriptors for tc= 17455868Sbostic * recursions. 17555868Sbostic * 17655868Sbostic * Getent returns the same success/failure codes as cgetent. On success, a 17755868Sbostic * pointer to a malloc'ed capability record with all tc= capabilities fully 17855868Sbostic * expanded and its length (not including trailing ASCII NUL) are left in 17955868Sbostic * *cap and *len. 18055868Sbostic * 18155868Sbostic * Basic algorithm: 18255868Sbostic * + Allocate memory incrementally as needed in chunks of size BFRAG 18355868Sbostic * for capability buffer. 18455868Sbostic * + Recurse for each tc=name and interpolate result. Stop when all 18555868Sbostic * names interpolated, a name can't be found, or depth exceeds 18655868Sbostic * MAX_RECURSION. 18755868Sbostic */ 18855868Sbostic static int 189*56197Selan getent(cap, len, db_array, fd, name, depth, nfield) 190*56197Selan char **cap, **db_array, *name, *nfield; 19155868Sbostic u_int *len; 19255868Sbostic int fd, depth; 19355868Sbostic { 19455915Selan DB *capdbp; 19555915Selan DBT key, data; 19655868Sbostic register char *r_end, *rp, **db_p; 19755915Selan int myfd, eof, foundit, retval; 19855868Sbostic char *record; 19955915Selan int tc_not_resolved; 20055915Selan char pbuf[_POSIX_PATH_MAX]; 20155915Selan 20255868Sbostic /* 20355868Sbostic * Return with ``loop detected'' error if we've recursed more than 20455868Sbostic * MAX_RECURSION times. 20555868Sbostic */ 20655868Sbostic if (depth > MAX_RECURSION) 20755868Sbostic return (-3); 20855868Sbostic 20955868Sbostic /* 21055868Sbostic * Check if we have a top record from cgetset(). 21155868Sbostic */ 21255876Selan if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) { 21355868Sbostic if ((record = malloc (topreclen + BFRAG)) == NULL) { 21455868Sbostic errno = ENOMEM; 21555868Sbostic return (-2); 21655868Sbostic } 21755868Sbostic (void)strcpy(record, toprec); 21855868Sbostic myfd = 0; 21955868Sbostic db_p = db_array; 22055868Sbostic rp = record + topreclen + 1; 22155868Sbostic r_end = rp + BFRAG; 22255868Sbostic goto tc_exp; 22355868Sbostic } 22455868Sbostic /* 22555868Sbostic * Allocate first chunk of memory. 22655868Sbostic */ 22755868Sbostic if ((record = malloc(BFRAG)) == NULL) { 22855868Sbostic errno = ENOMEM; 22955868Sbostic return (-2); 23055868Sbostic } 23155868Sbostic r_end = record + BFRAG; 23255868Sbostic foundit = 0; 23355868Sbostic /* 23455868Sbostic * Loop through database array until finding the record. 23555868Sbostic */ 23655868Sbostic 23755868Sbostic for (db_p = db_array; *db_p != NULL; db_p++) { 23855868Sbostic eof = 0; 23955868Sbostic 24055868Sbostic /* 24155868Sbostic * Open database if not already open. 24255868Sbostic */ 24355915Selan 24455868Sbostic if (fd >= 0) { 24555868Sbostic (void)lseek(fd, 0L, L_SET); 24655868Sbostic myfd = 0; 24755868Sbostic } else { 24855915Selan sprintf(pbuf, "%s.db", *db_p); 24955915Selan if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0)) 25055915Selan != NULL) { 25155868Sbostic free(record); 25255915Selan retval = cdbget(capdbp, &record, name); 25355915Selan if (capdbp->close(capdbp) < 0) 25455915Selan return (-2); 25555915Selan if (retval < 0) 25655915Selan return (retval); 25755915Selan rp = record + strlen(record) + 1; 25855915Selan myfd = 0; 25955915Selan goto tc_exp; 26055915Selan } else { 26155915Selan fd = open(*db_p, O_RDONLY, 0); 26255915Selan if (fd < 0) { 26355915Selan free(record); 26455915Selan return (-2); 26555915Selan } 26655915Selan myfd = 1; 26755868Sbostic } 26855868Sbostic } 26955868Sbostic /* 27055868Sbostic * Find the requested capability record ... 27155868Sbostic */ 27255868Sbostic { 27355868Sbostic char buf[BUFSIZ]; 27455868Sbostic register char *b_end, *bp; 27555868Sbostic register int c; 27655868Sbostic 27755868Sbostic /* 27855868Sbostic * Loop invariants: 27955868Sbostic * There is always room for one more character in record. 28055868Sbostic * R_end always points just past end of record. 28155868Sbostic * Rp always points just past last character in record. 28255868Sbostic * B_end always points just past last character in buf. 28355868Sbostic * Bp always points at next character in buf. 28455868Sbostic */ 28555868Sbostic b_end = buf; 28655868Sbostic bp = buf; 28755868Sbostic for (;;) { 28855868Sbostic 28955868Sbostic /* 29055868Sbostic * Read in a line implementing (\, newline) 29155868Sbostic * line continuation. 29255868Sbostic */ 29355868Sbostic rp = record; 29455868Sbostic for (;;) { 29555868Sbostic if (bp >= b_end) { 29655868Sbostic int n; 29755868Sbostic 29855868Sbostic n = read(fd, buf, sizeof(buf)); 29955868Sbostic if (n <= 0) { 30055868Sbostic if (myfd) 30155868Sbostic (void)close(fd); 30255868Sbostic if (n < 0) { 30355868Sbostic free(record); 30455868Sbostic return (-2); 30555868Sbostic } else { 30655868Sbostic fd = -1; 30755868Sbostic eof = 1; 30855868Sbostic break; 30955868Sbostic } 31055868Sbostic } 31155868Sbostic b_end = buf+n; 31255868Sbostic bp = buf; 31355868Sbostic } 31455868Sbostic 31555868Sbostic c = *bp++; 31655868Sbostic if (c == '\n') { 31755868Sbostic if (rp > record && *(rp-1) == '\\') { 31855868Sbostic rp--; 31955868Sbostic continue; 32055868Sbostic } else 32155868Sbostic break; 32255868Sbostic } 32355868Sbostic *rp++ = c; 32455868Sbostic 32555868Sbostic /* 32655868Sbostic * Enforce loop invariant: if no room 32755868Sbostic * left in record buffer, try to get 32855868Sbostic * some more. 32955868Sbostic */ 33055868Sbostic if (rp >= r_end) { 33155875Selan u_int pos; 33255875Selan size_t newsize; 33355868Sbostic 33455868Sbostic pos = rp - record; 33555868Sbostic newsize = r_end - record + BFRAG; 33655868Sbostic record = realloc(record, newsize); 33755868Sbostic if (record == NULL) { 33855868Sbostic errno = ENOMEM; 33955868Sbostic if (myfd) 34055868Sbostic (void)close(fd); 34155868Sbostic return (-2); 34255868Sbostic } 34355868Sbostic r_end = record + newsize; 34455868Sbostic rp = record + pos; 34555868Sbostic } 34655868Sbostic } 34755868Sbostic /* loop invariant let's us do this */ 34855868Sbostic *rp++ = '\0'; 34955868Sbostic 35055868Sbostic /* 35155868Sbostic * If encountered eof check next file. 35255868Sbostic */ 35355868Sbostic if (eof) 35455868Sbostic break; 35555868Sbostic 35655868Sbostic /* 35755868Sbostic * Toss blank lines and comments. 35855868Sbostic */ 35955868Sbostic if (*record == '\0' || *record == '#') 36055868Sbostic continue; 36155868Sbostic 36255868Sbostic /* 36355868Sbostic * See if this is the record we want ... 36455868Sbostic */ 36555868Sbostic if (cgetmatch(record, name) == 0) { 366*56197Selan if (nfield == NULL || !nfcmp(nfield, record)) { 367*56197Selan foundit = 1; 368*56197Selan break; /* found it! */ 369*56197Selan } 37055868Sbostic } 37155868Sbostic } 372*56197Selan } 37355868Sbostic if (foundit) 37455868Sbostic break; 37555868Sbostic } 37655868Sbostic 37755868Sbostic if (!foundit) 37855868Sbostic return (-1); 37955876Selan 38055868Sbostic /* 38155868Sbostic * Got the capability record, but now we have to expand all tc=name 38255868Sbostic * references in it ... 38355868Sbostic */ 38455868Sbostic tc_exp: { 38555868Sbostic register char *newicap, *s; 38655868Sbostic register int newilen; 38755868Sbostic u_int ilen; 38855868Sbostic int diff, iret, tclen; 38955868Sbostic char *icap, *scan, *tc, *tcstart, *tcend; 39055868Sbostic 39155868Sbostic /* 39255868Sbostic * Loop invariants: 39355868Sbostic * There is room for one more character in record. 39455868Sbostic * R_end points just past end of record. 39555868Sbostic * Rp points just past last character in record. 39655868Sbostic * Scan points at remainder of record that needs to be 39755868Sbostic * scanned for tc=name constructs. 39855868Sbostic */ 39955868Sbostic scan = record; 40055915Selan tc_not_resolved = 0; 40155868Sbostic for (;;) { 40255868Sbostic if ((tc = cgetcap(scan, "tc", '=')) == NULL) 40355868Sbostic break; 40455868Sbostic 40555868Sbostic /* 40655868Sbostic * Find end of tc=name and stomp on the trailing `:' 40755868Sbostic * (if present) so we can use it to call ourselves. 40855868Sbostic */ 40955868Sbostic s = tc; 41055868Sbostic for (;;) 41155868Sbostic if (*s == '\0') 41255868Sbostic break; 41355868Sbostic else 41455868Sbostic if (*s++ == ':') { 41556184Selan *(s - 1) = '\0'; 41655868Sbostic break; 41755868Sbostic } 41855868Sbostic tcstart = tc - 3; 41955868Sbostic tclen = s - tcstart; 42055868Sbostic tcend = s; 42155868Sbostic 422*56197Selan iret = getent(&icap, &ilen, db_p, fd, tc, depth+1, 423*56197Selan NULL); 42455868Sbostic newicap = icap; /* Put into a register. */ 42555868Sbostic newilen = ilen; 42655868Sbostic if (iret != 0) { 42755915Selan /* couldn't resolve tc */ 42855915Selan if (iret == 1) 42955915Selan tc_not_resolved = 1; 43055915Selan if (iret == -1) { 43156184Selan *(s - 1) = ':'; 43255915Selan scan = s - 1; 43355915Selan tc_not_resolved = 1; 43455915Selan continue; 43555915Selan 43655915Selan } 43755915Selan /* an error */ 43855868Sbostic if (myfd) 43955868Sbostic (void)close(fd); 44055868Sbostic free(record); 44155868Sbostic return (iret); 44255868Sbostic } 44355868Sbostic 44455868Sbostic /* not interested in name field of tc'ed record */ 44555868Sbostic s = newicap; 44655868Sbostic for (;;) 44755868Sbostic if (*s == '\0') 44855868Sbostic break; 44955868Sbostic else 45055868Sbostic if (*s++ == ':') 45155868Sbostic break; 45255868Sbostic newilen -= s - newicap; 45355868Sbostic newicap = s; 45455868Sbostic 45555868Sbostic /* make sure interpolated record is `:'-terminated */ 45655868Sbostic s += newilen; 45755868Sbostic if (*(s-1) != ':') { 45855868Sbostic *s = ':'; /* overwrite NUL with : */ 45955868Sbostic newilen++; 46055868Sbostic } 46155868Sbostic 46255868Sbostic /* 46355868Sbostic * Make sure there's enough room to insert the 46455868Sbostic * new record. 46555868Sbostic */ 46655868Sbostic diff = newilen - tclen; 46755868Sbostic if (diff >= r_end - rp) { 46855875Selan u_int pos, tcpos, tcposend; 46955875Selan size_t newsize; 47055868Sbostic 47155868Sbostic pos = rp - record; 47255868Sbostic newsize = r_end - record + diff + BFRAG; 47355868Sbostic tcpos = tcstart - record; 47455868Sbostic tcposend = tcend - record; 47555868Sbostic record = realloc(record, newsize); 47655868Sbostic if (record == NULL) { 47755868Sbostic errno = ENOMEM; 47855868Sbostic if (myfd) 47955868Sbostic (void)close(fd); 48055868Sbostic free(icap); 48155868Sbostic return (-2); 48255868Sbostic } 48355868Sbostic r_end = record + newsize; 48455868Sbostic rp = record + pos; 48555868Sbostic tcstart = record + tcpos; 48655868Sbostic tcend = record + tcposend; 48755868Sbostic } 48855868Sbostic 48955868Sbostic /* 49055868Sbostic * Insert tc'ed record into our record. 49155868Sbostic */ 49255868Sbostic s = tcstart + newilen; 49355868Sbostic bcopy(tcend, s, rp - tcend); 49455868Sbostic bcopy(newicap, tcstart, newilen); 49555868Sbostic rp += diff; 49655868Sbostic free(icap); 49755868Sbostic 49855868Sbostic /* 49955868Sbostic * Start scan on `:' so next cgetcap works properly 50055868Sbostic * (cgetcap always skips first field). 50155868Sbostic */ 50255868Sbostic scan = s-1; 50355868Sbostic } 504*56197Selan 50555868Sbostic } 50655868Sbostic /* 50755868Sbostic * Close file (if we opened it), give back any extra memory, and 50855868Sbostic * return capability, length and success. 50955868Sbostic */ 51055868Sbostic if (myfd) 51155868Sbostic (void)close(fd); 51255868Sbostic *len = rp - record - 1; /* don't count NUL */ 51355868Sbostic if (r_end > rp) 51456184Selan if ((record = 51556184Selan realloc(record, (size_t)(rp - record))) == NULL) { 51656184Selan errno = ENOMEM; 51756184Selan return (-2); 51856184Selan } 51956184Selan 52055868Sbostic *cap = record; 52155915Selan if (tc_not_resolved) 52255915Selan return (1); 52355868Sbostic return (0); 524*56197Selan } 52555868Sbostic 52655915Selan static int 52755915Selan cdbget(capdbp, bp, name) 52855915Selan DB *capdbp; 52955915Selan char **bp, *name; 53055915Selan { 53155915Selan DBT key, data; 53255915Selan char *buf; 53355915Selan int st; 53455915Selan 53555915Selan key.data = name; 53655915Selan key.size = strlen(name) + 1; 53755915Selan 53855915Selan if ((st = capdbp->get(capdbp, &key, &data, 0)) < 0) 53955915Selan return(-2); 54055915Selan if (st == 1) 54155915Selan return(-1); 54255915Selan 54355915Selan if (((char *)(data.data))[0] == RECORD) { 54455915Selan *bp = &((char *)(data.data))[1]; 54555915Selan return(0); 54655915Selan } 54755915Selan if ((buf = malloc(data.size - 1)) == NULL) 54855915Selan return(-2); 54955915Selan 55055915Selan strcpy(buf, &((char *)(data.data))[1]); 55155915Selan 55255915Selan key.data = buf; 55355915Selan key.size = data.size - 1; 55455915Selan 55555915Selan if (capdbp->get(capdbp, &key, &data, 0) < 0) { 55655915Selan free(buf); 55755915Selan return(-2); 55855915Selan } 55955915Selan free(buf); 56055915Selan *bp = &((char *)(data.data))[1]; 56155915Selan return(0); 56255915Selan } 56355915Selan 56455915Selan 56555868Sbostic /* 56655868Sbostic * Cgetmatch will return 0 if name is one of the names of the capability 56755868Sbostic * record buf, -1 if not. 56855868Sbostic */ 56955868Sbostic int 57055868Sbostic cgetmatch(buf, name) 57155868Sbostic char *buf, *name; 57255868Sbostic { 57355868Sbostic register char *np, *bp; 57455868Sbostic 57555868Sbostic /* 57655868Sbostic * Start search at beginning of record. 57755868Sbostic */ 57855868Sbostic bp = buf; 57955868Sbostic for (;;) { 58055868Sbostic /* 58155868Sbostic * Try to match a record name. 58255868Sbostic */ 58355868Sbostic np = name; 58455868Sbostic for (;;) 58555868Sbostic if (*np == '\0') 58655868Sbostic if (*bp == '|' || *bp == ':' || *bp == '\0') 58755868Sbostic return (0); 58855868Sbostic else 58955868Sbostic break; 59055868Sbostic else 59155868Sbostic if (*bp++ != *np++) 59255868Sbostic break; 59355868Sbostic 59455868Sbostic /* 59555868Sbostic * Match failed, skip to next name in record. 59655868Sbostic */ 59755868Sbostic bp--; /* a '|' or ':' may have stopped the match */ 59855868Sbostic for (;;) 59955868Sbostic if (*bp == '\0' || *bp == ':') 60055868Sbostic return (-1); /* match failed totally */ 60155868Sbostic else 60255868Sbostic if (*bp++ == '|') 60355868Sbostic break; /* found next name */ 60455868Sbostic } 60555868Sbostic } 60655868Sbostic 60755876Selan 60855876Selan 60955876Selan 61055876Selan 61155868Sbostic int 61255868Sbostic cgetfirst(buf, db_array) 61355868Sbostic char **buf, **db_array; 61455868Sbostic { 61555868Sbostic (void)cgetclose(); 61655868Sbostic return (cgetnext(buf, db_array)); 61755868Sbostic } 61855868Sbostic 61955868Sbostic static FILE *pfp; 62055868Sbostic static int slash; 62155868Sbostic static char **dbp; 62255868Sbostic 62355868Sbostic int 62455868Sbostic cgetclose() 62555868Sbostic { 62655868Sbostic if (pfp != NULL) { 62755868Sbostic (void)fclose(pfp); 62855868Sbostic pfp = NULL; 62955868Sbostic } 63055868Sbostic dbp = NULL; 63155875Selan gottoprec = 0; 63255868Sbostic slash = 0; 63355875Selan return(0); 63455868Sbostic } 63555868Sbostic 63655868Sbostic /* 63755868Sbostic * Cgetnext() gets either the first or next entry in the logical database 63855868Sbostic * specified by db_array. It returns 0 upon completion of the database, 1 63955868Sbostic * upon returning an entry with more remaining, and -1 if an error occurs. 64055868Sbostic */ 64155868Sbostic int 64255868Sbostic cgetnext(bp, db_array) 64355868Sbostic register char **bp; 64455868Sbostic char **db_array; 64555868Sbostic { 64655868Sbostic size_t len; 647*56197Selan int status, i, done; 648*56197Selan char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE]; 64955876Selan u_int dummy; 65055868Sbostic 65155876Selan if (dbp == NULL) 65255868Sbostic dbp = db_array; 65355876Selan 65455875Selan if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) { 65555875Selan (void)cgetclose(); 65655868Sbostic return (-1); 65755875Selan } 65855868Sbostic for(;;) { 65955876Selan if (toprec && !gottoprec) { 66055876Selan gottoprec = 1; 66155876Selan line = toprec; 66255876Selan } else { 66355876Selan line = fgetline(pfp, &len); 66455876Selan if (line == NULL && pfp) { 66555876Selan (void)fclose(pfp); 66655876Selan if (ferror(pfp)) { 66755875Selan (void)cgetclose(); 66855868Sbostic return (-1); 66955876Selan } else { 67055876Selan dbp++; 67155876Selan if (*dbp == NULL) { 67255876Selan (void)cgetclose(); 67355876Selan return (0); 67455876Selan } else if ((pfp = fopen(*dbp, "r")) == 67555876Selan NULL) { 67655876Selan (void)cgetclose(); 67755876Selan return (-1); 67855876Selan } else 67955876Selan continue; 68055876Selan } 68155868Sbostic } 68255876Selan if (isspace(*line) || *line == ':' || *line == '#' 68355876Selan || len == 0 || slash) { 68455876Selan if (len > 0 && line[len - 1] == '\\') 68555876Selan slash = 1; 68655876Selan else 68755876Selan slash = 0; 68855876Selan continue; 68955876Selan } 69055868Sbostic if (len > 0 && line[len - 1] == '\\') 69155868Sbostic slash = 1; 69255868Sbostic else 69355868Sbostic slash = 0; 69455876Selan } 69555868Sbostic 696*56197Selan 697*56197Selan /* 698*56197Selan * Line points to a name line. 699*56197Selan */ 700*56197Selan i = 0; 701*56197Selan done = 0; 702*56197Selan np = nbuf; 703*56197Selan for (;;) { 704*56197Selan for (cp = line; *cp != '\0'; cp++) { 705*56197Selan if (*cp == ':') { 706*56197Selan *np++ = ':'; 707*56197Selan done = 1; 708*56197Selan break; 709*56197Selan } 710*56197Selan if (*cp == '\\') 711*56197Selan break; 712*56197Selan *np++ = *cp; 713*56197Selan } 714*56197Selan if (done) { 715*56197Selan *np = '\0'; 716*56197Selan break; 717*56197Selan } else { /* name field extends beyond the line */ 718*56197Selan line = fgetline(pfp, &len); 719*56197Selan if (line == NULL && pfp) { 720*56197Selan (void)fclose(pfp); 721*56197Selan if (ferror(pfp)) { 722*56197Selan (void)cgetclose(); 723*56197Selan return (-1); 724*56197Selan } 725*56197Selan } 726*56197Selan } 727*56197Selan } 72855868Sbostic rp = buf; 729*56197Selan for(cp = nbuf; *cp != NULL; cp++) 73055868Sbostic if (*cp == '|' || *cp == ':') 73155868Sbostic break; 73255868Sbostic else 73355868Sbostic *rp++ = *cp; 73455868Sbostic 73555868Sbostic *rp = '\0'; 736*56197Selan status = getent(bp, &dummy, db_array, -1, buf, 0, nbuf); 73755915Selan if (status == -2 || status == -3) 73855875Selan (void)cgetclose(); 73955915Selan 74055915Selan return (status + 1); 74155868Sbostic } 74255868Sbostic /* NOTREACHED */ 74355868Sbostic } 74455868Sbostic 74555868Sbostic /* 74655868Sbostic * Cgetstr retrieves the value of the string capability cap from the 74755868Sbostic * capability record pointed to by buf. A pointer to a decoded, NUL 74855868Sbostic * terminated, malloc'd copy of the string is returned in the char * 74955868Sbostic * pointed to by str. The length of the string not including the trailing 75055868Sbostic * NUL is returned on success, -1 if the requested string capability 75155868Sbostic * couldn't be found, -2 if a system error was encountered (storage 75255868Sbostic * allocation failure). 75355868Sbostic */ 75455868Sbostic int 75555868Sbostic cgetstr(buf, cap, str) 75655868Sbostic char *buf, *cap; 75755868Sbostic char **str; 75855868Sbostic { 75955868Sbostic register u_int m_room; 76055868Sbostic register char *bp, *mp; 76155868Sbostic int len; 76255868Sbostic char *mem; 76355868Sbostic 76455868Sbostic /* 76555868Sbostic * Find string capability cap 76655868Sbostic */ 76755868Sbostic bp = cgetcap(buf, cap, '='); 76855868Sbostic if (bp == NULL) 76955868Sbostic return (-1); 77055868Sbostic 77155868Sbostic /* 77255868Sbostic * Conversion / storage allocation loop ... Allocate memory in 77355868Sbostic * chunks SFRAG in size. 77455868Sbostic */ 77555868Sbostic if ((mem = malloc(SFRAG)) == NULL) { 77655868Sbostic errno = ENOMEM; 77755868Sbostic return (-2); /* couldn't even allocate the first fragment */ 77855868Sbostic } 77955868Sbostic m_room = SFRAG; 78055868Sbostic mp = mem; 78155868Sbostic 78255868Sbostic while (*bp != ':' && *bp != '\0') { 78355868Sbostic /* 78455868Sbostic * Loop invariants: 78555868Sbostic * There is always room for one more character in mem. 78655868Sbostic * Mp always points just past last character in mem. 78755868Sbostic * Bp always points at next character in buf. 78855868Sbostic */ 78955868Sbostic if (*bp == '^') { 79055868Sbostic bp++; 79155868Sbostic if (*bp == ':' || *bp == '\0') 79255868Sbostic break; /* drop unfinished escape */ 79355868Sbostic *mp++ = *bp++ & 037; 79455868Sbostic } else if (*bp == '\\') { 79555868Sbostic bp++; 79655868Sbostic if (*bp == ':' || *bp == '\0') 79755868Sbostic break; /* drop unfinished escape */ 79855868Sbostic if ('0' <= *bp && *bp <= '7') { 79955868Sbostic register int n, i; 80055868Sbostic 80155868Sbostic n = 0; 80255868Sbostic i = 3; /* maximum of three octal digits */ 80355868Sbostic do { 80455868Sbostic n = n * 8 + (*bp++ - '0'); 80555868Sbostic } while (--i && '0' <= *bp && *bp <= '7'); 80655868Sbostic *mp++ = n; 80755868Sbostic } 80855868Sbostic else switch (*bp++) { 80955868Sbostic case 'b': case 'B': 81055868Sbostic *mp++ = '\b'; 81155868Sbostic break; 81255868Sbostic case 't': case 'T': 81355868Sbostic *mp++ = '\t'; 81455868Sbostic break; 81555868Sbostic case 'n': case 'N': 81655868Sbostic *mp++ = '\n'; 81755868Sbostic break; 81855868Sbostic case 'f': case 'F': 81955868Sbostic *mp++ = '\f'; 82055868Sbostic break; 82155868Sbostic case 'r': case 'R': 82255868Sbostic *mp++ = '\r'; 82355868Sbostic break; 82455868Sbostic case 'e': case 'E': 82555868Sbostic *mp++ = ESC; 82655868Sbostic break; 82755868Sbostic case 'c': case 'C': 82855868Sbostic *mp++ = ':'; 82955868Sbostic break; 83055868Sbostic default: 83155868Sbostic /* 83255868Sbostic * Catches '\', '^', and 83355868Sbostic * everything else. 83455868Sbostic */ 83555868Sbostic *mp++ = *(bp-1); 83655868Sbostic break; 83755868Sbostic } 83855868Sbostic } else 83955868Sbostic *mp++ = *bp++; 84055868Sbostic m_room--; 84155868Sbostic 84255868Sbostic /* 84355868Sbostic * Enforce loop invariant: if no room left in current 84455868Sbostic * buffer, try to get some more. 84555868Sbostic */ 84655868Sbostic if (m_room == 0) { 84755875Selan size_t size = mp - mem; 84855868Sbostic 84955868Sbostic if ((mem = realloc(mem, size + SFRAG)) == NULL) 85055868Sbostic return (-2); 85155868Sbostic m_room = SFRAG; 85255868Sbostic mp = mem + size; 85355868Sbostic } 85455868Sbostic } 85555868Sbostic *mp++ = '\0'; /* loop invariant let's us do this */ 85655868Sbostic m_room--; 85755868Sbostic len = mp - mem - 1; 85855868Sbostic 85955868Sbostic /* 86055868Sbostic * Give back any extra memory and return value and success. 86155868Sbostic */ 86255868Sbostic if (m_room != 0) 86356184Selan if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 86456184Selan return (-2); 86555868Sbostic *str = mem; 86655868Sbostic return (len); 86755868Sbostic } 86855868Sbostic 86955868Sbostic /* 87055868Sbostic * Cgetustr retrieves the value of the string capability cap from the 87155868Sbostic * capability record pointed to by buf. The difference between cgetustr() 87255868Sbostic * and cgetstr() is that cgetustr does not decode escapes but rather treats 87355868Sbostic * all characters literally. A pointer to a NUL terminated malloc'd 87455868Sbostic * copy of the string is returned in the char pointed to by str. The 87555868Sbostic * length of the string not including the trailing NUL is returned on success, 87655868Sbostic * -1 if the requested string capability couldn't be found, -2 if a system 87755868Sbostic * error was encountered (storage allocation failure). 87855868Sbostic */ 87955868Sbostic int 88055868Sbostic cgetustr(buf, cap, str) 88155868Sbostic char *buf, *cap, **str; 88255868Sbostic { 88355868Sbostic register u_int m_room; 88455868Sbostic register char *bp, *mp; 88555868Sbostic int len; 88655868Sbostic char *mem; 88755868Sbostic 88855868Sbostic /* 88955868Sbostic * Find string capability cap 89055868Sbostic */ 89155868Sbostic if ((bp = cgetcap(buf, cap, '=')) == NULL) 89255868Sbostic return (-1); 89355868Sbostic 89455868Sbostic /* 89555868Sbostic * Conversion / storage allocation loop ... Allocate memory in 89655868Sbostic * chunks SFRAG in size. 89755868Sbostic */ 89855868Sbostic if ((mem = malloc(SFRAG)) == NULL) { 89955868Sbostic errno = ENOMEM; 90055868Sbostic return (-2); /* couldn't even allocate the first fragment */ 90155868Sbostic } 90255868Sbostic m_room = SFRAG; 90355868Sbostic mp = mem; 90455868Sbostic 90555868Sbostic while (*bp != ':' && *bp != '\0') { 90655868Sbostic /* 90755868Sbostic * Loop invariants: 90855868Sbostic * There is always room for one more character in mem. 90955868Sbostic * Mp always points just past last character in mem. 91055868Sbostic * Bp always points at next character in buf. 91155868Sbostic */ 91255868Sbostic *mp++ = *bp++; 91355868Sbostic m_room--; 91455868Sbostic 91555868Sbostic /* 91655868Sbostic * Enforce loop invariant: if no room left in current 91755868Sbostic * buffer, try to get some more. 91855868Sbostic */ 91955868Sbostic if (m_room == 0) { 92055875Selan size_t size = mp - mem; 92155868Sbostic 92255868Sbostic if ((mem = realloc(mem, size + SFRAG)) == NULL) 92355868Sbostic return (-2); 92455868Sbostic m_room = SFRAG; 92555868Sbostic mp = mem + size; 92655868Sbostic } 92755868Sbostic } 92855868Sbostic *mp++ = '\0'; /* loop invariant let's us do this */ 92955868Sbostic m_room--; 93055868Sbostic len = mp - mem - 1; 93155868Sbostic 93255868Sbostic /* 93355868Sbostic * Give back any extra memory and return value and success. 93455868Sbostic */ 93555868Sbostic if (m_room != 0) 93656184Selan if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 93756184Selan return (-2); 93855868Sbostic *str = mem; 93955868Sbostic return (len); 94055868Sbostic } 94155868Sbostic 94255868Sbostic /* 94355868Sbostic * Cgetnum retrieves the value of the numeric capability cap from the 94455868Sbostic * capability record pointed to by buf. The numeric value is returned in 94555868Sbostic * the long pointed to by num. 0 is returned on success, -1 if the requested 94655868Sbostic * numeric capability couldn't be found. 94755868Sbostic */ 94855868Sbostic int 94955868Sbostic cgetnum(buf, cap, num) 95055868Sbostic char *buf, *cap; 95155868Sbostic long *num; 95255868Sbostic { 95355868Sbostic register long n; 95455868Sbostic register int base, digit; 95555868Sbostic register char *bp; 95655868Sbostic 95755868Sbostic /* 95855868Sbostic * Find numeric capability cap 95955868Sbostic */ 96055868Sbostic bp = cgetcap(buf, cap, '#'); 96155868Sbostic if (bp == NULL) 96255868Sbostic return (-1); 96355868Sbostic 96455868Sbostic /* 96555868Sbostic * Look at value and determine numeric base: 96655868Sbostic * 0x... or 0X... hexadecimal, 96755868Sbostic * else 0... octal, 96855868Sbostic * else decimal. 96955868Sbostic */ 97055868Sbostic if (*bp == '0') { 97155868Sbostic bp++; 97255868Sbostic if (*bp == 'x' || *bp == 'X') { 97355868Sbostic bp++; 97455868Sbostic base = 16; 97555868Sbostic } else 97655868Sbostic base = 8; 97755868Sbostic } else 97855868Sbostic base = 10; 97955868Sbostic 98055868Sbostic /* 98155868Sbostic * Conversion loop ... 98255868Sbostic */ 98355868Sbostic n = 0; 98455868Sbostic for (;;) { 98555868Sbostic if ('0' <= *bp && *bp <= '9') 98655868Sbostic digit = *bp - '0'; 98755868Sbostic else 98855868Sbostic if ( ('a' <= *bp && *bp <= 'f') 98955868Sbostic || ('A' <= *bp && *bp <= 'F')) 99055868Sbostic digit = 10 + *bp - 'a'; 99155868Sbostic else 99255868Sbostic break; 99355868Sbostic 99455868Sbostic if (digit >= base) 99555868Sbostic break; 99655868Sbostic 99755868Sbostic n = n * base + digit; 99855868Sbostic bp++; 99955868Sbostic } 100055868Sbostic 100155868Sbostic /* 100255868Sbostic * Return value and success. 100355868Sbostic */ 100455868Sbostic *num = n; 100555868Sbostic return (0); 100655868Sbostic } 1007*56197Selan 1008*56197Selan 1009*56197Selan /* 1010*56197Selan * Compare name field of record. 1011*56197Selan */ 1012*56197Selan static int 1013*56197Selan nfcmp(nf, rec) 1014*56197Selan char *nf, *rec; 1015*56197Selan { 1016*56197Selan char *cp, tmp; 1017*56197Selan int ret; 1018*56197Selan 1019*56197Selan for (cp = rec; *cp != ':'; cp++) 1020*56197Selan ; 1021*56197Selan 1022*56197Selan tmp = *(cp + 1); 1023*56197Selan *(cp + 1) = '\0'; 1024*56197Selan ret = strcmp(nf, rec); 1025*56197Selan *(cp + 1) = tmp; 1026*56197Selan 1027*56197Selan return (ret); 1028*56197Selan } 1029