12286d8edStholo /* Context-format output routines for GNU DIFF.
2b2346922Stholo Copyright (C) 1988,1989,1991,1992,1993,1994,1998 Free Software Foundation, Inc.
32286d8edStholo
42286d8edStholo This file is part of GNU DIFF.
52286d8edStholo
62286d8edStholo GNU DIFF is free software; you can redistribute it and/or modify
72286d8edStholo it under the terms of the GNU General Public License as published by
82286d8edStholo the Free Software Foundation; either version 2, or (at your option)
92286d8edStholo any later version.
102286d8edStholo
112286d8edStholo GNU DIFF is distributed in the hope that it will be useful,
122286d8edStholo but WITHOUT ANY WARRANTY; without even the implied warranty of
132286d8edStholo MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
142286d8edStholo GNU General Public License for more details.
152286d8edStholo
16*c71bc7e2Stholo */
172286d8edStholo
182286d8edStholo #include "diff.h"
192286d8edStholo
202286d8edStholo static struct change *find_hunk PARAMS((struct change *));
212286d8edStholo static void find_function PARAMS((struct file_data const *, int, char const **, size_t *));
222286d8edStholo static void mark_ignorable PARAMS((struct change *));
232286d8edStholo static void pr_context_hunk PARAMS((struct change *));
242286d8edStholo static void pr_unidiff_hunk PARAMS((struct change *));
252286d8edStholo static void print_context_label PARAMS ((char const *, struct file_data *, char const *));
262286d8edStholo static void print_context_number_range PARAMS((struct file_data const *, int, int));
272286d8edStholo static void print_unidiff_number_range PARAMS((struct file_data const *, int, int));
282286d8edStholo
292286d8edStholo /* Last place find_function started searching from. */
302286d8edStholo static int find_function_last_search;
312286d8edStholo
322286d8edStholo /* The value find_function returned when it started searching there. */
332286d8edStholo static int find_function_last_match;
342286d8edStholo
352286d8edStholo /* Print a label for a context diff, with a file name and date or a label. */
362286d8edStholo
372286d8edStholo static void
print_context_label(mark,inf,label)382286d8edStholo print_context_label (mark, inf, label)
392286d8edStholo char const *mark;
402286d8edStholo struct file_data *inf;
412286d8edStholo char const *label;
422286d8edStholo {
432286d8edStholo if (label)
44b2346922Stholo printf_output ("%s %s\n", mark, label);
452286d8edStholo else
462286d8edStholo {
472286d8edStholo char const *ct = ctime (&inf->stat.st_mtime);
482286d8edStholo if (!ct)
492286d8edStholo ct = "?\n";
502286d8edStholo /* See Posix.2 section 4.17.6.1.4 for this format. */
51b2346922Stholo printf_output ("%s %s\t%s", mark, inf->name, ct);
522286d8edStholo }
532286d8edStholo }
542286d8edStholo
552286d8edStholo /* Print a header for a context diff, with the file names and dates. */
562286d8edStholo
572286d8edStholo void
print_context_header(inf,unidiff_flag)582286d8edStholo print_context_header (inf, unidiff_flag)
592286d8edStholo struct file_data inf[];
602286d8edStholo int unidiff_flag;
612286d8edStholo {
622286d8edStholo if (unidiff_flag)
632286d8edStholo {
642286d8edStholo print_context_label ("---", &inf[0], file_label[0]);
652286d8edStholo print_context_label ("+++", &inf[1], file_label[1]);
662286d8edStholo }
672286d8edStholo else
682286d8edStholo {
692286d8edStholo print_context_label ("***", &inf[0], file_label[0]);
702286d8edStholo print_context_label ("---", &inf[1], file_label[1]);
712286d8edStholo }
722286d8edStholo }
732286d8edStholo
742286d8edStholo /* Print an edit script in context format. */
752286d8edStholo
762286d8edStholo void
print_context_script(script,unidiff_flag)772286d8edStholo print_context_script (script, unidiff_flag)
782286d8edStholo struct change *script;
792286d8edStholo int unidiff_flag;
802286d8edStholo {
812286d8edStholo if (ignore_blank_lines_flag || ignore_regexp_list)
822286d8edStholo mark_ignorable (script);
832286d8edStholo else
842286d8edStholo {
852286d8edStholo struct change *e;
862286d8edStholo for (e = script; e; e = e->link)
872286d8edStholo e->ignore = 0;
882286d8edStholo }
892286d8edStholo
902286d8edStholo find_function_last_search = - files[0].prefix_lines;
912286d8edStholo find_function_last_match = find_function_last_search - 1;
922286d8edStholo
932286d8edStholo if (unidiff_flag)
942286d8edStholo print_script (script, find_hunk, pr_unidiff_hunk);
952286d8edStholo else
962286d8edStholo print_script (script, find_hunk, pr_context_hunk);
972286d8edStholo }
982286d8edStholo
992286d8edStholo /* Print a pair of line numbers with a comma, translated for file FILE.
1002286d8edStholo If the second number is not greater, use the first in place of it.
1012286d8edStholo
1022286d8edStholo Args A and B are internal line numbers.
1032286d8edStholo We print the translated (real) line numbers. */
1042286d8edStholo
1052286d8edStholo static void
print_context_number_range(file,a,b)1062286d8edStholo print_context_number_range (file, a, b)
1072286d8edStholo struct file_data const *file;
1082286d8edStholo int a, b;
1092286d8edStholo {
1102286d8edStholo int trans_a, trans_b;
1112286d8edStholo translate_range (file, a, b, &trans_a, &trans_b);
1122286d8edStholo
1132286d8edStholo /* Note: we can have B < A in the case of a range of no lines.
1142286d8edStholo In this case, we should print the line number before the range,
1152286d8edStholo which is B. */
1162286d8edStholo if (trans_b > trans_a)
117b2346922Stholo printf_output ("%d,%d", trans_a, trans_b);
1182286d8edStholo else
119b2346922Stholo printf_output ("%d", trans_b);
1202286d8edStholo }
1212286d8edStholo
1222286d8edStholo /* Print a portion of an edit script in context format.
1232286d8edStholo HUNK is the beginning of the portion to be printed.
1242286d8edStholo The end is marked by a `link' that has been nulled out.
1252286d8edStholo
1262286d8edStholo Prints out lines from both files, and precedes each
1272286d8edStholo line with the appropriate flag-character. */
1282286d8edStholo
1292286d8edStholo static void
pr_context_hunk(hunk)1302286d8edStholo pr_context_hunk (hunk)
1312286d8edStholo struct change *hunk;
1322286d8edStholo {
1332286d8edStholo int first0, last0, first1, last1, show_from, show_to, i;
1342286d8edStholo struct change *next;
1352286d8edStholo char const *prefix;
1362286d8edStholo char const *function;
1372286d8edStholo size_t function_length;
1382286d8edStholo
1392286d8edStholo /* Determine range of line numbers involved in each file. */
1402286d8edStholo
1412286d8edStholo analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to);
1422286d8edStholo
1432286d8edStholo if (!show_from && !show_to)
1442286d8edStholo return;
1452286d8edStholo
1462286d8edStholo /* Include a context's width before and after. */
1472286d8edStholo
1482286d8edStholo i = - files[0].prefix_lines;
1492286d8edStholo first0 = max (first0 - context, i);
1502286d8edStholo first1 = max (first1 - context, i);
1512286d8edStholo last0 = min (last0 + context, files[0].valid_lines - 1);
1522286d8edStholo last1 = min (last1 + context, files[1].valid_lines - 1);
1532286d8edStholo
1542286d8edStholo /* If desired, find the preceding function definition line in file 0. */
1552286d8edStholo function = 0;
1562286d8edStholo if (function_regexp_list)
1572286d8edStholo find_function (&files[0], first0, &function, &function_length);
1582286d8edStholo
1592286d8edStholo begin_output ();
1602286d8edStholo
1612286d8edStholo /* If we looked for and found a function this is part of,
1622286d8edStholo include its name in the header of the diff section. */
163b2346922Stholo printf_output ("***************");
1642286d8edStholo
1652286d8edStholo if (function)
1662286d8edStholo {
167b2346922Stholo printf_output (" ");
168b2346922Stholo write_output (function, min (function_length - 1, 40));
1692286d8edStholo }
1702286d8edStholo
171b2346922Stholo printf_output ("\n*** ");
1722286d8edStholo print_context_number_range (&files[0], first0, last0);
173b2346922Stholo printf_output (" ****\n");
1742286d8edStholo
1752286d8edStholo if (show_from)
1762286d8edStholo {
1772286d8edStholo next = hunk;
1782286d8edStholo
1792286d8edStholo for (i = first0; i <= last0; i++)
1802286d8edStholo {
1812286d8edStholo /* Skip past changes that apply (in file 0)
1822286d8edStholo only to lines before line I. */
1832286d8edStholo
1842286d8edStholo while (next && next->line0 + next->deleted <= i)
1852286d8edStholo next = next->link;
1862286d8edStholo
1872286d8edStholo /* Compute the marking for line I. */
1882286d8edStholo
1892286d8edStholo prefix = " ";
1902286d8edStholo if (next && next->line0 <= i)
1912286d8edStholo /* The change NEXT covers this line.
1922286d8edStholo If lines were inserted here in file 1, this is "changed".
1932286d8edStholo Otherwise it is "deleted". */
1942286d8edStholo prefix = (next->inserted > 0 ? "!" : "-");
1952286d8edStholo
1962286d8edStholo print_1_line (prefix, &files[0].linbuf[i]);
1972286d8edStholo }
1982286d8edStholo }
1992286d8edStholo
200b2346922Stholo printf_output ("--- ");
2012286d8edStholo print_context_number_range (&files[1], first1, last1);
202b2346922Stholo printf_output (" ----\n");
2032286d8edStholo
2042286d8edStholo if (show_to)
2052286d8edStholo {
2062286d8edStholo next = hunk;
2072286d8edStholo
2082286d8edStholo for (i = first1; i <= last1; i++)
2092286d8edStholo {
2102286d8edStholo /* Skip past changes that apply (in file 1)
2112286d8edStholo only to lines before line I. */
2122286d8edStholo
2132286d8edStholo while (next && next->line1 + next->inserted <= i)
2142286d8edStholo next = next->link;
2152286d8edStholo
2162286d8edStholo /* Compute the marking for line I. */
2172286d8edStholo
2182286d8edStholo prefix = " ";
2192286d8edStholo if (next && next->line1 <= i)
2202286d8edStholo /* The change NEXT covers this line.
2212286d8edStholo If lines were deleted here in file 0, this is "changed".
2222286d8edStholo Otherwise it is "inserted". */
2232286d8edStholo prefix = (next->deleted > 0 ? "!" : "+");
2242286d8edStholo
2252286d8edStholo print_1_line (prefix, &files[1].linbuf[i]);
2262286d8edStholo }
2272286d8edStholo }
2282286d8edStholo }
2292286d8edStholo
2302286d8edStholo /* Print a pair of line numbers with a comma, translated for file FILE.
2312286d8edStholo If the second number is smaller, use the first in place of it.
2322286d8edStholo If the numbers are equal, print just one number.
2332286d8edStholo
2342286d8edStholo Args A and B are internal line numbers.
2352286d8edStholo We print the translated (real) line numbers. */
2362286d8edStholo
2372286d8edStholo static void
print_unidiff_number_range(file,a,b)2382286d8edStholo print_unidiff_number_range (file, a, b)
2392286d8edStholo struct file_data const *file;
2402286d8edStholo int a, b;
2412286d8edStholo {
2422286d8edStholo int trans_a, trans_b;
2432286d8edStholo translate_range (file, a, b, &trans_a, &trans_b);
2442286d8edStholo
2452286d8edStholo /* Note: we can have B < A in the case of a range of no lines.
2462286d8edStholo In this case, we should print the line number before the range,
2472286d8edStholo which is B. */
2482286d8edStholo if (trans_b <= trans_a)
249b2346922Stholo printf_output (trans_b == trans_a ? "%d" : "%d,0", trans_b);
2502286d8edStholo else
251b2346922Stholo printf_output ("%d,%d", trans_a, trans_b - trans_a + 1);
2522286d8edStholo }
2532286d8edStholo
2542286d8edStholo /* Print a portion of an edit script in unidiff format.
2552286d8edStholo HUNK is the beginning of the portion to be printed.
2562286d8edStholo The end is marked by a `link' that has been nulled out.
2572286d8edStholo
2582286d8edStholo Prints out lines from both files, and precedes each
2592286d8edStholo line with the appropriate flag-character. */
2602286d8edStholo
2612286d8edStholo static void
pr_unidiff_hunk(hunk)2622286d8edStholo pr_unidiff_hunk (hunk)
2632286d8edStholo struct change *hunk;
2642286d8edStholo {
2652286d8edStholo int first0, last0, first1, last1, show_from, show_to, i, j, k;
2662286d8edStholo struct change *next;
2672286d8edStholo char const *function;
2682286d8edStholo size_t function_length;
2692286d8edStholo
2702286d8edStholo /* Determine range of line numbers involved in each file. */
2712286d8edStholo
2722286d8edStholo analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to);
2732286d8edStholo
2742286d8edStholo if (!show_from && !show_to)
2752286d8edStholo return;
2762286d8edStholo
2772286d8edStholo /* Include a context's width before and after. */
2782286d8edStholo
2792286d8edStholo i = - files[0].prefix_lines;
2802286d8edStholo first0 = max (first0 - context, i);
2812286d8edStholo first1 = max (first1 - context, i);
2822286d8edStholo last0 = min (last0 + context, files[0].valid_lines - 1);
2832286d8edStholo last1 = min (last1 + context, files[1].valid_lines - 1);
2842286d8edStholo
2852286d8edStholo /* If desired, find the preceding function definition line in file 0. */
2862286d8edStholo function = 0;
2872286d8edStholo if (function_regexp_list)
2882286d8edStholo find_function (&files[0], first0, &function, &function_length);
2892286d8edStholo
2902286d8edStholo begin_output ();
2912286d8edStholo
292b2346922Stholo printf_output ("@@ -");
2932286d8edStholo print_unidiff_number_range (&files[0], first0, last0);
294b2346922Stholo printf_output (" +");
2952286d8edStholo print_unidiff_number_range (&files[1], first1, last1);
296b2346922Stholo printf_output (" @@");
2972286d8edStholo
2982286d8edStholo /* If we looked for and found a function this is part of,
2992286d8edStholo include its name in the header of the diff section. */
3002286d8edStholo
3012286d8edStholo if (function)
3022286d8edStholo {
303b2346922Stholo write_output (" ", 1);
304b2346922Stholo write_output (function, min (function_length - 1, 40));
3052286d8edStholo }
306b2346922Stholo write_output ("\n", 1);
3072286d8edStholo
3082286d8edStholo next = hunk;
3092286d8edStholo i = first0;
3102286d8edStholo j = first1;
3112286d8edStholo
3122286d8edStholo while (i <= last0 || j <= last1)
3132286d8edStholo {
3142286d8edStholo
3152286d8edStholo /* If the line isn't a difference, output the context from file 0. */
3162286d8edStholo
3172286d8edStholo if (!next || i < next->line0)
3182286d8edStholo {
319b2346922Stholo write_output (tab_align_flag ? "\t" : " ", 1);
3202286d8edStholo print_1_line (0, &files[0].linbuf[i++]);
3212286d8edStholo j++;
3222286d8edStholo }
3232286d8edStholo else
3242286d8edStholo {
3252286d8edStholo /* For each difference, first output the deleted part. */
3262286d8edStholo
3272286d8edStholo k = next->deleted;
3282286d8edStholo while (k--)
3292286d8edStholo {
330b2346922Stholo write_output ("-", 1);
3312286d8edStholo if (tab_align_flag)
332b2346922Stholo write_output ("\t", 1);
3332286d8edStholo print_1_line (0, &files[0].linbuf[i++]);
3342286d8edStholo }
3352286d8edStholo
3362286d8edStholo /* Then output the inserted part. */
3372286d8edStholo
3382286d8edStholo k = next->inserted;
3392286d8edStholo while (k--)
3402286d8edStholo {
341b2346922Stholo write_output ("+", 1);
3422286d8edStholo if (tab_align_flag)
343b2346922Stholo write_output ("\t", 1);
3442286d8edStholo print_1_line (0, &files[1].linbuf[j++]);
3452286d8edStholo }
3462286d8edStholo
3472286d8edStholo /* We're done with this hunk, so on to the next! */
3482286d8edStholo
3492286d8edStholo next = next->link;
3502286d8edStholo }
3512286d8edStholo }
3522286d8edStholo }
3532286d8edStholo
3542286d8edStholo /* Scan a (forward-ordered) edit script for the first place that more than
3552286d8edStholo 2*CONTEXT unchanged lines appear, and return a pointer
3562286d8edStholo to the `struct change' for the last change before those lines. */
3572286d8edStholo
3582286d8edStholo static struct change *
find_hunk(start)3592286d8edStholo find_hunk (start)
3602286d8edStholo struct change *start;
3612286d8edStholo {
3622286d8edStholo struct change *prev;
3632286d8edStholo int top0, top1;
3642286d8edStholo int thresh;
3652286d8edStholo
3662286d8edStholo do
3672286d8edStholo {
3682286d8edStholo /* Compute number of first line in each file beyond this changed. */
3692286d8edStholo top0 = start->line0 + start->deleted;
3702286d8edStholo top1 = start->line1 + start->inserted;
3712286d8edStholo prev = start;
3722286d8edStholo start = start->link;
3732286d8edStholo /* Threshold distance is 2*CONTEXT between two non-ignorable changes,
3742286d8edStholo but only CONTEXT if one is ignorable. */
3752286d8edStholo thresh = ((prev->ignore || (start && start->ignore))
3762286d8edStholo ? context
3772286d8edStholo : 2 * context + 1);
3782286d8edStholo /* It is not supposed to matter which file we check in the end-test.
3792286d8edStholo If it would matter, crash. */
3802286d8edStholo if (start && start->line0 - top0 != start->line1 - top1)
3812286d8edStholo abort ();
3822286d8edStholo } while (start
3832286d8edStholo /* Keep going if less than THRESH lines
3842286d8edStholo elapse before the affected line. */
3852286d8edStholo && start->line0 < top0 + thresh);
3862286d8edStholo
3872286d8edStholo return prev;
3882286d8edStholo }
3892286d8edStholo
3902286d8edStholo /* Set the `ignore' flag properly in each change in SCRIPT.
3912286d8edStholo It should be 1 if all the lines inserted or deleted in that change
3922286d8edStholo are ignorable lines. */
3932286d8edStholo
3942286d8edStholo static void
mark_ignorable(script)3952286d8edStholo mark_ignorable (script)
3962286d8edStholo struct change *script;
3972286d8edStholo {
3982286d8edStholo while (script)
3992286d8edStholo {
4002286d8edStholo struct change *next = script->link;
4012286d8edStholo int first0, last0, first1, last1, deletes, inserts;
4022286d8edStholo
4032286d8edStholo /* Turn this change into a hunk: detach it from the others. */
4042286d8edStholo script->link = 0;
4052286d8edStholo
4062286d8edStholo /* Determine whether this change is ignorable. */
4072286d8edStholo analyze_hunk (script, &first0, &last0, &first1, &last1, &deletes, &inserts);
4082286d8edStholo /* Reconnect the chain as before. */
4092286d8edStholo script->link = next;
4102286d8edStholo
4112286d8edStholo /* If the change is ignorable, mark it. */
4122286d8edStholo script->ignore = (!deletes && !inserts);
4132286d8edStholo
4142286d8edStholo /* Advance to the following change. */
4152286d8edStholo script = next;
4162286d8edStholo }
4172286d8edStholo }
4182286d8edStholo
4192286d8edStholo /* Find the last function-header line in FILE prior to line number LINENUM.
4202286d8edStholo This is a line containing a match for the regexp in `function_regexp'.
4212286d8edStholo Store the address of the line text into LINEP and the length of the
4222286d8edStholo line into LENP.
4232286d8edStholo Do not store anything if no function-header is found. */
4242286d8edStholo
4252286d8edStholo static void
find_function(file,linenum,linep,lenp)4262286d8edStholo find_function (file, linenum, linep, lenp)
4272286d8edStholo struct file_data const *file;
4282286d8edStholo int linenum;
4292286d8edStholo char const **linep;
4302286d8edStholo size_t *lenp;
4312286d8edStholo {
4322286d8edStholo int i = linenum;
4332286d8edStholo int last = find_function_last_search;
4342286d8edStholo find_function_last_search = i;
4352286d8edStholo
4362286d8edStholo while (--i >= last)
4372286d8edStholo {
4382286d8edStholo /* See if this line is what we want. */
4392286d8edStholo struct regexp_list *r;
4402286d8edStholo char const *line = file->linbuf[i];
4412286d8edStholo size_t len = file->linbuf[i + 1] - line;
4422286d8edStholo
4432286d8edStholo for (r = function_regexp_list; r; r = r->next)
4442286d8edStholo if (0 <= re_search (&r->buf, line, len, 0, len, 0))
4452286d8edStholo {
4462286d8edStholo *linep = line;
4472286d8edStholo *lenp = len;
4482286d8edStholo find_function_last_match = i;
4492286d8edStholo return;
4502286d8edStholo }
4512286d8edStholo }
4522286d8edStholo /* If we search back to where we started searching the previous time,
4532286d8edStholo find the line we found last time. */
4542286d8edStholo if (find_function_last_match >= - file->prefix_lines)
4552286d8edStholo {
4562286d8edStholo i = find_function_last_match;
4572286d8edStholo *linep = file->linbuf[i];
4582286d8edStholo *lenp = file->linbuf[i + 1] - *linep;
4592286d8edStholo return;
4602286d8edStholo }
4612286d8edStholo return;
4622286d8edStholo }
463