1170754Sdelphij/* Context-format output routines for GNU DIFF. 2170754Sdelphij 3170754Sdelphij Copyright (C) 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1998, 2001, 4170754Sdelphij 2002, 2004 Free Software Foundation, Inc. 5170754Sdelphij 6170754Sdelphij This file is part of GNU DIFF. 7170754Sdelphij 8170754Sdelphij GNU DIFF is free software; you can redistribute it and/or modify 9170754Sdelphij it under the terms of the GNU General Public License as published by 10170754Sdelphij the Free Software Foundation; either version 2, or (at your option) 11170754Sdelphij any later version. 12170754Sdelphij 13170754Sdelphij GNU DIFF is distributed in the hope that it will be useful, 14170754Sdelphij but WITHOUT ANY WARRANTY; without even the implied warranty of 15170754Sdelphij MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16170754Sdelphij GNU General Public License for more details. 17170754Sdelphij 18170754Sdelphij You should have received a copy of the GNU General Public License 19170754Sdelphij along with this program; see the file COPYING. 20170754Sdelphij If not, write to the Free Software Foundation, 21170754Sdelphij 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 22170754Sdelphij 23170754Sdelphij#include "diff.h" 24170754Sdelphij#include <inttostr.h> 25170754Sdelphij 26170754Sdelphij#ifdef ST_MTIM_NSEC 27170754Sdelphij# define TIMESPEC_NS(timespec) ((timespec).ST_MTIM_NSEC) 28170754Sdelphij#else 29170754Sdelphij# define TIMESPEC_NS(timespec) 0 30170754Sdelphij#endif 31170754Sdelphij 32239360Sobriensize_t nstrftime (char *, size_t, char const *, struct tm const *, int, long); 33170754Sdelphij 34170754Sdelphijstatic char const *find_function (char const * const *, lin); 35170754Sdelphijstatic struct change *find_hunk (struct change *); 36170754Sdelphijstatic void mark_ignorable (struct change *); 37170754Sdelphijstatic void pr_context_hunk (struct change *); 38170754Sdelphijstatic void pr_unidiff_hunk (struct change *); 39170754Sdelphij 40170754Sdelphij/* Last place find_function started searching from. */ 41170754Sdelphijstatic lin find_function_last_search; 42170754Sdelphij 43170754Sdelphij/* The value find_function returned when it started searching there. */ 44170754Sdelphijstatic lin find_function_last_match; 45170754Sdelphij 46170754Sdelphij/* Print a label for a context diff, with a file name and date or a label. */ 47170754Sdelphij 48170754Sdelphijstatic void 49170754Sdelphijprint_context_label (char const *mark, 50170754Sdelphij struct file_data *inf, 51170754Sdelphij char const *label) 52170754Sdelphij{ 53170754Sdelphij if (label) 54170754Sdelphij fprintf (outfile, "%s %s\n", mark, label); 55170754Sdelphij else 56170754Sdelphij { 57170754Sdelphij char buf[MAX (INT_STRLEN_BOUND (int) + 32, 58170754Sdelphij INT_STRLEN_BOUND (time_t) + 11)]; 59170754Sdelphij struct tm const *tm = localtime (&inf->stat.st_mtime); 60239360Sobrien long nsec = TIMESPEC_NS (inf->stat.st_mtim); 61170754Sdelphij if (! (tm && nstrftime (buf, sizeof buf, time_format, tm, 0, nsec))) 62170754Sdelphij { 63239360Sobrien time_t sec = inf->stat.st_mtime; 64170754Sdelphij verify (info_preserved, sizeof inf->stat.st_mtime <= sizeof sec); 65239360Sobrien sprintf (buf, "%jd.%.9d", (intmax_t)sec, nsec); 66170754Sdelphij } 67170754Sdelphij fprintf (outfile, "%s %s\t%s\n", mark, inf->name, buf); 68170754Sdelphij } 69170754Sdelphij} 70170754Sdelphij 71170754Sdelphij/* Print a header for a context diff, with the file names and dates. */ 72170754Sdelphij 73170754Sdelphijvoid 74170754Sdelphijprint_context_header (struct file_data inf[], bool unidiff) 75170754Sdelphij{ 76170754Sdelphij if (unidiff) 77170754Sdelphij { 78170754Sdelphij print_context_label ("---", &inf[0], file_label[0]); 79170754Sdelphij print_context_label ("+++", &inf[1], file_label[1]); 80170754Sdelphij } 81170754Sdelphij else 82170754Sdelphij { 83170754Sdelphij print_context_label ("***", &inf[0], file_label[0]); 84170754Sdelphij print_context_label ("---", &inf[1], file_label[1]); 85170754Sdelphij } 86170754Sdelphij} 87170754Sdelphij 88170754Sdelphij/* Print an edit script in context format. */ 89170754Sdelphij 90170754Sdelphijvoid 91170754Sdelphijprint_context_script (struct change *script, bool unidiff) 92170754Sdelphij{ 93170754Sdelphij if (ignore_blank_lines || ignore_regexp.fastmap) 94170754Sdelphij mark_ignorable (script); 95170754Sdelphij else 96170754Sdelphij { 97170754Sdelphij struct change *e; 98170754Sdelphij for (e = script; e; e = e->link) 99170754Sdelphij e->ignore = false; 100170754Sdelphij } 101170754Sdelphij 102170754Sdelphij find_function_last_search = - files[0].prefix_lines; 103170754Sdelphij find_function_last_match = LIN_MAX; 104170754Sdelphij 105170754Sdelphij if (unidiff) 106170754Sdelphij print_script (script, find_hunk, pr_unidiff_hunk); 107170754Sdelphij else 108170754Sdelphij print_script (script, find_hunk, pr_context_hunk); 109170754Sdelphij} 110170754Sdelphij 111170754Sdelphij/* Print a pair of line numbers with a comma, translated for file FILE. 112170754Sdelphij If the second number is not greater, use the first in place of it. 113170754Sdelphij 114170754Sdelphij Args A and B are internal line numbers. 115170754Sdelphij We print the translated (real) line numbers. */ 116170754Sdelphij 117170754Sdelphijstatic void 118170754Sdelphijprint_context_number_range (struct file_data const *file, lin a, lin b) 119170754Sdelphij{ 120170754Sdelphij long int trans_a, trans_b; 121170754Sdelphij translate_range (file, a, b, &trans_a, &trans_b); 122170754Sdelphij 123170754Sdelphij /* We can have B <= A in the case of a range of no lines. 124170754Sdelphij In this case, we should print the line number before the range, 125170754Sdelphij which is B. 126170754Sdelphij 127170754Sdelphij POSIX 1003.1-2001 requires two line numbers separated by a comma 128170754Sdelphij even if the line numbers are the same. However, this does not 129170754Sdelphij match existing practice and is surely an error in the 130170754Sdelphij specification. */ 131170754Sdelphij 132170754Sdelphij if (trans_b <= trans_a) 133170754Sdelphij fprintf (outfile, "%ld", trans_b); 134170754Sdelphij else 135170754Sdelphij fprintf (outfile, "%ld,%ld", trans_a, trans_b); 136170754Sdelphij} 137170754Sdelphij 138170754Sdelphij/* Print FUNCTION in a context header. */ 139170754Sdelphijstatic void 140170754Sdelphijprint_context_function (FILE *out, char const *function) 141170754Sdelphij{ 142170754Sdelphij int i; 143170754Sdelphij putc (' ', out); 144170754Sdelphij for (i = 0; i < 40 && function[i] != '\n'; i++) 145170754Sdelphij continue; 146170754Sdelphij fwrite (function, sizeof (char), i, out); 147170754Sdelphij} 148170754Sdelphij 149170754Sdelphij/* Print a portion of an edit script in context format. 150170754Sdelphij HUNK is the beginning of the portion to be printed. 151170754Sdelphij The end is marked by a `link' that has been nulled out. 152170754Sdelphij 153170754Sdelphij Prints out lines from both files, and precedes each 154170754Sdelphij line with the appropriate flag-character. */ 155170754Sdelphij 156170754Sdelphijstatic void 157170754Sdelphijpr_context_hunk (struct change *hunk) 158170754Sdelphij{ 159170754Sdelphij lin first0, last0, first1, last1, i; 160170754Sdelphij char const *prefix; 161170754Sdelphij char const *function; 162170754Sdelphij FILE *out; 163170754Sdelphij 164170754Sdelphij /* Determine range of line numbers involved in each file. */ 165170754Sdelphij 166170754Sdelphij enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1); 167170754Sdelphij if (! changes) 168170754Sdelphij return; 169170754Sdelphij 170170754Sdelphij /* Include a context's width before and after. */ 171170754Sdelphij 172170754Sdelphij i = - files[0].prefix_lines; 173170754Sdelphij first0 = MAX (first0 - context, i); 174170754Sdelphij first1 = MAX (first1 - context, i); 175170754Sdelphij if (last0 < files[0].valid_lines - context) 176170754Sdelphij last0 += context; 177170754Sdelphij else 178170754Sdelphij last0 = files[0].valid_lines - 1; 179170754Sdelphij if (last1 < files[1].valid_lines - context) 180170754Sdelphij last1 += context; 181170754Sdelphij else 182170754Sdelphij last1 = files[1].valid_lines - 1; 183170754Sdelphij 184170754Sdelphij /* If desired, find the preceding function definition line in file 0. */ 185170754Sdelphij function = 0; 186170754Sdelphij if (function_regexp.fastmap) 187170754Sdelphij function = find_function (files[0].linbuf, first0); 188170754Sdelphij 189170754Sdelphij begin_output (); 190170754Sdelphij out = outfile; 191170754Sdelphij 192170754Sdelphij fprintf (out, "***************"); 193170754Sdelphij 194170754Sdelphij if (function) 195170754Sdelphij print_context_function (out, function); 196170754Sdelphij 197170754Sdelphij fprintf (out, "\n*** "); 198170754Sdelphij print_context_number_range (&files[0], first0, last0); 199170754Sdelphij fprintf (out, " ****\n"); 200170754Sdelphij 201170754Sdelphij if (changes & OLD) 202170754Sdelphij { 203170754Sdelphij struct change *next = hunk; 204170754Sdelphij 205170754Sdelphij for (i = first0; i <= last0; i++) 206170754Sdelphij { 207170754Sdelphij /* Skip past changes that apply (in file 0) 208170754Sdelphij only to lines before line I. */ 209170754Sdelphij 210170754Sdelphij while (next && next->line0 + next->deleted <= i) 211170754Sdelphij next = next->link; 212170754Sdelphij 213170754Sdelphij /* Compute the marking for line I. */ 214170754Sdelphij 215170754Sdelphij prefix = " "; 216170754Sdelphij if (next && next->line0 <= i) 217170754Sdelphij /* The change NEXT covers this line. 218170754Sdelphij If lines were inserted here in file 1, this is "changed". 219170754Sdelphij Otherwise it is "deleted". */ 220170754Sdelphij prefix = (next->inserted > 0 ? "!" : "-"); 221170754Sdelphij 222170754Sdelphij print_1_line (prefix, &files[0].linbuf[i]); 223170754Sdelphij } 224170754Sdelphij } 225170754Sdelphij 226170754Sdelphij fprintf (out, "--- "); 227170754Sdelphij print_context_number_range (&files[1], first1, last1); 228170754Sdelphij fprintf (out, " ----\n"); 229170754Sdelphij 230170754Sdelphij if (changes & NEW) 231170754Sdelphij { 232170754Sdelphij struct change *next = hunk; 233170754Sdelphij 234170754Sdelphij for (i = first1; i <= last1; i++) 235170754Sdelphij { 236170754Sdelphij /* Skip past changes that apply (in file 1) 237170754Sdelphij only to lines before line I. */ 238170754Sdelphij 239170754Sdelphij while (next && next->line1 + next->inserted <= i) 240170754Sdelphij next = next->link; 241170754Sdelphij 242170754Sdelphij /* Compute the marking for line I. */ 243170754Sdelphij 244170754Sdelphij prefix = " "; 245170754Sdelphij if (next && next->line1 <= i) 246170754Sdelphij /* The change NEXT covers this line. 247170754Sdelphij If lines were deleted here in file 0, this is "changed". 248170754Sdelphij Otherwise it is "inserted". */ 249170754Sdelphij prefix = (next->deleted > 0 ? "!" : "+"); 250170754Sdelphij 251170754Sdelphij print_1_line (prefix, &files[1].linbuf[i]); 252170754Sdelphij } 253170754Sdelphij } 254170754Sdelphij} 255170754Sdelphij 256170754Sdelphij/* Print a pair of line numbers with a comma, translated for file FILE. 257170754Sdelphij If the second number is smaller, use the first in place of it. 258170754Sdelphij If the numbers are equal, print just one number. 259170754Sdelphij 260170754Sdelphij Args A and B are internal line numbers. 261170754Sdelphij We print the translated (real) line numbers. */ 262170754Sdelphij 263170754Sdelphijstatic void 264170754Sdelphijprint_unidiff_number_range (struct file_data const *file, lin a, lin b) 265170754Sdelphij{ 266170754Sdelphij long int trans_a, trans_b; 267170754Sdelphij translate_range (file, a, b, &trans_a, &trans_b); 268170754Sdelphij 269170754Sdelphij /* We can have B < A in the case of a range of no lines. 270170754Sdelphij In this case, we print the line number before the range, 271170754Sdelphij which is B. It would be more logical to print A, but 272170754Sdelphij 'patch' expects B in order to detect diffs against empty files. */ 273170754Sdelphij if (trans_b <= trans_a) 274170754Sdelphij fprintf (outfile, trans_b < trans_a ? "%ld,0" : "%ld", trans_b); 275170754Sdelphij else 276170754Sdelphij fprintf (outfile, "%ld,%ld", trans_a, trans_b - trans_a + 1); 277170754Sdelphij} 278170754Sdelphij 279170754Sdelphij/* Print a portion of an edit script in unidiff format. 280170754Sdelphij HUNK is the beginning of the portion to be printed. 281170754Sdelphij The end is marked by a `link' that has been nulled out. 282170754Sdelphij 283170754Sdelphij Prints out lines from both files, and precedes each 284170754Sdelphij line with the appropriate flag-character. */ 285170754Sdelphij 286170754Sdelphijstatic void 287170754Sdelphijpr_unidiff_hunk (struct change *hunk) 288170754Sdelphij{ 289170754Sdelphij lin first0, last0, first1, last1; 290170754Sdelphij lin i, j, k; 291170754Sdelphij struct change *next; 292170754Sdelphij char const *function; 293170754Sdelphij FILE *out; 294170754Sdelphij 295170754Sdelphij /* Determine range of line numbers involved in each file. */ 296170754Sdelphij 297170754Sdelphij if (! analyze_hunk (hunk, &first0, &last0, &first1, &last1)) 298170754Sdelphij return; 299170754Sdelphij 300170754Sdelphij /* Include a context's width before and after. */ 301170754Sdelphij 302170754Sdelphij i = - files[0].prefix_lines; 303170754Sdelphij first0 = MAX (first0 - context, i); 304170754Sdelphij first1 = MAX (first1 - context, i); 305170754Sdelphij if (last0 < files[0].valid_lines - context) 306170754Sdelphij last0 += context; 307170754Sdelphij else 308170754Sdelphij last0 = files[0].valid_lines - 1; 309170754Sdelphij if (last1 < files[1].valid_lines - context) 310170754Sdelphij last1 += context; 311170754Sdelphij else 312170754Sdelphij last1 = files[1].valid_lines - 1; 313170754Sdelphij 314170754Sdelphij /* If desired, find the preceding function definition line in file 0. */ 315170754Sdelphij function = 0; 316170754Sdelphij if (function_regexp.fastmap) 317170754Sdelphij function = find_function (files[0].linbuf, first0); 318170754Sdelphij 319170754Sdelphij begin_output (); 320170754Sdelphij out = outfile; 321170754Sdelphij 322170754Sdelphij fprintf (out, "@@ -"); 323170754Sdelphij print_unidiff_number_range (&files[0], first0, last0); 324170754Sdelphij fprintf (out, " +"); 325170754Sdelphij print_unidiff_number_range (&files[1], first1, last1); 326170754Sdelphij fprintf (out, " @@"); 327170754Sdelphij 328170754Sdelphij if (function) 329170754Sdelphij print_context_function (out, function); 330170754Sdelphij 331170754Sdelphij putc ('\n', out); 332170754Sdelphij 333170754Sdelphij next = hunk; 334170754Sdelphij i = first0; 335170754Sdelphij j = first1; 336170754Sdelphij 337170754Sdelphij while (i <= last0 || j <= last1) 338170754Sdelphij { 339170754Sdelphij 340170754Sdelphij /* If the line isn't a difference, output the context from file 0. */ 341170754Sdelphij 342170754Sdelphij if (!next || i < next->line0) 343170754Sdelphij { 344170754Sdelphij putc (initial_tab ? '\t' : ' ', out); 345170754Sdelphij print_1_line (0, &files[0].linbuf[i++]); 346170754Sdelphij j++; 347170754Sdelphij } 348170754Sdelphij else 349170754Sdelphij { 350170754Sdelphij /* For each difference, first output the deleted part. */ 351170754Sdelphij 352170754Sdelphij k = next->deleted; 353170754Sdelphij while (k--) 354170754Sdelphij { 355170754Sdelphij putc ('-', out); 356170754Sdelphij if (initial_tab) 357170754Sdelphij putc ('\t', out); 358170754Sdelphij print_1_line (0, &files[0].linbuf[i++]); 359170754Sdelphij } 360170754Sdelphij 361170754Sdelphij /* Then output the inserted part. */ 362170754Sdelphij 363170754Sdelphij k = next->inserted; 364170754Sdelphij while (k--) 365170754Sdelphij { 366170754Sdelphij putc ('+', out); 367170754Sdelphij if (initial_tab) 368170754Sdelphij putc ('\t', out); 369170754Sdelphij print_1_line (0, &files[1].linbuf[j++]); 370170754Sdelphij } 371170754Sdelphij 372170754Sdelphij /* We're done with this hunk, so on to the next! */ 373170754Sdelphij 374170754Sdelphij next = next->link; 375170754Sdelphij } 376170754Sdelphij } 377170754Sdelphij} 378170754Sdelphij 379170754Sdelphij/* Scan a (forward-ordered) edit script for the first place that more than 380170754Sdelphij 2*CONTEXT unchanged lines appear, and return a pointer 381170754Sdelphij to the `struct change' for the last change before those lines. */ 382170754Sdelphij 383170754Sdelphijstatic struct change * 384170754Sdelphijfind_hunk (struct change *start) 385170754Sdelphij{ 386170754Sdelphij struct change *prev; 387170754Sdelphij lin top0, top1; 388170754Sdelphij lin thresh; 389170754Sdelphij 390170754Sdelphij /* Threshold distance is 2 * CONTEXT + 1 between two non-ignorable 391170754Sdelphij changes, but only CONTEXT if one is ignorable. Watch out for 392170754Sdelphij integer overflow, though. */ 393170754Sdelphij lin non_ignorable_threshold = 394170754Sdelphij (LIN_MAX - 1) / 2 < context ? LIN_MAX : 2 * context + 1; 395170754Sdelphij lin ignorable_threshold = context; 396170754Sdelphij 397170754Sdelphij do 398170754Sdelphij { 399170754Sdelphij /* Compute number of first line in each file beyond this changed. */ 400170754Sdelphij top0 = start->line0 + start->deleted; 401170754Sdelphij top1 = start->line1 + start->inserted; 402170754Sdelphij prev = start; 403170754Sdelphij start = start->link; 404170754Sdelphij thresh = (prev->ignore || (start && start->ignore) 405170754Sdelphij ? ignorable_threshold 406170754Sdelphij : non_ignorable_threshold); 407170754Sdelphij /* It is not supposed to matter which file we check in the end-test. 408170754Sdelphij If it would matter, crash. */ 409170754Sdelphij if (start && start->line0 - top0 != start->line1 - top1) 410170754Sdelphij abort (); 411170754Sdelphij } while (start 412170754Sdelphij /* Keep going if less than THRESH lines 413170754Sdelphij elapse before the affected line. */ 414170754Sdelphij && start->line0 - top0 < thresh); 415170754Sdelphij 416170754Sdelphij return prev; 417170754Sdelphij} 418170754Sdelphij 419170754Sdelphij/* Set the `ignore' flag properly in each change in SCRIPT. 420170754Sdelphij It should be 1 if all the lines inserted or deleted in that change 421170754Sdelphij are ignorable lines. */ 422170754Sdelphij 423170754Sdelphijstatic void 424170754Sdelphijmark_ignorable (struct change *script) 425170754Sdelphij{ 426170754Sdelphij while (script) 427170754Sdelphij { 428170754Sdelphij struct change *next = script->link; 429170754Sdelphij lin first0, last0, first1, last1; 430170754Sdelphij 431170754Sdelphij /* Turn this change into a hunk: detach it from the others. */ 432170754Sdelphij script->link = 0; 433170754Sdelphij 434170754Sdelphij /* Determine whether this change is ignorable. */ 435170754Sdelphij script->ignore = ! analyze_hunk (script, 436170754Sdelphij &first0, &last0, &first1, &last1); 437170754Sdelphij 438170754Sdelphij /* Reconnect the chain as before. */ 439170754Sdelphij script->link = next; 440170754Sdelphij 441170754Sdelphij /* Advance to the following change. */ 442170754Sdelphij script = next; 443170754Sdelphij } 444170754Sdelphij} 445170754Sdelphij 446170754Sdelphij/* Find the last function-header line in LINBUF prior to line number LINENUM. 447170754Sdelphij This is a line containing a match for the regexp in `function_regexp'. 448170754Sdelphij Return the address of the text, or 0 if no function-header is found. */ 449170754Sdelphij 450170754Sdelphijstatic char const * 451170754Sdelphijfind_function (char const * const *linbuf, lin linenum) 452170754Sdelphij{ 453170754Sdelphij lin i = linenum; 454170754Sdelphij lin last = find_function_last_search; 455170754Sdelphij find_function_last_search = i; 456170754Sdelphij 457170754Sdelphij while (last <= --i) 458170754Sdelphij { 459170754Sdelphij /* See if this line is what we want. */ 460170754Sdelphij char const *line = linbuf[i]; 461170754Sdelphij size_t linelen = linbuf[i + 1] - line - 1; 462170754Sdelphij 463170754Sdelphij /* FIXME: re_search's size args should be size_t, not int. */ 464170754Sdelphij int len = MIN (linelen, INT_MAX); 465170754Sdelphij 466170754Sdelphij if (0 <= re_search (&function_regexp, line, len, 0, len, 0)) 467170754Sdelphij { 468170754Sdelphij find_function_last_match = i; 469170754Sdelphij return line; 470170754Sdelphij } 471170754Sdelphij } 472170754Sdelphij /* If we search back to where we started searching the previous time, 473170754Sdelphij find the line we found last time. */ 474170754Sdelphij if (find_function_last_match != LIN_MAX) 475170754Sdelphij return linbuf[find_function_last_match]; 476170754Sdelphij 477170754Sdelphij return 0; 478170754Sdelphij} 479