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