source: vendor/diffutils/2.8.1/src/ifdef.c

Last change on this file was 2556, checked in by bird, 19 years ago

diffutils 2.8.1

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