1 /* $FreeBSD: src/usr.sbin/ctm/mkCTM/mkctm.c,v 1.9.6.1 2000/12/11 01:03:30 obrien Exp $ */
9 * -q decrease verbosity
10 * -v increase verbosity
19 #include <sys/types.h>
35 #define DEFAULT_IGNORE "/CVS$|/\\.#|00_TRANS\\.TBL$"
36 #define DEFAULT_BOGUS "\\.core$|\\.orig$|\\.rej$|\\.o$"
37 regex_t reg_ignore, reg_bogus;
38 int flag_ignore, flag_bogus;
41 int damage, damage_limit;
46 u_long s1_ignored, s2_ignored;
47 u_long s1_bogus, s2_bogus;
48 u_long s1_wrong, s2_wrong;
49 u_long s_new_dirs, s_new_files, s_new_bytes;
50 u_long s_del_dirs, s_del_files, s_del_bytes;
51 u_long s_files_chg, s_bytes_add, s_bytes_del;
52 u_long s_same_dirs, s_same_files, s_same_bytes;
53 u_long s_edit_files, s_edit_bytes, s_edit_saves;
54 u_long s_sub_files, s_sub_bytes;
60 "usage: mkctm [-options] name number timestamp prefix dir1 dir2\n");
61 fprintf(stderr, "options:\n");
62 fprintf(stderr, "\t\t-B bogus_regexp\n");
63 fprintf(stderr, "\t\t-D damage_limit\n");
64 fprintf(stderr, "\t\t-I ignore_regexp\n");
65 fprintf(stderr, "\t\t-q\n");
66 fprintf(stderr, "\t\t-v\n");
70 print_stat(FILE *fd, char *pre)
72 fprintf(fd, "%sNames:\n", pre);
73 fprintf(fd, "%s ignore: %5lu ref %5lu target\n",
74 pre, s1_ignored, s2_ignored);
75 fprintf(fd, "%s bogus: %5lu ref %5lu target\n",
76 pre, s1_bogus, s2_bogus);
77 fprintf(fd, "%s wrong: %5lu ref %5lu target\n",
78 pre, s1_wrong, s2_wrong);
79 fprintf(fd, "%sDelta:\n", pre);
80 fprintf(fd, "%s new: %5lu dirs %5lu files %9lu plus\n",
81 pre, s_new_dirs, s_new_files, s_new_bytes);
82 fprintf(fd, "%s del: %5lu dirs %5lu files %9lu minus\n",
83 pre, s_del_dirs, s_del_files, s_del_bytes);
84 fprintf(fd, "%s chg: %5lu files %9lu plus %9lu minus\n",
85 pre, s_files_chg, s_bytes_add, s_bytes_del);
86 fprintf(fd, "%s same: %5lu dirs %5lu files %9lu bytes\n",
87 pre, s_same_dirs, s_same_files, s_same_bytes);
88 fprintf(fd, "%sMethod:\n", pre);
89 fprintf(fd, "%s edit: %5lu files %9lu bytes %9lu saved\n",
90 pre, s_edit_files, s_edit_bytes, s_edit_saves);
91 fprintf(fd, "%s sub: %5lu files %9lu bytes\n",
92 pre, s_sub_files, s_sub_bytes);
99 signal(SIGINFO, stat_info);
100 print_stat(stderr, "INFO: ");
103 void DoDir(const char *dir1, const char *dir2, const char *name);
105 static struct stat st;
106 static __inline struct stat *
109 if (lstat(name, &st) < 0)
110 err(1, "couldn't stat %s", name);
115 dirselect(struct dirent *de)
117 if (!strcmp(de->d_name, ".")) return 0;
118 if (!strcmp(de->d_name, "..")) return 0;
123 name_stat(const char *pfx, const char *dir, const char *name, struct dirent *de)
125 char *buf = alloca(strlen(dir) + strlen(name) +
126 strlen(de->d_name) + 3);
130 strcat(buf, "/"); strcat(buf, name);
131 strcat(buf, "/"); strcat(buf, de->d_name);
133 printf("%s %s%s %u %u %o",
134 pfx, name, de->d_name,
135 st->st_uid, st->st_gid, st->st_mode & ~S_IFMT);
136 fprintf(logf, "%s %s%s\n", pfx, name, de->d_name);
138 fprintf(stderr, "%s %s%s\n", pfx, name, de->d_name);
143 Equ(const char *dir1, const char *dir2, const char *name, struct dirent *de)
145 if (de->d_type == DT_DIR) {
146 char *p = alloca(strlen(name)+strlen(de->d_name)+2);
148 strcpy(p, name); strcat(p, de->d_name); strcat(p, "/");
149 DoDir(dir1, dir2, p);
152 char *buf1 = alloca(strlen(dir1) + strlen(name) +
153 strlen(de->d_name) + 3);
154 char *buf2 = alloca(strlen(dir2) + strlen(name) +
155 strlen(de->d_name) + 3);
156 char *m1, md5_1[33], *m2, md5_2[33];
162 strcat(buf1, "/"); strcat(buf1, name);
163 strcat(buf1, "/"); strcat(buf1, de->d_name);
164 fd1 = open(buf1, O_RDONLY);
165 if(fd1 < 0) { err(3, "%s", buf1); }
168 strcat(buf2, "/"); strcat(buf2, name);
169 strcat(buf2, "/"); strcat(buf2, de->d_name);
170 fd2 = open(buf2, O_RDONLY);
171 if(fd2 < 0) { err(3, "%s", buf2); }
174 /* XXX if we could just trust the size to change... */
175 if (s1.st_size == s2.st_size) {
177 s_same_bytes += s1.st_size;
183 p1=mmap(0, s1.st_size, PROT_READ, MAP_PRIVATE, fd1, 0);
184 if (p1 == (u_char *)MAP_FAILED) { err(3, "%s", buf1); }
187 p2=mmap(0, s2.st_size, PROT_READ, MAP_PRIVATE, fd2, 0);
188 if (p2 == (u_char *)MAP_FAILED) { err(3, "%s", buf2); }
191 /* If identical, we're done. */
192 if((s1.st_size == s2.st_size) && !memcmp(p1, p2, s1.st_size)) {
194 s_same_bytes += s1.st_size;
200 if (s1.st_size > s2.st_size)
201 s_bytes_del += (s1.st_size - s2.st_size);
203 s_bytes_add += (s2.st_size - s1.st_size);
205 m1 = MD5Data(p1, s1.st_size, md5_1);
206 m2 = MD5Data(p2, s2.st_size, md5_2);
208 /* Just a curiosity... */
209 if(!strcmp(m1, m2)) {
210 if (s1.st_size != s2.st_size)
212 "Notice: MD5 same for files of diffent size:\n\t%s\n\t%s\n",
218 u_long l = s2.st_size + 2;
219 u_char *cmd = alloca(strlen(buf1)+strlen(buf2)+100);
220 u_char *ob = alloca(l), *p;
224 if (s1.st_size && p1[s1.st_size-1] != '\n') {
227 "last char != \\n in %s\n",
232 if (s2.st_size && p2[s2.st_size-1] != '\n') {
235 "last char != \\n in %s\n",
240 for (p=p1; p<p1+s1.st_size; p++)
249 for (p=p2; p<p2+s2.st_size; p++)
258 strcpy(cmd, "diff -n ");
263 for (j = 1, l = 0; l < s2.st_size; ) {
264 j = fread(ob+l, 1, s2.st_size - l, F);
272 while (EOF != fgetc(F))
277 if (l && l < s2.st_size) {
278 name_stat("CTMFN", dir2, name, de);
279 printf(" %s %s %d\n", m1, m2, (unsigned)l);
280 fwrite(ob, 1, l, stdout);
284 s_edit_saves += (s2.st_size - l);
287 name_stat("CTMFS", dir2, name, de);
288 printf(" %s %s %u\n", m1, m2, (unsigned)s2.st_size);
289 fwrite(p2, 1, s2.st_size, stdout);
292 s_sub_bytes += s2.st_size;
296 munmap(p1, s1.st_size);
297 munmap(p2, s2.st_size);
302 Add(const char *dir1, const char *dir2, const char *name, struct dirent *de)
305 if (de->d_type == DT_DIR) {
306 char *p = alloca(strlen(name)+strlen(de->d_name)+2);
307 strcpy(p, name); strcat(p, de->d_name); strcat(p, "/");
308 name_stat("CTMDM", dir2, name, de);
311 DoDir(dir1, dir2, p);
312 } else if (de->d_type == DT_REG) {
313 char *buf2 = alloca(strlen(dir2) + strlen(name) +
314 strlen(de->d_name) + 3);
321 strcat(buf2, "/"); strcat(buf2, name);
322 strcat(buf2, "/"); strcat(buf2, de->d_name);
323 fd1 = open(buf2, O_RDONLY);
324 if (fd1 < 0) { err(3, "%s", buf2); }
326 p1=mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd1, 0);
327 if (p1 == (u_char *)MAP_FAILED) { err(3, "%s", buf2); }
329 m2 = MD5Data(p1, st.st_size, md5_2);
330 name_stat("CTMFM", dir2, name, de);
331 printf(" %s %u\n", m2, (unsigned)st.st_size);
332 fwrite(p1, 1, st.st_size, stdout);
334 munmap(p1, st.st_size);
336 s_new_bytes += st.st_size;
341 Del (const char *dir1, const char *dir2, const char *name, struct dirent *de)
345 if (de->d_type == DT_DIR) {
346 char *p = alloca(strlen(name)+strlen(de->d_name)+2);
347 strcpy(p, name); strcat(p, de->d_name); strcat(p, "/");
348 DoDir(dir1, dir2, p);
349 printf("CTMDR %s%s\n", name, de->d_name);
350 fprintf(logf, "CTMDR %s%s\n", name, de->d_name);
352 fprintf(stderr, "CTMDR %s%s\n", name, de->d_name);
355 } else if (de->d_type == DT_REG) {
356 char *buf1 = alloca(strlen(dir1) + strlen(name) +
357 strlen(de->d_name) + 3);
360 strcat(buf1, "/"); strcat(buf1, name);
361 strcat(buf1, "/"); strcat(buf1, de->d_name);
362 m1 = MD5File(buf1, md5_1);
363 printf("CTMFR %s%s %s\n", name, de->d_name, m1);
364 fprintf(logf, "CTMFR %s%s %s\n", name, de->d_name, m1);
366 fprintf(stderr, "CTMFR %s%s\n", name, de->d_name);
369 s_del_bytes += StatFile(buf1)->st_size;
374 GetNext(int *i, int *n, struct dirent **nl, const char *dir, const char *name, u_long *ignored, u_long *bogus, u_long *wrong)
385 if (buf1[strlen(buf1)-1] != '/')
387 strcat(buf1, nl[*i]->d_name);
389 !regexec(®_ignore, buf1, 0, 0, 0)) {
391 fprintf(logf, "Ignore %s\n", buf1);
393 fprintf(stderr, "Ignore %s\n", buf1);
395 } else if (flag_bogus &&
396 !regexec(®_bogus, buf1, 0, 0, 0)) {
398 fprintf(logf, "Bogus %s\n", buf1);
399 fprintf(stderr, "Bogus %s\n", buf1);
406 if (buf[strlen(buf)-1] != '/')
411 free(nl[*i]); nl[*i] = 0;
413 /* If the filesystem didn't tell us, find type */
414 if (nl[*i]->d_type == DT_UNKNOWN)
415 nl[*i]->d_type = IFTODT(StatFile(buf)->st_mode);
416 if (nl[*i]->d_type == DT_REG || nl[*i]->d_type == DT_DIR)
420 fprintf(stderr, "Wrong %s\n", buf);
421 free(nl[*i]); nl[*i] = 0;
426 DoDir(const char *dir1, const char *dir2, const char *name)
428 int i1, i2, n1, n2, i;
429 struct dirent **nl1, **nl2;
430 char *buf1 = alloca(strlen(dir1) + strlen(name) + 4);
431 char *buf2 = alloca(strlen(dir2) + strlen(name) + 4);
433 strcpy(buf1, dir1); strcat(buf1, "/"); strcat(buf1, name);
434 strcpy(buf2, dir2); strcat(buf2, "/"); strcat(buf2, name);
435 n1 = scandir(buf1, &nl1, dirselect, alphasort);
436 n2 = scandir(buf2, &nl2, dirselect, alphasort);
438 GetNext(&i1, &n1, nl1, dir1, name, &s1_ignored, &s1_bogus, &s1_wrong);
439 GetNext(&i2, &n2, nl2, dir2, name, &s2_ignored, &s2_bogus, &s2_wrong);
440 for (;i1 < n1 || i2 < n2;) {
442 if (damage_limit && damage > damage_limit)
445 /* Get next item from list 1 */
446 if (i1 < n1 && !nl1[i1])
447 GetNext(&i1, &n1, nl1, dir1, name,
448 &s1_ignored, &s1_bogus, &s1_wrong);
450 /* Get next item from list 2 */
451 if (i2 < n2 && !nl2[i2])
452 GetNext(&i2, &n2, nl2, dir2, name,
453 &s2_ignored, &s2_bogus, &s2_wrong);
455 if (i1 >= n1 && i2 >= n2) {
458 } else if (i1 >= n1 && i2 < n2) {
459 /* end of list 1, add anything left on list 2 */
460 Add(dir1, dir2, name, nl2[i2]);
461 free(nl2[i2]); nl2[i2] = 0;
462 } else if (i1 < n1 && i2 >= n2) {
463 /* end of list 2, delete anything left on list 1 */
464 Del(dir1, dir2, name, nl1[i1]);
465 free(nl1[i1]); nl1[i1] = 0;
466 } else if (!(i = strcmp(nl1[i1]->d_name, nl2[i2]->d_name))) {
467 /* Identical names */
468 if (nl1[i1]->d_type == nl2[i2]->d_type) {
470 Equ(dir1, dir2, name, nl1[i1]);
472 /* different types */
473 Del(dir1, dir2, name, nl1[i1]);
474 Add(dir1, dir2, name, nl2[i2]);
476 free(nl1[i1]); nl1[i1] = 0;
477 free(nl2[i2]); nl2[i2] = 0;
479 /* Something extra in list 1, delete it */
480 Del(dir1, dir2, name, nl1[i1]);
481 free(nl1[i1]); nl1[i1] = 0;
483 /* Something extra in list 2, add it */
484 Add(dir1, dir2, name, nl2[i2]);
485 free(nl2[i2]); nl2[i2] = 0;
495 main(int argc, char **argv)
501 setbuf(stderr, NULL);
504 if (regcomp(®_bogus, DEFAULT_BOGUS, REG_EXTENDED | REG_NEWLINE))
505 /* XXX use regerror to explain it */
506 errx(1, "default regular expression argument to -B is botched");
509 if (regcomp(®_ignore, DEFAULT_IGNORE, REG_EXTENDED | REG_NEWLINE))
510 /* XXX use regerror to explain it */
511 errx(1, "default regular expression argument to -I is botched");
515 while ((i = getopt(argc, argv, "D:I:B:l:qv")) != -1)
518 damage_limit = strtol(optarg, 0, 0);
519 if (damage_limit < 0)
520 errx(1, "damage limit must be positive");
524 regfree(®_ignore);
528 if (regcomp(®_ignore, optarg,
529 REG_EXTENDED | REG_NEWLINE))
530 /* XXX use regerror to explain it */
531 errx(1, "regular expression argument to -I is botched");
540 if (regcomp(®_bogus, optarg,
541 REG_EXTENDED | REG_NEWLINE))
542 /* XXX use regerror to explain it */
543 errx(1, "regular expression argument to -B is botched");
547 logf = fopen(optarg, "w");
549 err(1, "%s", optarg);
566 logf = fopen(_PATH_DEVNULL, "w");
575 signal(SIGINFO, stat_info);
577 fprintf(stderr, "CTM_BEGIN 2.0 %s %s %s %s\n",
578 argv[0], argv[1], argv[2], argv[3]);
579 fprintf(logf, "CTM_BEGIN 2.0 %s %s %s %s\n",
580 argv[0], argv[1], argv[2], argv[3]);
581 printf("CTM_BEGIN 2.0 %s %s %s %s\n",
582 argv[0], argv[1], argv[2], argv[3]);
583 DoDir(argv[4], argv[5], "");
584 if (damage_limit && damage > damage_limit) {
585 print_stat(stderr, "DAMAGE: ");
586 errx(1, "damage of %d would exceed %d files",
587 damage, damage_limit);
588 } else if (change < 2) {
589 errx(4, "no changes");
592 fprintf(logf, "CTM_END\n");
593 print_stat(stderr, "END: ");