1 /* Analyze file differences for GNU DIFF.
2 Copyright (C) 1988, 1989, 1992, 1993, 1997 Free Software Foundation, Inc.
4 This file is part of GNU DIFF.
6 GNU DIFF is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU DIFF is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
18 /* The basic algorithm is described in:
19 "An O(ND) Difference Algorithm and its Variations", Eugene Myers,
20 Algorithmica Vol. 1 No. 2, 1986, pp. 251-266;
21 see especially section 4.2, which describes the variation used below.
22 Unless the --minimal option is specified, this code uses the TOO_EXPENSIVE
23 heuristic, by Paul Eggert, to limit the cost to O(N**1.5 log N)
24 at the price of producing suboptimal output for large inputs with
27 The basic algorithm was independently discovered as described in:
28 "Algorithms for Approximate String Matching", E. Ukkonen,
29 Information and Control Vol. 64, 1985, pp. 100-118. */
34 extern int no_discards;
36 static int *xvec, *yvec; /* Vectors being compared. */
37 static int *fdiag; /* Vector, indexed by diagonal, containing
38 1 + the X coordinate of the point furthest
39 along the given diagonal in the forward
40 search of the edit matrix. */
41 static int *bdiag; /* Vector, indexed by diagonal, containing
42 the X coordinate of the point furthest
43 along the given diagonal in the backward
44 search of the edit matrix. */
45 static int too_expensive; /* Edit scripts longer than this are too
46 expensive to compute. */
48 #define SNAKE_LIMIT 20 /* Snakes bigger than this are considered `big'. */
52 int xmid, ymid; /* Midpoints of this partition. */
53 int lo_minimal; /* Nonzero if low half will be analyzed minimally. */
54 int hi_minimal; /* Likewise for high half. */
57 static int diag PARAMS((int, int, int, int, int, struct partition *));
58 static struct change *add_change PARAMS((int, int, int, int, struct change *));
59 static struct change *build_reverse_script PARAMS((struct file_data const[]));
60 static struct change *build_script PARAMS((struct file_data const[]));
61 static void briefly_report PARAMS((int, struct file_data const[]));
62 static void compareseq PARAMS((int, int, int, int, int));
63 static void discard_confusing_lines PARAMS((struct file_data[]));
64 static void shift_boundaries PARAMS((struct file_data[]));
66 /* Find the midpoint of the shortest edit script for a specified
67 portion of the two files.
69 Scan from the beginnings of the files, and simultaneously from the ends,
70 doing a breadth-first search through the space of edit-sequence.
71 When the two searches meet, we have found the midpoint of the shortest
74 If MINIMAL is nonzero, find the minimal edit script regardless
75 of expense. Otherwise, if the search is too expensive, use
76 heuristics to stop the search and report a suboptimal answer.
78 Set PART->(XMID,YMID) to the midpoint (XMID,YMID). The diagonal number
79 XMID - YMID equals the number of inserted lines minus the number
80 of deleted lines (counting only lines before the midpoint).
81 Return the approximate edit cost; this is the total number of
82 lines inserted or deleted (counting only lines before the midpoint),
83 unless a heuristic is used to terminate the search prematurely.
85 Set PART->LEFT_MINIMAL to nonzero iff the minimal edit script for the
86 left half of the partition is known; similarly for PART->RIGHT_MINIMAL.
88 This function assumes that the first lines of the specified portions
89 of the two files do not match, and likewise that the last lines do not
90 match. The caller must trim matching lines from the beginning and end
91 of the portions it is going to specify.
93 If we return the "wrong" partitions,
94 the worst this can do is cause suboptimal diff output.
95 It cannot cause incorrect diff output. */
98 diag (xoff, xlim, yoff, ylim, minimal, part)
99 int xoff, xlim, yoff, ylim, minimal;
100 struct partition *part;
102 int *const fd = fdiag; /* Give the compiler a chance. */
103 int *const bd = bdiag; /* Additional help for the compiler. */
104 int const *const xv = xvec; /* Still more help for the compiler. */
105 int const *const yv = yvec; /* And more and more . . . */
106 int const dmin = xoff - ylim; /* Minimum valid diagonal. */
107 int const dmax = xlim - yoff; /* Maximum valid diagonal. */
108 int const fmid = xoff - yoff; /* Center diagonal of top-down search. */
109 int const bmid = xlim - ylim; /* Center diagonal of bottom-up search. */
110 int fmin = fmid, fmax = fmid; /* Limits of top-down search. */
111 int bmin = bmid, bmax = bmid; /* Limits of bottom-up search. */
113 int odd = (fmid - bmid) & 1; /* True if southeast corner is on an odd
114 diagonal with respect to the northwest. */
121 int d; /* Active diagonal. */
124 /* Extend the top-down search by an edit step in each diagonal. */
125 fmin > dmin ? fd[--fmin - 1] = -1 : ++fmin;
126 fmax < dmax ? fd[++fmax + 1] = -1 : --fmax;
127 for (d = fmax; d >= fmin; d -= 2)
129 int x, y, oldx, tlo = fd[d - 1], thi = fd[d + 1];
137 while (x < xlim && y < ylim && xv[x] == yv[y])
139 if (x - oldx > SNAKE_LIMIT)
142 if (odd && bmin <= d && d <= bmax && bd[d] <= x)
146 part->lo_minimal = part->hi_minimal = 1;
151 /* Similarly extend the bottom-up search. */
152 bmin > dmin ? bd[--bmin - 1] = INT_MAX : ++bmin;
153 bmax < dmax ? bd[++bmax + 1] = INT_MAX : --bmax;
154 for (d = bmax; d >= bmin; d -= 2)
156 int x, y, oldx, tlo = bd[d - 1], thi = bd[d + 1];
164 while (x > xoff && y > yoff && xv[x - 1] == yv[y - 1])
166 if (oldx - x > SNAKE_LIMIT)
169 if (!odd && fmin <= d && d <= fmax && x <= fd[d])
173 part->lo_minimal = part->hi_minimal = 1;
181 /* Heuristic: check occasionally for a diagonal that has made
182 lots of progress compared with the edit distance.
183 If we have any such, find the one that has made the most
184 progress and return it as if it had succeeded.
186 With this heuristic, for files with a constant small density
187 of changes, the algorithm is linear in the file size. */
189 if (c > 200 && big_snake && heuristic)
194 for (d = fmax; d >= fmin; d -= 2)
199 int v = (x - xoff) * 2 - dd;
200 if (v > 12 * (c + (dd < 0 ? -dd : dd)))
203 && xoff + SNAKE_LIMIT <= x && x < xlim
204 && yoff + SNAKE_LIMIT <= y && y < ylim)
206 /* We have a good enough best diagonal;
207 now insist that it end with a significant snake. */
210 for (k = 1; xv[x - k] == yv[y - k]; k++)
211 if (k == SNAKE_LIMIT)
223 part->lo_minimal = 1;
224 part->hi_minimal = 0;
229 for (d = bmax; d >= bmin; d -= 2)
234 int v = (xlim - x) * 2 + dd;
235 if (v > 12 * (c + (dd < 0 ? -dd : dd)))
238 && xoff < x && x <= xlim - SNAKE_LIMIT
239 && yoff < y && y <= ylim - SNAKE_LIMIT)
241 /* We have a good enough best diagonal;
242 now insist that it end with a significant snake. */
245 for (k = 0; xv[x + k] == yv[y + k]; k++)
246 if (k == SNAKE_LIMIT - 1)
258 part->lo_minimal = 0;
259 part->hi_minimal = 1;
264 /* Heuristic: if we've gone well beyond the call of duty,
265 give up and report halfway between our best results so far. */
266 if (c >= too_expensive)
271 fxbest = bxbest = 0; /* Pacify `gcc -Wall'. */
273 /* Find forward diagonal that maximizes X + Y. */
275 for (d = fmax; d >= fmin; d -= 2)
277 int x = min (fd[d], xlim);
280 x = ylim + d, y = ylim;
288 /* Find backward diagonal that minimizes X + Y. */
290 for (d = bmax; d >= bmin; d -= 2)
292 int x = max (xoff, bd[d]);
295 x = yoff + d, y = yoff;
303 /* Use the better of the two diagonals. */
304 if ((xlim + ylim) - bxybest < fxybest - (xoff + yoff))
307 part->ymid = fxybest - fxbest;
308 part->lo_minimal = 1;
309 part->hi_minimal = 0;
314 part->ymid = bxybest - bxbest;
315 part->lo_minimal = 0;
316 part->hi_minimal = 1;
323 /* Compare in detail contiguous subsequences of the two files
324 which are known, as a whole, to match each other.
326 The results are recorded in the vectors files[N].changed_flag, by
327 storing a 1 in the element for each line that is an insertion or deletion.
329 The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1.
331 Note that XLIM, YLIM are exclusive bounds.
332 All line numbers are origin-0 and discarded lines are not counted.
334 If MINIMAL is nonzero, find a minimal difference no matter how
338 compareseq (xoff, xlim, yoff, ylim, minimal)
339 int xoff, xlim, yoff, ylim, minimal;
341 int * const xv = xvec; /* Help the compiler. */
342 int * const yv = yvec;
344 /* Slide down the bottom initial diagonal. */
345 while (xoff < xlim && yoff < ylim && xv[xoff] == yv[yoff])
347 /* Slide up the top initial diagonal. */
348 while (xlim > xoff && ylim > yoff && xv[xlim - 1] == yv[ylim - 1])
351 /* Handle simple cases. */
354 files[1].changed_flag[files[1].realindexes[yoff++]] = 1;
355 else if (yoff == ylim)
357 files[0].changed_flag[files[0].realindexes[xoff++]] = 1;
361 struct partition part;
363 /* Find a point of correspondence in the middle of the files. */
365 c = diag (xoff, xlim, yoff, ylim, minimal, &part);
369 /* This should be impossible, because it implies that
370 one of the two subsequences is empty,
371 and that case was handled above without calling `diag'.
372 Let's verify that this is true. */
375 /* The two subsequences differ by a single insert or delete;
376 record it and we are done. */
377 if (part.xmid - part.ymid < xoff - yoff)
378 files[1].changed_flag[files[1].realindexes[part.ymid - 1]] = 1;
380 files[0].changed_flag[files[0].realindexes[part.xmid]] = 1;
385 /* Use the partitions to split this problem into subproblems. */
386 compareseq (xoff, part.xmid, yoff, part.ymid, part.lo_minimal);
387 compareseq (part.xmid, xlim, part.ymid, ylim, part.hi_minimal);
392 /* Discard lines from one file that have no matches in the other file.
394 A line which is discarded will not be considered by the actual
395 comparison algorithm; it will be as if that line were not in the file.
396 The file's `realindexes' table maps virtual line numbers
397 (which don't count the discarded lines) into real line numbers;
398 this is how the actual comparison algorithm produces results
399 that are comprehensible when the discarded lines are counted.
401 When we discard a line, we also mark it as a deletion or insertion
402 so that it will be printed in the output. */
405 discard_confusing_lines (filevec)
406 struct file_data filevec[];
413 /* Allocate our results. */
414 p = (int *) xmalloc ((filevec[0].buffered_lines + filevec[1].buffered_lines)
415 * (2 * sizeof (int)));
416 for (f = 0; f < 2; f++)
418 filevec[f].undiscarded = p; p += filevec[f].buffered_lines;
419 filevec[f].realindexes = p; p += filevec[f].buffered_lines;
422 /* Set up equiv_count[F][I] as the number of lines in file F
423 that fall in equivalence class I. */
425 p = (int *) xmalloc (filevec[0].equiv_max * (2 * sizeof (int)));
427 equiv_count[1] = p + filevec[0].equiv_max;
428 bzero (p, filevec[0].equiv_max * (2 * sizeof (int)));
430 for (i = 0; i < filevec[0].buffered_lines; ++i)
431 ++equiv_count[0][filevec[0].equivs[i]];
432 for (i = 0; i < filevec[1].buffered_lines; ++i)
433 ++equiv_count[1][filevec[1].equivs[i]];
435 /* Set up tables of which lines are going to be discarded. */
437 discarded[0] = xmalloc (sizeof (char)
438 * (filevec[0].buffered_lines
439 + filevec[1].buffered_lines));
440 discarded[1] = discarded[0] + filevec[0].buffered_lines;
441 bzero (discarded[0], sizeof (char) * (filevec[0].buffered_lines
442 + filevec[1].buffered_lines));
444 /* Mark to be discarded each line that matches no line of the other file.
445 If a line matches many lines, mark it as provisionally discardable. */
447 for (f = 0; f < 2; f++)
449 unsigned int end = filevec[f].buffered_lines;
450 char *discards = discarded[f];
451 int *counts = equiv_count[1 - f];
452 int *equivs = filevec[f].equivs;
453 unsigned int many = 5;
454 unsigned int tem = end / 64;
456 /* Multiply MANY by approximate square root of number of lines.
457 That is the threshold for provisionally discardable lines. */
458 while ((tem = tem >> 2) > 0)
461 for (i = 0; i < end; i++)
466 nmatch = counts[equivs[i]];
469 else if (nmatch > many)
474 /* Don't really discard the provisional lines except when they occur
475 in a run of discardables, with nonprovisionals at the beginning
478 for (f = 0; f < 2; f++)
480 unsigned int end = filevec[f].buffered_lines;
481 register char *discards = discarded[f];
483 for (i = 0; i < end; i++)
485 /* Cancel provisional discards not in middle of run of discards. */
486 if (discards[i] == 2)
488 else if (discards[i] != 0)
490 /* We have found a nonprovisional discard. */
493 unsigned int provisional = 0;
495 /* Find end of this run of discardable lines.
496 Count how many are provisionally discardable. */
497 for (j = i; j < end; j++)
499 if (discards[j] == 0)
501 if (discards[j] == 2)
505 /* Cancel provisional discards at end, and shrink the run. */
506 while (j > i && discards[j - 1] == 2)
507 discards[--j] = 0, --provisional;
509 /* Now we have the length of a run of discardable lines
510 whose first and last are not provisional. */
513 /* If 1/4 of the lines in the run are provisional,
514 cancel discarding of all provisional lines in the run. */
515 if (provisional * 4 > length)
518 if (discards[--j] == 2)
523 register unsigned int consec;
524 unsigned int minimum = 1;
525 unsigned int tem = length / 4;
527 /* MINIMUM is approximate square root of LENGTH/4.
528 A subrun of two or more provisionals can stand
529 when LENGTH is at least 16.
530 A subrun of 4 or more can stand when LENGTH >= 64. */
531 while ((tem = tem >> 2) > 0)
535 /* Cancel any subrun of MINIMUM or more provisionals
536 within the larger run. */
537 for (j = 0, consec = 0; j < length; j++)
538 if (discards[i + j] != 2)
540 else if (minimum == ++consec)
541 /* Back up to start of subrun, to cancel it all. */
543 else if (minimum < consec)
546 /* Scan from beginning of run
547 until we find 3 or more nonprovisionals in a row
548 or until the first nonprovisional at least 8 lines in.
549 Until that point, cancel any provisionals. */
550 for (j = 0, consec = 0; j < length; j++)
552 if (j >= 8 && discards[i + j] == 1)
554 if (discards[i + j] == 2)
555 consec = 0, discards[i + j] = 0;
556 else if (discards[i + j] == 0)
564 /* I advances to the last line of the run. */
567 /* Same thing, from end. */
568 for (j = 0, consec = 0; j < length; j++)
570 if (j >= 8 && discards[i - j] == 1)
572 if (discards[i - j] == 2)
573 consec = 0, discards[i - j] = 0;
574 else if (discards[i - j] == 0)
586 /* Actually discard the lines. */
587 for (f = 0; f < 2; f++)
589 char *discards = discarded[f];
590 unsigned int end = filevec[f].buffered_lines;
592 for (i = 0; i < end; ++i)
593 if (no_discards || discards[i] == 0)
595 filevec[f].undiscarded[j] = filevec[f].equivs[i];
596 filevec[f].realindexes[j++] = i;
599 filevec[f].changed_flag[i] = 1;
600 filevec[f].nondiscarded_lines = j;
604 free (equiv_count[0]);
607 /* Adjust inserts/deletes of identical lines to join changes
610 We do something when a run of changed lines include a
611 line at one end and have an excluded, identical line at the other.
612 We are free to choose which identical line is included.
613 `compareseq' usually chooses the one at the beginning,
614 but usually it is cleaner to consider the following identical line
615 to be the "change". */
620 shift_boundaries (filevec)
621 struct file_data filevec[];
628 for (f = 0; f < 2; f++)
630 char *changed = filevec[f].changed_flag;
631 char const *other_changed = filevec[1-f].changed_flag;
632 int const *equivs = filevec[f].equivs;
635 int i_end = filevec[f].buffered_lines;
639 int runlength, start, corresponding;
641 /* Scan forwards to find beginning of another run of changes.
642 Also keep track of the corresponding point in the other file. */
644 while (i < i_end && changed[i] == 0)
646 while (other_changed[j++])
656 /* Find the end of this run of changes. */
660 while (other_changed[j])
665 /* Record the length of this run of changes, so that
666 we can later determine whether the run has grown. */
667 runlength = i - start;
669 /* Move the changed region back, so long as the
670 previous unchanged line matches the last changed one.
671 This merges with previous changed regions. */
673 while (start && equivs[start - 1] == equivs[i - 1])
675 changed[--start] = 1;
677 while (changed[start - 1])
679 while (other_changed[--j])
683 /* Set CORRESPONDING to the end of the changed run, at the last
684 point where it corresponds to a changed run in the other file.
685 CORRESPONDING == I_END means no such point has been found. */
686 corresponding = other_changed[j - 1] ? i : i_end;
688 /* Move the changed region forward, so long as the
689 first changed line matches the following unchanged one.
690 This merges with following changed regions.
691 Do this second, so that if there are no merges,
692 the changed region is moved forward as far as possible. */
694 while (i != i_end && equivs[start] == equivs[i])
696 changed[start++] = 0;
700 while (other_changed[++j])
704 while (runlength != i - start);
706 /* If possible, move the fully-merged run of changes
707 back to a corresponding run in the other file. */
709 while (corresponding < i)
711 changed[--start] = 1;
713 while (other_changed[--j])
720 /* Cons an additional entry onto the front of an edit script OLD.
721 LINE0 and LINE1 are the first affected lines in the two files (origin 0).
722 DELETED is the number of lines deleted here from file 0.
723 INSERTED is the number of lines inserted here in file 1.
725 If DELETED is 0 then LINE0 is the number of the line before
726 which the insertion was done; vice versa for INSERTED and LINE1. */
728 static struct change *
729 add_change (line0, line1, deleted, inserted, old)
730 int line0, line1, deleted, inserted;
733 struct change *new = (struct change *) xmalloc (sizeof (struct change));
737 new->inserted = inserted;
738 new->deleted = deleted;
743 /* Scan the tables of which lines are inserted and deleted,
744 producing an edit script in reverse order. */
746 static struct change *
747 build_reverse_script (filevec)
748 struct file_data const filevec[];
750 struct change *script = 0;
751 char *changed0 = filevec[0].changed_flag;
752 char *changed1 = filevec[1].changed_flag;
753 int len0 = filevec[0].buffered_lines;
754 int len1 = filevec[1].buffered_lines;
756 /* Note that changedN[len0] does exist, and contains 0. */
760 while (i0 < len0 || i1 < len1)
762 if (changed0[i0] || changed1[i1])
764 int line0 = i0, line1 = i1;
766 /* Find # lines changed here in each file. */
767 while (changed0[i0]) ++i0;
768 while (changed1[i1]) ++i1;
770 /* Record this change. */
771 script = add_change (line0, line1, i0 - line0, i1 - line1, script);
774 /* We have reached lines in the two files that match each other. */
781 /* Scan the tables of which lines are inserted and deleted,
782 producing an edit script in forward order. */
784 static struct change *
785 build_script (filevec)
786 struct file_data const filevec[];
788 struct change *script = 0;
789 char *changed0 = filevec[0].changed_flag;
790 char *changed1 = filevec[1].changed_flag;
791 int i0 = filevec[0].buffered_lines, i1 = filevec[1].buffered_lines;
793 /* Note that changedN[-1] does exist, and contains 0. */
795 while (i0 >= 0 || i1 >= 0)
797 if (changed0[i0 - 1] || changed1[i1 - 1])
799 int line0 = i0, line1 = i1;
801 /* Find # lines changed here in each file. */
802 while (changed0[i0 - 1]) --i0;
803 while (changed1[i1 - 1]) --i1;
805 /* Record this change. */
806 script = add_change (i0, i1, line0 - i0, line1 - i1, script);
809 /* We have reached lines in the two files that match each other. */
816 /* If CHANGES, briefly report that two files differed. */
818 briefly_report (changes, filevec)
820 struct file_data const filevec[];
823 message (no_details_flag ? "Files %s and %s differ\n"
824 : "Binary files %s and %s differ\n",
825 filevec[0].name, filevec[1].name);
828 /* Report the differences of two files. DEPTH is the current directory
831 diff_2_files (filevec, depth)
832 struct file_data filevec[];
837 struct change *e, *p;
838 struct change *script;
842 /* If we have detected that either file is binary,
843 compare the two files as binary. This can happen
844 only when the first chunk is read.
845 Also, --brief without any --ignore-* options means
846 we can speed things up by treating the files as binary. */
848 if (read_files (filevec, no_details_flag & ~ignore_some_changes))
850 /* Files with different lengths must be different. */
851 if (filevec[0].stat.st_size != filevec[1].stat.st_size
852 && (filevec[0].desc < 0 || S_ISREG (filevec[0].stat.st_mode))
853 && (filevec[1].desc < 0 || S_ISREG (filevec[1].stat.st_mode)))
856 /* Standard input equals itself. */
857 else if (filevec[0].desc == filevec[1].desc)
861 /* Scan both files, a buffer at a time, looking for a difference. */
863 /* Allocate same-sized buffers for both files. */
864 size_t buffer_size = buffer_lcm (STAT_BLOCKSIZE (filevec[0].stat),
865 STAT_BLOCKSIZE (filevec[1].stat));
866 for (i = 0; i < 2; i++)
867 filevec[i].buffer = xrealloc (filevec[i].buffer, buffer_size);
869 for (;; filevec[0].buffered_chars = filevec[1].buffered_chars = 0)
871 /* Read a buffer's worth from both files. */
872 for (i = 0; i < 2; i++)
873 if (0 <= filevec[i].desc)
874 while (filevec[i].buffered_chars != buffer_size)
876 int r = read (filevec[i].desc,
878 + filevec[i].buffered_chars,
879 buffer_size - filevec[i].buffered_chars);
883 pfatal_with_name (filevec[i].name);
884 filevec[i].buffered_chars += r;
887 /* If the buffers differ, the files differ. */
888 if (filevec[0].buffered_chars != filevec[1].buffered_chars
889 || (filevec[0].buffered_chars != 0
890 && memcmp (filevec[0].buffer,
892 filevec[0].buffered_chars) != 0))
898 /* If we reach end of file, the files are the same. */
899 if (filevec[0].buffered_chars != buffer_size)
907 briefly_report (changes, filevec);
911 /* Allocate vectors for the results of comparison:
912 a flag for each line of each file, saying whether that line
913 is an insertion or deletion.
914 Allocate an extra element, always zero, at each end of each vector. */
916 size_t s = filevec[0].buffered_lines + filevec[1].buffered_lines + 4;
917 filevec[0].changed_flag = xmalloc (s);
918 bzero (filevec[0].changed_flag, s);
919 filevec[0].changed_flag++;
920 filevec[1].changed_flag = filevec[0].changed_flag
921 + filevec[0].buffered_lines + 2;
923 /* Some lines are obviously insertions or deletions
924 because they don't match anything. Detect them now, and
925 avoid even thinking about them in the main comparison algorithm. */
927 discard_confusing_lines (filevec);
929 /* Now do the main comparison algorithm, considering just the
930 undiscarded lines. */
932 xvec = filevec[0].undiscarded;
933 yvec = filevec[1].undiscarded;
934 diags = filevec[0].nondiscarded_lines + filevec[1].nondiscarded_lines + 3;
935 fdiag = (int *) xmalloc (diags * (2 * sizeof (int)));
936 bdiag = fdiag + diags;
937 fdiag += filevec[1].nondiscarded_lines + 1;
938 bdiag += filevec[1].nondiscarded_lines + 1;
940 /* Set TOO_EXPENSIVE to be approximate square root of input size,
941 bounded below by 256. */
943 for (i = filevec[0].nondiscarded_lines + filevec[1].nondiscarded_lines;
946 too_expensive = max (256, too_expensive);
948 files[0] = filevec[0];
949 files[1] = filevec[1];
951 compareseq (0, filevec[0].nondiscarded_lines,
952 0, filevec[1].nondiscarded_lines, no_discards);
954 free (fdiag - (filevec[1].nondiscarded_lines + 1));
956 /* Modify the results slightly to make them prettier
957 in cases where that can validly be done. */
959 shift_boundaries (filevec);
961 /* Get the results of comparison in the form of a chain
962 of `struct change's -- an edit script. */
964 if (output_style == OUTPUT_ED)
965 script = build_reverse_script (filevec);
967 script = build_script (filevec);
969 /* Set CHANGES if we had any diffs.
970 If some changes are ignored, we must scan the script to decide. */
971 if (ignore_blank_lines_flag || ignore_regexp_list)
973 struct change *next = script;
976 while (next && changes == 0)
978 struct change *this, *end;
979 int first0, last0, first1, last1, deletes, inserts;
981 /* Find a set of changes that belong together. */
983 end = find_change (next);
985 /* Disconnect them from the rest of the changes, making them
986 a hunk, and remember the rest for next iteration. */
990 /* Determine whether this hunk is really a difference. */
991 analyze_hunk (this, &first0, &last0, &first1, &last1,
994 /* Reconnect the script so it will all be freed properly. */
997 if (deletes || inserts)
1002 changes = (script != 0);
1004 if (no_details_flag)
1005 briefly_report (changes, filevec);
1008 if (changes || ! no_diff_means_no_output)
1010 /* Record info for starting up output,
1011 to be used if and when we have some output to print. */
1012 setup_output (files[0].name, files[1].name, depth);
1014 switch (output_style)
1016 case OUTPUT_CONTEXT:
1017 print_context_script (script, 0);
1020 case OUTPUT_UNIFIED:
1021 print_context_script (script, 1);
1025 print_ed_script (script);
1028 case OUTPUT_FORWARD_ED:
1029 pr_forward_ed_script (script);
1033 print_rcs_script (script);
1037 print_normal_script (script);
1041 print_ifdef_script (script);
1045 print_sdiff_script (script);
1052 free (filevec[0].undiscarded);
1054 free (filevec[0].changed_flag - 1);
1056 for (i = 1; i >= 0; --i)
1057 free (filevec[i].equivs);
1059 for (i = 0; i < 2; ++i)
1060 free (filevec[i].linbuf + filevec[i].linbuf_base);
1062 for (e = script; e; e = p)
1068 if (! ROBUST_OUTPUT_STYLE (output_style))
1069 for (i = 0; i < 2; ++i)
1070 if (filevec[i].missing_newline)
1072 diff_error ("No newline at end of file %s", filevec[i].name, "");
1077 if (filevec[0].buffer != filevec[1].buffer)
1078 free (filevec[0].buffer);
1079 free (filevec[1].buffer);