1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include "libbb.h"
16
17
18
19
20
21
22
23
24
25enum {
26
27
28
29 D_EMPTY1 = 2 * ENABLE_FEATURE_DIFF_DIR,
30 D_EMPTY2 = 4 * ENABLE_FEATURE_DIFF_DIR,
31};
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47#define D_SAME 0
48#define D_DIFFER (1 << 0)
49#define D_BINARY (1 << 1)
50#define D_COMMON (1 << 2)
51
52#define D_ISDIR1 (1 << 4)
53#define D_ISDIR2 (1 << 5)
54#define D_ERROR (1 << 6)
55#define D_SKIPPED1 (1 << 7)
56#define D_SKIPPED2 (1 << 8)
57
58
59#define FLAG_a (1 << 0)
60#define FLAG_b (1 << 1)
61#define FLAG_d (1 << 2)
62#define FLAG_i (1 << 3)
63#define FLAG_L (1 << 4)
64#define FLAG_N (1 << 5)
65#define FLAG_q (1 << 6)
66#define FLAG_r (1 << 7)
67#define FLAG_s (1 << 8)
68#define FLAG_S (1 << 9)
69#define FLAG_t (1 << 10)
70#define FLAG_T (1 << 11)
71#define FLAG_U (1 << 12)
72#define FLAG_w (1 << 13)
73
74
75struct cand {
76 int x;
77 int y;
78 int pred;
79};
80
81struct line {
82 int serial;
83 int value;
84};
85
86
87
88
89
90
91struct context_vec {
92 int a;
93 int b;
94 int c;
95 int d;
96};
97
98
99#define g_read_buf bb_common_bufsiz1
100
101struct globals {
102 bool anychange;
103 smallint exit_status;
104 int opt_U_context;
105 size_t max_context;
106 IF_FEATURE_DIFF_DIR(int dl_count;)
107 IF_FEATURE_DIFF_DIR(char **dl;)
108 char *opt_S_start;
109 const char *label1;
110 const char *label2;
111 int *J;
112 int clen;
113 int pref, suff;
114 int nlen[2];
115 int slen[2];
116 int clistlen;
117 struct cand *clist;
118 long *ixnew;
119 long *ixold;
120 struct line *nfile[2];
121 struct line *sfile[2];
122 struct context_vec *context_vec_start;
123 struct context_vec *context_vec_end;
124 struct context_vec *context_vec_ptr;
125 char *tempname1, *tempname2;
126 struct stat stb1, stb2;
127};
128#define G (*ptr_to_globals)
129#define anychange (G.anychange )
130#define exit_status (G.exit_status )
131#define opt_U_context (G.opt_U_context )
132#define max_context (G.max_context )
133#define dl_count (G.dl_count )
134#define dl (G.dl )
135#define opt_S_start (G.opt_S_start )
136#define label1 (G.label1 )
137#define label2 (G.label2 )
138#define J (G.J )
139#define clen (G.clen )
140#define pref (G.pref )
141#define suff (G.suff )
142#define nlen (G.nlen )
143#define slen (G.slen )
144#define clistlen (G.clistlen )
145#define clist (G.clist )
146#define ixnew (G.ixnew )
147#define ixold (G.ixold )
148#define nfile (G.nfile )
149#define sfile (G.sfile )
150#define context_vec_start (G.context_vec_start )
151#define context_vec_end (G.context_vec_end )
152#define context_vec_ptr (G.context_vec_ptr )
153#define stb1 (G.stb1 )
154#define stb2 (G.stb2 )
155#define tempname1 (G.tempname1 )
156#define tempname2 (G.tempname2 )
157#define INIT_G() do { \
158 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
159 opt_U_context = 3; \
160 max_context = 64; \
161} while (0)
162
163
164#if ENABLE_FEATURE_DIFF_DIR
165static void print_only(const char *path, const char *entry)
166{
167 printf("Only in %s: %s\n", path, entry);
168}
169#endif
170
171
172static void print_status(int val, char *_path1, char *_path2)
173{
174
175
176
177
178 switch (val) {
179
180
181
182
183 case D_COMMON:
184 printf("Common subdirectories: %s and %s\n", _path1, _path2);
185 break;
186 case D_BINARY:
187 printf("Binary files %s and %s differ\n", _path1, _path2);
188 break;
189 case D_DIFFER:
190 if (option_mask32 & FLAG_q)
191 printf("Files %s and %s differ\n", _path1, _path2);
192 break;
193 case D_SAME:
194 if (option_mask32 & FLAG_s)
195 printf("Files %s and %s are identical\n", _path1, _path2);
196 break;
197 case D_ISDIR1:
198 printf("File %s is a %s while file %s is a %s\n",
199 _path1, "directory", _path2, "regular file");
200 break;
201 case D_ISDIR2:
202 printf("File %s is a %s while file %s is a %s\n",
203 _path1, "regular file", _path2, "directory");
204 break;
205 case D_SKIPPED1:
206 printf("File %s is not a regular file or directory and was skipped\n",
207 _path1);
208 break;
209 case D_SKIPPED2:
210 printf("File %s is not a regular file or directory and was skipped\n",
211 _path2);
212 break;
213 }
214
215
216
217
218
219
220}
221
222
223
224
225
226
227static ALWAYS_INLINE int fiddle_sum(int sum, int t)
228{
229 return sum * 127 + t;
230}
231static int readhash(FILE *fp)
232{
233 int i, t, space;
234 int sum;
235
236 sum = 1;
237 space = 0;
238 i = 0;
239 if (!(option_mask32 & (FLAG_b | FLAG_w))) {
240 while ((t = getc(fp)) != '\n') {
241 if (t == EOF) {
242 if (i == 0)
243 return 0;
244 break;
245 }
246 sum = fiddle_sum(sum, t);
247 i = 1;
248 }
249 } else {
250 while (1) {
251 switch (t = getc(fp)) {
252 case '\t':
253 case '\r':
254 case '\v':
255 case '\f':
256 case ' ':
257 space = 1;
258 continue;
259 default:
260 if (space && !(option_mask32 & FLAG_w)) {
261 i = 1;
262 space = 0;
263 }
264 sum = fiddle_sum(sum, t);
265 i = 1;
266 continue;
267 case EOF:
268 if (i == 0)
269 return 0;
270
271 case '\n':
272 break;
273 }
274 break;
275 }
276 }
277
278
279
280
281 return (sum == 0 ? 1 : sum);
282}
283
284
285
286
287
288static char *make_temp(FILE *f, struct stat *sb)
289{
290 char *name;
291 int fd;
292
293 if (S_ISREG(sb->st_mode) || S_ISBLK(sb->st_mode))
294 return NULL;
295 name = xstrdup("/tmp/difXXXXXX");
296 fd = mkstemp(name);
297 if (fd < 0)
298 bb_perror_msg_and_die("mkstemp");
299 if (bb_copyfd_eof(fileno(f), fd) < 0) {
300 clean_up:
301 unlink(name);
302 xfunc_die();
303 }
304 fstat(fd, sb);
305 close(fd);
306 if (freopen(name, "r+", f) == NULL) {
307 bb_perror_msg("freopen");
308 goto clean_up;
309 }
310 return name;
311}
312
313
314
315
316
317
318static NOINLINE int files_differ(FILE *f1, FILE *f2)
319{
320 size_t i, j;
321
322
323
324 tempname1 = make_temp(f1, &stb1);
325 tempname2 = make_temp(f2, &stb2);
326 if (stb1.st_size != stb2.st_size) {
327 return 1;
328 }
329 while (1) {
330 i = fread(g_read_buf, 1, COMMON_BUFSIZE/2, f1);
331 j = fread(g_read_buf + COMMON_BUFSIZE/2, 1, COMMON_BUFSIZE/2, f2);
332 if (i != j)
333 return 1;
334 if (i == 0)
335 return (ferror(f1) || ferror(f2)) ? -1 : 0;
336 if (memcmp(g_read_buf,
337 g_read_buf + COMMON_BUFSIZE/2, i) != 0)
338 return 1;
339 }
340}
341
342
343static void prepare(int i, FILE *fp )
344{
345 struct line *p;
346 int h;
347 size_t j, sz;
348
349 rewind(fp);
350
351
352
353 sz = 100;
354
355 p = xmalloc((sz + 3) * sizeof(p[0]));
356 j = 0;
357 while ((h = readhash(fp)) != 0) {
358 if (j == sz) {
359 sz = sz * 3 / 2;
360 p = xrealloc(p, (sz + 3) * sizeof(p[0]));
361 }
362 p[++j].value = h;
363 }
364 nlen[i] = j;
365 nfile[i] = p;
366}
367
368
369static void prune(void)
370{
371 int i, j;
372
373 for (pref = 0; pref < nlen[0] && pref < nlen[1] &&
374 nfile[0][pref + 1].value == nfile[1][pref + 1].value; pref++)
375 continue;
376 for (suff = 0; suff < nlen[0] - pref && suff < nlen[1] - pref &&
377 nfile[0][nlen[0] - suff].value == nfile[1][nlen[1] - suff].value;
378 suff++)
379 continue;
380 for (j = 0; j < 2; j++) {
381 sfile[j] = nfile[j] + pref;
382 slen[j] = nlen[j] - pref - suff;
383 for (i = 0; i <= slen[j]; i++)
384 sfile[j][i].serial = i;
385 }
386}
387
388
389static void equiv(struct line *a, int n, struct line *b, int m, int *c)
390{
391 int i, j;
392
393 i = j = 1;
394 while (i <= n && j <= m) {
395 if (a[i].value < b[j].value)
396 a[i++].value = 0;
397 else if (a[i].value == b[j].value)
398 a[i++].value = j;
399 else
400 j++;
401 }
402 while (i <= n)
403 a[i++].value = 0;
404 b[m + 1].value = 0;
405 j = 0;
406 while (++j <= m) {
407 c[j] = -b[j].serial;
408 while (b[j + 1].value == b[j].value) {
409 j++;
410 c[j] = b[j].serial;
411 }
412 }
413 c[j] = -1;
414}
415
416
417static int isqrt(int n)
418{
419 int y, x;
420
421 if (n == 0)
422 return 0;
423 x = 1;
424 do {
425 y = x;
426 x = n / x;
427 x += y;
428 x /= 2;
429 } while ((x - y) > 1 || (x - y) < -1);
430
431 return x;
432}
433
434
435static int newcand(int x, int y, int pred)
436{
437 struct cand *q;
438
439 if (clen == clistlen) {
440 clistlen = clistlen * 11 / 10;
441 clist = xrealloc(clist, clistlen * sizeof(struct cand));
442 }
443 q = clist + clen;
444 q->x = x;
445 q->y = y;
446 q->pred = pred;
447 return clen++;
448}
449
450
451static int search(int *c, int k, int y)
452{
453 int i, j, l, t;
454
455 if (clist[c[k]].y < y)
456 return k + 1;
457 i = 0;
458 j = k + 1;
459 while (1) {
460 l = i + j;
461 if ((l >>= 1) <= i)
462 break;
463 t = clist[c[l]].y;
464 if (t > y)
465 j = l;
466 else if (t < y)
467 i = l;
468 else
469 return l;
470 }
471 return l + 1;
472}
473
474
475static int stone(int *a, int n, int *b, int *c)
476{
477 int i, k, y, j, l;
478 int oldc, tc, oldl;
479 unsigned int numtries;
480#if ENABLE_FEATURE_DIFF_MINIMAL
481 const unsigned int bound =
482 (option_mask32 & FLAG_d) ? UINT_MAX : MAX(256, isqrt(n));
483#else
484 const unsigned int bound = MAX(256, isqrt(n));
485#endif
486
487 k = 0;
488 c[0] = newcand(0, 0, 0);
489 for (i = 1; i <= n; i++) {
490 j = a[i];
491 if (j == 0)
492 continue;
493 y = -b[j];
494 oldl = 0;
495 oldc = c[0];
496 numtries = 0;
497 do {
498 if (y <= clist[oldc].y)
499 continue;
500 l = search(c, k, y);
501 if (l != oldl + 1)
502 oldc = c[l - 1];
503 if (l <= k) {
504 if (clist[c[l]].y <= y)
505 continue;
506 tc = c[l];
507 c[l] = newcand(i, y, oldc);
508 oldc = tc;
509 oldl = l;
510 numtries++;
511 } else {
512 c[l] = newcand(i, y, oldc);
513 k++;
514 break;
515 }
516 } while ((y = b[++j]) > 0 && numtries < bound);
517 }
518 return k;
519}
520
521
522static void unravel(int p)
523{
524 struct cand *q;
525 int i;
526
527 for (i = 0; i <= nlen[0]; i++)
528 J[i] = i <= pref ? i : i > nlen[0] - suff ? i + nlen[1] - nlen[0] : 0;
529 for (q = clist + p; q->y != 0; q = clist + q->pred)
530 J[q->x + pref] = q->y + pref;
531}
532
533
534static void unsort(struct line *f, int l, int *b)
535{
536 int *a, i;
537
538 a = xmalloc((l + 1) * sizeof(int));
539 for (i = 1; i <= l; i++)
540 a[f[i].serial] = f[i].value;
541 for (i = 1; i <= l; i++)
542 b[i] = a[i];
543 free(a);
544}
545
546
547static int skipline(FILE *f)
548{
549 int i, c;
550
551 for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
552 continue;
553 return i;
554}
555
556
557
558
559
560
561
562
563static NOINLINE void check(FILE *f1, FILE *f2)
564{
565 int i, j, jackpot, c, d;
566 long ctold, ctnew;
567
568 rewind(f1);
569 rewind(f2);
570 j = 1;
571 ixold[0] = ixnew[0] = 0;
572 jackpot = 0;
573 ctold = ctnew = 0;
574 for (i = 1; i <= nlen[0]; i++) {
575 if (J[i] == 0) {
576 ixold[i] = ctold += skipline(f1);
577 continue;
578 }
579 while (j < J[i]) {
580 ixnew[j] = ctnew += skipline(f2);
581 j++;
582 }
583 if (option_mask32 & (FLAG_b | FLAG_w | FLAG_i)) {
584 while (1) {
585 c = getc(f1);
586 d = getc(f2);
587
588
589
590
591 if ((option_mask32 & (FLAG_b | FLAG_w))
592 && ((c == EOF && d == '\n') || (c == '\n' && d == EOF))
593 ) {
594 break;
595 }
596 ctold++;
597 ctnew++;
598 if ((option_mask32 & FLAG_b) && isspace(c) && isspace(d)) {
599 do {
600 if (c == '\n')
601 break;
602 ctold++;
603 c = getc(f1);
604 } while (isspace(c));
605 do {
606 if (d == '\n')
607 break;
608 ctnew++;
609 d = getc(f2);
610 } while (isspace(d));
611 } else if (option_mask32 & FLAG_w) {
612 while (isspace(c) && c != '\n') {
613 c = getc(f1);
614 ctold++;
615 }
616 while (isspace(d) && d != '\n') {
617 d = getc(f2);
618 ctnew++;
619 }
620 }
621 if (c != d) {
622 jackpot++;
623 J[i] = 0;
624 if (c != '\n' && c != EOF)
625 ctold += skipline(f1);
626 if (d != '\n' && c != EOF)
627 ctnew += skipline(f2);
628 break;
629 }
630 if (c == '\n' || c == EOF)
631 break;
632 }
633 } else {
634 while (1) {
635 ctold++;
636 ctnew++;
637 c = getc(f1);
638 d = getc(f2);
639 if (c != d) {
640 J[i] = 0;
641 if (c != '\n' && c != EOF)
642 ctold += skipline(f1);
643
644 if (d != '\n' && d != EOF)
645 ctnew += skipline(f2);
646 break;
647 }
648 if (c == '\n' || c == EOF)
649 break;
650 }
651 }
652 ixold[i] = ctold;
653 ixnew[j] = ctnew;
654 j++;
655 }
656 for (; j <= nlen[1]; j++)
657 ixnew[j] = ctnew += skipline(f2);
658}
659
660
661
662static void sort(struct line *a, int n)
663{
664 struct line *ai, *aim, w;
665 int j, m = 0, k;
666
667 if (n == 0)
668 return;
669 for (j = 1; j <= n; j *= 2)
670 m = 2 * j - 1;
671 for (m /= 2; m != 0; m /= 2) {
672 k = n - m;
673 for (j = 1; j <= k; j++) {
674 for (ai = &a[j]; ai > a; ai -= m) {
675 aim = &ai[m];
676 if (aim < ai)
677 break;
678 if (aim->value > ai[0].value
679 || (aim->value == ai[0].value && aim->serial > ai[0].serial)
680 ) {
681 break;
682 }
683 w.value = ai[0].value;
684 ai[0].value = aim->value;
685 aim->value = w.value;
686 w.serial = ai[0].serial;
687 ai[0].serial = aim->serial;
688 aim->serial = w.serial;
689 }
690 }
691 }
692}
693
694
695static void uni_range(int a, int b)
696{
697 if (a < b)
698 printf("%d,%d", a, b - a + 1);
699 else if (a == b)
700 printf("%d", b);
701 else
702 printf("%d,0", b);
703}
704
705
706static void fetch(long *f, int a, int b, FILE *lb, int ch)
707{
708 int i, j, c, lastc, col, nc;
709
710 if (a > b)
711 return;
712 for (i = a; i <= b; i++) {
713 fseek(lb, f[i - 1], SEEK_SET);
714 nc = f[i] - f[i - 1];
715 if (ch != '\0') {
716 putchar(ch);
717 if (option_mask32 & FLAG_T)
718 putchar('\t');
719 }
720 col = 0;
721 for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {
722 c = getc(lb);
723 if (c == EOF) {
724 printf("\n\\ No newline at end of file\n");
725 return;
726 }
727 if (c == '\t' && (option_mask32 & FLAG_t)) {
728 do {
729 putchar(' ');
730 } while (++col & 7);
731 } else {
732 putchar(c);
733 col++;
734 }
735 }
736 }
737}
738
739
740#if ENABLE_FEATURE_DIFF_BINARY
741static int asciifile(FILE *f)
742{
743 int i, cnt;
744
745 if (option_mask32 & FLAG_a)
746 return 1;
747 rewind(f);
748 cnt = fread(g_read_buf, 1, COMMON_BUFSIZE, f);
749 for (i = 0; i < cnt; i++) {
750 if (!isprint(g_read_buf[i])
751 && !isspace(g_read_buf[i])
752 ) {
753 return 0;
754 }
755 }
756 return 1;
757}
758#else
759#define asciifile(f) 1
760#endif
761
762
763
764static void dump_unified_vec(FILE *f1, FILE *f2)
765{
766 struct context_vec *cvp = context_vec_start;
767 int lowa, upb, lowc, upd;
768 int a, b, c, d;
769 char ch;
770
771 if (context_vec_start > context_vec_ptr)
772 return;
773
774 b = d = 0;
775 lowa = MAX(1, cvp->a - opt_U_context);
776 upb = MIN(nlen[0], context_vec_ptr->b + opt_U_context);
777 lowc = MAX(1, cvp->c - opt_U_context);
778 upd = MIN(nlen[1], context_vec_ptr->d + opt_U_context);
779
780 printf("@@ -");
781 uni_range(lowa, upb);
782 printf(" +");
783 uni_range(lowc, upd);
784 printf(" @@\n");
785
786
787
788
789
790 for (; cvp <= context_vec_ptr; cvp++) {
791 a = cvp->a;
792 b = cvp->b;
793 c = cvp->c;
794 d = cvp->d;
795
796
797
798
799
800
801 if (a <= b && c <= d)
802 ch = 'c';
803 else
804 ch = (a <= b) ? 'd' : 'a';
805#if 0
806 switch (ch) {
807 case 'c':
808
809 fetch(ixold, lowa, a - 1, f1, ' ');
810 fetch(ixold, a, b, f1, '-');
811 fetch(ixnew, c, d, f2, '+');
812 break;
813 case 'd':
814 fetch(ixold, lowa, a - 1, f1, ' ');
815 fetch(ixold, a, b, f1, '-');
816 break;
817 case 'a':
818 fetch(ixnew, lowc, c - 1, f2, ' ');
819 fetch(ixnew, c, d, f2, '+');
820 break;
821 }
822#else
823 if (ch == 'c' || ch == 'd') {
824 fetch(ixold, lowa, a - 1, f1, ' ');
825 fetch(ixold, a, b, f1, '-');
826 }
827 if (ch == 'a')
828 fetch(ixnew, lowc, c - 1, f2, ' ');
829 if (ch == 'c' || ch == 'a')
830 fetch(ixnew, c, d, f2, '+');
831#endif
832 lowa = b + 1;
833 lowc = d + 1;
834 }
835 fetch(ixnew, d + 1, upd, f2, ' ');
836
837 context_vec_ptr = context_vec_start - 1;
838}
839
840
841static void print_header(const char *file1, const char *file2)
842{
843 if (label1)
844 printf("--- %s\n", label1);
845 else
846 printf("--- %s\t%s", file1, ctime(&stb1.st_mtime));
847 if (label2)
848 printf("+++ %s\n", label2);
849 else
850 printf("+++ %s\t%s", file2, ctime(&stb2.st_mtime));
851}
852
853
854
855
856
857
858
859
860
861static void change(const char *file1, FILE *f1, const char *file2, FILE *f2,
862 int a, int b, int c, int d)
863{
864 if ((a > b && c > d) || (option_mask32 & FLAG_q)) {
865 anychange = 1;
866 return;
867 }
868
869
870
871
872 if (context_vec_ptr == context_vec_end - 1) {
873 ptrdiff_t offset = context_vec_ptr - context_vec_start;
874
875 max_context <<= 1;
876 context_vec_start = xrealloc(context_vec_start,
877 max_context * sizeof(struct context_vec));
878 context_vec_end = context_vec_start + max_context;
879 context_vec_ptr = context_vec_start + offset;
880 }
881 if (anychange == 0) {
882
883
884
885 print_header(file1, file2);
886 } else if (a > context_vec_ptr->b + (2 * opt_U_context) + 1
887 && c > context_vec_ptr->d + (2 * opt_U_context) + 1
888 ) {
889
890
891
892
893
894 dump_unified_vec(f1, f2);
895 }
896 context_vec_ptr++;
897 context_vec_ptr->a = a;
898 context_vec_ptr->b = b;
899 context_vec_ptr->c = c;
900 context_vec_ptr->d = d;
901 anychange = 1;
902}
903
904
905static void output(const char *file1, FILE *f1, const char *file2, FILE *f2)
906{
907
908
909 int m, i0, i1, j00, j01;
910
911 rewind(f1);
912 rewind(f2);
913 m = nlen[0];
914 J[0] = 0;
915 J[m + 1] = nlen[1] + 1;
916 for (i0 = 1; i0 <= m; i0 = i1 + 1) {
917 while (i0 <= m && J[i0] == J[i0 - 1] + 1)
918 i0++;
919 j00 = J[i0 - 1] + 1;
920 i1 = i0 - 1;
921 while (i1 < m && J[i1 + 1] == 0)
922 i1++;
923 j01 = J[i1 + 1] - 1;
924 J[i1] = j01;
925
926 change(file1, f1, file2, f2, i0, i1, j00, j01);
927 }
928 if (m == 0) {
929
930 change(file1, f1, file2, f2, 1, 0, 1, nlen[1]);
931 }
932 if (anychange != 0 && !(option_mask32 & FLAG_q)) {
933
934 dump_unified_vec(f1, f2);
935 }
936}
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002static unsigned diffreg(const char *file1, const char *file2, int flags)
1003{
1004 int *member;
1005 int *class;
1006 int *klist;
1007 FILE *f1;
1008 FILE *f2;
1009 unsigned rval;
1010 int i;
1011
1012 anychange = 0;
1013 context_vec_ptr = context_vec_start - 1;
1014 tempname1 = tempname2 = NULL;
1015
1016
1017 if (S_ISDIR(stb1.st_mode) != S_ISDIR(stb2.st_mode))
1018 return (S_ISDIR(stb1.st_mode) ? D_ISDIR1 : D_ISDIR2);
1019
1020
1021 rval = D_SAME;
1022
1023 if (flags & D_EMPTY1)
1024
1025 file1 = bb_dev_null;
1026 f1 = xfopen_stdin(file1);
1027 if (flags & D_EMPTY2)
1028 file2 = bb_dev_null;
1029 f2 = xfopen_stdin(file2);
1030
1031
1032
1033
1034 if (!(flags & (D_EMPTY1 | D_EMPTY2))) {
1035
1036
1037 i = files_differ(f1, f2);
1038 if (i != 1) {
1039 if (i != 0)
1040 exit_status |= 2;
1041 goto closem;
1042 }
1043 }
1044
1045 if (!asciifile(f1) || !asciifile(f2)) {
1046 rval = D_BINARY;
1047 exit_status |= 1;
1048 goto closem;
1049 }
1050
1051
1052 prepare(0, f1 );
1053 prepare(1, f2 );
1054 prune();
1055 sort(sfile[0], slen[0]);
1056 sort(sfile[1], slen[1]);
1057
1058 member = (int *) nfile[1];
1059 equiv(sfile[0], slen[0], sfile[1], slen[1], member);
1060
1061 member = xrealloc(member, (slen[1] + 2) * sizeof(int));
1062
1063 class = (int *) nfile[0];
1064 unsort(sfile[0], slen[0], class);
1065 class = xrealloc(class, (slen[0] + 2) * sizeof(int));
1066
1067 klist = xmalloc((slen[0] + 2) * sizeof(int));
1068 clen = 0;
1069 clistlen = 100;
1070 clist = xmalloc(clistlen * sizeof(struct cand));
1071 i = stone(class, slen[0], member, klist);
1072 free(member);
1073 free(class);
1074
1075 J = xrealloc(J, (nlen[0] + 2) * sizeof(int));
1076 unravel(klist[i]);
1077 free(clist);
1078 free(klist);
1079
1080 ixold = xrealloc(ixold, (nlen[0] + 2) * sizeof(long));
1081 ixnew = xrealloc(ixnew, (nlen[1] + 2) * sizeof(long));
1082
1083 check(f1, f2);
1084
1085 output(file1, f1, file2, f2);
1086
1087 closem:
1088 if (anychange) {
1089 exit_status |= 1;
1090 if (rval == D_SAME)
1091 rval = D_DIFFER;
1092 }
1093 fclose_if_not_stdin(f1);
1094 fclose_if_not_stdin(f2);
1095 if (tempname1) {
1096 unlink(tempname1);
1097 free(tempname1);
1098 }
1099 if (tempname2) {
1100 unlink(tempname2);
1101 free(tempname2);
1102 }
1103 return rval;
1104}
1105
1106
1107#if ENABLE_FEATURE_DIFF_DIR
1108static void do_diff(char *dir1, char *path1, char *dir2, char *path2)
1109{
1110 int flags = 0;
1111 int val;
1112 char *fullpath1 = NULL;
1113 char *fullpath2 = NULL;
1114
1115 if (path1)
1116 fullpath1 = concat_path_file(dir1, path1);
1117 if (path2)
1118 fullpath2 = concat_path_file(dir2, path2);
1119
1120 if (!fullpath1 || stat(fullpath1, &stb1) != 0) {
1121 flags |= D_EMPTY1;
1122 memset(&stb1, 0, sizeof(stb1));
1123 if (path2) {
1124 free(fullpath1);
1125 fullpath1 = concat_path_file(dir1, path2);
1126 }
1127 }
1128 if (!fullpath2 || stat(fullpath2, &stb2) != 0) {
1129 flags |= D_EMPTY2;
1130 memset(&stb2, 0, sizeof(stb2));
1131 stb2.st_mode = stb1.st_mode;
1132 if (path1) {
1133 free(fullpath2);
1134 fullpath2 = concat_path_file(dir2, path1);
1135 }
1136 }
1137
1138 if (stb1.st_mode == 0)
1139 stb1.st_mode = stb2.st_mode;
1140
1141 if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
1142 printf("Common subdirectories: %s and %s\n", fullpath1, fullpath2);
1143 goto ret;
1144 }
1145
1146 if (!S_ISREG(stb1.st_mode) && !S_ISDIR(stb1.st_mode))
1147 val = D_SKIPPED1;
1148 else if (!S_ISREG(stb2.st_mode) && !S_ISDIR(stb2.st_mode))
1149 val = D_SKIPPED2;
1150 else {
1151
1152 val = diffreg(fullpath1, fullpath2, flags);
1153 }
1154
1155 print_status(val, fullpath1, fullpath2 );
1156 ret:
1157 free(fullpath1);
1158 free(fullpath2);
1159}
1160#endif
1161
1162
1163#if ENABLE_FEATURE_DIFF_DIR
1164
1165static int FAST_FUNC add_to_dirlist(const char *filename,
1166 struct stat *sb UNUSED_PARAM,
1167 void *userdata,
1168 int depth UNUSED_PARAM)
1169{
1170 dl = xrealloc_vector(dl, 5, dl_count);
1171 dl[dl_count] = xstrdup(filename + (int)(ptrdiff_t)userdata);
1172 dl_count++;
1173 return TRUE;
1174}
1175
1176
1177
1178static char **get_recursive_dirlist(char *path)
1179{
1180 dl_count = 0;
1181 dl = xzalloc(sizeof(dl[0]));
1182
1183
1184
1185
1186 if (option_mask32 & FLAG_r) {
1187 recursive_action(path, ACTION_RECURSE|ACTION_FOLLOWLINKS,
1188 add_to_dirlist,
1189 NULL,
1190 (void*)(ptrdiff_t)(strlen(path) + 1),
1191 0);
1192 } else {
1193 DIR *dp;
1194 struct dirent *ep;
1195
1196 dp = warn_opendir(path);
1197 while ((ep = readdir(dp))) {
1198 if (!strcmp(ep->d_name, "..") || LONE_CHAR(ep->d_name, '.'))
1199 continue;
1200 add_to_dirlist(ep->d_name, NULL, (void*)(int)0, 0);
1201 }
1202 closedir(dp);
1203 }
1204
1205
1206 qsort_string_vector(dl, dl_count);
1207
1208 dl[dl_count] = NULL;
1209 return dl;
1210}
1211
1212
1213static void diffdir(char *p1, char *p2)
1214{
1215 char **dirlist1, **dirlist2;
1216 char *dp1, *dp2;
1217 int pos;
1218
1219
1220 dp1 = last_char_is(p1, '/');
1221 if (dp1 != NULL)
1222 *dp1 = '\0';
1223 dp2 = last_char_is(p2, '/');
1224 if (dp2 != NULL)
1225 *dp2 = '\0';
1226
1227
1228 dirlist1 = get_recursive_dirlist(p1);
1229 dirlist2 = get_recursive_dirlist(p2);
1230
1231
1232 if (opt_S_start) {
1233 while (*dirlist1 != NULL && strcmp(*dirlist1, opt_S_start) < 0)
1234 dirlist1++;
1235 while (*dirlist2 != NULL && strcmp(*dirlist2, opt_S_start) < 0)
1236 dirlist2++;
1237 if ((*dirlist1 == NULL) || (*dirlist2 == NULL))
1238 bb_error_msg(bb_msg_invalid_arg, "NULL", "-S");
1239 }
1240
1241
1242
1243
1244
1245 while (*dirlist1 != NULL || *dirlist2 != NULL) {
1246 dp1 = *dirlist1;
1247 dp2 = *dirlist2;
1248 pos = dp1 == NULL ? 1 : (dp2 == NULL ? -1 : strcmp(dp1, dp2));
1249 if (pos == 0) {
1250 do_diff(p1, dp1, p2, dp2);
1251 dirlist1++;
1252 dirlist2++;
1253 } else if (pos < 0) {
1254 if (option_mask32 & FLAG_N)
1255 do_diff(p1, dp1, p2, NULL);
1256 else
1257 print_only(p1, dp1);
1258 dirlist1++;
1259 } else {
1260 if (option_mask32 & FLAG_N)
1261 do_diff(p1, NULL, p2, dp2);
1262 else
1263 print_only(p2, dp2);
1264 dirlist2++;
1265 }
1266 }
1267}
1268#endif
1269
1270
1271int diff_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1272int diff_main(int argc UNUSED_PARAM, char **argv)
1273{
1274 int gotstdin = 0;
1275 char *f1, *f2;
1276 llist_t *L_arg = NULL;
1277
1278 INIT_G();
1279
1280
1281 opt_complementary = "=2:L::U+";
1282 getopt32(argv, "abdiL:NqrsS:tTU:wu"
1283 "p" ,
1284 &L_arg, &opt_S_start, &opt_U_context);
1285
1286 argv += optind;
1287 while (L_arg) {
1288 if (label1 && label2)
1289 bb_show_usage();
1290 if (label1)
1291 label2 = label1;
1292 label1 = llist_pop(&L_arg);
1293 }
1294
1295
1296
1297
1298
1299 f1 = argv[0];
1300 f2 = argv[1];
1301
1302 xfunc_error_retval = 2;
1303 if (LONE_DASH(f1)) {
1304 fstat(STDIN_FILENO, &stb1);
1305 gotstdin++;
1306 } else
1307 xstat(f1, &stb1);
1308 if (LONE_DASH(f2)) {
1309 fstat(STDIN_FILENO, &stb2);
1310 gotstdin++;
1311 } else
1312 xstat(f2, &stb2);
1313 xfunc_error_retval = 1;
1314
1315 if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode)))
1316 bb_error_msg_and_die("can't compare stdin to a directory");
1317
1318 if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
1319#if ENABLE_FEATURE_DIFF_DIR
1320 diffdir(f1, f2);
1321 return exit_status;
1322#else
1323 bb_error_msg_and_die("no support for directory comparison");
1324#endif
1325 }
1326
1327 if (S_ISDIR(stb1.st_mode)) {
1328
1329
1330 char *slash = strrchr(f2, '/');
1331 f1 = concat_path_file(f1, slash ? slash + 1 : f2);
1332 xstat(f1, &stb1);
1333 }
1334 if (S_ISDIR(stb2.st_mode)) {
1335 char *slash = strrchr(f1, '/');
1336 f2 = concat_path_file(f2, slash ? slash + 1 : f1);
1337 xstat(f2, &stb2);
1338 }
1339
1340
1341
1342 print_status((gotstdin > 1 ? D_SAME : diffreg(f1, f2, 0)),
1343 f1, f2 );
1344 return exit_status;
1345}
1346