xref: /netbsd-src/tests/lib/libc/db/h_db.c (revision 2b55b3112e53514454685c365504d5019ffd1007)
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