1 /* #ifdef-format output routines for GNU DIFF.
2 Copyright (C) 1989, 1991, 1992, 1993, 1994, 1998 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((int, 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((int, 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 (1, format, '\0', groups);
107 }
108
109 /* If DOIT is non-zero, output 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(doit,format,endchar,groups)116 format_group (doit, format, endchar, groups)
117 int doit;
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 int thendoit, elsedoit;
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 thendoit = doit, elsedoit = 0;
163 else
164 thendoit = 0, elsedoit = doit;
165 f = format_group (thendoit, f, ':', groups);
166 if (*f)
167 {
168 f = format_group (elsedoit, 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 (doit, line_format[OLD], &groups[0]);
178 continue;
179
180 case '=':
181 /* Print common lines. */
182 print_ifdef_lines (doit, line_format[UNCHANGED], &groups[0]);
183 continue;
184
185 case '>':
186 /* Print lines inserted from second file. */
187 print_ifdef_lines (doit, 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 (doit)
215 {
216 /* Temporarily replace e.g. "%3dnx" with "%3d\0x". */
217 *speclim = 0;
218 printf_output (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 (doit)
232 {
233 /* Don't take the address of a register variable. */
234 char cc = c;
235 write_output (&cc, 1);
236 }
237 }
238 return f;
239 }
240
241 /* For the line group pair G, return the number corresponding to LETTER.
242 Return -1 if LETTER is not a group format letter. */
243 static int
groups_letter_value(g,letter)244 groups_letter_value (g, letter)
245 struct group const *g;
246 int letter;
247 {
248 if (ISUPPER (letter))
249 {
250 g++;
251 letter = tolower (letter);
252 }
253 switch (letter)
254 {
255 case 'e': return translate_line_number (g->file, g->from) - 1;
256 case 'f': return translate_line_number (g->file, g->from);
257 case 'l': return translate_line_number (g->file, g->upto) - 1;
258 case 'm': return translate_line_number (g->file, g->upto);
259 case 'n': return g->upto - g->from;
260 default: return -1;
261 }
262 }
263
264 /* Output using FORMAT to print the line group GROUP.
265 But do nothing if DOIT is zero. */
266 static void
print_ifdef_lines(doit,format,group)267 print_ifdef_lines (doit, format, group)
268 int doit;
269 char *format;
270 struct group const *group;
271 {
272 struct file_data const *file = group->file;
273 char const * const *linbuf = file->linbuf;
274 int from = group->from, upto = group->upto;
275
276 if (!doit)
277 return;
278
279 /* If possible, use a single fwrite; it's faster. */
280 if (!tab_expand_flag && format[0] == '%')
281 {
282 if (format[1] == 'l' && format[2] == '\n' && !format[3])
283 {
284 write_output (linbuf[from],
285 (linbuf[upto] + (linbuf[upto][-1] != '\n')
286 - linbuf[from]));
287 return;
288 }
289 if (format[1] == 'L' && !format[2])
290 {
291 write_output (linbuf[from],
292 linbuf[upto] - linbuf[from]);
293 return;
294 }
295 }
296
297 for (; from < upto; from++)
298 {
299 register char c;
300 register char *f = format;
301 char cc;
302
303 while ((c = *f++) != 0)
304 {
305 if (c == '%')
306 {
307 char *spec = f;
308 switch ((c = *f++))
309 {
310 case '%':
311 break;
312
313 case 'l':
314 output_1_line (linbuf[from],
315 linbuf[from + 1]
316 - (linbuf[from + 1][-1] == '\n'), 0, 0);
317 continue;
318
319 case 'L':
320 output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
321 continue;
322
323 default:
324 {
325 int value;
326 char *speclim;
327
328 f = scan_printf_spec (spec);
329 if (!f)
330 goto bad_format;
331 speclim = f;
332 c = *f++;
333 switch (c)
334 {
335 case '\'':
336 f = scan_char_literal (f, &value);
337 if (!f)
338 goto bad_format;
339 break;
340
341 case 'n':
342 value = translate_line_number (file, from);
343 break;
344
345 default:
346 goto bad_format;
347 }
348 /* Temporarily replace e.g. "%3dnx" with "%3d\0x". */
349 *speclim = 0;
350 printf_output (spec - 1, value);
351 /* Undo the temporary replacement. */
352 *speclim = c;
353 }
354 continue;
355
356 bad_format:
357 c = '%';
358 f = spec;
359 break;
360 }
361 }
362
363 /* Don't take the address of a register variable. */
364 cc = c;
365 write_output (&cc, 1);
366 }
367 }
368 }
369
370 /* Scan the character literal represented in the string LIT; LIT points just
371 after the initial apostrophe. Put the literal's value into *INTPTR.
372 Yield the address of the first character after the closing apostrophe,
373 or zero if the literal is ill-formed. */
374 static char *
scan_char_literal(lit,intptr)375 scan_char_literal (lit, intptr)
376 char *lit;
377 int *intptr;
378 {
379 register char *p = lit;
380 int value, digits;
381 char c = *p++;
382
383 switch (c)
384 {
385 case 0:
386 case '\'':
387 return 0;
388
389 case '\\':
390 value = 0;
391 while ((c = *p++) != '\'')
392 {
393 unsigned digit = c - '0';
394 if (8 <= digit)
395 return 0;
396 value = 8 * value + digit;
397 }
398 digits = p - lit - 2;
399 if (! (1 <= digits && digits <= 3))
400 return 0;
401 break;
402
403 default:
404 value = c;
405 if (*p++ != '\'')
406 return 0;
407 break;
408 }
409 *intptr = value;
410 return p;
411 }
412
413 /* Scan optional printf-style SPEC of the form `-*[0-9]*(.[0-9]*)?[cdoxX]'.
414 Return the address of the character following SPEC, or zero if failure. */
415 static char *
scan_printf_spec(spec)416 scan_printf_spec (spec)
417 register char *spec;
418 {
419 register unsigned char c;
420
421 while ((c = *spec++) == '-')
422 continue;
423 while (ISDIGIT (c))
424 c = *spec++;
425 if (c == '.')
426 while (ISDIGIT (c = *spec++))
427 continue;
428 switch (c)
429 {
430 case 'c': case 'd': case 'o': case 'x': case 'X':
431 return spec;
432
433 default:
434 return 0;
435 }
436 }
437