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