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