132785Speter/* Context-format output routines for GNU DIFF. 244852Speter Copyright (C) 1988,1989,1991,1992,1993,1994,1998 Free Software Foundation, Inc. 332785Speter 432785SpeterThis file is part of GNU DIFF. 532785Speter 632785SpeterGNU DIFF is free software; you can redistribute it and/or modify 732785Speterit under the terms of the GNU General Public License as published by 832785Speterthe Free Software Foundation; either version 2, or (at your option) 932785Speterany later version. 1032785Speter 1132785SpeterGNU DIFF is distributed in the hope that it will be useful, 1232785Speterbut WITHOUT ANY WARRANTY; without even the implied warranty of 1332785SpeterMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1432785SpeterGNU General Public License for more details. 1532785Speter 1654427Speter*/ 1732785Speter 1832785Speter#include "diff.h" 1932785Speter 2032785Speterstatic struct change *find_hunk PARAMS((struct change *)); 2132785Speterstatic void find_function PARAMS((struct file_data const *, int, char const **, size_t *)); 2232785Speterstatic void mark_ignorable PARAMS((struct change *)); 2332785Speterstatic void pr_context_hunk PARAMS((struct change *)); 2432785Speterstatic void pr_unidiff_hunk PARAMS((struct change *)); 2532785Speterstatic void print_context_label PARAMS ((char const *, struct file_data *, char const *)); 2632785Speterstatic void print_context_number_range PARAMS((struct file_data const *, int, int)); 2732785Speterstatic void print_unidiff_number_range PARAMS((struct file_data const *, int, int)); 2832785Speter 2932785Speter/* Last place find_function started searching from. */ 3032785Speterstatic int find_function_last_search; 3132785Speter 3232785Speter/* The value find_function returned when it started searching there. */ 3332785Speterstatic int find_function_last_match; 3432785Speter 3532785Speter/* Print a label for a context diff, with a file name and date or a label. */ 3632785Speter 3732785Speterstatic void 3832785Speterprint_context_label (mark, inf, label) 3932785Speter char const *mark; 4032785Speter struct file_data *inf; 4132785Speter char const *label; 4232785Speter{ 4332785Speter if (label) 4444852Speter printf_output ("%s %s\n", mark, label); 4532785Speter else 4632785Speter { 4732785Speter char const *ct = ctime (&inf->stat.st_mtime); 4832785Speter if (!ct) 4932785Speter ct = "?\n"; 5032785Speter /* See Posix.2 section 4.17.6.1.4 for this format. */ 5144852Speter printf_output ("%s %s\t%s", mark, inf->name, ct); 5232785Speter } 5332785Speter} 5432785Speter 5532785Speter/* Print a header for a context diff, with the file names and dates. */ 5632785Speter 5732785Spetervoid 5832785Speterprint_context_header (inf, unidiff_flag) 5932785Speter struct file_data inf[]; 6032785Speter int unidiff_flag; 6132785Speter{ 6232785Speter if (unidiff_flag) 6332785Speter { 6432785Speter print_context_label ("---", &inf[0], file_label[0]); 6532785Speter print_context_label ("+++", &inf[1], file_label[1]); 6632785Speter } 6732785Speter else 6832785Speter { 6932785Speter print_context_label ("***", &inf[0], file_label[0]); 7032785Speter print_context_label ("---", &inf[1], file_label[1]); 7132785Speter } 7232785Speter} 7332785Speter 7432785Speter/* Print an edit script in context format. */ 7532785Speter 7632785Spetervoid 7732785Speterprint_context_script (script, unidiff_flag) 7832785Speter struct change *script; 7932785Speter int unidiff_flag; 8032785Speter{ 8132785Speter if (ignore_blank_lines_flag || ignore_regexp_list) 8232785Speter mark_ignorable (script); 8332785Speter else 8432785Speter { 8532785Speter struct change *e; 8632785Speter for (e = script; e; e = e->link) 8732785Speter e->ignore = 0; 8832785Speter } 8932785Speter 9032785Speter find_function_last_search = - files[0].prefix_lines; 9132785Speter find_function_last_match = find_function_last_search - 1; 9232785Speter 9332785Speter if (unidiff_flag) 9432785Speter print_script (script, find_hunk, pr_unidiff_hunk); 9532785Speter else 9632785Speter print_script (script, find_hunk, pr_context_hunk); 9732785Speter} 9832785Speter 9932785Speter/* Print a pair of line numbers with a comma, translated for file FILE. 10032785Speter If the second number is not greater, use the first in place of it. 10132785Speter 10232785Speter Args A and B are internal line numbers. 10332785Speter We print the translated (real) line numbers. */ 10432785Speter 10532785Speterstatic void 10632785Speterprint_context_number_range (file, a, b) 10732785Speter struct file_data const *file; 10832785Speter int a, b; 10932785Speter{ 11032785Speter int trans_a, trans_b; 11132785Speter translate_range (file, a, b, &trans_a, &trans_b); 11232785Speter 11332785Speter /* Note: we can have B < A in the case of a range of no lines. 11432785Speter In this case, we should print the line number before the range, 11532785Speter which is B. */ 11632785Speter if (trans_b > trans_a) 11744852Speter printf_output ("%d,%d", trans_a, trans_b); 11832785Speter else 11944852Speter printf_output ("%d", trans_b); 12032785Speter} 12132785Speter 12232785Speter/* Print a portion of an edit script in context format. 12332785Speter HUNK is the beginning of the portion to be printed. 12432785Speter The end is marked by a `link' that has been nulled out. 12532785Speter 12632785Speter Prints out lines from both files, and precedes each 12732785Speter line with the appropriate flag-character. */ 12832785Speter 12932785Speterstatic void 13032785Speterpr_context_hunk (hunk) 13132785Speter struct change *hunk; 13232785Speter{ 13332785Speter int first0, last0, first1, last1, show_from, show_to, i; 13432785Speter struct change *next; 13532785Speter char const *prefix; 13632785Speter char const *function; 13732785Speter size_t function_length; 13832785Speter 13932785Speter /* Determine range of line numbers involved in each file. */ 14032785Speter 14132785Speter analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to); 14232785Speter 14332785Speter if (!show_from && !show_to) 14432785Speter return; 14532785Speter 14632785Speter /* Include a context's width before and after. */ 14732785Speter 14832785Speter i = - files[0].prefix_lines; 14932785Speter first0 = max (first0 - context, i); 15032785Speter first1 = max (first1 - context, i); 15132785Speter last0 = min (last0 + context, files[0].valid_lines - 1); 15232785Speter last1 = min (last1 + context, files[1].valid_lines - 1); 15332785Speter 15432785Speter /* If desired, find the preceding function definition line in file 0. */ 15532785Speter function = 0; 15632785Speter if (function_regexp_list) 15732785Speter find_function (&files[0], first0, &function, &function_length); 15832785Speter 15932785Speter begin_output (); 16032785Speter 16132785Speter /* If we looked for and found a function this is part of, 16232785Speter include its name in the header of the diff section. */ 16344852Speter printf_output ("***************"); 16432785Speter 16532785Speter if (function) 16632785Speter { 16744852Speter printf_output (" "); 16844852Speter write_output (function, min (function_length - 1, 40)); 16932785Speter } 17032785Speter 17144852Speter printf_output ("\n*** "); 17232785Speter print_context_number_range (&files[0], first0, last0); 17344852Speter printf_output (" ****\n"); 17432785Speter 17532785Speter if (show_from) 17632785Speter { 17732785Speter next = hunk; 17832785Speter 17932785Speter for (i = first0; i <= last0; i++) 18032785Speter { 18132785Speter /* Skip past changes that apply (in file 0) 18232785Speter only to lines before line I. */ 18332785Speter 18432785Speter while (next && next->line0 + next->deleted <= i) 18532785Speter next = next->link; 18632785Speter 18732785Speter /* Compute the marking for line I. */ 18832785Speter 18932785Speter prefix = " "; 19032785Speter if (next && next->line0 <= i) 19132785Speter /* The change NEXT covers this line. 19232785Speter If lines were inserted here in file 1, this is "changed". 19332785Speter Otherwise it is "deleted". */ 19432785Speter prefix = (next->inserted > 0 ? "!" : "-"); 19532785Speter 19632785Speter print_1_line (prefix, &files[0].linbuf[i]); 19732785Speter } 19832785Speter } 19932785Speter 20044852Speter printf_output ("--- "); 20132785Speter print_context_number_range (&files[1], first1, last1); 20244852Speter printf_output (" ----\n"); 20332785Speter 20432785Speter if (show_to) 20532785Speter { 20632785Speter next = hunk; 20732785Speter 20832785Speter for (i = first1; i <= last1; i++) 20932785Speter { 21032785Speter /* Skip past changes that apply (in file 1) 21132785Speter only to lines before line I. */ 21232785Speter 21332785Speter while (next && next->line1 + next->inserted <= i) 21432785Speter next = next->link; 21532785Speter 21632785Speter /* Compute the marking for line I. */ 21732785Speter 21832785Speter prefix = " "; 21932785Speter if (next && next->line1 <= i) 22032785Speter /* The change NEXT covers this line. 22132785Speter If lines were deleted here in file 0, this is "changed". 22232785Speter Otherwise it is "inserted". */ 22332785Speter prefix = (next->deleted > 0 ? "!" : "+"); 22432785Speter 22532785Speter print_1_line (prefix, &files[1].linbuf[i]); 22632785Speter } 22732785Speter } 22832785Speter} 22932785Speter 23032785Speter/* Print a pair of line numbers with a comma, translated for file FILE. 23132785Speter If the second number is smaller, use the first in place of it. 23232785Speter If the numbers are equal, print just one number. 23332785Speter 23432785Speter Args A and B are internal line numbers. 23532785Speter We print the translated (real) line numbers. */ 23632785Speter 23732785Speterstatic void 23832785Speterprint_unidiff_number_range (file, a, b) 23932785Speter struct file_data const *file; 24032785Speter int a, b; 24132785Speter{ 24232785Speter int trans_a, trans_b; 24332785Speter translate_range (file, a, b, &trans_a, &trans_b); 24432785Speter 24532785Speter /* Note: we can have B < A in the case of a range of no lines. 24632785Speter In this case, we should print the line number before the range, 24732785Speter which is B. */ 24832785Speter if (trans_b <= trans_a) 24944852Speter printf_output (trans_b == trans_a ? "%d" : "%d,0", trans_b); 25032785Speter else 25144852Speter printf_output ("%d,%d", trans_a, trans_b - trans_a + 1); 25232785Speter} 25332785Speter 25432785Speter/* Print a portion of an edit script in unidiff format. 25532785Speter HUNK is the beginning of the portion to be printed. 25632785Speter The end is marked by a `link' that has been nulled out. 25732785Speter 25832785Speter Prints out lines from both files, and precedes each 25932785Speter line with the appropriate flag-character. */ 26032785Speter 26132785Speterstatic void 26232785Speterpr_unidiff_hunk (hunk) 26332785Speter struct change *hunk; 26432785Speter{ 26532785Speter int first0, last0, first1, last1, show_from, show_to, i, j, k; 26632785Speter struct change *next; 26732785Speter char const *function; 26832785Speter size_t function_length; 26932785Speter 27032785Speter /* Determine range of line numbers involved in each file. */ 27132785Speter 27232785Speter analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to); 27332785Speter 27432785Speter if (!show_from && !show_to) 27532785Speter return; 27632785Speter 27732785Speter /* Include a context's width before and after. */ 27832785Speter 27932785Speter i = - files[0].prefix_lines; 28032785Speter first0 = max (first0 - context, i); 28132785Speter first1 = max (first1 - context, i); 28232785Speter last0 = min (last0 + context, files[0].valid_lines - 1); 28332785Speter last1 = min (last1 + context, files[1].valid_lines - 1); 28432785Speter 28532785Speter /* If desired, find the preceding function definition line in file 0. */ 28632785Speter function = 0; 28732785Speter if (function_regexp_list) 28832785Speter find_function (&files[0], first0, &function, &function_length); 28932785Speter 29032785Speter begin_output (); 29132785Speter 29244852Speter printf_output ("@@ -"); 29332785Speter print_unidiff_number_range (&files[0], first0, last0); 29444852Speter printf_output (" +"); 29532785Speter print_unidiff_number_range (&files[1], first1, last1); 29644852Speter printf_output (" @@"); 29732785Speter 29832785Speter /* If we looked for and found a function this is part of, 29932785Speter include its name in the header of the diff section. */ 30032785Speter 30132785Speter if (function) 30232785Speter { 30344852Speter write_output (" ", 1); 30444852Speter write_output (function, min (function_length - 1, 40)); 30532785Speter } 30644852Speter write_output ("\n", 1); 30732785Speter 30832785Speter next = hunk; 30932785Speter i = first0; 31032785Speter j = first1; 31132785Speter 31232785Speter while (i <= last0 || j <= last1) 31332785Speter { 31432785Speter 31532785Speter /* If the line isn't a difference, output the context from file 0. */ 31632785Speter 31732785Speter if (!next || i < next->line0) 31832785Speter { 31944852Speter write_output (tab_align_flag ? "\t" : " ", 1); 32032785Speter print_1_line (0, &files[0].linbuf[i++]); 32132785Speter j++; 32232785Speter } 32332785Speter else 32432785Speter { 32532785Speter /* For each difference, first output the deleted part. */ 32632785Speter 32732785Speter k = next->deleted; 32832785Speter while (k--) 32932785Speter { 33044852Speter write_output ("-", 1); 33132785Speter if (tab_align_flag) 33244852Speter write_output ("\t", 1); 33332785Speter print_1_line (0, &files[0].linbuf[i++]); 33432785Speter } 33532785Speter 33632785Speter /* Then output the inserted part. */ 33732785Speter 33832785Speter k = next->inserted; 33932785Speter while (k--) 34032785Speter { 34144852Speter write_output ("+", 1); 34232785Speter if (tab_align_flag) 34344852Speter write_output ("\t", 1); 34432785Speter print_1_line (0, &files[1].linbuf[j++]); 34532785Speter } 34632785Speter 34732785Speter /* We're done with this hunk, so on to the next! */ 34832785Speter 34932785Speter next = next->link; 35032785Speter } 35132785Speter } 35232785Speter} 35332785Speter 35432785Speter/* Scan a (forward-ordered) edit script for the first place that more than 35532785Speter 2*CONTEXT unchanged lines appear, and return a pointer 35632785Speter to the `struct change' for the last change before those lines. */ 35732785Speter 35832785Speterstatic struct change * 35932785Speterfind_hunk (start) 36032785Speter struct change *start; 36132785Speter{ 36232785Speter struct change *prev; 36332785Speter int top0, top1; 36432785Speter int thresh; 36532785Speter 36632785Speter do 36732785Speter { 36832785Speter /* Compute number of first line in each file beyond this changed. */ 36932785Speter top0 = start->line0 + start->deleted; 37032785Speter top1 = start->line1 + start->inserted; 37132785Speter prev = start; 37232785Speter start = start->link; 37332785Speter /* Threshold distance is 2*CONTEXT between two non-ignorable changes, 37432785Speter but only CONTEXT if one is ignorable. */ 37532785Speter thresh = ((prev->ignore || (start && start->ignore)) 37632785Speter ? context 37732785Speter : 2 * context + 1); 37832785Speter /* It is not supposed to matter which file we check in the end-test. 37932785Speter If it would matter, crash. */ 38032785Speter if (start && start->line0 - top0 != start->line1 - top1) 38132785Speter abort (); 38232785Speter } while (start 38332785Speter /* Keep going if less than THRESH lines 38432785Speter elapse before the affected line. */ 38532785Speter && start->line0 < top0 + thresh); 38632785Speter 38732785Speter return prev; 38832785Speter} 38932785Speter 39032785Speter/* Set the `ignore' flag properly in each change in SCRIPT. 39132785Speter It should be 1 if all the lines inserted or deleted in that change 39232785Speter are ignorable lines. */ 39332785Speter 39432785Speterstatic void 39532785Spetermark_ignorable (script) 39632785Speter struct change *script; 39732785Speter{ 39832785Speter while (script) 39932785Speter { 40032785Speter struct change *next = script->link; 40132785Speter int first0, last0, first1, last1, deletes, inserts; 40232785Speter 40332785Speter /* Turn this change into a hunk: detach it from the others. */ 40432785Speter script->link = 0; 40532785Speter 40632785Speter /* Determine whether this change is ignorable. */ 40732785Speter analyze_hunk (script, &first0, &last0, &first1, &last1, &deletes, &inserts); 40832785Speter /* Reconnect the chain as before. */ 40932785Speter script->link = next; 41032785Speter 41132785Speter /* If the change is ignorable, mark it. */ 41232785Speter script->ignore = (!deletes && !inserts); 41332785Speter 41432785Speter /* Advance to the following change. */ 41532785Speter script = next; 41632785Speter } 41732785Speter} 41832785Speter 41932785Speter/* Find the last function-header line in FILE prior to line number LINENUM. 42032785Speter This is a line containing a match for the regexp in `function_regexp'. 42132785Speter Store the address of the line text into LINEP and the length of the 42232785Speter line into LENP. 42332785Speter Do not store anything if no function-header is found. */ 42432785Speter 42532785Speterstatic void 42632785Speterfind_function (file, linenum, linep, lenp) 42732785Speter struct file_data const *file; 42832785Speter int linenum; 42932785Speter char const **linep; 43032785Speter size_t *lenp; 43132785Speter{ 43232785Speter int i = linenum; 43332785Speter int last = find_function_last_search; 43432785Speter find_function_last_search = i; 43532785Speter 43632785Speter while (--i >= last) 43732785Speter { 43832785Speter /* See if this line is what we want. */ 43932785Speter struct regexp_list *r; 44032785Speter char const *line = file->linbuf[i]; 44132785Speter size_t len = file->linbuf[i + 1] - line; 44232785Speter 44332785Speter for (r = function_regexp_list; r; r = r->next) 44432785Speter if (0 <= re_search (&r->buf, line, len, 0, len, 0)) 44532785Speter { 44632785Speter *linep = line; 44732785Speter *lenp = len; 44832785Speter find_function_last_match = i; 44932785Speter return; 45032785Speter } 45132785Speter } 45232785Speter /* If we search back to where we started searching the previous time, 45332785Speter find the line we found last time. */ 45432785Speter if (find_function_last_match >= - file->prefix_lines) 45532785Speter { 45632785Speter i = find_function_last_match; 45732785Speter *linep = file->linbuf[i]; 45832785Speter *lenp = file->linbuf[i + 1] - *linep; 45932785Speter return; 46032785Speter } 46132785Speter return; 46232785Speter} 463