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