Home | History | Annotate | Line # | Download | only in db
db.c revision 1.5
      1 /*	$NetBSD: db.c,v 1.5 2002/12/29 11:17:04 seb 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.5 2002/12/29 11:17:04 seb 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 
     53 
     54 /*
     55  * TODO --
     56  *   -	add option to strunvis(3) key (& value) from infile ?
     57  */
     58 
     59 typedef enum {
     60 	F_WRITE		= 1<<0,
     61 	F_DELETE	= 1<<1,
     62 	F_SHOW_KEY	= 1<<2,
     63 	F_SHOW_VALUE	= 1<<3,
     64 	F_QUIET		= 1<<10,
     65 	F_IGNORECASE	= 1<<11,
     66 	F_ENDIAN_BIG	= 1<<12,
     67 	F_ENDIAN_LITTLE	= 1<<13,
     68 	F_NO_NUL	= 1<<14,
     69 	F_CREATENEW	= 1<<20,
     70 	F_DUPLICATES	= 1<<21,
     71 	F_REPLACE	= 1<<22,
     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 void	usage(void);
     82 
     83 flags_t	 flags;
     84 DB	*db;
     85 char	*outputsep = "\t";
     86 
     87 int
     88 main(int argc, char *argv[])
     89 {
     90 	struct {
     91 		char	*file;
     92 		char	*type;
     93 		DBTYPE	 dbtype;
     94 		void	*info;
     95 		int	 flags;
     96 		mode_t	 mode;
     97 	} oi;
     98 	BTREEINFO	btreeinfo;
     99 	HASHINFO	hashinfo;
    100 	FILE		*infp;
    101 	const char	*infile, *fieldsep;
    102 	char		*p, *key, *val;
    103 	int		ch, rv;
    104 
    105 	setprogname(argv[0]);
    106 
    107 	flags = 0;
    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, "CDdE:F:f:iKm:NqRs:Vw")) != -1) {
    116 		switch (ch) {
    117 
    118 		case 'C':
    119 			flags |= F_CREATENEW;
    120 			break;
    121 
    122 		case 'D':
    123 			flags |= F_DUPLICATES;
    124 			break;
    125 
    126 		case 'd':
    127 			flags |= F_DELETE;
    128 			break;
    129 
    130 		case 'E':
    131 			if (! optarg[0] || optarg[1])
    132 				goto badendian;
    133 			switch (toupper((int)optarg[0])) {
    134 			case 'B':
    135 				flags |= F_ENDIAN_BIG;
    136 				break;
    137 			case 'L':
    138 				flags |= F_ENDIAN_LITTLE;
    139 				break;
    140 			case 'H':
    141 				flags &= ~(F_ENDIAN_BIG | F_ENDIAN_LITTLE);
    142 				break;
    143 			default:
    144  badendian:
    145 				errx(1, "Bad endian `%s'", optarg);
    146 			}
    147 			break;
    148 
    149 		case 'F':
    150 			if (! optarg[0] || optarg[1])
    151 				errx(1, "Invalid field separator `%s'",
    152 				    optarg);
    153 			fieldsep = optarg;
    154 			break;
    155 
    156 		case 'f':
    157 			infile = optarg;
    158 			break;
    159 
    160 		case 'i':
    161 			flags |= F_IGNORECASE;
    162 			break;
    163 
    164 		case 'K':
    165 			flags |= F_SHOW_KEY;
    166 			break;
    167 
    168 		case 'm':
    169 			oi.mode = (int)strtol(optarg, &p, 8);
    170 			if (p == optarg || *p != '\0')
    171 				errx(1, "Invalid octal number `%s'", optarg);
    172 			break;
    173 
    174 		case 'N':
    175 			flags |= F_NO_NUL;
    176 			break;
    177 
    178 		case 'q':
    179 			flags |= F_QUIET;
    180 			break;
    181 
    182 		case 'R':
    183 			flags |= F_REPLACE;
    184 			break;
    185 
    186 		case 's':
    187 			outputsep = optarg;
    188 			break;
    189 
    190 		case 'V':
    191 			flags |= F_SHOW_VALUE;
    192 			break;
    193 
    194 		case 'w':
    195 			flags |= F_WRITE;
    196 			break;
    197 
    198 		default:
    199 			usage();
    200 
    201 		}
    202 	}
    203 	argc -= optind;
    204 	argv += optind;
    205 
    206 				/* validate arguments */
    207 	if (argc < 2)
    208 		usage();
    209 	oi.type = argv[0];
    210 	oi.file = argv[1];
    211 	argc -= 2;
    212 	argv += 2;
    213 
    214 	if (flags & F_WRITE) {
    215 		if (flags & (F_SHOW_KEY | F_SHOW_VALUE | F_DELETE))
    216 			usage();
    217 		if ((!infile && argc < 2) || (argc % 2))
    218 			usage();
    219 		oi.flags = O_RDWR | O_CREAT | O_EXLOCK;
    220 		if (flags & F_CREATENEW)
    221 			flags |= O_TRUNC;
    222 	} else if (flags & F_DELETE) {
    223 		if (flags & (F_SHOW_KEY | F_SHOW_VALUE | F_WRITE))
    224 			usage();
    225 		if (!infile && argc < 1)
    226 			usage();
    227 		oi.flags = O_RDWR | O_CREAT | O_EXLOCK;
    228 	} else {
    229 		if (! (flags & (F_SHOW_KEY | F_SHOW_VALUE)))
    230 			flags |= (F_SHOW_KEY | F_SHOW_VALUE);
    231 		oi.flags = O_RDONLY | O_SHLOCK;
    232 	}
    233 
    234 				/* validate oi.type */
    235 	if (strcmp(oi.type, "btree") == 0) {
    236 		memset(&btreeinfo, 0, sizeof(btreeinfo));
    237 		if (flags & F_ENDIAN_BIG)
    238 			btreeinfo.lorder = 4321;
    239 		else if (flags & F_ENDIAN_LITTLE)
    240 			btreeinfo.lorder = 1234;
    241 		if (flags & F_DUPLICATES)
    242 			btreeinfo.flags = R_DUP;
    243 		btreeinfo.cachesize = 1024 * 1024;
    244 		oi.info = &btreeinfo;
    245 		oi.dbtype = DB_BTREE;
    246 	} else if (strcmp(oi.type, "hash") == 0) {
    247 		memset(&hashinfo, 0, sizeof(hashinfo));
    248 		if (flags & F_ENDIAN_BIG)
    249 			hashinfo.lorder = 4321;
    250 		else if (flags & F_ENDIAN_LITTLE)
    251 			hashinfo.lorder = 1234;
    252 		hashinfo.cachesize = 1024 * 1024;
    253 		oi.info = &hashinfo;
    254 		oi.dbtype = DB_HASH;
    255 	} else {
    256 		warnx("Unknown database type `%s'", oi.type);
    257 		usage();
    258 	}
    259 
    260 	if (infile) {
    261 		if (strcmp(infile, "-") == 0)
    262 			infp = stdin;
    263 		else if ((infp = fopen(infile, "r")) == NULL)
    264 			err(1, "Opening input file `%s'", infile);
    265 	}
    266 
    267 				/* open database */
    268 	db = dbopen(oi.file, oi.flags, oi.mode, oi.dbtype, oi.info);
    269 	if (db == NULL)
    270 		err(1, "Opening database `%s'", oi.file);
    271 
    272 
    273 				/* manipulate database */
    274 	rv = 0;
    275 	if (flags & F_WRITE) {			/* write entries */
    276 		for (ch = 0; ch < argc; ch += 2)
    277 			if ((rv = db_put(argv[ch], argv[ch+1])))
    278 				goto cleanup;
    279 		if (infp) {
    280 			while (parseline(infp, fieldsep, &key, &val)) {
    281 				if ((rv = db_put(key, val)))
    282 					goto cleanup;
    283 			}
    284 			if (ferror(infp)) {
    285 				warnx("Reading `%s'", infile);
    286 				goto cleanup;
    287 			}
    288 		}
    289 	} else if (!infp && argc == 0) {	/* read all */
    290 		db_dump();
    291 	} else {				/* read/delete specific */
    292 		int	(*dbop)(char *);
    293 
    294 		if (flags & F_DELETE)
    295 			dbop = db_del;
    296 		else
    297 			dbop = db_get;
    298 		for (ch = 0; ch < argc; ch++) {
    299 			if ((rv = dbop(argv[ch])))
    300 				goto cleanup;
    301 		}
    302 		if (infp) {
    303 			while (parseline(infp, fieldsep, &key, NULL)) {
    304 				if ((rv = dbop(key)))
    305 					goto cleanup;
    306 			}
    307 			if (ferror(infp)) {
    308 				warnx("Reading `%s'", infile);
    309 				goto cleanup;
    310 			}
    311 		}
    312 	}
    313 
    314 				/* close database */
    315  cleanup:
    316 	if (db->close(db) == -1)
    317 		err(1, "Closing database `%s'", oi.file);
    318 	if (infp)
    319 		fclose(infp);
    320 	return (rv);
    321 }
    322 
    323 void
    324 db_print(DBT *key, DBT *val)
    325 {
    326 
    327 	if (flags & F_SHOW_KEY)
    328 		printf("%.*s", (int)key->size, (char *)key->data);
    329 	if ((flags & F_SHOW_KEY) && (flags & F_SHOW_VALUE))
    330 		printf("%s", outputsep);
    331 	if (flags & F_SHOW_VALUE)
    332 		printf("%.*s", (int)val->size, (char *)val->data);
    333 	printf("\n");
    334 }
    335 
    336 int
    337 db_dump(void)
    338 {
    339 	DBT	key, val;
    340 	int	rv;
    341 
    342 	while ((rv = db->seq(db, &key, &val, R_NEXT)) == 0)
    343 		db_print(&key, &val);
    344 	if (rv == -1)
    345 		warn("Error dumping database");
    346 	return (rv == 1 ? 0 : 1);
    347 }
    348 
    349 static void
    350 db_makekey(DBT *key, char *keystr, int downcase)
    351 {
    352 	char	*p;
    353 
    354 	memset(key, 0, sizeof(*key));
    355 	key->data = keystr;
    356 	key->size = strlen(keystr) + (flags & F_NO_NUL ? 0 : 1);
    357 	if (downcase && flags & F_IGNORECASE) {
    358 		for (p = keystr; *p; p++)
    359 			if (isupper((int)*p))
    360 				*p = tolower((int)*p);
    361 	}
    362 }
    363 
    364 int
    365 db_del(char *keystr)
    366 {
    367 	DBT	key;
    368 
    369 	db_makekey(&key, keystr, 1);
    370 	switch (db->del(db, &key, 0)) {
    371 	case -1:
    372 		warn("Error deleting key `%s'", keystr);
    373 		return (1);
    374 	case 0:
    375 		if (! (flags & F_QUIET))
    376 			printf("Deleted key `%s'\n", keystr);
    377 		break;
    378 	case 1:
    379 		warnx("Key `%s' does not exist", keystr);
    380 		return (1);
    381 	}
    382 	return (0);
    383 }
    384 
    385 int
    386 db_get(char *keystr)
    387 {
    388 	DBT	key, val;
    389 
    390 	db_makekey(&key, keystr, 1);
    391 	switch (db->get(db, &key, &val, 0)) {
    392 	case -1:
    393 		warn("Error reading key `%s'", keystr);
    394 		return (1);
    395 	case 0:
    396 		db_print(&key, &val);
    397 		break;
    398 	case 1:
    399 		if (! (flags & F_QUIET)) {
    400 			warnx("Unknown key `%s'", keystr);
    401 			return (1);
    402 		}
    403 		break;
    404 	}
    405 	return (0);
    406 }
    407 
    408 int
    409 db_put(char *keystr, char *valstr)
    410 {
    411 	DBT	key, val;
    412 
    413 	db_makekey(&key, keystr, 1);
    414 	db_makekey(&val, valstr, 1);
    415 	switch (db->put(db, &key, &val,
    416 	    (flags & F_REPLACE) ? 0 : R_NOOVERWRITE)) {
    417 	case -1:
    418 		warn("Error writing key `%s'", keystr);
    419 		return (1);
    420 	case 0:
    421 		if (! (flags & F_QUIET))
    422 			printf("Added key `%s'\n", keystr);
    423 		break;
    424 	case 1:
    425 		if (! (flags & F_QUIET))
    426 			warnx("Key `%s' already exists", keystr);
    427 		return (1);
    428 	}
    429 	return (0);
    430 }
    431 
    432 int
    433 parseline(FILE *fp, const char *sep, char **kp, char **vp)
    434 {
    435 	size_t	len;
    436 	char	*key, *val;
    437 
    438 	key = fgetln(fp, &len);
    439 	if (key == NULL)		/* end of file, or error */
    440 		return (0);
    441 
    442 	if (key[len-1] == '\n')		/* check for \n at EOL */
    443 		key[--len] = '\0';
    444 	else
    445 		return (0);
    446 
    447 	*kp = key;
    448 	if (vp == NULL)			/* don't split if don't want value */
    449 		return (1);
    450 	if ((val = strchr(key, sep[0])) == NULL)
    451 		val = key + len;
    452 	else
    453 		*val++ = '\0';
    454 	*vp = val;
    455 	return (1);
    456 }
    457 
    458 void
    459 usage(void)
    460 {
    461 	const char *p = getprogname();
    462 
    463 	fprintf(stderr,
    464     "Usage: %s [-KV] [-Niq] [-E end] [-f inf] type dbfile [key [...]]\n"
    465     "       %s -d [-Niq] [-E end] [-f inf] type dbfile [key [...]]\n"
    466     "       %s -w [-Niq] [-E end] [-f inf] [-CDR] [-F sep] [-m mod] [-s str]\n"
    467     "             type dbfile [key val [...]]\n"
    468 	    ,p ,p ,p );
    469 	fprintf(stderr,
    470 	    "Supported modes:\n"
    471 	    "\t\tread keys  [default]\n"
    472 	    "\t-d\tdelete keys\n"
    473 	    "\t-w\twrite (add) keys/values\n"
    474 	    "Supported options:\n"
    475 	    "\t-C\tcreate empty (truncated) database\n"
    476 	    "\t-D\tallow duplicates\n"
    477 	    "\t-E end\tdatabase endian: `B'ig, `L'ittle, `H'ost  [default: H]\n"
    478 	    "\t-F sep\tfield separator character  [default: ' ']\n"
    479 	    "\t-K\tprint key\n"
    480 	    "\t-N\tdon't NUL terminate key\n"
    481 	    "\t-R\treplace existing keys\n"
    482 	    "\t-V\tprint value\n"
    483 	    "\t-f inf\tfile of keys (read|delete) or keys/vals (write)\n"
    484 	    "\t-i\tignore case of key by converting to lower case\n"
    485 	    "\t-m mod\tmode of created database  [default: 0644]\n"
    486 	    "\t-q\tquiet operation (missing keys aren't errors)\n"
    487 	    "\t-s sep\toutput field separator string [default: '\\t']\n"
    488 	    );
    489 	exit(1);
    490 }
    491