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