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