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