1 /* #ifdef-format output routines for GNU DIFF.
3 Copyright (C) 1989, 1991, 1992, 1993, 1994, 2001, 2002 Free
4 Software Foundation, Inc.
6 This file is part of GNU DIFF.
8 GNU DIFF is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY. No author or distributor
10 accepts responsibility to anyone for the consequences of using it
11 or for whether it serves any particular purpose or works at all,
12 unless he says so in writing. Refer to the GNU DIFF General Public
13 License for full details.
15 Everyone is granted permission to copy, modify and redistribute
16 GNU DIFF, but only under the conditions described in the
17 GNU DIFF General Public License. A copy of this license is
18 supposed to have been given to you along with GNU DIFF so you
19 can know your rights and responsibilities. It should be in a
20 file named COPYING. Among other things, the copyright notice
21 and this notice must be preserved on all copies. */
29 struct file_data
const *file
;
30 lin from
, upto
; /* start and limit lines for this group of lines */
33 static char const *format_group (FILE *, char const *, char,
34 struct group
const *);
35 static char const *do_printf_spec (FILE *, char const *,
36 struct file_data
const *, lin
,
37 struct group
const *);
38 static char const *scan_char_literal (char const *, char *);
39 static lin
groups_letter_value (struct group
const *, char);
40 static void format_ifdef (char const *, lin
, lin
, lin
, lin
);
41 static void print_ifdef_hunk (struct change
*);
42 static void print_ifdef_lines (FILE *, char const *, struct group
const *);
46 /* Print the edit-script SCRIPT as a merged #ifdef file. */
49 print_ifdef_script (struct change
*script
)
51 next_line
= - files
[0].prefix_lines
;
52 print_script (script
, find_change
, print_ifdef_hunk
);
53 if (next_line
< files
[0].valid_lines
)
56 format_ifdef (group_format
[UNCHANGED
], next_line
, files
[0].valid_lines
,
57 next_line
- files
[0].valid_lines
+ files
[1].valid_lines
,
58 files
[1].valid_lines
);
62 /* Print a hunk of an ifdef diff.
63 This is a contiguous portion of a complete edit script,
64 describing changes in consecutive lines. */
67 print_ifdef_hunk (struct change
*hunk
)
69 lin first0
, last0
, first1
, last1
;
71 /* Determine range of line numbers involved in each file. */
72 enum changes changes
= analyze_hunk (hunk
, &first0
, &last0
, &first1
, &last1
);
78 /* Print lines up to this change. */
79 if (next_line
< first0
)
80 format_ifdef (group_format
[UNCHANGED
], next_line
, first0
,
81 next_line
- first0
+ first1
, first1
);
83 /* Print this change. */
84 next_line
= last0
+ 1;
85 format_ifdef (group_format
[changes
], first0
, next_line
, first1
, last1
+ 1);
88 /* Print a set of lines according to FORMAT.
89 Lines BEG0 up to END0 are from the first file;
90 lines BEG1 up to END1 are from the second file. */
93 format_ifdef (char const *format
, lin beg0
, lin end0
, lin beg1
, lin end1
)
95 struct group groups
[2];
97 groups
[0].file
= &files
[0];
98 groups
[0].from
= beg0
;
99 groups
[0].upto
= end0
;
100 groups
[1].file
= &files
[1];
101 groups
[1].from
= beg1
;
102 groups
[1].upto
= end1
;
103 format_group (outfile
, format
, 0, groups
);
106 /* Print to file OUT a set of lines according to FORMAT.
107 The format ends at the first free instance of ENDCHAR.
108 Yield the address of the terminating character.
109 GROUPS specifies which lines to print.
110 If OUT is zero, do not actually print anything; just scan the format. */
113 format_group (register FILE *out
, char const *format
, char endchar
,
114 struct group
const *groups
)
117 register char const *f
= format
;
119 while ((c
= *f
) != endchar
&& c
!= 0)
121 char const *f1
= ++f
;
129 /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'. */
133 FILE *thenout
, *elseout
;
135 for (i
= 0; i
< 2; i
++)
141 value
[i
] = strtoumax (f
, &fend
, 10);
148 value
[i
] = groups_letter_value (groups
, *f
);
156 if (value
[0] == value
[1])
157 thenout
= out
, elseout
= 0;
159 thenout
= 0, elseout
= out
;
160 f
= format_group (thenout
, f
, ':', groups
);
163 f
= format_group (elseout
, f
+ 1, ')', groups
);
171 /* Print lines deleted from first file. */
172 print_ifdef_lines (out
, line_format
[OLD
], &groups
[0]);
176 /* Print common lines. */
177 print_ifdef_lines (out
, line_format
[UNCHANGED
], &groups
[0]);
181 /* Print lines inserted from second file. */
182 print_ifdef_lines (out
, line_format
[NEW
], &groups
[1]);
186 f
= do_printf_spec (out
, f
- 2, 0, 0, groups
);
203 /* For the line group pair G, return the number corresponding to LETTER.
204 Return -1 if LETTER is not a group format letter. */
206 groups_letter_value (struct group
const *g
, char letter
)
210 case 'E': letter
= 'e'; g
++; break;
211 case 'F': letter
= 'f'; g
++; break;
212 case 'L': letter
= 'l'; g
++; break;
213 case 'M': letter
= 'm'; g
++; break;
214 case 'N': letter
= 'n'; g
++; break;
219 case 'e': return translate_line_number (g
->file
, g
->from
) - 1;
220 case 'f': return translate_line_number (g
->file
, g
->from
);
221 case 'l': return translate_line_number (g
->file
, g
->upto
) - 1;
222 case 'm': return translate_line_number (g
->file
, g
->upto
);
223 case 'n': return g
->upto
- g
->from
;
228 /* Print to file OUT, using FORMAT to print the line group GROUP.
229 But do nothing if OUT is zero. */
231 print_ifdef_lines (register FILE *out
, char const *format
,
232 struct group
const *group
)
234 struct file_data
const *file
= group
->file
;
235 char const * const *linbuf
= file
->linbuf
;
236 lin from
= group
->from
, upto
= group
->upto
;
241 /* If possible, use a single fwrite; it's faster. */
242 if (!expand_tabs
&& format
[0] == '%')
244 if (format
[1] == 'l' && format
[2] == '\n' && !format
[3] && from
< upto
)
246 fwrite (linbuf
[from
], sizeof (char),
247 linbuf
[upto
] + (linbuf
[upto
][-1] != '\n') - linbuf
[from
],
251 if (format
[1] == 'L' && !format
[2])
253 fwrite (linbuf
[from
], sizeof (char),
254 linbuf
[upto
] - linbuf
[from
], out
);
259 for (; from
< upto
; from
++)
262 register char const *f
= format
;
264 while ((c
= *f
++) != 0)
274 output_1_line (linbuf
[from
],
276 - (linbuf
[from
+ 1][-1] == '\n')),
281 output_1_line (linbuf
[from
], linbuf
[from
+ 1], 0, 0);
285 f
= do_printf_spec (out
, f
- 2, file
, from
, 0);
299 do_printf_spec (FILE *out
, char const *spec
,
300 struct file_data
const *file
, lin n
,
301 struct group
const *groups
)
303 char const *f
= spec
;
307 /* Scan printf-style SPEC of the form %[-'0]*[0-9]*(.[0-9]*)?[cdoxX]. */
308 /* assert (*f == '%'); */
310 while ((c
= *f
++) == '-' || c
== '\'' || c
== '0')
315 while (ISDIGIT (c
= *f
++))
327 f
= scan_char_literal (f
, &value
);
335 case 'd': case 'o': case 'x': case 'X':
343 value
= translate_line_number (file
, n
);
347 value
= groups_letter_value (groups
, c1
);
354 /* For example, if the spec is "%3xn", use the printf
355 format spec "%3lx". Here the spec prefix is "%3". */
356 long long_value
= value
;
357 size_t spec_prefix_len
= f
- spec
- 2;
359 char format
[spec_prefix_len
+ 3];
361 char *format
= xmalloc (spec_prefix_len
+ 3);
363 char *p
= format
+ spec_prefix_len
;
364 memcpy (format
, spec
, spec_prefix_len
);
368 fprintf (out
, format
, long_value
);
369 #if ! HAVE_C_VARARRAYS
383 /* Scan the character literal represented in the string LIT; LIT points just
384 after the initial apostrophe. Put the literal's value into *VALPTR.
385 Yield the address of the first character after the closing apostrophe,
386 or zero if the literal is ill-formed. */
388 scan_char_literal (char const *lit
, char *valptr
)
390 register char const *p
= lit
;
403 while ((c
= *p
++) != '\'')
405 unsigned int digit
= c
- '0';
408 value
= 8 * value
+ digit
;
410 digits
= p
- lit
- 2;
411 if (! (1 <= digits
&& digits
<= 3))