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