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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 * 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 * 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 * 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 747 chkcmd(enum S state) 748 { 749 if (state != COMMAND) 750 errx(1, "line %zu: not expecting command", lineno); 751 } 752 753 static void 754 chkdata(enum S state) 755 { 756 if (state != DATA) 757 errx(1, "line %zu: not expecting data", lineno); 758 } 759 760 static void 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 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