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