1272343Sngie/*	$NetBSD: h_db.c,v 1.1 2011/01/07 15:05:58 pgoyette Exp $	*/
2272343Sngie
3272343Sngie/*-
4272343Sngie * Copyright (c) 1992, 1993, 1994
5272343Sngie *	The Regents of the University of California.  All rights reserved.
6272343Sngie *
7272343Sngie * Redistribution and use in source and binary forms, with or without
8272343Sngie * modification, are permitted provided that the following conditions
9272343Sngie * are met:
10272343Sngie * 1. Redistributions of source code must retain the above copyright
11272343Sngie *    notice, this list of conditions and the following disclaimer.
12272343Sngie * 2. Redistributions in binary form must reproduce the above copyright
13272343Sngie *    notice, this list of conditions and the following disclaimer in the
14272343Sngie *    documentation and/or other materials provided with the distribution.
15272343Sngie * 3. Neither the name of the University nor the names of its contributors
16272343Sngie *    may be used to endorse or promote products derived from this software
17272343Sngie *    without specific prior written permission.
18272343Sngie *
19272343Sngie * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20272343Sngie * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21272343Sngie * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22272343Sngie * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23272343Sngie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24272343Sngie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25272343Sngie * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26272343Sngie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27272343Sngie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28272343Sngie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29272343Sngie * SUCH DAMAGE.
30272343Sngie */
31272343Sngie
32272343Sngie#include <sys/cdefs.h>
33272343Sngie#ifndef lint
34272343Sngie__COPYRIGHT("@(#) Copyright (c) 1992, 1993, 1994\
35272343Sngie	The Regents of the University of California.  All rights reserved.");
36272343Sngie#endif /* not lint */
37272343Sngie
38272343Sngie#ifndef lint
39272343Sngie#if 0
40272343Sngiestatic char sccsid[] = "@(#)dbtest.c	8.17 (Berkeley) 9/1/94";
41272343Sngie#else
42272343Sngie__RCSID("$NetBSD: h_db.c,v 1.1 2011/01/07 15:05:58 pgoyette Exp $");
43272343Sngie#endif
44272343Sngie#endif /* not lint */
45272343Sngie
46272343Sngie#include <sys/param.h>
47272343Sngie#include <sys/stat.h>
48272343Sngie
49272343Sngie#include <ctype.h>
50272343Sngie#include <errno.h>
51272343Sngie#include <fcntl.h>
52272343Sngie#include <limits.h>
53272343Sngie#include <stdio.h>
54272343Sngie#include <stdlib.h>
55272343Sngie#include <string.h>
56272343Sngie#include <stdbool.h>
57272343Sngie#include <unistd.h>
58272343Sngie#include <err.h>
59272343Sngie#include <db.h>
60272343Sngie
61272343Sngieenum S { COMMAND, COMPARE, GET, PUT, REMOVE, SEQ, SEQFLAG, KEY, DATA };
62272343Sngie
63272343Sngiestatic void	 compare(DBT *, DBT *);
64272343Sngiestatic DBTYPE	 dbtype(const char *);
65272343Sngiestatic void	 dump(DB *, int);
66272343Sngiestatic void	 get(DB *, DBT *);
67272343Sngiestatic void	 getdata(DB *, DBT *, DBT *);
68272343Sngiestatic void	 put(DB *, DBT *, DBT *);
69272343Sngiestatic void	 rem(DB *, DBT *);
70272343Sngiestatic const char *sflags(int);
71272343Sngiestatic void	 synk(DB *);
72272343Sngiestatic void	*rfile(char *, size_t *);
73272343Sngiestatic void	 seq(DB *, DBT *);
74272343Sngiestatic u_int	 setflags(char *);
75272343Sngiestatic void	*setinfo(DBTYPE, char *);
76272343Sngiestatic void	 usage(void) __attribute__((__noreturn__));
77272343Sngiestatic void	*xcopy(void *, size_t);
78272343Sngiestatic void	 chkcmd(enum S);
79272343Sngiestatic void	 chkdata(enum S);
80272343Sngiestatic void	 chkkey(enum S);
81272343Sngie
82272343Sngie#ifdef STATISTICS
83272343Sngieextern void __bt_stat(DB *);
84272343Sngie#endif
85272343Sngie
86272343Sngiestatic DBTYPE type;			/* Database type. */
87272343Sngiestatic void *infop;			/* Iflags. */
88272343Sngiestatic size_t lineno;			/* Current line in test script. */
89272343Sngiestatic u_int flags;				/* Current DB flags. */
90272343Sngiestatic int ofd = STDOUT_FILENO;		/* Standard output fd. */
91272343Sngie
92272343Sngiestatic DB *XXdbp;			/* Global for gdb. */
93272343Sngiestatic size_t XXlineno;			/* Fast breakpoint for gdb. */
94272343Sngie
95272343Sngieint
96272343Sngiemain(int argc, char *argv[])
97272343Sngie{
98272343Sngie	extern int optind;
99272343Sngie	extern char *optarg;
100272343Sngie	enum S command = COMMAND, state;
101272343Sngie	DB *dbp;
102272343Sngie	DBT data, key, keydata;
103272343Sngie	size_t len;
104272343Sngie	int ch, oflags, sflag;
105272343Sngie	char *fname, *infoarg, *p, *t, buf[8 * 1024];
106272343Sngie	bool unlink_dbfile;
107272343Sngie
108272343Sngie	infoarg = NULL;
109272343Sngie	fname = NULL;
110272343Sngie	unlink_dbfile = false;
111272343Sngie	oflags = O_CREAT | O_RDWR;
112272343Sngie	sflag = 0;
113272343Sngie	while ((ch = getopt(argc, argv, "f:i:lo:s")) != -1)
114272343Sngie		switch (ch) {
115272343Sngie		case 'f':
116272343Sngie			fname = optarg;
117272343Sngie			break;
118272343Sngie		case 'i':
119272343Sngie			infoarg = optarg;
120272343Sngie			break;
121272343Sngie		case 'l':
122272343Sngie			oflags |= DB_LOCK;
123272343Sngie			break;
124272343Sngie		case 'o':
125272343Sngie			if ((ofd = open(optarg,
126272343Sngie			    O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
127272343Sngie				err(1, "Cannot create `%s'", optarg);
128272343Sngie			break;
129272343Sngie		case 's':
130272343Sngie			sflag = 1;
131272343Sngie			break;
132272343Sngie		case '?':
133272343Sngie		default:
134272343Sngie			usage();
135272343Sngie		}
136272343Sngie	argc -= optind;
137272343Sngie	argv += optind;
138272343Sngie
139272343Sngie	if (argc != 2)
140272343Sngie		usage();
141272343Sngie
142272343Sngie	/* Set the type. */
143272343Sngie	type = dbtype(*argv++);
144272343Sngie
145272343Sngie	/* Open the descriptor file. */
146272343Sngie        if (strcmp(*argv, "-") && freopen(*argv, "r", stdin) == NULL)
147272343Sngie	    err(1, "Cannot reopen `%s'", *argv);
148272343Sngie
149272343Sngie	/* Set up the db structure as necessary. */
150272343Sngie	if (infoarg == NULL)
151272343Sngie		infop = NULL;
152272343Sngie	else
153272343Sngie		for (p = strtok(infoarg, ",\t "); p != NULL;
154272343Sngie		    p = strtok(0, ",\t "))
155272343Sngie			if (*p != '\0')
156272343Sngie				infop = setinfo(type, p);
157272343Sngie
158272343Sngie	/*
159272343Sngie	 * Open the DB.  Delete any preexisting copy, you almost never
160272343Sngie	 * want it around, and it often screws up tests.
161272343Sngie	 */
162272343Sngie	if (fname == NULL) {
163272343Sngie		const char *q = getenv("TMPDIR");
164272343Sngie		if (q == NULL)
165272343Sngie			q = "/var/tmp";
166272343Sngie		(void)snprintf(buf, sizeof(buf), "%s/__dbtest", q);
167272343Sngie		fname = buf;
168272343Sngie		(void)unlink(buf);
169272343Sngie		unlink_dbfile = true;
170272343Sngie	} else  if (!sflag)
171272343Sngie		(void)unlink(fname);
172272343Sngie
173272343Sngie	if ((dbp = dbopen(fname,
174272343Sngie	    oflags, S_IRUSR | S_IWUSR, type, infop)) == NULL)
175272343Sngie		err(1, "Cannot dbopen `%s'", fname);
176272343Sngie	XXdbp = dbp;
177272343Sngie	if (unlink_dbfile)
178272343Sngie		(void)unlink(fname);
179272343Sngie
180272343Sngie	state = COMMAND;
181272343Sngie	for (lineno = 1;
182272343Sngie	    (p = fgets(buf, sizeof(buf), stdin)) != NULL; ++lineno) {
183272343Sngie		/* Delete the newline, displaying the key/data is easier. */
184272343Sngie		if (ofd == STDOUT_FILENO && (t = strchr(p, '\n')) != NULL)
185272343Sngie			*t = '\0';
186272343Sngie		if ((len = strlen(buf)) == 0 || isspace((unsigned char)*p) ||
187272343Sngie		    *p == '#')
188272343Sngie			continue;
189272343Sngie
190272343Sngie		/* Convenient gdb break point. */
191272343Sngie		if (XXlineno == lineno)
192272343Sngie			XXlineno = 1;
193272343Sngie		switch (*p) {
194272343Sngie		case 'c':			/* compare */
195272343Sngie			chkcmd(state);
196272343Sngie			state = KEY;
197272343Sngie			command = COMPARE;
198272343Sngie			break;
199272343Sngie		case 'e':			/* echo */
200272343Sngie			chkcmd(state);
201272343Sngie			/* Don't display the newline, if CR at EOL. */
202272343Sngie			if (p[len - 2] == '\r')
203272343Sngie				--len;
204272343Sngie			if (write(ofd, p + 1, len - 1) != (ssize_t)len - 1 ||
205272343Sngie			    write(ofd, "\n", 1) != 1)
206272343Sngie				err(1, "write failed");
207272343Sngie			break;
208272343Sngie		case 'g':			/* get */
209272343Sngie			chkcmd(state);
210272343Sngie			state = KEY;
211272343Sngie			command = GET;
212272343Sngie			break;
213272343Sngie		case 'p':			/* put */
214272343Sngie			chkcmd(state);
215272343Sngie			state = KEY;
216272343Sngie			command = PUT;
217272343Sngie			break;
218272343Sngie		case 'r':			/* remove */
219272343Sngie			chkcmd(state);
220272343Sngie                        if (flags == R_CURSOR) {
221272343Sngie				rem(dbp, &key);
222272343Sngie				state = COMMAND;
223272343Sngie                        } else {
224272343Sngie				state = KEY;
225272343Sngie				command = REMOVE;
226272343Sngie			}
227272343Sngie			break;
228272343Sngie		case 'S':			/* sync */
229272343Sngie			chkcmd(state);
230272343Sngie			synk(dbp);
231272343Sngie			state = COMMAND;
232272343Sngie			break;
233272343Sngie		case 's':			/* seq */
234272343Sngie			chkcmd(state);
235272343Sngie			if (flags == R_CURSOR) {
236272343Sngie				state = KEY;
237272343Sngie				command = SEQ;
238272343Sngie			} else
239272343Sngie				seq(dbp, &key);
240272343Sngie			break;
241272343Sngie		case 'f':
242272343Sngie			flags = setflags(p + 1);
243272343Sngie			break;
244272343Sngie		case 'D':			/* data file */
245272343Sngie			chkdata(state);
246272343Sngie			data.data = rfile(p + 1, &data.size);
247272343Sngie			goto ldata;
248272343Sngie		case 'd':			/* data */
249272343Sngie			chkdata(state);
250272343Sngie			data.data = xcopy(p + 1, len - 1);
251272343Sngie			data.size = len - 1;
252272343Sngieldata:			switch (command) {
253272343Sngie			case COMPARE:
254272343Sngie				compare(&keydata, &data);
255272343Sngie				break;
256272343Sngie			case PUT:
257272343Sngie				put(dbp, &key, &data);
258272343Sngie				break;
259272343Sngie			default:
260272343Sngie				errx(1, "line %zu: command doesn't take data",
261272343Sngie				    lineno);
262272343Sngie			}
263272343Sngie			if (type != DB_RECNO)
264272343Sngie				free(key.data);
265272343Sngie			free(data.data);
266272343Sngie			state = COMMAND;
267272343Sngie			break;
268272343Sngie		case 'K':			/* key file */
269272343Sngie			chkkey(state);
270272343Sngie			if (type == DB_RECNO)
271272343Sngie				errx(1, "line %zu: 'K' not available for recno",
272272343Sngie				    lineno);
273272343Sngie			key.data = rfile(p + 1, &key.size);
274272343Sngie			goto lkey;
275272343Sngie		case 'k':			/* key */
276272343Sngie			chkkey(state);
277272343Sngie			if (type == DB_RECNO) {
278272343Sngie				static recno_t recno;
279272343Sngie				recno = atoi(p + 1);
280272343Sngie				key.data = &recno;
281272343Sngie				key.size = sizeof(recno);
282272343Sngie			} else {
283272343Sngie				key.data = xcopy(p + 1, len - 1);
284272343Sngie				key.size = len - 1;
285272343Sngie			}
286272343Sngielkey:			switch (command) {
287272343Sngie			case COMPARE:
288272343Sngie				getdata(dbp, &key, &keydata);
289272343Sngie				state = DATA;
290272343Sngie				break;
291272343Sngie			case GET:
292272343Sngie				get(dbp, &key);
293272343Sngie				if (type != DB_RECNO)
294272343Sngie					free(key.data);
295272343Sngie				state = COMMAND;
296272343Sngie				break;
297272343Sngie			case PUT:
298272343Sngie				state = DATA;
299272343Sngie				break;
300272343Sngie			case REMOVE:
301272343Sngie				rem(dbp, &key);
302272343Sngie				if ((type != DB_RECNO) && (flags != R_CURSOR))
303272343Sngie					free(key.data);
304272343Sngie				state = COMMAND;
305272343Sngie				break;
306272343Sngie			case SEQ:
307272343Sngie				seq(dbp, &key);
308272343Sngie				if ((type != DB_RECNO) && (flags != R_CURSOR))
309272343Sngie					free(key.data);
310272343Sngie				state = COMMAND;
311272343Sngie				break;
312272343Sngie			default:
313272343Sngie				errx(1, "line %zu: command doesn't take a key",
314272343Sngie				    lineno);
315272343Sngie			}
316272343Sngie			break;
317272343Sngie		case 'o':
318272343Sngie			dump(dbp, p[1] == 'r');
319272343Sngie			break;
320272343Sngie		default:
321272343Sngie			errx(1, "line %zu: %s: unknown command character",
322272343Sngie			    lineno, p);
323272343Sngie		}
324272343Sngie	}
325272343Sngie#ifdef STATISTICS
326272343Sngie	/*
327272343Sngie	 * -l must be used (DB_LOCK must be set) for this to be
328272343Sngie	 * used, otherwise a page will be locked and it will fail.
329272343Sngie	 */
330272343Sngie	if (type == DB_BTREE && oflags & DB_LOCK)
331272343Sngie		__bt_stat(dbp);
332272343Sngie#endif
333272343Sngie	if ((*dbp->close)(dbp))
334272343Sngie		err(1, "db->close failed");
335272343Sngie	(void)close(ofd);
336272343Sngie	return 0;
337272343Sngie}
338272343Sngie
339272343Sngie#define	NOOVERWRITE	"put failed, would overwrite key\n"
340272343Sngie
341272343Sngiestatic void
342272343Sngiecompare(DBT *db1, DBT *db2)
343272343Sngie{
344272343Sngie	size_t len;
345272343Sngie	u_char *p1, *p2;
346272343Sngie
347272343Sngie	if (db1->size != db2->size)
348272343Sngie		printf("compare failed: key->data len %zu != data len %zu\n",
349272343Sngie		    db1->size, db2->size);
350272343Sngie
351272343Sngie	len = MIN(db1->size, db2->size);
352272343Sngie	for (p1 = db1->data, p2 = db2->data; len--;)
353272343Sngie		if (*p1++ != *p2++) {
354272343Sngie			printf("compare failed at offset %lu\n",
355272343Sngie			    (unsigned long)(p1 - (u_char *)db1->data));
356272343Sngie			break;
357272343Sngie		}
358272343Sngie}
359272343Sngie
360272343Sngiestatic void
361272343Sngieget(DB *dbp, DBT *kp)
362272343Sngie{
363272343Sngie	DBT data;
364272343Sngie
365272343Sngie	switch ((*dbp->get)(dbp, kp, &data, flags)) {
366272343Sngie	case 0:
367272343Sngie		(void)write(ofd, data.data, data.size);
368272343Sngie		if (ofd == STDOUT_FILENO)
369272343Sngie			(void)write(ofd, "\n", 1);
370272343Sngie		break;
371272343Sngie	case -1:
372272343Sngie		err(1, "line %zu: get failed", lineno);
373272343Sngie		/* NOTREACHED */
374272343Sngie	case 1:
375272343Sngie#define	NOSUCHKEY	"get failed, no such key\n"
376272343Sngie		if (ofd != STDOUT_FILENO)
377272343Sngie			(void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
378272343Sngie		else
379272343Sngie			(void)fprintf(stderr, "%zu: %.*s: %s",
380272343Sngie			    lineno, (int)MIN(kp->size, 20),
381272343Sngie			    (const char *)kp->data,
382272343Sngie			    NOSUCHKEY);
383272343Sngie#undef	NOSUCHKEY
384272343Sngie		break;
385272343Sngie	}
386272343Sngie}
387272343Sngie
388272343Sngiestatic void
389272343Sngiegetdata(DB *dbp, DBT *kp, DBT *dp)
390272343Sngie{
391272343Sngie	switch ((*dbp->get)(dbp, kp, dp, flags)) {
392272343Sngie	case 0:
393272343Sngie		return;
394272343Sngie	case -1:
395272343Sngie		err(1, "line %zu: getdata failed", lineno);
396272343Sngie		/* NOTREACHED */
397272343Sngie	case 1:
398272343Sngie		errx(1, "line %zu: getdata failed, no such key", lineno);
399272343Sngie		/* NOTREACHED */
400272343Sngie	}
401272343Sngie}
402272343Sngie
403272343Sngiestatic void
404272343Sngieput(DB *dbp, DBT *kp, DBT *dp)
405272343Sngie{
406272343Sngie	switch ((*dbp->put)(dbp, kp, dp, flags)) {
407272343Sngie	case 0:
408272343Sngie		break;
409272343Sngie	case -1:
410272343Sngie		err(1, "line %zu: put failed", lineno);
411272343Sngie		/* NOTREACHED */
412272343Sngie	case 1:
413272343Sngie		(void)write(ofd, NOOVERWRITE, sizeof(NOOVERWRITE) - 1);
414272343Sngie		break;
415272343Sngie	}
416272343Sngie}
417272343Sngie
418272343Sngiestatic void
419272343Sngierem(DB *dbp, DBT *kp)
420272343Sngie{
421272343Sngie	switch ((*dbp->del)(dbp, kp, flags)) {
422272343Sngie	case 0:
423272343Sngie		break;
424272343Sngie	case -1:
425272343Sngie		err(1, "line %zu: rem failed", lineno);
426272343Sngie		/* NOTREACHED */
427272343Sngie	case 1:
428272343Sngie#define	NOSUCHKEY	"rem failed, no such key\n"
429272343Sngie		if (ofd != STDOUT_FILENO)
430272343Sngie			(void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
431272343Sngie		else if (flags != R_CURSOR)
432272343Sngie			(void)fprintf(stderr, "%zu: %.*s: %s",
433272343Sngie			    lineno, (int)MIN(kp->size, 20),
434272343Sngie			    (const char *)kp->data, NOSUCHKEY);
435272343Sngie		else
436272343Sngie			(void)fprintf(stderr,
437272343Sngie			    "%zu: rem of cursor failed\n", lineno);
438272343Sngie#undef	NOSUCHKEY
439272343Sngie		break;
440272343Sngie	}
441272343Sngie}
442272343Sngie
443272343Sngiestatic void
444272343Sngiesynk(DB *dbp)
445272343Sngie{
446272343Sngie	switch ((*dbp->sync)(dbp, flags)) {
447272343Sngie	case 0:
448272343Sngie		break;
449272343Sngie	case -1:
450272343Sngie		err(1, "line %zu: synk failed", lineno);
451272343Sngie		/* NOTREACHED */
452272343Sngie	}
453272343Sngie}
454272343Sngie
455272343Sngiestatic void
456272343Sngieseq(DB *dbp, DBT *kp)
457272343Sngie{
458272343Sngie	DBT data;
459272343Sngie
460272343Sngie	switch (dbp->seq(dbp, kp, &data, flags)) {
461272343Sngie	case 0:
462272343Sngie		(void)write(ofd, data.data, data.size);
463272343Sngie		if (ofd == STDOUT_FILENO)
464272343Sngie			(void)write(ofd, "\n", 1);
465272343Sngie		break;
466272343Sngie	case -1:
467272343Sngie		err(1, "line %zu: seq failed", lineno);
468272343Sngie		/* NOTREACHED */
469272343Sngie	case 1:
470272343Sngie#define	NOSUCHKEY	"seq failed, no such key\n"
471272343Sngie		if (ofd != STDOUT_FILENO)
472272343Sngie			(void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
473272343Sngie		else if (flags == R_CURSOR)
474272343Sngie			(void)fprintf(stderr, "%zu: %.*s: %s",
475272343Sngie			    lineno, (int)MIN(kp->size, 20),
476272343Sngie			    (const char *)kp->data, NOSUCHKEY);
477272343Sngie		else
478272343Sngie			(void)fprintf(stderr,
479272343Sngie			    "%zu: seq (%s) failed\n", lineno, sflags(flags));
480272343Sngie#undef	NOSUCHKEY
481272343Sngie		break;
482272343Sngie	}
483272343Sngie}
484272343Sngie
485272343Sngiestatic void
486272343Sngiedump(DB *dbp, int rev)
487272343Sngie{
488272343Sngie	DBT key, data;
489272343Sngie	int xflags, nflags;
490272343Sngie
491272343Sngie	if (rev) {
492272343Sngie		xflags = R_LAST;
493272343Sngie		nflags = R_PREV;
494272343Sngie	} else {
495272343Sngie		xflags = R_FIRST;
496272343Sngie		nflags = R_NEXT;
497272343Sngie	}
498272343Sngie	for (;; xflags = nflags)
499272343Sngie		switch (dbp->seq(dbp, &key, &data, xflags)) {
500272343Sngie		case 0:
501272343Sngie			(void)write(ofd, data.data, data.size);
502272343Sngie			if (ofd == STDOUT_FILENO)
503272343Sngie				(void)write(ofd, "\n", 1);
504272343Sngie			break;
505272343Sngie		case 1:
506272343Sngie			goto done;
507272343Sngie		case -1:
508272343Sngie			err(1, "line %zu: (dump) seq failed", lineno);
509272343Sngie			/* NOTREACHED */
510272343Sngie		}
511272343Sngiedone:	return;
512272343Sngie}
513272343Sngie
514272343Sngiestatic u_int
515272343Sngiesetflags(char *s)
516272343Sngie{
517272343Sngie	char *p;
518272343Sngie
519272343Sngie	for (; isspace((unsigned char)*s); ++s);
520272343Sngie	if (*s == '\n' || *s == '\0')
521272343Sngie		return 0;
522272343Sngie	if ((p = strchr(s, '\n')) != NULL)
523272343Sngie		*p = '\0';
524272343Sngie	if (!strcmp(s, "R_CURSOR"))		return R_CURSOR;
525272343Sngie	if (!strcmp(s, "R_FIRST"))		return R_FIRST;
526272343Sngie	if (!strcmp(s, "R_IAFTER")) 		return R_IAFTER;
527272343Sngie	if (!strcmp(s, "R_IBEFORE")) 		return R_IBEFORE;
528272343Sngie	if (!strcmp(s, "R_LAST")) 		return R_LAST;
529272343Sngie	if (!strcmp(s, "R_NEXT")) 		return R_NEXT;
530272343Sngie	if (!strcmp(s, "R_NOOVERWRITE"))	return R_NOOVERWRITE;
531272343Sngie	if (!strcmp(s, "R_PREV"))		return R_PREV;
532272343Sngie	if (!strcmp(s, "R_SETCURSOR"))		return R_SETCURSOR;
533272343Sngie
534272343Sngie	errx(1, "line %zu: %s: unknown flag", lineno, s);
535272343Sngie	/* NOTREACHED */
536272343Sngie}
537272343Sngie
538272343Sngiestatic const char *
539272343Sngiesflags(int xflags)
540272343Sngie{
541272343Sngie	switch (xflags) {
542272343Sngie	case R_CURSOR:		return "R_CURSOR";
543272343Sngie	case R_FIRST:		return "R_FIRST";
544272343Sngie	case R_IAFTER:		return "R_IAFTER";
545272343Sngie	case R_IBEFORE:		return "R_IBEFORE";
546272343Sngie	case R_LAST:		return "R_LAST";
547272343Sngie	case R_NEXT:		return "R_NEXT";
548272343Sngie	case R_NOOVERWRITE:	return "R_NOOVERWRITE";
549272343Sngie	case R_PREV:		return "R_PREV";
550272343Sngie	case R_SETCURSOR:	return "R_SETCURSOR";
551272343Sngie	}
552272343Sngie
553272343Sngie	return "UNKNOWN!";
554272343Sngie}
555272343Sngie
556272343Sngiestatic DBTYPE
557272343Sngiedbtype(const char *s)
558272343Sngie{
559272343Sngie	if (!strcmp(s, "btree"))
560272343Sngie		return DB_BTREE;
561272343Sngie	if (!strcmp(s, "hash"))
562272343Sngie		return DB_HASH;
563272343Sngie	if (!strcmp(s, "recno"))
564272343Sngie		return DB_RECNO;
565272343Sngie	errx(1, "%s: unknown type (use btree, hash or recno)", s);
566272343Sngie	/* NOTREACHED */
567272343Sngie}
568272343Sngie
569272343Sngiestatic void *
570272343Sngiesetinfo(DBTYPE dtype, char *s)
571272343Sngie{
572272343Sngie	static BTREEINFO ib;
573272343Sngie	static HASHINFO ih;
574272343Sngie	static RECNOINFO rh;
575272343Sngie	char *eq;
576272343Sngie
577272343Sngie	if ((eq = strchr(s, '=')) == NULL)
578272343Sngie		errx(1, "%s: illegal structure set statement", s);
579272343Sngie	*eq++ = '\0';
580272343Sngie	if (!isdigit((unsigned char)*eq))
581272343Sngie		errx(1, "%s: structure set statement must be a number", s);
582272343Sngie
583272343Sngie	switch (dtype) {
584272343Sngie	case DB_BTREE:
585272343Sngie		if (!strcmp("flags", s)) {
586272343Sngie			ib.flags = atoi(eq);
587272343Sngie			return &ib;
588272343Sngie		}
589272343Sngie		if (!strcmp("cachesize", s)) {
590272343Sngie			ib.cachesize = atoi(eq);
591272343Sngie			return &ib;
592272343Sngie		}
593272343Sngie		if (!strcmp("maxkeypage", s)) {
594272343Sngie			ib.maxkeypage = atoi(eq);
595272343Sngie			return &ib;
596272343Sngie		}
597272343Sngie		if (!strcmp("minkeypage", s)) {
598272343Sngie			ib.minkeypage = atoi(eq);
599272343Sngie			return &ib;
600272343Sngie		}
601272343Sngie		if (!strcmp("lorder", s)) {
602272343Sngie			ib.lorder = atoi(eq);
603272343Sngie			return &ib;
604272343Sngie		}
605272343Sngie		if (!strcmp("psize", s)) {
606272343Sngie			ib.psize = atoi(eq);
607272343Sngie			return &ib;
608272343Sngie		}
609272343Sngie		break;
610272343Sngie	case DB_HASH:
611272343Sngie		if (!strcmp("bsize", s)) {
612272343Sngie			ih.bsize = atoi(eq);
613272343Sngie			return &ih;
614272343Sngie		}
615272343Sngie		if (!strcmp("ffactor", s)) {
616272343Sngie			ih.ffactor = atoi(eq);
617272343Sngie			return &ih;
618272343Sngie		}
619272343Sngie		if (!strcmp("nelem", s)) {
620272343Sngie			ih.nelem = atoi(eq);
621272343Sngie			return &ih;
622272343Sngie		}
623272343Sngie		if (!strcmp("cachesize", s)) {
624272343Sngie			ih.cachesize = atoi(eq);
625272343Sngie			return &ih;
626272343Sngie		}
627272343Sngie		if (!strcmp("lorder", s)) {
628272343Sngie			ih.lorder = atoi(eq);
629272343Sngie			return &ih;
630272343Sngie		}
631272343Sngie		break;
632272343Sngie	case DB_RECNO:
633272343Sngie		if (!strcmp("flags", s)) {
634272343Sngie			rh.flags = atoi(eq);
635272343Sngie			return &rh;
636272343Sngie		}
637272343Sngie		if (!strcmp("cachesize", s)) {
638272343Sngie			rh.cachesize = atoi(eq);
639272343Sngie			return &rh;
640272343Sngie		}
641272343Sngie		if (!strcmp("lorder", s)) {
642272343Sngie			rh.lorder = atoi(eq);
643272343Sngie			return &rh;
644272343Sngie		}
645272343Sngie		if (!strcmp("reclen", s)) {
646272343Sngie			rh.reclen = atoi(eq);
647272343Sngie			return &rh;
648272343Sngie		}
649272343Sngie		if (!strcmp("bval", s)) {
650272343Sngie			rh.bval = atoi(eq);
651272343Sngie			return &rh;
652272343Sngie		}
653272343Sngie		if (!strcmp("psize", s)) {
654272343Sngie			rh.psize = atoi(eq);
655272343Sngie			return &rh;
656272343Sngie		}
657272343Sngie		break;
658272343Sngie	}
659272343Sngie	errx(1, "%s: unknown structure value", s);
660272343Sngie	/* NOTREACHED */
661272343Sngie}
662272343Sngie
663272343Sngiestatic void *
664272343Sngierfile(char *name, size_t *lenp)
665272343Sngie{
666272343Sngie	struct stat sb;
667272343Sngie	void *p;
668272343Sngie	int fd;
669272343Sngie	char *np;
670272343Sngie
671272343Sngie	for (; isspace((unsigned char)*name); ++name)
672272343Sngie		continue;
673272343Sngie	if ((np = strchr(name, '\n')) != NULL)
674272343Sngie		*np = '\0';
675272343Sngie	if ((fd = open(name, O_RDONLY, 0)) == -1 || fstat(fd, &sb) == -1)
676272343Sngie		err(1, "Cannot open `%s'", name);
677272343Sngie#ifdef NOT_PORTABLE
678272343Sngie	if (sb.st_size > (off_t)SIZE_T_MAX) {
679272343Sngie		errno = E2BIG;
680272343Sngie		err("Cannot process `%s'", name);
681272343Sngie	}
682272343Sngie#endif
683272343Sngie	if ((p = malloc((size_t)sb.st_size)) == NULL)
684272343Sngie		err(1, "Cannot allocate %zu bytes", (size_t)sb.st_size);
685272343Sngie	if (read(fd, p, (ssize_t)sb.st_size) != (ssize_t)sb.st_size)
686272343Sngie		err(1, "read failed");
687272343Sngie	*lenp = (size_t)sb.st_size;
688272343Sngie	(void)close(fd);
689272343Sngie	return p;
690272343Sngie}
691272343Sngie
692272343Sngiestatic void *
693272343Sngiexcopy(void *text, size_t len)
694272343Sngie{
695272343Sngie	void *p;
696272343Sngie
697272343Sngie	if ((p = malloc(len)) == NULL)
698272343Sngie		err(1, "Cannot allocate %zu bytes", len);
699272343Sngie	(void)memmove(p, text, len);
700272343Sngie	return p;
701272343Sngie}
702272343Sngie
703272343Sngiestatic void
704272343Sngiechkcmd(enum S state)
705272343Sngie{
706272343Sngie	if (state != COMMAND)
707272343Sngie		errx(1, "line %zu: not expecting command", lineno);
708272343Sngie}
709272343Sngie
710272343Sngiestatic void
711272343Sngiechkdata(enum S state)
712272343Sngie{
713272343Sngie	if (state != DATA)
714272343Sngie		errx(1, "line %zu: not expecting data", lineno);
715272343Sngie}
716272343Sngie
717272343Sngiestatic void
718272343Sngiechkkey(enum S state)
719272343Sngie{
720272343Sngie	if (state != KEY)
721272343Sngie		errx(1, "line %zu: not expecting a key", lineno);
722272343Sngie}
723272343Sngie
724272343Sngiestatic void
725272343Sngieusage(void)
726272343Sngie{
727272343Sngie	(void)fprintf(stderr,
728272343Sngie	    "Usage: %s [-l] [-f file] [-i info] [-o file] type script\n",
729272343Sngie	    getprogname());
730272343Sngie	exit(1);
731272343Sngie}
732