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