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 USE_FEATURE_DIFF_DIR(int dl_count;)
107 USE_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(char *file1, FILE *f1, 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(char *file1, FILE *f1, 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(char *file1, 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 f1 = xfopen_stdin(bb_dev_null);
1026 else
1027 f1 = xfopen_stdin(file1);
1028 if (flags & D_EMPTY2)
1029 f2 = xfopen_stdin(bb_dev_null);
1030 else
1031 f2 = xfopen_stdin(file2);
1032
1033
1034
1035
1036 if (!(flags & (D_EMPTY1 | D_EMPTY2))) {
1037
1038
1039 i = files_differ(f1, f2);
1040 if (i != 1) {
1041 if (i != 0)
1042 exit_status |= 2;
1043 goto closem;
1044 }
1045 }
1046
1047 if (!asciifile(f1) || !asciifile(f2)) {
1048 rval = D_BINARY;
1049 exit_status |= 1;
1050 goto closem;
1051 }
1052
1053
1054 prepare(0, f1 );
1055 prepare(1, f2 );
1056 prune();
1057 sort(sfile[0], slen[0]);
1058 sort(sfile[1], slen[1]);
1059
1060 member = (int *) nfile[1];
1061 equiv(sfile[0], slen[0], sfile[1], slen[1], member);
1062
1063 member = xrealloc(member, (slen[1] + 2) * sizeof(int));
1064
1065 class = (int *) nfile[0];
1066 unsort(sfile[0], slen[0], class);
1067 class = xrealloc(class, (slen[0] + 2) * sizeof(int));
1068
1069 klist = xmalloc((slen[0] + 2) * sizeof(int));
1070 clen = 0;
1071 clistlen = 100;
1072 clist = xmalloc(clistlen * sizeof(struct cand));
1073 i = stone(class, slen[0], member, klist);
1074 free(member);
1075 free(class);
1076
1077 J = xrealloc(J, (nlen[0] + 2) * sizeof(int));
1078 unravel(klist[i]);
1079 free(clist);
1080 free(klist);
1081
1082 ixold = xrealloc(ixold, (nlen[0] + 2) * sizeof(long));
1083 ixnew = xrealloc(ixnew, (nlen[1] + 2) * sizeof(long));
1084
1085 check(f1, f2);
1086
1087 output(file1, f1, file2, f2);
1088
1089 closem:
1090 if (anychange) {
1091 exit_status |= 1;
1092 if (rval == D_SAME)
1093 rval = D_DIFFER;
1094 }
1095 fclose_if_not_stdin(f1);
1096 fclose_if_not_stdin(f2);
1097 if (tempname1) {
1098 unlink(tempname1);
1099 free(tempname1);
1100 }
1101 if (tempname2) {
1102 unlink(tempname2);
1103 free(tempname2);
1104 }
1105 return rval;
1106}
1107
1108
1109#if ENABLE_FEATURE_DIFF_DIR
1110static void do_diff(char *dir1, char *path1, char *dir2, char *path2)
1111{
1112 int flags = 0;
1113 int val;
1114 char *fullpath1 = NULL;
1115 char *fullpath2 = NULL;
1116
1117 if (path1)
1118 fullpath1 = concat_path_file(dir1, path1);
1119 if (path2)
1120 fullpath2 = concat_path_file(dir2, path2);
1121
1122 if (!fullpath1 || stat(fullpath1, &stb1) != 0) {
1123 flags |= D_EMPTY1;
1124 memset(&stb1, 0, sizeof(stb1));
1125 if (path2) {
1126 free(fullpath1);
1127 fullpath1 = concat_path_file(dir1, path2);
1128 }
1129 }
1130 if (!fullpath2 || stat(fullpath2, &stb2) != 0) {
1131 flags |= D_EMPTY2;
1132 memset(&stb2, 0, sizeof(stb2));
1133 stb2.st_mode = stb1.st_mode;
1134 if (path1) {
1135 free(fullpath2);
1136 fullpath2 = concat_path_file(dir2, path1);
1137 }
1138 }
1139
1140 if (stb1.st_mode == 0)
1141 stb1.st_mode = stb2.st_mode;
1142
1143 if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
1144 printf("Common subdirectories: %s and %s\n", fullpath1, fullpath2);
1145 goto ret;
1146 }
1147
1148 if (!S_ISREG(stb1.st_mode) && !S_ISDIR(stb1.st_mode))
1149 val = D_SKIPPED1;
1150 else if (!S_ISREG(stb2.st_mode) && !S_ISDIR(stb2.st_mode))
1151 val = D_SKIPPED2;
1152 else {
1153
1154 val = diffreg(fullpath1, fullpath2, flags);
1155 }
1156
1157 print_status(val, fullpath1, fullpath2 );
1158 ret:
1159 free(fullpath1);
1160 free(fullpath2);
1161}
1162#endif
1163
1164
1165#if ENABLE_FEATURE_DIFF_DIR
1166
1167static int FAST_FUNC add_to_dirlist(const char *filename,
1168 struct stat *sb UNUSED_PARAM,
1169 void *userdata,
1170 int depth UNUSED_PARAM)
1171{
1172 dl = xrealloc_vector(dl, 5, dl_count);
1173 dl[dl_count] = xstrdup(filename + (int)(ptrdiff_t)userdata);
1174 dl_count++;
1175 return TRUE;
1176}
1177
1178
1179
1180static char **get_recursive_dirlist(char *path)
1181{
1182 dl_count = 0;
1183 dl = xzalloc(sizeof(dl[0]));
1184
1185
1186
1187
1188 if (option_mask32 & FLAG_r) {
1189 recursive_action(path, ACTION_RECURSE|ACTION_FOLLOWLINKS,
1190 add_to_dirlist,
1191 NULL,
1192 (void*)(ptrdiff_t)(strlen(path) + 1),
1193 0);
1194 } else {
1195 DIR *dp;
1196 struct dirent *ep;
1197
1198 dp = warn_opendir(path);
1199 while ((ep = readdir(dp))) {
1200 if (!strcmp(ep->d_name, "..") || LONE_CHAR(ep->d_name, '.'))
1201 continue;
1202 add_to_dirlist(ep->d_name, NULL, (void*)(int)0, 0);
1203 }
1204 closedir(dp);
1205 }
1206
1207
1208 qsort_string_vector(dl, dl_count);
1209
1210 dl[dl_count] = NULL;
1211 return dl;
1212}
1213
1214
1215static void diffdir(char *p1, char *p2)
1216{
1217 char **dirlist1, **dirlist2;
1218 char *dp1, *dp2;
1219 int pos;
1220
1221
1222 dp1 = last_char_is(p1, '/');
1223 if (dp1 != NULL)
1224 *dp1 = '\0';
1225 dp2 = last_char_is(p2, '/');
1226 if (dp2 != NULL)
1227 *dp2 = '\0';
1228
1229
1230 dirlist1 = get_recursive_dirlist(p1);
1231 dirlist2 = get_recursive_dirlist(p2);
1232
1233
1234 if (opt_S_start) {
1235 while (*dirlist1 != NULL && strcmp(*dirlist1, opt_S_start) < 0)
1236 dirlist1++;
1237 while (*dirlist2 != NULL && strcmp(*dirlist2, opt_S_start) < 0)
1238 dirlist2++;
1239 if ((*dirlist1 == NULL) || (*dirlist2 == NULL))
1240 bb_error_msg(bb_msg_invalid_arg, "NULL", "-S");
1241 }
1242
1243
1244
1245
1246
1247 while (*dirlist1 != NULL || *dirlist2 != NULL) {
1248 dp1 = *dirlist1;
1249 dp2 = *dirlist2;
1250 pos = dp1 == NULL ? 1 : (dp2 == NULL ? -1 : strcmp(dp1, dp2));
1251 if (pos == 0) {
1252 do_diff(p1, dp1, p2, dp2);
1253 dirlist1++;
1254 dirlist2++;
1255 } else if (pos < 0) {
1256 if (option_mask32 & FLAG_N)
1257 do_diff(p1, dp1, p2, NULL);
1258 else
1259 print_only(p1, dp1);
1260 dirlist1++;
1261 } else {
1262 if (option_mask32 & FLAG_N)
1263 do_diff(p1, NULL, p2, dp2);
1264 else
1265 print_only(p2, dp2);
1266 dirlist2++;
1267 }
1268 }
1269}
1270#endif
1271
1272
1273int diff_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1274int diff_main(int argc UNUSED_PARAM, char **argv)
1275{
1276 int gotstdin = 0;
1277 char *f1, *f2;
1278 llist_t *L_arg = NULL;
1279
1280 INIT_G();
1281
1282
1283 opt_complementary = "=2:L::U+";
1284 getopt32(argv, "abdiL:NqrsS:tTU:wu"
1285 "p" ,
1286 &L_arg, &opt_S_start, &opt_U_context);
1287
1288 argv += optind;
1289 while (L_arg) {
1290 if (label1 && label2)
1291 bb_show_usage();
1292 if (label1)
1293 label2 = label1;
1294 label1 = llist_pop(&L_arg);
1295 }
1296
1297
1298
1299
1300
1301 f1 = argv[0];
1302 f2 = argv[1];
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
1314 if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode)))
1315 bb_error_msg_and_die("can't compare stdin to a directory");
1316
1317 if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
1318#if ENABLE_FEATURE_DIFF_DIR
1319 diffdir(f1, f2);
1320 return exit_status;
1321#else
1322 bb_error_msg_and_die("no support for directory comparison");
1323#endif
1324 }
1325
1326 if (S_ISDIR(stb1.st_mode)) {
1327
1328
1329 char *slash = strrchr(f2, '/');
1330 f1 = concat_path_file(f1, slash ? slash + 1 : f2);
1331 xstat(f1, &stb1);
1332 }
1333 if (S_ISDIR(stb2.st_mode)) {
1334 char *slash = strrchr(f1, '/');
1335 f2 = concat_path_file(f2, slash ? slash + 1 : f1);
1336 xstat(f2, &stb2);
1337 }
1338
1339
1340
1341 print_status((gotstdin > 1 ? D_SAME : diffreg(f1, f2, 0)),
1342 f1, f2 );
1343 return exit_status;
1344}
1345