155868Sbostic /*-
261111Sbostic * Copyright (c) 1992, 1993
361111Sbostic * The Regents of the University of California. 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*66450Sbostic static char sccsid[] = "@(#)getcap.c 8.3 (Berkeley) 03/25/94";
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
cgetset(ent)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 *
cgetcap(buf,cap,type)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
cgetent(buf,db_array,name)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
getent(cap,len,db_array,fd,name,depth,nfield)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;
171*66450Sbostic int myfd, eof, foundit, retval, clen;
172*66450Sbostic char *record, *cbuf;
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) {
21958735Sbostic (void)lseek(fd, (off_t)0, L_SET);
22055868Sbostic myfd = 0;
22155868Sbostic } else {
22258735Sbostic (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);
227*66450Sbostic if (retval < 0) {
228*66450Sbostic /* no record available */
229*66450Sbostic (void)capdbp->close(capdbp);
230*66450Sbostic return (retval);
231*66450Sbostic }
232*66450Sbostic /* save the data; close frees it */
233*66450Sbostic clen = strlen(record);
234*66450Sbostic cbuf = malloc(clen + 1);
235*66450Sbostic memcpy(cbuf, record, clen + 1);
236*66450Sbostic if (capdbp->close(capdbp) < 0) {
237*66450Sbostic free(cbuf);
23855915Selan return (-2);
239*66450Sbostic }
240*66450Sbostic *len = clen;
241*66450Sbostic *cap = cbuf;
24256208Selan return (retval);
24355915Selan } else {
24455915Selan fd = open(*db_p, O_RDONLY, 0);
24555915Selan if (fd < 0) {
24656216Selan /* No error on unfound file. */
24756216Selan if (errno == ENOENT)
24856216Selan continue;
24955915Selan free(record);
25055915Selan return (-2);
25155915Selan }
25255915Selan myfd = 1;
25355868Sbostic }
25455868Sbostic }
25555868Sbostic /*
25655868Sbostic * Find the requested capability record ...
25755868Sbostic */
25855868Sbostic {
25955868Sbostic char buf[BUFSIZ];
26055868Sbostic register char *b_end, *bp;
26155868Sbostic register int c;
26255868Sbostic
26355868Sbostic /*
26455868Sbostic * Loop invariants:
26555868Sbostic * There is always room for one more character in record.
26655868Sbostic * R_end always points just past end of record.
26755868Sbostic * Rp always points just past last character in record.
26855868Sbostic * B_end always points just past last character in buf.
26955868Sbostic * Bp always points at next character in buf.
27055868Sbostic */
27155868Sbostic b_end = buf;
27255868Sbostic bp = buf;
27355868Sbostic for (;;) {
27455868Sbostic
27555868Sbostic /*
27655868Sbostic * Read in a line implementing (\, newline)
27755868Sbostic * line continuation.
27855868Sbostic */
27955868Sbostic rp = record;
28055868Sbostic for (;;) {
28155868Sbostic if (bp >= b_end) {
28255868Sbostic int n;
28355868Sbostic
28455868Sbostic n = read(fd, buf, sizeof(buf));
28555868Sbostic if (n <= 0) {
28655868Sbostic if (myfd)
28755868Sbostic (void)close(fd);
28855868Sbostic if (n < 0) {
28955868Sbostic free(record);
29055868Sbostic return (-2);
29155868Sbostic } else {
29255868Sbostic fd = -1;
29355868Sbostic eof = 1;
29455868Sbostic break;
29555868Sbostic }
29655868Sbostic }
29755868Sbostic b_end = buf+n;
29855868Sbostic bp = buf;
29955868Sbostic }
30055868Sbostic
30155868Sbostic c = *bp++;
30255868Sbostic if (c == '\n') {
30355868Sbostic if (rp > record && *(rp-1) == '\\') {
30455868Sbostic rp--;
30555868Sbostic continue;
30655868Sbostic } else
30755868Sbostic break;
30855868Sbostic }
30955868Sbostic *rp++ = c;
31055868Sbostic
31155868Sbostic /*
31255868Sbostic * Enforce loop invariant: if no room
31355868Sbostic * left in record buffer, try to get
31455868Sbostic * some more.
31555868Sbostic */
31655868Sbostic if (rp >= r_end) {
31755875Selan u_int pos;
31855875Selan size_t newsize;
31955868Sbostic
32055868Sbostic pos = rp - record;
32155868Sbostic newsize = r_end - record + BFRAG;
32255868Sbostic record = realloc(record, newsize);
32355868Sbostic if (record == NULL) {
32455868Sbostic errno = ENOMEM;
32555868Sbostic if (myfd)
32655868Sbostic (void)close(fd);
32755868Sbostic return (-2);
32855868Sbostic }
32955868Sbostic r_end = record + newsize;
33055868Sbostic rp = record + pos;
33155868Sbostic }
33255868Sbostic }
33355868Sbostic /* loop invariant let's us do this */
33455868Sbostic *rp++ = '\0';
33555868Sbostic
33655868Sbostic /*
33755868Sbostic * If encountered eof check next file.
33855868Sbostic */
33955868Sbostic if (eof)
34055868Sbostic break;
34155868Sbostic
34255868Sbostic /*
34355868Sbostic * Toss blank lines and comments.
34455868Sbostic */
34555868Sbostic if (*record == '\0' || *record == '#')
34655868Sbostic continue;
34755868Sbostic
34855868Sbostic /*
34955868Sbostic * See if this is the record we want ...
35055868Sbostic */
35155868Sbostic if (cgetmatch(record, name) == 0) {
35256197Selan if (nfield == NULL || !nfcmp(nfield, record)) {
35356197Selan foundit = 1;
35456197Selan break; /* found it! */
35556197Selan }
35655868Sbostic }
35755868Sbostic }
35856197Selan }
35955868Sbostic if (foundit)
36055868Sbostic break;
36155868Sbostic }
36255868Sbostic
36355868Sbostic if (!foundit)
36455868Sbostic return (-1);
36555876Selan
36655868Sbostic /*
36755868Sbostic * Got the capability record, but now we have to expand all tc=name
36855868Sbostic * references in it ...
36955868Sbostic */
37055868Sbostic tc_exp: {
37155868Sbostic register char *newicap, *s;
37255868Sbostic register int newilen;
37355868Sbostic u_int ilen;
37455868Sbostic int diff, iret, tclen;
37555868Sbostic char *icap, *scan, *tc, *tcstart, *tcend;
37655868Sbostic
37755868Sbostic /*
37855868Sbostic * Loop invariants:
37955868Sbostic * There is room for one more character in record.
38055868Sbostic * R_end points just past end of record.
38155868Sbostic * Rp points just past last character in record.
38255868Sbostic * Scan points at remainder of record that needs to be
38355868Sbostic * scanned for tc=name constructs.
38455868Sbostic */
38555868Sbostic scan = record;
38655915Selan tc_not_resolved = 0;
38755868Sbostic for (;;) {
38855868Sbostic if ((tc = cgetcap(scan, "tc", '=')) == NULL)
38955868Sbostic break;
39055868Sbostic
39155868Sbostic /*
39255868Sbostic * Find end of tc=name and stomp on the trailing `:'
39355868Sbostic * (if present) so we can use it to call ourselves.
39455868Sbostic */
39555868Sbostic s = tc;
39655868Sbostic for (;;)
39755868Sbostic if (*s == '\0')
39855868Sbostic break;
39955868Sbostic else
40055868Sbostic if (*s++ == ':') {
40156184Selan *(s - 1) = '\0';
40255868Sbostic break;
40355868Sbostic }
40455868Sbostic tcstart = tc - 3;
40555868Sbostic tclen = s - tcstart;
40655868Sbostic tcend = s;
40755868Sbostic
40856197Selan iret = getent(&icap, &ilen, db_p, fd, tc, depth+1,
40956197Selan NULL);
41055868Sbostic newicap = icap; /* Put into a register. */
41155868Sbostic newilen = ilen;
41255868Sbostic if (iret != 0) {
41356208Selan /* an error */
41456208Selan if (iret < -1) {
41556208Selan if (myfd)
41656208Selan (void)close(fd);
41756208Selan free(record);
41856208Selan return (iret);
41956208Selan }
42055915Selan if (iret == 1)
42155915Selan tc_not_resolved = 1;
42256208Selan /* couldn't resolve tc */
42355915Selan if (iret == -1) {
42456184Selan *(s - 1) = ':';
42555915Selan scan = s - 1;
42655915Selan tc_not_resolved = 1;
42755915Selan continue;
42855915Selan
42955915Selan }
43055868Sbostic }
43155868Sbostic /* not interested in name field of tc'ed record */
43255868Sbostic s = newicap;
43355868Sbostic for (;;)
43455868Sbostic if (*s == '\0')
43555868Sbostic break;
43655868Sbostic else
43755868Sbostic if (*s++ == ':')
43855868Sbostic break;
43955868Sbostic newilen -= s - newicap;
44055868Sbostic newicap = s;
44155868Sbostic
44255868Sbostic /* make sure interpolated record is `:'-terminated */
44355868Sbostic s += newilen;
44455868Sbostic if (*(s-1) != ':') {
44555868Sbostic *s = ':'; /* overwrite NUL with : */
44655868Sbostic newilen++;
44755868Sbostic }
44855868Sbostic
44955868Sbostic /*
45055868Sbostic * Make sure there's enough room to insert the
45155868Sbostic * new record.
45255868Sbostic */
45355868Sbostic diff = newilen - tclen;
45455868Sbostic if (diff >= r_end - rp) {
45555875Selan u_int pos, tcpos, tcposend;
45655875Selan size_t newsize;
45755868Sbostic
45855868Sbostic pos = rp - record;
45955868Sbostic newsize = r_end - record + diff + BFRAG;
46055868Sbostic tcpos = tcstart - record;
46155868Sbostic tcposend = tcend - record;
46255868Sbostic record = realloc(record, newsize);
46355868Sbostic if (record == NULL) {
46455868Sbostic errno = ENOMEM;
46555868Sbostic if (myfd)
46655868Sbostic (void)close(fd);
46755868Sbostic free(icap);
46855868Sbostic return (-2);
46955868Sbostic }
47055868Sbostic r_end = record + newsize;
47155868Sbostic rp = record + pos;
47255868Sbostic tcstart = record + tcpos;
47355868Sbostic tcend = record + tcposend;
47455868Sbostic }
47555868Sbostic
47655868Sbostic /*
47755868Sbostic * Insert tc'ed record into our record.
47855868Sbostic */
47955868Sbostic s = tcstart + newilen;
48055868Sbostic bcopy(tcend, s, rp - tcend);
48155868Sbostic bcopy(newicap, tcstart, newilen);
48255868Sbostic rp += diff;
48355868Sbostic free(icap);
48455868Sbostic
48555868Sbostic /*
48655868Sbostic * Start scan on `:' so next cgetcap works properly
48755868Sbostic * (cgetcap always skips first field).
48855868Sbostic */
48955868Sbostic scan = s-1;
49055868Sbostic }
49156197Selan
49255868Sbostic }
49355868Sbostic /*
49455868Sbostic * Close file (if we opened it), give back any extra memory, and
49555868Sbostic * return capability, length and success.
49655868Sbostic */
49755868Sbostic if (myfd)
49855868Sbostic (void)close(fd);
49955868Sbostic *len = rp - record - 1; /* don't count NUL */
50055868Sbostic if (r_end > rp)
50156184Selan if ((record =
50256184Selan realloc(record, (size_t)(rp - record))) == NULL) {
50356184Selan errno = ENOMEM;
50456184Selan return (-2);
50556184Selan }
50656184Selan
50755868Sbostic *cap = record;
50855915Selan if (tc_not_resolved)
50955915Selan return (1);
51055868Sbostic return (0);
51156197Selan }
51255868Sbostic
51355915Selan static int
cdbget(capdbp,bp,name)51455915Selan cdbget(capdbp, bp, name)
51555915Selan DB *capdbp;
51655915Selan char **bp, *name;
51755915Selan {
51855915Selan DBT key, data;
51955915Selan char *buf;
52055915Selan int st;
52155915Selan
52255915Selan key.data = name;
52358522Sbostic key.size = strlen(name);
52455915Selan
52558522Sbostic for (;;) {
52658522Sbostic /* Get the reference. */
52758522Sbostic switch(capdbp->get(capdbp, &key, &data, 0)) {
52858522Sbostic case -1:
52958522Sbostic return (-2);
53058522Sbostic case 1:
53158522Sbostic return (-1);
53258522Sbostic }
53356208Selan
53458735Sbostic /* If not an index to another record, leave. */
53558522Sbostic if (((char *)data.data)[0] != SHADOW)
53658522Sbostic break;
53755915Selan
53858735Sbostic key.data = (char *)data.data + 1;
53958522Sbostic key.size = data.size - 1;
54058522Sbostic }
54155915Selan
54258735Sbostic *bp = (char *)data.data + 1;
54358735Sbostic return (((char *)(data.data))[0] == TCERR ? 1 : 0);
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
cgetmatch(buf,name)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
cgetfirst(buf,db_array)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
cgetclose()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
cgetnext(bp,db_array)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 {
64465333Sbostic line = fgetln(pfp, &len);
64555876Selan if (line == NULL && pfp) {
64655876Selan (void)fclose(pfp);
64755876Selan if (ferror(pfp)) {
64855875Selan (void)cgetclose();
64955868Sbostic return (-1);
65055876Selan } else {
65158522Sbostic if (*++dbp == NULL) {
65255876Selan (void)cgetclose();
65355876Selan return (0);
65458522Sbostic } else if ((pfp =
65558522Sbostic fopen(*dbp, "r")) == NULL) {
65655876Selan (void)cgetclose();
65755876Selan return (-1);
65855876Selan } else
65955876Selan continue;
66055876Selan }
66158452Selan } else
66258452Selan line[len - 1] = '\0';
66358522Sbostic if (len == 1) {
66458522Sbostic slash = 0;
66558522Sbostic continue;
66658522Sbostic }
66758522Sbostic if (isspace(*line) ||
66858522Sbostic *line == ':' || *line == '#' || slash) {
66958522Sbostic if (line[len - 2] == '\\')
67055876Selan slash = 1;
67155876Selan else
67255876Selan slash = 0;
67355876Selan continue;
67455876Selan }
67558522Sbostic if (line[len - 2] == '\\')
67655868Sbostic slash = 1;
67755868Sbostic else
67855868Sbostic slash = 0;
67955876Selan }
68055868Sbostic
68156197Selan
68256197Selan /*
68356197Selan * Line points to a name line.
68456197Selan */
68556197Selan i = 0;
68656197Selan done = 0;
68756197Selan np = nbuf;
68856197Selan for (;;) {
68956197Selan for (cp = line; *cp != '\0'; cp++) {
69056197Selan if (*cp == ':') {
69156197Selan *np++ = ':';
69256197Selan done = 1;
69356197Selan break;
69456197Selan }
69556197Selan if (*cp == '\\')
69656197Selan break;
69756197Selan *np++ = *cp;
69856197Selan }
69956197Selan if (done) {
70056197Selan *np = '\0';
70156197Selan break;
70256197Selan } else { /* name field extends beyond the line */
70365333Sbostic line = fgetln(pfp, &len);
70456197Selan if (line == NULL && pfp) {
70556197Selan (void)fclose(pfp);
70656197Selan if (ferror(pfp)) {
70756197Selan (void)cgetclose();
70856197Selan return (-1);
70956197Selan }
71058457Sbostic } else
71158452Selan line[len - 1] = '\0';
71256197Selan }
71356197Selan }
71455868Sbostic rp = buf;
71556197Selan for(cp = nbuf; *cp != NULL; cp++)
71655868Sbostic if (*cp == '|' || *cp == ':')
71755868Sbostic break;
71855868Sbostic else
71955868Sbostic *rp++ = *cp;
72055868Sbostic
72155868Sbostic *rp = '\0';
72256200Selan /*
72356200Selan * XXX
72456200Selan * Last argument of getent here should be nbuf if we want true
72556200Selan * sequential access in the case of duplicates.
72656200Selan * With NULL, getent will return the first entry found
72756200Selan * rather than the duplicate entry record. This is a
72856200Selan * matter of semantics that should be resolved.
72956200Selan */
73056200Selan status = getent(bp, &dummy, db_array, -1, buf, 0, NULL);
73155915Selan if (status == -2 || status == -3)
73255875Selan (void)cgetclose();
73355915Selan
73455915Selan return (status + 1);
73555868Sbostic }
73655868Sbostic /* NOTREACHED */
73755868Sbostic }
73855868Sbostic
73955868Sbostic /*
74055868Sbostic * Cgetstr retrieves the value of the string capability cap from the
74155868Sbostic * capability record pointed to by buf. A pointer to a decoded, NUL
74255868Sbostic * terminated, malloc'd copy of the string is returned in the char *
74355868Sbostic * pointed to by str. The length of the string not including the trailing
74455868Sbostic * NUL is returned on success, -1 if the requested string capability
74555868Sbostic * couldn't be found, -2 if a system error was encountered (storage
74655868Sbostic * allocation failure).
74755868Sbostic */
74855868Sbostic int
cgetstr(buf,cap,str)74955868Sbostic cgetstr(buf, cap, str)
75055868Sbostic char *buf, *cap;
75155868Sbostic char **str;
75255868Sbostic {
75355868Sbostic register u_int m_room;
75455868Sbostic register char *bp, *mp;
75555868Sbostic int len;
75655868Sbostic char *mem;
75755868Sbostic
75855868Sbostic /*
75955868Sbostic * Find string capability cap
76055868Sbostic */
76155868Sbostic bp = cgetcap(buf, cap, '=');
76255868Sbostic if (bp == NULL)
76355868Sbostic return (-1);
76455868Sbostic
76555868Sbostic /*
76655868Sbostic * Conversion / storage allocation loop ... Allocate memory in
76755868Sbostic * chunks SFRAG in size.
76855868Sbostic */
76955868Sbostic if ((mem = malloc(SFRAG)) == NULL) {
77055868Sbostic errno = ENOMEM;
77155868Sbostic return (-2); /* couldn't even allocate the first fragment */
77255868Sbostic }
77355868Sbostic m_room = SFRAG;
77455868Sbostic mp = mem;
77555868Sbostic
77655868Sbostic while (*bp != ':' && *bp != '\0') {
77755868Sbostic /*
77855868Sbostic * Loop invariants:
77955868Sbostic * There is always room for one more character in mem.
78055868Sbostic * Mp always points just past last character in mem.
78155868Sbostic * Bp always points at next character in buf.
78255868Sbostic */
78355868Sbostic if (*bp == '^') {
78455868Sbostic bp++;
78555868Sbostic if (*bp == ':' || *bp == '\0')
78655868Sbostic break; /* drop unfinished escape */
78755868Sbostic *mp++ = *bp++ & 037;
78855868Sbostic } else if (*bp == '\\') {
78955868Sbostic bp++;
79055868Sbostic if (*bp == ':' || *bp == '\0')
79155868Sbostic break; /* drop unfinished escape */
79255868Sbostic if ('0' <= *bp && *bp <= '7') {
79355868Sbostic register int n, i;
79455868Sbostic
79555868Sbostic n = 0;
79655868Sbostic i = 3; /* maximum of three octal digits */
79755868Sbostic do {
79855868Sbostic n = n * 8 + (*bp++ - '0');
79955868Sbostic } while (--i && '0' <= *bp && *bp <= '7');
80055868Sbostic *mp++ = n;
80155868Sbostic }
80255868Sbostic else switch (*bp++) {
80355868Sbostic case 'b': case 'B':
80455868Sbostic *mp++ = '\b';
80555868Sbostic break;
80655868Sbostic case 't': case 'T':
80755868Sbostic *mp++ = '\t';
80855868Sbostic break;
80955868Sbostic case 'n': case 'N':
81055868Sbostic *mp++ = '\n';
81155868Sbostic break;
81255868Sbostic case 'f': case 'F':
81355868Sbostic *mp++ = '\f';
81455868Sbostic break;
81555868Sbostic case 'r': case 'R':
81655868Sbostic *mp++ = '\r';
81755868Sbostic break;
81855868Sbostic case 'e': case 'E':
81955868Sbostic *mp++ = ESC;
82055868Sbostic break;
82155868Sbostic case 'c': case 'C':
82255868Sbostic *mp++ = ':';
82355868Sbostic break;
82455868Sbostic default:
82555868Sbostic /*
82655868Sbostic * Catches '\', '^', and
82755868Sbostic * everything else.
82855868Sbostic */
82955868Sbostic *mp++ = *(bp-1);
83055868Sbostic break;
83155868Sbostic }
83255868Sbostic } else
83355868Sbostic *mp++ = *bp++;
83455868Sbostic m_room--;
83555868Sbostic
83655868Sbostic /*
83755868Sbostic * Enforce loop invariant: if no room left in current
83855868Sbostic * buffer, try to get some more.
83955868Sbostic */
84055868Sbostic if (m_room == 0) {
84155875Selan size_t size = mp - mem;
84255868Sbostic
84355868Sbostic if ((mem = realloc(mem, size + SFRAG)) == NULL)
84455868Sbostic return (-2);
84555868Sbostic m_room = SFRAG;
84655868Sbostic mp = mem + size;
84755868Sbostic }
84855868Sbostic }
84955868Sbostic *mp++ = '\0'; /* loop invariant let's us do this */
85055868Sbostic m_room--;
85155868Sbostic len = mp - mem - 1;
85255868Sbostic
85355868Sbostic /*
85455868Sbostic * Give back any extra memory and return value and success.
85555868Sbostic */
85655868Sbostic if (m_room != 0)
85756184Selan if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL)
85856184Selan return (-2);
85955868Sbostic *str = mem;
86055868Sbostic return (len);
86155868Sbostic }
86255868Sbostic
86355868Sbostic /*
86455868Sbostic * Cgetustr retrieves the value of the string capability cap from the
86555868Sbostic * capability record pointed to by buf. The difference between cgetustr()
86655868Sbostic * and cgetstr() is that cgetustr does not decode escapes but rather treats
86755868Sbostic * all characters literally. A pointer to a NUL terminated malloc'd
86855868Sbostic * copy of the string is returned in the char pointed to by str. The
86955868Sbostic * length of the string not including the trailing NUL is returned on success,
87055868Sbostic * -1 if the requested string capability couldn't be found, -2 if a system
87155868Sbostic * error was encountered (storage allocation failure).
87255868Sbostic */
87355868Sbostic int
cgetustr(buf,cap,str)87455868Sbostic cgetustr(buf, cap, str)
87555868Sbostic char *buf, *cap, **str;
87655868Sbostic {
87755868Sbostic register u_int m_room;
87855868Sbostic register char *bp, *mp;
87955868Sbostic int len;
88055868Sbostic char *mem;
88155868Sbostic
88255868Sbostic /*
88355868Sbostic * Find string capability cap
88455868Sbostic */
88555868Sbostic if ((bp = cgetcap(buf, cap, '=')) == NULL)
88655868Sbostic return (-1);
88755868Sbostic
88855868Sbostic /*
88955868Sbostic * Conversion / storage allocation loop ... Allocate memory in
89055868Sbostic * chunks SFRAG in size.
89155868Sbostic */
89255868Sbostic if ((mem = malloc(SFRAG)) == NULL) {
89355868Sbostic errno = ENOMEM;
89455868Sbostic return (-2); /* couldn't even allocate the first fragment */
89555868Sbostic }
89655868Sbostic m_room = SFRAG;
89755868Sbostic mp = mem;
89855868Sbostic
89955868Sbostic while (*bp != ':' && *bp != '\0') {
90055868Sbostic /*
90155868Sbostic * Loop invariants:
90255868Sbostic * There is always room for one more character in mem.
90355868Sbostic * Mp always points just past last character in mem.
90455868Sbostic * Bp always points at next character in buf.
90555868Sbostic */
90655868Sbostic *mp++ = *bp++;
90755868Sbostic m_room--;
90855868Sbostic
90955868Sbostic /*
91055868Sbostic * Enforce loop invariant: if no room left in current
91155868Sbostic * buffer, try to get some more.
91255868Sbostic */
91355868Sbostic if (m_room == 0) {
91455875Selan size_t size = mp - mem;
91555868Sbostic
91655868Sbostic if ((mem = realloc(mem, size + SFRAG)) == NULL)
91755868Sbostic return (-2);
91855868Sbostic m_room = SFRAG;
91955868Sbostic mp = mem + size;
92055868Sbostic }
92155868Sbostic }
92255868Sbostic *mp++ = '\0'; /* loop invariant let's us do this */
92355868Sbostic m_room--;
92455868Sbostic len = mp - mem - 1;
92555868Sbostic
92655868Sbostic /*
92755868Sbostic * Give back any extra memory and return value and success.
92855868Sbostic */
92955868Sbostic if (m_room != 0)
93056184Selan if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL)
93156184Selan return (-2);
93255868Sbostic *str = mem;
93355868Sbostic return (len);
93455868Sbostic }
93555868Sbostic
93655868Sbostic /*
93755868Sbostic * Cgetnum retrieves the value of the numeric capability cap from the
93855868Sbostic * capability record pointed to by buf. The numeric value is returned in
93955868Sbostic * the long pointed to by num. 0 is returned on success, -1 if the requested
94055868Sbostic * numeric capability couldn't be found.
94155868Sbostic */
94255868Sbostic int
cgetnum(buf,cap,num)94355868Sbostic cgetnum(buf, cap, num)
94455868Sbostic char *buf, *cap;
94555868Sbostic long *num;
94655868Sbostic {
94755868Sbostic register long n;
94855868Sbostic register int base, digit;
94955868Sbostic register char *bp;
95055868Sbostic
95155868Sbostic /*
95255868Sbostic * Find numeric capability cap
95355868Sbostic */
95455868Sbostic bp = cgetcap(buf, cap, '#');
95555868Sbostic if (bp == NULL)
95655868Sbostic return (-1);
95755868Sbostic
95855868Sbostic /*
95955868Sbostic * Look at value and determine numeric base:
96055868Sbostic * 0x... or 0X... hexadecimal,
96155868Sbostic * else 0... octal,
96255868Sbostic * else decimal.
96355868Sbostic */
96455868Sbostic if (*bp == '0') {
96555868Sbostic bp++;
96655868Sbostic if (*bp == 'x' || *bp == 'X') {
96755868Sbostic bp++;
96855868Sbostic base = 16;
96955868Sbostic } else
97055868Sbostic base = 8;
97155868Sbostic } else
97255868Sbostic base = 10;
97355868Sbostic
97455868Sbostic /*
97555868Sbostic * Conversion loop ...
97655868Sbostic */
97755868Sbostic n = 0;
97855868Sbostic for (;;) {
97955868Sbostic if ('0' <= *bp && *bp <= '9')
98055868Sbostic digit = *bp - '0';
98156236Sralph else if ('a' <= *bp && *bp <= 'f')
98256236Sralph digit = 10 + *bp - 'a';
98356236Sralph else if ('A' <= *bp && *bp <= 'F')
98456236Sralph digit = 10 + *bp - 'A';
98555868Sbostic else
98656236Sralph break;
98755868Sbostic
98855868Sbostic if (digit >= base)
98955868Sbostic break;
99055868Sbostic
99155868Sbostic n = n * base + digit;
99255868Sbostic bp++;
99355868Sbostic }
99455868Sbostic
99555868Sbostic /*
99655868Sbostic * Return value and success.
99755868Sbostic */
99855868Sbostic *num = n;
99955868Sbostic return (0);
100055868Sbostic }
100156197Selan
100256197Selan
100356197Selan /*
100456197Selan * Compare name field of record.
100556197Selan */
100656197Selan static int
nfcmp(nf,rec)100756197Selan nfcmp(nf, rec)
100856197Selan char *nf, *rec;
100956197Selan {
101056197Selan char *cp, tmp;
101156197Selan int ret;
101256197Selan
101356197Selan for (cp = rec; *cp != ':'; cp++)
101456197Selan ;
101556197Selan
101656197Selan tmp = *(cp + 1);
101756197Selan *(cp + 1) = '\0';
101856197Selan ret = strcmp(nf, rec);
101956197Selan *(cp + 1) = tmp;
102056197Selan
102156197Selan return (ret);
102256197Selan }
1023