xref: /openbsd-src/gnu/usr.bin/cvs/diff/context.c (revision c71bc7e269286e43816004eb0fcd7a55f036cd69)
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