diff options
author | Thorsten Lockert <tholo@cvs.openbsd.org> | 1996-07-22 03:16:32 +0000 |
---|---|---|
committer | Thorsten Lockert <tholo@cvs.openbsd.org> | 1996-07-22 03:16:32 +0000 |
commit | 057aa2750e8eeba205eb8b79a0c9c9e97485d6b5 (patch) | |
tree | b1f43cd797a27b7713113f75a04018bec731e04c | |
parent | be45d9da65aa8de64b568f8c18d999b1c4e2fe04 (diff) |
Add support for compiling a terminfo source file into terminfo.db (using
the same format as termcap.db)
-rw-r--r-- | usr.bin/Makefile | 4 | ||||
-rw-r--r-- | usr.bin/info_mkdb/Makefile | 7 | ||||
-rw-r--r-- | usr.bin/info_mkdb/getinfo.c | 625 | ||||
-rw-r--r-- | usr.bin/info_mkdb/info_mkdb.1 | 98 | ||||
-rw-r--r-- | usr.bin/info_mkdb/info_mkdb.c | 279 |
5 files changed, 1011 insertions, 2 deletions
diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 0db8a2dd909..dd7c919d534 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.12 1996/07/16 07:36:16 pefo Exp $ +# $OpenBSD: Makefile,v 1.13 1996/07/22 03:16:29 tholo Exp $ # $NetBSD: Makefile,v 1.62 1996/03/10 05:45:43 thorpej Exp $ # from: @(#)Makefile 5.8.1.1 (Berkeley) 5/8/91 @@ -6,7 +6,7 @@ SUBDIR= apply apropos arch asa at awk banner basename bdes biff cal calendar \ cap_mkdb checknr chflags chpass cksum cmp col colcrt colrm column \ comm compress cpp crontab ctags cut dirname du \ env error expand false file find finger fmt fold fpr from \ - fsplit fstat ftp gencat getconf getopt head hexdump id indent \ + fsplit fstat ftp gencat getconf getopt head hexdump id indent info_mkdb \ ipcrm ipcs join jot kdump ktrace lam last lastcomm leave less lex \ locate lock logger login logname look lorder m4 machine mail make man \ mesg mkdep mkfifo mkstr modstat msgs netstat newsyslog nfsstat nice \ diff --git a/usr.bin/info_mkdb/Makefile b/usr.bin/info_mkdb/Makefile new file mode 100644 index 00000000000..0e4c6b8a1d8 --- /dev/null +++ b/usr.bin/info_mkdb/Makefile @@ -0,0 +1,7 @@ +# $OpenBSD: Makefile,v 1.1 1996/07/22 03:16:29 tholo Exp $ + +PROG= info_mkdb +SRCS= getinfo.c info_mkdb.c +CFLAGS+=-Wall -Wstrict-prototypes -Wmissing-prototypes + +.include <bsd.prog.mk> diff --git a/usr.bin/info_mkdb/getinfo.c b/usr.bin/info_mkdb/getinfo.c new file mode 100644 index 00000000000..8e29baa792b --- /dev/null +++ b/usr.bin/info_mkdb/getinfo.c @@ -0,0 +1,625 @@ +/* $OpenBSD: getinfo.c,v 1.1 1996/07/22 03:16:30 tholo Exp $ */ + +/*- + * Copyright (c) 1996 SigmaSoft, Th. Lockert <tholo@sigmasoft.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by SigmaSoft, Th. Lockert. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$OpenBSD: getinfo.c,v 1.1 1996/07/22 03:16:30 tholo Exp $"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <db.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define BFRAG 1024 +#define BSIZE 1024 +#define ESC ('[' & 037) /* ASCII ESC */ +#define MAX_RECURSION 32 /* maximum getent recursion */ +#define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */ + +#define RECOK (char)0 +#define TCERR (char)1 +#define SHADOW (char)2 + +static int getent __P((char **, u_int *, char **, int, char *, int)); +static char *igetcap __P((char *, char *, int)); +static int igetmatch __P((char *, char *)); +static int igetclose __P((void)); + +int igetnext __P((char **, char **)); + +/* + * Cgetcap searches the capability record buf for the capability cap with + * type `type'. A pointer to the value of cap is returned on success, NULL + * if the requested capability couldn't be found. + * + * Specifying a type of ',' means that nothing should follow cap (,cap,). + * In this case a pointer to the terminating ',' or NUL will be returned if + * cap is found. + * + * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator) + * return NULL. + */ +static char * +igetcap(buf, cap, type) + char *buf, *cap; + int type; +{ + register char *bp, *cp; + + bp = buf; + for (;;) { + /* + * Skip past the current capability field - it's either the + * name field if this is the first time through the loop, or + * the remainder of a field whose name failed to match cap. + */ + for (;;) + if (*bp == '\0') + return (NULL); + else + if (*bp++ == ',') + break; + + /* + * Try to match (cap, type) in buf. + */ + for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++) + continue; + if (*cp != '\0') + continue; + if (*bp == '@') + return (NULL); + if (type == ',') { + if (*bp != '\0' && *bp != ',') + continue; + return(bp); + } + if (*bp != type) + continue; + bp++; + return (*bp == '@' ? NULL : bp); + } + /* NOTREACHED */ +} + +/* + * Getent implements the functions of igetent. If fd is non-negative, + * *db_array has already been opened and fd is the open file descriptor. We + * do this to save time and avoid using up file descriptors for use= + * recursions. + * + * Getent returns the same success/failure codes as igetent. On success, a + * pointer to a malloc'ed capability record with all use= capabilities fully + * expanded and its length (not including trailing ASCII NUL) are left in + * *cap and *len. + * + * Basic algorithm: + * + Allocate memory incrementally as needed in chunks of size BFRAG + * for capability buffer. + * + Recurse for each use=name and interpolate result. Stop when all + * names interpolated, a name can't be found, or depth exceeds + * MAX_RECURSION. + */ +static int +getent(cap, len, db_array, fd, name, depth) + char **cap, **db_array, *name; + u_int *len; + int fd, depth; +{ + register char *r_end, *rp, **db_p; + int myfd, eof, foundit; + char *record; + int tc_not_resolved; + + /* + * Return with ``loop detected'' error if we've recursed more than + * MAX_RECURSION times. + */ + if (depth > MAX_RECURSION) + return (-3); + + /* + * Allocate first chunk of memory. + */ + if ((record = malloc(BFRAG)) == NULL) { + errno = ENOMEM; + return (-2); + } + r_end = record + BFRAG; + foundit = 0; + rp = NULL; + myfd = -1; + /* + * Loop through database array until finding the record. + */ + + for (db_p = db_array; *db_p != NULL; db_p++) { + eof = 0; + + /* + * Open database if not already open. + */ + + if (fd >= 0) { + (void)lseek(fd, (off_t)0, L_SET); + myfd = 0; + } else { + fd = open(*db_p, O_RDONLY, 0); + if (fd < 0) { + /* No error on unfound file. */ + continue; + } + myfd = 1; + } + /* + * Find the requested capability record ... + */ + { + char buf[BUFSIZ]; + register char *b_end, *bp; + register int c; + + /* + * Loop invariants: + * There is always room for one more character in record. + * R_end always points just past end of record. + * Rp always points just past last character in record. + * B_end always points just past last character in buf. + * Bp always points at next character in buf. + */ + b_end = buf; + bp = buf; + for (;;) { + + /* + * Read in a line implementing (\, newline) + * line continuation. + */ + rp = record; + for (;;) { + if (bp >= b_end) { + int n; + + n = read(fd, buf, sizeof(buf)); + if (n <= 0) { + if (myfd) + (void)close(fd); + if (n < 0) { + free(record); + return (-2); + } else { + fd = -1; + eof = 1; + break; + } + } + b_end = buf+n; + bp = buf; + } + + c = *bp++; + if (c == '\n') { + if (bp >= b_end) { + int n; + + n = read(fd, buf, sizeof(buf)); + if (n <= 0) { + if (myfd) + (void)close(fd); + if (n < 0) { + free(record); + return (-2); + } else { + fd = -1; + eof = 1; + break; + } + } + b_end = buf+n; + bp = buf; + } + if (rp > record && isspace(*bp)) + continue; + else + break; + } + if (rp <= record || *(rp - 1) != ',' || !isspace(c)) + *rp++ = c; + + /* + * Enforce loop invariant: if no room + * left in record buffer, try to get + * some more. + */ + if (rp >= r_end) { + u_int pos; + size_t newsize; + + pos = rp - record; + newsize = r_end - record + BFRAG; + record = realloc(record, newsize); + if (record == NULL) { + errno = ENOMEM; + if (myfd) + (void)close(fd); + return (-2); + } + r_end = record + newsize; + rp = record + pos; + } + } + /* loop invariant let's us do this */ + *rp++ = '\0'; + + /* + * If encountered eof check next file. + */ + if (eof) + break; + + /* + * Toss blank lines and comments. + */ + if (*record == '\0' || *record == '#') + continue; + + /* + * See if this is the record we want ... + */ + if (igetmatch(record, name) == 0) { + foundit = 1; + break; /* found it! */ + } + } + } + if (foundit) + break; + } + + if (!foundit) + return (-1); + + /* + * Got the capability record, but now we have to expand all use=name + * references in it ... + */ + { + register char *newicap, *s; + register int newilen; + u_int ilen; + int diff, iret, tclen; + char *icap, *scan, *tc, *tcstart, *tcend; + + /* + * Loop invariants: + * There is room for one more character in record. + * R_end points just past end of record. + * Rp points just past last character in record. + * Scan points at remainder of record that needs to be + * scanned for use=name constructs. + */ + scan = record; + tc_not_resolved = 0; + for (;;) { + if ((tc = igetcap(scan, "use", '=')) == NULL) + break; + + /* + * Find end of use=name and stomp on the trailing `,' + * (if present) so we can use it to call ourselves. + */ + s = tc; + for (;;) + if (*s == '\0') + break; + else + if (*s++ == ',') { + *(s - 1) = '\0'; + break; + } + tcstart = tc - 3; + tclen = s - tcstart; + tcend = s; + + iret = getent(&icap, &ilen, db_p, fd, tc, depth+1); + newicap = icap; /* Put into a register. */ + newilen = ilen; + if (iret != 0) { + /* an error */ + if (iret < -1) { + if (myfd) + (void)close(fd); + free(record); + return (iret); + } + if (iret == 1) + tc_not_resolved = 1; + /* couldn't resolve tc */ + if (iret == -1) { + *(s - 1) = ','; + scan = s - 1; + tc_not_resolved = 1; + continue; + + } + } + /* not interested in name field of tc'ed record */ + s = newicap; + for (;;) + if (*s == '\0') + break; + else + if (*s++ == ',') + break; + newilen -= s - newicap; + newicap = s; + + /* make sure interpolated record is `,'-terminated */ + s += newilen; + if (*(s-1) != ',') { + *s = ','; /* overwrite NUL with , */ + newilen++; + } + + /* + * Make sure there's enough room to insert the + * new record. + */ + diff = newilen - tclen; + if (diff >= r_end - rp) { + u_int pos, tcpos, tcposend; + size_t newsize; + + pos = rp - record; + newsize = r_end - record + diff + BFRAG; + tcpos = tcstart - record; + tcposend = tcend - record; + record = realloc(record, newsize); + if (record == NULL) { + errno = ENOMEM; + if (myfd) + (void)close(fd); + free(icap); + return (-2); + } + r_end = record + newsize; + rp = record + pos; + tcstart = record + tcpos; + tcend = record + tcposend; + } + + /* + * Insert tc'ed record into our record. + */ + s = tcstart + newilen; + bcopy(tcend, s, (size_t)(rp - tcend)); + bcopy(newicap, tcstart, (size_t)newilen); + rp += diff; + free(icap); + + /* + * Start scan on `,' so next igetcap works properly + * (igetcap always skips first field). + */ + scan = s-1; + } + + } + /* + * Close file (if we opened it), give back any extra memory, and + * return capability, length and success. + */ + if (myfd) + (void)close(fd); + *len = rp - record - 1; /* don't count NUL */ + if (r_end > rp) + if ((record = + realloc(record, (size_t)(rp - record))) == NULL) { + errno = ENOMEM; + return (-2); + } + + *cap = record; + if (tc_not_resolved) + return (1); + return (0); +} + +/* + * Cgetmatch will return 0 if name is one of the names of the capability + * record buf, -1 if not. + */ +static int +igetmatch(buf, name) + char *buf, *name; +{ + register char *np, *bp; + + /* + * Start search at beginning of record. + */ + bp = buf; + for (;;) { + /* + * Try to match a record name. + */ + np = name; + for (;;) + if (*np == '\0') + if (*bp == '|' || *bp == ',' || *bp == '\0') + return (0); + else + break; + else + if (*bp++ != *np++) + break; + + /* + * Match failed, skip to next name in record. + */ + bp--; /* a '|' or ',' may have stopped the match */ + for (;;) + if (*bp == '\0' || *bp == ',') + return (-1); /* match failed totally */ + else + if (*bp++ == '|') + break; /* found next name */ + } +} + +static FILE *pfp; +static int slash; +static char **dbp; + +static int +igetclose() +{ + if (pfp != NULL) { + (void)fclose(pfp); + pfp = NULL; + } + dbp = NULL; + slash = 0; + return(0); +} + +/* + * Cgetnext() gets either the first or next entry in the logical database + * specified by db_array. It returns 0 upon completion of the database, 1 + * upon returning an entry with more remaining, and -1 if an error occurs. + */ +int +igetnext(bp, db_array) + register char **bp; + char **db_array; +{ + size_t len; + int status, done; + char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE]; + u_int dummy; + + if (dbp == NULL) + dbp = db_array; + + if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) { + (void)igetclose(); + return (-1); + } + for(;;) { + line = fgetln(pfp, &len); + if (line == NULL && pfp) { + (void)fclose(pfp); + if (ferror(pfp)) { + (void)igetclose(); + return (-1); + } else { + if (*++dbp == NULL) { + (void)igetclose(); + return (0); + } else if ((pfp = + fopen(*dbp, "r")) == NULL) { + (void)igetclose(); + return (-1); + } else + continue; + } + } else + line[len - 1] = '\0'; + if (len == 1) { + slash = 0; + continue; + } + if (isspace(*line) || + *line == ',' || *line == '#' || slash) { + if (line[len - 2] == '\\') + slash = 1; + else + slash = 0; + continue; + } + if (line[len - 2] == '\\') + slash = 1; + else + slash = 0; + + /* + * Line points to a name line. + */ + done = 0; + np = nbuf; + for (;;) { + for (cp = line; *cp != '\0'; cp++) { + if (*cp == ',') { + *np++ = ','; + done = 1; + break; + } + *np++ = *cp; + } + if (done) { + *np = '\0'; + break; + } else { /* name field extends beyond the line */ + line = fgetln(pfp, &len); + if (line == NULL && pfp) { + (void)fclose(pfp); + if (ferror(pfp)) { + (void)igetclose(); + return (-1); + } + } else + line[len - 1] = '\0'; + } + } + rp = buf; + for(cp = nbuf; *cp != NULL; cp++) + if (*cp == '|' || *cp == ',') + break; + else + *rp++ = *cp; + + *rp = '\0'; + status = getent(bp, &dummy, db_array, -1, buf, 0); + if (status == -2 || status == -3) + (void)igetclose(); + + return (status + 1); + } + /* NOTREACHED */ +} diff --git a/usr.bin/info_mkdb/info_mkdb.1 b/usr.bin/info_mkdb/info_mkdb.1 new file mode 100644 index 00000000000..d3c95370384 --- /dev/null +++ b/usr.bin/info_mkdb/info_mkdb.1 @@ -0,0 +1,98 @@ +.\" $OpenBSD: info_mkdb.1,v 1.1 1996/07/22 03:16:30 tholo Exp $ +.\" +.\" Copyright (c) 1996 SigmaSoft, Th. Lockert +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by SigmaSoft, Th. Lockert. +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd "July 21, 1996" +.Dt INFO_MKDB 1 +.Os +.Sh NAME +.Nm info_mkdb +.Nd create capability database +.Pp +.Sh SYNOPSIS +.Nm info_mkdb +.Op Fl v +.Op Fl f Ar outfile +.Ar file1 +.Op Ar file2 ... +.Pp +.Sh DESCRIPTION +.Nm info_mkdb +builds a hashed database out of the +.Xr terminfo 5 +logical database constructed by the concatenation of the specified +files . +.Pp +The database is named by the basename of the first file argument and +the string +.Dq .db . +The +.Xr getcap 3 +routines can access the database in this form much more quickly +than they can the original text file(s). +.Pp +The ``tc'' capabilities of the records are expanded before the +record is stored into the database. +.Pp +The options as as follows: +.Bl -tag -width XXXXXX -indent +.It Fl f Ar outfile +Specify a different database basename. +.It Fl v +Print out the number of capability records in the database. +.El +.Pp +.Sh FORMAT +Each record is stored in the database using two different types of keys. +.Pp +The first type is a key which consists of the first capability of +the record (not including the trailing colon (``:'')) with a data +field consisting of a special byte followed by the rest of the record. +The special byte is either a 0 or 1, where a 0 means that the record +is okay, and a 1 means that there was a ``tc'' capability in the record +that couldn't be expanded. +.Pp +The second type is a key which consists of one of the names from the +first capability of the record with a data field consisting a special +byte followed by the the first capability of the record. +The special byte is a 2. +.Pp +In normal operation names are looked up in the database, resulting +in a key/data pair of the second type. +The data field of this key/data pair is used to look up a key/data +pair of the first type which has the real data associated with the +name. +.Sh RETURN VALUE +The +.Nm info_mkdb +utility exits 0 on success and >0 if an error occurs. +.Sh SEE ALSO +.Xr dbopen 3 , +.Xr getcap 3 , +.Xr terminfo 5 diff --git a/usr.bin/info_mkdb/info_mkdb.c b/usr.bin/info_mkdb/info_mkdb.c new file mode 100644 index 00000000000..cad011e300e --- /dev/null +++ b/usr.bin/info_mkdb/info_mkdb.c @@ -0,0 +1,279 @@ +/* $OpenBSD: info_mkdb.c,v 1.1 1996/07/22 03:16:31 tholo Exp $ */ + +/*- + * Copyright (c) 1996 SigmaSoft, Th. Lockert <tholo@sigmasoft.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by SigmaSoft, Th. Lockert. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$OpenBSD: info_mkdb.c,v 1.1 1996/07/22 03:16:31 tholo Exp $"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <db.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <unistd.h> + +void db_build __P((char **)); +void dounlink __P((void)); +void usage __P((void)); +int igetnext __P((char **, char **)); +int main __P((int, char *[])); + +DB *infodbp; +int verbose; +char *infoname, buf[8 * 1024]; + +HASHINFO openinfo = { + 4096, /* bsize */ + 16, /* ffactor */ + 256, /* nelem */ + 2048 * 1024, /* cachesize */ + NULL, /* hash() */ + 0 /* lorder */ +}; + +/* + * info_mkdb creates a capability hash database for quick retrieval of capability + * records. The database contains 2 types of entries: records and references + * marked by the first byte in the data. A record entry contains the actual + * capability record whereas a reference contains the name (key) under which + * the correct record is stored. + */ +int +main(argc, argv) + int argc; + char *argv[]; +{ + int c; + + infoname = NULL; + while ((c = getopt(argc, argv, "f:v")) != EOF) { + switch(c) { + case 'f': + infoname = optarg; + break; + case 'v': + verbose = 1; + break; + case '?': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (*argv == NULL) + usage(); + + /* + * The database file is the first argument if no name is specified. + * Make arrangements to unlink it if exit badly. + */ + (void)snprintf(buf, sizeof(buf), "%s.db", infoname ? infoname : *argv); + if ((infoname = strdup(buf)) == NULL) { + err(1, "strdup"); + /* NOTREACHED */ + } + if ((infodbp = dbopen(infoname, O_CREAT | O_TRUNC | O_RDWR, + DEFFILEMODE, DB_HASH, &openinfo)) == NULL) { + err(1, "%s", buf); + /* NOTREACHED */ + } + + if (atexit(dounlink)) { + err(1, "atexit"); + /* NOTREACHED */ + } + + db_build(argv); + + if (infodbp->close(infodbp) < 0) { + err(1, "%s", infoname); + /* NOTREACHED */ + } + infoname = NULL; + exit(0); + /* NOTREACHED */ +} + +void +dounlink() +{ + if (infoname != NULL) + (void)unlink(infoname); +} + +/* + * Any changes to these definitions should be made also in the getcap(3) + * library routines. + */ +#define RECOK (char)0 +#define TCERR (char)1 +#define SHADOW (char)2 + +/* + * db_build() builds the name and capability databases according to the + * details above. + */ +void +db_build(ifiles) + char **ifiles; +{ + DBT key, data; + recno_t reccnt; + size_t len, bplen; + int st; + char *bp, *p, *t; + + data.data = NULL; + key.data = NULL; + for (reccnt = 0, bplen = 0; (st = igetnext(&bp, ifiles)) > 0;) { + + /* + * Allocate enough memory to store record, terminating + * NULL and one extra byte. + */ + len = strlen(bp); + if (bplen <= len + 2) { + bplen += MAX(256, len + 2); + if ((data.data = realloc(data.data, bplen)) == NULL) { + err(1, "realloc"); + /* NOTREACHED */ + } + } + + /* Find the end of the name field. */ + if ((p = strchr(bp, ',')) == NULL) { + warnx("no name field: %.*s", (int)MIN(len, 20), bp); + continue; + } + + if (isspace(*bp)) { + warnx("bad name field: %.*s", (int)MIN(len, 20), bp); + continue; + } + + /* First byte of stored record indicates status. */ + switch(st) { + case 1: + ((char *)(data.data))[0] = RECOK; + break; + case 2: + ((char *)(data.data))[0] = TCERR; + warnx("Record not use expanded: %.*s", (int)(p - bp), bp); + break; + } + + /* Create the stored record. */ + (void) memmove(&((u_char *)(data.data))[1], bp, len + 1); + data.size = len + 2; + for (t = memchr((char *)data.data + 1, ',', data.size - 1) ; t ; t = memchr(t, ',', data.size - (t - (char *)data.data))) + *t++ = ':'; + + if (memchr((char *)data.data + 1, '\0', data.size - 2)) { + warnx("NUL in entry: %.*s", (int)MIN(len, 20), bp); + continue; + } + + /* Store the record under the name field. */ + key.data = bp; + key.size = p - bp; + + switch(infodbp->put(infodbp, &key, &data, R_NOOVERWRITE)) { + case -1: + err(1, "put"); + /* NOTREACHED */ + case 1: + warnx("ignored duplicate: %.*s", + (int)key.size, (char *)key.data); + continue; + } + ++reccnt; + + /* If only one name, ignore the rest. */ + if ((p = strchr(bp, '|')) == NULL) + continue; + + /* The rest of the names reference the entire name. */ + ((char *)(data.data))[0] = SHADOW; + (void) memmove(&((u_char *)(data.data))[1], key.data, key.size); + data.size = key.size + 1; + + /* Store references for other names. */ + for (p = t = bp;; ++p) { + if (p > t && (*p == ',' || *p == '|')) { + key.size = p - t; + key.data = t; + + switch(infodbp->put(infodbp, + &key, &data, R_NOOVERWRITE)) { + case -1: + err(1, "put"); + /* NOTREACHED */ + case 1: + warnx("ignored duplicate: %.*s", + (int)key.size, (char *)key.data); + } + t = p + 1; + } + if (*p == ',') + break; + } + } + + switch(st) { + case -1: + err(1, "file argument"); + /* NOTREACHED */ + case -2: + errx(1, "potential reference loop detected"); + /* NOTREACHED */ + } + + if (verbose) + (void)printf("info_mkdb: %d capability records\n", reccnt); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: info_mkdb [-v] [-f outfile] file1 [file2 ...]\n"); + exit(1); +} |