Import of diffutils 2.8.1
[dragonfly.git] / contrib / diffutils-2.8.1 / src / ifdef.c
blob69c2cf8e999918ab374fe25ae97b8da70f74bc84
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. */
23 #include "diff.h"
25 #include <xalloc.h>
27 struct group
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_line;
46 /* Print the edit-script SCRIPT as a merged #ifdef file. */
48 void
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)
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);
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. */
66 static void
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);
73 if (!changes)
74 return;
76 begin_output ();
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. */
92 static void
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. */
112 static char const *
113 format_group (register FILE *out, char const *format, char endchar,
114 struct group const *groups)
116 register char c;
117 register char const *f = format;
119 while ((c = *f) != endchar && c != 0)
121 char const *f1 = ++f;
122 if (c == '%')
123 switch ((c = *f++))
125 case '%':
126 break;
128 case '(':
129 /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'. */
131 int i;
132 uintmax_t value[2];
133 FILE *thenout, *elseout;
135 for (i = 0; i < 2; i++)
137 if (ISDIGIT (*f))
139 char *fend;
140 errno = 0;
141 value[i] = strtoumax (f, &fend, 10);
142 if (errno)
143 goto bad_format;
144 f = fend;
146 else
148 value[i] = groups_letter_value (groups, *f);
149 if (value[i] == -1)
150 goto bad_format;
151 f++;
153 if (*f++ != "=?"[i])
154 goto bad_format;
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)
163 f = format_group (elseout, f + 1, ')', groups);
164 if (*f)
165 f++;
168 continue;
170 case '<':
171 /* Print lines deleted from first file. */
172 print_ifdef_lines (out, line_format[OLD], &groups[0]);
173 continue;
175 case '=':
176 /* Print common lines. */
177 print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]);
178 continue;
180 case '>':
181 /* Print lines inserted from second file. */
182 print_ifdef_lines (out, line_format[NEW], &groups[1]);
183 continue;
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;
196 if (out)
197 putc (c, out);
200 return f;
203 /* For the line group pair G, return the number corresponding to LETTER.
204 Return -1 if LETTER is not a group format letter. */
205 static lin
206 groups_letter_value (struct group const *g, char letter)
208 switch (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;
217 switch (letter)
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;
228 /* Print to file OUT, using FORMAT to print the line group GROUP.
229 But do nothing if OUT is zero. */
230 static void
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;
238 if (!out)
239 return;
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],
248 out);
249 return;
251 if (format[1] == 'L' && !format[2])
253 fwrite (linbuf[from], sizeof (char),
254 linbuf[upto] - linbuf[from], out);
255 return;
259 for (; from < upto; from++)
261 register char c;
262 register char const *f = format;
264 while ((c = *f++) != 0)
266 char const *f1 = f;
267 if (c == '%')
268 switch ((c = *f++))
270 case '%':
271 break;
273 case 'l':
274 output_1_line (linbuf[from],
275 (linbuf[from + 1]
276 - (linbuf[from + 1][-1] == '\n')),
277 0, 0);
278 continue;
280 case 'L':
281 output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
282 continue;
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;
293 putc (c, out);
298 static char const *
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;
304 char c;
305 char c1;
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++;
319 switch (c)
321 case 'c':
322 if (c1 != '\'')
323 return 0;
324 else
326 char value;
327 f = scan_char_literal (f, &value);
328 if (!f)
329 return 0;
330 if (out)
331 putc (value, out);
333 break;
335 case 'd': case 'o': case 'x': case 'X':
337 lin value;
339 if (file)
341 if (c1 != 'n')
342 return 0;
343 value = translate_line_number (file, n);
345 else
347 value = groups_letter_value (groups, c1);
348 if (value < 0)
349 return 0;
352 if (out)
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
374 break;
376 default:
377 return 0;
380 return f;
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. */
387 static char const *
388 scan_char_literal (char const *lit, char *valptr)
390 register char const *p = lit;
391 char value;
392 ptrdiff_t digits;
393 char c = *p++;
395 switch (c)
397 case 0:
398 case '\'':
399 return 0;
401 case '\\':
402 value = 0;
403 while ((c = *p++) != '\'')
405 unsigned int digit = c - '0';
406 if (8 <= digit)
407 return 0;
408 value = 8 * value + digit;
410 digits = p - lit - 2;
411 if (! (1 <= digits && digits <= 3))
412 return 0;
413 break;
415 default:
416 value = c;
417 if (*p++ != '\'')
418 return 0;
419 break;
422 *valptr = value;
423 return p;