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