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