Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / usr.bin / db / db.c
blobc440713054ee30dce0b4f0792709fe4c6a863c84
1 /* $NetBSD: db.c,v 1.23 2009/01/18 01:04:34 lukem Exp $ */
3 /*-
4 * Copyright (c) 2002-2009 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Luke Mewburn of Wasabi Systems.
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.
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.
32 #if HAVE_NBTOOL_CONFIG_H
33 #include "nbtool_config.h"
34 #endif
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #ifdef __RCSID
39 __RCSID("$NetBSD: db.c,v 1.23 2009/01/18 01:04:34 lukem Exp $");
40 #endif /* __RCSID */
41 #endif /* not lint */
43 #include <ctype.h>
44 #include <db.h>
45 #include <err.h>
46 #include <fcntl.h>
47 #include <limits.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 #include <vis.h>
55 typedef enum {
56 F_WRITE = 1<<0,
57 F_DELETE = 1<<1,
58 F_SHOW_KEY = 1<<2,
59 F_SHOW_VALUE = 1<<3,
60 F_QUIET = 1<<10,
61 F_IGNORECASE = 1<<11,
62 F_ENDIAN_BIG = 1<<12,
63 F_ENDIAN_LITTLE = 1<<13,
64 F_NO_NUL = 1<<14,
65 F_CREATENEW = 1<<20,
66 F_DUPLICATES = 1<<21,
67 F_REPLACE = 1<<22,
68 F_ENCODE_KEY = 1<<23,
69 F_ENCODE_VAL = 1<<24,
70 F_DECODE_KEY = 1<<25,
71 F_DECODE_VAL = 1<<26,
72 } flags_t;
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_seq(char *);
80 int db_put(char *, char *);
81 int parseline(FILE *, const char *, char **, char **);
82 int encode_data(size_t, char *, char **);
83 int decode_data(char *, char **);
84 void parse_encode_decode_arg(const char *, int);
85 int parse_encode_option(char **);
86 void usage(void);
88 flags_t flags = 0;
89 DB *db;
90 const char *outputsep = "\t";
91 int visflags = 0;
92 const char *extra_echars = NULL;
94 int
95 main(int argc, char *argv[])
97 struct {
98 char *file;
99 char *type;
100 DBTYPE dbtype;
101 void *info;
102 int dbflags;
103 mode_t mode;
104 unsigned int pagesize;
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 long lval;
114 setprogname(argv[0]);
116 infile = NULL;
117 fieldsep = " ";
118 infp = NULL;
119 memset(&oi, 0, sizeof(oi));
120 oi.mode = 0644;
121 oi.pagesize = 4096;
123 /* parse arguments */
124 while ( (ch = getopt(argc, argv,
125 "CDdE:F:f:iKm:NO:P:qRS:T:U:VwX:")) != -1) {
126 switch (ch) {
128 case 'C':
129 flags |= F_CREATENEW;
130 break;
132 case 'D':
133 flags |= F_DUPLICATES;
134 break;
136 case 'd':
137 flags |= F_DELETE;
138 break;
140 case 'E':
141 if (! optarg[0] || optarg[1])
142 goto badendian;
143 switch (toupper((int)optarg[0])) {
144 case 'B':
145 flags |= F_ENDIAN_BIG;
146 break;
147 case 'L':
148 flags |= F_ENDIAN_LITTLE;
149 break;
150 case 'H':
151 flags &= ~(F_ENDIAN_BIG | F_ENDIAN_LITTLE);
152 break;
153 default:
154 badendian:
155 errx(1, "Bad endian `%s'", optarg);
157 break;
159 case 'F':
160 if (! optarg[0])
161 errx(1, "Invalid field separator `%s'",
162 optarg);
163 fieldsep = optarg;
164 break;
166 case 'f':
167 infile = optarg;
168 break;
170 case 'i':
171 flags |= F_IGNORECASE;
172 break;
174 case 'K':
175 flags |= F_SHOW_KEY;
176 break;
178 case 'm':
179 lval = strtol(optarg, &p, 8);
180 if (p == optarg || *p != '\0')
181 errx(1, "Invalid octal number `%s'", optarg);
182 if (lval < 0 || lval > 07777)
183 errx(1, "Invalid mode `%s'", optarg);
184 oi.mode = (mode_t)lval;
185 break;
187 case 'N':
188 flags |= F_NO_NUL;
189 break;
191 case 'O':
192 outputsep = optarg;
193 break;
195 case 'P':
196 lval = strtol(optarg, &p, 10);
197 if (p == optarg || *p != '\0')
198 errx(1, "Invalid pagesize `%s'", optarg);
199 if (lval < 0 || (unsigned int)lval >= UINT_MAX)
200 errx(1, "Pagesize `%s' out of range", optarg);
201 oi.pagesize = (unsigned int)lval;
202 break;
204 case 'q':
205 flags |= F_QUIET;
206 break;
208 case 'R':
209 flags |= F_REPLACE;
210 break;
212 case 'S':
213 parse_encode_decode_arg(optarg, 1 /* encode */);
214 if (! (flags & (F_ENCODE_KEY | F_ENCODE_VAL)))
215 errx(1, "Invalid encoding argument `%s'",
216 optarg);
217 break;
219 case 'T':
220 visflags = parse_encode_option(&optarg);
221 if (! visflags)
222 errx(1, "Invalid encoding/decoding option `%s'",
223 optarg);
224 break;
226 case 'U':
227 parse_encode_decode_arg(optarg, 0 /* decode */);
228 if (! (flags & (F_DECODE_KEY | F_DECODE_VAL)))
229 errx(1, "Invalid decoding argument `%s'",
230 optarg);
231 break;
233 case 'V':
234 flags |= F_SHOW_VALUE;
235 break;
237 case 'w':
238 flags |= F_WRITE;
239 break;
241 case 'X':
242 extra_echars = optarg;
243 break;
245 default:
246 usage();
250 argc -= optind;
251 argv += optind;
253 /* validate arguments */
254 if (argc < 2)
255 usage();
256 oi.type = argv[0];
257 oi.file = argv[1];
258 argc -= 2;
259 argv += 2;
261 if (flags & F_WRITE) {
262 if (flags & (F_SHOW_KEY | F_SHOW_VALUE | F_DELETE))
263 usage();
264 if ((!infile && argc < 2) || (argc % 2))
265 usage();
266 if (0 != (visflags & ~(VIS_HTTPSTYLE)))
267 errx(1, "Unsupported decoding option provided to -T");
268 oi.dbflags = O_RDWR | O_CREAT | O_EXLOCK;
269 if (flags & F_CREATENEW)
270 oi.dbflags |= O_TRUNC;
271 } else if (flags & F_DELETE) {
272 if (flags & (F_SHOW_KEY | F_SHOW_VALUE | F_WRITE))
273 usage();
274 if (!infile && argc < 1)
275 usage();
276 if (0 != (visflags & ~(VIS_HTTPSTYLE)))
277 errx(1, "Unsupported decoding option provided to -T");
278 oi.dbflags = O_RDWR | O_CREAT | O_EXLOCK;
279 } else {
280 if (! (flags & (F_SHOW_KEY | F_SHOW_VALUE)))
281 flags |= (F_SHOW_KEY | F_SHOW_VALUE);
282 oi.dbflags = O_RDONLY | O_SHLOCK;
285 /* validate oi.type */
286 if (strcmp(oi.type, "btree") == 0) {
287 memset(&btreeinfo, 0, sizeof(btreeinfo));
288 if (flags & F_ENDIAN_BIG)
289 btreeinfo.lorder = 4321;
290 else if (flags & F_ENDIAN_LITTLE)
291 btreeinfo.lorder = 1234;
292 if (flags & F_DUPLICATES)
293 btreeinfo.flags = R_DUP;
294 btreeinfo.psize = oi.pagesize;
295 btreeinfo.cachesize = 1024 * 1024;
296 oi.info = &btreeinfo;
297 oi.dbtype = DB_BTREE;
298 } else if (strcmp(oi.type, "hash") == 0) {
299 memset(&hashinfo, 0, sizeof(hashinfo));
300 if (flags & F_ENDIAN_BIG)
301 hashinfo.lorder = 4321;
302 else if (flags & F_ENDIAN_LITTLE)
303 hashinfo.lorder = 1234;
304 hashinfo.bsize = oi.pagesize;
305 hashinfo.cachesize = 1024 * 1024;
306 oi.info = &hashinfo;
307 oi.dbtype = DB_HASH;
308 } else {
309 warnx("Unknown database type `%s'", oi.type);
310 usage();
313 if (infile) {
314 if (strcmp(infile, "-") == 0)
315 infp = stdin;
316 else if ((infp = fopen(infile, "r")) == NULL)
317 err(1, "Opening input file `%s'", infile);
320 /* open database */
321 db = dbopen(oi.file, oi.dbflags, oi.mode, oi.dbtype, oi.info);
322 if (db == NULL)
323 err(1, "Opening database `%s'", oi.file);
326 /* manipulate database */
327 rv = 0;
328 if (flags & F_WRITE) { /* write entries */
329 for (ch = 0; ch < argc; ch += 2)
330 if ((rv = db_put(argv[ch], argv[ch+1])))
331 goto cleanup;
332 if (infp) {
333 while (parseline(infp, fieldsep, &key, &val)) {
334 if ((rv = db_put(key, val)))
335 goto cleanup;
337 if (ferror(infp)) {
338 warnx("Reading `%s'", infile);
339 goto cleanup;
342 } else if (!infp && argc == 0) { /* read all */
343 db_dump();
344 } else { /* read/delete specific */
345 int (*dbop)(char *);
347 if (flags & F_DELETE)
348 dbop = db_del;
349 else if (DB_BTREE == oi.dbtype)
350 dbop = db_seq;
351 else if (DB_HASH == oi.dbtype)
352 dbop = db_get;
353 else
354 errx(5, "internal error: unsupported dbtype %d",
355 oi.dbtype);
356 for (ch = 0; ch < argc; ch++) {
357 if ((rv = dbop(argv[ch])))
358 goto cleanup;
360 if (infp) {
361 while (parseline(infp, fieldsep, &key, NULL)) {
362 if ((rv = dbop(key)))
363 goto cleanup;
365 if (ferror(infp)) {
366 warnx("Reading `%s'", infile);
367 goto cleanup;
372 /* close database */
373 cleanup:
374 if (db->close(db) == -1)
375 err(1, "Closing database `%s'", oi.file);
376 if (infp)
377 fclose(infp);
378 return (rv);
381 void
382 db_print(DBT *key, DBT *val)
384 int len;
385 char *data;
387 #define MINUSNUL(x) ((x) > 0 ? (x) - (flags & F_NO_NUL ? 0 : 1) : 0)
389 if (flags & F_SHOW_KEY) {
390 if (flags & F_ENCODE_KEY) {
391 len = encode_data(MINUSNUL(key->size),
392 (char *)key->data, &data);
393 } else {
394 len = (int)MINUSNUL(key->size);
395 data = (char *)key->data;
397 printf("%.*s", len, data);
399 if ((flags & F_SHOW_KEY) && (flags & F_SHOW_VALUE))
400 printf("%s", outputsep);
401 if (flags & F_SHOW_VALUE) {
402 if (flags & F_ENCODE_VAL) {
403 len = encode_data(MINUSNUL(val->size),
404 (char *)val->data, &data);
405 } else {
406 len = (int)MINUSNUL(val->size);
407 data = (char *)val->data;
409 printf("%.*s", len, data);
411 printf("\n");
415 db_dump(void)
417 DBT key, val;
418 int rv;
420 while ((rv = db->seq(db, &key, &val, R_NEXT)) == 0)
421 db_print(&key, &val);
422 if (rv == -1)
423 warn("Error dumping database");
424 return (rv == 1 ? 0 : 1);
427 static void
428 db_makekey(DBT *key, char *keystr, int downcase, int decode)
430 char *p, *ks;
431 int klen;
433 memset(key, 0, sizeof(*key));
434 if (decode) {
435 if ((klen = decode_data(keystr, &ks)) == -1)
436 errx(1, "Invalid escape sequence in `%s'", keystr);
437 } else {
438 klen = strlen(keystr);
439 ks = keystr;
441 key->data = ks;
442 key->size = klen + (flags & F_NO_NUL ? 0 : 1);
443 if (downcase && (flags & F_IGNORECASE)) {
444 for (p = ks; *p; p++)
445 if (isupper((int)*p))
446 *p = tolower((int)*p);
451 db_del(char *keystr)
453 DBT key;
454 int r;
456 db_makekey(&key, keystr, 1, (flags & F_DECODE_KEY ? 1 : 0));
457 r = db->del(db, &key, 0);
458 switch (r) {
459 case -1:
460 if (! (flags & F_QUIET))
461 warn("Error deleting key `%s'", keystr);
462 r = 1;
463 break;
464 case 0:
465 if (! (flags & F_QUIET))
466 printf("Deleted key `%s'\n", keystr);
467 break;
468 case 1:
469 if (! (flags & F_QUIET))
470 warnx("Unknown key `%s'", keystr);
471 break;
472 default:
473 errx(5, "%s: unexpected result %d from db", __func__, r);
475 if (flags & F_DECODE_KEY)
476 free(key.data);
477 return (r);
481 db_get(char *keystr)
483 DBT key, val;
484 int r;
486 db_makekey(&key, keystr, 1, (flags & F_DECODE_KEY ? 1 : 0));
488 r = db->get(db, &key, &val, 0);
489 switch (r) {
490 case -1:
491 warn("Error reading key `%s'", keystr);
492 r = 1;
493 break;
494 case 0:
495 db_print(&key, &val);
496 break;
497 case 1:
498 if (! (flags & F_QUIET)) {
499 warnx("Unknown key `%s'", keystr);
501 break;
502 default:
503 errx(5, "%s: unexpected result %d from db", __func__, r);
505 if (flags & F_DECODE_KEY)
506 free(key.data);
507 return (r);
511 db_seq(char *keystr)
513 DBT key, val, want;
514 int r, found;
515 u_int seqflags;
517 db_makekey(&key, keystr, 1, (flags & F_DECODE_KEY ? 1 : 0));
518 /* remember key in want, since db->seq() changes key */
519 want.data = key.data;
520 want.size = key.size;
522 found = 0;
523 seqflags = R_CURSOR;
524 while ((r = db->seq(db, &key, &val, seqflags)) == 0) {
525 if (key.size != want.size ||
526 0 != strcmp((char *)key.data, (char *)want.data)) {
527 r = 1;
528 break;
530 seqflags = R_NEXT;
531 found++;
532 db_print(&key, &val);
533 if (! (flags & F_DUPLICATES))
534 break;
537 switch (r) {
538 case -1:
539 warn("Error reading key `%s'", keystr);
540 r = 1;
541 break;
542 case 0:
543 break;
544 case 1:
545 if (found) {
546 r = 0;
547 break;
549 if (! (flags & F_QUIET)) {
550 warnx("Unknown key `%s'", keystr);
552 break;
553 default:
554 errx(5, "%s: unexpected result %d from db", __func__, r);
556 if (flags & F_DECODE_KEY)
557 free(want.data);
558 return (r);
562 db_put(char *keystr, char *valstr)
564 DBT key, val;
565 int r = 0;
567 db_makekey(&key, keystr, 1, (flags & F_DECODE_KEY ? 1 : 0));
568 db_makekey(&val, valstr, 0, (flags & F_DECODE_VAL ? 1 : 0));
569 r = db->put(db, &key, &val, (flags & F_REPLACE) ? 0 : R_NOOVERWRITE);
570 switch (r) {
571 case -1:
572 warn("Error writing key `%s'", keystr);
573 r = 1;
574 break;
575 case 0:
576 if (! (flags & F_QUIET))
577 printf("Added key `%s'\n", keystr);
578 break;
579 case 1:
580 if (! (flags & F_QUIET))
581 warnx("Key `%s' already exists", keystr);
582 break;
583 default:
584 errx(5, "Unexpected result %d in %s", r, __func__);
586 if (flags & F_DECODE_KEY)
587 free(key.data);
588 if (flags & F_DECODE_VAL)
589 free(val.data);
590 return (r);
594 parseline(FILE *fp, const char *sep, char **kp, char **vp)
596 size_t len;
597 char *key, *val;
599 key = fgetln(fp, &len);
600 if (key == NULL) /* end of file, or error */
601 return (0);
603 if (key[len-1] == '\n') /* check for \n at EOL */
604 key[--len] = '\0';
605 else
606 return (0);
608 *kp = key;
609 if (vp == NULL) /* don't split if don't want value */
610 return (1);
611 if ((val = strstr(key, sep)) == NULL)
612 val = key + len;
613 else {
614 *val = '\0';
615 val += strlen(sep);
617 *vp = val;
618 return (1);
622 encode_data(size_t len, char *data, char **edata)
624 static char *buf = NULL;
625 char *nbuf;
626 static size_t buflen = 0;
627 size_t elen;
629 elen = 1 + (len * 4);
630 if (elen > buflen) {
631 if ((nbuf = realloc(buf, elen)) == NULL)
632 err(1, "Cannot allocate encoding buffer");
633 buf = nbuf;
634 buflen = elen;
636 *edata = buf;
637 if (extra_echars) {
638 return (strsvisx(buf, data, len, visflags, extra_echars));
639 } else {
640 return (strvisx(buf, data, len, visflags));
645 decode_data(char *data, char **ddata)
647 char *buf;
649 if ((buf = malloc(strlen(data) + 1)) == NULL)
650 err(1, "Cannot allocate decoding buffer");
651 *ddata = buf;
652 return (strunvisx(buf, data, (visflags & VIS_HTTPSTYLE)));
655 void
656 parse_encode_decode_arg(const char *arg, int encode)
658 if (! arg[0] || arg[1])
659 return;
660 if (arg[0] == 'k' || arg[0] == 'b') {
661 if (encode)
662 flags |= F_ENCODE_KEY;
663 else
664 flags |= F_DECODE_KEY;
666 if (arg[0] == 'v' || arg[0] == 'b') {
667 if (encode)
668 flags |= F_ENCODE_VAL;
669 else
670 flags |= F_DECODE_VAL;
672 return;
676 parse_encode_option(char **arg)
678 int r = 0;
679 int encmask = ~(VIS_CSTYLE | VIS_HTTPSTYLE | VIS_OCTAL);
681 for(; **arg; (*arg)++) {
682 switch (**arg) {
683 case 'b':
684 r |= VIS_NOSLASH;
685 break;
686 case 'c':
687 r &= encmask;
688 r |= VIS_CSTYLE;
689 break;
690 case 'h':
691 r &= encmask;
692 r |= VIS_HTTPSTYLE;
693 break;
694 case 'o':
695 r &= encmask;
696 r |= VIS_OCTAL;
697 break;
698 case 's':
699 r |= VIS_SAFE;
700 break;
701 case 't':
702 r |= VIS_TAB;
703 break;
704 case 'w':
705 r |= VIS_WHITE;
706 break;
707 default:
708 return (0);
709 break;
712 return (r);
715 void
716 usage(void)
718 const char *p = getprogname();
720 fprintf(stderr,
721 "usage: %s [-DKiNqV] [-E endian] [-f infile] [-O outsep] [-S visitem]\n"
722 " [-T visspec] [-U unvisitem] [-X extravis] type dbfile [key [...]]\n"
723 " %s -d [-iNq] [-E endian] [-f infile] [-T visspec] [-U unvisitem]\n"
724 " type dbfile [key [...]]\n"
725 " %s -w [-CDiNqR] [-E endian] [-F isep] [-f infile] [-m mode]\n"
726 " [-P pagesize] [-T visspec] [-U unvisitem]\n"
727 " type dbfile [key value [...]]\n"
728 ,p ,p ,p );
729 fprintf(stderr,
730 "Supported modes:\n"
731 " read keys [default]\n"
732 " -d delete keys\n"
733 " -w write (add) keys/values\n"
734 "Supported options:\n"
735 " -C create empty (truncated) database\n"
736 " -D allow duplicates\n"
737 " -E endian database endian: `B'ig, `L'ittle, `H'ost [default: H]\n"
738 " -F isep input field separator string [default: a space]\n"
739 " -f infile file of keys (read|delete) or keys/vals (write)\n"
740 " -i ignore case of key by converting to lower case\n"
741 " -K print key\n"
742 " -m mode mode of created database [default: 0644]\n"
743 " -N don't NUL terminate key\n"
744 " -O outsep output field separator string [default: a tab]\n"
745 " -P pagesize database page size [default: 4096]\n"
746 " -q quiet operation (missing keys aren't errors)\n"
747 " -R replace existing keys\n"
748 " -S visitem items to strvis(3) encode: 'k'ey, 'v'alue, 'b'oth\n"
749 " -T visspec options to control -S and -U; like vis(1) options\n"
750 " -U unvisitem items to strunvis(3) decode: 'k'ey, 'v'alue, 'b'oth\n"
751 " -V print value\n"
752 " -X extravis extra characters to encode with -S\n"
754 exit(1);