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