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*56237Sralph * %sccs.include.redist.c% 955868Sbostic */ 1055868Sbostic 1155868Sbostic #if defined(LIBC_SCCS) && !defined(lint) 12*56237Sralph static char sccsid[] = "@(#)getcap.c 5.11 (Berkeley) 09/13/92"; 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 3555915Selan 3655868Sbostic static size_t topreclen; /* toprec length */ 3755868Sbostic static char *toprec; /* Additional record specified by cgetset() */ 3855875Selan static int gottoprec; /* Flag indicating retrieval of toprecord */ 3955868Sbostic 4055915Selan static int cdbget __P((DB *, char **, char *)); 4156197Selan static int getent __P((char **, u_int *, char **, int, char *, int, char *)); 4256197Selan static int nfcmp __P((char *, char *)); 4355868Sbostic 4455868Sbostic /* 4555868Sbostic * Cgetset() allows the addition of a user specified buffer to be added 4655868Sbostic * to the database array, in effect "pushing" the buffer on top of the 4755868Sbostic * virtual database. 0 is returned on success, -1 on failure. 4855868Sbostic */ 4955868Sbostic int 5055868Sbostic cgetset(ent) 5155868Sbostic char *ent; 5255868Sbostic { 5355868Sbostic if (ent == NULL) { 5455868Sbostic if (toprec) 5555868Sbostic free(toprec); 5655868Sbostic toprec = NULL; 5755868Sbostic topreclen = 0; 5855868Sbostic return (0); 5955868Sbostic } 6055868Sbostic topreclen = strlen(ent); 6155868Sbostic if ((toprec = malloc (topreclen + 1)) == NULL) { 6255868Sbostic errno = ENOMEM; 6355868Sbostic return (-1); 6455868Sbostic } 6555876Selan gottoprec = 0; 6655868Sbostic (void)strcpy(toprec, ent); 6755868Sbostic return (0); 6855868Sbostic } 6955868Sbostic 7055868Sbostic /* 7155868Sbostic * Cgetcap searches the capability record buf for the capability cap with 7255868Sbostic * type `type'. A pointer to the value of cap is returned on success, NULL 7355868Sbostic * if the requested capability couldn't be found. 7455868Sbostic * 7555868Sbostic * Specifying a type of ':' means that nothing should follow cap (:cap:). 7655868Sbostic * In this case a pointer to the terminating ':' or NUL will be returned if 7755868Sbostic * cap is found. 7855868Sbostic * 7955868Sbostic * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator) 8055868Sbostic * return NULL. 8155868Sbostic */ 8255868Sbostic char * 8355868Sbostic cgetcap(buf, cap, type) 8455868Sbostic char *buf, *cap; 8555868Sbostic int type; 8655868Sbostic { 8755868Sbostic register char *bp, *cp; 8855868Sbostic 8955868Sbostic bp = buf; 9055868Sbostic for (;;) { 9155868Sbostic /* 9255868Sbostic * Skip past the current capability field - it's either the 9355868Sbostic * name field if this is the first time through the loop, or 9455868Sbostic * the remainder of a field whose name failed to match cap. 9555868Sbostic */ 9655868Sbostic for (;;) 9755868Sbostic if (*bp == '\0') 9855868Sbostic return (NULL); 9955868Sbostic else 10055868Sbostic if (*bp++ == ':') 10155868Sbostic break; 10255868Sbostic 10355868Sbostic /* 10455868Sbostic * Try to match (cap, type) in buf. 10555868Sbostic */ 10655868Sbostic for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++) 10755868Sbostic continue; 10855868Sbostic if (*cp != '\0') 10955868Sbostic continue; 11055868Sbostic if (*bp == '@') 11155868Sbostic return (NULL); 11255868Sbostic if (type == ':') { 11355868Sbostic if (*bp != '\0' && *bp != ':') 11455868Sbostic continue; 11555868Sbostic return(bp); 11655868Sbostic } 11755868Sbostic if (*bp != type) 11855868Sbostic continue; 11955868Sbostic bp++; 12055868Sbostic return (*bp == '@' ? NULL : bp); 12155868Sbostic } 12255868Sbostic /* NOTREACHED */ 12355868Sbostic } 12455868Sbostic 12555868Sbostic /* 12655868Sbostic * Cgetent extracts the capability record name from the NULL terminated file 12755868Sbostic * array db_array and returns a pointer to a malloc'd copy of it in buf. 12855868Sbostic * Buf must be retained through all subsequent calls to cgetcap, cgetnum, 12955868Sbostic * cgetflag, and cgetstr, but may then be free'd. 0 is returned on success, 13055868Sbostic * -1 if the requested record couldn't be found, -2 if a system error was 13155868Sbostic * encountered (couldn't open/read a file, etc.), and -3 if a potential 13255868Sbostic * reference loop is detected. 13355868Sbostic */ 13455868Sbostic int 13555868Sbostic cgetent(buf, db_array, name) 13655868Sbostic char **buf, **db_array, *name; 13755868Sbostic { 13855868Sbostic u_int dummy; 13955868Sbostic 14056197Selan return (getent(buf, &dummy, db_array, -1, name, 0, NULL)); 14155868Sbostic } 14255868Sbostic 14355868Sbostic /* 14455868Sbostic * Getent implements the functions of cgetent. If fd is non-negative, 14555868Sbostic * *db_array has already been opened and fd is the open file descriptor. We 14655868Sbostic * do this to save time and avoid using up file descriptors for tc= 14755868Sbostic * recursions. 14855868Sbostic * 14955868Sbostic * Getent returns the same success/failure codes as cgetent. On success, a 15055868Sbostic * pointer to a malloc'ed capability record with all tc= capabilities fully 15155868Sbostic * expanded and its length (not including trailing ASCII NUL) are left in 15255868Sbostic * *cap and *len. 15355868Sbostic * 15455868Sbostic * Basic algorithm: 15555868Sbostic * + Allocate memory incrementally as needed in chunks of size BFRAG 15655868Sbostic * for capability buffer. 15755868Sbostic * + Recurse for each tc=name and interpolate result. Stop when all 15855868Sbostic * names interpolated, a name can't be found, or depth exceeds 15955868Sbostic * MAX_RECURSION. 16055868Sbostic */ 16155868Sbostic static int 16256197Selan getent(cap, len, db_array, fd, name, depth, nfield) 16356197Selan char **cap, **db_array, *name, *nfield; 16455868Sbostic u_int *len; 16555868Sbostic int fd, depth; 16655868Sbostic { 16755915Selan DB *capdbp; 16855915Selan DBT key, data; 16955868Sbostic register char *r_end, *rp, **db_p; 17055915Selan int myfd, eof, foundit, retval; 17155868Sbostic char *record; 17255915Selan int tc_not_resolved; 17355915Selan char pbuf[_POSIX_PATH_MAX]; 17455915Selan 17555868Sbostic /* 17655868Sbostic * Return with ``loop detected'' error if we've recursed more than 17755868Sbostic * MAX_RECURSION times. 17855868Sbostic */ 17955868Sbostic if (depth > MAX_RECURSION) 18055868Sbostic return (-3); 18155868Sbostic 18255868Sbostic /* 18355868Sbostic * Check if we have a top record from cgetset(). 18455868Sbostic */ 18555876Selan if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) { 18655868Sbostic if ((record = malloc (topreclen + BFRAG)) == NULL) { 18755868Sbostic errno = ENOMEM; 18855868Sbostic return (-2); 18955868Sbostic } 19055868Sbostic (void)strcpy(record, toprec); 19155868Sbostic myfd = 0; 19255868Sbostic db_p = db_array; 19355868Sbostic rp = record + topreclen + 1; 19455868Sbostic r_end = rp + BFRAG; 19555868Sbostic goto tc_exp; 19655868Sbostic } 19755868Sbostic /* 19855868Sbostic * Allocate first chunk of memory. 19955868Sbostic */ 20055868Sbostic if ((record = malloc(BFRAG)) == NULL) { 20155868Sbostic errno = ENOMEM; 20255868Sbostic return (-2); 20355868Sbostic } 20455868Sbostic r_end = record + BFRAG; 20555868Sbostic foundit = 0; 20655868Sbostic /* 20755868Sbostic * Loop through database array until finding the record. 20855868Sbostic */ 20955868Sbostic 21055868Sbostic for (db_p = db_array; *db_p != NULL; db_p++) { 21155868Sbostic eof = 0; 21255868Sbostic 21355868Sbostic /* 21455868Sbostic * Open database if not already open. 21555868Sbostic */ 21655915Selan 21755868Sbostic if (fd >= 0) { 21855868Sbostic (void)lseek(fd, 0L, L_SET); 21955868Sbostic myfd = 0; 22055868Sbostic } else { 22155915Selan sprintf(pbuf, "%s.db", *db_p); 22255915Selan if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0)) 22355915Selan != NULL) { 22455868Sbostic free(record); 22555915Selan retval = cdbget(capdbp, &record, name); 22655915Selan if (capdbp->close(capdbp) < 0) 22755915Selan return (-2); 22856208Selan *cap = malloc (strlen(record) + 1); 22956208Selan strcpy(*cap, record); 23056208Selan return (retval); 23155915Selan } else { 23255915Selan fd = open(*db_p, O_RDONLY, 0); 23355915Selan if (fd < 0) { 23456216Selan /* No error on unfound file. */ 23556216Selan if (errno == ENOENT) 23656216Selan continue; 23755915Selan free(record); 23855915Selan return (-2); 23955915Selan } 24055915Selan myfd = 1; 24155868Sbostic } 24255868Sbostic } 24355868Sbostic /* 24455868Sbostic * Find the requested capability record ... 24555868Sbostic */ 24655868Sbostic { 24755868Sbostic char buf[BUFSIZ]; 24855868Sbostic register char *b_end, *bp; 24955868Sbostic register int c; 25055868Sbostic 25155868Sbostic /* 25255868Sbostic * Loop invariants: 25355868Sbostic * There is always room for one more character in record. 25455868Sbostic * R_end always points just past end of record. 25555868Sbostic * Rp always points just past last character in record. 25655868Sbostic * B_end always points just past last character in buf. 25755868Sbostic * Bp always points at next character in buf. 25855868Sbostic */ 25955868Sbostic b_end = buf; 26055868Sbostic bp = buf; 26155868Sbostic for (;;) { 26255868Sbostic 26355868Sbostic /* 26455868Sbostic * Read in a line implementing (\, newline) 26555868Sbostic * line continuation. 26655868Sbostic */ 26755868Sbostic rp = record; 26855868Sbostic for (;;) { 26955868Sbostic if (bp >= b_end) { 27055868Sbostic int n; 27155868Sbostic 27255868Sbostic n = read(fd, buf, sizeof(buf)); 27355868Sbostic if (n <= 0) { 27455868Sbostic if (myfd) 27555868Sbostic (void)close(fd); 27655868Sbostic if (n < 0) { 27755868Sbostic free(record); 27855868Sbostic return (-2); 27955868Sbostic } else { 28055868Sbostic fd = -1; 28155868Sbostic eof = 1; 28255868Sbostic break; 28355868Sbostic } 28455868Sbostic } 28555868Sbostic b_end = buf+n; 28655868Sbostic bp = buf; 28755868Sbostic } 28855868Sbostic 28955868Sbostic c = *bp++; 29055868Sbostic if (c == '\n') { 29155868Sbostic if (rp > record && *(rp-1) == '\\') { 29255868Sbostic rp--; 29355868Sbostic continue; 29455868Sbostic } else 29555868Sbostic break; 29655868Sbostic } 29755868Sbostic *rp++ = c; 29855868Sbostic 29955868Sbostic /* 30055868Sbostic * Enforce loop invariant: if no room 30155868Sbostic * left in record buffer, try to get 30255868Sbostic * some more. 30355868Sbostic */ 30455868Sbostic if (rp >= r_end) { 30555875Selan u_int pos; 30655875Selan size_t newsize; 30755868Sbostic 30855868Sbostic pos = rp - record; 30955868Sbostic newsize = r_end - record + BFRAG; 31055868Sbostic record = realloc(record, newsize); 31155868Sbostic if (record == NULL) { 31255868Sbostic errno = ENOMEM; 31355868Sbostic if (myfd) 31455868Sbostic (void)close(fd); 31555868Sbostic return (-2); 31655868Sbostic } 31755868Sbostic r_end = record + newsize; 31855868Sbostic rp = record + pos; 31955868Sbostic } 32055868Sbostic } 32155868Sbostic /* loop invariant let's us do this */ 32255868Sbostic *rp++ = '\0'; 32355868Sbostic 32455868Sbostic /* 32555868Sbostic * If encountered eof check next file. 32655868Sbostic */ 32755868Sbostic if (eof) 32855868Sbostic break; 32955868Sbostic 33055868Sbostic /* 33155868Sbostic * Toss blank lines and comments. 33255868Sbostic */ 33355868Sbostic if (*record == '\0' || *record == '#') 33455868Sbostic continue; 33555868Sbostic 33655868Sbostic /* 33755868Sbostic * See if this is the record we want ... 33855868Sbostic */ 33955868Sbostic if (cgetmatch(record, name) == 0) { 34056197Selan if (nfield == NULL || !nfcmp(nfield, record)) { 34156197Selan foundit = 1; 34256197Selan break; /* found it! */ 34356197Selan } 34455868Sbostic } 34555868Sbostic } 34656197Selan } 34755868Sbostic if (foundit) 34855868Sbostic break; 34955868Sbostic } 35055868Sbostic 35155868Sbostic if (!foundit) 35255868Sbostic return (-1); 35355876Selan 35455868Sbostic /* 35555868Sbostic * Got the capability record, but now we have to expand all tc=name 35655868Sbostic * references in it ... 35755868Sbostic */ 35855868Sbostic tc_exp: { 35955868Sbostic register char *newicap, *s; 36055868Sbostic register int newilen; 36155868Sbostic u_int ilen; 36255868Sbostic int diff, iret, tclen; 36355868Sbostic char *icap, *scan, *tc, *tcstart, *tcend; 36455868Sbostic 36555868Sbostic /* 36655868Sbostic * Loop invariants: 36755868Sbostic * There is room for one more character in record. 36855868Sbostic * R_end points just past end of record. 36955868Sbostic * Rp points just past last character in record. 37055868Sbostic * Scan points at remainder of record that needs to be 37155868Sbostic * scanned for tc=name constructs. 37255868Sbostic */ 37355868Sbostic scan = record; 37455915Selan tc_not_resolved = 0; 37555868Sbostic for (;;) { 37655868Sbostic if ((tc = cgetcap(scan, "tc", '=')) == NULL) 37755868Sbostic break; 37855868Sbostic 37955868Sbostic /* 38055868Sbostic * Find end of tc=name and stomp on the trailing `:' 38155868Sbostic * (if present) so we can use it to call ourselves. 38255868Sbostic */ 38355868Sbostic s = tc; 38455868Sbostic for (;;) 38555868Sbostic if (*s == '\0') 38655868Sbostic break; 38755868Sbostic else 38855868Sbostic if (*s++ == ':') { 38956184Selan *(s - 1) = '\0'; 39055868Sbostic break; 39155868Sbostic } 39255868Sbostic tcstart = tc - 3; 39355868Sbostic tclen = s - tcstart; 39455868Sbostic tcend = s; 39555868Sbostic 39656197Selan iret = getent(&icap, &ilen, db_p, fd, tc, depth+1, 39756197Selan NULL); 39855868Sbostic newicap = icap; /* Put into a register. */ 39955868Sbostic newilen = ilen; 40055868Sbostic if (iret != 0) { 40156208Selan /* an error */ 40256208Selan if (iret < -1) { 40356208Selan if (myfd) 40456208Selan (void)close(fd); 40556208Selan free(record); 40656208Selan return (iret); 40756208Selan } 40855915Selan if (iret == 1) 40955915Selan tc_not_resolved = 1; 41056208Selan /* couldn't resolve tc */ 41155915Selan if (iret == -1) { 41256184Selan *(s - 1) = ':'; 41355915Selan scan = s - 1; 41455915Selan tc_not_resolved = 1; 41555915Selan continue; 41655915Selan 41755915Selan } 41855868Sbostic } 41955868Sbostic /* not interested in name field of tc'ed record */ 42055868Sbostic s = newicap; 42155868Sbostic for (;;) 42255868Sbostic if (*s == '\0') 42355868Sbostic break; 42455868Sbostic else 42555868Sbostic if (*s++ == ':') 42655868Sbostic break; 42755868Sbostic newilen -= s - newicap; 42855868Sbostic newicap = s; 42955868Sbostic 43055868Sbostic /* make sure interpolated record is `:'-terminated */ 43155868Sbostic s += newilen; 43255868Sbostic if (*(s-1) != ':') { 43355868Sbostic *s = ':'; /* overwrite NUL with : */ 43455868Sbostic newilen++; 43555868Sbostic } 43655868Sbostic 43755868Sbostic /* 43855868Sbostic * Make sure there's enough room to insert the 43955868Sbostic * new record. 44055868Sbostic */ 44155868Sbostic diff = newilen - tclen; 44255868Sbostic if (diff >= r_end - rp) { 44355875Selan u_int pos, tcpos, tcposend; 44455875Selan size_t newsize; 44555868Sbostic 44655868Sbostic pos = rp - record; 44755868Sbostic newsize = r_end - record + diff + BFRAG; 44855868Sbostic tcpos = tcstart - record; 44955868Sbostic tcposend = tcend - record; 45055868Sbostic record = realloc(record, newsize); 45155868Sbostic if (record == NULL) { 45255868Sbostic errno = ENOMEM; 45355868Sbostic if (myfd) 45455868Sbostic (void)close(fd); 45555868Sbostic free(icap); 45655868Sbostic return (-2); 45755868Sbostic } 45855868Sbostic r_end = record + newsize; 45955868Sbostic rp = record + pos; 46055868Sbostic tcstart = record + tcpos; 46155868Sbostic tcend = record + tcposend; 46255868Sbostic } 46355868Sbostic 46455868Sbostic /* 46555868Sbostic * Insert tc'ed record into our record. 46655868Sbostic */ 46755868Sbostic s = tcstart + newilen; 46855868Sbostic bcopy(tcend, s, rp - tcend); 46955868Sbostic bcopy(newicap, tcstart, newilen); 47055868Sbostic rp += diff; 47155868Sbostic free(icap); 47255868Sbostic 47355868Sbostic /* 47455868Sbostic * Start scan on `:' so next cgetcap works properly 47555868Sbostic * (cgetcap always skips first field). 47655868Sbostic */ 47755868Sbostic scan = s-1; 47855868Sbostic } 47956197Selan 48055868Sbostic } 48155868Sbostic /* 48255868Sbostic * Close file (if we opened it), give back any extra memory, and 48355868Sbostic * return capability, length and success. 48455868Sbostic */ 48555868Sbostic if (myfd) 48655868Sbostic (void)close(fd); 48755868Sbostic *len = rp - record - 1; /* don't count NUL */ 48855868Sbostic if (r_end > rp) 48956184Selan if ((record = 49056184Selan realloc(record, (size_t)(rp - record))) == NULL) { 49156184Selan errno = ENOMEM; 49256184Selan return (-2); 49356184Selan } 49456184Selan 49555868Sbostic *cap = record; 49655915Selan if (tc_not_resolved) 49755915Selan return (1); 49855868Sbostic return (0); 49956197Selan } 50055868Sbostic 50155915Selan static int 50255915Selan cdbget(capdbp, bp, name) 50355915Selan DB *capdbp; 50455915Selan char **bp, *name; 50555915Selan { 50655915Selan DBT key, data; 50755915Selan char *buf; 50855915Selan int st; 50955915Selan 51055915Selan key.data = name; 51155915Selan key.size = strlen(name) + 1; 51255915Selan 51356208Selan 51456208Selan /* 51556208Selan * Get the reference. 51656208Selan */ 51755915Selan if ((st = capdbp->get(capdbp, &key, &data, 0)) < 0) 51855915Selan return(-2); 51955915Selan if (st == 1) 52055915Selan return(-1); 52155915Selan 52255915Selan if ((buf = malloc(data.size - 1)) == NULL) 52355915Selan return(-2); 52455915Selan 52556208Selan strcpy(buf, (char *)(data.data)); 52655915Selan 52755915Selan key.data = buf; 52856216Selan key.size = data.size; 52955915Selan 53056208Selan /* 53156208Selan * Get the record. 53256208Selan */ 53355915Selan if (capdbp->get(capdbp, &key, &data, 0) < 0) { 53455915Selan free(buf); 53555915Selan return(-2); 53655915Selan } 53755915Selan free(buf); 53855915Selan *bp = &((char *)(data.data))[1]; 53956208Selan if (((char *)(data.data))[0] == TCERR) 54056208Selan return 1; 54156208Selan else 54256208Selan return 0; 54355915Selan } 54455915Selan 54555915Selan 54655868Sbostic /* 54755868Sbostic * Cgetmatch will return 0 if name is one of the names of the capability 54855868Sbostic * record buf, -1 if not. 54955868Sbostic */ 55055868Sbostic int 55155868Sbostic cgetmatch(buf, name) 55255868Sbostic char *buf, *name; 55355868Sbostic { 55455868Sbostic register char *np, *bp; 55555868Sbostic 55655868Sbostic /* 55755868Sbostic * Start search at beginning of record. 55855868Sbostic */ 55955868Sbostic bp = buf; 56055868Sbostic for (;;) { 56155868Sbostic /* 56255868Sbostic * Try to match a record name. 56355868Sbostic */ 56455868Sbostic np = name; 56555868Sbostic for (;;) 56655868Sbostic if (*np == '\0') 56755868Sbostic if (*bp == '|' || *bp == ':' || *bp == '\0') 56855868Sbostic return (0); 56955868Sbostic else 57055868Sbostic break; 57155868Sbostic else 57255868Sbostic if (*bp++ != *np++) 57355868Sbostic break; 57455868Sbostic 57555868Sbostic /* 57655868Sbostic * Match failed, skip to next name in record. 57755868Sbostic */ 57855868Sbostic bp--; /* a '|' or ':' may have stopped the match */ 57955868Sbostic for (;;) 58055868Sbostic if (*bp == '\0' || *bp == ':') 58155868Sbostic return (-1); /* match failed totally */ 58255868Sbostic else 58355868Sbostic if (*bp++ == '|') 58455868Sbostic break; /* found next name */ 58555868Sbostic } 58655868Sbostic } 58755868Sbostic 58855876Selan 58955876Selan 59055876Selan 59155876Selan 59255868Sbostic int 59355868Sbostic cgetfirst(buf, db_array) 59455868Sbostic char **buf, **db_array; 59555868Sbostic { 59655868Sbostic (void)cgetclose(); 59755868Sbostic return (cgetnext(buf, db_array)); 59855868Sbostic } 59955868Sbostic 60055868Sbostic static FILE *pfp; 60155868Sbostic static int slash; 60255868Sbostic static char **dbp; 60355868Sbostic 60455868Sbostic int 60555868Sbostic cgetclose() 60655868Sbostic { 60755868Sbostic if (pfp != NULL) { 60855868Sbostic (void)fclose(pfp); 60955868Sbostic pfp = NULL; 61055868Sbostic } 61155868Sbostic dbp = NULL; 61255875Selan gottoprec = 0; 61355868Sbostic slash = 0; 61455875Selan return(0); 61555868Sbostic } 61655868Sbostic 61755868Sbostic /* 61855868Sbostic * Cgetnext() gets either the first or next entry in the logical database 61955868Sbostic * specified by db_array. It returns 0 upon completion of the database, 1 62055868Sbostic * upon returning an entry with more remaining, and -1 if an error occurs. 62155868Sbostic */ 62255868Sbostic int 62355868Sbostic cgetnext(bp, db_array) 62455868Sbostic register char **bp; 62555868Sbostic char **db_array; 62655868Sbostic { 62755868Sbostic size_t len; 62856197Selan int status, i, done; 62956197Selan char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE]; 63055876Selan u_int dummy; 63155868Sbostic 63255876Selan if (dbp == NULL) 63355868Sbostic dbp = db_array; 63455876Selan 63555875Selan if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) { 63655875Selan (void)cgetclose(); 63755868Sbostic return (-1); 63855875Selan } 63955868Sbostic for(;;) { 64055876Selan if (toprec && !gottoprec) { 64155876Selan gottoprec = 1; 64255876Selan line = toprec; 64355876Selan } else { 64455876Selan line = fgetline(pfp, &len); 64555876Selan if (line == NULL && pfp) { 64655876Selan (void)fclose(pfp); 64755876Selan if (ferror(pfp)) { 64855875Selan (void)cgetclose(); 64955868Sbostic return (-1); 65055876Selan } else { 65155876Selan dbp++; 65255876Selan if (*dbp == NULL) { 65355876Selan (void)cgetclose(); 65455876Selan return (0); 65555876Selan } else if ((pfp = fopen(*dbp, "r")) == 65655876Selan NULL) { 65755876Selan (void)cgetclose(); 65855876Selan return (-1); 65955876Selan } else 66055876Selan continue; 66155876Selan } 66255868Sbostic } 66355876Selan if (isspace(*line) || *line == ':' || *line == '#' 66455876Selan || len == 0 || slash) { 66555876Selan if (len > 0 && line[len - 1] == '\\') 66655876Selan slash = 1; 66755876Selan else 66855876Selan slash = 0; 66955876Selan continue; 67055876Selan } 67155868Sbostic if (len > 0 && line[len - 1] == '\\') 67255868Sbostic slash = 1; 67355868Sbostic else 67455868Sbostic slash = 0; 67555876Selan } 67655868Sbostic 67756197Selan 67856197Selan /* 67956197Selan * Line points to a name line. 68056197Selan */ 68156197Selan i = 0; 68256197Selan done = 0; 68356197Selan np = nbuf; 68456197Selan for (;;) { 68556197Selan for (cp = line; *cp != '\0'; cp++) { 68656197Selan if (*cp == ':') { 68756197Selan *np++ = ':'; 68856197Selan done = 1; 68956197Selan break; 69056197Selan } 69156197Selan if (*cp == '\\') 69256197Selan break; 69356197Selan *np++ = *cp; 69456197Selan } 69556197Selan if (done) { 69656197Selan *np = '\0'; 69756197Selan break; 69856197Selan } else { /* name field extends beyond the line */ 69956197Selan line = fgetline(pfp, &len); 70056197Selan if (line == NULL && pfp) { 70156197Selan (void)fclose(pfp); 70256197Selan if (ferror(pfp)) { 70356197Selan (void)cgetclose(); 70456197Selan return (-1); 70556197Selan } 70656197Selan } 70756197Selan } 70856197Selan } 70955868Sbostic rp = buf; 71056197Selan for(cp = nbuf; *cp != NULL; cp++) 71155868Sbostic if (*cp == '|' || *cp == ':') 71255868Sbostic break; 71355868Sbostic else 71455868Sbostic *rp++ = *cp; 71555868Sbostic 71655868Sbostic *rp = '\0'; 71756200Selan /* 71856200Selan * XXX 71956200Selan * Last argument of getent here should be nbuf if we want true 72056200Selan * sequential access in the case of duplicates. 72156200Selan * With NULL, getent will return the first entry found 72256200Selan * rather than the duplicate entry record. This is a 72356200Selan * matter of semantics that should be resolved. 72456200Selan */ 72556200Selan status = getent(bp, &dummy, db_array, -1, buf, 0, NULL); 72655915Selan if (status == -2 || status == -3) 72755875Selan (void)cgetclose(); 72855915Selan 72955915Selan return (status + 1); 73055868Sbostic } 73155868Sbostic /* NOTREACHED */ 73255868Sbostic } 73355868Sbostic 73455868Sbostic /* 73555868Sbostic * Cgetstr retrieves the value of the string capability cap from the 73655868Sbostic * capability record pointed to by buf. A pointer to a decoded, NUL 73755868Sbostic * terminated, malloc'd copy of the string is returned in the char * 73855868Sbostic * pointed to by str. The length of the string not including the trailing 73955868Sbostic * NUL is returned on success, -1 if the requested string capability 74055868Sbostic * couldn't be found, -2 if a system error was encountered (storage 74155868Sbostic * allocation failure). 74255868Sbostic */ 74355868Sbostic int 74455868Sbostic cgetstr(buf, cap, str) 74555868Sbostic char *buf, *cap; 74655868Sbostic char **str; 74755868Sbostic { 74855868Sbostic register u_int m_room; 74955868Sbostic register char *bp, *mp; 75055868Sbostic int len; 75155868Sbostic char *mem; 75255868Sbostic 75355868Sbostic /* 75455868Sbostic * Find string capability cap 75555868Sbostic */ 75655868Sbostic bp = cgetcap(buf, cap, '='); 75755868Sbostic if (bp == NULL) 75855868Sbostic return (-1); 75955868Sbostic 76055868Sbostic /* 76155868Sbostic * Conversion / storage allocation loop ... Allocate memory in 76255868Sbostic * chunks SFRAG in size. 76355868Sbostic */ 76455868Sbostic if ((mem = malloc(SFRAG)) == NULL) { 76555868Sbostic errno = ENOMEM; 76655868Sbostic return (-2); /* couldn't even allocate the first fragment */ 76755868Sbostic } 76855868Sbostic m_room = SFRAG; 76955868Sbostic mp = mem; 77055868Sbostic 77155868Sbostic while (*bp != ':' && *bp != '\0') { 77255868Sbostic /* 77355868Sbostic * Loop invariants: 77455868Sbostic * There is always room for one more character in mem. 77555868Sbostic * Mp always points just past last character in mem. 77655868Sbostic * Bp always points at next character in buf. 77755868Sbostic */ 77855868Sbostic if (*bp == '^') { 77955868Sbostic bp++; 78055868Sbostic if (*bp == ':' || *bp == '\0') 78155868Sbostic break; /* drop unfinished escape */ 78255868Sbostic *mp++ = *bp++ & 037; 78355868Sbostic } else if (*bp == '\\') { 78455868Sbostic bp++; 78555868Sbostic if (*bp == ':' || *bp == '\0') 78655868Sbostic break; /* drop unfinished escape */ 78755868Sbostic if ('0' <= *bp && *bp <= '7') { 78855868Sbostic register int n, i; 78955868Sbostic 79055868Sbostic n = 0; 79155868Sbostic i = 3; /* maximum of three octal digits */ 79255868Sbostic do { 79355868Sbostic n = n * 8 + (*bp++ - '0'); 79455868Sbostic } while (--i && '0' <= *bp && *bp <= '7'); 79555868Sbostic *mp++ = n; 79655868Sbostic } 79755868Sbostic else switch (*bp++) { 79855868Sbostic case 'b': case 'B': 79955868Sbostic *mp++ = '\b'; 80055868Sbostic break; 80155868Sbostic case 't': case 'T': 80255868Sbostic *mp++ = '\t'; 80355868Sbostic break; 80455868Sbostic case 'n': case 'N': 80555868Sbostic *mp++ = '\n'; 80655868Sbostic break; 80755868Sbostic case 'f': case 'F': 80855868Sbostic *mp++ = '\f'; 80955868Sbostic break; 81055868Sbostic case 'r': case 'R': 81155868Sbostic *mp++ = '\r'; 81255868Sbostic break; 81355868Sbostic case 'e': case 'E': 81455868Sbostic *mp++ = ESC; 81555868Sbostic break; 81655868Sbostic case 'c': case 'C': 81755868Sbostic *mp++ = ':'; 81855868Sbostic break; 81955868Sbostic default: 82055868Sbostic /* 82155868Sbostic * Catches '\', '^', and 82255868Sbostic * everything else. 82355868Sbostic */ 82455868Sbostic *mp++ = *(bp-1); 82555868Sbostic break; 82655868Sbostic } 82755868Sbostic } else 82855868Sbostic *mp++ = *bp++; 82955868Sbostic m_room--; 83055868Sbostic 83155868Sbostic /* 83255868Sbostic * Enforce loop invariant: if no room left in current 83355868Sbostic * buffer, try to get some more. 83455868Sbostic */ 83555868Sbostic if (m_room == 0) { 83655875Selan size_t size = mp - mem; 83755868Sbostic 83855868Sbostic if ((mem = realloc(mem, size + SFRAG)) == NULL) 83955868Sbostic return (-2); 84055868Sbostic m_room = SFRAG; 84155868Sbostic mp = mem + size; 84255868Sbostic } 84355868Sbostic } 84455868Sbostic *mp++ = '\0'; /* loop invariant let's us do this */ 84555868Sbostic m_room--; 84655868Sbostic len = mp - mem - 1; 84755868Sbostic 84855868Sbostic /* 84955868Sbostic * Give back any extra memory and return value and success. 85055868Sbostic */ 85155868Sbostic if (m_room != 0) 85256184Selan if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 85356184Selan return (-2); 85455868Sbostic *str = mem; 85555868Sbostic return (len); 85655868Sbostic } 85755868Sbostic 85855868Sbostic /* 85955868Sbostic * Cgetustr retrieves the value of the string capability cap from the 86055868Sbostic * capability record pointed to by buf. The difference between cgetustr() 86155868Sbostic * and cgetstr() is that cgetustr does not decode escapes but rather treats 86255868Sbostic * all characters literally. A pointer to a NUL terminated malloc'd 86355868Sbostic * copy of the string is returned in the char pointed to by str. The 86455868Sbostic * length of the string not including the trailing NUL is returned on success, 86555868Sbostic * -1 if the requested string capability couldn't be found, -2 if a system 86655868Sbostic * error was encountered (storage allocation failure). 86755868Sbostic */ 86855868Sbostic int 86955868Sbostic cgetustr(buf, cap, str) 87055868Sbostic char *buf, *cap, **str; 87155868Sbostic { 87255868Sbostic register u_int m_room; 87355868Sbostic register char *bp, *mp; 87455868Sbostic int len; 87555868Sbostic char *mem; 87655868Sbostic 87755868Sbostic /* 87855868Sbostic * Find string capability cap 87955868Sbostic */ 88055868Sbostic if ((bp = cgetcap(buf, cap, '=')) == NULL) 88155868Sbostic return (-1); 88255868Sbostic 88355868Sbostic /* 88455868Sbostic * Conversion / storage allocation loop ... Allocate memory in 88555868Sbostic * chunks SFRAG in size. 88655868Sbostic */ 88755868Sbostic if ((mem = malloc(SFRAG)) == NULL) { 88855868Sbostic errno = ENOMEM; 88955868Sbostic return (-2); /* couldn't even allocate the first fragment */ 89055868Sbostic } 89155868Sbostic m_room = SFRAG; 89255868Sbostic mp = mem; 89355868Sbostic 89455868Sbostic while (*bp != ':' && *bp != '\0') { 89555868Sbostic /* 89655868Sbostic * Loop invariants: 89755868Sbostic * There is always room for one more character in mem. 89855868Sbostic * Mp always points just past last character in mem. 89955868Sbostic * Bp always points at next character in buf. 90055868Sbostic */ 90155868Sbostic *mp++ = *bp++; 90255868Sbostic m_room--; 90355868Sbostic 90455868Sbostic /* 90555868Sbostic * Enforce loop invariant: if no room left in current 90655868Sbostic * buffer, try to get some more. 90755868Sbostic */ 90855868Sbostic if (m_room == 0) { 90955875Selan size_t size = mp - mem; 91055868Sbostic 91155868Sbostic if ((mem = realloc(mem, size + SFRAG)) == NULL) 91255868Sbostic return (-2); 91355868Sbostic m_room = SFRAG; 91455868Sbostic mp = mem + size; 91555868Sbostic } 91655868Sbostic } 91755868Sbostic *mp++ = '\0'; /* loop invariant let's us do this */ 91855868Sbostic m_room--; 91955868Sbostic len = mp - mem - 1; 92055868Sbostic 92155868Sbostic /* 92255868Sbostic * Give back any extra memory and return value and success. 92355868Sbostic */ 92455868Sbostic if (m_room != 0) 92556184Selan if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 92656184Selan return (-2); 92755868Sbostic *str = mem; 92855868Sbostic return (len); 92955868Sbostic } 93055868Sbostic 93155868Sbostic /* 93255868Sbostic * Cgetnum retrieves the value of the numeric capability cap from the 93355868Sbostic * capability record pointed to by buf. The numeric value is returned in 93455868Sbostic * the long pointed to by num. 0 is returned on success, -1 if the requested 93555868Sbostic * numeric capability couldn't be found. 93655868Sbostic */ 93755868Sbostic int 93855868Sbostic cgetnum(buf, cap, num) 93955868Sbostic char *buf, *cap; 94055868Sbostic long *num; 94155868Sbostic { 94255868Sbostic register long n; 94355868Sbostic register int base, digit; 94455868Sbostic register char *bp; 94555868Sbostic 94655868Sbostic /* 94755868Sbostic * Find numeric capability cap 94855868Sbostic */ 94955868Sbostic bp = cgetcap(buf, cap, '#'); 95055868Sbostic if (bp == NULL) 95155868Sbostic return (-1); 95255868Sbostic 95355868Sbostic /* 95455868Sbostic * Look at value and determine numeric base: 95555868Sbostic * 0x... or 0X... hexadecimal, 95655868Sbostic * else 0... octal, 95755868Sbostic * else decimal. 95855868Sbostic */ 95955868Sbostic if (*bp == '0') { 96055868Sbostic bp++; 96155868Sbostic if (*bp == 'x' || *bp == 'X') { 96255868Sbostic bp++; 96355868Sbostic base = 16; 96455868Sbostic } else 96555868Sbostic base = 8; 96655868Sbostic } else 96755868Sbostic base = 10; 96855868Sbostic 96955868Sbostic /* 97055868Sbostic * Conversion loop ... 97155868Sbostic */ 97255868Sbostic n = 0; 97355868Sbostic for (;;) { 97455868Sbostic if ('0' <= *bp && *bp <= '9') 97555868Sbostic digit = *bp - '0'; 97656236Sralph else if ('a' <= *bp && *bp <= 'f') 97756236Sralph digit = 10 + *bp - 'a'; 97856236Sralph else if ('A' <= *bp && *bp <= 'F') 97956236Sralph digit = 10 + *bp - 'A'; 98055868Sbostic else 98156236Sralph break; 98255868Sbostic 98355868Sbostic if (digit >= base) 98455868Sbostic break; 98555868Sbostic 98655868Sbostic n = n * base + digit; 98755868Sbostic bp++; 98855868Sbostic } 98955868Sbostic 99055868Sbostic /* 99155868Sbostic * Return value and success. 99255868Sbostic */ 99355868Sbostic *num = n; 99455868Sbostic return (0); 99555868Sbostic } 99656197Selan 99756197Selan 99856197Selan /* 99956197Selan * Compare name field of record. 100056197Selan */ 100156197Selan static int 100256197Selan nfcmp(nf, rec) 100356197Selan char *nf, *rec; 100456197Selan { 100556197Selan char *cp, tmp; 100656197Selan int ret; 100756197Selan 100856197Selan for (cp = rec; *cp != ':'; cp++) 100956197Selan ; 101056197Selan 101156197Selan tmp = *(cp + 1); 101256197Selan *(cp + 1) = '\0'; 101356197Selan ret = strcmp(nf, rec); 101456197Selan *(cp + 1) = tmp; 101556197Selan 101656197Selan return (ret); 101756197Selan } 1018