summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThorsten Lockert <tholo@cvs.openbsd.org>1996-07-22 03:16:32 +0000
committerThorsten Lockert <tholo@cvs.openbsd.org>1996-07-22 03:16:32 +0000
commit057aa2750e8eeba205eb8b79a0c9c9e97485d6b5 (patch)
treeb1f43cd797a27b7713113f75a04018bec731e04c
parentbe45d9da65aa8de64b568f8c18d999b1c4e2fe04 (diff)
Add support for compiling a terminfo source file into terminfo.db (using
the same format as termcap.db)
-rw-r--r--usr.bin/Makefile4
-rw-r--r--usr.bin/info_mkdb/Makefile7
-rw-r--r--usr.bin/info_mkdb/getinfo.c625
-rw-r--r--usr.bin/info_mkdb/info_mkdb.198
-rw-r--r--usr.bin/info_mkdb/info_mkdb.c279
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);
+}