import cvs 2:1.12.13+real-5 (see mircvs://src/gnu/usr.bin/cvs/ for VCS history)
[alioth/cvs.git] / 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 __RCSID("$MirOS: ports/devel/cvs/patches/patch-diff_ifdef_c,v 1.1 2010/09/15 23:41:20 tg Exp $");
25
26 struct group
27 {
28   struct file_data const *file;
29   int from, upto; /* start and limit lines for this group of lines */
30 };
31
32 static char *format_group PARAMS((int, char *, int, struct group const *));
33 static char *scan_char_literal PARAMS((char *, int *));
34 static char *scan_printf_spec PARAMS((char *));
35 static int groups_letter_value PARAMS((struct group const *, int));
36 static void format_ifdef PARAMS((char *, int, int, int, int));
37 static void print_ifdef_hunk PARAMS((struct change *));
38 static void print_ifdef_lines PARAMS((int, char *, struct group const *));
39
40 static int next_line;
41
42 /* Print the edit-script SCRIPT as a merged #ifdef file.  */
43
44 void
45 print_ifdef_script (script)
46      struct change *script;
47 {
48   next_line = - files[0].prefix_lines;
49   print_script (script, find_change, print_ifdef_hunk);
50   if (next_line < files[0].valid_lines)
51     {
52       begin_output ();
53       format_ifdef (group_format[UNCHANGED], next_line, files[0].valid_lines,
54                     next_line - files[0].valid_lines + files[1].valid_lines,
55                     files[1].valid_lines);
56     }
57 }
58
59 /* Print a hunk of an ifdef diff.
60    This is a contiguous portion of a complete edit script,
61    describing changes in consecutive lines.  */
62
63 static void
64 print_ifdef_hunk (hunk)
65      struct change *hunk;
66 {
67   int first0, last0, first1, last1, deletes, inserts;
68   char *format;
69
70   /* Determine range of line numbers involved in each file.  */
71   analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts);
72   if (inserts)
73     format = deletes ? group_format[CHANGED] : group_format[NEW];
74   else if (deletes)
75     format = group_format[OLD];
76   else
77     return;
78
79   begin_output ();
80
81   /* Print lines up to this change.  */
82   if (next_line < first0)
83     format_ifdef (group_format[UNCHANGED], next_line, first0,
84                   next_line - first0 + first1, first1);
85
86   /* Print this change.  */
87   next_line = last0 + 1;
88   format_ifdef (format, first0, next_line, first1, last1 + 1);
89 }
90
91 /* Print a set of lines according to FORMAT.
92    Lines BEG0 up to END0 are from the first file;
93    lines BEG1 up to END1 are from the second file.  */
94
95 static void
96 format_ifdef (format, beg0, end0, beg1, end1)
97      char *format;
98      int beg0, end0, beg1, end1;
99 {
100   struct group groups[2];
101
102   groups[0].file = &files[0];
103   groups[0].from = beg0;
104   groups[0].upto = end0;
105   groups[1].file = &files[1];
106   groups[1].from = beg1;
107   groups[1].upto = end1;
108   format_group (1, format, '\0', groups);
109 }
110
111 /* If DOIT is non-zero, output a set of lines according to FORMAT.
112    The format ends at the first free instance of ENDCHAR.
113    Yield the address of the terminating character.
114    GROUPS specifies which lines to print.
115    If OUT is zero, do not actually print anything; just scan the format.  */
116
117 static char *
118 format_group (doit, format, endchar, groups)
119      int doit;
120      char *format;
121      int endchar;
122      struct group const *groups;
123 {
124   register char c;
125   register char *f = format;
126
127   while ((c = *f) != endchar && c != 0)
128     {
129       f++;
130       if (c == '%')
131         {
132           char *spec = f;
133           switch ((c = *f++))
134             {
135             case '%':
136               break;
137
138             case '(':
139               /* Print if-then-else format e.g. `%(n=1?thenpart:elsepart)'.  */
140               {
141                 int i, value[2];
142                 int thendoit, elsedoit;
143
144                 for (i = 0; i < 2; i++)
145                   {
146                     unsigned char f0 = f[0];
147                     if (ISDIGIT (f0))
148                       {
149                         value[i] = atoi (f);
150                         while (ISDIGIT ((unsigned char) *++f))
151                           continue;
152                       }
153                     else
154                       {
155                         value[i] = groups_letter_value (groups, f0);
156                         if (value[i] < 0)
157                           goto bad_format;
158                         f++;
159                       }
160                     if (*f++ != "=?"[i])
161                       goto bad_format;
162                   }
163                 if (value[0] == value[1])
164                   thendoit = doit, elsedoit = 0;
165                 else
166                   thendoit = 0, elsedoit = doit;
167                 f = format_group (thendoit, f, ':', groups);
168                 if (*f)
169                   {
170                     f = format_group (elsedoit, f + 1, ')', groups);
171                     if (*f)
172                       f++;
173                   }
174               }
175               continue;
176
177             case '<':
178               /* Print lines deleted from first file.  */
179               print_ifdef_lines (doit, line_format[OLD], &groups[0]);
180               continue;
181
182             case '=':
183               /* Print common lines.  */
184               print_ifdef_lines (doit, line_format[UNCHANGED], &groups[0]);
185               continue;
186
187             case '>':
188               /* Print lines inserted from second file.  */
189               print_ifdef_lines (doit, line_format[NEW], &groups[1]);
190               continue;
191
192             default:
193               {
194                 int value = 0;
195                 char *speclim;
196
197                 f = scan_printf_spec (spec);
198                 if (!f)
199                   goto bad_format;
200                 speclim = f;
201                 c = *f++;
202                 switch (c)
203                   {
204                     case '\'':
205                       f = scan_char_literal (f, &value);
206                       if (!f)
207                         goto bad_format;
208                       break;
209
210                     default:
211                       value = groups_letter_value (groups, c);
212                       if (value < 0)
213                         goto bad_format;
214                       break;
215                   }
216                 if (doit)
217                   {
218                     /* Temporarily replace e.g. "%3dnx" with "%3d\0x".  */
219                     *speclim = 0;
220                     printf_output (spec - 1, value);
221                     /* Undo the temporary replacement.  */
222                     *speclim = c;
223                   }
224               }
225               continue;
226
227             bad_format:
228               c = '%';
229               f = spec;
230               break;
231             }
232         }
233       if (doit)
234         {
235           /* Don't take the address of a register variable.  */
236           char cc = c;
237           write_output (&cc, 1);
238         }
239     }
240   return f;
241 }
242
243 /* For the line group pair G, return the number corresponding to LETTER.
244    Return -1 if LETTER is not a group format letter.  */
245 static int
246 groups_letter_value (g, letter)
247      struct group const *g;
248      int letter;
249 {
250   if (ISUPPER (letter))
251     {
252       g++;
253       letter = tolower (letter);
254     }
255   switch (letter)
256     {
257       case 'e': return translate_line_number (g->file, g->from) - 1;
258       case 'f': return translate_line_number (g->file, g->from);
259       case 'l': return translate_line_number (g->file, g->upto) - 1;
260       case 'm': return translate_line_number (g->file, g->upto);
261       case 'n': return g->upto - g->from;
262       default: return -1;
263     }
264 }
265
266 /* Output using FORMAT to print the line group GROUP.
267    But do nothing if DOIT is zero.  */
268 static void
269 print_ifdef_lines (doit, format, group)
270      int doit;
271      char *format;
272      struct group const *group;
273 {
274   struct file_data const *file = group->file;
275   char const * const *linbuf = file->linbuf;
276   int from = group->from, upto = group->upto;
277
278   if (!doit)
279     return;
280
281   /* If possible, use a single fwrite; it's faster.  */
282   if (!tab_expand_flag && format[0] == '%')
283     {
284       if (format[1] == 'l' && format[2] == '\n' && !format[3])
285         {
286           write_output (linbuf[from],
287                         (linbuf[upto] + (linbuf[upto][-1] != '\n')
288                          - linbuf[from]));
289           return;
290         }
291       if (format[1] == 'L' && !format[2])
292         {
293           write_output (linbuf[from],
294                         linbuf[upto] -  linbuf[from]);
295           return;
296         }
297     }
298
299   for (;  from < upto;  from++)
300     {
301       register char c;
302       register char *f = format;
303       char cc;
304
305       while ((c = *f++) != 0)
306         {
307           if (c == '%')
308             {
309               char *spec = f;
310               switch ((c = *f++))
311                 {
312                 case '%':
313                   break;
314
315                 case 'l':
316                   output_1_line (linbuf[from],
317                                  linbuf[from + 1]
318                                    - (linbuf[from + 1][-1] == '\n'), 0, 0);
319                   continue;
320
321                 case 'L':
322                   output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
323                   continue;
324
325                 default:
326                   {
327                     int value = 0;
328                     char *speclim;
329
330                     f = scan_printf_spec (spec);
331                     if (!f)
332                       goto bad_format;
333                     speclim = f;
334                     c = *f++;
335                     switch (c)
336                       {
337                         case '\'':
338                           f = scan_char_literal (f, &value);
339                           if (!f)
340                             goto bad_format;
341                           break;
342
343                         case 'n':
344                           value = translate_line_number (file, from);
345                           break;
346
347                         default:
348                           goto bad_format;
349                       }
350                     /* Temporarily replace e.g. "%3dnx" with "%3d\0x".  */
351                     *speclim = 0;
352                     printf_output (spec - 1, value);
353                     /* Undo the temporary replacement.  */
354                     *speclim = c;
355                   }
356                   continue;
357
358                 bad_format:
359                   c = '%';
360                   f = spec;
361                   break;
362                 }
363             }
364
365           /* Don't take the address of a register variable.  */
366           cc = c;
367           write_output (&cc, 1);
368         }
369     }
370 }
371
372 /* Scan the character literal represented in the string LIT; LIT points just
373    after the initial apostrophe.  Put the literal's value into *INTPTR.
374    Yield the address of the first character after the closing apostrophe,
375    or zero if the literal is ill-formed.  */
376 static char *
377 scan_char_literal (lit, intptr)
378      char *lit;
379      int *intptr;
380 {
381   register char *p = lit;
382   int value, digits;
383   char c = *p++;
384
385   switch (c)
386     {
387       case 0:
388       case '\'':
389         return 0;
390
391       case '\\':
392         value = 0;
393         while ((c = *p++) != '\'')
394           {
395             unsigned digit = c - '0';
396             if (8 <= digit)
397               return 0;
398             value = 8 * value + digit;
399           }
400         digits = p - lit - 2;
401         if (! (1 <= digits && digits <= 3))
402           return 0;
403         break;
404
405       default:
406         value = c;
407         if (*p++ != '\'')
408           return 0;
409         break;
410     }
411   *intptr = value;
412   return p;
413 }
414
415 /* Scan optional printf-style SPEC of the form `-*[0-9]*(.[0-9]*)?[cdoxX]'.
416    Return the address of the character following SPEC, or zero if failure.  */
417 static char *
418 scan_printf_spec (spec)
419      register char *spec;
420 {
421   register unsigned char c;
422
423   while ((c = *spec++) == '-')
424     continue;
425   while (ISDIGIT (c))
426     c = *spec++;
427   if (c == '.')
428     while (ISDIGIT (c = *spec++))
429       continue;
430   switch (c)
431     {
432       case 'c': case 'd': case 'o': case 'x': case 'X':
433         return spec;
434
435       default:
436         return 0;
437     }
438 }