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