1 /* $NetBSD: db.c,v 1.7 2003/01/05 13:07:38 seb Exp $ */ 2 3 /*- 4 * Copyright (c) 2002 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 __RCSID("$NetBSD: db.c,v 1.7 2003/01/05 13:07:38 seb Exp $"); 42 #endif /* not lint */ 43 44 #include <db.h> 45 #include <ctype.h> 46 #include <err.h> 47 #include <fcntl.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <unistd.h> 52 53 54 /* 55 * TODO -- 56 * - add option to strunvis(3) key (& value) from infile ? 57 */ 58 59 typedef enum { 60 F_WRITE = 1<<0, 61 F_DELETE = 1<<1, 62 F_SHOW_KEY = 1<<2, 63 F_SHOW_VALUE = 1<<3, 64 F_QUIET = 1<<10, 65 F_IGNORECASE = 1<<11, 66 F_ENDIAN_BIG = 1<<12, 67 F_ENDIAN_LITTLE = 1<<13, 68 F_NO_NUL = 1<<14, 69 F_CREATENEW = 1<<20, 70 F_DUPLICATES = 1<<21, 71 F_REPLACE = 1<<22, 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_put(char *, char *); 80 int parseline(FILE *, const char *, char **, char **); 81 void usage(void); 82 83 flags_t flags; 84 DB *db; 85 char *outputsep = "\t"; 86 87 int 88 main(int argc, char *argv[]) 89 { 90 struct { 91 char *file; 92 char *type; 93 DBTYPE dbtype; 94 void *info; 95 int flags; 96 mode_t mode; 97 } oi; 98 BTREEINFO btreeinfo; 99 HASHINFO hashinfo; 100 FILE *infp; 101 const char *infile, *fieldsep; 102 char *p, *key, *val; 103 int ch, rv; 104 105 setprogname(argv[0]); 106 107 flags = 0; 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, "CDdE:F:f:iKm:NO:qRVw")) != -1) { 116 switch (ch) { 117 118 case 'C': 119 flags |= F_CREATENEW; 120 break; 121 122 case 'D': 123 flags |= F_DUPLICATES; 124 break; 125 126 case 'd': 127 flags |= F_DELETE; 128 break; 129 130 case 'E': 131 if (! optarg[0] || optarg[1]) 132 goto badendian; 133 switch (toupper((int)optarg[0])) { 134 case 'B': 135 flags |= F_ENDIAN_BIG; 136 break; 137 case 'L': 138 flags |= F_ENDIAN_LITTLE; 139 break; 140 case 'H': 141 flags &= ~(F_ENDIAN_BIG | F_ENDIAN_LITTLE); 142 break; 143 default: 144 badendian: 145 errx(1, "Bad endian `%s'", optarg); 146 } 147 break; 148 149 case 'F': 150 if (! optarg[0] || optarg[1]) 151 errx(1, "Invalid field separator `%s'", 152 optarg); 153 fieldsep = optarg; 154 break; 155 156 case 'f': 157 infile = optarg; 158 break; 159 160 case 'i': 161 flags |= F_IGNORECASE; 162 break; 163 164 case 'K': 165 flags |= F_SHOW_KEY; 166 break; 167 168 case 'm': 169 oi.mode = (int)strtol(optarg, &p, 8); 170 if (p == optarg || *p != '\0') 171 errx(1, "Invalid octal number `%s'", optarg); 172 break; 173 174 case 'N': 175 flags |= F_NO_NUL; 176 break; 177 178 case 'O': 179 outputsep = optarg; 180 break; 181 182 case 'q': 183 flags |= F_QUIET; 184 break; 185 186 case 'R': 187 flags |= F_REPLACE; 188 break; 189 190 case 'V': 191 flags |= F_SHOW_VALUE; 192 break; 193 194 case 'w': 195 flags |= F_WRITE; 196 break; 197 198 default: 199 usage(); 200 201 } 202 } 203 argc -= optind; 204 argv += optind; 205 206 /* validate arguments */ 207 if (argc < 2) 208 usage(); 209 oi.type = argv[0]; 210 oi.file = argv[1]; 211 argc -= 2; 212 argv += 2; 213 214 if (flags & F_WRITE) { 215 if (flags & (F_SHOW_KEY | F_SHOW_VALUE | F_DELETE)) 216 usage(); 217 if ((!infile && argc < 2) || (argc % 2)) 218 usage(); 219 oi.flags = O_RDWR | O_CREAT | O_EXLOCK; 220 if (flags & F_CREATENEW) 221 flags |= O_TRUNC; 222 } else if (flags & F_DELETE) { 223 if (flags & (F_SHOW_KEY | F_SHOW_VALUE | F_WRITE)) 224 usage(); 225 if (!infile && argc < 1) 226 usage(); 227 oi.flags = O_RDWR | O_CREAT | O_EXLOCK; 228 } else { 229 if (! (flags & (F_SHOW_KEY | F_SHOW_VALUE))) 230 flags |= (F_SHOW_KEY | F_SHOW_VALUE); 231 oi.flags = O_RDONLY | O_SHLOCK; 232 } 233 234 /* validate oi.type */ 235 if (strcmp(oi.type, "btree") == 0) { 236 memset(&btreeinfo, 0, sizeof(btreeinfo)); 237 if (flags & F_ENDIAN_BIG) 238 btreeinfo.lorder = 4321; 239 else if (flags & F_ENDIAN_LITTLE) 240 btreeinfo.lorder = 1234; 241 if (flags & F_DUPLICATES) 242 btreeinfo.flags = R_DUP; 243 btreeinfo.cachesize = 1024 * 1024; 244 oi.info = &btreeinfo; 245 oi.dbtype = DB_BTREE; 246 } else if (strcmp(oi.type, "hash") == 0) { 247 memset(&hashinfo, 0, sizeof(hashinfo)); 248 if (flags & F_ENDIAN_BIG) 249 hashinfo.lorder = 4321; 250 else if (flags & F_ENDIAN_LITTLE) 251 hashinfo.lorder = 1234; 252 hashinfo.cachesize = 1024 * 1024; 253 oi.info = &hashinfo; 254 oi.dbtype = DB_HASH; 255 } else { 256 warnx("Unknown database type `%s'", oi.type); 257 usage(); 258 } 259 260 if (infile) { 261 if (strcmp(infile, "-") == 0) 262 infp = stdin; 263 else if ((infp = fopen(infile, "r")) == NULL) 264 err(1, "Opening input file `%s'", infile); 265 } 266 267 /* open database */ 268 db = dbopen(oi.file, oi.flags, oi.mode, oi.dbtype, oi.info); 269 if (db == NULL) 270 err(1, "Opening database `%s'", oi.file); 271 272 273 /* manipulate database */ 274 rv = 0; 275 if (flags & F_WRITE) { /* write entries */ 276 for (ch = 0; ch < argc; ch += 2) 277 if ((rv = db_put(argv[ch], argv[ch+1]))) 278 goto cleanup; 279 if (infp) { 280 while (parseline(infp, fieldsep, &key, &val)) { 281 if ((rv = db_put(key, val))) 282 goto cleanup; 283 } 284 if (ferror(infp)) { 285 warnx("Reading `%s'", infile); 286 goto cleanup; 287 } 288 } 289 } else if (!infp && argc == 0) { /* read all */ 290 db_dump(); 291 } else { /* read/delete specific */ 292 int (*dbop)(char *); 293 294 if (flags & F_DELETE) 295 dbop = db_del; 296 else 297 dbop = db_get; 298 for (ch = 0; ch < argc; ch++) { 299 if ((rv = dbop(argv[ch]))) 300 goto cleanup; 301 } 302 if (infp) { 303 while (parseline(infp, fieldsep, &key, NULL)) { 304 if ((rv = dbop(key))) 305 goto cleanup; 306 } 307 if (ferror(infp)) { 308 warnx("Reading `%s'", infile); 309 goto cleanup; 310 } 311 } 312 } 313 314 /* close database */ 315 cleanup: 316 if (db->close(db) == -1) 317 err(1, "Closing database `%s'", oi.file); 318 if (infp) 319 fclose(infp); 320 return (rv); 321 } 322 323 void 324 db_print(DBT *key, DBT *val) 325 { 326 327 if (flags & F_SHOW_KEY) 328 printf("%.*s", (int)key->size, (char *)key->data); 329 if ((flags & F_SHOW_KEY) && (flags & F_SHOW_VALUE)) 330 printf("%s", outputsep); 331 if (flags & F_SHOW_VALUE) 332 printf("%.*s", (int)val->size, (char *)val->data); 333 printf("\n"); 334 } 335 336 int 337 db_dump(void) 338 { 339 DBT key, val; 340 int rv; 341 342 while ((rv = db->seq(db, &key, &val, R_NEXT)) == 0) 343 db_print(&key, &val); 344 if (rv == -1) 345 warn("Error dumping database"); 346 return (rv == 1 ? 0 : 1); 347 } 348 349 static void 350 db_makekey(DBT *key, char *keystr, int downcase) 351 { 352 char *p; 353 354 memset(key, 0, sizeof(*key)); 355 key->data = keystr; 356 key->size = strlen(keystr) + (flags & F_NO_NUL ? 0 : 1); 357 if (downcase && flags & F_IGNORECASE) { 358 for (p = keystr; *p; p++) 359 if (isupper((int)*p)) 360 *p = tolower((int)*p); 361 } 362 } 363 364 int 365 db_del(char *keystr) 366 { 367 DBT key; 368 369 db_makekey(&key, keystr, 1); 370 switch (db->del(db, &key, 0)) { 371 case -1: 372 warn("Error deleting key `%s'", keystr); 373 return (1); 374 case 0: 375 if (! (flags & F_QUIET)) 376 printf("Deleted key `%s'\n", keystr); 377 break; 378 case 1: 379 warnx("Key `%s' does not exist", keystr); 380 return (1); 381 } 382 return (0); 383 } 384 385 int 386 db_get(char *keystr) 387 { 388 DBT key, val; 389 390 db_makekey(&key, keystr, 1); 391 switch (db->get(db, &key, &val, 0)) { 392 case -1: 393 warn("Error reading key `%s'", keystr); 394 return (1); 395 case 0: 396 db_print(&key, &val); 397 break; 398 case 1: 399 if (! (flags & F_QUIET)) { 400 warnx("Unknown key `%s'", keystr); 401 return (1); 402 } 403 break; 404 } 405 return (0); 406 } 407 408 int 409 db_put(char *keystr, char *valstr) 410 { 411 DBT key, val; 412 413 db_makekey(&key, keystr, 1); 414 db_makekey(&val, valstr, 1); 415 switch (db->put(db, &key, &val, 416 (flags & F_REPLACE) ? 0 : R_NOOVERWRITE)) { 417 case -1: 418 warn("Error writing key `%s'", keystr); 419 return (1); 420 case 0: 421 if (! (flags & F_QUIET)) 422 printf("Added key `%s'\n", keystr); 423 break; 424 case 1: 425 if (! (flags & F_QUIET)) 426 warnx("Key `%s' already exists", keystr); 427 return (1); 428 } 429 return (0); 430 } 431 432 int 433 parseline(FILE *fp, const char *sep, char **kp, char **vp) 434 { 435 size_t len; 436 char *key, *val; 437 438 key = fgetln(fp, &len); 439 if (key == NULL) /* end of file, or error */ 440 return (0); 441 442 if (key[len-1] == '\n') /* check for \n at EOL */ 443 key[--len] = '\0'; 444 else 445 return (0); 446 447 *kp = key; 448 if (vp == NULL) /* don't split if don't want value */ 449 return (1); 450 if ((val = strchr(key, sep[0])) == NULL) 451 val = key + len; 452 else 453 *val++ = '\0'; 454 *vp = val; 455 return (1); 456 } 457 458 void 459 usage(void) 460 { 461 const char *p = getprogname(); 462 463 fprintf(stderr, 464 "Usage: %s [-KV] [-Niq] [-E end] [-f inf] [-O str] type dbfile [key [...]]\n" 465 " %s -d [-Niq] [-E end] [-f inf] type dbfile [key [...]]\n" 466 " %s -w [-Niq] [-E end] [-f inf] [-CDR] [-F sep] [-m mod]\n" 467 " type dbfile [key val [...]]\n" 468 ,p ,p ,p ); 469 fprintf(stderr, 470 "Supported modes:\n" 471 "\t\tread keys [default]\n" 472 "\t-d\tdelete keys\n" 473 "\t-w\twrite (add) keys/values\n" 474 "Supported options:\n" 475 "\t-C\tcreate empty (truncated) database\n" 476 "\t-D\tallow duplicates\n" 477 "\t-E end\tdatabase endian: `B'ig, `L'ittle, `H'ost [default: H]\n" 478 "\t-F sep\tfield separator character [default: ' ']\n" 479 "\t-K\tprint key\n" 480 "\t-N\tdon't NUL terminate key\n" 481 "\t-R\treplace existing keys\n" 482 "\t-V\tprint value\n" 483 "\t-f inf\tfile of keys (read|delete) or keys/vals (write)\n" 484 "\t-i\tignore case of key by converting to lower case\n" 485 "\t-m mod\tmode of created database [default: 0644]\n" 486 "\t-q\tquiet operation (missing keys aren't errors)\n" 487 "\t-O str\toutput field separator string [default: '\\t']\n" 488 ); 489 exit(1); 490 } 491