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