1 /* $NetBSD: db.c,v 1.24 2009/01/28 05:48:49 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 2002-2009 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Luke Mewburn of Wasabi Systems. 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 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #if HAVE_NBTOOL_CONFIG_H 33 #include "nbtool_config.h" 34 #endif 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 #ifdef __RCSID 39 __RCSID("$NetBSD: db.c,v 1.24 2009/01/28 05:48:49 lukem Exp $"); 40 #endif /* __RCSID */ 41 #endif /* not lint */ 42 43 #include <ctype.h> 44 #include <db.h> 45 #include <err.h> 46 #include <fcntl.h> 47 #include <limits.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <unistd.h> 52 #include <vis.h> 53 54 55 typedef enum { 56 F_WRITE = 1<<0, 57 F_DELETE = 1<<1, 58 F_SHOW_KEY = 1<<2, 59 F_SHOW_VALUE = 1<<3, 60 F_QUIET = 1<<10, 61 F_IGNORECASE = 1<<11, 62 F_ENDIAN_BIG = 1<<12, 63 F_ENDIAN_LITTLE = 1<<13, 64 F_NO_NUL = 1<<14, 65 F_CREATENEW = 1<<20, 66 F_DUPLICATES = 1<<21, 67 F_REPLACE = 1<<22, 68 F_ENCODE_KEY = 1<<23, 69 F_ENCODE_VAL = 1<<24, 70 F_DECODE_KEY = 1<<25, 71 F_DECODE_VAL = 1<<26, 72 } flags_t; 73 74 int main(int, char *[]); 75 void db_print(DBT *, DBT *); 76 int db_dump(void); 77 int db_del(char *); 78 int db_get(char *); 79 int db_seq(char *); 80 int db_put(char *, char *); 81 int parseline(FILE *, const char *, char **, char **); 82 int encode_data(size_t, char *, char **); 83 int decode_data(char *, char **); 84 void parse_encode_decode_arg(const char *, int); 85 int parse_encode_option(char **); 86 void usage(void); 87 88 flags_t flags = 0; 89 DB *db; 90 const char *outputsep = "\t"; 91 int visflags = 0; 92 const char *extra_echars = NULL; 93 94 int 95 main(int argc, char *argv[]) 96 { 97 struct { 98 char *file; 99 char *type; 100 DBTYPE dbtype; 101 void *info; 102 int dbflags; 103 mode_t mode; 104 unsigned int pagesize; 105 } oi; 106 BTREEINFO btreeinfo; 107 HASHINFO hashinfo; 108 FILE *infp; 109 const char *infile, *fieldsep; 110 char *p, *key, *val; 111 int ch, rv; 112 long lval; 113 114 setprogname(argv[0]); 115 116 infile = NULL; 117 fieldsep = " "; 118 infp = NULL; 119 memset(&oi, 0, sizeof(oi)); 120 oi.mode = 0644; 121 oi.pagesize = 4096; 122 123 /* parse arguments */ 124 while ( (ch = getopt(argc, argv, 125 "CDdE:F:f:iKm:NO:P:qRS:T:U:VwX:")) != -1) { 126 switch (ch) { 127 128 case 'C': 129 flags |= F_CREATENEW; 130 break; 131 132 case 'D': 133 flags |= F_DUPLICATES; 134 break; 135 136 case 'd': 137 flags |= F_DELETE; 138 break; 139 140 case 'E': 141 if (! optarg[0] || optarg[1]) 142 goto badendian; 143 switch (toupper((int)optarg[0])) { 144 case 'B': 145 flags |= F_ENDIAN_BIG; 146 break; 147 case 'L': 148 flags |= F_ENDIAN_LITTLE; 149 break; 150 case 'H': 151 flags &= ~(F_ENDIAN_BIG | F_ENDIAN_LITTLE); 152 break; 153 default: 154 badendian: 155 errx(1, "Bad endian `%s'", optarg); 156 } 157 break; 158 159 case 'F': 160 if (! optarg[0]) 161 errx(1, "Invalid field separator `%s'", 162 optarg); 163 fieldsep = optarg; 164 break; 165 166 case 'f': 167 infile = optarg; 168 break; 169 170 case 'i': 171 flags |= F_IGNORECASE; 172 break; 173 174 case 'K': 175 flags |= F_SHOW_KEY; 176 break; 177 178 case 'm': 179 lval = strtol(optarg, &p, 8); 180 if (p == optarg || *p != '\0') 181 errx(1, "Invalid octal number `%s'", optarg); 182 if (lval < 0 || lval > 07777) 183 errx(1, "Invalid mode `%s'", optarg); 184 oi.mode = (mode_t)lval; 185 break; 186 187 case 'N': 188 flags |= F_NO_NUL; 189 break; 190 191 case 'O': 192 outputsep = optarg; 193 break; 194 195 case 'P': 196 lval = strtol(optarg, &p, 10); 197 if (p == optarg || *p != '\0') 198 errx(1, "Invalid pagesize `%s'", optarg); 199 if (lval < 0 || (unsigned int)lval >= UINT_MAX) 200 errx(1, "Pagesize `%s' out of range", optarg); 201 oi.pagesize = (unsigned int)lval; 202 break; 203 204 case 'q': 205 flags |= F_QUIET; 206 break; 207 208 case 'R': 209 flags |= F_REPLACE; 210 break; 211 212 case 'S': 213 parse_encode_decode_arg(optarg, 1 /* encode */); 214 if (! (flags & (F_ENCODE_KEY | F_ENCODE_VAL))) 215 errx(1, "Invalid encoding argument `%s'", 216 optarg); 217 break; 218 219 case 'T': 220 visflags = parse_encode_option(&optarg); 221 if (! visflags) 222 errx(1, "Invalid encoding/decoding option `%s'", 223 optarg); 224 break; 225 226 case 'U': 227 parse_encode_decode_arg(optarg, 0 /* decode */); 228 if (! (flags & (F_DECODE_KEY | F_DECODE_VAL))) 229 errx(1, "Invalid decoding argument `%s'", 230 optarg); 231 break; 232 233 case 'V': 234 flags |= F_SHOW_VALUE; 235 break; 236 237 case 'w': 238 flags |= F_WRITE; 239 break; 240 241 case 'X': 242 extra_echars = optarg; 243 break; 244 245 default: 246 usage(); 247 248 } 249 } 250 argc -= optind; 251 argv += optind; 252 253 /* validate arguments */ 254 if (argc < 2) 255 usage(); 256 oi.type = argv[0]; 257 oi.file = argv[1]; 258 argc -= 2; 259 argv += 2; 260 261 if (flags & F_WRITE) { 262 if (flags & (F_SHOW_KEY | F_SHOW_VALUE | F_DELETE)) 263 usage(); 264 if ((!infile && argc < 2) || (argc % 2)) 265 usage(); 266 if (0 != (visflags & ~(VIS_HTTPSTYLE))) 267 errx(1, "Unsupported decoding option provided to -T"); 268 oi.dbflags = O_RDWR | O_CREAT | O_EXLOCK; 269 if (flags & F_CREATENEW) 270 oi.dbflags |= O_TRUNC; 271 } else if (flags & F_DELETE) { 272 if (flags & (F_SHOW_KEY | F_SHOW_VALUE | F_WRITE)) 273 usage(); 274 if (!infile && argc < 1) 275 usage(); 276 if (0 != (visflags & ~(VIS_HTTPSTYLE))) 277 errx(1, "Unsupported decoding option provided to -T"); 278 oi.dbflags = O_RDWR | O_CREAT | O_EXLOCK; 279 } else { 280 if (! (flags & (F_SHOW_KEY | F_SHOW_VALUE))) 281 flags |= (F_SHOW_KEY | F_SHOW_VALUE); 282 oi.dbflags = O_RDONLY | O_SHLOCK; 283 } 284 285 /* validate oi.type */ 286 if (strcmp(oi.type, "btree") == 0) { 287 memset(&btreeinfo, 0, sizeof(btreeinfo)); 288 if (flags & F_ENDIAN_BIG) 289 btreeinfo.lorder = 4321; 290 else if (flags & F_ENDIAN_LITTLE) 291 btreeinfo.lorder = 1234; 292 if (flags & F_DUPLICATES) 293 btreeinfo.flags = R_DUP; 294 btreeinfo.psize = oi.pagesize; 295 btreeinfo.cachesize = 1024 * 1024; 296 oi.info = &btreeinfo; 297 oi.dbtype = DB_BTREE; 298 } else if (strcmp(oi.type, "hash") == 0) { 299 memset(&hashinfo, 0, sizeof(hashinfo)); 300 if (flags & F_ENDIAN_BIG) 301 hashinfo.lorder = 4321; 302 else if (flags & F_ENDIAN_LITTLE) 303 hashinfo.lorder = 1234; 304 hashinfo.bsize = oi.pagesize; 305 hashinfo.cachesize = 1024 * 1024; 306 oi.info = &hashinfo; 307 oi.dbtype = DB_HASH; 308 } else { 309 warnx("Unknown database type `%s'", oi.type); 310 usage(); 311 } 312 313 if (infile) { 314 if (strcmp(infile, "-") == 0) 315 infp = stdin; 316 else if ((infp = fopen(infile, "r")) == NULL) 317 err(1, "Opening input file `%s'", infile); 318 } 319 320 /* open database */ 321 db = dbopen(oi.file, oi.dbflags, oi.mode, oi.dbtype, oi.info); 322 if (db == NULL) 323 err(1, "Opening database `%s'", oi.file); 324 325 326 /* manipulate database */ 327 rv = 0; 328 if (flags & F_WRITE) { /* write entries */ 329 for (ch = 0; ch < argc; ch += 2) 330 if ((rv = db_put(argv[ch], argv[ch+1]))) 331 goto cleanup; 332 if (infp) { 333 while (parseline(infp, fieldsep, &key, &val)) { 334 if ((rv = db_put(key, val))) 335 goto cleanup; 336 } 337 if (ferror(infp)) { 338 warnx("Reading `%s'", infile); 339 goto cleanup; 340 } 341 } 342 } else if (!infp && argc == 0) { /* read all */ 343 db_dump(); 344 } else { /* read/delete specific */ 345 int (*dbop)(char *); 346 347 if (flags & F_DELETE) 348 dbop = db_del; 349 else if (DB_BTREE == oi.dbtype) 350 dbop = db_seq; 351 else if (DB_HASH == oi.dbtype) 352 dbop = db_get; 353 else 354 errx(5, "internal error: unsupported dbtype %d", 355 oi.dbtype); 356 for (ch = 0; ch < argc; ch++) { 357 if ((rv = dbop(argv[ch]))) 358 goto cleanup; 359 } 360 if (infp) { 361 while (parseline(infp, fieldsep, &key, NULL)) { 362 if ((rv = dbop(key))) 363 goto cleanup; 364 } 365 if (ferror(infp)) { 366 warnx("Reading `%s'", infile); 367 goto cleanup; 368 } 369 } 370 } 371 372 /* close database */ 373 cleanup: 374 if (db->close(db) == -1) 375 err(1, "Closing database `%s'", oi.file); 376 if (infp) 377 fclose(infp); 378 return (rv); 379 } 380 381 void 382 db_print(DBT *key, DBT *val) 383 { 384 int len; 385 char *data; 386 387 #define MINUSNUL(x) ((x) > 0 ? (x) - (flags & F_NO_NUL ? 0 : 1) : 0) 388 389 if (flags & F_SHOW_KEY) { 390 if (flags & F_ENCODE_KEY) { 391 len = encode_data(MINUSNUL(key->size), 392 (char *)key->data, &data); 393 } else { 394 len = (int)MINUSNUL(key->size); 395 data = (char *)key->data; 396 } 397 printf("%.*s", len, data); 398 } 399 if ((flags & F_SHOW_KEY) && (flags & F_SHOW_VALUE)) 400 printf("%s", outputsep); 401 if (flags & F_SHOW_VALUE) { 402 if (flags & F_ENCODE_VAL) { 403 len = encode_data(MINUSNUL(val->size), 404 (char *)val->data, &data); 405 } else { 406 len = (int)MINUSNUL(val->size); 407 data = (char *)val->data; 408 } 409 printf("%.*s", len, data); 410 } 411 printf("\n"); 412 } 413 414 int 415 db_dump(void) 416 { 417 DBT key, val; 418 int rv; 419 420 while ((rv = db->seq(db, &key, &val, R_NEXT)) == 0) 421 db_print(&key, &val); 422 if (rv == -1) 423 warn("Error dumping database"); 424 return (rv == 1 ? 0 : 1); 425 } 426 427 static void 428 db_makekey(DBT *key, char *keystr, int downcase, int decode) 429 { 430 char *p, *ks; 431 int klen; 432 433 memset(key, 0, sizeof(*key)); 434 if (decode) { 435 if ((klen = decode_data(keystr, &ks)) == -1) 436 errx(1, "Invalid escape sequence in `%s'", keystr); 437 } else { 438 klen = strlen(keystr); 439 ks = keystr; 440 } 441 key->data = ks; 442 key->size = klen + (flags & F_NO_NUL ? 0 : 1); 443 if (downcase && (flags & F_IGNORECASE)) { 444 for (p = ks; *p; p++) 445 if (isupper((int)*p)) 446 *p = tolower((int)*p); 447 } 448 } 449 450 int 451 db_del(char *keystr) 452 { 453 DBT key; 454 int r; 455 456 db_makekey(&key, keystr, 1, (flags & F_DECODE_KEY ? 1 : 0)); 457 r = db->del(db, &key, 0); 458 switch (r) { 459 case -1: 460 if (! (flags & F_QUIET)) 461 warn("Error deleting key `%s'", keystr); 462 r = 1; 463 break; 464 case 0: 465 if (! (flags & F_QUIET)) 466 printf("Deleted key `%s'\n", keystr); 467 break; 468 case 1: 469 if (! (flags & F_QUIET)) 470 warnx("Unknown key `%s'", keystr); 471 break; 472 default: 473 errx(5, "%s: unexpected result %d from db", __func__, r); 474 } 475 if (flags & F_DECODE_KEY) 476 free(key.data); 477 return (r); 478 } 479 480 int 481 db_get(char *keystr) 482 { 483 DBT key, val; 484 int r; 485 486 db_makekey(&key, keystr, 1, (flags & F_DECODE_KEY ? 1 : 0)); 487 488 r = db->get(db, &key, &val, 0); 489 switch (r) { 490 case -1: 491 warn("Error reading key `%s'", keystr); 492 r = 1; 493 break; 494 case 0: 495 db_print(&key, &val); 496 break; 497 case 1: 498 if (! (flags & F_QUIET)) { 499 warnx("Unknown key `%s'", keystr); 500 } 501 break; 502 default: 503 errx(5, "%s: unexpected result %d from db", __func__, r); 504 } 505 if (flags & F_DECODE_KEY) 506 free(key.data); 507 return (r); 508 } 509 510 int 511 db_seq(char *keystr) 512 { 513 DBT key, val, want; 514 int r, found; 515 u_int seqflags; 516 517 db_makekey(&key, keystr, 1, (flags & F_DECODE_KEY ? 1 : 0)); 518 /* remember key in want, since db->seq() changes key */ 519 want.data = key.data; 520 want.size = key.size; 521 522 found = 0; 523 seqflags = R_CURSOR; 524 while ((r = db->seq(db, &key, &val, seqflags)) == 0) { 525 if (key.size != want.size || 526 0 != strcmp((char *)key.data, (char *)want.data)) { 527 r = 1; 528 break; 529 } 530 seqflags = R_NEXT; 531 found++; 532 db_print(&key, &val); 533 if (! (flags & F_DUPLICATES)) 534 break; 535 } 536 537 switch (r) { 538 case -1: 539 warn("Error reading key `%s'", keystr); 540 r = 1; 541 break; 542 case 0: 543 break; 544 case 1: 545 if (found) { 546 r = 0; 547 break; 548 } 549 if (! (flags & F_QUIET)) { 550 warnx("Unknown key `%s'", keystr); 551 } 552 break; 553 default: 554 errx(5, "%s: unexpected result %d from db", __func__, r); 555 } 556 if (flags & F_DECODE_KEY) 557 free(want.data); 558 return (r); 559 } 560 561 int 562 db_put(char *keystr, char *valstr) 563 { 564 DBT key, val; 565 int r = 0; 566 567 db_makekey(&key, keystr, 1, (flags & F_DECODE_KEY ? 1 : 0)); 568 db_makekey(&val, valstr, 0, (flags & F_DECODE_VAL ? 1 : 0)); 569 r = db->put(db, &key, &val, (flags & F_REPLACE) ? 0 : R_NOOVERWRITE); 570 switch (r) { 571 case -1: 572 warn("Error writing key `%s'", keystr); 573 r = 1; 574 break; 575 case 0: 576 if (! (flags & F_QUIET)) 577 printf("Added key `%s'\n", keystr); 578 break; 579 case 1: 580 if (! (flags & F_QUIET)) 581 warnx("Key `%s' already exists", keystr); 582 break; 583 default: 584 errx(5, "Unexpected result %d in %s", r, __func__); 585 } 586 if (flags & F_DECODE_KEY) 587 free(key.data); 588 if (flags & F_DECODE_VAL) 589 free(val.data); 590 return (r); 591 } 592 593 int 594 parseline(FILE *fp, const char *sep, char **kp, char **vp) 595 { 596 size_t len; 597 char *key, *val; 598 599 key = fgetln(fp, &len); 600 if (key == NULL) /* end of file, or error */ 601 return (0); 602 603 if (key[len-1] == '\n') /* check for \n at EOL */ 604 key[--len] = '\0'; 605 else 606 return (0); 607 608 *kp = key; 609 if (vp == NULL) /* don't split if don't want value */ 610 return (1); 611 if ((val = strstr(key, sep)) == NULL) 612 val = key + len; 613 else { 614 *val = '\0'; 615 val += strlen(sep); 616 } 617 *vp = val; 618 return (1); 619 } 620 621 int 622 encode_data(size_t len, char *data, char **edata) 623 { 624 static char *buf = NULL; 625 char *nbuf; 626 static size_t buflen = 0; 627 size_t elen; 628 629 elen = 1 + (len * 4); 630 if (elen > buflen) { 631 if ((nbuf = realloc(buf, elen)) == NULL) 632 err(1, "Cannot allocate encoding buffer"); 633 buf = nbuf; 634 buflen = elen; 635 } 636 *edata = buf; 637 if (extra_echars) { 638 return (strsvisx(buf, data, len, visflags, extra_echars)); 639 } else { 640 return (strvisx(buf, data, len, visflags)); 641 } 642 } 643 644 int 645 decode_data(char *data, char **ddata) 646 { 647 char *buf; 648 649 if ((buf = malloc(strlen(data) + 1)) == NULL) 650 err(1, "Cannot allocate decoding buffer"); 651 *ddata = buf; 652 return (strunvisx(buf, data, (visflags & VIS_HTTPSTYLE))); 653 } 654 655 void 656 parse_encode_decode_arg(const char *arg, int encode) 657 { 658 if (! arg[0] || arg[1]) 659 return; 660 if (arg[0] == 'k' || arg[0] == 'b') { 661 if (encode) 662 flags |= F_ENCODE_KEY; 663 else 664 flags |= F_DECODE_KEY; 665 } 666 if (arg[0] == 'v' || arg[0] == 'b') { 667 if (encode) 668 flags |= F_ENCODE_VAL; 669 else 670 flags |= F_DECODE_VAL; 671 } 672 return; 673 } 674 675 int 676 parse_encode_option(char **arg) 677 { 678 int r = 0; 679 int encmask = ~(VIS_CSTYLE | VIS_HTTPSTYLE | VIS_OCTAL); 680 681 for(; **arg; (*arg)++) { 682 switch (**arg) { 683 case 'b': 684 r |= VIS_NOSLASH; 685 break; 686 case 'c': 687 r &= encmask; 688 r |= VIS_CSTYLE; 689 break; 690 case 'h': 691 r &= encmask; 692 r |= VIS_HTTPSTYLE; 693 break; 694 case 'o': 695 r &= encmask; 696 r |= VIS_OCTAL; 697 break; 698 case 's': 699 r |= VIS_SAFE; 700 break; 701 case 't': 702 r |= VIS_TAB; 703 break; 704 case 'w': 705 r |= VIS_WHITE; 706 break; 707 default: 708 return (0); 709 break; 710 } 711 } 712 return (r); 713 } 714 715 void 716 usage(void) 717 { 718 const char *p = getprogname(); 719 720 fprintf(stderr, 721 "usage: %s [-DKiNqV] [-E endian] [-f infile] [-O outsep] [-S visitem]\n" 722 " [-T visspec] [-U unvisitem] [-X extravis] type dbfile [key [...]]\n" 723 " %s -d [-iNq] [-E endian] [-f infile] [-T visspec] [-U unvisitem]\n" 724 " type dbfile [key [...]]\n" 725 " %s -w [-CDiNqR] [-E endian] [-F isep] [-f infile] [-m mode]\n" 726 " [-P pagesize] [-T visspec] [-U unvisitem]\n" 727 " type dbfile [key value [...]]\n" 728 ,p ,p ,p ); 729 fprintf(stderr, 730 "Supported modes:\n" 731 " read keys [default]\n" 732 " -d delete keys\n" 733 " -w write (add) keys/values\n" 734 "Supported options:\n" 735 " -C create empty (truncated) database\n" 736 " -D allow duplicates\n" 737 " -E endian database endian: `B'ig, `L'ittle, `H'ost [default: H]\n" 738 " -F isep input field separator string [default: a space]\n" 739 " -f infile file of keys (read|delete) or keys/vals (write)\n" 740 " -i ignore case of key by converting to lower case\n" 741 " -K print key\n" 742 " -m mode mode of created database [default: 0644]\n" 743 " -N don't NUL terminate key\n" 744 " -O outsep output field separator string [default: a tab]\n" 745 " -P pagesize database page size [default: 4096]\n" 746 " -q quiet operation (missing keys aren't errors)\n" 747 " -R replace existing keys\n" 748 " -S visitem items to strvis(3) encode: 'k'ey, 'v'alue, 'b'oth\n" 749 " -T visspec options to control -S and -U; like vis(1) options\n" 750 " -U unvisitem items to strunvis(3) decode: 'k'ey, 'v'alue, 'b'oth\n" 751 " -V print value\n" 752 " -X extravis extra characters to encode with -S\n" 753 ); 754 exit(1); 755 } 756