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