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