1 /* #ifdef-format output routines for GNU DIFF.
2
3 Copyright (C) 1989, 1991, 1992, 1993, 1994, 2001, 2002, 2004 Free
4 Software Foundation, Inc.
5
6 This file is part of GNU DIFF.
7
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.
14
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. */
22
23 #include "diff.h"
24
25 #include <xalloc.h>
26
27 struct group
28 {
29 struct file_data const *file;
30 lin from, upto; /* start and limit lines for this group of lines */
31 };
32
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 *);
43
44 static lin next_line0;
45 static lin next_line1;
46
47 /* Print the edit-script SCRIPT as a merged #ifdef file. */
48
49 void
print_ifdef_script(struct change * script)50 print_ifdef_script (struct change *script)
51 {
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)
56 {
57 begin_output ();
58 format_ifdef (group_format[UNCHANGED],
59 next_line0, files[0].valid_lines,
60 next_line1, files[1].valid_lines);
61 }
62 }
63
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. */
67
68 static void
print_ifdef_hunk(struct change * hunk)69 print_ifdef_hunk (struct change *hunk)
70 {
71 lin first0, last0, first1, last1;
72
73 /* Determine range of line numbers involved in each file. */
74 enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
75 if (!changes)
76 return;
77
78 begin_output ();
79
80 /* Print lines up to this change. */
81 if (next_line0 < first0 || next_line1 < first1)
82 format_ifdef (group_format[UNCHANGED],
83 next_line0, first0,
84 next_line1, first1);
85
86 /* Print this change. */
87 next_line0 = last0 + 1;
88 next_line1 = last1 + 1;
89 format_ifdef (group_format[changes],
90 first0, next_line0,
91 first1, next_line1);
92 }
93
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. */
97
98 static void
format_ifdef(char const * format,lin beg0,lin end0,lin beg1,lin end1)99 format_ifdef (char const *format, lin beg0, lin end0, lin beg1, lin end1)
100 {
101 struct group groups[2];
102
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);
110 }
111
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. */
117
118 static char const *
format_group(register FILE * out,char const * format,char endchar,struct group const * groups)119 format_group (register FILE *out, char const *format, char endchar,
120 struct group const *groups)
121 {
122 register char c;
123 register char const *f = format;
124
125 while ((c = *f) != endchar && c != 0)
126 {
127 char const *f1 = ++f;
128 if (c == '%')
129 switch ((c = *f++))
130 {
131 case '%':
132 break;
133
134 case '(':
135 /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'. */
136 {
137 int i;
138 uintmax_t value[2];
139 FILE *thenout, *elseout;
140
141 for (i = 0; i < 2; i++)
142 {
143 if (ISDIGIT (*f))
144 {
145 char *fend;
146 errno = 0;
147 value[i] = strtoumax (f, &fend, 10);
148 if (errno)
149 goto bad_format;
150 f = fend;
151 }
152 else
153 {
154 value[i] = groups_letter_value (groups, *f);
155 if (value[i] == -1)
156 goto bad_format;
157 f++;
158 }
159 if (*f++ != "=?"[i])
160 goto bad_format;
161 }
162 if (value[0] == value[1])
163 thenout = out, elseout = 0;
164 else
165 thenout = 0, elseout = out;
166 f = format_group (thenout, f, ':', groups);
167 if (*f)
168 {
169 f = format_group (elseout, f + 1, ')', groups);
170 if (*f)
171 f++;
172 }
173 }
174 continue;
175
176 case '<':
177 /* Print lines deleted from first file. */
178 print_ifdef_lines (out, line_format[OLD], &groups[0]);
179 continue;
180
181 case '=':
182 /* Print common lines. */
183 print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]);
184 continue;
185
186 case '>':
187 /* Print lines inserted from second file. */
188 print_ifdef_lines (out, line_format[NEW], &groups[1]);
189 continue;
190
191 default:
192 f = do_printf_spec (out, f - 2, 0, 0, groups);
193 if (f)
194 continue;
195 /* Fall through. */
196 bad_format:
197 c = '%';
198 f = f1;
199 break;
200 }
201
202 if (out)
203 putc (c, out);
204 }
205
206 return f;
207 }
208
209 /* For the line group pair G, return the number corresponding to LETTER.
210 Return -1 if LETTER is not a group format letter. */
211 static lin
groups_letter_value(struct group const * g,char letter)212 groups_letter_value (struct group const *g, char letter)
213 {
214 switch (letter)
215 {
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;
221 }
222
223 switch (letter)
224 {
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;
230 default: return -1;
231 }
232 }
233
234 /* Print to file OUT, using FORMAT to print the line group GROUP.
235 But do nothing if OUT is zero. */
236 static void
print_ifdef_lines(register FILE * out,char const * format,struct group const * group)237 print_ifdef_lines (register FILE *out, char const *format,
238 struct group const *group)
239 {
240 struct file_data const *file = group->file;
241 char const * const *linbuf = file->linbuf;
242 lin from = group->from, upto = group->upto;
243
244 if (!out)
245 return;
246
247 /* If possible, use a single fwrite; it's faster. */
248 if (!expand_tabs && format[0] == '%')
249 {
250 if (format[1] == 'l' && format[2] == '\n' && !format[3] && from < upto)
251 {
252 fwrite (linbuf[from], sizeof (char),
253 linbuf[upto] + (linbuf[upto][-1] != '\n') - linbuf[from],
254 out);
255 return;
256 }
257 if (format[1] == 'L' && !format[2])
258 {
259 fwrite (linbuf[from], sizeof (char),
260 linbuf[upto] - linbuf[from], out);
261 return;
262 }
263 }
264
265 for (; from < upto; from++)
266 {
267 register char c;
268 register char const *f = format;
269
270 while ((c = *f++) != 0)
271 {
272 char const *f1 = f;
273 if (c == '%')
274 switch ((c = *f++))
275 {
276 case '%':
277 break;
278
279 case 'l':
280 output_1_line (linbuf[from],
281 (linbuf[from + 1]
282 - (linbuf[from + 1][-1] == '\n')),
283 0, 0);
284 continue;
285
286 case 'L':
287 output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
288 continue;
289
290 default:
291 f = do_printf_spec (out, f - 2, file, from, 0);
292 if (f)
293 continue;
294 c = '%';
295 f = f1;
296 break;
297 }
298
299 putc (c, out);
300 }
301 }
302 }
303
304 static char const *
do_printf_spec(FILE * out,char const * spec,struct file_data const * file,lin n,struct group const * groups)305 do_printf_spec (FILE *out, char const *spec,
306 struct file_data const *file, lin n,
307 struct group const *groups)
308 {
309 char const *f = spec;
310 char c;
311 char c1;
312
313 /* Scan printf-style SPEC of the form %[-'0]*[0-9]*(.[0-9]*)?[cdoxX]. */
314 /* assert (*f == '%'); */
315 f++;
316 while ((c = *f++) == '-' || c == '\'' || c == '0')
317 continue;
318 while (ISDIGIT (c))
319 c = *f++;
320 if (c == '.')
321 while (ISDIGIT (c = *f++))
322 continue;
323 c1 = *f++;
324
325 switch (c)
326 {
327 case 'c':
328 if (c1 != '\'')
329 return 0;
330 else
331 {
332 char value;
333 f = scan_char_literal (f, &value);
334 if (!f)
335 return 0;
336 if (out)
337 putc (value, out);
338 }
339 break;
340
341 case 'd': case 'o': case 'x': case 'X':
342 {
343 lin value;
344
345 if (file)
346 {
347 if (c1 != 'n')
348 return 0;
349 value = translate_line_number (file, n);
350 }
351 else
352 {
353 value = groups_letter_value (groups, c1);
354 if (value < 0)
355 return 0;
356 }
357
358 if (out)
359 {
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;
364 #if HAVE_C_VARARRAYS
365 char format[spec_prefix_len + 3];
366 #else
367 char *format = xmalloc (spec_prefix_len + 3);
368 #endif
369 char *p = format + spec_prefix_len;
370 memcpy (format, spec, spec_prefix_len);
371 *p++ = 'l';
372 *p++ = c;
373 *p = '\0';
374 fprintf (out, format, long_value);
375 #if ! HAVE_C_VARARRAYS
376 free (format);
377 #endif
378 }
379 }
380 break;
381
382 default:
383 return 0;
384 }
385
386 return f;
387 }
388
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. */
393 static char const *
scan_char_literal(char const * lit,char * valptr)394 scan_char_literal (char const *lit, char *valptr)
395 {
396 register char const *p = lit;
397 char value;
398 ptrdiff_t digits;
399 char c = *p++;
400
401 switch (c)
402 {
403 case 0:
404 case '\'':
405 return 0;
406
407 case '\\':
408 value = 0;
409 while ((c = *p++) != '\'')
410 {
411 unsigned int digit = c - '0';
412 if (8 <= digit)
413 return 0;
414 value = 8 * value + digit;
415 }
416 digits = p - lit - 2;
417 if (! (1 <= digits && digits <= 3))
418 return 0;
419 break;
420
421 default:
422 value = c;
423 if (*p++ != '\'')
424 return 0;
425 break;
426 }
427
428 *valptr = value;
429 return p;
430 }
431