Home | History | Annotate | Line # | Download | only in db
db.c revision 1.9
      1 /*	$NetBSD: db.c,v 1.9 2003/05/20 08:34:52 wiz 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.9 2003/05/20 08:34:52 wiz 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 #include <vis.h>
     53 
     54 
     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;
     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 int	encode_data(size_t, char *, char **);
     82 int	decode_data(char *, char **);
     83 void	parse_encode_decode_arg(const char *, int);
     84 int	parse_encode_option(char **);
     85 void	usage(void);
     86 
     87 flags_t	 flags = 0;
     88 DB	*db;
     89 char	*outputsep = "\t";
     90 int	encflags = 0;
     91 char	*extra_echars = NULL;
     92 
     93 int
     94 main(int argc, char *argv[])
     95 {
     96 	struct {
     97 		char	*file;
     98 		char	*type;
     99 		DBTYPE	 dbtype;
    100 		void	*info;
    101 		int	 flags;
    102 		mode_t	 mode;
    103 	} oi;
    104 	BTREEINFO	btreeinfo;
    105 	HASHINFO	hashinfo;
    106 	FILE		*infp;
    107 	const char	*infile, *fieldsep;
    108 	char		*p, *key, *val;
    109 	int		ch, rv;
    110 
    111 	setprogname(argv[0]);
    112 
    113 	infile = NULL;
    114 	fieldsep = " ";
    115 	infp = NULL;
    116 	memset(&oi, 0, sizeof(oi));
    117 	oi.mode = 0644;
    118 
    119 				/* parse arguments */
    120 	while ( (ch = getopt(argc, argv,
    121 			     "CDdE:F:f:iKm:NO:qRS:T:U:VwX:")) != -1) {
    122 		switch (ch) {
    123 
    124 		case 'C':
    125 			flags |= F_CREATENEW;
    126 			break;
    127 
    128 		case 'D':
    129 			flags |= F_DUPLICATES;
    130 			break;
    131 
    132 		case 'd':
    133 			flags |= F_DELETE;
    134 			break;
    135 
    136 		case 'E':
    137 			if (! optarg[0] || optarg[1])
    138 				goto badendian;
    139 			switch (toupper((int)optarg[0])) {
    140 			case 'B':
    141 				flags |= F_ENDIAN_BIG;
    142 				break;
    143 			case 'L':
    144 				flags |= F_ENDIAN_LITTLE;
    145 				break;
    146 			case 'H':
    147 				flags &= ~(F_ENDIAN_BIG | F_ENDIAN_LITTLE);
    148 				break;
    149 			default:
    150  badendian:
    151 				errx(1, "Bad endian `%s'", optarg);
    152 			}
    153 			break;
    154 
    155 		case 'F':
    156 			if (! optarg[0] || optarg[1])
    157 				errx(1, "Invalid field separator `%s'",
    158 				    optarg);
    159 			fieldsep = optarg;
    160 			break;
    161 
    162 		case 'f':
    163 			infile = optarg;
    164 			break;
    165 
    166 		case 'i':
    167 			flags |= F_IGNORECASE;
    168 			break;
    169 
    170 		case 'K':
    171 			flags |= F_SHOW_KEY;
    172 			break;
    173 
    174 		case 'm':
    175 			oi.mode = (int)strtol(optarg, &p, 8);
    176 			if (p == optarg || *p != '\0')
    177 				errx(1, "Invalid octal number `%s'", optarg);
    178 			break;
    179 
    180 		case 'N':
    181 			flags |= F_NO_NUL;
    182 			break;
    183 
    184 		case 'O':
    185 			outputsep = optarg;
    186 			break;
    187 
    188 		case 'q':
    189 			flags |= F_QUIET;
    190 			break;
    191 
    192 		case 'R':
    193 			flags |= F_REPLACE;
    194 			break;
    195 
    196 		case 'S':
    197 			parse_encode_decode_arg(optarg, 1 /* encode */);
    198 			if (! (flags & (F_ENCODE_KEY | F_ENCODE_VAL)))
    199 				errx(1, "Invalid encoding argument `%s'",
    200 				    optarg);
    201 			break;
    202 
    203 		case 'T':
    204 			encflags = parse_encode_option(&optarg);
    205 			if (! encflags)
    206 				errx(1, "Invalid encoding option `%s'",
    207 				    optarg);
    208 			break;
    209 
    210 		case 'U':
    211 			parse_encode_decode_arg(optarg, 0 /* decode */);
    212 			if (! (flags & (F_DECODE_KEY | F_DECODE_VAL)))
    213 				errx(1, "Invalid decoding argument `%s'",
    214 				    optarg);
    215 			break;
    216 
    217 		case 'V':
    218 			flags |= F_SHOW_VALUE;
    219 			break;
    220 
    221 		case 'w':
    222 			flags |= F_WRITE;
    223 			break;
    224 
    225 		case 'X':
    226 			extra_echars = optarg;
    227 			break;
    228 
    229 		default:
    230 			usage();
    231 
    232 		}
    233 	}
    234 	argc -= optind;
    235 	argv += optind;
    236 
    237 				/* validate arguments */
    238 	if (argc < 2)
    239 		usage();
    240 	oi.type = argv[0];
    241 	oi.file = argv[1];
    242 	argc -= 2;
    243 	argv += 2;
    244 
    245 	if (flags & F_WRITE) {
    246 		if (flags & (F_SHOW_KEY | F_SHOW_VALUE | F_DELETE))
    247 			usage();
    248 		if ((!infile && argc < 2) || (argc % 2))
    249 			usage();
    250 		oi.flags = O_RDWR | O_CREAT | O_EXLOCK;
    251 		if (flags & F_CREATENEW)
    252 			flags |= O_TRUNC;
    253 	} else if (flags & F_DELETE) {
    254 		if (flags & (F_SHOW_KEY | F_SHOW_VALUE | F_WRITE))
    255 			usage();
    256 		if (!infile && argc < 1)
    257 			usage();
    258 		oi.flags = O_RDWR | O_CREAT | O_EXLOCK;
    259 	} else {
    260 		if (! (flags & (F_SHOW_KEY | F_SHOW_VALUE)))
    261 			flags |= (F_SHOW_KEY | F_SHOW_VALUE);
    262 		oi.flags = O_RDONLY | O_SHLOCK;
    263 	}
    264 
    265 				/* validate oi.type */
    266 	if (strcmp(oi.type, "btree") == 0) {
    267 		memset(&btreeinfo, 0, sizeof(btreeinfo));
    268 		if (flags & F_ENDIAN_BIG)
    269 			btreeinfo.lorder = 4321;
    270 		else if (flags & F_ENDIAN_LITTLE)
    271 			btreeinfo.lorder = 1234;
    272 		if (flags & F_DUPLICATES)
    273 			btreeinfo.flags = R_DUP;
    274 		btreeinfo.cachesize = 1024 * 1024;
    275 		oi.info = &btreeinfo;
    276 		oi.dbtype = DB_BTREE;
    277 	} else if (strcmp(oi.type, "hash") == 0) {
    278 		memset(&hashinfo, 0, sizeof(hashinfo));
    279 		if (flags & F_ENDIAN_BIG)
    280 			hashinfo.lorder = 4321;
    281 		else if (flags & F_ENDIAN_LITTLE)
    282 			hashinfo.lorder = 1234;
    283 		hashinfo.cachesize = 1024 * 1024;
    284 		oi.info = &hashinfo;
    285 		oi.dbtype = DB_HASH;
    286 	} else {
    287 		warnx("Unknown database type `%s'", oi.type);
    288 		usage();
    289 	}
    290 
    291 	if (infile) {
    292 		if (strcmp(infile, "-") == 0)
    293 			infp = stdin;
    294 		else if ((infp = fopen(infile, "r")) == NULL)
    295 			err(1, "Opening input file `%s'", infile);
    296 	}
    297 
    298 				/* open database */
    299 	db = dbopen(oi.file, oi.flags, oi.mode, oi.dbtype, oi.info);
    300 	if (db == NULL)
    301 		err(1, "Opening database `%s'", oi.file);
    302 
    303 
    304 				/* manipulate database */
    305 	rv = 0;
    306 	if (flags & F_WRITE) {			/* write entries */
    307 		for (ch = 0; ch < argc; ch += 2)
    308 			if ((rv = db_put(argv[ch], argv[ch+1])))
    309 				goto cleanup;
    310 		if (infp) {
    311 			while (parseline(infp, fieldsep, &key, &val)) {
    312 				if ((rv = db_put(key, val)))
    313 					goto cleanup;
    314 			}
    315 			if (ferror(infp)) {
    316 				warnx("Reading `%s'", infile);
    317 				goto cleanup;
    318 			}
    319 		}
    320 	} else if (!infp && argc == 0) {	/* read all */
    321 		db_dump();
    322 	} else {				/* read/delete specific */
    323 		int	(*dbop)(char *);
    324 
    325 		if (flags & F_DELETE)
    326 			dbop = db_del;
    327 		else
    328 			dbop = db_get;
    329 		for (ch = 0; ch < argc; ch++) {
    330 			if ((rv = dbop(argv[ch])))
    331 				goto cleanup;
    332 		}
    333 		if (infp) {
    334 			while (parseline(infp, fieldsep, &key, NULL)) {
    335 				if ((rv = dbop(key)))
    336 					goto cleanup;
    337 			}
    338 			if (ferror(infp)) {
    339 				warnx("Reading `%s'", infile);
    340 				goto cleanup;
    341 			}
    342 		}
    343 	}
    344 
    345 				/* close database */
    346  cleanup:
    347 	if (db->close(db) == -1)
    348 		err(1, "Closing database `%s'", oi.file);
    349 	if (infp)
    350 		fclose(infp);
    351 	return (rv);
    352 }
    353 
    354 void
    355 db_print(DBT *key, DBT *val)
    356 {
    357 
    358 	int len;
    359 	char *data;
    360 
    361 	if (flags & F_SHOW_KEY) {
    362 		if (flags & F_ENCODE_KEY)
    363 			len = encode_data(key->size - 1, (char *)key->data,
    364 					 &data);
    365 		else {
    366 			len = (int)key->size;
    367 			data = (char *)key->data;
    368 		}
    369 		printf("%.*s", len, data);
    370 	}
    371 	if ((flags & F_SHOW_KEY) && (flags & F_SHOW_VALUE))
    372 		printf("%s", outputsep);
    373 	if (flags & F_SHOW_VALUE) {
    374 		if (flags & F_ENCODE_VAL)
    375 			len = encode_data(val->size - 1, (char *)val->data,
    376 					 &data);
    377 		else {
    378 			len = (int)val->size;
    379 			data = (char *)val->data;
    380 		}
    381 		printf("%.*s", len, data);
    382 	}
    383 	printf("\n");
    384 }
    385 
    386 int
    387 db_dump(void)
    388 {
    389 	DBT	key, val;
    390 	int	rv;
    391 
    392 	while ((rv = db->seq(db, &key, &val, R_NEXT)) == 0)
    393 		db_print(&key, &val);
    394 	if (rv == -1)
    395 		warn("Error dumping database");
    396 	return (rv == 1 ? 0 : 1);
    397 }
    398 
    399 static void
    400 db_makekey(DBT *key, char *keystr, int downcase, int decode)
    401 {
    402 	char	*p, *ks;
    403 	int	klen;
    404 
    405 	memset(key, 0, sizeof(*key));
    406 	if (decode) {
    407 		if ((klen = decode_data(keystr, &ks)) == -1)
    408 			errx(1, "Invalid escape sequence in `%s'",
    409 		  	  keystr);
    410 	} else {
    411 		klen = strlen(keystr);
    412 		ks = keystr;
    413 	}
    414 	key->data = ks;
    415 	key->size = klen + (flags & F_NO_NUL ? 0 : 1);
    416 	if (downcase && flags & F_IGNORECASE) {
    417 		for (p = ks; *p; p++)
    418 			if (isupper((int)*p))
    419 				*p = tolower((int)*p);
    420 	}
    421 }
    422 
    423 int
    424 db_del(char *keystr)
    425 {
    426 	DBT	key;
    427 	int	r = 0;
    428 
    429 	db_makekey(&key, keystr, 1, (flags & F_DECODE_KEY ? 1 : 0));
    430 	switch (db->del(db, &key, 0)) {
    431 	case -1:
    432 		warn("Error deleting key `%s'", keystr);
    433 		r = 1;
    434 		break;
    435 	case 0:
    436 		if (! (flags & F_QUIET))
    437 			printf("Deleted key `%s'\n", keystr);
    438 		break;
    439 	case 1:
    440 		warnx("Key `%s' does not exist", keystr);
    441 		r = 1;
    442 		break;
    443 	}
    444 	if (flags & F_DECODE_KEY)
    445 		free(key.data);
    446 	return (r);
    447 }
    448 
    449 int
    450 db_get(char *keystr)
    451 {
    452 	DBT	key, val;
    453 	int	r = 0;
    454 
    455 	db_makekey(&key, keystr, 1, (flags & F_DECODE_KEY ? 1 : 0));
    456 	switch (db->get(db, &key, &val, 0)) {
    457 	case -1:
    458 		warn("Error reading key `%s'", keystr);
    459 		r = 1;
    460 		break;
    461 	case 0:
    462 		db_print(&key, &val);
    463 		break;
    464 	case 1:
    465 		if (! (flags & F_QUIET)) {
    466 			warnx("Unknown key `%s'", keystr);
    467 			r = 1;
    468 		}
    469 		break;
    470 	}
    471 	if (flags & F_DECODE_KEY)
    472 		free(key.data);
    473 	return (r);
    474 }
    475 
    476 int
    477 db_put(char *keystr, char *valstr)
    478 {
    479 	DBT	key, val;
    480 	int	r = 0;
    481 
    482 	db_makekey(&key, keystr, 1, (flags & F_DECODE_KEY ? 1 : 0));
    483 	db_makekey(&val, valstr, 0, (flags & F_DECODE_VAL ? 1 : 0));
    484 	switch (db->put(db, &key, &val,
    485 	    (flags & F_REPLACE) ? 0 : R_NOOVERWRITE)) {
    486 	case -1:
    487 		warn("Error writing key `%s'", keystr);
    488 		r = 1;
    489 		break;
    490 	case 0:
    491 		if (! (flags & F_QUIET))
    492 			printf("Added key `%s'\n", keystr);
    493 		break;
    494 	case 1:
    495 		if (! (flags & F_QUIET))
    496 			warnx("Key `%s' already exists", keystr);
    497 		r = 1;
    498 		break;
    499 	}
    500 	if (flags & F_DECODE_KEY)
    501 		free(key.data);
    502 	if (flags & F_DECODE_VAL)
    503 		free(val.data);
    504 	return (r);
    505 }
    506 
    507 int
    508 parseline(FILE *fp, const char *sep, char **kp, char **vp)
    509 {
    510 	size_t	len;
    511 	char	*key, *val;
    512 
    513 	key = fgetln(fp, &len);
    514 	if (key == NULL)		/* end of file, or error */
    515 		return (0);
    516 
    517 	if (key[len-1] == '\n')		/* check for \n at EOL */
    518 		key[--len] = '\0';
    519 	else
    520 		return (0);
    521 
    522 	*kp = key;
    523 	if (vp == NULL)			/* don't split if don't want value */
    524 		return (1);
    525 	if ((val = strchr(key, sep[0])) == NULL)
    526 		val = key + len;
    527 	else
    528 		*val++ = '\0';
    529 	*vp = val;
    530 	return (1);
    531 }
    532 
    533 int
    534 encode_data(size_t len, char *data, char **edata)
    535 {
    536 	static char	*buf = NULL;
    537 	static size_t	buflen = 0;
    538 	size_t		elen;
    539 
    540 	elen = 1 + (len * 4);
    541 	if (elen > buflen) {
    542 		if ((buf = realloc(buf, elen)) == NULL)
    543 			err(1, "Cannot allocate encoding buffer");
    544 		buflen = elen;
    545 	}
    546 	*edata = buf;
    547 	if (extra_echars) {
    548 		return (strsvisx(buf, data, len, encflags, extra_echars));
    549 	} else {
    550 		return (strvisx(buf, data, len, encflags));
    551 	}
    552 }
    553 
    554 int
    555 decode_data(char *data, char **ddata)
    556 {
    557 	char	*buf;
    558 
    559 	if ((buf = malloc(strlen(data) + 1)) == NULL)
    560 		err(1, "Cannot allocate decoding buffer");
    561 	*ddata = buf;
    562 	return (strunvis(buf, data));
    563 }
    564 
    565 void
    566 parse_encode_decode_arg(const char *arg, int encode)
    567 {
    568 	if (! arg[0] || arg[1])
    569 		return;
    570 	if (arg[0] == 'k' || arg[0] == 'b') {
    571 		if (encode)
    572 			flags |= F_ENCODE_KEY;
    573 		else
    574 			flags |= F_DECODE_KEY;
    575 	}
    576 	if (arg[0] == 'v' || arg[0] == 'b') {
    577 		if (encode)
    578 			flags |= F_ENCODE_VAL;
    579 		else
    580 			flags |= F_DECODE_VAL;
    581 	}
    582 	return;
    583 }
    584 
    585 int
    586 parse_encode_option(char **arg)
    587 {
    588 	int	r = 0;
    589 
    590 	for(; **arg; (*arg)++) {
    591 		switch (**arg) {
    592 			case 'b':
    593 				r |= VIS_NOSLASH;
    594 				break;
    595 			case 'c':
    596 				r |= VIS_CSTYLE;
    597 				break;
    598 			case 'o':
    599 				r |= VIS_OCTAL;
    600 				break;
    601 			case 's':
    602 				r |= VIS_SAFE;
    603 				break;
    604 			case 't':
    605 				r |= VIS_TAB;
    606 				break;
    607 			case 'w':
    608 				r |= VIS_WHITE;
    609 				break;
    610 			default:
    611 				return (0);
    612 				break;
    613 		}
    614 	}
    615 	return (r);
    616 }
    617 
    618 void
    619 usage(void)
    620 {
    621 	const char *p = getprogname();
    622 
    623 	fprintf(stderr,
    624     "Usage: %s [-KiNqV] [-E end] [-f inf] [-O str] [-S chr] [-T str] [-X str]\n"
    625     "             type dbfile [key [...]]\n"
    626     "       %s -d [-iNq] [-E end] [-f inf] [-U chr] type dbfile [key [...]]\n"
    627     "       %s -w [-CDiNqR] [-E end] [-F sep] [-f inf] [-m mod] [-U chr]\n"
    628     "             type dbfile [key val [...]]\n"
    629 	    ,p ,p ,p );
    630 	fprintf(stderr,
    631 	    "Supported modes:\n"
    632 	    "\t\tread keys  [default]\n"
    633 	    "\t-d\tdelete keys\n"
    634 	    "\t-w\twrite (add) keys/values\n"
    635 	    "Supported options:\n"
    636 	    "\t-C\tcreate empty (truncated) database\n"
    637 	    "\t-D\tallow duplicates\n"
    638 	    "\t-E end\tdatabase endian: `B'ig, `L'ittle, `H'ost  [default: H]\n"
    639 	    "\t-F sep\tfield separator character  [default: ' ']\n"
    640 	    "\t-f inf\tfile of keys (read|delete) or keys/vals (write)\n"
    641 	    "\t-i\tignore case of key by converting to lower case\n"
    642 	    "\t-K\tprint key\n"
    643 	    "\t-m mod\tmode of created database  [default: 0644]\n"
    644 	    "\t-N\tdon't NUL terminate key\n"
    645 	    "\t-O str\toutput field separator string [default: '\\t']\n"
    646 	    "\t-q\tquiet operation (missing keys aren't errors)\n"
    647 	    "\t-R\treplace existing keys\n"
    648 	    "\t-S chr\titems to strvis(3) encode: 'k'ey, 'v'alue, 'b'oth\n"
    649 	    "\t-T str\toptions to control -S encoding like vis(1) options\n"
    650 	    "\t-U chr\titems to strunvis(3) decode: 'k'ey, 'v'alue, 'b'oth\n"
    651 	    "\t-V\tprint value\n"
    652 	    "\t-X str\textra characters to encode with -S\n"
    653 	    );
    654 	exit(1);
    655 }
    656