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
45typedef 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
53int stopstop;
54DB *globaldb;
55
56void append(DB *, char **);
57void bstat(DB *, char **);
58void cursor(DB *, char **);
59void delcur(DB *, char **);
60void delete(DB *, char **);
61void dump(DB *, char **);
62void first(DB *, char **);
63void get(DB *, char **);
64void help(DB *, char **);
65void iafter(DB *, char **);
66void ibefore(DB *, char **);
67void icursor(DB *, char **);
68void insert(DB *, char **);
69void keydata(DBT *, DBT *);
70void last(DB *, char **);
71void list(DB *, char **);
72void load(DB *, char **);
73void mstat(DB *, char **);
74void next(DB *, char **);
75int  parse(char *, char **, int);
76void previous(DB *, char **);
77void show(DB *, char **);
78void usage(void);
79void user(DB *);
80
81cmd_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
108int recno;					/* use record numbers */
109char *dict = "words";				/* default dictionary */
110char *progname;
111
112int
113main(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
182void
183user(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		}
234uselast:	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
243int
244parse(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
266void
267append(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
296void
297cursor(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
323void
324delcur(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
336void
337delete(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
363void
364dump(db, argv)
365	DB *db;
366	char **argv;
367{
368	__bt_dump(db);
369}
370
371void
372first(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
394void
395get(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
423void
424help(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
436void
437iafter(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
466void
467ibefore(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
496void
497icursor(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
525void
526insert(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
554void
555last(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
577void
578list(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
599DB *BUGdb;
600void
601load(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
655void
656next(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
678void
679previous(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
701void
702show(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
723void
724bstat(db, argv)
725	DB *db;
726	char **argv;
727{
728	(void)printf("BTREE\n");
729	__bt_stat(db);
730}
731
732void
733mstat(db, argv)
734	DB *db;
735	char **argv;
736{
737	(void)printf("MPOOL\n");
738	mpool_stat(((BTREE *)db->internal)->bt_mp);
739}
740
741void
742keydata(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
752void
753usage()
754{
755	(void)fprintf(stderr,
756	    "usage: %s [-bdlu] [-c cache] [-i file] [-p page] [file]\n",
757	    progname);
758	exit (1);
759}
760