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