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