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*58522Sbostic static char sccsid[] = "@(#)getcap.c 5.14 (Berkeley) 03/06/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 35*58522Sbostic #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) { 21955868Sbostic (void)lseek(fd, 0L, L_SET); 22055868Sbostic myfd = 0; 22155868Sbostic } else { 22255915Selan sprintf(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); 22956208Selan *cap = malloc (strlen(record) + 1); 23056208Selan strcpy(*cap, record); 23156208Selan return (retval); 23255915Selan } else { 23355915Selan fd = open(*db_p, O_RDONLY, 0); 23455915Selan if (fd < 0) { 23556216Selan /* No error on unfound file. */ 23656216Selan if (errno == ENOENT) 23756216Selan continue; 23855915Selan free(record); 23955915Selan return (-2); 24055915Selan } 24155915Selan 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) { 30655875Selan u_int pos; 30755875Selan 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) { 34156197Selan if (nfield == NULL || !nfcmp(nfield, record)) { 34256197Selan foundit = 1; 34356197Selan break; /* found it! */ 34456197Selan } 34555868Sbostic } 34655868Sbostic } 34756197Selan } 34855868Sbostic if (foundit) 34955868Sbostic break; 35055868Sbostic } 35155868Sbostic 35255868Sbostic if (!foundit) 35355868Sbostic return (-1); 35455876Selan 35555868Sbostic /* 35655868Sbostic * Got the capability record, but now we have to expand all tc=name 35755868Sbostic * references in it ... 35855868Sbostic */ 35955868Sbostic tc_exp: { 36055868Sbostic register char *newicap, *s; 36155868Sbostic register int newilen; 36255868Sbostic u_int ilen; 36355868Sbostic int diff, iret, tclen; 36455868Sbostic char *icap, *scan, *tc, *tcstart, *tcend; 36555868Sbostic 36655868Sbostic /* 36755868Sbostic * Loop invariants: 36855868Sbostic * There is room for one more character in record. 36955868Sbostic * R_end points just past end of record. 37055868Sbostic * Rp points just past last character in record. 37155868Sbostic * Scan points at remainder of record that needs to be 37255868Sbostic * scanned for tc=name constructs. 37355868Sbostic */ 37455868Sbostic scan = record; 37555915Selan tc_not_resolved = 0; 37655868Sbostic for (;;) { 37755868Sbostic if ((tc = cgetcap(scan, "tc", '=')) == NULL) 37855868Sbostic break; 37955868Sbostic 38055868Sbostic /* 38155868Sbostic * Find end of tc=name and stomp on the trailing `:' 38255868Sbostic * (if present) so we can use it to call ourselves. 38355868Sbostic */ 38455868Sbostic s = tc; 38555868Sbostic for (;;) 38655868Sbostic if (*s == '\0') 38755868Sbostic break; 38855868Sbostic else 38955868Sbostic if (*s++ == ':') { 39056184Selan *(s - 1) = '\0'; 39155868Sbostic break; 39255868Sbostic } 39355868Sbostic tcstart = tc - 3; 39455868Sbostic tclen = s - tcstart; 39555868Sbostic tcend = s; 39655868Sbostic 39756197Selan iret = getent(&icap, &ilen, db_p, fd, tc, depth+1, 39856197Selan NULL); 39955868Sbostic newicap = icap; /* Put into a register. */ 40055868Sbostic newilen = ilen; 40155868Sbostic if (iret != 0) { 40256208Selan /* an error */ 40356208Selan if (iret < -1) { 40456208Selan if (myfd) 40556208Selan (void)close(fd); 40656208Selan free(record); 40756208Selan return (iret); 40856208Selan } 40955915Selan if (iret == 1) 41055915Selan tc_not_resolved = 1; 41156208Selan /* couldn't resolve tc */ 41255915Selan if (iret == -1) { 41356184Selan *(s - 1) = ':'; 41455915Selan scan = s - 1; 41555915Selan tc_not_resolved = 1; 41655915Selan continue; 41755915Selan 41855915Selan } 41955868Sbostic } 42055868Sbostic /* not interested in name field of tc'ed record */ 42155868Sbostic s = newicap; 42255868Sbostic for (;;) 42355868Sbostic if (*s == '\0') 42455868Sbostic break; 42555868Sbostic else 42655868Sbostic if (*s++ == ':') 42755868Sbostic break; 42855868Sbostic newilen -= s - newicap; 42955868Sbostic newicap = s; 43055868Sbostic 43155868Sbostic /* make sure interpolated record is `:'-terminated */ 43255868Sbostic s += newilen; 43355868Sbostic if (*(s-1) != ':') { 43455868Sbostic *s = ':'; /* overwrite NUL with : */ 43555868Sbostic newilen++; 43655868Sbostic } 43755868Sbostic 43855868Sbostic /* 43955868Sbostic * Make sure there's enough room to insert the 44055868Sbostic * new record. 44155868Sbostic */ 44255868Sbostic diff = newilen - tclen; 44355868Sbostic if (diff >= r_end - rp) { 44455875Selan u_int pos, tcpos, tcposend; 44555875Selan size_t newsize; 44655868Sbostic 44755868Sbostic pos = rp - record; 44855868Sbostic newsize = r_end - record + diff + BFRAG; 44955868Sbostic tcpos = tcstart - record; 45055868Sbostic tcposend = tcend - record; 45155868Sbostic record = realloc(record, newsize); 45255868Sbostic if (record == NULL) { 45355868Sbostic errno = ENOMEM; 45455868Sbostic if (myfd) 45555868Sbostic (void)close(fd); 45655868Sbostic free(icap); 45755868Sbostic return (-2); 45855868Sbostic } 45955868Sbostic r_end = record + newsize; 46055868Sbostic rp = record + pos; 46155868Sbostic tcstart = record + tcpos; 46255868Sbostic tcend = record + tcposend; 46355868Sbostic } 46455868Sbostic 46555868Sbostic /* 46655868Sbostic * Insert tc'ed record into our record. 46755868Sbostic */ 46855868Sbostic s = tcstart + newilen; 46955868Sbostic bcopy(tcend, s, rp - tcend); 47055868Sbostic bcopy(newicap, tcstart, newilen); 47155868Sbostic rp += diff; 47255868Sbostic free(icap); 47355868Sbostic 47455868Sbostic /* 47555868Sbostic * Start scan on `:' so next cgetcap works properly 47655868Sbostic * (cgetcap always skips first field). 47755868Sbostic */ 47855868Sbostic scan = s-1; 47955868Sbostic } 48056197Selan 48155868Sbostic } 48255868Sbostic /* 48355868Sbostic * Close file (if we opened it), give back any extra memory, and 48455868Sbostic * return capability, length and success. 48555868Sbostic */ 48655868Sbostic if (myfd) 48755868Sbostic (void)close(fd); 48855868Sbostic *len = rp - record - 1; /* don't count NUL */ 48955868Sbostic if (r_end > rp) 49056184Selan if ((record = 49156184Selan realloc(record, (size_t)(rp - record))) == NULL) { 49256184Selan errno = ENOMEM; 49356184Selan return (-2); 49456184Selan } 49556184Selan 49655868Sbostic *cap = record; 49755915Selan if (tc_not_resolved) 49855915Selan return (1); 49955868Sbostic return (0); 50056197Selan } 50155868Sbostic 50255915Selan static int 50355915Selan cdbget(capdbp, bp, name) 50455915Selan DB *capdbp; 50555915Selan char **bp, *name; 50655915Selan { 50755915Selan DBT key, data; 50855915Selan char *buf; 50955915Selan int st; 51055915Selan 51155915Selan key.data = name; 512*58522Sbostic key.size = strlen(name); 51355915Selan 514*58522Sbostic for (;;) { 515*58522Sbostic /* Get the reference. */ 516*58522Sbostic switch(capdbp->get(capdbp, &key, &data, 0)) { 517*58522Sbostic case -1: 518*58522Sbostic return (-2); 519*58522Sbostic case 1: 520*58522Sbostic return (-1); 521*58522Sbostic } 52256208Selan 523*58522Sbostic if (((char *)data.data)[0] != SHADOW) 524*58522Sbostic break; 52555915Selan 526*58522Sbostic key.data = data.data + 1; 527*58522Sbostic key.size = data.size - 1; 528*58522Sbostic } 52955915Selan 530*58522Sbostic *bp = &((char *)(data.data))[1]; 53155915Selan 53256208Selan if (((char *)(data.data))[0] == TCERR) 533*58522Sbostic return (1); 53456208Selan else 535*58522Sbostic return (0); 53655915Selan } 53755915Selan 53855915Selan 53955868Sbostic /* 54055868Sbostic * Cgetmatch will return 0 if name is one of the names of the capability 54155868Sbostic * record buf, -1 if not. 54255868Sbostic */ 54355868Sbostic int 54455868Sbostic cgetmatch(buf, name) 54555868Sbostic char *buf, *name; 54655868Sbostic { 54755868Sbostic register char *np, *bp; 54855868Sbostic 54955868Sbostic /* 55055868Sbostic * Start search at beginning of record. 55155868Sbostic */ 55255868Sbostic bp = buf; 55355868Sbostic for (;;) { 55455868Sbostic /* 55555868Sbostic * Try to match a record name. 55655868Sbostic */ 55755868Sbostic np = name; 55855868Sbostic for (;;) 55955868Sbostic if (*np == '\0') 56055868Sbostic if (*bp == '|' || *bp == ':' || *bp == '\0') 56155868Sbostic return (0); 56255868Sbostic else 56355868Sbostic break; 56455868Sbostic else 56555868Sbostic if (*bp++ != *np++) 56655868Sbostic break; 56755868Sbostic 56855868Sbostic /* 56955868Sbostic * Match failed, skip to next name in record. 57055868Sbostic */ 57155868Sbostic bp--; /* a '|' or ':' may have stopped the match */ 57255868Sbostic for (;;) 57355868Sbostic if (*bp == '\0' || *bp == ':') 57455868Sbostic return (-1); /* match failed totally */ 57555868Sbostic else 57655868Sbostic if (*bp++ == '|') 57755868Sbostic break; /* found next name */ 57855868Sbostic } 57955868Sbostic } 58055868Sbostic 58155876Selan 58255876Selan 58355876Selan 58455876Selan 58555868Sbostic int 58655868Sbostic cgetfirst(buf, db_array) 58755868Sbostic char **buf, **db_array; 58855868Sbostic { 58955868Sbostic (void)cgetclose(); 59055868Sbostic return (cgetnext(buf, db_array)); 59155868Sbostic } 59255868Sbostic 59355868Sbostic static FILE *pfp; 59455868Sbostic static int slash; 59555868Sbostic static char **dbp; 59655868Sbostic 59755868Sbostic int 59855868Sbostic cgetclose() 59955868Sbostic { 60055868Sbostic if (pfp != NULL) { 60155868Sbostic (void)fclose(pfp); 60255868Sbostic pfp = NULL; 60355868Sbostic } 60455868Sbostic dbp = NULL; 60555875Selan gottoprec = 0; 60655868Sbostic slash = 0; 60755875Selan return(0); 60855868Sbostic } 60955868Sbostic 61055868Sbostic /* 61155868Sbostic * Cgetnext() gets either the first or next entry in the logical database 61255868Sbostic * specified by db_array. It returns 0 upon completion of the database, 1 61355868Sbostic * upon returning an entry with more remaining, and -1 if an error occurs. 61455868Sbostic */ 61555868Sbostic int 61655868Sbostic cgetnext(bp, db_array) 61755868Sbostic register char **bp; 61855868Sbostic char **db_array; 61955868Sbostic { 62055868Sbostic size_t len; 62156197Selan int status, i, done; 62256197Selan char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE]; 62355876Selan u_int dummy; 62455868Sbostic 62555876Selan if (dbp == NULL) 62655868Sbostic dbp = db_array; 62755876Selan 62855875Selan if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) { 62955875Selan (void)cgetclose(); 63055868Sbostic return (-1); 63155875Selan } 63255868Sbostic for(;;) { 63355876Selan if (toprec && !gottoprec) { 63455876Selan gottoprec = 1; 63555876Selan line = toprec; 63655876Selan } else { 63755876Selan line = fgetline(pfp, &len); 63855876Selan if (line == NULL && pfp) { 63955876Selan (void)fclose(pfp); 64055876Selan if (ferror(pfp)) { 64155875Selan (void)cgetclose(); 64255868Sbostic return (-1); 64355876Selan } else { 644*58522Sbostic if (*++dbp == NULL) { 64555876Selan (void)cgetclose(); 64655876Selan return (0); 647*58522Sbostic } else if ((pfp = 648*58522Sbostic fopen(*dbp, "r")) == NULL) { 64955876Selan (void)cgetclose(); 65055876Selan return (-1); 65155876Selan } else 65255876Selan continue; 65355876Selan } 65458452Selan } else 65558452Selan line[len - 1] = '\0'; 656*58522Sbostic if (len == 1) { 657*58522Sbostic slash = 0; 658*58522Sbostic continue; 659*58522Sbostic } 660*58522Sbostic if (isspace(*line) || 661*58522Sbostic *line == ':' || *line == '#' || slash) { 662*58522Sbostic if (line[len - 2] == '\\') 66355876Selan slash = 1; 66455876Selan else 66555876Selan slash = 0; 66655876Selan continue; 66755876Selan } 668*58522Sbostic if (line[len - 2] == '\\') 66955868Sbostic slash = 1; 67055868Sbostic else 67155868Sbostic slash = 0; 67255876Selan } 67355868Sbostic 67456197Selan 67556197Selan /* 67656197Selan * Line points to a name line. 67756197Selan */ 67856197Selan i = 0; 67956197Selan done = 0; 68056197Selan np = nbuf; 68156197Selan for (;;) { 68256197Selan for (cp = line; *cp != '\0'; cp++) { 68356197Selan if (*cp == ':') { 68456197Selan *np++ = ':'; 68556197Selan done = 1; 68656197Selan break; 68756197Selan } 68856197Selan if (*cp == '\\') 68956197Selan break; 69056197Selan *np++ = *cp; 69156197Selan } 69256197Selan if (done) { 69356197Selan *np = '\0'; 69456197Selan break; 69556197Selan } else { /* name field extends beyond the line */ 69656197Selan line = fgetline(pfp, &len); 69756197Selan if (line == NULL && pfp) { 69856197Selan (void)fclose(pfp); 69956197Selan if (ferror(pfp)) { 70056197Selan (void)cgetclose(); 70156197Selan return (-1); 70256197Selan } 70358457Sbostic } else 70458452Selan line[len - 1] = '\0'; 70556197Selan } 70656197Selan } 70755868Sbostic rp = buf; 70856197Selan for(cp = nbuf; *cp != NULL; cp++) 70955868Sbostic if (*cp == '|' || *cp == ':') 71055868Sbostic break; 71155868Sbostic else 71255868Sbostic *rp++ = *cp; 71355868Sbostic 71455868Sbostic *rp = '\0'; 71556200Selan /* 71656200Selan * XXX 71756200Selan * Last argument of getent here should be nbuf if we want true 71856200Selan * sequential access in the case of duplicates. 71956200Selan * With NULL, getent will return the first entry found 72056200Selan * rather than the duplicate entry record. This is a 72156200Selan * matter of semantics that should be resolved. 72256200Selan */ 72356200Selan status = getent(bp, &dummy, db_array, -1, buf, 0, NULL); 72455915Selan if (status == -2 || status == -3) 72555875Selan (void)cgetclose(); 72655915Selan 72755915Selan return (status + 1); 72855868Sbostic } 72955868Sbostic /* NOTREACHED */ 73055868Sbostic } 73155868Sbostic 73255868Sbostic /* 73355868Sbostic * Cgetstr retrieves the value of the string capability cap from the 73455868Sbostic * capability record pointed to by buf. A pointer to a decoded, NUL 73555868Sbostic * terminated, malloc'd copy of the string is returned in the char * 73655868Sbostic * pointed to by str. The length of the string not including the trailing 73755868Sbostic * NUL is returned on success, -1 if the requested string capability 73855868Sbostic * couldn't be found, -2 if a system error was encountered (storage 73955868Sbostic * allocation failure). 74055868Sbostic */ 74155868Sbostic int 74255868Sbostic cgetstr(buf, cap, str) 74355868Sbostic char *buf, *cap; 74455868Sbostic char **str; 74555868Sbostic { 74655868Sbostic register u_int m_room; 74755868Sbostic register char *bp, *mp; 74855868Sbostic int len; 74955868Sbostic char *mem; 75055868Sbostic 75155868Sbostic /* 75255868Sbostic * Find string capability cap 75355868Sbostic */ 75455868Sbostic bp = cgetcap(buf, cap, '='); 75555868Sbostic if (bp == NULL) 75655868Sbostic return (-1); 75755868Sbostic 75855868Sbostic /* 75955868Sbostic * Conversion / storage allocation loop ... Allocate memory in 76055868Sbostic * chunks SFRAG in size. 76155868Sbostic */ 76255868Sbostic if ((mem = malloc(SFRAG)) == NULL) { 76355868Sbostic errno = ENOMEM; 76455868Sbostic return (-2); /* couldn't even allocate the first fragment */ 76555868Sbostic } 76655868Sbostic m_room = SFRAG; 76755868Sbostic mp = mem; 76855868Sbostic 76955868Sbostic while (*bp != ':' && *bp != '\0') { 77055868Sbostic /* 77155868Sbostic * Loop invariants: 77255868Sbostic * There is always room for one more character in mem. 77355868Sbostic * Mp always points just past last character in mem. 77455868Sbostic * Bp always points at next character in buf. 77555868Sbostic */ 77655868Sbostic if (*bp == '^') { 77755868Sbostic bp++; 77855868Sbostic if (*bp == ':' || *bp == '\0') 77955868Sbostic break; /* drop unfinished escape */ 78055868Sbostic *mp++ = *bp++ & 037; 78155868Sbostic } else if (*bp == '\\') { 78255868Sbostic bp++; 78355868Sbostic if (*bp == ':' || *bp == '\0') 78455868Sbostic break; /* drop unfinished escape */ 78555868Sbostic if ('0' <= *bp && *bp <= '7') { 78655868Sbostic register int n, i; 78755868Sbostic 78855868Sbostic n = 0; 78955868Sbostic i = 3; /* maximum of three octal digits */ 79055868Sbostic do { 79155868Sbostic n = n * 8 + (*bp++ - '0'); 79255868Sbostic } while (--i && '0' <= *bp && *bp <= '7'); 79355868Sbostic *mp++ = n; 79455868Sbostic } 79555868Sbostic else switch (*bp++) { 79655868Sbostic case 'b': case 'B': 79755868Sbostic *mp++ = '\b'; 79855868Sbostic break; 79955868Sbostic case 't': case 'T': 80055868Sbostic *mp++ = '\t'; 80155868Sbostic break; 80255868Sbostic case 'n': case 'N': 80355868Sbostic *mp++ = '\n'; 80455868Sbostic break; 80555868Sbostic case 'f': case 'F': 80655868Sbostic *mp++ = '\f'; 80755868Sbostic break; 80855868Sbostic case 'r': case 'R': 80955868Sbostic *mp++ = '\r'; 81055868Sbostic break; 81155868Sbostic case 'e': case 'E': 81255868Sbostic *mp++ = ESC; 81355868Sbostic break; 81455868Sbostic case 'c': case 'C': 81555868Sbostic *mp++ = ':'; 81655868Sbostic break; 81755868Sbostic default: 81855868Sbostic /* 81955868Sbostic * Catches '\', '^', and 82055868Sbostic * everything else. 82155868Sbostic */ 82255868Sbostic *mp++ = *(bp-1); 82355868Sbostic break; 82455868Sbostic } 82555868Sbostic } else 82655868Sbostic *mp++ = *bp++; 82755868Sbostic m_room--; 82855868Sbostic 82955868Sbostic /* 83055868Sbostic * Enforce loop invariant: if no room left in current 83155868Sbostic * buffer, try to get some more. 83255868Sbostic */ 83355868Sbostic if (m_room == 0) { 83455875Selan size_t size = mp - mem; 83555868Sbostic 83655868Sbostic if ((mem = realloc(mem, size + SFRAG)) == NULL) 83755868Sbostic return (-2); 83855868Sbostic m_room = SFRAG; 83955868Sbostic mp = mem + size; 84055868Sbostic } 84155868Sbostic } 84255868Sbostic *mp++ = '\0'; /* loop invariant let's us do this */ 84355868Sbostic m_room--; 84455868Sbostic len = mp - mem - 1; 84555868Sbostic 84655868Sbostic /* 84755868Sbostic * Give back any extra memory and return value and success. 84855868Sbostic */ 84955868Sbostic if (m_room != 0) 85056184Selan if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 85156184Selan return (-2); 85255868Sbostic *str = mem; 85355868Sbostic return (len); 85455868Sbostic } 85555868Sbostic 85655868Sbostic /* 85755868Sbostic * Cgetustr retrieves the value of the string capability cap from the 85855868Sbostic * capability record pointed to by buf. The difference between cgetustr() 85955868Sbostic * and cgetstr() is that cgetustr does not decode escapes but rather treats 86055868Sbostic * all characters literally. A pointer to a NUL terminated malloc'd 86155868Sbostic * copy of the string is returned in the char pointed to by str. The 86255868Sbostic * length of the string not including the trailing NUL is returned on success, 86355868Sbostic * -1 if the requested string capability couldn't be found, -2 if a system 86455868Sbostic * error was encountered (storage allocation failure). 86555868Sbostic */ 86655868Sbostic int 86755868Sbostic cgetustr(buf, cap, str) 86855868Sbostic char *buf, *cap, **str; 86955868Sbostic { 87055868Sbostic register u_int m_room; 87155868Sbostic register char *bp, *mp; 87255868Sbostic int len; 87355868Sbostic char *mem; 87455868Sbostic 87555868Sbostic /* 87655868Sbostic * Find string capability cap 87755868Sbostic */ 87855868Sbostic if ((bp = cgetcap(buf, cap, '=')) == NULL) 87955868Sbostic return (-1); 88055868Sbostic 88155868Sbostic /* 88255868Sbostic * Conversion / storage allocation loop ... Allocate memory in 88355868Sbostic * chunks SFRAG in size. 88455868Sbostic */ 88555868Sbostic if ((mem = malloc(SFRAG)) == NULL) { 88655868Sbostic errno = ENOMEM; 88755868Sbostic return (-2); /* couldn't even allocate the first fragment */ 88855868Sbostic } 88955868Sbostic m_room = SFRAG; 89055868Sbostic mp = mem; 89155868Sbostic 89255868Sbostic while (*bp != ':' && *bp != '\0') { 89355868Sbostic /* 89455868Sbostic * Loop invariants: 89555868Sbostic * There is always room for one more character in mem. 89655868Sbostic * Mp always points just past last character in mem. 89755868Sbostic * Bp always points at next character in buf. 89855868Sbostic */ 89955868Sbostic *mp++ = *bp++; 90055868Sbostic m_room--; 90155868Sbostic 90255868Sbostic /* 90355868Sbostic * Enforce loop invariant: if no room left in current 90455868Sbostic * buffer, try to get some more. 90555868Sbostic */ 90655868Sbostic if (m_room == 0) { 90755875Selan size_t size = mp - mem; 90855868Sbostic 90955868Sbostic if ((mem = realloc(mem, size + SFRAG)) == NULL) 91055868Sbostic return (-2); 91155868Sbostic m_room = SFRAG; 91255868Sbostic mp = mem + size; 91355868Sbostic } 91455868Sbostic } 91555868Sbostic *mp++ = '\0'; /* loop invariant let's us do this */ 91655868Sbostic m_room--; 91755868Sbostic len = mp - mem - 1; 91855868Sbostic 91955868Sbostic /* 92055868Sbostic * Give back any extra memory and return value and success. 92155868Sbostic */ 92255868Sbostic if (m_room != 0) 92356184Selan if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 92456184Selan return (-2); 92555868Sbostic *str = mem; 92655868Sbostic return (len); 92755868Sbostic } 92855868Sbostic 92955868Sbostic /* 93055868Sbostic * Cgetnum retrieves the value of the numeric capability cap from the 93155868Sbostic * capability record pointed to by buf. The numeric value is returned in 93255868Sbostic * the long pointed to by num. 0 is returned on success, -1 if the requested 93355868Sbostic * numeric capability couldn't be found. 93455868Sbostic */ 93555868Sbostic int 93655868Sbostic cgetnum(buf, cap, num) 93755868Sbostic char *buf, *cap; 93855868Sbostic long *num; 93955868Sbostic { 94055868Sbostic register long n; 94155868Sbostic register int base, digit; 94255868Sbostic register char *bp; 94355868Sbostic 94455868Sbostic /* 94555868Sbostic * Find numeric capability cap 94655868Sbostic */ 94755868Sbostic bp = cgetcap(buf, cap, '#'); 94855868Sbostic if (bp == NULL) 94955868Sbostic return (-1); 95055868Sbostic 95155868Sbostic /* 95255868Sbostic * Look at value and determine numeric base: 95355868Sbostic * 0x... or 0X... hexadecimal, 95455868Sbostic * else 0... octal, 95555868Sbostic * else decimal. 95655868Sbostic */ 95755868Sbostic if (*bp == '0') { 95855868Sbostic bp++; 95955868Sbostic if (*bp == 'x' || *bp == 'X') { 96055868Sbostic bp++; 96155868Sbostic base = 16; 96255868Sbostic } else 96355868Sbostic base = 8; 96455868Sbostic } else 96555868Sbostic base = 10; 96655868Sbostic 96755868Sbostic /* 96855868Sbostic * Conversion loop ... 96955868Sbostic */ 97055868Sbostic n = 0; 97155868Sbostic for (;;) { 97255868Sbostic if ('0' <= *bp && *bp <= '9') 97355868Sbostic digit = *bp - '0'; 97456236Sralph else if ('a' <= *bp && *bp <= 'f') 97556236Sralph digit = 10 + *bp - 'a'; 97656236Sralph else if ('A' <= *bp && *bp <= 'F') 97756236Sralph digit = 10 + *bp - 'A'; 97855868Sbostic else 97956236Sralph break; 98055868Sbostic 98155868Sbostic if (digit >= base) 98255868Sbostic break; 98355868Sbostic 98455868Sbostic n = n * base + digit; 98555868Sbostic bp++; 98655868Sbostic } 98755868Sbostic 98855868Sbostic /* 98955868Sbostic * Return value and success. 99055868Sbostic */ 99155868Sbostic *num = n; 99255868Sbostic return (0); 99355868Sbostic } 99456197Selan 99556197Selan 99656197Selan /* 99756197Selan * Compare name field of record. 99856197Selan */ 99956197Selan static int 100056197Selan nfcmp(nf, rec) 100156197Selan char *nf, *rec; 100256197Selan { 100356197Selan char *cp, tmp; 100456197Selan int ret; 100556197Selan 100656197Selan for (cp = rec; *cp != ':'; cp++) 100756197Selan ; 100856197Selan 100956197Selan tmp = *(cp + 1); 101056197Selan *(cp + 1) = '\0'; 101156197Selan ret = strcmp(nf, rec); 101256197Selan *(cp + 1) = tmp; 101356197Selan 101456197Selan return (ret); 101556197Selan } 1016