3 /* #ifdef-format output routines for GNU DIFF.
5 Copyright (C) 1989, 1991, 1992, 1993, 1994, 2001, 2002 Free
6 Software Foundation, Inc.
8 This file is part of GNU DIFF.
10 GNU DIFF is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY. No author or distributor
12 accepts responsibility to anyone for the consequences of using it
13 or for whether it serves any particular purpose or works at all,
14 unless he says so in writing. Refer to the GNU DIFF General Public
15 License for full details.
17 Everyone is granted permission to copy, modify and redistribute
18 GNU DIFF, but only under the conditions described in the
19 GNU DIFF General Public License. A copy of this license is
20 supposed to have been given to you along with GNU DIFF so you
21 can know your rights and responsibilities. It should be in a
22 file named COPYING. Among other things, the copyright notice
23 and this notice must be preserved on all copies. */
31 struct file_data
const *file
;
32 lin from
, upto
; /* start and limit lines for this group of lines */
35 static char const *format_group (FILE *, char const *, char,
36 struct group
const *);
37 static char const *do_printf_spec (FILE *, char const *,
38 struct file_data
const *, lin
,
39 struct group
const *);
40 static char const *scan_char_literal (char const *, char *);
41 static lin
groups_letter_value (struct group
const *, char);
42 static void format_ifdef (char const *, lin
, lin
, lin
, lin
);
43 static void print_ifdef_hunk (struct change
*);
44 static void print_ifdef_lines (FILE *, char const *, struct group
const *);
48 /* Print the edit-script SCRIPT as a merged #ifdef file. */
51 print_ifdef_script (struct change
*script
)
53 next_line
= - files
[0].prefix_lines
;
54 print_script (script
, find_change
, print_ifdef_hunk
);
55 if (next_line
< files
[0].valid_lines
)
58 format_ifdef (group_format
[UNCHANGED
], next_line
, files
[0].valid_lines
,
59 next_line
- files
[0].valid_lines
+ files
[1].valid_lines
,
60 files
[1].valid_lines
);
64 /* Print a hunk of an ifdef diff.
65 This is a contiguous portion of a complete edit script,
66 describing changes in consecutive lines. */
69 print_ifdef_hunk (struct change
*hunk
)
71 lin first0
, last0
, first1
, last1
;
73 /* Determine range of line numbers involved in each file. */
74 enum changes changes
= analyze_hunk (hunk
, &first0
, &last0
, &first1
, &last1
);
80 /* Print lines up to this change. */
81 if (next_line
< first0
)
82 format_ifdef (group_format
[UNCHANGED
], next_line
, first0
,
83 next_line
- first0
+ first1
, first1
);
85 /* Print this change. */
86 next_line
= last0
+ 1;
87 format_ifdef (group_format
[changes
], first0
, next_line
, first1
, last1
+ 1);
90 /* Print a set of lines according to FORMAT.
91 Lines BEG0 up to END0 are from the first file;
92 lines BEG1 up to END1 are from the second file. */
95 format_ifdef (char const *format
, lin beg0
, lin end0
, lin beg1
, lin end1
)
97 struct group groups
[2];
99 groups
[0].file
= &files
[0];
100 groups
[0].from
= beg0
;
101 groups
[0].upto
= end0
;
102 groups
[1].file
= &files
[1];
103 groups
[1].from
= beg1
;
104 groups
[1].upto
= end1
;
105 format_group (outfile
, format
, 0, groups
);
108 /* Print to file OUT a set of lines according to FORMAT.
109 The format ends at the first free instance of ENDCHAR.
110 Yield the address of the terminating character.
111 GROUPS specifies which lines to print.
112 If OUT is zero, do not actually print anything; just scan the format. */
115 format_group (register FILE *out
, char const *format
, char endchar
,
116 struct group
const *groups
)
119 register char const *f
= format
;
121 while ((c
= *f
) != endchar
&& c
!= 0)
123 char const *f1
= ++f
;
131 /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'. */
135 FILE *thenout
, *elseout
;
137 for (i
= 0; i
< 2; i
++)
143 value
[i
] = strtoumax (f
, &fend
, 10);
150 value
[i
] = groups_letter_value (groups
, *f
);
158 if (value
[0] == value
[1])
159 thenout
= out
, elseout
= 0;
161 thenout
= 0, elseout
= out
;
162 f
= format_group (thenout
, f
, ':', groups
);
165 f
= format_group (elseout
, f
+ 1, ')', groups
);
173 /* Print lines deleted from first file. */
174 print_ifdef_lines (out
, line_format
[OLD
], &groups
[0]);
178 /* Print common lines. */
179 print_ifdef_lines (out
, line_format
[UNCHANGED
], &groups
[0]);
183 /* Print lines inserted from second file. */
184 print_ifdef_lines (out
, line_format
[NEW
], &groups
[1]);
188 f
= do_printf_spec (out
, f
- 2, 0, 0, groups
);
205 /* For the line group pair G, return the number corresponding to LETTER.
206 Return -1 if LETTER is not a group format letter. */
208 groups_letter_value (struct group
const *g
, char letter
)
212 case 'E': letter
= 'e'; g
++; break;
213 case 'F': letter
= 'f'; g
++; break;
214 case 'L': letter
= 'l'; g
++; break;
215 case 'M': letter
= 'm'; g
++; break;
216 case 'N': letter
= 'n'; g
++; break;
221 case 'e': return translate_line_number (g
->file
, g
->from
) - 1;
222 case 'f': return translate_line_number (g
->file
, g
->from
);
223 case 'l': return translate_line_number (g
->file
, g
->upto
) - 1;
224 case 'm': return translate_line_number (g
->file
, g
->upto
);
225 case 'n': return g
->upto
- g
->from
;
230 /* Print to file OUT, using FORMAT to print the line group GROUP.
231 But do nothing if OUT is zero. */
233 print_ifdef_lines (register FILE *out
, char const *format
,
234 struct group
const *group
)
236 struct file_data
const *file
= group
->file
;
237 char const * const *linbuf
= file
->linbuf
;
238 lin from
= group
->from
, upto
= group
->upto
;
243 /* If possible, use a single fwrite; it's faster. */
244 if (!expand_tabs
&& format
[0] == '%')
246 if (format
[1] == 'l' && format
[2] == '\n' && !format
[3] && from
< upto
)
248 fwrite (linbuf
[from
], sizeof (char),
249 linbuf
[upto
] + (linbuf
[upto
][-1] != '\n') - linbuf
[from
],
253 if (format
[1] == 'L' && !format
[2])
255 fwrite (linbuf
[from
], sizeof (char),
256 linbuf
[upto
] - linbuf
[from
], out
);
261 for (; from
< upto
; from
++)
264 register char const *f
= format
;
266 while ((c
= *f
++) != 0)
276 output_1_line (linbuf
[from
],
278 - (linbuf
[from
+ 1][-1] == '\n')),
283 output_1_line (linbuf
[from
], linbuf
[from
+ 1], 0, 0);
287 f
= do_printf_spec (out
, f
- 2, file
, from
, 0);
301 do_printf_spec (FILE *out
, char const *spec
,
302 struct file_data
const *file
, lin n
,
303 struct group
const *groups
)
305 char const *f
= spec
;
309 /* Scan printf-style SPEC of the form %[-'0]*[0-9]*(.[0-9]*)?[cdoxX]. */
310 /* assert (*f == '%'); */
312 while ((c
= *f
++) == '-' || c
== '\'' || c
== '0')
317 while (ISDIGIT (c
= *f
++))
329 f
= scan_char_literal (f
, &value
);
337 case 'd': case 'o': case 'x': case 'X':
345 value
= translate_line_number (file
, n
);
349 value
= groups_letter_value (groups
, c1
);
356 /* For example, if the spec is "%3xn", use the printf
357 format spec "%3lx". Here the spec prefix is "%3". */
358 long long_value
= value
;
359 size_t spec_prefix_len
= f
- spec
- 2;
361 char format
[spec_prefix_len
+ 3];
363 char *format
= xmalloc (spec_prefix_len
+ 3);
365 char *p
= format
+ spec_prefix_len
;
366 memcpy (format
, spec
, spec_prefix_len
);
370 fprintf (out
, format
, long_value
);
371 #if ! HAVE_C_VARARRAYS
385 /* Scan the character literal represented in the string LIT; LIT points just
386 after the initial apostrophe. Put the literal's value into *VALPTR.
387 Yield the address of the first character after the closing apostrophe,
388 or zero if the literal is ill-formed. */
390 scan_char_literal (char const *lit
, char *valptr
)
392 register char const *p
= lit
;
405 while ((c
= *p
++) != '\'')
407 unsigned int digit
= c
- '0';
410 value
= 8 * value
+ digit
;
412 digits
= p
- lit
- 2;
413 if (! (1 <= digits
&& digits
<= 3))