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))