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