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