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