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