h_db.c revision 313535
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
40static 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
62enum S { COMMAND, COMPARE, GET, PUT, REMOVE, SEQ, SEQFLAG, KEY, DATA };
63
64static void	 compare(DBT *, DBT *);
65static DBTYPE	 dbtype(const char *);
66static void	 dump(DB *, int, int);
67static void	 get(DB *, DBT *);
68static void	 getdata(DB *, DBT *, DBT *);
69static void	 put(DB *, DBT *, DBT *);
70static void	 rem(DB *, DBT *);
71static const char *sflags(int);
72static void	 synk(DB *);
73static void	*rfile(char *, size_t *);
74static void	 seq(DB *, DBT *);
75static u_int	 setflags(char *);
76static void	*setinfo(DBTYPE, char *);
77#ifdef	__NetBSD__
78static void	 unlinkpg(DB *);
79#endif
80static void	 usage(void) __attribute__((__noreturn__));
81static void	*xcopy(void *, size_t);
82static void	 chkcmd(enum S);
83static void	 chkdata(enum S);
84static void	 chkkey(enum S);
85
86#ifdef STATISTICS
87extern void __bt_stat(DB *);
88#endif
89#ifdef	__NetBSD__
90extern int __bt_relink(BTREE *, PAGE *);
91#endif
92
93static DBTYPE type;			/* Database type. */
94static void *infop;			/* Iflags. */
95static size_t lineno;			/* Current line in test script. */
96static u_int flags;				/* Current DB flags. */
97static int ofd = STDOUT_FILENO;		/* Standard output fd. */
98
99static DB *XXdbp;			/* Global for gdb. */
100static size_t XXlineno;			/* Fast breakpoint for gdb. */
101
102int
103main(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;
259ldata:			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			}
293lkey:			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
356static void
357compare(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
375static void
376get(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
403static void
404getdata(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
418static void
419put(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
433static void
434rem(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
458static void
459synk(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
470static void
471seq(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
500static void
501dump(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		}
534done:	return;
535}
536
537#ifdef __NetBSD__
538void
539unlinkpg(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;
568cleanup:
569	mpool_put(t->bt_mp, h, MPOOL_DIRTY);
570}
571#endif
572
573static u_int
574setflags(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
597static const char *
598sflags(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
615static DBTYPE
616dbtype(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
628static void *
629setinfo(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
722static void *
723rfile(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
751static void *
752xcopy(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
762static void
763chkcmd(enum S state)
764{
765	if (state != COMMAND)
766		errx(1, "line %zu: not expecting command", lineno);
767}
768
769static void
770chkdata(enum S state)
771{
772	if (state != DATA)
773		errx(1, "line %zu: not expecting data", lineno);
774}
775
776static void
777chkkey(enum S state)
778{
779	if (state != KEY)
780		errx(1, "line %zu: not expecting a key", lineno);
781}
782
783static void
784usage(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