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