]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/diff/ifdef.c
This commit was generated by cvs2svn to compensate for changes in r96263,
[FreeBSD/FreeBSD.git] / contrib / diff / ifdef.c
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
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 (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 *
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
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
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 *
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 *
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 }