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