1 /* #ifdef-format output routines for GNU DIFF.
3 Copyright (C) 1989, 1991, 1992, 1993, 1994, 2001, 2002, 2004 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 *);
44 static lin next_line0;
45 static lin next_line1;
47 /* Print the edit-script SCRIPT as a merged #ifdef file. */
50 print_ifdef_script (struct change *script)
52 next_line0 = next_line1 = - files[0].prefix_lines;
53 print_script (script, find_change, print_ifdef_hunk);
54 if (next_line0 < files[0].valid_lines
55 || next_line1 < files[1].valid_lines)
58 format_ifdef (group_format[UNCHANGED],
59 next_line0, files[0].valid_lines,
60 next_line1, 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_line0 < first0 || next_line1 < first1)
82 format_ifdef (group_format[UNCHANGED],
86 /* Print this change. */
87 next_line0 = last0 + 1;
88 next_line1 = last1 + 1;
89 format_ifdef (group_format[changes],
94 /* Print a set of lines according to FORMAT.
95 Lines BEG0 up to END0 are from the first file;
96 lines BEG1 up to END1 are from the second file. */
99 format_ifdef (char const *format, lin beg0, lin end0, lin beg1, lin end1)
101 struct group groups[2];
103 groups[0].file = &files[0];
104 groups[0].from = beg0;
105 groups[0].upto = end0;
106 groups[1].file = &files[1];
107 groups[1].from = beg1;
108 groups[1].upto = end1;
109 format_group (outfile, format, 0, groups);
112 /* Print to file OUT a set of lines according to FORMAT.
113 The format ends at the first free instance of ENDCHAR.
114 Yield the address of the terminating character.
115 GROUPS specifies which lines to print.
116 If OUT is zero, do not actually print anything; just scan the format. */
119 format_group (register FILE *out, char const *format, char endchar,
120 struct group const *groups)
123 register char const *f = format;
125 while ((c = *f) != endchar && c != 0)
127 char const *f1 = ++f;
135 /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'. */
139 FILE *thenout, *elseout;
141 for (i = 0; i < 2; i++)
147 value[i] = strtoumax (f, &fend, 10);
154 value[i] = groups_letter_value (groups, *f);
162 if (value[0] == value[1])
163 thenout = out, elseout = 0;
165 thenout = 0, elseout = out;
166 f = format_group (thenout, f, ':', groups);
169 f = format_group (elseout, f + 1, ')', groups);
177 /* Print lines deleted from first file. */
178 print_ifdef_lines (out, line_format[OLD], &groups[0]);
182 /* Print common lines. */
183 print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]);
187 /* Print lines inserted from second file. */
188 print_ifdef_lines (out, line_format[NEW], &groups[1]);
192 f = do_printf_spec (out, f - 2, 0, 0, groups);
209 /* For the line group pair G, return the number corresponding to LETTER.
210 Return -1 if LETTER is not a group format letter. */
212 groups_letter_value (struct group const *g, char letter)
216 case 'E': letter = 'e'; g++; break;
217 case 'F': letter = 'f'; g++; break;
218 case 'L': letter = 'l'; g++; break;
219 case 'M': letter = 'm'; g++; break;
220 case 'N': letter = 'n'; g++; break;
225 case 'e': return translate_line_number (g->file, g->from) - 1;
226 case 'f': return translate_line_number (g->file, g->from);
227 case 'l': return translate_line_number (g->file, g->upto) - 1;
228 case 'm': return translate_line_number (g->file, g->upto);
229 case 'n': return g->upto - g->from;
234 /* Print to file OUT, using FORMAT to print the line group GROUP.
235 But do nothing if OUT is zero. */
237 print_ifdef_lines (register FILE *out, char const *format,
238 struct group const *group)
240 struct file_data const *file = group->file;
241 char const * const *linbuf = file->linbuf;
242 lin from = group->from, upto = group->upto;
247 /* If possible, use a single fwrite; it's faster. */
248 if (!expand_tabs && format[0] == '%')
250 if (format[1] == 'l' && format[2] == '\n' && !format[3] && from < upto)
252 fwrite (linbuf[from], sizeof (char),
253 linbuf[upto] + (linbuf[upto][-1] != '\n') - linbuf[from],
257 if (format[1] == 'L' && !format[2])
259 fwrite (linbuf[from], sizeof (char),
260 linbuf[upto] - linbuf[from], out);
265 for (; from < upto; from++)
268 register char const *f = format;
270 while ((c = *f++) != 0)
280 output_1_line (linbuf[from],
282 - (linbuf[from + 1][-1] == '\n')),
287 output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
291 f = do_printf_spec (out, f - 2, file, from, 0);
305 do_printf_spec (FILE *out, char const *spec,
306 struct file_data const *file, lin n,
307 struct group const *groups)
309 char const *f = spec;
313 /* Scan printf-style SPEC of the form %[-'0]*[0-9]*(.[0-9]*)?[cdoxX]. */
314 /* assert (*f == '%'); */
316 while ((c = *f++) == '-' || c == '\'' || c == '0')
321 while (ISDIGIT (c = *f++))
333 f = scan_char_literal (f, &value);
341 case 'd': case 'o': case 'x': case 'X':
349 value = translate_line_number (file, n);
353 value = groups_letter_value (groups, c1);
360 /* For example, if the spec is "%3xn", use the printf
361 format spec "%3lx". Here the spec prefix is "%3". */
362 long int long_value = value;
363 size_t spec_prefix_len = f - spec - 2;
365 char format[spec_prefix_len + 3];
367 char *format = xmalloc (spec_prefix_len + 3);
369 char *p = format + spec_prefix_len;
370 memcpy (format, spec, spec_prefix_len);
374 fprintf (out, format, long_value);
375 #if ! HAVE_C_VARARRAYS
389 /* Scan the character literal represented in the string LIT; LIT points just
390 after the initial apostrophe. Put the literal's value into *VALPTR.
391 Yield the address of the first character after the closing apostrophe,
392 or zero if the literal is ill-formed. */
394 scan_char_literal (char const *lit, char *valptr)
396 register char const *p = lit;
409 while ((c = *p++) != '\'')
411 unsigned int digit = c - '0';
414 value = 8 * value + digit;
416 digits = p - lit - 2;
417 if (! (1 <= digits && digits <= 3))