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