Deleted Added
full compact
dbtest.c (165903) dbtest.c (176380)
1/*-
2 * Copyright (c) 1992, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#ifndef lint
31static char copyright[] =
32"@(#) Copyright (c) 1992, 1993, 1994\n\
33 The Regents of the University of California. All rights reserved.\n";
34#endif /* not lint */
35
36#if defined(LIBC_SCCS) && !defined(lint)
37static char sccsid[] = "@(#)dbtest.c 8.17 (Berkeley) 9/1/94";
38#endif /* LIBC_SCCS and not lint */
39#include <sys/cdefs.h>
1/*-
2 * Copyright (c) 1992, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#ifndef lint
31static char copyright[] =
32"@(#) Copyright (c) 1992, 1993, 1994\n\
33 The Regents of the University of California. All rights reserved.\n";
34#endif /* not lint */
35
36#if defined(LIBC_SCCS) && !defined(lint)
37static char sccsid[] = "@(#)dbtest.c 8.17 (Berkeley) 9/1/94";
38#endif /* LIBC_SCCS and not lint */
39#include <sys/cdefs.h>
40__FBSDID("$FreeBSD: head/lib/libc/db/test/dbtest.c 165903 2007-01-09 00:28:16Z imp $");
40__FBSDID("$FreeBSD: head/lib/libc/db/test/dbtest.c 176380 2008-02-18 03:19:25Z kevlo $");
41
42#include <sys/param.h>
43#include <sys/stat.h>
44
45#include <ctype.h>
46#include <errno.h>
47#include <fcntl.h>
48#include <limits.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include <string.h>
52#include <unistd.h>
53
54#include <db.h>
55
56enum S { COMMAND, COMPARE, GET, PUT, REMOVE, SEQ, SEQFLAG, KEY, DATA };
57
58void compare(DBT *, DBT *);
59DBTYPE dbtype(char *);
60void dump(DB *, int);
61void err(const char *, ...) __printflike(1, 2);
62void get(DB *, DBT *);
63void getdata(DB *, DBT *, DBT *);
64void put(DB *, DBT *, DBT *);
65void rem(DB *, DBT *);
66char *sflags(int);
67void synk(DB *);
68void *rfile(char *, size_t *);
69void seq(DB *, DBT *);
70u_int setflags(char *);
71void *setinfo(DBTYPE, char *);
72void usage(void);
73void *xmalloc(char *, size_t);
74
75DBTYPE type; /* Database type. */
76void *infop; /* Iflags. */
77u_long lineno; /* Current line in test script. */
78u_int flags; /* Current DB flags. */
79int ofd = STDOUT_FILENO; /* Standard output fd. */
80
81DB *XXdbp; /* Global for gdb. */
82int XXlineno; /* Fast breakpoint for gdb. */
83
84int
85main(argc, argv)
86 int argc;
87 char *argv[];
88{
89 extern int optind;
90 extern char *optarg;
91 enum S command, state;
92 DB *dbp;
93 DBT data, key, keydata;
94 size_t len;
95 int ch, oflags, sflag;
96 char *fname, *infoarg, *p, *t, buf[8 * 1024];
97
98 infoarg = NULL;
99 fname = NULL;
100 oflags = O_CREAT | O_RDWR;
101 sflag = 0;
41
42#include <sys/param.h>
43#include <sys/stat.h>
44
45#include <ctype.h>
46#include <errno.h>
47#include <fcntl.h>
48#include <limits.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include <string.h>
52#include <unistd.h>
53
54#include <db.h>
55
56enum S { COMMAND, COMPARE, GET, PUT, REMOVE, SEQ, SEQFLAG, KEY, DATA };
57
58void compare(DBT *, DBT *);
59DBTYPE dbtype(char *);
60void dump(DB *, int);
61void err(const char *, ...) __printflike(1, 2);
62void get(DB *, DBT *);
63void getdata(DB *, DBT *, DBT *);
64void put(DB *, DBT *, DBT *);
65void rem(DB *, DBT *);
66char *sflags(int);
67void synk(DB *);
68void *rfile(char *, size_t *);
69void seq(DB *, DBT *);
70u_int setflags(char *);
71void *setinfo(DBTYPE, char *);
72void usage(void);
73void *xmalloc(char *, size_t);
74
75DBTYPE type; /* Database type. */
76void *infop; /* Iflags. */
77u_long lineno; /* Current line in test script. */
78u_int flags; /* Current DB flags. */
79int ofd = STDOUT_FILENO; /* Standard output fd. */
80
81DB *XXdbp; /* Global for gdb. */
82int XXlineno; /* Fast breakpoint for gdb. */
83
84int
85main(argc, argv)
86 int argc;
87 char *argv[];
88{
89 extern int optind;
90 extern char *optarg;
91 enum S command, state;
92 DB *dbp;
93 DBT data, key, keydata;
94 size_t len;
95 int ch, oflags, sflag;
96 char *fname, *infoarg, *p, *t, buf[8 * 1024];
97
98 infoarg = NULL;
99 fname = NULL;
100 oflags = O_CREAT | O_RDWR;
101 sflag = 0;
102 while ((ch = getopt(argc, argv, "f:i:lo:s")) != EOF)
102 while ((ch = getopt(argc, argv, "f:i:lo:s")) != -1)
103 switch (ch) {
104 case 'f':
105 fname = optarg;
106 break;
107 case 'i':
108 infoarg = optarg;
109 break;
110 case 'l':
111 oflags |= DB_LOCK;
112 break;
113 case 'o':
114 if ((ofd = open(optarg,
115 O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
116 err("%s: %s", optarg, strerror(errno));
117 break;
118 case 's':
119 sflag = 1;
120 break;
121 case '?':
122 default:
123 usage();
124 }
125 argc -= optind;
126 argv += optind;
127
128 if (argc != 2)
129 usage();
130
131 /* Set the type. */
132 type = dbtype(*argv++);
133
134 /* Open the descriptor file. */
135 if (strcmp(*argv, "-") && freopen(*argv, "r", stdin) == NULL)
136 err("%s: %s", *argv, strerror(errno));
137
138 /* Set up the db structure as necessary. */
139 if (infoarg == NULL)
140 infop = NULL;
141 else
142 for (p = strtok(infoarg, ",\t "); p != NULL;
143 p = strtok(0, ",\t "))
144 if (*p != '\0')
145 infop = setinfo(type, p);
146
147 /*
148 * Open the DB. Delete any preexisting copy, you almost never
149 * want it around, and it often screws up tests.
150 */
151 if (fname == NULL) {
152 p = getenv("TMPDIR");
153 if (p == NULL)
154 p = "/var/tmp";
155 (void)snprintf(buf, sizeof(buf), "%s/__dbtest", p);
156 fname = buf;
157 (void)unlink(buf);
158 } else if (!sflag)
159 (void)unlink(fname);
160
161 if ((dbp = dbopen(fname,
162 oflags, S_IRUSR | S_IWUSR, type, infop)) == NULL)
163 err("dbopen: %s", strerror(errno));
164 XXdbp = dbp;
165
166 state = COMMAND;
167 for (lineno = 1;
168 (p = fgets(buf, sizeof(buf), stdin)) != NULL; ++lineno) {
169 /* Delete the newline, displaying the key/data is easier. */
170 if (ofd == STDOUT_FILENO && (t = strchr(p, '\n')) != NULL)
171 *t = '\0';
172 if ((len = strlen(buf)) == 0 || isspace(*p) || *p == '#')
173 continue;
174
175 /* Convenient gdb break point. */
176 if (XXlineno == lineno)
177 XXlineno = 1;
178 switch (*p) {
179 case 'c': /* compare */
180 if (state != COMMAND)
181 err("line %lu: not expecting command", lineno);
182 state = KEY;
183 command = COMPARE;
184 break;
185 case 'e': /* echo */
186 if (state != COMMAND)
187 err("line %lu: not expecting command", lineno);
188 /* Don't display the newline, if CR at EOL. */
189 if (p[len - 2] == '\r')
190 --len;
191 if (write(ofd, p + 1, len - 1) != len - 1 ||
192 write(ofd, "\n", 1) != 1)
193 err("write: %s", strerror(errno));
194 break;
195 case 'g': /* get */
196 if (state != COMMAND)
197 err("line %lu: not expecting command", lineno);
198 state = KEY;
199 command = GET;
200 break;
201 case 'p': /* put */
202 if (state != COMMAND)
203 err("line %lu: not expecting command", lineno);
204 state = KEY;
205 command = PUT;
206 break;
207 case 'r': /* remove */
208 if (state != COMMAND)
209 err("line %lu: not expecting command", lineno);
210 if (flags == R_CURSOR) {
211 rem(dbp, &key);
212 state = COMMAND;
213 } else {
214 state = KEY;
215 command = REMOVE;
216 }
217 break;
218 case 'S': /* sync */
219 if (state != COMMAND)
220 err("line %lu: not expecting command", lineno);
221 synk(dbp);
222 state = COMMAND;
223 break;
224 case 's': /* seq */
225 if (state != COMMAND)
226 err("line %lu: not expecting command", lineno);
227 if (flags == R_CURSOR) {
228 state = KEY;
229 command = SEQ;
230 } else
231 seq(dbp, &key);
232 break;
233 case 'f':
234 flags = setflags(p + 1);
235 break;
236 case 'D': /* data file */
237 if (state != DATA)
238 err("line %lu: not expecting data", lineno);
239 data.data = rfile(p + 1, &data.size);
240 goto ldata;
241 case 'd': /* data */
242 if (state != DATA)
243 err("line %lu: not expecting data", lineno);
244 data.data = xmalloc(p + 1, len - 1);
245 data.size = len - 1;
246ldata: switch (command) {
247 case COMPARE:
248 compare(&keydata, &data);
249 break;
250 case PUT:
251 put(dbp, &key, &data);
252 break;
253 default:
254 err("line %lu: command doesn't take data",
255 lineno);
256 }
257 if (type != DB_RECNO)
258 free(key.data);
259 free(data.data);
260 state = COMMAND;
261 break;
262 case 'K': /* key file */
263 if (state != KEY)
264 err("line %lu: not expecting a key", lineno);
265 if (type == DB_RECNO)
266 err("line %lu: 'K' not available for recno",
267 lineno);
268 key.data = rfile(p + 1, &key.size);
269 goto lkey;
270 case 'k': /* key */
271 if (state != KEY)
272 err("line %lu: not expecting a key", lineno);
273 if (type == DB_RECNO) {
274 static recno_t recno;
275 recno = atoi(p + 1);
276 key.data = &recno;
277 key.size = sizeof(recno);
278 } else {
279 key.data = xmalloc(p + 1, len - 1);
280 key.size = len - 1;
281 }
282lkey: switch (command) {
283 case COMPARE:
284 getdata(dbp, &key, &keydata);
285 state = DATA;
286 break;
287 case GET:
288 get(dbp, &key);
289 if (type != DB_RECNO)
290 free(key.data);
291 state = COMMAND;
292 break;
293 case PUT:
294 state = DATA;
295 break;
296 case REMOVE:
297 rem(dbp, &key);
298 if ((type != DB_RECNO) && (flags != R_CURSOR))
299 free(key.data);
300 state = COMMAND;
301 break;
302 case SEQ:
303 seq(dbp, &key);
304 if ((type != DB_RECNO) && (flags != R_CURSOR))
305 free(key.data);
306 state = COMMAND;
307 break;
308 default:
309 err("line %lu: command doesn't take a key",
310 lineno);
311 }
312 break;
313 case 'o':
314 dump(dbp, p[1] == 'r');
315 break;
316 default:
317 err("line %lu: %s: unknown command character",
318 lineno, p);
319 }
320 }
321#ifdef STATISTICS
322 /*
323 * -l must be used (DB_LOCK must be set) for this to be
324 * used, otherwise a page will be locked and it will fail.
325 */
326 if (type == DB_BTREE && oflags & DB_LOCK)
327 __bt_stat(dbp);
328#endif
329 if (dbp->close(dbp))
330 err("db->close: %s", strerror(errno));
331 (void)close(ofd);
332 exit(0);
333}
334
335#define NOOVERWRITE "put failed, would overwrite key\n"
336
337void
338compare(db1, db2)
339 DBT *db1, *db2;
340{
341 size_t len;
342 u_char *p1, *p2;
343
344 if (db1->size != db2->size)
345 printf("compare failed: key->data len %lu != data len %lu\n",
346 db1->size, db2->size);
347
348 len = MIN(db1->size, db2->size);
349 for (p1 = db1->data, p2 = db2->data; len--;)
350 if (*p1++ != *p2++) {
351 printf("compare failed at offset %d\n",
352 p1 - (u_char *)db1->data);
353 break;
354 }
355}
356
357void
358get(dbp, kp)
359 DB *dbp;
360 DBT *kp;
361{
362 DBT data;
363
364 switch (dbp->get(dbp, kp, &data, flags)) {
365 case 0:
366 (void)write(ofd, data.data, data.size);
367 if (ofd == STDOUT_FILENO)
368 (void)write(ofd, "\n", 1);
369 break;
370 case -1:
371 err("line %lu: get: %s", lineno, strerror(errno));
372 /* NOTREACHED */
373 case 1:
374#define NOSUCHKEY "get failed, no such key\n"
375 if (ofd != STDOUT_FILENO)
376 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
377 else
378 (void)fprintf(stderr, "%d: %.*s: %s",
379 lineno, MIN(kp->size, 20), kp->data, NOSUCHKEY);
380#undef NOSUCHKEY
381 break;
382 }
383}
384
385void
386getdata(dbp, kp, dp)
387 DB *dbp;
388 DBT *kp, *dp;
389{
390 switch (dbp->get(dbp, kp, dp, flags)) {
391 case 0:
392 return;
393 case -1:
394 err("line %lu: getdata: %s", lineno, strerror(errno));
395 /* NOTREACHED */
396 case 1:
397 err("line %lu: getdata failed, no such key", lineno);
398 /* NOTREACHED */
399 }
400}
401
402void
403put(dbp, kp, dp)
404 DB *dbp;
405 DBT *kp, *dp;
406{
407 switch (dbp->put(dbp, kp, dp, flags)) {
408 case 0:
409 break;
410 case -1:
411 err("line %lu: put: %s", lineno, strerror(errno));
412 /* NOTREACHED */
413 case 1:
414 (void)write(ofd, NOOVERWRITE, sizeof(NOOVERWRITE) - 1);
415 break;
416 }
417}
418
419void
420rem(dbp, kp)
421 DB *dbp;
422 DBT *kp;
423{
424 switch (dbp->del(dbp, kp, flags)) {
425 case 0:
426 break;
427 case -1:
428 err("line %lu: rem: %s", lineno, strerror(errno));
429 /* NOTREACHED */
430 case 1:
431#define NOSUCHKEY "rem failed, no such key\n"
432 if (ofd != STDOUT_FILENO)
433 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
434 else if (flags != R_CURSOR)
435 (void)fprintf(stderr, "%d: %.*s: %s",
436 lineno, MIN(kp->size, 20), kp->data, NOSUCHKEY);
437 else
438 (void)fprintf(stderr,
439 "%d: rem of cursor failed\n", lineno);
440#undef NOSUCHKEY
441 break;
442 }
443}
444
445void
446synk(dbp)
447 DB *dbp;
448{
449 switch (dbp->sync(dbp, flags)) {
450 case 0:
451 break;
452 case -1:
453 err("line %lu: synk: %s", lineno, strerror(errno));
454 /* NOTREACHED */
455 }
456}
457
458void
459seq(dbp, kp)
460 DB *dbp;
461 DBT *kp;
462{
463 DBT data;
464
465 switch (dbp->seq(dbp, kp, &data, flags)) {
466 case 0:
467 (void)write(ofd, data.data, data.size);
468 if (ofd == STDOUT_FILENO)
469 (void)write(ofd, "\n", 1);
470 break;
471 case -1:
472 err("line %lu: seq: %s", lineno, strerror(errno));
473 /* NOTREACHED */
474 case 1:
475#define NOSUCHKEY "seq failed, no such key\n"
476 if (ofd != STDOUT_FILENO)
477 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
478 else if (flags == R_CURSOR)
479 (void)fprintf(stderr, "%d: %.*s: %s",
480 lineno, MIN(kp->size, 20), kp->data, NOSUCHKEY);
481 else
482 (void)fprintf(stderr,
483 "%d: seq (%s) failed\n", lineno, sflags(flags));
484#undef NOSUCHKEY
485 break;
486 }
487}
488
489void
490dump(dbp, rev)
491 DB *dbp;
492 int rev;
493{
494 DBT key, data;
495 int flags, nflags;
496
497 if (rev) {
498 flags = R_LAST;
499 nflags = R_PREV;
500 } else {
501 flags = R_FIRST;
502 nflags = R_NEXT;
503 }
504 for (;; flags = nflags)
505 switch (dbp->seq(dbp, &key, &data, flags)) {
506 case 0:
507 (void)write(ofd, data.data, data.size);
508 if (ofd == STDOUT_FILENO)
509 (void)write(ofd, "\n", 1);
510 break;
511 case 1:
512 goto done;
513 case -1:
514 err("line %lu: (dump) seq: %s",
515 lineno, strerror(errno));
516 /* NOTREACHED */
517 }
518done: return;
519}
520
521u_int
522setflags(s)
523 char *s;
524{
525 char *p, *index();
526
527 for (; isspace(*s); ++s);
528 if (*s == '\n' || *s == '\0')
529 return (0);
530 if ((p = index(s, '\n')) != NULL)
531 *p = '\0';
532 if (!strcmp(s, "R_CURSOR")) return (R_CURSOR);
533 if (!strcmp(s, "R_FIRST")) return (R_FIRST);
534 if (!strcmp(s, "R_IAFTER")) return (R_IAFTER);
535 if (!strcmp(s, "R_IBEFORE")) return (R_IBEFORE);
536 if (!strcmp(s, "R_LAST")) return (R_LAST);
537 if (!strcmp(s, "R_NEXT")) return (R_NEXT);
538 if (!strcmp(s, "R_NOOVERWRITE")) return (R_NOOVERWRITE);
539 if (!strcmp(s, "R_PREV")) return (R_PREV);
540 if (!strcmp(s, "R_SETCURSOR")) return (R_SETCURSOR);
541
542 err("line %lu: %s: unknown flag", lineno, s);
543 /* NOTREACHED */
544}
545
546char *
547sflags(flags)
548 int flags;
549{
550 switch (flags) {
551 case R_CURSOR: return ("R_CURSOR");
552 case R_FIRST: return ("R_FIRST");
553 case R_IAFTER: return ("R_IAFTER");
554 case R_IBEFORE: return ("R_IBEFORE");
555 case R_LAST: return ("R_LAST");
556 case R_NEXT: return ("R_NEXT");
557 case R_NOOVERWRITE: return ("R_NOOVERWRITE");
558 case R_PREV: return ("R_PREV");
559 case R_SETCURSOR: return ("R_SETCURSOR");
560 }
561
562 return ("UNKNOWN!");
563}
564
565DBTYPE
566dbtype(s)
567 char *s;
568{
569 if (!strcmp(s, "btree"))
570 return (DB_BTREE);
571 if (!strcmp(s, "hash"))
572 return (DB_HASH);
573 if (!strcmp(s, "recno"))
574 return (DB_RECNO);
575 err("%s: unknown type (use btree, hash or recno)", s);
576 /* NOTREACHED */
577}
578
579void *
580setinfo(type, s)
581 DBTYPE type;
582 char *s;
583{
584 static BTREEINFO ib;
585 static HASHINFO ih;
586 static RECNOINFO rh;
587 char *eq, *index();
588
589 if ((eq = index(s, '=')) == NULL)
590 err("%s: illegal structure set statement", s);
591 *eq++ = '\0';
592 if (!isdigit(*eq))
593 err("%s: structure set statement must be a number", s);
594
595 switch (type) {
596 case DB_BTREE:
597 if (!strcmp("flags", s)) {
598 ib.flags = atoi(eq);
599 return (&ib);
600 }
601 if (!strcmp("cachesize", s)) {
602 ib.cachesize = atoi(eq);
603 return (&ib);
604 }
605 if (!strcmp("maxkeypage", s)) {
606 ib.maxkeypage = atoi(eq);
607 return (&ib);
608 }
609 if (!strcmp("minkeypage", s)) {
610 ib.minkeypage = atoi(eq);
611 return (&ib);
612 }
613 if (!strcmp("lorder", s)) {
614 ib.lorder = atoi(eq);
615 return (&ib);
616 }
617 if (!strcmp("psize", s)) {
618 ib.psize = atoi(eq);
619 return (&ib);
620 }
621 break;
622 case DB_HASH:
623 if (!strcmp("bsize", s)) {
624 ih.bsize = atoi(eq);
625 return (&ih);
626 }
627 if (!strcmp("ffactor", s)) {
628 ih.ffactor = atoi(eq);
629 return (&ih);
630 }
631 if (!strcmp("nelem", s)) {
632 ih.nelem = atoi(eq);
633 return (&ih);
634 }
635 if (!strcmp("cachesize", s)) {
636 ih.cachesize = atoi(eq);
637 return (&ih);
638 }
639 if (!strcmp("lorder", s)) {
640 ih.lorder = atoi(eq);
641 return (&ih);
642 }
643 break;
644 case DB_RECNO:
645 if (!strcmp("flags", s)) {
646 rh.flags = atoi(eq);
647 return (&rh);
648 }
649 if (!strcmp("cachesize", s)) {
650 rh.cachesize = atoi(eq);
651 return (&rh);
652 }
653 if (!strcmp("lorder", s)) {
654 rh.lorder = atoi(eq);
655 return (&rh);
656 }
657 if (!strcmp("reclen", s)) {
658 rh.reclen = atoi(eq);
659 return (&rh);
660 }
661 if (!strcmp("bval", s)) {
662 rh.bval = atoi(eq);
663 return (&rh);
664 }
665 if (!strcmp("psize", s)) {
666 rh.psize = atoi(eq);
667 return (&rh);
668 }
669 break;
670 }
671 err("%s: unknown structure value", s);
672 /* NOTREACHED */
673}
674
675void *
676rfile(name, lenp)
677 char *name;
678 size_t *lenp;
679{
680 struct stat sb;
681 void *p;
682 int fd;
683 char *np, *index();
684
685 for (; isspace(*name); ++name);
686 if ((np = index(name, '\n')) != NULL)
687 *np = '\0';
688 if ((fd = open(name, O_RDONLY, 0)) < 0 ||
689 fstat(fd, &sb))
690 err("%s: %s\n", name, strerror(errno));
691#ifdef NOT_PORTABLE
692 if (sb.st_size > (off_t)SIZE_T_MAX)
693 err("%s: %s\n", name, strerror(E2BIG));
694#endif
695 if ((p = (void *)malloc((u_int)sb.st_size)) == NULL)
696 err("%s", strerror(errno));
697 (void)read(fd, p, (int)sb.st_size);
698 *lenp = sb.st_size;
699 (void)close(fd);
700 return (p);
701}
702
703void *
704xmalloc(text, len)
705 char *text;
706 size_t len;
707{
708 void *p;
709
710 if ((p = (void *)malloc(len)) == NULL)
711 err("%s", strerror(errno));
712 memmove(p, text, len);
713 return (p);
714}
715
716void
717usage()
718{
719 (void)fprintf(stderr,
720 "usage: dbtest [-l] [-f file] [-i info] [-o file] type script\n");
721 exit(1);
722}
723
724#include <stdarg.h>
725
726void
727err(const char *fmt, ...)
728{
729 va_list ap;
730
731 va_start(ap, fmt);
732 (void)fprintf(stderr, "dbtest: ");
733 (void)vfprintf(stderr, fmt, ap);
734 va_end(ap);
735 (void)fprintf(stderr, "\n");
736 exit(1);
737 /* NOTREACHED */
738}
103 switch (ch) {
104 case 'f':
105 fname = optarg;
106 break;
107 case 'i':
108 infoarg = optarg;
109 break;
110 case 'l':
111 oflags |= DB_LOCK;
112 break;
113 case 'o':
114 if ((ofd = open(optarg,
115 O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
116 err("%s: %s", optarg, strerror(errno));
117 break;
118 case 's':
119 sflag = 1;
120 break;
121 case '?':
122 default:
123 usage();
124 }
125 argc -= optind;
126 argv += optind;
127
128 if (argc != 2)
129 usage();
130
131 /* Set the type. */
132 type = dbtype(*argv++);
133
134 /* Open the descriptor file. */
135 if (strcmp(*argv, "-") && freopen(*argv, "r", stdin) == NULL)
136 err("%s: %s", *argv, strerror(errno));
137
138 /* Set up the db structure as necessary. */
139 if (infoarg == NULL)
140 infop = NULL;
141 else
142 for (p = strtok(infoarg, ",\t "); p != NULL;
143 p = strtok(0, ",\t "))
144 if (*p != '\0')
145 infop = setinfo(type, p);
146
147 /*
148 * Open the DB. Delete any preexisting copy, you almost never
149 * want it around, and it often screws up tests.
150 */
151 if (fname == NULL) {
152 p = getenv("TMPDIR");
153 if (p == NULL)
154 p = "/var/tmp";
155 (void)snprintf(buf, sizeof(buf), "%s/__dbtest", p);
156 fname = buf;
157 (void)unlink(buf);
158 } else if (!sflag)
159 (void)unlink(fname);
160
161 if ((dbp = dbopen(fname,
162 oflags, S_IRUSR | S_IWUSR, type, infop)) == NULL)
163 err("dbopen: %s", strerror(errno));
164 XXdbp = dbp;
165
166 state = COMMAND;
167 for (lineno = 1;
168 (p = fgets(buf, sizeof(buf), stdin)) != NULL; ++lineno) {
169 /* Delete the newline, displaying the key/data is easier. */
170 if (ofd == STDOUT_FILENO && (t = strchr(p, '\n')) != NULL)
171 *t = '\0';
172 if ((len = strlen(buf)) == 0 || isspace(*p) || *p == '#')
173 continue;
174
175 /* Convenient gdb break point. */
176 if (XXlineno == lineno)
177 XXlineno = 1;
178 switch (*p) {
179 case 'c': /* compare */
180 if (state != COMMAND)
181 err("line %lu: not expecting command", lineno);
182 state = KEY;
183 command = COMPARE;
184 break;
185 case 'e': /* echo */
186 if (state != COMMAND)
187 err("line %lu: not expecting command", lineno);
188 /* Don't display the newline, if CR at EOL. */
189 if (p[len - 2] == '\r')
190 --len;
191 if (write(ofd, p + 1, len - 1) != len - 1 ||
192 write(ofd, "\n", 1) != 1)
193 err("write: %s", strerror(errno));
194 break;
195 case 'g': /* get */
196 if (state != COMMAND)
197 err("line %lu: not expecting command", lineno);
198 state = KEY;
199 command = GET;
200 break;
201 case 'p': /* put */
202 if (state != COMMAND)
203 err("line %lu: not expecting command", lineno);
204 state = KEY;
205 command = PUT;
206 break;
207 case 'r': /* remove */
208 if (state != COMMAND)
209 err("line %lu: not expecting command", lineno);
210 if (flags == R_CURSOR) {
211 rem(dbp, &key);
212 state = COMMAND;
213 } else {
214 state = KEY;
215 command = REMOVE;
216 }
217 break;
218 case 'S': /* sync */
219 if (state != COMMAND)
220 err("line %lu: not expecting command", lineno);
221 synk(dbp);
222 state = COMMAND;
223 break;
224 case 's': /* seq */
225 if (state != COMMAND)
226 err("line %lu: not expecting command", lineno);
227 if (flags == R_CURSOR) {
228 state = KEY;
229 command = SEQ;
230 } else
231 seq(dbp, &key);
232 break;
233 case 'f':
234 flags = setflags(p + 1);
235 break;
236 case 'D': /* data file */
237 if (state != DATA)
238 err("line %lu: not expecting data", lineno);
239 data.data = rfile(p + 1, &data.size);
240 goto ldata;
241 case 'd': /* data */
242 if (state != DATA)
243 err("line %lu: not expecting data", lineno);
244 data.data = xmalloc(p + 1, len - 1);
245 data.size = len - 1;
246ldata: switch (command) {
247 case COMPARE:
248 compare(&keydata, &data);
249 break;
250 case PUT:
251 put(dbp, &key, &data);
252 break;
253 default:
254 err("line %lu: command doesn't take data",
255 lineno);
256 }
257 if (type != DB_RECNO)
258 free(key.data);
259 free(data.data);
260 state = COMMAND;
261 break;
262 case 'K': /* key file */
263 if (state != KEY)
264 err("line %lu: not expecting a key", lineno);
265 if (type == DB_RECNO)
266 err("line %lu: 'K' not available for recno",
267 lineno);
268 key.data = rfile(p + 1, &key.size);
269 goto lkey;
270 case 'k': /* key */
271 if (state != KEY)
272 err("line %lu: not expecting a key", lineno);
273 if (type == DB_RECNO) {
274 static recno_t recno;
275 recno = atoi(p + 1);
276 key.data = &recno;
277 key.size = sizeof(recno);
278 } else {
279 key.data = xmalloc(p + 1, len - 1);
280 key.size = len - 1;
281 }
282lkey: switch (command) {
283 case COMPARE:
284 getdata(dbp, &key, &keydata);
285 state = DATA;
286 break;
287 case GET:
288 get(dbp, &key);
289 if (type != DB_RECNO)
290 free(key.data);
291 state = COMMAND;
292 break;
293 case PUT:
294 state = DATA;
295 break;
296 case REMOVE:
297 rem(dbp, &key);
298 if ((type != DB_RECNO) && (flags != R_CURSOR))
299 free(key.data);
300 state = COMMAND;
301 break;
302 case SEQ:
303 seq(dbp, &key);
304 if ((type != DB_RECNO) && (flags != R_CURSOR))
305 free(key.data);
306 state = COMMAND;
307 break;
308 default:
309 err("line %lu: command doesn't take a key",
310 lineno);
311 }
312 break;
313 case 'o':
314 dump(dbp, p[1] == 'r');
315 break;
316 default:
317 err("line %lu: %s: unknown command character",
318 lineno, p);
319 }
320 }
321#ifdef STATISTICS
322 /*
323 * -l must be used (DB_LOCK must be set) for this to be
324 * used, otherwise a page will be locked and it will fail.
325 */
326 if (type == DB_BTREE && oflags & DB_LOCK)
327 __bt_stat(dbp);
328#endif
329 if (dbp->close(dbp))
330 err("db->close: %s", strerror(errno));
331 (void)close(ofd);
332 exit(0);
333}
334
335#define NOOVERWRITE "put failed, would overwrite key\n"
336
337void
338compare(db1, db2)
339 DBT *db1, *db2;
340{
341 size_t len;
342 u_char *p1, *p2;
343
344 if (db1->size != db2->size)
345 printf("compare failed: key->data len %lu != data len %lu\n",
346 db1->size, db2->size);
347
348 len = MIN(db1->size, db2->size);
349 for (p1 = db1->data, p2 = db2->data; len--;)
350 if (*p1++ != *p2++) {
351 printf("compare failed at offset %d\n",
352 p1 - (u_char *)db1->data);
353 break;
354 }
355}
356
357void
358get(dbp, kp)
359 DB *dbp;
360 DBT *kp;
361{
362 DBT data;
363
364 switch (dbp->get(dbp, kp, &data, flags)) {
365 case 0:
366 (void)write(ofd, data.data, data.size);
367 if (ofd == STDOUT_FILENO)
368 (void)write(ofd, "\n", 1);
369 break;
370 case -1:
371 err("line %lu: get: %s", lineno, strerror(errno));
372 /* NOTREACHED */
373 case 1:
374#define NOSUCHKEY "get failed, no such key\n"
375 if (ofd != STDOUT_FILENO)
376 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
377 else
378 (void)fprintf(stderr, "%d: %.*s: %s",
379 lineno, MIN(kp->size, 20), kp->data, NOSUCHKEY);
380#undef NOSUCHKEY
381 break;
382 }
383}
384
385void
386getdata(dbp, kp, dp)
387 DB *dbp;
388 DBT *kp, *dp;
389{
390 switch (dbp->get(dbp, kp, dp, flags)) {
391 case 0:
392 return;
393 case -1:
394 err("line %lu: getdata: %s", lineno, strerror(errno));
395 /* NOTREACHED */
396 case 1:
397 err("line %lu: getdata failed, no such key", lineno);
398 /* NOTREACHED */
399 }
400}
401
402void
403put(dbp, kp, dp)
404 DB *dbp;
405 DBT *kp, *dp;
406{
407 switch (dbp->put(dbp, kp, dp, flags)) {
408 case 0:
409 break;
410 case -1:
411 err("line %lu: put: %s", lineno, strerror(errno));
412 /* NOTREACHED */
413 case 1:
414 (void)write(ofd, NOOVERWRITE, sizeof(NOOVERWRITE) - 1);
415 break;
416 }
417}
418
419void
420rem(dbp, kp)
421 DB *dbp;
422 DBT *kp;
423{
424 switch (dbp->del(dbp, kp, flags)) {
425 case 0:
426 break;
427 case -1:
428 err("line %lu: rem: %s", lineno, strerror(errno));
429 /* NOTREACHED */
430 case 1:
431#define NOSUCHKEY "rem failed, no such key\n"
432 if (ofd != STDOUT_FILENO)
433 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
434 else if (flags != R_CURSOR)
435 (void)fprintf(stderr, "%d: %.*s: %s",
436 lineno, MIN(kp->size, 20), kp->data, NOSUCHKEY);
437 else
438 (void)fprintf(stderr,
439 "%d: rem of cursor failed\n", lineno);
440#undef NOSUCHKEY
441 break;
442 }
443}
444
445void
446synk(dbp)
447 DB *dbp;
448{
449 switch (dbp->sync(dbp, flags)) {
450 case 0:
451 break;
452 case -1:
453 err("line %lu: synk: %s", lineno, strerror(errno));
454 /* NOTREACHED */
455 }
456}
457
458void
459seq(dbp, kp)
460 DB *dbp;
461 DBT *kp;
462{
463 DBT data;
464
465 switch (dbp->seq(dbp, kp, &data, flags)) {
466 case 0:
467 (void)write(ofd, data.data, data.size);
468 if (ofd == STDOUT_FILENO)
469 (void)write(ofd, "\n", 1);
470 break;
471 case -1:
472 err("line %lu: seq: %s", lineno, strerror(errno));
473 /* NOTREACHED */
474 case 1:
475#define NOSUCHKEY "seq failed, no such key\n"
476 if (ofd != STDOUT_FILENO)
477 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
478 else if (flags == R_CURSOR)
479 (void)fprintf(stderr, "%d: %.*s: %s",
480 lineno, MIN(kp->size, 20), kp->data, NOSUCHKEY);
481 else
482 (void)fprintf(stderr,
483 "%d: seq (%s) failed\n", lineno, sflags(flags));
484#undef NOSUCHKEY
485 break;
486 }
487}
488
489void
490dump(dbp, rev)
491 DB *dbp;
492 int rev;
493{
494 DBT key, data;
495 int flags, nflags;
496
497 if (rev) {
498 flags = R_LAST;
499 nflags = R_PREV;
500 } else {
501 flags = R_FIRST;
502 nflags = R_NEXT;
503 }
504 for (;; flags = nflags)
505 switch (dbp->seq(dbp, &key, &data, flags)) {
506 case 0:
507 (void)write(ofd, data.data, data.size);
508 if (ofd == STDOUT_FILENO)
509 (void)write(ofd, "\n", 1);
510 break;
511 case 1:
512 goto done;
513 case -1:
514 err("line %lu: (dump) seq: %s",
515 lineno, strerror(errno));
516 /* NOTREACHED */
517 }
518done: return;
519}
520
521u_int
522setflags(s)
523 char *s;
524{
525 char *p, *index();
526
527 for (; isspace(*s); ++s);
528 if (*s == '\n' || *s == '\0')
529 return (0);
530 if ((p = index(s, '\n')) != NULL)
531 *p = '\0';
532 if (!strcmp(s, "R_CURSOR")) return (R_CURSOR);
533 if (!strcmp(s, "R_FIRST")) return (R_FIRST);
534 if (!strcmp(s, "R_IAFTER")) return (R_IAFTER);
535 if (!strcmp(s, "R_IBEFORE")) return (R_IBEFORE);
536 if (!strcmp(s, "R_LAST")) return (R_LAST);
537 if (!strcmp(s, "R_NEXT")) return (R_NEXT);
538 if (!strcmp(s, "R_NOOVERWRITE")) return (R_NOOVERWRITE);
539 if (!strcmp(s, "R_PREV")) return (R_PREV);
540 if (!strcmp(s, "R_SETCURSOR")) return (R_SETCURSOR);
541
542 err("line %lu: %s: unknown flag", lineno, s);
543 /* NOTREACHED */
544}
545
546char *
547sflags(flags)
548 int flags;
549{
550 switch (flags) {
551 case R_CURSOR: return ("R_CURSOR");
552 case R_FIRST: return ("R_FIRST");
553 case R_IAFTER: return ("R_IAFTER");
554 case R_IBEFORE: return ("R_IBEFORE");
555 case R_LAST: return ("R_LAST");
556 case R_NEXT: return ("R_NEXT");
557 case R_NOOVERWRITE: return ("R_NOOVERWRITE");
558 case R_PREV: return ("R_PREV");
559 case R_SETCURSOR: return ("R_SETCURSOR");
560 }
561
562 return ("UNKNOWN!");
563}
564
565DBTYPE
566dbtype(s)
567 char *s;
568{
569 if (!strcmp(s, "btree"))
570 return (DB_BTREE);
571 if (!strcmp(s, "hash"))
572 return (DB_HASH);
573 if (!strcmp(s, "recno"))
574 return (DB_RECNO);
575 err("%s: unknown type (use btree, hash or recno)", s);
576 /* NOTREACHED */
577}
578
579void *
580setinfo(type, s)
581 DBTYPE type;
582 char *s;
583{
584 static BTREEINFO ib;
585 static HASHINFO ih;
586 static RECNOINFO rh;
587 char *eq, *index();
588
589 if ((eq = index(s, '=')) == NULL)
590 err("%s: illegal structure set statement", s);
591 *eq++ = '\0';
592 if (!isdigit(*eq))
593 err("%s: structure set statement must be a number", s);
594
595 switch (type) {
596 case DB_BTREE:
597 if (!strcmp("flags", s)) {
598 ib.flags = atoi(eq);
599 return (&ib);
600 }
601 if (!strcmp("cachesize", s)) {
602 ib.cachesize = atoi(eq);
603 return (&ib);
604 }
605 if (!strcmp("maxkeypage", s)) {
606 ib.maxkeypage = atoi(eq);
607 return (&ib);
608 }
609 if (!strcmp("minkeypage", s)) {
610 ib.minkeypage = atoi(eq);
611 return (&ib);
612 }
613 if (!strcmp("lorder", s)) {
614 ib.lorder = atoi(eq);
615 return (&ib);
616 }
617 if (!strcmp("psize", s)) {
618 ib.psize = atoi(eq);
619 return (&ib);
620 }
621 break;
622 case DB_HASH:
623 if (!strcmp("bsize", s)) {
624 ih.bsize = atoi(eq);
625 return (&ih);
626 }
627 if (!strcmp("ffactor", s)) {
628 ih.ffactor = atoi(eq);
629 return (&ih);
630 }
631 if (!strcmp("nelem", s)) {
632 ih.nelem = atoi(eq);
633 return (&ih);
634 }
635 if (!strcmp("cachesize", s)) {
636 ih.cachesize = atoi(eq);
637 return (&ih);
638 }
639 if (!strcmp("lorder", s)) {
640 ih.lorder = atoi(eq);
641 return (&ih);
642 }
643 break;
644 case DB_RECNO:
645 if (!strcmp("flags", s)) {
646 rh.flags = atoi(eq);
647 return (&rh);
648 }
649 if (!strcmp("cachesize", s)) {
650 rh.cachesize = atoi(eq);
651 return (&rh);
652 }
653 if (!strcmp("lorder", s)) {
654 rh.lorder = atoi(eq);
655 return (&rh);
656 }
657 if (!strcmp("reclen", s)) {
658 rh.reclen = atoi(eq);
659 return (&rh);
660 }
661 if (!strcmp("bval", s)) {
662 rh.bval = atoi(eq);
663 return (&rh);
664 }
665 if (!strcmp("psize", s)) {
666 rh.psize = atoi(eq);
667 return (&rh);
668 }
669 break;
670 }
671 err("%s: unknown structure value", s);
672 /* NOTREACHED */
673}
674
675void *
676rfile(name, lenp)
677 char *name;
678 size_t *lenp;
679{
680 struct stat sb;
681 void *p;
682 int fd;
683 char *np, *index();
684
685 for (; isspace(*name); ++name);
686 if ((np = index(name, '\n')) != NULL)
687 *np = '\0';
688 if ((fd = open(name, O_RDONLY, 0)) < 0 ||
689 fstat(fd, &sb))
690 err("%s: %s\n", name, strerror(errno));
691#ifdef NOT_PORTABLE
692 if (sb.st_size > (off_t)SIZE_T_MAX)
693 err("%s: %s\n", name, strerror(E2BIG));
694#endif
695 if ((p = (void *)malloc((u_int)sb.st_size)) == NULL)
696 err("%s", strerror(errno));
697 (void)read(fd, p, (int)sb.st_size);
698 *lenp = sb.st_size;
699 (void)close(fd);
700 return (p);
701}
702
703void *
704xmalloc(text, len)
705 char *text;
706 size_t len;
707{
708 void *p;
709
710 if ((p = (void *)malloc(len)) == NULL)
711 err("%s", strerror(errno));
712 memmove(p, text, len);
713 return (p);
714}
715
716void
717usage()
718{
719 (void)fprintf(stderr,
720 "usage: dbtest [-l] [-f file] [-i info] [-o file] type script\n");
721 exit(1);
722}
723
724#include <stdarg.h>
725
726void
727err(const char *fmt, ...)
728{
729 va_list ap;
730
731 va_start(ap, fmt);
732 (void)fprintf(stderr, "dbtest: ");
733 (void)vfprintf(stderr, fmt, ap);
734 va_end(ap);
735 (void)fprintf(stderr, "\n");
736 exit(1);
737 /* NOTREACHED */
738}