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