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