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