xref: /netbsd-src/tests/lib/libc/db/h_db.c (revision 2b55b3112e53514454685c365504d5019ffd1007)
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
main(int argc,char * argv[])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
compare(DBT * db1,DBT * db2)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
get(DB * dbp,DBT * kp)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
getdata(DB * dbp,DBT * kp,DBT * dp)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
put(DB * dbp,DBT * kp,DBT * dp)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
rem(DB * dbp,DBT * kp)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
synk(DB * dbp)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
seq(DB * dbp,DBT * kp)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
dump(DB * dbp,int rev,int recurse)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
unlinkpg(DB * dbp)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
setflags(char * s)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 *
sflags(int xflags)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
dbtype(const char * s)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 *
setinfo(DBTYPE dtype,char * s)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 *
rfile(char * name,size_t * lenp)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 *
xcopy(void * text,size_t len)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
chkcmd(enum S state)747 chkcmd(enum S state)
748 {
749 	if (state != COMMAND)
750 		errx(1, "line %zu: not expecting command", lineno);
751 }
752 
753 static void
chkdata(enum S state)754 chkdata(enum S state)
755 {
756 	if (state != DATA)
757 		errx(1, "line %zu: not expecting data", lineno);
758 }
759 
760 static void
chkkey(enum S state)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
usage(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