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