Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/* Context-format output routines for GNU DIFF.
2
   Copyright (C) 1988,1989,1991,1992,1993,1994 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
    fprintf (outfile, "%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
      fprintf (outfile, "%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
    fprintf (outfile, "%d,%d", trans_a, trans_b);
120
  else
121
    fprintf (outfile, "%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
  FILE *out;
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
  out = outfile;
164
 
165
  /* If we looked for and found a function this is part of,
166
     include its name in the header of the diff section.  */
167
  fprintf (out, "***************");
168
 
169
  if (function)
170
    {
171
      fprintf (out, " ");
172
      fwrite (function, 1, min (function_length - 1, 40), out);
173
    }
174
 
175
  fprintf (out, "\n*** ");
176
  print_context_number_range (&files[0], first0, last0);
177
  fprintf (out, " ****\n");
178
 
179
  if (show_from)
180
    {
181
      next = hunk;
182
 
183
      for (i = first0; i <= last0; i++)
184
	{
185
	  /* Skip past changes that apply (in file 0)
186
	     only to lines before line I.  */
187
 
188
	  while (next && next->line0 + next->deleted <= i)
189
	    next = next->link;
190
 
191
	  /* Compute the marking for line I.  */
192
 
193
	  prefix = " ";
194
	  if (next && next->line0 <= i)
195
	    /* The change NEXT covers this line.
196
	       If lines were inserted here in file 1, this is "changed".
197
	       Otherwise it is "deleted".  */
198
	    prefix = (next->inserted > 0 ? "!" : "-");
199
 
200
	  print_1_line (prefix, &files[0].linbuf[i]);
201
	}
202
    }
203
 
204
  fprintf (out, "--- ");
205
  print_context_number_range (&files[1], first1, last1);
206
  fprintf (out, " ----\n");
207
 
208
  if (show_to)
209
    {
210
      next = hunk;
211
 
212
      for (i = first1; i <= last1; i++)
213
	{
214
	  /* Skip past changes that apply (in file 1)
215
	     only to lines before line I.  */
216
 
217
	  while (next && next->line1 + next->inserted <= i)
218
	    next = next->link;
219
 
220
	  /* Compute the marking for line I.  */
221
 
222
	  prefix = " ";
223
	  if (next && next->line1 <= i)
224
	    /* The change NEXT covers this line.
225
	       If lines were deleted here in file 0, this is "changed".
226
	       Otherwise it is "inserted".  */
227
	    prefix = (next->deleted > 0 ? "!" : "+");
228
 
229
	  print_1_line (prefix, &files[1].linbuf[i]);
230
	}
231
    }
232
}
233
 
234
/* 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
    fprintf (outfile, trans_b == trans_a ? "%d" : "%d,0", trans_b);
254
  else
255
    fprintf (outfile, "%d,%d", trans_a, trans_b - trans_a + 1);
256
}
257
 
258
/* Print a portion of an edit script in unidiff format.
259
   HUNK is the beginning of the portion to be printed.
260
   The end is marked by a `link' that has been nulled out.
261
 
262
   Prints out lines from both files, and precedes each
263
   line with the appropriate flag-character.  */
264
 
265
static void
266
pr_unidiff_hunk (hunk)
267
     struct change *hunk;
268
{
269
  int first0, last0, first1, last1, show_from, show_to, i, j, k;
270
  struct change *next;
271
  char const *function;
272
  size_t function_length;
273
  FILE *out;
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
  out = outfile;
297
 
298
  fprintf (out, "@@ -");
299
  print_unidiff_number_range (&files[0], first0, last0);
300
  fprintf (out, " +");
301
  print_unidiff_number_range (&files[1], first1, last1);
302
  fprintf (out, " @@");
303
 
304
  /* If we looked for and found a function this is part of,
305
     include its name in the header of the diff section.  */
306
 
307
  if (function)
308
    {
309
      putc (' ', out);
310
      fwrite (function, 1, min (function_length - 1, 40), out);
311
    }
312
  putc ('\n', out);
313
 
314
  next = hunk;
315
  i = first0;
316
  j = first1;
317
 
318
  while (i <= last0 || j <= last1)
319
    {
320
 
321
      /* If the line isn't a difference, output the context from file 0. */
322
 
323
      if (!next || i < next->line0)
324
	{
325
	  putc (tab_align_flag ? '\t' : ' ', out);
326
	  print_1_line (0, &files[0].linbuf[i++]);
327
	  j++;
328
	}
329
      else
330
	{
331
	  /* For each difference, first output the deleted part. */
332
 
333
	  k = next->deleted;
334
	  while (k--)
335
	    {
336
	      putc ('-', out);
337
	      if (tab_align_flag)
338
		putc ('\t', out);
339
	      print_1_line (0, &files[0].linbuf[i++]);
340
	    }
341
 
342
	  /* Then output the inserted part. */
343
 
344
	  k = next->inserted;
345
	  while (k--)
346
	    {
347
	      putc ('+', out);
348
	      if (tab_align_flag)
349
		putc ('\t', out);
350
	      print_1_line (0, &files[1].linbuf[j++]);
351
	    }
352
 
353
	  /* We're done with this hunk, so on to the next! */
354
 
355
	  next = next->link;
356
	}
357
    }
358
}
359
 
360
/* 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.
426
   This is a line containing a match for the regexp in `function_regexp'.
427
   Store the address of the line text into LINEP and the length of the
428
   line into LENP.
429
   Do not store anything if no function-header is found.  */
430
 
431
static void
432
find_function (file, linenum, linep, lenp)
433
     struct file_data const *file;
434
     int linenum;
435
     char const **linep;
436
     size_t *lenp;
437
{
438
  int i = linenum;
439
  int last = find_function_last_search;
440
  find_function_last_search = i;
441
 
442
  while (--i >= last)
443
    {
444
      /* See if this line is what we want.  */
445
      struct regexp_list *r;
446
      char const *line = file->linbuf[i];
447
      size_t len = file->linbuf[i + 1] - line;
448
 
449
      for (r = function_regexp_list; r; r = r->next)
450
	if (0 <= re_search (&r->buf, line, len, 0, len, 0))
451
	  {
452
	    *linep = line;
453
	    *lenp = len;
454
	    find_function_last_match = i;
455
	    return;
456
	  }
457
    }
458
  /* If we search back to where we started searching the previous time,
459
     find the line we found last time.  */
460
  if (find_function_last_match >= - file->prefix_lines)
461
    {
462
      i = find_function_last_match;
463
      *linep = file->linbuf[i];
464
      *lenp = file->linbuf[i + 1] - *linep;
465
      return;
466
    }
467
  return;
468
}