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