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