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