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