1745 lines
48 KiB
C
1745 lines
48 KiB
C
/* diff3 - compare three files line by line
|
||
|
||
Copyright (C) 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1998, 2001,
|
||
2002, 2004 Free Software Foundation, Inc.
|
||
|
||
This program is free software; you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 2, or (at your option)
|
||
any later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
See the GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program; see the file COPYING.
|
||
If not, write to the Free Software Foundation,
|
||
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||
|
||
#include "system.h"
|
||
#include "paths.h"
|
||
|
||
#include <stdio.h>
|
||
#include <unlocked-io.h>
|
||
|
||
#include <c-stack.h>
|
||
#include <cmpbuf.h>
|
||
#include <error.h>
|
||
#include <exitfail.h>
|
||
#include <file-type.h>
|
||
#include <getopt.h>
|
||
#include <inttostr.h>
|
||
#include <quotesys.h>
|
||
#include <version-etc.h>
|
||
#include <xalloc.h>
|
||
|
||
/* Internal data structures and macros for the diff3 program; includes
|
||
data structures for both diff3 diffs and normal diffs. */
|
||
|
||
/* Different files within a three way diff. */
|
||
#define FILE0 0
|
||
#define FILE1 1
|
||
#define FILE2 2
|
||
|
||
/* A three way diff is built from two two-way diffs; the file which
|
||
the two two-way diffs share is: */
|
||
#define FILEC FILE2
|
||
|
||
/* Different files within a two way diff.
|
||
FC is the common file, FO the other file. */
|
||
#define FO 0
|
||
#define FC 1
|
||
|
||
/* The ranges are indexed by */
|
||
#define RANGE_START 0
|
||
#define RANGE_END 1
|
||
|
||
enum diff_type {
|
||
ERROR, /* Should not be used */
|
||
ADD, /* Two way diff add */
|
||
CHANGE, /* Two way diff change */
|
||
DELETE, /* Two way diff delete */
|
||
DIFF_ALL, /* All three are different */
|
||
DIFF_1ST, /* Only the first is different */
|
||
DIFF_2ND, /* Only the second */
|
||
DIFF_3RD /* Only the third */
|
||
};
|
||
|
||
/* Two way diff */
|
||
struct diff_block {
|
||
lin ranges[2][2]; /* Ranges are inclusive */
|
||
char **lines[2]; /* The actual lines (may contain nulls) */
|
||
size_t *lengths[2]; /* Line lengths (including newlines, if any) */
|
||
struct diff_block *next;
|
||
};
|
||
|
||
/* Three way diff */
|
||
|
||
struct diff3_block {
|
||
enum diff_type correspond; /* Type of diff */
|
||
lin ranges[3][2]; /* Ranges are inclusive */
|
||
char **lines[3]; /* The actual lines (may contain nulls) */
|
||
size_t *lengths[3]; /* Line lengths (including newlines, if any) */
|
||
struct diff3_block *next;
|
||
};
|
||
|
||
/* Access the ranges on a diff block. */
|
||
#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)
|
||
|
||
/* Access the line numbers in a file in a diff by relative line
|
||
numbers (i.e. line number within the diff itself). Note that these
|
||
are lvalues and can be used for assignment. */
|
||
#define D_RELNUM(diff, filenum, linenum) \
|
||
((diff)->lines[filenum][linenum])
|
||
#define D_RELLEN(diff, filenum, linenum) \
|
||
((diff)->lengths[filenum][linenum])
|
||
|
||
/* And get at them directly, when that should be necessary. */
|
||
#define D_LINEARRAY(diff, filenum) \
|
||
((diff)->lines[filenum])
|
||
#define D_LENARRAY(diff, filenum) \
|
||
((diff)->lengths[filenum])
|
||
|
||
/* Next block. */
|
||
#define D_NEXT(diff) ((diff)->next)
|
||
|
||
/* Access the type of a diff3 block. */
|
||
#define D3_TYPE(diff) ((diff)->correspond)
|
||
|
||
/* Line mappings based on diffs. The first maps off the top of the
|
||
diff, the second off of the bottom. */
|
||
#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)))
|
||
|
||
/* Options variables for flags set on command line. */
|
||
|
||
/* If nonzero, treat all files as text files, never as binary. */
|
||
static bool text;
|
||
|
||
/* Remove trailing carriage returns from input. */
|
||
static bool strip_trailing_cr;
|
||
|
||
/* If nonzero, write out an ed script instead of the standard diff3 format. */
|
||
static bool edscript;
|
||
|
||
/* If nonzero, in the case of overlapping diffs (type DIFF_ALL),
|
||
preserve the lines which would normally be deleted from
|
||
file 1 with a special flagging mechanism. */
|
||
static bool flagging;
|
||
|
||
/* Use a tab to align output lines (-T). */
|
||
static bool initial_tab;
|
||
|
||
/* If nonzero, do not output information for overlapping diffs. */
|
||
static bool simple_only;
|
||
|
||
/* If nonzero, do not output information for non-overlapping diffs. */
|
||
static bool overlap_only;
|
||
|
||
/* If nonzero, show information for DIFF_2ND diffs. */
|
||
static bool show_2nd;
|
||
|
||
/* If nonzero, include `:wq' at the end of the script
|
||
to write out the file being edited. */
|
||
static bool finalwrite;
|
||
|
||
/* If nonzero, output a merged file. */
|
||
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;
|
||
|
||
/* Values for long options that do not have single-letter equivalents. */
|
||
enum
|
||
{
|
||
DIFF_PROGRAM_OPTION = CHAR_MAX + 1,
|
||
HELP_OPTION,
|
||
STRIP_TRAILING_CR_OPTION
|
||
};
|
||
|
||
static struct option const longopts[] =
|
||
{
|
||
{"diff-program", 1, 0, DIFF_PROGRAM_OPTION},
|
||
{"easy-only", 0, 0, '3'},
|
||
{"ed", 0, 0, 'e'},
|
||
{"help", 0, 0, HELP_OPTION},
|
||
{"initial-tab", 0, 0, 'T'},
|
||
{"label", 1, 0, 'L'},
|
||
{"merge", 0, 0, 'm'},
|
||
{"overlap-only", 0, 0, 'x'},
|
||
{"show-all", 0, 0, 'A'},
|
||
{"show-overlap", 0, 0, 'E'},
|
||
{"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
|
||
{"text", 0, 0, 'a'},
|
||
{"version", 0, 0, 'v'},
|
||
{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 (0);
|
||
|
||
while ((c = getopt_long (argc, argv, "aeimvx3AEL:TX", longopts, 0)) != -1)
|
||
{
|
||
switch (c)
|
||
{
|
||
case 'a':
|
||
text = true;
|
||
break;
|
||
case 'A':
|
||
show_2nd = true;
|
||
flagging = true;
|
||
incompat++;
|
||
break;
|
||
case 'x':
|
||
overlap_only = true;
|
||
incompat++;
|
||
break;
|
||
case '3':
|
||
simple_only = true;
|
||
incompat++;
|
||
break;
|
||
case 'i':
|
||
finalwrite = true;
|
||
break;
|
||
case 'm':
|
||
merge = true;
|
||
break;
|
||
case 'X':
|
||
overlap_only = true;
|
||
/* Fall through. */
|
||
case 'E':
|
||
flagging = true;
|
||
/* Fall through. */
|
||
case 'e':
|
||
incompat++;
|
||
break;
|
||
case 'T':
|
||
initial_tab = true;
|
||
break;
|
||
case STRIP_TRAILING_CR_OPTION:
|
||
strip_trailing_cr = true;
|
||
break;
|
||
case 'v':
|
||
version_etc (stdout, "diff3", PACKAGE_NAME, PACKAGE_VERSION,
|
||
"Randy Smith", (char *) 0);
|
||
check_stdout ();
|
||
return EXIT_SUCCESS;
|
||
case DIFF_PROGRAM_OPTION:
|
||
diff_program = optarg;
|
||
break;
|
||
case HELP_OPTION:
|
||
usage ();
|
||
check_stdout ();
|
||
return EXIT_SUCCESS;
|
||
case 'L':
|
||
/* Handle up to three -L options. */
|
||
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; /* -AeExX3 without -m implies ed script. */
|
||
show_2nd |= ~incompat & merge; /* -m without -AeExX3 implies -A. */
|
||
flagging |= ~incompat & merge;
|
||
|
||
if (incompat > 1 /* Ensure at most one of -AeExX3. */
|
||
|| finalwrite & merge /* -i -m would rewrite input file. */
|
||
|| (tag_count && ! flagging)) /* -L requires one of -AEX. */
|
||
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];
|
||
|
||
/* Always compare file1 to file2, even if file2 is "-".
|
||
This is needed for -mAeExX3. Using the file0 as
|
||
the common file would produce wrong results, because if the
|
||
file0-file1 diffs didn't line up with the file0-file2 diffs
|
||
(which is entirely possible since we don't use diff's -n option),
|
||
diff3 might report phantom changes from file1 to file2.
|
||
|
||
Also, try to compare file0 to file1, because this is where
|
||
changes are expected to come from. Diffing between these pairs
|
||
of files is more likely to avoid phantom changes from file0 to file1.
|
||
|
||
Historically, the default common file was file2, so some older
|
||
applications (e.g. Emacs ediff) used file2 as the ancestor. So,
|
||
for compatibility, if this is a 3-way diff (not a merge or
|
||
edscript), prefer file2 as the common file. */
|
||
|
||
common = 2 - (edscript | merge);
|
||
|
||
if (strcmp (file[common], "-") == 0)
|
||
{
|
||
/* Sigh. We've got standard input as the common file. We can't
|
||
call diff twice on stdin. Use the other arg as the common
|
||
file instead. */
|
||
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
|
||
/* System V fork+wait does not work if SIGCHLD is ignored. */
|
||
signal (SIGCHLD, SIG_DFL);
|
||
#endif
|
||
|
||
/* Invoke diff twice on two pairs of input files, combine the two
|
||
diffs, and output them. */
|
||
|
||
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 = false;
|
||
}
|
||
|
||
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_("--strip-trailing-cr Strip trailing carriage return on input."),
|
||
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%s\n\n%s\n",
|
||
_("If a FILE is `-', read standard input."),
|
||
_("Exit status is 0 if successful, 1 if conflicts, 2 if trouble."),
|
||
_("Report bugs to <bug-gnu-utils@gnu.org>."));
|
||
}
|
||
|
||
/* Combine the two diffs together into one.
|
||
Here is the algorithm:
|
||
|
||
File2 is shared in common between the two diffs.
|
||
Diff02 is the diff between 0 and 2.
|
||
Diff12 is the diff between 1 and 2.
|
||
|
||
1) Find the range for the first block in File2.
|
||
a) Take the lowest of the two ranges (in File2) in the two
|
||
current blocks (one from each diff) as being the low
|
||
water mark. Assign the upper end of this block as
|
||
being the high water mark and move the current block up
|
||
one. Mark the block just moved over as to be used.
|
||
b) Check the next block in the diff that the high water
|
||
mark is *not* from.
|
||
|
||
*If* the high water mark is above
|
||
the low end of the range in that block,
|
||
|
||
mark that block as to be used and move the current
|
||
block up. Set the high water mark to the max of
|
||
the high end of this block and the current. Repeat b.
|
||
|
||
2) Find the corresponding ranges in File0 (from the blocks
|
||
in diff02; line per line outside of diffs) and in File1.
|
||
Create a diff3_block, reserving space as indicated by the ranges.
|
||
|
||
3) Copy all of the pointers for file2 in. At least for now,
|
||
do memcmp's between corresponding strings in the two diffs.
|
||
|
||
4) Copy all of the pointers for file0 and 1 in. Get what is
|
||
needed from file2 (when there isn't a diff block, it's
|
||
identical to file2 within the range between diff blocks).
|
||
|
||
5) If the diff blocks used came from only one of the two
|
||
strings of diffs, then that file (i.e. the one other than
|
||
the common file in that diff) is the odd person out. If
|
||
diff blocks are used from both sets, check to see if files
|
||
0 and 1 match:
|
||
|
||
Same number of lines? If so, do a set of memcmp's (if
|
||
a memcmp matches; copy the pointer over; it'll be easier
|
||
later during comparisons). If they match, 0 & 1 are the
|
||
same. If not, all three different.
|
||
|
||
Then do it again, until the blocks are exhausted. */
|
||
|
||
|
||
/* Make a three way diff (chain of diff3_block's) from two two way
|
||
diffs (chains of diff_block's). Assume that each of the two diffs
|
||
passed are onto the same file (i.e. that each of the diffs were
|
||
made "to" the same file). Return a three way diff pointer with
|
||
numbering FILE0 = the other file in diff02, FILE1 = the other file
|
||
in diff12, and FILEC = the common file. */
|
||
|
||
static struct diff3_block *
|
||
make_3way_diff (struct diff_block *thread0, struct diff_block *thread1)
|
||
{
|
||
/* Work on the two diffs passed to it as threads. Thread number 0
|
||
is diff02, thread number 1 is diff12. USING is the base of the
|
||
list of blocks to be used to construct each block of the three
|
||
way diff; if no blocks from a particular thread are to be used,
|
||
that element of USING is 0. LAST_USING contains the last
|
||
elements on each of the using lists.
|
||
|
||
HIGH_WATER_MARK is the highest line number in the common file
|
||
described in any of the diffs in either of the USING lists.
|
||
HIGH_WATER_THREAD names the thread. Similarly BASE_WATER_MARK
|
||
and BASE_WATER_THREAD describe the lowest line number in the
|
||
common file described in any of the diffs in either of the USING
|
||
lists. HIGH_WATER_DIFF is the diff from which the
|
||
HIGH_WATER_MARK was taken.
|
||
|
||
HIGH_WATER_DIFF should always be equal to
|
||
LAST_USING[HIGH_WATER_THREAD]. OTHER_DIFF is the next diff to
|
||
check for higher water, and should always be equal to
|
||
CURRENT[HIGH_WATER_THREAD ^ 1]. OTHER_THREAD is the thread in
|
||
which the OTHER_DIFF is, and hence should always be equal to
|
||
HIGH_WATER_THREAD ^ 1.
|
||
|
||
LAST_DIFF is the last diff block produced by this routine, for
|
||
line correspondence purposes between that diff and the one
|
||
currently being worked on. It is ZERO_DIFF before any blocks
|
||
have been created. */
|
||
|
||
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;
|
||
|
||
/* Initialization */
|
||
result = 0;
|
||
result_end = &result;
|
||
current[0] = thread0; current[1] = thread1;
|
||
last_diff3 = &zero_diff3;
|
||
|
||
/* Sniff up the threads until we reach the end */
|
||
|
||
while (current[0] || current[1])
|
||
{
|
||
using[0] = using[1] = last_using[0] = last_using[1] = 0;
|
||
|
||
/* Setup low and high water threads, diffs, and marks. */
|
||
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);
|
||
|
||
/* Make the diff you just got info from into the using class */
|
||
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;
|
||
|
||
/* And mark the other diff */
|
||
other_thread = high_water_thread ^ 0x1;
|
||
other_diff = current[other_thread];
|
||
|
||
/* Shuffle up the ladder, checking the other diff to see if it
|
||
needs to be incorporated. */
|
||
while (other_diff
|
||
&& D_LOWLINE (other_diff, FC) <= high_water_mark + 1)
|
||
{
|
||
|
||
/* Incorporate this diff into the using list. Note that
|
||
this doesn't take it off the current list */
|
||
if (using[other_thread])
|
||
last_using[other_thread]->next = other_diff;
|
||
else
|
||
using[other_thread] = other_diff;
|
||
last_using[other_thread] = other_diff;
|
||
|
||
/* Take it off the current list. Note that this following
|
||
code assumes that other_diff enters it equal to
|
||
current[high_water_thread ^ 0x1] */
|
||
current[other_thread] = current[other_thread]->next;
|
||
other_diff->next = 0;
|
||
|
||
/* Set the high_water stuff
|
||
If this comparison is equal, then this is the last pass
|
||
through this loop; since diff blocks within a given
|
||
thread cannot overlap, the high_water_mark will be
|
||
*below* the range_start of either of the next diffs. */
|
||
|
||
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);
|
||
}
|
||
|
||
/* Set the other diff */
|
||
other_thread = high_water_thread ^ 0x1;
|
||
other_diff = current[other_thread];
|
||
}
|
||
|
||
/* The using lists contain a list of all of the blocks to be
|
||
included in this diff3_block. Create it. */
|
||
|
||
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");
|
||
|
||
/* Put it on the list. */
|
||
*result_end = tmpblock;
|
||
result_end = &tmpblock->next;
|
||
|
||
/* Set up corresponding lines correctly. */
|
||
last_diff3 = tmpblock;
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/* Take two lists of blocks (from two separate diff threads) and put
|
||
them together into one diff3 block. Return a pointer to this diff3
|
||
block or 0 for failure.
|
||
|
||
All arguments besides using are for the convenience of the routine;
|
||
they could be derived from the using array. LAST_USING is a pair
|
||
of pointers to the last blocks in the using structure. LOW_THREAD
|
||
and HIGH_THREAD tell which threads contain the lowest and highest
|
||
line numbers for File0. LAST_DIFF3 contains the last diff produced
|
||
in the calling routine. This is used for lines mappings that
|
||
would still be identical to the state that diff ended in.
|
||
|
||
A distinction should be made in this routine between the two diffs
|
||
that are part of a normal two diff block, and the three diffs that
|
||
are part of a diff3_block. */
|
||
|
||
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;
|
||
|
||
/* Find the range in the common file. */
|
||
lin lowc = D_LOWLINE (using[low_thread], FC);
|
||
lin highc = D_HIGHLINE (last_using[high_thread], FC);
|
||
|
||
/* Find the ranges in the other files.
|
||
If using[d] is null, that means that the file to which that diff
|
||
refers is equivalent to the common file over this range. */
|
||
|
||
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);
|
||
}
|
||
|
||
/* Create a block with the appropriate sizes */
|
||
result = create_diff3_block (low[0], high[0], low[1], high[1], lowc, highc);
|
||
|
||
/* Copy information for the common file.
|
||
Return with a zero if any of the compares failed. */
|
||
|
||
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;
|
||
}
|
||
|
||
/* Copy information for file d. First deal with anything that might be
|
||
before the first diff. */
|
||
|
||
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;
|
||
|
||
/* Catch the lines between here and the next diff */
|
||
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++;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Set correspond */
|
||
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;
|
||
}
|
||
|
||
/* Copy pointers from a list of strings to a different list of
|
||
strings. If a spot in the second list is already filled, make sure
|
||
that it is filled with the same string; if not, return false, the copy
|
||
incomplete. Upon successful completion of the copy, return true. */
|
||
|
||
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) != 0)
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
*t = *f;
|
||
*tl = *fl;
|
||
}
|
||
|
||
t++; f++; tl++; fl++;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Create a diff3_block, with ranges as specified in the arguments.
|
||
Allocate the arrays for the various pointers (and zero them) based
|
||
on the arguments passed. Return the block as a result. */
|
||
|
||
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;
|
||
|
||
/* Assign ranges */
|
||
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;
|
||
|
||
/* Allocate and zero space */
|
||
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 */
|
||
return result;
|
||
}
|
||
|
||
/* Compare two lists of lines of text.
|
||
Return 1 if they are equivalent, 0 if not. */
|
||
|
||
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;
|
||
char * const *l2 = list2;
|
||
size_t const *lgths1 = lengths1;
|
||
size_t const *lgths2 = lengths2;
|
||
|
||
while (nl--)
|
||
if (!*l1 || !*l2 || *lgths1 != *lgths2++
|
||
|| memcmp (*l1++, *l2++, *lgths1++) != 0)
|
||
return false;
|
||
return true;
|
||
}
|
||
|
||
/* Input and parse two way diffs. */
|
||
|
||
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; /* Pacify `gcc -W'. */
|
||
|
||
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++;
|
||
|
||
/* Force appropriate ranges to be null, if necessary */
|
||
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;
|
||
}
|
||
|
||
/* Allocate space for the pointers for the lines from filea, and
|
||
parcel them out among these pointers */
|
||
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,
|
||
'<');
|
||
}
|
||
|
||
/* Get past the separator for changes */
|
||
if (dt == CHANGE)
|
||
{
|
||
if (strncmp (scan_diff, "---\n", 4))
|
||
fatal ("invalid diff format; invalid change separator");
|
||
scan_diff += 4;
|
||
}
|
||
|
||
/* Allocate space for the pointers for the lines from fileb, and
|
||
parcel them out among these pointers */
|
||
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,
|
||
'>');
|
||
}
|
||
|
||
/* Place this block on the blocklist. */
|
||
*block_list_end = bptr;
|
||
block_list_end = &bptr->next;
|
||
}
|
||
|
||
*block_list_end = 0;
|
||
*last_block = bptr;
|
||
return block_list;
|
||
}
|
||
|
||
/* Skip tabs and spaces, and return the first character after them. */
|
||
|
||
static char *
|
||
skipwhite (char *s)
|
||
{
|
||
while (*s == ' ' || *s == '\t')
|
||
s++;
|
||
return s;
|
||
}
|
||
|
||
/* Read a nonnegative line number from S, returning the address of the
|
||
first character after the line number, and storing the number into
|
||
*PNUM. Return 0 if S does not point to a valid line number. */
|
||
|
||
static char *
|
||
readnum (char *s, lin *pnum)
|
||
{
|
||
unsigned char c = *s;
|
||
lin num = 0;
|
||
|
||
if (! ISDIGIT (c))
|
||
return 0;
|
||
|
||
do
|
||
{
|
||
num = c - '0' + num * 10;
|
||
c = *++s;
|
||
}
|
||
while (ISDIGIT (c));
|
||
|
||
*pnum = num;
|
||
return s;
|
||
}
|
||
|
||
/* Parse a normal format diff control string. Return the type of the
|
||
diff (ERROR if the format is bad). All of the other important
|
||
information is filled into to the structure pointed to by db, and
|
||
the string pointer (whose location is passed to this routine) is
|
||
updated to point beyond the end of the string parsed. Note that
|
||
only the ranges in the diff_block will be set by this routine.
|
||
|
||
If some specific pair of numbers has been reduced to a single
|
||
number, then both corresponding numbers in the diff block are set
|
||
to that number. In general these numbers are interpreted as ranges
|
||
inclusive, unless being used by the ADD or DELETE commands. It is
|
||
assumed that these will be special cased in a superior routine. */
|
||
|
||
static enum diff_type
|
||
process_diff_control (char **string, struct diff_block *db)
|
||
{
|
||
char *s = *string;
|
||
enum diff_type type;
|
||
|
||
/* Read first set of digits */
|
||
s = readnum (skipwhite (s), &db->ranges[0][RANGE_START]);
|
||
if (! s)
|
||
return ERROR;
|
||
|
||
/* Was that the only digit? */
|
||
s = skipwhite (s);
|
||
if (*s == ',')
|
||
{
|
||
s = readnum (s + 1, &db->ranges[0][RANGE_END]);
|
||
if (! s)
|
||
return ERROR;
|
||
}
|
||
else
|
||
db->ranges[0][RANGE_END] = db->ranges[0][RANGE_START];
|
||
|
||
/* Get the letter */
|
||
s = skipwhite (s);
|
||
switch (*s)
|
||
{
|
||
case 'a':
|
||
type = ADD;
|
||
break;
|
||
case 'c':
|
||
type = CHANGE;
|
||
break;
|
||
case 'd':
|
||
type = DELETE;
|
||
break;
|
||
default:
|
||
return ERROR; /* Bad format */
|
||
}
|
||
s++; /* Past letter */
|
||
|
||
/* Read second set of digits */
|
||
s = readnum (skipwhite (s), &db->ranges[1][RANGE_START]);
|
||
if (! s)
|
||
return ERROR;
|
||
|
||
/* Was that the only digit? */
|
||
s = skipwhite (s);
|
||
if (*s == ',')
|
||
{
|
||
s = readnum (s + 1, &db->ranges[1][RANGE_END]);
|
||
if (! s)
|
||
return ERROR;
|
||
s = skipwhite (s); /* To move to end */
|
||
}
|
||
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, status;
|
||
int werrno = 0;
|
||
struct stat pipestat;
|
||
|
||
#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
|
||
|
||
char const *argv[9];
|
||
char const **ap;
|
||
int fds[2];
|
||
pid_t pid;
|
||
|
||
ap = argv;
|
||
*ap++ = diff_program;
|
||
if (text)
|
||
*ap++ = "-a";
|
||
if (strip_trailing_cr)
|
||
*ap++ = "--strip-trailing-cr";
|
||
*ap++ = "--horizon-lines=100";
|
||
*ap++ = "--";
|
||
*ap++ = filea;
|
||
*ap++ = fileb;
|
||
*ap = 0;
|
||
|
||
if (pipe (fds) != 0)
|
||
perror_with_exit ("pipe");
|
||
|
||
pid = vfork ();
|
||
if (pid == 0)
|
||
{
|
||
/* Child */
|
||
close (fds[0]);
|
||
if (fds[1] != STDOUT_FILENO)
|
||
{
|
||
dup2 (fds[1], STDOUT_FILENO);
|
||
close (fds[1]);
|
||
}
|
||
|
||
/* The cast to (char **) is needed for portability to older
|
||
hosts with a nonstandard prototype for execvp. */
|
||
execvp (diff_program, (char **) argv);
|
||
|
||
_exit (errno == ENOENT ? 127 : 126);
|
||
}
|
||
|
||
if (pid == -1)
|
||
perror_with_exit ("fork");
|
||
|
||
close (fds[1]); /* Prevent erroneous lack of EOF */
|
||
fd = fds[0];
|
||
|
||
#else
|
||
|
||
FILE *fpipe;
|
||
char const args[] = " --horizon-lines=100 -- ";
|
||
char *command = xmalloc (quote_system_arg (0, diff_program)
|
||
+ sizeof "-a"
|
||
+ sizeof "--strip-trailing-cr"
|
||
+ 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);
|
||
if (text)
|
||
{
|
||
strcpy (p, " -a");
|
||
p += 3;
|
||
}
|
||
if (strip_trailing_cr)
|
||
{
|
||
strcpy (p, " --strip-trailing-cr");
|
||
p += 20;
|
||
}
|
||
strcpy (p, args);
|
||
p += sizeof args - 1;
|
||
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
|
||
|
||
status = ! werrno && WIFEXITED (wstatus) ? WEXITSTATUS (wstatus) : INT_MAX;
|
||
|
||
if (EXIT_TROUBLE <= status)
|
||
error (EXIT_TROUBLE, werrno,
|
||
_(status == 126
|
||
? "subsidiary program `%s' could not be invoked"
|
||
: status == 127
|
||
? "subsidiary program `%s' not found"
|
||
: status == INT_MAX
|
||
? "subsidiary program `%s' failed"
|
||
: "subsidiary program `%s' failed (exit status %d)"),
|
||
diff_program, status);
|
||
|
||
return diff_result + total;
|
||
}
|
||
|
||
|
||
/* Scan a regular diff line (consisting of > or <, followed by a
|
||
space, followed by text (including nulls) up to a newline.
|
||
|
||
This next routine began life as a macro and many parameters in it
|
||
are used as call-by-reference values. */
|
||
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;
|
||
|
||
/* Include newline if the original line ended in a newline,
|
||
or if an edit script is being generated.
|
||
Copy any missing newline message to stderr if an edit script is being
|
||
generated, because edit scripts cannot handle missing newlines.
|
||
Return the beginning of the next line. */
|
||
*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;
|
||
}
|
||
|
||
/* Output a three way diff passed as a list of diff3_block's. The
|
||
argument MAPPING is indexed by external file number (in the
|
||
argument list) and contains the internal file number (from the diff
|
||
passed). This is important because the user expects outputs in
|
||
terms of the argument list number, and the diff passed may have
|
||
been done slightly differently (if the last argument was "-", for
|
||
example). REV_MAPPING is the inverse of MAPPING. */
|
||
|
||
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 }; /* 0==>2==>1==>3 */
|
||
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; /* Print them all */
|
||
oddoneout = 3; /* Nobody's odder than anyone else */
|
||
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);
|
||
|
||
/* Go 0, 2, 1 if the first and third outputs are equivalent. */
|
||
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 int llowt = lowt;
|
||
long int 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"));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* Output to OUTPUTFILE the lines of B taken from FILENUM. Double any
|
||
initial '.'s; yield nonzero if any initial '.'s were doubled. */
|
||
|
||
static bool
|
||
dotlines (FILE *outputfile, struct diff3_block *b, int filenum)
|
||
{
|
||
lin i;
|
||
bool leading_dot = false;
|
||
|
||
for (i = 0;
|
||
i < D_NUMLINES (b, filenum);
|
||
i++)
|
||
{
|
||
char *line = D_RELNUM (b, filenum, i);
|
||
if (line[0] == '.')
|
||
{
|
||
leading_dot = true;
|
||
fprintf (outputfile, ".");
|
||
}
|
||
fwrite (line, sizeof (char),
|
||
D_RELLEN (b, filenum, i), outputfile);
|
||
}
|
||
|
||
return leading_dot;
|
||
}
|
||
|
||
/* Output to OUTPUTFILE a '.' line. If LEADING_DOT is true, also
|
||
output a command that removes initial '.'s starting with line START
|
||
and continuing for NUM lines. (START is long int, not lin, for
|
||
convenience with printf %ld formats.) */
|
||
|
||
static void
|
||
undotlines (FILE *outputfile, bool leading_dot, long int 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);
|
||
}
|
||
}
|
||
|
||
/* Output a diff3 set of blocks as an ed script. This script applies
|
||
the changes between file's 2 & 3 to file 1. Take the precise
|
||
format of the ed script to be output from global variables set
|
||
during options processing. Reverse the order of
|
||
the set of diff3 blocks in DIFF; this gets
|
||
around the problems involved with changing line numbers in an ed
|
||
script.
|
||
|
||
As in `output_diff3', the variable MAPPING maps from file number
|
||
according to the argument list to file number according to the diff
|
||
passed. All files listed below are in terms of the argument list.
|
||
REV_MAPPING is the inverse of MAPPING.
|
||
|
||
FILE0, FILE1 and FILE2 are the strings to print as the names of the
|
||
three files. These may be the actual names, or may be the
|
||
arguments specified with -L.
|
||
|
||
Return 1 if conflicts were found. */
|
||
|
||
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 = false;
|
||
bool conflict;
|
||
struct diff3_block *b;
|
||
|
||
for (b = reverse_diff3_blocklist (diff); b; b = b->next)
|
||
{
|
||
/* Must do mapping correctly. */
|
||
enum diff_type type
|
||
= (b->correspond == DIFF_ALL
|
||
? DIFF_ALL
|
||
: DIFF_1ST + rev_mapping[b->correspond - DIFF_1ST]);
|
||
|
||
long int low0, high0;
|
||
|
||
/* If we aren't supposed to do this output block, skip it. */
|
||
switch (type)
|
||
{
|
||
default: continue;
|
||
case DIFF_2ND: if (!show_2nd) continue; conflict = true; break;
|
||
case DIFF_3RD: if (overlap_only) continue; conflict = false; 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 = true;
|
||
|
||
|
||
/* Mark end of conflict. */
|
||
|
||
fprintf (outputfile, "%lda\n", high0);
|
||
leading_dot = false;
|
||
if (type == DIFF_ALL)
|
||
{
|
||
if (show_2nd)
|
||
{
|
||
/* Append lines from FILE1. */
|
||
fprintf (outputfile, "||||||| %s\n", file1);
|
||
leading_dot = dotlines (outputfile, b, mapping[FILE1]);
|
||
}
|
||
/* Append lines from FILE2. */
|
||
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));
|
||
|
||
|
||
/* Mark start of conflict. */
|
||
|
||
fprintf (outputfile, "%lda\n<<<<<<< %s\n", low0 - 1,
|
||
type == DIFF_ALL ? file0 : file1);
|
||
leading_dot = false;
|
||
if (type == DIFF_2ND)
|
||
{
|
||
/* Prepend lines from FILE1. */
|
||
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)
|
||
/* Write out a delete */
|
||
{
|
||
if (low0 == high0)
|
||
fprintf (outputfile, "%ldd\n", low0);
|
||
else
|
||
fprintf (outputfile, "%ld,%ldd\n", low0, high0);
|
||
}
|
||
else
|
||
/* Write out an add or change */
|
||
{
|
||
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;
|
||
}
|
||
|
||
/* Read from INFILE and output to OUTPUTFILE a set of diff3_blocks
|
||
DIFF as a merged file. This acts like 'ed file0
|
||
<[output_diff3_edscript]', except that it works even for binary
|
||
data or incomplete lines.
|
||
|
||
As before, MAPPING maps from arg list file number to diff file
|
||
number, REV_MAPPING is its inverse, and FILE0, FILE1, and FILE2 are
|
||
the names of the files.
|
||
|
||
Return 1 if conflicts were 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 = false;
|
||
bool conflict;
|
||
struct diff3_block *b;
|
||
lin linesread = 0;
|
||
|
||
for (b = diff; b; b = b->next)
|
||
{
|
||
/* Must do mapping correctly. */
|
||
enum diff_type type
|
||
= ((b->correspond == DIFF_ALL)
|
||
? DIFF_ALL
|
||
: DIFF_1ST + rev_mapping[b->correspond - DIFF_1ST]);
|
||
char const *format_2nd = "<<<<<<< %s\n";
|
||
|
||
/* If we aren't supposed to do this output block, skip it. */
|
||
switch (type)
|
||
{
|
||
default: continue;
|
||
case DIFF_2ND: if (!show_2nd) continue; conflict = true; break;
|
||
case DIFF_3RD: if (overlap_only) continue; conflict = false; break;
|
||
case DIFF_ALL: if (simple_only) continue; conflict = flagging;
|
||
format_2nd = "||||||| %s\n";
|
||
break;
|
||
}
|
||
|
||
/* Copy I lines from file 0. */
|
||
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 = true;
|
||
|
||
if (type == DIFF_ALL)
|
||
{
|
||
/* Put in lines from FILE0 with bracket. */
|
||
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)
|
||
{
|
||
/* Put in lines from FILE1 with bracket. */
|
||
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");
|
||
}
|
||
|
||
/* Put in lines from FILE2. */
|
||
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);
|
||
|
||
/* Skip I lines in file 0. */
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
/* Copy rest of common file. */
|
||
while ((c = getc (infile)) != EOF || !(ferror (infile) | feof (infile)))
|
||
putc (c, outputfile);
|
||
return conflicts_found;
|
||
}
|
||
|
||
/* Reverse the order of the list of diff3 blocks. */
|
||
|
||
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 ();
|
||
}
|