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