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