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