#include "diff.h"
#include <dirname.h>
#include <error.h>
#include <quotesys.h>
#include <regex.h>
#include <xalloc.h>
char const pr_program[] = PR_PROGRAM;
struct msg
{
struct msg *next;
char args[1];
};
static struct msg *msg_chain;
static struct msg **msg_chain_end = &msg_chain;
void
perror_with_name (char const *name)
{
error (0, errno, "%s", name);
}
void
pfatal_with_name (char const *name)
{
int e = errno;
print_message_queue ();
error (EXIT_TROUBLE, e, "%s", name);
abort ();
}
void
fatal (char const *msgid)
{
print_message_queue ();
error (EXIT_TROUBLE, 0, "%s", _(msgid));
abort ();
}
void
message (char const *format_msgid, char const *arg1, char const *arg2)
{
message5 (format_msgid, arg1, arg2, 0, 0);
}
void
message5 (char const *format_msgid, char const *arg1, char const *arg2,
char const *arg3, char const *arg4)
{
if (paginate)
{
char *p;
char const *arg[5];
int i;
size_t size[5];
size_t total_size = offsetof (struct msg, args);
struct msg *new;
arg[0] = format_msgid;
arg[1] = arg1;
arg[2] = arg2;
arg[3] = arg3 ? arg3 : "";
arg[4] = arg4 ? arg4 : "";
for (i = 0; i < 5; i++)
total_size += size[i] = strlen (arg[i]) + 1;
new = xmalloc (total_size);
for (i = 0, p = new->args; i < 5; p += size[i++])
memcpy (p, arg[i], size[i]);
*msg_chain_end = new;
new->next = 0;
msg_chain_end = &new->next;
}
else
{
if (sdiff_merge_assist)
putchar (' ');
printf (_(format_msgid), arg1, arg2, arg3, arg4);
}
}
void
print_message_queue (void)
{
char const *arg[5];
int i;
struct msg *m = msg_chain;
while (m)
{
struct msg *next = m->next;
arg[0] = m->args;
for (i = 0; i < 4; i++)
arg[i + 1] = arg[i] + strlen (arg[i]) + 1;
printf (_(arg[0]), arg[1], arg[2], arg[3], arg[4]);
free (m);
m = next;
}
}
static char const *current_name0;
static char const *current_name1;
static bool currently_recursive;
void
setup_output (char const *name0, char const *name1, bool recursive)
{
current_name0 = name0;
current_name1 = name1;
currently_recursive = recursive;
outfile = 0;
}
#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
static pid_t pr_pid;
#endif
void
begin_output (void)
{
char *name;
if (outfile != 0)
return;
name = xmalloc (strlen (current_name0) + strlen (current_name1)
+ strlen (switch_string) + 7);
sprintf (name, "diff%s %s %s", switch_string, current_name0, current_name1);
if (paginate)
{
if (fflush (stdout) != 0)
pfatal_with_name (_("write failed"));
{
#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
int pipes[2];
if (pipe (pipes) != 0)
pfatal_with_name ("pipe");
pr_pid = vfork ();
if (pr_pid < 0)
pfatal_with_name ("fork");
if (pr_pid == 0)
{
close (pipes[1]);
if (pipes[0] != STDIN_FILENO)
{
if (dup2 (pipes[0], STDIN_FILENO) < 0)
pfatal_with_name ("dup2");
close (pipes[0]);
}
execl (pr_program, pr_program, "-h", name, 0);
_exit (errno == ENOEXEC ? 126 : 127);
}
else
{
close (pipes[0]);
outfile = fdopen (pipes[1], "w");
if (!outfile)
pfatal_with_name ("fdopen");
}
#else
char *command = xmalloc (sizeof pr_program - 1 + 7
+ quote_system_arg ((char *) 0, name) + 1);
char *p;
sprintf (command, "%s -f -h ", pr_program);
p = command + sizeof pr_program - 1 + 7;
p += quote_system_arg (p, name);
*p = 0;
errno = 0;
outfile = popen (command, "w");
if (!outfile)
pfatal_with_name (command);
free (command);
#endif
}
}
else
{
outfile = stdout;
if (currently_recursive)
printf ("%s\n", name);
}
free (name);
switch (output_style)
{
case OUTPUT_CONTEXT:
print_context_header (files, 0);
break;
case OUTPUT_UNIFIED:
print_context_header (files, 1);
break;
default:
break;
}
}
void
finish_output (void)
{
if (outfile != 0 && outfile != stdout)
{
int wstatus;
int werrno = 0;
if (ferror (outfile))
fatal ("write failed");
#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
wstatus = pclose (outfile);
if (wstatus == -1)
werrno = errno;
#else
if (fclose (outfile) != 0)
pfatal_with_name (_("write failed"));
if (waitpid (pr_pid, &wstatus, 0) < 0)
pfatal_with_name ("waitpid");
#endif
if (! werrno && WIFEXITED (wstatus) && WEXITSTATUS (wstatus) == 127)
error (EXIT_TROUBLE, 0, _("subsidiary program `%s' not found"),
pr_program);
if (wstatus != 0)
error (EXIT_TROUBLE, werrno, _("subsidiary program `%s' failed"),
pr_program);
}
outfile = 0;
}
bool
lines_differ (char const *s1, char const *s2)
{
register unsigned char const *t1 = (unsigned char const *) s1;
register unsigned char const *t2 = (unsigned char const *) s2;
size_t column = 0;
while (1)
{
register unsigned char c1 = *t1++;
register unsigned char c2 = *t2++;
if (c1 != c2)
{
switch (ignore_white_space)
{
case IGNORE_ALL_SPACE:
while (ISSPACE (c1) && c1 != '\n') c1 = *t1++;
while (ISSPACE (c2) && c2 != '\n') c2 = *t2++;
break;
case IGNORE_SPACE_CHANGE:
if (ISSPACE (c1))
{
while (c1 != '\n')
{
c1 = *t1++;
if (! ISSPACE (c1))
{
--t1;
c1 = ' ';
break;
}
}
}
if (ISSPACE (c2))
{
while (c2 != '\n')
{
c2 = *t2++;
if (! ISSPACE (c2))
{
--t2;
c2 = ' ';
break;
}
}
}
if (c1 != c2)
{
if (c2 == ' ' && c1 != '\n'
&& (unsigned char const *) s1 + 1 < t1
&& ISSPACE (t1[-2]))
{
--t1;
continue;
}
if (c1 == ' ' && c2 != '\n'
&& (unsigned char const *) s2 + 1 < t2
&& ISSPACE (t2[-2]))
{
--t2;
continue;
}
}
break;
case IGNORE_TAB_EXPANSION:
if ((c1 == ' ' && c2 == '\t')
|| (c1 == '\t' && c2 == ' '))
{
size_t column2 = column;
for (;; c1 = *t1++)
{
if (c1 == ' ')
column++;
else if (c1 == '\t')
column += TAB_WIDTH - column % TAB_WIDTH;
else
break;
}
for (;; c2 = *t2++)
{
if (c2 == ' ')
column2++;
else if (c2 == '\t')
column2 += TAB_WIDTH - column2 % TAB_WIDTH;
else
break;
}
if (column != column2)
return 1;
}
break;
case IGNORE_NO_WHITE_SPACE:
break;
}
if (ignore_case)
{
c1 = TOLOWER (c1);
c2 = TOLOWER (c2);
}
if (c1 != c2)
break;
}
if (c1 == '\n')
return 0;
column += c1 == '\t' ? TAB_WIDTH - column % TAB_WIDTH : 1;
}
return 1;
}
struct change *
find_change (struct change *start)
{
return start;
}
struct change *
find_reverse_change (struct change *start)
{
return start;
}
void
print_script (struct change *script,
struct change * (*hunkfun) (struct change *),
void (*printfun) (struct change *))
{
struct change *next = script;
while (next)
{
struct change *this, *end;
this = next;
end = (*hunkfun) (next);
next = end->link;
end->link = 0;
#ifdef DEBUG
debug_script (this);
#endif
(*printfun) (this);
end->link = next;
}
}
void
print_1_line (char const *line_flag, char const *const *line)
{
char const *base = line[0], *limit = line[1];
FILE *out = outfile;
char const *flag_format = 0;
if (line_flag && *line_flag)
{
flag_format = initial_tab ? "%s\t" : "%s ";
fprintf (out, flag_format, line_flag);
}
output_1_line (base, limit, flag_format, line_flag);
if ((!line_flag || line_flag[0]) && limit[-1] != '\n')
fprintf (out, "\n\\ %s\n", _("No newline at end of file"));
}
void
output_1_line (char const *base, char const *limit, char const *flag_format,
char const *line_flag)
{
if (!expand_tabs)
fwrite (base, limit - base, 1, outfile);
else
{
register FILE *out = outfile;
register unsigned char c;
register char const *t = base;
register unsigned int column = 0;
while (t < limit)
switch ((c = *t++))
{
case '\t':
{
unsigned int spaces = TAB_WIDTH - column % TAB_WIDTH;
column += spaces;
do
putc (' ', out);
while (--spaces);
}
break;
case '\r':
putc (c, out);
if (flag_format && t < limit && *t != '\n')
fprintf (out, flag_format, line_flag);
column = 0;
break;
case '\b':
if (column == 0)
continue;
column--;
putc (c, out);
break;
default:
if (ISPRINT (c))
column++;
putc (c, out);
break;
}
}
}
char const change_letter[] = { 0, 'd', 'a', 'c' };
lin
translate_line_number (struct file_data const *file, lin i)
{
return i + file->prefix_lines + 1;
}
void
translate_range (struct file_data const *file,
lin a, lin b,
long *aptr, long *bptr)
{
*aptr = translate_line_number (file, a - 1) + 1;
*bptr = translate_line_number (file, b + 1) - 1;
}
void
print_number_range (char sepchar, struct file_data *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%c%ld", trans_a, sepchar, trans_b);
else
fprintf (outfile, "%ld", trans_b);
}
enum changes
analyze_hunk (struct change *hunk,
lin *first0, lin *last0,
lin *first1, lin *last1)
{
struct change *next;
lin l0, l1;
lin show_from, show_to;
lin i;
bool trivial = ignore_blank_lines || ignore_regexp.fastmap;
size_t trivial_length = (int) ignore_blank_lines - 1;
char const * const *linbuf0 = files[0].linbuf;
char const * const *linbuf1 = files[1].linbuf;
show_from = show_to = 0;
*first0 = hunk->line0;
*first1 = hunk->line1;
next = hunk;
do
{
l0 = next->line0 + next->deleted - 1;
l1 = next->line1 + next->inserted - 1;
show_from += next->deleted;
show_to += next->inserted;
for (i = next->line0; i <= l0 && trivial; i++)
{
char const *line = linbuf0[i];
size_t len = linbuf0[i + 1] - line - 1;
if (len != trivial_length
&& (! ignore_regexp.fastmap
|| re_search (&ignore_regexp, line, len, 0, len, 0) < 0))
trivial = 0;
}
for (i = next->line1; i <= l1 && trivial; i++)
{
char const *line = linbuf1[i];
size_t len = linbuf1[i + 1] - line - 1;
if (len != trivial_length
&& (! ignore_regexp.fastmap
|| re_search (&ignore_regexp, line, len, 0, len, 0) < 0))
trivial = 0;
}
}
while ((next = next->link) != 0);
*last0 = l0;
*last1 = l1;
if (trivial)
return UNCHANGED;
return (show_from ? OLD : UNCHANGED) | (show_to ? NEW : UNCHANGED);
}
char *
concat (char const *s1, char const *s2, char const *s3)
{
char *new = xmalloc (strlen (s1) + strlen (s2) + strlen (s3) + 1);
sprintf (new, "%s%s%s", s1, s2, s3);
return new;
}
void *
zalloc (size_t size)
{
void *p = xmalloc (size);
memset (p, 0, size);
return p;
}
char *
dir_file_pathname (char const *dir, char const *file)
{
char const *base = base_name (dir);
bool omit_slash = !*base || base[strlen (base) - 1] == '/';
return concat (dir, "/" + omit_slash, file);
}
void
debug_script (struct change *sp)
{
fflush (stdout);
for (; sp; sp = sp->link)
{
long line0 = sp->line0;
long line1 = sp->line1;
long deleted = sp->deleted;
long inserted = sp->inserted;
fprintf (stderr, "%3ld %3ld delete %ld insert %ld\n",
line0, line1, deleted, inserted);
}
fflush (stderr);
}