xref: /openbsd-src/gnu/usr.bin/cvs/diff/diff3.c (revision e77048c1007676349fedef3cd7d0b6b93f74c675)
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 
44*e77048c1Stholo char * cvs_temp_name PARAMS((void));
45*e77048c1Stholo 
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.  */
1742286d8edStholo static int 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
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.  */
3732286d8edStholo 
3742286d8edStholo   if (strcmp (file[2], "-") == 0)
3752286d8edStholo     {
3762286d8edStholo       /* Sigh.  We've got standard input as the last arg.  We can't
3772286d8edStholo 	 call diff twice on stdin.  Use the middle arg as the common
3782286d8edStholo 	 file instead.  */
3792286d8edStholo       if (strcmp (file[0], "-") == 0 || strcmp (file[1], "-") == 0)
3802286d8edStholo         {
3812286d8edStholo 	  diff_error ("%s", "`-' specified for more than one input file", 0);
3822286d8edStholo 	  return 2;
3832286d8edStholo         }
3842286d8edStholo       mapping[0] = 0;
3852286d8edStholo       mapping[1] = 2;
3862286d8edStholo       mapping[2] = 1;
3872286d8edStholo     }
3882286d8edStholo   else
3892286d8edStholo     {
3902286d8edStholo       /* Normal, what you'd expect */
3912286d8edStholo       mapping[0] = 0;
3922286d8edStholo       mapping[1] = 1;
3932286d8edStholo       mapping[2] = 2;
3942286d8edStholo     }
3952286d8edStholo 
3962286d8edStholo   for (i = 0; i < 3; i++)
3972286d8edStholo     rev_mapping[mapping[i]] = i;
3982286d8edStholo 
3992286d8edStholo   for (i = 0; i < 3; i++)
4002286d8edStholo     if (strcmp (file[i], "-") != 0)
4012286d8edStholo       {
4022286d8edStholo 	if (stat (file[i], &statb) < 0)
4032286d8edStholo 	  {
4042286d8edStholo 	    perror_with_name (file[i]);
4052286d8edStholo 	    return 2;
4062286d8edStholo           }
4072286d8edStholo 	else if (S_ISDIR(statb.st_mode))
4082286d8edStholo 	  {
409b2346922Stholo 	    diff_error ("%s: Is a directory", file[i], 0);
4102286d8edStholo 	    return 2;
4112286d8edStholo 	  }
4122286d8edStholo       }
4132286d8edStholo 
414b2346922Stholo   if (callbacks && callbacks->write_output)
415b2346922Stholo     {
416b2346922Stholo       if (out != NULL)
417b2346922Stholo 	{
418b2346922Stholo 	  diff_error ("write callback with output file", 0, 0);
419b2346922Stholo 	  return 2;
420b2346922Stholo 	}
421b2346922Stholo     }
4222286d8edStholo   else
4232286d8edStholo     {
424b2346922Stholo       if (out == NULL)
425b2346922Stholo 	outfile = stdout;
426b2346922Stholo       else
427b2346922Stholo 	{
428b2346922Stholo 	  outfile = fopen (out, "w");
429b2346922Stholo 	  if (outfile == NULL)
4302286d8edStholo 	    {
4312286d8edStholo 	      perror_with_name ("could not open output file");
4322286d8edStholo 	      return 2;
4332286d8edStholo 	    }
434b2346922Stholo 	  opened_file = 1;
435b2346922Stholo 	}
4362286d8edStholo     }
4372286d8edStholo 
4382286d8edStholo   /* Set the jump buffer, so that diff may abort execution without
4392286d8edStholo      terminating the process. */
4402286d8edStholo   status = setjmp (diff3_abort_buf);
4412286d8edStholo   if (status != 0)
4422286d8edStholo       return status;
4432286d8edStholo 
4442286d8edStholo   commonname = file[rev_mapping[FILEC]];
4452286d8edStholo   thread1 = process_diff (file[rev_mapping[FILE1]], commonname, &last_block,
4462286d8edStholo 			  &content1);
4472286d8edStholo   if (thread1)
4482286d8edStholo     for (i = 0; i < 2; i++)
4492286d8edStholo       {
4502286d8edStholo 	horizon_lines = max (horizon_lines, D_NUMLINES (thread1, i));
4512286d8edStholo 	horizon_lines = max (horizon_lines, D_NUMLINES (last_block, i));
4522286d8edStholo       }
4532286d8edStholo   thread0 = process_diff (file[rev_mapping[FILE0]], commonname, &last_block,
4542286d8edStholo 			  &content0);
4552286d8edStholo   diff3 = make_3way_diff (thread0, thread1);
4562286d8edStholo   if (edscript)
4572286d8edStholo     conflicts_found
458b2346922Stholo       = output_diff3_edscript (diff3, mapping, rev_mapping,
4592286d8edStholo 			       tag_strings[0], tag_strings[1], tag_strings[2]);
4602286d8edStholo   else if (merge)
4612286d8edStholo     {
462892c0aadStholo       FILE *mfp = fopen (file[rev_mapping[FILE0]], "r");
463892c0aadStholo       if (! mfp)
4642286d8edStholo 	diff3_perror_with_exit (file[rev_mapping[FILE0]]);
465892c0aadStholo       conflicts_found = output_diff3_merge (mfp, diff3, mapping, rev_mapping,
4662286d8edStholo 			      tag_strings[0], tag_strings[1], tag_strings[2]);
467892c0aadStholo       if (ferror (mfp))
4682286d8edStholo 	diff3_fatal ("read error");
469892c0aadStholo       if (fclose(mfp) != 0)
470892c0aadStholo 	perror_with_name (file[rev_mapping[FILE0]]);
4712286d8edStholo     }
4722286d8edStholo   else
4732286d8edStholo     {
474b2346922Stholo       output_diff3 (diff3, mapping, rev_mapping);
4752286d8edStholo       conflicts_found = 0;
4762286d8edStholo     }
4772286d8edStholo 
4782286d8edStholo   free(content0);
4792286d8edStholo   free(content1);
4802286d8edStholo   free_diff3_blocks(diff3);
4812286d8edStholo 
482b2346922Stholo   if (! callbacks || ! callbacks->write_output)
483b2346922Stholo     check_output (outfile);
484b2346922Stholo 
485b2346922Stholo   if (opened_file)
486b2346922Stholo     if (fclose (outfile) != 0)
487b2346922Stholo 	perror_with_name ("close error on output file");
488b2346922Stholo 
4892286d8edStholo   return conflicts_found;
4902286d8edStholo }
4912286d8edStholo 
4922286d8edStholo static int
4932286d8edStholo try_help (reason)
4942286d8edStholo      char const *reason;
4952286d8edStholo {
4962286d8edStholo   if (reason)
497b2346922Stholo     diff_error ("%s", reason, 0);
498b2346922Stholo   diff_error ("Try `%s --help' for more information.", diff_program_name, 0);
4992286d8edStholo   return 2;
5002286d8edStholo }
5012286d8edStholo 
5022286d8edStholo static void
5032286d8edStholo check_output (stream)
5042286d8edStholo      FILE *stream;
5052286d8edStholo {
5062286d8edStholo   if (ferror (stream) || fflush (stream) != 0)
5072286d8edStholo     diff3_fatal ("write error");
5082286d8edStholo }
5092286d8edStholo 
5102286d8edStholo /*
5112286d8edStholo  * Explain, patiently and kindly, how to use this program.
5122286d8edStholo  */
5132286d8edStholo static void
5142286d8edStholo usage ()
5152286d8edStholo {
516b2346922Stholo   if (callbacks && callbacks->write_stdout)
517b2346922Stholo     {
518b2346922Stholo       (*callbacks->write_stdout) ("Usage: ");
519b2346922Stholo       (*callbacks->write_stdout) (diff_program_name);
520b2346922Stholo       (*callbacks->write_stdout) (" [OPTION]... MYFILE OLDFILE YOURFILE\n\n");
521b2346922Stholo 
522b2346922Stholo       (*callbacks->write_stdout) ("\
523b2346922Stholo   -e  --ed  Output unmerged changes from OLDFILE to YOURFILE into MYFILE.\n\
524b2346922Stholo   -E  --show-overlap  Output unmerged changes, bracketing conflicts.\n\
525b2346922Stholo   -A  --show-all  Output all changes, bracketing conflicts.\n\
526b2346922Stholo   -x  --overlap-only  Output overlapping changes.\n\
527b2346922Stholo   -X  Output overlapping changes, bracketing them.\n\
528b2346922Stholo   -3  --easy-only  Output unmerged nonoverlapping changes.\n\n");
529b2346922Stholo       (*callbacks->write_stdout) ("\
530b2346922Stholo   -m  --merge  Output merged file instead of ed script (default -A).\n\
531b2346922Stholo   -L LABEL  --label=LABEL  Use LABEL instead of file name.\n\
532b2346922Stholo   -i  Append `w' and `q' commands to ed scripts.\n\
533b2346922Stholo   -a  --text  Treat all files as text.\n\
534b2346922Stholo   -T  --initial-tab  Make tabs line up by prepending a tab.\n\n");
535b2346922Stholo       (*callbacks->write_stdout) ("\
536b2346922Stholo   -v  --version  Output version info.\n\
537b2346922Stholo   --help  Output this help.\n\n");
538b2346922Stholo       (*callbacks->write_stdout) ("If a FILE is `-', read standard input.\n");
539b2346922Stholo     }
540b2346922Stholo   else
541b2346922Stholo     {
5422286d8edStholo       printf ("Usage: %s [OPTION]... MYFILE OLDFILE YOURFILE\n\n", diff_program_name);
5432286d8edStholo 
5442286d8edStholo       printf ("%s", "\
5452286d8edStholo   -e  --ed  Output unmerged changes from OLDFILE to YOURFILE into MYFILE.\n\
5462286d8edStholo   -E  --show-overlap  Output unmerged changes, bracketing conflicts.\n\
5472286d8edStholo   -A  --show-all  Output all changes, bracketing conflicts.\n\
5482286d8edStholo   -x  --overlap-only  Output overlapping changes.\n\
5492286d8edStholo   -X  Output overlapping changes, bracketing them.\n\
5502286d8edStholo   -3  --easy-only  Output unmerged nonoverlapping changes.\n\n");
5512286d8edStholo       printf ("%s", "\
5522286d8edStholo   -m  --merge  Output merged file instead of ed script (default -A).\n\
5532286d8edStholo   -L LABEL  --label=LABEL  Use LABEL instead of file name.\n\
5542286d8edStholo   -i  Append `w' and `q' commands to ed scripts.\n\
5552286d8edStholo   -a  --text  Treat all files as text.\n\
5562286d8edStholo   -T  --initial-tab  Make tabs line up by prepending a tab.\n\n");
5572286d8edStholo       printf ("%s", "\
5582286d8edStholo   -v  --version  Output version info.\n\
5592286d8edStholo   --help  Output this help.\n\n");
5602286d8edStholo       printf ("If a FILE is `-', read standard input.\n");
5612286d8edStholo     }
562b2346922Stholo }
5632286d8edStholo 
5642286d8edStholo /*
5652286d8edStholo  * Routines that combine the two diffs together into one.  The
5662286d8edStholo  * algorithm used follows:
5672286d8edStholo  *
5682286d8edStholo  *   File2 is shared in common between the two diffs.
5692286d8edStholo  *   Diff02 is the diff between 0 and 2.
5702286d8edStholo  *   Diff12 is the diff between 1 and 2.
5712286d8edStholo  *
5722286d8edStholo  *	1) Find the range for the first block in File2.
5732286d8edStholo  *	    a) Take the lowest of the two ranges (in File2) in the two
5742286d8edStholo  *	       current blocks (one from each diff) as being the low
5752286d8edStholo  *	       water mark.  Assign the upper end of this block as
5762286d8edStholo  *	       being the high water mark and move the current block up
5772286d8edStholo  *	       one.  Mark the block just moved over as to be used.
5782286d8edStholo  *	    b) Check the next block in the diff that the high water
5792286d8edStholo  *	       mark is *not* from.
5802286d8edStholo  *
5812286d8edStholo  *	       *If* the high water mark is above
5822286d8edStholo  *	       the low end of the range in that block,
5832286d8edStholo  *
5842286d8edStholo  *		   mark that block as to be used and move the current
5852286d8edStholo  *		   block up.  Set the high water mark to the max of
5862286d8edStholo  *		   the high end of this block and the current.  Repeat b.
5872286d8edStholo  *
5882286d8edStholo  *	 2) Find the corresponding ranges in File0 (from the blocks
5892286d8edStholo  *	    in diff02; line per line outside of diffs) and in File1.
5902286d8edStholo  *	    Create a diff3_block, reserving space as indicated by the ranges.
5912286d8edStholo  *
5922286d8edStholo  *	 3) Copy all of the pointers for file2 in.  At least for now,
5932286d8edStholo  *	    do memcmp's between corresponding strings in the two diffs.
5942286d8edStholo  *
5952286d8edStholo  *	 4) Copy all of the pointers for file0 and 1 in.  Get what you
5962286d8edStholo  *	    need from file2 (when there isn't a diff block, it's
5972286d8edStholo  *	    identical to file2 within the range between diff blocks).
5982286d8edStholo  *
5992286d8edStholo  *	 5) If the diff blocks you used came from only one of the two
6002286d8edStholo  *	    strings of diffs, then that file (i.e. the one other than
6012286d8edStholo  *	    the common file in that diff) is the odd person out.  If you used
6022286d8edStholo  *	    diff blocks from both sets, check to see if files 0 and 1 match:
6032286d8edStholo  *
6042286d8edStholo  *		Same number of lines?  If so, do a set of memcmp's (if a
6052286d8edStholo  *	    memcmp matches; copy the pointer over; it'll be easier later
6062286d8edStholo  *	    if you have to do any compares).  If they match, 0 & 1 are
6072286d8edStholo  *	    the same.  If not, all three different.
6082286d8edStholo  *
6092286d8edStholo  *   Then you do it again, until you run out of blocks.
6102286d8edStholo  *
6112286d8edStholo  */
6122286d8edStholo 
6132286d8edStholo /*
6142286d8edStholo  * This routine makes a three way diff (chain of diff3_block's) from two
6152286d8edStholo  * two way diffs (chains of diff_block's).  It is assumed that each of
6162286d8edStholo  * the two diffs passed are onto the same file (i.e. that each of the
6172286d8edStholo  * diffs were made "to" the same file).  The three way diff pointer
6182286d8edStholo  * returned will have numbering FILE0--the other file in diff02,
6192286d8edStholo  * FILE1--the other file in diff12, and FILEC--the common file.
6202286d8edStholo  */
6212286d8edStholo static struct diff3_block *
6222286d8edStholo make_3way_diff (thread0, thread1)
6232286d8edStholo      struct diff_block *thread0, *thread1;
6242286d8edStholo {
6252286d8edStholo /*
6262286d8edStholo  * This routine works on the two diffs passed to it as threads.
6272286d8edStholo  * Thread number 0 is diff02, thread number 1 is diff12.  The USING
6282286d8edStholo  * array is set to the base of the list of blocks to be used to
6292286d8edStholo  * construct each block of the three way diff; if no blocks from a
6302286d8edStholo  * particular thread are to be used, that element of the using array
6312286d8edStholo  * is set to 0.  The elements LAST_USING array are set to the last
6322286d8edStholo  * elements on each of the using lists.
6332286d8edStholo  *
6342286d8edStholo  * The HIGH_WATER_MARK is set to the highest line number in the common file
6352286d8edStholo  * described in any of the diffs in either of the USING lists.  The
6362286d8edStholo  * HIGH_WATER_THREAD names the thread.  Similarly the BASE_WATER_MARK
6372286d8edStholo  * and BASE_WATER_THREAD describe the lowest line number in the common file
6382286d8edStholo  * described in any of the diffs in either of the USING lists.  The
6392286d8edStholo  * HIGH_WATER_DIFF is the diff from which the HIGH_WATER_MARK was
6402286d8edStholo  * taken.
6412286d8edStholo  *
6422286d8edStholo  * The HIGH_WATER_DIFF should always be equal to LAST_USING
6432286d8edStholo  * [HIGH_WATER_THREAD].  The OTHER_DIFF is the next diff to check for
6442286d8edStholo  * higher water, and should always be equal to
6452286d8edStholo  * CURRENT[HIGH_WATER_THREAD ^ 0x1].  The OTHER_THREAD is the thread
6462286d8edStholo  * in which the OTHER_DIFF is, and hence should always be equal to
6472286d8edStholo  * HIGH_WATER_THREAD ^ 0x1.
6482286d8edStholo  *
6492286d8edStholo  * The variable LAST_DIFF is kept set to the last diff block produced
6502286d8edStholo  * by this routine, for line correspondence purposes between that diff
6512286d8edStholo  * and the one currently being worked on.  It is initialized to
6522286d8edStholo  * ZERO_DIFF before any blocks have been created.
6532286d8edStholo  */
6542286d8edStholo 
6552286d8edStholo   struct diff_block
6562286d8edStholo     *using[2],
6572286d8edStholo     *last_using[2],
6582286d8edStholo     *current[2];
6592286d8edStholo 
6602286d8edStholo   int
6612286d8edStholo     high_water_mark;
6622286d8edStholo 
6632286d8edStholo   int
6642286d8edStholo     high_water_thread,
6652286d8edStholo     base_water_thread,
6662286d8edStholo     other_thread;
6672286d8edStholo 
6682286d8edStholo   struct diff_block
6692286d8edStholo     *high_water_diff,
6702286d8edStholo     *other_diff;
6712286d8edStholo 
6722286d8edStholo   struct diff3_block
6732286d8edStholo     *result,
6742286d8edStholo     *tmpblock,
6752286d8edStholo     **result_end;
6762286d8edStholo 
6772286d8edStholo   struct diff3_block const *last_diff3;
6782286d8edStholo 
679*e77048c1Stholo   static struct diff3_block const zero_diff3 = { 0 };
6802286d8edStholo 
6812286d8edStholo   /* Initialization */
6822286d8edStholo   result = 0;
6832286d8edStholo   result_end = &result;
6842286d8edStholo   current[0] = thread0; current[1] = thread1;
6852286d8edStholo   last_diff3 = &zero_diff3;
6862286d8edStholo 
6872286d8edStholo   /* Sniff up the threads until we reach the end */
6882286d8edStholo 
6892286d8edStholo   while (current[0] || current[1])
6902286d8edStholo     {
6912286d8edStholo       using[0] = using[1] = last_using[0] = last_using[1] = 0;
6922286d8edStholo 
6932286d8edStholo       /* Setup low and high water threads, diffs, and marks.  */
6942286d8edStholo       if (!current[0])
6952286d8edStholo 	base_water_thread = 1;
6962286d8edStholo       else if (!current[1])
6972286d8edStholo 	base_water_thread = 0;
6982286d8edStholo       else
6992286d8edStholo 	base_water_thread =
7002286d8edStholo 	  (D_LOWLINE (current[0], FC) > D_LOWLINE (current[1], FC));
7012286d8edStholo 
7022286d8edStholo       high_water_thread = base_water_thread;
7032286d8edStholo 
7042286d8edStholo       high_water_diff = current[high_water_thread];
7052286d8edStholo 
7062286d8edStholo #if 0
7072286d8edStholo       /* low and high waters start off same diff */
7082286d8edStholo       base_water_mark = D_LOWLINE (high_water_diff, FC);
7092286d8edStholo #endif
7102286d8edStholo 
7112286d8edStholo       high_water_mark = D_HIGHLINE (high_water_diff, FC);
7122286d8edStholo 
7132286d8edStholo       /* Make the diff you just got info from into the using class */
7142286d8edStholo       using[high_water_thread]
7152286d8edStholo 	= last_using[high_water_thread]
7162286d8edStholo 	= high_water_diff;
7172286d8edStholo       current[high_water_thread] = high_water_diff->next;
7182286d8edStholo       last_using[high_water_thread]->next = 0;
7192286d8edStholo 
7202286d8edStholo       /* And mark the other diff */
7212286d8edStholo       other_thread = high_water_thread ^ 0x1;
7222286d8edStholo       other_diff = current[other_thread];
7232286d8edStholo 
7242286d8edStholo       /* Shuffle up the ladder, checking the other diff to see if it
7252286d8edStholo 	 needs to be incorporated.  */
7262286d8edStholo       while (other_diff
7272286d8edStholo 	     && D_LOWLINE (other_diff, FC) <= high_water_mark + 1)
7282286d8edStholo 	{
7292286d8edStholo 
7302286d8edStholo 	  /* Incorporate this diff into the using list.  Note that
7312286d8edStholo 	     this doesn't take it off the current list */
7322286d8edStholo 	  if (using[other_thread])
7332286d8edStholo 	    last_using[other_thread]->next = other_diff;
7342286d8edStholo 	  else
7352286d8edStholo 	    using[other_thread] = other_diff;
7362286d8edStholo 	  last_using[other_thread] = other_diff;
7372286d8edStholo 
7382286d8edStholo 	  /* Take it off the current list.  Note that this following
7392286d8edStholo 	     code assumes that other_diff enters it equal to
7402286d8edStholo 	     current[high_water_thread ^ 0x1] */
7412286d8edStholo 	  current[other_thread] = current[other_thread]->next;
7422286d8edStholo 	  other_diff->next = 0;
7432286d8edStholo 
7442286d8edStholo 	  /* Set the high_water stuff
7452286d8edStholo 	     If this comparison is equal, then this is the last pass
7462286d8edStholo 	     through this loop; since diff blocks within a given
7472286d8edStholo 	     thread cannot overlap, the high_water_mark will be
7482286d8edStholo 	     *below* the range_start of either of the next diffs.  */
7492286d8edStholo 
7502286d8edStholo 	  if (high_water_mark < D_HIGHLINE (other_diff, FC))
7512286d8edStholo 	    {
7522286d8edStholo 	      high_water_thread ^= 1;
7532286d8edStholo 	      high_water_diff = other_diff;
7542286d8edStholo 	      high_water_mark = D_HIGHLINE (other_diff, FC);
7552286d8edStholo 	    }
7562286d8edStholo 
7572286d8edStholo 	  /* Set the other diff */
7582286d8edStholo 	  other_thread = high_water_thread ^ 0x1;
7592286d8edStholo 	  other_diff = current[other_thread];
7602286d8edStholo 	}
7612286d8edStholo 
7622286d8edStholo       /* The using lists contain a list of all of the blocks to be
7632286d8edStholo 	 included in this diff3_block.  Create it.  */
7642286d8edStholo 
7652286d8edStholo       tmpblock = using_to_diff3_block (using, last_using,
7662286d8edStholo 				       base_water_thread, high_water_thread,
7672286d8edStholo 				       last_diff3);
768*e77048c1Stholo       free_diff_blocks(using[0]);
769*e77048c1Stholo       free_diff_blocks(using[1]);
7702286d8edStholo 
7712286d8edStholo       if (!tmpblock)
7722286d8edStholo 	diff3_fatal ("internal error: screwup in format of diff blocks");
7732286d8edStholo 
7742286d8edStholo       /* Put it on the list.  */
7752286d8edStholo       *result_end = tmpblock;
7762286d8edStholo       result_end = &tmpblock->next;
7772286d8edStholo 
7782286d8edStholo       /* Set up corresponding lines correctly.  */
7792286d8edStholo       last_diff3 = tmpblock;
7802286d8edStholo     }
7812286d8edStholo   return result;
7822286d8edStholo }
7832286d8edStholo 
7842286d8edStholo /*
7852286d8edStholo  * using_to_diff3_block:
7862286d8edStholo  *   This routine takes two lists of blocks (from two separate diff
7872286d8edStholo  * threads) and puts them together into one diff3 block.
7882286d8edStholo  * It then returns a pointer to this diff3 block or 0 for failure.
7892286d8edStholo  *
7902286d8edStholo  * All arguments besides using are for the convenience of the routine;
7912286d8edStholo  * they could be derived from the using array.
7922286d8edStholo  * LAST_USING is a pair of pointers to the last blocks in the using
7932286d8edStholo  * structure.
7942286d8edStholo  * LOW_THREAD and HIGH_THREAD tell which threads contain the lowest
7952286d8edStholo  * and highest line numbers for File0.
7962286d8edStholo  * last_diff3 contains the last diff produced in the calling routine.
7972286d8edStholo  * This is used for lines mappings which would still be identical to
7982286d8edStholo  * the state that diff ended in.
7992286d8edStholo  *
8002286d8edStholo  * A distinction should be made in this routine between the two diffs
8012286d8edStholo  * that are part of a normal two diff block, and the three diffs that
8022286d8edStholo  * are part of a diff3_block.
8032286d8edStholo  */
8042286d8edStholo static struct diff3_block *
8052286d8edStholo using_to_diff3_block (using, last_using, low_thread, high_thread, last_diff3)
8062286d8edStholo      struct diff_block
8072286d8edStholo        *using[2],
8082286d8edStholo        *last_using[2];
8092286d8edStholo      int low_thread, high_thread;
8102286d8edStholo      struct diff3_block const *last_diff3;
8112286d8edStholo {
8122286d8edStholo   int low[2], high[2];
8132286d8edStholo   struct diff3_block *result;
8142286d8edStholo   struct diff_block *ptr;
8152286d8edStholo   int d, i;
8162286d8edStholo 
8172286d8edStholo   /* Find the range in the common file.  */
8182286d8edStholo   int lowc = D_LOWLINE (using[low_thread], FC);
8192286d8edStholo   int highc = D_HIGHLINE (last_using[high_thread], FC);
8202286d8edStholo 
8212286d8edStholo   /* Find the ranges in the other files.
8222286d8edStholo      If using[d] is null, that means that the file to which that diff
8232286d8edStholo      refers is equivalent to the common file over this range.  */
8242286d8edStholo 
8252286d8edStholo   for (d = 0; d < 2; d++)
8262286d8edStholo     if (using[d])
8272286d8edStholo       {
8282286d8edStholo 	low[d] = D_LOW_MAPLINE (using[d], FC, FO, lowc);
8292286d8edStholo 	high[d] = D_HIGH_MAPLINE (last_using[d], FC, FO, highc);
8302286d8edStholo       }
8312286d8edStholo     else
8322286d8edStholo       {
8332286d8edStholo 	low[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, lowc);
8342286d8edStholo 	high[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, highc);
8352286d8edStholo       }
8362286d8edStholo 
8372286d8edStholo   /* Create a block with the appropriate sizes */
8382286d8edStholo   result = create_diff3_block (low[0], high[0], low[1], high[1], lowc, highc);
8392286d8edStholo 
8402286d8edStholo   /* Copy information for the common file.
8412286d8edStholo      Return with a zero if any of the compares failed.  */
8422286d8edStholo 
8432286d8edStholo   for (d = 0; d < 2; d++)
8442286d8edStholo     for (ptr = using[d]; ptr; ptr = D_NEXT (ptr))
8452286d8edStholo       {
8462286d8edStholo 	int result_offset = D_LOWLINE (ptr, FC) - lowc;
8472286d8edStholo 
8482286d8edStholo 	if (!copy_stringlist (D_LINEARRAY (ptr, FC),
8492286d8edStholo 			      D_LENARRAY (ptr, FC),
8502286d8edStholo 			      D_LINEARRAY (result, FILEC) + result_offset,
8512286d8edStholo 			      D_LENARRAY (result, FILEC) + result_offset,
8522286d8edStholo 			      D_NUMLINES (ptr, FC)))
8532286d8edStholo 	  return 0;
8542286d8edStholo       }
8552286d8edStholo 
8562286d8edStholo   /* Copy information for file d.  First deal with anything that might be
8572286d8edStholo      before the first diff.  */
8582286d8edStholo 
8592286d8edStholo   for (d = 0; d < 2; d++)
8602286d8edStholo     {
8612286d8edStholo       struct diff_block *u = using[d];
8622286d8edStholo       int lo = low[d], hi = high[d];
8632286d8edStholo 
8642286d8edStholo       for (i = 0;
8652286d8edStholo 	   i + lo < (u ? D_LOWLINE (u, FO) : hi + 1);
8662286d8edStholo 	   i++)
8672286d8edStholo 	{
8682286d8edStholo 	  D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, i);
8692286d8edStholo 	  D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, i);
8702286d8edStholo 	}
8712286d8edStholo 
8722286d8edStholo       for (ptr = u; ptr; ptr = D_NEXT (ptr))
8732286d8edStholo 	{
8742286d8edStholo 	  int result_offset = D_LOWLINE (ptr, FO) - lo;
8752286d8edStholo 	  int linec;
8762286d8edStholo 
8772286d8edStholo 	  if (!copy_stringlist (D_LINEARRAY (ptr, FO),
8782286d8edStholo 				D_LENARRAY (ptr, FO),
8792286d8edStholo 				D_LINEARRAY (result, FILE0 + d) + result_offset,
8802286d8edStholo 				D_LENARRAY (result, FILE0 + d) + result_offset,
8812286d8edStholo 				D_NUMLINES (ptr, FO)))
8822286d8edStholo 	    return 0;
8832286d8edStholo 
8842286d8edStholo 	  /* Catch the lines between here and the next diff */
8852286d8edStholo 	  linec = D_HIGHLINE (ptr, FC) + 1 - lowc;
8862286d8edStholo 	  for (i = D_HIGHLINE (ptr, FO) + 1 - lo;
8872286d8edStholo 	       i < (D_NEXT (ptr) ? D_LOWLINE (D_NEXT (ptr), FO) : hi + 1) - lo;
8882286d8edStholo 	       i++)
8892286d8edStholo 	    {
8902286d8edStholo 	      D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, linec);
8912286d8edStholo 	      D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, linec);
8922286d8edStholo 	      linec++;
8932286d8edStholo 	    }
8942286d8edStholo 	}
8952286d8edStholo     }
8962286d8edStholo 
8972286d8edStholo   /* Set correspond */
8982286d8edStholo   if (!using[0])
8992286d8edStholo     D3_TYPE (result) = DIFF_2ND;
9002286d8edStholo   else if (!using[1])
9012286d8edStholo     D3_TYPE (result) = DIFF_1ST;
9022286d8edStholo   else
9032286d8edStholo     {
9042286d8edStholo       int nl0 = D_NUMLINES (result, FILE0);
9052286d8edStholo       int nl1 = D_NUMLINES (result, FILE1);
9062286d8edStholo 
9072286d8edStholo       if (nl0 != nl1
9082286d8edStholo 	  || !compare_line_list (D_LINEARRAY (result, FILE0),
9092286d8edStholo 				 D_LENARRAY (result, FILE0),
9102286d8edStholo 				 D_LINEARRAY (result, FILE1),
9112286d8edStholo 				 D_LENARRAY (result, FILE1),
9122286d8edStholo 				 nl0))
9132286d8edStholo 	D3_TYPE (result) = DIFF_ALL;
9142286d8edStholo       else
9152286d8edStholo 	D3_TYPE (result) = DIFF_3RD;
9162286d8edStholo     }
9172286d8edStholo 
9182286d8edStholo   return result;
9192286d8edStholo }
9202286d8edStholo 
9212286d8edStholo /*
9222286d8edStholo  * This routine copies pointers from a list of strings to a different list
9232286d8edStholo  * of strings.  If a spot in the second list is already filled, it
9242286d8edStholo  * makes sure that it is filled with the same string; if not it
9252286d8edStholo  * returns 0, the copy incomplete.
9262286d8edStholo  * Upon successful completion of the copy, it returns 1.
9272286d8edStholo  */
9282286d8edStholo static int
9292286d8edStholo copy_stringlist (fromptrs, fromlengths, toptrs, tolengths, copynum)
9302286d8edStholo      char * const fromptrs[];
9312286d8edStholo      char *toptrs[];
9322286d8edStholo      size_t const fromlengths[];
9332286d8edStholo      size_t tolengths[];
9342286d8edStholo      int copynum;
9352286d8edStholo {
9362286d8edStholo   register char * const *f = fromptrs;
9372286d8edStholo   register char **t = toptrs;
9382286d8edStholo   register size_t const *fl = fromlengths;
9392286d8edStholo   register size_t *tl = tolengths;
9402286d8edStholo 
9412286d8edStholo   while (copynum--)
9422286d8edStholo     {
9432286d8edStholo       if (*t)
9442286d8edStholo 	{ if (*fl != *tl || memcmp (*f, *t, *fl)) return 0; }
9452286d8edStholo       else
9462286d8edStholo 	{ *t = *f ; *tl = *fl; }
9472286d8edStholo 
9482286d8edStholo       t++; f++; tl++; fl++;
9492286d8edStholo     }
9502286d8edStholo   return 1;
9512286d8edStholo }
9522286d8edStholo 
9532286d8edStholo /*
9542286d8edStholo  * Create a diff3_block, with ranges as specified in the arguments.
9552286d8edStholo  * Allocate the arrays for the various pointers (and zero them) based
9562286d8edStholo  * on the arguments passed.  Return the block as a result.
9572286d8edStholo  */
9582286d8edStholo static struct diff3_block *
9592286d8edStholo create_diff3_block (low0, high0, low1, high1, low2, high2)
9602286d8edStholo      register int low0, high0, low1, high1, low2, high2;
9612286d8edStholo {
9622286d8edStholo   struct diff3_block *result = ALLOCATE (1, struct diff3_block);
9632286d8edStholo   int numlines;
9642286d8edStholo 
9652286d8edStholo   D3_TYPE (result) = ERROR;
9662286d8edStholo   D_NEXT (result) = 0;
9672286d8edStholo 
9682286d8edStholo   /* Assign ranges */
9692286d8edStholo   D_LOWLINE (result, FILE0) = low0;
9702286d8edStholo   D_HIGHLINE (result, FILE0) = high0;
9712286d8edStholo   D_LOWLINE (result, FILE1) = low1;
9722286d8edStholo   D_HIGHLINE (result, FILE1) = high1;
9732286d8edStholo   D_LOWLINE (result, FILE2) = low2;
9742286d8edStholo   D_HIGHLINE (result, FILE2) = high2;
9752286d8edStholo 
9762286d8edStholo   /* Allocate and zero space */
9772286d8edStholo   numlines = D_NUMLINES (result, FILE0);
9782286d8edStholo   if (numlines)
9792286d8edStholo     {
9802286d8edStholo       D_LINEARRAY (result, FILE0) = ALLOCATE (numlines, char *);
9812286d8edStholo       D_LENARRAY (result, FILE0) = ALLOCATE (numlines, size_t);
9822286d8edStholo       bzero (D_LINEARRAY (result, FILE0), (numlines * sizeof (char *)));
9832286d8edStholo       bzero (D_LENARRAY (result, FILE0), (numlines * sizeof (size_t)));
9842286d8edStholo     }
9852286d8edStholo   else
9862286d8edStholo     {
9872286d8edStholo       D_LINEARRAY (result, FILE0) = 0;
9882286d8edStholo       D_LENARRAY (result, FILE0) = 0;
9892286d8edStholo     }
9902286d8edStholo 
9912286d8edStholo   numlines = D_NUMLINES (result, FILE1);
9922286d8edStholo   if (numlines)
9932286d8edStholo     {
9942286d8edStholo       D_LINEARRAY (result, FILE1) = ALLOCATE (numlines, char *);
9952286d8edStholo       D_LENARRAY (result, FILE1) = ALLOCATE (numlines, size_t);
9962286d8edStholo       bzero (D_LINEARRAY (result, FILE1), (numlines * sizeof (char *)));
9972286d8edStholo       bzero (D_LENARRAY (result, FILE1), (numlines * sizeof (size_t)));
9982286d8edStholo     }
9992286d8edStholo   else
10002286d8edStholo     {
10012286d8edStholo       D_LINEARRAY (result, FILE1) = 0;
10022286d8edStholo       D_LENARRAY (result, FILE1) = 0;
10032286d8edStholo     }
10042286d8edStholo 
10052286d8edStholo   numlines = D_NUMLINES (result, FILE2);
10062286d8edStholo   if (numlines)
10072286d8edStholo     {
10082286d8edStholo       D_LINEARRAY (result, FILE2) = ALLOCATE (numlines, char *);
10092286d8edStholo       D_LENARRAY (result, FILE2) = ALLOCATE (numlines, size_t);
10102286d8edStholo       bzero (D_LINEARRAY (result, FILE2), (numlines * sizeof (char *)));
10112286d8edStholo       bzero (D_LENARRAY (result, FILE2), (numlines * sizeof (size_t)));
10122286d8edStholo     }
10132286d8edStholo   else
10142286d8edStholo     {
10152286d8edStholo       D_LINEARRAY (result, FILE2) = 0;
10162286d8edStholo       D_LENARRAY (result, FILE2) = 0;
10172286d8edStholo     }
10182286d8edStholo 
10192286d8edStholo   /* Return */
10202286d8edStholo   return result;
10212286d8edStholo }
10222286d8edStholo 
10232286d8edStholo /*
10242286d8edStholo  * Compare two lists of lines of text.
10252286d8edStholo  * Return 1 if they are equivalent, 0 if not.
10262286d8edStholo  */
10272286d8edStholo static int
10282286d8edStholo compare_line_list (list1, lengths1, list2, lengths2, nl)
10292286d8edStholo      char * const list1[], * const list2[];
10302286d8edStholo      size_t const lengths1[], lengths2[];
10312286d8edStholo      int nl;
10322286d8edStholo {
10332286d8edStholo   char
10342286d8edStholo     * const *l1 = list1,
10352286d8edStholo     * const *l2 = list2;
10362286d8edStholo   size_t const
10372286d8edStholo     *lgths1 = lengths1,
10382286d8edStholo     *lgths2 = lengths2;
10392286d8edStholo 
10402286d8edStholo   while (nl--)
10412286d8edStholo     if (!*l1 || !*l2 || *lgths1 != *lgths2++
10422286d8edStholo 	|| memcmp (*l1++, *l2++, *lgths1++))
10432286d8edStholo       return 0;
10442286d8edStholo   return 1;
10452286d8edStholo }
10462286d8edStholo 
10472286d8edStholo /*
10482286d8edStholo  * Routines to input and parse two way diffs.
10492286d8edStholo  */
10502286d8edStholo 
10512286d8edStholo extern char **environ;
10522286d8edStholo 
10532286d8edStholo static struct diff_block *
10542286d8edStholo process_diff (filea, fileb, last_block, diff_contents)
10552286d8edStholo      char const *filea, *fileb;
10562286d8edStholo      struct diff_block **last_block;
10572286d8edStholo      char **diff_contents;
10582286d8edStholo {
10592286d8edStholo   char *diff_limit;
10602286d8edStholo   char *scan_diff;
10612286d8edStholo   enum diff_type dt;
10622286d8edStholo   int i;
10632286d8edStholo   struct diff_block *block_list, **block_list_end, *bptr;
10642286d8edStholo 
10652286d8edStholo   diff_limit = read_diff (filea, fileb, diff_contents);
10662286d8edStholo   scan_diff = *diff_contents;
10672286d8edStholo   block_list_end = &block_list;
10682286d8edStholo   bptr = 0; /* Pacify `gcc -W'.  */
10692286d8edStholo 
10702286d8edStholo   while (scan_diff < diff_limit)
10712286d8edStholo     {
10722286d8edStholo       bptr = ALLOCATE (1, struct diff_block);
10732286d8edStholo       bptr->lines[0] = bptr->lines[1] = 0;
10742286d8edStholo       bptr->lengths[0] = bptr->lengths[1] = 0;
10752286d8edStholo 
10762286d8edStholo       dt = process_diff_control (&scan_diff, bptr);
10772286d8edStholo       if (dt == ERROR || *scan_diff != '\n')
10782286d8edStholo 	{
1079b2346922Stholo 	  char *serr;
1080b2346922Stholo 
1081b2346922Stholo 	  for (serr = scan_diff; *serr != '\n'; serr++)
1082b2346922Stholo 	    ;
1083b2346922Stholo 	  *serr = '\0';
1084b2346922Stholo 	  diff_error ("diff error: %s", scan_diff, 0);
1085b2346922Stholo 	  *serr = '\n';
10862286d8edStholo 	  DIFF3_ABORT (2);
10872286d8edStholo 	}
10882286d8edStholo       scan_diff++;
10892286d8edStholo 
10902286d8edStholo       /* Force appropriate ranges to be null, if necessary */
10912286d8edStholo       switch (dt)
10922286d8edStholo 	{
10932286d8edStholo 	case ADD:
10942286d8edStholo 	  bptr->ranges[0][0]++;
10952286d8edStholo 	  break;
10962286d8edStholo 	case DELETE:
10972286d8edStholo 	  bptr->ranges[1][0]++;
10982286d8edStholo 	  break;
10992286d8edStholo 	case CHANGE:
11002286d8edStholo 	  break;
11012286d8edStholo 	default:
11022286d8edStholo 	  diff3_fatal ("internal error: invalid diff type in process_diff");
11032286d8edStholo 	  break;
11042286d8edStholo 	}
11052286d8edStholo 
11062286d8edStholo       /* Allocate space for the pointers for the lines from filea, and
11072286d8edStholo 	 parcel them out among these pointers */
11082286d8edStholo       if (dt != ADD)
11092286d8edStholo 	{
11102286d8edStholo 	  int numlines = D_NUMLINES (bptr, 0);
11112286d8edStholo 	  bptr->lines[0] = ALLOCATE (numlines, char *);
11122286d8edStholo 	  bptr->lengths[0] = ALLOCATE (numlines, size_t);
11132286d8edStholo 	  for (i = 0; i < numlines; i++)
11142286d8edStholo 	    scan_diff = scan_diff_line (scan_diff,
11152286d8edStholo 					&(bptr->lines[0][i]),
11162286d8edStholo 					&(bptr->lengths[0][i]),
11172286d8edStholo 					diff_limit,
11182286d8edStholo 					'<');
11192286d8edStholo 	}
11202286d8edStholo 
11212286d8edStholo       /* Get past the separator for changes */
11222286d8edStholo       if (dt == CHANGE)
11232286d8edStholo 	{
11242286d8edStholo 	  if (strncmp (scan_diff, "---\n", 4))
11252286d8edStholo 	    diff3_fatal ("invalid diff format; invalid change separator");
11262286d8edStholo 	  scan_diff += 4;
11272286d8edStholo 	}
11282286d8edStholo 
11292286d8edStholo       /* Allocate space for the pointers for the lines from fileb, and
11302286d8edStholo 	 parcel them out among these pointers */
11312286d8edStholo       if (dt != DELETE)
11322286d8edStholo 	{
11332286d8edStholo 	  int numlines = D_NUMLINES (bptr, 1);
11342286d8edStholo 	  bptr->lines[1] = ALLOCATE (numlines, char *);
11352286d8edStholo 	  bptr->lengths[1] = ALLOCATE (numlines, size_t);
11362286d8edStholo 	  for (i = 0; i < numlines; i++)
11372286d8edStholo 	    scan_diff = scan_diff_line (scan_diff,
11382286d8edStholo 					&(bptr->lines[1][i]),
11392286d8edStholo 					&(bptr->lengths[1][i]),
11402286d8edStholo 					diff_limit,
11412286d8edStholo 					'>');
11422286d8edStholo 	}
11432286d8edStholo 
11442286d8edStholo       /* Place this block on the blocklist.  */
11452286d8edStholo       *block_list_end = bptr;
11462286d8edStholo       block_list_end = &bptr->next;
11472286d8edStholo     }
11482286d8edStholo 
11492286d8edStholo   *block_list_end = 0;
11502286d8edStholo   *last_block = bptr;
11512286d8edStholo   return block_list;
11522286d8edStholo }
11532286d8edStholo 
11542286d8edStholo /*
11552286d8edStholo  * This routine will parse a normal format diff control string.  It
11562286d8edStholo  * returns the type of the diff (ERROR if the format is bad).  All of
11572286d8edStholo  * the other important information is filled into to the structure
11582286d8edStholo  * pointed to by db, and the string pointer (whose location is passed
11592286d8edStholo  * to this routine) is updated to point beyond the end of the string
11602286d8edStholo  * parsed.  Note that only the ranges in the diff_block will be set by
11612286d8edStholo  * this routine.
11622286d8edStholo  *
11632286d8edStholo  * If some specific pair of numbers has been reduced to a single
11642286d8edStholo  * number, then both corresponding numbers in the diff block are set
11652286d8edStholo  * to that number.  In general these numbers are interpetted as ranges
11662286d8edStholo  * inclusive, unless being used by the ADD or DELETE commands.  It is
11672286d8edStholo  * assumed that these will be special cased in a superior routine.
11682286d8edStholo  */
11692286d8edStholo 
11702286d8edStholo static enum diff_type
11712286d8edStholo process_diff_control (string, db)
11722286d8edStholo      char **string;
11732286d8edStholo      struct diff_block *db;
11742286d8edStholo {
11752286d8edStholo   char *s = *string;
11762286d8edStholo   int holdnum;
11772286d8edStholo   enum diff_type type;
11782286d8edStholo 
11792286d8edStholo /* These macros are defined here because they can use variables
11802286d8edStholo    defined in this function.  Don't try this at home kids, we're
11812286d8edStholo    trained professionals!
11822286d8edStholo 
11832286d8edStholo    Also note that SKIPWHITE only recognizes tabs and spaces, and
11842286d8edStholo    that READNUM can only read positive, integral numbers */
11852286d8edStholo 
11862286d8edStholo #define	SKIPWHITE(s)	{ while (*s == ' ' || *s == '\t') s++; }
11872286d8edStholo #define	READNUM(s, num)	\
11882286d8edStholo 	{ unsigned char c = *s; if (!ISDIGIT (c)) return ERROR; holdnum = 0; \
11892286d8edStholo 	  do { holdnum = (c - '0' + holdnum * 10); }	\
11902286d8edStholo 	  while (ISDIGIT (c = *++s)); (num) = holdnum; }
11912286d8edStholo 
11922286d8edStholo   /* Read first set of digits */
11932286d8edStholo   SKIPWHITE (s);
11942286d8edStholo   READNUM (s, db->ranges[0][START]);
11952286d8edStholo 
11962286d8edStholo   /* Was that the only digit? */
11972286d8edStholo   SKIPWHITE (s);
11982286d8edStholo   if (*s == ',')
11992286d8edStholo     {
12002286d8edStholo       /* Get the next digit */
12012286d8edStholo       s++;
12022286d8edStholo       READNUM (s, db->ranges[0][END]);
12032286d8edStholo     }
12042286d8edStholo   else
12052286d8edStholo     db->ranges[0][END] = db->ranges[0][START];
12062286d8edStholo 
12072286d8edStholo   /* Get the letter */
12082286d8edStholo   SKIPWHITE (s);
12092286d8edStholo   switch (*s)
12102286d8edStholo     {
12112286d8edStholo     case 'a':
12122286d8edStholo       type = ADD;
12132286d8edStholo       break;
12142286d8edStholo     case 'c':
12152286d8edStholo       type = CHANGE;
12162286d8edStholo       break;
12172286d8edStholo     case 'd':
12182286d8edStholo       type = DELETE;
12192286d8edStholo       break;
12202286d8edStholo     default:
12212286d8edStholo       return ERROR;			/* Bad format */
12222286d8edStholo     }
12232286d8edStholo   s++;				/* Past letter */
12242286d8edStholo 
12252286d8edStholo   /* Read second set of digits */
12262286d8edStholo   SKIPWHITE (s);
12272286d8edStholo   READNUM (s, db->ranges[1][START]);
12282286d8edStholo 
12292286d8edStholo   /* Was that the only digit? */
12302286d8edStholo   SKIPWHITE (s);
12312286d8edStholo   if (*s == ',')
12322286d8edStholo     {
12332286d8edStholo       /* Get the next digit */
12342286d8edStholo       s++;
12352286d8edStholo       READNUM (s, db->ranges[1][END]);
12362286d8edStholo       SKIPWHITE (s);		/* To move to end */
12372286d8edStholo     }
12382286d8edStholo   else
12392286d8edStholo     db->ranges[1][END] = db->ranges[1][START];
12402286d8edStholo 
12412286d8edStholo   *string = s;
12422286d8edStholo   return type;
12432286d8edStholo }
12442286d8edStholo 
12452286d8edStholo static char *
12462286d8edStholo read_diff (filea, fileb, output_placement)
12472286d8edStholo      char const *filea, *fileb;
12482286d8edStholo      char **output_placement;
12492286d8edStholo {
12502286d8edStholo   char *diff_result;
12512286d8edStholo   size_t bytes, current_chunk_size, total;
12522286d8edStholo   int fd, wstatus;
12532286d8edStholo   struct stat pipestat;
1254b2346922Stholo   FILE *outfile_hold;
1255b2346922Stholo   const struct diff_callbacks *callbacks_hold;
1256b2346922Stholo   struct diff_callbacks my_callbacks;
1257b2346922Stholo   struct diff_callbacks *my_callbacks_arg;
12582286d8edStholo 
12592286d8edStholo   /* 302 / 1000 is log10(2.0) rounded up.  Subtract 1 for the sign bit;
12602286d8edStholo      add 1 for integer division truncation; add 1 more for a minus sign.  */
12612286d8edStholo #define INT_STRLEN_BOUND(type) ((sizeof(type)*CHAR_BIT - 1) * 302 / 1000 + 2)
12622286d8edStholo 
12632286d8edStholo   char const *argv[7];
12642286d8edStholo   char horizon_arg[17 + INT_STRLEN_BOUND (int)];
12652286d8edStholo   char const **ap;
12662286d8edStholo   char *diffout;
12672286d8edStholo 
12682286d8edStholo   ap = argv;
12692286d8edStholo   *ap++ = "diff";
12702286d8edStholo   if (always_text)
12712286d8edStholo     *ap++ = "-a";
12722286d8edStholo   sprintf (horizon_arg, "--horizon-lines=%d", horizon_lines);
12732286d8edStholo   *ap++ = horizon_arg;
12742286d8edStholo   *ap++ = "--";
12752286d8edStholo   *ap++ = filea;
12762286d8edStholo   *ap++ = fileb;
12772286d8edStholo   *ap = 0;
12782286d8edStholo 
1279*e77048c1Stholo   diffout = cvs_temp_name ();
1280b2346922Stholo 
1281b2346922Stholo   outfile_hold = outfile;
1282b2346922Stholo   callbacks_hold = callbacks;
1283b2346922Stholo 
1284b2346922Stholo   /* We want to call diff_run preserving any stdout and stderr
1285b2346922Stholo      callbacks, but discarding any callbacks to handle file output,
1286b2346922Stholo      since we want the file output to go to our temporary file.
1287b2346922Stholo      FIXME: We should use callbacks to just read it into a memory
1288b2346922Stholo      buffer; that's we do with the temporary file just below anyhow.  */
1289b2346922Stholo   if (callbacks == NULL)
1290b2346922Stholo     my_callbacks_arg = NULL;
1291b2346922Stholo   else
1292b2346922Stholo     {
1293b2346922Stholo       my_callbacks = *callbacks;
1294b2346922Stholo       my_callbacks.write_output = NULL;
1295b2346922Stholo       my_callbacks.flush_output = NULL;
1296b2346922Stholo       my_callbacks_arg = &my_callbacks;
1297b2346922Stholo     }
1298b2346922Stholo 
1299b2346922Stholo   wstatus = diff_run (ap - argv, (char **) argv, diffout, my_callbacks_arg);
1300b2346922Stholo 
1301b2346922Stholo   outfile = outfile_hold;
1302b2346922Stholo   callbacks = callbacks_hold;
1303b2346922Stholo 
13042286d8edStholo   if (wstatus == 2)
13052286d8edStholo     diff3_fatal ("subsidiary diff failed");
13062286d8edStholo 
13072286d8edStholo   if (-1 == (fd = open (diffout, O_RDONLY)))
13082286d8edStholo     diff3_fatal ("could not open temporary diff file");
13092286d8edStholo 
13102286d8edStholo   current_chunk_size = 8 * 1024;
13112286d8edStholo   if (fstat (fd, &pipestat) == 0)
13122286d8edStholo     current_chunk_size = max (current_chunk_size, STAT_BLOCKSIZE (pipestat));
13132286d8edStholo 
13142286d8edStholo   diff_result = xmalloc (current_chunk_size);
13152286d8edStholo   total = 0;
13162286d8edStholo   do {
13172286d8edStholo     bytes = myread (fd,
13182286d8edStholo 		    diff_result + total,
13192286d8edStholo 		    current_chunk_size - total);
13202286d8edStholo     total += bytes;
13212286d8edStholo     if (total == current_chunk_size)
13222286d8edStholo       {
13232286d8edStholo 	if (current_chunk_size < 2 * current_chunk_size)
13242286d8edStholo 	  current_chunk_size = 2 * current_chunk_size;
13252286d8edStholo 	else if (current_chunk_size < (size_t) -1)
13262286d8edStholo 	  current_chunk_size = (size_t) -1;
13272286d8edStholo 	else
13282286d8edStholo 	  diff3_fatal ("files are too large to fit into memory");
13292286d8edStholo 	diff_result = xrealloc (diff_result, (current_chunk_size *= 2));
13302286d8edStholo       }
13312286d8edStholo   } while (bytes);
13322286d8edStholo 
13332286d8edStholo   if (total != 0 && diff_result[total-1] != '\n')
13342286d8edStholo     diff3_fatal ("invalid diff format; incomplete last line");
13352286d8edStholo 
13362286d8edStholo   *output_placement = diff_result;
13372286d8edStholo 
13382286d8edStholo   if (close (fd) != 0)
13392286d8edStholo     diff3_perror_with_exit ("pipe close");
13402286d8edStholo   unlink (diffout);
13412286d8edStholo 
13422286d8edStholo   return diff_result + total;
13432286d8edStholo }
13442286d8edStholo 
13452286d8edStholo 
13462286d8edStholo /*
13472286d8edStholo  * Scan a regular diff line (consisting of > or <, followed by a
13482286d8edStholo  * space, followed by text (including nulls) up to a newline.
13492286d8edStholo  *
13502286d8edStholo  * This next routine began life as a macro and many parameters in it
13512286d8edStholo  * are used as call-by-reference values.
13522286d8edStholo  */
13532286d8edStholo static char *
13542286d8edStholo scan_diff_line (scan_ptr, set_start, set_length, limit, leadingchar)
13552286d8edStholo      char *scan_ptr, **set_start;
13562286d8edStholo      size_t *set_length;
13572286d8edStholo      char *limit;
13582286d8edStholo      int leadingchar;
13592286d8edStholo {
13602286d8edStholo   char *line_ptr;
13612286d8edStholo 
13622286d8edStholo   if (!(scan_ptr[0] == leadingchar
13632286d8edStholo 	&& scan_ptr[1] == ' '))
13642286d8edStholo     diff3_fatal ("invalid diff format; incorrect leading line chars");
13652286d8edStholo 
13662286d8edStholo   *set_start = line_ptr = scan_ptr + 2;
13672286d8edStholo   while (*line_ptr++ != '\n')
13682286d8edStholo     ;
13692286d8edStholo 
13702286d8edStholo   /* Include newline if the original line ended in a newline,
13712286d8edStholo      or if an edit script is being generated.
13722286d8edStholo      Copy any missing newline message to stderr if an edit script is being
13732286d8edStholo      generated, because edit scripts cannot handle missing newlines.
13742286d8edStholo      Return the beginning of the next line.  */
13752286d8edStholo   *set_length = line_ptr - *set_start;
13762286d8edStholo   if (line_ptr < limit && *line_ptr == '\\')
13772286d8edStholo     {
1378b2346922Stholo       if (! edscript)
1379b2346922Stholo 	{
13802286d8edStholo 	  --*set_length;
13812286d8edStholo 	  line_ptr++;
1382b2346922Stholo 	  while (*line_ptr++ != '\n')
1383b2346922Stholo 	    ;
13842286d8edStholo 	}
1385b2346922Stholo       else
1386b2346922Stholo 	{
1387b2346922Stholo 	  char *serr;
1388b2346922Stholo 
1389b2346922Stholo 	  line_ptr++;
1390b2346922Stholo 	  serr = line_ptr;
1391b2346922Stholo 	  while (*line_ptr++ != '\n')
1392b2346922Stholo 	    ;
1393b2346922Stholo 	  line_ptr[-1] = '\0';
1394b2346922Stholo 	  diff_error ("%s", serr, 0);
1395b2346922Stholo 	  line_ptr[-1] = '\n';
1396b2346922Stholo 	}
13972286d8edStholo     }
13982286d8edStholo 
13992286d8edStholo   return line_ptr;
14002286d8edStholo }
14012286d8edStholo 
14022286d8edStholo /*
14032286d8edStholo  * This routine outputs a three way diff passed as a list of
14042286d8edStholo  * diff3_block's.
14052286d8edStholo  * The argument MAPPING is indexed by external file number (in the
14062286d8edStholo  * argument list) and contains the internal file number (from the
14072286d8edStholo  * diff passed).  This is important because the user expects his
14082286d8edStholo  * outputs in terms of the argument list number, and the diff passed
14092286d8edStholo  * may have been done slightly differently (if the last argument
14102286d8edStholo  * was "-", for example).
14112286d8edStholo  * REV_MAPPING is the inverse of MAPPING.
14122286d8edStholo  */
14132286d8edStholo static void
1414b2346922Stholo output_diff3 (diff, mapping, rev_mapping)
14152286d8edStholo      struct diff3_block *diff;
14162286d8edStholo      int const mapping[3], rev_mapping[3];
14172286d8edStholo {
14182286d8edStholo   int i;
14192286d8edStholo   int oddoneout;
14202286d8edStholo   char *cp;
14212286d8edStholo   struct diff3_block *ptr;
14222286d8edStholo   int line;
14232286d8edStholo   size_t length;
14242286d8edStholo   int dontprint;
14252286d8edStholo   static int skew_increment[3] = { 2, 3, 1 }; /* 0==>2==>1==>3 */
14262286d8edStholo   char const *line_prefix = tab_align_flag ? "\t" : "  ";
14272286d8edStholo 
14282286d8edStholo   for (ptr = diff; ptr; ptr = D_NEXT (ptr))
14292286d8edStholo     {
14302286d8edStholo       char x[2];
14312286d8edStholo 
14322286d8edStholo       switch (ptr->correspond)
14332286d8edStholo 	{
14342286d8edStholo 	case DIFF_ALL:
14352286d8edStholo 	  x[0] = '\0';
14362286d8edStholo 	  dontprint = 3;	/* Print them all */
14372286d8edStholo 	  oddoneout = 3;	/* Nobody's odder than anyone else */
14382286d8edStholo 	  break;
14392286d8edStholo 	case DIFF_1ST:
14402286d8edStholo 	case DIFF_2ND:
14412286d8edStholo 	case DIFF_3RD:
14422286d8edStholo 	  oddoneout = rev_mapping[(int) ptr->correspond - (int) DIFF_1ST];
14432286d8edStholo 
14442286d8edStholo 	  x[0] = oddoneout + '1';
14452286d8edStholo 	  x[1] = '\0';
14462286d8edStholo 	  dontprint = oddoneout==0;
14472286d8edStholo 	  break;
14482286d8edStholo 	default:
14492286d8edStholo 	  diff3_fatal ("internal error: invalid diff type passed to output");
14502286d8edStholo 	}
1451b2346922Stholo       printf_output ("====%s\n", x);
14522286d8edStholo 
14532286d8edStholo       /* Go 0, 2, 1 if the first and third outputs are equivalent.  */
14542286d8edStholo       for (i = 0; i < 3;
14552286d8edStholo 	   i = (oddoneout == 1 ? skew_increment[i] : i + 1))
14562286d8edStholo 	{
14572286d8edStholo 	  int realfile = mapping[i];
14582286d8edStholo 	  int
14592286d8edStholo 	    lowt = D_LOWLINE (ptr, realfile),
14602286d8edStholo 	    hight = D_HIGHLINE (ptr, realfile);
14612286d8edStholo 
1462b2346922Stholo 	  printf_output ("%d:", i + 1);
14632286d8edStholo 	  switch (lowt - hight)
14642286d8edStholo 	    {
14652286d8edStholo 	    case 1:
1466b2346922Stholo 	      printf_output ("%da\n", lowt - 1);
14672286d8edStholo 	      break;
14682286d8edStholo 	    case 0:
1469b2346922Stholo 	      printf_output ("%dc\n", lowt);
14702286d8edStholo 	      break;
14712286d8edStholo 	    default:
1472b2346922Stholo 	      printf_output ("%d,%dc\n", lowt, hight);
14732286d8edStholo 	      break;
14742286d8edStholo 	    }
14752286d8edStholo 
14762286d8edStholo 	  if (i == dontprint) continue;
14772286d8edStholo 
14782286d8edStholo 	  if (lowt <= hight)
14792286d8edStholo 	    {
14802286d8edStholo 	      line = 0;
14812286d8edStholo 	      do
14822286d8edStholo 		{
1483b2346922Stholo 		  printf_output (line_prefix);
14842286d8edStholo 		  cp = D_RELNUM (ptr, realfile, line);
14852286d8edStholo 		  length = D_RELLEN (ptr, realfile, line);
1486b2346922Stholo 		  write_output (cp, length);
14872286d8edStholo 		}
14882286d8edStholo 	      while (++line < hight - lowt + 1);
14892286d8edStholo 	      if (cp[length - 1] != '\n')
1490b2346922Stholo 		printf_output ("\n\\ No newline at end of file\n");
14912286d8edStholo 	    }
14922286d8edStholo 	}
14932286d8edStholo     }
14942286d8edStholo }
14952286d8edStholo 
14962286d8edStholo 
14972286d8edStholo /*
1498b2346922Stholo  * Output the lines of B taken from FILENUM.
14992286d8edStholo  * Double any initial '.'s; yield nonzero if any initial '.'s were doubled.
15002286d8edStholo  */
15012286d8edStholo static int
1502b2346922Stholo dotlines (b, filenum)
15032286d8edStholo      struct diff3_block *b;
15042286d8edStholo      int filenum;
15052286d8edStholo {
15062286d8edStholo   int i;
15072286d8edStholo   int leading_dot = 0;
15082286d8edStholo 
15092286d8edStholo   for (i = 0;
15102286d8edStholo        i < D_NUMLINES (b, filenum);
15112286d8edStholo        i++)
15122286d8edStholo     {
15132286d8edStholo       char *line = D_RELNUM (b, filenum, i);
15142286d8edStholo       if (line[0] == '.')
15152286d8edStholo 	{
15162286d8edStholo 	  leading_dot = 1;
1517b2346922Stholo 	  write_output (".", 1);
15182286d8edStholo 	}
1519b2346922Stholo       write_output (line, D_RELLEN (b, filenum, i));
15202286d8edStholo     }
15212286d8edStholo 
15222286d8edStholo   return leading_dot;
15232286d8edStholo }
15242286d8edStholo 
15252286d8edStholo /*
15262286d8edStholo  * Output to OUTPUTFILE a '.' line.  If LEADING_DOT is nonzero,
15272286d8edStholo  * also output a command that removes initial '.'s
15282286d8edStholo  * starting with line START and continuing for NUM lines.
15292286d8edStholo  */
15302286d8edStholo static void
1531b2346922Stholo undotlines (leading_dot, start, num)
15322286d8edStholo      int leading_dot, start, num;
15332286d8edStholo {
1534b2346922Stholo   write_output (".\n", 2);
15352286d8edStholo   if (leading_dot)
15362286d8edStholo     if (num == 1)
1537b2346922Stholo       printf_output ("%ds/^\\.//\n", start);
15382286d8edStholo     else
1539b2346922Stholo       printf_output ("%d,%ds/^\\.//\n", start, start + num - 1);
15402286d8edStholo }
15412286d8edStholo 
15422286d8edStholo /*
15432286d8edStholo  * This routine outputs a diff3 set of blocks as an ed script.  This
15442286d8edStholo  * script applies the changes between file's 2 & 3 to file 1.  It
15452286d8edStholo  * takes the precise format of the ed script to be output from global
15462286d8edStholo  * variables set during options processing.  Note that it does
15472286d8edStholo  * destructive things to the set of diff3 blocks it is passed; it
15482286d8edStholo  * reverses their order (this gets around the problems involved with
15492286d8edStholo  * changing line numbers in an ed script).
15502286d8edStholo  *
15512286d8edStholo  * Note that this routine has the same problem of mapping as the last
15522286d8edStholo  * one did; the variable MAPPING maps from file number according to
15532286d8edStholo  * the argument list to file number according to the diff passed.  All
15542286d8edStholo  * files listed below are in terms of the argument list.
15552286d8edStholo  * REV_MAPPING is the inverse of MAPPING.
15562286d8edStholo  *
15572286d8edStholo  * The arguments FILE0, FILE1 and FILE2 are the strings to print
15582286d8edStholo  * as the names of the three files.  These may be the actual names,
15592286d8edStholo  * or may be the arguments specified with -L.
15602286d8edStholo  *
15612286d8edStholo  * Returns 1 if conflicts were found.
15622286d8edStholo  */
15632286d8edStholo 
15642286d8edStholo static int
1565b2346922Stholo output_diff3_edscript (diff, mapping, rev_mapping, file0, file1, file2)
15662286d8edStholo      struct diff3_block *diff;
15672286d8edStholo      int const mapping[3], rev_mapping[3];
15682286d8edStholo      char const *file0, *file1, *file2;
15692286d8edStholo {
15702286d8edStholo   int leading_dot;
15712286d8edStholo   int conflicts_found = 0, conflict;
15722286d8edStholo   struct diff3_block *b;
15732286d8edStholo 
15742286d8edStholo   for (b = reverse_diff3_blocklist (diff); b; b = b->next)
15752286d8edStholo     {
15762286d8edStholo       /* Must do mapping correctly.  */
15772286d8edStholo       enum diff_type type
15782286d8edStholo 	= ((b->correspond == DIFF_ALL) ?
15792286d8edStholo 	   DIFF_ALL :
15802286d8edStholo 	   ((enum diff_type)
15812286d8edStholo 	    (((int) DIFF_1ST)
15822286d8edStholo 	     + rev_mapping[(int) b->correspond - (int) DIFF_1ST])));
15832286d8edStholo 
15842286d8edStholo       /* If we aren't supposed to do this output block, skip it.  */
15852286d8edStholo       switch (type)
15862286d8edStholo 	{
15872286d8edStholo 	default: continue;
15882286d8edStholo 	case DIFF_2ND: if (!show_2nd) continue; conflict = 1; break;
15892286d8edStholo 	case DIFF_3RD: if (overlap_only) continue; conflict = 0; break;
15902286d8edStholo 	case DIFF_ALL: if (simple_only) continue; conflict = flagging; break;
15912286d8edStholo 	}
15922286d8edStholo 
15932286d8edStholo       if (conflict)
15942286d8edStholo 	{
15952286d8edStholo 	  conflicts_found = 1;
15962286d8edStholo 
15972286d8edStholo 
15982286d8edStholo 	  /* Mark end of conflict.  */
15992286d8edStholo 
1600b2346922Stholo 	  printf_output ("%da\n", D_HIGHLINE (b, mapping[FILE0]));
16012286d8edStholo 	  leading_dot = 0;
16022286d8edStholo 	  if (type == DIFF_ALL)
16032286d8edStholo 	    {
16042286d8edStholo 	      if (show_2nd)
16052286d8edStholo 		{
16062286d8edStholo 		  /* Append lines from FILE1.  */
1607b2346922Stholo 		  printf_output ("||||||| %s\n", file1);
1608b2346922Stholo 		  leading_dot = dotlines (b, mapping[FILE1]);
16092286d8edStholo 		}
16102286d8edStholo 	      /* Append lines from FILE2.  */
1611b2346922Stholo 	      printf_output ("=======\n");
1612b2346922Stholo 	      leading_dot |= dotlines (b, mapping[FILE2]);
16132286d8edStholo 	    }
1614b2346922Stholo 	  printf_output (">>>>>>> %s\n", file2);
1615b2346922Stholo 	  undotlines (leading_dot,
16162286d8edStholo 		      D_HIGHLINE (b, mapping[FILE0]) + 2,
16172286d8edStholo 		      (D_NUMLINES (b, mapping[FILE1])
16182286d8edStholo 		       + D_NUMLINES (b, mapping[FILE2]) + 1));
16192286d8edStholo 
16202286d8edStholo 
16212286d8edStholo 	  /* Mark start of conflict.  */
16222286d8edStholo 
1623b2346922Stholo 	  printf_output ("%da\n<<<<<<< %s\n",
16242286d8edStholo 			 D_LOWLINE (b, mapping[FILE0]) - 1,
16252286d8edStholo 			 type == DIFF_ALL ? file0 : file1);
16262286d8edStholo 	  leading_dot = 0;
16272286d8edStholo 	  if (type == DIFF_2ND)
16282286d8edStholo 	    {
16292286d8edStholo 	      /* Prepend lines from FILE1.  */
1630b2346922Stholo 	      leading_dot = dotlines (b, mapping[FILE1]);
1631b2346922Stholo 	      printf_output ("=======\n");
16322286d8edStholo 	    }
1633b2346922Stholo 	  undotlines (leading_dot,
16342286d8edStholo 		      D_LOWLINE (b, mapping[FILE0]) + 1,
16352286d8edStholo 		      D_NUMLINES (b, mapping[FILE1]));
16362286d8edStholo 	}
16372286d8edStholo       else if (D_NUMLINES (b, mapping[FILE2]) == 0)
16382286d8edStholo 	/* Write out a delete */
16392286d8edStholo 	{
16402286d8edStholo 	  if (D_NUMLINES (b, mapping[FILE0]) == 1)
1641b2346922Stholo 	    printf_output ("%dd\n", D_LOWLINE (b, mapping[FILE0]));
16422286d8edStholo 	  else
1643b2346922Stholo 	    printf_output ("%d,%dd\n",
16442286d8edStholo 			   D_LOWLINE (b, mapping[FILE0]),
16452286d8edStholo 			   D_HIGHLINE (b, mapping[FILE0]));
16462286d8edStholo 	}
16472286d8edStholo       else
16482286d8edStholo 	/* Write out an add or change */
16492286d8edStholo 	{
16502286d8edStholo 	  switch (D_NUMLINES (b, mapping[FILE0]))
16512286d8edStholo 	    {
16522286d8edStholo 	    case 0:
1653b2346922Stholo 	      printf_output ("%da\n", D_HIGHLINE (b, mapping[FILE0]));
16542286d8edStholo 	      break;
16552286d8edStholo 	    case 1:
1656b2346922Stholo 	      printf_output ("%dc\n", D_HIGHLINE (b, mapping[FILE0]));
16572286d8edStholo 	      break;
16582286d8edStholo 	    default:
1659b2346922Stholo 	      printf_output ("%d,%dc\n",
16602286d8edStholo 			     D_LOWLINE (b, mapping[FILE0]),
16612286d8edStholo 			     D_HIGHLINE (b, mapping[FILE0]));
16622286d8edStholo 	      break;
16632286d8edStholo 	    }
16642286d8edStholo 
1665b2346922Stholo 	  undotlines (dotlines (b, mapping[FILE2]),
16662286d8edStholo 		      D_LOWLINE (b, mapping[FILE0]),
16672286d8edStholo 		      D_NUMLINES (b, mapping[FILE2]));
16682286d8edStholo 	}
16692286d8edStholo     }
1670b2346922Stholo   if (finalwrite) printf_output ("w\nq\n");
16712286d8edStholo   return conflicts_found;
16722286d8edStholo }
16732286d8edStholo 
16742286d8edStholo /*
1675b2346922Stholo  * Read from INFILE and output to the standard output file a set of
1676b2346922Stholo  * diff3_ blocks DIFF as a merged file.  This acts like 'ed file0
1677b2346922Stholo  * <[output_diff3_edscript]', except that it works even for binary
1678b2346922Stholo  * data or incomplete lines.
16792286d8edStholo  *
16802286d8edStholo  * As before, MAPPING maps from arg list file number to diff file number,
16812286d8edStholo  * REV_MAPPING is its inverse,
16822286d8edStholo  * and FILE0, FILE1, and FILE2 are the names of the files.
16832286d8edStholo  *
16842286d8edStholo  * Returns 1 if conflicts were found.
16852286d8edStholo  */
16862286d8edStholo 
16872286d8edStholo static int
1688b2346922Stholo output_diff3_merge (infile, diff, mapping, rev_mapping,
16892286d8edStholo 		    file0, file1, file2)
1690b2346922Stholo      FILE *infile;
16912286d8edStholo      struct diff3_block *diff;
16922286d8edStholo      int const mapping[3], rev_mapping[3];
16932286d8edStholo      char const *file0, *file1, *file2;
16942286d8edStholo {
16952286d8edStholo   int c, i;
1696b2346922Stholo   char cc;
16972286d8edStholo   int conflicts_found = 0, conflict;
16982286d8edStholo   struct diff3_block *b;
16992286d8edStholo   int linesread = 0;
17002286d8edStholo 
17012286d8edStholo   for (b = diff; b; b = b->next)
17022286d8edStholo     {
17032286d8edStholo       /* Must do mapping correctly.  */
17042286d8edStholo       enum diff_type type
17052286d8edStholo 	= ((b->correspond == DIFF_ALL) ?
17062286d8edStholo 	   DIFF_ALL :
17072286d8edStholo 	   ((enum diff_type)
17082286d8edStholo 	    (((int) DIFF_1ST)
17092286d8edStholo 	     + rev_mapping[(int) b->correspond - (int) DIFF_1ST])));
17102286d8edStholo       char const *format_2nd = "<<<<<<< %s\n";
17112286d8edStholo 
17122286d8edStholo       /* If we aren't supposed to do this output block, skip it.  */
17132286d8edStholo       switch (type)
17142286d8edStholo 	{
17152286d8edStholo 	default: continue;
17162286d8edStholo 	case DIFF_2ND: if (!show_2nd) continue; conflict = 1; break;
17172286d8edStholo 	case DIFF_3RD: if (overlap_only) continue; conflict = 0; break;
17182286d8edStholo 	case DIFF_ALL: if (simple_only) continue; conflict = flagging;
17192286d8edStholo 	  format_2nd = "||||||| %s\n";
17202286d8edStholo 	  break;
17212286d8edStholo 	}
17222286d8edStholo 
17232286d8edStholo       /* Copy I lines from file 0.  */
17242286d8edStholo       i = D_LOWLINE (b, FILE0) - linesread - 1;
17252286d8edStholo       linesread += i;
17262286d8edStholo       while (0 <= --i)
17272286d8edStholo 	do
17282286d8edStholo 	  {
17292286d8edStholo 	    c = getc (infile);
17302286d8edStholo 	    if (c == EOF)
17312286d8edStholo 	      if (ferror (infile))
17322286d8edStholo 		diff3_perror_with_exit ("input file");
17332286d8edStholo 	      else if (feof (infile))
17342286d8edStholo 		diff3_fatal ("input file shrank");
1735b2346922Stholo 	    cc = c;
1736b2346922Stholo 	    write_output (&cc, 1);
17372286d8edStholo 	  }
17382286d8edStholo 	while (c != '\n');
17392286d8edStholo 
17402286d8edStholo       if (conflict)
17412286d8edStholo 	{
17422286d8edStholo 	  conflicts_found = 1;
17432286d8edStholo 
17442286d8edStholo 	  if (type == DIFF_ALL)
17452286d8edStholo 	    {
17462286d8edStholo 	      /* Put in lines from FILE0 with bracket.  */
1747b2346922Stholo 	      printf_output ("<<<<<<< %s\n", file0);
17482286d8edStholo 	      for (i = 0;
17492286d8edStholo 		   i < D_NUMLINES (b, mapping[FILE0]);
17502286d8edStholo 		   i++)
1751b2346922Stholo 		write_output (D_RELNUM (b, mapping[FILE0], i),
1752b2346922Stholo 			      D_RELLEN (b, mapping[FILE0], i));
17532286d8edStholo 	    }
17542286d8edStholo 
17552286d8edStholo 	  if (show_2nd)
17562286d8edStholo 	    {
17572286d8edStholo 	      /* Put in lines from FILE1 with bracket.  */
1758b2346922Stholo 	      printf_output (format_2nd, file1);
17592286d8edStholo 	      for (i = 0;
17602286d8edStholo 		   i < D_NUMLINES (b, mapping[FILE1]);
17612286d8edStholo 		   i++)
1762b2346922Stholo 		write_output (D_RELNUM (b, mapping[FILE1], i),
1763b2346922Stholo 			      D_RELLEN (b, mapping[FILE1], i));
17642286d8edStholo 	    }
17652286d8edStholo 
1766b2346922Stholo 	  printf_output ("=======\n");
17672286d8edStholo 	}
17682286d8edStholo 
17692286d8edStholo       /* Put in lines from FILE2.  */
17702286d8edStholo       for (i = 0;
17712286d8edStholo 	   i < D_NUMLINES (b, mapping[FILE2]);
17722286d8edStholo 	   i++)
1773b2346922Stholo 	write_output (D_RELNUM (b, mapping[FILE2], i),
1774b2346922Stholo 		      D_RELLEN (b, mapping[FILE2], i));
17752286d8edStholo 
17762286d8edStholo       if (conflict)
1777b2346922Stholo 	printf_output (">>>>>>> %s\n", file2);
17782286d8edStholo 
17792286d8edStholo       /* Skip I lines in file 0.  */
17802286d8edStholo       i = D_NUMLINES (b, FILE0);
17812286d8edStholo       linesread += i;
17822286d8edStholo       while (0 <= --i)
17832286d8edStholo 	while ((c = getc (infile)) != '\n')
17842286d8edStholo 	  if (c == EOF)
17852286d8edStholo 	    if (ferror (infile))
17862286d8edStholo 	      diff3_perror_with_exit ("input file");
17872286d8edStholo 	    else if (feof (infile))
17882286d8edStholo 	      {
17892286d8edStholo 		if (i || b->next)
17902286d8edStholo 		  diff3_fatal ("input file shrank");
17912286d8edStholo 		return conflicts_found;
17922286d8edStholo 	      }
17932286d8edStholo     }
17942286d8edStholo   /* Copy rest of common file.  */
17952286d8edStholo   while ((c = getc (infile)) != EOF || !(ferror (infile) | feof (infile)))
1796b2346922Stholo     {
1797b2346922Stholo       cc = c;
1798b2346922Stholo       write_output (&cc, 1);
1799b2346922Stholo     }
18002286d8edStholo   return conflicts_found;
18012286d8edStholo }
18022286d8edStholo 
18032286d8edStholo /*
18042286d8edStholo  * Reverse the order of the list of diff3 blocks.
18052286d8edStholo  */
18062286d8edStholo static struct diff3_block *
18072286d8edStholo reverse_diff3_blocklist (diff)
18082286d8edStholo      struct diff3_block *diff;
18092286d8edStholo {
18102286d8edStholo   register struct diff3_block *tmp, *next, *prev;
18112286d8edStholo 
18122286d8edStholo   for (tmp = diff, prev = 0;  tmp;  tmp = next)
18132286d8edStholo     {
18142286d8edStholo       next = tmp->next;
18152286d8edStholo       tmp->next = prev;
18162286d8edStholo       prev = tmp;
18172286d8edStholo     }
18182286d8edStholo 
18192286d8edStholo   return prev;
18202286d8edStholo }
18212286d8edStholo 
18222286d8edStholo static size_t
18232286d8edStholo myread (fd, ptr, size)
18242286d8edStholo      int fd;
18252286d8edStholo      char *ptr;
18262286d8edStholo      size_t size;
18272286d8edStholo {
18282286d8edStholo   size_t result = read (fd, ptr, size);
18292286d8edStholo   if (result == -1)
18302286d8edStholo     diff3_perror_with_exit ("read failed");
18312286d8edStholo   return result;
18322286d8edStholo }
18332286d8edStholo 
18342286d8edStholo static void
18352286d8edStholo diff3_fatal (string)
18362286d8edStholo      char const *string;
18372286d8edStholo {
1838b2346922Stholo   diff_error ("%s", string, 0);
18392286d8edStholo   DIFF3_ABORT (2);
18402286d8edStholo }
18412286d8edStholo 
18422286d8edStholo static void
18432286d8edStholo diff3_perror_with_exit (string)
18442286d8edStholo      char const *string;
18452286d8edStholo {
1846b2346922Stholo   perror_with_name (string);
18472286d8edStholo   DIFF3_ABORT (2);
18482286d8edStholo }
18492286d8edStholo 
18502286d8edStholo static void
18512286d8edStholo initialize_main (argcp, argvp)
18522286d8edStholo     int *argcp;
18532286d8edStholo     char ***argvp;
18542286d8edStholo {
18552286d8edStholo   always_text = 0;
18562286d8edStholo   edscript = 0;
18572286d8edStholo   flagging = 0;
18582286d8edStholo   horizon_lines = 10;
18592286d8edStholo   tab_align_flag = 0;
18602286d8edStholo   simple_only = 0;
18612286d8edStholo   overlap_only = 0;
18622286d8edStholo   show_2nd = 0;
18632286d8edStholo   finalwrite = 0;
18642286d8edStholo   merge = 0;
18652286d8edStholo   diff_program_name = (*argvp)[0];
1866b2346922Stholo   outfile = NULL;
18672286d8edStholo }
18682286d8edStholo 
18692286d8edStholo static void
18702286d8edStholo free_diff_blocks(p)
18712286d8edStholo     struct diff_block *p;
18722286d8edStholo {
18732286d8edStholo   register struct diff_block *next;
18742286d8edStholo 
18752286d8edStholo   while (p)
18762286d8edStholo     {
18772286d8edStholo       next = p->next;
18782286d8edStholo       if (p->lines[0]) free(p->lines[0]);
18792286d8edStholo       if (p->lines[1]) free(p->lines[1]);
18802286d8edStholo       if (p->lengths[0]) free(p->lengths[0]);
18812286d8edStholo       if (p->lengths[1]) free(p->lengths[1]);
18822286d8edStholo       free(p);
18832286d8edStholo       p = next;
18842286d8edStholo     }
18852286d8edStholo }
18862286d8edStholo 
18872286d8edStholo static void
18882286d8edStholo free_diff3_blocks(p)
18892286d8edStholo     struct diff3_block *p;
18902286d8edStholo {
18912286d8edStholo   register struct diff3_block *next;
18922286d8edStholo 
18932286d8edStholo   while (p)
18942286d8edStholo     {
18952286d8edStholo       next = p->next;
18962286d8edStholo       if (p->lines[0]) free(p->lines[0]);
18972286d8edStholo       if (p->lines[1]) free(p->lines[1]);
18982286d8edStholo       if (p->lines[2]) free(p->lines[2]);
18992286d8edStholo       if (p->lengths[0]) free(p->lengths[0]);
19002286d8edStholo       if (p->lengths[1]) free(p->lengths[1]);
19012286d8edStholo       if (p->lengths[2]) free(p->lengths[2]);
19022286d8edStholo       free(p);
19032286d8edStholo       p = next;
19042286d8edStholo     }
19052286d8edStholo }
1906