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