1 /*
2 * dbz - use and test dbz in various ways
3 *
4 * -Log-
5 */
6
7 #include <stdio.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <string.h>
11 #include <stdlib.h>
12 #include <dbz.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15
16 #ifdef FUNNYSEEKS
17 #include <unistd.h>
18 #else
19 #define SEEK_SET 0
20 #endif
21
22 #define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
23
24 #ifndef lint
25 static char RCSid[] = "$Header$";
26 #endif
27
28 char *progname;
29
30 const char *inname = "(no file)"; /* filename for messages etc. */
31 long lineno; /* line number for messages etc. */
32
33 char *base_name;
34 char *pagname;
35 char *dir_name;
36 FILE *base;
37
38 int op = 'b'; /* what to do, default build a new table */
39 const char *badop = "only one of -a -x -c -m -v can be given";
40 int baseinput = 1; /* is the base file also the input? */
41
42 char *from = NULL; /* old table to use for dbzagain() */
43 int omitzero = 0; /* omit lines tagged with 0 */
44 long every = 0; /* report every n lines */
45 int syncs = 0; /* dbzsync() on each report */
46 int quick = 0; /* quick checking, not too thorough */
47 int sweep = 0; /* sweep file checking all offsets */
48 int useincore = 1; /* should we use incore facility? */
49 long xxx = 0; /* debugging variable */
50 int printx = 0; /* print xxx after all is done */
51 int unique = 1; /* before store(), check with fetch() */
52 int usefresh = 0; /* use dbzfresh? */
53 long siz = 0; /* -p size */
54 char map = 'C'; /* -p map */
55 long tag = 0; /* -p tag mask */
56 int exact = 0; /* do not run dbzsize(siz) */
57 int dbzint = 1; /* use new interface? */
58 char fs = '\t'; /* field separator, default tab */
59 int unopen = 0; /* make base unopenable during dbminit? */
60 char *change = NULL; /* chdir here before dbmclose */
61 long tagsize = 0; /* if non-zero, file size for tag sizing */
62 int dowt = 0; /* do writethrough? */
63
64 #define DEFBUF 1024 /* default line-buffer size */
65 int buflen = DEFBUF; /* line length limit */
66 char lbuf[DEFBUF];
67 char *line = lbuf;
68 char cbuf[DEFBUF];
69 char *cmp = cbuf;
70
71 /* forwards */
72 char *str2dup(const char *s1, const char *s2);
73 void fail(const char *s1, const char *s2);
74 void dofile(char *name);
75 void runs(char *file);
76 void dosweep(const char *fn, const char *pn);
77 void verify(char *dir);
78 void mkfiles(void);
79 void crfile(char *name);
80 void doline(char *lp, long inoffset);
81 void process(FILE *in, const char *name);
82 datum dofetch(datum key);
83 int dostore(datum key, datum value);
84
85 #ifdef HAVERFCIZE
86 extern char *rfc822ize();
87 #else
88 #define rfc822ize(n) (n)
89 #endif
90
91 /*
92 - main - parse arguments and handle options
93 */
main(argc,argv)94 main(argc, argv)
95 int argc;
96 char *argv[];
97 {
98 int c;
99 int errflg = 0;
100 extern int optind;
101 extern char *optarg;
102 int doruns = 0;
103
104 progname = argv[0];
105
106 while ((c = getopt(argc, argv, "axcmvt:l:R0E:SqOiX:Yuf:p:eMUC:T:wd")) != EOF)
107 switch (c) {
108 case 'a': /* append to existing table */
109 if (op != 'b')
110 fail(badop, "");
111 op = 'a';
112 baseinput = 0;
113 break;
114 case 'x': /* extract from existing table */
115 if (op != 'b')
116 fail(badop, "");
117 op = 'x';
118 baseinput = 0;
119 break;
120 case 'c': /* check existing table */
121 if (op != 'b')
122 fail(badop, "");
123 op = 'c';
124 break;
125 case 'm': /* extract missing (complement of -x) */
126 if (op != 'b')
127 fail(badop, "");
128 op = 'm';
129 baseinput = 0;
130 break;
131 case 'v': /* verify that this is a dbz file */
132 if (op != 'b')
133 fail(badop, "");
134 op = 'v';
135 break;
136 case 't': /* set field separator */
137 if (strlen(optarg) > (size_t)1)
138 fail("only one field separator allowed", "");
139 fs = *optarg;
140 break;
141 case 'l': /* override line-length limit */
142 buflen = atoi(optarg) + 1;
143 if (buflen <= 2)
144 fail("bad -l value `%s'", optarg);
145 line = malloc(buflen);
146 cmp = malloc(buflen);
147 if (line == NULL || cmp == NULL)
148 fail("cannot allocate %s-byte buffers", optarg);
149 break;
150 case 'R': /* print run statistics */
151 doruns = 1;
152 break;
153 case '0': /* omit lines tagged (by fake -t) with 0 */
154 omitzero = 1;
155 break;
156 case 'E': /* report every n items */
157 every = atol(optarg);
158 break;
159 case 'S': /* dbzsync() on each -E report */
160 syncs = 1;
161 break;
162 case 'q': /* quick check or extract */
163 quick = 1;
164 break;
165 case 'O': /* sweep file checking all offsets */
166 sweep = 1;
167 break;
168 case 'i': /* don't use incore */
169 useincore = 0;
170 break;
171 case 'X': /* set xxx */
172 xxx = atoi(optarg);
173 break;
174 case 'Y': /* print xxx afterward */
175 printx = 1;
176 break;
177 case 'u': /* don't check uniqueness */
178 unique = 0;
179 break;
180 case 'f': /* init from existing table's parameters */
181 from = optarg;
182 break;
183 case 'p': /* parameters for dbzfresh */
184 if (sscanf(optarg, "%ld %1s %lx", &siz, &map, &tag) != 3) {
185 map = '?';
186 tag = 0;
187 if (sscanf(optarg, "%ld", &siz) != 1)
188 fail("bad -n value `%s'", optarg);
189 }
190 usefresh = 1;
191 break;
192 case 'e': /* -p size is exact, don't dbzsize() it */
193 exact = 1;
194 break;
195 case 'M': /* use old dbm interface + rfc822ize */
196 dbzint = 0;
197 break;
198 case 'U': /* make base unopenable during init */
199 unopen = 1;
200 break;
201 case 'C': /* change directories before dbmclose */
202 change = optarg;
203 break;
204 case 'd': /* Debugging. */
205 if (dbzdebug(1) < 0)
206 fail("dbz debugging not available", "");
207 break;
208 case 'T': /* file size for tag sizing */
209 tagsize = atol(optarg);
210 break;
211 case 'w': /* do writethrough */
212 dowt = 1;
213 break;
214 case '?':
215 default:
216 errflg++;
217 break;
218 }
219 if (errflg || optind >= argc || (optind+1 < argc && baseinput)) {
220 fprintf(stderr, "usage: %s ", progname);
221 fprintf(stderr, "[-{axcmv}] database [file] ...\n");
222 exit(2);
223 }
224
225 (void) dbzincore(useincore);
226 (void) dbzwritethrough(dowt);
227 base_name = argv[optind];
228 pagname = str2dup(base_name, ".pag");
229 dir_name = str2dup(base_name, ".dir");
230
231 if (op == 'v') {
232 verify(dir_name);
233 /* NOTREACHED */
234 }
235
236 mkfiles();
237 optind++;
238
239 if (baseinput) /* implies no further arguments */
240 process(base, base_name);
241 else if (optind >= argc)
242 process(stdin, "stdin");
243 else
244 for (; optind < argc; optind++)
245 dofile(argv[optind]);
246
247 if (change != NULL)
248 (void) chdir(change);
249 if (dbmclose() < 0)
250 fail("dbmclose failed", "");
251 if (doruns)
252 runs(pagname);
253 if (sweep)
254 dosweep(base_name, pagname);
255 if (printx)
256 printf("%ld\n", xxx);
257 exit(0);
258 }
259
260 /*
261 - verify - just check whether the .dir file looks right or not
262 */
263 void /* does not return */
verify(dir)264 verify(dir)
265 char *dir;
266 {
267 FILE *f;
268 char buf[4];
269 size_t n;
270
271 f = fopen(dir, "r");
272 if (f == NULL)
273 exit(1);
274 n = fread(buf, sizeof(buf), 1, f);
275 (void) fclose(f);
276
277 if (n != 1 || memcmp(buf, "dbz ", (size_t)4) != 0)
278 exit(1);
279
280 exit(0);
281 /* NOTREACHED */
282 }
283
284 /*
285 - dofile - open a file and invoke process()
286 */
287 void
dofile(name)288 dofile(name)
289 char *name;
290 {
291 register FILE *in;
292
293 if (STREQ(name, "-"))
294 process(stdin, "-");
295 else {
296 in = fopen(name, "r");
297 if (in == NULL)
298 fail("cannot open `%s'", name);
299 process(in, name);
300 (void) fclose(in);
301 }
302 }
303
304 /*
305 - mkfiles - create empty files and open them up
306 */
307 void
mkfiles()308 mkfiles()
309 {
310 if (op == 'b' && !dbzint) {
311 crfile(dir_name);
312 crfile(pagname);
313 }
314
315 base = fopen(base_name, (op == 'a') ? "a" : "r");
316 if (base == NULL)
317 fail("cannot open `%s'", base_name);
318 if (unopen)
319 (void) chmod(base_name, 0);
320 if (from != NULL) {
321 if (dbzagain(base_name, from) < 0)
322 fail("dbzagain(`%s'...) failed", base_name);
323 } else if (op == 'b' && dbzint) {
324 if (!exact)
325 siz = dbzsize(siz);
326 if (tagsize != 0)
327 tag = dbztagmask(tagsize);
328 if (dbzfresh(base_name, siz, (int)fs, map, tag) < 0)
329 fail("dbzfresh(`%s'...) failed", base_name);
330 } else if (dbminit(base_name) < 0)
331 fail("dbminit(`%s') failed", base_name);
332 if (unopen)
333 (void) chmod(base_name, 0600); /* hard to restore original */
334 }
335
336 /*
337 - crfile - create a file
338 */
339 void
crfile(name)340 crfile(name)
341 char *name;
342 {
343 register int f;
344
345 f = creat(name, 0666);
346 if (f < 0)
347 fail("cannot create `%s'", name);
348 (void) close(f);
349 }
350
351 /*
352 - process - process input file
353 */
354 void
process(in,name)355 process(in, name)
356 FILE *in;
357 const char *name;
358 {
359 register long place;
360
361 inname = name;
362 lineno = 0;
363
364 for (;;) {
365 place = ftell(in);
366 if (fgets(line, buflen, in) == NULL)
367 return;
368 lineno++;
369 if (every > 0 && lineno%every == 0) {
370 fprintf(stderr, "%ld\n", lineno);
371 if (dbzsync() < 0)
372 fail("dbzsync failed", "");
373 }
374 doline(line, place);
375 }
376 /* NOTREACHED */
377 }
378
379 /*
380 - doline - process input line
381 */
382 void
doline(lp,inoffset)383 doline(lp, inoffset)
384 char *lp;
385 long inoffset;
386 {
387 register char *p;
388 register char pc;
389 datum key, value;
390 long place = inoffset;
391 register int shouldfind;
392 register int llen;
393 char keytext[DBZMAXKEY+1];
394
395 p = NULL;
396 if (fs != '\0')
397 p = strchr(lp, fs);
398 if (p == NULL)
399 p = lp + strlen(lp);
400 if (p > lp && *(p-1) == '\n')
401 p--;
402 if (p - lp > DBZMAXKEY)
403 fail("key of `%s' too long", lp);
404 pc = *p;
405 *p = '\0';
406 (void) strcpy(keytext, lp);
407 *p = pc;
408 key.dptr = (dbzint) ? keytext : rfc822ize(keytext);
409 key.dsize = strlen(keytext)+1;
410
411 switch (op) {
412 case 'a':
413 place = ftell(base);
414 llen = strlen(lp);
415 if (fwrite(lp, 1, llen, base) != llen)
416 fail("write error in `%s'", base_name);
417 /* FALLTHROUGH */
418 case 'b':
419 if (omitzero && p != NULL && *(p+1) == '0')
420 return;
421 if (unique) {
422 value = dofetch(key);
423 if (value.dptr != NULL)
424 fail("`%s' already present", lp);
425 }
426 value.dptr = (char *)&place;
427 value.dsize = (int)sizeof(place);
428 if (dostore(key, value) < 0)
429 fail("store failed on `%s'", lp);
430 break;
431 case 'c':
432 value = dofetch(key);
433 shouldfind = (omitzero && p != NULL && *(p+1) == '0') ? 0 : 1;
434 if (!shouldfind && (value.dptr != NULL || value.dsize != 0))
435 fail("`%s' found, shouldn't be", lp);
436 if (shouldfind && (value.dptr == NULL ||
437 value.dsize != sizeof(place)))
438 fail("can't find `%s'", lp);
439 if (shouldfind && !quick) {
440 (void) memcpy((char *)&place, value.dptr, sizeof(place));
441 if (place != inoffset)
442 fail("offset mismatch on `%s'", lp);
443 if (fseek(base, place, SEEK_SET) != 0)
444 fail("fseek failed on `%s'", lp);
445 if (fgets(cmp, buflen, base) == NULL)
446 fail("can't read line for `%s'", lp);
447 if (!STREQ(lp, cmp))
448 fail("compare failed on `%s'", lp);
449 }
450 break;
451 case 'x':
452 value = dofetch(key);
453 if (value.dptr != NULL && !quick) {
454 (void) memcpy((char *)&place, value.dptr, sizeof(place));
455 if (fseek(base, place, SEEK_SET) != 0)
456 fail("fseek failed on `%s'", lp);
457 if (fgets(cmp, buflen, base) == NULL)
458 fail("can't read line for `%s'", lp);
459 fputs(cmp, stdout);
460 } else if (value.dptr != NULL)
461 fputs(lp, stdout);
462 break;
463 case 'm':
464 value = dofetch(key);
465 if (value.dptr == NULL) {
466 fputs(keytext, stdout);
467 putchar('\n');
468 }
469 break;
470 default:
471 fail("unknown operator -- can't happen", "");
472 break;
473 }
474 }
475
476 /*
477 - runs - print run statistics
478 */
479 void
runs(file)480 runs(file)
481 char *file;
482 {
483 register FILE *fd;
484 long it;
485 register long run;
486
487 fd = fopen(file, "r");
488 if (fd == NULL)
489 fail("cannot reopen `%s'", file);
490 run = 0;
491 while (fread((char *)&it, sizeof(it), 1, fd) == 1) {
492 if (it != 0)
493 run++;
494 else if (run > 0) {
495 printf("%ld\n", run);
496 run = 0;
497 }
498 }
499 (void) fclose(fd);
500 }
501
502 /*
503 - dosweep - sweep pag file checking for valid offsets
504 */
505 void
dosweep(fn,pn)506 dosweep(fn, pn)
507 const char *fn;
508 const char *pn;
509 {
510 register FILE *pf;
511 long it;
512 char nl;
513 register FILE *hf;
514
515 hf = fopen(fn, "r");
516 if (hf == NULL)
517 fail("cannot reopen `%s'", fn);
518 pf = fopen(pn, "r");
519 if (pf == NULL)
520 fail("cannot reopen `%s'", pn);
521 while (fread((char *)&it, sizeof(it), 1, pf) == 1) {
522 it = (it & 0x80000000) ? (it&~0xf8000000) : it;
523 if (it != 0 && it != 1) { /* 0 empty, 1 known okay */
524 it--; /* get rid of bias */
525 (void) fseek(hf, it-1, SEEK_SET);
526 nl = getc(hf);
527 if (nl != '\n')
528 fprintf(stderr, "offset 0%lo does not point to line\n",
529 (long)it);
530 }
531 }
532 (void) fclose(hf);
533 (void) fclose(pf);
534 }
535
536 /*
537 - fail - complain and die
538 */
539 void
fail(s1,s2)540 fail(s1, s2)
541 const char *s1;
542 const char *s2;
543 {
544 # define MAXS2 50
545 char s2buf[MAXS2+10];
546
547 fprintf(stderr, "%s: (file `%s', line %ld) ", progname, inname, lineno);
548 if (strlen(s2) <= (size_t)MAXS2)
549 fprintf(stderr, s1, s2);
550 else {
551 sprintf(s2buf, "%.*s...", MAXS2, s2);
552 fprintf(stderr, s1, s2buf);
553 }
554 fprintf(stderr, "\n");
555 exit(1);
556 }
557
558 /*
559 - str2dup - concatenate strings and malloc result
560 */
561 char *
str2dup(s1,s2)562 str2dup(s1, s2)
563 const char *s1;
564 const char *s2;
565 {
566 register char *p;
567
568 p = malloc((size_t)strlen(s1) + strlen(s2) + 1);
569 if (p == NULL)
570 fail("can't allocate space for strings", "");
571 (void) strcpy(p, s1);
572 (void) strcat(p, s2);
573 return(p);
574 }
575
576 /*
577 - dofetch - do a fetch or dbzfetch
578 */
579 datum
dofetch(key)580 dofetch(key)
581 datum key;
582 {
583 if (dbzint)
584 return(dbzfetch(key));
585 else
586 return(fetch(key));
587 }
588
589 /*
590 - dostore - do a store or dbzstore
591 */
592 int
dostore(key,value)593 dostore(key, value)
594 datum key;
595 datum value;
596 {
597 if (dbzint)
598 return(dbzstore(key, value));
599 else
600 return(store(key, value));
601 }
602