]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - lib/libc/db/test/dbtest.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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  * 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
31 static 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)
37 static 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$");
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
56 enum S { COMMAND, COMPARE, GET, PUT, REMOVE, SEQ, SEQFLAG, KEY, DATA };
57
58 void     compare(DBT *, DBT *);
59 DBTYPE   dbtype(char *);
60 void     dump(DB *, int);
61 void     err(const char *, ...) __printflike(1, 2);
62 void     get(DB *, DBT *);
63 void     getdata(DB *, DBT *, DBT *);
64 void     put(DB *, DBT *, DBT *);
65 void     rem(DB *, DBT *);
66 char    *sflags(int);
67 void     synk(DB *);
68 void    *rfile(char *, size_t *);
69 void     seq(DB *, DBT *);
70 u_int    setflags(char *);
71 void    *setinfo(DBTYPE, char *);
72 void     usage(void);
73 void    *xmalloc(char *, size_t);
74
75 DBTYPE type;                            /* Database type. */
76 void *infop;                            /* Iflags. */
77 u_long lineno;                          /* Current line in test script. */
78 u_int flags;                            /* Current DB flags. */
79 int ofd = STDOUT_FILENO;                /* Standard output fd. */
80
81 DB *XXdbp;                              /* Global for gdb. */
82 int XXlineno;                           /* Fast breakpoint for gdb. */
83
84 int
85 main(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")) != -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;
246 ldata:                  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                         }
282 lkey:                   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
337 void
338 compare(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
357 void
358 get(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
385 void
386 getdata(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
402 void
403 put(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
419 void
420 rem(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
445 void
446 synk(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
458 void
459 seq(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
489 void
490 dump(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                 }
518 done:   return;
519 }
520         
521 u_int
522 setflags(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
546 char *
547 sflags(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         
565 DBTYPE
566 dbtype(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
579 void *
580 setinfo(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
675 void *
676 rfile(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
703 void *
704 xmalloc(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
716 void
717 usage()
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
726 void
727 err(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 }