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