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.5 (Berkeley) 09/07/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 if (dbp->close(dbp)) 267 err("db->close: %s", strerror(errno)); 268 (void)close(ofd); 269 exit(0); 270 } 271 272 #define NOOVERWRITE "put failed, would overwrite key\n" 273 #define NOSUCHKEY "get failed, no such key\n" 274 275 void 276 compare(db1, db2) 277 DBT *db1, *db2; 278 { 279 register size_t len; 280 register u_char *p1, *p2; 281 282 if (db1->size != db2->size) 283 printf("compare failed: key->data len %lu != data len %lu\n", 284 db1->size, db2->size); 285 286 len = MIN(db1->size, db2->size); 287 for (p1 = db1->data, p2 = db2->data; len--;) 288 if (*p1++ != *p2++) { 289 printf("compare failed at offset %d\n", 290 p1 - (u_char *)db1->data); 291 break; 292 } 293 } 294 295 void 296 get(dbp, kp) 297 DB *dbp; 298 DBT *kp; 299 { 300 DBT data; 301 302 switch(dbp->get(dbp, kp, &data, flags)) { 303 case 0: 304 (void)write(ofd, data.data, data.size); 305 break; 306 case -1: 307 err("line %lu: get: %s", lineno, strerror(errno)); 308 /* NOTREACHED */ 309 case 1: 310 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); 311 (void)fprintf(stderr, "%d: %.*s: %s\n", 312 lineno, kp->size, kp->data, NOSUCHKEY); 313 break; 314 } 315 } 316 317 void 318 getdata(dbp, kp, dp) 319 DB *dbp; 320 DBT *kp, *dp; 321 { 322 switch(dbp->get(dbp, kp, dp, flags)) { 323 case 0: 324 return; 325 case -1: 326 err("line %lu: getdata: %s", lineno, strerror(errno)); 327 /* NOTREACHED */ 328 case 1: 329 err("line %lu: get failed, no such key", lineno); 330 /* NOTREACHED */ 331 } 332 } 333 334 void 335 put(dbp, kp, dp) 336 DB *dbp; 337 DBT *kp, *dp; 338 { 339 switch(dbp->put(dbp, kp, dp, flags)) { 340 case 0: 341 break; 342 case -1: 343 err("line %lu: put: %s", lineno, strerror(errno)); 344 /* NOTREACHED */ 345 case 1: 346 (void)write(ofd, NOOVERWRITE, sizeof(NOOVERWRITE) - 1); 347 break; 348 } 349 } 350 351 void 352 rem(dbp, kp) 353 DB *dbp; 354 DBT *kp; 355 { 356 switch(dbp->del(dbp, kp, flags)) { 357 case 0: 358 break; 359 case -1: 360 err("line %lu: get: %s", lineno, strerror(errno)); 361 /* NOTREACHED */ 362 case 1: 363 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); 364 break; 365 } 366 } 367 368 void 369 seq(dbp, kp) 370 DB *dbp; 371 DBT *kp; 372 { 373 DBT data; 374 375 switch(dbp->seq(dbp, kp, &data, flags)) { 376 case 0: 377 (void)write(ofd, data.data, data.size); 378 break; 379 case -1: 380 err("line %lu: seq: %s", lineno, strerror(errno)); 381 /* NOTREACHED */ 382 case 1: 383 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1); 384 break; 385 } 386 } 387 388 void 389 dump(dbp, rev) 390 DB *dbp; 391 int rev; 392 { 393 DBT key, data; 394 int flags, nflags; 395 396 if (rev) { 397 flags = R_LAST; 398 nflags = R_PREV; 399 } else { 400 flags = R_FIRST; 401 nflags = R_NEXT; 402 } 403 for (;; flags = nflags) 404 switch(dbp->seq(dbp, &key, &data, flags)) { 405 case 0: 406 (void)write(ofd, data.data, data.size); 407 break; 408 case 1: 409 goto done; 410 case -1: 411 err("line %lu: (dump) seq: %s", 412 lineno, strerror(errno)); 413 /* NOTREACHED */ 414 } 415 done: return; 416 } 417 418 u_int 419 setflags(s) 420 char *s; 421 { 422 char *p; 423 424 for (; isspace(*s); ++s); 425 if (*s == '\n') 426 return (0); 427 if ((p = index(s, '\n')) != NULL) 428 *p = '\0'; 429 if (!strcmp(s, "R_CURSOR")) 430 return (R_CURSOR); 431 if (!strcmp(s, "R_FIRST")) 432 return (R_FIRST); 433 if (!strcmp(s, "R_IAFTER")) 434 return (R_IAFTER); 435 if (!strcmp(s, "R_IBEFORE")) 436 return (R_IBEFORE); 437 if (!strcmp(s, "R_LAST")) 438 return (R_LAST); 439 if (!strcmp(s, "R_NEXT")) 440 return (R_NEXT); 441 if (!strcmp(s, "R_NOOVERWRITE")) 442 return (R_NOOVERWRITE); 443 if (!strcmp(s, "R_PREV")) 444 return (R_PREV); 445 if (!strcmp(s, "R_SETCURSOR")) 446 return (R_SETCURSOR); 447 err("line %lu: %s: unknown flag", lineno, s); 448 /* NOTREACHED */ 449 } 450 451 DBTYPE 452 dbtype(s) 453 char *s; 454 { 455 if (!strcmp(s, "btree")) 456 return (DB_BTREE); 457 if (!strcmp(s, "hash")) 458 return (DB_HASH); 459 if (!strcmp(s, "recno")) 460 return (DB_RECNO); 461 err("%s: unknown type (use btree, hash or recno)", s); 462 /* NOTREACHED */ 463 } 464 465 void * 466 setinfo(type, s) 467 DBTYPE type; 468 char *s; 469 { 470 static BTREEINFO ib; 471 static HASHINFO ih; 472 static RECNOINFO rh; 473 char *eq; 474 475 if ((eq = index(s, '=')) == NULL) 476 err("%s: illegal structure set statement", s); 477 *eq++ = '\0'; 478 if (!isdigit(*eq)) 479 err("%s: structure set statement must be a number", s); 480 481 switch(type) { 482 case DB_BTREE: 483 if (!strcmp("flags", s)) { 484 ib.flags = atoi(eq); 485 return (&ib); 486 } 487 if (!strcmp("cachesize", s)) { 488 ib.cachesize = atoi(eq); 489 return (&ib); 490 } 491 if (!strcmp("maxkeypage", s)) { 492 ib.maxkeypage = atoi(eq); 493 return (&ib); 494 } 495 if (!strcmp("minkeypage", s)) { 496 ib.minkeypage = atoi(eq); 497 return (&ib); 498 } 499 if (!strcmp("lorder", s)) { 500 ib.lorder = atoi(eq); 501 return (&ib); 502 } 503 if (!strcmp("psize", s)) { 504 ib.psize = atoi(eq); 505 return (&ib); 506 } 507 break; 508 case DB_HASH: 509 if (!strcmp("bsize", s)) { 510 ih.bsize = atoi(eq); 511 return (&ih); 512 } 513 if (!strcmp("ffactor", s)) { 514 ih.ffactor = atoi(eq); 515 return (&ih); 516 } 517 if (!strcmp("nelem", s)) { 518 ih.nelem = atoi(eq); 519 return (&ih); 520 } 521 if (!strcmp("cachesize", s)) { 522 ih.cachesize = atoi(eq); 523 return (&ih); 524 } 525 if (!strcmp("lorder", s)) { 526 ih.lorder = atoi(eq); 527 return (&ih); 528 } 529 break; 530 case DB_RECNO: 531 if (!strcmp("flags", s)) { 532 rh.flags = atoi(eq); 533 return (&rh); 534 } 535 if (!strcmp("cachesize", s)) { 536 rh.cachesize = atoi(eq); 537 return (&rh); 538 } 539 if (!strcmp("lorder", s)) { 540 rh.lorder = atoi(eq); 541 return (&rh); 542 } 543 if (!strcmp("reclen", s)) { 544 rh.reclen = atoi(eq); 545 return (&rh); 546 } 547 if (!strcmp("bval", s)) { 548 rh.bval = atoi(eq); 549 return (&rh); 550 } 551 if (!strcmp("psize", s)) { 552 rh.psize = atoi(eq); 553 return (&rh); 554 } 555 break; 556 } 557 err("%s: unknown structure value", s); 558 /* NOTREACHED */ 559 } 560 561 void * 562 rfile(name, lenp) 563 char *name; 564 size_t *lenp; 565 { 566 struct stat sb; 567 void *p; 568 int fd; 569 char *np; 570 571 for (; isspace(*name); ++name); 572 if ((np = index(name, '\n')) != NULL) 573 *np = '\0'; 574 if ((fd = open(name, O_RDONLY, 0)) < 0 || 575 fstat(fd, &sb)) 576 err("%s: %s\n", name, strerror(errno)); 577 #ifdef NOT_PORTABLE 578 if (sb.st_size > (off_t)SIZE_T_MAX) 579 err("%s: %s\n", name, strerror(E2BIG)); 580 #endif 581 if ((p = malloc((u_int)sb.st_size)) == NULL) 582 err("%s", strerror(errno)); 583 (void)read(fd, p, (int)sb.st_size); 584 *lenp = sb.st_size; 585 (void)close(fd); 586 return (p); 587 } 588 589 void * 590 xmalloc(text, len) 591 char *text; 592 size_t len; 593 { 594 void *p; 595 596 if ((p = malloc(len)) == NULL) 597 err("%s", strerror(errno)); 598 memmove(p, text, len); 599 return (p); 600 } 601 602 void 603 usage() 604 { 605 (void)fprintf(stderr, 606 "usage: dbtest [-l] [-f file] [-i info] [-o file] type script\n"); 607 exit(1); 608 } 609 610 #if __STDC__ 611 #include <stdarg.h> 612 #else 613 #include <varargs.h> 614 #endif 615 616 void 617 #if __STDC__ 618 err(const char *fmt, ...) 619 #else 620 err(fmt, va_alist) 621 char *fmt; 622 va_dcl 623 #endif 624 { 625 va_list ap; 626 #if __STDC__ 627 va_start(ap, fmt); 628 #else 629 va_start(ap); 630 #endif 631 (void)fprintf(stderr, "dbtest: "); 632 (void)vfprintf(stderr, fmt, ap); 633 va_end(ap); 634 (void)fprintf(stderr, "\n"); 635 exit(1); 636 /* NOTREACHED */ 637 } 638