Deleted Added
full compact
mkctm.c (19912) mkctm.c (21786)
1/* Still missing:
2 *
3 * mkctm
4 * -B regex Bogus
5 * -I regex Ignore
6 * -D int Damage
7 * -q decrease verbosity
8 * -v increase verbosity
9 * -l file logfile
10 * name cvs-cur
11 * prefix src/secure
12 * dir1 "Soll"
13 * dir2 "Ist"
14 *
15 */
16
17#include <sys/types.h>
18#include <sys/stat.h>
19#include <sys/mman.h>
20#include <sys/wait.h>
21#include <dirent.h>
22#include <regex.h>
23#include <stdio.h>
24#include <fcntl.h>
25#include <string.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <md5.h>
29#include <err.h>
30#include <signal.h>
31
32#define DEFAULT_IGNORE "/CVS$|/\\.#|00_TRANS\\.TBL$"
33#define DEFAULT_BOGUS "\\.core$|\\.orig$|\\.rej$|\\.o$"
34regex_t reg_ignore, reg_bogus;
35int flag_ignore, flag_bogus;
36
37int verbose;
38int damage, damage_limit;
39int change;
40
41FILE *logf;
42
43u_long s1_ignored, s2_ignored;
44u_long s1_bogus, s2_bogus;
45u_long s1_wrong, s2_wrong;
46u_long s_new_dirs, s_new_files, s_new_bytes;
47u_long s_del_dirs, s_del_files, s_del_bytes;
48u_long s_files_chg, s_bytes_add, s_bytes_del;
49u_long s_same_dirs, s_same_files, s_same_bytes;
50u_long s_edit_files, s_edit_bytes, s_edit_saves;
51u_long s_sub_files, s_sub_bytes;
52
53void
54Usage(void)
55{
56 fprintf(stderr, "Usage:\n");
57 fprintf(stderr, "\tmkctm [-options] name number timestamp prefix");
58 fprintf(stderr, " dir1 dir2");
59 fprintf(stderr, "Options:\n");
60 fprintf(stderr, "\t\t-B bogus_regexp\n");
61 fprintf(stderr, "\t\t-D damage_limit\n");
62 fprintf(stderr, "\t\t-I ignore_regexp\n");
63 fprintf(stderr, "\t\t-q\n");
64 fprintf(stderr, "\t\t-v\n");
65}
66
67void
68print_stat(FILE *fd, char *pre)
69{
70 fprintf(fd, "%sNames:\n", pre);
71 fprintf(fd, "%s ignore: %5lu ref %5lu target\n",
72 pre, s1_ignored, s2_ignored);
73 fprintf(fd, "%s bogus: %5lu ref %5lu target\n",
74 pre, s1_bogus, s2_bogus);
75 fprintf(fd, "%s wrong: %5lu ref %5lu target\n",
76 pre, s1_wrong, s2_wrong);
77 fprintf(fd, "%sDelta:\n", pre);
78 fprintf(fd, "%s new: %5lu dirs %5lu files %9lu plus\n",
79 pre, s_new_dirs, s_new_files, s_new_bytes);
80 fprintf(fd, "%s del: %5lu dirs %5lu files %9lu minus\n",
81 pre, s_del_dirs, s_del_files, s_del_bytes);
82 fprintf(fd, "%s chg: %5lu files %9lu plus %9lu minus\n",
83 pre, s_files_chg, s_bytes_add, s_bytes_del);
84 fprintf(fd, "%s same: %5lu dirs %5lu files %9lu bytes\n",
85 pre, s_same_dirs, s_same_files, s_same_bytes);
86 fprintf(fd, "%sMethod:\n", pre);
87 fprintf(fd, "%s edit: %5lu files %9lu bytes %9lu saved\n",
88 pre, s_edit_files, s_edit_bytes, s_edit_saves);
89 fprintf(fd, "%s sub: %5lu files %9lu bytes\n",
90 pre, s_sub_files, s_sub_bytes);
91
92}
93
94void
95stat_info(int foo)
96{
97 signal(SIGINFO, stat_info);
98 print_stat(stderr, "INFO: ");
99}
100
101void DoDir(const char *dir1, const char *dir2, const char *name);
102
103static struct stat st;
104static __inline struct stat *
105StatFile(char *name)
106{
107 if (lstat(name, &st) < 0)
108 err(1, "Couldn't stat %s\n", name);
109 return &st;
110}
111
112int
113dirselect(struct dirent *de)
114{
115 if (!strcmp(de->d_name, ".")) return 0;
116 if (!strcmp(de->d_name, "..")) return 0;
117 return 1;
118}
119
120void
121name_stat(const char *pfx, const char *dir, const char *name, struct dirent *de)
122{
123 char *buf = alloca(strlen(dir) + strlen(name) +
124 strlen(de->d_name) + 3);
125 struct stat *st;
126
127 strcpy(buf, dir);
128 strcat(buf, "/"); strcat(buf, name);
129 strcat(buf, "/"); strcat(buf, de->d_name);
130 st = StatFile(buf);
131 printf("%s %s%s %u %u %o",
132 pfx, name, de->d_name,
133 st->st_uid, st->st_gid, st->st_mode & ~S_IFMT);
134 fprintf(logf, "%s %s%s\n", pfx, name, de->d_name);
135 if (verbose > 1) {
136 fprintf(stderr, "%s %s%s\n", pfx, name, de->d_name);
137 }
138}
139
140void
141Equ(const char *dir1, const char *dir2, const char *name, struct dirent *de)
142{
143 if (de->d_type == DT_DIR) {
144 char *p = alloca(strlen(name)+strlen(de->d_name)+2);
145
146 strcpy(p, name); strcat(p, de->d_name); strcat(p, "/");
147 DoDir(dir1, dir2, p);
148 s_same_dirs++;
149 } else {
150 char *buf1 = alloca(strlen(dir1) + strlen(name) +
151 strlen(de->d_name) + 3);
152 char *buf2 = alloca(strlen(dir2) + strlen(name) +
153 strlen(de->d_name) + 3);
154 char *m1, md5_1[33], *m2, md5_2[33];
155 u_char *p1, *p2;
156 int fd1, fd2;
157 struct stat s1, s2;
158
159 strcpy(buf1, dir1);
160 strcat(buf1, "/"); strcat(buf1, name);
161 strcat(buf1, "/"); strcat(buf1, de->d_name);
162 fd1 = open(buf1, O_RDONLY);
163 if(fd1 < 0) { perror(buf1); exit(3); }
164 fstat(fd1, &s1);
165 strcpy(buf2, dir2);
166 strcat(buf2, "/"); strcat(buf2, name);
167 strcat(buf2, "/"); strcat(buf2, de->d_name);
168 fd2 = open(buf2, O_RDONLY);
169 if(fd2 < 0) { perror(buf2); exit(3); }
170 fstat(fd2, &s2);
171#if 0
172 /* XXX if we could just trust the size to change... */
173 if (s1.st_size == s2.st_size) {
174 s_same_files++;
175 s_same_bytes += s1.st_size;
176 close(fd1);
177 close(fd2);
178 goto finish;
179 }
180#endif
181 p1=mmap(0, s1.st_size, PROT_READ, MAP_PRIVATE, fd1, 0);
1/* Still missing:
2 *
3 * mkctm
4 * -B regex Bogus
5 * -I regex Ignore
6 * -D int Damage
7 * -q decrease verbosity
8 * -v increase verbosity
9 * -l file logfile
10 * name cvs-cur
11 * prefix src/secure
12 * dir1 "Soll"
13 * dir2 "Ist"
14 *
15 */
16
17#include <sys/types.h>
18#include <sys/stat.h>
19#include <sys/mman.h>
20#include <sys/wait.h>
21#include <dirent.h>
22#include <regex.h>
23#include <stdio.h>
24#include <fcntl.h>
25#include <string.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <md5.h>
29#include <err.h>
30#include <signal.h>
31
32#define DEFAULT_IGNORE "/CVS$|/\\.#|00_TRANS\\.TBL$"
33#define DEFAULT_BOGUS "\\.core$|\\.orig$|\\.rej$|\\.o$"
34regex_t reg_ignore, reg_bogus;
35int flag_ignore, flag_bogus;
36
37int verbose;
38int damage, damage_limit;
39int change;
40
41FILE *logf;
42
43u_long s1_ignored, s2_ignored;
44u_long s1_bogus, s2_bogus;
45u_long s1_wrong, s2_wrong;
46u_long s_new_dirs, s_new_files, s_new_bytes;
47u_long s_del_dirs, s_del_files, s_del_bytes;
48u_long s_files_chg, s_bytes_add, s_bytes_del;
49u_long s_same_dirs, s_same_files, s_same_bytes;
50u_long s_edit_files, s_edit_bytes, s_edit_saves;
51u_long s_sub_files, s_sub_bytes;
52
53void
54Usage(void)
55{
56 fprintf(stderr, "Usage:\n");
57 fprintf(stderr, "\tmkctm [-options] name number timestamp prefix");
58 fprintf(stderr, " dir1 dir2");
59 fprintf(stderr, "Options:\n");
60 fprintf(stderr, "\t\t-B bogus_regexp\n");
61 fprintf(stderr, "\t\t-D damage_limit\n");
62 fprintf(stderr, "\t\t-I ignore_regexp\n");
63 fprintf(stderr, "\t\t-q\n");
64 fprintf(stderr, "\t\t-v\n");
65}
66
67void
68print_stat(FILE *fd, char *pre)
69{
70 fprintf(fd, "%sNames:\n", pre);
71 fprintf(fd, "%s ignore: %5lu ref %5lu target\n",
72 pre, s1_ignored, s2_ignored);
73 fprintf(fd, "%s bogus: %5lu ref %5lu target\n",
74 pre, s1_bogus, s2_bogus);
75 fprintf(fd, "%s wrong: %5lu ref %5lu target\n",
76 pre, s1_wrong, s2_wrong);
77 fprintf(fd, "%sDelta:\n", pre);
78 fprintf(fd, "%s new: %5lu dirs %5lu files %9lu plus\n",
79 pre, s_new_dirs, s_new_files, s_new_bytes);
80 fprintf(fd, "%s del: %5lu dirs %5lu files %9lu minus\n",
81 pre, s_del_dirs, s_del_files, s_del_bytes);
82 fprintf(fd, "%s chg: %5lu files %9lu plus %9lu minus\n",
83 pre, s_files_chg, s_bytes_add, s_bytes_del);
84 fprintf(fd, "%s same: %5lu dirs %5lu files %9lu bytes\n",
85 pre, s_same_dirs, s_same_files, s_same_bytes);
86 fprintf(fd, "%sMethod:\n", pre);
87 fprintf(fd, "%s edit: %5lu files %9lu bytes %9lu saved\n",
88 pre, s_edit_files, s_edit_bytes, s_edit_saves);
89 fprintf(fd, "%s sub: %5lu files %9lu bytes\n",
90 pre, s_sub_files, s_sub_bytes);
91
92}
93
94void
95stat_info(int foo)
96{
97 signal(SIGINFO, stat_info);
98 print_stat(stderr, "INFO: ");
99}
100
101void DoDir(const char *dir1, const char *dir2, const char *name);
102
103static struct stat st;
104static __inline struct stat *
105StatFile(char *name)
106{
107 if (lstat(name, &st) < 0)
108 err(1, "Couldn't stat %s\n", name);
109 return &st;
110}
111
112int
113dirselect(struct dirent *de)
114{
115 if (!strcmp(de->d_name, ".")) return 0;
116 if (!strcmp(de->d_name, "..")) return 0;
117 return 1;
118}
119
120void
121name_stat(const char *pfx, const char *dir, const char *name, struct dirent *de)
122{
123 char *buf = alloca(strlen(dir) + strlen(name) +
124 strlen(de->d_name) + 3);
125 struct stat *st;
126
127 strcpy(buf, dir);
128 strcat(buf, "/"); strcat(buf, name);
129 strcat(buf, "/"); strcat(buf, de->d_name);
130 st = StatFile(buf);
131 printf("%s %s%s %u %u %o",
132 pfx, name, de->d_name,
133 st->st_uid, st->st_gid, st->st_mode & ~S_IFMT);
134 fprintf(logf, "%s %s%s\n", pfx, name, de->d_name);
135 if (verbose > 1) {
136 fprintf(stderr, "%s %s%s\n", pfx, name, de->d_name);
137 }
138}
139
140void
141Equ(const char *dir1, const char *dir2, const char *name, struct dirent *de)
142{
143 if (de->d_type == DT_DIR) {
144 char *p = alloca(strlen(name)+strlen(de->d_name)+2);
145
146 strcpy(p, name); strcat(p, de->d_name); strcat(p, "/");
147 DoDir(dir1, dir2, p);
148 s_same_dirs++;
149 } else {
150 char *buf1 = alloca(strlen(dir1) + strlen(name) +
151 strlen(de->d_name) + 3);
152 char *buf2 = alloca(strlen(dir2) + strlen(name) +
153 strlen(de->d_name) + 3);
154 char *m1, md5_1[33], *m2, md5_2[33];
155 u_char *p1, *p2;
156 int fd1, fd2;
157 struct stat s1, s2;
158
159 strcpy(buf1, dir1);
160 strcat(buf1, "/"); strcat(buf1, name);
161 strcat(buf1, "/"); strcat(buf1, de->d_name);
162 fd1 = open(buf1, O_RDONLY);
163 if(fd1 < 0) { perror(buf1); exit(3); }
164 fstat(fd1, &s1);
165 strcpy(buf2, dir2);
166 strcat(buf2, "/"); strcat(buf2, name);
167 strcat(buf2, "/"); strcat(buf2, de->d_name);
168 fd2 = open(buf2, O_RDONLY);
169 if(fd2 < 0) { perror(buf2); exit(3); }
170 fstat(fd2, &s2);
171#if 0
172 /* XXX if we could just trust the size to change... */
173 if (s1.st_size == s2.st_size) {
174 s_same_files++;
175 s_same_bytes += s1.st_size;
176 close(fd1);
177 close(fd2);
178 goto finish;
179 }
180#endif
181 p1=mmap(0, s1.st_size, PROT_READ, MAP_PRIVATE, fd1, 0);
182 if ((int)p1 == -1) { perror(buf1); exit(3); }
182 if (p1 == (u_char *)MAP_FAILED) { perror(buf1); exit(3); }
183 close(fd1);
184
185 p2=mmap(0, s2.st_size, PROT_READ, MAP_PRIVATE, fd2, 0);
183 close(fd1);
184
185 p2=mmap(0, s2.st_size, PROT_READ, MAP_PRIVATE, fd2, 0);
186 if ((int)p2 == -1) { perror(buf2); exit(3); }
186 if (p2 == (u_char *)MAP_FAILED) { perror(buf2); exit(3); }
187 close(fd2);
188
189 /* If identical, we're done. */
190 if((s1.st_size == s2.st_size) && !memcmp(p1, p2, s1.st_size)) {
191 s_same_files++;
192 s_same_bytes += s1.st_size;
193 goto finish;
194 }
195
196 s_files_chg++;
197 change++;
198 if (s1.st_size > s2.st_size)
199 s_bytes_del += (s1.st_size - s2.st_size);
200 else
201 s_bytes_add += (s2.st_size - s1.st_size);
202
203 m1 = MD5Data(p1, s1.st_size, md5_1);
204 m2 = MD5Data(p2, s2.st_size, md5_2);
205
206 /* Just a curiosity... */
207 if(!strcmp(m1, m2)) {
208 if (s1.st_size != s2.st_size)
209 fprintf(stderr,
210 "Notice: MD5 same for files of diffent size:\n\t%s\n\t%s\n",
211 buf1, buf2);
212 goto finish;
213 }
214
215 {
216 u_long l = s2.st_size + 2;
217 u_char *cmd = alloca(strlen(buf1)+strlen(buf2)+100);
218 u_char *ob = alloca(l), *p;
219 int j;
220 FILE *F;
221
222 if (s1.st_size && p1[s1.st_size-1] != '\n') {
223 if (verbose > 0)
224 fprintf(stderr,
225 "last char != \\n in %s\n",
226 buf1);
227 goto subst;
228 }
229
230 if (s2.st_size && p2[s2.st_size-1] != '\n') {
231 if (verbose > 0)
232 fprintf(stderr,
233 "last char != \\n in %s\n",
234 buf2);
235 goto subst;
236 }
237
238 for (p=p1; p<p1+s1.st_size; p++)
239 if (!*p) {
240 if (verbose > 0)
241 fprintf(stderr,
242 "NULL char in %s\n",
243 buf1);
244 goto subst;
245 }
246
247 for (p=p2; p<p2+s2.st_size; p++)
248 if (!*p) {
249 if (verbose > 0)
250 fprintf(stderr,
251 "NULL char in %s\n",
252 buf2);
253 goto subst;
254 }
255
256 strcpy(cmd, "diff -n ");
257 strcat(cmd, buf1);
258 strcat(cmd, " ");
259 strcat(cmd, buf2);
260 F = popen(cmd, "r");
261 for (j = 1, l = 0; l < s2.st_size; ) {
262 j = fread(ob+l, 1, s2.st_size - l, F);
263 if (j < 1)
264 break;
265 l += j;
266 continue;
267 }
268 if (j) {
269 l = 0;
270 while (EOF != fgetc(F))
271 continue;
272 }
273 pclose(F);
274
275 if (l && l < s2.st_size) {
276 name_stat("CTMFN", dir2, name, de);
277 printf(" %s %s %d\n", m1, m2, (unsigned)l);
278 fwrite(ob, 1, l, stdout);
279 putchar('\n');
280 s_edit_files++;
281 s_edit_bytes += l;
282 s_edit_saves += (s2.st_size - l);
283 } else {
284 subst:
285 name_stat("CTMFS", dir2, name, de);
286 printf(" %s %s %u\n", m1, m2, (unsigned)s2.st_size);
287 fwrite(p2, 1, s2.st_size, stdout);
288 putchar('\n');
289 s_sub_files++;
290 s_sub_bytes += s2.st_size;
291 }
292 }
293 finish:
294 munmap(p1, s1.st_size);
295 munmap(p2, s2.st_size);
296 }
297}
298
299void
300Add(const char *dir1, const char *dir2, const char *name, struct dirent *de)
301{
302 change++;
303 if (de->d_type == DT_DIR) {
304 char *p = alloca(strlen(name)+strlen(de->d_name)+2);
305 strcpy(p, name); strcat(p, de->d_name); strcat(p, "/");
306 name_stat("CTMDM", dir2, name, de);
307 putchar('\n');
308 s_new_dirs++;
309 DoDir(dir1, dir2, p);
310 } else if (de->d_type == DT_REG) {
311 char *buf2 = alloca(strlen(dir2) + strlen(name) +
312 strlen(de->d_name) + 3);
313 char *m2, md5_2[33];
314 u_char *p1;
315 struct stat st;
316 int fd1;
317
318 strcpy(buf2, dir2);
319 strcat(buf2, "/"); strcat(buf2, name);
320 strcat(buf2, "/"); strcat(buf2, de->d_name);
321 fd1 = open(buf2, O_RDONLY);
322 if (fd1 < 0) {perror(buf2); exit (3); }
323 fstat(fd1, &st);
324 p1=mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd1, 0);
187 close(fd2);
188
189 /* If identical, we're done. */
190 if((s1.st_size == s2.st_size) && !memcmp(p1, p2, s1.st_size)) {
191 s_same_files++;
192 s_same_bytes += s1.st_size;
193 goto finish;
194 }
195
196 s_files_chg++;
197 change++;
198 if (s1.st_size > s2.st_size)
199 s_bytes_del += (s1.st_size - s2.st_size);
200 else
201 s_bytes_add += (s2.st_size - s1.st_size);
202
203 m1 = MD5Data(p1, s1.st_size, md5_1);
204 m2 = MD5Data(p2, s2.st_size, md5_2);
205
206 /* Just a curiosity... */
207 if(!strcmp(m1, m2)) {
208 if (s1.st_size != s2.st_size)
209 fprintf(stderr,
210 "Notice: MD5 same for files of diffent size:\n\t%s\n\t%s\n",
211 buf1, buf2);
212 goto finish;
213 }
214
215 {
216 u_long l = s2.st_size + 2;
217 u_char *cmd = alloca(strlen(buf1)+strlen(buf2)+100);
218 u_char *ob = alloca(l), *p;
219 int j;
220 FILE *F;
221
222 if (s1.st_size && p1[s1.st_size-1] != '\n') {
223 if (verbose > 0)
224 fprintf(stderr,
225 "last char != \\n in %s\n",
226 buf1);
227 goto subst;
228 }
229
230 if (s2.st_size && p2[s2.st_size-1] != '\n') {
231 if (verbose > 0)
232 fprintf(stderr,
233 "last char != \\n in %s\n",
234 buf2);
235 goto subst;
236 }
237
238 for (p=p1; p<p1+s1.st_size; p++)
239 if (!*p) {
240 if (verbose > 0)
241 fprintf(stderr,
242 "NULL char in %s\n",
243 buf1);
244 goto subst;
245 }
246
247 for (p=p2; p<p2+s2.st_size; p++)
248 if (!*p) {
249 if (verbose > 0)
250 fprintf(stderr,
251 "NULL char in %s\n",
252 buf2);
253 goto subst;
254 }
255
256 strcpy(cmd, "diff -n ");
257 strcat(cmd, buf1);
258 strcat(cmd, " ");
259 strcat(cmd, buf2);
260 F = popen(cmd, "r");
261 for (j = 1, l = 0; l < s2.st_size; ) {
262 j = fread(ob+l, 1, s2.st_size - l, F);
263 if (j < 1)
264 break;
265 l += j;
266 continue;
267 }
268 if (j) {
269 l = 0;
270 while (EOF != fgetc(F))
271 continue;
272 }
273 pclose(F);
274
275 if (l && l < s2.st_size) {
276 name_stat("CTMFN", dir2, name, de);
277 printf(" %s %s %d\n", m1, m2, (unsigned)l);
278 fwrite(ob, 1, l, stdout);
279 putchar('\n');
280 s_edit_files++;
281 s_edit_bytes += l;
282 s_edit_saves += (s2.st_size - l);
283 } else {
284 subst:
285 name_stat("CTMFS", dir2, name, de);
286 printf(" %s %s %u\n", m1, m2, (unsigned)s2.st_size);
287 fwrite(p2, 1, s2.st_size, stdout);
288 putchar('\n');
289 s_sub_files++;
290 s_sub_bytes += s2.st_size;
291 }
292 }
293 finish:
294 munmap(p1, s1.st_size);
295 munmap(p2, s2.st_size);
296 }
297}
298
299void
300Add(const char *dir1, const char *dir2, const char *name, struct dirent *de)
301{
302 change++;
303 if (de->d_type == DT_DIR) {
304 char *p = alloca(strlen(name)+strlen(de->d_name)+2);
305 strcpy(p, name); strcat(p, de->d_name); strcat(p, "/");
306 name_stat("CTMDM", dir2, name, de);
307 putchar('\n');
308 s_new_dirs++;
309 DoDir(dir1, dir2, p);
310 } else if (de->d_type == DT_REG) {
311 char *buf2 = alloca(strlen(dir2) + strlen(name) +
312 strlen(de->d_name) + 3);
313 char *m2, md5_2[33];
314 u_char *p1;
315 struct stat st;
316 int fd1;
317
318 strcpy(buf2, dir2);
319 strcat(buf2, "/"); strcat(buf2, name);
320 strcat(buf2, "/"); strcat(buf2, de->d_name);
321 fd1 = open(buf2, O_RDONLY);
322 if (fd1 < 0) {perror(buf2); exit (3); }
323 fstat(fd1, &st);
324 p1=mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd1, 0);
325 if ((int)p1 == -1) { perror(buf2); exit(3); }
325 if (p1 == (u_char *)MAP_FAILED) { perror(buf2); exit(3); }
326 close(fd1);
327 m2 = MD5Data(p1, st.st_size, md5_2);
328 name_stat("CTMFM", dir2, name, de);
329 printf(" %s %u\n", m2, (unsigned)st.st_size);
330 fwrite(p1, 1, st.st_size, stdout);
331 putchar('\n');
332 munmap(p1, st.st_size);
333 s_new_files++;
334 s_new_bytes += st.st_size;
335 }
336}
337
338void
339Del (const char *dir1, const char *dir2, const char *name, struct dirent *de)
340{
341 damage++;
342 change++;
343 if (de->d_type == DT_DIR) {
344 char *p = alloca(strlen(name)+strlen(de->d_name)+2);
345 strcpy(p, name); strcat(p, de->d_name); strcat(p, "/");
346 DoDir(dir1, dir2, p);
347 printf("CTMDR %s%s\n", name, de->d_name);
348 fprintf(logf, "CTMDR %s%s\n", name, de->d_name);
349 if (verbose > 1) {
350 fprintf(stderr, "CTMDR %s%s\n", name, de->d_name);
351 }
352 s_del_dirs++;
353 } else if (de->d_type == DT_REG) {
354 char *buf1 = alloca(strlen(dir1) + strlen(name) +
355 strlen(de->d_name) + 3);
356 char *m1, md5_1[33];
357 strcpy(buf1, dir1);
358 strcat(buf1, "/"); strcat(buf1, name);
359 strcat(buf1, "/"); strcat(buf1, de->d_name);
360 m1 = MD5File(buf1, md5_1);
361 printf("CTMFR %s%s %s\n", name, de->d_name, m1);
362 fprintf(logf, "CTMFR %s%s %s\n", name, de->d_name, m1);
363 if (verbose > 1) {
364 fprintf(stderr, "CTMFR %s%s\n", name, de->d_name);
365 }
366 s_del_files++;
367 s_del_bytes += StatFile(buf1)->st_size;
368 }
369}
370
371void
372GetNext(int *i, int *n, struct dirent **nl, const char *dir, const char *name, u_long *ignored, u_long *bogus, u_long *wrong)
373{
374 char buf[BUFSIZ];
375 char buf1[BUFSIZ];
376
377 for (;;) {
378 for (;;) {
379 (*i)++;
380 if (*i >= *n)
381 return;
382 strcpy(buf1, name);
383 if (buf1[strlen(buf1)-1] != '/')
384 strcat(buf1, "/");
385 strcat(buf1, nl[*i]->d_name);
386 if (flag_ignore &&
387 !regexec(&reg_ignore, buf1, 0, 0, 0)) {
388 (*ignored)++;
389 fprintf(logf, "Ignore %s\n", buf1);
390 if (verbose > 2) {
391 fprintf(stderr, "Ignore %s\n", buf1);
392 }
393 } else if (flag_bogus &&
394 !regexec(&reg_bogus, buf1, 0, 0, 0)) {
395 (*bogus)++;
396 fprintf(logf, "Bogus %s\n", buf1);
397 fprintf(stderr, "Bogus %s\n", buf1);
398 damage++;
399 } else {
400 *buf = 0;
401 if (*dir != '/')
402 strcat(buf, "/");
403 strcat(buf, dir);
404 if (buf[strlen(buf)-1] != '/')
405 strcat(buf, "/");
406 strcat(buf, buf1);
407 break;
408 }
409 free(nl[*i]); nl[*i] = 0;
410 }
411 /* If the filesystem didn't tell us, find type */
412 if (nl[*i]->d_type == DT_UNKNOWN)
413 nl[*i]->d_type = IFTODT(StatFile(buf)->st_mode);
414 if (nl[*i]->d_type == DT_REG || nl[*i]->d_type == DT_DIR)
415 break;
416 (*wrong)++;
417 if (verbose > 0)
418 fprintf(stderr, "Wrong %s\n", buf);
419 free(nl[*i]); nl[*i] = 0;
420 }
421}
422
423void
424DoDir(const char *dir1, const char *dir2, const char *name)
425{
426 int i1, i2, n1, n2, i;
427 struct dirent **nl1, **nl2;
428 char *buf1 = alloca(strlen(dir1) + strlen(name) + 4);
429 char *buf2 = alloca(strlen(dir2) + strlen(name) + 4);
430
431 strcpy(buf1, dir1); strcat(buf1, "/"); strcat(buf1, name);
432 strcpy(buf2, dir2); strcat(buf2, "/"); strcat(buf2, name);
433 n1 = scandir(buf1, &nl1, dirselect, alphasort);
434 n2 = scandir(buf2, &nl2, dirselect, alphasort);
435 i1 = i2 = -1;
436 GetNext(&i1, &n1, nl1, dir1, name, &s1_ignored, &s1_bogus, &s1_wrong);
437 GetNext(&i2, &n2, nl2, dir2, name, &s2_ignored, &s2_bogus, &s2_wrong);
438 for (;i1 < n1 || i2 < n2;) {
439
440 if (damage_limit && damage > damage_limit)
441 break;
442
443 /* Get next item from list 1 */
444 if (i1 < n1 && !nl1[i1])
445 GetNext(&i1, &n1, nl1, dir1, name,
446 &s1_ignored, &s1_bogus, &s1_wrong);
447
448 /* Get next item from list 2 */
449 if (i2 < n2 && !nl2[i2])
450 GetNext(&i2, &n2, nl2, dir2, name,
451 &s2_ignored, &s2_bogus, &s2_wrong);
452
453 if (i1 >= n1 && i2 >= n2) {
454 /* Done */
455 break;
456 } else if (i1 >= n1 && i2 < n2) {
457 /* end of list 1, add anything left on list 2 */
458 Add(dir1, dir2, name, nl2[i2]);
459 free(nl2[i2]); nl2[i2] = 0;
460 } else if (i1 < n1 && i2 >= n2) {
461 /* end of list 2, delete anything left on list 1 */
462 Del(dir1, dir2, name, nl1[i1]);
463 free(nl1[i1]); nl1[i1] = 0;
464 } else if (!(i = strcmp(nl1[i1]->d_name, nl2[i2]->d_name))) {
465 /* Identical names */
466 if (nl1[i1]->d_type == nl2[i2]->d_type) {
467 /* same type */
468 Equ(dir1, dir2, name, nl1[i1]);
469 } else {
470 /* different types */
471 Del(dir1, dir2, name, nl1[i1]);
472 Add(dir1, dir2, name, nl2[i2]);
473 }
474 free(nl1[i1]); nl1[i1] = 0;
475 free(nl2[i2]); nl2[i2] = 0;
476 } else if (i < 0) {
477 /* Something extra in list 1, delete it */
478 Del(dir1, dir2, name, nl1[i1]);
479 free(nl1[i1]); nl1[i1] = 0;
480 } else {
481 /* Something extra in list 2, add it */
482 Add(dir1, dir2, name, nl2[i2]);
483 free(nl2[i2]); nl2[i2] = 0;
484 }
485 }
486 if (n1 >= 0)
487 free(nl1);
488 if (n2 >= 0)
489 free(nl2);
490}
491
492int
493main(int argc, char **argv)
494{
495 int i;
496 extern char *optarg;
497 extern int optind;
498
499 setbuf(stderr, NULL);
500
501#if 0
502 if (regcomp(&reg_bogus, DEFAULT_BOGUS, REG_EXTENDED | REG_NEWLINE))
503 /* XXX use regerror to explain it */
504 errx(1, "Default regular expression argument to -B is botched");
505 flag_bogus = 1;
506
507 if (regcomp(&reg_ignore, DEFAULT_IGNORE, REG_EXTENDED | REG_NEWLINE))
508 /* XXX use regerror to explain it */
509 errx(1, "Default regular expression argument to -I is botched");
510 flag_ignore = 1;
511#endif
512
513 while ((i = getopt(argc, argv, "D:I:B:l:qv")) != EOF)
514 switch (i) {
515 case 'D':
516 damage_limit = strtol(optarg, 0, 0);
517 if (damage_limit < 0)
518 errx(1, "Damage limit must be positive");
519 break;
520 case 'I':
521 if (flag_ignore)
522 regfree(&reg_ignore);
523 flag_ignore = 0;
524 if (!*optarg)
525 break;
526 if (regcomp(&reg_ignore, optarg,
527 REG_EXTENDED | REG_NEWLINE))
528 /* XXX use regerror to explain it */
529 errx(1, "Regular expression argument to -I is botched");
530 flag_ignore = 1;
531 break;
532 case 'B':
533 if (flag_bogus)
534 regfree(&reg_bogus);
535 flag_bogus = 0;
536 if (!*optarg)
537 break;
538 if (regcomp(&reg_bogus, optarg,
539 REG_EXTENDED | REG_NEWLINE))
540 /* XXX use regerror to explain it */
541 errx(1, "Regular expression argument to -B is botched");
542 flag_bogus = 1;
543 break;
544 case 'l':
545 logf = fopen(optarg, "w");
546 if (!logf)
547 err(1, optarg);
548 break;
549 case 'q':
550 verbose--;
551 break;
552 case 'v':
553 verbose++;
554 break;
555 case '?':
556 default:
557 Usage();
558 return (1);
559 }
560 argc -= optind;
561 argv += optind;
562
563 if (!logf)
564 logf = fopen("/dev/null", "w");
565
566 setbuf(stdout, 0);
567
568 if (argc != 6) {
569 Usage();
570 return (1);
571 }
572
573 signal(SIGINFO, stat_info);
574
575 fprintf(stderr, "CTM_BEGIN 2.0 %s %s %s %s\n",
576 argv[0], argv[1], argv[2], argv[3]);
577 fprintf(logf, "CTM_BEGIN 2.0 %s %s %s %s\n",
578 argv[0], argv[1], argv[2], argv[3]);
579 printf("CTM_BEGIN 2.0 %s %s %s %s\n",
580 argv[0], argv[1], argv[2], argv[3]);
581 DoDir(argv[4], argv[5], "");
582 if (damage_limit && damage > damage_limit) {
583 print_stat(stderr, "DAMAGE: ");
584 errx(1, "Damage of %d would exceed %d files",
585 damage, damage_limit);
586 } else if (change < 2) {
587 errx(4, "No changes");
588 } else {
589 printf("CTM_END ");
590 fprintf(logf, "CTM_END\n");
591 print_stat(stderr, "END: ");
592 }
593 exit(0);
594}
326 close(fd1);
327 m2 = MD5Data(p1, st.st_size, md5_2);
328 name_stat("CTMFM", dir2, name, de);
329 printf(" %s %u\n", m2, (unsigned)st.st_size);
330 fwrite(p1, 1, st.st_size, stdout);
331 putchar('\n');
332 munmap(p1, st.st_size);
333 s_new_files++;
334 s_new_bytes += st.st_size;
335 }
336}
337
338void
339Del (const char *dir1, const char *dir2, const char *name, struct dirent *de)
340{
341 damage++;
342 change++;
343 if (de->d_type == DT_DIR) {
344 char *p = alloca(strlen(name)+strlen(de->d_name)+2);
345 strcpy(p, name); strcat(p, de->d_name); strcat(p, "/");
346 DoDir(dir1, dir2, p);
347 printf("CTMDR %s%s\n", name, de->d_name);
348 fprintf(logf, "CTMDR %s%s\n", name, de->d_name);
349 if (verbose > 1) {
350 fprintf(stderr, "CTMDR %s%s\n", name, de->d_name);
351 }
352 s_del_dirs++;
353 } else if (de->d_type == DT_REG) {
354 char *buf1 = alloca(strlen(dir1) + strlen(name) +
355 strlen(de->d_name) + 3);
356 char *m1, md5_1[33];
357 strcpy(buf1, dir1);
358 strcat(buf1, "/"); strcat(buf1, name);
359 strcat(buf1, "/"); strcat(buf1, de->d_name);
360 m1 = MD5File(buf1, md5_1);
361 printf("CTMFR %s%s %s\n", name, de->d_name, m1);
362 fprintf(logf, "CTMFR %s%s %s\n", name, de->d_name, m1);
363 if (verbose > 1) {
364 fprintf(stderr, "CTMFR %s%s\n", name, de->d_name);
365 }
366 s_del_files++;
367 s_del_bytes += StatFile(buf1)->st_size;
368 }
369}
370
371void
372GetNext(int *i, int *n, struct dirent **nl, const char *dir, const char *name, u_long *ignored, u_long *bogus, u_long *wrong)
373{
374 char buf[BUFSIZ];
375 char buf1[BUFSIZ];
376
377 for (;;) {
378 for (;;) {
379 (*i)++;
380 if (*i >= *n)
381 return;
382 strcpy(buf1, name);
383 if (buf1[strlen(buf1)-1] != '/')
384 strcat(buf1, "/");
385 strcat(buf1, nl[*i]->d_name);
386 if (flag_ignore &&
387 !regexec(&reg_ignore, buf1, 0, 0, 0)) {
388 (*ignored)++;
389 fprintf(logf, "Ignore %s\n", buf1);
390 if (verbose > 2) {
391 fprintf(stderr, "Ignore %s\n", buf1);
392 }
393 } else if (flag_bogus &&
394 !regexec(&reg_bogus, buf1, 0, 0, 0)) {
395 (*bogus)++;
396 fprintf(logf, "Bogus %s\n", buf1);
397 fprintf(stderr, "Bogus %s\n", buf1);
398 damage++;
399 } else {
400 *buf = 0;
401 if (*dir != '/')
402 strcat(buf, "/");
403 strcat(buf, dir);
404 if (buf[strlen(buf)-1] != '/')
405 strcat(buf, "/");
406 strcat(buf, buf1);
407 break;
408 }
409 free(nl[*i]); nl[*i] = 0;
410 }
411 /* If the filesystem didn't tell us, find type */
412 if (nl[*i]->d_type == DT_UNKNOWN)
413 nl[*i]->d_type = IFTODT(StatFile(buf)->st_mode);
414 if (nl[*i]->d_type == DT_REG || nl[*i]->d_type == DT_DIR)
415 break;
416 (*wrong)++;
417 if (verbose > 0)
418 fprintf(stderr, "Wrong %s\n", buf);
419 free(nl[*i]); nl[*i] = 0;
420 }
421}
422
423void
424DoDir(const char *dir1, const char *dir2, const char *name)
425{
426 int i1, i2, n1, n2, i;
427 struct dirent **nl1, **nl2;
428 char *buf1 = alloca(strlen(dir1) + strlen(name) + 4);
429 char *buf2 = alloca(strlen(dir2) + strlen(name) + 4);
430
431 strcpy(buf1, dir1); strcat(buf1, "/"); strcat(buf1, name);
432 strcpy(buf2, dir2); strcat(buf2, "/"); strcat(buf2, name);
433 n1 = scandir(buf1, &nl1, dirselect, alphasort);
434 n2 = scandir(buf2, &nl2, dirselect, alphasort);
435 i1 = i2 = -1;
436 GetNext(&i1, &n1, nl1, dir1, name, &s1_ignored, &s1_bogus, &s1_wrong);
437 GetNext(&i2, &n2, nl2, dir2, name, &s2_ignored, &s2_bogus, &s2_wrong);
438 for (;i1 < n1 || i2 < n2;) {
439
440 if (damage_limit && damage > damage_limit)
441 break;
442
443 /* Get next item from list 1 */
444 if (i1 < n1 && !nl1[i1])
445 GetNext(&i1, &n1, nl1, dir1, name,
446 &s1_ignored, &s1_bogus, &s1_wrong);
447
448 /* Get next item from list 2 */
449 if (i2 < n2 && !nl2[i2])
450 GetNext(&i2, &n2, nl2, dir2, name,
451 &s2_ignored, &s2_bogus, &s2_wrong);
452
453 if (i1 >= n1 && i2 >= n2) {
454 /* Done */
455 break;
456 } else if (i1 >= n1 && i2 < n2) {
457 /* end of list 1, add anything left on list 2 */
458 Add(dir1, dir2, name, nl2[i2]);
459 free(nl2[i2]); nl2[i2] = 0;
460 } else if (i1 < n1 && i2 >= n2) {
461 /* end of list 2, delete anything left on list 1 */
462 Del(dir1, dir2, name, nl1[i1]);
463 free(nl1[i1]); nl1[i1] = 0;
464 } else if (!(i = strcmp(nl1[i1]->d_name, nl2[i2]->d_name))) {
465 /* Identical names */
466 if (nl1[i1]->d_type == nl2[i2]->d_type) {
467 /* same type */
468 Equ(dir1, dir2, name, nl1[i1]);
469 } else {
470 /* different types */
471 Del(dir1, dir2, name, nl1[i1]);
472 Add(dir1, dir2, name, nl2[i2]);
473 }
474 free(nl1[i1]); nl1[i1] = 0;
475 free(nl2[i2]); nl2[i2] = 0;
476 } else if (i < 0) {
477 /* Something extra in list 1, delete it */
478 Del(dir1, dir2, name, nl1[i1]);
479 free(nl1[i1]); nl1[i1] = 0;
480 } else {
481 /* Something extra in list 2, add it */
482 Add(dir1, dir2, name, nl2[i2]);
483 free(nl2[i2]); nl2[i2] = 0;
484 }
485 }
486 if (n1 >= 0)
487 free(nl1);
488 if (n2 >= 0)
489 free(nl2);
490}
491
492int
493main(int argc, char **argv)
494{
495 int i;
496 extern char *optarg;
497 extern int optind;
498
499 setbuf(stderr, NULL);
500
501#if 0
502 if (regcomp(&reg_bogus, DEFAULT_BOGUS, REG_EXTENDED | REG_NEWLINE))
503 /* XXX use regerror to explain it */
504 errx(1, "Default regular expression argument to -B is botched");
505 flag_bogus = 1;
506
507 if (regcomp(&reg_ignore, DEFAULT_IGNORE, REG_EXTENDED | REG_NEWLINE))
508 /* XXX use regerror to explain it */
509 errx(1, "Default regular expression argument to -I is botched");
510 flag_ignore = 1;
511#endif
512
513 while ((i = getopt(argc, argv, "D:I:B:l:qv")) != EOF)
514 switch (i) {
515 case 'D':
516 damage_limit = strtol(optarg, 0, 0);
517 if (damage_limit < 0)
518 errx(1, "Damage limit must be positive");
519 break;
520 case 'I':
521 if (flag_ignore)
522 regfree(&reg_ignore);
523 flag_ignore = 0;
524 if (!*optarg)
525 break;
526 if (regcomp(&reg_ignore, optarg,
527 REG_EXTENDED | REG_NEWLINE))
528 /* XXX use regerror to explain it */
529 errx(1, "Regular expression argument to -I is botched");
530 flag_ignore = 1;
531 break;
532 case 'B':
533 if (flag_bogus)
534 regfree(&reg_bogus);
535 flag_bogus = 0;
536 if (!*optarg)
537 break;
538 if (regcomp(&reg_bogus, optarg,
539 REG_EXTENDED | REG_NEWLINE))
540 /* XXX use regerror to explain it */
541 errx(1, "Regular expression argument to -B is botched");
542 flag_bogus = 1;
543 break;
544 case 'l':
545 logf = fopen(optarg, "w");
546 if (!logf)
547 err(1, optarg);
548 break;
549 case 'q':
550 verbose--;
551 break;
552 case 'v':
553 verbose++;
554 break;
555 case '?':
556 default:
557 Usage();
558 return (1);
559 }
560 argc -= optind;
561 argv += optind;
562
563 if (!logf)
564 logf = fopen("/dev/null", "w");
565
566 setbuf(stdout, 0);
567
568 if (argc != 6) {
569 Usage();
570 return (1);
571 }
572
573 signal(SIGINFO, stat_info);
574
575 fprintf(stderr, "CTM_BEGIN 2.0 %s %s %s %s\n",
576 argv[0], argv[1], argv[2], argv[3]);
577 fprintf(logf, "CTM_BEGIN 2.0 %s %s %s %s\n",
578 argv[0], argv[1], argv[2], argv[3]);
579 printf("CTM_BEGIN 2.0 %s %s %s %s\n",
580 argv[0], argv[1], argv[2], argv[3]);
581 DoDir(argv[4], argv[5], "");
582 if (damage_limit && damage > damage_limit) {
583 print_stat(stderr, "DAMAGE: ");
584 errx(1, "Damage of %d would exceed %d files",
585 damage, damage_limit);
586 } else if (change < 2) {
587 errx(4, "No changes");
588 } else {
589 printf("CTM_END ");
590 fprintf(logf, "CTM_END\n");
591 print_stat(stderr, "END: ");
592 }
593 exit(0);
594}