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