db.c revision 1.18 1 /* $NetBSD: db.c,v 1.18 2008/09/05 03:41:35 lukem Exp $ */
2
3 /*-
4 * Copyright (c) 2002-2008 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.18 2008/09/05 03:41:35 lukem 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 oi.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 int encmask = ~(VIS_CSTYLE | VIS_HTTPSTYLE | VIS_OCTAL);
610
611 for(; **arg; (*arg)++) {
612 switch (**arg) {
613 case 'b':
614 r |= VIS_NOSLASH;
615 break;
616 case 'c':
617 r &= encmask;
618 r |= VIS_CSTYLE;
619 break;
620 case 'h':
621 r &= encmask;
622 r |= VIS_HTTPSTYLE;
623 break;
624 case 'o':
625 r &= encmask;
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