Home | History | Annotate | Line # | Download | only in db
      1 /*	$NetBSD: h_db.c,v 1.3 2016/09/24 21:18:22 christos Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1992, 1993, 1994
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. Neither the name of the University nor the names of its contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 __COPYRIGHT("@(#) Copyright (c) 1992, 1993, 1994\
     35 	The Regents of the University of California.  All rights reserved.");
     36 #endif /* not lint */
     37 
     38 #ifndef lint
     39 #if 0
     40 static char sccsid[] = "@(#)dbtest.c	8.17 (Berkeley) 9/1/94";
     41 #else
     42 __RCSID("$NetBSD: h_db.c,v 1.3 2016/09/24 21:18:22 christos Exp $");
     43 #endif
     44 #endif /* not lint */
     45 
     46 #include <sys/param.h>
     47 #include <sys/stat.h>
     48 
     49 #include <ctype.h>
     50 #include <errno.h>
     51 #include <fcntl.h>
     52 #include <limits.h>
     53 #include <stdio.h>
     54 #include <stdlib.h>
     55 #include <string.h>
     56 #include <stdbool.h>
     57 #include <unistd.h>
     58 #include <err.h>
     59 #include <db.h>
     60 #include "btree.h"
     61 
     62 enum S { COMMAND, COMPARE, GET, PUT, REMOVE, SEQ, SEQFLAG, KEY, DATA };
     63 
     64 static void	 compare(DBT *, DBT *);
     65 static DBTYPE	 dbtype(const char *);
     66 static void	 dump(DB *, int, int);
     67 static void	 get(DB *, DBT *);
     68 static void	 getdata(DB *, DBT *, DBT *);
     69 static void	 put(DB *, DBT *, DBT *);
     70 static void	 rem(DB *, DBT *);
     71 static const char *sflags(int);
     72 static void	 synk(DB *);
     73 static void	*rfile(char *, size_t *);
     74 static void	 seq(DB *, DBT *);
     75 static u_int	 setflags(char *);
     76 static void	*setinfo(DBTYPE, char *);
     77 static void	 unlinkpg(DB *);
     78 static void	 usage(void) __attribute__((__noreturn__));
     79 static void	*xcopy(void *, size_t);
     80 static void	 chkcmd(enum S);
     81 static void	 chkdata(enum S);
     82 static void	 chkkey(enum S);
     83 
     84 #ifdef STATISTICS
     85 extern void __bt_stat(DB *);
     86 #endif
     87 extern int __bt_relink(BTREE *, PAGE *);
     88 
     89 static DBTYPE type;			/* Database type. */
     90 static void *infop;			/* Iflags. */
     91 static size_t lineno;			/* Current line in test script. */
     92 static u_int flags;				/* Current DB flags. */
     93 static int ofd = STDOUT_FILENO;		/* Standard output fd. */
     94 
     95 static DB *XXdbp;			/* Global for gdb. */
     96 static size_t XXlineno;			/* Fast breakpoint for gdb. */
     97 
     98 int
     99 main(int argc, char *argv[])
    100 {
    101 	extern int optind;
    102 	extern char *optarg;
    103 	enum S command = COMMAND, state;
    104 	DB *dbp;
    105 	DBT data, key, keydata;
    106 	size_t len;
    107 	int ch, oflags, sflag;
    108 	char *fname, *infoarg, *p, *t, buf[8 * 1024];
    109 	bool unlink_dbfile;
    110 
    111 	infoarg = NULL;
    112 	fname = NULL;
    113 	unlink_dbfile = false;
    114 	oflags = O_CREAT | O_RDWR;
    115 	sflag = 0;
    116 	while ((ch = getopt(argc, argv, "f:i:lo:s")) != -1)
    117 		switch (ch) {
    118 		case 'f':
    119 			fname = optarg;
    120 			break;
    121 		case 'i':
    122 			infoarg = optarg;
    123 			break;
    124 		case 'l':
    125 			oflags |= DB_LOCK;
    126 			break;
    127 		case 'o':
    128 			if ((ofd = open(optarg,
    129 			    O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
    130 				err(1, "Cannot create `%s'", optarg);
    131 			break;
    132 		case 's':
    133 			sflag = 1;
    134 			break;
    135 		case '?':
    136 		default:
    137 			usage();
    138 		}
    139 	argc -= optind;
    140 	argv += optind;
    141 
    142 	if (argc != 2)
    143 		usage();
    144 
    145 	/* Set the type. */
    146 	type = dbtype(*argv++);
    147 
    148 	/* Open the descriptor file. */
    149         if (strcmp(*argv, "-") && freopen(*argv, "r", stdin) == NULL)
    150 	    err(1, "Cannot reopen `%s'", *argv);
    151 
    152 	/* Set up the db structure as necessary. */
    153 	if (infoarg == NULL)
    154 		infop = NULL;
    155 	else
    156 		for (p = strtok(infoarg, ",\t "); p != NULL;
    157 		    p = strtok(0, ",\t "))
    158 			if (*p != '\0')
    159 				infop = setinfo(type, p);
    160 
    161 	/*
    162 	 * Open the DB.  Delete any preexisting copy, you almost never
    163 	 * want it around, and it often screws up tests.
    164 	 */
    165 	if (fname == NULL) {
    166 		const char *q = getenv("TMPDIR");
    167 		if (q == NULL)
    168 			q = "/var/tmp";
    169 		(void)snprintf(buf, sizeof(buf), "%s/__dbtest", q);
    170 		fname = buf;
    171 		(void)unlink(buf);
    172 		unlink_dbfile = true;
    173 	} else  if (!sflag)
    174 		(void)unlink(fname);
    175 
    176 	if ((dbp = dbopen(fname,
    177 	    oflags, S_IRUSR | S_IWUSR, type, infop)) == NULL)
    178 		err(1, "Cannot dbopen `%s'", fname);
    179 	XXdbp = dbp;
    180 	if (unlink_dbfile)
    181 		(void)unlink(fname);
    182 
    183 	state = COMMAND;
    184 	for (lineno = 1;
    185 	    (p = fgets(buf, sizeof(buf), stdin)) != NULL; ++lineno) {
    186 		/* Delete the newline, displaying the key/data is easier. */
    187 		if (ofd == STDOUT_FILENO && (t = strchr(p, '\n')) != NULL)
    188 			*t = '\0';
    189 		if ((len = strlen(buf)) == 0 || isspace((unsigned char)*p) ||
    190 		    *p == '#')
    191 			continue;
    192 
    193 		/* Convenient gdb break point. */
    194 		if (XXlineno == lineno)
    195 			XXlineno = 1;
    196 		switch (*p) {
    197 		case 'c':			/* compare */
    198 			chkcmd(state);
    199 			state = KEY;
    200 			command = COMPARE;
    201 			break;
    202 		case 'e':			/* echo */
    203 			chkcmd(state);
    204 			/* Don't display the newline, if CR at EOL. */
    205 			if (p[len - 2] == '\r')
    206 				--len;
    207 			if (write(ofd, p + 1, len - 1) != (ssize_t)len - 1 ||
    208 			    write(ofd, "\n", 1) != 1)
    209 				err(1, "write failed");
    210 			break;
    211 		case 'g':			/* get */
    212 			chkcmd(state);
    213 			state = KEY;
    214 			command = GET;
    215 			break;
    216 		case 'p':			/* put */
    217 			chkcmd(state);
    218 			state = KEY;
    219 			command = PUT;
    220 			break;
    221 		case 'r':			/* remove */
    222 			chkcmd(state);
    223                         if (flags == R_CURSOR) {
    224 				rem(dbp, &key);
    225 				state = COMMAND;
    226                         } else {
    227 				state = KEY;
    228 				command = REMOVE;
    229 			}
    230 			break;
    231 		case 'S':			/* sync */
    232 			chkcmd(state);
    233 			synk(dbp);
    234 			state = COMMAND;
    235 			break;
    236 		case 's':			/* seq */
    237 			chkcmd(state);
    238 			if (flags == R_CURSOR) {
    239 				state = KEY;
    240 				command = SEQ;
    241 			} else
    242 				seq(dbp, &key);
    243 			break;
    244 		case 'f':
    245 			flags = setflags(p + 1);
    246 			break;
    247 		case 'D':			/* data file */
    248 			chkdata(state);
    249 			data.data = rfile(p + 1, &data.size);
    250 			goto ldata;
    251 		case 'd':			/* data */
    252 			chkdata(state);
    253 			data.data = xcopy(p + 1, len - 1);
    254 			data.size = len - 1;
    255 ldata:			switch (command) {
    256 			case COMPARE:
    257 				compare(&keydata, &data);
    258 				break;
    259 			case PUT:
    260 				put(dbp, &key, &data);
    261 				break;
    262 			default:
    263 				errx(1, "line %zu: command doesn't take data",
    264 				    lineno);
    265 			}
    266 			if (type != DB_RECNO)
    267 				free(key.data);
    268 			free(data.data);
    269 			state = COMMAND;
    270 			break;
    271 		case 'K':			/* key file */
    272 			chkkey(state);
    273 			if (type == DB_RECNO)
    274 				errx(1, "line %zu: 'K' not available for recno",
    275 				    lineno);
    276 			key.data = rfile(p + 1, &key.size);
    277 			goto lkey;
    278 		case 'k':			/* key */
    279 			chkkey(state);
    280 			if (type == DB_RECNO) {
    281 				static recno_t recno;
    282 				recno = atoi(p + 1);
    283 				key.data = &recno;
    284 				key.size = sizeof(recno);
    285 			} else {
    286 				key.data = xcopy(p + 1, len - 1);
    287 				key.size = len - 1;
    288 			}
    289 lkey:			switch (command) {
    290 			case COMPARE:
    291 				getdata(dbp, &key, &keydata);
    292 				state = DATA;
    293 				break;
    294 			case GET:
    295 				get(dbp, &key);
    296 				if (type != DB_RECNO)
    297 					free(key.data);
    298 				state = COMMAND;
    299 				break;
    300 			case PUT:
    301 				state = DATA;
    302 				break;
    303 			case REMOVE:
    304 				rem(dbp, &key);
    305 				if ((type != DB_RECNO) && (flags != R_CURSOR))
    306 					free(key.data);
    307 				state = COMMAND;
    308 				break;
    309 			case SEQ:
    310 				seq(dbp, &key);
    311 				if ((type != DB_RECNO) && (flags != R_CURSOR))
    312 					free(key.data);
    313 				state = COMMAND;
    314 				break;
    315 			default:
    316 				errx(1, "line %zu: command doesn't take a key",
    317 				    lineno);
    318 			}
    319 			break;
    320 		case 'o':
    321 			dump(dbp, p[1] == 'r', 0);
    322 			break;
    323 		case 'O':
    324 			dump(dbp, p[1] == 'r', 1);
    325 			break;
    326 		case 'u':
    327 			unlinkpg(dbp);
    328 			break;
    329 		default:
    330 			errx(1, "line %zu: %s: unknown command character",
    331 			    lineno, p);
    332 		}
    333 	}
    334 #ifdef STATISTICS
    335 	/*
    336 	 * -l must be used (DB_LOCK must be set) for this to be
    337 	 * used, otherwise a page will be locked and it will fail.
    338 	 */
    339 	if (type == DB_BTREE && oflags & DB_LOCK)
    340 		__bt_stat(dbp);
    341 #endif
    342 	if ((*dbp->close)(dbp))
    343 		err(1, "db->close failed");
    344 	(void)close(ofd);
    345 	return 0;
    346 }
    347 
    348 #define	NOOVERWRITE	"put failed, would overwrite key\n"
    349 
    350 static void
    351 compare(DBT *db1, DBT *db2)
    352 {
    353 	size_t len;
    354 	u_char *p1, *p2;
    355 
    356 	if (db1->size != db2->size)
    357 		printf("compare failed: key->data len %zu != data len %zu\n",
    358 		    db1->size, db2->size);
    359 
    360 	len = MIN(db1->size, db2->size);
    361 	for (p1 = db1->data, p2 = db2->data; len--;)
    362 		if (*p1++ != *p2++) {
    363 			printf("compare failed at offset %lu\n",
    364 			    (unsigned long)(p1 - (u_char *)db1->data));
    365 			break;
    366 		}
    367 }
    368 
    369 static void
    370 get(DB *dbp, DBT *kp)
    371 {
    372 	DBT data;
    373 
    374 	switch ((*dbp->get)(dbp, kp, &data, flags)) {
    375 	case 0:
    376 		(void)write(ofd, data.data, data.size);
    377 		if (ofd == STDOUT_FILENO)
    378 			(void)write(ofd, "\n", 1);
    379 		break;
    380 	case -1:
    381 		err(1, "line %zu: get failed", lineno);
    382 		/* NOTREACHED */
    383 	case 1:
    384 #define	NOSUCHKEY	"get failed, no such key\n"
    385 		if (ofd != STDOUT_FILENO)
    386 			(void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
    387 		else
    388 			(void)fprintf(stderr, "%zu: %.*s: %s",
    389 			    lineno, (int)MIN(kp->size, 20),
    390 			    (const char *)kp->data,
    391 			    NOSUCHKEY);
    392 #undef	NOSUCHKEY
    393 		break;
    394 	}
    395 }
    396 
    397 static void
    398 getdata(DB *dbp, DBT *kp, DBT *dp)
    399 {
    400 	switch ((*dbp->get)(dbp, kp, dp, flags)) {
    401 	case 0:
    402 		return;
    403 	case -1:
    404 		err(1, "line %zu: getdata failed", lineno);
    405 		/* NOTREACHED */
    406 	case 1:
    407 		errx(1, "line %zu: getdata failed, no such key", lineno);
    408 		/* NOTREACHED */
    409 	}
    410 }
    411 
    412 static void
    413 put(DB *dbp, DBT *kp, DBT *dp)
    414 {
    415 	switch ((*dbp->put)(dbp, kp, dp, flags)) {
    416 	case 0:
    417 		break;
    418 	case -1:
    419 		err(1, "line %zu: put failed", lineno);
    420 		/* NOTREACHED */
    421 	case 1:
    422 		(void)write(ofd, NOOVERWRITE, sizeof(NOOVERWRITE) - 1);
    423 		break;
    424 	}
    425 }
    426 
    427 static void
    428 rem(DB *dbp, DBT *kp)
    429 {
    430 	switch ((*dbp->del)(dbp, kp, flags)) {
    431 	case 0:
    432 		break;
    433 	case -1:
    434 		err(1, "line %zu: rem failed", lineno);
    435 		/* NOTREACHED */
    436 	case 1:
    437 #define	NOSUCHKEY	"rem failed, no such key\n"
    438 		if (ofd != STDOUT_FILENO)
    439 			(void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
    440 		else if (flags != R_CURSOR)
    441 			(void)fprintf(stderr, "%zu: %.*s: %s",
    442 			    lineno, (int)MIN(kp->size, 20),
    443 			    (const char *)kp->data, NOSUCHKEY);
    444 		else
    445 			(void)fprintf(stderr,
    446 			    "%zu: rem of cursor failed\n", lineno);
    447 #undef	NOSUCHKEY
    448 		break;
    449 	}
    450 }
    451 
    452 static void
    453 synk(DB *dbp)
    454 {
    455 	switch ((*dbp->sync)(dbp, flags)) {
    456 	case 0:
    457 		break;
    458 	case -1:
    459 		err(1, "line %zu: synk failed", lineno);
    460 		/* NOTREACHED */
    461 	}
    462 }
    463 
    464 static void
    465 seq(DB *dbp, DBT *kp)
    466 {
    467 	DBT data;
    468 
    469 	switch (dbp->seq(dbp, kp, &data, flags)) {
    470 	case 0:
    471 		(void)write(ofd, data.data, data.size);
    472 		if (ofd == STDOUT_FILENO)
    473 			(void)write(ofd, "\n", 1);
    474 		break;
    475 	case -1:
    476 		err(1, "line %zu: seq failed", lineno);
    477 		/* NOTREACHED */
    478 	case 1:
    479 #define	NOSUCHKEY	"seq failed, no such key\n"
    480 		if (ofd != STDOUT_FILENO)
    481 			(void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
    482 		else if (flags == R_CURSOR)
    483 			(void)fprintf(stderr, "%zu: %.*s: %s",
    484 			    lineno, (int)MIN(kp->size, 20),
    485 			    (const char *)kp->data, NOSUCHKEY);
    486 		else
    487 			(void)fprintf(stderr,
    488 			    "%zu: seq (%s) failed\n", lineno, sflags(flags));
    489 #undef	NOSUCHKEY
    490 		break;
    491 	}
    492 }
    493 
    494 static void
    495 dump(DB *dbp, int rev, int recurse)
    496 {
    497 	DBT key, data;
    498 	int xflags, nflags;
    499 
    500 	if (rev) {
    501 		xflags = R_LAST;
    502 		nflags = recurse ? R_RPREV : R_PREV;
    503 	} else {
    504 		xflags = R_FIRST;
    505 		nflags = recurse ? R_RNEXT : R_NEXT;
    506 	}
    507 	for (;; xflags = nflags)
    508 		switch (dbp->seq(dbp, &key, &data, xflags)) {
    509 		case 0:
    510 			(void)write(ofd, data.data, data.size);
    511 			if (ofd == STDOUT_FILENO)
    512 				(void)write(ofd, "\n", 1);
    513 			break;
    514 		case 1:
    515 			goto done;
    516 		case -1:
    517 			err(1, "line %zu: (dump) seq failed", lineno);
    518 			/* NOTREACHED */
    519 		}
    520 done:	return;
    521 }
    522 
    523 void
    524 unlinkpg(DB *dbp)
    525 {
    526 	BTREE *t = dbp->internal;
    527 	PAGE *h = NULL;
    528 	pgno_t pg;
    529 
    530 	for (pg = P_ROOT; pg < t->bt_mp->npages;
    531 	     mpool_put(t->bt_mp, h, 0), pg++) {
    532 		if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
    533 			break;
    534 		/* Look for a nonempty leaf page that has both left
    535 		 * and right siblings. */
    536 		if (h->prevpg == P_INVALID || h->nextpg == P_INVALID)
    537 			continue;
    538 		if (NEXTINDEX(h) == 0)
    539 			continue;
    540 		if ((h->flags & (P_BLEAF | P_RLEAF)))
    541 			break;
    542 	}
    543 	if (h == NULL || pg == t->bt_mp->npages) {
    544 		errx(1, "%s: no appropriate page found", __func__);
    545 		return;
    546 	}
    547 	if (__bt_relink(t, h) != 0) {
    548 		perror("unlinkpg");
    549 		goto cleanup;
    550 	}
    551 	h->prevpg = P_INVALID;
    552 	h->nextpg = P_INVALID;
    553 cleanup:
    554 	mpool_put(t->bt_mp, h, MPOOL_DIRTY);
    555 }
    556 
    557 static u_int
    558 setflags(char *s)
    559 {
    560 	char *p;
    561 
    562 	for (; isspace((unsigned char)*s); ++s);
    563 	if (*s == '\n' || *s == '\0')
    564 		return 0;
    565 	if ((p = strchr(s, '\n')) != NULL)
    566 		*p = '\0';
    567 	if (!strcmp(s, "R_CURSOR"))		return R_CURSOR;
    568 	if (!strcmp(s, "R_FIRST"))		return R_FIRST;
    569 	if (!strcmp(s, "R_IAFTER")) 		return R_IAFTER;
    570 	if (!strcmp(s, "R_IBEFORE")) 		return R_IBEFORE;
    571 	if (!strcmp(s, "R_LAST")) 		return R_LAST;
    572 	if (!strcmp(s, "R_NEXT")) 		return R_NEXT;
    573 	if (!strcmp(s, "R_NOOVERWRITE"))	return R_NOOVERWRITE;
    574 	if (!strcmp(s, "R_PREV"))		return R_PREV;
    575 	if (!strcmp(s, "R_SETCURSOR"))		return R_SETCURSOR;
    576 
    577 	errx(1, "line %zu: %s: unknown flag", lineno, s);
    578 	/* NOTREACHED */
    579 }
    580 
    581 static const char *
    582 sflags(int xflags)
    583 {
    584 	switch (xflags) {
    585 	case R_CURSOR:		return "R_CURSOR";
    586 	case R_FIRST:		return "R_FIRST";
    587 	case R_IAFTER:		return "R_IAFTER";
    588 	case R_IBEFORE:		return "R_IBEFORE";
    589 	case R_LAST:		return "R_LAST";
    590 	case R_NEXT:		return "R_NEXT";
    591 	case R_NOOVERWRITE:	return "R_NOOVERWRITE";
    592 	case R_PREV:		return "R_PREV";
    593 	case R_SETCURSOR:	return "R_SETCURSOR";
    594 	}
    595 
    596 	return "UNKNOWN!";
    597 }
    598 
    599 static DBTYPE
    600 dbtype(const char *s)
    601 {
    602 	if (!strcmp(s, "btree"))
    603 		return DB_BTREE;
    604 	if (!strcmp(s, "hash"))
    605 		return DB_HASH;
    606 	if (!strcmp(s, "recno"))
    607 		return DB_RECNO;
    608 	errx(1, "%s: unknown type (use btree, hash or recno)", s);
    609 	/* NOTREACHED */
    610 }
    611 
    612 static void *
    613 setinfo(DBTYPE dtype, char *s)
    614 {
    615 	static BTREEINFO ib;
    616 	static HASHINFO ih;
    617 	static RECNOINFO rh;
    618 	char *eq;
    619 
    620 	if ((eq = strchr(s, '=')) == NULL)
    621 		errx(1, "%s: illegal structure set statement", s);
    622 	*eq++ = '\0';
    623 	if (!isdigit((unsigned char)*eq))
    624 		errx(1, "%s: structure set statement must be a number", s);
    625 
    626 	switch (dtype) {
    627 	case DB_BTREE:
    628 		if (!strcmp("flags", s)) {
    629 			ib.flags = atoi(eq);
    630 			return &ib;
    631 		}
    632 		if (!strcmp("cachesize", s)) {
    633 			ib.cachesize = atoi(eq);
    634 			return &ib;
    635 		}
    636 		if (!strcmp("maxkeypage", s)) {
    637 			ib.maxkeypage = atoi(eq);
    638 			return &ib;
    639 		}
    640 		if (!strcmp("minkeypage", s)) {
    641 			ib.minkeypage = atoi(eq);
    642 			return &ib;
    643 		}
    644 		if (!strcmp("lorder", s)) {
    645 			ib.lorder = atoi(eq);
    646 			return &ib;
    647 		}
    648 		if (!strcmp("psize", s)) {
    649 			ib.psize = atoi(eq);
    650 			return &ib;
    651 		}
    652 		break;
    653 	case DB_HASH:
    654 		if (!strcmp("bsize", s)) {
    655 			ih.bsize = atoi(eq);
    656 			return &ih;
    657 		}
    658 		if (!strcmp("ffactor", s)) {
    659 			ih.ffactor = atoi(eq);
    660 			return &ih;
    661 		}
    662 		if (!strcmp("nelem", s)) {
    663 			ih.nelem = atoi(eq);
    664 			return &ih;
    665 		}
    666 		if (!strcmp("cachesize", s)) {
    667 			ih.cachesize = atoi(eq);
    668 			return &ih;
    669 		}
    670 		if (!strcmp("lorder", s)) {
    671 			ih.lorder = atoi(eq);
    672 			return &ih;
    673 		}
    674 		break;
    675 	case DB_RECNO:
    676 		if (!strcmp("flags", s)) {
    677 			rh.flags = atoi(eq);
    678 			return &rh;
    679 		}
    680 		if (!strcmp("cachesize", s)) {
    681 			rh.cachesize = atoi(eq);
    682 			return &rh;
    683 		}
    684 		if (!strcmp("lorder", s)) {
    685 			rh.lorder = atoi(eq);
    686 			return &rh;
    687 		}
    688 		if (!strcmp("reclen", s)) {
    689 			rh.reclen = atoi(eq);
    690 			return &rh;
    691 		}
    692 		if (!strcmp("bval", s)) {
    693 			rh.bval = atoi(eq);
    694 			return &rh;
    695 		}
    696 		if (!strcmp("psize", s)) {
    697 			rh.psize = atoi(eq);
    698 			return &rh;
    699 		}
    700 		break;
    701 	}
    702 	errx(1, "%s: unknown structure value", s);
    703 	/* NOTREACHED */
    704 }
    705 
    706 static void *
    707 rfile(char *name, size_t *lenp)
    708 {
    709 	struct stat sb;
    710 	void *p;
    711 	int fd;
    712 	char *np;
    713 
    714 	for (; isspace((unsigned char)*name); ++name)
    715 		continue;
    716 	if ((np = strchr(name, '\n')) != NULL)
    717 		*np = '\0';
    718 	if ((fd = open(name, O_RDONLY, 0)) == -1 || fstat(fd, &sb) == -1)
    719 		err(1, "Cannot open `%s'", name);
    720 #ifdef NOT_PORTABLE
    721 	if (sb.st_size > (off_t)SIZE_T_MAX) {
    722 		errno = E2BIG;
    723 		err("Cannot process `%s'", name);
    724 	}
    725 #endif
    726 	if ((p = malloc((size_t)sb.st_size)) == NULL)
    727 		err(1, "Cannot allocate %zu bytes", (size_t)sb.st_size);
    728 	if (read(fd, p, (ssize_t)sb.st_size) != (ssize_t)sb.st_size)
    729 		err(1, "read failed");
    730 	*lenp = (size_t)sb.st_size;
    731 	(void)close(fd);
    732 	return p;
    733 }
    734 
    735 static void *
    736 xcopy(void *text, size_t len)
    737 {
    738 	void *p;
    739 
    740 	if ((p = malloc(len)) == NULL)
    741 		err(1, "Cannot allocate %zu bytes", len);
    742 	(void)memmove(p, text, len);
    743 	return p;
    744 }
    745 
    746 static void
    747 chkcmd(enum S state)
    748 {
    749 	if (state != COMMAND)
    750 		errx(1, "line %zu: not expecting command", lineno);
    751 }
    752 
    753 static void
    754 chkdata(enum S state)
    755 {
    756 	if (state != DATA)
    757 		errx(1, "line %zu: not expecting data", lineno);
    758 }
    759 
    760 static void
    761 chkkey(enum S state)
    762 {
    763 	if (state != KEY)
    764 		errx(1, "line %zu: not expecting a key", lineno);
    765 }
    766 
    767 static void
    768 usage(void)
    769 {
    770 	(void)fprintf(stderr,
    771 	    "Usage: %s [-lu] [-f file] [-i info] [-o file] [-O file] "
    772 		"type script\n", getprogname());
    773 	exit(1);
    774 }
    775