xref: /freebsd/lib/libc/db/test/btree.tests/main.c (revision fed1ca4b719c56c930f2259d80663cd34be812bb)
1 /*-
2  * Copyright (c) 1990, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Mike Olson.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 4. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #if defined(LIBC_SCCS) && !defined(lint)
34 static char sccsid[] = "@(#)main.c	8.1 (Berkeley) 6/4/93";
35 #endif /* LIBC_SCCS and not lint */
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
38 
39 #include <sys/param.h>
40 #include <fcntl.h>
41 #include <db.h>
42 #include <errno.h>
43 #include <stdio.h>
44 #include <ctype.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include "btree.h"
48 
49 typedef struct cmd_table {
50 	char *cmd;
51 	int nargs;
52 	int rconv;
53 	void (*func)(DB *, char **);
54 	char *usage, *descrip;
55 } cmd_table;
56 
57 int stopstop;
58 DB *globaldb;
59 
60 void append(DB *, char **);
61 void bstat(DB *, char **);
62 void cursor(DB *, char **);
63 void delcur(DB *, char **);
64 void delete(DB *, char **);
65 void dump(DB *, char **);
66 void first(DB *, char **);
67 void get(DB *, char **);
68 void help(DB *, char **);
69 void iafter(DB *, char **);
70 void ibefore(DB *, char **);
71 void icursor(DB *, char **);
72 void insert(DB *, char **);
73 void keydata(DBT *, DBT *);
74 void last(DB *, char **);
75 void list(DB *, char **);
76 void load(DB *, char **);
77 void mstat(DB *, char **);
78 void next(DB *, char **);
79 int  parse(char *, char **, int);
80 void previous(DB *, char **);
81 void show(DB *, char **);
82 void usage(void);
83 void user(DB *);
84 
85 cmd_table commands[] = {
86 	"?",	0, 0, help, "help", NULL,
87 	"a",	2, 1, append, "append key def", "append key with data def",
88 	"b",	0, 0, bstat, "bstat", "stat btree",
89 	"c",	1, 1, cursor,  "cursor word", "move cursor to word",
90 	"delc",	0, 0, delcur, "delcur", "delete key the cursor references",
91 	"dele",	1, 1, delete, "delete word", "delete word",
92 	"d",	0, 0, dump, "dump", "dump database",
93 	"f",	0, 0, first, "first", "move cursor to first record",
94 	"g",	1, 1, get, "get key", "locate key",
95 	"h",	0, 0, help, "help", "print command summary",
96 	"ia",	2, 1, iafter, "iafter key data", "insert data after key",
97 	"ib",	2, 1, ibefore, "ibefore key data", "insert data before key",
98 	"ic",	2, 1, icursor, "icursor key data", "replace cursor",
99 	"in",	2, 1, insert, "insert key def", "insert key with data def",
100 	"la",	0, 0, last, "last", "move cursor to last record",
101 	"li",	1, 1, list, "list file", "list to a file",
102 	"loa",	1, 0, load, "load file", NULL,
103 	"loc",	1, 1, get, "get key", NULL,
104 	"m",	0, 0, mstat, "mstat", "stat memory pool",
105 	"n",	0, 0, next, "next", "move cursor forward one record",
106 	"p",	0, 0, previous, "previous", "move cursor back one record",
107 	"q",	0, 0, NULL, "quit", "quit",
108 	"sh",	1, 0, show, "show page", "dump a page",
109 	{ NULL },
110 };
111 
112 int recno;					/* use record numbers */
113 char *dict = "words";				/* default dictionary */
114 char *progname;
115 
116 int
117 main(argc, argv)
118 	int argc;
119 	char **argv;
120 {
121 	int c;
122 	DB *db;
123 	BTREEINFO b;
124 
125 	progname = *argv;
126 
127 	b.flags = 0;
128 	b.cachesize = 0;
129 	b.maxkeypage = 0;
130 	b.minkeypage = 0;
131 	b.psize = 0;
132 	b.compare = NULL;
133 	b.prefix = NULL;
134 	b.lorder = 0;
135 
136 	while ((c = getopt(argc, argv, "bc:di:lp:ru")) != -1) {
137 		switch (c) {
138 		case 'b':
139 			b.lorder = BIG_ENDIAN;
140 			break;
141 		case 'c':
142 			b.cachesize = atoi(optarg);
143 			break;
144 		case 'd':
145 			b.flags |= R_DUP;
146 			break;
147 		case 'i':
148 			dict = optarg;
149 			break;
150 		case 'l':
151 			b.lorder = LITTLE_ENDIAN;
152 			break;
153 		case 'p':
154 			b.psize = atoi(optarg);
155 			break;
156 		case 'r':
157 			recno = 1;
158 			break;
159 		case 'u':
160 			b.flags = 0;
161 			break;
162 		default:
163 			usage();
164 		}
165 	}
166 	argc -= optind;
167 	argv += optind;
168 
169 	if (recno)
170 		db = dbopen(*argv == NULL ? NULL : *argv, O_RDWR,
171 		    0, DB_RECNO, NULL);
172 	else
173 		db = dbopen(*argv == NULL ? NULL : *argv, O_CREAT|O_RDWR,
174 		    0600, DB_BTREE, &b);
175 
176 	if (db == NULL) {
177 		(void)fprintf(stderr, "dbopen: %s\n", strerror(errno));
178 		exit(1);
179 	}
180 	globaldb = db;
181 	user(db);
182 	exit(0);
183 	/* NOTREACHED */
184 }
185 
186 void
187 user(db)
188 	DB *db;
189 {
190 	FILE *ifp;
191 	int argc, i, last;
192 	char *lbuf, *argv[4], buf[512];
193 
194 	if ((ifp = fopen("/dev/tty", "r")) == NULL) {
195 		(void)fprintf(stderr,
196 		    "/dev/tty: %s\n", strerror(errno));
197 		exit(1);
198 	}
199 	for (last = 0;;) {
200 		(void)printf("> ");
201 		(void)fflush(stdout);
202 		if ((lbuf = fgets(&buf[0], 512, ifp)) == NULL)
203 			break;
204 		if (lbuf[0] == '\n') {
205 			i = last;
206 			goto uselast;
207 		}
208 		lbuf[strlen(lbuf) - 1] = '\0';
209 
210 		if (lbuf[0] == 'q')
211 			break;
212 
213 		argc = parse(lbuf, &argv[0], 3);
214 		if (argc == 0)
215 			continue;
216 
217 		for (i = 0; commands[i].cmd != NULL; i++)
218 			if (strncmp(commands[i].cmd, argv[0],
219 			    strlen(commands[i].cmd)) == 0)
220 				break;
221 
222 		if (commands[i].cmd == NULL) {
223 			(void)fprintf(stderr,
224 			    "%s: command unknown ('help' for help)\n", lbuf);
225 			continue;
226 		}
227 
228 		if (commands[i].nargs != argc - 1) {
229 			(void)fprintf(stderr, "usage: %s\n", commands[i].usage);
230 			continue;
231 		}
232 
233 		if (recno && commands[i].rconv) {
234 			static recno_t nlong;
235 			nlong = atoi(argv[1]);
236 			argv[1] = (char *)&nlong;
237 		}
238 uselast:	last = i;
239 		(*commands[i].func)(db, argv);
240 	}
241 	if ((db->sync)(db) == RET_ERROR)
242 		perror("dbsync");
243 	else if ((db->close)(db) == RET_ERROR)
244 		perror("dbclose");
245 }
246 
247 int
248 parse(lbuf, argv, maxargc)
249 	char *lbuf, **argv;
250 	int maxargc;
251 {
252 	int argc = 0;
253 	char *c;
254 
255 	c = lbuf;
256 	while (isspace(*c))
257 		c++;
258 	while (*c != '\0' && argc < maxargc) {
259 		*argv++ = c;
260 		argc++;
261 		while (!isspace(*c) && *c != '\0') {
262 			c++;
263 		}
264 		while (isspace(*c))
265 			*c++ = '\0';
266 	}
267 	return (argc);
268 }
269 
270 void
271 append(db, argv)
272 	DB *db;
273 	char **argv;
274 {
275 	DBT key, data;
276 	int status;
277 
278 	if (!recno) {
279 		(void)fprintf(stderr,
280 		    "append only available for recno db's.\n");
281 		return;
282 	}
283 	key.data = argv[1];
284 	key.size = sizeof(recno_t);
285 	data.data = argv[2];
286 	data.size = strlen(data.data);
287 	status = (db->put)(db, &key, &data, R_APPEND);
288 	switch (status) {
289 	case RET_ERROR:
290 		perror("append/put");
291 		break;
292 	case RET_SPECIAL:
293 		(void)printf("%s (duplicate key)\n", argv[1]);
294 		break;
295 	case RET_SUCCESS:
296 		break;
297 	}
298 }
299 
300 void
301 cursor(db, argv)
302 	DB *db;
303 	char **argv;
304 {
305 	DBT data, key;
306 	int status;
307 
308 	key.data = argv[1];
309 	if (recno)
310 		key.size = sizeof(recno_t);
311 	else
312 		key.size = strlen(argv[1]) + 1;
313 	status = (*db->seq)(db, &key, &data, R_CURSOR);
314 	switch (status) {
315 	case RET_ERROR:
316 		perror("cursor/seq");
317 		break;
318 	case RET_SPECIAL:
319 		(void)printf("key not found\n");
320 		break;
321 	case RET_SUCCESS:
322 		keydata(&key, &data);
323 		break;
324 	}
325 }
326 
327 void
328 delcur(db, argv)
329 	DB *db;
330 	char **argv;
331 {
332 	int status;
333 
334 	status = (*db->del)(db, NULL, R_CURSOR);
335 
336 	if (status == RET_ERROR)
337 		perror("delcur/del");
338 }
339 
340 void
341 delete(db, argv)
342 	DB *db;
343 	char **argv;
344 {
345 	DBT key;
346 	int status;
347 
348 	key.data = argv[1];
349 	if (recno)
350 		key.size = sizeof(recno_t);
351 	else
352 		key.size = strlen(argv[1]) + 1;
353 
354 	status = (*db->del)(db, &key, 0);
355 	switch (status) {
356 	case RET_ERROR:
357 		perror("delete/del");
358 		break;
359 	case RET_SPECIAL:
360 		(void)printf("key not found\n");
361 		break;
362 	case RET_SUCCESS:
363 		break;
364 	}
365 }
366 
367 void
368 dump(db, argv)
369 	DB *db;
370 	char **argv;
371 {
372 	__bt_dump(db);
373 }
374 
375 void
376 first(db, argv)
377 	DB *db;
378 	char **argv;
379 {
380 	DBT data, key;
381 	int status;
382 
383 	status = (*db->seq)(db, &key, &data, R_FIRST);
384 
385 	switch (status) {
386 	case RET_ERROR:
387 		perror("first/seq");
388 		break;
389 	case RET_SPECIAL:
390 		(void)printf("no more keys\n");
391 		break;
392 	case RET_SUCCESS:
393 		keydata(&key, &data);
394 		break;
395 	}
396 }
397 
398 void
399 get(db, argv)
400 	DB *db;
401 	char **argv;
402 {
403 	DBT data, key;
404 	int status;
405 
406 	key.data = argv[1];
407 	if (recno)
408 		key.size = sizeof(recno_t);
409 	else
410 		key.size = strlen(argv[1]) + 1;
411 
412 	status = (*db->get)(db, &key, &data, 0);
413 
414 	switch (status) {
415 	case RET_ERROR:
416 		perror("get/get");
417 		break;
418 	case RET_SPECIAL:
419 		(void)printf("key not found\n");
420 		break;
421 	case RET_SUCCESS:
422 		keydata(&key, &data);
423 		break;
424 	}
425 }
426 
427 void
428 help(db, argv)
429 	DB *db;
430 	char **argv;
431 {
432 	int i;
433 
434 	for (i = 0; commands[i].cmd; i++)
435 		if (commands[i].descrip)
436 			(void)printf("%s: %s\n",
437 			    commands[i].usage, commands[i].descrip);
438 }
439 
440 void
441 iafter(db, argv)
442 	DB *db;
443 	char **argv;
444 {
445 	DBT key, data;
446 	int status;
447 
448 	if (!recno) {
449 		(void)fprintf(stderr,
450 		    "iafter only available for recno db's.\n");
451 		return;
452 	}
453 	key.data = argv[1];
454 	key.size = sizeof(recno_t);
455 	data.data = argv[2];
456 	data.size = strlen(data.data);
457 	status = (db->put)(db, &key, &data, R_IAFTER);
458 	switch (status) {
459 	case RET_ERROR:
460 		perror("iafter/put");
461 		break;
462 	case RET_SPECIAL:
463 		(void)printf("%s (duplicate key)\n", argv[1]);
464 		break;
465 	case RET_SUCCESS:
466 		break;
467 	}
468 }
469 
470 void
471 ibefore(db, argv)
472 	DB *db;
473 	char **argv;
474 {
475 	DBT key, data;
476 	int status;
477 
478 	if (!recno) {
479 		(void)fprintf(stderr,
480 		    "ibefore only available for recno db's.\n");
481 		return;
482 	}
483 	key.data = argv[1];
484 	key.size = sizeof(recno_t);
485 	data.data = argv[2];
486 	data.size = strlen(data.data);
487 	status = (db->put)(db, &key, &data, R_IBEFORE);
488 	switch (status) {
489 	case RET_ERROR:
490 		perror("ibefore/put");
491 		break;
492 	case RET_SPECIAL:
493 		(void)printf("%s (duplicate key)\n", argv[1]);
494 		break;
495 	case RET_SUCCESS:
496 		break;
497 	}
498 }
499 
500 void
501 icursor(db, argv)
502 	DB *db;
503 	char **argv;
504 {
505 	int status;
506 	DBT data, key;
507 
508 	key.data = argv[1];
509 	if (recno)
510 		key.size = sizeof(recno_t);
511 	else
512 		key.size = strlen(argv[1]) + 1;
513 	data.data = argv[2];
514 	data.size = strlen(argv[2]) + 1;
515 
516 	status = (*db->put)(db, &key, &data, R_CURSOR);
517 	switch (status) {
518 	case RET_ERROR:
519 		perror("icursor/put");
520 		break;
521 	case RET_SPECIAL:
522 		(void)printf("%s (duplicate key)\n", argv[1]);
523 		break;
524 	case RET_SUCCESS:
525 		break;
526 	}
527 }
528 
529 void
530 insert(db, argv)
531 	DB *db;
532 	char **argv;
533 {
534 	int status;
535 	DBT data, key;
536 
537 	key.data = argv[1];
538 	if (recno)
539 		key.size = sizeof(recno_t);
540 	else
541 		key.size = strlen(argv[1]) + 1;
542 	data.data = argv[2];
543 	data.size = strlen(argv[2]) + 1;
544 
545 	status = (*db->put)(db, &key, &data, R_NOOVERWRITE);
546 	switch (status) {
547 	case RET_ERROR:
548 		perror("insert/put");
549 		break;
550 	case RET_SPECIAL:
551 		(void)printf("%s (duplicate key)\n", argv[1]);
552 		break;
553 	case RET_SUCCESS:
554 		break;
555 	}
556 }
557 
558 void
559 last(db, argv)
560 	DB *db;
561 	char **argv;
562 {
563 	DBT data, key;
564 	int status;
565 
566 	status = (*db->seq)(db, &key, &data, R_LAST);
567 
568 	switch (status) {
569 	case RET_ERROR:
570 		perror("last/seq");
571 		break;
572 	case RET_SPECIAL:
573 		(void)printf("no more keys\n");
574 		break;
575 	case RET_SUCCESS:
576 		keydata(&key, &data);
577 		break;
578 	}
579 }
580 
581 void
582 list(db, argv)
583 	DB *db;
584 	char **argv;
585 {
586 	DBT data, key;
587 	FILE *fp;
588 	int status;
589 
590 	if ((fp = fopen(argv[1], "w")) == NULL) {
591 		(void)fprintf(stderr, "%s: %s\n", argv[1], strerror(errno));
592 		return;
593 	}
594 	status = (*db->seq)(db, &key, &data, R_FIRST);
595 	while (status == RET_SUCCESS) {
596 		(void)fprintf(fp, "%s\n", key.data);
597 		status = (*db->seq)(db, &key, &data, R_NEXT);
598 	}
599 	if (status == RET_ERROR)
600 		perror("list/seq");
601 }
602 
603 DB *BUGdb;
604 void
605 load(db, argv)
606 	DB *db;
607 	char **argv;
608 {
609 	char *p, *t;
610 	FILE *fp;
611 	DBT data, key;
612 	recno_t cnt;
613 	size_t len;
614 	int status;
615 	char *lp, buf[16 * 1024];
616 
617 	BUGdb = db;
618 	if ((fp = fopen(argv[1], "r")) == NULL) {
619 		(void)fprintf(stderr, "%s: %s\n", argv[1], strerror(errno));
620 		return;
621 	}
622 	(void)printf("loading %s...\n", argv[1]);
623 
624 	for (cnt = 1; (lp = fgetline(fp, &len)) != NULL; ++cnt) {
625 		if (recno) {
626 			key.data = &cnt;
627 			key.size = sizeof(recno_t);
628 			data.data = lp;
629 			data.size = len + 1;
630 		} else {
631 			key.data = lp;
632 			key.size = len + 1;
633 			for (p = lp + len - 1, t = buf; p >= lp; *t++ = *p--);
634 			*t = '\0';
635 			data.data = buf;
636 			data.size = len + 1;
637 		}
638 
639 		status = (*db->put)(db, &key, &data, R_NOOVERWRITE);
640 		switch (status) {
641 		case RET_ERROR:
642 			perror("load/put");
643 			exit(1);
644 		case RET_SPECIAL:
645 			if (recno)
646 				(void)fprintf(stderr,
647 				    "duplicate: %ld {%s}\n", cnt, data.data);
648 			else
649 				(void)fprintf(stderr,
650 				    "duplicate: %ld {%s}\n", cnt, key.data);
651 			exit(1);
652 		case RET_SUCCESS:
653 			break;
654 		}
655 	}
656 	(void)fclose(fp);
657 }
658 
659 void
660 next(db, argv)
661 	DB *db;
662 	char **argv;
663 {
664 	DBT data, key;
665 	int status;
666 
667 	status = (*db->seq)(db, &key, &data, R_NEXT);
668 
669 	switch (status) {
670 	case RET_ERROR:
671 		perror("next/seq");
672 		break;
673 	case RET_SPECIAL:
674 		(void)printf("no more keys\n");
675 		break;
676 	case RET_SUCCESS:
677 		keydata(&key, &data);
678 		break;
679 	}
680 }
681 
682 void
683 previous(db, argv)
684 	DB *db;
685 	char **argv;
686 {
687 	DBT data, key;
688 	int status;
689 
690 	status = (*db->seq)(db, &key, &data, R_PREV);
691 
692 	switch (status) {
693 	case RET_ERROR:
694 		perror("previous/seq");
695 		break;
696 	case RET_SPECIAL:
697 		(void)printf("no more keys\n");
698 		break;
699 	case RET_SUCCESS:
700 		keydata(&key, &data);
701 		break;
702 	}
703 }
704 
705 void
706 show(db, argv)
707 	DB *db;
708 	char **argv;
709 {
710 	BTREE *t;
711 	PAGE *h;
712 	pgno_t pg;
713 
714 	pg = atoi(argv[1]);
715 	t = db->internal;
716 	if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL) {
717 		(void)printf("getpage of %ld failed\n", pg);
718 		return;
719 	}
720 	if (pg == 0)
721 		__bt_dmpage(h);
722 	else
723 		__bt_dpage(h);
724 	mpool_put(t->bt_mp, h, 0);
725 }
726 
727 void
728 bstat(db, argv)
729 	DB *db;
730 	char **argv;
731 {
732 	(void)printf("BTREE\n");
733 	__bt_stat(db);
734 }
735 
736 void
737 mstat(db, argv)
738 	DB *db;
739 	char **argv;
740 {
741 	(void)printf("MPOOL\n");
742 	mpool_stat(((BTREE *)db->internal)->bt_mp);
743 }
744 
745 void
746 keydata(key, data)
747 	DBT *key, *data;
748 {
749 	if (!recno && key->size > 0)
750 		(void)printf("%s/", key->data);
751 	if (data->size > 0)
752 		(void)printf("%s", data->data);
753 	(void)printf("\n");
754 }
755 
756 void
757 usage()
758 {
759 	(void)fprintf(stderr,
760 	    "usage: %s [-bdlu] [-c cache] [-i file] [-p page] [file]\n",
761 	    progname);
762 	exit (1);
763 }
764