#include "system.h"
static char const copyright_string[] =
"Copyright (C) 2002 Free Software Foundation, Inc.";
static char const authorship_msgid[] = N_("Written by Randy Smith.");
#include <c-stack.h>
#include <cmpbuf.h>
#include <error.h>
#include <exitfail.h>
#include <freesoft.h>
#include <getopt.h>
#include <inttostr.h>
#include <quotesys.h>
#include <stdio.h>
#include <xalloc.h>
extern char const version_string[];
#define FILE0 0
#define FILE1 1
#define FILE2 2
#define FILEC FILE2
#define FO 0
#define FC 1
#define RANGE_START 0
#define RANGE_END 1
enum diff_type {
ERROR,
ADD,
CHANGE,
DELETE,
DIFF_ALL,
DIFF_1ST,
DIFF_2ND,
DIFF_3RD
};
struct diff_block {
lin ranges[2][2];
char **lines[2];
size_t *lengths[2];
struct diff_block *next;
};
struct diff3_block {
enum diff_type correspond;
lin ranges[3][2];
char **lines[3];
size_t *lengths[3];
struct diff3_block *next;
};
#define D_LOWLINE(diff, filenum) \
((diff)->ranges[filenum][RANGE_START])
#define D_HIGHLINE(diff, filenum) \
((diff)->ranges[filenum][RANGE_END])
#define D_NUMLINES(diff, filenum) \
(D_HIGHLINE (diff, filenum) - D_LOWLINE (diff, filenum) + 1)
#define D_RELNUM(diff, filenum, linenum) \
((diff)->lines[filenum][linenum])
#define D_RELLEN(diff, filenum, linenum) \
((diff)->lengths[filenum][linenum])
#define D_LINEARRAY(diff, filenum) \
((diff)->lines[filenum])
#define D_LENARRAY(diff, filenum) \
((diff)->lengths[filenum])
#define D_NEXT(diff) ((diff)->next)
#define D3_TYPE(diff) ((diff)->correspond)
#define D_HIGH_MAPLINE(diff, fromfile, tofile, linenum) \
((linenum) \
- D_HIGHLINE ((diff), (fromfile)) \
+ D_HIGHLINE ((diff), (tofile)))
#define D_LOW_MAPLINE(diff, fromfile, tofile, linenum) \
((linenum) \
- D_LOWLINE ((diff), (fromfile)) \
+ D_LOWLINE ((diff), (tofile)))
static bool text;
static bool edscript;
static bool flagging;
static bool initial_tab;
static bool simple_only;
static bool overlap_only;
static bool show_2nd;
static bool finalwrite;
static bool merge;
char *program_name;
static char *read_diff (char const *, char const *, char **);
static char *scan_diff_line (char *, char **, size_t *, char *, char);
static enum diff_type process_diff_control (char **, struct diff_block *);
static bool compare_line_list (char * const[], size_t const[], char * const[], size_t const[], lin);
static bool copy_stringlist (char * const[], size_t const[], char *[], size_t[], lin);
static bool output_diff3_edscript (FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *);
static bool output_diff3_merge (FILE *, FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *);
static struct diff3_block *create_diff3_block (lin, lin, lin, lin, lin, lin);
static struct diff3_block *make_3way_diff (struct diff_block *, struct diff_block *);
static struct diff3_block *reverse_diff3_blocklist (struct diff3_block *);
static struct diff3_block *using_to_diff3_block (struct diff_block *[2], struct diff_block *[2], int, int, struct diff3_block const *);
static struct diff_block *process_diff (char const *, char const *, struct diff_block **);
static void check_stdout (void);
static void fatal (char const *) __attribute__((noreturn));
static void output_diff3 (FILE *, struct diff3_block *, int const[3], int const[3]);
static void perror_with_exit (char const *) __attribute__((noreturn));
static void try_help (char const *, char const *) __attribute__((noreturn));
static void usage (void);
static char const *diff_program = DEFAULT_DIFF_PROGRAM;
enum
{
DIFF_PROGRAM_OPTION = CHAR_MAX + 1,
HELP_OPTION
};
static struct option const longopts[] =
{
{"text", 0, 0, 'a'},
{"show-all", 0, 0, 'A'},
{"ed", 0, 0, 'e'},
{"diff-program", 1, 0, DIFF_PROGRAM_OPTION},
{"show-overlap", 0, 0, 'E'},
{"label", 1, 0, 'L'},
{"merge", 0, 0, 'm'},
{"initial-tab", 0, 0, 'T'},
{"overlap-only", 0, 0, 'x'},
{"easy-only", 0, 0, '3'},
{"version", 0, 0, 'v'},
{"help", 0, 0, HELP_OPTION},
{0, 0, 0, 0}
};
int
main (int argc, char **argv)
{
int c, i;
int common;
int mapping[3];
int rev_mapping[3];
int incompat = 0;
bool conflicts_found;
struct diff_block *thread0, *thread1, *last_block;
struct diff3_block *diff3;
int tag_count = 0;
char *tag_strings[3];
char *commonname;
char **file;
struct stat statb;
exit_failure = 2;
initialize_main (&argc, &argv);
program_name = argv[0];
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
c_stack_action (c_stack_die);
while ((c = getopt_long (argc, argv, "aeimvx3AEL:TX", longopts, 0)) != -1)
{
switch (c)
{
case 'a':
text = 1;
break;
case 'A':
show_2nd = 1;
flagging = 1;
incompat++;
break;
case 'x':
overlap_only = 1;
incompat++;
break;
case '3':
simple_only = 1;
incompat++;
break;
case 'i':
finalwrite = 1;
break;
case 'm':
merge = 1;
break;
case 'X':
overlap_only = 1;
case 'E':
flagging = 1;
case 'e':
incompat++;
break;
case 'T':
initial_tab = 1;
break;
case 'v':
printf ("diff3 %s\n%s\n\n%s\n\n%s\n",
version_string, copyright_string,
_(free_software_msgid), _(authorship_msgid));
check_stdout ();
return EXIT_SUCCESS;
case DIFF_PROGRAM_OPTION:
diff_program = optarg;
break;
case HELP_OPTION:
usage ();
check_stdout ();
return EXIT_SUCCESS;
case 'L':
if (tag_count < 3)
{
tag_strings[tag_count++] = optarg;
break;
}
try_help ("too many file label options", 0);
default:
try_help (0, 0);
}
}
edscript = incompat & ~merge;
show_2nd |= ~incompat & merge;
flagging |= ~incompat & merge;
if (incompat > 1
|| finalwrite & merge
|| (tag_count && ! flagging))
try_help ("incompatible options", 0);
if (argc - optind != 3)
{
if (argc - optind < 3)
try_help ("missing operand after `%s'", argv[argc - 1]);
else
try_help ("extra operand `%s'", argv[optind + 3]);
}
file = &argv[optind];
for (i = tag_count; i < 3; i++)
tag_strings[i] = file[i];
common = 2 - (edscript | merge);
if (strcmp (file[common], "-") == 0)
{
common = 3 - common;
if (strcmp (file[0], "-") == 0 || strcmp (file[common], "-") == 0)
fatal ("`-' specified for more than one input file");
}
mapping[0] = 0;
mapping[1] = 3 - common;
mapping[2] = common;
for (i = 0; i < 3; i++)
rev_mapping[mapping[i]] = i;
for (i = 0; i < 3; i++)
if (strcmp (file[i], "-") != 0)
{
if (stat (file[i], &statb) < 0)
perror_with_exit (file[i]);
else if (S_ISDIR (statb.st_mode))
error (EXIT_TROUBLE, EISDIR, "%s", file[i]);
}
#ifdef SIGCHLD
signal (SIGCHLD, SIG_DFL);
#endif
commonname = file[rev_mapping[FILEC]];
thread1 = process_diff (file[rev_mapping[FILE1]], commonname, &last_block);
thread0 = process_diff (file[rev_mapping[FILE0]], commonname, &last_block);
diff3 = make_3way_diff (thread0, thread1);
if (edscript)
conflicts_found
= output_diff3_edscript (stdout, diff3, mapping, rev_mapping,
tag_strings[0], tag_strings[1], tag_strings[2]);
else if (merge)
{
if (! freopen (file[rev_mapping[FILE0]], "r", stdin))
perror_with_exit (file[rev_mapping[FILE0]]);
conflicts_found
= output_diff3_merge (stdin, stdout, diff3, mapping, rev_mapping,
tag_strings[0], tag_strings[1], tag_strings[2]);
if (ferror (stdin))
fatal ("read failed");
}
else
{
output_diff3 (stdout, diff3, mapping, rev_mapping);
conflicts_found = 0;
}
check_stdout ();
exit (conflicts_found);
return conflicts_found;
}
static void
try_help (char const *reason_msgid, char const *operand)
{
if (reason_msgid)
error (0, 0, _(reason_msgid), operand);
error (EXIT_TROUBLE, 0,
_("Try `%s --help' for more information."), program_name);
abort ();
}
static void
check_stdout (void)
{
if (ferror (stdout))
fatal ("write failed");
else if (fclose (stdout) != 0)
perror_with_exit (_("standard output"));
}
static char const * const option_help_msgid[] = {
N_("-e --ed Output unmerged changes from OLDFILE to YOURFILE into MYFILE."),
N_("-E --show-overlap Output unmerged changes, bracketing conflicts."),
N_("-A --show-all Output all changes, bracketing conflicts."),
N_("-x --overlap-only Output overlapping changes."),
N_("-X Output overlapping changes, bracketing them."),
N_("-3 --easy-only Output unmerged nonoverlapping changes."),
"",
N_("-m --merge Output merged file instead of ed script (default -A)."),
N_("-L LABEL --label=LABEL Use LABEL instead of file name."),
N_("-i Append `w' and `q' commands to ed scripts."),
N_("-a --text Treat all files as text."),
N_("-T --initial-tab Make tabs line up by prepending a tab."),
N_("--diff-program=PROGRAM Use PROGRAM to compare files."),
"",
N_("-v --version Output version info."),
N_("--help Output this help."),
0
};
static void
usage (void)
{
char const * const *p;
printf (_("Usage: %s [OPTION]... MYFILE OLDFILE YOURFILE\n"),
program_name);
printf ("%s\n\n", _("Compare three files line by line."));
for (p = option_help_msgid; *p; p++)
if (**p)
printf (" %s\n", _(*p));
else
putchar ('\n');
printf ("\n%s\n\n%s\n",
_("If a FILE is `-', read standard input."),
_("Report bugs to <bug-gnu-utils@gnu.org>."));
}
static struct diff3_block *
make_3way_diff (struct diff_block *thread0, struct diff_block *thread1)
{
struct diff_block *using[2];
struct diff_block *last_using[2];
struct diff_block *current[2];
lin high_water_mark;
int high_water_thread;
int base_water_thread;
int other_thread;
struct diff_block *high_water_diff;
struct diff_block *other_diff;
struct diff3_block *result;
struct diff3_block *tmpblock;
struct diff3_block **result_end;
struct diff3_block const *last_diff3;
static struct diff3_block const zero_diff3;
result = 0;
result_end = &result;
current[0] = thread0; current[1] = thread1;
last_diff3 = &zero_diff3;
while (current[0] || current[1])
{
using[0] = using[1] = last_using[0] = last_using[1] = 0;
if (!current[0])
base_water_thread = 1;
else if (!current[1])
base_water_thread = 0;
else
base_water_thread =
(D_LOWLINE (current[0], FC) > D_LOWLINE (current[1], FC));
high_water_thread = base_water_thread;
high_water_diff = current[high_water_thread];
high_water_mark = D_HIGHLINE (high_water_diff, FC);
using[high_water_thread]
= last_using[high_water_thread]
= high_water_diff;
current[high_water_thread] = high_water_diff->next;
last_using[high_water_thread]->next = 0;
other_thread = high_water_thread ^ 0x1;
other_diff = current[other_thread];
while (other_diff
&& D_LOWLINE (other_diff, FC) <= high_water_mark + 1)
{
if (using[other_thread])
last_using[other_thread]->next = other_diff;
else
using[other_thread] = other_diff;
last_using[other_thread] = other_diff;
current[other_thread] = current[other_thread]->next;
other_diff->next = 0;
if (high_water_mark < D_HIGHLINE (other_diff, FC))
{
high_water_thread ^= 1;
high_water_diff = other_diff;
high_water_mark = D_HIGHLINE (other_diff, FC);
}
other_thread = high_water_thread ^ 0x1;
other_diff = current[other_thread];
}
tmpblock = using_to_diff3_block (using, last_using,
base_water_thread, high_water_thread,
last_diff3);
if (!tmpblock)
fatal ("internal error: screwup in format of diff blocks");
*result_end = tmpblock;
result_end = &tmpblock->next;
last_diff3 = tmpblock;
}
return result;
}
static struct diff3_block *
using_to_diff3_block (struct diff_block *using[2],
struct diff_block *last_using[2],
int low_thread, int high_thread,
struct diff3_block const *last_diff3)
{
lin low[2], high[2];
struct diff3_block *result;
struct diff_block *ptr;
int d;
lin i;
lin lowc = D_LOWLINE (using[low_thread], FC);
lin highc = D_HIGHLINE (last_using[high_thread], FC);
for (d = 0; d < 2; d++)
if (using[d])
{
low[d] = D_LOW_MAPLINE (using[d], FC, FO, lowc);
high[d] = D_HIGH_MAPLINE (last_using[d], FC, FO, highc);
}
else
{
low[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, lowc);
high[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, highc);
}
result = create_diff3_block (low[0], high[0], low[1], high[1], lowc, highc);
for (d = 0; d < 2; d++)
for (ptr = using[d]; ptr; ptr = D_NEXT (ptr))
{
lin result_offset = D_LOWLINE (ptr, FC) - lowc;
if (!copy_stringlist (D_LINEARRAY (ptr, FC),
D_LENARRAY (ptr, FC),
D_LINEARRAY (result, FILEC) + result_offset,
D_LENARRAY (result, FILEC) + result_offset,
D_NUMLINES (ptr, FC)))
return 0;
}
for (d = 0; d < 2; d++)
{
struct diff_block *u = using[d];
lin lo = low[d], hi = high[d];
for (i = 0;
i + lo < (u ? D_LOWLINE (u, FO) : hi + 1);
i++)
{
D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, i);
D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, i);
}
for (ptr = u; ptr; ptr = D_NEXT (ptr))
{
lin result_offset = D_LOWLINE (ptr, FO) - lo;
lin linec;
if (!copy_stringlist (D_LINEARRAY (ptr, FO),
D_LENARRAY (ptr, FO),
D_LINEARRAY (result, FILE0 + d) + result_offset,
D_LENARRAY (result, FILE0 + d) + result_offset,
D_NUMLINES (ptr, FO)))
return 0;
linec = D_HIGHLINE (ptr, FC) + 1 - lowc;
for (i = D_HIGHLINE (ptr, FO) + 1 - lo;
i < (D_NEXT (ptr) ? D_LOWLINE (D_NEXT (ptr), FO) : hi + 1) - lo;
i++)
{
D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, linec);
D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, linec);
linec++;
}
}
}
if (!using[0])
D3_TYPE (result) = DIFF_2ND;
else if (!using[1])
D3_TYPE (result) = DIFF_1ST;
else
{
lin nl0 = D_NUMLINES (result, FILE0);
lin nl1 = D_NUMLINES (result, FILE1);
if (nl0 != nl1
|| !compare_line_list (D_LINEARRAY (result, FILE0),
D_LENARRAY (result, FILE0),
D_LINEARRAY (result, FILE1),
D_LENARRAY (result, FILE1),
nl0))
D3_TYPE (result) = DIFF_ALL;
else
D3_TYPE (result) = DIFF_3RD;
}
return result;
}
static bool
copy_stringlist (char * const fromptrs[], size_t const fromlengths[],
char *toptrs[], size_t tolengths[],
lin copynum)
{
register char * const *f = fromptrs;
register char **t = toptrs;
register size_t const *fl = fromlengths;
register size_t *tl = tolengths;
while (copynum--)
{
if (*t)
{ if (*fl != *tl || memcmp (*f, *t, *fl)) return 0; }
else
{ *t = *f ; *tl = *fl; }
t++; f++; tl++; fl++;
}
return 1;
}
static struct diff3_block *
create_diff3_block (lin low0, lin high0,
lin low1, lin high1,
lin low2, lin high2)
{
struct diff3_block *result = xmalloc (sizeof *result);
lin numlines;
D3_TYPE (result) = ERROR;
D_NEXT (result) = 0;
D_LOWLINE (result, FILE0) = low0;
D_HIGHLINE (result, FILE0) = high0;
D_LOWLINE (result, FILE1) = low1;
D_HIGHLINE (result, FILE1) = high1;
D_LOWLINE (result, FILE2) = low2;
D_HIGHLINE (result, FILE2) = high2;
numlines = D_NUMLINES (result, FILE0);
if (numlines)
{
D_LINEARRAY (result, FILE0) = xcalloc (numlines, sizeof (char *));
D_LENARRAY (result, FILE0) = xcalloc (numlines, sizeof (size_t));
}
else
{
D_LINEARRAY (result, FILE0) = 0;
D_LENARRAY (result, FILE0) = 0;
}
numlines = D_NUMLINES (result, FILE1);
if (numlines)
{
D_LINEARRAY (result, FILE1) = xcalloc (numlines, sizeof (char *));
D_LENARRAY (result, FILE1) = xcalloc (numlines, sizeof (size_t));
}
else
{
D_LINEARRAY (result, FILE1) = 0;
D_LENARRAY (result, FILE1) = 0;
}
numlines = D_NUMLINES (result, FILE2);
if (numlines)
{
D_LINEARRAY (result, FILE2) = xcalloc (numlines, sizeof (char *));
D_LENARRAY (result, FILE2) = xcalloc (numlines, sizeof (size_t));
}
else
{
D_LINEARRAY (result, FILE2) = 0;
D_LENARRAY (result, FILE2) = 0;
}
return result;
}
static bool
compare_line_list (char * const list1[], size_t const lengths1[],
char * const list2[], size_t const lengths2[],
lin nl)
{
char
* const *l1 = list1,
* const *l2 = list2;
size_t const
*lgths1 = lengths1,
*lgths2 = lengths2;
while (nl--)
if (!*l1 || !*l2 || *lgths1 != *lgths2++
|| memcmp (*l1++, *l2++, *lgths1++))
return 0;
return 1;
}
static struct diff_block *
process_diff (char const *filea,
char const *fileb,
struct diff_block **last_block)
{
char *diff_contents;
char *diff_limit;
char *scan_diff;
enum diff_type dt;
lin i;
struct diff_block *block_list, **block_list_end, *bptr;
size_t too_many_lines = (PTRDIFF_MAX
/ MIN (sizeof *bptr->lines[1],
sizeof *bptr->lengths[1]));
diff_limit = read_diff (filea, fileb, &diff_contents);
scan_diff = diff_contents;
block_list_end = &block_list;
bptr = 0;
while (scan_diff < diff_limit)
{
bptr = xmalloc (sizeof *bptr);
bptr->lines[0] = bptr->lines[1] = 0;
bptr->lengths[0] = bptr->lengths[1] = 0;
dt = process_diff_control (&scan_diff, bptr);
if (dt == ERROR || *scan_diff != '\n')
{
fprintf (stderr, _("%s: diff failed: "), program_name);
do
{
putc (*scan_diff, stderr);
}
while (*scan_diff++ != '\n');
exit (EXIT_TROUBLE);
}
scan_diff++;
switch (dt)
{
case ADD:
bptr->ranges[0][0]++;
break;
case DELETE:
bptr->ranges[1][0]++;
break;
case CHANGE:
break;
default:
fatal ("internal error: invalid diff type in process_diff");
break;
}
if (dt != ADD)
{
lin numlines = D_NUMLINES (bptr, 0);
if (too_many_lines <= numlines)
xalloc_die ();
bptr->lines[0] = xmalloc (numlines * sizeof *bptr->lines[0]);
bptr->lengths[0] = xmalloc (numlines * sizeof *bptr->lengths[0]);
for (i = 0; i < numlines; i++)
scan_diff = scan_diff_line (scan_diff,
&(bptr->lines[0][i]),
&(bptr->lengths[0][i]),
diff_limit,
'<');
}
if (dt == CHANGE)
{
if (strncmp (scan_diff, "---\n", 4))
fatal ("invalid diff format; invalid change separator");
scan_diff += 4;
}
if (dt != DELETE)
{
lin numlines = D_NUMLINES (bptr, 1);
if (too_many_lines <= numlines)
xalloc_die ();
bptr->lines[1] = xmalloc (numlines * sizeof *bptr->lines[1]);
bptr->lengths[1] = xmalloc (numlines * sizeof *bptr->lengths[1]);
for (i = 0; i < numlines; i++)
scan_diff = scan_diff_line (scan_diff,
&(bptr->lines[1][i]),
&(bptr->lengths[1][i]),
diff_limit,
'>');
}
*block_list_end = bptr;
block_list_end = &bptr->next;
}
*block_list_end = 0;
*last_block = bptr;
return block_list;
}
static enum diff_type
process_diff_control (char **string, struct diff_block *db)
{
char *s = *string;
lin holdnum;
enum diff_type type;
#define SKIPWHITE(s) { while (*s == ' ' || *s == '\t') s++; }
#define READNUM(s, num) \
{ unsigned char c = *s; if (!ISDIGIT (c)) return ERROR; holdnum = 0; \
do { holdnum = (c - '0' + holdnum * 10); } \
while (ISDIGIT (c = *++s)); (num) = holdnum; }
SKIPWHITE (s);
READNUM (s, db->ranges[0][RANGE_START]);
SKIPWHITE (s);
if (*s == ',')
{
s++;
READNUM (s, db->ranges[0][RANGE_END]);
}
else
db->ranges[0][RANGE_END] = db->ranges[0][RANGE_START];
SKIPWHITE (s);
switch (*s)
{
case 'a':
type = ADD;
break;
case 'c':
type = CHANGE;
break;
case 'd':
type = DELETE;
break;
default:
return ERROR;
}
s++;
SKIPWHITE (s);
READNUM (s, db->ranges[1][RANGE_START]);
SKIPWHITE (s);
if (*s == ',')
{
s++;
READNUM (s, db->ranges[1][RANGE_END]);
SKIPWHITE (s);
}
else
db->ranges[1][RANGE_END] = db->ranges[1][RANGE_START];
*string = s;
return type;
}
static char *
read_diff (char const *filea,
char const *fileb,
char **output_placement)
{
char *diff_result;
size_t current_chunk_size, total;
int fd, wstatus;
int werrno = 0;
struct stat pipestat;
#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
char const *argv[8];
char const **ap;
int fds[2];
pid_t pid;
ap = argv;
*ap++ = diff_program;
if (text)
*ap++ = "-a";
*ap++ = "--horizon-lines=100";
*ap++ = "--";
*ap++ = filea;
*ap++ = fileb;
*ap = 0;
if (pipe (fds) != 0)
perror_with_exit ("pipe");
pid = vfork ();
if (pid == 0)
{
close (fds[0]);
if (fds[1] != STDOUT_FILENO)
{
dup2 (fds[1], STDOUT_FILENO);
close (fds[1]);
}
execvp (diff_program, (char **) argv);
_exit (errno == ENOEXEC ? 126 : 127);
}
if (pid == -1)
perror_with_exit ("fork");
close (fds[1]);
fd = fds[0];
#else
FILE *fpipe;
char const args[] = " -a --horizon-lines=100 -- ";
char *command = xmalloc (quote_system_arg (0, diff_program)
+ sizeof args - 1
+ quote_system_arg (0, filea) + 1
+ quote_system_arg (0, fileb) + 1);
char *p = command;
p += quote_system_arg (p, diff_program);
strcpy (p, args + (text ? 0 : 3));
p += strlen (p);
p += quote_system_arg (p, filea);
*p++ = ' ';
p += quote_system_arg (p, fileb);
*p = 0;
errno = 0;
fpipe = popen (command, "r");
if (!fpipe)
perror_with_exit (command);
free (command);
fd = fileno (fpipe);
#endif
if (fstat (fd, &pipestat) != 0)
perror_with_exit ("fstat");
current_chunk_size = MAX (1, STAT_BLOCKSIZE (pipestat));
diff_result = xmalloc (current_chunk_size);
total = 0;
for (;;)
{
size_t bytes_to_read = current_chunk_size - total;
size_t bytes = block_read (fd, diff_result + total, bytes_to_read);
total += bytes;
if (bytes != bytes_to_read)
{
if (bytes == SIZE_MAX)
perror_with_exit (_("read failed"));
break;
}
if (PTRDIFF_MAX / 2 <= current_chunk_size)
xalloc_die ();
current_chunk_size *= 2;
diff_result = xrealloc (diff_result, current_chunk_size);
}
if (total != 0 && diff_result[total-1] != '\n')
fatal ("invalid diff format; incomplete last line");
*output_placement = diff_result;
#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
wstatus = pclose (fpipe);
if (wstatus == -1)
werrno = errno;
#else
if (close (fd) != 0)
perror_with_exit ("close");
if (waitpid (pid, &wstatus, 0) < 0)
perror_with_exit ("waitpid");
#endif
if (! werrno && WIFEXITED (wstatus))
switch (WEXITSTATUS (wstatus))
{
case 126:
error (EXIT_TROUBLE, 0, _("subsidiary program `%s' not executable"),
diff_program);
case 127:
error (EXIT_TROUBLE, 0, _("subsidiary program `%s' not found"),
diff_program);
}
if (werrno || ! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2))
error (EXIT_TROUBLE, werrno, _("subsidiary program `%s' failed"),
diff_program);
return diff_result + total;
}
static char *
scan_diff_line (char *scan_ptr, char **set_start, size_t *set_length,
char *limit, char leadingchar)
{
char *line_ptr;
if (!(scan_ptr[0] == leadingchar
&& scan_ptr[1] == ' '))
fatal ("invalid diff format; incorrect leading line chars");
*set_start = line_ptr = scan_ptr + 2;
while (*line_ptr++ != '\n')
continue;
*set_length = line_ptr - *set_start;
if (line_ptr < limit && *line_ptr == '\\')
{
if (edscript)
fprintf (stderr, "%s:", program_name);
else
--*set_length;
line_ptr++;
do
{
if (edscript)
putc (*line_ptr, stderr);
}
while (*line_ptr++ != '\n');
}
return line_ptr;
}
static void
output_diff3 (FILE *outputfile, struct diff3_block *diff,
int const mapping[3], int const rev_mapping[3])
{
int i;
int oddoneout;
char *cp;
struct diff3_block *ptr;
lin line;
size_t length;
int dontprint;
static int skew_increment[3] = { 2, 3, 1 };
char const *line_prefix = initial_tab ? "\t" : " ";
for (ptr = diff; ptr; ptr = D_NEXT (ptr))
{
char x[2];
switch (ptr->correspond)
{
case DIFF_ALL:
x[0] = 0;
dontprint = 3;
oddoneout = 3;
break;
case DIFF_1ST:
case DIFF_2ND:
case DIFF_3RD:
oddoneout = rev_mapping[ptr->correspond - DIFF_1ST];
x[0] = oddoneout + '1';
x[1] = 0;
dontprint = oddoneout == 0;
break;
default:
fatal ("internal error: invalid diff type passed to output");
}
fprintf (outputfile, "====%s\n", x);
for (i = 0; i < 3;
i = (oddoneout == 1 ? skew_increment[i] : i + 1))
{
int realfile = mapping[i];
lin lowt = D_LOWLINE (ptr, realfile);
lin hight = D_HIGHLINE (ptr, realfile);
long llowt = lowt;
long lhight = hight;
fprintf (outputfile, "%d:", i + 1);
switch (lowt - hight)
{
case 1:
fprintf (outputfile, "%lda\n", llowt - 1);
break;
case 0:
fprintf (outputfile, "%ldc\n", llowt);
break;
default:
fprintf (outputfile, "%ld,%ldc\n", llowt, lhight);
break;
}
if (i == dontprint) continue;
if (lowt <= hight)
{
line = 0;
do
{
fprintf (outputfile, line_prefix);
cp = D_RELNUM (ptr, realfile, line);
length = D_RELLEN (ptr, realfile, line);
fwrite (cp, sizeof (char), length, outputfile);
}
while (++line < hight - lowt + 1);
if (cp[length - 1] != '\n')
fprintf (outputfile, "\n\\ %s\n",
_("No newline at end of file"));
}
}
}
}
static bool
dotlines (FILE *outputfile, struct diff3_block *b, int filenum)
{
lin i;
bool leading_dot = 0;
for (i = 0;
i < D_NUMLINES (b, filenum);
i++)
{
char *line = D_RELNUM (b, filenum, i);
if (line[0] == '.')
{
leading_dot = 1;
fprintf (outputfile, ".");
}
fwrite (line, sizeof (char),
D_RELLEN (b, filenum, i), outputfile);
}
return leading_dot;
}
static void
undotlines (FILE *outputfile, bool leading_dot, long start, lin num)
{
fprintf (outputfile, ".\n");
if (leading_dot)
{
if (num == 1)
fprintf (outputfile, "%lds/^\\.//\n", start);
else
fprintf (outputfile, "%ld,%lds/^\\.//\n", start, start + num - 1);
}
}
static bool
output_diff3_edscript (FILE *outputfile, struct diff3_block *diff,
int const mapping[3], int const rev_mapping[3],
char const *file0, char const *file1, char const *file2)
{
bool leading_dot;
bool conflicts_found = 0, conflict;
struct diff3_block *b;
for (b = reverse_diff3_blocklist (diff); b; b = b->next)
{
enum diff_type type
= (b->correspond == DIFF_ALL
? DIFF_ALL
: DIFF_1ST + rev_mapping[b->correspond - DIFF_1ST]);
long low0, high0;
switch (type)
{
default: continue;
case DIFF_2ND: if (!show_2nd) continue; conflict = 1; break;
case DIFF_3RD: if (overlap_only) continue; conflict = 0; break;
case DIFF_ALL: if (simple_only) continue; conflict = flagging; break;
}
low0 = D_LOWLINE (b, mapping[FILE0]);
high0 = D_HIGHLINE (b, mapping[FILE0]);
if (conflict)
{
conflicts_found = 1;
fprintf (outputfile, "%lda\n", high0);
leading_dot = 0;
if (type == DIFF_ALL)
{
if (show_2nd)
{
fprintf (outputfile, "||||||| %s\n", file1);
leading_dot = dotlines (outputfile, b, mapping[FILE1]);
}
fprintf (outputfile, "=======\n");
leading_dot |= dotlines (outputfile, b, mapping[FILE2]);
}
fprintf (outputfile, ">>>>>>> %s\n", file2);
undotlines (outputfile, leading_dot, high0 + 2,
(D_NUMLINES (b, mapping[FILE1])
+ D_NUMLINES (b, mapping[FILE2]) + 1));
fprintf (outputfile, "%lda\n<<<<<<< %s\n", low0 - 1,
type == DIFF_ALL ? file0 : file1);
leading_dot = 0;
if (type == DIFF_2ND)
{
leading_dot = dotlines (outputfile, b, mapping[FILE1]);
fprintf (outputfile, "=======\n");
}
undotlines (outputfile, leading_dot, low0 + 1,
D_NUMLINES (b, mapping[FILE1]));
}
else if (D_NUMLINES (b, mapping[FILE2]) == 0)
{
if (low0 == high0)
fprintf (outputfile, "%ldd\n", low0);
else
fprintf (outputfile, "%ld,%ldd\n", low0, high0);
}
else
{
switch (high0 - low0)
{
case -1:
fprintf (outputfile, "%lda\n", high0);
break;
case 0:
fprintf (outputfile, "%ldc\n", high0);
break;
default:
fprintf (outputfile, "%ld,%ldc\n", low0, high0);
break;
}
undotlines (outputfile, dotlines (outputfile, b, mapping[FILE2]),
low0, D_NUMLINES (b, mapping[FILE2]));
}
}
if (finalwrite) fprintf (outputfile, "w\nq\n");
return conflicts_found;
}
static bool
output_diff3_merge (FILE *infile, FILE *outputfile, struct diff3_block *diff,
int const mapping[3], int const rev_mapping[3],
char const *file0, char const *file1, char const *file2)
{
int c;
lin i;
bool conflicts_found = 0, conflict;
struct diff3_block *b;
lin linesread = 0;
for (b = diff; b; b = b->next)
{
enum diff_type type
= ((b->correspond == DIFF_ALL)
? DIFF_ALL
: DIFF_1ST + rev_mapping[b->correspond - DIFF_1ST]);
char const *format_2nd = "<<<<<<< %s\n";
switch (type)
{
default: continue;
case DIFF_2ND: if (!show_2nd) continue; conflict = 1; break;
case DIFF_3RD: if (overlap_only) continue; conflict = 0; break;
case DIFF_ALL: if (simple_only) continue; conflict = flagging;
format_2nd = "||||||| %s\n";
break;
}
i = D_LOWLINE (b, FILE0) - linesread - 1;
linesread += i;
while (0 <= --i)
do
{
c = getc (infile);
if (c == EOF)
{
if (ferror (infile))
perror_with_exit (_("read failed"));
else if (feof (infile))
fatal ("input file shrank");
}
putc (c, outputfile);
}
while (c != '\n');
if (conflict)
{
conflicts_found = 1;
if (type == DIFF_ALL)
{
fprintf (outputfile, "<<<<<<< %s\n", file0);
for (i = 0;
i < D_NUMLINES (b, mapping[FILE0]);
i++)
fwrite (D_RELNUM (b, mapping[FILE0], i), sizeof (char),
D_RELLEN (b, mapping[FILE0], i), outputfile);
}
if (show_2nd)
{
fprintf (outputfile, format_2nd, file1);
for (i = 0;
i < D_NUMLINES (b, mapping[FILE1]);
i++)
fwrite (D_RELNUM (b, mapping[FILE1], i), sizeof (char),
D_RELLEN (b, mapping[FILE1], i), outputfile);
}
fprintf (outputfile, "=======\n");
}
for (i = 0;
i < D_NUMLINES (b, mapping[FILE2]);
i++)
fwrite (D_RELNUM (b, mapping[FILE2], i), sizeof (char),
D_RELLEN (b, mapping[FILE2], i), outputfile);
if (conflict)
fprintf (outputfile, ">>>>>>> %s\n", file2);
i = D_NUMLINES (b, FILE0);
linesread += i;
while (0 <= --i)
while ((c = getc (infile)) != '\n')
if (c == EOF)
{
if (ferror (infile))
perror_with_exit (_("read failed"));
else if (feof (infile))
{
if (i || b->next)
fatal ("input file shrank");
return conflicts_found;
}
}
}
while ((c = getc (infile)) != EOF || !(ferror (infile) | feof (infile)))
putc (c, outputfile);
return conflicts_found;
}
static struct diff3_block *
reverse_diff3_blocklist (struct diff3_block *diff)
{
register struct diff3_block *tmp, *next, *prev;
for (tmp = diff, prev = 0; tmp; tmp = next)
{
next = tmp->next;
tmp->next = prev;
prev = tmp;
}
return prev;
}
static void
fatal (char const *msgid)
{
error (EXIT_TROUBLE, 0, "%s", _(msgid));
abort ();
}
static void
perror_with_exit (char const *string)
{
error (EXIT_TROUBLE, errno, "%s", string);
abort ();
}