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