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