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