Sync usage with man page.
[netbsd-mini2440.git] / gnu / dist / diffutils / src / ifdef.c
blobc5193fc2ab1f00f4ffe749714f53be920eb6e7a9
1 /* $NetBSD$ */
3 /* #ifdef-format output routines for GNU DIFF.
5 Copyright (C) 1989, 1991, 1992, 1993, 1994, 2001, 2002 Free
6 Software Foundation, Inc.
8 This file is part of GNU DIFF.
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.
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. */
25 #include "diff.h"
27 #include <xalloc.h>
29 struct group
31 struct file_data const *file;
32 lin from, upto; /* start and limit lines for this group of lines */
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 *);
46 static lin next_line;
48 /* Print the edit-script SCRIPT as a merged #ifdef file. */
50 void
51 print_ifdef_script (struct change *script)
53 next_line = - files[0].prefix_lines;
54 print_script (script, find_change, print_ifdef_hunk);
55 if (next_line < files[0].valid_lines)
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);
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. */
68 static void
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);
75 if (!changes)
76 return;
78 begin_output ();
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);
85 /* Print this change. */
86 next_line = last0 + 1;
87 format_ifdef (group_format[changes], first0, next_line, first1, last1 + 1);
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. */
94 static void
95 format_ifdef (char const *format, lin beg0, lin end0, lin beg1, lin end1)
97 struct group groups[2];
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);
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. */
114 static char const *
115 format_group (register FILE *out, char const *format, char endchar,
116 struct group const *groups)
118 register char c;
119 register char const *f = format;
121 while ((c = *f) != endchar && c != 0)
123 char const *f1 = ++f;
124 if (c == '%')
125 switch ((c = *f++))
127 case '%':
128 break;
130 case '(':
131 /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'. */
133 int i;
134 uintmax_t value[2];
135 FILE *thenout, *elseout;
137 for (i = 0; i < 2; i++)
139 if (ISDIGIT (*f))
141 char *fend;
142 errno = 0;
143 value[i] = strtoumax (f, &fend, 10);
144 if (errno)
145 goto bad_format;
146 f = fend;
148 else
150 value[i] = groups_letter_value (groups, *f);
151 if (value[i] == -1)
152 goto bad_format;
153 f++;
155 if (*f++ != "=?"[i])
156 goto bad_format;
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)
165 f = format_group (elseout, f + 1, ')', groups);
166 if (*f)
167 f++;
170 continue;
172 case '<':
173 /* Print lines deleted from first file. */
174 print_ifdef_lines (out, line_format[OLD], &groups[0]);
175 continue;
177 case '=':
178 /* Print common lines. */
179 print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]);
180 continue;
182 case '>':
183 /* Print lines inserted from second file. */
184 print_ifdef_lines (out, line_format[NEW], &groups[1]);
185 continue;
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;
198 if (out)
199 putc (c, out);
202 return f;
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
208 groups_letter_value (struct group const *g, char letter)
210 switch (letter)
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;
219 switch (letter)
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;
230 /* Print to file OUT, using FORMAT to print the line group GROUP.
231 But do nothing if OUT is zero. */
232 static void
233 print_ifdef_lines (register FILE *out, char const *format,
234 struct group const *group)
236 struct file_data const *file = group->file;
237 char const * const *linbuf = file->linbuf;
238 lin from = group->from, upto = group->upto;
240 if (!out)
241 return;
243 /* If possible, use a single fwrite; it's faster. */
244 if (!expand_tabs && format[0] == '%')
246 if (format[1] == 'l' && format[2] == '\n' && !format[3] && from < upto)
248 fwrite (linbuf[from], sizeof (char),
249 linbuf[upto] + (linbuf[upto][-1] != '\n') - linbuf[from],
250 out);
251 return;
253 if (format[1] == 'L' && !format[2])
255 fwrite (linbuf[from], sizeof (char),
256 linbuf[upto] - linbuf[from], out);
257 return;
261 for (; from < upto; from++)
263 register char c;
264 register char const *f = format;
266 while ((c = *f++) != 0)
268 char const *f1 = f;
269 if (c == '%')
270 switch ((c = *f++))
272 case '%':
273 break;
275 case 'l':
276 output_1_line (linbuf[from],
277 (linbuf[from + 1]
278 - (linbuf[from + 1][-1] == '\n')),
279 0, 0);
280 continue;
282 case 'L':
283 output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
284 continue;
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;
295 putc (c, out);
300 static char const *
301 do_printf_spec (FILE *out, char const *spec,
302 struct file_data const *file, lin n,
303 struct group const *groups)
305 char const *f = spec;
306 char c;
307 char c1;
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++;
321 switch (c)
323 case 'c':
324 if (c1 != '\'')
325 return 0;
326 else
328 char value;
329 f = scan_char_literal (f, &value);
330 if (!f)
331 return 0;
332 if (out)
333 putc (value, out);
335 break;
337 case 'd': case 'o': case 'x': case 'X':
339 lin value;
341 if (file)
343 if (c1 != 'n')
344 return 0;
345 value = translate_line_number (file, n);
347 else
349 value = groups_letter_value (groups, c1);
350 if (value < 0)
351 return 0;
354 if (out)
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
376 break;
378 default:
379 return 0;
382 return f;
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 *
390 scan_char_literal (char const *lit, char *valptr)
392 register char const *p = lit;
393 char value;
394 ptrdiff_t digits;
395 char c = *p++;
397 switch (c)
399 case 0:
400 case '\'':
401 return 0;
403 case '\\':
404 value = 0;
405 while ((c = *p++) != '\'')
407 unsigned int digit = c - '0';
408 if (8 <= digit)
409 return 0;
410 value = 8 * value + digit;
412 digits = p - lit - 2;
413 if (! (1 <= digits && digits <= 3))
414 return 0;
415 break;
417 default:
418 value = c;
419 if (*p++ != '\'')
420 return 0;
421 break;
424 *valptr = value;
425 return p;