Merge from vendor branch BIND:
[dragonfly.git] / contrib / cvs-1.12 / diff / ifdef.c
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
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
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
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 *
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
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
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 *
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 *
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 }