mkctm.c revision 124993
169793Sobrien/* $FreeBSD: head/usr.sbin/ctm/mkCTM/mkctm.c 124993 2004-01-26 04:08:43Z mckay $ */
269793Sobrien
315702Sphk/* Still missing:
415702Sphk *
515702Sphk * mkctm
615702Sphk *	-B regex	Bogus
715702Sphk *	-I regex	Ignore
815773Sphk *      -D int		Damage
915773Sphk *	-q		decrease verbosity
1015702Sphk *	-v		increase verbosity
1119813Sphk *      -l file		logfile
1215702Sphk *	name		cvs-cur
1315702Sphk *	prefix		src/secure
1415702Sphk *	dir1		"Soll"
1515702Sphk *	dir2		"Ist"
1615702Sphk *
17118583Simp * $FreeBSD: head/usr.sbin/ctm/mkCTM/mkctm.c 124993 2004-01-26 04:08:43Z mckay $
1815702Sphk */
1915702Sphk
2015702Sphk#include <sys/types.h>
2115702Sphk#include <sys/stat.h>
2215702Sphk#include <sys/mman.h>
2315702Sphk#include <sys/wait.h>
2415702Sphk#include <dirent.h>
2515702Sphk#include <regex.h>
2615702Sphk#include <stdio.h>
2715702Sphk#include <fcntl.h>
2815702Sphk#include <string.h>
2915702Sphk#include <stdlib.h>
3015702Sphk#include <unistd.h>
3115702Sphk#include <md5.h>
3215702Sphk#include <err.h>
3369793Sobrien#include <paths.h>
3415702Sphk#include <signal.h>
3515702Sphk
3615773Sphk#define DEFAULT_IGNORE	"/CVS$|/\\.#|00_TRANS\\.TBL$"
3719813Sphk#define DEFAULT_BOGUS	"\\.core$|\\.orig$|\\.rej$|\\.o$"
3815702Sphkregex_t reg_ignore,  reg_bogus;
3915702Sphkint	flag_ignore, flag_bogus;
4015702Sphk
4115773Sphkint	verbose;
4215773Sphkint	damage, damage_limit;
4315773Sphkint	change;
4415773Sphk
4519813SphkFILE	*logf;
4619813Sphk
4715702Sphku_long s1_ignored,	s2_ignored;
4815702Sphku_long s1_bogus,	s2_bogus;
4915702Sphku_long s1_wrong,	s2_wrong;
5015773Sphku_long s_new_dirs,	s_new_files,	s_new_bytes;
5115773Sphku_long s_del_dirs,	s_del_files,	                s_del_bytes;
5215773Sphku_long 			s_files_chg,	s_bytes_add,	s_bytes_del;
5315702Sphku_long s_same_dirs,	s_same_files,	s_same_bytes;
5415702Sphku_long 			s_edit_files,	s_edit_bytes,	s_edit_saves;
5515702Sphku_long 			s_sub_files,	s_sub_bytes;
5615702Sphk
5715702Sphkvoid
5815773SphkUsage(void)
5915773Sphk{
6029526Scharnier	fprintf(stderr,
6129526Scharnier		"usage: mkctm [-options] name number timestamp prefix dir1 dir2\n");
6229526Scharnier	fprintf(stderr, "options:\n");
6315773Sphk	fprintf(stderr, "\t\t-B bogus_regexp\n");
6415773Sphk	fprintf(stderr, "\t\t-D damage_limit\n");
6515773Sphk	fprintf(stderr, "\t\t-I ignore_regexp\n");
6615773Sphk	fprintf(stderr, "\t\t-q\n");
6715773Sphk	fprintf(stderr, "\t\t-v\n");
6815773Sphk}
6915773Sphk
7015773Sphkvoid
7115702Sphkprint_stat(FILE *fd, char *pre)
7215702Sphk{
7319813Sphk    fprintf(fd, "%sNames:\n", pre);
7419813Sphk    fprintf(fd, "%s  ignore:  %5lu ref   %5lu target\n",
7515702Sphk	    pre, s1_ignored, s2_ignored);
7619813Sphk    fprintf(fd, "%s  bogus:   %5lu ref   %5lu target\n",
7715702Sphk	    pre, s1_bogus, s2_bogus);
7819813Sphk    fprintf(fd, "%s  wrong:   %5lu ref   %5lu target\n",
7915702Sphk	    pre, s1_wrong, s2_wrong);
8019813Sphk    fprintf(fd, "%sDelta:\n", pre);
8119813Sphk    fprintf(fd, "%s  new:     %5lu dirs  %5lu files  %9lu plus\n",
8215702Sphk	    pre, s_new_dirs, s_new_files, s_new_bytes);
8319813Sphk    fprintf(fd, "%s  del:     %5lu dirs  %5lu files                   %9lu minus\n",
8415702Sphk	    pre, s_del_dirs, s_del_files, s_del_bytes);
8519813Sphk    fprintf(fd, "%s  chg:                 %5lu files  %9lu plus   %9lu minus\n",
8615702Sphk	    pre, s_files_chg, s_bytes_add, s_bytes_del);
8719813Sphk    fprintf(fd, "%s  same:    %5lu dirs  %5lu files  %9lu bytes\n",
8815702Sphk	    pre, s_same_dirs, s_same_files, s_same_bytes);
8919813Sphk    fprintf(fd, "%sMethod:\n", pre);
9019813Sphk    fprintf(fd, "%s  edit:                %5lu files  %9lu bytes  %9lu saved\n",
9115702Sphk	    pre, s_edit_files, s_edit_bytes, s_edit_saves);
9219813Sphk    fprintf(fd, "%s  sub:                 %5lu files  %9lu bytes\n",
9315702Sphk	    pre, s_sub_files, s_sub_bytes);
9419813Sphk
9515702Sphk}
9615702Sphk
9715702Sphkvoid
9815702Sphkstat_info(int foo)
9915702Sphk{
10019813Sphk	signal(SIGINFO, stat_info);
10119813Sphk	print_stat(stderr, "INFO: ");
10215702Sphk}
10315702Sphk
10415702Sphkvoid DoDir(const char *dir1, const char *dir2, const char *name);
10515702Sphk
10615702Sphkstatic struct stat st;
10715702Sphkstatic __inline struct stat *
10815702SphkStatFile(char *name)
10915702Sphk{
11019813Sphk	if (lstat(name, &st) < 0)
11129526Scharnier		err(1, "couldn't stat %s", name);
11215702Sphk	return &st;
11315702Sphk}
11415702Sphk
11515702Sphkint
11615702Sphkdirselect(struct dirent *de)
11715702Sphk{
11819813Sphk	if (!strcmp(de->d_name, "."))	return 0;
11919813Sphk	if (!strcmp(de->d_name, ".."))	return 0;
12015702Sphk	return 1;
12115702Sphk}
12215702Sphk
12315702Sphkvoid
12415702Sphkname_stat(const char *pfx, const char *dir, const char *name, struct dirent *de)
12515702Sphk{
12615702Sphk	char *buf = alloca(strlen(dir) + strlen(name) +
12715702Sphk		strlen(de->d_name) + 3);
12815702Sphk	struct stat *st;
12915702Sphk
13019813Sphk	strcpy(buf, dir);
13119813Sphk		strcat(buf, "/"); strcat(buf, name);
13219813Sphk		strcat(buf, "/"); strcat(buf, de->d_name);
13315702Sphk	st = StatFile(buf);
13419813Sphk	printf("%s %s%s %u %u %o",
13515702Sphk	    pfx, name, de->d_name,
13615702Sphk	    st->st_uid, st->st_gid, st->st_mode & ~S_IFMT);
13719813Sphk	fprintf(logf, "%s %s%s\n", pfx, name, de->d_name);
13815773Sphk	if (verbose > 1) {
13919813Sphk		fprintf(stderr, "%s %s%s\n", pfx, name, de->d_name);
14015773Sphk	}
14115702Sphk}
14215702Sphk
14315702Sphkvoid
14415702SphkEqu(const char *dir1, const char *dir2, const char *name, struct dirent *de)
14515702Sphk{
14615702Sphk	if (de->d_type == DT_DIR) {
14715702Sphk		char *p = alloca(strlen(name)+strlen(de->d_name)+2);
14815702Sphk
14919813Sphk		strcpy(p, name);  strcat(p, de->d_name); strcat(p, "/");
15019813Sphk		DoDir(dir1, dir2, p);
15115702Sphk		s_same_dirs++;
15215702Sphk	} else {
15315702Sphk		char *buf1 = alloca(strlen(dir1) + strlen(name) +
15415702Sphk			strlen(de->d_name) + 3);
15515702Sphk		char *buf2 = alloca(strlen(dir2) + strlen(name) +
15615702Sphk			strlen(de->d_name) + 3);
15719813Sphk		char *m1, md5_1[33], *m2, md5_2[33];
15819813Sphk		u_char *p1, *p2;
15919813Sphk		int fd1, fd2;
16019813Sphk		struct stat s1, s2;
16115702Sphk
16219813Sphk		strcpy(buf1, dir1);
16319813Sphk			strcat(buf1, "/"); strcat(buf1, name);
16419813Sphk			strcat(buf1, "/"); strcat(buf1, de->d_name);
16519813Sphk		fd1 = open(buf1, O_RDONLY);
16629526Scharnier		if(fd1 < 0) { err(3, "%s", buf1); }
16719813Sphk		fstat(fd1, &s1);
16819813Sphk		strcpy(buf2, dir2);
16919813Sphk			strcat(buf2, "/"); strcat(buf2, name);
17019813Sphk			strcat(buf2, "/"); strcat(buf2, de->d_name);
17119813Sphk		fd2 = open(buf2, O_RDONLY);
17229526Scharnier		if(fd2 < 0) { err(3, "%s", buf2); }
17319813Sphk		fstat(fd2, &s2);
17415773Sphk#if 0
17515773Sphk		/* XXX if we could just trust the size to change... */
17615702Sphk		if (s1.st_size == s2.st_size) {
17715702Sphk			s_same_files++;
17815702Sphk			s_same_bytes += s1.st_size;
17915702Sphk			close(fd1);
18015702Sphk			close(fd2);
18115702Sphk			goto finish;
18215702Sphk		}
18315702Sphk#endif
18419813Sphk		p1=mmap(0, s1.st_size, PROT_READ, MAP_PRIVATE, fd1, 0);
18529526Scharnier		if (p1 == (u_char *)MAP_FAILED) { err(3, "%s", buf1); }
18615702Sphk		close(fd1);
18715702Sphk
18819813Sphk		p2=mmap(0, s2.st_size, PROT_READ, MAP_PRIVATE, fd2, 0);
18929526Scharnier		if (p2 == (u_char *)MAP_FAILED) { err(3, "%s", buf2); }
19015702Sphk		close(fd2);
19115702Sphk
19215702Sphk		/* If identical, we're done. */
19319813Sphk		if((s1.st_size == s2.st_size) && !memcmp(p1, p2, s1.st_size)) {
19415702Sphk			s_same_files++;
19515702Sphk			s_same_bytes += s1.st_size;
19615702Sphk			goto finish;
19715702Sphk		}
19815702Sphk
19915702Sphk		s_files_chg++;
20015773Sphk		change++;
20115702Sphk		if (s1.st_size > s2.st_size)
20215702Sphk			s_bytes_del += (s1.st_size - s2.st_size);
20315702Sphk		else
20415702Sphk			s_bytes_add += (s2.st_size - s1.st_size);
20515702Sphk
20615702Sphk		m1 = MD5Data(p1, s1.st_size, md5_1);
20715702Sphk		m2 = MD5Data(p2, s2.st_size, md5_2);
20815702Sphk
20915702Sphk		/* Just a curiosity... */
21019813Sphk		if(!strcmp(m1, m2)) {
21115702Sphk			if (s1.st_size != s2.st_size)
21215702Sphk				fprintf(stderr,
21315702Sphk		"Notice: MD5 same for files of diffent size:\n\t%s\n\t%s\n",
21419813Sphk					buf1, buf2);
21515702Sphk			goto finish;
21615702Sphk		}
21715702Sphk
21815702Sphk		{
21915773Sphk			u_long l = s2.st_size + 2;
22015773Sphk			u_char *cmd = alloca(strlen(buf1)+strlen(buf2)+100);
22115773Sphk			u_char *ob = alloca(l), *p;
22215773Sphk			int j;
22315773Sphk			FILE *F;
22415773Sphk
22519912Sphk			if (s1.st_size && p1[s1.st_size-1] != '\n') {
22615773Sphk				if (verbose > 0)
22715773Sphk					fprintf(stderr,
22815773Sphk					    "last char != \\n in %s\n",
22915773Sphk					     buf1);
23015773Sphk				goto subst;
23115773Sphk			}
23215773Sphk
23319912Sphk			if (s2.st_size && p2[s2.st_size-1] != '\n') {
23415773Sphk				if (verbose > 0)
23515773Sphk					fprintf(stderr,
23615773Sphk					    "last char != \\n in %s\n",
23715773Sphk					     buf2);
23815773Sphk				goto subst;
23915773Sphk			}
24015773Sphk
24115773Sphk			for (p=p1; p<p1+s1.st_size; p++)
24215773Sphk				if (!*p) {
24315773Sphk					if (verbose > 0)
24415773Sphk						fprintf(stderr,
24515773Sphk						    "NULL char in %s\n",
24615773Sphk						     buf1);
24715773Sphk					goto subst;
24815773Sphk				}
24915773Sphk
25015773Sphk			for (p=p2; p<p2+s2.st_size; p++)
25115773Sphk				if (!*p) {
25215773Sphk					if (verbose > 0)
25315773Sphk						fprintf(stderr,
25415773Sphk						    "NULL char in %s\n",
25515773Sphk						     buf2);
25615773Sphk					goto subst;
25715773Sphk				}
25815773Sphk
25915773Sphk			strcpy(cmd, "diff -n ");
26015773Sphk			strcat(cmd, buf1);
26115773Sphk			strcat(cmd, " ");
26215773Sphk			strcat(cmd, buf2);
26319813Sphk			F = popen(cmd, "r");
26415773Sphk			for (j = 1, l = 0; l < s2.st_size; ) {
26515773Sphk				j = fread(ob+l, 1, s2.st_size - l, F);
26615773Sphk				if (j < 1)
26715773Sphk					break;
26815773Sphk				l += j;
26915773Sphk				continue;
27015773Sphk			}
27115773Sphk			if (j) {
27215773Sphk				l = 0;
27315773Sphk				while (EOF != fgetc(F))
27415773Sphk					continue;
27515773Sphk			}
27615773Sphk			pclose(F);
27715773Sphk
27815702Sphk			if (l && l < s2.st_size) {
27919813Sphk				name_stat("CTMFN", dir2, name, de);
28019813Sphk				printf(" %s %s %d\n", m1, m2, (unsigned)l);
28119813Sphk				fwrite(ob, 1, l, stdout);
28215773Sphk				putchar('\n');
28315702Sphk				s_edit_files++;
28415702Sphk				s_edit_bytes += l;
28515702Sphk				s_edit_saves += (s2.st_size - l);
28615702Sphk			} else {
28715773Sphk			subst:
28819813Sphk				name_stat("CTMFS", dir2, name, de);
28919813Sphk				printf(" %s %s %u\n", m1, m2, (unsigned)s2.st_size);
29019813Sphk				fwrite(p2, 1, s2.st_size, stdout);
29115773Sphk				putchar('\n');
29215702Sphk				s_sub_files++;
29315702Sphk				s_sub_bytes += s2.st_size;
29415702Sphk			}
29515702Sphk		}
29615702Sphk	    finish:
29719813Sphk		munmap(p1, s1.st_size);
29819813Sphk		munmap(p2, s2.st_size);
29915702Sphk	}
30015702Sphk}
30115702Sphk
30215702Sphkvoid
30315702SphkAdd(const char *dir1, const char *dir2, const char *name, struct dirent *de)
30415702Sphk{
30515773Sphk	change++;
30615702Sphk	if (de->d_type == DT_DIR) {
30715702Sphk		char *p = alloca(strlen(name)+strlen(de->d_name)+2);
30819813Sphk		strcpy(p, name);  strcat(p, de->d_name); strcat(p, "/");
30919813Sphk		name_stat("CTMDM", dir2, name, de);
31015702Sphk		putchar('\n');
31115702Sphk		s_new_dirs++;
31219813Sphk		DoDir(dir1, dir2, p);
31315702Sphk	} else if (de->d_type == DT_REG) {
31415702Sphk		char *buf2 = alloca(strlen(dir2) + strlen(name) +
31515702Sphk			strlen(de->d_name) + 3);
31615702Sphk		char *m2, md5_2[33];
31715702Sphk		u_char *p1;
31815702Sphk		struct stat st;
31915702Sphk		int fd1;
32015702Sphk
32119813Sphk		strcpy(buf2, dir2);
32219813Sphk			strcat(buf2, "/"); strcat(buf2, name);
32319813Sphk			strcat(buf2, "/"); strcat(buf2, de->d_name);
32419813Sphk		fd1 = open(buf2, O_RDONLY);
32529526Scharnier		if (fd1 < 0) { err(3, "%s", buf2); }
32619813Sphk		fstat(fd1, &st);
32719813Sphk		p1=mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd1, 0);
32829526Scharnier		if (p1 == (u_char *)MAP_FAILED) { err(3, "%s", buf2); }
32915702Sphk		close(fd1);
33015702Sphk		m2 = MD5Data(p1, st.st_size, md5_2);
33119813Sphk		name_stat("CTMFM", dir2, name, de);
33219813Sphk		printf(" %s %u\n", m2, (unsigned)st.st_size);
33319813Sphk		fwrite(p1, 1, st.st_size, stdout);
33415702Sphk		putchar('\n');
33519813Sphk		munmap(p1, st.st_size);
33615702Sphk		s_new_files++;
33715702Sphk		s_new_bytes += st.st_size;
33815702Sphk	}
33915702Sphk}
34015702Sphk
34115702Sphkvoid
34215702SphkDel (const char *dir1, const char *dir2, const char *name, struct dirent *de)
34315702Sphk{
34415773Sphk	damage++;
34515773Sphk	change++;
34615702Sphk	if (de->d_type == DT_DIR) {
34715702Sphk		char *p = alloca(strlen(name)+strlen(de->d_name)+2);
34819813Sphk		strcpy(p, name);  strcat(p, de->d_name); strcat(p, "/");
34919813Sphk		DoDir(dir1, dir2, p);
35019813Sphk		printf("CTMDR %s%s\n", name, de->d_name);
35119813Sphk		fprintf(logf, "CTMDR %s%s\n", name, de->d_name);
35219813Sphk		if (verbose > 1) {
35319813Sphk			fprintf(stderr, "CTMDR %s%s\n", name, de->d_name);
35419813Sphk		}
35515702Sphk		s_del_dirs++;
35615702Sphk	} else if (de->d_type == DT_REG) {
35715702Sphk		char *buf1 = alloca(strlen(dir1) + strlen(name) +
35815702Sphk			strlen(de->d_name) + 3);
35915702Sphk		char *m1, md5_1[33];
36019813Sphk		strcpy(buf1, dir1);
36119813Sphk			strcat(buf1, "/"); strcat(buf1, name);
36219813Sphk			strcat(buf1, "/"); strcat(buf1, de->d_name);
36315702Sphk		m1 = MD5File(buf1, md5_1);
36419813Sphk		printf("CTMFR %s%s %s\n", name, de->d_name, m1);
36519813Sphk		fprintf(logf, "CTMFR %s%s %s\n", name, de->d_name, m1);
36619813Sphk		if (verbose > 1) {
36719813Sphk			fprintf(stderr, "CTMFR %s%s\n", name, de->d_name);
36819813Sphk		}
36915702Sphk		s_del_files++;
37015702Sphk		s_del_bytes += StatFile(buf1)->st_size;
37115702Sphk	}
37215702Sphk}
37315702Sphk
37415702Sphkvoid
37515702SphkGetNext(int *i, int *n, struct dirent **nl, const char *dir, const char *name, u_long *ignored, u_long *bogus, u_long *wrong)
37615702Sphk{
37715702Sphk	char buf[BUFSIZ];
37819816Sphk	char buf1[BUFSIZ];
37915702Sphk
38015702Sphk	for (;;) {
38115702Sphk		for (;;) {
38215702Sphk			(*i)++;
38315702Sphk			if (*i >= *n)
38415702Sphk				return;
38519816Sphk			strcpy(buf1, name);
38619816Sphk			if (buf1[strlen(buf1)-1] != '/')
38719816Sphk				strcat(buf1, "/");
38819816Sphk			strcat(buf1, nl[*i]->d_name);
38915773Sphk			if (flag_ignore &&
39019816Sphk			    !regexec(&reg_ignore, buf1, 0, 0, 0)) {
39115702Sphk				(*ignored)++;
39219816Sphk				fprintf(logf, "Ignore %s\n", buf1);
39315773Sphk				if (verbose > 2) {
39419816Sphk					fprintf(stderr, "Ignore %s\n", buf1);
39515773Sphk				}
39615773Sphk			} else if (flag_bogus &&
39719816Sphk			    !regexec(&reg_bogus, buf1, 0, 0, 0)) {
39815702Sphk				(*bogus)++;
39919816Sphk				fprintf(logf, "Bogus %s\n", buf1);
40019816Sphk				fprintf(stderr, "Bogus %s\n", buf1);
40119813Sphk				damage++;
40215773Sphk			} else {
40319816Sphk				*buf = 0;
40419816Sphk				if (*dir != '/')
40519816Sphk					strcat(buf, "/");
40619816Sphk				strcat(buf, dir);
40719816Sphk				if (buf[strlen(buf)-1] != '/')
40819816Sphk					strcat(buf, "/");
40919816Sphk				strcat(buf, buf1);
41015702Sphk				break;
41115773Sphk			}
41215702Sphk			free(nl[*i]); nl[*i] = 0;
41315702Sphk		}
41415702Sphk		/* If the filesystem didn't tell us, find type */
41515702Sphk		if (nl[*i]->d_type == DT_UNKNOWN)
41615702Sphk			nl[*i]->d_type = IFTODT(StatFile(buf)->st_mode);
41715702Sphk		if (nl[*i]->d_type == DT_REG || nl[*i]->d_type == DT_DIR)
41815702Sphk			break;
41915702Sphk		(*wrong)++;
42015773Sphk		if (verbose > 0)
42119813Sphk			fprintf(stderr, "Wrong %s\n", buf);
42215702Sphk		free(nl[*i]); nl[*i] = 0;
42315702Sphk	}
42415702Sphk}
42515702Sphk
42615702Sphkvoid
42715702SphkDoDir(const char *dir1, const char *dir2, const char *name)
42815702Sphk{
42919813Sphk	int i1, i2, n1, n2, i;
43019813Sphk	struct dirent **nl1, **nl2;
43115702Sphk	char *buf1 = alloca(strlen(dir1) + strlen(name) + 4);
43215702Sphk	char *buf2 = alloca(strlen(dir2) + strlen(name) + 4);
43315702Sphk
43419813Sphk	strcpy(buf1, dir1); strcat(buf1, "/"); strcat(buf1, name);
43519813Sphk	strcpy(buf2, dir2); strcat(buf2, "/"); strcat(buf2, name);
43615702Sphk	n1 = scandir(buf1, &nl1, dirselect, alphasort);
43715702Sphk	n2 = scandir(buf2, &nl2, dirselect, alphasort);
43815702Sphk	i1 = i2 = -1;
43915702Sphk	GetNext(&i1, &n1, nl1, dir1, name, &s1_ignored, &s1_bogus, &s1_wrong);
44015702Sphk	GetNext(&i2, &n2, nl2, dir2, name, &s2_ignored, &s2_bogus, &s2_wrong);
44115702Sphk	for (;i1 < n1 || i2 < n2;) {
44215702Sphk
44315773Sphk		if (damage_limit && damage > damage_limit)
44415773Sphk			break;
44515773Sphk
44615702Sphk		/* Get next item from list 1 */
44715702Sphk		if (i1 < n1 && !nl1[i1])
44815702Sphk			GetNext(&i1, &n1, nl1, dir1, name,
44915702Sphk				&s1_ignored, &s1_bogus, &s1_wrong);
45015702Sphk
45115702Sphk		/* Get next item from list 2 */
45215702Sphk		if (i2 < n2 && !nl2[i2])
45315702Sphk			GetNext(&i2, &n2, nl2, dir2, name,
45415702Sphk				&s2_ignored, &s2_bogus, &s2_wrong);
45515702Sphk
45615702Sphk		if (i1 >= n1 && i2 >= n2) {
45715702Sphk			/* Done */
45815702Sphk			break;
45915702Sphk		} else if (i1 >= n1 && i2 < n2) {
46015702Sphk			/* end of list 1, add anything left on list 2 */
46119813Sphk			Add(dir1, dir2, name, nl2[i2]);
46215702Sphk			free(nl2[i2]); nl2[i2] = 0;
46315702Sphk		} else if (i1 < n1 && i2 >= n2) {
46415702Sphk			/* end of list 2, delete anything left on list 1 */
46519813Sphk			Del(dir1, dir2, name, nl1[i1]);
46615702Sphk			free(nl1[i1]); nl1[i1] = 0;
46715702Sphk		} else if (!(i = strcmp(nl1[i1]->d_name, nl2[i2]->d_name))) {
46815702Sphk			/* Identical names */
46915702Sphk			if (nl1[i1]->d_type == nl2[i2]->d_type) {
47015702Sphk				/* same type */
47119813Sphk				Equ(dir1, dir2, name, nl1[i1]);
47215702Sphk			} else {
47315702Sphk				/* different types */
47419813Sphk				Del(dir1, dir2, name, nl1[i1]);
47519813Sphk				Add(dir1, dir2, name, nl2[i2]);
47615702Sphk			}
47715702Sphk			free(nl1[i1]); nl1[i1] = 0;
47815702Sphk			free(nl2[i2]); nl2[i2] = 0;
47915702Sphk		} else if (i < 0) {
48015702Sphk			/* Something extra in list 1, delete it */
48119813Sphk			Del(dir1, dir2, name, nl1[i1]);
48215702Sphk			free(nl1[i1]); nl1[i1] = 0;
48315702Sphk		} else {
48415702Sphk			/* Something extra in list 2, add it */
48519813Sphk			Add(dir1, dir2, name, nl2[i2]);
48615702Sphk			free(nl2[i2]); nl2[i2] = 0;
48715702Sphk		}
48815702Sphk	}
48915702Sphk	if (n1 >= 0)
49015702Sphk		free(nl1);
49115702Sphk	if (n2 >= 0)
49215702Sphk		free(nl2);
49315702Sphk}
49415702Sphk
49515702Sphkint
49615702Sphkmain(int argc, char **argv)
49715702Sphk{
49815702Sphk	int i;
49915702Sphk
50015773Sphk	setbuf(stderr, NULL);
50115773Sphk
50219813Sphk#if 0
50319813Sphk	if (regcomp(&reg_bogus, DEFAULT_BOGUS, REG_EXTENDED | REG_NEWLINE))
50415702Sphk		/* XXX use regerror to explain it */
50529526Scharnier		errx(1, "default regular expression argument to -B is botched");
50615702Sphk	flag_bogus = 1;
50715702Sphk
50819813Sphk	if (regcomp(&reg_ignore, DEFAULT_IGNORE, REG_EXTENDED | REG_NEWLINE))
50915702Sphk		/* XXX use regerror to explain it */
51029526Scharnier		errx(1, "default regular expression argument to -I is botched");
51115702Sphk	flag_ignore = 1;
51219813Sphk#endif
51315702Sphk
51424428Simp	while ((i = getopt(argc, argv, "D:I:B:l:qv")) != -1)
51515702Sphk		switch (i) {
51615773Sphk		case 'D':
51719813Sphk			damage_limit = strtol(optarg, 0, 0);
51815773Sphk			if (damage_limit < 0)
51929526Scharnier				errx(1, "damage limit must be positive");
52015773Sphk			break;
52115702Sphk		case 'I':
52215702Sphk			if (flag_ignore)
52315702Sphk				regfree(&reg_ignore);
52415702Sphk			flag_ignore = 0;
52515702Sphk			if (!*optarg)
52615702Sphk				break;
52719813Sphk			if (regcomp(&reg_ignore, optarg,
52815702Sphk			    REG_EXTENDED | REG_NEWLINE))
52915702Sphk				/* XXX use regerror to explain it */
53029526Scharnier				errx(1, "regular expression argument to -I is botched");
53115702Sphk			flag_ignore = 1;
53215702Sphk			break;
53315702Sphk		case 'B':
53415702Sphk			if (flag_bogus)
53515702Sphk				regfree(&reg_bogus);
53615702Sphk			flag_bogus = 0;
53715702Sphk			if (!*optarg)
53815702Sphk				break;
53919813Sphk			if (regcomp(&reg_bogus, optarg,
54015702Sphk			    REG_EXTENDED | REG_NEWLINE))
54115702Sphk				/* XXX use regerror to explain it */
54229526Scharnier				errx(1, "regular expression argument to -B is botched");
54315702Sphk			flag_bogus = 1;
54415702Sphk			break;
54519813Sphk		case 'l':
54619813Sphk			logf = fopen(optarg, "w");
54719813Sphk			if (!logf)
54829526Scharnier				err(1, "%s", optarg);
549124993Smckay			setlinebuf(logf);
55019813Sphk			break;
55115773Sphk		case 'q':
55215773Sphk			verbose--;
55315773Sphk			break;
55415773Sphk		case 'v':
55515773Sphk			verbose++;
55615773Sphk			break;
55715702Sphk		case '?':
55815702Sphk		default:
55915773Sphk			Usage();
56015702Sphk			return (1);
56115702Sphk		}
56215702Sphk	argc -= optind;
56315702Sphk	argv += optind;
56415702Sphk
56519813Sphk	if (!logf)
56669793Sobrien		logf = fopen(_PATH_DEVNULL, "w");
56715702Sphk
56819813Sphk	setbuf(stdout, 0);
56919813Sphk
57015773Sphk	if (argc != 6) {
57115773Sphk		Usage();
57215773Sphk		return (1);
57315773Sphk	}
57415773Sphk
57519813Sphk	signal(SIGINFO, stat_info);
57615773Sphk
57719813Sphk	fprintf(stderr, "CTM_BEGIN 2.0 %s %s %s %s\n",
57819813Sphk		argv[0], argv[1], argv[2], argv[3]);
57919813Sphk	fprintf(logf, "CTM_BEGIN 2.0 %s %s %s %s\n",
58019813Sphk		argv[0], argv[1], argv[2], argv[3]);
58115773Sphk	printf("CTM_BEGIN 2.0 %s %s %s %s\n",
58215773Sphk		argv[0], argv[1], argv[2], argv[3]);
58319813Sphk	DoDir(argv[4], argv[5], "");
58415773Sphk	if (damage_limit && damage > damage_limit) {
58519813Sphk		print_stat(stderr, "DAMAGE: ");
58629526Scharnier		errx(1, "damage of %d would exceed %d files",
58719813Sphk			damage, damage_limit);
58819813Sphk	} else if (change < 2) {
58929526Scharnier		errx(4, "no changes");
59015773Sphk	} else {
59115773Sphk		printf("CTM_END ");
59219813Sphk		fprintf(logf, "CTM_END\n");
59319813Sphk		print_stat(stderr, "END: ");
59415773Sphk	}
59515702Sphk	exit(0);
59615702Sphk}
597