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