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