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