#include "diff.h"
struct group
{
struct file_data const *file;
int from, upto;
};
static char *format_group PARAMS((int, char *, int, struct group const *));
static char *scan_char_literal PARAMS((char *, int *));
static char *scan_printf_spec PARAMS((char *));
static int groups_letter_value PARAMS((struct group const *, int));
static void format_ifdef PARAMS((char *, int, int, int, int));
static void print_ifdef_hunk PARAMS((struct change *));
static void print_ifdef_lines PARAMS((int, char *, struct group const *));
static int next_line;
void
print_ifdef_script (script)
struct change *script;
{
next_line = - files[0].prefix_lines;
print_script (script, find_change, print_ifdef_hunk);
if (next_line < files[0].valid_lines)
{
begin_output ();
format_ifdef (group_format[UNCHANGED], next_line, files[0].valid_lines,
next_line - files[0].valid_lines + files[1].valid_lines,
files[1].valid_lines);
}
}
static void
print_ifdef_hunk (hunk)
struct change *hunk;
{
int first0, last0, first1, last1, deletes, inserts;
char *format;
analyze_hunk (hunk, &first0, &last0, &first1, &last1, &deletes, &inserts);
if (inserts)
format = deletes ? group_format[CHANGED] : group_format[NEW];
else if (deletes)
format = group_format[OLD];
else
return;
begin_output ();
if (next_line < first0)
format_ifdef (group_format[UNCHANGED], next_line, first0,
next_line - first0 + first1, first1);
next_line = last0 + 1;
format_ifdef (format, first0, next_line, first1, last1 + 1);
}
static void
format_ifdef (format, beg0, end0, beg1, end1)
char *format;
int beg0, end0, beg1, end1;
{
struct group groups[2];
groups[0].file = &files[0];
groups[0].from = beg0;
groups[0].upto = end0;
groups[1].file = &files[1];
groups[1].from = beg1;
groups[1].upto = end1;
format_group (1, format, '\0', groups);
}
static char *
format_group (doit, format, endchar, groups)
int doit;
char *format;
int endchar;
struct group const *groups;
{
register char c;
register char *f = format;
while ((c = *f) != endchar && c != 0)
{
f++;
if (c == '%')
{
char *spec = f;
switch ((c = *f++))
{
case '%':
break;
case '(':
{
int i, value[2];
int thendoit, elsedoit;
for (i = 0; i < 2; i++)
{
unsigned char f0 = f[0];
if (ISDIGIT (f0))
{
value[i] = atoi (f);
while (ISDIGIT ((unsigned char) *++f))
continue;
}
else
{
value[i] = groups_letter_value (groups, f0);
if (value[i] < 0)
goto bad_format;
f++;
}
if (*f++ != "=?"[i])
goto bad_format;
}
if (value[0] == value[1])
thendoit = doit, elsedoit = 0;
else
thendoit = 0, elsedoit = doit;
f = format_group (thendoit, f, ':', groups);
if (*f)
{
f = format_group (elsedoit, f + 1, ')', groups);
if (*f)
f++;
}
}
continue;
case '<':
print_ifdef_lines (doit, line_format[OLD], &groups[0]);
continue;
case '=':
print_ifdef_lines (doit, line_format[UNCHANGED], &groups[0]);
continue;
case '>':
print_ifdef_lines (doit, line_format[NEW], &groups[1]);
continue;
default:
{
int value;
char *speclim;
f = scan_printf_spec (spec);
if (!f)
goto bad_format;
speclim = f;
c = *f++;
switch (c)
{
case '\'':
f = scan_char_literal (f, &value);
if (!f)
goto bad_format;
break;
default:
value = groups_letter_value (groups, c);
if (value < 0)
goto bad_format;
break;
}
if (doit)
{
*speclim = 0;
printf_output (spec - 1, value);
*speclim = c;
}
}
continue;
bad_format:
c = '%';
f = spec;
break;
}
}
if (doit)
{
char cc = c;
write_output (&cc, 1);
}
}
return f;
}
static int
groups_letter_value (g, letter)
struct group const *g;
int letter;
{
if (ISUPPER (letter))
{
g++;
letter = tolower (letter);
}
switch (letter)
{
case 'e': return translate_line_number (g->file, g->from) - 1;
case 'f': return translate_line_number (g->file, g->from);
case 'l': return translate_line_number (g->file, g->upto) - 1;
case 'm': return translate_line_number (g->file, g->upto);
case 'n': return g->upto - g->from;
default: return -1;
}
}
static void
print_ifdef_lines (doit, format, group)
int doit;
char *format;
struct group const *group;
{
struct file_data const *file = group->file;
char const * const *linbuf = file->linbuf;
int from = group->from, upto = group->upto;
if (!doit)
return;
if (!tab_expand_flag && format[0] == '%')
{
if (format[1] == 'l' && format[2] == '\n' && !format[3])
{
write_output (linbuf[from],
(linbuf[upto] + (linbuf[upto][-1] != '\n')
- linbuf[from]));
return;
}
if (format[1] == 'L' && !format[2])
{
write_output (linbuf[from],
linbuf[upto] - linbuf[from]);
return;
}
}
for (; from < upto; from++)
{
register char c;
register char *f = format;
char cc;
while ((c = *f++) != 0)
{
if (c == '%')
{
char *spec = f;
switch ((c = *f++))
{
case '%':
break;
case 'l':
output_1_line (linbuf[from],
linbuf[from + 1]
- (linbuf[from + 1][-1] == '\n'), 0, 0);
continue;
case 'L':
output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
continue;
default:
{
int value;
char *speclim;
f = scan_printf_spec (spec);
if (!f)
goto bad_format;
speclim = f;
c = *f++;
switch (c)
{
case '\'':
f = scan_char_literal (f, &value);
if (!f)
goto bad_format;
break;
case 'n':
value = translate_line_number (file, from);
break;
default:
goto bad_format;
}
*speclim = 0;
printf_output (spec - 1, value);
*speclim = c;
}
continue;
bad_format:
c = '%';
f = spec;
break;
}
}
cc = c;
write_output (&cc, 1);
}
}
}
static char *
scan_char_literal (lit, intptr)
char *lit;
int *intptr;
{
register char *p = lit;
int value, digits;
char c = *p++;
switch (c)
{
case 0:
case '\'':
return 0;
case '\\':
value = 0;
while ((c = *p++) != '\'')
{
unsigned digit = c - '0';
if (8 <= digit)
return 0;
value = 8 * value + digit;
}
digits = p - lit - 2;
if (! (1 <= digits && digits <= 3))
return 0;
break;
default:
value = c;
if (*p++ != '\'')
return 0;
break;
}
*intptr = value;
return p;
}
static char *
scan_printf_spec (spec)
register char *spec;
{
register unsigned char c;
while ((c = *spec++) == '-')
continue;
while (ISDIGIT (c))
c = *spec++;
if (c == '.')
while (ISDIGIT (c = *spec++))
continue;
switch (c)
{
case 'c': case 'd': case 'o': case 'x': case 'X':
return spec;
default:
return 0;
}
}