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