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