xref: /freebsd/crypto/krb5/src/plugins/kdb/db2/libdb2/test/btree.tests/main.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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  * Copyright (C) 2016 by the Massachusetts Institute of Technology.
38  * All rights reserved.
39  *
40  * Redistribution and use in source and binary forms, with or without
41  * modification, are permitted provided that the following conditions
42  * are met:
43  *
44  * * Redistributions of source code must retain the above copyright
45  *   notice, this list of conditions and the following disclaimer.
46  *
47  * * Redistributions in binary form must reproduce the above copyright
48  *   notice, this list of conditions and the following disclaimer in
49  *   the documentation and/or other materials provided with the
50  *   distribution.
51  *
52  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
53  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
54  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
55  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
56  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
57  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
58  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
59  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
61  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
62  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
63  * OF THE POSSIBILITY OF SUCH DAMAGE.
64  */
65 
66 #if defined(LIBC_SCCS) && !defined(lint)
67 static char sccsid[] = "@(#)main.c	8.1 (Berkeley) 6/4/93";
68 #endif /* LIBC_SCCS and not lint */
69 
70 #include <sys/param.h>
71 #include <fcntl.h>
72 #include "db-int.h"
73 #include <errno.h>
74 #include <stdio.h>
75 #include <ctype.h>
76 #include <stdlib.h>
77 #include <string.h>
78 #include "btree.h"
79 
80 typedef struct cmd_table {
81 	char *cmd;
82 	int nargs;
83 	int rconv;
84 	void (*func) __P((DB *, char **));
85 	char *usage, *descrip;
86 } cmd_table;
87 
88 int stopstop;
89 DB *globaldb;
90 
91 #if 0
92 void append	__P((DB *, char **));
93 #endif
94 #ifdef STATISTICS
95 void bstat	__P((DB *, char **));
96 #endif
97 void cursor	__P((DB *, char **));
98 void delcur	__P((DB *, char **));
99 void delete	__P((DB *, char **));
100 #ifdef DEBUG
101 void dump	__P((DB *, char **));
102 #endif
103 void first	__P((DB *, char **));
104 void get	__P((DB *, char **));
105 void help	__P((DB *, char **));
106 void iafter	__P((DB *, char **));
107 void ibefore	__P((DB *, char **));
108 void icursor	__P((DB *, char **));
109 void insert	__P((DB *, char **));
110 void keydata	__P((DBT *, DBT *));
111 void last	__P((DB *, char **));
112 void list	__P((DB *, char **));
113 #if 0
114 void load	__P((DB *, char **));
115 #endif
116 #ifdef STATISTICS
117 void mstat	__P((DB *, char **));
118 #endif
119 void next	__P((DB *, char **));
120 int  parse	__P((char *, char **, int));
121 void previous	__P((DB *, char **));
122 #ifdef DEBUG
123 void show	__P((DB *, char **));
124 #endif
125 void rlist	__P((DB *, char **));
126 void rnext	__P((DB *, char **));
127 void rprev	__P((DB *, char **));
128 void usage	__P((void));
129 void user	__P((DB *));
130 void unlinkpg	__P((DB *, char **));
131 
132 cmd_table commands[] = {
133 	"?",	0, 0, help, "help", NULL,
134 #if 0
135 	"a",	2, 1, append, "append key def", "append key with data def",
136 #endif
137 #ifdef STATISTICS
138 	"b",	0, 0, bstat, "bstat", "stat btree",
139 #endif
140 	"c",	1, 1, cursor,  "cursor word", "move cursor to word",
141 	"delc",	0, 0, delcur, "delcur", "delete key the cursor references",
142 	"dele",	1, 1, delete, "delete word", "delete word",
143 #ifdef DEBUG
144 	"d",	0, 0, dump, "dump", "dump database",
145 #endif
146 	"f",	0, 0, first, "first", "move cursor to first record",
147 	"g",	1, 1, get, "get key", "locate key",
148 	"h",	0, 0, help, "help", "print command summary",
149 	"ia",	2, 1, iafter, "iafter key data", "insert data after key",
150 	"ib",	2, 1, ibefore, "ibefore key data", "insert data before key",
151 	"ic",	2, 1, icursor, "icursor key data", "replace cursor",
152 	"in",	2, 1, insert, "insert key def", "insert key with data def",
153 	"la",	0, 0, last, "last", "move cursor to last record",
154 	"li",	1, 1, list, "list file", "list to a file",
155 #if 0
156 	"loa",	1, 0, load, "load file", NULL,
157 #endif
158 	"loc",	1, 1, get, "get key", NULL,
159 #ifdef STATISTICS
160 	"m",	0, 0, mstat, "mstat", "stat memory pool",
161 #endif
162 	"n",	0, 0, next, "next", "move cursor forward one record",
163 	"p",	0, 0, previous, "previous", "move cursor back one record",
164 	"q",	0, 0, NULL, "quit", "quit",
165 	"rli",	1, 1, rlist, "rlist file", "list to a file (recursive)",
166 	"rn",	0, 0, rnext, "rnext", "move cursor forward one record (recursive)",
167 	"rp",	0, 0, rprev, "rprev", "move cursor back one record (recursive)",
168 #ifdef DEBUG
169 	"sh",	1, 0, show, "show page", "dump a page",
170 #endif
171 	"u",	1, 0, unlinkpg, "unlink pgno|internal|leaf", "unlink a page",
172 
173 	{ NULL },
174 };
175 
176 int recno;					/* use record numbers */
177 char *dict = "words";				/* default dictionary */
178 char *progname;
179 
180 int
main(argc,argv)181 main(argc, argv)
182 	int argc;
183 	char **argv;
184 {
185 	int c;
186 	int omode;
187 	DB *db;
188 	BTREEINFO b;
189 
190 	progname = *argv;
191 
192 	omode = O_RDONLY;
193 	b.flags = 0;
194 	b.cachesize = 0;
195 	b.maxkeypage = 0;
196 	b.minkeypage = 0;
197 	b.psize = 0;
198 	b.compare = NULL;
199 	b.prefix = NULL;
200 	b.lorder = 0;
201 
202 	while ((c = getopt(argc, argv, "bc:di:lp:ruw")) != -1) {
203 		switch (c) {
204 		case 'b':
205 			b.lorder = DB_BIG_ENDIAN;
206 			break;
207 		case 'c':
208 			b.cachesize = atoi(optarg);
209 			break;
210 		case 'd':
211 			b.flags |= R_DUP;
212 			break;
213 		case 'i':
214 			dict = optarg;
215 			break;
216 		case 'l':
217 			b.lorder = DB_LITTLE_ENDIAN;
218 			break;
219 		case 'p':
220 			b.psize = atoi(optarg);
221 			break;
222 		case 'r':
223 			recno = 1;
224 			break;
225 		case 'u':
226 			b.flags = 0;
227 			break;
228 		case 'w':
229 			omode = O_RDWR;
230 			break;
231 		default:
232 			usage();
233 		}
234 	}
235 	argc -= optind;
236 	argv += optind;
237 
238 	if (recno)
239 		db = dbopen(*argv == NULL ? NULL : *argv, omode|O_BINARY,
240 		    0, DB_RECNO, NULL);
241 	else
242 		db = dbopen(*argv == NULL ? NULL : *argv, O_CREAT|omode|O_BINARY,
243 		    0600, DB_BTREE, &b);
244 
245 	if (db == NULL) {
246 		(void)fprintf(stderr, "dbopen: %s\n", strerror(errno));
247 		exit(1);
248 	}
249 	globaldb = db;
250 	user(db);
251 	exit(0);
252 	/* NOTREACHED */
253 }
254 
255 void
user(db)256 user(db)
257 	DB *db;
258 {
259 	FILE *ifp;
260 	int argc, i, last;
261 	char *lbuf, *argv[4], buf[512];
262 
263 	if ((ifp = fopen("/dev/tty", "r")) == NULL) {
264 		(void)fprintf(stderr,
265 		    "/dev/tty: %s\n", strerror(errno));
266 		exit(1);
267 	}
268 	for (last = 0;;) {
269 		(void)printf("> ");
270 		(void)fflush(stdout);
271 		if ((lbuf = fgets(&buf[0], 512, ifp)) == NULL) {
272 			(void)printf("\n");
273 			if (ferror(ifp) && errno == EINTR) {
274 				clearerr(ifp);
275 				continue;
276 			}
277 			break;
278 		}
279 		if (lbuf[0] == '\n') {
280 			i = last;
281 			goto uselast;
282 		}
283 		lbuf[strlen(lbuf) - 1] = '\0';
284 
285 		if (lbuf[0] == 'q')
286 			break;
287 
288 		argc = parse(lbuf, &argv[0], 3);
289 		if (argc == 0)
290 			continue;
291 
292 		for (i = 0; commands[i].cmd != NULL; i++)
293 			if (strncmp(commands[i].cmd, argv[0],
294 			    strlen(commands[i].cmd)) == 0)
295 				break;
296 
297 		if (commands[i].cmd == NULL) {
298 			(void)fprintf(stderr,
299 			    "%s: command unknown ('help' for help)\n", lbuf);
300 			continue;
301 		}
302 
303 		if (commands[i].nargs != argc - 1) {
304 			(void)fprintf(stderr, "usage: %s\n", commands[i].usage);
305 			continue;
306 		}
307 
308 		if (recno && commands[i].rconv) {
309 			static recno_t nlong;
310 			nlong = atoi(argv[1]);
311 			argv[1] = (char *)&nlong;
312 		}
313 uselast:	last = i;
314 		(*commands[i].func)(db, argv);
315 	}
316 	if ((db->sync)(db, 0) == RET_ERROR)
317 		perror("dbsync");
318 	else if ((db->close)(db) == RET_ERROR)
319 		perror("dbclose");
320 }
321 
322 int
parse(lbuf,argv,maxargc)323 parse(lbuf, argv, maxargc)
324 	char *lbuf, **argv;
325 	int maxargc;
326 {
327 	int argc = 0;
328 	char *c;
329 
330 	c = lbuf;
331 	while (isspace(*c))
332 		c++;
333 	while (*c != '\0' && argc < maxargc) {
334 		*argv++ = c;
335 		argc++;
336 		while (!isspace(*c) && *c != '\0') {
337 			c++;
338 		}
339 		while (isspace(*c))
340 			*c++ = '\0';
341 	}
342 	return (argc);
343 }
344 
345 #if 0
346 void
347 append(db, argv)
348 	DB *db;
349 	char **argv;
350 {
351 	DBT key, data;
352 	int status;
353 
354 	if (!recno) {
355 		(void)fprintf(stderr,
356 		    "append only available for recno db's.\n");
357 		return;
358 	}
359 	key.data = argv[1];
360 	key.size = sizeof(recno_t);
361 	data.data = argv[2];
362 	data.size = strlen(data.data);
363 	status = (db->put)(db, &key, &data, R_APPEND);
364 	switch (status) {
365 	case RET_ERROR:
366 		perror("append/put");
367 		break;
368 	case RET_SPECIAL:
369 		(void)printf("%s (duplicate key)\n", argv[1]);
370 		break;
371 	case RET_SUCCESS:
372 		break;
373 	}
374 }
375 #endif
376 
377 void
cursor(db,argv)378 cursor(db, argv)
379 	DB *db;
380 	char **argv;
381 {
382 	DBT data, key;
383 	int status;
384 
385 	key.data = argv[1];
386 	if (recno)
387 		key.size = sizeof(recno_t);
388 	else
389 		key.size = strlen(argv[1]) + 1;
390 	status = (*db->seq)(db, &key, &data, R_CURSOR);
391 	switch (status) {
392 	case RET_ERROR:
393 		perror("cursor/seq");
394 		break;
395 	case RET_SPECIAL:
396 		(void)printf("key not found\n");
397 		break;
398 	case RET_SUCCESS:
399 		keydata(&key, &data);
400 		break;
401 	}
402 }
403 
404 void
delcur(db,argv)405 delcur(db, argv)
406 	DB *db;
407 	char **argv;
408 {
409 	int status;
410 
411 	status = (*db->del)(db, NULL, R_CURSOR);
412 
413 	if (status == RET_ERROR)
414 		perror("delcur/del");
415 }
416 
417 void
delete(db,argv)418 delete(db, argv)
419 	DB *db;
420 	char **argv;
421 {
422 	DBT key;
423 	int status;
424 
425 	key.data = argv[1];
426 	if (recno)
427 		key.size = sizeof(recno_t);
428 	else
429 		key.size = strlen(argv[1]) + 1;
430 
431 	status = (*db->del)(db, &key, 0);
432 	switch (status) {
433 	case RET_ERROR:
434 		perror("delete/del");
435 		break;
436 	case RET_SPECIAL:
437 		(void)printf("key not found\n");
438 		break;
439 	case RET_SUCCESS:
440 		break;
441 	}
442 }
443 
444 #ifdef DEBUG
445 void
dump(db,argv)446 dump(db, argv)
447 	DB *db;
448 	char **argv;
449 {
450 	__bt_dump(db);
451 }
452 #endif
453 
454 void
first(db,argv)455 first(db, argv)
456 	DB *db;
457 	char **argv;
458 {
459 	DBT data, key;
460 	int status;
461 
462 	status = (*db->seq)(db, &key, &data, R_FIRST);
463 
464 	switch (status) {
465 	case RET_ERROR:
466 		perror("first/seq");
467 		break;
468 	case RET_SPECIAL:
469 		(void)printf("no more keys\n");
470 		break;
471 	case RET_SUCCESS:
472 		keydata(&key, &data);
473 		break;
474 	}
475 }
476 
477 void
get(db,argv)478 get(db, argv)
479 	DB *db;
480 	char **argv;
481 {
482 	DBT data, key;
483 	int status;
484 
485 	key.data = argv[1];
486 	if (recno)
487 		key.size = sizeof(recno_t);
488 	else
489 		key.size = strlen(argv[1]) + 1;
490 
491 	status = (*db->get)(db, &key, &data, 0);
492 
493 	switch (status) {
494 	case RET_ERROR:
495 		perror("get/get");
496 		break;
497 	case RET_SPECIAL:
498 		(void)printf("key not found\n");
499 		break;
500 	case RET_SUCCESS:
501 		keydata(&key, &data);
502 		break;
503 	}
504 }
505 
506 void
help(db,argv)507 help(db, argv)
508 	DB *db;
509 	char **argv;
510 {
511 	int i;
512 
513 	for (i = 0; commands[i].cmd; i++)
514 		if (commands[i].descrip)
515 			(void)printf("%s: %s\n",
516 			    commands[i].usage, commands[i].descrip);
517 }
518 
519 void
iafter(db,argv)520 iafter(db, argv)
521 	DB *db;
522 	char **argv;
523 {
524 	DBT key, data;
525 	int status;
526 
527 	if (!recno) {
528 		(void)fprintf(stderr,
529 		    "iafter only available for recno db's.\n");
530 		return;
531 	}
532 	key.data = argv[1];
533 	key.size = sizeof(recno_t);
534 	data.data = argv[2];
535 	data.size = strlen(data.data);
536 	status = (db->put)(db, &key, &data, R_IAFTER);
537 	switch (status) {
538 	case RET_ERROR:
539 		perror("iafter/put");
540 		break;
541 	case RET_SPECIAL:
542 		(void)printf("%s (duplicate key)\n", argv[1]);
543 		break;
544 	case RET_SUCCESS:
545 		break;
546 	}
547 }
548 
549 void
ibefore(db,argv)550 ibefore(db, argv)
551 	DB *db;
552 	char **argv;
553 {
554 	DBT key, data;
555 	int status;
556 
557 	if (!recno) {
558 		(void)fprintf(stderr,
559 		    "ibefore only available for recno db's.\n");
560 		return;
561 	}
562 	key.data = argv[1];
563 	key.size = sizeof(recno_t);
564 	data.data = argv[2];
565 	data.size = strlen(data.data);
566 	status = (db->put)(db, &key, &data, R_IBEFORE);
567 	switch (status) {
568 	case RET_ERROR:
569 		perror("ibefore/put");
570 		break;
571 	case RET_SPECIAL:
572 		(void)printf("%s (duplicate key)\n", argv[1]);
573 		break;
574 	case RET_SUCCESS:
575 		break;
576 	}
577 }
578 
579 void
icursor(db,argv)580 icursor(db, argv)
581 	DB *db;
582 	char **argv;
583 {
584 	int status;
585 	DBT data, key;
586 
587 	key.data = argv[1];
588 	if (recno)
589 		key.size = sizeof(recno_t);
590 	else
591 		key.size = strlen(argv[1]) + 1;
592 	data.data = argv[2];
593 	data.size = strlen(argv[2]) + 1;
594 
595 	status = (*db->put)(db, &key, &data, R_CURSOR);
596 	switch (status) {
597 	case RET_ERROR:
598 		perror("icursor/put");
599 		break;
600 	case RET_SPECIAL:
601 		(void)printf("%s (duplicate key)\n", argv[1]);
602 		break;
603 	case RET_SUCCESS:
604 		break;
605 	}
606 }
607 
608 void
insert(db,argv)609 insert(db, argv)
610 	DB *db;
611 	char **argv;
612 {
613 	int status;
614 	DBT data, key;
615 
616 	key.data = argv[1];
617 	if (recno)
618 		key.size = sizeof(recno_t);
619 	else
620 		key.size = strlen(argv[1]) + 1;
621 	data.data = argv[2];
622 	data.size = strlen(argv[2]) + 1;
623 
624 	status = (*db->put)(db, &key, &data, R_NOOVERWRITE);
625 	switch (status) {
626 	case RET_ERROR:
627 		perror("insert/put");
628 		break;
629 	case RET_SPECIAL:
630 		(void)printf("%s (duplicate key)\n", argv[1]);
631 		break;
632 	case RET_SUCCESS:
633 		break;
634 	}
635 }
636 
637 void
last(db,argv)638 last(db, argv)
639 	DB *db;
640 	char **argv;
641 {
642 	DBT data, key;
643 	int status;
644 
645 	status = (*db->seq)(db, &key, &data, R_LAST);
646 
647 	switch (status) {
648 	case RET_ERROR:
649 		perror("last/seq");
650 		break;
651 	case RET_SPECIAL:
652 		(void)printf("no more keys\n");
653 		break;
654 	case RET_SUCCESS:
655 		keydata(&key, &data);
656 		break;
657 	}
658 }
659 
660 void
list(db,argv)661 list(db, argv)
662 	DB *db;
663 	char **argv;
664 {
665 	DBT data, key;
666 	FILE *fp;
667 	int status;
668 
669 	if ((fp = fopen(argv[1], "w")) == NULL) {
670 		(void)fprintf(stderr, "%s: %s\n", argv[1], strerror(errno));
671 		return;
672 	}
673 	status = (*db->seq)(db, &key, &data, R_FIRST);
674 	while (status == RET_SUCCESS) {
675 		(void)fprintf(fp, "%.*s\n", (int)key.size, key.data);
676 		status = (*db->seq)(db, &key, &data, R_NEXT);
677 	}
678 	(void)fclose(fp);
679 	if (status == RET_ERROR)
680 		perror("list/seq");
681 }
682 
683 void
rlist(db,argv)684 rlist(db, argv)
685 	DB *db;
686 	char **argv;
687 {
688 	DBT data, key;
689 	FILE *fp;
690 	int status;
691 
692 	if ((fp = fopen(argv[1], "w")) == NULL) {
693 		(void)fprintf(stderr, "%s: %s\n", argv[1], strerror(errno));
694 		return;
695 	}
696 	status = (*db->seq)(db, &key, &data, R_FIRST);
697 	while (status == RET_SUCCESS) {
698 		(void)fprintf(fp, "%.*s\n", (int)key.size, key.data);
699 		status = (*db->seq)(db, &key, &data, R_RNEXT);
700 	}
701 	(void)fclose(fp);
702 	if (status == RET_ERROR)
703 		perror("list/seq");
704 }
705 
706 #if 0
707 DB *BUGdb;
708 void
709 load(db, argv)
710 	DB *db;
711 	char **argv;
712 {
713 	char *p, *t;
714 	FILE *fp;
715 	DBT data, key;
716 	recno_t cnt;
717 	size_t len;
718 	int status;
719 	char *lp, buf[16 * 1024];
720 
721 	BUGdb = db;
722 	if ((fp = fopen(argv[1], "r")) == NULL) {
723 		(void)fprintf(stderr, "%s: %s\n", argv[1], strerror(errno));
724 		return;
725 	}
726 	(void)printf("loading %s...\n", argv[1]);
727 
728 	for (cnt = 1; (lp = fgetline(fp, &len)) != NULL; ++cnt) {
729 		if (recno) {
730 			key.data = &cnt;
731 			key.size = sizeof(recno_t);
732 			data.data = lp;
733 			data.size = len + 1;
734 		} else {
735 			key.data = lp;
736 			key.size = len + 1;
737 			for (p = lp + len - 1, t = buf; p >= lp; *t++ = *p--);
738 			*t = '\0';
739 			data.data = buf;
740 			data.size = len + 1;
741 		}
742 
743 		status = (*db->put)(db, &key, &data, R_NOOVERWRITE);
744 		switch (status) {
745 		case RET_ERROR:
746 			perror("load/put");
747 			exit(1);
748 		case RET_SPECIAL:
749 			if (recno)
750 				(void)fprintf(stderr,
751 				    "duplicate: %ld {%s}\n", cnt, data.data);
752 			else
753 				(void)fprintf(stderr,
754 				    "duplicate: %ld {%s}\n", cnt, key.data);
755 			exit(1);
756 		case RET_SUCCESS:
757 			break;
758 		}
759 	}
760 	(void)fclose(fp);
761 }
762 #endif
763 
764 void
next(db,argv)765 next(db, argv)
766 	DB *db;
767 	char **argv;
768 {
769 	DBT data, key;
770 	int status;
771 
772 	status = (*db->seq)(db, &key, &data, R_NEXT);
773 
774 	switch (status) {
775 	case RET_ERROR:
776 		perror("next/seq");
777 		break;
778 	case RET_SPECIAL:
779 		(void)printf("no more keys\n");
780 		break;
781 	case RET_SUCCESS:
782 		keydata(&key, &data);
783 		break;
784 	}
785 }
786 
787 void
previous(db,argv)788 previous(db, argv)
789 	DB *db;
790 	char **argv;
791 {
792 	DBT data, key;
793 	int status;
794 
795 	status = (*db->seq)(db, &key, &data, R_PREV);
796 
797 	switch (status) {
798 	case RET_ERROR:
799 		perror("previous/seq");
800 		break;
801 	case RET_SPECIAL:
802 		(void)printf("no more keys\n");
803 		break;
804 	case RET_SUCCESS:
805 		keydata(&key, &data);
806 		break;
807 	}
808 }
809 
810 void
rnext(db,argv)811 rnext(db, argv)
812 	DB *db;
813 	char **argv;
814 {
815 	DBT data, key;
816 	int status;
817 
818 	status = (*db->seq)(db, &key, &data, R_RNEXT);
819 
820 	switch (status) {
821 	case RET_ERROR:
822 		perror("rnext/seq");
823 		break;
824 	case RET_SPECIAL:
825 		(void)printf("no more keys\n");
826 		break;
827 	case RET_SUCCESS:
828 		keydata(&key, &data);
829 		break;
830 	}
831 }
832 
833 void
rprev(db,argv)834 rprev(db, argv)
835 	DB *db;
836 	char **argv;
837 {
838 	DBT data, key;
839 	int status;
840 
841 	status = (*db->seq)(db, &key, &data, R_RPREV);
842 
843 	switch (status) {
844 	case RET_ERROR:
845 		perror("rprev/seq");
846 		break;
847 	case RET_SPECIAL:
848 		(void)printf("no more keys\n");
849 		break;
850 	case RET_SUCCESS:
851 		keydata(&key, &data);
852 		break;
853 	}
854 }
855 
856 #ifdef DEBUG
857 void
show(db,argv)858 show(db, argv)
859 	DB *db;
860 	char **argv;
861 {
862 	BTREE *t;
863 	PAGE *h;
864 	db_pgno_t pg;
865 
866 	pg = atoi(argv[1]);
867 	t = db->internal;
868 	if ((h = mpool_get(t->bt_mp, pg, MPOOL_IGNOREPIN)) == NULL) {
869 		(void)printf("getpage of %ld failed\n", pg);
870 		return;
871 	}
872 	if (pg == 0)
873 		__bt_dmpage(h);
874 	else
875 		__bt_dpage(db, h);
876 }
877 #endif
878 
879 #ifdef STATISTICS
880 void
bstat(db,argv)881 bstat(db, argv)
882 	DB *db;
883 	char **argv;
884 {
885 	(void)printf("BTREE\n");
886 	__bt_stat(db);
887 }
888 
889 void
mstat(db,argv)890 mstat(db, argv)
891 	DB *db;
892 	char **argv;
893 {
894 	(void)printf("MPOOL\n");
895 	mpool_stat(((BTREE *)db->internal)->bt_mp);
896 }
897 #endif
898 
899 void
keydata(key,data)900 keydata(key, data)
901 	DBT *key, *data;
902 {
903 	if (!recno && key->size > 0)
904 		(void)printf("%.*s/", (int)key->size, key->data);
905 	if (data->size > 0)
906 		(void)printf("%.*s", (int)data->size, data->data);
907 	(void)printf("\n");
908 }
909 
910 void
usage()911 usage()
912 {
913 	(void)fprintf(stderr,
914 	    "usage: %s [-bdluw] [-c cache] [-i file] [-p page] [file]\n",
915 	    progname);
916 	exit (1);
917 }
918 
919 /* Find a candidate page to unlink. */
920 static PAGE *
candidatepg(BTREE * t,char * arg)921 candidatepg(BTREE *t, char *arg)
922 {
923 	PAGE *h = NULL;
924 	db_pgno_t pg;
925 	u_int32_t sflags;
926 
927 	if (arg[0] == 'i')
928 		sflags = P_BINTERNAL | P_RINTERNAL;
929 	if (arg[0] == 'l')
930 		sflags = P_BLEAF | P_RLEAF;
931 	for (pg = P_ROOT; pg < t->bt_mp->npages;
932 	     mpool_put(t->bt_mp, h, 0), pg++) {
933 		if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
934 			return h;
935 		/* Look for a nonempty page of the correct
936 		 * type that has both left and right siblings. */
937 		if (h->prevpg == P_INVALID || h->nextpg == P_INVALID)
938 			continue;
939 		if ((h->flags & sflags) && NEXTINDEX(h) != 0)
940 			break;
941 	}
942 	if (pg == t->bt_mp->npages)
943 		h = NULL;
944 	return h;
945 }
946 
947 void
unlinkpg(DB * db,char ** argv)948 unlinkpg(DB *db, char **argv)
949 {
950 	BTREE *t = db->internal;
951 	PAGE *h = NULL;
952 	db_pgno_t pg;
953 
954 	pg = atoi(argv[1]);
955 	if (pg == 0)
956 		h = candidatepg(t, argv[1]);
957 	else
958 		h = mpool_get(t->bt_mp, pg, 0);
959 
960 	if (h == NULL) {
961 		fprintf(stderr, "unable to find appropriate page to unlink\n");
962 		return;
963 	}
964 	printf("chain %d <- %d -> %d\n", h->prevpg, h->pgno, h->nextpg);
965 	if (__bt_relink(t, h) != 0) {
966 		perror("unlinkpg");
967 		goto cleanup;
968 	}
969 	h->prevpg = P_INVALID;
970 	h->nextpg = P_INVALID;
971 cleanup:
972 	mpool_put(t->bt_mp, h, MPOOL_DIRTY);
973 }
974