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