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