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