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 <fcntl.h> 4755915Selan #include <limits.h> 4855868Sbostic #include <stdio.h> 4955868Sbostic #include <stdlib.h> 5055868Sbostic #include <string.h> 5155868Sbostic #include <unistd.h> 5255868Sbostic 5355868Sbostic #define BFRAG 1024 5456197Selan #define BSIZE 1024 5555868Sbostic #define ESC ('[' & 037) /* ASCII ESC */ 5655868Sbostic #define MAX_RECURSION 32 /* maximum getent recursion */ 5755868Sbostic #define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */ 5855868Sbostic 5956208Selan #define RECOK (char)0 6056208Selan #define TCERR (char)1 6155915Selan 6255868Sbostic static size_t topreclen; /* toprec length */ 6355868Sbostic static char *toprec; /* Additional record specified by cgetset() */ 6455875Selan static int gottoprec; /* Flag indicating retrieval of toprecord */ 6555868Sbostic 6655915Selan static int cdbget __P((DB *, char **, char *)); 6756197Selan static int getent __P((char **, u_int *, char **, int, char *, int, char *)); 6856197Selan static int nfcmp __P((char *, char *)); 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 16656197Selan return (getent(buf, &dummy, db_array, -1, name, 0, NULL)); 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 18856197Selan getent(cap, len, db_array, fd, name, depth, nfield) 18956197Selan char **cap, **db_array, *name, *nfield; 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); 25456208Selan *cap = malloc (strlen(record) + 1); 25556208Selan strcpy(*cap, record); 25656208Selan return (retval); 25755915Selan } else { 25855915Selan fd = open(*db_p, O_RDONLY, 0); 25955915Selan if (fd < 0) { 26056216Selan /* No error on unfound file. */ 26156216Selan if (errno == ENOENT) 26256216Selan continue; 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) { 36656197Selan if (nfield == NULL || !nfcmp(nfield, record)) { 36756197Selan foundit = 1; 36856197Selan break; /* found it! */ 36956197Selan } 37055868Sbostic } 37155868Sbostic } 37256197Selan } 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 42256197Selan iret = getent(&icap, &ilen, db_p, fd, tc, depth+1, 42356197Selan NULL); 42455868Sbostic newicap = icap; /* Put into a register. */ 42555868Sbostic newilen = ilen; 42655868Sbostic if (iret != 0) { 42756208Selan /* an error */ 42856208Selan if (iret < -1) { 42956208Selan if (myfd) 43056208Selan (void)close(fd); 43156208Selan free(record); 43256208Selan return (iret); 43356208Selan } 43455915Selan if (iret == 1) 43555915Selan tc_not_resolved = 1; 43656208Selan /* couldn't resolve tc */ 43755915Selan if (iret == -1) { 43856184Selan *(s - 1) = ':'; 43955915Selan scan = s - 1; 44055915Selan tc_not_resolved = 1; 44155915Selan continue; 44255915Selan 44355915Selan } 44455868Sbostic } 44555868Sbostic /* not interested in name field of tc'ed record */ 44655868Sbostic s = newicap; 44755868Sbostic for (;;) 44855868Sbostic if (*s == '\0') 44955868Sbostic break; 45055868Sbostic else 45155868Sbostic if (*s++ == ':') 45255868Sbostic break; 45355868Sbostic newilen -= s - newicap; 45455868Sbostic newicap = s; 45555868Sbostic 45655868Sbostic /* make sure interpolated record is `:'-terminated */ 45755868Sbostic s += newilen; 45855868Sbostic if (*(s-1) != ':') { 45955868Sbostic *s = ':'; /* overwrite NUL with : */ 46055868Sbostic newilen++; 46155868Sbostic } 46255868Sbostic 46355868Sbostic /* 46455868Sbostic * Make sure there's enough room to insert the 46555868Sbostic * new record. 46655868Sbostic */ 46755868Sbostic diff = newilen - tclen; 46855868Sbostic if (diff >= r_end - rp) { 46955875Selan u_int pos, tcpos, tcposend; 47055875Selan size_t newsize; 47155868Sbostic 47255868Sbostic pos = rp - record; 47355868Sbostic newsize = r_end - record + diff + BFRAG; 47455868Sbostic tcpos = tcstart - record; 47555868Sbostic tcposend = tcend - record; 47655868Sbostic record = realloc(record, newsize); 47755868Sbostic if (record == NULL) { 47855868Sbostic errno = ENOMEM; 47955868Sbostic if (myfd) 48055868Sbostic (void)close(fd); 48155868Sbostic free(icap); 48255868Sbostic return (-2); 48355868Sbostic } 48455868Sbostic r_end = record + newsize; 48555868Sbostic rp = record + pos; 48655868Sbostic tcstart = record + tcpos; 48755868Sbostic tcend = record + tcposend; 48855868Sbostic } 48955868Sbostic 49055868Sbostic /* 49155868Sbostic * Insert tc'ed record into our record. 49255868Sbostic */ 49355868Sbostic s = tcstart + newilen; 49455868Sbostic bcopy(tcend, s, rp - tcend); 49555868Sbostic bcopy(newicap, tcstart, newilen); 49655868Sbostic rp += diff; 49755868Sbostic free(icap); 49855868Sbostic 49955868Sbostic /* 50055868Sbostic * Start scan on `:' so next cgetcap works properly 50155868Sbostic * (cgetcap always skips first field). 50255868Sbostic */ 50355868Sbostic scan = s-1; 50455868Sbostic } 50556197Selan 50655868Sbostic } 50755868Sbostic /* 50855868Sbostic * Close file (if we opened it), give back any extra memory, and 50955868Sbostic * return capability, length and success. 51055868Sbostic */ 51155868Sbostic if (myfd) 51255868Sbostic (void)close(fd); 51355868Sbostic *len = rp - record - 1; /* don't count NUL */ 51455868Sbostic if (r_end > rp) 51556184Selan if ((record = 51656184Selan realloc(record, (size_t)(rp - record))) == NULL) { 51756184Selan errno = ENOMEM; 51856184Selan return (-2); 51956184Selan } 52056184Selan 52155868Sbostic *cap = record; 52255915Selan if (tc_not_resolved) 52355915Selan return (1); 52455868Sbostic return (0); 52556197Selan } 52655868Sbostic 52755915Selan static int 52855915Selan cdbget(capdbp, bp, name) 52955915Selan DB *capdbp; 53055915Selan char **bp, *name; 53155915Selan { 53255915Selan DBT key, data; 53355915Selan char *buf; 53455915Selan int st; 53555915Selan 53655915Selan key.data = name; 53755915Selan key.size = strlen(name) + 1; 53855915Selan 53956208Selan 54056208Selan /* 54156208Selan * Get the reference. 54256208Selan */ 54355915Selan if ((st = capdbp->get(capdbp, &key, &data, 0)) < 0) 54455915Selan return(-2); 54555915Selan if (st == 1) 54655915Selan return(-1); 54755915Selan 54855915Selan if ((buf = malloc(data.size - 1)) == NULL) 54955915Selan return(-2); 55055915Selan 55156208Selan strcpy(buf, (char *)(data.data)); 55255915Selan 55355915Selan key.data = buf; 55456216Selan key.size = data.size; 55555915Selan 55656208Selan /* 55756208Selan * Get the record. 55856208Selan */ 55955915Selan if (capdbp->get(capdbp, &key, &data, 0) < 0) { 56055915Selan free(buf); 56155915Selan return(-2); 56255915Selan } 56355915Selan free(buf); 56455915Selan *bp = &((char *)(data.data))[1]; 56556208Selan if (((char *)(data.data))[0] == TCERR) 56656208Selan return 1; 56756208Selan else 56856208Selan return 0; 56955915Selan } 57055915Selan 57155915Selan 57255868Sbostic /* 57355868Sbostic * Cgetmatch will return 0 if name is one of the names of the capability 57455868Sbostic * record buf, -1 if not. 57555868Sbostic */ 57655868Sbostic int 57755868Sbostic cgetmatch(buf, name) 57855868Sbostic char *buf, *name; 57955868Sbostic { 58055868Sbostic register char *np, *bp; 58155868Sbostic 58255868Sbostic /* 58355868Sbostic * Start search at beginning of record. 58455868Sbostic */ 58555868Sbostic bp = buf; 58655868Sbostic for (;;) { 58755868Sbostic /* 58855868Sbostic * Try to match a record name. 58955868Sbostic */ 59055868Sbostic np = name; 59155868Sbostic for (;;) 59255868Sbostic if (*np == '\0') 59355868Sbostic if (*bp == '|' || *bp == ':' || *bp == '\0') 59455868Sbostic return (0); 59555868Sbostic else 59655868Sbostic break; 59755868Sbostic else 59855868Sbostic if (*bp++ != *np++) 59955868Sbostic break; 60055868Sbostic 60155868Sbostic /* 60255868Sbostic * Match failed, skip to next name in record. 60355868Sbostic */ 60455868Sbostic bp--; /* a '|' or ':' may have stopped the match */ 60555868Sbostic for (;;) 60655868Sbostic if (*bp == '\0' || *bp == ':') 60755868Sbostic return (-1); /* match failed totally */ 60855868Sbostic else 60955868Sbostic if (*bp++ == '|') 61055868Sbostic break; /* found next name */ 61155868Sbostic } 61255868Sbostic } 61355868Sbostic 61455876Selan 61555876Selan 61655876Selan 61755876Selan 61855868Sbostic int 61955868Sbostic cgetfirst(buf, db_array) 62055868Sbostic char **buf, **db_array; 62155868Sbostic { 62255868Sbostic (void)cgetclose(); 62355868Sbostic return (cgetnext(buf, db_array)); 62455868Sbostic } 62555868Sbostic 62655868Sbostic static FILE *pfp; 62755868Sbostic static int slash; 62855868Sbostic static char **dbp; 62955868Sbostic 63055868Sbostic int 63155868Sbostic cgetclose() 63255868Sbostic { 63355868Sbostic if (pfp != NULL) { 63455868Sbostic (void)fclose(pfp); 63555868Sbostic pfp = NULL; 63655868Sbostic } 63755868Sbostic dbp = NULL; 63855875Selan gottoprec = 0; 63955868Sbostic slash = 0; 64055875Selan return(0); 64155868Sbostic } 64255868Sbostic 64355868Sbostic /* 64455868Sbostic * Cgetnext() gets either the first or next entry in the logical database 64555868Sbostic * specified by db_array. It returns 0 upon completion of the database, 1 64655868Sbostic * upon returning an entry with more remaining, and -1 if an error occurs. 64755868Sbostic */ 64855868Sbostic int 64955868Sbostic cgetnext(bp, db_array) 65055868Sbostic register char **bp; 65155868Sbostic char **db_array; 65255868Sbostic { 65355868Sbostic size_t len; 65456197Selan int status, i, done; 65556197Selan char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE]; 65655876Selan u_int dummy; 65755868Sbostic 65855876Selan if (dbp == NULL) 65955868Sbostic dbp = db_array; 66055876Selan 66155875Selan if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) { 66255875Selan (void)cgetclose(); 66355868Sbostic return (-1); 66455875Selan } 66555868Sbostic for(;;) { 66655876Selan if (toprec && !gottoprec) { 66755876Selan gottoprec = 1; 66855876Selan line = toprec; 66955876Selan } else { 67055876Selan line = fgetline(pfp, &len); 67155876Selan if (line == NULL && pfp) { 67255876Selan (void)fclose(pfp); 67355876Selan if (ferror(pfp)) { 67455875Selan (void)cgetclose(); 67555868Sbostic return (-1); 67655876Selan } else { 67755876Selan dbp++; 67855876Selan if (*dbp == NULL) { 67955876Selan (void)cgetclose(); 68055876Selan return (0); 68155876Selan } else if ((pfp = fopen(*dbp, "r")) == 68255876Selan NULL) { 68355876Selan (void)cgetclose(); 68455876Selan return (-1); 68555876Selan } else 68655876Selan continue; 68755876Selan } 68855868Sbostic } 68955876Selan if (isspace(*line) || *line == ':' || *line == '#' 69055876Selan || len == 0 || slash) { 69155876Selan if (len > 0 && line[len - 1] == '\\') 69255876Selan slash = 1; 69355876Selan else 69455876Selan slash = 0; 69555876Selan continue; 69655876Selan } 69755868Sbostic if (len > 0 && line[len - 1] == '\\') 69855868Sbostic slash = 1; 69955868Sbostic else 70055868Sbostic slash = 0; 70155876Selan } 70255868Sbostic 70356197Selan 70456197Selan /* 70556197Selan * Line points to a name line. 70656197Selan */ 70756197Selan i = 0; 70856197Selan done = 0; 70956197Selan np = nbuf; 71056197Selan for (;;) { 71156197Selan for (cp = line; *cp != '\0'; cp++) { 71256197Selan if (*cp == ':') { 71356197Selan *np++ = ':'; 71456197Selan done = 1; 71556197Selan break; 71656197Selan } 71756197Selan if (*cp == '\\') 71856197Selan break; 71956197Selan *np++ = *cp; 72056197Selan } 72156197Selan if (done) { 72256197Selan *np = '\0'; 72356197Selan break; 72456197Selan } else { /* name field extends beyond the line */ 72556197Selan line = fgetline(pfp, &len); 72656197Selan if (line == NULL && pfp) { 72756197Selan (void)fclose(pfp); 72856197Selan if (ferror(pfp)) { 72956197Selan (void)cgetclose(); 73056197Selan return (-1); 73156197Selan } 73256197Selan } 73356197Selan } 73456197Selan } 73555868Sbostic rp = buf; 73656197Selan for(cp = nbuf; *cp != NULL; cp++) 73755868Sbostic if (*cp == '|' || *cp == ':') 73855868Sbostic break; 73955868Sbostic else 74055868Sbostic *rp++ = *cp; 74155868Sbostic 74255868Sbostic *rp = '\0'; 74356200Selan /* 74456200Selan * XXX 74556200Selan * Last argument of getent here should be nbuf if we want true 74656200Selan * sequential access in the case of duplicates. 74756200Selan * With NULL, getent will return the first entry found 74856200Selan * rather than the duplicate entry record. This is a 74956200Selan * matter of semantics that should be resolved. 75056200Selan */ 75156200Selan status = getent(bp, &dummy, db_array, -1, buf, 0, NULL); 75255915Selan if (status == -2 || status == -3) 75355875Selan (void)cgetclose(); 75455915Selan 75555915Selan return (status + 1); 75655868Sbostic } 75755868Sbostic /* NOTREACHED */ 75855868Sbostic } 75955868Sbostic 76055868Sbostic /* 76155868Sbostic * Cgetstr retrieves the value of the string capability cap from the 76255868Sbostic * capability record pointed to by buf. A pointer to a decoded, NUL 76355868Sbostic * terminated, malloc'd copy of the string is returned in the char * 76455868Sbostic * pointed to by str. The length of the string not including the trailing 76555868Sbostic * NUL is returned on success, -1 if the requested string capability 76655868Sbostic * couldn't be found, -2 if a system error was encountered (storage 76755868Sbostic * allocation failure). 76855868Sbostic */ 76955868Sbostic int 77055868Sbostic cgetstr(buf, cap, str) 77155868Sbostic char *buf, *cap; 77255868Sbostic char **str; 77355868Sbostic { 77455868Sbostic register u_int m_room; 77555868Sbostic register char *bp, *mp; 77655868Sbostic int len; 77755868Sbostic char *mem; 77855868Sbostic 77955868Sbostic /* 78055868Sbostic * Find string capability cap 78155868Sbostic */ 78255868Sbostic bp = cgetcap(buf, cap, '='); 78355868Sbostic if (bp == NULL) 78455868Sbostic return (-1); 78555868Sbostic 78655868Sbostic /* 78755868Sbostic * Conversion / storage allocation loop ... Allocate memory in 78855868Sbostic * chunks SFRAG in size. 78955868Sbostic */ 79055868Sbostic if ((mem = malloc(SFRAG)) == NULL) { 79155868Sbostic errno = ENOMEM; 79255868Sbostic return (-2); /* couldn't even allocate the first fragment */ 79355868Sbostic } 79455868Sbostic m_room = SFRAG; 79555868Sbostic mp = mem; 79655868Sbostic 79755868Sbostic while (*bp != ':' && *bp != '\0') { 79855868Sbostic /* 79955868Sbostic * Loop invariants: 80055868Sbostic * There is always room for one more character in mem. 80155868Sbostic * Mp always points just past last character in mem. 80255868Sbostic * Bp always points at next character in buf. 80355868Sbostic */ 80455868Sbostic if (*bp == '^') { 80555868Sbostic bp++; 80655868Sbostic if (*bp == ':' || *bp == '\0') 80755868Sbostic break; /* drop unfinished escape */ 80855868Sbostic *mp++ = *bp++ & 037; 80955868Sbostic } else if (*bp == '\\') { 81055868Sbostic bp++; 81155868Sbostic if (*bp == ':' || *bp == '\0') 81255868Sbostic break; /* drop unfinished escape */ 81355868Sbostic if ('0' <= *bp && *bp <= '7') { 81455868Sbostic register int n, i; 81555868Sbostic 81655868Sbostic n = 0; 81755868Sbostic i = 3; /* maximum of three octal digits */ 81855868Sbostic do { 81955868Sbostic n = n * 8 + (*bp++ - '0'); 82055868Sbostic } while (--i && '0' <= *bp && *bp <= '7'); 82155868Sbostic *mp++ = n; 82255868Sbostic } 82355868Sbostic else switch (*bp++) { 82455868Sbostic case 'b': case 'B': 82555868Sbostic *mp++ = '\b'; 82655868Sbostic break; 82755868Sbostic case 't': case 'T': 82855868Sbostic *mp++ = '\t'; 82955868Sbostic break; 83055868Sbostic case 'n': case 'N': 83155868Sbostic *mp++ = '\n'; 83255868Sbostic break; 83355868Sbostic case 'f': case 'F': 83455868Sbostic *mp++ = '\f'; 83555868Sbostic break; 83655868Sbostic case 'r': case 'R': 83755868Sbostic *mp++ = '\r'; 83855868Sbostic break; 83955868Sbostic case 'e': case 'E': 84055868Sbostic *mp++ = ESC; 84155868Sbostic break; 84255868Sbostic case 'c': case 'C': 84355868Sbostic *mp++ = ':'; 84455868Sbostic break; 84555868Sbostic default: 84655868Sbostic /* 84755868Sbostic * Catches '\', '^', and 84855868Sbostic * everything else. 84955868Sbostic */ 85055868Sbostic *mp++ = *(bp-1); 85155868Sbostic break; 85255868Sbostic } 85355868Sbostic } else 85455868Sbostic *mp++ = *bp++; 85555868Sbostic m_room--; 85655868Sbostic 85755868Sbostic /* 85855868Sbostic * Enforce loop invariant: if no room left in current 85955868Sbostic * buffer, try to get some more. 86055868Sbostic */ 86155868Sbostic if (m_room == 0) { 86255875Selan size_t size = mp - mem; 86355868Sbostic 86455868Sbostic if ((mem = realloc(mem, size + SFRAG)) == NULL) 86555868Sbostic return (-2); 86655868Sbostic m_room = SFRAG; 86755868Sbostic mp = mem + size; 86855868Sbostic } 86955868Sbostic } 87055868Sbostic *mp++ = '\0'; /* loop invariant let's us do this */ 87155868Sbostic m_room--; 87255868Sbostic len = mp - mem - 1; 87355868Sbostic 87455868Sbostic /* 87555868Sbostic * Give back any extra memory and return value and success. 87655868Sbostic */ 87755868Sbostic if (m_room != 0) 87856184Selan if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 87956184Selan return (-2); 88055868Sbostic *str = mem; 88155868Sbostic return (len); 88255868Sbostic } 88355868Sbostic 88455868Sbostic /* 88555868Sbostic * Cgetustr retrieves the value of the string capability cap from the 88655868Sbostic * capability record pointed to by buf. The difference between cgetustr() 88755868Sbostic * and cgetstr() is that cgetustr does not decode escapes but rather treats 88855868Sbostic * all characters literally. A pointer to a NUL terminated malloc'd 88955868Sbostic * copy of the string is returned in the char pointed to by str. The 89055868Sbostic * length of the string not including the trailing NUL is returned on success, 89155868Sbostic * -1 if the requested string capability couldn't be found, -2 if a system 89255868Sbostic * error was encountered (storage allocation failure). 89355868Sbostic */ 89455868Sbostic int 89555868Sbostic cgetustr(buf, cap, str) 89655868Sbostic char *buf, *cap, **str; 89755868Sbostic { 89855868Sbostic register u_int m_room; 89955868Sbostic register char *bp, *mp; 90055868Sbostic int len; 90155868Sbostic char *mem; 90255868Sbostic 90355868Sbostic /* 90455868Sbostic * Find string capability cap 90555868Sbostic */ 90655868Sbostic if ((bp = cgetcap(buf, cap, '=')) == NULL) 90755868Sbostic return (-1); 90855868Sbostic 90955868Sbostic /* 91055868Sbostic * Conversion / storage allocation loop ... Allocate memory in 91155868Sbostic * chunks SFRAG in size. 91255868Sbostic */ 91355868Sbostic if ((mem = malloc(SFRAG)) == NULL) { 91455868Sbostic errno = ENOMEM; 91555868Sbostic return (-2); /* couldn't even allocate the first fragment */ 91655868Sbostic } 91755868Sbostic m_room = SFRAG; 91855868Sbostic mp = mem; 91955868Sbostic 92055868Sbostic while (*bp != ':' && *bp != '\0') { 92155868Sbostic /* 92255868Sbostic * Loop invariants: 92355868Sbostic * There is always room for one more character in mem. 92455868Sbostic * Mp always points just past last character in mem. 92555868Sbostic * Bp always points at next character in buf. 92655868Sbostic */ 92755868Sbostic *mp++ = *bp++; 92855868Sbostic m_room--; 92955868Sbostic 93055868Sbostic /* 93155868Sbostic * Enforce loop invariant: if no room left in current 93255868Sbostic * buffer, try to get some more. 93355868Sbostic */ 93455868Sbostic if (m_room == 0) { 93555875Selan size_t size = mp - mem; 93655868Sbostic 93755868Sbostic if ((mem = realloc(mem, size + SFRAG)) == NULL) 93855868Sbostic return (-2); 93955868Sbostic m_room = SFRAG; 94055868Sbostic mp = mem + size; 94155868Sbostic } 94255868Sbostic } 94355868Sbostic *mp++ = '\0'; /* loop invariant let's us do this */ 94455868Sbostic m_room--; 94555868Sbostic len = mp - mem - 1; 94655868Sbostic 94755868Sbostic /* 94855868Sbostic * Give back any extra memory and return value and success. 94955868Sbostic */ 95055868Sbostic if (m_room != 0) 95156184Selan if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 95256184Selan return (-2); 95355868Sbostic *str = mem; 95455868Sbostic return (len); 95555868Sbostic } 95655868Sbostic 95755868Sbostic /* 95855868Sbostic * Cgetnum retrieves the value of the numeric capability cap from the 95955868Sbostic * capability record pointed to by buf. The numeric value is returned in 96055868Sbostic * the long pointed to by num. 0 is returned on success, -1 if the requested 96155868Sbostic * numeric capability couldn't be found. 96255868Sbostic */ 96355868Sbostic int 96455868Sbostic cgetnum(buf, cap, num) 96555868Sbostic char *buf, *cap; 96655868Sbostic long *num; 96755868Sbostic { 96855868Sbostic register long n; 96955868Sbostic register int base, digit; 97055868Sbostic register char *bp; 97155868Sbostic 97255868Sbostic /* 97355868Sbostic * Find numeric capability cap 97455868Sbostic */ 97555868Sbostic bp = cgetcap(buf, cap, '#'); 97655868Sbostic if (bp == NULL) 97755868Sbostic return (-1); 97855868Sbostic 97955868Sbostic /* 98055868Sbostic * Look at value and determine numeric base: 98155868Sbostic * 0x... or 0X... hexadecimal, 98255868Sbostic * else 0... octal, 98355868Sbostic * else decimal. 98455868Sbostic */ 98555868Sbostic if (*bp == '0') { 98655868Sbostic bp++; 98755868Sbostic if (*bp == 'x' || *bp == 'X') { 98855868Sbostic bp++; 98955868Sbostic base = 16; 99055868Sbostic } else 99155868Sbostic base = 8; 99255868Sbostic } else 99355868Sbostic base = 10; 99455868Sbostic 99555868Sbostic /* 99655868Sbostic * Conversion loop ... 99755868Sbostic */ 99855868Sbostic n = 0; 99955868Sbostic for (;;) { 100055868Sbostic if ('0' <= *bp && *bp <= '9') 100155868Sbostic digit = *bp - '0'; 1002*56236Sralph else if ('a' <= *bp && *bp <= 'f') 1003*56236Sralph digit = 10 + *bp - 'a'; 1004*56236Sralph else if ('A' <= *bp && *bp <= 'F') 1005*56236Sralph digit = 10 + *bp - 'A'; 100655868Sbostic else 1007*56236Sralph break; 100855868Sbostic 100955868Sbostic if (digit >= base) 101055868Sbostic break; 101155868Sbostic 101255868Sbostic n = n * base + digit; 101355868Sbostic bp++; 101455868Sbostic } 101555868Sbostic 101655868Sbostic /* 101755868Sbostic * Return value and success. 101855868Sbostic */ 101955868Sbostic *num = n; 102055868Sbostic return (0); 102155868Sbostic } 102256197Selan 102356197Selan 102456197Selan /* 102556197Selan * Compare name field of record. 102656197Selan */ 102756197Selan static int 102856197Selan nfcmp(nf, rec) 102956197Selan char *nf, *rec; 103056197Selan { 103156197Selan char *cp, tmp; 103256197Selan int ret; 103356197Selan 103456197Selan for (cp = rec; *cp != ':'; cp++) 103556197Selan ; 103656197Selan 103756197Selan tmp = *(cp + 1); 103856197Selan *(cp + 1) = '\0'; 103956197Selan ret = strcmp(nf, rec); 104056197Selan *(cp + 1) = tmp; 104156197Selan 104256197Selan return (ret); 104356197Selan } 1044