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