12286d8edStholo /* Three way file comparison program (diff3) for Project GNU.
2b2346922Stholo Copyright (C) 1988, 1989, 1992, 1993, 1994, 1997, 1998 Free Software Foundation, Inc.
32286d8edStholo
42286d8edStholo This program is free software; you can redistribute it and/or modify
52286d8edStholo it under the terms of the GNU General Public License as published by
62286d8edStholo the Free Software Foundation; either version 2, or (at your option)
72286d8edStholo any later version.
82286d8edStholo
92286d8edStholo This program is distributed in the hope that it will be useful,
102286d8edStholo but WITHOUT ANY WARRANTY; without even the implied warranty of
112286d8edStholo MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
122286d8edStholo GNU General Public License for more details.
132286d8edStholo
14c71bc7e2Stholo */
152286d8edStholo
162286d8edStholo /* Written by Randy Smith */
172286d8edStholo /* Librarification by Tim Pierce */
182286d8edStholo
192286d8edStholo #include "system.h"
202286d8edStholo #include <stdio.h>
212286d8edStholo #include <setjmp.h>
222286d8edStholo #include "getopt.h"
23b2346922Stholo #include "diffrun.h"
242286d8edStholo
252286d8edStholo /* diff3.c has a real initialize_main function. */
262286d8edStholo #ifdef initialize_main
272286d8edStholo #undef initialize_main
282286d8edStholo #endif
292286d8edStholo
302286d8edStholo extern char const diff_version_string[];
312286d8edStholo
32b2346922Stholo extern FILE *outfile;
33b2346922Stholo
34b2346922Stholo extern const struct diff_callbacks *callbacks;
35b2346922Stholo
36b2346922Stholo void write_output PARAMS((char const *, size_t));
37b2346922Stholo void printf_output PARAMS((char const *, ...))
38b2346922Stholo #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 6)
39b2346922Stholo __attribute__ ((__format__ (__printf__, 1, 2)))
40b2346922Stholo #endif
41b2346922Stholo ;
42b2346922Stholo void flush_output PARAMS((void));
43b2346922Stholo
44e77048c1Stholo char * cvs_temp_name PARAMS((void));
45e77048c1Stholo
462286d8edStholo /*
472286d8edStholo * Internal data structures and macros for the diff3 program; includes
482286d8edStholo * data structures for both diff3 diffs and normal diffs.
492286d8edStholo */
502286d8edStholo
512286d8edStholo /* Different files within a three way diff. */
522286d8edStholo #define FILE0 0
532286d8edStholo #define FILE1 1
542286d8edStholo #define FILE2 2
552286d8edStholo
562286d8edStholo /*
572286d8edStholo * A three way diff is built from two two-way diffs; the file which
582286d8edStholo * the two two-way diffs share is:
592286d8edStholo */
602286d8edStholo #define FILEC FILE2
612286d8edStholo
622286d8edStholo /*
632286d8edStholo * Different files within a two way diff.
642286d8edStholo * FC is the common file, FO the other file.
652286d8edStholo */
662286d8edStholo #define FO 0
672286d8edStholo #define FC 1
682286d8edStholo
692286d8edStholo /* The ranges are indexed by */
702286d8edStholo #define START 0
712286d8edStholo #define END 1
722286d8edStholo
732286d8edStholo enum diff_type {
742286d8edStholo ERROR, /* Should not be used */
752286d8edStholo ADD, /* Two way diff add */
762286d8edStholo CHANGE, /* Two way diff change */
772286d8edStholo DELETE, /* Two way diff delete */
782286d8edStholo DIFF_ALL, /* All three are different */
792286d8edStholo DIFF_1ST, /* Only the first is different */
802286d8edStholo DIFF_2ND, /* Only the second */
812286d8edStholo DIFF_3RD /* Only the third */
822286d8edStholo };
832286d8edStholo
842286d8edStholo /* Two way diff */
852286d8edStholo struct diff_block {
862286d8edStholo int ranges[2][2]; /* Ranges are inclusive */
872286d8edStholo char **lines[2]; /* The actual lines (may contain nulls) */
882286d8edStholo size_t *lengths[2]; /* Line lengths (including newlines, if any) */
892286d8edStholo struct diff_block *next;
902286d8edStholo };
912286d8edStholo
922286d8edStholo /* Three way diff */
932286d8edStholo
942286d8edStholo struct diff3_block {
952286d8edStholo enum diff_type correspond; /* Type of diff */
962286d8edStholo int ranges[3][2]; /* Ranges are inclusive */
972286d8edStholo char **lines[3]; /* The actual lines (may contain nulls) */
982286d8edStholo size_t *lengths[3]; /* Line lengths (including newlines, if any) */
992286d8edStholo struct diff3_block *next;
1002286d8edStholo };
1012286d8edStholo
1022286d8edStholo /*
1032286d8edStholo * Access the ranges on a diff block.
1042286d8edStholo */
1052286d8edStholo #define D_LOWLINE(diff, filenum) \
1062286d8edStholo ((diff)->ranges[filenum][START])
1072286d8edStholo #define D_HIGHLINE(diff, filenum) \
1082286d8edStholo ((diff)->ranges[filenum][END])
1092286d8edStholo #define D_NUMLINES(diff, filenum) \
1102286d8edStholo (D_HIGHLINE (diff, filenum) - D_LOWLINE (diff, filenum) + 1)
1112286d8edStholo
1122286d8edStholo /*
1132286d8edStholo * Access the line numbers in a file in a diff by relative line
1142286d8edStholo * numbers (i.e. line number within the diff itself). Note that these
1152286d8edStholo * are lvalues and can be used for assignment.
1162286d8edStholo */
1172286d8edStholo #define D_RELNUM(diff, filenum, linenum) \
1182286d8edStholo ((diff)->lines[filenum][linenum])
1192286d8edStholo #define D_RELLEN(diff, filenum, linenum) \
1202286d8edStholo ((diff)->lengths[filenum][linenum])
1212286d8edStholo
1222286d8edStholo /*
1232286d8edStholo * And get at them directly, when that should be necessary.
1242286d8edStholo */
1252286d8edStholo #define D_LINEARRAY(diff, filenum) \
1262286d8edStholo ((diff)->lines[filenum])
1272286d8edStholo #define D_LENARRAY(diff, filenum) \
1282286d8edStholo ((diff)->lengths[filenum])
1292286d8edStholo
1302286d8edStholo /*
1312286d8edStholo * Next block.
1322286d8edStholo */
1332286d8edStholo #define D_NEXT(diff) ((diff)->next)
1342286d8edStholo
1352286d8edStholo /*
1362286d8edStholo * Access the type of a diff3 block.
1372286d8edStholo */
1382286d8edStholo #define D3_TYPE(diff) ((diff)->correspond)
1392286d8edStholo
1402286d8edStholo /*
1412286d8edStholo * Line mappings based on diffs. The first maps off the top of the
1422286d8edStholo * diff, the second off of the bottom.
1432286d8edStholo */
1442286d8edStholo #define D_HIGH_MAPLINE(diff, fromfile, tofile, lineno) \
1452286d8edStholo ((lineno) \
1462286d8edStholo - D_HIGHLINE ((diff), (fromfile)) \
1472286d8edStholo + D_HIGHLINE ((diff), (tofile)))
1482286d8edStholo
1492286d8edStholo #define D_LOW_MAPLINE(diff, fromfile, tofile, lineno) \
1502286d8edStholo ((lineno) \
1512286d8edStholo - D_LOWLINE ((diff), (fromfile)) \
1522286d8edStholo + D_LOWLINE ((diff), (tofile)))
1532286d8edStholo
1542286d8edStholo /*
1552286d8edStholo * General memory allocation function.
1562286d8edStholo */
1572286d8edStholo #define ALLOCATE(number, type) \
1582286d8edStholo (type *) xmalloc ((number) * sizeof (type))
1592286d8edStholo
1602286d8edStholo /* Options variables for flags set on command line. */
1612286d8edStholo
1622286d8edStholo /* If nonzero, treat all files as text files, never as binary. */
1632286d8edStholo static int always_text;
1642286d8edStholo
1652286d8edStholo /* If nonzero, write out an ed script instead of the standard diff3 format. */
1662286d8edStholo static int edscript;
1672286d8edStholo
1682286d8edStholo /* If nonzero, in the case of overlapping diffs (type DIFF_ALL),
1692286d8edStholo preserve the lines which would normally be deleted from
1702286d8edStholo file 1 with a special flagging mechanism. */
1712286d8edStholo static int flagging;
1722286d8edStholo
1732286d8edStholo /* Number of lines to keep in identical prefix and suffix. */
17443c1707eStholo static int const horizon_lines = 10;
1752286d8edStholo
1762286d8edStholo /* Use a tab to align output lines (-T). */
1772286d8edStholo static int tab_align_flag;
1782286d8edStholo
1792286d8edStholo /* If nonzero, do not output information for overlapping diffs. */
1802286d8edStholo static int simple_only;
1812286d8edStholo
1822286d8edStholo /* If nonzero, do not output information for non-overlapping diffs. */
1832286d8edStholo static int overlap_only;
1842286d8edStholo
1852286d8edStholo /* If nonzero, show information for DIFF_2ND diffs. */
1862286d8edStholo static int show_2nd;
1872286d8edStholo
1882286d8edStholo /* If nonzero, include `:wq' at the end of the script
1892286d8edStholo to write out the file being edited. */
1902286d8edStholo static int finalwrite;
1912286d8edStholo
1922286d8edStholo /* If nonzero, output a merged file. */
1932286d8edStholo static int merge;
1942286d8edStholo
1952286d8edStholo extern char *diff_program_name;
1962286d8edStholo
1972286d8edStholo static char *read_diff PARAMS((char const *, char const *, char **));
1982286d8edStholo static char *scan_diff_line PARAMS((char *, char **, size_t *, char *, int));
1992286d8edStholo static enum diff_type process_diff_control PARAMS((char **, struct diff_block *));
2002286d8edStholo static int compare_line_list PARAMS((char * const[], size_t const[], char * const[], size_t const[], int));
2012286d8edStholo static int copy_stringlist PARAMS((char * const[], size_t const[], char *[], size_t[], int));
202b2346922Stholo static int dotlines PARAMS((struct diff3_block *, int));
203b2346922Stholo static int output_diff3_edscript PARAMS((struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *));
204b2346922Stholo static int output_diff3_merge PARAMS((FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *));
2052286d8edStholo static size_t myread PARAMS((int, char *, size_t));
2062286d8edStholo static struct diff3_block *create_diff3_block PARAMS((int, int, int, int, int, int));
2072286d8edStholo static struct diff3_block *make_3way_diff PARAMS((struct diff_block *, struct diff_block *));
2082286d8edStholo static struct diff3_block *reverse_diff3_blocklist PARAMS((struct diff3_block *));
2092286d8edStholo static struct diff3_block *using_to_diff3_block PARAMS((struct diff_block *[2], struct diff_block *[2], int, int, struct diff3_block const *));
2102286d8edStholo static struct diff_block *process_diff PARAMS((char const *, char const *, struct diff_block **, char **));
2112286d8edStholo static void check_output PARAMS((FILE *));
2122286d8edStholo static void diff3_fatal PARAMS((char const *));
213b2346922Stholo static void output_diff3 PARAMS((struct diff3_block *, int const[3], int const[3]));
2142286d8edStholo static void diff3_perror_with_exit PARAMS((char const *));
2152286d8edStholo static int try_help PARAMS((char const *));
216b2346922Stholo static void undotlines PARAMS((int, int, int));
2172286d8edStholo static void usage PARAMS((void));
2182286d8edStholo static void initialize_main PARAMS((int *, char ***));
2192286d8edStholo static void free_diff_blocks PARAMS((struct diff_block *));
2202286d8edStholo static void free_diff3_blocks PARAMS((struct diff3_block *));
2212286d8edStholo
2222286d8edStholo /* Functions provided in libdiff.a or other external sources. */
2232286d8edStholo VOID *xmalloc PARAMS((size_t));
2242286d8edStholo VOID *xrealloc PARAMS((VOID *, size_t));
2252286d8edStholo void perror_with_name PARAMS((char const *));
2262286d8edStholo void diff_error PARAMS((char const *, char const *, char const *));
2272286d8edStholo
2282286d8edStholo /* Permit non-local exits from diff3. */
2292286d8edStholo static jmp_buf diff3_abort_buf;
2302286d8edStholo #define DIFF3_ABORT(retval) longjmp(diff3_abort_buf, retval)
2312286d8edStholo
2322286d8edStholo static struct option const longopts[] =
2332286d8edStholo {
2342286d8edStholo {"text", 0, 0, 'a'},
2352286d8edStholo {"show-all", 0, 0, 'A'},
2362286d8edStholo {"ed", 0, 0, 'e'},
2372286d8edStholo {"show-overlap", 0, 0, 'E'},
2382286d8edStholo {"label", 1, 0, 'L'},
2392286d8edStholo {"merge", 0, 0, 'm'},
2402286d8edStholo {"initial-tab", 0, 0, 'T'},
2412286d8edStholo {"overlap-only", 0, 0, 'x'},
2422286d8edStholo {"easy-only", 0, 0, '3'},
2432286d8edStholo {"version", 0, 0, 'v'},
2442286d8edStholo {"help", 0, 0, 129},
2452286d8edStholo {0, 0, 0, 0}
2462286d8edStholo };
2472286d8edStholo
2482286d8edStholo /*
2492286d8edStholo * Main program. Calls diff twice on two pairs of input files,
2502286d8edStholo * combines the two diffs, and outputs them.
2512286d8edStholo */
2522286d8edStholo int
diff3_run(argc,argv,out,callbacks_arg)253b2346922Stholo diff3_run (argc, argv, out, callbacks_arg)
2542286d8edStholo int argc;
2552286d8edStholo char **argv;
256b2346922Stholo char *out;
257b2346922Stholo const struct diff_callbacks *callbacks_arg;
2582286d8edStholo {
2592286d8edStholo int c, i;
2602286d8edStholo int mapping[3];
2612286d8edStholo int rev_mapping[3];
2622286d8edStholo int incompat = 0;
2632286d8edStholo int conflicts_found;
2642286d8edStholo int status;
2652286d8edStholo struct diff_block *thread0, *thread1, *last_block;
2662286d8edStholo char *content0, *content1;
2672286d8edStholo struct diff3_block *diff3;
2682286d8edStholo int tag_count = 0;
2692286d8edStholo char *tag_strings[3];
2702286d8edStholo char *commonname;
2712286d8edStholo char **file;
2722286d8edStholo struct stat statb;
2732286d8edStholo int optind_old;
274b2346922Stholo int opened_file = 0;
275b2346922Stholo
276b2346922Stholo callbacks = callbacks_arg;
2772286d8edStholo
2782286d8edStholo initialize_main (&argc, &argv);
2792286d8edStholo
2802286d8edStholo optind_old = optind;
2812286d8edStholo optind = 0;
2822286d8edStholo while ((c = getopt_long (argc, argv, "aeimvx3AEL:TX", longopts, 0)) != EOF)
2832286d8edStholo {
2842286d8edStholo switch (c)
2852286d8edStholo {
2862286d8edStholo case 'a':
2872286d8edStholo always_text = 1;
2882286d8edStholo break;
2892286d8edStholo case 'A':
2902286d8edStholo show_2nd = 1;
2912286d8edStholo flagging = 1;
2922286d8edStholo incompat++;
2932286d8edStholo break;
2942286d8edStholo case 'x':
2952286d8edStholo overlap_only = 1;
2962286d8edStholo incompat++;
2972286d8edStholo break;
2982286d8edStholo case '3':
2992286d8edStholo simple_only = 1;
3002286d8edStholo incompat++;
3012286d8edStholo break;
3022286d8edStholo case 'i':
3032286d8edStholo finalwrite = 1;
3042286d8edStholo break;
3052286d8edStholo case 'm':
3062286d8edStholo merge = 1;
3072286d8edStholo break;
3082286d8edStholo case 'X':
3092286d8edStholo overlap_only = 1;
3102286d8edStholo /* Falls through */
3112286d8edStholo case 'E':
3122286d8edStholo flagging = 1;
3132286d8edStholo /* Falls through */
3142286d8edStholo case 'e':
3152286d8edStholo incompat++;
3162286d8edStholo break;
3172286d8edStholo case 'T':
3182286d8edStholo tab_align_flag = 1;
3192286d8edStholo break;
3202286d8edStholo case 'v':
321b2346922Stholo if (callbacks && callbacks->write_stdout)
322b2346922Stholo {
323b2346922Stholo (*callbacks->write_stdout) ("diff3 - GNU diffutils version ");
324b2346922Stholo (*callbacks->write_stdout) (diff_version_string);
325b2346922Stholo (*callbacks->write_stdout) ("\n");
326b2346922Stholo }
327b2346922Stholo else
3282286d8edStholo printf ("diff3 - GNU diffutils version %s\n", diff_version_string);
3292286d8edStholo return 0;
3302286d8edStholo case 129:
3312286d8edStholo usage ();
332b2346922Stholo if (! callbacks || ! callbacks->write_stdout)
3332286d8edStholo check_output (stdout);
3342286d8edStholo return 0;
3352286d8edStholo case 'L':
3362286d8edStholo /* Handle up to three -L options. */
3372286d8edStholo if (tag_count < 3)
3382286d8edStholo {
3392286d8edStholo tag_strings[tag_count++] = optarg;
3402286d8edStholo break;
3412286d8edStholo }
3422286d8edStholo return try_help ("Too many labels were given. The limit is 3.");
3432286d8edStholo default:
3442286d8edStholo return try_help (0);
3452286d8edStholo }
3462286d8edStholo }
3472286d8edStholo
3482286d8edStholo edscript = incompat & ~merge; /* -AeExX3 without -m implies ed script. */
3492286d8edStholo show_2nd |= ~incompat & merge; /* -m without -AeExX3 implies -A. */
3502286d8edStholo flagging |= ~incompat & merge;
3512286d8edStholo
3522286d8edStholo if (incompat > 1 /* Ensure at most one of -AeExX3. */
3532286d8edStholo || finalwrite & merge /* -i -m would rewrite input file. */
3542286d8edStholo || (tag_count && ! flagging)) /* -L requires one of -AEX. */
3552286d8edStholo return try_help ("incompatible options");
3562286d8edStholo
3572286d8edStholo if (argc - optind != 3)
3582286d8edStholo return try_help (argc - optind < 3 ? "missing operand" : "extra operand");
3592286d8edStholo
3602286d8edStholo file = &argv[optind];
3612286d8edStholo
3622286d8edStholo optind = optind_old;
3632286d8edStholo
3642286d8edStholo for (i = tag_count; i < 3; i++)
3652286d8edStholo tag_strings[i] = file[i];
3662286d8edStholo
3672286d8edStholo /* Always compare file1 to file2, even if file2 is "-".
3682286d8edStholo This is needed for -mAeExX3. Using the file0 as
3692286d8edStholo the common file would produce wrong results, because if the
3702286d8edStholo file0-file1 diffs didn't line up with the file0-file2 diffs
3712286d8edStholo (which is entirely possible since we don't use diff's -n option),
3722286d8edStholo diff3 might report phantom changes from file1 to file2. */
37343c1707eStholo /* Also try to compare file0 to file1 because this is the where
37443c1707eStholo changes are expected to come from. Diffing between these pairs
37543c1707eStholo of files is is most likely to return the intended changes. There
37643c1707eStholo can also be the same problem with phantom changes from file0 to
37743c1707eStholo file1. */
37843c1707eStholo /* Historically, the default common file was file2. Ediff for emacs
37943c1707eStholo and possibly other applications, have therefore made file2 the
38043c1707eStholo ancestor. So, for compatibility, if this is simply a three
38143c1707eStholo way diff (not a merge or edscript) then use the old way with
38243c1707eStholo file2 as the common file. */
3832286d8edStholo
3842286d8edStholo {
38543c1707eStholo int common;
38643c1707eStholo if (edscript || merge )
38743c1707eStholo {
38843c1707eStholo common = 1;
38943c1707eStholo }
39043c1707eStholo else
39143c1707eStholo {
39243c1707eStholo common = 2;
39343c1707eStholo }
39443c1707eStholo if (strcmp (file[common], "-") == 0)
39543c1707eStholo {
39643c1707eStholo /* Sigh. We've got standard input as the arg corresponding to
39743c1707eStholo the desired common file. We can't call diff twice on
39843c1707eStholo stdin. Use another arg as the common file instead. */
39943c1707eStholo common = 3 - common;
40043c1707eStholo if (strcmp (file[0], "-") == 0 || strcmp (file[common], "-") == 0)
4012286d8edStholo {
4022286d8edStholo diff_error ("%s", "`-' specified for more than one input file", 0);
4032286d8edStholo return 2;
4042286d8edStholo }
4052286d8edStholo }
40643c1707eStholo
4072286d8edStholo mapping[0] = 0;
40843c1707eStholo mapping[1] = 3 - common;
40943c1707eStholo mapping[2] = common;
4102286d8edStholo }
4112286d8edStholo
4122286d8edStholo for (i = 0; i < 3; i++)
4132286d8edStholo rev_mapping[mapping[i]] = i;
4142286d8edStholo
4152286d8edStholo for (i = 0; i < 3; i++)
4162286d8edStholo if (strcmp (file[i], "-") != 0)
4172286d8edStholo {
4182286d8edStholo if (stat (file[i], &statb) < 0)
4192286d8edStholo {
4202286d8edStholo perror_with_name (file[i]);
4212286d8edStholo return 2;
4222286d8edStholo }
4232286d8edStholo else if (S_ISDIR(statb.st_mode))
4242286d8edStholo {
425b2346922Stholo diff_error ("%s: Is a directory", file[i], 0);
4262286d8edStholo return 2;
4272286d8edStholo }
4282286d8edStholo }
4292286d8edStholo
430b2346922Stholo if (callbacks && callbacks->write_output)
431b2346922Stholo {
432b2346922Stholo if (out != NULL)
433b2346922Stholo {
434b2346922Stholo diff_error ("write callback with output file", 0, 0);
435b2346922Stholo return 2;
436b2346922Stholo }
437b2346922Stholo }
4382286d8edStholo else
4392286d8edStholo {
440b2346922Stholo if (out == NULL)
441b2346922Stholo outfile = stdout;
442b2346922Stholo else
443b2346922Stholo {
444b2346922Stholo outfile = fopen (out, "w");
445b2346922Stholo if (outfile == NULL)
4462286d8edStholo {
4472286d8edStholo perror_with_name ("could not open output file");
4482286d8edStholo return 2;
4492286d8edStholo }
450b2346922Stholo opened_file = 1;
451b2346922Stholo }
4522286d8edStholo }
4532286d8edStholo
4542286d8edStholo /* Set the jump buffer, so that diff may abort execution without
4552286d8edStholo terminating the process. */
4562286d8edStholo status = setjmp (diff3_abort_buf);
4572286d8edStholo if (status != 0)
4582286d8edStholo return status;
4592286d8edStholo
4602286d8edStholo commonname = file[rev_mapping[FILEC]];
4612286d8edStholo thread1 = process_diff (file[rev_mapping[FILE1]], commonname, &last_block,
4622286d8edStholo &content1);
46343c1707eStholo /* What is the intention behind determining horizon_lines from first
46443c1707eStholo diff? I think it is better to use the same parameters for each
46543c1707eStholo diff so that equal differences in each diff will appear the
46643c1707eStholo same. */
46743c1707eStholo /*
4682286d8edStholo if (thread1)
4692286d8edStholo for (i = 0; i < 2; i++)
4702286d8edStholo {
4712286d8edStholo horizon_lines = max (horizon_lines, D_NUMLINES (thread1, i));
4722286d8edStholo horizon_lines = max (horizon_lines, D_NUMLINES (last_block, i));
4732286d8edStholo }
47443c1707eStholo */
4752286d8edStholo thread0 = process_diff (file[rev_mapping[FILE0]], commonname, &last_block,
4762286d8edStholo &content0);
4772286d8edStholo diff3 = make_3way_diff (thread0, thread1);
4782286d8edStholo if (edscript)
4792286d8edStholo conflicts_found
480b2346922Stholo = output_diff3_edscript (diff3, mapping, rev_mapping,
4812286d8edStholo tag_strings[0], tag_strings[1], tag_strings[2]);
4822286d8edStholo else if (merge)
4832286d8edStholo {
484892c0aadStholo FILE *mfp = fopen (file[rev_mapping[FILE0]], "r");
485892c0aadStholo if (! mfp)
4862286d8edStholo diff3_perror_with_exit (file[rev_mapping[FILE0]]);
487892c0aadStholo conflicts_found = output_diff3_merge (mfp, diff3, mapping, rev_mapping,
4882286d8edStholo tag_strings[0], tag_strings[1], tag_strings[2]);
489892c0aadStholo if (ferror (mfp))
4902286d8edStholo diff3_fatal ("read error");
491892c0aadStholo if (fclose(mfp) != 0)
492892c0aadStholo perror_with_name (file[rev_mapping[FILE0]]);
4932286d8edStholo }
4942286d8edStholo else
4952286d8edStholo {
496b2346922Stholo output_diff3 (diff3, mapping, rev_mapping);
4972286d8edStholo conflicts_found = 0;
4982286d8edStholo }
4992286d8edStholo
5002286d8edStholo free(content0);
5012286d8edStholo free(content1);
5022286d8edStholo free_diff3_blocks(diff3);
5032286d8edStholo
504b2346922Stholo if (! callbacks || ! callbacks->write_output)
505b2346922Stholo check_output (outfile);
506b2346922Stholo
507b2346922Stholo if (opened_file)
508b2346922Stholo if (fclose (outfile) != 0)
509b2346922Stholo perror_with_name ("close error on output file");
510b2346922Stholo
5112286d8edStholo return conflicts_found;
5122286d8edStholo }
5132286d8edStholo
5142286d8edStholo static int
try_help(reason)5152286d8edStholo try_help (reason)
5162286d8edStholo char const *reason;
5172286d8edStholo {
5182286d8edStholo if (reason)
519b2346922Stholo diff_error ("%s", reason, 0);
520b2346922Stholo diff_error ("Try `%s --help' for more information.", diff_program_name, 0);
5212286d8edStholo return 2;
5222286d8edStholo }
5232286d8edStholo
5242286d8edStholo static void
check_output(stream)5252286d8edStholo check_output (stream)
5262286d8edStholo FILE *stream;
5272286d8edStholo {
5282286d8edStholo if (ferror (stream) || fflush (stream) != 0)
5292286d8edStholo diff3_fatal ("write error");
5302286d8edStholo }
5312286d8edStholo
5322286d8edStholo /*
5332286d8edStholo * Explain, patiently and kindly, how to use this program.
5342286d8edStholo */
5352286d8edStholo static void
usage()5362286d8edStholo usage ()
5372286d8edStholo {
538b2346922Stholo if (callbacks && callbacks->write_stdout)
539b2346922Stholo {
540b2346922Stholo (*callbacks->write_stdout) ("Usage: ");
541b2346922Stholo (*callbacks->write_stdout) (diff_program_name);
542b2346922Stholo (*callbacks->write_stdout) (" [OPTION]... MYFILE OLDFILE YOURFILE\n\n");
543b2346922Stholo
544b2346922Stholo (*callbacks->write_stdout) ("\
545b2346922Stholo -e --ed Output unmerged changes from OLDFILE to YOURFILE into MYFILE.\n\
546b2346922Stholo -E --show-overlap Output unmerged changes, bracketing conflicts.\n\
547b2346922Stholo -A --show-all Output all changes, bracketing conflicts.\n\
548b2346922Stholo -x --overlap-only Output overlapping changes.\n\
549b2346922Stholo -X Output overlapping changes, bracketing them.\n\
550b2346922Stholo -3 --easy-only Output unmerged nonoverlapping changes.\n\n");
551b2346922Stholo (*callbacks->write_stdout) ("\
552b2346922Stholo -m --merge Output merged file instead of ed script (default -A).\n\
553b2346922Stholo -L LABEL --label=LABEL Use LABEL instead of file name.\n\
554b2346922Stholo -i Append `w' and `q' commands to ed scripts.\n\
555b2346922Stholo -a --text Treat all files as text.\n\
556b2346922Stholo -T --initial-tab Make tabs line up by prepending a tab.\n\n");
557b2346922Stholo (*callbacks->write_stdout) ("\
558b2346922Stholo -v --version Output version info.\n\
559b2346922Stholo --help Output this help.\n\n");
560b2346922Stholo (*callbacks->write_stdout) ("If a FILE is `-', read standard input.\n");
561b2346922Stholo }
562b2346922Stholo else
563b2346922Stholo {
5642286d8edStholo printf ("Usage: %s [OPTION]... MYFILE OLDFILE YOURFILE\n\n", diff_program_name);
5652286d8edStholo
5662286d8edStholo printf ("%s", "\
5672286d8edStholo -e --ed Output unmerged changes from OLDFILE to YOURFILE into MYFILE.\n\
5682286d8edStholo -E --show-overlap Output unmerged changes, bracketing conflicts.\n\
5692286d8edStholo -A --show-all Output all changes, bracketing conflicts.\n\
5702286d8edStholo -x --overlap-only Output overlapping changes.\n\
5712286d8edStholo -X Output overlapping changes, bracketing them.\n\
5722286d8edStholo -3 --easy-only Output unmerged nonoverlapping changes.\n\n");
5732286d8edStholo printf ("%s", "\
5742286d8edStholo -m --merge Output merged file instead of ed script (default -A).\n\
5752286d8edStholo -L LABEL --label=LABEL Use LABEL instead of file name.\n\
5762286d8edStholo -i Append `w' and `q' commands to ed scripts.\n\
5772286d8edStholo -a --text Treat all files as text.\n\
5782286d8edStholo -T --initial-tab Make tabs line up by prepending a tab.\n\n");
5792286d8edStholo printf ("%s", "\
5802286d8edStholo -v --version Output version info.\n\
5812286d8edStholo --help Output this help.\n\n");
5822286d8edStholo printf ("If a FILE is `-', read standard input.\n");
5832286d8edStholo }
584b2346922Stholo }
5852286d8edStholo
5862286d8edStholo /*
5872286d8edStholo * Routines that combine the two diffs together into one. The
5882286d8edStholo * algorithm used follows:
5892286d8edStholo *
5902286d8edStholo * File2 is shared in common between the two diffs.
5912286d8edStholo * Diff02 is the diff between 0 and 2.
5922286d8edStholo * Diff12 is the diff between 1 and 2.
5932286d8edStholo *
5942286d8edStholo * 1) Find the range for the first block in File2.
5952286d8edStholo * a) Take the lowest of the two ranges (in File2) in the two
5962286d8edStholo * current blocks (one from each diff) as being the low
5972286d8edStholo * water mark. Assign the upper end of this block as
5982286d8edStholo * being the high water mark and move the current block up
5992286d8edStholo * one. Mark the block just moved over as to be used.
6002286d8edStholo * b) Check the next block in the diff that the high water
6012286d8edStholo * mark is *not* from.
6022286d8edStholo *
6032286d8edStholo * *If* the high water mark is above
6042286d8edStholo * the low end of the range in that block,
6052286d8edStholo *
6062286d8edStholo * mark that block as to be used and move the current
6072286d8edStholo * block up. Set the high water mark to the max of
6082286d8edStholo * the high end of this block and the current. Repeat b.
6092286d8edStholo *
6102286d8edStholo * 2) Find the corresponding ranges in File0 (from the blocks
6112286d8edStholo * in diff02; line per line outside of diffs) and in File1.
6122286d8edStholo * Create a diff3_block, reserving space as indicated by the ranges.
6132286d8edStholo *
6142286d8edStholo * 3) Copy all of the pointers for file2 in. At least for now,
6152286d8edStholo * do memcmp's between corresponding strings in the two diffs.
6162286d8edStholo *
6172286d8edStholo * 4) Copy all of the pointers for file0 and 1 in. Get what you
6182286d8edStholo * need from file2 (when there isn't a diff block, it's
6192286d8edStholo * identical to file2 within the range between diff blocks).
6202286d8edStholo *
6212286d8edStholo * 5) If the diff blocks you used came from only one of the two
6222286d8edStholo * strings of diffs, then that file (i.e. the one other than
6232286d8edStholo * the common file in that diff) is the odd person out. If you used
6242286d8edStholo * diff blocks from both sets, check to see if files 0 and 1 match:
6252286d8edStholo *
6262286d8edStholo * Same number of lines? If so, do a set of memcmp's (if a
6272286d8edStholo * memcmp matches; copy the pointer over; it'll be easier later
6282286d8edStholo * if you have to do any compares). If they match, 0 & 1 are
6292286d8edStholo * the same. If not, all three different.
6302286d8edStholo *
6312286d8edStholo * Then you do it again, until you run out of blocks.
6322286d8edStholo *
6332286d8edStholo */
6342286d8edStholo
6352286d8edStholo /*
6362286d8edStholo * This routine makes a three way diff (chain of diff3_block's) from two
6372286d8edStholo * two way diffs (chains of diff_block's). It is assumed that each of
6382286d8edStholo * the two diffs passed are onto the same file (i.e. that each of the
6392286d8edStholo * diffs were made "to" the same file). The three way diff pointer
6402286d8edStholo * returned will have numbering FILE0--the other file in diff02,
6412286d8edStholo * FILE1--the other file in diff12, and FILEC--the common file.
6422286d8edStholo */
6432286d8edStholo static struct diff3_block *
make_3way_diff(thread0,thread1)6442286d8edStholo make_3way_diff (thread0, thread1)
6452286d8edStholo struct diff_block *thread0, *thread1;
6462286d8edStholo {
6472286d8edStholo /*
6482286d8edStholo * This routine works on the two diffs passed to it as threads.
6492286d8edStholo * Thread number 0 is diff02, thread number 1 is diff12. The USING
6502286d8edStholo * array is set to the base of the list of blocks to be used to
6512286d8edStholo * construct each block of the three way diff; if no blocks from a
6522286d8edStholo * particular thread are to be used, that element of the using array
6532286d8edStholo * is set to 0. The elements LAST_USING array are set to the last
6542286d8edStholo * elements on each of the using lists.
6552286d8edStholo *
6562286d8edStholo * The HIGH_WATER_MARK is set to the highest line number in the common file
6572286d8edStholo * described in any of the diffs in either of the USING lists. The
6582286d8edStholo * HIGH_WATER_THREAD names the thread. Similarly the BASE_WATER_MARK
6592286d8edStholo * and BASE_WATER_THREAD describe the lowest line number in the common file
6602286d8edStholo * described in any of the diffs in either of the USING lists. The
6612286d8edStholo * HIGH_WATER_DIFF is the diff from which the HIGH_WATER_MARK was
6622286d8edStholo * taken.
6632286d8edStholo *
6642286d8edStholo * The HIGH_WATER_DIFF should always be equal to LAST_USING
6652286d8edStholo * [HIGH_WATER_THREAD]. The OTHER_DIFF is the next diff to check for
6662286d8edStholo * higher water, and should always be equal to
6672286d8edStholo * CURRENT[HIGH_WATER_THREAD ^ 0x1]. The OTHER_THREAD is the thread
6682286d8edStholo * in which the OTHER_DIFF is, and hence should always be equal to
6692286d8edStholo * HIGH_WATER_THREAD ^ 0x1.
6702286d8edStholo *
6712286d8edStholo * The variable LAST_DIFF is kept set to the last diff block produced
6722286d8edStholo * by this routine, for line correspondence purposes between that diff
6732286d8edStholo * and the one currently being worked on. It is initialized to
6742286d8edStholo * ZERO_DIFF before any blocks have been created.
6752286d8edStholo */
6762286d8edStholo
6772286d8edStholo struct diff_block
6782286d8edStholo *using[2],
6792286d8edStholo *last_using[2],
6802286d8edStholo *current[2];
6812286d8edStholo
6822286d8edStholo int
6832286d8edStholo high_water_mark;
6842286d8edStholo
6852286d8edStholo int
6862286d8edStholo high_water_thread,
6872286d8edStholo base_water_thread,
6882286d8edStholo other_thread;
6892286d8edStholo
6902286d8edStholo struct diff_block
6912286d8edStholo *high_water_diff,
6922286d8edStholo *other_diff;
6932286d8edStholo
6942286d8edStholo struct diff3_block
6952286d8edStholo *result,
6962286d8edStholo *tmpblock,
6972286d8edStholo **result_end;
6982286d8edStholo
6992286d8edStholo struct diff3_block const *last_diff3;
7002286d8edStholo
701e77048c1Stholo static struct diff3_block const zero_diff3 = { 0 };
7022286d8edStholo
7032286d8edStholo /* Initialization */
7042286d8edStholo result = 0;
7052286d8edStholo result_end = &result;
7062286d8edStholo current[0] = thread0; current[1] = thread1;
7072286d8edStholo last_diff3 = &zero_diff3;
7082286d8edStholo
7092286d8edStholo /* Sniff up the threads until we reach the end */
7102286d8edStholo
7112286d8edStholo while (current[0] || current[1])
7122286d8edStholo {
7132286d8edStholo using[0] = using[1] = last_using[0] = last_using[1] = 0;
7142286d8edStholo
7152286d8edStholo /* Setup low and high water threads, diffs, and marks. */
7162286d8edStholo if (!current[0])
7172286d8edStholo base_water_thread = 1;
7182286d8edStholo else if (!current[1])
7192286d8edStholo base_water_thread = 0;
7202286d8edStholo else
7212286d8edStholo base_water_thread =
7222286d8edStholo (D_LOWLINE (current[0], FC) > D_LOWLINE (current[1], FC));
7232286d8edStholo
7242286d8edStholo high_water_thread = base_water_thread;
7252286d8edStholo
7262286d8edStholo high_water_diff = current[high_water_thread];
7272286d8edStholo
7282286d8edStholo #if 0
7292286d8edStholo /* low and high waters start off same diff */
7302286d8edStholo base_water_mark = D_LOWLINE (high_water_diff, FC);
7312286d8edStholo #endif
7322286d8edStholo
7332286d8edStholo high_water_mark = D_HIGHLINE (high_water_diff, FC);
7342286d8edStholo
7352286d8edStholo /* Make the diff you just got info from into the using class */
7362286d8edStholo using[high_water_thread]
7372286d8edStholo = last_using[high_water_thread]
7382286d8edStholo = high_water_diff;
7392286d8edStholo current[high_water_thread] = high_water_diff->next;
7402286d8edStholo last_using[high_water_thread]->next = 0;
7412286d8edStholo
7422286d8edStholo /* And mark the other diff */
7432286d8edStholo other_thread = high_water_thread ^ 0x1;
7442286d8edStholo other_diff = current[other_thread];
7452286d8edStholo
7462286d8edStholo /* Shuffle up the ladder, checking the other diff to see if it
7472286d8edStholo needs to be incorporated. */
7482286d8edStholo while (other_diff
7492286d8edStholo && D_LOWLINE (other_diff, FC) <= high_water_mark + 1)
7502286d8edStholo {
7512286d8edStholo
7522286d8edStholo /* Incorporate this diff into the using list. Note that
7532286d8edStholo this doesn't take it off the current list */
7542286d8edStholo if (using[other_thread])
7552286d8edStholo last_using[other_thread]->next = other_diff;
7562286d8edStholo else
7572286d8edStholo using[other_thread] = other_diff;
7582286d8edStholo last_using[other_thread] = other_diff;
7592286d8edStholo
7602286d8edStholo /* Take it off the current list. Note that this following
7612286d8edStholo code assumes that other_diff enters it equal to
7622286d8edStholo current[high_water_thread ^ 0x1] */
7632286d8edStholo current[other_thread] = current[other_thread]->next;
7642286d8edStholo other_diff->next = 0;
7652286d8edStholo
7662286d8edStholo /* Set the high_water stuff
7672286d8edStholo If this comparison is equal, then this is the last pass
7682286d8edStholo through this loop; since diff blocks within a given
7692286d8edStholo thread cannot overlap, the high_water_mark will be
7702286d8edStholo *below* the range_start of either of the next diffs. */
7712286d8edStholo
7722286d8edStholo if (high_water_mark < D_HIGHLINE (other_diff, FC))
7732286d8edStholo {
7742286d8edStholo high_water_thread ^= 1;
7752286d8edStholo high_water_diff = other_diff;
7762286d8edStholo high_water_mark = D_HIGHLINE (other_diff, FC);
7772286d8edStholo }
7782286d8edStholo
7792286d8edStholo /* Set the other diff */
7802286d8edStholo other_thread = high_water_thread ^ 0x1;
7812286d8edStholo other_diff = current[other_thread];
7822286d8edStholo }
7832286d8edStholo
7842286d8edStholo /* The using lists contain a list of all of the blocks to be
7852286d8edStholo included in this diff3_block. Create it. */
7862286d8edStholo
7872286d8edStholo tmpblock = using_to_diff3_block (using, last_using,
7882286d8edStholo base_water_thread, high_water_thread,
7892286d8edStholo last_diff3);
790e77048c1Stholo free_diff_blocks(using[0]);
791e77048c1Stholo free_diff_blocks(using[1]);
7922286d8edStholo
7932286d8edStholo if (!tmpblock)
7942286d8edStholo diff3_fatal ("internal error: screwup in format of diff blocks");
7952286d8edStholo
7962286d8edStholo /* Put it on the list. */
7972286d8edStholo *result_end = tmpblock;
7982286d8edStholo result_end = &tmpblock->next;
7992286d8edStholo
8002286d8edStholo /* Set up corresponding lines correctly. */
8012286d8edStholo last_diff3 = tmpblock;
8022286d8edStholo }
8032286d8edStholo return result;
8042286d8edStholo }
8052286d8edStholo
8062286d8edStholo /*
8072286d8edStholo * using_to_diff3_block:
8082286d8edStholo * This routine takes two lists of blocks (from two separate diff
8092286d8edStholo * threads) and puts them together into one diff3 block.
8102286d8edStholo * It then returns a pointer to this diff3 block or 0 for failure.
8112286d8edStholo *
8122286d8edStholo * All arguments besides using are for the convenience of the routine;
8132286d8edStholo * they could be derived from the using array.
8142286d8edStholo * LAST_USING is a pair of pointers to the last blocks in the using
8152286d8edStholo * structure.
8162286d8edStholo * LOW_THREAD and HIGH_THREAD tell which threads contain the lowest
8172286d8edStholo * and highest line numbers for File0.
8182286d8edStholo * last_diff3 contains the last diff produced in the calling routine.
8192286d8edStholo * This is used for lines mappings which would still be identical to
8202286d8edStholo * the state that diff ended in.
8212286d8edStholo *
8222286d8edStholo * A distinction should be made in this routine between the two diffs
8232286d8edStholo * that are part of a normal two diff block, and the three diffs that
8242286d8edStholo * are part of a diff3_block.
8252286d8edStholo */
8262286d8edStholo static struct diff3_block *
using_to_diff3_block(using,last_using,low_thread,high_thread,last_diff3)8272286d8edStholo using_to_diff3_block (using, last_using, low_thread, high_thread, last_diff3)
8282286d8edStholo struct diff_block
8292286d8edStholo *using[2],
8302286d8edStholo *last_using[2];
8312286d8edStholo int low_thread, high_thread;
8322286d8edStholo struct diff3_block const *last_diff3;
8332286d8edStholo {
8342286d8edStholo int low[2], high[2];
8352286d8edStholo struct diff3_block *result;
8362286d8edStholo struct diff_block *ptr;
8372286d8edStholo int d, i;
8382286d8edStholo
8392286d8edStholo /* Find the range in the common file. */
8402286d8edStholo int lowc = D_LOWLINE (using[low_thread], FC);
8412286d8edStholo int highc = D_HIGHLINE (last_using[high_thread], FC);
8422286d8edStholo
8432286d8edStholo /* Find the ranges in the other files.
8442286d8edStholo If using[d] is null, that means that the file to which that diff
8452286d8edStholo refers is equivalent to the common file over this range. */
8462286d8edStholo
8472286d8edStholo for (d = 0; d < 2; d++)
8482286d8edStholo if (using[d])
8492286d8edStholo {
8502286d8edStholo low[d] = D_LOW_MAPLINE (using[d], FC, FO, lowc);
8512286d8edStholo high[d] = D_HIGH_MAPLINE (last_using[d], FC, FO, highc);
8522286d8edStholo }
8532286d8edStholo else
8542286d8edStholo {
8552286d8edStholo low[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, lowc);
8562286d8edStholo high[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, highc);
8572286d8edStholo }
8582286d8edStholo
8592286d8edStholo /* Create a block with the appropriate sizes */
8602286d8edStholo result = create_diff3_block (low[0], high[0], low[1], high[1], lowc, highc);
8612286d8edStholo
8622286d8edStholo /* Copy information for the common file.
8632286d8edStholo Return with a zero if any of the compares failed. */
8642286d8edStholo
8652286d8edStholo for (d = 0; d < 2; d++)
8662286d8edStholo for (ptr = using[d]; ptr; ptr = D_NEXT (ptr))
8672286d8edStholo {
8682286d8edStholo int result_offset = D_LOWLINE (ptr, FC) - lowc;
8692286d8edStholo
8702286d8edStholo if (!copy_stringlist (D_LINEARRAY (ptr, FC),
8712286d8edStholo D_LENARRAY (ptr, FC),
8722286d8edStholo D_LINEARRAY (result, FILEC) + result_offset,
8732286d8edStholo D_LENARRAY (result, FILEC) + result_offset,
8742286d8edStholo D_NUMLINES (ptr, FC)))
8752286d8edStholo return 0;
8762286d8edStholo }
8772286d8edStholo
8782286d8edStholo /* Copy information for file d. First deal with anything that might be
8792286d8edStholo before the first diff. */
8802286d8edStholo
8812286d8edStholo for (d = 0; d < 2; d++)
8822286d8edStholo {
8832286d8edStholo struct diff_block *u = using[d];
8842286d8edStholo int lo = low[d], hi = high[d];
8852286d8edStholo
8862286d8edStholo for (i = 0;
8872286d8edStholo i + lo < (u ? D_LOWLINE (u, FO) : hi + 1);
8882286d8edStholo i++)
8892286d8edStholo {
8902286d8edStholo D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, i);
8912286d8edStholo D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, i);
8922286d8edStholo }
8932286d8edStholo
8942286d8edStholo for (ptr = u; ptr; ptr = D_NEXT (ptr))
8952286d8edStholo {
8962286d8edStholo int result_offset = D_LOWLINE (ptr, FO) - lo;
8972286d8edStholo int linec;
8982286d8edStholo
8992286d8edStholo if (!copy_stringlist (D_LINEARRAY (ptr, FO),
9002286d8edStholo D_LENARRAY (ptr, FO),
9012286d8edStholo D_LINEARRAY (result, FILE0 + d) + result_offset,
9022286d8edStholo D_LENARRAY (result, FILE0 + d) + result_offset,
9032286d8edStholo D_NUMLINES (ptr, FO)))
9042286d8edStholo return 0;
9052286d8edStholo
9062286d8edStholo /* Catch the lines between here and the next diff */
9072286d8edStholo linec = D_HIGHLINE (ptr, FC) + 1 - lowc;
9082286d8edStholo for (i = D_HIGHLINE (ptr, FO) + 1 - lo;
9092286d8edStholo i < (D_NEXT (ptr) ? D_LOWLINE (D_NEXT (ptr), FO) : hi + 1) - lo;
9102286d8edStholo i++)
9112286d8edStholo {
9122286d8edStholo D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, linec);
9132286d8edStholo D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, linec);
9142286d8edStholo linec++;
9152286d8edStholo }
9162286d8edStholo }
9172286d8edStholo }
9182286d8edStholo
9192286d8edStholo /* Set correspond */
9202286d8edStholo if (!using[0])
9212286d8edStholo D3_TYPE (result) = DIFF_2ND;
9222286d8edStholo else if (!using[1])
9232286d8edStholo D3_TYPE (result) = DIFF_1ST;
9242286d8edStholo else
9252286d8edStholo {
9262286d8edStholo int nl0 = D_NUMLINES (result, FILE0);
9272286d8edStholo int nl1 = D_NUMLINES (result, FILE1);
9282286d8edStholo
9292286d8edStholo if (nl0 != nl1
9302286d8edStholo || !compare_line_list (D_LINEARRAY (result, FILE0),
9312286d8edStholo D_LENARRAY (result, FILE0),
9322286d8edStholo D_LINEARRAY (result, FILE1),
9332286d8edStholo D_LENARRAY (result, FILE1),
9342286d8edStholo nl0))
9352286d8edStholo D3_TYPE (result) = DIFF_ALL;
9362286d8edStholo else
9372286d8edStholo D3_TYPE (result) = DIFF_3RD;
9382286d8edStholo }
9392286d8edStholo
9402286d8edStholo return result;
9412286d8edStholo }
9422286d8edStholo
9432286d8edStholo /*
9442286d8edStholo * This routine copies pointers from a list of strings to a different list
9452286d8edStholo * of strings. If a spot in the second list is already filled, it
9462286d8edStholo * makes sure that it is filled with the same string; if not it
9472286d8edStholo * returns 0, the copy incomplete.
9482286d8edStholo * Upon successful completion of the copy, it returns 1.
9492286d8edStholo */
9502286d8edStholo static int
copy_stringlist(fromptrs,fromlengths,toptrs,tolengths,copynum)9512286d8edStholo copy_stringlist (fromptrs, fromlengths, toptrs, tolengths, copynum)
9522286d8edStholo char * const fromptrs[];
9532286d8edStholo char *toptrs[];
9542286d8edStholo size_t const fromlengths[];
9552286d8edStholo size_t tolengths[];
9562286d8edStholo int copynum;
9572286d8edStholo {
9582286d8edStholo register char * const *f = fromptrs;
9592286d8edStholo register char **t = toptrs;
9602286d8edStholo register size_t const *fl = fromlengths;
9612286d8edStholo register size_t *tl = tolengths;
9622286d8edStholo
9632286d8edStholo while (copynum--)
9642286d8edStholo {
9652286d8edStholo if (*t)
9662286d8edStholo { if (*fl != *tl || memcmp (*f, *t, *fl)) return 0; }
9672286d8edStholo else
9682286d8edStholo { *t = *f ; *tl = *fl; }
9692286d8edStholo
9702286d8edStholo t++; f++; tl++; fl++;
9712286d8edStholo }
9722286d8edStholo return 1;
9732286d8edStholo }
9742286d8edStholo
9752286d8edStholo /*
9762286d8edStholo * Create a diff3_block, with ranges as specified in the arguments.
9772286d8edStholo * Allocate the arrays for the various pointers (and zero them) based
9782286d8edStholo * on the arguments passed. Return the block as a result.
9792286d8edStholo */
9802286d8edStholo static struct diff3_block *
create_diff3_block(low0,high0,low1,high1,low2,high2)9812286d8edStholo create_diff3_block (low0, high0, low1, high1, low2, high2)
9822286d8edStholo register int low0, high0, low1, high1, low2, high2;
9832286d8edStholo {
9842286d8edStholo struct diff3_block *result = ALLOCATE (1, struct diff3_block);
9852286d8edStholo int numlines;
9862286d8edStholo
9872286d8edStholo D3_TYPE (result) = ERROR;
9882286d8edStholo D_NEXT (result) = 0;
9892286d8edStholo
9902286d8edStholo /* Assign ranges */
9912286d8edStholo D_LOWLINE (result, FILE0) = low0;
9922286d8edStholo D_HIGHLINE (result, FILE0) = high0;
9932286d8edStholo D_LOWLINE (result, FILE1) = low1;
9942286d8edStholo D_HIGHLINE (result, FILE1) = high1;
9952286d8edStholo D_LOWLINE (result, FILE2) = low2;
9962286d8edStholo D_HIGHLINE (result, FILE2) = high2;
9972286d8edStholo
9982286d8edStholo /* Allocate and zero space */
9992286d8edStholo numlines = D_NUMLINES (result, FILE0);
10002286d8edStholo if (numlines)
10012286d8edStholo {
10022286d8edStholo D_LINEARRAY (result, FILE0) = ALLOCATE (numlines, char *);
10032286d8edStholo D_LENARRAY (result, FILE0) = ALLOCATE (numlines, size_t);
10042286d8edStholo bzero (D_LINEARRAY (result, FILE0), (numlines * sizeof (char *)));
10052286d8edStholo bzero (D_LENARRAY (result, FILE0), (numlines * sizeof (size_t)));
10062286d8edStholo }
10072286d8edStholo else
10082286d8edStholo {
10092286d8edStholo D_LINEARRAY (result, FILE0) = 0;
10102286d8edStholo D_LENARRAY (result, FILE0) = 0;
10112286d8edStholo }
10122286d8edStholo
10132286d8edStholo numlines = D_NUMLINES (result, FILE1);
10142286d8edStholo if (numlines)
10152286d8edStholo {
10162286d8edStholo D_LINEARRAY (result, FILE1) = ALLOCATE (numlines, char *);
10172286d8edStholo D_LENARRAY (result, FILE1) = ALLOCATE (numlines, size_t);
10182286d8edStholo bzero (D_LINEARRAY (result, FILE1), (numlines * sizeof (char *)));
10192286d8edStholo bzero (D_LENARRAY (result, FILE1), (numlines * sizeof (size_t)));
10202286d8edStholo }
10212286d8edStholo else
10222286d8edStholo {
10232286d8edStholo D_LINEARRAY (result, FILE1) = 0;
10242286d8edStholo D_LENARRAY (result, FILE1) = 0;
10252286d8edStholo }
10262286d8edStholo
10272286d8edStholo numlines = D_NUMLINES (result, FILE2);
10282286d8edStholo if (numlines)
10292286d8edStholo {
10302286d8edStholo D_LINEARRAY (result, FILE2) = ALLOCATE (numlines, char *);
10312286d8edStholo D_LENARRAY (result, FILE2) = ALLOCATE (numlines, size_t);
10322286d8edStholo bzero (D_LINEARRAY (result, FILE2), (numlines * sizeof (char *)));
10332286d8edStholo bzero (D_LENARRAY (result, FILE2), (numlines * sizeof (size_t)));
10342286d8edStholo }
10352286d8edStholo else
10362286d8edStholo {
10372286d8edStholo D_LINEARRAY (result, FILE2) = 0;
10382286d8edStholo D_LENARRAY (result, FILE2) = 0;
10392286d8edStholo }
10402286d8edStholo
10412286d8edStholo /* Return */
10422286d8edStholo return result;
10432286d8edStholo }
10442286d8edStholo
10452286d8edStholo /*
10462286d8edStholo * Compare two lists of lines of text.
10472286d8edStholo * Return 1 if they are equivalent, 0 if not.
10482286d8edStholo */
10492286d8edStholo static int
compare_line_list(list1,lengths1,list2,lengths2,nl)10502286d8edStholo compare_line_list (list1, lengths1, list2, lengths2, nl)
10512286d8edStholo char * const list1[], * const list2[];
10522286d8edStholo size_t const lengths1[], lengths2[];
10532286d8edStholo int nl;
10542286d8edStholo {
10552286d8edStholo char
10562286d8edStholo * const *l1 = list1,
10572286d8edStholo * const *l2 = list2;
10582286d8edStholo size_t const
10592286d8edStholo *lgths1 = lengths1,
10602286d8edStholo *lgths2 = lengths2;
10612286d8edStholo
10622286d8edStholo while (nl--)
10632286d8edStholo if (!*l1 || !*l2 || *lgths1 != *lgths2++
10642286d8edStholo || memcmp (*l1++, *l2++, *lgths1++))
10652286d8edStholo return 0;
10662286d8edStholo return 1;
10672286d8edStholo }
10682286d8edStholo
10692286d8edStholo /*
10702286d8edStholo * Routines to input and parse two way diffs.
10712286d8edStholo */
10722286d8edStholo
10732286d8edStholo extern char **environ;
10742286d8edStholo
10752286d8edStholo static struct diff_block *
process_diff(filea,fileb,last_block,diff_contents)10762286d8edStholo process_diff (filea, fileb, last_block, diff_contents)
10772286d8edStholo char const *filea, *fileb;
10782286d8edStholo struct diff_block **last_block;
10792286d8edStholo char **diff_contents;
10802286d8edStholo {
10812286d8edStholo char *diff_limit;
10822286d8edStholo char *scan_diff;
10832286d8edStholo enum diff_type dt;
10842286d8edStholo int i;
10852286d8edStholo struct diff_block *block_list, **block_list_end, *bptr;
10862286d8edStholo
10872286d8edStholo diff_limit = read_diff (filea, fileb, diff_contents);
10882286d8edStholo scan_diff = *diff_contents;
10892286d8edStholo block_list_end = &block_list;
10902286d8edStholo bptr = 0; /* Pacify `gcc -W'. */
10912286d8edStholo
10922286d8edStholo while (scan_diff < diff_limit)
10932286d8edStholo {
10942286d8edStholo bptr = ALLOCATE (1, struct diff_block);
10952286d8edStholo bptr->lines[0] = bptr->lines[1] = 0;
10962286d8edStholo bptr->lengths[0] = bptr->lengths[1] = 0;
10972286d8edStholo
10982286d8edStholo dt = process_diff_control (&scan_diff, bptr);
10992286d8edStholo if (dt == ERROR || *scan_diff != '\n')
11002286d8edStholo {
1101b2346922Stholo char *serr;
1102b2346922Stholo
1103b2346922Stholo for (serr = scan_diff; *serr != '\n'; serr++)
1104b2346922Stholo ;
1105b2346922Stholo *serr = '\0';
1106b2346922Stholo diff_error ("diff error: %s", scan_diff, 0);
1107b2346922Stholo *serr = '\n';
11082286d8edStholo DIFF3_ABORT (2);
11092286d8edStholo }
11102286d8edStholo scan_diff++;
11112286d8edStholo
11122286d8edStholo /* Force appropriate ranges to be null, if necessary */
11132286d8edStholo switch (dt)
11142286d8edStholo {
11152286d8edStholo case ADD:
11162286d8edStholo bptr->ranges[0][0]++;
11172286d8edStholo break;
11182286d8edStholo case DELETE:
11192286d8edStholo bptr->ranges[1][0]++;
11202286d8edStholo break;
11212286d8edStholo case CHANGE:
11222286d8edStholo break;
11232286d8edStholo default:
11242286d8edStholo diff3_fatal ("internal error: invalid diff type in process_diff");
11252286d8edStholo break;
11262286d8edStholo }
11272286d8edStholo
11282286d8edStholo /* Allocate space for the pointers for the lines from filea, and
11292286d8edStholo parcel them out among these pointers */
11302286d8edStholo if (dt != ADD)
11312286d8edStholo {
11322286d8edStholo int numlines = D_NUMLINES (bptr, 0);
11332286d8edStholo bptr->lines[0] = ALLOCATE (numlines, char *);
11342286d8edStholo bptr->lengths[0] = ALLOCATE (numlines, size_t);
11352286d8edStholo for (i = 0; i < numlines; i++)
11362286d8edStholo scan_diff = scan_diff_line (scan_diff,
11372286d8edStholo &(bptr->lines[0][i]),
11382286d8edStholo &(bptr->lengths[0][i]),
11392286d8edStholo diff_limit,
11402286d8edStholo '<');
11412286d8edStholo }
11422286d8edStholo
11432286d8edStholo /* Get past the separator for changes */
11442286d8edStholo if (dt == CHANGE)
11452286d8edStholo {
11462286d8edStholo if (strncmp (scan_diff, "---\n", 4))
11472286d8edStholo diff3_fatal ("invalid diff format; invalid change separator");
11482286d8edStholo scan_diff += 4;
11492286d8edStholo }
11502286d8edStholo
11512286d8edStholo /* Allocate space for the pointers for the lines from fileb, and
11522286d8edStholo parcel them out among these pointers */
11532286d8edStholo if (dt != DELETE)
11542286d8edStholo {
11552286d8edStholo int numlines = D_NUMLINES (bptr, 1);
11562286d8edStholo bptr->lines[1] = ALLOCATE (numlines, char *);
11572286d8edStholo bptr->lengths[1] = ALLOCATE (numlines, size_t);
11582286d8edStholo for (i = 0; i < numlines; i++)
11592286d8edStholo scan_diff = scan_diff_line (scan_diff,
11602286d8edStholo &(bptr->lines[1][i]),
11612286d8edStholo &(bptr->lengths[1][i]),
11622286d8edStholo diff_limit,
11632286d8edStholo '>');
11642286d8edStholo }
11652286d8edStholo
11662286d8edStholo /* Place this block on the blocklist. */
11672286d8edStholo *block_list_end = bptr;
11682286d8edStholo block_list_end = &bptr->next;
11692286d8edStholo }
11702286d8edStholo
11712286d8edStholo *block_list_end = 0;
11722286d8edStholo *last_block = bptr;
11732286d8edStholo return block_list;
11742286d8edStholo }
11752286d8edStholo
11762286d8edStholo /*
11772286d8edStholo * This routine will parse a normal format diff control string. It
11782286d8edStholo * returns the type of the diff (ERROR if the format is bad). All of
11792286d8edStholo * the other important information is filled into to the structure
11802286d8edStholo * pointed to by db, and the string pointer (whose location is passed
11812286d8edStholo * to this routine) is updated to point beyond the end of the string
11822286d8edStholo * parsed. Note that only the ranges in the diff_block will be set by
11832286d8edStholo * this routine.
11842286d8edStholo *
11852286d8edStholo * If some specific pair of numbers has been reduced to a single
11862286d8edStholo * number, then both corresponding numbers in the diff block are set
11872286d8edStholo * to that number. In general these numbers are interpetted as ranges
11882286d8edStholo * inclusive, unless being used by the ADD or DELETE commands. It is
11892286d8edStholo * assumed that these will be special cased in a superior routine.
11902286d8edStholo */
11912286d8edStholo
11922286d8edStholo static enum diff_type
process_diff_control(string,db)11932286d8edStholo process_diff_control (string, db)
11942286d8edStholo char **string;
11952286d8edStholo struct diff_block *db;
11962286d8edStholo {
11972286d8edStholo char *s = *string;
11982286d8edStholo int holdnum;
11992286d8edStholo enum diff_type type;
12002286d8edStholo
12012286d8edStholo /* These macros are defined here because they can use variables
12022286d8edStholo defined in this function. Don't try this at home kids, we're
12032286d8edStholo trained professionals!
12042286d8edStholo
12052286d8edStholo Also note that SKIPWHITE only recognizes tabs and spaces, and
12062286d8edStholo that READNUM can only read positive, integral numbers */
12072286d8edStholo
12082286d8edStholo #define SKIPWHITE(s) { while (*s == ' ' || *s == '\t') s++; }
12092286d8edStholo #define READNUM(s, num) \
12102286d8edStholo { unsigned char c = *s; if (!ISDIGIT (c)) return ERROR; holdnum = 0; \
12112286d8edStholo do { holdnum = (c - '0' + holdnum * 10); } \
12122286d8edStholo while (ISDIGIT (c = *++s)); (num) = holdnum; }
12132286d8edStholo
12142286d8edStholo /* Read first set of digits */
12152286d8edStholo SKIPWHITE (s);
12162286d8edStholo READNUM (s, db->ranges[0][START]);
12172286d8edStholo
12182286d8edStholo /* Was that the only digit? */
12192286d8edStholo SKIPWHITE (s);
12202286d8edStholo if (*s == ',')
12212286d8edStholo {
12222286d8edStholo /* Get the next digit */
12232286d8edStholo s++;
12242286d8edStholo READNUM (s, db->ranges[0][END]);
12252286d8edStholo }
12262286d8edStholo else
12272286d8edStholo db->ranges[0][END] = db->ranges[0][START];
12282286d8edStholo
12292286d8edStholo /* Get the letter */
12302286d8edStholo SKIPWHITE (s);
12312286d8edStholo switch (*s)
12322286d8edStholo {
12332286d8edStholo case 'a':
12342286d8edStholo type = ADD;
12352286d8edStholo break;
12362286d8edStholo case 'c':
12372286d8edStholo type = CHANGE;
12382286d8edStholo break;
12392286d8edStholo case 'd':
12402286d8edStholo type = DELETE;
12412286d8edStholo break;
12422286d8edStholo default:
12432286d8edStholo return ERROR; /* Bad format */
12442286d8edStholo }
12452286d8edStholo s++; /* Past letter */
12462286d8edStholo
12472286d8edStholo /* Read second set of digits */
12482286d8edStholo SKIPWHITE (s);
12492286d8edStholo READNUM (s, db->ranges[1][START]);
12502286d8edStholo
12512286d8edStholo /* Was that the only digit? */
12522286d8edStholo SKIPWHITE (s);
12532286d8edStholo if (*s == ',')
12542286d8edStholo {
12552286d8edStholo /* Get the next digit */
12562286d8edStholo s++;
12572286d8edStholo READNUM (s, db->ranges[1][END]);
12582286d8edStholo SKIPWHITE (s); /* To move to end */
12592286d8edStholo }
12602286d8edStholo else
12612286d8edStholo db->ranges[1][END] = db->ranges[1][START];
12622286d8edStholo
12632286d8edStholo *string = s;
12642286d8edStholo return type;
12652286d8edStholo }
12662286d8edStholo
12672286d8edStholo static char *
read_diff(filea,fileb,output_placement)12682286d8edStholo read_diff (filea, fileb, output_placement)
12692286d8edStholo char const *filea, *fileb;
12702286d8edStholo char **output_placement;
12712286d8edStholo {
12722286d8edStholo char *diff_result;
12732286d8edStholo size_t bytes, current_chunk_size, total;
12742286d8edStholo int fd, wstatus;
12752286d8edStholo struct stat pipestat;
1276b2346922Stholo FILE *outfile_hold;
1277b2346922Stholo const struct diff_callbacks *callbacks_hold;
1278b2346922Stholo struct diff_callbacks my_callbacks;
1279b2346922Stholo struct diff_callbacks *my_callbacks_arg;
12802286d8edStholo
12812286d8edStholo /* 302 / 1000 is log10(2.0) rounded up. Subtract 1 for the sign bit;
12822286d8edStholo add 1 for integer division truncation; add 1 more for a minus sign. */
12832286d8edStholo #define INT_STRLEN_BOUND(type) ((sizeof(type)*CHAR_BIT - 1) * 302 / 1000 + 2)
12842286d8edStholo
12852286d8edStholo char const *argv[7];
12862286d8edStholo char horizon_arg[17 + INT_STRLEN_BOUND (int)];
12872286d8edStholo char const **ap;
12882286d8edStholo char *diffout;
12892286d8edStholo
12902286d8edStholo ap = argv;
12912286d8edStholo *ap++ = "diff";
12922286d8edStholo if (always_text)
12932286d8edStholo *ap++ = "-a";
12942286d8edStholo sprintf (horizon_arg, "--horizon-lines=%d", horizon_lines);
12952286d8edStholo *ap++ = horizon_arg;
12962286d8edStholo *ap++ = "--";
12972286d8edStholo *ap++ = filea;
12982286d8edStholo *ap++ = fileb;
12992286d8edStholo *ap = 0;
13002286d8edStholo
1301e77048c1Stholo diffout = cvs_temp_name ();
1302b2346922Stholo
1303b2346922Stholo outfile_hold = outfile;
1304b2346922Stholo callbacks_hold = callbacks;
1305b2346922Stholo
1306b2346922Stholo /* We want to call diff_run preserving any stdout and stderr
1307b2346922Stholo callbacks, but discarding any callbacks to handle file output,
1308b2346922Stholo since we want the file output to go to our temporary file.
1309b2346922Stholo FIXME: We should use callbacks to just read it into a memory
1310b2346922Stholo buffer; that's we do with the temporary file just below anyhow. */
1311b2346922Stholo if (callbacks == NULL)
1312b2346922Stholo my_callbacks_arg = NULL;
1313b2346922Stholo else
1314b2346922Stholo {
1315b2346922Stholo my_callbacks = *callbacks;
1316b2346922Stholo my_callbacks.write_output = NULL;
1317b2346922Stholo my_callbacks.flush_output = NULL;
1318b2346922Stholo my_callbacks_arg = &my_callbacks;
1319b2346922Stholo }
1320b2346922Stholo
1321b2346922Stholo wstatus = diff_run (ap - argv, (char **) argv, diffout, my_callbacks_arg);
1322b2346922Stholo
1323b2346922Stholo outfile = outfile_hold;
1324b2346922Stholo callbacks = callbacks_hold;
1325b2346922Stholo
13262286d8edStholo if (wstatus == 2)
13272286d8edStholo diff3_fatal ("subsidiary diff failed");
13282286d8edStholo
13292286d8edStholo if (-1 == (fd = open (diffout, O_RDONLY)))
13302286d8edStholo diff3_fatal ("could not open temporary diff file");
13312286d8edStholo
13322286d8edStholo current_chunk_size = 8 * 1024;
13332286d8edStholo if (fstat (fd, &pipestat) == 0)
13342286d8edStholo current_chunk_size = max (current_chunk_size, STAT_BLOCKSIZE (pipestat));
13352286d8edStholo
13362286d8edStholo diff_result = xmalloc (current_chunk_size);
13372286d8edStholo total = 0;
13382286d8edStholo do {
13392286d8edStholo bytes = myread (fd,
13402286d8edStholo diff_result + total,
13412286d8edStholo current_chunk_size - total);
13422286d8edStholo total += bytes;
13432286d8edStholo if (total == current_chunk_size)
13442286d8edStholo {
13452286d8edStholo if (current_chunk_size < 2 * current_chunk_size)
13462286d8edStholo current_chunk_size = 2 * current_chunk_size;
13472286d8edStholo else if (current_chunk_size < (size_t) -1)
13482286d8edStholo current_chunk_size = (size_t) -1;
13492286d8edStholo else
13502286d8edStholo diff3_fatal ("files are too large to fit into memory");
13512286d8edStholo diff_result = xrealloc (diff_result, (current_chunk_size *= 2));
13522286d8edStholo }
13532286d8edStholo } while (bytes);
13542286d8edStholo
13552286d8edStholo if (total != 0 && diff_result[total-1] != '\n')
13562286d8edStholo diff3_fatal ("invalid diff format; incomplete last line");
13572286d8edStholo
13582286d8edStholo *output_placement = diff_result;
13592286d8edStholo
13602286d8edStholo if (close (fd) != 0)
13612286d8edStholo diff3_perror_with_exit ("pipe close");
13622286d8edStholo unlink (diffout);
13632286d8edStholo
13642286d8edStholo return diff_result + total;
13652286d8edStholo }
13662286d8edStholo
13672286d8edStholo
13682286d8edStholo /*
13692286d8edStholo * Scan a regular diff line (consisting of > or <, followed by a
13702286d8edStholo * space, followed by text (including nulls) up to a newline.
13712286d8edStholo *
13722286d8edStholo * This next routine began life as a macro and many parameters in it
13732286d8edStholo * are used as call-by-reference values.
13742286d8edStholo */
13752286d8edStholo static char *
scan_diff_line(scan_ptr,set_start,set_length,limit,leadingchar)13762286d8edStholo scan_diff_line (scan_ptr, set_start, set_length, limit, leadingchar)
13772286d8edStholo char *scan_ptr, **set_start;
13782286d8edStholo size_t *set_length;
13792286d8edStholo char *limit;
13802286d8edStholo int leadingchar;
13812286d8edStholo {
13822286d8edStholo char *line_ptr;
13832286d8edStholo
13842286d8edStholo if (!(scan_ptr[0] == leadingchar
13852286d8edStholo && scan_ptr[1] == ' '))
13862286d8edStholo diff3_fatal ("invalid diff format; incorrect leading line chars");
13872286d8edStholo
13882286d8edStholo *set_start = line_ptr = scan_ptr + 2;
13892286d8edStholo while (*line_ptr++ != '\n')
13902286d8edStholo ;
13912286d8edStholo
13922286d8edStholo /* Include newline if the original line ended in a newline,
13932286d8edStholo or if an edit script is being generated.
13942286d8edStholo Copy any missing newline message to stderr if an edit script is being
13952286d8edStholo generated, because edit scripts cannot handle missing newlines.
13962286d8edStholo Return the beginning of the next line. */
13972286d8edStholo *set_length = line_ptr - *set_start;
13982286d8edStholo if (line_ptr < limit && *line_ptr == '\\')
13992286d8edStholo {
1400b2346922Stholo if (! edscript)
1401b2346922Stholo {
14022286d8edStholo --*set_length;
14032286d8edStholo line_ptr++;
1404b2346922Stholo while (*line_ptr++ != '\n')
1405b2346922Stholo ;
14062286d8edStholo }
1407b2346922Stholo else
1408b2346922Stholo {
1409b2346922Stholo char *serr;
1410b2346922Stholo
1411b2346922Stholo line_ptr++;
1412b2346922Stholo serr = line_ptr;
1413b2346922Stholo while (*line_ptr++ != '\n')
1414b2346922Stholo ;
1415b2346922Stholo line_ptr[-1] = '\0';
1416b2346922Stholo diff_error ("%s", serr, 0);
1417b2346922Stholo line_ptr[-1] = '\n';
1418b2346922Stholo }
14192286d8edStholo }
14202286d8edStholo
14212286d8edStholo return line_ptr;
14222286d8edStholo }
14232286d8edStholo
14242286d8edStholo /*
14252286d8edStholo * This routine outputs a three way diff passed as a list of
14262286d8edStholo * diff3_block's.
14272286d8edStholo * The argument MAPPING is indexed by external file number (in the
14282286d8edStholo * argument list) and contains the internal file number (from the
14292286d8edStholo * diff passed). This is important because the user expects his
14302286d8edStholo * outputs in terms of the argument list number, and the diff passed
14312286d8edStholo * may have been done slightly differently (if the last argument
14322286d8edStholo * was "-", for example).
14332286d8edStholo * REV_MAPPING is the inverse of MAPPING.
14342286d8edStholo */
14352286d8edStholo static void
output_diff3(diff,mapping,rev_mapping)1436b2346922Stholo output_diff3 (diff, mapping, rev_mapping)
14372286d8edStholo struct diff3_block *diff;
14382286d8edStholo int const mapping[3], rev_mapping[3];
14392286d8edStholo {
14402286d8edStholo int i;
14412286d8edStholo int oddoneout;
14422286d8edStholo char *cp;
14432286d8edStholo struct diff3_block *ptr;
14442286d8edStholo int line;
14452286d8edStholo size_t length;
14462286d8edStholo int dontprint;
14472286d8edStholo static int skew_increment[3] = { 2, 3, 1 }; /* 0==>2==>1==>3 */
14482286d8edStholo char const *line_prefix = tab_align_flag ? "\t" : " ";
14492286d8edStholo
14502286d8edStholo for (ptr = diff; ptr; ptr = D_NEXT (ptr))
14512286d8edStholo {
14522286d8edStholo char x[2];
14532286d8edStholo
14542286d8edStholo switch (ptr->correspond)
14552286d8edStholo {
14562286d8edStholo case DIFF_ALL:
14572286d8edStholo x[0] = '\0';
14582286d8edStholo dontprint = 3; /* Print them all */
14592286d8edStholo oddoneout = 3; /* Nobody's odder than anyone else */
14602286d8edStholo break;
14612286d8edStholo case DIFF_1ST:
14622286d8edStholo case DIFF_2ND:
14632286d8edStholo case DIFF_3RD:
14642286d8edStholo oddoneout = rev_mapping[(int) ptr->correspond - (int) DIFF_1ST];
14652286d8edStholo
14662286d8edStholo x[0] = oddoneout + '1';
14672286d8edStholo x[1] = '\0';
14682286d8edStholo dontprint = oddoneout==0;
14692286d8edStholo break;
14702286d8edStholo default:
14712286d8edStholo diff3_fatal ("internal error: invalid diff type passed to output");
14722286d8edStholo }
1473b2346922Stholo printf_output ("====%s\n", x);
14742286d8edStholo
14752286d8edStholo /* Go 0, 2, 1 if the first and third outputs are equivalent. */
14762286d8edStholo for (i = 0; i < 3;
14772286d8edStholo i = (oddoneout == 1 ? skew_increment[i] : i + 1))
14782286d8edStholo {
14792286d8edStholo int realfile = mapping[i];
14802286d8edStholo int
14812286d8edStholo lowt = D_LOWLINE (ptr, realfile),
14822286d8edStholo hight = D_HIGHLINE (ptr, realfile);
14832286d8edStholo
1484b2346922Stholo printf_output ("%d:", i + 1);
14852286d8edStholo switch (lowt - hight)
14862286d8edStholo {
14872286d8edStholo case 1:
1488b2346922Stholo printf_output ("%da\n", lowt - 1);
14892286d8edStholo break;
14902286d8edStholo case 0:
1491b2346922Stholo printf_output ("%dc\n", lowt);
14922286d8edStholo break;
14932286d8edStholo default:
1494b2346922Stholo printf_output ("%d,%dc\n", lowt, hight);
14952286d8edStholo break;
14962286d8edStholo }
14972286d8edStholo
14982286d8edStholo if (i == dontprint) continue;
14992286d8edStholo
15002286d8edStholo if (lowt <= hight)
15012286d8edStholo {
15022286d8edStholo line = 0;
15032286d8edStholo do
15042286d8edStholo {
1505*0971b67fStb printf_output ("%s", line_prefix);
15062286d8edStholo cp = D_RELNUM (ptr, realfile, line);
15072286d8edStholo length = D_RELLEN (ptr, realfile, line);
1508b2346922Stholo write_output (cp, length);
15092286d8edStholo }
15102286d8edStholo while (++line < hight - lowt + 1);
15112286d8edStholo if (cp[length - 1] != '\n')
1512b2346922Stholo printf_output ("\n\\ No newline at end of file\n");
15132286d8edStholo }
15142286d8edStholo }
15152286d8edStholo }
15162286d8edStholo }
15172286d8edStholo
15182286d8edStholo
15192286d8edStholo /*
1520b2346922Stholo * Output the lines of B taken from FILENUM.
15212286d8edStholo * Double any initial '.'s; yield nonzero if any initial '.'s were doubled.
15222286d8edStholo */
15232286d8edStholo static int
dotlines(b,filenum)1524b2346922Stholo dotlines (b, filenum)
15252286d8edStholo struct diff3_block *b;
15262286d8edStholo int filenum;
15272286d8edStholo {
15282286d8edStholo int i;
15292286d8edStholo int leading_dot = 0;
15302286d8edStholo
15312286d8edStholo for (i = 0;
15322286d8edStholo i < D_NUMLINES (b, filenum);
15332286d8edStholo i++)
15342286d8edStholo {
15352286d8edStholo char *line = D_RELNUM (b, filenum, i);
15362286d8edStholo if (line[0] == '.')
15372286d8edStholo {
15382286d8edStholo leading_dot = 1;
1539b2346922Stholo write_output (".", 1);
15402286d8edStholo }
1541b2346922Stholo write_output (line, D_RELLEN (b, filenum, i));
15422286d8edStholo }
15432286d8edStholo
15442286d8edStholo return leading_dot;
15452286d8edStholo }
15462286d8edStholo
15472286d8edStholo /*
15482286d8edStholo * Output to OUTPUTFILE a '.' line. If LEADING_DOT is nonzero,
15492286d8edStholo * also output a command that removes initial '.'s
15502286d8edStholo * starting with line START and continuing for NUM lines.
15512286d8edStholo */
15522286d8edStholo static void
undotlines(leading_dot,start,num)1553b2346922Stholo undotlines (leading_dot, start, num)
15542286d8edStholo int leading_dot, start, num;
15552286d8edStholo {
1556b2346922Stholo write_output (".\n", 2);
1557*0971b67fStb if (leading_dot) {
15582286d8edStholo if (num == 1)
1559b2346922Stholo printf_output ("%ds/^\\.//\n", start);
15602286d8edStholo else
1561b2346922Stholo printf_output ("%d,%ds/^\\.//\n", start, start + num - 1);
15622286d8edStholo }
1563*0971b67fStb }
15642286d8edStholo
15652286d8edStholo /*
15662286d8edStholo * This routine outputs a diff3 set of blocks as an ed script. This
15672286d8edStholo * script applies the changes between file's 2 & 3 to file 1. It
15682286d8edStholo * takes the precise format of the ed script to be output from global
15692286d8edStholo * variables set during options processing. Note that it does
15702286d8edStholo * destructive things to the set of diff3 blocks it is passed; it
15712286d8edStholo * reverses their order (this gets around the problems involved with
15722286d8edStholo * changing line numbers in an ed script).
15732286d8edStholo *
15742286d8edStholo * Note that this routine has the same problem of mapping as the last
15752286d8edStholo * one did; the variable MAPPING maps from file number according to
15762286d8edStholo * the argument list to file number according to the diff passed. All
15772286d8edStholo * files listed below are in terms of the argument list.
15782286d8edStholo * REV_MAPPING is the inverse of MAPPING.
15792286d8edStholo *
15802286d8edStholo * The arguments FILE0, FILE1 and FILE2 are the strings to print
15812286d8edStholo * as the names of the three files. These may be the actual names,
15822286d8edStholo * or may be the arguments specified with -L.
15832286d8edStholo *
15842286d8edStholo * Returns 1 if conflicts were found.
15852286d8edStholo */
15862286d8edStholo
15872286d8edStholo static int
output_diff3_edscript(diff,mapping,rev_mapping,file0,file1,file2)1588b2346922Stholo output_diff3_edscript (diff, mapping, rev_mapping, file0, file1, file2)
15892286d8edStholo struct diff3_block *diff;
15902286d8edStholo int const mapping[3], rev_mapping[3];
15912286d8edStholo char const *file0, *file1, *file2;
15922286d8edStholo {
15932286d8edStholo int leading_dot;
15942286d8edStholo int conflicts_found = 0, conflict;
15952286d8edStholo struct diff3_block *b;
15962286d8edStholo
15972286d8edStholo for (b = reverse_diff3_blocklist (diff); b; b = b->next)
15982286d8edStholo {
15992286d8edStholo /* Must do mapping correctly. */
16002286d8edStholo enum diff_type type
16012286d8edStholo = ((b->correspond == DIFF_ALL) ?
16022286d8edStholo DIFF_ALL :
16032286d8edStholo ((enum diff_type)
16042286d8edStholo (((int) DIFF_1ST)
16052286d8edStholo + rev_mapping[(int) b->correspond - (int) DIFF_1ST])));
16062286d8edStholo
16072286d8edStholo /* If we aren't supposed to do this output block, skip it. */
16082286d8edStholo switch (type)
16092286d8edStholo {
16102286d8edStholo default: continue;
16112286d8edStholo case DIFF_2ND: if (!show_2nd) continue; conflict = 1; break;
16122286d8edStholo case DIFF_3RD: if (overlap_only) continue; conflict = 0; break;
16132286d8edStholo case DIFF_ALL: if (simple_only) continue; conflict = flagging; break;
16142286d8edStholo }
16152286d8edStholo
16162286d8edStholo if (conflict)
16172286d8edStholo {
16182286d8edStholo conflicts_found = 1;
16192286d8edStholo
16202286d8edStholo
16212286d8edStholo /* Mark end of conflict. */
16222286d8edStholo
1623b2346922Stholo printf_output ("%da\n", D_HIGHLINE (b, mapping[FILE0]));
16242286d8edStholo leading_dot = 0;
16252286d8edStholo if (type == DIFF_ALL)
16262286d8edStholo {
16272286d8edStholo if (show_2nd)
16282286d8edStholo {
16292286d8edStholo /* Append lines from FILE1. */
1630b2346922Stholo printf_output ("||||||| %s\n", file1);
1631b2346922Stholo leading_dot = dotlines (b, mapping[FILE1]);
16322286d8edStholo }
16332286d8edStholo /* Append lines from FILE2. */
1634b2346922Stholo printf_output ("=======\n");
1635b2346922Stholo leading_dot |= dotlines (b, mapping[FILE2]);
16362286d8edStholo }
1637b2346922Stholo printf_output (">>>>>>> %s\n", file2);
1638b2346922Stholo undotlines (leading_dot,
16392286d8edStholo D_HIGHLINE (b, mapping[FILE0]) + 2,
16402286d8edStholo (D_NUMLINES (b, mapping[FILE1])
16412286d8edStholo + D_NUMLINES (b, mapping[FILE2]) + 1));
16422286d8edStholo
16432286d8edStholo
16442286d8edStholo /* Mark start of conflict. */
16452286d8edStholo
1646b2346922Stholo printf_output ("%da\n<<<<<<< %s\n",
16472286d8edStholo D_LOWLINE (b, mapping[FILE0]) - 1,
16482286d8edStholo type == DIFF_ALL ? file0 : file1);
16492286d8edStholo leading_dot = 0;
16502286d8edStholo if (type == DIFF_2ND)
16512286d8edStholo {
16522286d8edStholo /* Prepend lines from FILE1. */
1653b2346922Stholo leading_dot = dotlines (b, mapping[FILE1]);
1654b2346922Stholo printf_output ("=======\n");
16552286d8edStholo }
1656b2346922Stholo undotlines (leading_dot,
16572286d8edStholo D_LOWLINE (b, mapping[FILE0]) + 1,
16582286d8edStholo D_NUMLINES (b, mapping[FILE1]));
16592286d8edStholo }
16602286d8edStholo else if (D_NUMLINES (b, mapping[FILE2]) == 0)
16612286d8edStholo /* Write out a delete */
16622286d8edStholo {
16632286d8edStholo if (D_NUMLINES (b, mapping[FILE0]) == 1)
1664b2346922Stholo printf_output ("%dd\n", D_LOWLINE (b, mapping[FILE0]));
16652286d8edStholo else
1666b2346922Stholo printf_output ("%d,%dd\n",
16672286d8edStholo D_LOWLINE (b, mapping[FILE0]),
16682286d8edStholo D_HIGHLINE (b, mapping[FILE0]));
16692286d8edStholo }
16702286d8edStholo else
16712286d8edStholo /* Write out an add or change */
16722286d8edStholo {
16732286d8edStholo switch (D_NUMLINES (b, mapping[FILE0]))
16742286d8edStholo {
16752286d8edStholo case 0:
1676b2346922Stholo printf_output ("%da\n", D_HIGHLINE (b, mapping[FILE0]));
16772286d8edStholo break;
16782286d8edStholo case 1:
1679b2346922Stholo printf_output ("%dc\n", D_HIGHLINE (b, mapping[FILE0]));
16802286d8edStholo break;
16812286d8edStholo default:
1682b2346922Stholo printf_output ("%d,%dc\n",
16832286d8edStholo D_LOWLINE (b, mapping[FILE0]),
16842286d8edStholo D_HIGHLINE (b, mapping[FILE0]));
16852286d8edStholo break;
16862286d8edStholo }
16872286d8edStholo
1688b2346922Stholo undotlines (dotlines (b, mapping[FILE2]),
16892286d8edStholo D_LOWLINE (b, mapping[FILE0]),
16902286d8edStholo D_NUMLINES (b, mapping[FILE2]));
16912286d8edStholo }
16922286d8edStholo }
1693b2346922Stholo if (finalwrite) printf_output ("w\nq\n");
16942286d8edStholo return conflicts_found;
16952286d8edStholo }
16962286d8edStholo
16972286d8edStholo /*
1698b2346922Stholo * Read from INFILE and output to the standard output file a set of
1699b2346922Stholo * diff3_ blocks DIFF as a merged file. This acts like 'ed file0
1700b2346922Stholo * <[output_diff3_edscript]', except that it works even for binary
1701b2346922Stholo * data or incomplete lines.
17022286d8edStholo *
17032286d8edStholo * As before, MAPPING maps from arg list file number to diff file number,
17042286d8edStholo * REV_MAPPING is its inverse,
17052286d8edStholo * and FILE0, FILE1, and FILE2 are the names of the files.
17062286d8edStholo *
17072286d8edStholo * Returns 1 if conflicts were found.
17082286d8edStholo */
17092286d8edStholo
17102286d8edStholo static int
output_diff3_merge(infile,diff,mapping,rev_mapping,file0,file1,file2)1711b2346922Stholo output_diff3_merge (infile, diff, mapping, rev_mapping,
17122286d8edStholo file0, file1, file2)
1713b2346922Stholo FILE *infile;
17142286d8edStholo struct diff3_block *diff;
17152286d8edStholo int const mapping[3], rev_mapping[3];
17162286d8edStholo char const *file0, *file1, *file2;
17172286d8edStholo {
17182286d8edStholo int c, i;
1719b2346922Stholo char cc;
17202286d8edStholo int conflicts_found = 0, conflict;
17212286d8edStholo struct diff3_block *b;
17222286d8edStholo int linesread = 0;
17232286d8edStholo
17242286d8edStholo for (b = diff; b; b = b->next)
17252286d8edStholo {
17262286d8edStholo /* Must do mapping correctly. */
17272286d8edStholo enum diff_type type
17282286d8edStholo = ((b->correspond == DIFF_ALL) ?
17292286d8edStholo DIFF_ALL :
17302286d8edStholo ((enum diff_type)
17312286d8edStholo (((int) DIFF_1ST)
17322286d8edStholo + rev_mapping[(int) b->correspond - (int) DIFF_1ST])));
17332286d8edStholo char const *format_2nd = "<<<<<<< %s\n";
17342286d8edStholo
17352286d8edStholo /* If we aren't supposed to do this output block, skip it. */
17362286d8edStholo switch (type)
17372286d8edStholo {
17382286d8edStholo default: continue;
17392286d8edStholo case DIFF_2ND: if (!show_2nd) continue; conflict = 1; break;
17402286d8edStholo case DIFF_3RD: if (overlap_only) continue; conflict = 0; break;
17412286d8edStholo case DIFF_ALL: if (simple_only) continue; conflict = flagging;
17422286d8edStholo format_2nd = "||||||| %s\n";
17432286d8edStholo break;
17442286d8edStholo }
17452286d8edStholo
17462286d8edStholo /* Copy I lines from file 0. */
17472286d8edStholo i = D_LOWLINE (b, FILE0) - linesread - 1;
17482286d8edStholo linesread += i;
17492286d8edStholo while (0 <= --i)
17502286d8edStholo do
17512286d8edStholo {
17522286d8edStholo c = getc (infile);
1753*0971b67fStb if (c == EOF) {
17542286d8edStholo if (ferror (infile))
17552286d8edStholo diff3_perror_with_exit ("input file");
17562286d8edStholo else if (feof (infile))
17572286d8edStholo diff3_fatal ("input file shrank");
1758*0971b67fStb }
1759b2346922Stholo cc = c;
1760b2346922Stholo write_output (&cc, 1);
17612286d8edStholo }
17622286d8edStholo while (c != '\n');
17632286d8edStholo
17642286d8edStholo if (conflict)
17652286d8edStholo {
17662286d8edStholo conflicts_found = 1;
17672286d8edStholo
17682286d8edStholo if (type == DIFF_ALL)
17692286d8edStholo {
17702286d8edStholo /* Put in lines from FILE0 with bracket. */
1771b2346922Stholo printf_output ("<<<<<<< %s\n", file0);
17722286d8edStholo for (i = 0;
17732286d8edStholo i < D_NUMLINES (b, mapping[FILE0]);
17742286d8edStholo i++)
1775b2346922Stholo write_output (D_RELNUM (b, mapping[FILE0], i),
1776b2346922Stholo D_RELLEN (b, mapping[FILE0], i));
17772286d8edStholo }
17782286d8edStholo
17792286d8edStholo if (show_2nd)
17802286d8edStholo {
17812286d8edStholo /* Put in lines from FILE1 with bracket. */
1782b2346922Stholo printf_output (format_2nd, file1);
17832286d8edStholo for (i = 0;
17842286d8edStholo i < D_NUMLINES (b, mapping[FILE1]);
17852286d8edStholo i++)
1786b2346922Stholo write_output (D_RELNUM (b, mapping[FILE1], i),
1787b2346922Stholo D_RELLEN (b, mapping[FILE1], i));
17882286d8edStholo }
17892286d8edStholo
1790b2346922Stholo printf_output ("=======\n");
17912286d8edStholo }
17922286d8edStholo
17932286d8edStholo /* Put in lines from FILE2. */
17942286d8edStholo for (i = 0;
17952286d8edStholo i < D_NUMLINES (b, mapping[FILE2]);
17962286d8edStholo i++)
1797b2346922Stholo write_output (D_RELNUM (b, mapping[FILE2], i),
1798b2346922Stholo D_RELLEN (b, mapping[FILE2], i));
17992286d8edStholo
18002286d8edStholo if (conflict)
1801b2346922Stholo printf_output (">>>>>>> %s\n", file2);
18022286d8edStholo
18032286d8edStholo /* Skip I lines in file 0. */
18042286d8edStholo i = D_NUMLINES (b, FILE0);
18052286d8edStholo linesread += i;
18062286d8edStholo while (0 <= --i)
18072286d8edStholo while ((c = getc (infile)) != '\n')
1808*0971b67fStb if (c == EOF) {
18092286d8edStholo if (ferror (infile))
18102286d8edStholo diff3_perror_with_exit ("input file");
18112286d8edStholo else if (feof (infile))
18122286d8edStholo {
18132286d8edStholo if (i || b->next)
18142286d8edStholo diff3_fatal ("input file shrank");
18152286d8edStholo return conflicts_found;
18162286d8edStholo }
18172286d8edStholo }
1818*0971b67fStb }
18192286d8edStholo /* Copy rest of common file. */
18202286d8edStholo while ((c = getc (infile)) != EOF || !(ferror (infile) | feof (infile)))
1821b2346922Stholo {
1822b2346922Stholo cc = c;
1823b2346922Stholo write_output (&cc, 1);
1824b2346922Stholo }
18252286d8edStholo return conflicts_found;
18262286d8edStholo }
18272286d8edStholo
18282286d8edStholo /*
18292286d8edStholo * Reverse the order of the list of diff3 blocks.
18302286d8edStholo */
18312286d8edStholo static struct diff3_block *
reverse_diff3_blocklist(diff)18322286d8edStholo reverse_diff3_blocklist (diff)
18332286d8edStholo struct diff3_block *diff;
18342286d8edStholo {
18352286d8edStholo register struct diff3_block *tmp, *next, *prev;
18362286d8edStholo
18372286d8edStholo for (tmp = diff, prev = 0; tmp; tmp = next)
18382286d8edStholo {
18392286d8edStholo next = tmp->next;
18402286d8edStholo tmp->next = prev;
18412286d8edStholo prev = tmp;
18422286d8edStholo }
18432286d8edStholo
18442286d8edStholo return prev;
18452286d8edStholo }
18462286d8edStholo
18472286d8edStholo static size_t
myread(fd,ptr,size)18482286d8edStholo myread (fd, ptr, size)
18492286d8edStholo int fd;
18502286d8edStholo char *ptr;
18512286d8edStholo size_t size;
18522286d8edStholo {
18532286d8edStholo size_t result = read (fd, ptr, size);
18542286d8edStholo if (result == -1)
18552286d8edStholo diff3_perror_with_exit ("read failed");
18562286d8edStholo return result;
18572286d8edStholo }
18582286d8edStholo
18592286d8edStholo static void
diff3_fatal(string)18602286d8edStholo diff3_fatal (string)
18612286d8edStholo char const *string;
18622286d8edStholo {
1863b2346922Stholo diff_error ("%s", string, 0);
18642286d8edStholo DIFF3_ABORT (2);
18652286d8edStholo }
18662286d8edStholo
18672286d8edStholo static void
diff3_perror_with_exit(string)18682286d8edStholo diff3_perror_with_exit (string)
18692286d8edStholo char const *string;
18702286d8edStholo {
1871b2346922Stholo perror_with_name (string);
18722286d8edStholo DIFF3_ABORT (2);
18732286d8edStholo }
18742286d8edStholo
18752286d8edStholo static void
initialize_main(argcp,argvp)18762286d8edStholo initialize_main (argcp, argvp)
18772286d8edStholo int *argcp;
18782286d8edStholo char ***argvp;
18792286d8edStholo {
18802286d8edStholo always_text = 0;
18812286d8edStholo edscript = 0;
18822286d8edStholo flagging = 0;
18832286d8edStholo tab_align_flag = 0;
18842286d8edStholo simple_only = 0;
18852286d8edStholo overlap_only = 0;
18862286d8edStholo show_2nd = 0;
18872286d8edStholo finalwrite = 0;
18882286d8edStholo merge = 0;
18892286d8edStholo diff_program_name = (*argvp)[0];
1890b2346922Stholo outfile = NULL;
18912286d8edStholo }
18922286d8edStholo
18932286d8edStholo static void
free_diff_blocks(p)18942286d8edStholo free_diff_blocks(p)
18952286d8edStholo struct diff_block *p;
18962286d8edStholo {
18972286d8edStholo register struct diff_block *next;
18982286d8edStholo
18992286d8edStholo while (p)
19002286d8edStholo {
19012286d8edStholo next = p->next;
19022286d8edStholo if (p->lines[0]) free(p->lines[0]);
19032286d8edStholo if (p->lines[1]) free(p->lines[1]);
19042286d8edStholo if (p->lengths[0]) free(p->lengths[0]);
19052286d8edStholo if (p->lengths[1]) free(p->lengths[1]);
19062286d8edStholo free(p);
19072286d8edStholo p = next;
19082286d8edStholo }
19092286d8edStholo }
19102286d8edStholo
19112286d8edStholo static void
free_diff3_blocks(p)19122286d8edStholo free_diff3_blocks(p)
19132286d8edStholo struct diff3_block *p;
19142286d8edStholo {
19152286d8edStholo register struct diff3_block *next;
19162286d8edStholo
19172286d8edStholo while (p)
19182286d8edStholo {
19192286d8edStholo next = p->next;
19202286d8edStholo if (p->lines[0]) free(p->lines[0]);
19212286d8edStholo if (p->lines[1]) free(p->lines[1]);
19222286d8edStholo if (p->lines[2]) free(p->lines[2]);
19232286d8edStholo if (p->lengths[0]) free(p->lengths[0]);
19242286d8edStholo if (p->lengths[1]) free(p->lengths[1]);
19252286d8edStholo if (p->lengths[2]) free(p->lengths[2]);
19262286d8edStholo free(p);
19272286d8edStholo p = next;
19282286d8edStholo }
19292286d8edStholo }
1930