1 /* $NetBSD: db.c,v 1.23 2009/01/18 01:04:34 lukem Exp $ */
4 * Copyright (c) 2002-2009 The NetBSD Foundation, Inc.
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
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"
36 #include <sys/cdefs.h>
39 __RCSID("$NetBSD: db.c,v 1.23 2009/01/18 01:04:34 lukem Exp $");
63 F_ENDIAN_LITTLE
= 1<<13,
74 int main(int, char *[]);
75 void db_print(DBT
*, DBT
*);
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 **);
90 const char *outputsep
= "\t";
92 const char *extra_echars
= NULL
;
95 main(int argc
, char *argv
[])
104 unsigned int pagesize
;
109 const char *infile
, *fieldsep
;
114 setprogname(argv
[0]);
119 memset(&oi
, 0, sizeof(oi
));
123 /* parse arguments */
124 while ( (ch
= getopt(argc
, argv
,
125 "CDdE:F:f:iKm:NO:P:qRS:T:U:VwX:")) != -1) {
129 flags
|= F_CREATENEW
;
133 flags
|= F_DUPLICATES
;
141 if (! optarg
[0] || optarg
[1])
143 switch (toupper((int)optarg
[0])) {
145 flags
|= F_ENDIAN_BIG
;
148 flags
|= F_ENDIAN_LITTLE
;
151 flags
&= ~(F_ENDIAN_BIG
| F_ENDIAN_LITTLE
);
155 errx(1, "Bad endian `%s'", optarg
);
161 errx(1, "Invalid field separator `%s'",
171 flags
|= F_IGNORECASE
;
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
;
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
;
213 parse_encode_decode_arg(optarg
, 1 /* encode */);
214 if (! (flags
& (F_ENCODE_KEY
| F_ENCODE_VAL
)))
215 errx(1, "Invalid encoding argument `%s'",
220 visflags
= parse_encode_option(&optarg
);
222 errx(1, "Invalid encoding/decoding option `%s'",
227 parse_encode_decode_arg(optarg
, 0 /* decode */);
228 if (! (flags
& (F_DECODE_KEY
| F_DECODE_VAL
)))
229 errx(1, "Invalid decoding argument `%s'",
234 flags
|= F_SHOW_VALUE
;
242 extra_echars
= optarg
;
253 /* validate arguments */
261 if (flags
& F_WRITE
) {
262 if (flags
& (F_SHOW_KEY
| F_SHOW_VALUE
| F_DELETE
))
264 if ((!infile
&& argc
< 2) || (argc
% 2))
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
))
274 if (!infile
&& argc
< 1)
276 if (0 != (visflags
& ~(VIS_HTTPSTYLE
)))
277 errx(1, "Unsupported decoding option provided to -T");
278 oi
.dbflags
= O_RDWR
| O_CREAT
| O_EXLOCK
;
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;
309 warnx("Unknown database type `%s'", oi
.type
);
314 if (strcmp(infile
, "-") == 0)
316 else if ((infp
= fopen(infile
, "r")) == NULL
)
317 err(1, "Opening input file `%s'", infile
);
321 db
= dbopen(oi
.file
, oi
.dbflags
, oi
.mode
, oi
.dbtype
, oi
.info
);
323 err(1, "Opening database `%s'", oi
.file
);
326 /* manipulate database */
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])))
333 while (parseline(infp
, fieldsep
, &key
, &val
)) {
334 if ((rv
= db_put(key
, val
)))
338 warnx("Reading `%s'", infile
);
342 } else if (!infp
&& argc
== 0) { /* read all */
344 } else { /* read/delete specific */
347 if (flags
& F_DELETE
)
349 else if (DB_BTREE
== oi
.dbtype
)
351 else if (DB_HASH
== oi
.dbtype
)
354 errx(5, "internal error: unsupported dbtype %d",
356 for (ch
= 0; ch
< argc
; ch
++) {
357 if ((rv
= dbop(argv
[ch
])))
361 while (parseline(infp
, fieldsep
, &key
, NULL
)) {
362 if ((rv
= dbop(key
)))
366 warnx("Reading `%s'", infile
);
374 if (db
->close(db
) == -1)
375 err(1, "Closing database `%s'", oi
.file
);
382 db_print(DBT
*key
, DBT
*val
)
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
);
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
);
406 len
= (int)MINUSNUL(val
->size
);
407 data
= (char *)val
->data
;
409 printf("%.*s", len
, data
);
420 while ((rv
= db
->seq(db
, &key
, &val
, R_NEXT
)) == 0)
421 db_print(&key
, &val
);
423 warn("Error dumping database");
424 return (rv
== 1 ? 0 : 1);
428 db_makekey(DBT
*key
, char *keystr
, int downcase
, int decode
)
433 memset(key
, 0, sizeof(*key
));
435 if ((klen
= decode_data(keystr
, &ks
)) == -1)
436 errx(1, "Invalid escape sequence in `%s'", keystr
);
438 klen
= strlen(keystr
);
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
);
456 db_makekey(&key
, keystr
, 1, (flags
& F_DECODE_KEY
? 1 : 0));
457 r
= db
->del(db
, &key
, 0);
460 if (! (flags
& F_QUIET
))
461 warn("Error deleting key `%s'", keystr
);
465 if (! (flags
& F_QUIET
))
466 printf("Deleted key `%s'\n", keystr
);
469 if (! (flags
& F_QUIET
))
470 warnx("Unknown key `%s'", keystr
);
473 errx(5, "%s: unexpected result %d from db", __func__
, r
);
475 if (flags
& F_DECODE_KEY
)
486 db_makekey(&key
, keystr
, 1, (flags
& F_DECODE_KEY
? 1 : 0));
488 r
= db
->get(db
, &key
, &val
, 0);
491 warn("Error reading key `%s'", keystr
);
495 db_print(&key
, &val
);
498 if (! (flags
& F_QUIET
)) {
499 warnx("Unknown key `%s'", keystr
);
503 errx(5, "%s: unexpected result %d from db", __func__
, r
);
505 if (flags
& F_DECODE_KEY
)
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
;
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
)) {
532 db_print(&key
, &val
);
533 if (! (flags
& F_DUPLICATES
))
539 warn("Error reading key `%s'", keystr
);
549 if (! (flags
& F_QUIET
)) {
550 warnx("Unknown key `%s'", keystr
);
554 errx(5, "%s: unexpected result %d from db", __func__
, r
);
556 if (flags
& F_DECODE_KEY
)
562 db_put(char *keystr
, char *valstr
)
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
);
572 warn("Error writing key `%s'", keystr
);
576 if (! (flags
& F_QUIET
))
577 printf("Added key `%s'\n", keystr
);
580 if (! (flags
& F_QUIET
))
581 warnx("Key `%s' already exists", keystr
);
584 errx(5, "Unexpected result %d in %s", r
, __func__
);
586 if (flags
& F_DECODE_KEY
)
588 if (flags
& F_DECODE_VAL
)
594 parseline(FILE *fp
, const char *sep
, char **kp
, char **vp
)
599 key
= fgetln(fp
, &len
);
600 if (key
== NULL
) /* end of file, or error */
603 if (key
[len
-1] == '\n') /* check for \n at EOL */
609 if (vp
== NULL
) /* don't split if don't want value */
611 if ((val
= strstr(key
, sep
)) == NULL
)
622 encode_data(size_t len
, char *data
, char **edata
)
624 static char *buf
= NULL
;
626 static size_t buflen
= 0;
629 elen
= 1 + (len
* 4);
631 if ((nbuf
= realloc(buf
, elen
)) == NULL
)
632 err(1, "Cannot allocate encoding buffer");
638 return (strsvisx(buf
, data
, len
, visflags
, extra_echars
));
640 return (strvisx(buf
, data
, len
, visflags
));
645 decode_data(char *data
, char **ddata
)
649 if ((buf
= malloc(strlen(data
) + 1)) == NULL
)
650 err(1, "Cannot allocate decoding buffer");
652 return (strunvisx(buf
, data
, (visflags
& VIS_HTTPSTYLE
)));
656 parse_encode_decode_arg(const char *arg
, int encode
)
658 if (! arg
[0] || arg
[1])
660 if (arg
[0] == 'k' || arg
[0] == 'b') {
662 flags
|= F_ENCODE_KEY
;
664 flags
|= F_DECODE_KEY
;
666 if (arg
[0] == 'v' || arg
[0] == 'b') {
668 flags
|= F_ENCODE_VAL
;
670 flags
|= F_DECODE_VAL
;
676 parse_encode_option(char **arg
)
679 int encmask
= ~(VIS_CSTYLE
| VIS_HTTPSTYLE
| VIS_OCTAL
);
681 for(; **arg
; (*arg
)++) {
718 const char *p
= getprogname();
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"
731 " read keys [default]\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"
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"
752 " -X extravis extra characters to encode with -S\n"