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