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