1*f8c23a2bSchristos /* $NetBSD: diff3.c,v 1.2 2016/01/13 03:39:28 christos Exp $ */
275f6d617Schristos
375f6d617Schristos /* diff3 - compare three files line by line
475f6d617Schristos
575f6d617Schristos Copyright (C) 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1998, 2001,
675f6d617Schristos 2002 Free Software Foundation, Inc.
775f6d617Schristos
875f6d617Schristos This program is free software; you can redistribute it and/or modify
975f6d617Schristos it under the terms of the GNU General Public License as published by
1075f6d617Schristos the Free Software Foundation; either version 2, or (at your option)
1175f6d617Schristos any later version.
1275f6d617Schristos
1375f6d617Schristos This program is distributed in the hope that it will be useful,
1475f6d617Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
1575f6d617Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1675f6d617Schristos See the GNU General Public License for more details.
1775f6d617Schristos
1875f6d617Schristos You should have received a copy of the GNU General Public License
1975f6d617Schristos along with this program; see the file COPYING.
2075f6d617Schristos If not, write to the Free Software Foundation,
2175f6d617Schristos 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
2275f6d617Schristos
2375f6d617Schristos #include "system.h"
2475f6d617Schristos
2575f6d617Schristos static char const copyright_string[] =
2675f6d617Schristos "Copyright (C) 2002 Free Software Foundation, Inc.";
2775f6d617Schristos
2875f6d617Schristos static char const authorship_msgid[] = N_("Written by Randy Smith.");
2975f6d617Schristos
3075f6d617Schristos #include <c-stack.h>
3175f6d617Schristos #include <cmpbuf.h>
3275f6d617Schristos #include <error.h>
3375f6d617Schristos #include <exitfail.h>
3475f6d617Schristos #include <freesoft.h>
3575f6d617Schristos #include <getopt.h>
3675f6d617Schristos #include <inttostr.h>
3775f6d617Schristos #include <quotesys.h>
3875f6d617Schristos #include <stdio.h>
3975f6d617Schristos #include <xalloc.h>
4075f6d617Schristos
4175f6d617Schristos extern char const version_string[];
4275f6d617Schristos
4375f6d617Schristos /*
4475f6d617Schristos * Internal data structures and macros for the diff3 program; includes
4575f6d617Schristos * data structures for both diff3 diffs and normal diffs.
4675f6d617Schristos */
4775f6d617Schristos
4875f6d617Schristos /* Different files within a three way diff. */
4975f6d617Schristos #define FILE0 0
5075f6d617Schristos #define FILE1 1
5175f6d617Schristos #define FILE2 2
5275f6d617Schristos
5375f6d617Schristos /*
5475f6d617Schristos * A three way diff is built from two two-way diffs; the file which
5575f6d617Schristos * the two two-way diffs share is:
5675f6d617Schristos */
5775f6d617Schristos #define FILEC FILE2
5875f6d617Schristos
5975f6d617Schristos /*
6075f6d617Schristos * Different files within a two way diff.
6175f6d617Schristos * FC is the common file, FO the other file.
6275f6d617Schristos */
6375f6d617Schristos #define FO 0
6475f6d617Schristos #define FC 1
6575f6d617Schristos
6675f6d617Schristos /* The ranges are indexed by */
6775f6d617Schristos #define RANGE_START 0
6875f6d617Schristos #define RANGE_END 1
6975f6d617Schristos
7075f6d617Schristos enum diff_type {
7175f6d617Schristos ERROR, /* Should not be used */
7275f6d617Schristos ADD, /* Two way diff add */
7375f6d617Schristos CHANGE, /* Two way diff change */
7475f6d617Schristos DELETE, /* Two way diff delete */
7575f6d617Schristos DIFF_ALL, /* All three are different */
7675f6d617Schristos DIFF_1ST, /* Only the first is different */
7775f6d617Schristos DIFF_2ND, /* Only the second */
7875f6d617Schristos DIFF_3RD /* Only the third */
7975f6d617Schristos };
8075f6d617Schristos
8175f6d617Schristos /* Two way diff */
8275f6d617Schristos struct diff_block {
8375f6d617Schristos lin ranges[2][2]; /* Ranges are inclusive */
8475f6d617Schristos char **lines[2]; /* The actual lines (may contain nulls) */
8575f6d617Schristos size_t *lengths[2]; /* Line lengths (including newlines, if any) */
8675f6d617Schristos struct diff_block *next;
8775f6d617Schristos };
8875f6d617Schristos
8975f6d617Schristos /* Three way diff */
9075f6d617Schristos
9175f6d617Schristos struct diff3_block {
9275f6d617Schristos enum diff_type correspond; /* Type of diff */
9375f6d617Schristos lin ranges[3][2]; /* Ranges are inclusive */
9475f6d617Schristos char **lines[3]; /* The actual lines (may contain nulls) */
9575f6d617Schristos size_t *lengths[3]; /* Line lengths (including newlines, if any) */
9675f6d617Schristos struct diff3_block *next;
9775f6d617Schristos };
9875f6d617Schristos
9975f6d617Schristos /*
10075f6d617Schristos * Access the ranges on a diff block.
10175f6d617Schristos */
10275f6d617Schristos #define D_LOWLINE(diff, filenum) \
10375f6d617Schristos ((diff)->ranges[filenum][RANGE_START])
10475f6d617Schristos #define D_HIGHLINE(diff, filenum) \
10575f6d617Schristos ((diff)->ranges[filenum][RANGE_END])
10675f6d617Schristos #define D_NUMLINES(diff, filenum) \
10775f6d617Schristos (D_HIGHLINE (diff, filenum) - D_LOWLINE (diff, filenum) + 1)
10875f6d617Schristos
10975f6d617Schristos /*
11075f6d617Schristos * Access the line numbers in a file in a diff by relative line
11175f6d617Schristos * numbers (i.e. line number within the diff itself). Note that these
11275f6d617Schristos * are lvalues and can be used for assignment.
11375f6d617Schristos */
11475f6d617Schristos #define D_RELNUM(diff, filenum, linenum) \
11575f6d617Schristos ((diff)->lines[filenum][linenum])
11675f6d617Schristos #define D_RELLEN(diff, filenum, linenum) \
11775f6d617Schristos ((diff)->lengths[filenum][linenum])
11875f6d617Schristos
11975f6d617Schristos /*
12075f6d617Schristos * And get at them directly, when that should be necessary.
12175f6d617Schristos */
12275f6d617Schristos #define D_LINEARRAY(diff, filenum) \
12375f6d617Schristos ((diff)->lines[filenum])
12475f6d617Schristos #define D_LENARRAY(diff, filenum) \
12575f6d617Schristos ((diff)->lengths[filenum])
12675f6d617Schristos
12775f6d617Schristos /*
12875f6d617Schristos * Next block.
12975f6d617Schristos */
13075f6d617Schristos #define D_NEXT(diff) ((diff)->next)
13175f6d617Schristos
13275f6d617Schristos /*
13375f6d617Schristos * Access the type of a diff3 block.
13475f6d617Schristos */
13575f6d617Schristos #define D3_TYPE(diff) ((diff)->correspond)
13675f6d617Schristos
13775f6d617Schristos /*
13875f6d617Schristos * Line mappings based on diffs. The first maps off the top of the
13975f6d617Schristos * diff, the second off of the bottom.
14075f6d617Schristos */
14175f6d617Schristos #define D_HIGH_MAPLINE(diff, fromfile, tofile, linenum) \
14275f6d617Schristos ((linenum) \
14375f6d617Schristos - D_HIGHLINE ((diff), (fromfile)) \
14475f6d617Schristos + D_HIGHLINE ((diff), (tofile)))
14575f6d617Schristos
14675f6d617Schristos #define D_LOW_MAPLINE(diff, fromfile, tofile, linenum) \
14775f6d617Schristos ((linenum) \
14875f6d617Schristos - D_LOWLINE ((diff), (fromfile)) \
14975f6d617Schristos + D_LOWLINE ((diff), (tofile)))
15075f6d617Schristos
15175f6d617Schristos /* Options variables for flags set on command line. */
15275f6d617Schristos
15375f6d617Schristos /* If nonzero, treat all files as text files, never as binary. */
15475f6d617Schristos static bool text;
15575f6d617Schristos
15675f6d617Schristos /* If nonzero, write out an ed script instead of the standard diff3 format. */
15775f6d617Schristos static bool edscript;
15875f6d617Schristos
15975f6d617Schristos /* If nonzero, in the case of overlapping diffs (type DIFF_ALL),
16075f6d617Schristos preserve the lines which would normally be deleted from
16175f6d617Schristos file 1 with a special flagging mechanism. */
16275f6d617Schristos static bool flagging;
16375f6d617Schristos
16475f6d617Schristos /* Use a tab to align output lines (-T). */
16575f6d617Schristos static bool initial_tab;
16675f6d617Schristos
16775f6d617Schristos /* If nonzero, do not output information for overlapping diffs. */
16875f6d617Schristos static bool simple_only;
16975f6d617Schristos
17075f6d617Schristos /* If nonzero, do not output information for non-overlapping diffs. */
17175f6d617Schristos static bool overlap_only;
17275f6d617Schristos
17375f6d617Schristos /* If nonzero, show information for DIFF_2ND diffs. */
17475f6d617Schristos static bool show_2nd;
17575f6d617Schristos
17675f6d617Schristos /* If nonzero, include `:wq' at the end of the script
17775f6d617Schristos to write out the file being edited. */
17875f6d617Schristos static bool finalwrite;
17975f6d617Schristos
18075f6d617Schristos /* If nonzero, output a merged file. */
18175f6d617Schristos static bool merge;
18275f6d617Schristos
18375f6d617Schristos char *program_name;
18475f6d617Schristos
18575f6d617Schristos static char *read_diff (char const *, char const *, char **);
18675f6d617Schristos static char *scan_diff_line (char *, char **, size_t *, char *, char);
18775f6d617Schristos static enum diff_type process_diff_control (char **, struct diff_block *);
18875f6d617Schristos static bool compare_line_list (char * const[], size_t const[], char * const[], size_t const[], lin);
18975f6d617Schristos static bool copy_stringlist (char * const[], size_t const[], char *[], size_t[], lin);
19075f6d617Schristos static bool output_diff3_edscript (FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *);
19175f6d617Schristos static bool output_diff3_merge (FILE *, FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *);
19275f6d617Schristos static struct diff3_block *create_diff3_block (lin, lin, lin, lin, lin, lin);
19375f6d617Schristos static struct diff3_block *make_3way_diff (struct diff_block *, struct diff_block *);
19475f6d617Schristos static struct diff3_block *reverse_diff3_blocklist (struct diff3_block *);
19575f6d617Schristos static struct diff3_block *using_to_diff3_block (struct diff_block *[2], struct diff_block *[2], int, int, struct diff3_block const *);
19675f6d617Schristos static struct diff_block *process_diff (char const *, char const *, struct diff_block **);
19775f6d617Schristos static void check_stdout (void);
19875f6d617Schristos static void fatal (char const *) __attribute__((noreturn));
19975f6d617Schristos static void output_diff3 (FILE *, struct diff3_block *, int const[3], int const[3]);
20075f6d617Schristos static void perror_with_exit (char const *) __attribute__((noreturn));
20175f6d617Schristos static void try_help (char const *, char const *) __attribute__((noreturn));
20275f6d617Schristos static void usage (void);
20375f6d617Schristos
20475f6d617Schristos static char const *diff_program = DEFAULT_DIFF_PROGRAM;
20575f6d617Schristos
20675f6d617Schristos /* Values for long options that do not have single-letter equivalents. */
20775f6d617Schristos enum
20875f6d617Schristos {
20975f6d617Schristos DIFF_PROGRAM_OPTION = CHAR_MAX + 1,
21075f6d617Schristos HELP_OPTION
21175f6d617Schristos };
21275f6d617Schristos
21375f6d617Schristos static struct option const longopts[] =
21475f6d617Schristos {
21575f6d617Schristos {"text", 0, 0, 'a'},
21675f6d617Schristos {"show-all", 0, 0, 'A'},
21775f6d617Schristos {"ed", 0, 0, 'e'},
21875f6d617Schristos {"diff-program", 1, 0, DIFF_PROGRAM_OPTION},
21975f6d617Schristos {"show-overlap", 0, 0, 'E'},
22075f6d617Schristos {"label", 1, 0, 'L'},
22175f6d617Schristos {"merge", 0, 0, 'm'},
22275f6d617Schristos {"initial-tab", 0, 0, 'T'},
22375f6d617Schristos {"overlap-only", 0, 0, 'x'},
22475f6d617Schristos {"easy-only", 0, 0, '3'},
22575f6d617Schristos {"version", 0, 0, 'v'},
22675f6d617Schristos {"help", 0, 0, HELP_OPTION},
22775f6d617Schristos {0, 0, 0, 0}
22875f6d617Schristos };
22975f6d617Schristos
23075f6d617Schristos /*
23175f6d617Schristos * Main program. Calls diff twice on two pairs of input files,
23275f6d617Schristos * combines the two diffs, and outputs them.
23375f6d617Schristos */
23475f6d617Schristos int
main(int argc,char ** argv)23575f6d617Schristos main (int argc, char **argv)
23675f6d617Schristos {
23775f6d617Schristos int c, i;
23875f6d617Schristos int common;
23975f6d617Schristos int mapping[3];
24075f6d617Schristos int rev_mapping[3];
24175f6d617Schristos int incompat = 0;
24275f6d617Schristos bool conflicts_found;
24375f6d617Schristos struct diff_block *thread0, *thread1, *last_block;
24475f6d617Schristos struct diff3_block *diff3;
24575f6d617Schristos int tag_count = 0;
24675f6d617Schristos char *tag_strings[3];
24775f6d617Schristos char *commonname;
24875f6d617Schristos char **file;
24975f6d617Schristos struct stat statb;
25075f6d617Schristos
25175f6d617Schristos exit_failure = 2;
25275f6d617Schristos initialize_main (&argc, &argv);
25375f6d617Schristos program_name = argv[0];
25475f6d617Schristos setlocale (LC_ALL, "");
25575f6d617Schristos bindtextdomain (PACKAGE, LOCALEDIR);
25675f6d617Schristos textdomain (PACKAGE);
25775f6d617Schristos c_stack_action (c_stack_die);
25875f6d617Schristos
25975f6d617Schristos while ((c = getopt_long (argc, argv, "aeimvx3AEL:TX", longopts, 0)) != -1)
26075f6d617Schristos {
26175f6d617Schristos switch (c)
26275f6d617Schristos {
26375f6d617Schristos case 'a':
26475f6d617Schristos text = 1;
26575f6d617Schristos break;
26675f6d617Schristos case 'A':
26775f6d617Schristos show_2nd = 1;
26875f6d617Schristos flagging = 1;
26975f6d617Schristos incompat++;
27075f6d617Schristos break;
27175f6d617Schristos case 'x':
27275f6d617Schristos overlap_only = 1;
27375f6d617Schristos incompat++;
27475f6d617Schristos break;
27575f6d617Schristos case '3':
27675f6d617Schristos simple_only = 1;
27775f6d617Schristos incompat++;
27875f6d617Schristos break;
27975f6d617Schristos case 'i':
28075f6d617Schristos finalwrite = 1;
28175f6d617Schristos break;
28275f6d617Schristos case 'm':
28375f6d617Schristos merge = 1;
28475f6d617Schristos break;
28575f6d617Schristos case 'X':
28675f6d617Schristos overlap_only = 1;
28775f6d617Schristos /* Fall through. */
28875f6d617Schristos case 'E':
28975f6d617Schristos flagging = 1;
29075f6d617Schristos /* Fall through. */
29175f6d617Schristos case 'e':
29275f6d617Schristos incompat++;
29375f6d617Schristos break;
29475f6d617Schristos case 'T':
29575f6d617Schristos initial_tab = 1;
29675f6d617Schristos break;
29775f6d617Schristos case 'v':
29875f6d617Schristos printf ("diff3 %s\n%s\n\n%s\n\n%s\n",
29975f6d617Schristos version_string, copyright_string,
30075f6d617Schristos _(free_software_msgid), _(authorship_msgid));
30175f6d617Schristos check_stdout ();
30275f6d617Schristos return EXIT_SUCCESS;
30375f6d617Schristos case DIFF_PROGRAM_OPTION:
30475f6d617Schristos diff_program = optarg;
30575f6d617Schristos break;
30675f6d617Schristos case HELP_OPTION:
30775f6d617Schristos usage ();
30875f6d617Schristos check_stdout ();
30975f6d617Schristos return EXIT_SUCCESS;
31075f6d617Schristos case 'L':
31175f6d617Schristos /* Handle up to three -L options. */
31275f6d617Schristos if (tag_count < 3)
31375f6d617Schristos {
31475f6d617Schristos tag_strings[tag_count++] = optarg;
31575f6d617Schristos break;
31675f6d617Schristos }
31775f6d617Schristos try_help ("too many file label options", 0);
31875f6d617Schristos default:
31975f6d617Schristos try_help (0, 0);
32075f6d617Schristos }
32175f6d617Schristos }
32275f6d617Schristos
32375f6d617Schristos edscript = incompat & ~merge; /* -AeExX3 without -m implies ed script. */
32475f6d617Schristos show_2nd |= ~incompat & merge; /* -m without -AeExX3 implies -A. */
32575f6d617Schristos flagging |= ~incompat & merge;
32675f6d617Schristos
32775f6d617Schristos if (incompat > 1 /* Ensure at most one of -AeExX3. */
32875f6d617Schristos || finalwrite & merge /* -i -m would rewrite input file. */
32975f6d617Schristos || (tag_count && ! flagging)) /* -L requires one of -AEX. */
33075f6d617Schristos try_help ("incompatible options", 0);
33175f6d617Schristos
33275f6d617Schristos if (argc - optind != 3)
33375f6d617Schristos {
33475f6d617Schristos if (argc - optind < 3)
33575f6d617Schristos try_help ("missing operand after `%s'", argv[argc - 1]);
33675f6d617Schristos else
33775f6d617Schristos try_help ("extra operand `%s'", argv[optind + 3]);
33875f6d617Schristos }
33975f6d617Schristos
34075f6d617Schristos file = &argv[optind];
34175f6d617Schristos
34275f6d617Schristos for (i = tag_count; i < 3; i++)
34375f6d617Schristos tag_strings[i] = file[i];
34475f6d617Schristos
34575f6d617Schristos /* Always compare file1 to file2, even if file2 is "-".
34675f6d617Schristos This is needed for -mAeExX3. Using the file0 as
34775f6d617Schristos the common file would produce wrong results, because if the
34875f6d617Schristos file0-file1 diffs didn't line up with the file0-file2 diffs
34975f6d617Schristos (which is entirely possible since we don't use diff's -n option),
35075f6d617Schristos diff3 might report phantom changes from file1 to file2.
35175f6d617Schristos
35275f6d617Schristos Also, try to compare file0 to file1, because this is where
35375f6d617Schristos changes are expected to come from. Diffing between these pairs
35475f6d617Schristos of files is more likely to avoid phantom changes from file0 to file1.
35575f6d617Schristos
35675f6d617Schristos Historically, the default common file was file2, so some older
35775f6d617Schristos applications (e.g. Emacs ediff) used file2 as the ancestor. So,
35875f6d617Schristos for compatibility, if this is a 3-way diff (not a merge or
35975f6d617Schristos edscript), prefer file2 as the common file. */
36075f6d617Schristos
36175f6d617Schristos common = 2 - (edscript | merge);
36275f6d617Schristos
36375f6d617Schristos if (strcmp (file[common], "-") == 0)
36475f6d617Schristos {
36575f6d617Schristos /* Sigh. We've got standard input as the common file. We can't
36675f6d617Schristos call diff twice on stdin. Use the other arg as the common
36775f6d617Schristos file instead. */
36875f6d617Schristos common = 3 - common;
36975f6d617Schristos if (strcmp (file[0], "-") == 0 || strcmp (file[common], "-") == 0)
37075f6d617Schristos fatal ("`-' specified for more than one input file");
37175f6d617Schristos }
37275f6d617Schristos
37375f6d617Schristos mapping[0] = 0;
37475f6d617Schristos mapping[1] = 3 - common;
37575f6d617Schristos mapping[2] = common;
37675f6d617Schristos
37775f6d617Schristos for (i = 0; i < 3; i++)
37875f6d617Schristos rev_mapping[mapping[i]] = i;
37975f6d617Schristos
38075f6d617Schristos for (i = 0; i < 3; i++)
38175f6d617Schristos if (strcmp (file[i], "-") != 0)
38275f6d617Schristos {
38375f6d617Schristos if (stat (file[i], &statb) < 0)
38475f6d617Schristos perror_with_exit (file[i]);
38575f6d617Schristos else if (S_ISDIR (statb.st_mode))
38675f6d617Schristos error (EXIT_TROUBLE, EISDIR, "%s", file[i]);
38775f6d617Schristos }
38875f6d617Schristos
38975f6d617Schristos #ifdef SIGCHLD
39075f6d617Schristos /* System V fork+wait does not work if SIGCHLD is ignored. */
39175f6d617Schristos signal (SIGCHLD, SIG_DFL);
39275f6d617Schristos #endif
39375f6d617Schristos
39475f6d617Schristos commonname = file[rev_mapping[FILEC]];
39575f6d617Schristos thread1 = process_diff (file[rev_mapping[FILE1]], commonname, &last_block);
39675f6d617Schristos thread0 = process_diff (file[rev_mapping[FILE0]], commonname, &last_block);
39775f6d617Schristos diff3 = make_3way_diff (thread0, thread1);
39875f6d617Schristos if (edscript)
39975f6d617Schristos conflicts_found
40075f6d617Schristos = output_diff3_edscript (stdout, diff3, mapping, rev_mapping,
40175f6d617Schristos tag_strings[0], tag_strings[1], tag_strings[2]);
40275f6d617Schristos else if (merge)
40375f6d617Schristos {
40475f6d617Schristos if (! freopen (file[rev_mapping[FILE0]], "r", stdin))
40575f6d617Schristos perror_with_exit (file[rev_mapping[FILE0]]);
40675f6d617Schristos conflicts_found
40775f6d617Schristos = output_diff3_merge (stdin, stdout, diff3, mapping, rev_mapping,
40875f6d617Schristos tag_strings[0], tag_strings[1], tag_strings[2]);
40975f6d617Schristos if (ferror (stdin))
41075f6d617Schristos fatal ("read failed");
41175f6d617Schristos }
41275f6d617Schristos else
41375f6d617Schristos {
41475f6d617Schristos output_diff3 (stdout, diff3, mapping, rev_mapping);
41575f6d617Schristos conflicts_found = 0;
41675f6d617Schristos }
41775f6d617Schristos
41875f6d617Schristos check_stdout ();
41975f6d617Schristos exit (conflicts_found);
42075f6d617Schristos return conflicts_found;
42175f6d617Schristos }
42275f6d617Schristos
42375f6d617Schristos static void
try_help(char const * reason_msgid,char const * operand)42475f6d617Schristos try_help (char const *reason_msgid, char const *operand)
42575f6d617Schristos {
42675f6d617Schristos if (reason_msgid)
42775f6d617Schristos error (0, 0, _(reason_msgid), operand);
42875f6d617Schristos error (EXIT_TROUBLE, 0,
42975f6d617Schristos _("Try `%s --help' for more information."), program_name);
43075f6d617Schristos abort ();
43175f6d617Schristos }
43275f6d617Schristos
43375f6d617Schristos static void
check_stdout(void)43475f6d617Schristos check_stdout (void)
43575f6d617Schristos {
43675f6d617Schristos if (ferror (stdout))
43775f6d617Schristos fatal ("write failed");
43875f6d617Schristos else if (fclose (stdout) != 0)
43975f6d617Schristos perror_with_exit (_("standard output"));
44075f6d617Schristos }
44175f6d617Schristos
44275f6d617Schristos static char const * const option_help_msgid[] = {
44375f6d617Schristos N_("-e --ed Output unmerged changes from OLDFILE to YOURFILE into MYFILE."),
44475f6d617Schristos N_("-E --show-overlap Output unmerged changes, bracketing conflicts."),
44575f6d617Schristos N_("-A --show-all Output all changes, bracketing conflicts."),
44675f6d617Schristos N_("-x --overlap-only Output overlapping changes."),
44775f6d617Schristos N_("-X Output overlapping changes, bracketing them."),
44875f6d617Schristos N_("-3 --easy-only Output unmerged nonoverlapping changes."),
44975f6d617Schristos "",
45075f6d617Schristos N_("-m --merge Output merged file instead of ed script (default -A)."),
45175f6d617Schristos N_("-L LABEL --label=LABEL Use LABEL instead of file name."),
45275f6d617Schristos N_("-i Append `w' and `q' commands to ed scripts."),
45375f6d617Schristos N_("-a --text Treat all files as text."),
45475f6d617Schristos N_("-T --initial-tab Make tabs line up by prepending a tab."),
45575f6d617Schristos N_("--diff-program=PROGRAM Use PROGRAM to compare files."),
45675f6d617Schristos "",
45775f6d617Schristos N_("-v --version Output version info."),
45875f6d617Schristos N_("--help Output this help."),
45975f6d617Schristos 0
46075f6d617Schristos };
46175f6d617Schristos
46275f6d617Schristos static void
usage(void)46375f6d617Schristos usage (void)
46475f6d617Schristos {
46575f6d617Schristos char const * const *p;
46675f6d617Schristos
46775f6d617Schristos printf (_("Usage: %s [OPTION]... MYFILE OLDFILE YOURFILE\n"),
46875f6d617Schristos program_name);
46975f6d617Schristos printf ("%s\n\n", _("Compare three files line by line."));
47075f6d617Schristos for (p = option_help_msgid; *p; p++)
47175f6d617Schristos if (**p)
47275f6d617Schristos printf (" %s\n", _(*p));
47375f6d617Schristos else
47475f6d617Schristos putchar ('\n');
47575f6d617Schristos printf ("\n%s\n\n%s\n",
47675f6d617Schristos _("If a FILE is `-', read standard input."),
47775f6d617Schristos _("Report bugs to <bug-gnu-utils@gnu.org>."));
47875f6d617Schristos }
47975f6d617Schristos
48075f6d617Schristos /*
48175f6d617Schristos * Routines that combine the two diffs together into one. The
48275f6d617Schristos * algorithm used follows:
48375f6d617Schristos *
48475f6d617Schristos * File2 is shared in common between the two diffs.
48575f6d617Schristos * Diff02 is the diff between 0 and 2.
48675f6d617Schristos * Diff12 is the diff between 1 and 2.
48775f6d617Schristos *
48875f6d617Schristos * 1) Find the range for the first block in File2.
48975f6d617Schristos * a) Take the lowest of the two ranges (in File2) in the two
49075f6d617Schristos * current blocks (one from each diff) as being the low
49175f6d617Schristos * water mark. Assign the upper end of this block as
49275f6d617Schristos * being the high water mark and move the current block up
49375f6d617Schristos * one. Mark the block just moved over as to be used.
49475f6d617Schristos * b) Check the next block in the diff that the high water
49575f6d617Schristos * mark is *not* from.
49675f6d617Schristos *
49775f6d617Schristos * *If* the high water mark is above
49875f6d617Schristos * the low end of the range in that block,
49975f6d617Schristos *
50075f6d617Schristos * mark that block as to be used and move the current
50175f6d617Schristos * block up. Set the high water mark to the max of
50275f6d617Schristos * the high end of this block and the current. Repeat b.
50375f6d617Schristos *
50475f6d617Schristos * 2) Find the corresponding ranges in File0 (from the blocks
50575f6d617Schristos * in diff02; line per line outside of diffs) and in File1.
50675f6d617Schristos * Create a diff3_block, reserving space as indicated by the ranges.
50775f6d617Schristos *
50875f6d617Schristos * 3) Copy all of the pointers for file2 in. At least for now,
50975f6d617Schristos * do memcmp's between corresponding strings in the two diffs.
51075f6d617Schristos *
51175f6d617Schristos * 4) Copy all of the pointers for file0 and 1 in. Get what you
51275f6d617Schristos * need from file2 (when there isn't a diff block, it's
51375f6d617Schristos * identical to file2 within the range between diff blocks).
51475f6d617Schristos *
51575f6d617Schristos * 5) If the diff blocks you used came from only one of the two
51675f6d617Schristos * strings of diffs, then that file (i.e. the one other than
51775f6d617Schristos * the common file in that diff) is the odd person out. If you used
51875f6d617Schristos * diff blocks from both sets, check to see if files 0 and 1 match:
51975f6d617Schristos *
52075f6d617Schristos * Same number of lines? If so, do a set of memcmp's (if a
52175f6d617Schristos * memcmp matches; copy the pointer over; it'll be easier later
52275f6d617Schristos * if you have to do any compares). If they match, 0 & 1 are
52375f6d617Schristos * the same. If not, all three different.
52475f6d617Schristos *
52575f6d617Schristos * Then you do it again, until you run out of blocks.
52675f6d617Schristos *
52775f6d617Schristos */
52875f6d617Schristos
52975f6d617Schristos /*
53075f6d617Schristos * This routine makes a three way diff (chain of diff3_block's) from two
53175f6d617Schristos * two way diffs (chains of diff_block's). It is assumed that each of
53275f6d617Schristos * the two diffs passed are onto the same file (i.e. that each of the
53375f6d617Schristos * diffs were made "to" the same file). The three way diff pointer
53475f6d617Schristos * returned will have numbering FILE0--the other file in diff02,
53575f6d617Schristos * FILE1--the other file in diff12, and FILEC--the common file.
53675f6d617Schristos */
53775f6d617Schristos static struct diff3_block *
make_3way_diff(struct diff_block * thread0,struct diff_block * thread1)53875f6d617Schristos make_3way_diff (struct diff_block *thread0, struct diff_block *thread1)
53975f6d617Schristos {
54075f6d617Schristos /*
54175f6d617Schristos * This routine works on the two diffs passed to it as threads.
54275f6d617Schristos * Thread number 0 is diff02, thread number 1 is diff12. The USING
54375f6d617Schristos * array is set to the base of the list of blocks to be used to
54475f6d617Schristos * construct each block of the three way diff; if no blocks from a
54575f6d617Schristos * particular thread are to be used, that element of the using array
54675f6d617Schristos * is set to 0. The elements LAST_USING array are set to the last
54775f6d617Schristos * elements on each of the using lists.
54875f6d617Schristos *
54975f6d617Schristos * The HIGH_WATER_MARK is set to the highest line number in the common file
55075f6d617Schristos * described in any of the diffs in either of the USING lists. The
55175f6d617Schristos * HIGH_WATER_THREAD names the thread. Similarly the BASE_WATER_MARK
55275f6d617Schristos * and BASE_WATER_THREAD describe the lowest line number in the common file
55375f6d617Schristos * described in any of the diffs in either of the USING lists. The
55475f6d617Schristos * HIGH_WATER_DIFF is the diff from which the HIGH_WATER_MARK was
55575f6d617Schristos * taken.
55675f6d617Schristos *
55775f6d617Schristos * The HIGH_WATER_DIFF should always be equal to LAST_USING
55875f6d617Schristos * [HIGH_WATER_THREAD]. The OTHER_DIFF is the next diff to check for
55975f6d617Schristos * higher water, and should always be equal to
56075f6d617Schristos * CURRENT[HIGH_WATER_THREAD ^ 0x1]. The OTHER_THREAD is the thread
56175f6d617Schristos * in which the OTHER_DIFF is, and hence should always be equal to
56275f6d617Schristos * HIGH_WATER_THREAD ^ 0x1.
56375f6d617Schristos *
56475f6d617Schristos * The variable LAST_DIFF is kept set to the last diff block produced
56575f6d617Schristos * by this routine, for line correspondence purposes between that diff
56675f6d617Schristos * and the one currently being worked on. It is initialized to
56775f6d617Schristos * ZERO_DIFF before any blocks have been created.
56875f6d617Schristos */
56975f6d617Schristos
57075f6d617Schristos struct diff_block *using[2];
57175f6d617Schristos struct diff_block *last_using[2];
57275f6d617Schristos struct diff_block *current[2];
57375f6d617Schristos
57475f6d617Schristos lin high_water_mark;
57575f6d617Schristos
57675f6d617Schristos int high_water_thread;
57775f6d617Schristos int base_water_thread;
57875f6d617Schristos int other_thread;
57975f6d617Schristos
58075f6d617Schristos struct diff_block *high_water_diff;
58175f6d617Schristos struct diff_block *other_diff;
58275f6d617Schristos
58375f6d617Schristos struct diff3_block *result;
58475f6d617Schristos struct diff3_block *tmpblock;
58575f6d617Schristos struct diff3_block **result_end;
58675f6d617Schristos
58775f6d617Schristos struct diff3_block const *last_diff3;
58875f6d617Schristos
58975f6d617Schristos static struct diff3_block const zero_diff3;
59075f6d617Schristos
59175f6d617Schristos /* Initialization */
59275f6d617Schristos result = 0;
59375f6d617Schristos result_end = &result;
59475f6d617Schristos current[0] = thread0; current[1] = thread1;
59575f6d617Schristos last_diff3 = &zero_diff3;
59675f6d617Schristos
59775f6d617Schristos /* Sniff up the threads until we reach the end */
59875f6d617Schristos
59975f6d617Schristos while (current[0] || current[1])
60075f6d617Schristos {
60175f6d617Schristos using[0] = using[1] = last_using[0] = last_using[1] = 0;
60275f6d617Schristos
60375f6d617Schristos /* Setup low and high water threads, diffs, and marks. */
60475f6d617Schristos if (!current[0])
60575f6d617Schristos base_water_thread = 1;
60675f6d617Schristos else if (!current[1])
60775f6d617Schristos base_water_thread = 0;
60875f6d617Schristos else
60975f6d617Schristos base_water_thread =
61075f6d617Schristos (D_LOWLINE (current[0], FC) > D_LOWLINE (current[1], FC));
61175f6d617Schristos
61275f6d617Schristos high_water_thread = base_water_thread;
61375f6d617Schristos
61475f6d617Schristos high_water_diff = current[high_water_thread];
61575f6d617Schristos
61675f6d617Schristos high_water_mark = D_HIGHLINE (high_water_diff, FC);
61775f6d617Schristos
61875f6d617Schristos /* Make the diff you just got info from into the using class */
61975f6d617Schristos using[high_water_thread]
62075f6d617Schristos = last_using[high_water_thread]
62175f6d617Schristos = high_water_diff;
62275f6d617Schristos current[high_water_thread] = high_water_diff->next;
62375f6d617Schristos last_using[high_water_thread]->next = 0;
62475f6d617Schristos
62575f6d617Schristos /* And mark the other diff */
62675f6d617Schristos other_thread = high_water_thread ^ 0x1;
62775f6d617Schristos other_diff = current[other_thread];
62875f6d617Schristos
62975f6d617Schristos /* Shuffle up the ladder, checking the other diff to see if it
63075f6d617Schristos needs to be incorporated. */
63175f6d617Schristos while (other_diff
63275f6d617Schristos && D_LOWLINE (other_diff, FC) <= high_water_mark + 1)
63375f6d617Schristos {
63475f6d617Schristos
63575f6d617Schristos /* Incorporate this diff into the using list. Note that
63675f6d617Schristos this doesn't take it off the current list */
63775f6d617Schristos if (using[other_thread])
63875f6d617Schristos last_using[other_thread]->next = other_diff;
63975f6d617Schristos else
64075f6d617Schristos using[other_thread] = other_diff;
64175f6d617Schristos last_using[other_thread] = other_diff;
64275f6d617Schristos
64375f6d617Schristos /* Take it off the current list. Note that this following
64475f6d617Schristos code assumes that other_diff enters it equal to
64575f6d617Schristos current[high_water_thread ^ 0x1] */
64675f6d617Schristos current[other_thread] = current[other_thread]->next;
64775f6d617Schristos other_diff->next = 0;
64875f6d617Schristos
64975f6d617Schristos /* Set the high_water stuff
65075f6d617Schristos If this comparison is equal, then this is the last pass
65175f6d617Schristos through this loop; since diff blocks within a given
65275f6d617Schristos thread cannot overlap, the high_water_mark will be
65375f6d617Schristos *below* the range_start of either of the next diffs. */
65475f6d617Schristos
65575f6d617Schristos if (high_water_mark < D_HIGHLINE (other_diff, FC))
65675f6d617Schristos {
65775f6d617Schristos high_water_thread ^= 1;
65875f6d617Schristos high_water_diff = other_diff;
65975f6d617Schristos high_water_mark = D_HIGHLINE (other_diff, FC);
66075f6d617Schristos }
66175f6d617Schristos
66275f6d617Schristos /* Set the other diff */
66375f6d617Schristos other_thread = high_water_thread ^ 0x1;
66475f6d617Schristos other_diff = current[other_thread];
66575f6d617Schristos }
66675f6d617Schristos
66775f6d617Schristos /* The using lists contain a list of all of the blocks to be
66875f6d617Schristos included in this diff3_block. Create it. */
66975f6d617Schristos
67075f6d617Schristos tmpblock = using_to_diff3_block (using, last_using,
67175f6d617Schristos base_water_thread, high_water_thread,
67275f6d617Schristos last_diff3);
67375f6d617Schristos
67475f6d617Schristos if (!tmpblock)
67575f6d617Schristos fatal ("internal error: screwup in format of diff blocks");
67675f6d617Schristos
67775f6d617Schristos /* Put it on the list. */
67875f6d617Schristos *result_end = tmpblock;
67975f6d617Schristos result_end = &tmpblock->next;
68075f6d617Schristos
68175f6d617Schristos /* Set up corresponding lines correctly. */
68275f6d617Schristos last_diff3 = tmpblock;
68375f6d617Schristos }
68475f6d617Schristos return result;
68575f6d617Schristos }
68675f6d617Schristos
68775f6d617Schristos /*
68875f6d617Schristos * using_to_diff3_block:
68975f6d617Schristos * This routine takes two lists of blocks (from two separate diff
69075f6d617Schristos * threads) and puts them together into one diff3 block.
69175f6d617Schristos * It then returns a pointer to this diff3 block or 0 for failure.
69275f6d617Schristos *
69375f6d617Schristos * All arguments besides using are for the convenience of the routine;
69475f6d617Schristos * they could be derived from the using array.
69575f6d617Schristos * LAST_USING is a pair of pointers to the last blocks in the using
69675f6d617Schristos * structure.
69775f6d617Schristos * LOW_THREAD and HIGH_THREAD tell which threads contain the lowest
69875f6d617Schristos * and highest line numbers for File0.
69975f6d617Schristos * last_diff3 contains the last diff produced in the calling routine.
70075f6d617Schristos * This is used for lines mappings which would still be identical to
70175f6d617Schristos * the state that diff ended in.
70275f6d617Schristos *
70375f6d617Schristos * A distinction should be made in this routine between the two diffs
70475f6d617Schristos * that are part of a normal two diff block, and the three diffs that
70575f6d617Schristos * are part of a diff3_block.
70675f6d617Schristos */
70775f6d617Schristos 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)70875f6d617Schristos using_to_diff3_block (struct diff_block *using[2],
70975f6d617Schristos struct diff_block *last_using[2],
71075f6d617Schristos int low_thread, int high_thread,
71175f6d617Schristos struct diff3_block const *last_diff3)
71275f6d617Schristos {
71375f6d617Schristos lin low[2], high[2];
71475f6d617Schristos struct diff3_block *result;
71575f6d617Schristos struct diff_block *ptr;
71675f6d617Schristos int d;
71775f6d617Schristos lin i;
71875f6d617Schristos
71975f6d617Schristos /* Find the range in the common file. */
72075f6d617Schristos lin lowc = D_LOWLINE (using[low_thread], FC);
72175f6d617Schristos lin highc = D_HIGHLINE (last_using[high_thread], FC);
72275f6d617Schristos
72375f6d617Schristos /* Find the ranges in the other files.
72475f6d617Schristos If using[d] is null, that means that the file to which that diff
72575f6d617Schristos refers is equivalent to the common file over this range. */
72675f6d617Schristos
72775f6d617Schristos for (d = 0; d < 2; d++)
72875f6d617Schristos if (using[d])
72975f6d617Schristos {
73075f6d617Schristos low[d] = D_LOW_MAPLINE (using[d], FC, FO, lowc);
73175f6d617Schristos high[d] = D_HIGH_MAPLINE (last_using[d], FC, FO, highc);
73275f6d617Schristos }
73375f6d617Schristos else
73475f6d617Schristos {
73575f6d617Schristos low[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, lowc);
73675f6d617Schristos high[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, highc);
73775f6d617Schristos }
73875f6d617Schristos
73975f6d617Schristos /* Create a block with the appropriate sizes */
74075f6d617Schristos result = create_diff3_block (low[0], high[0], low[1], high[1], lowc, highc);
74175f6d617Schristos
74275f6d617Schristos /* Copy information for the common file.
74375f6d617Schristos Return with a zero if any of the compares failed. */
74475f6d617Schristos
74575f6d617Schristos for (d = 0; d < 2; d++)
74675f6d617Schristos for (ptr = using[d]; ptr; ptr = D_NEXT (ptr))
74775f6d617Schristos {
74875f6d617Schristos lin result_offset = D_LOWLINE (ptr, FC) - lowc;
74975f6d617Schristos
75075f6d617Schristos if (!copy_stringlist (D_LINEARRAY (ptr, FC),
75175f6d617Schristos D_LENARRAY (ptr, FC),
75275f6d617Schristos D_LINEARRAY (result, FILEC) + result_offset,
75375f6d617Schristos D_LENARRAY (result, FILEC) + result_offset,
75475f6d617Schristos D_NUMLINES (ptr, FC)))
75575f6d617Schristos return 0;
75675f6d617Schristos }
75775f6d617Schristos
75875f6d617Schristos /* Copy information for file d. First deal with anything that might be
75975f6d617Schristos before the first diff. */
76075f6d617Schristos
76175f6d617Schristos for (d = 0; d < 2; d++)
76275f6d617Schristos {
76375f6d617Schristos struct diff_block *u = using[d];
76475f6d617Schristos lin lo = low[d], hi = high[d];
76575f6d617Schristos
76675f6d617Schristos for (i = 0;
76775f6d617Schristos i + lo < (u ? D_LOWLINE (u, FO) : hi + 1);
76875f6d617Schristos i++)
76975f6d617Schristos {
77075f6d617Schristos D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, i);
77175f6d617Schristos D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, i);
77275f6d617Schristos }
77375f6d617Schristos
77475f6d617Schristos for (ptr = u; ptr; ptr = D_NEXT (ptr))
77575f6d617Schristos {
77675f6d617Schristos lin result_offset = D_LOWLINE (ptr, FO) - lo;
77775f6d617Schristos lin linec;
77875f6d617Schristos
77975f6d617Schristos if (!copy_stringlist (D_LINEARRAY (ptr, FO),
78075f6d617Schristos D_LENARRAY (ptr, FO),
78175f6d617Schristos D_LINEARRAY (result, FILE0 + d) + result_offset,
78275f6d617Schristos D_LENARRAY (result, FILE0 + d) + result_offset,
78375f6d617Schristos D_NUMLINES (ptr, FO)))
78475f6d617Schristos return 0;
78575f6d617Schristos
78675f6d617Schristos /* Catch the lines between here and the next diff */
78775f6d617Schristos linec = D_HIGHLINE (ptr, FC) + 1 - lowc;
78875f6d617Schristos for (i = D_HIGHLINE (ptr, FO) + 1 - lo;
78975f6d617Schristos i < (D_NEXT (ptr) ? D_LOWLINE (D_NEXT (ptr), FO) : hi + 1) - lo;
79075f6d617Schristos i++)
79175f6d617Schristos {
79275f6d617Schristos D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, linec);
79375f6d617Schristos D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, linec);
79475f6d617Schristos linec++;
79575f6d617Schristos }
79675f6d617Schristos }
79775f6d617Schristos }
79875f6d617Schristos
79975f6d617Schristos /* Set correspond */
80075f6d617Schristos if (!using[0])
80175f6d617Schristos D3_TYPE (result) = DIFF_2ND;
80275f6d617Schristos else if (!using[1])
80375f6d617Schristos D3_TYPE (result) = DIFF_1ST;
80475f6d617Schristos else
80575f6d617Schristos {
80675f6d617Schristos lin nl0 = D_NUMLINES (result, FILE0);
80775f6d617Schristos lin nl1 = D_NUMLINES (result, FILE1);
80875f6d617Schristos
80975f6d617Schristos if (nl0 != nl1
81075f6d617Schristos || !compare_line_list (D_LINEARRAY (result, FILE0),
81175f6d617Schristos D_LENARRAY (result, FILE0),
81275f6d617Schristos D_LINEARRAY (result, FILE1),
81375f6d617Schristos D_LENARRAY (result, FILE1),
81475f6d617Schristos nl0))
81575f6d617Schristos D3_TYPE (result) = DIFF_ALL;
81675f6d617Schristos else
81775f6d617Schristos D3_TYPE (result) = DIFF_3RD;
81875f6d617Schristos }
81975f6d617Schristos
82075f6d617Schristos return result;
82175f6d617Schristos }
82275f6d617Schristos
82375f6d617Schristos /*
82475f6d617Schristos * This routine copies pointers from a list of strings to a different list
82575f6d617Schristos * of strings. If a spot in the second list is already filled, it
82675f6d617Schristos * makes sure that it is filled with the same string; if not it
82775f6d617Schristos * returns 0, the copy incomplete.
82875f6d617Schristos * Upon successful completion of the copy, it returns 1.
82975f6d617Schristos */
83075f6d617Schristos static bool
copy_stringlist(char * const fromptrs[],size_t const fromlengths[],char * toptrs[],size_t tolengths[],lin copynum)83175f6d617Schristos copy_stringlist (char * const fromptrs[], size_t const fromlengths[],
83275f6d617Schristos char *toptrs[], size_t tolengths[],
83375f6d617Schristos lin copynum)
83475f6d617Schristos {
83575f6d617Schristos register char * const *f = fromptrs;
83675f6d617Schristos register char **t = toptrs;
83775f6d617Schristos register size_t const *fl = fromlengths;
83875f6d617Schristos register size_t *tl = tolengths;
83975f6d617Schristos
84075f6d617Schristos while (copynum--)
84175f6d617Schristos {
84275f6d617Schristos if (*t)
84375f6d617Schristos { if (*fl != *tl || memcmp (*f, *t, *fl)) return 0; }
84475f6d617Schristos else
84575f6d617Schristos { *t = *f ; *tl = *fl; }
84675f6d617Schristos
84775f6d617Schristos t++; f++; tl++; fl++;
84875f6d617Schristos }
84975f6d617Schristos return 1;
85075f6d617Schristos }
85175f6d617Schristos
85275f6d617Schristos /*
85375f6d617Schristos * Create a diff3_block, with ranges as specified in the arguments.
85475f6d617Schristos * Allocate the arrays for the various pointers (and zero them) based
85575f6d617Schristos * on the arguments passed. Return the block as a result.
85675f6d617Schristos */
85775f6d617Schristos static struct diff3_block *
create_diff3_block(lin low0,lin high0,lin low1,lin high1,lin low2,lin high2)85875f6d617Schristos create_diff3_block (lin low0, lin high0,
85975f6d617Schristos lin low1, lin high1,
86075f6d617Schristos lin low2, lin high2)
86175f6d617Schristos {
86275f6d617Schristos struct diff3_block *result = xmalloc (sizeof *result);
86375f6d617Schristos lin numlines;
86475f6d617Schristos
86575f6d617Schristos D3_TYPE (result) = ERROR;
86675f6d617Schristos D_NEXT (result) = 0;
86775f6d617Schristos
86875f6d617Schristos /* Assign ranges */
86975f6d617Schristos D_LOWLINE (result, FILE0) = low0;
87075f6d617Schristos D_HIGHLINE (result, FILE0) = high0;
87175f6d617Schristos D_LOWLINE (result, FILE1) = low1;
87275f6d617Schristos D_HIGHLINE (result, FILE1) = high1;
87375f6d617Schristos D_LOWLINE (result, FILE2) = low2;
87475f6d617Schristos D_HIGHLINE (result, FILE2) = high2;
87575f6d617Schristos
87675f6d617Schristos /* Allocate and zero space */
87775f6d617Schristos numlines = D_NUMLINES (result, FILE0);
87875f6d617Schristos if (numlines)
87975f6d617Schristos {
88075f6d617Schristos D_LINEARRAY (result, FILE0) = xcalloc (numlines, sizeof (char *));
88175f6d617Schristos D_LENARRAY (result, FILE0) = xcalloc (numlines, sizeof (size_t));
88275f6d617Schristos }
88375f6d617Schristos else
88475f6d617Schristos {
88575f6d617Schristos D_LINEARRAY (result, FILE0) = 0;
88675f6d617Schristos D_LENARRAY (result, FILE0) = 0;
88775f6d617Schristos }
88875f6d617Schristos
88975f6d617Schristos numlines = D_NUMLINES (result, FILE1);
89075f6d617Schristos if (numlines)
89175f6d617Schristos {
89275f6d617Schristos D_LINEARRAY (result, FILE1) = xcalloc (numlines, sizeof (char *));
89375f6d617Schristos D_LENARRAY (result, FILE1) = xcalloc (numlines, sizeof (size_t));
89475f6d617Schristos }
89575f6d617Schristos else
89675f6d617Schristos {
89775f6d617Schristos D_LINEARRAY (result, FILE1) = 0;
89875f6d617Schristos D_LENARRAY (result, FILE1) = 0;
89975f6d617Schristos }
90075f6d617Schristos
90175f6d617Schristos numlines = D_NUMLINES (result, FILE2);
90275f6d617Schristos if (numlines)
90375f6d617Schristos {
90475f6d617Schristos D_LINEARRAY (result, FILE2) = xcalloc (numlines, sizeof (char *));
90575f6d617Schristos D_LENARRAY (result, FILE2) = xcalloc (numlines, sizeof (size_t));
90675f6d617Schristos }
90775f6d617Schristos else
90875f6d617Schristos {
90975f6d617Schristos D_LINEARRAY (result, FILE2) = 0;
91075f6d617Schristos D_LENARRAY (result, FILE2) = 0;
91175f6d617Schristos }
91275f6d617Schristos
91375f6d617Schristos /* Return */
91475f6d617Schristos return result;
91575f6d617Schristos }
91675f6d617Schristos
91775f6d617Schristos /*
91875f6d617Schristos * Compare two lists of lines of text.
91975f6d617Schristos * Return 1 if they are equivalent, 0 if not.
92075f6d617Schristos */
92175f6d617Schristos static bool
compare_line_list(char * const list1[],size_t const lengths1[],char * const list2[],size_t const lengths2[],lin nl)92275f6d617Schristos compare_line_list (char * const list1[], size_t const lengths1[],
92375f6d617Schristos char * const list2[], size_t const lengths2[],
92475f6d617Schristos lin nl)
92575f6d617Schristos {
92675f6d617Schristos char
92775f6d617Schristos * const *l1 = list1,
92875f6d617Schristos * const *l2 = list2;
92975f6d617Schristos size_t const
93075f6d617Schristos *lgths1 = lengths1,
93175f6d617Schristos *lgths2 = lengths2;
93275f6d617Schristos
93375f6d617Schristos while (nl--)
93475f6d617Schristos if (!*l1 || !*l2 || *lgths1 != *lgths2++
93575f6d617Schristos || memcmp (*l1++, *l2++, *lgths1++))
93675f6d617Schristos return 0;
93775f6d617Schristos return 1;
93875f6d617Schristos }
93975f6d617Schristos
94075f6d617Schristos /*
94175f6d617Schristos * Routines to input and parse two way diffs.
94275f6d617Schristos */
94375f6d617Schristos
94475f6d617Schristos static struct diff_block *
process_diff(char const * filea,char const * fileb,struct diff_block ** last_block)94575f6d617Schristos process_diff (char const *filea,
94675f6d617Schristos char const *fileb,
94775f6d617Schristos struct diff_block **last_block)
94875f6d617Schristos {
94975f6d617Schristos char *diff_contents;
95075f6d617Schristos char *diff_limit;
95175f6d617Schristos char *scan_diff;
95275f6d617Schristos enum diff_type dt;
95375f6d617Schristos lin i;
95475f6d617Schristos struct diff_block *block_list, **block_list_end, *bptr;
95575f6d617Schristos size_t too_many_lines = (PTRDIFF_MAX
95675f6d617Schristos / MIN (sizeof *bptr->lines[1],
95775f6d617Schristos sizeof *bptr->lengths[1]));
95875f6d617Schristos
95975f6d617Schristos diff_limit = read_diff (filea, fileb, &diff_contents);
96075f6d617Schristos scan_diff = diff_contents;
96175f6d617Schristos block_list_end = &block_list;
96275f6d617Schristos bptr = 0; /* Pacify `gcc -W'. */
96375f6d617Schristos
96475f6d617Schristos while (scan_diff < diff_limit)
96575f6d617Schristos {
96675f6d617Schristos bptr = xmalloc (sizeof *bptr);
96775f6d617Schristos bptr->lines[0] = bptr->lines[1] = 0;
96875f6d617Schristos bptr->lengths[0] = bptr->lengths[1] = 0;
96975f6d617Schristos
97075f6d617Schristos dt = process_diff_control (&scan_diff, bptr);
97175f6d617Schristos if (dt == ERROR || *scan_diff != '\n')
97275f6d617Schristos {
97375f6d617Schristos fprintf (stderr, _("%s: diff failed: "), program_name);
97475f6d617Schristos do
97575f6d617Schristos {
97675f6d617Schristos putc (*scan_diff, stderr);
97775f6d617Schristos }
97875f6d617Schristos while (*scan_diff++ != '\n');
97975f6d617Schristos exit (EXIT_TROUBLE);
98075f6d617Schristos }
98175f6d617Schristos scan_diff++;
98275f6d617Schristos
98375f6d617Schristos /* Force appropriate ranges to be null, if necessary */
98475f6d617Schristos switch (dt)
98575f6d617Schristos {
98675f6d617Schristos case ADD:
98775f6d617Schristos bptr->ranges[0][0]++;
98875f6d617Schristos break;
98975f6d617Schristos case DELETE:
99075f6d617Schristos bptr->ranges[1][0]++;
99175f6d617Schristos break;
99275f6d617Schristos case CHANGE:
99375f6d617Schristos break;
99475f6d617Schristos default:
99575f6d617Schristos fatal ("internal error: invalid diff type in process_diff");
99675f6d617Schristos break;
99775f6d617Schristos }
99875f6d617Schristos
99975f6d617Schristos /* Allocate space for the pointers for the lines from filea, and
100075f6d617Schristos parcel them out among these pointers */
100175f6d617Schristos if (dt != ADD)
100275f6d617Schristos {
100375f6d617Schristos lin numlines = D_NUMLINES (bptr, 0);
100475f6d617Schristos if (too_many_lines <= numlines)
100575f6d617Schristos xalloc_die ();
100675f6d617Schristos bptr->lines[0] = xmalloc (numlines * sizeof *bptr->lines[0]);
100775f6d617Schristos bptr->lengths[0] = xmalloc (numlines * sizeof *bptr->lengths[0]);
100875f6d617Schristos for (i = 0; i < numlines; i++)
100975f6d617Schristos scan_diff = scan_diff_line (scan_diff,
101075f6d617Schristos &(bptr->lines[0][i]),
101175f6d617Schristos &(bptr->lengths[0][i]),
101275f6d617Schristos diff_limit,
101375f6d617Schristos '<');
101475f6d617Schristos }
101575f6d617Schristos
101675f6d617Schristos /* Get past the separator for changes */
101775f6d617Schristos if (dt == CHANGE)
101875f6d617Schristos {
101975f6d617Schristos if (strncmp (scan_diff, "---\n", 4))
102075f6d617Schristos fatal ("invalid diff format; invalid change separator");
102175f6d617Schristos scan_diff += 4;
102275f6d617Schristos }
102375f6d617Schristos
102475f6d617Schristos /* Allocate space for the pointers for the lines from fileb, and
102575f6d617Schristos parcel them out among these pointers */
102675f6d617Schristos if (dt != DELETE)
102775f6d617Schristos {
102875f6d617Schristos lin numlines = D_NUMLINES (bptr, 1);
102975f6d617Schristos if (too_many_lines <= numlines)
103075f6d617Schristos xalloc_die ();
103175f6d617Schristos bptr->lines[1] = xmalloc (numlines * sizeof *bptr->lines[1]);
103275f6d617Schristos bptr->lengths[1] = xmalloc (numlines * sizeof *bptr->lengths[1]);
103375f6d617Schristos for (i = 0; i < numlines; i++)
103475f6d617Schristos scan_diff = scan_diff_line (scan_diff,
103575f6d617Schristos &(bptr->lines[1][i]),
103675f6d617Schristos &(bptr->lengths[1][i]),
103775f6d617Schristos diff_limit,
103875f6d617Schristos '>');
103975f6d617Schristos }
104075f6d617Schristos
104175f6d617Schristos /* Place this block on the blocklist. */
104275f6d617Schristos *block_list_end = bptr;
104375f6d617Schristos block_list_end = &bptr->next;
104475f6d617Schristos }
104575f6d617Schristos
104675f6d617Schristos *block_list_end = 0;
104775f6d617Schristos *last_block = bptr;
104875f6d617Schristos return block_list;
104975f6d617Schristos }
105075f6d617Schristos
105175f6d617Schristos /*
105275f6d617Schristos * This routine will parse a normal format diff control string. It
105375f6d617Schristos * returns the type of the diff (ERROR if the format is bad). All of
105475f6d617Schristos * the other important information is filled into to the structure
105575f6d617Schristos * pointed to by db, and the string pointer (whose location is passed
105675f6d617Schristos * to this routine) is updated to point beyond the end of the string
105775f6d617Schristos * parsed. Note that only the ranges in the diff_block will be set by
105875f6d617Schristos * this routine.
105975f6d617Schristos *
106075f6d617Schristos * If some specific pair of numbers has been reduced to a single
106175f6d617Schristos * number, then both corresponding numbers in the diff block are set
106275f6d617Schristos * to that number. In general these numbers are interpreted as ranges
106375f6d617Schristos * inclusive, unless being used by the ADD or DELETE commands. It is
106475f6d617Schristos * assumed that these will be special cased in a superior routine.
106575f6d617Schristos */
106675f6d617Schristos
106775f6d617Schristos static enum diff_type
process_diff_control(char ** string,struct diff_block * db)106875f6d617Schristos process_diff_control (char **string, struct diff_block *db)
106975f6d617Schristos {
107075f6d617Schristos char *s = *string;
107175f6d617Schristos lin holdnum;
107275f6d617Schristos enum diff_type type;
107375f6d617Schristos
107475f6d617Schristos /* These macros are defined here because they can use variables
107575f6d617Schristos defined in this function. Don't try this at home kids, we're
107675f6d617Schristos trained professionals!
107775f6d617Schristos
107875f6d617Schristos Also note that SKIPWHITE only recognizes tabs and spaces, and
107975f6d617Schristos that READNUM can only read positive, integral numbers */
108075f6d617Schristos
108175f6d617Schristos #define SKIPWHITE(s) { while (*s == ' ' || *s == '\t') s++; }
108275f6d617Schristos #define READNUM(s, num) \
108375f6d617Schristos { unsigned char c = *s; if (!ISDIGIT (c)) return ERROR; holdnum = 0; \
108475f6d617Schristos do { holdnum = (c - '0' + holdnum * 10); } \
108575f6d617Schristos while (ISDIGIT (c = *++s)); (num) = holdnum; }
108675f6d617Schristos
108775f6d617Schristos /* Read first set of digits */
108875f6d617Schristos SKIPWHITE (s);
108975f6d617Schristos READNUM (s, db->ranges[0][RANGE_START]);
109075f6d617Schristos
109175f6d617Schristos /* Was that the only digit? */
109275f6d617Schristos SKIPWHITE (s);
109375f6d617Schristos if (*s == ',')
109475f6d617Schristos {
109575f6d617Schristos /* Get the next digit */
109675f6d617Schristos s++;
109775f6d617Schristos READNUM (s, db->ranges[0][RANGE_END]);
109875f6d617Schristos }
109975f6d617Schristos else
110075f6d617Schristos db->ranges[0][RANGE_END] = db->ranges[0][RANGE_START];
110175f6d617Schristos
110275f6d617Schristos /* Get the letter */
110375f6d617Schristos SKIPWHITE (s);
110475f6d617Schristos switch (*s)
110575f6d617Schristos {
110675f6d617Schristos case 'a':
110775f6d617Schristos type = ADD;
110875f6d617Schristos break;
110975f6d617Schristos case 'c':
111075f6d617Schristos type = CHANGE;
111175f6d617Schristos break;
111275f6d617Schristos case 'd':
111375f6d617Schristos type = DELETE;
111475f6d617Schristos break;
111575f6d617Schristos default:
111675f6d617Schristos return ERROR; /* Bad format */
111775f6d617Schristos }
111875f6d617Schristos s++; /* Past letter */
111975f6d617Schristos
112075f6d617Schristos /* Read second set of digits */
112175f6d617Schristos SKIPWHITE (s);
112275f6d617Schristos READNUM (s, db->ranges[1][RANGE_START]);
112375f6d617Schristos
112475f6d617Schristos /* Was that the only digit? */
112575f6d617Schristos SKIPWHITE (s);
112675f6d617Schristos if (*s == ',')
112775f6d617Schristos {
112875f6d617Schristos /* Get the next digit */
112975f6d617Schristos s++;
113075f6d617Schristos READNUM (s, db->ranges[1][RANGE_END]);
113175f6d617Schristos SKIPWHITE (s); /* To move to end */
113275f6d617Schristos }
113375f6d617Schristos else
113475f6d617Schristos db->ranges[1][RANGE_END] = db->ranges[1][RANGE_START];
113575f6d617Schristos
113675f6d617Schristos *string = s;
113775f6d617Schristos return type;
113875f6d617Schristos }
113975f6d617Schristos
114075f6d617Schristos static char *
read_diff(char const * filea,char const * fileb,char ** output_placement)114175f6d617Schristos read_diff (char const *filea,
114275f6d617Schristos char const *fileb,
114375f6d617Schristos char **output_placement)
114475f6d617Schristos {
114575f6d617Schristos char *diff_result;
114675f6d617Schristos size_t current_chunk_size, total;
114775f6d617Schristos int fd, wstatus;
114875f6d617Schristos int werrno = 0;
114975f6d617Schristos struct stat pipestat;
115075f6d617Schristos
115175f6d617Schristos #if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
115275f6d617Schristos
115375f6d617Schristos char const *argv[8];
115475f6d617Schristos char const **ap;
115575f6d617Schristos int fds[2];
115675f6d617Schristos pid_t pid;
115775f6d617Schristos
115875f6d617Schristos ap = argv;
115975f6d617Schristos *ap++ = diff_program;
116075f6d617Schristos if (text)
116175f6d617Schristos *ap++ = "-a";
116275f6d617Schristos *ap++ = "--horizon-lines=100";
116375f6d617Schristos *ap++ = "--";
116475f6d617Schristos *ap++ = filea;
116575f6d617Schristos *ap++ = fileb;
116675f6d617Schristos *ap = 0;
116775f6d617Schristos
116875f6d617Schristos if (pipe (fds) != 0)
116975f6d617Schristos perror_with_exit ("pipe");
117075f6d617Schristos
117175f6d617Schristos pid = vfork ();
117275f6d617Schristos if (pid == 0)
117375f6d617Schristos {
117475f6d617Schristos /* Child */
117575f6d617Schristos close (fds[0]);
117675f6d617Schristos if (fds[1] != STDOUT_FILENO)
117775f6d617Schristos {
117875f6d617Schristos dup2 (fds[1], STDOUT_FILENO);
117975f6d617Schristos close (fds[1]);
118075f6d617Schristos }
118175f6d617Schristos
118275f6d617Schristos /* The cast to (char **) is needed for portability to older
118375f6d617Schristos hosts with a nonstandard prototype for execvp. */
118475f6d617Schristos execvp (diff_program, (char **) argv);
118575f6d617Schristos
118675f6d617Schristos _exit (errno == ENOEXEC ? 126 : 127);
118775f6d617Schristos }
118875f6d617Schristos
118975f6d617Schristos if (pid == -1)
119075f6d617Schristos perror_with_exit ("fork");
119175f6d617Schristos
119275f6d617Schristos close (fds[1]); /* Prevent erroneous lack of EOF */
119375f6d617Schristos fd = fds[0];
119475f6d617Schristos
119575f6d617Schristos #else
119675f6d617Schristos
119775f6d617Schristos FILE *fpipe;
119875f6d617Schristos char const args[] = " -a --horizon-lines=100 -- ";
119975f6d617Schristos char *command = xmalloc (quote_system_arg (0, diff_program)
120075f6d617Schristos + sizeof args - 1
120175f6d617Schristos + quote_system_arg (0, filea) + 1
120275f6d617Schristos + quote_system_arg (0, fileb) + 1);
120375f6d617Schristos char *p = command;
120475f6d617Schristos p += quote_system_arg (p, diff_program);
120575f6d617Schristos strcpy (p, args + (text ? 0 : 3));
120675f6d617Schristos p += strlen (p);
120775f6d617Schristos p += quote_system_arg (p, filea);
120875f6d617Schristos *p++ = ' ';
120975f6d617Schristos p += quote_system_arg (p, fileb);
121075f6d617Schristos *p = 0;
121175f6d617Schristos errno = 0;
121275f6d617Schristos fpipe = popen (command, "r");
121375f6d617Schristos if (!fpipe)
121475f6d617Schristos perror_with_exit (command);
121575f6d617Schristos free (command);
121675f6d617Schristos fd = fileno (fpipe);
121775f6d617Schristos
121875f6d617Schristos #endif
121975f6d617Schristos
122075f6d617Schristos if (fstat (fd, &pipestat) != 0)
122175f6d617Schristos perror_with_exit ("fstat");
122275f6d617Schristos current_chunk_size = MAX (1, STAT_BLOCKSIZE (pipestat));
122375f6d617Schristos diff_result = xmalloc (current_chunk_size);
122475f6d617Schristos total = 0;
122575f6d617Schristos
122675f6d617Schristos for (;;)
122775f6d617Schristos {
122875f6d617Schristos size_t bytes_to_read = current_chunk_size - total;
122975f6d617Schristos size_t bytes = block_read (fd, diff_result + total, bytes_to_read);
123075f6d617Schristos total += bytes;
123175f6d617Schristos if (bytes != bytes_to_read)
123275f6d617Schristos {
123375f6d617Schristos if (bytes == SIZE_MAX)
123475f6d617Schristos perror_with_exit (_("read failed"));
123575f6d617Schristos break;
123675f6d617Schristos }
123775f6d617Schristos if (PTRDIFF_MAX / 2 <= current_chunk_size)
123875f6d617Schristos xalloc_die ();
123975f6d617Schristos current_chunk_size *= 2;
124075f6d617Schristos diff_result = xrealloc (diff_result, current_chunk_size);
124175f6d617Schristos }
124275f6d617Schristos
124375f6d617Schristos if (total != 0 && diff_result[total-1] != '\n')
124475f6d617Schristos fatal ("invalid diff format; incomplete last line");
124575f6d617Schristos
124675f6d617Schristos *output_placement = diff_result;
124775f6d617Schristos
124875f6d617Schristos #if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
124975f6d617Schristos
125075f6d617Schristos wstatus = pclose (fpipe);
125175f6d617Schristos if (wstatus == -1)
125275f6d617Schristos werrno = errno;
125375f6d617Schristos
125475f6d617Schristos #else
125575f6d617Schristos
125675f6d617Schristos if (close (fd) != 0)
125775f6d617Schristos perror_with_exit ("close");
125875f6d617Schristos if (waitpid (pid, &wstatus, 0) < 0)
125975f6d617Schristos perror_with_exit ("waitpid");
126075f6d617Schristos
126175f6d617Schristos #endif
126275f6d617Schristos
126375f6d617Schristos if (! werrno && WIFEXITED (wstatus))
126475f6d617Schristos switch (WEXITSTATUS (wstatus))
126575f6d617Schristos {
126675f6d617Schristos case 126:
126775f6d617Schristos error (EXIT_TROUBLE, 0, _("subsidiary program `%s' not executable"),
126875f6d617Schristos diff_program);
126975f6d617Schristos case 127:
127075f6d617Schristos error (EXIT_TROUBLE, 0, _("subsidiary program `%s' not found"),
127175f6d617Schristos diff_program);
127275f6d617Schristos }
127375f6d617Schristos if (werrno || ! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2))
127475f6d617Schristos error (EXIT_TROUBLE, werrno, _("subsidiary program `%s' failed"),
127575f6d617Schristos diff_program);
127675f6d617Schristos
127775f6d617Schristos return diff_result + total;
127875f6d617Schristos }
127975f6d617Schristos
128075f6d617Schristos
128175f6d617Schristos /*
128275f6d617Schristos * Scan a regular diff line (consisting of > or <, followed by a
128375f6d617Schristos * space, followed by text (including nulls) up to a newline.
128475f6d617Schristos *
128575f6d617Schristos * This next routine began life as a macro and many parameters in it
128675f6d617Schristos * are used as call-by-reference values.
128775f6d617Schristos */
128875f6d617Schristos static char *
scan_diff_line(char * scan_ptr,char ** set_start,size_t * set_length,char * limit,char leadingchar)128975f6d617Schristos scan_diff_line (char *scan_ptr, char **set_start, size_t *set_length,
129075f6d617Schristos char *limit, char leadingchar)
129175f6d617Schristos {
129275f6d617Schristos char *line_ptr;
129375f6d617Schristos
129475f6d617Schristos if (!(scan_ptr[0] == leadingchar
129575f6d617Schristos && scan_ptr[1] == ' '))
129675f6d617Schristos fatal ("invalid diff format; incorrect leading line chars");
129775f6d617Schristos
129875f6d617Schristos *set_start = line_ptr = scan_ptr + 2;
129975f6d617Schristos while (*line_ptr++ != '\n')
130075f6d617Schristos continue;
130175f6d617Schristos
130275f6d617Schristos /* Include newline if the original line ended in a newline,
130375f6d617Schristos or if an edit script is being generated.
130475f6d617Schristos Copy any missing newline message to stderr if an edit script is being
130575f6d617Schristos generated, because edit scripts cannot handle missing newlines.
130675f6d617Schristos Return the beginning of the next line. */
130775f6d617Schristos *set_length = line_ptr - *set_start;
130875f6d617Schristos if (line_ptr < limit && *line_ptr == '\\')
130975f6d617Schristos {
131075f6d617Schristos if (edscript)
131175f6d617Schristos fprintf (stderr, "%s:", program_name);
131275f6d617Schristos else
131375f6d617Schristos --*set_length;
131475f6d617Schristos line_ptr++;
131575f6d617Schristos do
131675f6d617Schristos {
131775f6d617Schristos if (edscript)
131875f6d617Schristos putc (*line_ptr, stderr);
131975f6d617Schristos }
132075f6d617Schristos while (*line_ptr++ != '\n');
132175f6d617Schristos }
132275f6d617Schristos
132375f6d617Schristos return line_ptr;
132475f6d617Schristos }
132575f6d617Schristos
132675f6d617Schristos /*
132775f6d617Schristos * This routine outputs a three way diff passed as a list of
132875f6d617Schristos * diff3_block's.
132975f6d617Schristos * The argument MAPPING is indexed by external file number (in the
133075f6d617Schristos * argument list) and contains the internal file number (from the
133175f6d617Schristos * diff passed). This is important because the user expects his
133275f6d617Schristos * outputs in terms of the argument list number, and the diff passed
133375f6d617Schristos * may have been done slightly differently (if the last argument
133475f6d617Schristos * was "-", for example).
133575f6d617Schristos * REV_MAPPING is the inverse of MAPPING.
133675f6d617Schristos */
133775f6d617Schristos static void
output_diff3(FILE * outputfile,struct diff3_block * diff,int const mapping[3],int const rev_mapping[3])133875f6d617Schristos output_diff3 (FILE *outputfile, struct diff3_block *diff,
133975f6d617Schristos int const mapping[3], int const rev_mapping[3])
134075f6d617Schristos {
134175f6d617Schristos int i;
134275f6d617Schristos int oddoneout;
134375f6d617Schristos char *cp;
134475f6d617Schristos struct diff3_block *ptr;
134575f6d617Schristos lin line;
134675f6d617Schristos size_t length;
134775f6d617Schristos int dontprint;
134875f6d617Schristos static int skew_increment[3] = { 2, 3, 1 }; /* 0==>2==>1==>3 */
134975f6d617Schristos char const *line_prefix = initial_tab ? "\t" : " ";
135075f6d617Schristos
135175f6d617Schristos for (ptr = diff; ptr; ptr = D_NEXT (ptr))
135275f6d617Schristos {
135375f6d617Schristos char x[2];
135475f6d617Schristos
135575f6d617Schristos switch (ptr->correspond)
135675f6d617Schristos {
135775f6d617Schristos case DIFF_ALL:
135875f6d617Schristos x[0] = 0;
135975f6d617Schristos dontprint = 3; /* Print them all */
136075f6d617Schristos oddoneout = 3; /* Nobody's odder than anyone else */
136175f6d617Schristos break;
136275f6d617Schristos case DIFF_1ST:
136375f6d617Schristos case DIFF_2ND:
136475f6d617Schristos case DIFF_3RD:
136575f6d617Schristos oddoneout = rev_mapping[ptr->correspond - DIFF_1ST];
136675f6d617Schristos
136775f6d617Schristos x[0] = oddoneout + '1';
136875f6d617Schristos x[1] = 0;
136975f6d617Schristos dontprint = oddoneout == 0;
137075f6d617Schristos break;
137175f6d617Schristos default:
137275f6d617Schristos fatal ("internal error: invalid diff type passed to output");
137375f6d617Schristos }
137475f6d617Schristos fprintf (outputfile, "====%s\n", x);
137575f6d617Schristos
137675f6d617Schristos /* Go 0, 2, 1 if the first and third outputs are equivalent. */
137775f6d617Schristos for (i = 0; i < 3;
137875f6d617Schristos i = (oddoneout == 1 ? skew_increment[i] : i + 1))
137975f6d617Schristos {
138075f6d617Schristos int realfile = mapping[i];
138175f6d617Schristos lin lowt = D_LOWLINE (ptr, realfile);
138275f6d617Schristos lin hight = D_HIGHLINE (ptr, realfile);
138375f6d617Schristos long llowt = lowt;
138475f6d617Schristos long lhight = hight;
138575f6d617Schristos
138675f6d617Schristos fprintf (outputfile, "%d:", i + 1);
138775f6d617Schristos switch (lowt - hight)
138875f6d617Schristos {
138975f6d617Schristos case 1:
139075f6d617Schristos fprintf (outputfile, "%lda\n", llowt - 1);
139175f6d617Schristos break;
139275f6d617Schristos case 0:
139375f6d617Schristos fprintf (outputfile, "%ldc\n", llowt);
139475f6d617Schristos break;
139575f6d617Schristos default:
139675f6d617Schristos fprintf (outputfile, "%ld,%ldc\n", llowt, lhight);
139775f6d617Schristos break;
139875f6d617Schristos }
139975f6d617Schristos
140075f6d617Schristos if (i == dontprint) continue;
140175f6d617Schristos
140275f6d617Schristos if (lowt <= hight)
140375f6d617Schristos {
140475f6d617Schristos line = 0;
140575f6d617Schristos do
140675f6d617Schristos {
1407*f8c23a2bSchristos fprintf (outputfile, "%s", line_prefix);
140875f6d617Schristos cp = D_RELNUM (ptr, realfile, line);
140975f6d617Schristos length = D_RELLEN (ptr, realfile, line);
141075f6d617Schristos fwrite (cp, sizeof (char), length, outputfile);
141175f6d617Schristos }
141275f6d617Schristos while (++line < hight - lowt + 1);
141375f6d617Schristos if (cp[length - 1] != '\n')
141475f6d617Schristos fprintf (outputfile, "\n\\ %s\n",
141575f6d617Schristos _("No newline at end of file"));
141675f6d617Schristos }
141775f6d617Schristos }
141875f6d617Schristos }
141975f6d617Schristos }
142075f6d617Schristos
142175f6d617Schristos
142275f6d617Schristos /*
142375f6d617Schristos * Output to OUTPUTFILE the lines of B taken from FILENUM.
142475f6d617Schristos * Double any initial '.'s; yield nonzero if any initial '.'s were doubled.
142575f6d617Schristos */
142675f6d617Schristos static bool
dotlines(FILE * outputfile,struct diff3_block * b,int filenum)142775f6d617Schristos dotlines (FILE *outputfile, struct diff3_block *b, int filenum)
142875f6d617Schristos {
142975f6d617Schristos lin i;
143075f6d617Schristos bool leading_dot = 0;
143175f6d617Schristos
143275f6d617Schristos for (i = 0;
143375f6d617Schristos i < D_NUMLINES (b, filenum);
143475f6d617Schristos i++)
143575f6d617Schristos {
143675f6d617Schristos char *line = D_RELNUM (b, filenum, i);
143775f6d617Schristos if (line[0] == '.')
143875f6d617Schristos {
143975f6d617Schristos leading_dot = 1;
144075f6d617Schristos fprintf (outputfile, ".");
144175f6d617Schristos }
144275f6d617Schristos fwrite (line, sizeof (char),
144375f6d617Schristos D_RELLEN (b, filenum, i), outputfile);
144475f6d617Schristos }
144575f6d617Schristos
144675f6d617Schristos return leading_dot;
144775f6d617Schristos }
144875f6d617Schristos
144975f6d617Schristos /*
145075f6d617Schristos * Output to OUTPUTFILE a '.' line. If LEADING_DOT is nonzero,
145175f6d617Schristos * also output a command that removes initial '.'s
145275f6d617Schristos * starting with line START and continuing for NUM lines.
145375f6d617Schristos * (START is long, not lin, for convenience with printf %ld formats.)
145475f6d617Schristos */
145575f6d617Schristos static void
undotlines(FILE * outputfile,bool leading_dot,long start,lin num)145675f6d617Schristos undotlines (FILE *outputfile, bool leading_dot, long start, lin num)
145775f6d617Schristos {
145875f6d617Schristos fprintf (outputfile, ".\n");
145975f6d617Schristos if (leading_dot)
146075f6d617Schristos {
146175f6d617Schristos if (num == 1)
146275f6d617Schristos fprintf (outputfile, "%lds/^\\.//\n", start);
146375f6d617Schristos else
146475f6d617Schristos fprintf (outputfile, "%ld,%lds/^\\.//\n", start, start + num - 1);
146575f6d617Schristos }
146675f6d617Schristos }
146775f6d617Schristos
146875f6d617Schristos /*
146975f6d617Schristos * This routine outputs a diff3 set of blocks as an ed script. This
147075f6d617Schristos * script applies the changes between file's 2 & 3 to file 1. It
147175f6d617Schristos * takes the precise format of the ed script to be output from global
147275f6d617Schristos * variables set during options processing. Note that it does
147375f6d617Schristos * destructive things to the set of diff3 blocks it is passed; it
147475f6d617Schristos * reverses their order (this gets around the problems involved with
147575f6d617Schristos * changing line numbers in an ed script).
147675f6d617Schristos *
147775f6d617Schristos * Note that this routine has the same problem of mapping as the last
147875f6d617Schristos * one did; the variable MAPPING maps from file number according to
147975f6d617Schristos * the argument list to file number according to the diff passed. All
148075f6d617Schristos * files listed below are in terms of the argument list.
148175f6d617Schristos * REV_MAPPING is the inverse of MAPPING.
148275f6d617Schristos *
148375f6d617Schristos * The arguments FILE0, FILE1 and FILE2 are the strings to print
148475f6d617Schristos * as the names of the three files. These may be the actual names,
148575f6d617Schristos * or may be the arguments specified with -L.
148675f6d617Schristos *
148775f6d617Schristos * Returns 1 if conflicts were found.
148875f6d617Schristos */
148975f6d617Schristos
149075f6d617Schristos 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)149175f6d617Schristos output_diff3_edscript (FILE *outputfile, struct diff3_block *diff,
149275f6d617Schristos int const mapping[3], int const rev_mapping[3],
149375f6d617Schristos char const *file0, char const *file1, char const *file2)
149475f6d617Schristos {
149575f6d617Schristos bool leading_dot;
149675f6d617Schristos bool conflicts_found = 0, conflict;
149775f6d617Schristos struct diff3_block *b;
149875f6d617Schristos
149975f6d617Schristos for (b = reverse_diff3_blocklist (diff); b; b = b->next)
150075f6d617Schristos {
150175f6d617Schristos /* Must do mapping correctly. */
150275f6d617Schristos enum diff_type type
150375f6d617Schristos = (b->correspond == DIFF_ALL
150475f6d617Schristos ? DIFF_ALL
150575f6d617Schristos : DIFF_1ST + rev_mapping[b->correspond - DIFF_1ST]);
150675f6d617Schristos
150775f6d617Schristos long low0, high0;
150875f6d617Schristos
150975f6d617Schristos /* If we aren't supposed to do this output block, skip it. */
151075f6d617Schristos switch (type)
151175f6d617Schristos {
151275f6d617Schristos default: continue;
151375f6d617Schristos case DIFF_2ND: if (!show_2nd) continue; conflict = 1; break;
151475f6d617Schristos case DIFF_3RD: if (overlap_only) continue; conflict = 0; break;
151575f6d617Schristos case DIFF_ALL: if (simple_only) continue; conflict = flagging; break;
151675f6d617Schristos }
151775f6d617Schristos
151875f6d617Schristos low0 = D_LOWLINE (b, mapping[FILE0]);
151975f6d617Schristos high0 = D_HIGHLINE (b, mapping[FILE0]);
152075f6d617Schristos
152175f6d617Schristos if (conflict)
152275f6d617Schristos {
152375f6d617Schristos conflicts_found = 1;
152475f6d617Schristos
152575f6d617Schristos
152675f6d617Schristos /* Mark end of conflict. */
152775f6d617Schristos
152875f6d617Schristos fprintf (outputfile, "%lda\n", high0);
152975f6d617Schristos leading_dot = 0;
153075f6d617Schristos if (type == DIFF_ALL)
153175f6d617Schristos {
153275f6d617Schristos if (show_2nd)
153375f6d617Schristos {
153475f6d617Schristos /* Append lines from FILE1. */
153575f6d617Schristos fprintf (outputfile, "||||||| %s\n", file1);
153675f6d617Schristos leading_dot = dotlines (outputfile, b, mapping[FILE1]);
153775f6d617Schristos }
153875f6d617Schristos /* Append lines from FILE2. */
153975f6d617Schristos fprintf (outputfile, "=======\n");
154075f6d617Schristos leading_dot |= dotlines (outputfile, b, mapping[FILE2]);
154175f6d617Schristos }
154275f6d617Schristos fprintf (outputfile, ">>>>>>> %s\n", file2);
154375f6d617Schristos undotlines (outputfile, leading_dot, high0 + 2,
154475f6d617Schristos (D_NUMLINES (b, mapping[FILE1])
154575f6d617Schristos + D_NUMLINES (b, mapping[FILE2]) + 1));
154675f6d617Schristos
154775f6d617Schristos
154875f6d617Schristos /* Mark start of conflict. */
154975f6d617Schristos
155075f6d617Schristos fprintf (outputfile, "%lda\n<<<<<<< %s\n", low0 - 1,
155175f6d617Schristos type == DIFF_ALL ? file0 : file1);
155275f6d617Schristos leading_dot = 0;
155375f6d617Schristos if (type == DIFF_2ND)
155475f6d617Schristos {
155575f6d617Schristos /* Prepend lines from FILE1. */
155675f6d617Schristos leading_dot = dotlines (outputfile, b, mapping[FILE1]);
155775f6d617Schristos fprintf (outputfile, "=======\n");
155875f6d617Schristos }
155975f6d617Schristos undotlines (outputfile, leading_dot, low0 + 1,
156075f6d617Schristos D_NUMLINES (b, mapping[FILE1]));
156175f6d617Schristos }
156275f6d617Schristos else if (D_NUMLINES (b, mapping[FILE2]) == 0)
156375f6d617Schristos /* Write out a delete */
156475f6d617Schristos {
156575f6d617Schristos if (low0 == high0)
156675f6d617Schristos fprintf (outputfile, "%ldd\n", low0);
156775f6d617Schristos else
156875f6d617Schristos fprintf (outputfile, "%ld,%ldd\n", low0, high0);
156975f6d617Schristos }
157075f6d617Schristos else
157175f6d617Schristos /* Write out an add or change */
157275f6d617Schristos {
157375f6d617Schristos switch (high0 - low0)
157475f6d617Schristos {
157575f6d617Schristos case -1:
157675f6d617Schristos fprintf (outputfile, "%lda\n", high0);
157775f6d617Schristos break;
157875f6d617Schristos case 0:
157975f6d617Schristos fprintf (outputfile, "%ldc\n", high0);
158075f6d617Schristos break;
158175f6d617Schristos default:
158275f6d617Schristos fprintf (outputfile, "%ld,%ldc\n", low0, high0);
158375f6d617Schristos break;
158475f6d617Schristos }
158575f6d617Schristos
158675f6d617Schristos undotlines (outputfile, dotlines (outputfile, b, mapping[FILE2]),
158775f6d617Schristos low0, D_NUMLINES (b, mapping[FILE2]));
158875f6d617Schristos }
158975f6d617Schristos }
159075f6d617Schristos if (finalwrite) fprintf (outputfile, "w\nq\n");
159175f6d617Schristos return conflicts_found;
159275f6d617Schristos }
159375f6d617Schristos
159475f6d617Schristos /*
159575f6d617Schristos * Read from INFILE and output to OUTPUTFILE a set of diff3_ blocks DIFF
159675f6d617Schristos * as a merged file. This acts like 'ed file0 <[output_diff3_edscript]',
159775f6d617Schristos * except that it works even for binary data or incomplete lines.
159875f6d617Schristos *
159975f6d617Schristos * As before, MAPPING maps from arg list file number to diff file number,
160075f6d617Schristos * REV_MAPPING is its inverse,
160175f6d617Schristos * and FILE0, FILE1, and FILE2 are the names of the files.
160275f6d617Schristos *
160375f6d617Schristos * Returns 1 if conflicts were found.
160475f6d617Schristos */
160575f6d617Schristos
160675f6d617Schristos 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)160775f6d617Schristos output_diff3_merge (FILE *infile, FILE *outputfile, struct diff3_block *diff,
160875f6d617Schristos int const mapping[3], int const rev_mapping[3],
160975f6d617Schristos char const *file0, char const *file1, char const *file2)
161075f6d617Schristos {
161175f6d617Schristos int c;
161275f6d617Schristos lin i;
161375f6d617Schristos bool conflicts_found = 0, conflict;
161475f6d617Schristos struct diff3_block *b;
161575f6d617Schristos lin linesread = 0;
161675f6d617Schristos
161775f6d617Schristos for (b = diff; b; b = b->next)
161875f6d617Schristos {
161975f6d617Schristos /* Must do mapping correctly. */
162075f6d617Schristos enum diff_type type
162175f6d617Schristos = ((b->correspond == DIFF_ALL)
162275f6d617Schristos ? DIFF_ALL
162375f6d617Schristos : DIFF_1ST + rev_mapping[b->correspond - DIFF_1ST]);
162475f6d617Schristos char const *format_2nd = "<<<<<<< %s\n";
162575f6d617Schristos
162675f6d617Schristos /* If we aren't supposed to do this output block, skip it. */
162775f6d617Schristos switch (type)
162875f6d617Schristos {
162975f6d617Schristos default: continue;
163075f6d617Schristos case DIFF_2ND: if (!show_2nd) continue; conflict = 1; break;
163175f6d617Schristos case DIFF_3RD: if (overlap_only) continue; conflict = 0; break;
163275f6d617Schristos case DIFF_ALL: if (simple_only) continue; conflict = flagging;
163375f6d617Schristos format_2nd = "||||||| %s\n";
163475f6d617Schristos break;
163575f6d617Schristos }
163675f6d617Schristos
163775f6d617Schristos /* Copy I lines from file 0. */
163875f6d617Schristos i = D_LOWLINE (b, FILE0) - linesread - 1;
163975f6d617Schristos linesread += i;
164075f6d617Schristos while (0 <= --i)
164175f6d617Schristos do
164275f6d617Schristos {
164375f6d617Schristos c = getc (infile);
164475f6d617Schristos if (c == EOF)
164575f6d617Schristos {
164675f6d617Schristos if (ferror (infile))
164775f6d617Schristos perror_with_exit (_("read failed"));
164875f6d617Schristos else if (feof (infile))
164975f6d617Schristos fatal ("input file shrank");
165075f6d617Schristos }
165175f6d617Schristos putc (c, outputfile);
165275f6d617Schristos }
165375f6d617Schristos while (c != '\n');
165475f6d617Schristos
165575f6d617Schristos if (conflict)
165675f6d617Schristos {
165775f6d617Schristos conflicts_found = 1;
165875f6d617Schristos
165975f6d617Schristos if (type == DIFF_ALL)
166075f6d617Schristos {
166175f6d617Schristos /* Put in lines from FILE0 with bracket. */
166275f6d617Schristos fprintf (outputfile, "<<<<<<< %s\n", file0);
166375f6d617Schristos for (i = 0;
166475f6d617Schristos i < D_NUMLINES (b, mapping[FILE0]);
166575f6d617Schristos i++)
166675f6d617Schristos fwrite (D_RELNUM (b, mapping[FILE0], i), sizeof (char),
166775f6d617Schristos D_RELLEN (b, mapping[FILE0], i), outputfile);
166875f6d617Schristos }
166975f6d617Schristos
167075f6d617Schristos if (show_2nd)
167175f6d617Schristos {
167275f6d617Schristos /* Put in lines from FILE1 with bracket. */
167375f6d617Schristos fprintf (outputfile, format_2nd, file1);
167475f6d617Schristos for (i = 0;
167575f6d617Schristos i < D_NUMLINES (b, mapping[FILE1]);
167675f6d617Schristos i++)
167775f6d617Schristos fwrite (D_RELNUM (b, mapping[FILE1], i), sizeof (char),
167875f6d617Schristos D_RELLEN (b, mapping[FILE1], i), outputfile);
167975f6d617Schristos }
168075f6d617Schristos
168175f6d617Schristos fprintf (outputfile, "=======\n");
168275f6d617Schristos }
168375f6d617Schristos
168475f6d617Schristos /* Put in lines from FILE2. */
168575f6d617Schristos for (i = 0;
168675f6d617Schristos i < D_NUMLINES (b, mapping[FILE2]);
168775f6d617Schristos i++)
168875f6d617Schristos fwrite (D_RELNUM (b, mapping[FILE2], i), sizeof (char),
168975f6d617Schristos D_RELLEN (b, mapping[FILE2], i), outputfile);
169075f6d617Schristos
169175f6d617Schristos if (conflict)
169275f6d617Schristos fprintf (outputfile, ">>>>>>> %s\n", file2);
169375f6d617Schristos
169475f6d617Schristos /* Skip I lines in file 0. */
169575f6d617Schristos i = D_NUMLINES (b, FILE0);
169675f6d617Schristos linesread += i;
169775f6d617Schristos while (0 <= --i)
169875f6d617Schristos while ((c = getc (infile)) != '\n')
169975f6d617Schristos if (c == EOF)
170075f6d617Schristos {
170175f6d617Schristos if (ferror (infile))
170275f6d617Schristos perror_with_exit (_("read failed"));
170375f6d617Schristos else if (feof (infile))
170475f6d617Schristos {
170575f6d617Schristos if (i || b->next)
170675f6d617Schristos fatal ("input file shrank");
170775f6d617Schristos return conflicts_found;
170875f6d617Schristos }
170975f6d617Schristos }
171075f6d617Schristos }
171175f6d617Schristos /* Copy rest of common file. */
171275f6d617Schristos while ((c = getc (infile)) != EOF || !(ferror (infile) | feof (infile)))
171375f6d617Schristos putc (c, outputfile);
171475f6d617Schristos return conflicts_found;
171575f6d617Schristos }
171675f6d617Schristos
171775f6d617Schristos /*
171875f6d617Schristos * Reverse the order of the list of diff3 blocks.
171975f6d617Schristos */
172075f6d617Schristos static struct diff3_block *
reverse_diff3_blocklist(struct diff3_block * diff)172175f6d617Schristos reverse_diff3_blocklist (struct diff3_block *diff)
172275f6d617Schristos {
172375f6d617Schristos register struct diff3_block *tmp, *next, *prev;
172475f6d617Schristos
172575f6d617Schristos for (tmp = diff, prev = 0; tmp; tmp = next)
172675f6d617Schristos {
172775f6d617Schristos next = tmp->next;
172875f6d617Schristos tmp->next = prev;
172975f6d617Schristos prev = tmp;
173075f6d617Schristos }
173175f6d617Schristos
173275f6d617Schristos return prev;
173375f6d617Schristos }
173475f6d617Schristos
173575f6d617Schristos static void
fatal(char const * msgid)173675f6d617Schristos fatal (char const *msgid)
173775f6d617Schristos {
173875f6d617Schristos error (EXIT_TROUBLE, 0, "%s", _(msgid));
173975f6d617Schristos abort ();
174075f6d617Schristos }
174175f6d617Schristos
174275f6d617Schristos static void
perror_with_exit(char const * string)174375f6d617Schristos perror_with_exit (char const *string)
174475f6d617Schristos {
174575f6d617Schristos error (EXIT_TROUBLE, errno, "%s", string);
174675f6d617Schristos abort ();
174775f6d617Schristos }
1748