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