aboutsummaryrefslogtreecommitdiff
path: root/lib/libc/db/h_db.c
diff options
context:
space:
mode:
authorEnji Cooper <ngie@FreeBSD.org>2014-10-01 04:07:17 +0000
committerEnji Cooper <ngie@FreeBSD.org>2014-10-01 04:07:17 +0000
commit478290db20b28ab28ce7e08cf33f1b44d82e4cb3 (patch)
treeb31430c773d683d336caf956649e9ed3f459369c /lib/libc/db/h_db.c
downloadsrc-478290db20b28ab28ce7e08cf33f1b44d82e4cb3.tar.gz
src-478290db20b28ab28ce7e08cf33f1b44d82e4cb3.zip
Notes
Diffstat (limited to 'lib/libc/db/h_db.c')
-rw-r--r--lib/libc/db/h_db.c731
1 files changed, 731 insertions, 0 deletions
diff --git a/lib/libc/db/h_db.c b/lib/libc/db/h_db.c
new file mode 100644
index 000000000000..dfb13859d3f2
--- /dev/null
+++ b/lib/libc/db/h_db.c
@@ -0,0 +1,731 @@
+/* $NetBSD: h_db.c,v 1.1 2011/01/07 15:05:58 pgoyette Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. 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. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1992, 1993, 1994\
+ The Regents of the University of California. All rights reserved.");
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)dbtest.c 8.17 (Berkeley) 9/1/94";
+#else
+__RCSID("$NetBSD: h_db.c,v 1.1 2011/01/07 15:05:58 pgoyette Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <err.h>
+#include <db.h>
+
+enum S { COMMAND, COMPARE, GET, PUT, REMOVE, SEQ, SEQFLAG, KEY, DATA };
+
+static void compare(DBT *, DBT *);
+static DBTYPE dbtype(const char *);
+static void dump(DB *, int);
+static void get(DB *, DBT *);
+static void getdata(DB *, DBT *, DBT *);
+static void put(DB *, DBT *, DBT *);
+static void rem(DB *, DBT *);
+static const char *sflags(int);
+static void synk(DB *);
+static void *rfile(char *, size_t *);
+static void seq(DB *, DBT *);
+static u_int setflags(char *);
+static void *setinfo(DBTYPE, char *);
+static void usage(void) __attribute__((__noreturn__));
+static void *xcopy(void *, size_t);
+static void chkcmd(enum S);
+static void chkdata(enum S);
+static void chkkey(enum S);
+
+#ifdef STATISTICS
+extern void __bt_stat(DB *);
+#endif
+
+static DBTYPE type; /* Database type. */
+static void *infop; /* Iflags. */
+static size_t lineno; /* Current line in test script. */
+static u_int flags; /* Current DB flags. */
+static int ofd = STDOUT_FILENO; /* Standard output fd. */
+
+static DB *XXdbp; /* Global for gdb. */
+static size_t XXlineno; /* Fast breakpoint for gdb. */
+
+int
+main(int argc, char *argv[])
+{
+ extern int optind;
+ extern char *optarg;
+ enum S command = COMMAND, state;
+ DB *dbp;
+ DBT data, key, keydata;
+ size_t len;
+ int ch, oflags, sflag;
+ char *fname, *infoarg, *p, *t, buf[8 * 1024];
+ bool unlink_dbfile;
+
+ infoarg = NULL;
+ fname = NULL;
+ unlink_dbfile = false;
+ oflags = O_CREAT | O_RDWR;
+ sflag = 0;
+ while ((ch = getopt(argc, argv, "f:i:lo:s")) != -1)
+ switch (ch) {
+ case 'f':
+ fname = optarg;
+ break;
+ case 'i':
+ infoarg = optarg;
+ break;
+ case 'l':
+ oflags |= DB_LOCK;
+ break;
+ case 'o':
+ if ((ofd = open(optarg,
+ O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
+ err(1, "Cannot create `%s'", optarg);
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ /* Set the type. */
+ type = dbtype(*argv++);
+
+ /* Open the descriptor file. */
+ if (strcmp(*argv, "-") && freopen(*argv, "r", stdin) == NULL)
+ err(1, "Cannot reopen `%s'", *argv);
+
+ /* Set up the db structure as necessary. */
+ if (infoarg == NULL)
+ infop = NULL;
+ else
+ for (p = strtok(infoarg, ",\t "); p != NULL;
+ p = strtok(0, ",\t "))
+ if (*p != '\0')
+ infop = setinfo(type, p);
+
+ /*
+ * Open the DB. Delete any preexisting copy, you almost never
+ * want it around, and it often screws up tests.
+ */
+ if (fname == NULL) {
+ const char *q = getenv("TMPDIR");
+ if (q == NULL)
+ q = "/var/tmp";
+ (void)snprintf(buf, sizeof(buf), "%s/__dbtest", q);
+ fname = buf;
+ (void)unlink(buf);
+ unlink_dbfile = true;
+ } else if (!sflag)
+ (void)unlink(fname);
+
+ if ((dbp = dbopen(fname,
+ oflags, S_IRUSR | S_IWUSR, type, infop)) == NULL)
+ err(1, "Cannot dbopen `%s'", fname);
+ XXdbp = dbp;
+ if (unlink_dbfile)
+ (void)unlink(fname);
+
+ state = COMMAND;
+ for (lineno = 1;
+ (p = fgets(buf, sizeof(buf), stdin)) != NULL; ++lineno) {
+ /* Delete the newline, displaying the key/data is easier. */
+ if (ofd == STDOUT_FILENO && (t = strchr(p, '\n')) != NULL)
+ *t = '\0';
+ if ((len = strlen(buf)) == 0 || isspace((unsigned char)*p) ||
+ *p == '#')
+ continue;
+
+ /* Convenient gdb break point. */
+ if (XXlineno == lineno)
+ XXlineno = 1;
+ switch (*p) {
+ case 'c': /* compare */
+ chkcmd(state);
+ state = KEY;
+ command = COMPARE;
+ break;
+ case 'e': /* echo */
+ chkcmd(state);
+ /* Don't display the newline, if CR at EOL. */
+ if (p[len - 2] == '\r')
+ --len;
+ if (write(ofd, p + 1, len - 1) != (ssize_t)len - 1 ||
+ write(ofd, "\n", 1) != 1)
+ err(1, "write failed");
+ break;
+ case 'g': /* get */
+ chkcmd(state);
+ state = KEY;
+ command = GET;
+ break;
+ case 'p': /* put */
+ chkcmd(state);
+ state = KEY;
+ command = PUT;
+ break;
+ case 'r': /* remove */
+ chkcmd(state);
+ if (flags == R_CURSOR) {
+ rem(dbp, &key);
+ state = COMMAND;
+ } else {
+ state = KEY;
+ command = REMOVE;
+ }
+ break;
+ case 'S': /* sync */
+ chkcmd(state);
+ synk(dbp);
+ state = COMMAND;
+ break;
+ case 's': /* seq */
+ chkcmd(state);
+ if (flags == R_CURSOR) {
+ state = KEY;
+ command = SEQ;
+ } else
+ seq(dbp, &key);
+ break;
+ case 'f':
+ flags = setflags(p + 1);
+ break;
+ case 'D': /* data file */
+ chkdata(state);
+ data.data = rfile(p + 1, &data.size);
+ goto ldata;
+ case 'd': /* data */
+ chkdata(state);
+ data.data = xcopy(p + 1, len - 1);
+ data.size = len - 1;
+ldata: switch (command) {
+ case COMPARE:
+ compare(&keydata, &data);
+ break;
+ case PUT:
+ put(dbp, &key, &data);
+ break;
+ default:
+ errx(1, "line %zu: command doesn't take data",
+ lineno);
+ }
+ if (type != DB_RECNO)
+ free(key.data);
+ free(data.data);
+ state = COMMAND;
+ break;
+ case 'K': /* key file */
+ chkkey(state);
+ if (type == DB_RECNO)
+ errx(1, "line %zu: 'K' not available for recno",
+ lineno);
+ key.data = rfile(p + 1, &key.size);
+ goto lkey;
+ case 'k': /* key */
+ chkkey(state);
+ if (type == DB_RECNO) {
+ static recno_t recno;
+ recno = atoi(p + 1);
+ key.data = &recno;
+ key.size = sizeof(recno);
+ } else {
+ key.data = xcopy(p + 1, len - 1);
+ key.size = len - 1;
+ }
+lkey: switch (command) {
+ case COMPARE:
+ getdata(dbp, &key, &keydata);
+ state = DATA;
+ break;
+ case GET:
+ get(dbp, &key);
+ if (type != DB_RECNO)
+ free(key.data);
+ state = COMMAND;
+ break;
+ case PUT:
+ state = DATA;
+ break;
+ case REMOVE:
+ rem(dbp, &key);
+ if ((type != DB_RECNO) && (flags != R_CURSOR))
+ free(key.data);
+ state = COMMAND;
+ break;
+ case SEQ:
+ seq(dbp, &key);
+ if ((type != DB_RECNO) && (flags != R_CURSOR))
+ free(key.data);
+ state = COMMAND;
+ break;
+ default:
+ errx(1, "line %zu: command doesn't take a key",
+ lineno);
+ }
+ break;
+ case 'o':
+ dump(dbp, p[1] == 'r');
+ break;
+ default:
+ errx(1, "line %zu: %s: unknown command character",
+ lineno, p);
+ }
+ }
+#ifdef STATISTICS
+ /*
+ * -l must be used (DB_LOCK must be set) for this to be
+ * used, otherwise a page will be locked and it will fail.
+ */
+ if (type == DB_BTREE && oflags & DB_LOCK)
+ __bt_stat(dbp);
+#endif
+ if ((*dbp->close)(dbp))
+ err(1, "db->close failed");
+ (void)close(ofd);
+ return 0;
+}
+
+#define NOOVERWRITE "put failed, would overwrite key\n"
+
+static void
+compare(DBT *db1, DBT *db2)
+{
+ size_t len;
+ u_char *p1, *p2;
+
+ if (db1->size != db2->size)
+ printf("compare failed: key->data len %zu != data len %zu\n",
+ db1->size, db2->size);
+
+ len = MIN(db1->size, db2->size);
+ for (p1 = db1->data, p2 = db2->data; len--;)
+ if (*p1++ != *p2++) {
+ printf("compare failed at offset %lu\n",
+ (unsigned long)(p1 - (u_char *)db1->data));
+ break;
+ }
+}
+
+static void
+get(DB *dbp, DBT *kp)
+{
+ DBT data;
+
+ switch ((*dbp->get)(dbp, kp, &data, flags)) {
+ case 0:
+ (void)write(ofd, data.data, data.size);
+ if (ofd == STDOUT_FILENO)
+ (void)write(ofd, "\n", 1);
+ break;
+ case -1:
+ err(1, "line %zu: get failed", lineno);
+ /* NOTREACHED */
+ case 1:
+#define NOSUCHKEY "get failed, no such key\n"
+ if (ofd != STDOUT_FILENO)
+ (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
+ else
+ (void)fprintf(stderr, "%zu: %.*s: %s",
+ lineno, (int)MIN(kp->size, 20),
+ (const char *)kp->data,
+ NOSUCHKEY);
+#undef NOSUCHKEY
+ break;
+ }
+}
+
+static void
+getdata(DB *dbp, DBT *kp, DBT *dp)
+{
+ switch ((*dbp->get)(dbp, kp, dp, flags)) {
+ case 0:
+ return;
+ case -1:
+ err(1, "line %zu: getdata failed", lineno);
+ /* NOTREACHED */
+ case 1:
+ errx(1, "line %zu: getdata failed, no such key", lineno);
+ /* NOTREACHED */
+ }
+}
+
+static void
+put(DB *dbp, DBT *kp, DBT *dp)
+{
+ switch ((*dbp->put)(dbp, kp, dp, flags)) {
+ case 0:
+ break;
+ case -1:
+ err(1, "line %zu: put failed", lineno);
+ /* NOTREACHED */
+ case 1:
+ (void)write(ofd, NOOVERWRITE, sizeof(NOOVERWRITE) - 1);
+ break;
+ }
+}
+
+static void
+rem(DB *dbp, DBT *kp)
+{
+ switch ((*dbp->del)(dbp, kp, flags)) {
+ case 0:
+ break;
+ case -1:
+ err(1, "line %zu: rem failed", lineno);
+ /* NOTREACHED */
+ case 1:
+#define NOSUCHKEY "rem failed, no such key\n"
+ if (ofd != STDOUT_FILENO)
+ (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
+ else if (flags != R_CURSOR)
+ (void)fprintf(stderr, "%zu: %.*s: %s",
+ lineno, (int)MIN(kp->size, 20),
+ (const char *)kp->data, NOSUCHKEY);
+ else
+ (void)fprintf(stderr,
+ "%zu: rem of cursor failed\n", lineno);
+#undef NOSUCHKEY
+ break;
+ }
+}
+
+static void
+synk(DB *dbp)
+{
+ switch ((*dbp->sync)(dbp, flags)) {
+ case 0:
+ break;
+ case -1:
+ err(1, "line %zu: synk failed", lineno);
+ /* NOTREACHED */
+ }
+}
+
+static void
+seq(DB *dbp, DBT *kp)
+{
+ DBT data;
+
+ switch (dbp->seq(dbp, kp, &data, flags)) {
+ case 0:
+ (void)write(ofd, data.data, data.size);
+ if (ofd == STDOUT_FILENO)
+ (void)write(ofd, "\n", 1);
+ break;
+ case -1:
+ err(1, "line %zu: seq failed", lineno);
+ /* NOTREACHED */
+ case 1:
+#define NOSUCHKEY "seq failed, no such key\n"
+ if (ofd != STDOUT_FILENO)
+ (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
+ else if (flags == R_CURSOR)
+ (void)fprintf(stderr, "%zu: %.*s: %s",
+ lineno, (int)MIN(kp->size, 20),
+ (const char *)kp->data, NOSUCHKEY);
+ else
+ (void)fprintf(stderr,
+ "%zu: seq (%s) failed\n", lineno, sflags(flags));
+#undef NOSUCHKEY
+ break;
+ }
+}
+
+static void
+dump(DB *dbp, int rev)
+{
+ DBT key, data;
+ int xflags, nflags;
+
+ if (rev) {
+ xflags = R_LAST;
+ nflags = R_PREV;
+ } else {
+ xflags = R_FIRST;
+ nflags = R_NEXT;
+ }
+ for (;; xflags = nflags)
+ switch (dbp->seq(dbp, &key, &data, xflags)) {
+ case 0:
+ (void)write(ofd, data.data, data.size);
+ if (ofd == STDOUT_FILENO)
+ (void)write(ofd, "\n", 1);
+ break;
+ case 1:
+ goto done;
+ case -1:
+ err(1, "line %zu: (dump) seq failed", lineno);
+ /* NOTREACHED */
+ }
+done: return;
+}
+
+static u_int
+setflags(char *s)
+{
+ char *p;
+
+ for (; isspace((unsigned char)*s); ++s);
+ if (*s == '\n' || *s == '\0')
+ return 0;
+ if ((p = strchr(s, '\n')) != NULL)
+ *p = '\0';
+ if (!strcmp(s, "R_CURSOR")) return R_CURSOR;
+ if (!strcmp(s, "R_FIRST")) return R_FIRST;
+ if (!strcmp(s, "R_IAFTER")) return R_IAFTER;
+ if (!strcmp(s, "R_IBEFORE")) return R_IBEFORE;
+ if (!strcmp(s, "R_LAST")) return R_LAST;
+ if (!strcmp(s, "R_NEXT")) return R_NEXT;
+ if (!strcmp(s, "R_NOOVERWRITE")) return R_NOOVERWRITE;
+ if (!strcmp(s, "R_PREV")) return R_PREV;
+ if (!strcmp(s, "R_SETCURSOR")) return R_SETCURSOR;
+
+ errx(1, "line %zu: %s: unknown flag", lineno, s);
+ /* NOTREACHED */
+}
+
+static const char *
+sflags(int xflags)
+{
+ switch (xflags) {
+ case R_CURSOR: return "R_CURSOR";
+ case R_FIRST: return "R_FIRST";
+ case R_IAFTER: return "R_IAFTER";
+ case R_IBEFORE: return "R_IBEFORE";
+ case R_LAST: return "R_LAST";
+ case R_NEXT: return "R_NEXT";
+ case R_NOOVERWRITE: return "R_NOOVERWRITE";
+ case R_PREV: return "R_PREV";
+ case R_SETCURSOR: return "R_SETCURSOR";
+ }
+
+ return "UNKNOWN!";
+}
+
+static DBTYPE
+dbtype(const char *s)
+{
+ if (!strcmp(s, "btree"))
+ return DB_BTREE;
+ if (!strcmp(s, "hash"))
+ return DB_HASH;
+ if (!strcmp(s, "recno"))
+ return DB_RECNO;
+ errx(1, "%s: unknown type (use btree, hash or recno)", s);
+ /* NOTREACHED */
+}
+
+static void *
+setinfo(DBTYPE dtype, char *s)
+{
+ static BTREEINFO ib;
+ static HASHINFO ih;
+ static RECNOINFO rh;
+ char *eq;
+
+ if ((eq = strchr(s, '=')) == NULL)
+ errx(1, "%s: illegal structure set statement", s);
+ *eq++ = '\0';
+ if (!isdigit((unsigned char)*eq))
+ errx(1, "%s: structure set statement must be a number", s);
+
+ switch (dtype) {
+ case DB_BTREE:
+ if (!strcmp("flags", s)) {
+ ib.flags = atoi(eq);
+ return &ib;
+ }
+ if (!strcmp("cachesize", s)) {
+ ib.cachesize = atoi(eq);
+ return &ib;
+ }
+ if (!strcmp("maxkeypage", s)) {
+ ib.maxkeypage = atoi(eq);
+ return &ib;
+ }
+ if (!strcmp("minkeypage", s)) {
+ ib.minkeypage = atoi(eq);
+ return &ib;
+ }
+ if (!strcmp("lorder", s)) {
+ ib.lorder = atoi(eq);
+ return &ib;
+ }
+ if (!strcmp("psize", s)) {
+ ib.psize = atoi(eq);
+ return &ib;
+ }
+ break;
+ case DB_HASH:
+ if (!strcmp("bsize", s)) {
+ ih.bsize = atoi(eq);
+ return &ih;
+ }
+ if (!strcmp("ffactor", s)) {
+ ih.ffactor = atoi(eq);
+ return &ih;
+ }
+ if (!strcmp("nelem", s)) {
+ ih.nelem = atoi(eq);
+ return &ih;
+ }
+ if (!strcmp("cachesize", s)) {
+ ih.cachesize = atoi(eq);
+ return &ih;
+ }
+ if (!strcmp("lorder", s)) {
+ ih.lorder = atoi(eq);
+ return &ih;
+ }
+ break;
+ case DB_RECNO:
+ if (!strcmp("flags", s)) {
+ rh.flags = atoi(eq);
+ return &rh;
+ }
+ if (!strcmp("cachesize", s)) {
+ rh.cachesize = atoi(eq);
+ return &rh;
+ }
+ if (!strcmp("lorder", s)) {
+ rh.lorder = atoi(eq);
+ return &rh;
+ }
+ if (!strcmp("reclen", s)) {
+ rh.reclen = atoi(eq);
+ return &rh;
+ }
+ if (!strcmp("bval", s)) {
+ rh.bval = atoi(eq);
+ return &rh;
+ }
+ if (!strcmp("psize", s)) {
+ rh.psize = atoi(eq);
+ return &rh;
+ }
+ break;
+ }
+ errx(1, "%s: unknown structure value", s);
+ /* NOTREACHED */
+}
+
+static void *
+rfile(char *name, size_t *lenp)
+{
+ struct stat sb;
+ void *p;
+ int fd;
+ char *np;
+
+ for (; isspace((unsigned char)*name); ++name)
+ continue;
+ if ((np = strchr(name, '\n')) != NULL)
+ *np = '\0';
+ if ((fd = open(name, O_RDONLY, 0)) == -1 || fstat(fd, &sb) == -1)
+ err(1, "Cannot open `%s'", name);
+#ifdef NOT_PORTABLE
+ if (sb.st_size > (off_t)SIZE_T_MAX) {
+ errno = E2BIG;
+ err("Cannot process `%s'", name);
+ }
+#endif
+ if ((p = malloc((size_t)sb.st_size)) == NULL)
+ err(1, "Cannot allocate %zu bytes", (size_t)sb.st_size);
+ if (read(fd, p, (ssize_t)sb.st_size) != (ssize_t)sb.st_size)
+ err(1, "read failed");
+ *lenp = (size_t)sb.st_size;
+ (void)close(fd);
+ return p;
+}
+
+static void *
+xcopy(void *text, size_t len)
+{
+ void *p;
+
+ if ((p = malloc(len)) == NULL)
+ err(1, "Cannot allocate %zu bytes", len);
+ (void)memmove(p, text, len);
+ return p;
+}
+
+static void
+chkcmd(enum S state)
+{
+ if (state != COMMAND)
+ errx(1, "line %zu: not expecting command", lineno);
+}
+
+static void
+chkdata(enum S state)
+{
+ if (state != DATA)
+ errx(1, "line %zu: not expecting data", lineno);
+}
+
+static void
+chkkey(enum S state)
+{
+ if (state != KEY)
+ errx(1, "line %zu: not expecting a key", lineno);
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr,
+ "Usage: %s [-l] [-f file] [-i info] [-o file] type script\n",
+ getprogname());
+ exit(1);
+}