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