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 * 8*55875Selan * Redistribution and use in source and binary forms, with or without 9*55875Selan * modification, are permitted provided that the following conditions 10*55875Selan * are met: 11*55875Selan * 1. Redistributions of source code must retain the above copyright 12*55875Selan * notice, this list of conditions and the following disclaimer. 13*55875Selan * 2. Redistributions in binary form must reproduce the above copyright 14*55875Selan * notice, this list of conditions and the following disclaimer in the 15*55875Selan * documentation and/or other materials provided with the distribution. 16*55875Selan * 3. All advertising materials mentioning features or use of this software 17*55875Selan * must display the following acknowledgement: 18*55875Selan * This product includes software developed by the University of 19*55875Selan * California, Berkeley and its contributors. 20*55875Selan * 4. Neither the name of the University nor the names of its contributors 21*55875Selan * may be used to endorse or promote products derived from this software 22*55875Selan * without specific prior written permission. 23*55875Selan * 24*55875Selan * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25*55875Selan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26*55875Selan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27*55875Selan * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28*55875Selan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29*55875Selan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30*55875Selan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31*55875Selan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32*55875Selan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33*55875Selan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34*55875Selan * SUCH DAMAGE. 3555868Sbostic */ 3655868Sbostic 3755868Sbostic #if defined(LIBC_SCCS) && !defined(lint) 38*55875Selan 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> 4455868Sbostic #include <errno.h> 4555868Sbostic #include <errno.h> 4655868Sbostic #include <fcntl.h> 4755868Sbostic #include <stdio.h> 4855868Sbostic #include <stdlib.h> 4955868Sbostic #include <string.h> 5055868Sbostic #include <unistd.h> 5155868Sbostic 5255868Sbostic #define BFRAG 1024 5355868Sbostic #define BSIZE 80 5455868Sbostic #define ESC ('[' & 037) /* ASCII ESC */ 5555868Sbostic #define MAX_RECURSION 32 /* maximum getent recursion */ 5655868Sbostic #define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */ 5755868Sbostic 5855868Sbostic static size_t topreclen; /* toprec length */ 5955868Sbostic static char *toprec; /* Additional record specified by cgetset() */ 60*55875Selan static int gottoprec; /* Flag indicating retrieval of toprecord */ 6155868Sbostic 6255868Sbostic static int getent __P((char **, u_int *, char **, int, char *, int)); 6355868Sbostic 6455868Sbostic /* 6555868Sbostic * Cgetset() allows the addition of a user specified buffer to be added 6655868Sbostic * to the database array, in effect "pushing" the buffer on top of the 6755868Sbostic * virtual database. 0 is returned on success, -1 on failure. 6855868Sbostic */ 6955868Sbostic int 7055868Sbostic cgetset(ent) 7155868Sbostic char *ent; 7255868Sbostic { 7355868Sbostic if (ent == NULL) { 7455868Sbostic if (toprec) 7555868Sbostic free(toprec); 7655868Sbostic toprec = NULL; 7755868Sbostic topreclen = 0; 7855868Sbostic return (0); 7955868Sbostic } 8055868Sbostic topreclen = strlen(ent); 8155868Sbostic if ((toprec = malloc (topreclen + 1)) == NULL) { 8255868Sbostic errno = ENOMEM; 8355868Sbostic return (-1); 8455868Sbostic } 8555868Sbostic (void)strcpy(toprec, ent); 8655868Sbostic return (0); 8755868Sbostic } 8855868Sbostic 8955868Sbostic /* 9055868Sbostic * Cgetcap searches the capability record buf for the capability cap with 9155868Sbostic * type `type'. A pointer to the value of cap is returned on success, NULL 9255868Sbostic * if the requested capability couldn't be found. 9355868Sbostic * 9455868Sbostic * Specifying a type of ':' means that nothing should follow cap (:cap:). 9555868Sbostic * In this case a pointer to the terminating ':' or NUL will be returned if 9655868Sbostic * cap is found. 9755868Sbostic * 9855868Sbostic * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator) 9955868Sbostic * return NULL. 10055868Sbostic */ 10155868Sbostic char * 10255868Sbostic cgetcap(buf, cap, type) 10355868Sbostic char *buf, *cap; 10455868Sbostic int type; 10555868Sbostic { 10655868Sbostic register char *bp, *cp; 10755868Sbostic 10855868Sbostic bp = buf; 10955868Sbostic for (;;) { 11055868Sbostic /* 11155868Sbostic * Skip past the current capability field - it's either the 11255868Sbostic * name field if this is the first time through the loop, or 11355868Sbostic * the remainder of a field whose name failed to match cap. 11455868Sbostic */ 11555868Sbostic for (;;) 11655868Sbostic if (*bp == '\0') 11755868Sbostic return (NULL); 11855868Sbostic else 11955868Sbostic if (*bp++ == ':') 12055868Sbostic break; 12155868Sbostic 12255868Sbostic /* 12355868Sbostic * Try to match (cap, type) in buf. 12455868Sbostic */ 12555868Sbostic for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++) 12655868Sbostic continue; 12755868Sbostic if (*cp != '\0') 12855868Sbostic continue; 12955868Sbostic if (*bp == '@') 13055868Sbostic return (NULL); 13155868Sbostic if (type == ':') { 13255868Sbostic if (*bp != '\0' && *bp != ':') 13355868Sbostic continue; 13455868Sbostic return(bp); 13555868Sbostic } 13655868Sbostic if (*bp != type) 13755868Sbostic continue; 13855868Sbostic bp++; 13955868Sbostic return (*bp == '@' ? NULL : bp); 14055868Sbostic } 14155868Sbostic /* NOTREACHED */ 14255868Sbostic } 14355868Sbostic 14455868Sbostic /* 14555868Sbostic * Cgetent extracts the capability record name from the NULL terminated file 14655868Sbostic * array db_array and returns a pointer to a malloc'd copy of it in buf. 14755868Sbostic * Buf must be retained through all subsequent calls to cgetcap, cgetnum, 14855868Sbostic * cgetflag, and cgetstr, but may then be free'd. 0 is returned on success, 14955868Sbostic * -1 if the requested record couldn't be found, -2 if a system error was 15055868Sbostic * encountered (couldn't open/read a file, etc.), and -3 if a potential 15155868Sbostic * reference loop is detected. 15255868Sbostic */ 15355868Sbostic int 15455868Sbostic cgetent(buf, db_array, name) 15555868Sbostic char **buf, **db_array, *name; 15655868Sbostic { 15755868Sbostic u_int dummy; 15855868Sbostic 15955868Sbostic return (getent(buf, &dummy, db_array, -1, name, 0)); 16055868Sbostic } 16155868Sbostic 16255868Sbostic /* 16355868Sbostic * Getent implements the functions of cgetent. If fd is non-negative, 16455868Sbostic * *db_array has already been opened and fd is the open file descriptor. We 16555868Sbostic * do this to save time and avoid using up file descriptors for tc= 16655868Sbostic * recursions. 16755868Sbostic * 16855868Sbostic * Getent returns the same success/failure codes as cgetent. On success, a 16955868Sbostic * pointer to a malloc'ed capability record with all tc= capabilities fully 17055868Sbostic * expanded and its length (not including trailing ASCII NUL) are left in 17155868Sbostic * *cap and *len. 17255868Sbostic * 17355868Sbostic * Basic algorithm: 17455868Sbostic * + Allocate memory incrementally as needed in chunks of size BFRAG 17555868Sbostic * for capability buffer. 17655868Sbostic * + Recurse for each tc=name and interpolate result. Stop when all 17755868Sbostic * names interpolated, a name can't be found, or depth exceeds 17855868Sbostic * MAX_RECURSION. 17955868Sbostic */ 18055868Sbostic static int 18155868Sbostic getent(cap, len, db_array, fd, name, depth) 18255868Sbostic char **cap, **db_array, *name; 18355868Sbostic u_int *len; 18455868Sbostic int fd, depth; 18555868Sbostic { 18655868Sbostic register char *r_end, *rp, **db_p; 18755868Sbostic int myfd, eof, foundit; 18855868Sbostic char *record; 18955868Sbostic 19055868Sbostic /* 19155868Sbostic * Return with ``loop detected'' error if we've recursed more than 19255868Sbostic * MAX_RECURSION times. 19355868Sbostic */ 19455868Sbostic if (depth > MAX_RECURSION) 19555868Sbostic return (-3); 19655868Sbostic 19755868Sbostic /* 19855868Sbostic * Check if we have a top record from cgetset(). 19955868Sbostic */ 200*55875Selan if (depth == 0 && toprec != NULL && !gottoprec) { 20155868Sbostic if ((record = malloc (topreclen + BFRAG)) == NULL) { 20255868Sbostic errno = ENOMEM; 20355868Sbostic return (-2); 20455868Sbostic } 20555868Sbostic (void)strcpy(record, toprec); 20655868Sbostic myfd = 0; 20755868Sbostic db_p = db_array; 20855868Sbostic rp = record + topreclen + 1; 20955868Sbostic r_end = rp + BFRAG; 21055868Sbostic goto tc_exp; 21155868Sbostic } 21255868Sbostic /* 21355868Sbostic * Allocate first chunk of memory. 21455868Sbostic */ 21555868Sbostic if ((record = malloc(BFRAG)) == NULL) { 21655868Sbostic errno = ENOMEM; 21755868Sbostic return (-2); 21855868Sbostic } 21955868Sbostic r_end = record + BFRAG; 22055868Sbostic foundit = 0; 22155868Sbostic 22255868Sbostic /* 22355868Sbostic * Loop through database array until finding the record. 22455868Sbostic */ 22555868Sbostic 22655868Sbostic for (db_p = db_array; *db_p != NULL; db_p++) { 22755868Sbostic eof = 0; 22855868Sbostic 22955868Sbostic /* 23055868Sbostic * Open database if not already open. 23155868Sbostic */ 23255868Sbostic if (fd >= 0) { 23355868Sbostic (void)lseek(fd, 0L, L_SET); 23455868Sbostic myfd = 0; 23555868Sbostic } else { 23655868Sbostic fd = open(*db_p, O_RDONLY, 0); 23755868Sbostic if (fd < 0) { 23855868Sbostic free(record); 23955868Sbostic return (-2); 24055868Sbostic } 24155868Sbostic myfd = 1; 24255868Sbostic } 24355868Sbostic 24455868Sbostic /* 24555868Sbostic * Find the requested capability record ... 24655868Sbostic */ 24755868Sbostic { 24855868Sbostic char buf[BUFSIZ]; 24955868Sbostic register char *b_end, *bp; 25055868Sbostic register int c; 25155868Sbostic 25255868Sbostic /* 25355868Sbostic * Loop invariants: 25455868Sbostic * There is always room for one more character in record. 25555868Sbostic * R_end always points just past end of record. 25655868Sbostic * Rp always points just past last character in record. 25755868Sbostic * B_end always points just past last character in buf. 25855868Sbostic * Bp always points at next character in buf. 25955868Sbostic */ 26055868Sbostic b_end = buf; 26155868Sbostic bp = buf; 26255868Sbostic for (;;) { 26355868Sbostic 26455868Sbostic /* 26555868Sbostic * Read in a line implementing (\, newline) 26655868Sbostic * line continuation. 26755868Sbostic */ 26855868Sbostic rp = record; 26955868Sbostic for (;;) { 27055868Sbostic if (bp >= b_end) { 27155868Sbostic int n; 27255868Sbostic 27355868Sbostic n = read(fd, buf, sizeof(buf)); 27455868Sbostic if (n <= 0) { 27555868Sbostic if (myfd) 27655868Sbostic (void)close(fd); 27755868Sbostic if (n < 0) { 27855868Sbostic free(record); 27955868Sbostic return (-2); 28055868Sbostic } else { 28155868Sbostic fd = -1; 28255868Sbostic eof = 1; 28355868Sbostic break; 28455868Sbostic } 28555868Sbostic } 28655868Sbostic b_end = buf+n; 28755868Sbostic bp = buf; 28855868Sbostic } 28955868Sbostic 29055868Sbostic c = *bp++; 29155868Sbostic if (c == '\n') { 29255868Sbostic if (rp > record && *(rp-1) == '\\') { 29355868Sbostic rp--; 29455868Sbostic continue; 29555868Sbostic } else 29655868Sbostic break; 29755868Sbostic } 29855868Sbostic *rp++ = c; 29955868Sbostic 30055868Sbostic /* 30155868Sbostic * Enforce loop invariant: if no room 30255868Sbostic * left in record buffer, try to get 30355868Sbostic * some more. 30455868Sbostic */ 30555868Sbostic if (rp >= r_end) { 306*55875Selan u_int pos; 307*55875Selan size_t newsize; 30855868Sbostic 30955868Sbostic pos = rp - record; 31055868Sbostic newsize = r_end - record + BFRAG; 31155868Sbostic record = realloc(record, newsize); 31255868Sbostic if (record == NULL) { 31355868Sbostic errno = ENOMEM; 31455868Sbostic if (myfd) 31555868Sbostic (void)close(fd); 31655868Sbostic return (-2); 31755868Sbostic } 31855868Sbostic r_end = record + newsize; 31955868Sbostic rp = record + pos; 32055868Sbostic } 32155868Sbostic } 32255868Sbostic /* loop invariant let's us do this */ 32355868Sbostic *rp++ = '\0'; 32455868Sbostic 32555868Sbostic /* 32655868Sbostic * If encountered eof check next file. 32755868Sbostic */ 32855868Sbostic if (eof) 32955868Sbostic break; 33055868Sbostic 33155868Sbostic /* 33255868Sbostic * Toss blank lines and comments. 33355868Sbostic */ 33455868Sbostic if (*record == '\0' || *record == '#') 33555868Sbostic continue; 33655868Sbostic 33755868Sbostic /* 33855868Sbostic * See if this is the record we want ... 33955868Sbostic */ 34055868Sbostic if (cgetmatch(record, name) == 0) { 34155868Sbostic foundit = 1; 34255868Sbostic break; /* found it! */ 34355868Sbostic } 34455868Sbostic } 34555868Sbostic } 34655868Sbostic if (foundit) 34755868Sbostic break; 34855868Sbostic } 34955868Sbostic 35055868Sbostic if (!foundit) 35155868Sbostic return (-1); 35255868Sbostic 35355868Sbostic /* 35455868Sbostic * Got the capability record, but now we have to expand all tc=name 35555868Sbostic * references in it ... 35655868Sbostic */ 35755868Sbostic tc_exp: { 35855868Sbostic register char *newicap, *s; 35955868Sbostic register int newilen; 36055868Sbostic u_int ilen; 36155868Sbostic int diff, iret, tclen; 36255868Sbostic char *icap, *scan, *tc, *tcstart, *tcend; 36355868Sbostic 36455868Sbostic /* 36555868Sbostic * Loop invariants: 36655868Sbostic * There is room for one more character in record. 36755868Sbostic * R_end points just past end of record. 36855868Sbostic * Rp points just past last character in record. 36955868Sbostic * Scan points at remainder of record that needs to be 37055868Sbostic * scanned for tc=name constructs. 37155868Sbostic */ 37255868Sbostic scan = record; 37355868Sbostic for (;;) { 37455868Sbostic if ((tc = cgetcap(scan, "tc", '=')) == NULL) 37555868Sbostic break; 37655868Sbostic 37755868Sbostic /* 37855868Sbostic * Find end of tc=name and stomp on the trailing `:' 37955868Sbostic * (if present) so we can use it to call ourselves. 38055868Sbostic */ 38155868Sbostic s = tc; 38255868Sbostic for (;;) 38355868Sbostic if (*s == '\0') 38455868Sbostic break; 38555868Sbostic else 38655868Sbostic if (*s++ == ':') { 38755868Sbostic *(s-1) = '\0'; 38855868Sbostic break; 38955868Sbostic } 39055868Sbostic tcstart = tc - 3; 39155868Sbostic tclen = s - tcstart; 39255868Sbostic tcend = s; 39355868Sbostic 39455868Sbostic iret = getent(&icap, &ilen, db_p, fd, tc, depth+1); 39555868Sbostic newicap = icap; /* Put into a register. */ 39655868Sbostic newilen = ilen; 39755868Sbostic if (iret != 0) { 39855868Sbostic /* an error or couldn't resolve tc= */ 39955868Sbostic if (myfd) 40055868Sbostic (void)close(fd); 40155868Sbostic free(record); 40255868Sbostic return (iret); 40355868Sbostic } 40455868Sbostic 40555868Sbostic /* not interested in name field of tc'ed record */ 40655868Sbostic s = newicap; 40755868Sbostic for (;;) 40855868Sbostic if (*s == '\0') 40955868Sbostic break; 41055868Sbostic else 41155868Sbostic if (*s++ == ':') 41255868Sbostic break; 41355868Sbostic newilen -= s - newicap; 41455868Sbostic newicap = s; 41555868Sbostic 41655868Sbostic /* make sure interpolated record is `:'-terminated */ 41755868Sbostic s += newilen; 41855868Sbostic if (*(s-1) != ':') { 41955868Sbostic *s = ':'; /* overwrite NUL with : */ 42055868Sbostic newilen++; 42155868Sbostic } 42255868Sbostic 42355868Sbostic /* 42455868Sbostic * Make sure there's enough room to insert the 42555868Sbostic * new record. 42655868Sbostic */ 42755868Sbostic diff = newilen - tclen; 42855868Sbostic if (diff >= r_end - rp) { 429*55875Selan u_int pos, tcpos, tcposend; 430*55875Selan size_t newsize; 43155868Sbostic 43255868Sbostic pos = rp - record; 43355868Sbostic newsize = r_end - record + diff + BFRAG; 43455868Sbostic tcpos = tcstart - record; 43555868Sbostic tcposend = tcend - record; 43655868Sbostic record = realloc(record, newsize); 43755868Sbostic if (record == NULL) { 43855868Sbostic errno = ENOMEM; 43955868Sbostic if (myfd) 44055868Sbostic (void)close(fd); 44155868Sbostic free(icap); 44255868Sbostic return (-2); 44355868Sbostic } 44455868Sbostic r_end = record + newsize; 44555868Sbostic rp = record + pos; 44655868Sbostic tcstart = record + tcpos; 44755868Sbostic tcend = record + tcposend; 44855868Sbostic } 44955868Sbostic 45055868Sbostic /* 45155868Sbostic * Insert tc'ed record into our record. 45255868Sbostic */ 45355868Sbostic s = tcstart + newilen; 45455868Sbostic bcopy(tcend, s, rp - tcend); 45555868Sbostic bcopy(newicap, tcstart, newilen); 45655868Sbostic rp += diff; 45755868Sbostic free(icap); 45855868Sbostic 45955868Sbostic /* 46055868Sbostic * Start scan on `:' so next cgetcap works properly 46155868Sbostic * (cgetcap always skips first field). 46255868Sbostic */ 46355868Sbostic scan = s-1; 46455868Sbostic } 46555868Sbostic } 46655868Sbostic 46755868Sbostic /* 46855868Sbostic * Close file (if we opened it), give back any extra memory, and 46955868Sbostic * return capability, length and success. 47055868Sbostic */ 47155868Sbostic if (myfd) 47255868Sbostic (void)close(fd); 47355868Sbostic *len = rp - record - 1; /* don't count NUL */ 47455868Sbostic if (r_end > rp) 475*55875Selan record = realloc(record, (size_t)(rp - record)); 47655868Sbostic *cap = record; 47755868Sbostic return (0); 47855868Sbostic } 47955868Sbostic 48055868Sbostic /* 48155868Sbostic * Cgetmatch will return 0 if name is one of the names of the capability 48255868Sbostic * record buf, -1 if not. 48355868Sbostic */ 48455868Sbostic int 48555868Sbostic cgetmatch(buf, name) 48655868Sbostic char *buf, *name; 48755868Sbostic { 48855868Sbostic register char *np, *bp; 48955868Sbostic 49055868Sbostic /* 49155868Sbostic * Start search at beginning of record. 49255868Sbostic */ 49355868Sbostic bp = buf; 49455868Sbostic for (;;) { 49555868Sbostic /* 49655868Sbostic * Try to match a record name. 49755868Sbostic */ 49855868Sbostic np = name; 49955868Sbostic for (;;) 50055868Sbostic if (*np == '\0') 50155868Sbostic if (*bp == '|' || *bp == ':' || *bp == '\0') 50255868Sbostic return (0); 50355868Sbostic else 50455868Sbostic break; 50555868Sbostic else 50655868Sbostic if (*bp++ != *np++) 50755868Sbostic break; 50855868Sbostic 50955868Sbostic /* 51055868Sbostic * Match failed, skip to next name in record. 51155868Sbostic */ 51255868Sbostic bp--; /* a '|' or ':' may have stopped the match */ 51355868Sbostic for (;;) 51455868Sbostic if (*bp == '\0' || *bp == ':') 51555868Sbostic return (-1); /* match failed totally */ 51655868Sbostic else 51755868Sbostic if (*bp++ == '|') 51855868Sbostic break; /* found next name */ 51955868Sbostic } 52055868Sbostic } 52155868Sbostic 52255868Sbostic int 52355868Sbostic cgetfirst(buf, db_array) 52455868Sbostic char **buf, **db_array; 52555868Sbostic { 526*55875Selan if (toprec) { 527*55875Selan if ((*buf = malloc(topreclen + 1)) == NULL) { 528*55875Selan errno = ENOMEM; 529*55875Selan return(-2); 530*55875Selan } 531*55875Selan strcpy(*buf, toprec); 532*55875Selan (void)cgetclose(); 533*55875Selan gottoprec = 1; 534*55875Selan return(1); 535*55875Selan } 53655868Sbostic (void)cgetclose(); 53755868Sbostic return (cgetnext(buf, db_array)); 53855868Sbostic } 53955868Sbostic 54055868Sbostic static FILE *pfp; 54155868Sbostic static int slash; 54255868Sbostic static char **dbp; 54355868Sbostic 54455868Sbostic int 54555868Sbostic cgetclose() 54655868Sbostic { 54755868Sbostic if (pfp != NULL) { 54855868Sbostic (void)fclose(pfp); 54955868Sbostic pfp = NULL; 55055868Sbostic } 55155868Sbostic dbp = NULL; 552*55875Selan gottoprec = 0; 55355868Sbostic slash = 0; 554*55875Selan return(0); 55555868Sbostic } 55655868Sbostic 55755868Sbostic /* 55855868Sbostic * Cgetnext() gets either the first or next entry in the logical database 55955868Sbostic * specified by db_array. It returns 0 upon completion of the database, 1 56055868Sbostic * upon returning an entry with more remaining, and -1 if an error occurs. 56155868Sbostic */ 56255868Sbostic int 56355868Sbostic cgetnext(bp, db_array) 56455868Sbostic register char **bp; 56555868Sbostic char **db_array; 56655868Sbostic { 56755868Sbostic size_t len; 56855868Sbostic int status; 56955868Sbostic char *cp, *line, *rp, buf[BSIZE]; 57055868Sbostic 571*55875Selan if (dbp == NULL) { 572*55875Selan if (toprec && !gottoprec) { 573*55875Selan if ((*bp = malloc(topreclen + 1)) == NULL) { 574*55875Selan errno = ENOMEM; 575*55875Selan return(-2); 576*55875Selan } 577*55875Selan strcpy(*bp, toprec); 578*55875Selan gottoprec = 1; 579*55875Selan return(1); 580*55875Selan } 58155868Sbostic dbp = db_array; 582*55875Selan } 583*55875Selan if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) { 584*55875Selan (void)cgetclose(); 58555868Sbostic return (-1); 586*55875Selan } 58755868Sbostic for(;;) { 58855868Sbostic line = fgetline(pfp, &len); 58955868Sbostic if (line == NULL) { 59055868Sbostic (void)fclose(pfp); 59155868Sbostic if (ferror(pfp)) { 592*55875Selan (void)cgetclose(); 59355868Sbostic return (-1); 59455868Sbostic } else { 59555868Sbostic dbp++; 59655868Sbostic if (*dbp == NULL) { 597*55875Selan (void)cgetclose(); 59855868Sbostic return (0); 59955868Sbostic } else if ((pfp = fopen(*dbp, "r")) == NULL) { 600*55875Selan (void)cgetclose(); 60155868Sbostic return (-1); 60255868Sbostic } else 60355868Sbostic continue; 60455868Sbostic } 60555868Sbostic } 60655868Sbostic if (isspace(*line) || *line == ':' || *line == '#' 60755868Sbostic || len == 0 || slash) { 60855868Sbostic if (len > 0 && line[len - 1] == '\\') 60955868Sbostic slash = 1; 61055868Sbostic else 61155868Sbostic slash = 0; 61255868Sbostic continue; 61355868Sbostic } 61455868Sbostic if (len > 0 && line[len - 1] == '\\') 61555868Sbostic slash = 1; 61655868Sbostic else 61755868Sbostic slash = 0; 61855868Sbostic 61955868Sbostic /* line points to a name line */ 62055868Sbostic 62155868Sbostic rp = buf; 62255868Sbostic for(cp = line; *cp != NULL; cp++) 62355868Sbostic if (*cp == '|' || *cp == ':') 62455868Sbostic break; 62555868Sbostic else 62655868Sbostic *rp++ = *cp; 62755868Sbostic 62855868Sbostic *rp = '\0'; 62955868Sbostic status = cgetent(bp, db_array, &buf[0]); 63055868Sbostic if (status == 0) 63155868Sbostic return (1); 63255868Sbostic if (status == -2 || status == -3) { 633*55875Selan (void)cgetclose(); 63455868Sbostic return (status + 1); 63555868Sbostic } 63655868Sbostic } 63755868Sbostic /* NOTREACHED */ 63855868Sbostic } 63955868Sbostic 64055868Sbostic /* 64155868Sbostic * Cgetstr retrieves the value of the string capability cap from the 64255868Sbostic * capability record pointed to by buf. A pointer to a decoded, NUL 64355868Sbostic * terminated, malloc'd copy of the string is returned in the char * 64455868Sbostic * pointed to by str. The length of the string not including the trailing 64555868Sbostic * NUL is returned on success, -1 if the requested string capability 64655868Sbostic * couldn't be found, -2 if a system error was encountered (storage 64755868Sbostic * allocation failure). 64855868Sbostic */ 64955868Sbostic int 65055868Sbostic cgetstr(buf, cap, str) 65155868Sbostic char *buf, *cap; 65255868Sbostic char **str; 65355868Sbostic { 65455868Sbostic register u_int m_room; 65555868Sbostic register char *bp, *mp; 65655868Sbostic int len; 65755868Sbostic char *mem; 65855868Sbostic 65955868Sbostic /* 66055868Sbostic * Find string capability cap 66155868Sbostic */ 66255868Sbostic bp = cgetcap(buf, cap, '='); 66355868Sbostic if (bp == NULL) 66455868Sbostic return (-1); 66555868Sbostic 66655868Sbostic /* 66755868Sbostic * Conversion / storage allocation loop ... Allocate memory in 66855868Sbostic * chunks SFRAG in size. 66955868Sbostic */ 67055868Sbostic if ((mem = malloc(SFRAG)) == NULL) { 67155868Sbostic errno = ENOMEM; 67255868Sbostic return (-2); /* couldn't even allocate the first fragment */ 67355868Sbostic } 67455868Sbostic m_room = SFRAG; 67555868Sbostic mp = mem; 67655868Sbostic 67755868Sbostic while (*bp != ':' && *bp != '\0') { 67855868Sbostic /* 67955868Sbostic * Loop invariants: 68055868Sbostic * There is always room for one more character in mem. 68155868Sbostic * Mp always points just past last character in mem. 68255868Sbostic * Bp always points at next character in buf. 68355868Sbostic */ 68455868Sbostic if (*bp == '^') { 68555868Sbostic bp++; 68655868Sbostic if (*bp == ':' || *bp == '\0') 68755868Sbostic break; /* drop unfinished escape */ 68855868Sbostic *mp++ = *bp++ & 037; 68955868Sbostic } else if (*bp == '\\') { 69055868Sbostic bp++; 69155868Sbostic if (*bp == ':' || *bp == '\0') 69255868Sbostic break; /* drop unfinished escape */ 69355868Sbostic if ('0' <= *bp && *bp <= '7') { 69455868Sbostic register int n, i; 69555868Sbostic 69655868Sbostic n = 0; 69755868Sbostic i = 3; /* maximum of three octal digits */ 69855868Sbostic do { 69955868Sbostic n = n * 8 + (*bp++ - '0'); 70055868Sbostic } while (--i && '0' <= *bp && *bp <= '7'); 70155868Sbostic *mp++ = n; 70255868Sbostic } 70355868Sbostic else switch (*bp++) { 70455868Sbostic case 'b': case 'B': 70555868Sbostic *mp++ = '\b'; 70655868Sbostic break; 70755868Sbostic case 't': case 'T': 70855868Sbostic *mp++ = '\t'; 70955868Sbostic break; 71055868Sbostic case 'n': case 'N': 71155868Sbostic *mp++ = '\n'; 71255868Sbostic break; 71355868Sbostic case 'f': case 'F': 71455868Sbostic *mp++ = '\f'; 71555868Sbostic break; 71655868Sbostic case 'r': case 'R': 71755868Sbostic *mp++ = '\r'; 71855868Sbostic break; 71955868Sbostic case 'e': case 'E': 72055868Sbostic *mp++ = ESC; 72155868Sbostic break; 72255868Sbostic case 'c': case 'C': 72355868Sbostic *mp++ = ':'; 72455868Sbostic break; 72555868Sbostic default: 72655868Sbostic /* 72755868Sbostic * Catches '\', '^', and 72855868Sbostic * everything else. 72955868Sbostic */ 73055868Sbostic *mp++ = *(bp-1); 73155868Sbostic break; 73255868Sbostic } 73355868Sbostic } else 73455868Sbostic *mp++ = *bp++; 73555868Sbostic m_room--; 73655868Sbostic 73755868Sbostic /* 73855868Sbostic * Enforce loop invariant: if no room left in current 73955868Sbostic * buffer, try to get some more. 74055868Sbostic */ 74155868Sbostic if (m_room == 0) { 742*55875Selan size_t size = mp - mem; 74355868Sbostic 74455868Sbostic if ((mem = realloc(mem, size + SFRAG)) == NULL) 74555868Sbostic return (-2); 74655868Sbostic m_room = SFRAG; 74755868Sbostic mp = mem + size; 74855868Sbostic } 74955868Sbostic } 75055868Sbostic *mp++ = '\0'; /* loop invariant let's us do this */ 75155868Sbostic m_room--; 75255868Sbostic len = mp - mem - 1; 75355868Sbostic 75455868Sbostic /* 75555868Sbostic * Give back any extra memory and return value and success. 75655868Sbostic */ 75755868Sbostic if (m_room != 0) 758*55875Selan mem = realloc(mem, (size_t)(mp - mem)); 75955868Sbostic *str = mem; 76055868Sbostic return (len); 76155868Sbostic } 76255868Sbostic 76355868Sbostic /* 76455868Sbostic * Cgetustr retrieves the value of the string capability cap from the 76555868Sbostic * capability record pointed to by buf. The difference between cgetustr() 76655868Sbostic * and cgetstr() is that cgetustr does not decode escapes but rather treats 76755868Sbostic * all characters literally. A pointer to a NUL terminated malloc'd 76855868Sbostic * copy of the string is returned in the char pointed to by str. The 76955868Sbostic * length of the string not including the trailing NUL is returned on success, 77055868Sbostic * -1 if the requested string capability couldn't be found, -2 if a system 77155868Sbostic * error was encountered (storage allocation failure). 77255868Sbostic */ 77355868Sbostic int 77455868Sbostic cgetustr(buf, cap, str) 77555868Sbostic char *buf, *cap, **str; 77655868Sbostic { 77755868Sbostic register u_int m_room; 77855868Sbostic register char *bp, *mp; 77955868Sbostic int len; 78055868Sbostic char *mem; 78155868Sbostic 78255868Sbostic /* 78355868Sbostic * Find string capability cap 78455868Sbostic */ 78555868Sbostic if ((bp = cgetcap(buf, cap, '=')) == NULL) 78655868Sbostic return (-1); 78755868Sbostic 78855868Sbostic /* 78955868Sbostic * Conversion / storage allocation loop ... Allocate memory in 79055868Sbostic * chunks SFRAG in size. 79155868Sbostic */ 79255868Sbostic if ((mem = malloc(SFRAG)) == NULL) { 79355868Sbostic errno = ENOMEM; 79455868Sbostic return (-2); /* couldn't even allocate the first fragment */ 79555868Sbostic } 79655868Sbostic m_room = SFRAG; 79755868Sbostic mp = mem; 79855868Sbostic 79955868Sbostic while (*bp != ':' && *bp != '\0') { 80055868Sbostic /* 80155868Sbostic * Loop invariants: 80255868Sbostic * There is always room for one more character in mem. 80355868Sbostic * Mp always points just past last character in mem. 80455868Sbostic * Bp always points at next character in buf. 80555868Sbostic */ 80655868Sbostic *mp++ = *bp++; 80755868Sbostic m_room--; 80855868Sbostic 80955868Sbostic /* 81055868Sbostic * Enforce loop invariant: if no room left in current 81155868Sbostic * buffer, try to get some more. 81255868Sbostic */ 81355868Sbostic if (m_room == 0) { 814*55875Selan size_t size = mp - mem; 81555868Sbostic 81655868Sbostic if ((mem = realloc(mem, size + SFRAG)) == NULL) 81755868Sbostic return (-2); 81855868Sbostic m_room = SFRAG; 81955868Sbostic mp = mem + size; 82055868Sbostic } 82155868Sbostic } 82255868Sbostic *mp++ = '\0'; /* loop invariant let's us do this */ 82355868Sbostic m_room--; 82455868Sbostic len = mp - mem - 1; 82555868Sbostic 82655868Sbostic /* 82755868Sbostic * Give back any extra memory and return value and success. 82855868Sbostic */ 82955868Sbostic if (m_room != 0) 830*55875Selan mem = realloc(mem, (size_t)(mp - mem)); 83155868Sbostic *str = mem; 83255868Sbostic return (len); 83355868Sbostic } 83455868Sbostic 83555868Sbostic /* 83655868Sbostic * Cgetnum retrieves the value of the numeric capability cap from the 83755868Sbostic * capability record pointed to by buf. The numeric value is returned in 83855868Sbostic * the long pointed to by num. 0 is returned on success, -1 if the requested 83955868Sbostic * numeric capability couldn't be found. 84055868Sbostic */ 84155868Sbostic int 84255868Sbostic cgetnum(buf, cap, num) 84355868Sbostic char *buf, *cap; 84455868Sbostic long *num; 84555868Sbostic { 84655868Sbostic register long n; 84755868Sbostic register int base, digit; 84855868Sbostic register char *bp; 84955868Sbostic 85055868Sbostic /* 85155868Sbostic * Find numeric capability cap 85255868Sbostic */ 85355868Sbostic bp = cgetcap(buf, cap, '#'); 85455868Sbostic if (bp == NULL) 85555868Sbostic return (-1); 85655868Sbostic 85755868Sbostic /* 85855868Sbostic * Look at value and determine numeric base: 85955868Sbostic * 0x... or 0X... hexadecimal, 86055868Sbostic * else 0... octal, 86155868Sbostic * else decimal. 86255868Sbostic */ 86355868Sbostic if (*bp == '0') { 86455868Sbostic bp++; 86555868Sbostic if (*bp == 'x' || *bp == 'X') { 86655868Sbostic bp++; 86755868Sbostic base = 16; 86855868Sbostic } else 86955868Sbostic base = 8; 87055868Sbostic } else 87155868Sbostic base = 10; 87255868Sbostic 87355868Sbostic /* 87455868Sbostic * Conversion loop ... 87555868Sbostic */ 87655868Sbostic n = 0; 87755868Sbostic for (;;) { 87855868Sbostic if ('0' <= *bp && *bp <= '9') 87955868Sbostic digit = *bp - '0'; 88055868Sbostic else 88155868Sbostic if ( ('a' <= *bp && *bp <= 'f') 88255868Sbostic || ('A' <= *bp && *bp <= 'F')) 88355868Sbostic digit = 10 + *bp - 'a'; 88455868Sbostic else 88555868Sbostic break; 88655868Sbostic 88755868Sbostic if (digit >= base) 88855868Sbostic break; 88955868Sbostic 89055868Sbostic n = n * base + digit; 89155868Sbostic bp++; 89255868Sbostic } 89355868Sbostic 89455868Sbostic /* 89555868Sbostic * Return value and success. 89655868Sbostic */ 89755868Sbostic *num = n; 89855868Sbostic return (0); 89955868Sbostic } 900