1*75f6d617Schristos /* $NetBSD: ifdef.c,v 1.1.1.1 2016/01/13 03:15:30 christos Exp $ */
2*75f6d617Schristos
3*75f6d617Schristos /* #ifdef-format output routines for GNU DIFF.
4*75f6d617Schristos
5*75f6d617Schristos Copyright (C) 1989, 1991, 1992, 1993, 1994, 2001, 2002 Free
6*75f6d617Schristos Software Foundation, Inc.
7*75f6d617Schristos
8*75f6d617Schristos This file is part of GNU DIFF.
9*75f6d617Schristos
10*75f6d617Schristos GNU DIFF is distributed in the hope that it will be useful,
11*75f6d617Schristos but WITHOUT ANY WARRANTY. No author or distributor
12*75f6d617Schristos accepts responsibility to anyone for the consequences of using it
13*75f6d617Schristos or for whether it serves any particular purpose or works at all,
14*75f6d617Schristos unless he says so in writing. Refer to the GNU DIFF General Public
15*75f6d617Schristos License for full details.
16*75f6d617Schristos
17*75f6d617Schristos Everyone is granted permission to copy, modify and redistribute
18*75f6d617Schristos GNU DIFF, but only under the conditions described in the
19*75f6d617Schristos GNU DIFF General Public License. A copy of this license is
20*75f6d617Schristos supposed to have been given to you along with GNU DIFF so you
21*75f6d617Schristos can know your rights and responsibilities. It should be in a
22*75f6d617Schristos file named COPYING. Among other things, the copyright notice
23*75f6d617Schristos and this notice must be preserved on all copies. */
24*75f6d617Schristos
25*75f6d617Schristos #include "diff.h"
26*75f6d617Schristos
27*75f6d617Schristos #include <xalloc.h>
28*75f6d617Schristos
29*75f6d617Schristos struct group
30*75f6d617Schristos {
31*75f6d617Schristos struct file_data const *file;
32*75f6d617Schristos lin from, upto; /* start and limit lines for this group of lines */
33*75f6d617Schristos };
34*75f6d617Schristos
35*75f6d617Schristos static char const *format_group (FILE *, char const *, char,
36*75f6d617Schristos struct group const *);
37*75f6d617Schristos static char const *do_printf_spec (FILE *, char const *,
38*75f6d617Schristos struct file_data const *, lin,
39*75f6d617Schristos struct group const *);
40*75f6d617Schristos static char const *scan_char_literal (char const *, char *);
41*75f6d617Schristos static lin groups_letter_value (struct group const *, char);
42*75f6d617Schristos static void format_ifdef (char const *, lin, lin, lin, lin);
43*75f6d617Schristos static void print_ifdef_hunk (struct change *);
44*75f6d617Schristos static void print_ifdef_lines (FILE *, char const *, struct group const *);
45*75f6d617Schristos
46*75f6d617Schristos static lin next_line;
47*75f6d617Schristos
48*75f6d617Schristos /* Print the edit-script SCRIPT as a merged #ifdef file. */
49*75f6d617Schristos
50*75f6d617Schristos void
print_ifdef_script(struct change * script)51*75f6d617Schristos print_ifdef_script (struct change *script)
52*75f6d617Schristos {
53*75f6d617Schristos next_line = - files[0].prefix_lines;
54*75f6d617Schristos print_script (script, find_change, print_ifdef_hunk);
55*75f6d617Schristos if (next_line < files[0].valid_lines)
56*75f6d617Schristos {
57*75f6d617Schristos begin_output ();
58*75f6d617Schristos format_ifdef (group_format[UNCHANGED], next_line, files[0].valid_lines,
59*75f6d617Schristos next_line - files[0].valid_lines + files[1].valid_lines,
60*75f6d617Schristos files[1].valid_lines);
61*75f6d617Schristos }
62*75f6d617Schristos }
63*75f6d617Schristos
64*75f6d617Schristos /* Print a hunk of an ifdef diff.
65*75f6d617Schristos This is a contiguous portion of a complete edit script,
66*75f6d617Schristos describing changes in consecutive lines. */
67*75f6d617Schristos
68*75f6d617Schristos static void
print_ifdef_hunk(struct change * hunk)69*75f6d617Schristos print_ifdef_hunk (struct change *hunk)
70*75f6d617Schristos {
71*75f6d617Schristos lin first0, last0, first1, last1;
72*75f6d617Schristos
73*75f6d617Schristos /* Determine range of line numbers involved in each file. */
74*75f6d617Schristos enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
75*75f6d617Schristos if (!changes)
76*75f6d617Schristos return;
77*75f6d617Schristos
78*75f6d617Schristos begin_output ();
79*75f6d617Schristos
80*75f6d617Schristos /* Print lines up to this change. */
81*75f6d617Schristos if (next_line < first0)
82*75f6d617Schristos format_ifdef (group_format[UNCHANGED], next_line, first0,
83*75f6d617Schristos next_line - first0 + first1, first1);
84*75f6d617Schristos
85*75f6d617Schristos /* Print this change. */
86*75f6d617Schristos next_line = last0 + 1;
87*75f6d617Schristos format_ifdef (group_format[changes], first0, next_line, first1, last1 + 1);
88*75f6d617Schristos }
89*75f6d617Schristos
90*75f6d617Schristos /* Print a set of lines according to FORMAT.
91*75f6d617Schristos Lines BEG0 up to END0 are from the first file;
92*75f6d617Schristos lines BEG1 up to END1 are from the second file. */
93*75f6d617Schristos
94*75f6d617Schristos static void
format_ifdef(char const * format,lin beg0,lin end0,lin beg1,lin end1)95*75f6d617Schristos format_ifdef (char const *format, lin beg0, lin end0, lin beg1, lin end1)
96*75f6d617Schristos {
97*75f6d617Schristos struct group groups[2];
98*75f6d617Schristos
99*75f6d617Schristos groups[0].file = &files[0];
100*75f6d617Schristos groups[0].from = beg0;
101*75f6d617Schristos groups[0].upto = end0;
102*75f6d617Schristos groups[1].file = &files[1];
103*75f6d617Schristos groups[1].from = beg1;
104*75f6d617Schristos groups[1].upto = end1;
105*75f6d617Schristos format_group (outfile, format, 0, groups);
106*75f6d617Schristos }
107*75f6d617Schristos
108*75f6d617Schristos /* Print to file OUT a set of lines according to FORMAT.
109*75f6d617Schristos The format ends at the first free instance of ENDCHAR.
110*75f6d617Schristos Yield the address of the terminating character.
111*75f6d617Schristos GROUPS specifies which lines to print.
112*75f6d617Schristos If OUT is zero, do not actually print anything; just scan the format. */
113*75f6d617Schristos
114*75f6d617Schristos static char const *
format_group(register FILE * out,char const * format,char endchar,struct group const * groups)115*75f6d617Schristos format_group (register FILE *out, char const *format, char endchar,
116*75f6d617Schristos struct group const *groups)
117*75f6d617Schristos {
118*75f6d617Schristos register char c;
119*75f6d617Schristos register char const *f = format;
120*75f6d617Schristos
121*75f6d617Schristos while ((c = *f) != endchar && c != 0)
122*75f6d617Schristos {
123*75f6d617Schristos char const *f1 = ++f;
124*75f6d617Schristos if (c == '%')
125*75f6d617Schristos switch ((c = *f++))
126*75f6d617Schristos {
127*75f6d617Schristos case '%':
128*75f6d617Schristos break;
129*75f6d617Schristos
130*75f6d617Schristos case '(':
131*75f6d617Schristos /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'. */
132*75f6d617Schristos {
133*75f6d617Schristos int i;
134*75f6d617Schristos uintmax_t value[2];
135*75f6d617Schristos FILE *thenout, *elseout;
136*75f6d617Schristos
137*75f6d617Schristos for (i = 0; i < 2; i++)
138*75f6d617Schristos {
139*75f6d617Schristos if (ISDIGIT (*f))
140*75f6d617Schristos {
141*75f6d617Schristos char *fend;
142*75f6d617Schristos errno = 0;
143*75f6d617Schristos value[i] = strtoumax (f, &fend, 10);
144*75f6d617Schristos if (errno)
145*75f6d617Schristos goto bad_format;
146*75f6d617Schristos f = fend;
147*75f6d617Schristos }
148*75f6d617Schristos else
149*75f6d617Schristos {
150*75f6d617Schristos value[i] = groups_letter_value (groups, *f);
151*75f6d617Schristos if (value[i] == -1)
152*75f6d617Schristos goto bad_format;
153*75f6d617Schristos f++;
154*75f6d617Schristos }
155*75f6d617Schristos if (*f++ != "=?"[i])
156*75f6d617Schristos goto bad_format;
157*75f6d617Schristos }
158*75f6d617Schristos if (value[0] == value[1])
159*75f6d617Schristos thenout = out, elseout = 0;
160*75f6d617Schristos else
161*75f6d617Schristos thenout = 0, elseout = out;
162*75f6d617Schristos f = format_group (thenout, f, ':', groups);
163*75f6d617Schristos if (*f)
164*75f6d617Schristos {
165*75f6d617Schristos f = format_group (elseout, f + 1, ')', groups);
166*75f6d617Schristos if (*f)
167*75f6d617Schristos f++;
168*75f6d617Schristos }
169*75f6d617Schristos }
170*75f6d617Schristos continue;
171*75f6d617Schristos
172*75f6d617Schristos case '<':
173*75f6d617Schristos /* Print lines deleted from first file. */
174*75f6d617Schristos print_ifdef_lines (out, line_format[OLD], &groups[0]);
175*75f6d617Schristos continue;
176*75f6d617Schristos
177*75f6d617Schristos case '=':
178*75f6d617Schristos /* Print common lines. */
179*75f6d617Schristos print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]);
180*75f6d617Schristos continue;
181*75f6d617Schristos
182*75f6d617Schristos case '>':
183*75f6d617Schristos /* Print lines inserted from second file. */
184*75f6d617Schristos print_ifdef_lines (out, line_format[NEW], &groups[1]);
185*75f6d617Schristos continue;
186*75f6d617Schristos
187*75f6d617Schristos default:
188*75f6d617Schristos f = do_printf_spec (out, f - 2, 0, 0, groups);
189*75f6d617Schristos if (f)
190*75f6d617Schristos continue;
191*75f6d617Schristos /* Fall through. */
192*75f6d617Schristos bad_format:
193*75f6d617Schristos c = '%';
194*75f6d617Schristos f = f1;
195*75f6d617Schristos break;
196*75f6d617Schristos }
197*75f6d617Schristos
198*75f6d617Schristos if (out)
199*75f6d617Schristos putc (c, out);
200*75f6d617Schristos }
201*75f6d617Schristos
202*75f6d617Schristos return f;
203*75f6d617Schristos }
204*75f6d617Schristos
205*75f6d617Schristos /* For the line group pair G, return the number corresponding to LETTER.
206*75f6d617Schristos Return -1 if LETTER is not a group format letter. */
207*75f6d617Schristos static lin
groups_letter_value(struct group const * g,char letter)208*75f6d617Schristos groups_letter_value (struct group const *g, char letter)
209*75f6d617Schristos {
210*75f6d617Schristos switch (letter)
211*75f6d617Schristos {
212*75f6d617Schristos case 'E': letter = 'e'; g++; break;
213*75f6d617Schristos case 'F': letter = 'f'; g++; break;
214*75f6d617Schristos case 'L': letter = 'l'; g++; break;
215*75f6d617Schristos case 'M': letter = 'm'; g++; break;
216*75f6d617Schristos case 'N': letter = 'n'; g++; break;
217*75f6d617Schristos }
218*75f6d617Schristos
219*75f6d617Schristos switch (letter)
220*75f6d617Schristos {
221*75f6d617Schristos case 'e': return translate_line_number (g->file, g->from) - 1;
222*75f6d617Schristos case 'f': return translate_line_number (g->file, g->from);
223*75f6d617Schristos case 'l': return translate_line_number (g->file, g->upto) - 1;
224*75f6d617Schristos case 'm': return translate_line_number (g->file, g->upto);
225*75f6d617Schristos case 'n': return g->upto - g->from;
226*75f6d617Schristos default: return -1;
227*75f6d617Schristos }
228*75f6d617Schristos }
229*75f6d617Schristos
230*75f6d617Schristos /* Print to file OUT, using FORMAT to print the line group GROUP.
231*75f6d617Schristos But do nothing if OUT is zero. */
232*75f6d617Schristos static void
print_ifdef_lines(register FILE * out,char const * format,struct group const * group)233*75f6d617Schristos print_ifdef_lines (register FILE *out, char const *format,
234*75f6d617Schristos struct group const *group)
235*75f6d617Schristos {
236*75f6d617Schristos struct file_data const *file = group->file;
237*75f6d617Schristos char const * const *linbuf = file->linbuf;
238*75f6d617Schristos lin from = group->from, upto = group->upto;
239*75f6d617Schristos
240*75f6d617Schristos if (!out)
241*75f6d617Schristos return;
242*75f6d617Schristos
243*75f6d617Schristos /* If possible, use a single fwrite; it's faster. */
244*75f6d617Schristos if (!expand_tabs && format[0] == '%')
245*75f6d617Schristos {
246*75f6d617Schristos if (format[1] == 'l' && format[2] == '\n' && !format[3] && from < upto)
247*75f6d617Schristos {
248*75f6d617Schristos fwrite (linbuf[from], sizeof (char),
249*75f6d617Schristos linbuf[upto] + (linbuf[upto][-1] != '\n') - linbuf[from],
250*75f6d617Schristos out);
251*75f6d617Schristos return;
252*75f6d617Schristos }
253*75f6d617Schristos if (format[1] == 'L' && !format[2])
254*75f6d617Schristos {
255*75f6d617Schristos fwrite (linbuf[from], sizeof (char),
256*75f6d617Schristos linbuf[upto] - linbuf[from], out);
257*75f6d617Schristos return;
258*75f6d617Schristos }
259*75f6d617Schristos }
260*75f6d617Schristos
261*75f6d617Schristos for (; from < upto; from++)
262*75f6d617Schristos {
263*75f6d617Schristos register char c;
264*75f6d617Schristos register char const *f = format;
265*75f6d617Schristos
266*75f6d617Schristos while ((c = *f++) != 0)
267*75f6d617Schristos {
268*75f6d617Schristos char const *f1 = f;
269*75f6d617Schristos if (c == '%')
270*75f6d617Schristos switch ((c = *f++))
271*75f6d617Schristos {
272*75f6d617Schristos case '%':
273*75f6d617Schristos break;
274*75f6d617Schristos
275*75f6d617Schristos case 'l':
276*75f6d617Schristos output_1_line (linbuf[from],
277*75f6d617Schristos (linbuf[from + 1]
278*75f6d617Schristos - (linbuf[from + 1][-1] == '\n')),
279*75f6d617Schristos 0, 0);
280*75f6d617Schristos continue;
281*75f6d617Schristos
282*75f6d617Schristos case 'L':
283*75f6d617Schristos output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
284*75f6d617Schristos continue;
285*75f6d617Schristos
286*75f6d617Schristos default:
287*75f6d617Schristos f = do_printf_spec (out, f - 2, file, from, 0);
288*75f6d617Schristos if (f)
289*75f6d617Schristos continue;
290*75f6d617Schristos c = '%';
291*75f6d617Schristos f = f1;
292*75f6d617Schristos break;
293*75f6d617Schristos }
294*75f6d617Schristos
295*75f6d617Schristos putc (c, out);
296*75f6d617Schristos }
297*75f6d617Schristos }
298*75f6d617Schristos }
299*75f6d617Schristos
300*75f6d617Schristos static char const *
do_printf_spec(FILE * out,char const * spec,struct file_data const * file,lin n,struct group const * groups)301*75f6d617Schristos do_printf_spec (FILE *out, char const *spec,
302*75f6d617Schristos struct file_data const *file, lin n,
303*75f6d617Schristos struct group const *groups)
304*75f6d617Schristos {
305*75f6d617Schristos char const *f = spec;
306*75f6d617Schristos char c;
307*75f6d617Schristos char c1;
308*75f6d617Schristos
309*75f6d617Schristos /* Scan printf-style SPEC of the form %[-'0]*[0-9]*(.[0-9]*)?[cdoxX]. */
310*75f6d617Schristos /* assert (*f == '%'); */
311*75f6d617Schristos f++;
312*75f6d617Schristos while ((c = *f++) == '-' || c == '\'' || c == '0')
313*75f6d617Schristos continue;
314*75f6d617Schristos while (ISDIGIT (c))
315*75f6d617Schristos c = *f++;
316*75f6d617Schristos if (c == '.')
317*75f6d617Schristos while (ISDIGIT (c = *f++))
318*75f6d617Schristos continue;
319*75f6d617Schristos c1 = *f++;
320*75f6d617Schristos
321*75f6d617Schristos switch (c)
322*75f6d617Schristos {
323*75f6d617Schristos case 'c':
324*75f6d617Schristos if (c1 != '\'')
325*75f6d617Schristos return 0;
326*75f6d617Schristos else
327*75f6d617Schristos {
328*75f6d617Schristos char value;
329*75f6d617Schristos f = scan_char_literal (f, &value);
330*75f6d617Schristos if (!f)
331*75f6d617Schristos return 0;
332*75f6d617Schristos if (out)
333*75f6d617Schristos putc (value, out);
334*75f6d617Schristos }
335*75f6d617Schristos break;
336*75f6d617Schristos
337*75f6d617Schristos case 'd': case 'o': case 'x': case 'X':
338*75f6d617Schristos {
339*75f6d617Schristos lin value;
340*75f6d617Schristos
341*75f6d617Schristos if (file)
342*75f6d617Schristos {
343*75f6d617Schristos if (c1 != 'n')
344*75f6d617Schristos return 0;
345*75f6d617Schristos value = translate_line_number (file, n);
346*75f6d617Schristos }
347*75f6d617Schristos else
348*75f6d617Schristos {
349*75f6d617Schristos value = groups_letter_value (groups, c1);
350*75f6d617Schristos if (value < 0)
351*75f6d617Schristos return 0;
352*75f6d617Schristos }
353*75f6d617Schristos
354*75f6d617Schristos if (out)
355*75f6d617Schristos {
356*75f6d617Schristos /* For example, if the spec is "%3xn", use the printf
357*75f6d617Schristos format spec "%3lx". Here the spec prefix is "%3". */
358*75f6d617Schristos long long_value = value;
359*75f6d617Schristos size_t spec_prefix_len = f - spec - 2;
360*75f6d617Schristos #if HAVE_C_VARARRAYS
361*75f6d617Schristos char format[spec_prefix_len + 3];
362*75f6d617Schristos #else
363*75f6d617Schristos char *format = xmalloc (spec_prefix_len + 3);
364*75f6d617Schristos #endif
365*75f6d617Schristos char *p = format + spec_prefix_len;
366*75f6d617Schristos memcpy (format, spec, spec_prefix_len);
367*75f6d617Schristos *p++ = 'l';
368*75f6d617Schristos *p++ = c;
369*75f6d617Schristos *p = '\0';
370*75f6d617Schristos fprintf (out, format, long_value);
371*75f6d617Schristos #if ! HAVE_C_VARARRAYS
372*75f6d617Schristos free (format);
373*75f6d617Schristos #endif
374*75f6d617Schristos }
375*75f6d617Schristos }
376*75f6d617Schristos break;
377*75f6d617Schristos
378*75f6d617Schristos default:
379*75f6d617Schristos return 0;
380*75f6d617Schristos }
381*75f6d617Schristos
382*75f6d617Schristos return f;
383*75f6d617Schristos }
384*75f6d617Schristos
385*75f6d617Schristos /* Scan the character literal represented in the string LIT; LIT points just
386*75f6d617Schristos after the initial apostrophe. Put the literal's value into *VALPTR.
387*75f6d617Schristos Yield the address of the first character after the closing apostrophe,
388*75f6d617Schristos or zero if the literal is ill-formed. */
389*75f6d617Schristos static char const *
scan_char_literal(char const * lit,char * valptr)390*75f6d617Schristos scan_char_literal (char const *lit, char *valptr)
391*75f6d617Schristos {
392*75f6d617Schristos register char const *p = lit;
393*75f6d617Schristos char value;
394*75f6d617Schristos ptrdiff_t digits;
395*75f6d617Schristos char c = *p++;
396*75f6d617Schristos
397*75f6d617Schristos switch (c)
398*75f6d617Schristos {
399*75f6d617Schristos case 0:
400*75f6d617Schristos case '\'':
401*75f6d617Schristos return 0;
402*75f6d617Schristos
403*75f6d617Schristos case '\\':
404*75f6d617Schristos value = 0;
405*75f6d617Schristos while ((c = *p++) != '\'')
406*75f6d617Schristos {
407*75f6d617Schristos unsigned int digit = c - '0';
408*75f6d617Schristos if (8 <= digit)
409*75f6d617Schristos return 0;
410*75f6d617Schristos value = 8 * value + digit;
411*75f6d617Schristos }
412*75f6d617Schristos digits = p - lit - 2;
413*75f6d617Schristos if (! (1 <= digits && digits <= 3))
414*75f6d617Schristos return 0;
415*75f6d617Schristos break;
416*75f6d617Schristos
417*75f6d617Schristos default:
418*75f6d617Schristos value = c;
419*75f6d617Schristos if (*p++ != '\'')
420*75f6d617Schristos return 0;
421*75f6d617Schristos break;
422*75f6d617Schristos }
423*75f6d617Schristos
424*75f6d617Schristos *valptr = value;
425*75f6d617Schristos return p;
426*75f6d617Schristos }
427