1 /* Context-format output routines for GNU DIFF.
2 Copyright (C) 1988, 89, 91, 92 Free Software Foundation, Inc.
3
4 This file is part of GNU DIFF.
5
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)
9 any later version.
10
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.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU DIFF; see the file COPYING. If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20 #include "diff.h"
21
22 static void pr_context_hunk ();
23 static void pr_unidiff_hunk ();
24 static struct change *find_hunk ();
25 static void mark_ignorable ();
26 static void find_function ();
27
28 /* Last place find_function started searching from. */
29 static int find_function_last_search;
30
31 /* The value find_function returned when it started searching there. */
32 static int find_function_last_match;
33
34 /* Print a label for a context diff, with a file name and date or a label. */
35
36 static void
print_context_label(mark,inf,label)37 print_context_label (mark, inf, label)
38 const char *mark;
39 struct file_data *inf;
40 const char *label;
41 {
42 if (label)
43 fprintf (outfile, "%s %s\n", mark, label);
44 else if (inf->stat.st_mtime)
45 fprintf (outfile, "%s %s\t%s", mark, inf->name, ctime(&inf->stat.st_mtime));
46 else
47 /* Don't pretend that standard input is ancient. */
48 fprintf (outfile, "%s %s\n", mark, inf->name);
49 }
50
51 /* Print a header for a context diff, with the file names and dates. */
52
53 void
print_context_header(inf,unidiff_flag)54 print_context_header (inf, unidiff_flag)
55 struct file_data *inf;
56 int unidiff_flag;
57 {
58 if (unidiff_flag)
59 {
60 print_context_label ("---", &inf[0], file_label[0]);
61 print_context_label ("+++", &inf[1], file_label[1]);
62 }
63 else
64 {
65 print_context_label ("***", &inf[0], file_label[0]);
66 print_context_label ("---", &inf[1], file_label[1]);
67 }
68 }
69
70 /* Print an edit script in context format. */
71
72 void
print_context_script(script,unidiff_flag)73 print_context_script (script, unidiff_flag)
74 struct change *script;
75 int unidiff_flag;
76 {
77 if (ignore_blank_lines_flag || ignore_regexp_list)
78 mark_ignorable (script);
79 else
80 {
81 struct change *e;
82 for (e = script; e; e = e->link)
83 e->ignore = 0;
84 }
85
86 find_function_last_search = - files[0].prefix_lines;
87 find_function_last_match = find_function_last_search - 1;
88
89 if (unidiff_flag)
90 print_script (script, find_hunk, pr_unidiff_hunk);
91 else
92 print_script (script, find_hunk, pr_context_hunk);
93 }
94
95 /* Print a pair of line numbers with a comma, translated for file FILE.
96 If the second number is not greater, use the first in place of it.
97
98 Args A and B are internal line numbers.
99 We print the translated (real) line numbers. */
100
101 static void
print_context_number_range(file,a,b)102 print_context_number_range (file, a, b)
103 struct file_data *file;
104 int a, b;
105 {
106 int trans_a, trans_b;
107 translate_range (file, a, b, &trans_a, &trans_b);
108
109 /* Note: we can have B < A in the case of a range of no lines.
110 In this case, we should print the line number before the range,
111 which is B. */
112 if (trans_b > trans_a)
113 fprintf (outfile, "%d,%d", trans_a, trans_b);
114 else
115 fprintf (outfile, "%d", trans_b);
116 }
117
118 /* Print a portion of an edit script in context format.
119 HUNK is the beginning of the portion to be printed.
120 The end is marked by a `link' that has been nulled out.
121
122 Prints out lines from both files, and precedes each
123 line with the appropriate flag-character. */
124
125 static void
pr_context_hunk(hunk)126 pr_context_hunk (hunk)
127 struct change *hunk;
128 {
129 int first0, last0, first1, last1, show_from, show_to, i;
130 struct change *next;
131 char *prefix;
132 const char *function;
133 int function_length;
134 FILE *out;
135
136 /* Determine range of line numbers involved in each file. */
137
138 analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to);
139
140 if (!show_from && !show_to)
141 return;
142
143 /* Include a context's width before and after. */
144
145 i = - files[0].prefix_lines;
146 first0 = max (first0 - context, i);
147 first1 = max (first1 - context, i);
148 last0 = min (last0 + context, files[0].valid_lines - 1);
149 last1 = min (last1 + context, files[1].valid_lines - 1);
150
151 /* If desired, find the preceding function definition line in file 0. */
152 function = 0;
153 if (function_regexp_list)
154 find_function (&files[0], first0, &function, &function_length);
155
156 begin_output ();
157 out = outfile;
158
159 /* If we looked for and found a function this is part of,
160 include its name in the header of the diff section. */
161 fprintf (out, "***************");
162
163 if (function)
164 {
165 fprintf (out, " ");
166 fwrite (function, 1, min (function_length - 1, 40), out);
167 }
168
169 fprintf (out, "\n*** ");
170 print_context_number_range (&files[0], first0, last0);
171 fprintf (out, " ****\n");
172
173 if (show_from)
174 {
175 next = hunk;
176
177 for (i = first0; i <= last0; i++)
178 {
179 /* Skip past changes that apply (in file 0)
180 only to lines before line I. */
181
182 while (next && next->line0 + next->deleted <= i)
183 next = next->link;
184
185 /* Compute the marking for line I. */
186
187 prefix = " ";
188 if (next && next->line0 <= i)
189 /* The change NEXT covers this line.
190 If lines were inserted here in file 1, this is "changed".
191 Otherwise it is "deleted". */
192 prefix = (next->inserted > 0 ? "!" : "-");
193
194 print_1_line (prefix, &files[0].linbuf[i]);
195 }
196 }
197
198 fprintf (out, "--- ");
199 print_context_number_range (&files[1], first1, last1);
200 fprintf (out, " ----\n");
201
202 if (show_to)
203 {
204 next = hunk;
205
206 for (i = first1; i <= last1; i++)
207 {
208 /* Skip past changes that apply (in file 1)
209 only to lines before line I. */
210
211 while (next && next->line1 + next->inserted <= i)
212 next = next->link;
213
214 /* Compute the marking for line I. */
215
216 prefix = " ";
217 if (next && next->line1 <= i)
218 /* The change NEXT covers this line.
219 If lines were deleted here in file 0, this is "changed".
220 Otherwise it is "inserted". */
221 prefix = (next->deleted > 0 ? "!" : "+");
222
223 print_1_line (prefix, &files[1].linbuf[i]);
224 }
225 }
226 }
227
228 /* Print a pair of line numbers with a comma, translated for file FILE.
229 If the second number is smaller, use the first in place of it.
230 If the numbers are equal, print just one number.
231
232 Args A and B are internal line numbers.
233 We print the translated (real) line numbers. */
234
235 static void
print_unidiff_number_range(file,a,b)236 print_unidiff_number_range (file, a, b)
237 struct file_data *file;
238 int a, b;
239 {
240 int trans_a, trans_b;
241 translate_range (file, a, b, &trans_a, &trans_b);
242
243 /* Note: we can have B < A in the case of a range of no lines.
244 In this case, we should print the line number before the range,
245 which is B. */
246 if (trans_b <= trans_a)
247 fprintf (outfile, trans_b == trans_a ? "%d" : "%d,0", trans_b);
248 else
249 fprintf (outfile, "%d,%d", trans_a, trans_b - trans_a + 1);
250 }
251
252 /* Print a portion of an edit script in unidiff format.
253 HUNK is the beginning of the portion to be printed.
254 The end is marked by a `link' that has been nulled out.
255
256 Prints out lines from both files, and precedes each
257 line with the appropriate flag-character. */
258
259 static void
pr_unidiff_hunk(hunk)260 pr_unidiff_hunk (hunk)
261 struct change *hunk;
262 {
263 int first0, last0, first1, last1, show_from, show_to, i, j, k;
264 struct change *next;
265 char *function;
266 int function_length;
267 FILE *out;
268
269 /* Determine range of line numbers involved in each file. */
270
271 analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to);
272
273 if (!show_from && !show_to)
274 return;
275
276 /* Include a context's width before and after. */
277
278 i = - files[0].prefix_lines;
279 first0 = max (first0 - context, i);
280 first1 = max (first1 - context, i);
281 last0 = min (last0 + context, files[0].valid_lines - 1);
282 last1 = min (last1 + context, files[1].valid_lines - 1);
283
284 /* If desired, find the preceding function definition line in file 0. */
285 function = 0;
286 if (function_regexp_list)
287 find_function (&files[0], first0, &function, &function_length);
288
289 begin_output ();
290 out = outfile;
291
292 fprintf (out, "@@ -");
293 print_unidiff_number_range (&files[0], first0, last0);
294 fprintf (out, " +");
295 print_unidiff_number_range (&files[1], first1, last1);
296 fprintf (out, " @@");
297
298 /* If we looked for and found a function this is part of,
299 include its name in the header of the diff section. */
300
301 if (function)
302 {
303 putc (' ', out);
304 fwrite (function, 1, min (function_length - 1, 40), out);
305 }
306 putc ('\n', out);
307
308 next = hunk;
309 i = first0;
310 j = first1;
311
312 while (i <= last0 || j <= last1)
313 {
314
315 /* If the line isn't a difference, output the context from file 0. */
316
317 if (!next || i < next->line0)
318 {
319 putc (tab_align_flag ? '\t' : ' ', out);
320 print_1_line ((char *)0, &files[0].linbuf[i++]);
321 j++;
322 }
323 else
324 {
325 /* For each difference, first output the deleted part. */
326
327 k = next->deleted;
328 while (k--)
329 {
330 putc ('-', out);
331 if (tab_align_flag)
332 putc ('\t', out);
333 print_1_line ((char *)0, &files[0].linbuf[i++]);
334 }
335
336 /* Then output the inserted part. */
337
338 k = next->inserted;
339 while (k--)
340 {
341 putc ('+', out);
342 if (tab_align_flag)
343 putc ('\t', out);
344 print_1_line ((char *)0, &files[1].linbuf[j++]);
345 }
346
347 /* We're done with this hunk, so on to the next! */
348
349 next = next->link;
350 }
351 }
352 }
353
354 /* Scan a (forward-ordered) edit script for the first place that more than
355 2*CONTEXT unchanged lines appear, and return a pointer
356 to the `struct change' for the last change before those lines. */
357
358 static struct change *
find_hunk(start)359 find_hunk (start)
360 struct change *start;
361 {
362 struct change *prev;
363 int top0, top1;
364 int thresh;
365
366 do
367 {
368 /* Compute number of first line in each file beyond this changed. */
369 top0 = start->line0 + start->deleted;
370 top1 = start->line1 + start->inserted;
371 prev = start;
372 start = start->link;
373 /* Threshold distance is 2*CONTEXT between two non-ignorable changes,
374 but only CONTEXT if one is ignorable. */
375 thresh = ((prev->ignore || (start && start->ignore))
376 ? context
377 : 2 * context + 1);
378 /* It is not supposed to matter which file we check in the end-test.
379 If it would matter, crash. */
380 if (start && start->line0 - top0 != start->line1 - top1)
381 abort ();
382 } while (start
383 /* Keep going if less than THRESH lines
384 elapse before the affected line. */
385 && start->line0 < top0 + thresh);
386
387 return prev;
388 }
389
390 /* Set the `ignore' flag properly in each change in SCRIPT.
391 It should be 1 if all the lines inserted or deleted in that change
392 are ignorable lines. */
393
394 static void
mark_ignorable(script)395 mark_ignorable (script)
396 struct change *script;
397 {
398 while (script)
399 {
400 struct change *next = script->link;
401 int first0, last0, first1, last1, deletes, inserts;
402
403 /* Turn this change into a hunk: detach it from the others. */
404 script->link = 0;
405
406 /* Determine whether this change is ignorable. */
407 analyze_hunk (script, &first0, &last0, &first1, &last1, &deletes, &inserts);
408 /* Reconnect the chain as before. */
409 script->link = next;
410
411 /* If the change is ignorable, mark it. */
412 script->ignore = (!deletes && !inserts);
413
414 /* Advance to the following change. */
415 script = next;
416 }
417 }
418
419 /* Find the last function-header line in FILE prior to line number LINENUM.
420 This is a line containing a match for the regexp in `function_regexp'.
421 Store the address of the line text into LINEP and the length of the
422 line into LENP.
423 Do not store anything if no function-header is found. */
424
425 static void
find_function(file,linenum,linep,lenp)426 find_function (file, linenum, linep, lenp)
427 struct file_data *file;
428 int linenum;
429 const char **linep;
430 int *lenp;
431 {
432 int i = linenum;
433 int last = find_function_last_search;
434 find_function_last_search = i;
435
436 while (--i >= last)
437 {
438 /* See if this line is what we want. */
439 struct regexp_list *r;
440 const char *line = file->linbuf[i];
441 int len = file->linbuf[i + 1] - line;
442
443 for (r = function_regexp_list; r; r = r->next)
444 if (0 <= re_search (&r->buf, line, len, 0, len, 0))
445 {
446 *linep = line;
447 *lenp = len;
448 find_function_last_match = i;
449 return;
450 }
451 }
452 /* If we search back to where we started searching the previous time,
453 find the line we found last time. */
454 if (find_function_last_match >= - file->prefix_lines)
455 {
456 i = find_function_last_match;
457 *linep = file->linbuf[i];
458 *lenp = file->linbuf[i + 1] - *linep;
459 return;
460 }
461 return;
462 }
463