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