1*2b55b311Schristos /* $NetBSD: h_db.c,v 1.3 2016/09/24 21:18:22 christos Exp $ */
20308b37fSpgoyette
30308b37fSpgoyette /*-
40308b37fSpgoyette * Copyright (c) 1992, 1993, 1994
50308b37fSpgoyette * The Regents of the University of California. All rights reserved.
60308b37fSpgoyette *
70308b37fSpgoyette * Redistribution and use in source and binary forms, with or without
80308b37fSpgoyette * modification, are permitted provided that the following conditions
90308b37fSpgoyette * are met:
100308b37fSpgoyette * 1. Redistributions of source code must retain the above copyright
110308b37fSpgoyette * notice, this list of conditions and the following disclaimer.
120308b37fSpgoyette * 2. Redistributions in binary form must reproduce the above copyright
130308b37fSpgoyette * notice, this list of conditions and the following disclaimer in the
140308b37fSpgoyette * documentation and/or other materials provided with the distribution.
150308b37fSpgoyette * 3. Neither the name of the University nor the names of its contributors
160308b37fSpgoyette * may be used to endorse or promote products derived from this software
170308b37fSpgoyette * without specific prior written permission.
180308b37fSpgoyette *
190308b37fSpgoyette * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
200308b37fSpgoyette * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
210308b37fSpgoyette * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
220308b37fSpgoyette * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
230308b37fSpgoyette * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
240308b37fSpgoyette * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
250308b37fSpgoyette * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
260308b37fSpgoyette * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
270308b37fSpgoyette * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
280308b37fSpgoyette * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
290308b37fSpgoyette * SUCH DAMAGE.
300308b37fSpgoyette */
310308b37fSpgoyette
320308b37fSpgoyette #include <sys/cdefs.h>
330308b37fSpgoyette #ifndef lint
340308b37fSpgoyette __COPYRIGHT("@(#) Copyright (c) 1992, 1993, 1994\
350308b37fSpgoyette The Regents of the University of California. All rights reserved.");
360308b37fSpgoyette #endif /* not lint */
370308b37fSpgoyette
380308b37fSpgoyette #ifndef lint
390308b37fSpgoyette #if 0
400308b37fSpgoyette static char sccsid[] = "@(#)dbtest.c 8.17 (Berkeley) 9/1/94";
410308b37fSpgoyette #else
42*2b55b311Schristos __RCSID("$NetBSD: h_db.c,v 1.3 2016/09/24 21:18:22 christos Exp $");
430308b37fSpgoyette #endif
440308b37fSpgoyette #endif /* not lint */
450308b37fSpgoyette
460308b37fSpgoyette #include <sys/param.h>
470308b37fSpgoyette #include <sys/stat.h>
480308b37fSpgoyette
490308b37fSpgoyette #include <ctype.h>
500308b37fSpgoyette #include <errno.h>
510308b37fSpgoyette #include <fcntl.h>
520308b37fSpgoyette #include <limits.h>
530308b37fSpgoyette #include <stdio.h>
540308b37fSpgoyette #include <stdlib.h>
550308b37fSpgoyette #include <string.h>
560308b37fSpgoyette #include <stdbool.h>
570308b37fSpgoyette #include <unistd.h>
580308b37fSpgoyette #include <err.h>
590308b37fSpgoyette #include <db.h>
605703b437Schristos #include "btree.h"
610308b37fSpgoyette
620308b37fSpgoyette enum S { COMMAND, COMPARE, GET, PUT, REMOVE, SEQ, SEQFLAG, KEY, DATA };
630308b37fSpgoyette
640308b37fSpgoyette static void compare(DBT *, DBT *);
650308b37fSpgoyette static DBTYPE dbtype(const char *);
665703b437Schristos static void dump(DB *, int, int);
670308b37fSpgoyette static void get(DB *, DBT *);
680308b37fSpgoyette static void getdata(DB *, DBT *, DBT *);
690308b37fSpgoyette static void put(DB *, DBT *, DBT *);
700308b37fSpgoyette static void rem(DB *, DBT *);
710308b37fSpgoyette static const char *sflags(int);
720308b37fSpgoyette static void synk(DB *);
730308b37fSpgoyette static void *rfile(char *, size_t *);
740308b37fSpgoyette static void seq(DB *, DBT *);
750308b37fSpgoyette static u_int setflags(char *);
760308b37fSpgoyette static void *setinfo(DBTYPE, char *);
775703b437Schristos static void unlinkpg(DB *);
780308b37fSpgoyette static void usage(void) __attribute__((__noreturn__));
790308b37fSpgoyette static void *xcopy(void *, size_t);
800308b37fSpgoyette static void chkcmd(enum S);
810308b37fSpgoyette static void chkdata(enum S);
820308b37fSpgoyette static void chkkey(enum S);
830308b37fSpgoyette
840308b37fSpgoyette #ifdef STATISTICS
850308b37fSpgoyette extern void __bt_stat(DB *);
860308b37fSpgoyette #endif
875703b437Schristos extern int __bt_relink(BTREE *, PAGE *);
880308b37fSpgoyette
890308b37fSpgoyette static DBTYPE type; /* Database type. */
900308b37fSpgoyette static void *infop; /* Iflags. */
910308b37fSpgoyette static size_t lineno; /* Current line in test script. */
920308b37fSpgoyette static u_int flags; /* Current DB flags. */
930308b37fSpgoyette static int ofd = STDOUT_FILENO; /* Standard output fd. */
940308b37fSpgoyette
950308b37fSpgoyette static DB *XXdbp; /* Global for gdb. */
960308b37fSpgoyette static size_t XXlineno; /* Fast breakpoint for gdb. */
970308b37fSpgoyette
980308b37fSpgoyette int
main(int argc,char * argv[])990308b37fSpgoyette main(int argc, char *argv[])
1000308b37fSpgoyette {
1010308b37fSpgoyette extern int optind;
1020308b37fSpgoyette extern char *optarg;
1030308b37fSpgoyette enum S command = COMMAND, state;
1040308b37fSpgoyette DB *dbp;
1050308b37fSpgoyette DBT data, key, keydata;
1060308b37fSpgoyette size_t len;
1070308b37fSpgoyette int ch, oflags, sflag;
1080308b37fSpgoyette char *fname, *infoarg, *p, *t, buf[8 * 1024];
1090308b37fSpgoyette bool unlink_dbfile;
1100308b37fSpgoyette
1110308b37fSpgoyette infoarg = NULL;
1120308b37fSpgoyette fname = NULL;
1130308b37fSpgoyette unlink_dbfile = false;
1140308b37fSpgoyette oflags = O_CREAT | O_RDWR;
1150308b37fSpgoyette sflag = 0;
1160308b37fSpgoyette while ((ch = getopt(argc, argv, "f:i:lo:s")) != -1)
1170308b37fSpgoyette switch (ch) {
1180308b37fSpgoyette case 'f':
1190308b37fSpgoyette fname = optarg;
1200308b37fSpgoyette break;
1210308b37fSpgoyette case 'i':
1220308b37fSpgoyette infoarg = optarg;
1230308b37fSpgoyette break;
1240308b37fSpgoyette case 'l':
1250308b37fSpgoyette oflags |= DB_LOCK;
1260308b37fSpgoyette break;
1270308b37fSpgoyette case 'o':
1280308b37fSpgoyette if ((ofd = open(optarg,
1290308b37fSpgoyette O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
1300308b37fSpgoyette err(1, "Cannot create `%s'", optarg);
1310308b37fSpgoyette break;
1320308b37fSpgoyette case 's':
1330308b37fSpgoyette sflag = 1;
1340308b37fSpgoyette break;
1350308b37fSpgoyette case '?':
1360308b37fSpgoyette default:
1370308b37fSpgoyette usage();
1380308b37fSpgoyette }
1390308b37fSpgoyette argc -= optind;
1400308b37fSpgoyette argv += optind;
1410308b37fSpgoyette
1420308b37fSpgoyette if (argc != 2)
1430308b37fSpgoyette usage();
1440308b37fSpgoyette
1450308b37fSpgoyette /* Set the type. */
1460308b37fSpgoyette type = dbtype(*argv++);
1470308b37fSpgoyette
1480308b37fSpgoyette /* Open the descriptor file. */
1490308b37fSpgoyette if (strcmp(*argv, "-") && freopen(*argv, "r", stdin) == NULL)
1500308b37fSpgoyette err(1, "Cannot reopen `%s'", *argv);
1510308b37fSpgoyette
1520308b37fSpgoyette /* Set up the db structure as necessary. */
1530308b37fSpgoyette if (infoarg == NULL)
1540308b37fSpgoyette infop = NULL;
1550308b37fSpgoyette else
1560308b37fSpgoyette for (p = strtok(infoarg, ",\t "); p != NULL;
1570308b37fSpgoyette p = strtok(0, ",\t "))
1580308b37fSpgoyette if (*p != '\0')
1590308b37fSpgoyette infop = setinfo(type, p);
1600308b37fSpgoyette
1610308b37fSpgoyette /*
1620308b37fSpgoyette * Open the DB. Delete any preexisting copy, you almost never
1630308b37fSpgoyette * want it around, and it often screws up tests.
1640308b37fSpgoyette */
1650308b37fSpgoyette if (fname == NULL) {
1660308b37fSpgoyette const char *q = getenv("TMPDIR");
1670308b37fSpgoyette if (q == NULL)
1680308b37fSpgoyette q = "/var/tmp";
1690308b37fSpgoyette (void)snprintf(buf, sizeof(buf), "%s/__dbtest", q);
1700308b37fSpgoyette fname = buf;
1710308b37fSpgoyette (void)unlink(buf);
1720308b37fSpgoyette unlink_dbfile = true;
1730308b37fSpgoyette } else if (!sflag)
1740308b37fSpgoyette (void)unlink(fname);
1750308b37fSpgoyette
1760308b37fSpgoyette if ((dbp = dbopen(fname,
1770308b37fSpgoyette oflags, S_IRUSR | S_IWUSR, type, infop)) == NULL)
1780308b37fSpgoyette err(1, "Cannot dbopen `%s'", fname);
1790308b37fSpgoyette XXdbp = dbp;
1800308b37fSpgoyette if (unlink_dbfile)
1810308b37fSpgoyette (void)unlink(fname);
1820308b37fSpgoyette
1830308b37fSpgoyette state = COMMAND;
1840308b37fSpgoyette for (lineno = 1;
1850308b37fSpgoyette (p = fgets(buf, sizeof(buf), stdin)) != NULL; ++lineno) {
1860308b37fSpgoyette /* Delete the newline, displaying the key/data is easier. */
1870308b37fSpgoyette if (ofd == STDOUT_FILENO && (t = strchr(p, '\n')) != NULL)
1880308b37fSpgoyette *t = '\0';
1890308b37fSpgoyette if ((len = strlen(buf)) == 0 || isspace((unsigned char)*p) ||
1900308b37fSpgoyette *p == '#')
1910308b37fSpgoyette continue;
1920308b37fSpgoyette
1930308b37fSpgoyette /* Convenient gdb break point. */
1940308b37fSpgoyette if (XXlineno == lineno)
1950308b37fSpgoyette XXlineno = 1;
1960308b37fSpgoyette switch (*p) {
1970308b37fSpgoyette case 'c': /* compare */
1980308b37fSpgoyette chkcmd(state);
1990308b37fSpgoyette state = KEY;
2000308b37fSpgoyette command = COMPARE;
2010308b37fSpgoyette break;
2020308b37fSpgoyette case 'e': /* echo */
2030308b37fSpgoyette chkcmd(state);
2040308b37fSpgoyette /* Don't display the newline, if CR at EOL. */
2050308b37fSpgoyette if (p[len - 2] == '\r')
2060308b37fSpgoyette --len;
2070308b37fSpgoyette if (write(ofd, p + 1, len - 1) != (ssize_t)len - 1 ||
2080308b37fSpgoyette write(ofd, "\n", 1) != 1)
2090308b37fSpgoyette err(1, "write failed");
2100308b37fSpgoyette break;
2110308b37fSpgoyette case 'g': /* get */
2120308b37fSpgoyette chkcmd(state);
2130308b37fSpgoyette state = KEY;
2140308b37fSpgoyette command = GET;
2150308b37fSpgoyette break;
2160308b37fSpgoyette case 'p': /* put */
2170308b37fSpgoyette chkcmd(state);
2180308b37fSpgoyette state = KEY;
2190308b37fSpgoyette command = PUT;
2200308b37fSpgoyette break;
2210308b37fSpgoyette case 'r': /* remove */
2220308b37fSpgoyette chkcmd(state);
2230308b37fSpgoyette if (flags == R_CURSOR) {
2240308b37fSpgoyette rem(dbp, &key);
2250308b37fSpgoyette state = COMMAND;
2260308b37fSpgoyette } else {
2270308b37fSpgoyette state = KEY;
2280308b37fSpgoyette command = REMOVE;
2290308b37fSpgoyette }
2300308b37fSpgoyette break;
2310308b37fSpgoyette case 'S': /* sync */
2320308b37fSpgoyette chkcmd(state);
2330308b37fSpgoyette synk(dbp);
2340308b37fSpgoyette state = COMMAND;
2350308b37fSpgoyette break;
2360308b37fSpgoyette case 's': /* seq */
2370308b37fSpgoyette chkcmd(state);
2380308b37fSpgoyette if (flags == R_CURSOR) {
2390308b37fSpgoyette state = KEY;
2400308b37fSpgoyette command = SEQ;
2410308b37fSpgoyette } else
2420308b37fSpgoyette seq(dbp, &key);
2430308b37fSpgoyette break;
2440308b37fSpgoyette case 'f':
2450308b37fSpgoyette flags = setflags(p + 1);
2460308b37fSpgoyette break;
2470308b37fSpgoyette case 'D': /* data file */
2480308b37fSpgoyette chkdata(state);
2490308b37fSpgoyette data.data = rfile(p + 1, &data.size);
2500308b37fSpgoyette goto ldata;
2510308b37fSpgoyette case 'd': /* data */
2520308b37fSpgoyette chkdata(state);
2530308b37fSpgoyette data.data = xcopy(p + 1, len - 1);
2540308b37fSpgoyette data.size = len - 1;
2550308b37fSpgoyette ldata: switch (command) {
2560308b37fSpgoyette case COMPARE:
2570308b37fSpgoyette compare(&keydata, &data);
2580308b37fSpgoyette break;
2590308b37fSpgoyette case PUT:
2600308b37fSpgoyette put(dbp, &key, &data);
2610308b37fSpgoyette break;
2620308b37fSpgoyette default:
2630308b37fSpgoyette errx(1, "line %zu: command doesn't take data",
2640308b37fSpgoyette lineno);
2650308b37fSpgoyette }
2660308b37fSpgoyette if (type != DB_RECNO)
2670308b37fSpgoyette free(key.data);
2680308b37fSpgoyette free(data.data);
2690308b37fSpgoyette state = COMMAND;
2700308b37fSpgoyette break;
2710308b37fSpgoyette case 'K': /* key file */
2720308b37fSpgoyette chkkey(state);
2730308b37fSpgoyette if (type == DB_RECNO)
2740308b37fSpgoyette errx(1, "line %zu: 'K' not available for recno",
2750308b37fSpgoyette lineno);
2760308b37fSpgoyette key.data = rfile(p + 1, &key.size);
2770308b37fSpgoyette goto lkey;
2780308b37fSpgoyette case 'k': /* key */
2790308b37fSpgoyette chkkey(state);
2800308b37fSpgoyette if (type == DB_RECNO) {
2810308b37fSpgoyette static recno_t recno;
2820308b37fSpgoyette recno = atoi(p + 1);
2830308b37fSpgoyette key.data = &recno;
2840308b37fSpgoyette key.size = sizeof(recno);
2850308b37fSpgoyette } else {
2860308b37fSpgoyette key.data = xcopy(p + 1, len - 1);
2870308b37fSpgoyette key.size = len - 1;
2880308b37fSpgoyette }
2890308b37fSpgoyette lkey: switch (command) {
2900308b37fSpgoyette case COMPARE:
2910308b37fSpgoyette getdata(dbp, &key, &keydata);
2920308b37fSpgoyette state = DATA;
2930308b37fSpgoyette break;
2940308b37fSpgoyette case GET:
2950308b37fSpgoyette get(dbp, &key);
2960308b37fSpgoyette if (type != DB_RECNO)
2970308b37fSpgoyette free(key.data);
2980308b37fSpgoyette state = COMMAND;
2990308b37fSpgoyette break;
3000308b37fSpgoyette case PUT:
3010308b37fSpgoyette state = DATA;
3020308b37fSpgoyette break;
3030308b37fSpgoyette case REMOVE:
3040308b37fSpgoyette rem(dbp, &key);
3050308b37fSpgoyette if ((type != DB_RECNO) && (flags != R_CURSOR))
3060308b37fSpgoyette free(key.data);
3070308b37fSpgoyette state = COMMAND;
3080308b37fSpgoyette break;
3090308b37fSpgoyette case SEQ:
3100308b37fSpgoyette seq(dbp, &key);
3110308b37fSpgoyette if ((type != DB_RECNO) && (flags != R_CURSOR))
3120308b37fSpgoyette free(key.data);
3130308b37fSpgoyette state = COMMAND;
3140308b37fSpgoyette break;
3150308b37fSpgoyette default:
3160308b37fSpgoyette errx(1, "line %zu: command doesn't take a key",
3170308b37fSpgoyette lineno);
3180308b37fSpgoyette }
3190308b37fSpgoyette break;
3200308b37fSpgoyette case 'o':
3215703b437Schristos dump(dbp, p[1] == 'r', 0);
3225703b437Schristos break;
3235703b437Schristos case 'O':
3245703b437Schristos dump(dbp, p[1] == 'r', 1);
3255703b437Schristos break;
3265703b437Schristos case 'u':
3275703b437Schristos unlinkpg(dbp);
3280308b37fSpgoyette break;
3290308b37fSpgoyette default:
3300308b37fSpgoyette errx(1, "line %zu: %s: unknown command character",
3310308b37fSpgoyette lineno, p);
3320308b37fSpgoyette }
3330308b37fSpgoyette }
3340308b37fSpgoyette #ifdef STATISTICS
3350308b37fSpgoyette /*
3360308b37fSpgoyette * -l must be used (DB_LOCK must be set) for this to be
3370308b37fSpgoyette * used, otherwise a page will be locked and it will fail.
3380308b37fSpgoyette */
3390308b37fSpgoyette if (type == DB_BTREE && oflags & DB_LOCK)
3400308b37fSpgoyette __bt_stat(dbp);
3410308b37fSpgoyette #endif
3420308b37fSpgoyette if ((*dbp->close)(dbp))
3430308b37fSpgoyette err(1, "db->close failed");
3440308b37fSpgoyette (void)close(ofd);
3450308b37fSpgoyette return 0;
3460308b37fSpgoyette }
3470308b37fSpgoyette
3480308b37fSpgoyette #define NOOVERWRITE "put failed, would overwrite key\n"
3490308b37fSpgoyette
3500308b37fSpgoyette static void
compare(DBT * db1,DBT * db2)3510308b37fSpgoyette compare(DBT *db1, DBT *db2)
3520308b37fSpgoyette {
3530308b37fSpgoyette size_t len;
3540308b37fSpgoyette u_char *p1, *p2;
3550308b37fSpgoyette
3560308b37fSpgoyette if (db1->size != db2->size)
3570308b37fSpgoyette printf("compare failed: key->data len %zu != data len %zu\n",
3580308b37fSpgoyette db1->size, db2->size);
3590308b37fSpgoyette
3600308b37fSpgoyette len = MIN(db1->size, db2->size);
3610308b37fSpgoyette for (p1 = db1->data, p2 = db2->data; len--;)
3620308b37fSpgoyette if (*p1++ != *p2++) {
3630308b37fSpgoyette printf("compare failed at offset %lu\n",
3640308b37fSpgoyette (unsigned long)(p1 - (u_char *)db1->data));
3650308b37fSpgoyette break;
3660308b37fSpgoyette }
3670308b37fSpgoyette }
3680308b37fSpgoyette
3690308b37fSpgoyette static void
get(DB * dbp,DBT * kp)3700308b37fSpgoyette get(DB *dbp, DBT *kp)
3710308b37fSpgoyette {
3720308b37fSpgoyette DBT data;
3730308b37fSpgoyette
3740308b37fSpgoyette switch ((*dbp->get)(dbp, kp, &data, flags)) {
3750308b37fSpgoyette case 0:
3760308b37fSpgoyette (void)write(ofd, data.data, data.size);
3770308b37fSpgoyette if (ofd == STDOUT_FILENO)
3780308b37fSpgoyette (void)write(ofd, "\n", 1);
3790308b37fSpgoyette break;
3800308b37fSpgoyette case -1:
3810308b37fSpgoyette err(1, "line %zu: get failed", lineno);
3820308b37fSpgoyette /* NOTREACHED */
3830308b37fSpgoyette case 1:
3840308b37fSpgoyette #define NOSUCHKEY "get failed, no such key\n"
3850308b37fSpgoyette if (ofd != STDOUT_FILENO)
3860308b37fSpgoyette (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
3870308b37fSpgoyette else
3880308b37fSpgoyette (void)fprintf(stderr, "%zu: %.*s: %s",
3890308b37fSpgoyette lineno, (int)MIN(kp->size, 20),
3900308b37fSpgoyette (const char *)kp->data,
3910308b37fSpgoyette NOSUCHKEY);
3920308b37fSpgoyette #undef NOSUCHKEY
3930308b37fSpgoyette break;
3940308b37fSpgoyette }
3950308b37fSpgoyette }
3960308b37fSpgoyette
3970308b37fSpgoyette static void
getdata(DB * dbp,DBT * kp,DBT * dp)3980308b37fSpgoyette getdata(DB *dbp, DBT *kp, DBT *dp)
3990308b37fSpgoyette {
4000308b37fSpgoyette switch ((*dbp->get)(dbp, kp, dp, flags)) {
4010308b37fSpgoyette case 0:
4020308b37fSpgoyette return;
4030308b37fSpgoyette case -1:
4040308b37fSpgoyette err(1, "line %zu: getdata failed", lineno);
4050308b37fSpgoyette /* NOTREACHED */
4060308b37fSpgoyette case 1:
4070308b37fSpgoyette errx(1, "line %zu: getdata failed, no such key", lineno);
4080308b37fSpgoyette /* NOTREACHED */
4090308b37fSpgoyette }
4100308b37fSpgoyette }
4110308b37fSpgoyette
4120308b37fSpgoyette static void
put(DB * dbp,DBT * kp,DBT * dp)4130308b37fSpgoyette put(DB *dbp, DBT *kp, DBT *dp)
4140308b37fSpgoyette {
4150308b37fSpgoyette switch ((*dbp->put)(dbp, kp, dp, flags)) {
4160308b37fSpgoyette case 0:
4170308b37fSpgoyette break;
4180308b37fSpgoyette case -1:
4190308b37fSpgoyette err(1, "line %zu: put failed", lineno);
4200308b37fSpgoyette /* NOTREACHED */
4210308b37fSpgoyette case 1:
4220308b37fSpgoyette (void)write(ofd, NOOVERWRITE, sizeof(NOOVERWRITE) - 1);
4230308b37fSpgoyette break;
4240308b37fSpgoyette }
4250308b37fSpgoyette }
4260308b37fSpgoyette
4270308b37fSpgoyette static void
rem(DB * dbp,DBT * kp)4280308b37fSpgoyette rem(DB *dbp, DBT *kp)
4290308b37fSpgoyette {
4300308b37fSpgoyette switch ((*dbp->del)(dbp, kp, flags)) {
4310308b37fSpgoyette case 0:
4320308b37fSpgoyette break;
4330308b37fSpgoyette case -1:
4340308b37fSpgoyette err(1, "line %zu: rem failed", lineno);
4350308b37fSpgoyette /* NOTREACHED */
4360308b37fSpgoyette case 1:
4370308b37fSpgoyette #define NOSUCHKEY "rem failed, no such key\n"
4380308b37fSpgoyette if (ofd != STDOUT_FILENO)
4390308b37fSpgoyette (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
4400308b37fSpgoyette else if (flags != R_CURSOR)
4410308b37fSpgoyette (void)fprintf(stderr, "%zu: %.*s: %s",
4420308b37fSpgoyette lineno, (int)MIN(kp->size, 20),
4430308b37fSpgoyette (const char *)kp->data, NOSUCHKEY);
4440308b37fSpgoyette else
4450308b37fSpgoyette (void)fprintf(stderr,
4460308b37fSpgoyette "%zu: rem of cursor failed\n", lineno);
4470308b37fSpgoyette #undef NOSUCHKEY
4480308b37fSpgoyette break;
4490308b37fSpgoyette }
4500308b37fSpgoyette }
4510308b37fSpgoyette
4520308b37fSpgoyette static void
synk(DB * dbp)4530308b37fSpgoyette synk(DB *dbp)
4540308b37fSpgoyette {
4550308b37fSpgoyette switch ((*dbp->sync)(dbp, flags)) {
4560308b37fSpgoyette case 0:
4570308b37fSpgoyette break;
4580308b37fSpgoyette case -1:
4590308b37fSpgoyette err(1, "line %zu: synk failed", lineno);
4600308b37fSpgoyette /* NOTREACHED */
4610308b37fSpgoyette }
4620308b37fSpgoyette }
4630308b37fSpgoyette
4640308b37fSpgoyette static void
seq(DB * dbp,DBT * kp)4650308b37fSpgoyette seq(DB *dbp, DBT *kp)
4660308b37fSpgoyette {
4670308b37fSpgoyette DBT data;
4680308b37fSpgoyette
4690308b37fSpgoyette switch (dbp->seq(dbp, kp, &data, flags)) {
4700308b37fSpgoyette case 0:
4710308b37fSpgoyette (void)write(ofd, data.data, data.size);
4720308b37fSpgoyette if (ofd == STDOUT_FILENO)
4730308b37fSpgoyette (void)write(ofd, "\n", 1);
4740308b37fSpgoyette break;
4750308b37fSpgoyette case -1:
4760308b37fSpgoyette err(1, "line %zu: seq failed", lineno);
4770308b37fSpgoyette /* NOTREACHED */
4780308b37fSpgoyette case 1:
4790308b37fSpgoyette #define NOSUCHKEY "seq failed, no such key\n"
4800308b37fSpgoyette if (ofd != STDOUT_FILENO)
4810308b37fSpgoyette (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
4820308b37fSpgoyette else if (flags == R_CURSOR)
4830308b37fSpgoyette (void)fprintf(stderr, "%zu: %.*s: %s",
4840308b37fSpgoyette lineno, (int)MIN(kp->size, 20),
4850308b37fSpgoyette (const char *)kp->data, NOSUCHKEY);
4860308b37fSpgoyette else
4870308b37fSpgoyette (void)fprintf(stderr,
4880308b37fSpgoyette "%zu: seq (%s) failed\n", lineno, sflags(flags));
4890308b37fSpgoyette #undef NOSUCHKEY
4900308b37fSpgoyette break;
4910308b37fSpgoyette }
4920308b37fSpgoyette }
4930308b37fSpgoyette
4940308b37fSpgoyette static void
dump(DB * dbp,int rev,int recurse)4955703b437Schristos dump(DB *dbp, int rev, int recurse)
4960308b37fSpgoyette {
4970308b37fSpgoyette DBT key, data;
4980308b37fSpgoyette int xflags, nflags;
4990308b37fSpgoyette
5000308b37fSpgoyette if (rev) {
5010308b37fSpgoyette xflags = R_LAST;
5025703b437Schristos nflags = recurse ? R_RPREV : R_PREV;
5030308b37fSpgoyette } else {
5040308b37fSpgoyette xflags = R_FIRST;
5055703b437Schristos nflags = recurse ? R_RNEXT : R_NEXT;
5060308b37fSpgoyette }
5070308b37fSpgoyette for (;; xflags = nflags)
5080308b37fSpgoyette switch (dbp->seq(dbp, &key, &data, xflags)) {
5090308b37fSpgoyette case 0:
5100308b37fSpgoyette (void)write(ofd, data.data, data.size);
5110308b37fSpgoyette if (ofd == STDOUT_FILENO)
5120308b37fSpgoyette (void)write(ofd, "\n", 1);
5130308b37fSpgoyette break;
5140308b37fSpgoyette case 1:
5150308b37fSpgoyette goto done;
5160308b37fSpgoyette case -1:
5170308b37fSpgoyette err(1, "line %zu: (dump) seq failed", lineno);
5180308b37fSpgoyette /* NOTREACHED */
5190308b37fSpgoyette }
5200308b37fSpgoyette done: return;
5210308b37fSpgoyette }
5220308b37fSpgoyette
5235703b437Schristos void
unlinkpg(DB * dbp)5245703b437Schristos unlinkpg(DB *dbp)
5255703b437Schristos {
5265703b437Schristos BTREE *t = dbp->internal;
5275703b437Schristos PAGE *h = NULL;
5285703b437Schristos pgno_t pg;
5295703b437Schristos
5305703b437Schristos for (pg = P_ROOT; pg < t->bt_mp->npages;
5315703b437Schristos mpool_put(t->bt_mp, h, 0), pg++) {
532*2b55b311Schristos if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
5335703b437Schristos break;
5345703b437Schristos /* Look for a nonempty leaf page that has both left
5355703b437Schristos * and right siblings. */
5365703b437Schristos if (h->prevpg == P_INVALID || h->nextpg == P_INVALID)
5375703b437Schristos continue;
5385703b437Schristos if (NEXTINDEX(h) == 0)
5395703b437Schristos continue;
5405703b437Schristos if ((h->flags & (P_BLEAF | P_RLEAF)))
5415703b437Schristos break;
5425703b437Schristos }
5435703b437Schristos if (h == NULL || pg == t->bt_mp->npages) {
5445703b437Schristos errx(1, "%s: no appropriate page found", __func__);
5455703b437Schristos return;
5465703b437Schristos }
5475703b437Schristos if (__bt_relink(t, h) != 0) {
5485703b437Schristos perror("unlinkpg");
5495703b437Schristos goto cleanup;
5505703b437Schristos }
5515703b437Schristos h->prevpg = P_INVALID;
5525703b437Schristos h->nextpg = P_INVALID;
5535703b437Schristos cleanup:
5545703b437Schristos mpool_put(t->bt_mp, h, MPOOL_DIRTY);
5555703b437Schristos }
5565703b437Schristos
5570308b37fSpgoyette static u_int
setflags(char * s)5580308b37fSpgoyette setflags(char *s)
5590308b37fSpgoyette {
5600308b37fSpgoyette char *p;
5610308b37fSpgoyette
5620308b37fSpgoyette for (; isspace((unsigned char)*s); ++s);
5630308b37fSpgoyette if (*s == '\n' || *s == '\0')
5640308b37fSpgoyette return 0;
5650308b37fSpgoyette if ((p = strchr(s, '\n')) != NULL)
5660308b37fSpgoyette *p = '\0';
5670308b37fSpgoyette if (!strcmp(s, "R_CURSOR")) return R_CURSOR;
5680308b37fSpgoyette if (!strcmp(s, "R_FIRST")) return R_FIRST;
5690308b37fSpgoyette if (!strcmp(s, "R_IAFTER")) return R_IAFTER;
5700308b37fSpgoyette if (!strcmp(s, "R_IBEFORE")) return R_IBEFORE;
5710308b37fSpgoyette if (!strcmp(s, "R_LAST")) return R_LAST;
5720308b37fSpgoyette if (!strcmp(s, "R_NEXT")) return R_NEXT;
5730308b37fSpgoyette if (!strcmp(s, "R_NOOVERWRITE")) return R_NOOVERWRITE;
5740308b37fSpgoyette if (!strcmp(s, "R_PREV")) return R_PREV;
5750308b37fSpgoyette if (!strcmp(s, "R_SETCURSOR")) return R_SETCURSOR;
5760308b37fSpgoyette
5770308b37fSpgoyette errx(1, "line %zu: %s: unknown flag", lineno, s);
5780308b37fSpgoyette /* NOTREACHED */
5790308b37fSpgoyette }
5800308b37fSpgoyette
5810308b37fSpgoyette static const char *
sflags(int xflags)5820308b37fSpgoyette sflags(int xflags)
5830308b37fSpgoyette {
5840308b37fSpgoyette switch (xflags) {
5850308b37fSpgoyette case R_CURSOR: return "R_CURSOR";
5860308b37fSpgoyette case R_FIRST: return "R_FIRST";
5870308b37fSpgoyette case R_IAFTER: return "R_IAFTER";
5880308b37fSpgoyette case R_IBEFORE: return "R_IBEFORE";
5890308b37fSpgoyette case R_LAST: return "R_LAST";
5900308b37fSpgoyette case R_NEXT: return "R_NEXT";
5910308b37fSpgoyette case R_NOOVERWRITE: return "R_NOOVERWRITE";
5920308b37fSpgoyette case R_PREV: return "R_PREV";
5930308b37fSpgoyette case R_SETCURSOR: return "R_SETCURSOR";
5940308b37fSpgoyette }
5950308b37fSpgoyette
5960308b37fSpgoyette return "UNKNOWN!";
5970308b37fSpgoyette }
5980308b37fSpgoyette
5990308b37fSpgoyette static DBTYPE
dbtype(const char * s)6000308b37fSpgoyette dbtype(const char *s)
6010308b37fSpgoyette {
6020308b37fSpgoyette if (!strcmp(s, "btree"))
6030308b37fSpgoyette return DB_BTREE;
6040308b37fSpgoyette if (!strcmp(s, "hash"))
6050308b37fSpgoyette return DB_HASH;
6060308b37fSpgoyette if (!strcmp(s, "recno"))
6070308b37fSpgoyette return DB_RECNO;
6080308b37fSpgoyette errx(1, "%s: unknown type (use btree, hash or recno)", s);
6090308b37fSpgoyette /* NOTREACHED */
6100308b37fSpgoyette }
6110308b37fSpgoyette
6120308b37fSpgoyette static void *
setinfo(DBTYPE dtype,char * s)6130308b37fSpgoyette setinfo(DBTYPE dtype, char *s)
6140308b37fSpgoyette {
6150308b37fSpgoyette static BTREEINFO ib;
6160308b37fSpgoyette static HASHINFO ih;
6170308b37fSpgoyette static RECNOINFO rh;
6180308b37fSpgoyette char *eq;
6190308b37fSpgoyette
6200308b37fSpgoyette if ((eq = strchr(s, '=')) == NULL)
6210308b37fSpgoyette errx(1, "%s: illegal structure set statement", s);
6220308b37fSpgoyette *eq++ = '\0';
6230308b37fSpgoyette if (!isdigit((unsigned char)*eq))
6240308b37fSpgoyette errx(1, "%s: structure set statement must be a number", s);
6250308b37fSpgoyette
6260308b37fSpgoyette switch (dtype) {
6270308b37fSpgoyette case DB_BTREE:
6280308b37fSpgoyette if (!strcmp("flags", s)) {
6290308b37fSpgoyette ib.flags = atoi(eq);
6300308b37fSpgoyette return &ib;
6310308b37fSpgoyette }
6320308b37fSpgoyette if (!strcmp("cachesize", s)) {
6330308b37fSpgoyette ib.cachesize = atoi(eq);
6340308b37fSpgoyette return &ib;
6350308b37fSpgoyette }
6360308b37fSpgoyette if (!strcmp("maxkeypage", s)) {
6370308b37fSpgoyette ib.maxkeypage = atoi(eq);
6380308b37fSpgoyette return &ib;
6390308b37fSpgoyette }
6400308b37fSpgoyette if (!strcmp("minkeypage", s)) {
6410308b37fSpgoyette ib.minkeypage = atoi(eq);
6420308b37fSpgoyette return &ib;
6430308b37fSpgoyette }
6440308b37fSpgoyette if (!strcmp("lorder", s)) {
6450308b37fSpgoyette ib.lorder = atoi(eq);
6460308b37fSpgoyette return &ib;
6470308b37fSpgoyette }
6480308b37fSpgoyette if (!strcmp("psize", s)) {
6490308b37fSpgoyette ib.psize = atoi(eq);
6500308b37fSpgoyette return &ib;
6510308b37fSpgoyette }
6520308b37fSpgoyette break;
6530308b37fSpgoyette case DB_HASH:
6540308b37fSpgoyette if (!strcmp("bsize", s)) {
6550308b37fSpgoyette ih.bsize = atoi(eq);
6560308b37fSpgoyette return &ih;
6570308b37fSpgoyette }
6580308b37fSpgoyette if (!strcmp("ffactor", s)) {
6590308b37fSpgoyette ih.ffactor = atoi(eq);
6600308b37fSpgoyette return &ih;
6610308b37fSpgoyette }
6620308b37fSpgoyette if (!strcmp("nelem", s)) {
6630308b37fSpgoyette ih.nelem = atoi(eq);
6640308b37fSpgoyette return &ih;
6650308b37fSpgoyette }
6660308b37fSpgoyette if (!strcmp("cachesize", s)) {
6670308b37fSpgoyette ih.cachesize = atoi(eq);
6680308b37fSpgoyette return &ih;
6690308b37fSpgoyette }
6700308b37fSpgoyette if (!strcmp("lorder", s)) {
6710308b37fSpgoyette ih.lorder = atoi(eq);
6720308b37fSpgoyette return &ih;
6730308b37fSpgoyette }
6740308b37fSpgoyette break;
6750308b37fSpgoyette case DB_RECNO:
6760308b37fSpgoyette if (!strcmp("flags", s)) {
6770308b37fSpgoyette rh.flags = atoi(eq);
6780308b37fSpgoyette return &rh;
6790308b37fSpgoyette }
6800308b37fSpgoyette if (!strcmp("cachesize", s)) {
6810308b37fSpgoyette rh.cachesize = atoi(eq);
6820308b37fSpgoyette return &rh;
6830308b37fSpgoyette }
6840308b37fSpgoyette if (!strcmp("lorder", s)) {
6850308b37fSpgoyette rh.lorder = atoi(eq);
6860308b37fSpgoyette return &rh;
6870308b37fSpgoyette }
6880308b37fSpgoyette if (!strcmp("reclen", s)) {
6890308b37fSpgoyette rh.reclen = atoi(eq);
6900308b37fSpgoyette return &rh;
6910308b37fSpgoyette }
6920308b37fSpgoyette if (!strcmp("bval", s)) {
6930308b37fSpgoyette rh.bval = atoi(eq);
6940308b37fSpgoyette return &rh;
6950308b37fSpgoyette }
6960308b37fSpgoyette if (!strcmp("psize", s)) {
6970308b37fSpgoyette rh.psize = atoi(eq);
6980308b37fSpgoyette return &rh;
6990308b37fSpgoyette }
7000308b37fSpgoyette break;
7010308b37fSpgoyette }
7020308b37fSpgoyette errx(1, "%s: unknown structure value", s);
7030308b37fSpgoyette /* NOTREACHED */
7040308b37fSpgoyette }
7050308b37fSpgoyette
7060308b37fSpgoyette static void *
rfile(char * name,size_t * lenp)7070308b37fSpgoyette rfile(char *name, size_t *lenp)
7080308b37fSpgoyette {
7090308b37fSpgoyette struct stat sb;
7100308b37fSpgoyette void *p;
7110308b37fSpgoyette int fd;
7120308b37fSpgoyette char *np;
7130308b37fSpgoyette
7140308b37fSpgoyette for (; isspace((unsigned char)*name); ++name)
7150308b37fSpgoyette continue;
7160308b37fSpgoyette if ((np = strchr(name, '\n')) != NULL)
7170308b37fSpgoyette *np = '\0';
7180308b37fSpgoyette if ((fd = open(name, O_RDONLY, 0)) == -1 || fstat(fd, &sb) == -1)
7190308b37fSpgoyette err(1, "Cannot open `%s'", name);
7200308b37fSpgoyette #ifdef NOT_PORTABLE
7210308b37fSpgoyette if (sb.st_size > (off_t)SIZE_T_MAX) {
7220308b37fSpgoyette errno = E2BIG;
7230308b37fSpgoyette err("Cannot process `%s'", name);
7240308b37fSpgoyette }
7250308b37fSpgoyette #endif
7260308b37fSpgoyette if ((p = malloc((size_t)sb.st_size)) == NULL)
7270308b37fSpgoyette err(1, "Cannot allocate %zu bytes", (size_t)sb.st_size);
7280308b37fSpgoyette if (read(fd, p, (ssize_t)sb.st_size) != (ssize_t)sb.st_size)
7290308b37fSpgoyette err(1, "read failed");
7300308b37fSpgoyette *lenp = (size_t)sb.st_size;
7310308b37fSpgoyette (void)close(fd);
7320308b37fSpgoyette return p;
7330308b37fSpgoyette }
7340308b37fSpgoyette
7350308b37fSpgoyette static void *
xcopy(void * text,size_t len)7360308b37fSpgoyette xcopy(void *text, size_t len)
7370308b37fSpgoyette {
7380308b37fSpgoyette void *p;
7390308b37fSpgoyette
7400308b37fSpgoyette if ((p = malloc(len)) == NULL)
7410308b37fSpgoyette err(1, "Cannot allocate %zu bytes", len);
7420308b37fSpgoyette (void)memmove(p, text, len);
7430308b37fSpgoyette return p;
7440308b37fSpgoyette }
7450308b37fSpgoyette
7460308b37fSpgoyette static void
chkcmd(enum S state)7470308b37fSpgoyette chkcmd(enum S state)
7480308b37fSpgoyette {
7490308b37fSpgoyette if (state != COMMAND)
7500308b37fSpgoyette errx(1, "line %zu: not expecting command", lineno);
7510308b37fSpgoyette }
7520308b37fSpgoyette
7530308b37fSpgoyette static void
chkdata(enum S state)7540308b37fSpgoyette chkdata(enum S state)
7550308b37fSpgoyette {
7560308b37fSpgoyette if (state != DATA)
7570308b37fSpgoyette errx(1, "line %zu: not expecting data", lineno);
7580308b37fSpgoyette }
7590308b37fSpgoyette
7600308b37fSpgoyette static void
chkkey(enum S state)7610308b37fSpgoyette chkkey(enum S state)
7620308b37fSpgoyette {
7630308b37fSpgoyette if (state != KEY)
7640308b37fSpgoyette errx(1, "line %zu: not expecting a key", lineno);
7650308b37fSpgoyette }
7660308b37fSpgoyette
7670308b37fSpgoyette static void
usage(void)7680308b37fSpgoyette usage(void)
7690308b37fSpgoyette {
7700308b37fSpgoyette (void)fprintf(stderr,
7715703b437Schristos "Usage: %s [-lu] [-f file] [-i info] [-o file] [-O file] "
7725703b437Schristos "type script\n", getprogname());
7730308b37fSpgoyette exit(1);
7740308b37fSpgoyette }
775