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