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