Sync usage with man page.
[netbsd-mini2440.git] / regress / lib / libc / db / dbtest.c
blobbfb079b1415d55e00899eec89f729d05cbd935b4
1 /* $NetBSD: dbtest.c,v 1.17 2009/01/25 13:40:05 lukem Exp $ */
3 /*-
4 * Copyright (c) 1992, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
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.
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.
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1992, 1993, 1994\
35 The Regents of the University of California. All rights reserved.");
36 #endif /* not lint */
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)dbtest.c 8.17 (Berkeley) 9/1/94";
41 #else
42 __RCSID("$NetBSD: dbtest.c,v 1.17 2009/01/25 13:40:05 lukem Exp $");
43 #endif
44 #endif /* not lint */
46 #include <sys/param.h>
47 #include <sys/stat.h>
49 #include <ctype.h>
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <limits.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <stdbool.h>
57 #include <unistd.h>
58 #include <err.h>
59 #include <db.h>
61 enum S { COMMAND, COMPARE, GET, PUT, REMOVE, SEQ, SEQFLAG, KEY, DATA };
63 static void compare(DBT *, DBT *);
64 static DBTYPE dbtype(const char *);
65 static void dump(DB *, int);
66 static void get(DB *, DBT *);
67 static void getdata(DB *, DBT *, DBT *);
68 static void put(DB *, DBT *, DBT *);
69 static void rem(DB *, DBT *);
70 static const char *sflags(int);
71 static void synk(DB *);
72 static void *rfile(char *, size_t *);
73 static void seq(DB *, DBT *);
74 static u_int setflags(char *);
75 static void *setinfo(DBTYPE, char *);
76 static void usage(void) __attribute__((__noreturn__));
77 static void *xcopy(void *, size_t);
78 static void chkcmd(enum S);
79 static void chkdata(enum S);
80 static void chkkey(enum S);
82 #ifdef STATISTICS
83 extern void __bt_stat(DB *);
84 #endif
86 static DBTYPE type; /* Database type. */
87 static void *infop; /* Iflags. */
88 static size_t lineno; /* Current line in test script. */
89 static u_int flags; /* Current DB flags. */
90 static int ofd = STDOUT_FILENO; /* Standard output fd. */
92 static DB *XXdbp; /* Global for gdb. */
93 static size_t XXlineno; /* Fast breakpoint for gdb. */
95 int
96 main(int argc, char *argv[])
98 extern int optind;
99 extern char *optarg;
100 enum S command = COMMAND, state;
101 DB *dbp;
102 DBT data, key, keydata;
103 size_t len;
104 int ch, oflags, sflag;
105 char *fname, *infoarg, *p, *t, buf[8 * 1024];
106 bool unlink_dbfile;
108 infoarg = NULL;
109 fname = NULL;
110 unlink_dbfile = false;
111 oflags = O_CREAT | O_RDWR;
112 sflag = 0;
113 while ((ch = getopt(argc, argv, "f:i:lo:s")) != -1)
114 switch (ch) {
115 case 'f':
116 fname = optarg;
117 break;
118 case 'i':
119 infoarg = optarg;
120 break;
121 case 'l':
122 oflags |= DB_LOCK;
123 break;
124 case 'o':
125 if ((ofd = open(optarg,
126 O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
127 err(1, "Cannot create `%s'", optarg);
128 break;
129 case 's':
130 sflag = 1;
131 break;
132 case '?':
133 default:
134 usage();
136 argc -= optind;
137 argv += optind;
139 if (argc != 2)
140 usage();
142 /* Set the type. */
143 type = dbtype(*argv++);
145 /* Open the descriptor file. */
146 if (strcmp(*argv, "-") && freopen(*argv, "r", stdin) == NULL)
147 err(1, "Cannot reopen `%s'", *argv);
149 /* Set up the db structure as necessary. */
150 if (infoarg == NULL)
151 infop = NULL;
152 else
153 for (p = strtok(infoarg, ",\t "); p != NULL;
154 p = strtok(0, ",\t "))
155 if (*p != '\0')
156 infop = setinfo(type, p);
159 * Open the DB. Delete any preexisting copy, you almost never
160 * want it around, and it often screws up tests.
162 if (fname == NULL) {
163 const char *q = getenv("TMPDIR");
164 if (q == NULL)
165 q = "/var/tmp";
166 (void)snprintf(buf, sizeof(buf), "%s/__dbtest", q);
167 fname = buf;
168 (void)unlink(buf);
169 unlink_dbfile = true;
170 } else if (!sflag)
171 (void)unlink(fname);
173 if ((dbp = dbopen(fname,
174 oflags, S_IRUSR | S_IWUSR, type, infop)) == NULL)
175 err(1, "Cannot dbopen `%s'", fname);
176 XXdbp = dbp;
177 if (unlink_dbfile)
178 (void)unlink(fname);
180 state = COMMAND;
181 for (lineno = 1;
182 (p = fgets(buf, sizeof(buf), stdin)) != NULL; ++lineno) {
183 /* Delete the newline, displaying the key/data is easier. */
184 if (ofd == STDOUT_FILENO && (t = strchr(p, '\n')) != NULL)
185 *t = '\0';
186 if ((len = strlen(buf)) == 0 || isspace((unsigned char)*p) ||
187 *p == '#')
188 continue;
190 /* Convenient gdb break point. */
191 if (XXlineno == lineno)
192 XXlineno = 1;
193 switch (*p) {
194 case 'c': /* compare */
195 chkcmd(state);
196 state = KEY;
197 command = COMPARE;
198 break;
199 case 'e': /* echo */
200 chkcmd(state);
201 /* Don't display the newline, if CR at EOL. */
202 if (p[len - 2] == '\r')
203 --len;
204 if ((size_t)write(ofd, p + 1, len - 1) != (len - 1) ||
205 write(ofd, "\n", 1) != 1)
206 err(1, "write failed");
207 break;
208 case 'g': /* get */
209 chkcmd(state);
210 state = KEY;
211 command = GET;
212 break;
213 case 'p': /* put */
214 chkcmd(state);
215 state = KEY;
216 command = PUT;
217 break;
218 case 'r': /* remove */
219 chkcmd(state);
220 if (flags == R_CURSOR) {
221 rem(dbp, &key);
222 state = COMMAND;
223 } else {
224 state = KEY;
225 command = REMOVE;
227 break;
228 case 'S': /* sync */
229 chkcmd(state);
230 synk(dbp);
231 state = COMMAND;
232 break;
233 case 's': /* seq */
234 chkcmd(state);
235 if (flags == R_CURSOR) {
236 state = KEY;
237 command = SEQ;
238 } else
239 seq(dbp, &key);
240 break;
241 case 'f':
242 flags = setflags(p + 1);
243 break;
244 case 'D': /* data file */
245 chkdata(state);
246 data.data = rfile(p + 1, &data.size);
247 goto ldata;
248 case 'd': /* data */
249 chkdata(state);
250 data.data = xcopy(p + 1, len - 1);
251 data.size = len - 1;
252 ldata: switch (command) {
253 case COMPARE:
254 compare(&keydata, &data);
255 break;
256 case PUT:
257 put(dbp, &key, &data);
258 break;
259 default:
260 errx(1, "line %zu: command doesn't take data",
261 lineno);
263 if (type != DB_RECNO)
264 free(key.data);
265 free(data.data);
266 state = COMMAND;
267 break;
268 case 'K': /* key file */
269 chkkey(state);
270 if (type == DB_RECNO)
271 errx(1, "line %zu: 'K' not available for recno",
272 lineno);
273 key.data = rfile(p + 1, &key.size);
274 goto lkey;
275 case 'k': /* key */
276 chkkey(state);
277 if (type == DB_RECNO) {
278 static recno_t recno;
279 recno = atoi(p + 1);
280 key.data = &recno;
281 key.size = sizeof(recno);
282 } else {
283 key.data = xcopy(p + 1, len - 1);
284 key.size = len - 1;
286 lkey: switch (command) {
287 case COMPARE:
288 getdata(dbp, &key, &keydata);
289 state = DATA;
290 break;
291 case GET:
292 get(dbp, &key);
293 if (type != DB_RECNO)
294 free(key.data);
295 state = COMMAND;
296 break;
297 case PUT:
298 state = DATA;
299 break;
300 case REMOVE:
301 rem(dbp, &key);
302 if ((type != DB_RECNO) && (flags != R_CURSOR))
303 free(key.data);
304 state = COMMAND;
305 break;
306 case SEQ:
307 seq(dbp, &key);
308 if ((type != DB_RECNO) && (flags != R_CURSOR))
309 free(key.data);
310 state = COMMAND;
311 break;
312 default:
313 errx(1, "line %zu: command doesn't take a key",
314 lineno);
316 break;
317 case 'o':
318 dump(dbp, p[1] == 'r');
319 break;
320 default:
321 errx(1, "line %zu: %s: unknown command character",
322 lineno, p);
325 #ifdef STATISTICS
327 * -l must be used (DB_LOCK must be set) for this to be
328 * used, otherwise a page will be locked and it will fail.
330 if (type == DB_BTREE && oflags & DB_LOCK)
331 __bt_stat(dbp);
332 #endif
333 if ((*dbp->close)(dbp))
334 err(1, "db->close failed");
335 (void)close(ofd);
336 return 0;
339 #define NOOVERWRITE "put failed, would overwrite key\n"
341 static void
342 compare(DBT *db1, DBT *db2)
344 size_t len;
345 u_char *p1, *p2;
347 if (db1->size != db2->size)
348 printf("compare failed: key->data len %zu != data len %zu\n",
349 db1->size, db2->size);
351 len = MIN(db1->size, db2->size);
352 for (p1 = db1->data, p2 = db2->data; len--;)
353 if (*p1++ != *p2++) {
354 printf("compare failed at offset %lu\n",
355 (unsigned long)(p1 - (u_char *)db1->data));
356 break;
360 static void
361 get(DB *dbp, DBT *kp)
363 DBT data;
365 switch ((*dbp->get)(dbp, kp, &data, flags)) {
366 case 0:
367 (void)write(ofd, data.data, data.size);
368 if (ofd == STDOUT_FILENO)
369 (void)write(ofd, "\n", 1);
370 break;
371 case -1:
372 err(1, "line %zu: get failed", lineno);
373 /* NOTREACHED */
374 case 1:
375 #define NOSUCHKEY "get failed, no such key\n"
376 if (ofd != STDOUT_FILENO)
377 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
378 else
379 (void)fprintf(stderr, "%zu: %.*s: %s",
380 lineno, (int)MIN(kp->size, 20),
381 (const char *)kp->data,
382 NOSUCHKEY);
383 #undef NOSUCHKEY
384 break;
388 static void
389 getdata(DB *dbp, DBT *kp, DBT *dp)
391 switch ((*dbp->get)(dbp, kp, dp, flags)) {
392 case 0:
393 return;
394 case -1:
395 err(1, "line %zu: getdata failed", lineno);
396 /* NOTREACHED */
397 case 1:
398 errx(1, "line %zu: getdata failed, no such key", lineno);
399 /* NOTREACHED */
403 static void
404 put(DB *dbp, DBT *kp, DBT *dp)
406 switch ((*dbp->put)(dbp, kp, dp, flags)) {
407 case 0:
408 break;
409 case -1:
410 err(1, "line %zu: put failed", lineno);
411 /* NOTREACHED */
412 case 1:
413 (void)write(ofd, NOOVERWRITE, sizeof(NOOVERWRITE) - 1);
414 break;
418 static void
419 rem(DB *dbp, DBT *kp)
421 switch ((*dbp->del)(dbp, kp, flags)) {
422 case 0:
423 break;
424 case -1:
425 err(1, "line %zu: rem failed", lineno);
426 /* NOTREACHED */
427 case 1:
428 #define NOSUCHKEY "rem failed, no such key\n"
429 if (ofd != STDOUT_FILENO)
430 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
431 else if (flags != R_CURSOR)
432 (void)fprintf(stderr, "%zu: %.*s: %s",
433 lineno, (int)MIN(kp->size, 20),
434 (const char *)kp->data, NOSUCHKEY);
435 else
436 (void)fprintf(stderr,
437 "%zu: rem of cursor failed\n", lineno);
438 #undef NOSUCHKEY
439 break;
443 static void
444 synk(DB *dbp)
446 switch ((*dbp->sync)(dbp, flags)) {
447 case 0:
448 break;
449 case -1:
450 err(1, "line %zu: synk failed", lineno);
451 /* NOTREACHED */
455 static void
456 seq(DB *dbp, DBT *kp)
458 DBT data;
460 switch (dbp->seq(dbp, kp, &data, flags)) {
461 case 0:
462 (void)write(ofd, data.data, data.size);
463 if (ofd == STDOUT_FILENO)
464 (void)write(ofd, "\n", 1);
465 break;
466 case -1:
467 err(1, "line %zu: seq failed", lineno);
468 /* NOTREACHED */
469 case 1:
470 #define NOSUCHKEY "seq failed, no such key\n"
471 if (ofd != STDOUT_FILENO)
472 (void)write(ofd, NOSUCHKEY, sizeof(NOSUCHKEY) - 1);
473 else if (flags == R_CURSOR)
474 (void)fprintf(stderr, "%zu: %.*s: %s",
475 lineno, (int)MIN(kp->size, 20),
476 (const char *)kp->data, NOSUCHKEY);
477 else
478 (void)fprintf(stderr,
479 "%zu: seq (%s) failed\n", lineno, sflags(flags));
480 #undef NOSUCHKEY
481 break;
485 static void
486 dump(DB *dbp, int rev)
488 DBT key, data;
489 int xflags, nflags;
491 if (rev) {
492 xflags = R_LAST;
493 nflags = R_PREV;
494 } else {
495 xflags = R_FIRST;
496 nflags = R_NEXT;
498 for (;; xflags = nflags)
499 switch (dbp->seq(dbp, &key, &data, xflags)) {
500 case 0:
501 (void)write(ofd, data.data, data.size);
502 if (ofd == STDOUT_FILENO)
503 (void)write(ofd, "\n", 1);
504 break;
505 case 1:
506 goto done;
507 case -1:
508 err(1, "line %zu: (dump) seq failed", lineno);
509 /* NOTREACHED */
511 done: return;
514 static u_int
515 setflags(char *s)
517 char *p;
519 for (; isspace((unsigned char)*s); ++s);
520 if (*s == '\n' || *s == '\0')
521 return 0;
522 if ((p = strchr(s, '\n')) != NULL)
523 *p = '\0';
524 if (!strcmp(s, "R_CURSOR")) return R_CURSOR;
525 if (!strcmp(s, "R_FIRST")) return R_FIRST;
526 if (!strcmp(s, "R_IAFTER")) return R_IAFTER;
527 if (!strcmp(s, "R_IBEFORE")) return R_IBEFORE;
528 if (!strcmp(s, "R_LAST")) return R_LAST;
529 if (!strcmp(s, "R_NEXT")) return R_NEXT;
530 if (!strcmp(s, "R_NOOVERWRITE")) return R_NOOVERWRITE;
531 if (!strcmp(s, "R_PREV")) return R_PREV;
532 if (!strcmp(s, "R_SETCURSOR")) return R_SETCURSOR;
534 errx(1, "line %zu: %s: unknown flag", lineno, s);
535 /* NOTREACHED */
538 static const char *
539 sflags(int xflags)
541 switch (xflags) {
542 case R_CURSOR: return "R_CURSOR";
543 case R_FIRST: return "R_FIRST";
544 case R_IAFTER: return "R_IAFTER";
545 case R_IBEFORE: return "R_IBEFORE";
546 case R_LAST: return "R_LAST";
547 case R_NEXT: return "R_NEXT";
548 case R_NOOVERWRITE: return "R_NOOVERWRITE";
549 case R_PREV: return "R_PREV";
550 case R_SETCURSOR: return "R_SETCURSOR";
553 return "UNKNOWN!";
556 static DBTYPE
557 dbtype(const char *s)
559 if (!strcmp(s, "btree"))
560 return DB_BTREE;
561 if (!strcmp(s, "hash"))
562 return DB_HASH;
563 if (!strcmp(s, "recno"))
564 return DB_RECNO;
565 errx(1, "%s: unknown type (use btree, hash or recno)", s);
566 /* NOTREACHED */
569 static void *
570 setinfo(DBTYPE dtype, char *s)
572 static BTREEINFO ib;
573 static HASHINFO ih;
574 static RECNOINFO rh;
575 char *eq;
577 if ((eq = strchr(s, '=')) == NULL)
578 errx(1, "%s: illegal structure set statement", s);
579 *eq++ = '\0';
580 if (!isdigit((unsigned char)*eq))
581 errx(1, "%s: structure set statement must be a number", s);
583 switch (dtype) {
584 case DB_BTREE:
585 if (!strcmp("flags", s)) {
586 ib.flags = atoi(eq);
587 return &ib;
589 if (!strcmp("cachesize", s)) {
590 ib.cachesize = atoi(eq);
591 return &ib;
593 if (!strcmp("maxkeypage", s)) {
594 ib.maxkeypage = atoi(eq);
595 return &ib;
597 if (!strcmp("minkeypage", s)) {
598 ib.minkeypage = atoi(eq);
599 return &ib;
601 if (!strcmp("lorder", s)) {
602 ib.lorder = atoi(eq);
603 return &ib;
605 if (!strcmp("psize", s)) {
606 ib.psize = atoi(eq);
607 return &ib;
609 break;
610 case DB_HASH:
611 if (!strcmp("bsize", s)) {
612 ih.bsize = atoi(eq);
613 return &ih;
615 if (!strcmp("ffactor", s)) {
616 ih.ffactor = atoi(eq);
617 return &ih;
619 if (!strcmp("nelem", s)) {
620 ih.nelem = atoi(eq);
621 return &ih;
623 if (!strcmp("cachesize", s)) {
624 ih.cachesize = atoi(eq);
625 return &ih;
627 if (!strcmp("lorder", s)) {
628 ih.lorder = atoi(eq);
629 return &ih;
631 break;
632 case DB_RECNO:
633 if (!strcmp("flags", s)) {
634 rh.flags = atoi(eq);
635 return &rh;
637 if (!strcmp("cachesize", s)) {
638 rh.cachesize = atoi(eq);
639 return &rh;
641 if (!strcmp("lorder", s)) {
642 rh.lorder = atoi(eq);
643 return &rh;
645 if (!strcmp("reclen", s)) {
646 rh.reclen = atoi(eq);
647 return &rh;
649 if (!strcmp("bval", s)) {
650 rh.bval = atoi(eq);
651 return &rh;
653 if (!strcmp("psize", s)) {
654 rh.psize = atoi(eq);
655 return &rh;
657 break;
659 errx(1, "%s: unknown structure value", s);
660 /* NOTREACHED */
663 static void *
664 rfile(char *name, size_t *lenp)
666 struct stat sb;
667 void *p;
668 int fd;
669 char *np;
671 for (; isspace((unsigned char)*name); ++name)
672 continue;
673 if ((np = strchr(name, '\n')) != NULL)
674 *np = '\0';
675 if ((fd = open(name, O_RDONLY, 0)) == -1 || fstat(fd, &sb) == -1)
676 err(1, "Cannot open `%s'", name);
677 #ifdef NOT_PORTABLE
678 if (sb.st_size > (off_t)SIZE_T_MAX) {
679 errno = E2BIG;
680 err("Cannot process `%s'", name);
682 #endif
683 if ((p = malloc((size_t)sb.st_size)) == NULL)
684 err(1, "Cannot allocate %zu bytes", (size_t)sb.st_size);
685 if (read(fd, p, (ssize_t)sb.st_size) != (ssize_t)sb.st_size)
686 err(1, "read failed");
687 *lenp = (size_t)sb.st_size;
688 (void)close(fd);
689 return p;
692 static void *
693 xcopy(void *text, size_t len)
695 void *p;
697 if ((p = malloc(len)) == NULL)
698 err(1, "Cannot allocate %zu bytes", len);
699 (void)memmove(p, text, len);
700 return p;
703 static void
704 chkcmd(enum S state)
706 if (state != COMMAND)
707 errx(1, "line %zu: not expecting command", lineno);
710 static void
711 chkdata(enum S state)
713 if (state != DATA)
714 errx(1, "line %zu: not expecting data", lineno);
717 static void
718 chkkey(enum S state)
720 if (state != KEY)
721 errx(1, "line %zu: not expecting a key", lineno);
724 static void
725 usage(void)
727 (void)fprintf(stderr,
728 "Usage: %s [-l] [-f file] [-i info] [-o file] type script\n",
729 getprogname());
730 exit(1);