Initial public busybox upstream commit
[busybox4maemo] / editors / diff.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini diff implementation for busybox, adapted from OpenBSD diff.
4  *
5  * Copyright (C) 2006 by Robert Sullivan <cogito.ergo.cogito@hotmail.com>
6  * Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
7  *
8  * Sponsored in part by the Defense Advanced Research Projects
9  * Agency (DARPA) and Air Force Research Laboratory, Air Force
10  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
11  *
12  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
13  */
14
15 #include "libbb.h"
16
17 #define FSIZE_MAX 32768
18
19 /*
20  * Output flags
21  */
22 #define D_HEADER        1       /* Print a header/footer between files */
23 #define D_EMPTY1        2       /* Treat first file as empty (/dev/null) */
24 #define D_EMPTY2        4       /* Treat second file as empty (/dev/null) */
25
26 /*
27  * Status values for print_status() and diffreg() return values
28  * Guide:
29  * D_SAME - files are the same
30  * D_DIFFER - files differ
31  * D_BINARY - binary files differ
32  * D_COMMON - subdirectory common to both dirs
33  * D_ONLY - file only exists in one dir
34  * D_MISMATCH1 - path1 a dir, path2 a file
35  * D_MISMATCH2 - path1 a file, path2 a dir
36  * D_ERROR - error occurred
37  * D_SKIPPED1 - skipped path1 as it is a special file
38  * D_SKIPPED2 - skipped path2 as it is a special file
39  */
40
41 #define D_SAME          0
42 #define D_DIFFER        (1<<0)
43 #define D_BINARY        (1<<1)
44 #define D_COMMON        (1<<2)
45 #define D_ONLY          (1<<3)
46 #define D_MISMATCH1     (1<<4)
47 #define D_MISMATCH2     (1<<5)
48 #define D_ERROR         (1<<6)
49 #define D_SKIPPED1      (1<<7)
50 #define D_SKIPPED2      (1<<8)
51
52 /* Command line options */
53 #define FLAG_a  (1<<0)
54 #define FLAG_b  (1<<1)
55 #define FLAG_d  (1<<2)
56 #define FLAG_i  (1<<3)
57 #define FLAG_L  (1<<4)
58 #define FLAG_N  (1<<5)
59 #define FLAG_q  (1<<6)
60 #define FLAG_r  (1<<7)
61 #define FLAG_s  (1<<8)
62 #define FLAG_S  (1<<9)
63 #define FLAG_t  (1<<10)
64 #define FLAG_T  (1<<11)
65 #define FLAG_U  (1<<12)
66 #define FLAG_w  (1<<13)
67
68 #define g_read_buf bb_common_bufsiz1
69
70 struct cand {
71         int x;
72         int y;
73         int pred;
74 };
75
76 struct line {
77         int serial;
78         int value;
79 };
80
81 /*
82  * The following struct is used to record change information
83  * doing a "context" or "unified" diff.  (see routine "change" to
84  * understand the highly mnemonic field names)
85  */
86 struct context_vec {
87         int a;          /* start line in old file */
88         int b;          /* end line in old file */
89         int c;          /* start line in new file */
90         int d;          /* end line in new file */
91 };
92
93 struct globals {
94         USE_FEATURE_DIFF_DIR(char **dl;)
95         USE_FEATURE_DIFF_DIR(int dl_count;)
96         /* This is the default number of lines of context. */
97         int context;
98         size_t max_context;
99         int status;
100         char *start;
101         const char *label1;
102         const char *label2;
103         struct line *file[2];
104         int *J;          /* will be overlaid on class */
105         int *class;      /* will be overlaid on file[0] */
106         int *klist;      /* will be overlaid on file[0] after class */
107         int *member;     /* will be overlaid on file[1] */
108         int clen;
109         int len[2];
110         int pref, suff;  /* length of prefix and suffix */
111         int slen[2];
112         bool anychange;
113         long *ixnew;     /* will be overlaid on file[1] */
114         long *ixold;     /* will be overlaid on klist */
115         struct cand *clist;  /* merely a free storage pot for candidates */
116         int clistlen;    /* the length of clist */
117         struct line *sfile[2];   /* shortened by pruning common prefix/suffix */
118         struct context_vec *context_vec_start;
119         struct context_vec *context_vec_end;
120         struct context_vec *context_vec_ptr;
121         struct stat stb1, stb2;
122 };
123 #define G (*ptr_to_globals)
124 #define dl                 (G.dl                )
125 #define dl_count           (G.dl_count          )
126 #define context            (G.context           )
127 #define max_context        (G.max_context       )
128 #define status             (G.status            )
129 #define start              (G.start             )
130 #define label1             (G.label1            )
131 #define label2             (G.label2            )
132 #define file               (G.file              )
133 #define J                  (G.J                 )
134 #define class              (G.class             )
135 #define klist              (G.klist             )
136 #define member             (G.member            )
137 #define clen               (G.clen              )
138 #define len                (G.len               )
139 #define pref               (G.pref              )
140 #define suff               (G.suff              )
141 #define slen               (G.slen              )
142 #define anychange          (G.anychange         )
143 #define ixnew              (G.ixnew             )
144 #define ixold              (G.ixold             )
145 #define clist              (G.clist             )
146 #define clistlen           (G.clistlen          )
147 #define sfile              (G.sfile             )
148 #define context_vec_start  (G.context_vec_start )
149 #define context_vec_end    (G.context_vec_end   )
150 #define context_vec_ptr    (G.context_vec_ptr   )
151 #define stb1               (G.stb1              )
152 #define stb2               (G.stb2              )
153 #define INIT_G() do { \
154         SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
155         context = 3; \
156         max_context = 64; \
157 } while (0)
158
159
160 static void print_only(const char *path, size_t dirlen, const char *entry)
161 {
162         if (dirlen > 1)
163                 dirlen--;
164         printf("Only in %.*s: %s\n", (int) dirlen, path, entry);
165 }
166
167 static void print_status(int val, char *path1, char *path2, char *entry)
168 {
169         const char *const _entry = entry ? entry : "";
170         char * const _path1 = entry ? concat_path_file(path1, _entry) : path1;
171         char * const _path2 = entry ? concat_path_file(path2, _entry) : path2;
172
173         switch (val) {
174         case D_ONLY:
175                 print_only(path1, strlen(path1), entry);
176                 break;
177         case D_COMMON:
178                 printf("Common subdirectories: %s and %s\n", _path1, _path2);
179                 break;
180         case D_BINARY:
181                 printf("Binary files %s and %s differ\n", _path1, _path2);
182                 break;
183         case D_DIFFER:
184                 if (option_mask32 & FLAG_q)
185                         printf("Files %s and %s differ\n", _path1, _path2);
186                 break;
187         case D_SAME:
188                 if (option_mask32 & FLAG_s)
189                         printf("Files %s and %s are identical\n", _path1, _path2);
190                 break;
191         case D_MISMATCH1:
192                 printf("File %s is a %s while file %s is a %s\n",
193                            _path1, "directory", _path2, "regular file");
194                 break;
195         case D_MISMATCH2:
196                 printf("File %s is a %s while file %s is a %s\n",
197                            _path1, "regular file", _path2, "directory");
198                 break;
199         case D_SKIPPED1:
200                 printf("File %s is not a regular file or directory and was skipped\n",
201                            _path1);
202                 break;
203         case D_SKIPPED2:
204                 printf("File %s is not a regular file or directory and was skipped\n",
205                            _path2);
206                 break;
207         }
208         if (entry) {
209                 free(_path1);
210                 free(_path2);
211         }
212 }
213 static ALWAYS_INLINE int fiddle_sum(int sum, int t)
214 {
215         return sum * 127 + t;
216 }
217 /*
218  * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
219  */
220 static int readhash(FILE *fp)
221 {
222         int i, t, space;
223         int sum;
224
225         sum = 1;
226         space = 0;
227         if (!(option_mask32 & (FLAG_b | FLAG_w))) {
228                 for (i = 0; (t = getc(fp)) != '\n'; i++) {
229                         if (t == EOF) {
230                                 if (i == 0)
231                                         return 0;
232                                 break;
233                         }
234                         sum = fiddle_sum(sum, t);
235                 }
236         } else {
237                 for (i = 0;;) {
238                         switch (t = getc(fp)) {
239                         case '\t':
240                         case '\r':
241                         case '\v':
242                         case '\f':
243                         case ' ':
244                                 space++;
245                                 continue;
246                         default:
247                                 if (space && !(option_mask32 & FLAG_w)) {
248                                         i++;
249                                         space = 0;
250                                 }
251                                 sum = fiddle_sum(sum, t);
252                                 i++;
253                                 continue;
254                         case EOF:
255                                 if (i == 0)
256                                         return 0;
257                                 /* FALLTHROUGH */
258                         case '\n':
259                                 break;
260                         }
261                         break;
262                 }
263         }
264         /*
265          * There is a remote possibility that we end up with a zero sum.
266          * Zero is used as an EOF marker, so return 1 instead.
267          */
268         return (sum == 0 ? 1 : sum);
269 }
270
271
272 /*
273  * Check to see if the given files differ.
274  * Returns 0 if they are the same, 1 if different, and -1 on error.
275  */
276 static int files_differ(FILE *f1, FILE *f2, int flags)
277 {
278         size_t i, j;
279
280         if ((flags & (D_EMPTY1 | D_EMPTY2)) || stb1.st_size != stb2.st_size
281          || (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT)
282         ) {
283                 return 1;
284         }
285         while (1) {
286                 i = fread(g_read_buf,                    1, COMMON_BUFSIZE/2, f1);
287                 j = fread(g_read_buf + COMMON_BUFSIZE/2, 1, COMMON_BUFSIZE/2, f2);
288                 if (i != j)
289                         return 1;
290                 if (i == 0)
291                         return (ferror(f1) || ferror(f2));
292                 if (memcmp(g_read_buf,
293                            g_read_buf + COMMON_BUFSIZE/2, i) != 0)
294                         return 1;
295         }
296 }
297
298
299 static void prepare(int i, FILE *fp /*, off_t filesize*/)
300 {
301         struct line *p;
302         int h;
303         size_t j, sz;
304
305         rewind(fp);
306
307         /*sz = (filesize <= FSIZE_MAX ? filesize : FSIZE_MAX) / 25;*/
308         /*if (sz < 100)*/
309         sz = 100;
310
311         p = xmalloc((sz + 3) * sizeof(p[0]));
312         j = 0;
313         while ((h = readhash(fp))) {
314                 if (j == sz) {
315                         sz = sz * 3 / 2;
316                         p = xrealloc(p, (sz + 3) * sizeof(p[0]));
317                 }
318                 p[++j].value = h;
319         }
320         len[i] = j;
321         file[i] = p;
322 }
323
324
325 static void prune(void)
326 {
327         int i, j;
328
329         for (pref = 0; pref < len[0] && pref < len[1] &&
330                  file[0][pref + 1].value == file[1][pref + 1].value; pref++)
331                 continue;
332         for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
333                  file[0][len[0] - suff].value == file[1][len[1] - suff].value;
334                  suff++)
335                 continue;
336         for (j = 0; j < 2; j++) {
337                 sfile[j] = file[j] + pref;
338                 slen[j] = len[j] - pref - suff;
339                 for (i = 0; i <= slen[j]; i++)
340                         sfile[j][i].serial = i;
341         }
342 }
343
344
345 static void equiv(struct line *a, int n, struct line *b, int m, int *c)
346 {
347         int i, j;
348
349         i = j = 1;
350         while (i <= n && j <= m) {
351                 if (a[i].value < b[j].value)
352                         a[i++].value = 0;
353                 else if (a[i].value == b[j].value)
354                         a[i++].value = j;
355                 else
356                         j++;
357         }
358         while (i <= n)
359                 a[i++].value = 0;
360         b[m + 1].value = 0;
361         j = 0;
362         while (++j <= m) {
363                 c[j] = -b[j].serial;
364                 while (b[j + 1].value == b[j].value) {
365                         j++;
366                         c[j] = b[j].serial;
367                 }
368         }
369         c[j] = -1;
370 }
371
372
373 static int isqrt(int n)
374 {
375         int y, x;
376
377         if (n == 0)
378                 return 0;
379         x = 1;
380         do {
381                 y = x;
382                 x = n / x;
383                 x += y;
384                 x /= 2;
385         } while ((x - y) > 1 || (x - y) < -1);
386
387         return x;
388 }
389
390
391 static int newcand(int x, int y, int pred)
392 {
393         struct cand *q;
394
395         if (clen == clistlen) {
396                 clistlen = clistlen * 11 / 10;
397                 clist = xrealloc(clist, clistlen * sizeof(struct cand));
398         }
399         q = clist + clen;
400         q->x = x;
401         q->y = y;
402         q->pred = pred;
403         return clen++;
404 }
405
406
407 static int search(int *c, int k, int y)
408 {
409         int i, j, l, t;
410
411         if (clist[c[k]].y < y)  /* quick look for typical case */
412                 return k + 1;
413         i = 0;
414         j = k + 1;
415         while (1) {
416                 l = i + j;
417                 if ((l >>= 1) <= i)
418                         break;
419                 t = clist[c[l]].y;
420                 if (t > y)
421                         j = l;
422                 else if (t < y)
423                         i = l;
424                 else
425                         return l;
426         }
427         return l + 1;
428 }
429
430
431 static int stone(int *a, int n, int *b, int *c)
432 {
433         int i, k, y, j, l;
434         int oldc, tc, oldl;
435         unsigned int numtries;
436
437 #if ENABLE_FEATURE_DIFF_MINIMAL
438         const unsigned int bound =
439                 (option_mask32 & FLAG_d) ? UINT_MAX : MAX(256, isqrt(n));
440 #else
441         const unsigned int bound = MAX(256, isqrt(n));
442 #endif
443         k = 0;
444         c[0] = newcand(0, 0, 0);
445         for (i = 1; i <= n; i++) {
446                 j = a[i];
447                 if (j == 0)
448                         continue;
449                 y = -b[j];
450                 oldl = 0;
451                 oldc = c[0];
452                 numtries = 0;
453                 do {
454                         if (y <= clist[oldc].y)
455                                 continue;
456                         l = search(c, k, y);
457                         if (l != oldl + 1)
458                                 oldc = c[l - 1];
459                         if (l <= k) {
460                                 if (clist[c[l]].y <= y)
461                                         continue;
462                                 tc = c[l];
463                                 c[l] = newcand(i, y, oldc);
464                                 oldc = tc;
465                                 oldl = l;
466                                 numtries++;
467                         } else {
468                                 c[l] = newcand(i, y, oldc);
469                                 k++;
470                                 break;
471                         }
472                 } while ((y = b[++j]) > 0 && numtries < bound);
473         }
474         return k;
475 }
476
477
478 static void unravel(int p)
479 {
480         struct cand *q;
481         int i;
482
483         for (i = 0; i <= len[0]; i++)
484                 J[i] = i <= pref ? i : i > len[0] - suff ? i + len[1] - len[0] : 0;
485         for (q = clist + p; q->y != 0; q = clist + q->pred)
486                 J[q->x + pref] = q->y + pref;
487 }
488
489
490 static void unsort(struct line *f, int l, int *b)
491 {
492         int *a, i;
493
494         a = xmalloc((l + 1) * sizeof(int));
495         for (i = 1; i <= l; i++)
496                 a[f[i].serial] = f[i].value;
497         for (i = 1; i <= l; i++)
498                 b[i] = a[i];
499         free(a);
500 }
501
502
503 static int skipline(FILE * f)
504 {
505         int i, c;
506
507         for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
508                 continue;
509         return i;
510 }
511
512
513 /*
514  * Check does double duty:
515  *  1.  ferret out any fortuitous correspondences due
516  *      to confounding by hashing (which result in "jackpot")
517  *  2.  collect random access indexes to the two files
518  */
519 static void check(FILE * f1, FILE * f2)
520 {
521         int i, j, jackpot, c, d;
522         long ctold, ctnew;
523
524         rewind(f1);
525         rewind(f2);
526         j = 1;
527         ixold[0] = ixnew[0] = 0;
528         jackpot = 0;
529         ctold = ctnew = 0;
530         for (i = 1; i <= len[0]; i++) {
531                 if (J[i] == 0) {
532                         ixold[i] = ctold += skipline(f1);
533                         continue;
534                 }
535                 while (j < J[i]) {
536                         ixnew[j] = ctnew += skipline(f2);
537                         j++;
538                 }
539                 if ((option_mask32 & FLAG_b) || (option_mask32 & FLAG_w)
540                         || (option_mask32 & FLAG_i)) {
541                         while (1) {
542                                 c = getc(f1);
543                                 d = getc(f2);
544                                 /*
545                                  * GNU diff ignores a missing newline
546                                  * in one file if bflag || wflag.
547                                  */
548                                 if (((option_mask32 & FLAG_b) || (option_mask32 & FLAG_w)) &&
549                                         ((c == EOF && d == '\n') || (c == '\n' && d == EOF))) {
550                                         break;
551                                 }
552                                 ctold++;
553                                 ctnew++;
554                                 if ((option_mask32 & FLAG_b) && isspace(c) && isspace(d)) {
555                                         do {
556                                                 if (c == '\n')
557                                                         break;
558                                                 ctold++;
559                                         } while (isspace(c = getc(f1)));
560                                         do {
561                                                 if (d == '\n')
562                                                         break;
563                                                 ctnew++;
564                                         } while (isspace(d = getc(f2)));
565                                 } else if (option_mask32 & FLAG_w) {
566                                         while (isspace(c) && c != '\n') {
567                                                 c = getc(f1);
568                                                 ctold++;
569                                         }
570                                         while (isspace(d) && d != '\n') {
571                                                 d = getc(f2);
572                                                 ctnew++;
573                                         }
574                                 }
575                                 if (c != d) {
576                                         jackpot++;
577                                         J[i] = 0;
578                                         if (c != '\n' && c != EOF)
579                                                 ctold += skipline(f1);
580                                         if (d != '\n' && c != EOF)
581                                                 ctnew += skipline(f2);
582                                         break;
583                                 }
584                                 if (c == '\n' || c == EOF)
585                                         break;
586                         }
587                 } else {
588                         while (1) {
589                                 ctold++;
590                                 ctnew++;
591                                 c = getc(f1);
592                                 d = getc(f2);
593                                 if (c != d) {
594                                         J[i] = 0;
595                                         if (c != '\n' && c != EOF)
596                                                 ctold += skipline(f1);
597                                         if (d != '\n' && c != EOF)
598                                                 ctnew += skipline(f2);
599                                         break;
600                                 }
601                                 if (c == '\n' || c == EOF)
602                                         break;
603                         }
604                 }
605                 ixold[i] = ctold;
606                 ixnew[j] = ctnew;
607                 j++;
608         }
609         for (; j <= len[1]; j++)
610                 ixnew[j] = ctnew += skipline(f2);
611 }
612
613
614 /* shellsort CACM #201 */
615 static void sort(struct line *a, int n)
616 {
617         struct line *ai, *aim, w;
618         int j, m = 0, k;
619
620         if (n == 0)
621                 return;
622         for (j = 1; j <= n; j *= 2)
623                 m = 2 * j - 1;
624         for (m /= 2; m != 0; m /= 2) {
625                 k = n - m;
626                 for (j = 1; j <= k; j++) {
627                         for (ai = &a[j]; ai > a; ai -= m) {
628                                 aim = &ai[m];
629                                 if (aim < ai)
630                                         break;  /* wraparound */
631                                 if (aim->value > ai[0].value ||
632                                         (aim->value == ai[0].value && aim->serial > ai[0].serial))
633                                         break;
634                                 w.value = ai[0].value;
635                                 ai[0].value = aim->value;
636                                 aim->value = w.value;
637                                 w.serial = ai[0].serial;
638                                 ai[0].serial = aim->serial;
639                                 aim->serial = w.serial;
640                         }
641                 }
642         }
643 }
644
645
646 static void uni_range(int a, int b)
647 {
648         if (a < b)
649                 printf("%d,%d", a, b - a + 1);
650         else if (a == b)
651                 printf("%d", b);
652         else
653                 printf("%d,0", b);
654 }
655
656
657 static void fetch(long *f, int a, int b, FILE * lb, int ch)
658 {
659         int i, j, c, lastc, col, nc;
660
661         if (a > b)
662                 return;
663         for (i = a; i <= b; i++) {
664                 fseek(lb, f[i - 1], SEEK_SET);
665                 nc = f[i] - f[i - 1];
666                 if (ch != '\0') {
667                         putchar(ch);
668                         if (option_mask32 & FLAG_T)
669                                 putchar('\t');
670                 }
671                 col = 0;
672                 for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {
673                         c = getc(lb);
674                         if (c == EOF) {
675                                 printf("\n\\ No newline at end of file\n");
676                                 return;
677                         }
678                         if (c == '\t' && (option_mask32 & FLAG_t)) {
679                                 do {
680                                         putchar(' ');
681                                 } while (++col & 7);
682                         } else {
683                                 putchar(c);
684                                 col++;
685                         }
686                 }
687         }
688 }
689
690
691 static int asciifile(FILE * f)
692 {
693 #if ENABLE_FEATURE_DIFF_BINARY
694         int i, cnt;
695 #endif
696
697         if ((option_mask32 & FLAG_a) || f == NULL)
698                 return 1;
699
700 #if ENABLE_FEATURE_DIFF_BINARY
701         rewind(f);
702         cnt = fread(g_read_buf, 1, COMMON_BUFSIZE, f);
703         for (i = 0; i < cnt; i++) {
704                 if (!isprint(g_read_buf[i])
705                  && !isspace(g_read_buf[i])) {
706                         return 0;
707                 }
708         }
709 #endif
710         return 1;
711 }
712
713
714 /* dump accumulated "unified" diff changes */
715 static void dump_unified_vec(FILE * f1, FILE * f2)
716 {
717         struct context_vec *cvp = context_vec_start;
718         int lowa, upb, lowc, upd;
719         int a, b, c, d;
720         char ch;
721
722         if (context_vec_start > context_vec_ptr)
723                 return;
724
725         b = d = 0;                      /* gcc */
726         lowa = MAX(1, cvp->a - context);
727         upb = MIN(len[0], context_vec_ptr->b + context);
728         lowc = MAX(1, cvp->c - context);
729         upd = MIN(len[1], context_vec_ptr->d + context);
730
731         printf("@@ -");
732         uni_range(lowa, upb);
733         printf(" +");
734         uni_range(lowc, upd);
735         printf(" @@\n");
736
737         /*
738          * Output changes in "unified" diff format--the old and new lines
739          * are printed together.
740          */
741         for (; cvp <= context_vec_ptr; cvp++) {
742                 a = cvp->a;
743                 b = cvp->b;
744                 c = cvp->c;
745                 d = cvp->d;
746
747                 /*
748                  * c: both new and old changes
749                  * d: only changes in the old file
750                  * a: only changes in the new file
751                  */
752                 if (a <= b && c <= d)
753                         ch = 'c';
754                 else
755                         ch = (a <= b) ? 'd' : 'a';
756 #if 0
757                 switch (ch) {
758                 case 'c':
759                         fetch(ixold, lowa, a - 1, f1, ' ');
760                         fetch(ixold, a, b, f1, '-');
761                         fetch(ixnew, c, d, f2, '+');
762                         break;
763                 case 'd':
764                         fetch(ixold, lowa, a - 1, f1, ' ');
765                         fetch(ixold, a, b, f1, '-');
766                         break;
767                 case 'a':
768                         fetch(ixnew, lowc, c - 1, f2, ' ');
769                         fetch(ixnew, c, d, f2, '+');
770                         break;
771                 }
772 #else
773                 if (ch == 'c' || ch == 'd') {
774                         fetch(ixold, lowa, a - 1, f1, ' ');
775                         fetch(ixold, a, b, f1, '-');
776                 }
777                 if (ch == 'a')
778                         fetch(ixnew, lowc, c - 1, f2, ' ');
779                 if (ch == 'c' || ch == 'a')
780                         fetch(ixnew, c, d, f2, '+');
781 #endif
782                 lowa = b + 1;
783                 lowc = d + 1;
784         }
785         fetch(ixnew, d + 1, upd, f2, ' ');
786
787         context_vec_ptr = context_vec_start - 1;
788 }
789
790
791 static void print_header(const char *file1, const char *file2)
792 {
793         if (label1)
794                 printf("--- %s\n", label1);
795         else
796                 printf("--- %s\t%s", file1, ctime(&stb1.st_mtime));
797         if (label2)
798                 printf("+++ %s\n", label2);
799         else
800                 printf("+++ %s\t%s", file2, ctime(&stb2.st_mtime));
801 }
802
803
804 /*
805  * Indicate that there is a difference between lines a and b of the from file
806  * to get to lines c to d of the to file.  If a is greater than b then there
807  * are no lines in the from file involved and this means that there were
808  * lines appended (beginning at b).  If c is greater than d then there are
809  * lines missing from the to file.
810  */
811 static void change(char *file1, FILE * f1, char *file2, FILE * f2, int a,
812                                    int b, int c, int d)
813 {
814         if ((a > b && c > d) || (option_mask32 & FLAG_q)) {
815                 anychange = 1;
816                 return;
817         }
818
819         /*
820          * Allocate change records as needed.
821          */
822         if (context_vec_ptr == context_vec_end - 1) {
823                 ptrdiff_t offset = context_vec_ptr - context_vec_start;
824
825                 max_context <<= 1;
826                 context_vec_start = xrealloc(context_vec_start,
827                                 max_context * sizeof(struct context_vec));
828                 context_vec_end = context_vec_start + max_context;
829                 context_vec_ptr = context_vec_start + offset;
830         }
831         if (anychange == 0) {
832                 /*
833                  * Print the context/unidiff header first time through.
834                  */
835                 print_header(file1, file2);
836         } else if (a > context_vec_ptr->b + (2 * context) + 1 &&
837                            c > context_vec_ptr->d + (2 * context) + 1) {
838                 /*
839                  * If this change is more than 'context' lines from the
840                  * previous change, dump the record and reset it.
841                  */
842                 dump_unified_vec(f1, f2);
843         }
844         context_vec_ptr++;
845         context_vec_ptr->a = a;
846         context_vec_ptr->b = b;
847         context_vec_ptr->c = c;
848         context_vec_ptr->d = d;
849         anychange = 1;
850 }
851
852
853 static void output(char *file1, FILE * f1, char *file2, FILE * f2)
854 {
855         /* Note that j0 and j1 can't be used as they are defined in math.h.
856          * This also allows the rather amusing variable 'j00'... */
857         int m, i0, i1, j00, j01;
858
859         rewind(f1);
860         rewind(f2);
861         m = len[0];
862         J[0] = 0;
863         J[m + 1] = len[1] + 1;
864         for (i0 = 1; i0 <= m; i0 = i1 + 1) {
865                 while (i0 <= m && J[i0] == J[i0 - 1] + 1)
866                         i0++;
867                 j00 = J[i0 - 1] + 1;
868                 i1 = i0 - 1;
869                 while (i1 < m && J[i1 + 1] == 0)
870                         i1++;
871                 j01 = J[i1 + 1] - 1;
872                 J[i1] = j01;
873                 change(file1, f1, file2, f2, i0, i1, j00, j01);
874         }
875         if (m == 0) {
876                 change(file1, f1, file2, f2, 1, 0, 1, len[1]);
877         }
878         if (anychange != 0 && !(option_mask32 & FLAG_q)) {
879                 dump_unified_vec(f1, f2);
880         }
881 }
882
883 /*
884  * The following code uses an algorithm due to Harold Stone,
885  * which finds a pair of longest identical subsequences in
886  * the two files.
887  *
888  * The major goal is to generate the match vector J.
889  * J[i] is the index of the line in file1 corresponding
890  * to line i file0. J[i] = 0 if there is no
891  * such line in file1.
892  *
893  * Lines are hashed so as to work in core. All potential
894  * matches are located by sorting the lines of each file
895  * on the hash (called ``value''). In particular, this
896  * collects the equivalence classes in file1 together.
897  * Subroutine equiv replaces the value of each line in
898  * file0 by the index of the first element of its
899  * matching equivalence in (the reordered) file1.
900  * To save space equiv squeezes file1 into a single
901  * array member in which the equivalence classes
902  * are simply concatenated, except that their first
903  * members are flagged by changing sign.
904  *
905  * Next the indices that point into member are unsorted into
906  * array class according to the original order of file0.
907  *
908  * The cleverness lies in routine stone. This marches
909  * through the lines of file0, developing a vector klist
910  * of "k-candidates". At step i a k-candidate is a matched
911  * pair of lines x,y (x in file0 y in file1) such that
912  * there is a common subsequence of length k
913  * between the first i lines of file0 and the first y
914  * lines of file1, but there is no such subsequence for
915  * any smaller y. x is the earliest possible mate to y
916  * that occurs in such a subsequence.
917  *
918  * Whenever any of the members of the equivalence class of
919  * lines in file1 matable to a line in file0 has serial number
920  * less than the y of some k-candidate, that k-candidate
921  * with the smallest such y is replaced. The new
922  * k-candidate is chained (via pred) to the current
923  * k-1 candidate so that the actual subsequence can
924  * be recovered. When a member has serial number greater
925  * that the y of all k-candidates, the klist is extended.
926  * At the end, the longest subsequence is pulled out
927  * and placed in the array J by unravel
928  *
929  * With J in hand, the matches there recorded are
930  * checked against reality to assure that no spurious
931  * matches have crept in due to hashing. If they have,
932  * they are broken, and "jackpot" is recorded--a harmless
933  * matter except that a true match for a spuriously
934  * mated line may now be unnecessarily reported as a change.
935  *
936  * Much of the complexity of the program comes simply
937  * from trying to minimize core utilization and
938  * maximize the range of doable problems by dynamically
939  * allocating what is needed and reusing what is not.
940  * The core requirements for problems larger than somewhat
941  * are (in words) 2*length(file0) + length(file1) +
942  * 3*(number of k-candidates installed),  typically about
943  * 6n words for files of length n.
944  */
945 static unsigned diffreg(char *ofile1, char *ofile2, int flags)
946 {
947         char *file1 = ofile1;
948         char *file2 = ofile2;
949         FILE *f1 = stdin, *f2 = stdin;
950         unsigned rval;
951         int i;
952
953         anychange = 0;
954         context_vec_ptr = context_vec_start - 1;
955
956         if (S_ISDIR(stb1.st_mode) != S_ISDIR(stb2.st_mode))
957                 return (S_ISDIR(stb1.st_mode) ? D_MISMATCH1 : D_MISMATCH2);
958
959         rval = D_SAME;
960
961         if (LONE_DASH(file1) && LONE_DASH(file2))
962                 goto closem;
963
964         if (flags & D_EMPTY1)
965                 f1 = xfopen(bb_dev_null, "r");
966         else if (NOT_LONE_DASH(file1))
967                 f1 = xfopen(file1, "r");
968         if (flags & D_EMPTY2)
969                 f2 = xfopen(bb_dev_null, "r");
970         else if (NOT_LONE_DASH(file2))
971                 f2 = xfopen(file2, "r");
972
973 /* We can't diff non-seekable stream - we use rewind(), fseek().
974  * This can be fixed (volunteers?).
975  * Meanwhile we should check it here by stat'ing input fds,
976  * but I am lazy and check that in main() instead.
977  * Check in main won't catch "diffing fifos buried in subdirectories"
978  * failure scenario - not very likely in real life... */
979
980         i = files_differ(f1, f2, flags);
981         if (i == 0)
982                 goto closem;
983         else if (i != 1) {      /* 1 == ok */
984                 /* error */
985                 status |= 2;
986                 goto closem;
987         }
988
989         if (!asciifile(f1) || !asciifile(f2)) {
990                 rval = D_BINARY;
991                 status |= 1;
992                 goto closem;
993         }
994
995         prepare(0, f1 /*, stb1.st_size*/);
996         prepare(1, f2 /*, stb2.st_size*/);
997         prune();
998         sort(sfile[0], slen[0]);
999         sort(sfile[1], slen[1]);
1000
1001         member = (int *) file[1];
1002         equiv(sfile[0], slen[0], sfile[1], slen[1], member);
1003         member = xrealloc(member, (slen[1] + 2) * sizeof(int));
1004
1005         class = (int *) file[0];
1006         unsort(sfile[0], slen[0], class);
1007         class = xrealloc(class, (slen[0] + 2) * sizeof(int));
1008
1009         klist = xmalloc((slen[0] + 2) * sizeof(int));
1010         clen = 0;
1011         clistlen = 100;
1012         clist = xmalloc(clistlen * sizeof(struct cand));
1013         i = stone(class, slen[0], member, klist);
1014         free(member);
1015         free(class);
1016
1017         J = xrealloc(J, (len[0] + 2) * sizeof(int));
1018         unravel(klist[i]);
1019         free(clist);
1020         free(klist);
1021
1022         ixold = xrealloc(ixold, (len[0] + 2) * sizeof(long));
1023         ixnew = xrealloc(ixnew, (len[1] + 2) * sizeof(long));
1024         check(f1, f2);
1025         output(file1, f1, file2, f2);
1026
1027  closem:
1028         if (anychange) {
1029                 status |= 1;
1030                 if (rval == D_SAME)
1031                         rval = D_DIFFER;
1032         }
1033         fclose_if_not_stdin(f1);
1034         fclose_if_not_stdin(f2);
1035         if (file1 != ofile1)
1036                 free(file1);
1037         if (file2 != ofile2)
1038                 free(file2);
1039         return rval;
1040 }
1041
1042
1043 #if ENABLE_FEATURE_DIFF_DIR
1044 static void do_diff(char *dir1, char *path1, char *dir2, char *path2)
1045 {
1046         int flags = D_HEADER;
1047         int val;
1048         char *fullpath1 = NULL; /* if -N */
1049         char *fullpath2 = NULL;
1050
1051         if (path1)
1052                 fullpath1 = concat_path_file(dir1, path1);
1053         if (path2)
1054                 fullpath2 = concat_path_file(dir2, path2);
1055
1056         if (!fullpath1 || stat(fullpath1, &stb1) != 0) {
1057                 flags |= D_EMPTY1;
1058                 memset(&stb1, 0, sizeof(stb1));
1059                 if (path2) {
1060                         free(fullpath1);
1061                         fullpath1 = concat_path_file(dir1, path2);
1062                 }
1063         }
1064         if (!fullpath2 || stat(fullpath2, &stb2) != 0) {
1065                 flags |= D_EMPTY2;
1066                 memset(&stb2, 0, sizeof(stb2));
1067                 stb2.st_mode = stb1.st_mode;
1068                 if (path1) {
1069                         free(fullpath2);
1070                         fullpath2 = concat_path_file(dir2, path1);
1071                 }
1072         }
1073
1074         if (stb1.st_mode == 0)
1075                 stb1.st_mode = stb2.st_mode;
1076
1077         if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
1078                 printf("Common subdirectories: %s and %s\n", fullpath1, fullpath2);
1079                 goto ret;
1080         }
1081
1082         if (!S_ISREG(stb1.st_mode) && !S_ISDIR(stb1.st_mode))
1083                 val = D_SKIPPED1;
1084         else if (!S_ISREG(stb2.st_mode) && !S_ISDIR(stb2.st_mode))
1085                 val = D_SKIPPED2;
1086         else
1087                 val = diffreg(fullpath1, fullpath2, flags);
1088
1089         print_status(val, fullpath1, fullpath2, NULL);
1090  ret:
1091         free(fullpath1);
1092         free(fullpath2);
1093 }
1094 #endif
1095
1096
1097 #if ENABLE_FEATURE_DIFF_DIR
1098 /* This function adds a filename to dl, the directory listing. */
1099 static int add_to_dirlist(const char *filename,
1100                 struct stat ATTRIBUTE_UNUSED * sb, void *userdata,
1101                 int depth ATTRIBUTE_UNUSED)
1102 {
1103         /* +2: with space for eventual trailing NULL */
1104         dl = xrealloc(dl, (dl_count+2) * sizeof(dl[0]));
1105         dl[dl_count] = xstrdup(filename + (int)(ptrdiff_t)userdata);
1106         dl_count++;
1107         return TRUE;
1108 }
1109
1110
1111 /* This returns a sorted directory listing. */
1112 static char **get_dir(char *path)
1113 {
1114         dl_count = 0;
1115         dl = xzalloc(sizeof(dl[0]));
1116
1117         /* If -r has been set, then the recursive_action function will be
1118          * used. Unfortunately, this outputs the root directory along with
1119          * the recursed paths, so use void *userdata to specify the string
1120          * length of the root directory - '(void*)(strlen(path)+)'.
1121          * add_to_dirlist then removes root dir prefix. */
1122
1123         if (option_mask32 & FLAG_r) {
1124                 recursive_action(path, ACTION_RECURSE|ACTION_FOLLOWLINKS,
1125                                         add_to_dirlist, NULL,
1126                                         (void*)(strlen(path)+1), 0);
1127         } else {
1128                 DIR *dp;
1129                 struct dirent *ep;
1130
1131                 dp = warn_opendir(path);
1132                 while ((ep = readdir(dp))) {
1133                         if (!strcmp(ep->d_name, "..") || LONE_CHAR(ep->d_name, '.'))
1134                                 continue;
1135                         add_to_dirlist(ep->d_name, NULL, (void*)(int)0, 0);
1136                 }
1137                 closedir(dp);
1138         }
1139
1140         /* Sort dl alphabetically. */
1141         qsort_string_vector(dl, dl_count);
1142
1143         dl[dl_count] = NULL;
1144         return dl;
1145 }
1146
1147
1148 static void diffdir(char *p1, char *p2)
1149 {
1150         char **dirlist1, **dirlist2;
1151         char *dp1, *dp2;
1152         int pos;
1153
1154         /* Check for trailing slashes. */
1155         dp1 = last_char_is(p1, '/');
1156         if (dp1 != NULL)
1157                 *dp1 = '\0';
1158         dp2 = last_char_is(p2, '/');
1159         if (dp2 != NULL)
1160                 *dp2 = '\0';
1161
1162         /* Get directory listings for p1 and p2. */
1163
1164         dirlist1 = get_dir(p1);
1165         dirlist2 = get_dir(p2);
1166
1167         /* If -S was set, find the starting point. */
1168         if (start) {
1169                 while (*dirlist1 != NULL && strcmp(*dirlist1, start) < 0)
1170                         dirlist1++;
1171                 while (*dirlist2 != NULL && strcmp(*dirlist2, start) < 0)
1172                         dirlist2++;
1173                 if ((*dirlist1 == NULL) || (*dirlist2 == NULL))
1174                         bb_error_msg(bb_msg_invalid_arg, "NULL", "-S");
1175         }
1176
1177         /* Now that both dirlist1 and dirlist2 contain sorted directory
1178          * listings, we can start to go through dirlist1. If both listings
1179          * contain the same file, then do a normal diff. Otherwise, behaviour
1180          * is determined by whether the -N flag is set. */
1181         while (*dirlist1 != NULL || *dirlist2 != NULL) {
1182                 dp1 = *dirlist1;
1183                 dp2 = *dirlist2;
1184                 pos = dp1 == NULL ? 1 : dp2 == NULL ? -1 : strcmp(dp1, dp2);
1185                 if (pos == 0) {
1186                         do_diff(p1, dp1, p2, dp2);
1187                         dirlist1++;
1188                         dirlist2++;
1189                 } else if (pos < 0) {
1190                         if (option_mask32 & FLAG_N)
1191                                 do_diff(p1, dp1, p2, NULL);
1192                         else
1193                                 print_only(p1, strlen(p1) + 1, dp1);
1194                         dirlist1++;
1195                 } else {
1196                         if (option_mask32 & FLAG_N)
1197                                 do_diff(p1, NULL, p2, dp2);
1198                         else
1199                                 print_only(p2, strlen(p2) + 1, dp2);
1200                         dirlist2++;
1201                 }
1202         }
1203 }
1204 #endif
1205
1206
1207 int diff_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1208 int diff_main(int argc ATTRIBUTE_UNUSED, char **argv)
1209 {
1210         bool gotstdin = 0;
1211         char *f1, *f2;
1212         llist_t *L_arg = NULL;
1213
1214         INIT_G();
1215
1216         /* exactly 2 params; collect multiple -L <label>; -U N */
1217         opt_complementary = "=2:L::U+";
1218         getopt32(argv, "abdiL:NqrsS:tTU:wu"
1219                         "p" /* ignored (for compatibility) */,
1220                         &L_arg, &start, &context);
1221         /*argc -= optind;*/
1222         argv += optind;
1223         while (L_arg) {
1224                 if (label1 && label2)
1225                         bb_show_usage();
1226                 if (!label1)
1227                         label1 = L_arg->data;
1228                 else { /* then label2 is NULL */
1229                         label2 = label1;
1230                         label1 = L_arg->data;
1231                 }
1232                 /* we leak L_arg here... */
1233                 L_arg = L_arg->link;
1234         }
1235
1236         /*
1237          * Do sanity checks, fill in stb1 and stb2 and call the appropriate
1238          * driver routine.  Both drivers use the contents of stb1 and stb2.
1239          */
1240
1241         f1 = argv[0];
1242         f2 = argv[1];
1243         if (LONE_DASH(f1)) {
1244                 fstat(STDIN_FILENO, &stb1);
1245                 gotstdin = 1;
1246         } else
1247                 xstat(f1, &stb1);
1248         if (LONE_DASH(f2)) {
1249                 fstat(STDIN_FILENO, &stb2);
1250                 gotstdin = 1;
1251         } else
1252                 xstat(f2, &stb2);
1253         if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode)))
1254                 bb_error_msg_and_die("can't compare - to a directory");
1255         if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
1256 #if ENABLE_FEATURE_DIFF_DIR
1257                 diffdir(f1, f2);
1258 #else
1259                 bb_error_msg_and_die("directory comparison not supported");
1260 #endif
1261         } else {
1262                 if (S_ISDIR(stb1.st_mode)) {
1263                         f1 = concat_path_file(f1, f2);
1264                         xstat(f1, &stb1);
1265                 }
1266                 if (S_ISDIR(stb2.st_mode)) {
1267                         f2 = concat_path_file(f2, f1);
1268                         xstat(f2, &stb2);
1269                 }
1270 /* XXX: FIXME: */
1271 /* We can't diff e.g. stdin supplied by a pipe - we use rewind(), fseek().
1272  * This can be fixed (volunteers?) */
1273                 if (!S_ISREG(stb1.st_mode) || !S_ISREG(stb2.st_mode))
1274                         bb_error_msg_and_die("can't diff non-seekable stream");
1275                 print_status(diffreg(f1, f2, 0), f1, f2, NULL);
1276         }
1277         return status;
1278 }