~ubuntu-branches/ubuntu/trusty/diffutils/trusty-proposed

« back to all changes in this revision

Viewing changes to src/ifdef.c

  • Committer: Bazaar Package Importer
  • Author(s): Santiago Vila
  • Date: 2005-02-15 22:45:18 UTC
  • Revision ID: james.westby@ubuntu.com-20050215224518-dw9ti3me00twpcmt
Tags: upstream-2.8.1
ImportĀ upstreamĀ versionĀ 2.8.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}