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 *); 77static void unlinkpg(DB *); 78static void usage(void) __attribute__((__noreturn__)); 79static void *xcopy(void *, size_t); 80static void chkcmd(enum S); 81static void chkdata(enum S); 82static void chkkey(enum S); 83 84#ifdef STATISTICS 85extern void __bt_stat(DB *); 86#endif 87extern int __bt_relink(BTREE *, PAGE *); 88 89static DBTYPE type; /* Database type. */ 90static void *infop; /* Iflags. */ 91static size_t lineno; /* Current line in test script. */ 92static u_int flags; /* Current DB flags. */ 93static int ofd = STDOUT_FILENO; /* Standard output fd. */ 94 95static DB *XXdbp; /* Global for gdb. */ 96static size_t XXlineno; /* Fast breakpoint for gdb. */ 97 98int 99main(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; 255ldata: 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 } 289lkey: 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 350static void 351compare(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 369static void 370get(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 397static void 398getdata(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 412static void 413put(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 427static void 428rem(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 452static void 453synk(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 464static void 465seq(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 494static void 495dump(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 } 520done: return; 521} 522 523void 524unlinkpg(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; 553cleanup: 554 mpool_put(t->bt_mp, h, MPOOL_DIRTY); 555} 556 557static u_int 558setflags(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 581static const char * 582sflags(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 599static DBTYPE 600dbtype(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 612static void * 613setinfo(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 706static void * 707rfile(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 735static void * 736xcopy(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 746static void 747chkcmd(enum S state) 748{ 749 if (state != COMMAND) 750 errx(1, "line %zu: not expecting command", lineno); 751} 752 753static void 754chkdata(enum S state) 755{ 756 if (state != DATA) 757 errx(1, "line %zu: not expecting data", lineno); 758} 759 760static void 761chkkey(enum S state) 762{ 763 if (state != KEY) 764 errx(1, "line %zu: not expecting a key", lineno); 765} 766 767static void 768usage(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