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