#include "diff.h"
#include <inttostr.h>
#include <regex.h>
#ifdef ST_MTIM_NSEC
# define TIMESPEC_NS(timespec) ((timespec).ST_MTIM_NSEC)
#else
# define TIMESPEC_NS(timespec) 0
#endif
size_t nstrftime (char *, size_t, char const *, struct tm const *, int, int);
static char const *find_function (char const * const *, lin);
static struct change *find_hunk (struct change *);
static void mark_ignorable (struct change *);
static void pr_context_hunk (struct change *);
static void pr_unidiff_hunk (struct change *);
static lin find_function_last_search;
static lin find_function_last_match;
static void
print_context_label (char const *mark,
struct file_data *inf,
char const *label)
{
if (label)
fprintf (outfile, "%s %s\n", mark, label);
else
{
char buf[MAX (INT_STRLEN_BOUND (int) + 32,
INT_STRLEN_BOUND (time_t) + 11)];
struct tm const *tm = localtime (&inf->stat.st_mtime);
int nsec = TIMESPEC_NS (inf->stat.st_mtim);
if (! (tm && nstrftime (buf, sizeof buf, time_format, tm, 0, nsec)))
{
time_t sec = inf->stat.st_mtime;
verify (info_preserved, sizeof inf->stat.st_mtime <= sizeof sec);
sprintf (buf, "%"PRIdMAX".%.9d", (intmax_t) sec, nsec);
}
fprintf (outfile, "%s %s\t%s\n", mark, inf->name, buf);
}
}
void
print_context_header (struct file_data inf[], bool unidiff)
{
if (unidiff)
{
print_context_label ("---", &inf[0], file_label[0]);
print_context_label ("+++", &inf[1], file_label[1]);
}
else
{
print_context_label ("***", &inf[0], file_label[0]);
print_context_label ("---", &inf[1], file_label[1]);
}
}
void
print_context_script (struct change *script, bool unidiff)
{
if (ignore_blank_lines || ignore_regexp.fastmap)
mark_ignorable (script);
else
{
struct change *e;
for (e = script; e; e = e->link)
e->ignore = 0;
}
find_function_last_search = - files[0].prefix_lines;
find_function_last_match = LIN_MAX;
if (unidiff)
print_script (script, find_hunk, pr_unidiff_hunk);
else
print_script (script, find_hunk, pr_context_hunk);
}
static void
print_context_number_range (struct file_data const *file, lin a, lin b)
{
long trans_a, trans_b;
translate_range (file, a, b, &trans_a, &trans_b);
if (trans_b <= trans_a)
fprintf (outfile, "%ld", trans_b);
else
fprintf (outfile, "%ld,%ld", trans_a, trans_b);
}
static void
print_context_function (FILE *out, char const *function)
{
int i;
putc (' ', out);
for (i = 0; i < 40 && function[i] != '\n'; i++)
continue;
fwrite (function, 1, i, out);
}
static void
pr_context_hunk (struct change *hunk)
{
lin first0, last0, first1, last1, i;
char const *prefix;
char const *function;
FILE *out;
enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
if (! changes)
return;
i = - files[0].prefix_lines;
first0 = MAX (first0 - context, i);
first1 = MAX (first1 - context, i);
if (last0 < files[0].valid_lines - context)
last0 += context;
else
last0 = files[0].valid_lines - 1;
if (last1 < files[1].valid_lines - context)
last1 += context;
else
last1 = files[1].valid_lines - 1;
function = 0;
if (function_regexp.fastmap)
function = find_function (files[0].linbuf, first0);
begin_output ();
out = outfile;
fprintf (out, "***************");
if (function)
print_context_function (out, function);
fprintf (out, "\n*** ");
print_context_number_range (&files[0], first0, last0);
fprintf (out, " ****\n");
if (changes & OLD)
{
struct change *next = hunk;
for (i = first0; i <= last0; i++)
{
while (next && next->line0 + next->deleted <= i)
next = next->link;
prefix = " ";
if (next && next->line0 <= i)
prefix = (next->inserted > 0 ? "!" : "-");
print_1_line (prefix, &files[0].linbuf[i]);
}
}
fprintf (out, "--- ");
print_context_number_range (&files[1], first1, last1);
fprintf (out, " ----\n");
if (changes & NEW)
{
struct change *next = hunk;
for (i = first1; i <= last1; i++)
{
while (next && next->line1 + next->inserted <= i)
next = next->link;
prefix = " ";
if (next && next->line1 <= i)
prefix = (next->deleted > 0 ? "!" : "+");
print_1_line (prefix, &files[1].linbuf[i]);
}
}
}
static void
print_unidiff_number_range (struct file_data const *file, lin a, lin b)
{
long trans_a, trans_b;
translate_range (file, a, b, &trans_a, &trans_b);
if (trans_b <= trans_a)
fprintf (outfile, trans_b < trans_a ? "%ld,0" : "%ld", trans_b);
else
fprintf (outfile, "%ld,%ld", trans_a, trans_b - trans_a + 1);
}
static void
pr_unidiff_hunk (struct change *hunk)
{
lin first0, last0, first1, last1;
lin i, j, k;
struct change *next;
char const *function;
FILE *out;
if (! analyze_hunk (hunk, &first0, &last0, &first1, &last1))
return;
i = - files[0].prefix_lines;
first0 = MAX (first0 - context, i);
first1 = MAX (first1 - context, i);
if (last0 < files[0].valid_lines - context)
last0 += context;
else
last0 = files[0].valid_lines - 1;
if (last1 < files[1].valid_lines - context)
last1 += context;
else
last1 = files[1].valid_lines - 1;
function = 0;
if (function_regexp.fastmap)
function = find_function (files[0].linbuf, first0);
begin_output ();
out = outfile;
fprintf (out, "@@ -");
print_unidiff_number_range (&files[0], first0, last0);
fprintf (out, " +");
print_unidiff_number_range (&files[1], first1, last1);
fprintf (out, " @@");
if (function)
print_context_function (out, function);
putc ('\n', out);
next = hunk;
i = first0;
j = first1;
while (i <= last0 || j <= last1)
{
if (!next || i < next->line0)
{
putc (initial_tab ? '\t' : ' ', out);
print_1_line (0, &files[0].linbuf[i++]);
j++;
}
else
{
k = next->deleted;
while (k--)
{
putc ('-', out);
if (initial_tab)
putc ('\t', out);
print_1_line (0, &files[0].linbuf[i++]);
}
k = next->inserted;
while (k--)
{
putc ('+', out);
if (initial_tab)
putc ('\t', out);
print_1_line (0, &files[1].linbuf[j++]);
}
next = next->link;
}
}
}
static struct change *
find_hunk (struct change *start)
{
struct change *prev;
lin top0, top1;
lin thresh;
lin non_ignorable_threshold =
(LIN_MAX - 1) / 2 < context ? LIN_MAX : 2 * context + 1;
lin ignorable_threshold = context;
do
{
top0 = start->line0 + start->deleted;
top1 = start->line1 + start->inserted;
prev = start;
start = start->link;
thresh = (prev->ignore || (start && start->ignore)
? ignorable_threshold
: non_ignorable_threshold);
if (start && start->line0 - top0 != start->line1 - top1)
abort ();
} while (start
&& start->line0 - top0 < thresh);
return prev;
}
static void
mark_ignorable (struct change *script)
{
while (script)
{
struct change *next = script->link;
lin first0, last0, first1, last1;
script->link = 0;
script->ignore = ! analyze_hunk (script,
&first0, &last0, &first1, &last1);
script->link = next;
script = next;
}
}
static char const *
find_function (char const * const *linbuf, lin linenum)
{
lin i = linenum;
lin last = find_function_last_search;
find_function_last_search = i;
while (last <= --i)
{
char const *line = linbuf[i];
size_t linelen = linbuf[i + 1] - line - 1;
int len = MIN (linelen, INT_MAX);
if (0 <= re_search (&function_regexp, line, len, 0, len, 0))
{
find_function_last_match = i;
return line;
}
}
if (find_function_last_match != LIN_MAX)
return linbuf[find_function_last_match];
return 0;
}