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 * 856237Sralph * %sccs.include.redist.c% 955868Sbostic */ 1055868Sbostic 1155868Sbostic #if defined(LIBC_SCCS) && !defined(lint) 12*58735Sbostic static char sccsid[] = "@(#)getcap.c 5.15 (Berkeley) 03/19/93"; 1355868Sbostic #endif /* LIBC_SCCS and not lint */ 1455868Sbostic 1555868Sbostic #include <sys/types.h> 1655868Sbostic 1755868Sbostic #include <ctype.h> 1855915Selan #include <db.h> 1955868Sbostic #include <errno.h> 2055868Sbostic #include <fcntl.h> 2155915Selan #include <limits.h> 2255868Sbostic #include <stdio.h> 2355868Sbostic #include <stdlib.h> 2455868Sbostic #include <string.h> 2555868Sbostic #include <unistd.h> 2655868Sbostic 2755868Sbostic #define BFRAG 1024 2856197Selan #define BSIZE 1024 2955868Sbostic #define ESC ('[' & 037) /* ASCII ESC */ 3055868Sbostic #define MAX_RECURSION 32 /* maximum getent recursion */ 3155868Sbostic #define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */ 3255868Sbostic 3356208Selan #define RECOK (char)0 3456208Selan #define TCERR (char)1 3558522Sbostic #define SHADOW (char)2 3655915Selan 3755868Sbostic static size_t topreclen; /* toprec length */ 3855868Sbostic static char *toprec; /* Additional record specified by cgetset() */ 3955875Selan static int gottoprec; /* Flag indicating retrieval of toprecord */ 4055868Sbostic 4155915Selan static int cdbget __P((DB *, char **, char *)); 4256197Selan static int getent __P((char **, u_int *, char **, int, char *, int, char *)); 4356197Selan static int nfcmp __P((char *, char *)); 4455868Sbostic 4555868Sbostic /* 4655868Sbostic * Cgetset() allows the addition of a user specified buffer to be added 4755868Sbostic * to the database array, in effect "pushing" the buffer on top of the 4855868Sbostic * virtual database. 0 is returned on success, -1 on failure. 4955868Sbostic */ 5055868Sbostic int 5155868Sbostic cgetset(ent) 5255868Sbostic char *ent; 5355868Sbostic { 5455868Sbostic if (ent == NULL) { 5555868Sbostic if (toprec) 5655868Sbostic free(toprec); 5755868Sbostic toprec = NULL; 5855868Sbostic topreclen = 0; 5955868Sbostic return (0); 6055868Sbostic } 6155868Sbostic topreclen = strlen(ent); 6255868Sbostic if ((toprec = malloc (topreclen + 1)) == NULL) { 6355868Sbostic errno = ENOMEM; 6455868Sbostic return (-1); 6555868Sbostic } 6655876Selan gottoprec = 0; 6755868Sbostic (void)strcpy(toprec, ent); 6855868Sbostic return (0); 6955868Sbostic } 7055868Sbostic 7155868Sbostic /* 7255868Sbostic * Cgetcap searches the capability record buf for the capability cap with 7355868Sbostic * type `type'. A pointer to the value of cap is returned on success, NULL 7455868Sbostic * if the requested capability couldn't be found. 7555868Sbostic * 7655868Sbostic * Specifying a type of ':' means that nothing should follow cap (:cap:). 7755868Sbostic * In this case a pointer to the terminating ':' or NUL will be returned if 7855868Sbostic * cap is found. 7955868Sbostic * 8055868Sbostic * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator) 8155868Sbostic * return NULL. 8255868Sbostic */ 8355868Sbostic char * 8455868Sbostic cgetcap(buf, cap, type) 8555868Sbostic char *buf, *cap; 8655868Sbostic int type; 8755868Sbostic { 8855868Sbostic register char *bp, *cp; 8955868Sbostic 9055868Sbostic bp = buf; 9155868Sbostic for (;;) { 9255868Sbostic /* 9355868Sbostic * Skip past the current capability field - it's either the 9455868Sbostic * name field if this is the first time through the loop, or 9555868Sbostic * the remainder of a field whose name failed to match cap. 9655868Sbostic */ 9755868Sbostic for (;;) 9855868Sbostic if (*bp == '\0') 9955868Sbostic return (NULL); 10055868Sbostic else 10155868Sbostic if (*bp++ == ':') 10255868Sbostic break; 10355868Sbostic 10455868Sbostic /* 10555868Sbostic * Try to match (cap, type) in buf. 10655868Sbostic */ 10755868Sbostic for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++) 10855868Sbostic continue; 10955868Sbostic if (*cp != '\0') 11055868Sbostic continue; 11155868Sbostic if (*bp == '@') 11255868Sbostic return (NULL); 11355868Sbostic if (type == ':') { 11455868Sbostic if (*bp != '\0' && *bp != ':') 11555868Sbostic continue; 11655868Sbostic return(bp); 11755868Sbostic } 11855868Sbostic if (*bp != type) 11955868Sbostic continue; 12055868Sbostic bp++; 12155868Sbostic return (*bp == '@' ? NULL : bp); 12255868Sbostic } 12355868Sbostic /* NOTREACHED */ 12455868Sbostic } 12555868Sbostic 12655868Sbostic /* 12755868Sbostic * Cgetent extracts the capability record name from the NULL terminated file 12855868Sbostic * array db_array and returns a pointer to a malloc'd copy of it in buf. 12955868Sbostic * Buf must be retained through all subsequent calls to cgetcap, cgetnum, 13055868Sbostic * cgetflag, and cgetstr, but may then be free'd. 0 is returned on success, 13155868Sbostic * -1 if the requested record couldn't be found, -2 if a system error was 13255868Sbostic * encountered (couldn't open/read a file, etc.), and -3 if a potential 13355868Sbostic * reference loop is detected. 13455868Sbostic */ 13555868Sbostic int 13655868Sbostic cgetent(buf, db_array, name) 13755868Sbostic char **buf, **db_array, *name; 13855868Sbostic { 13955868Sbostic u_int dummy; 14055868Sbostic 14156197Selan return (getent(buf, &dummy, db_array, -1, name, 0, NULL)); 14255868Sbostic } 14355868Sbostic 14455868Sbostic /* 14555868Sbostic * Getent implements the functions of cgetent. If fd is non-negative, 14655868Sbostic * *db_array has already been opened and fd is the open file descriptor. We 14755868Sbostic * do this to save time and avoid using up file descriptors for tc= 14855868Sbostic * recursions. 14955868Sbostic * 15055868Sbostic * Getent returns the same success/failure codes as cgetent. On success, a 15155868Sbostic * pointer to a malloc'ed capability record with all tc= capabilities fully 15255868Sbostic * expanded and its length (not including trailing ASCII NUL) are left in 15355868Sbostic * *cap and *len. 15455868Sbostic * 15555868Sbostic * Basic algorithm: 15655868Sbostic * + Allocate memory incrementally as needed in chunks of size BFRAG 15755868Sbostic * for capability buffer. 15855868Sbostic * + Recurse for each tc=name and interpolate result. Stop when all 15955868Sbostic * names interpolated, a name can't be found, or depth exceeds 16055868Sbostic * MAX_RECURSION. 16155868Sbostic */ 16255868Sbostic static int 16356197Selan getent(cap, len, db_array, fd, name, depth, nfield) 16456197Selan char **cap, **db_array, *name, *nfield; 16555868Sbostic u_int *len; 16655868Sbostic int fd, depth; 16755868Sbostic { 16855915Selan DB *capdbp; 16955915Selan DBT key, data; 17055868Sbostic register char *r_end, *rp, **db_p; 17155915Selan int myfd, eof, foundit, retval; 17255868Sbostic char *record; 17355915Selan int tc_not_resolved; 17455915Selan char pbuf[_POSIX_PATH_MAX]; 17555915Selan 17655868Sbostic /* 17755868Sbostic * Return with ``loop detected'' error if we've recursed more than 17855868Sbostic * MAX_RECURSION times. 17955868Sbostic */ 18055868Sbostic if (depth > MAX_RECURSION) 18155868Sbostic return (-3); 18255868Sbostic 18355868Sbostic /* 18455868Sbostic * Check if we have a top record from cgetset(). 18555868Sbostic */ 18655876Selan if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) { 18755868Sbostic if ((record = malloc (topreclen + BFRAG)) == NULL) { 18855868Sbostic errno = ENOMEM; 18955868Sbostic return (-2); 19055868Sbostic } 19155868Sbostic (void)strcpy(record, toprec); 19255868Sbostic myfd = 0; 19355868Sbostic db_p = db_array; 19455868Sbostic rp = record + topreclen + 1; 19555868Sbostic r_end = rp + BFRAG; 19655868Sbostic goto tc_exp; 19755868Sbostic } 19855868Sbostic /* 19955868Sbostic * Allocate first chunk of memory. 20055868Sbostic */ 20155868Sbostic if ((record = malloc(BFRAG)) == NULL) { 20255868Sbostic errno = ENOMEM; 20355868Sbostic return (-2); 20455868Sbostic } 20555868Sbostic r_end = record + BFRAG; 20655868Sbostic foundit = 0; 20755868Sbostic /* 20855868Sbostic * Loop through database array until finding the record. 20955868Sbostic */ 21055868Sbostic 21155868Sbostic for (db_p = db_array; *db_p != NULL; db_p++) { 21255868Sbostic eof = 0; 21355868Sbostic 21455868Sbostic /* 21555868Sbostic * Open database if not already open. 21655868Sbostic */ 21755915Selan 21855868Sbostic if (fd >= 0) { 219*58735Sbostic (void)lseek(fd, (off_t)0, L_SET); 22055868Sbostic myfd = 0; 22155868Sbostic } else { 222*58735Sbostic (void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p); 22355915Selan if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0)) 22455915Selan != NULL) { 22555868Sbostic free(record); 22655915Selan retval = cdbget(capdbp, &record, name); 22755915Selan if (capdbp->close(capdbp) < 0) 22855915Selan return (-2); 229*58735Sbostic *len = strlen(record); 230*58735Sbostic *cap = malloc(*len + 1); 231*58735Sbostic memmove(*cap, record, *len + 1); 23256208Selan return (retval); 23355915Selan } else { 23455915Selan fd = open(*db_p, O_RDONLY, 0); 23555915Selan if (fd < 0) { 23656216Selan /* No error on unfound file. */ 23756216Selan if (errno == ENOENT) 23856216Selan continue; 23955915Selan free(record); 24055915Selan return (-2); 24155915Selan } 24255915Selan myfd = 1; 24355868Sbostic } 24455868Sbostic } 24555868Sbostic /* 24655868Sbostic * Find the requested capability record ... 24755868Sbostic */ 24855868Sbostic { 24955868Sbostic char buf[BUFSIZ]; 25055868Sbostic register char *b_end, *bp; 25155868Sbostic register int c; 25255868Sbostic 25355868Sbostic /* 25455868Sbostic * Loop invariants: 25555868Sbostic * There is always room for one more character in record. 25655868Sbostic * R_end always points just past end of record. 25755868Sbostic * Rp always points just past last character in record. 25855868Sbostic * B_end always points just past last character in buf. 25955868Sbostic * Bp always points at next character in buf. 26055868Sbostic */ 26155868Sbostic b_end = buf; 26255868Sbostic bp = buf; 26355868Sbostic for (;;) { 26455868Sbostic 26555868Sbostic /* 26655868Sbostic * Read in a line implementing (\, newline) 26755868Sbostic * line continuation. 26855868Sbostic */ 26955868Sbostic rp = record; 27055868Sbostic for (;;) { 27155868Sbostic if (bp >= b_end) { 27255868Sbostic int n; 27355868Sbostic 27455868Sbostic n = read(fd, buf, sizeof(buf)); 27555868Sbostic if (n <= 0) { 27655868Sbostic if (myfd) 27755868Sbostic (void)close(fd); 27855868Sbostic if (n < 0) { 27955868Sbostic free(record); 28055868Sbostic return (-2); 28155868Sbostic } else { 28255868Sbostic fd = -1; 28355868Sbostic eof = 1; 28455868Sbostic break; 28555868Sbostic } 28655868Sbostic } 28755868Sbostic b_end = buf+n; 28855868Sbostic bp = buf; 28955868Sbostic } 29055868Sbostic 29155868Sbostic c = *bp++; 29255868Sbostic if (c == '\n') { 29355868Sbostic if (rp > record && *(rp-1) == '\\') { 29455868Sbostic rp--; 29555868Sbostic continue; 29655868Sbostic } else 29755868Sbostic break; 29855868Sbostic } 29955868Sbostic *rp++ = c; 30055868Sbostic 30155868Sbostic /* 30255868Sbostic * Enforce loop invariant: if no room 30355868Sbostic * left in record buffer, try to get 30455868Sbostic * some more. 30555868Sbostic */ 30655868Sbostic if (rp >= r_end) { 30755875Selan u_int pos; 30855875Selan size_t newsize; 30955868Sbostic 31055868Sbostic pos = rp - record; 31155868Sbostic newsize = r_end - record + BFRAG; 31255868Sbostic record = realloc(record, newsize); 31355868Sbostic if (record == NULL) { 31455868Sbostic errno = ENOMEM; 31555868Sbostic if (myfd) 31655868Sbostic (void)close(fd); 31755868Sbostic return (-2); 31855868Sbostic } 31955868Sbostic r_end = record + newsize; 32055868Sbostic rp = record + pos; 32155868Sbostic } 32255868Sbostic } 32355868Sbostic /* loop invariant let's us do this */ 32455868Sbostic *rp++ = '\0'; 32555868Sbostic 32655868Sbostic /* 32755868Sbostic * If encountered eof check next file. 32855868Sbostic */ 32955868Sbostic if (eof) 33055868Sbostic break; 33155868Sbostic 33255868Sbostic /* 33355868Sbostic * Toss blank lines and comments. 33455868Sbostic */ 33555868Sbostic if (*record == '\0' || *record == '#') 33655868Sbostic continue; 33755868Sbostic 33855868Sbostic /* 33955868Sbostic * See if this is the record we want ... 34055868Sbostic */ 34155868Sbostic if (cgetmatch(record, name) == 0) { 34256197Selan if (nfield == NULL || !nfcmp(nfield, record)) { 34356197Selan foundit = 1; 34456197Selan break; /* found it! */ 34556197Selan } 34655868Sbostic } 34755868Sbostic } 34856197Selan } 34955868Sbostic if (foundit) 35055868Sbostic break; 35155868Sbostic } 35255868Sbostic 35355868Sbostic if (!foundit) 35455868Sbostic return (-1); 35555876Selan 35655868Sbostic /* 35755868Sbostic * Got the capability record, but now we have to expand all tc=name 35855868Sbostic * references in it ... 35955868Sbostic */ 36055868Sbostic tc_exp: { 36155868Sbostic register char *newicap, *s; 36255868Sbostic register int newilen; 36355868Sbostic u_int ilen; 36455868Sbostic int diff, iret, tclen; 36555868Sbostic char *icap, *scan, *tc, *tcstart, *tcend; 36655868Sbostic 36755868Sbostic /* 36855868Sbostic * Loop invariants: 36955868Sbostic * There is room for one more character in record. 37055868Sbostic * R_end points just past end of record. 37155868Sbostic * Rp points just past last character in record. 37255868Sbostic * Scan points at remainder of record that needs to be 37355868Sbostic * scanned for tc=name constructs. 37455868Sbostic */ 37555868Sbostic scan = record; 37655915Selan tc_not_resolved = 0; 37755868Sbostic for (;;) { 37855868Sbostic if ((tc = cgetcap(scan, "tc", '=')) == NULL) 37955868Sbostic break; 38055868Sbostic 38155868Sbostic /* 38255868Sbostic * Find end of tc=name and stomp on the trailing `:' 38355868Sbostic * (if present) so we can use it to call ourselves. 38455868Sbostic */ 38555868Sbostic s = tc; 38655868Sbostic for (;;) 38755868Sbostic if (*s == '\0') 38855868Sbostic break; 38955868Sbostic else 39055868Sbostic if (*s++ == ':') { 39156184Selan *(s - 1) = '\0'; 39255868Sbostic break; 39355868Sbostic } 39455868Sbostic tcstart = tc - 3; 39555868Sbostic tclen = s - tcstart; 39655868Sbostic tcend = s; 39755868Sbostic 39856197Selan iret = getent(&icap, &ilen, db_p, fd, tc, depth+1, 39956197Selan NULL); 40055868Sbostic newicap = icap; /* Put into a register. */ 40155868Sbostic newilen = ilen; 40255868Sbostic if (iret != 0) { 40356208Selan /* an error */ 40456208Selan if (iret < -1) { 40556208Selan if (myfd) 40656208Selan (void)close(fd); 40756208Selan free(record); 40856208Selan return (iret); 40956208Selan } 41055915Selan if (iret == 1) 41155915Selan tc_not_resolved = 1; 41256208Selan /* couldn't resolve tc */ 41355915Selan if (iret == -1) { 41456184Selan *(s - 1) = ':'; 41555915Selan scan = s - 1; 41655915Selan tc_not_resolved = 1; 41755915Selan continue; 41855915Selan 41955915Selan } 42055868Sbostic } 42155868Sbostic /* not interested in name field of tc'ed record */ 42255868Sbostic s = newicap; 42355868Sbostic for (;;) 42455868Sbostic if (*s == '\0') 42555868Sbostic break; 42655868Sbostic else 42755868Sbostic if (*s++ == ':') 42855868Sbostic break; 42955868Sbostic newilen -= s - newicap; 43055868Sbostic newicap = s; 43155868Sbostic 43255868Sbostic /* make sure interpolated record is `:'-terminated */ 43355868Sbostic s += newilen; 43455868Sbostic if (*(s-1) != ':') { 43555868Sbostic *s = ':'; /* overwrite NUL with : */ 43655868Sbostic newilen++; 43755868Sbostic } 43855868Sbostic 43955868Sbostic /* 44055868Sbostic * Make sure there's enough room to insert the 44155868Sbostic * new record. 44255868Sbostic */ 44355868Sbostic diff = newilen - tclen; 44455868Sbostic if (diff >= r_end - rp) { 44555875Selan u_int pos, tcpos, tcposend; 44655875Selan size_t newsize; 44755868Sbostic 44855868Sbostic pos = rp - record; 44955868Sbostic newsize = r_end - record + diff + BFRAG; 45055868Sbostic tcpos = tcstart - record; 45155868Sbostic tcposend = tcend - record; 45255868Sbostic record = realloc(record, newsize); 45355868Sbostic if (record == NULL) { 45455868Sbostic errno = ENOMEM; 45555868Sbostic if (myfd) 45655868Sbostic (void)close(fd); 45755868Sbostic free(icap); 45855868Sbostic return (-2); 45955868Sbostic } 46055868Sbostic r_end = record + newsize; 46155868Sbostic rp = record + pos; 46255868Sbostic tcstart = record + tcpos; 46355868Sbostic tcend = record + tcposend; 46455868Sbostic } 46555868Sbostic 46655868Sbostic /* 46755868Sbostic * Insert tc'ed record into our record. 46855868Sbostic */ 46955868Sbostic s = tcstart + newilen; 47055868Sbostic bcopy(tcend, s, rp - tcend); 47155868Sbostic bcopy(newicap, tcstart, newilen); 47255868Sbostic rp += diff; 47355868Sbostic free(icap); 47455868Sbostic 47555868Sbostic /* 47655868Sbostic * Start scan on `:' so next cgetcap works properly 47755868Sbostic * (cgetcap always skips first field). 47855868Sbostic */ 47955868Sbostic scan = s-1; 48055868Sbostic } 48156197Selan 48255868Sbostic } 48355868Sbostic /* 48455868Sbostic * Close file (if we opened it), give back any extra memory, and 48555868Sbostic * return capability, length and success. 48655868Sbostic */ 48755868Sbostic if (myfd) 48855868Sbostic (void)close(fd); 48955868Sbostic *len = rp - record - 1; /* don't count NUL */ 49055868Sbostic if (r_end > rp) 49156184Selan if ((record = 49256184Selan realloc(record, (size_t)(rp - record))) == NULL) { 49356184Selan errno = ENOMEM; 49456184Selan return (-2); 49556184Selan } 49656184Selan 49755868Sbostic *cap = record; 49855915Selan if (tc_not_resolved) 49955915Selan return (1); 50055868Sbostic return (0); 50156197Selan } 50255868Sbostic 50355915Selan static int 50455915Selan cdbget(capdbp, bp, name) 50555915Selan DB *capdbp; 50655915Selan char **bp, *name; 50755915Selan { 50855915Selan DBT key, data; 50955915Selan char *buf; 51055915Selan int st; 51155915Selan 51255915Selan key.data = name; 51358522Sbostic key.size = strlen(name); 51455915Selan 51558522Sbostic for (;;) { 51658522Sbostic /* Get the reference. */ 51758522Sbostic switch(capdbp->get(capdbp, &key, &data, 0)) { 51858522Sbostic case -1: 51958522Sbostic return (-2); 52058522Sbostic case 1: 52158522Sbostic return (-1); 52258522Sbostic } 52356208Selan 524*58735Sbostic /* If not an index to another record, leave. */ 52558522Sbostic if (((char *)data.data)[0] != SHADOW) 52658522Sbostic break; 52755915Selan 528*58735Sbostic key.data = (char *)data.data + 1; 52958522Sbostic key.size = data.size - 1; 53058522Sbostic } 53155915Selan 532*58735Sbostic *bp = (char *)data.data + 1; 533*58735Sbostic return (((char *)(data.data))[0] == TCERR ? 1 : 0); 53455915Selan } 53555915Selan 53655868Sbostic /* 53755868Sbostic * Cgetmatch will return 0 if name is one of the names of the capability 53855868Sbostic * record buf, -1 if not. 53955868Sbostic */ 54055868Sbostic int 54155868Sbostic cgetmatch(buf, name) 54255868Sbostic char *buf, *name; 54355868Sbostic { 54455868Sbostic register char *np, *bp; 54555868Sbostic 54655868Sbostic /* 54755868Sbostic * Start search at beginning of record. 54855868Sbostic */ 54955868Sbostic bp = buf; 55055868Sbostic for (;;) { 55155868Sbostic /* 55255868Sbostic * Try to match a record name. 55355868Sbostic */ 55455868Sbostic np = name; 55555868Sbostic for (;;) 55655868Sbostic if (*np == '\0') 55755868Sbostic if (*bp == '|' || *bp == ':' || *bp == '\0') 55855868Sbostic return (0); 55955868Sbostic else 56055868Sbostic break; 56155868Sbostic else 56255868Sbostic if (*bp++ != *np++) 56355868Sbostic break; 56455868Sbostic 56555868Sbostic /* 56655868Sbostic * Match failed, skip to next name in record. 56755868Sbostic */ 56855868Sbostic bp--; /* a '|' or ':' may have stopped the match */ 56955868Sbostic for (;;) 57055868Sbostic if (*bp == '\0' || *bp == ':') 57155868Sbostic return (-1); /* match failed totally */ 57255868Sbostic else 57355868Sbostic if (*bp++ == '|') 57455868Sbostic break; /* found next name */ 57555868Sbostic } 57655868Sbostic } 57755868Sbostic 57855876Selan 57955876Selan 58055876Selan 58155876Selan 58255868Sbostic int 58355868Sbostic cgetfirst(buf, db_array) 58455868Sbostic char **buf, **db_array; 58555868Sbostic { 58655868Sbostic (void)cgetclose(); 58755868Sbostic return (cgetnext(buf, db_array)); 58855868Sbostic } 58955868Sbostic 59055868Sbostic static FILE *pfp; 59155868Sbostic static int slash; 59255868Sbostic static char **dbp; 59355868Sbostic 59455868Sbostic int 59555868Sbostic cgetclose() 59655868Sbostic { 59755868Sbostic if (pfp != NULL) { 59855868Sbostic (void)fclose(pfp); 59955868Sbostic pfp = NULL; 60055868Sbostic } 60155868Sbostic dbp = NULL; 60255875Selan gottoprec = 0; 60355868Sbostic slash = 0; 60455875Selan return(0); 60555868Sbostic } 60655868Sbostic 60755868Sbostic /* 60855868Sbostic * Cgetnext() gets either the first or next entry in the logical database 60955868Sbostic * specified by db_array. It returns 0 upon completion of the database, 1 61055868Sbostic * upon returning an entry with more remaining, and -1 if an error occurs. 61155868Sbostic */ 61255868Sbostic int 61355868Sbostic cgetnext(bp, db_array) 61455868Sbostic register char **bp; 61555868Sbostic char **db_array; 61655868Sbostic { 61755868Sbostic size_t len; 61856197Selan int status, i, done; 61956197Selan char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE]; 62055876Selan u_int dummy; 62155868Sbostic 62255876Selan if (dbp == NULL) 62355868Sbostic dbp = db_array; 62455876Selan 62555875Selan if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) { 62655875Selan (void)cgetclose(); 62755868Sbostic return (-1); 62855875Selan } 62955868Sbostic for(;;) { 63055876Selan if (toprec && !gottoprec) { 63155876Selan gottoprec = 1; 63255876Selan line = toprec; 63355876Selan } else { 63455876Selan line = fgetline(pfp, &len); 63555876Selan if (line == NULL && pfp) { 63655876Selan (void)fclose(pfp); 63755876Selan if (ferror(pfp)) { 63855875Selan (void)cgetclose(); 63955868Sbostic return (-1); 64055876Selan } else { 64158522Sbostic if (*++dbp == NULL) { 64255876Selan (void)cgetclose(); 64355876Selan return (0); 64458522Sbostic } else if ((pfp = 64558522Sbostic fopen(*dbp, "r")) == NULL) { 64655876Selan (void)cgetclose(); 64755876Selan return (-1); 64855876Selan } else 64955876Selan continue; 65055876Selan } 65158452Selan } else 65258452Selan line[len - 1] = '\0'; 65358522Sbostic if (len == 1) { 65458522Sbostic slash = 0; 65558522Sbostic continue; 65658522Sbostic } 65758522Sbostic if (isspace(*line) || 65858522Sbostic *line == ':' || *line == '#' || slash) { 65958522Sbostic if (line[len - 2] == '\\') 66055876Selan slash = 1; 66155876Selan else 66255876Selan slash = 0; 66355876Selan continue; 66455876Selan } 66558522Sbostic if (line[len - 2] == '\\') 66655868Sbostic slash = 1; 66755868Sbostic else 66855868Sbostic slash = 0; 66955876Selan } 67055868Sbostic 67156197Selan 67256197Selan /* 67356197Selan * Line points to a name line. 67456197Selan */ 67556197Selan i = 0; 67656197Selan done = 0; 67756197Selan np = nbuf; 67856197Selan for (;;) { 67956197Selan for (cp = line; *cp != '\0'; cp++) { 68056197Selan if (*cp == ':') { 68156197Selan *np++ = ':'; 68256197Selan done = 1; 68356197Selan break; 68456197Selan } 68556197Selan if (*cp == '\\') 68656197Selan break; 68756197Selan *np++ = *cp; 68856197Selan } 68956197Selan if (done) { 69056197Selan *np = '\0'; 69156197Selan break; 69256197Selan } else { /* name field extends beyond the line */ 69356197Selan line = fgetline(pfp, &len); 69456197Selan if (line == NULL && pfp) { 69556197Selan (void)fclose(pfp); 69656197Selan if (ferror(pfp)) { 69756197Selan (void)cgetclose(); 69856197Selan return (-1); 69956197Selan } 70058457Sbostic } else 70158452Selan line[len - 1] = '\0'; 70256197Selan } 70356197Selan } 70455868Sbostic rp = buf; 70556197Selan for(cp = nbuf; *cp != NULL; cp++) 70655868Sbostic if (*cp == '|' || *cp == ':') 70755868Sbostic break; 70855868Sbostic else 70955868Sbostic *rp++ = *cp; 71055868Sbostic 71155868Sbostic *rp = '\0'; 71256200Selan /* 71356200Selan * XXX 71456200Selan * Last argument of getent here should be nbuf if we want true 71556200Selan * sequential access in the case of duplicates. 71656200Selan * With NULL, getent will return the first entry found 71756200Selan * rather than the duplicate entry record. This is a 71856200Selan * matter of semantics that should be resolved. 71956200Selan */ 72056200Selan status = getent(bp, &dummy, db_array, -1, buf, 0, NULL); 72155915Selan if (status == -2 || status == -3) 72255875Selan (void)cgetclose(); 72355915Selan 72455915Selan return (status + 1); 72555868Sbostic } 72655868Sbostic /* NOTREACHED */ 72755868Sbostic } 72855868Sbostic 72955868Sbostic /* 73055868Sbostic * Cgetstr retrieves the value of the string capability cap from the 73155868Sbostic * capability record pointed to by buf. A pointer to a decoded, NUL 73255868Sbostic * terminated, malloc'd copy of the string is returned in the char * 73355868Sbostic * pointed to by str. The length of the string not including the trailing 73455868Sbostic * NUL is returned on success, -1 if the requested string capability 73555868Sbostic * couldn't be found, -2 if a system error was encountered (storage 73655868Sbostic * allocation failure). 73755868Sbostic */ 73855868Sbostic int 73955868Sbostic cgetstr(buf, cap, str) 74055868Sbostic char *buf, *cap; 74155868Sbostic char **str; 74255868Sbostic { 74355868Sbostic register u_int m_room; 74455868Sbostic register char *bp, *mp; 74555868Sbostic int len; 74655868Sbostic char *mem; 74755868Sbostic 74855868Sbostic /* 74955868Sbostic * Find string capability cap 75055868Sbostic */ 75155868Sbostic bp = cgetcap(buf, cap, '='); 75255868Sbostic if (bp == NULL) 75355868Sbostic return (-1); 75455868Sbostic 75555868Sbostic /* 75655868Sbostic * Conversion / storage allocation loop ... Allocate memory in 75755868Sbostic * chunks SFRAG in size. 75855868Sbostic */ 75955868Sbostic if ((mem = malloc(SFRAG)) == NULL) { 76055868Sbostic errno = ENOMEM; 76155868Sbostic return (-2); /* couldn't even allocate the first fragment */ 76255868Sbostic } 76355868Sbostic m_room = SFRAG; 76455868Sbostic mp = mem; 76555868Sbostic 76655868Sbostic while (*bp != ':' && *bp != '\0') { 76755868Sbostic /* 76855868Sbostic * Loop invariants: 76955868Sbostic * There is always room for one more character in mem. 77055868Sbostic * Mp always points just past last character in mem. 77155868Sbostic * Bp always points at next character in buf. 77255868Sbostic */ 77355868Sbostic if (*bp == '^') { 77455868Sbostic bp++; 77555868Sbostic if (*bp == ':' || *bp == '\0') 77655868Sbostic break; /* drop unfinished escape */ 77755868Sbostic *mp++ = *bp++ & 037; 77855868Sbostic } else if (*bp == '\\') { 77955868Sbostic bp++; 78055868Sbostic if (*bp == ':' || *bp == '\0') 78155868Sbostic break; /* drop unfinished escape */ 78255868Sbostic if ('0' <= *bp && *bp <= '7') { 78355868Sbostic register int n, i; 78455868Sbostic 78555868Sbostic n = 0; 78655868Sbostic i = 3; /* maximum of three octal digits */ 78755868Sbostic do { 78855868Sbostic n = n * 8 + (*bp++ - '0'); 78955868Sbostic } while (--i && '0' <= *bp && *bp <= '7'); 79055868Sbostic *mp++ = n; 79155868Sbostic } 79255868Sbostic else switch (*bp++) { 79355868Sbostic case 'b': case 'B': 79455868Sbostic *mp++ = '\b'; 79555868Sbostic break; 79655868Sbostic case 't': case 'T': 79755868Sbostic *mp++ = '\t'; 79855868Sbostic break; 79955868Sbostic case 'n': case 'N': 80055868Sbostic *mp++ = '\n'; 80155868Sbostic break; 80255868Sbostic case 'f': case 'F': 80355868Sbostic *mp++ = '\f'; 80455868Sbostic break; 80555868Sbostic case 'r': case 'R': 80655868Sbostic *mp++ = '\r'; 80755868Sbostic break; 80855868Sbostic case 'e': case 'E': 80955868Sbostic *mp++ = ESC; 81055868Sbostic break; 81155868Sbostic case 'c': case 'C': 81255868Sbostic *mp++ = ':'; 81355868Sbostic break; 81455868Sbostic default: 81555868Sbostic /* 81655868Sbostic * Catches '\', '^', and 81755868Sbostic * everything else. 81855868Sbostic */ 81955868Sbostic *mp++ = *(bp-1); 82055868Sbostic break; 82155868Sbostic } 82255868Sbostic } else 82355868Sbostic *mp++ = *bp++; 82455868Sbostic m_room--; 82555868Sbostic 82655868Sbostic /* 82755868Sbostic * Enforce loop invariant: if no room left in current 82855868Sbostic * buffer, try to get some more. 82955868Sbostic */ 83055868Sbostic if (m_room == 0) { 83155875Selan size_t size = mp - mem; 83255868Sbostic 83355868Sbostic if ((mem = realloc(mem, size + SFRAG)) == NULL) 83455868Sbostic return (-2); 83555868Sbostic m_room = SFRAG; 83655868Sbostic mp = mem + size; 83755868Sbostic } 83855868Sbostic } 83955868Sbostic *mp++ = '\0'; /* loop invariant let's us do this */ 84055868Sbostic m_room--; 84155868Sbostic len = mp - mem - 1; 84255868Sbostic 84355868Sbostic /* 84455868Sbostic * Give back any extra memory and return value and success. 84555868Sbostic */ 84655868Sbostic if (m_room != 0) 84756184Selan if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 84856184Selan return (-2); 84955868Sbostic *str = mem; 85055868Sbostic return (len); 85155868Sbostic } 85255868Sbostic 85355868Sbostic /* 85455868Sbostic * Cgetustr retrieves the value of the string capability cap from the 85555868Sbostic * capability record pointed to by buf. The difference between cgetustr() 85655868Sbostic * and cgetstr() is that cgetustr does not decode escapes but rather treats 85755868Sbostic * all characters literally. A pointer to a NUL terminated malloc'd 85855868Sbostic * copy of the string is returned in the char pointed to by str. The 85955868Sbostic * length of the string not including the trailing NUL is returned on success, 86055868Sbostic * -1 if the requested string capability couldn't be found, -2 if a system 86155868Sbostic * error was encountered (storage allocation failure). 86255868Sbostic */ 86355868Sbostic int 86455868Sbostic cgetustr(buf, cap, str) 86555868Sbostic char *buf, *cap, **str; 86655868Sbostic { 86755868Sbostic register u_int m_room; 86855868Sbostic register char *bp, *mp; 86955868Sbostic int len; 87055868Sbostic char *mem; 87155868Sbostic 87255868Sbostic /* 87355868Sbostic * Find string capability cap 87455868Sbostic */ 87555868Sbostic if ((bp = cgetcap(buf, cap, '=')) == NULL) 87655868Sbostic return (-1); 87755868Sbostic 87855868Sbostic /* 87955868Sbostic * Conversion / storage allocation loop ... Allocate memory in 88055868Sbostic * chunks SFRAG in size. 88155868Sbostic */ 88255868Sbostic if ((mem = malloc(SFRAG)) == NULL) { 88355868Sbostic errno = ENOMEM; 88455868Sbostic return (-2); /* couldn't even allocate the first fragment */ 88555868Sbostic } 88655868Sbostic m_room = SFRAG; 88755868Sbostic mp = mem; 88855868Sbostic 88955868Sbostic while (*bp != ':' && *bp != '\0') { 89055868Sbostic /* 89155868Sbostic * Loop invariants: 89255868Sbostic * There is always room for one more character in mem. 89355868Sbostic * Mp always points just past last character in mem. 89455868Sbostic * Bp always points at next character in buf. 89555868Sbostic */ 89655868Sbostic *mp++ = *bp++; 89755868Sbostic m_room--; 89855868Sbostic 89955868Sbostic /* 90055868Sbostic * Enforce loop invariant: if no room left in current 90155868Sbostic * buffer, try to get some more. 90255868Sbostic */ 90355868Sbostic if (m_room == 0) { 90455875Selan size_t size = mp - mem; 90555868Sbostic 90655868Sbostic if ((mem = realloc(mem, size + SFRAG)) == NULL) 90755868Sbostic return (-2); 90855868Sbostic m_room = SFRAG; 90955868Sbostic mp = mem + size; 91055868Sbostic } 91155868Sbostic } 91255868Sbostic *mp++ = '\0'; /* loop invariant let's us do this */ 91355868Sbostic m_room--; 91455868Sbostic len = mp - mem - 1; 91555868Sbostic 91655868Sbostic /* 91755868Sbostic * Give back any extra memory and return value and success. 91855868Sbostic */ 91955868Sbostic if (m_room != 0) 92056184Selan if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 92156184Selan return (-2); 92255868Sbostic *str = mem; 92355868Sbostic return (len); 92455868Sbostic } 92555868Sbostic 92655868Sbostic /* 92755868Sbostic * Cgetnum retrieves the value of the numeric capability cap from the 92855868Sbostic * capability record pointed to by buf. The numeric value is returned in 92955868Sbostic * the long pointed to by num. 0 is returned on success, -1 if the requested 93055868Sbostic * numeric capability couldn't be found. 93155868Sbostic */ 93255868Sbostic int 93355868Sbostic cgetnum(buf, cap, num) 93455868Sbostic char *buf, *cap; 93555868Sbostic long *num; 93655868Sbostic { 93755868Sbostic register long n; 93855868Sbostic register int base, digit; 93955868Sbostic register char *bp; 94055868Sbostic 94155868Sbostic /* 94255868Sbostic * Find numeric capability cap 94355868Sbostic */ 94455868Sbostic bp = cgetcap(buf, cap, '#'); 94555868Sbostic if (bp == NULL) 94655868Sbostic return (-1); 94755868Sbostic 94855868Sbostic /* 94955868Sbostic * Look at value and determine numeric base: 95055868Sbostic * 0x... or 0X... hexadecimal, 95155868Sbostic * else 0... octal, 95255868Sbostic * else decimal. 95355868Sbostic */ 95455868Sbostic if (*bp == '0') { 95555868Sbostic bp++; 95655868Sbostic if (*bp == 'x' || *bp == 'X') { 95755868Sbostic bp++; 95855868Sbostic base = 16; 95955868Sbostic } else 96055868Sbostic base = 8; 96155868Sbostic } else 96255868Sbostic base = 10; 96355868Sbostic 96455868Sbostic /* 96555868Sbostic * Conversion loop ... 96655868Sbostic */ 96755868Sbostic n = 0; 96855868Sbostic for (;;) { 96955868Sbostic if ('0' <= *bp && *bp <= '9') 97055868Sbostic digit = *bp - '0'; 97156236Sralph else if ('a' <= *bp && *bp <= 'f') 97256236Sralph digit = 10 + *bp - 'a'; 97356236Sralph else if ('A' <= *bp && *bp <= 'F') 97456236Sralph digit = 10 + *bp - 'A'; 97555868Sbostic else 97656236Sralph break; 97755868Sbostic 97855868Sbostic if (digit >= base) 97955868Sbostic break; 98055868Sbostic 98155868Sbostic n = n * base + digit; 98255868Sbostic bp++; 98355868Sbostic } 98455868Sbostic 98555868Sbostic /* 98655868Sbostic * Return value and success. 98755868Sbostic */ 98855868Sbostic *num = n; 98955868Sbostic return (0); 99055868Sbostic } 99156197Selan 99256197Selan 99356197Selan /* 99456197Selan * Compare name field of record. 99556197Selan */ 99656197Selan static int 99756197Selan nfcmp(nf, rec) 99856197Selan char *nf, *rec; 99956197Selan { 100056197Selan char *cp, tmp; 100156197Selan int ret; 100256197Selan 100356197Selan for (cp = rec; *cp != ':'; cp++) 100456197Selan ; 100556197Selan 100656197Selan tmp = *(cp + 1); 100756197Selan *(cp + 1) = '\0'; 100856197Selan ret = strcmp(nf, rec); 100956197Selan *(cp + 1) = tmp; 101056197Selan 101156197Selan return (ret); 101256197Selan } 1013