h_db.c revision 313535
1118611Snjl/*	$NetBSD: h_db.c,v 1.3 2016/09/24 21:18:22 christos Exp $	*/
2118611Snjl
3118611Snjl/*-
4118611Snjl * Copyright (c) 1992, 1993, 1994
5118611Snjl *	The Regents of the University of California.  All rights reserved.
6118611Snjl *
7217365Sjkim * Redistribution and use in source and binary forms, with or without
8245582Sjkim * modification, are permitted provided that the following conditions
9118611Snjl * are met:
10118611Snjl * 1. Redistributions of source code must retain the above copyright
11217365Sjkim *    notice, this list of conditions and the following disclaimer.
12217365Sjkim * 2. Redistributions in binary form must reproduce the above copyright
13217365Sjkim *    notice, this list of conditions and the following disclaimer in the
14217365Sjkim *    documentation and/or other materials provided with the distribution.
15217365Sjkim * 3. Neither the name of the University nor the names of its contributors
16217365Sjkim *    may be used to endorse or promote products derived from this software
17217365Sjkim *    without specific prior written permission.
18217365Sjkim *
19217365Sjkim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20217365Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21217365Sjkim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22217365Sjkim * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23217365Sjkim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24217365Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25118611Snjl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26217365Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27217365Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28217365Sjkim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29118611Snjl * SUCH DAMAGE.
30217365Sjkim */
31217365Sjkim
32217365Sjkim#include <sys/cdefs.h>
33217365Sjkim#ifndef lint
34217365Sjkim__COPYRIGHT("@(#) Copyright (c) 1992, 1993, 1994\
35217365Sjkim	The Regents of the University of California.  All rights reserved.");
36217365Sjkim#endif /* not lint */
37217365Sjkim
38217365Sjkim#ifndef lint
39217365Sjkim#if 0
40217365Sjkimstatic char sccsid[] = "@(#)dbtest.c	8.17 (Berkeley) 9/1/94";
41217365Sjkim#else
42217365Sjkim__RCSID("$NetBSD: h_db.c,v 1.3 2016/09/24 21:18:22 christos Exp $");
43118611Snjl#endif
44118611Snjl#endif /* not lint */
45118611Snjl
46151937Sjkim#include <sys/param.h>
47193529Sjkim#include <sys/stat.h>
48193529Sjkim
49193529Sjkim#include <ctype.h>
50118611Snjl#include <errno.h>
51118611Snjl#include <fcntl.h>
52118611Snjl#include <limits.h>
53118611Snjl#include <stdio.h>
54118611Snjl#include <stdlib.h>
55118611Snjl#include <string.h>
56151937Sjkim#include <stdbool.h>
57118611Snjl#include <unistd.h>
58151937Sjkim#include <err.h>
59151937Sjkim#include <db.h>
60151937Sjkim#include "btree.h"
61151937Sjkim
62151937Sjkimenum S { COMMAND, COMPARE, GET, PUT, REMOVE, SEQ, SEQFLAG, KEY, DATA };
63151937Sjkim
64151937Sjkimstatic void	 compare(DBT *, DBT *);
65151937Sjkimstatic DBTYPE	 dbtype(const char *);
66151937Sjkimstatic void	 dump(DB *, int, int);
67151937Sjkimstatic void	 get(DB *, DBT *);
68151937Sjkimstatic void	 getdata(DB *, DBT *, DBT *);
69151937Sjkimstatic void	 put(DB *, DBT *, DBT *);
70151937Sjkimstatic void	 rem(DB *, DBT *);
71151937Sjkimstatic const char *sflags(int);
72151937Sjkimstatic void	 synk(DB *);
73151937Sjkimstatic void	*rfile(char *, size_t *);
74151937Sjkimstatic void	 seq(DB *, DBT *);
75193529Sjkimstatic u_int	 setflags(char *);
76151937Sjkimstatic void	*setinfo(DBTYPE, char *);
77151937Sjkim#ifdef	__NetBSD__
78151937Sjkimstatic void	 unlinkpg(DB *);
79151937Sjkim#endif
80193529Sjkimstatic void	 usage(void) __attribute__((__noreturn__));
81193529Sjkimstatic void	*xcopy(void *, size_t);
82193529Sjkimstatic void	 chkcmd(enum S);
83193529Sjkimstatic void	 chkdata(enum S);
84193529Sjkimstatic void	 chkkey(enum S);
85151937Sjkim
86193529Sjkim#ifdef STATISTICS
87118611Snjlextern void __bt_stat(DB *);
88118611Snjl#endif
89118611Snjl#ifdef	__NetBSD__
90118611Snjlextern int __bt_relink(BTREE *, PAGE *);
91151937Sjkim#endif
92118611Snjl
93118611Snjlstatic DBTYPE type;			/* Database type. */
94118611Snjlstatic void *infop;			/* Iflags. */
95118611Snjlstatic size_t lineno;			/* Current line in test script. */
96193529Sjkimstatic u_int flags;				/* Current DB flags. */
97118611Snjlstatic int ofd = STDOUT_FILENO;		/* Standard output fd. */
98118611Snjl
99118611Snjlstatic DB *XXdbp;			/* Global for gdb. */
100118611Snjlstatic size_t XXlineno;			/* Fast breakpoint for gdb. */
101118611Snjl
102118611Snjlint
103118611Snjlmain(int argc, char *argv[])
104118611Snjl{
105118611Snjl	extern int optind;
106118611Snjl	extern char *optarg;
107118611Snjl	enum S command = COMMAND, state;
108118611Snjl	DB *dbp;
109118611Snjl	DBT data, key, keydata;
110118611Snjl	size_t len;
111118611Snjl	int ch, oflags, sflag;
112118611Snjl	char *fname, *infoarg, *p, *t, buf[8 * 1024];
113118611Snjl	bool unlink_dbfile;
114118611Snjl
115118611Snjl	infoarg = NULL;
116241973Sjkim	fname = NULL;
117118611Snjl	unlink_dbfile = false;
118118611Snjl	oflags = O_CREAT | O_RDWR;
119193529Sjkim	sflag = 0;
120118611Snjl	while ((ch = getopt(argc, argv, "f:i:lo:s")) != -1)
121118611Snjl		switch (ch) {
122193529Sjkim		case 'f':
123118611Snjl			fname = optarg;
124193529Sjkim			break;
125193529Sjkim		case 'i':
126193529Sjkim			infoarg = optarg;
127193529Sjkim			break;
128193529Sjkim		case 'l':
129118611Snjl			oflags |= DB_LOCK;
130118611Snjl			break;
131118611Snjl		case 'o':
132241973Sjkim			if ((ofd = open(optarg,
133118611Snjl			    O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
134118611Snjl				err(1, "Cannot create `%s'", optarg);
135118611Snjl			break;
136118611Snjl		case 's':
137118611Snjl			sflag = 1;
138118611Snjl			break;
139118611Snjl		case '?':
140151937Sjkim		default:
141118611Snjl			usage();
142118611Snjl		}
143118611Snjl	argc -= optind;
144118611Snjl	argv += optind;
145118611Snjl
146118611Snjl	if (argc != 2)
147118611Snjl		usage();
148118611Snjl
149118611Snjl	/* Set the type. */
150151937Sjkim	type = dbtype(*argv++);
151118611Snjl
152118611Snjl	/* Open the descriptor file. */
153118611Snjl        if (strcmp(*argv, "-") && freopen(*argv, "r", stdin) == NULL)
154118611Snjl	    err(1, "Cannot reopen `%s'", *argv);
155118611Snjl
156118611Snjl	/* Set up the db structure as necessary. */
157118611Snjl	if (infoarg == NULL)
158118611Snjl		infop = NULL;
159118611Snjl	else
160118611Snjl		for (p = strtok(infoarg, ",\t "); p != NULL;
161118611Snjl		    p = strtok(0, ",\t "))
162118611Snjl			if (*p != '\0')
163118611Snjl				infop = setinfo(type, p);
164118611Snjl
165118611Snjl	/*
166118611Snjl	 * Open the DB.  Delete any preexisting copy, you almost never
167118611Snjl	 * want it around, and it often screws up tests.
168118611Snjl	 */
169118611Snjl	if (fname == NULL) {
170118611Snjl		const char *q = getenv("TMPDIR");
171118611Snjl		if (q == NULL)
172118611Snjl			q = "/var/tmp";
173118611Snjl		(void)snprintf(buf, sizeof(buf), "%s/__dbtest", q);
174118611Snjl		fname = buf;
175118611Snjl		(void)unlink(buf);
176118611Snjl		unlink_dbfile = true;
177118611Snjl	} else  if (!sflag)
178118611Snjl		(void)unlink(fname);
179118611Snjl
180250838Sjkim	if ((dbp = dbopen(fname,
181118611Snjl	    oflags, S_IRUSR | S_IWUSR, type, infop)) == NULL)
182250838Sjkim		err(1, "Cannot dbopen `%s'", fname);
183118611Snjl	XXdbp = dbp;
184118611Snjl	if (unlink_dbfile)
185118611Snjl		(void)unlink(fname);
186118611Snjl
187118611Snjl	state = COMMAND;
188118611Snjl	for (lineno = 1;
189118611Snjl	    (p = fgets(buf, sizeof(buf), stdin)) != NULL; ++lineno) {
190118611Snjl		/* Delete the newline, displaying the key/data is easier. */
191118611Snjl		if (ofd == STDOUT_FILENO && (t = strchr(p, '\n')) != NULL)
192118611Snjl			*t = '\0';
193118611Snjl		if ((len = strlen(buf)) == 0 || isspace((unsigned char)*p) ||
194228110Sjkim		    *p == '#')
195118611Snjl			continue;
196118611Snjl
197118611Snjl		/* Convenient gdb break point. */
198118611Snjl		if (XXlineno == lineno)
199151937Sjkim			XXlineno = 1;
200151937Sjkim		switch (*p) {
201151937Sjkim		case 'c':			/* compare */
202151937Sjkim			chkcmd(state);
203151937Sjkim			state = KEY;
204151937Sjkim			command = COMPARE;
205151937Sjkim			break;
206118611Snjl		case 'e':			/* echo */
207118611Snjl			chkcmd(state);
208118611Snjl			/* Don't display the newline, if CR at EOL. */
209118611Snjl			if (p[len - 2] == '\r')
210151937Sjkim				--len;
211151937Sjkim			if (write(ofd, p + 1, len - 1) != (ssize_t)len - 1 ||
212151937Sjkim			    write(ofd, "\n", 1) != 1)
213118611Snjl				err(1, "write failed");
214118611Snjl			break;
215118611Snjl		case 'g':			/* get */
216118611Snjl			chkcmd(state);
217118611Snjl			state = KEY;
218118611Snjl			command = GET;
219151937Sjkim			break;
220151937Sjkim		case 'p':			/* put */
221118611Snjl			chkcmd(state);
222118611Snjl			state = KEY;
223118611Snjl			command = PUT;
224118611Snjl			break;
225151937Sjkim		case 'r':			/* remove */
226118611Snjl			chkcmd(state);
227118611Snjl                        if (flags == R_CURSOR) {
228118611Snjl				rem(dbp, &key);
229228110Sjkim				state = COMMAND;
230118611Snjl                        } else {
231118611Snjl				state = KEY;
232228110Sjkim				command = REMOVE;
233118611Snjl			}
234118611Snjl			break;
235118611Snjl		case 'S':			/* sync */
236118611Snjl			chkcmd(state);
237118611Snjl			synk(dbp);
238118611Snjl			state = COMMAND;
239118611Snjl			break;
240118611Snjl		case 's':			/* seq */
241151937Sjkim			chkcmd(state);
242118611Snjl			if (flags == R_CURSOR) {
243118611Snjl				state = KEY;
244118611Snjl				command = SEQ;
245118611Snjl			} else
246118611Snjl				seq(dbp, &key);
247118611Snjl			break;
248118611Snjl		case 'f':
249193529Sjkim			flags = setflags(p + 1);
250118611Snjl			break;
251118611Snjl		case 'D':			/* data file */
252118611Snjl			chkdata(state);
253118611Snjl			data.data = rfile(p + 1, &data.size);
254118611Snjl			goto ldata;
255151937Sjkim		case 'd':			/* data */
256118611Snjl			chkdata(state);
257118611Snjl			data.data = xcopy(p + 1, len - 1);
258118611Snjl			data.size = len - 1;
259118611Snjlldata:			switch (command) {
260118611Snjl			case COMPARE:
261118611Snjl				compare(&keydata, &data);
262118611Snjl				break;
263118611Snjl			case PUT:
264118611Snjl				put(dbp, &key, &data);
265118611Snjl				break;
266151937Sjkim			default:
267151937Sjkim				errx(1, "line %zu: command doesn't take data",
268118611Snjl				    lineno);
269118611Snjl			}
270151937Sjkim			if (type != DB_RECNO)
271151937Sjkim				free(key.data);
272151937Sjkim			free(data.data);
273118611Snjl			state = COMMAND;
274118611Snjl			break;
275151937Sjkim		case 'K':			/* key file */
276151937Sjkim			chkkey(state);
277151937Sjkim			if (type == DB_RECNO)
278151937Sjkim				errx(1, "line %zu: 'K' not available for recno",
279151937Sjkim				    lineno);
280151937Sjkim			key.data = rfile(p + 1, &key.size);
281151937Sjkim			goto lkey;
282151937Sjkim		case 'k':			/* key */
283118611Snjl			chkkey(state);
284118611Snjl			if (type == DB_RECNO) {
285118611Snjl				static recno_t recno;
286167802Sjkim				recno = atoi(p + 1);
287167802Sjkim				key.data = &recno;
288197104Sjkim				key.size = sizeof(recno);
289167802Sjkim			} else {
290118611Snjl				key.data = xcopy(p + 1, len - 1);
291118611Snjl				key.size = len - 1;
292118611Snjl			}
293118611Snjllkey:			switch (command) {
294118611Snjl			case COMPARE:
295118611Snjl				getdata(dbp, &key, &keydata);
296118611Snjl				state = DATA;
297118611Snjl				break;
298118611Snjl			case GET:
299118611Snjl				get(dbp, &key);
300151937Sjkim				if (type != DB_RECNO)
301151937Sjkim					free(key.data);
302151937Sjkim				state = COMMAND;
303151937Sjkim				break;
304151937Sjkim			case PUT:
305118611Snjl				state = DATA;
306118611Snjl				break;
307118611Snjl			case REMOVE:
308118611Snjl				rem(dbp, &key);
309118611Snjl				if ((type != DB_RECNO) && (flags != R_CURSOR))
310118611Snjl					free(key.data);
311228110Sjkim				state = COMMAND;
312228110Sjkim				break;
313118611Snjl			case SEQ:
314228110Sjkim				seq(dbp, &key);
315228110Sjkim				if ((type != DB_RECNO) && (flags != R_CURSOR))
316118611Snjl					free(key.data);
317151937Sjkim				state = COMMAND;
318228110Sjkim				break;
319118611Snjl			default:
320118611Snjl				errx(1, "line %zu: command doesn't take a key",
321118611Snjl				    lineno);
322118611Snjl			}
323118611Snjl			break;
324118611Snjl		case 'o':
325118611Snjl			dump(dbp, p[1] == 'r', 0);
326118611Snjl			break;
327118611Snjl#ifdef	__NetBSD__
328118611Snjl		case 'O':
329118611Snjl			dump(dbp, p[1] == 'r', 1);
330118611Snjl			break;
331118611Snjl		case 'u':
332118611Snjl			unlinkpg(dbp);
333118611Snjl			break;
334118611Snjl#endif
335193529Sjkim		default:
336118611Snjl			errx(1, "line %zu: %s: unknown command character",
337118611Snjl			    lineno, p);
338118611Snjl		}
339118611Snjl	}
340151937Sjkim#ifdef STATISTICS
341118611Snjl	/*
342118611Snjl	 * -l must be used (DB_LOCK must be set) for this to be
343118611Snjl	 * used, otherwise a page will be locked and it will fail.
344118611Snjl	 */
345118611Snjl	if (type == DB_BTREE && oflags & DB_LOCK)
346118611Snjl		__bt_stat(dbp);
347118611Snjl#endif
348118611Snjl	if ((*dbp->close)(dbp))
349118611Snjl		err(1, "db->close failed");
350118611Snjl	(void)close(ofd);
351118611Snjl	return 0;
352118611Snjl}
353118611Snjl
354118611Snjl#define	NOOVERWRITE	"put failed, would overwrite key\n"
355167802Sjkim
356118611Snjlstatic void
357118611Snjlcompare(DBT *db1, DBT *db2)
358167802Sjkim{
359118611Snjl	size_t len;
360118611Snjl	u_char *p1, *p2;
361118611Snjl
362118611Snjl	if (db1->size != db2->size)
363118611Snjl		printf("compare failed: key->data len %zu != data len %zu\n",
364118611Snjl		    db1->size, db2->size);
365118611Snjl
366118611Snjl	len = MIN(db1->size, db2->size);
367118611Snjl	for (p1 = db1->data, p2 = db2->data; len--;)
368118611Snjl		if (*p1++ != *p2++) {
369118611Snjl			printf("compare failed at offset %lu\n",
370118611Snjl			    (unsigned long)(p1 - (u_char *)db1->data));
371118611Snjl			break;
372118611Snjl		}
373118611Snjl}
374118611Snjl
375118611Snjlstatic void
376118611Snjlget(DB *dbp, DBT *kp)
377118611Snjl{
378118611Snjl	DBT data;
379250838Sjkim
380118611Snjl	switch ((*dbp->get)(dbp, kp, &data, flags)) {
381118611Snjl	case 0:
382118611Snjl		(void)write(ofd, data.data, data.size);
383118611Snjl		if (ofd == STDOUT_FILENO)
384118611Snjl			(void)write(ofd, "\n", 1);
385118611Snjl		break;
386118611Snjl	case -1:
387118611Snjl		err(1, "line %zu: get failed", lineno);
388118611Snjl		/* NOTREACHED */
389118611Snjl	case 1:
390118611Snjl#define	NOSUCHKEY	"get failed, no such key\n"
391118611Snjl		if (ofd != STDOUT_FILENO)
392118611Snjl			(void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
393118611Snjl		else
394118611Snjl			(void)fprintf(stderr, "%zu: %.*s: %s",
395118611Snjl			    lineno, (int)MIN(kp->size, 20),
396118611Snjl			    (const char *)kp->data,
397118611Snjl			    NOSUCHKEY);
398118611Snjl#undef	NOSUCHKEY
399118611Snjl		break;
400118611Snjl	}
401118611Snjl}
402151937Sjkim
403151937Sjkimstatic void
404118611Snjlgetdata(DB *dbp, DBT *kp, DBT *dp)
405167802Sjkim{
406167802Sjkim	switch ((*dbp->get)(dbp, kp, dp, flags)) {
407167802Sjkim	case 0:
408167802Sjkim		return;
409167802Sjkim	case -1:
410167802Sjkim		err(1, "line %zu: getdata failed", lineno);
411167802Sjkim		/* NOTREACHED */
412167802Sjkim	case 1:
413167802Sjkim		errx(1, "line %zu: getdata failed, no such key", lineno);
414167802Sjkim		/* NOTREACHED */
415118611Snjl	}
416118611Snjl}
417118611Snjl
418118611Snjlstatic void
419118611Snjlput(DB *dbp, DBT *kp, DBT *dp)
420118611Snjl{
421118611Snjl	switch ((*dbp->put)(dbp, kp, dp, flags)) {
422118611Snjl	case 0:
423118611Snjl		break;
424118611Snjl	case -1:
425118611Snjl		err(1, "line %zu: put failed", lineno);
426118611Snjl		/* NOTREACHED */
427118611Snjl	case 1:
428118611Snjl		(void)write(ofd, NOOVERWRITE, sizeof(NOOVERWRITE) - 1);
429118611Snjl		break;
430118611Snjl	}
431118611Snjl}
432118611Snjl
433118611Snjlstatic void
434118611Snjlrem(DB *dbp, DBT *kp)
435118611Snjl{
436118611Snjl	switch ((*dbp->del)(dbp, kp, flags)) {
437167802Sjkim	case 0:
438167802Sjkim		break;
439167802Sjkim	case -1:
440167802Sjkim		err(1, "line %zu: rem failed", lineno);
441167802Sjkim		/* NOTREACHED */
442167802Sjkim	case 1:
443167802Sjkim#define	NOSUCHKEY	"rem failed, no such key\n"
444167802Sjkim		if (ofd != STDOUT_FILENO)
445167802Sjkim			(void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
446167802Sjkim		else if (flags != R_CURSOR)
447167802Sjkim			(void)fprintf(stderr, "%zu: %.*s: %s",
448167802Sjkim			    lineno, (int)MIN(kp->size, 20),
449167802Sjkim			    (const char *)kp->data, NOSUCHKEY);
450167802Sjkim		else
451167802Sjkim			(void)fprintf(stderr,
452167802Sjkim			    "%zu: rem of cursor failed\n", lineno);
453167802Sjkim#undef	NOSUCHKEY
454167802Sjkim		break;
455167802Sjkim	}
456118611Snjl}
457118611Snjl
458118611Snjlstatic void
459118611Snjlsynk(DB *dbp)
460167802Sjkim{
461118611Snjl	switch ((*dbp->sync)(dbp, flags)) {
462118611Snjl	case 0:
463202771Sjkim		break;
464118611Snjl	case -1:
465118611Snjl		err(1, "line %zu: synk failed", lineno);
466118611Snjl		/* NOTREACHED */
467118611Snjl	}
468118611Snjl}
469118611Snjl
470118611Snjlstatic void
471118611Snjlseq(DB *dbp, DBT *kp)
472118611Snjl{
473118611Snjl	DBT data;
474118611Snjl
475193529Sjkim	switch (dbp->seq(dbp, kp, &data, flags)) {
476118611Snjl	case 0:
477118611Snjl		(void)write(ofd, data.data, data.size);
478151937Sjkim		if (ofd == STDOUT_FILENO)
479151937Sjkim			(void)write(ofd, "\n", 1);
480118611Snjl		break;
481118611Snjl	case -1:
482118611Snjl		err(1, "line %zu: seq failed", lineno);
483118611Snjl		/* NOTREACHED */
484118611Snjl	case 1:
485118611Snjl#define	NOSUCHKEY	"seq failed, no such key\n"
486151937Sjkim		if (ofd != STDOUT_FILENO)
487151937Sjkim			(void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
488151937Sjkim		else if (flags == R_CURSOR)
489151937Sjkim			(void)fprintf(stderr, "%zu: %.*s: %s",
490254745Sjkim			    lineno, (int)MIN(kp->size, 20),
491254745Sjkim			    (const char *)kp->data, NOSUCHKEY);
492254745Sjkim		else
493254745Sjkim			(void)fprintf(stderr,
494118611Snjl			    "%zu: seq (%s) failed\n", lineno, sflags(flags));
495118611Snjl#undef	NOSUCHKEY
496118611Snjl		break;
497118611Snjl	}
498118611Snjl}
499118611Snjl
500151937Sjkimstatic void
501151937Sjkimdump(DB *dbp, int rev, int recurse)
502151937Sjkim{
503151937Sjkim	DBT key, data;
504118611Snjl	int xflags, nflags;
505118611Snjl
506118611Snjl	if (rev) {
507198237Sjkim		xflags = R_LAST;
508198237Sjkim#ifdef __NetBSD__
509198237Sjkim		nflags = recurse ? R_RPREV : R_PREV;
510202771Sjkim#else
511118611Snjl		nflags = R_PREV;
512118611Snjl#endif
513118611Snjl	} else {
514118611Snjl		xflags = R_FIRST;
515118611Snjl#ifdef __NetBSD__
516118611Snjl		nflags = recurse ? R_RNEXT : R_NEXT;
517118611Snjl#else
518118611Snjl		nflags = R_NEXT;
519118611Snjl#endif
520118611Snjl	}
521118611Snjl	for (;; xflags = nflags)
522118611Snjl		switch (dbp->seq(dbp, &key, &data, xflags)) {
523118611Snjl		case 0:
524118611Snjl			(void)write(ofd, data.data, data.size);
525118611Snjl			if (ofd == STDOUT_FILENO)
526118611Snjl				(void)write(ofd, "\n", 1);
527118611Snjl			break;
528118611Snjl		case 1:
529118611Snjl			goto done;
530151937Sjkim		case -1:
531151937Sjkim			err(1, "line %zu: (dump) seq failed", lineno);
532118611Snjl			/* NOTREACHED */
533118611Snjl		}
534118611Snjldone:	return;
535118611Snjl}
536118611Snjl
537118611Snjl#ifdef __NetBSD__
538118611Snjlvoid
539128212Snjlunlinkpg(DB *dbp)
540118611Snjl{
541118611Snjl	BTREE *t = dbp->internal;
542118611Snjl	PAGE *h = NULL;
543151937Sjkim	pgno_t pg;
544151937Sjkim
545128212Snjl	for (pg = P_ROOT; pg < t->bt_mp->npages;
546151937Sjkim	     mpool_put(t->bt_mp, h, 0), pg++) {
547151937Sjkim		if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
548128212Snjl			break;
549128212Snjl		/* Look for a nonempty leaf page that has both left
550128212Snjl		 * and right siblings. */
551128212Snjl		if (h->prevpg == P_INVALID || h->nextpg == P_INVALID)
552118611Snjl			continue;
553118611Snjl		if (NEXTINDEX(h) == 0)
554118611Snjl			continue;
555118611Snjl		if ((h->flags & (P_BLEAF | P_RLEAF)))
556151937Sjkim			break;
557151937Sjkim	}
558151937Sjkim	if (h == NULL || pg == t->bt_mp->npages) {
559151937Sjkim		errx(1, "%s: no appropriate page found", __func__);
560118611Snjl		return;
561118611Snjl	}
562118611Snjl	if (__bt_relink(t, h) != 0) {
563118611Snjl		perror("unlinkpg");
564118611Snjl		goto cleanup;
565193529Sjkim	}
566118611Snjl	h->prevpg = P_INVALID;
567128212Snjl	h->nextpg = P_INVALID;
568151937Sjkimcleanup:
569151937Sjkim	mpool_put(t->bt_mp, h, MPOOL_DIRTY);
570128212Snjl}
571128212Snjl#endif
572128212Snjl
573128212Snjlstatic u_int
574118611Snjlsetflags(char *s)
575118611Snjl{
576118611Snjl	char *p;
577118611Snjl
578118611Snjl	for (; isspace((unsigned char)*s); ++s);
579118611Snjl	if (*s == '\n' || *s == '\0')
580118611Snjl		return 0;
581118611Snjl	if ((p = strchr(s, '\n')) != NULL)
582118611Snjl		*p = '\0';
583118611Snjl	if (!strcmp(s, "R_CURSOR"))		return R_CURSOR;
584118611Snjl	if (!strcmp(s, "R_FIRST"))		return R_FIRST;
585118611Snjl	if (!strcmp(s, "R_IAFTER")) 		return R_IAFTER;
586118611Snjl	if (!strcmp(s, "R_IBEFORE")) 		return R_IBEFORE;
587118611Snjl	if (!strcmp(s, "R_LAST")) 		return R_LAST;
588118611Snjl	if (!strcmp(s, "R_NEXT")) 		return R_NEXT;
589118611Snjl	if (!strcmp(s, "R_NOOVERWRITE"))	return R_NOOVERWRITE;
590118611Snjl	if (!strcmp(s, "R_PREV"))		return R_PREV;
591118611Snjl	if (!strcmp(s, "R_SETCURSOR"))		return R_SETCURSOR;
592118611Snjl
593118611Snjl	errx(1, "line %zu: %s: unknown flag", lineno, s);
594118611Snjl	/* NOTREACHED */
595118611Snjl}
596193529Sjkim
597193529Sjkimstatic const char *
598151937Sjkimsflags(int xflags)
599151937Sjkim{
600118611Snjl	switch (xflags) {
601118611Snjl	case R_CURSOR:		return "R_CURSOR";
602167802Sjkim	case R_FIRST:		return "R_FIRST";
603118611Snjl	case R_IAFTER:		return "R_IAFTER";
604118611Snjl	case R_IBEFORE:		return "R_IBEFORE";
605118611Snjl	case R_LAST:		return "R_LAST";
606118611Snjl	case R_NEXT:		return "R_NEXT";
607118611Snjl	case R_NOOVERWRITE:	return "R_NOOVERWRITE";
608118611Snjl	case R_PREV:		return "R_PREV";
609118611Snjl	case R_SETCURSOR:	return "R_SETCURSOR";
610118611Snjl	}
611167802Sjkim
612167802Sjkim	return "UNKNOWN!";
613118611Snjl}
614118611Snjl
615118611Snjlstatic DBTYPE
616193529Sjkimdbtype(const char *s)
617193529Sjkim{
618167802Sjkim	if (!strcmp(s, "btree"))
619167802Sjkim		return DB_BTREE;
620167802Sjkim	if (!strcmp(s, "hash"))
621167802Sjkim		return DB_HASH;
622167802Sjkim	if (!strcmp(s, "recno"))
623167802Sjkim		return DB_RECNO;
624167802Sjkim	errx(1, "%s: unknown type (use btree, hash or recno)", s);
625167802Sjkim	/* NOTREACHED */
626167802Sjkim}
627167802Sjkim
628167802Sjkimstatic void *
629167802Sjkimsetinfo(DBTYPE dtype, char *s)
630167802Sjkim{
631167802Sjkim	static BTREEINFO ib;
632167802Sjkim	static HASHINFO ih;
633167802Sjkim	static RECNOINFO rh;
634167802Sjkim	char *eq;
635167802Sjkim
636167802Sjkim	if ((eq = strchr(s, '=')) == NULL)
637167802Sjkim		errx(1, "%s: illegal structure set statement", s);
638118611Snjl	*eq++ = '\0';
639118611Snjl	if (!isdigit((unsigned char)*eq))
640167802Sjkim		errx(1, "%s: structure set statement must be a number", s);
641167802Sjkim
642151937Sjkim	switch (dtype) {
643151937Sjkim	case DB_BTREE:
644202771Sjkim		if (!strcmp("flags", s)) {
645118611Snjl			ib.flags = atoi(eq);
646118611Snjl			return &ib;
647118611Snjl		}
648118611Snjl		if (!strcmp("cachesize", s)) {
649151937Sjkim			ib.cachesize = atoi(eq);
650198237Sjkim			return &ib;
651202771Sjkim		}
652118611Snjl		if (!strcmp("maxkeypage", s)) {
653118611Snjl			ib.maxkeypage = atoi(eq);
654118611Snjl			return &ib;
655167802Sjkim		}
656167802Sjkim		if (!strcmp("minkeypage", s)) {
657167802Sjkim			ib.minkeypage = atoi(eq);
658167802Sjkim			return &ib;
659167802Sjkim		}
660167802Sjkim		if (!strcmp("lorder", s)) {
661167802Sjkim			ib.lorder = atoi(eq);
662167802Sjkim			return &ib;
663118611Snjl		}
664118611Snjl		if (!strcmp("psize", s)) {
665118611Snjl			ib.psize = atoi(eq);
666118611Snjl			return &ib;
667118611Snjl		}
668118611Snjl		break;
669118611Snjl	case DB_HASH:
670151937Sjkim		if (!strcmp("bsize", s)) {
671118611Snjl			ih.bsize = atoi(eq);
672118611Snjl			return &ih;
673118611Snjl		}
674118611Snjl		if (!strcmp("ffactor", s)) {
675118611Snjl			ih.ffactor = atoi(eq);
676118611Snjl			return &ih;
677151937Sjkim		}
678118611Snjl		if (!strcmp("nelem", s)) {
679118611Snjl			ih.nelem = atoi(eq);
680118611Snjl			return &ih;
681118611Snjl		}
682118611Snjl		if (!strcmp("cachesize", s)) {
683151937Sjkim			ih.cachesize = atoi(eq);
684151937Sjkim			return &ih;
685118611Snjl		}
686151937Sjkim		if (!strcmp("lorder", s)) {
687118611Snjl			ih.lorder = atoi(eq);
688118611Snjl			return &ih;
689202771Sjkim		}
690118611Snjl		break;
691118611Snjl	case DB_RECNO:
692118611Snjl		if (!strcmp("flags", s)) {
693118611Snjl			rh.flags = atoi(eq);
694118611Snjl			return &rh;
695193529Sjkim		}
696118611Snjl		if (!strcmp("cachesize", s)) {
697118611Snjl			rh.cachesize = atoi(eq);
698118611Snjl			return &rh;
699118611Snjl		}
700118611Snjl		if (!strcmp("lorder", s)) {
701193529Sjkim			rh.lorder = atoi(eq);
702193529Sjkim			return &rh;
703193529Sjkim		}
704193529Sjkim		if (!strcmp("reclen", s)) {
705193529Sjkim			rh.reclen = atoi(eq);
706193529Sjkim			return &rh;
707193529Sjkim		}
708193529Sjkim		if (!strcmp("bval", s)) {
709193529Sjkim			rh.bval = atoi(eq);
710193529Sjkim			return &rh;
711193529Sjkim		}
712193529Sjkim		if (!strcmp("psize", s)) {
713193529Sjkim			rh.psize = atoi(eq);
714193529Sjkim			return &rh;
715193529Sjkim		}
716193529Sjkim		break;
717193529Sjkim	}
718193529Sjkim	errx(1, "%s: unknown structure value", s);
719193529Sjkim	/* NOTREACHED */
720193529Sjkim}
721193529Sjkim
722193529Sjkimstatic void *
723193529Sjkimrfile(char *name, size_t *lenp)
724193529Sjkim{
725193529Sjkim	struct stat sb;
726193529Sjkim	void *p;
727193529Sjkim	int fd;
728193529Sjkim	char *np;
729193529Sjkim
730193529Sjkim	for (; isspace((unsigned char)*name); ++name)
731193529Sjkim		continue;
732193529Sjkim	if ((np = strchr(name, '\n')) != NULL)
733193529Sjkim		*np = '\0';
734193529Sjkim	if ((fd = open(name, O_RDONLY, 0)) == -1 || fstat(fd, &sb) == -1)
735193529Sjkim		err(1, "Cannot open `%s'", name);
736193529Sjkim#ifdef NOT_PORTABLE
737193529Sjkim	if (sb.st_size > (off_t)SIZE_T_MAX) {
738193529Sjkim		errno = E2BIG;
739193529Sjkim		err("Cannot process `%s'", name);
740193529Sjkim	}
741193529Sjkim#endif
742193529Sjkim	if ((p = malloc((size_t)sb.st_size)) == NULL)
743193529Sjkim		err(1, "Cannot allocate %zu bytes", (size_t)sb.st_size);
744193529Sjkim	if (read(fd, p, (ssize_t)sb.st_size) != (ssize_t)sb.st_size)
745193529Sjkim		err(1, "read failed");
746193529Sjkim	*lenp = (size_t)sb.st_size;
747193529Sjkim	(void)close(fd);
748193529Sjkim	return p;
749193529Sjkim}
750193529Sjkim
751193529Sjkimstatic void *
752193529Sjkimxcopy(void *text, size_t len)
753193529Sjkim{
754193529Sjkim	void *p;
755193529Sjkim
756193529Sjkim	if ((p = malloc(len)) == NULL)
757193529Sjkim		err(1, "Cannot allocate %zu bytes", len);
758193529Sjkim	(void)memmove(p, text, len);
759193529Sjkim	return p;
760193529Sjkim}
761193529Sjkim
762193529Sjkimstatic void
763193529Sjkimchkcmd(enum S state)
764193529Sjkim{
765193529Sjkim	if (state != COMMAND)
766193529Sjkim		errx(1, "line %zu: not expecting command", lineno);
767193529Sjkim}
768193529Sjkim
769193529Sjkimstatic void
770193529Sjkimchkdata(enum S state)
771193529Sjkim{
772193529Sjkim	if (state != DATA)
773193529Sjkim		errx(1, "line %zu: not expecting data", lineno);
774193529Sjkim}
775193529Sjkim
776193529Sjkimstatic void
777193529Sjkimchkkey(enum S state)
778193529Sjkim{
779193529Sjkim	if (state != KEY)
780193529Sjkim		errx(1, "line %zu: not expecting a key", lineno);
781193529Sjkim}
782193529Sjkim
783193529Sjkimstatic void
784193529Sjkimusage(void)
785193529Sjkim{
786193529Sjkim	(void)fprintf(stderr,
787193529Sjkim#ifdef __NetBSD__
788193529Sjkim	    "Usage: %s [-lu] [-f file] [-i info] [-o file] [-O file] "
789193529Sjkim#else
790193529Sjkim	    "Usage: %s [-l] [-f file] [-i info] [-o file] "
791193529Sjkim#endif
792193529Sjkim		"type script\n", getprogname());
793193529Sjkim	exit(1);
794193529Sjkim}
795193529Sjkim