1170754Sdelphij/* diff3 - compare three files line by line
2170754Sdelphij
3170754Sdelphij   Copyright (C) 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1998, 2001,
4170754Sdelphij   2002, 2004 Free Software Foundation, Inc.
5170754Sdelphij
6170754Sdelphij   This program is free software; you can redistribute it and/or modify
7170754Sdelphij   it under the terms of the GNU General Public License as published by
8170754Sdelphij   the Free Software Foundation; either version 2, or (at your option)
9170754Sdelphij   any later version.
10170754Sdelphij
11170754Sdelphij   This program is distributed in the hope that it will be useful,
12170754Sdelphij   but WITHOUT ANY WARRANTY; without even the implied warranty of
13170754Sdelphij   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14170754Sdelphij   See the GNU General Public License for more details.
15170754Sdelphij
16170754Sdelphij   You should have received a copy of the GNU General Public License
17170754Sdelphij   along with this program; see the file COPYING.
18170754Sdelphij   If not, write to the Free Software Foundation,
19170754Sdelphij   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
20170754Sdelphij
21170754Sdelphij#include "system.h"
22170754Sdelphij#include "paths.h"
23170754Sdelphij
24170754Sdelphij#include <stdio.h>
25170754Sdelphij#include <unlocked-io.h>
26170754Sdelphij
27170754Sdelphij#include <c-stack.h>
28170754Sdelphij#include <cmpbuf.h>
29170754Sdelphij#include <error.h>
30170754Sdelphij#include <exitfail.h>
31170754Sdelphij#include <file-type.h>
32170754Sdelphij#include <getopt.h>
33170754Sdelphij#include <inttostr.h>
34170754Sdelphij#include <quotesys.h>
35170754Sdelphij#include <version-etc.h>
36170754Sdelphij#include <xalloc.h>
37170754Sdelphij
38170754Sdelphij/* Internal data structures and macros for the diff3 program; includes
39170754Sdelphij   data structures for both diff3 diffs and normal diffs.  */
40170754Sdelphij
41170754Sdelphij/* Different files within a three way diff.  */
42170754Sdelphij#define	FILE0	0
43170754Sdelphij#define	FILE1	1
44170754Sdelphij#define	FILE2	2
45170754Sdelphij
46170754Sdelphij/* A three way diff is built from two two-way diffs; the file which
47170754Sdelphij   the two two-way diffs share is:  */
48170754Sdelphij#define	FILEC	FILE2
49170754Sdelphij
50170754Sdelphij/* Different files within a two way diff.
51170754Sdelphij   FC is the common file, FO the other file.  */
52170754Sdelphij#define FO 0
53170754Sdelphij#define FC 1
54170754Sdelphij
55170754Sdelphij/* The ranges are indexed by */
56170754Sdelphij#define	RANGE_START	0
57170754Sdelphij#define	RANGE_END	1
58170754Sdelphij
59170754Sdelphijenum diff_type {
60170754Sdelphij  ERROR,			/* Should not be used */
61170754Sdelphij  ADD,				/* Two way diff add */
62170754Sdelphij  CHANGE,			/* Two way diff change */
63170754Sdelphij  DELETE,			/* Two way diff delete */
64170754Sdelphij  DIFF_ALL,			/* All three are different */
65170754Sdelphij  DIFF_1ST,			/* Only the first is different */
66170754Sdelphij  DIFF_2ND,			/* Only the second */
67170754Sdelphij  DIFF_3RD			/* Only the third */
68170754Sdelphij};
69170754Sdelphij
70170754Sdelphij/* Two way diff */
71170754Sdelphijstruct diff_block {
72170754Sdelphij  lin ranges[2][2];		/* Ranges are inclusive */
73170754Sdelphij  char **lines[2];		/* The actual lines (may contain nulls) */
74170754Sdelphij  size_t *lengths[2];		/* Line lengths (including newlines, if any) */
75170754Sdelphij  struct diff_block *next;
76170754Sdelphij};
77170754Sdelphij
78170754Sdelphij/* Three way diff */
79170754Sdelphij
80170754Sdelphijstruct diff3_block {
81170754Sdelphij  enum diff_type correspond;	/* Type of diff */
82170754Sdelphij  lin ranges[3][2];		/* Ranges are inclusive */
83170754Sdelphij  char **lines[3];		/* The actual lines (may contain nulls) */
84170754Sdelphij  size_t *lengths[3];		/* Line lengths (including newlines, if any) */
85170754Sdelphij  struct diff3_block *next;
86170754Sdelphij};
87170754Sdelphij
88170754Sdelphij/* Access the ranges on a diff block.  */
89170754Sdelphij#define	D_LOWLINE(diff, filenum)	\
90170754Sdelphij  ((diff)->ranges[filenum][RANGE_START])
91170754Sdelphij#define	D_HIGHLINE(diff, filenum)	\
92170754Sdelphij  ((diff)->ranges[filenum][RANGE_END])
93170754Sdelphij#define	D_NUMLINES(diff, filenum)	\
94170754Sdelphij  (D_HIGHLINE (diff, filenum) - D_LOWLINE (diff, filenum) + 1)
95170754Sdelphij
96170754Sdelphij/* Access the line numbers in a file in a diff by relative line
97170754Sdelphij   numbers (i.e. line number within the diff itself).  Note that these
98170754Sdelphij   are lvalues and can be used for assignment.  */
99170754Sdelphij#define	D_RELNUM(diff, filenum, linenum)	\
100170754Sdelphij  ((diff)->lines[filenum][linenum])
101170754Sdelphij#define	D_RELLEN(diff, filenum, linenum)	\
102170754Sdelphij  ((diff)->lengths[filenum][linenum])
103170754Sdelphij
104170754Sdelphij/* And get at them directly, when that should be necessary.  */
105170754Sdelphij#define	D_LINEARRAY(diff, filenum)	\
106170754Sdelphij  ((diff)->lines[filenum])
107170754Sdelphij#define	D_LENARRAY(diff, filenum)	\
108170754Sdelphij  ((diff)->lengths[filenum])
109170754Sdelphij
110170754Sdelphij/* Next block.  */
111170754Sdelphij#define	D_NEXT(diff)	((diff)->next)
112170754Sdelphij
113170754Sdelphij/* Access the type of a diff3 block.  */
114170754Sdelphij#define	D3_TYPE(diff)	((diff)->correspond)
115170754Sdelphij
116170754Sdelphij/* Line mappings based on diffs.  The first maps off the top of the
117170754Sdelphij   diff, the second off of the bottom.  */
118170754Sdelphij#define	D_HIGH_MAPLINE(diff, fromfile, tofile, linenum)	\
119170754Sdelphij  ((linenum)						\
120170754Sdelphij   - D_HIGHLINE ((diff), (fromfile))			\
121170754Sdelphij   + D_HIGHLINE ((diff), (tofile)))
122170754Sdelphij
123170754Sdelphij#define	D_LOW_MAPLINE(diff, fromfile, tofile, linenum)	\
124170754Sdelphij  ((linenum)						\
125170754Sdelphij   - D_LOWLINE ((diff), (fromfile))			\
126170754Sdelphij   + D_LOWLINE ((diff), (tofile)))
127170754Sdelphij
128170754Sdelphij/* Options variables for flags set on command line.  */
129170754Sdelphij
130170754Sdelphij/* If nonzero, treat all files as text files, never as binary.  */
131170754Sdelphijstatic bool text;
132170754Sdelphij
133170754Sdelphij/* Remove trailing carriage returns from input.  */
134170754Sdelphijstatic bool strip_trailing_cr;
135170754Sdelphij
136170754Sdelphij/* If nonzero, write out an ed script instead of the standard diff3 format.  */
137170754Sdelphijstatic bool edscript;
138170754Sdelphij
139170754Sdelphij/* If nonzero, in the case of overlapping diffs (type DIFF_ALL),
140170754Sdelphij   preserve the lines which would normally be deleted from
141170754Sdelphij   file 1 with a special flagging mechanism.  */
142170754Sdelphijstatic bool flagging;
143170754Sdelphij
144170754Sdelphij/* Use a tab to align output lines (-T).  */
145170754Sdelphijstatic bool initial_tab;
146170754Sdelphij
147170754Sdelphij/* If nonzero, do not output information for overlapping diffs.  */
148170754Sdelphijstatic bool simple_only;
149170754Sdelphij
150170754Sdelphij/* If nonzero, do not output information for non-overlapping diffs.  */
151170754Sdelphijstatic bool overlap_only;
152170754Sdelphij
153170754Sdelphij/* If nonzero, show information for DIFF_2ND diffs.  */
154170754Sdelphijstatic bool show_2nd;
155170754Sdelphij
156170754Sdelphij/* If nonzero, include `:wq' at the end of the script
157170754Sdelphij   to write out the file being edited.   */
158170754Sdelphijstatic bool finalwrite;
159170754Sdelphij
160170754Sdelphij/* If nonzero, output a merged file.  */
161170754Sdelphijstatic bool merge;
162170754Sdelphij
163170754Sdelphijchar *program_name;
164170754Sdelphij
165170754Sdelphijstatic char *read_diff (char const *, char const *, char **);
166170754Sdelphijstatic char *scan_diff_line (char *, char **, size_t *, char *, char);
167170754Sdelphijstatic enum diff_type process_diff_control (char **, struct diff_block *);
168170754Sdelphijstatic bool compare_line_list (char * const[], size_t const[], char * const[], size_t const[], lin);
169170754Sdelphijstatic bool copy_stringlist (char * const[], size_t const[], char *[], size_t[], lin);
170170754Sdelphijstatic bool output_diff3_edscript (FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *);
171170754Sdelphijstatic bool output_diff3_merge (FILE *, FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *);
172170754Sdelphijstatic struct diff3_block *create_diff3_block (lin, lin, lin, lin, lin, lin);
173170754Sdelphijstatic struct diff3_block *make_3way_diff (struct diff_block *, struct diff_block *);
174170754Sdelphijstatic struct diff3_block *reverse_diff3_blocklist (struct diff3_block *);
175170754Sdelphijstatic struct diff3_block *using_to_diff3_block (struct diff_block *[2], struct diff_block *[2], int, int, struct diff3_block const *);
176170754Sdelphijstatic struct diff_block *process_diff (char const *, char const *, struct diff_block **);
177170754Sdelphijstatic void check_stdout (void);
178170754Sdelphijstatic void fatal (char const *) __attribute__((noreturn));
179170754Sdelphijstatic void output_diff3 (FILE *, struct diff3_block *, int const[3], int const[3]);
180170754Sdelphijstatic void perror_with_exit (char const *) __attribute__((noreturn));
181170754Sdelphijstatic void try_help (char const *, char const *) __attribute__((noreturn));
182170754Sdelphijstatic void usage (void);
183170754Sdelphij
184170754Sdelphijstatic char const *diff_program = DEFAULT_DIFF_PROGRAM;
185170754Sdelphij
186170754Sdelphij/* Values for long options that do not have single-letter equivalents.  */
187170754Sdelphijenum
188170754Sdelphij{
189170754Sdelphij  DIFF_PROGRAM_OPTION = CHAR_MAX + 1,
190170754Sdelphij  HELP_OPTION,
191170754Sdelphij  STRIP_TRAILING_CR_OPTION
192170754Sdelphij};
193170754Sdelphij
194170754Sdelphijstatic struct option const longopts[] =
195170754Sdelphij{
196170754Sdelphij  {"diff-program", 1, 0, DIFF_PROGRAM_OPTION},
197170754Sdelphij  {"easy-only", 0, 0, '3'},
198170754Sdelphij  {"ed", 0, 0, 'e'},
199170754Sdelphij  {"help", 0, 0, HELP_OPTION},
200170754Sdelphij  {"initial-tab", 0, 0, 'T'},
201170754Sdelphij  {"label", 1, 0, 'L'},
202170754Sdelphij  {"merge", 0, 0, 'm'},
203170754Sdelphij  {"overlap-only", 0, 0, 'x'},
204170754Sdelphij  {"show-all", 0, 0, 'A'},
205170754Sdelphij  {"show-overlap", 0, 0, 'E'},
206170754Sdelphij  {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
207170754Sdelphij  {"text", 0, 0, 'a'},
208170754Sdelphij  {"version", 0, 0, 'v'},
209170754Sdelphij  {0, 0, 0, 0}
210170754Sdelphij};
211170754Sdelphij
212170754Sdelphijint
213170754Sdelphijmain (int argc, char **argv)
214170754Sdelphij{
215170754Sdelphij  int c, i;
216170754Sdelphij  int common;
217170754Sdelphij  int mapping[3];
218170754Sdelphij  int rev_mapping[3];
219170754Sdelphij  int incompat = 0;
220170754Sdelphij  bool conflicts_found;
221170754Sdelphij  struct diff_block *thread0, *thread1, *last_block;
222170754Sdelphij  struct diff3_block *diff3;
223170754Sdelphij  int tag_count = 0;
224170754Sdelphij  char *tag_strings[3];
225170754Sdelphij  char *commonname;
226170754Sdelphij  char **file;
227170754Sdelphij  struct stat statb;
228170754Sdelphij
229170754Sdelphij  exit_failure = 2;
230170754Sdelphij  initialize_main (&argc, &argv);
231170754Sdelphij  program_name = argv[0];
232170754Sdelphij  setlocale (LC_ALL, "");
233170754Sdelphij  bindtextdomain (PACKAGE, LOCALEDIR);
234170754Sdelphij  textdomain (PACKAGE);
235170754Sdelphij  c_stack_action (0);
236170754Sdelphij
237170754Sdelphij  while ((c = getopt_long (argc, argv, "aeimvx3AEL:TX", longopts, 0)) != -1)
238170754Sdelphij    {
239170754Sdelphij      switch (c)
240170754Sdelphij	{
241170754Sdelphij	case 'a':
242170754Sdelphij	  text = true;
243170754Sdelphij	  break;
244170754Sdelphij	case 'A':
245170754Sdelphij	  show_2nd = true;
246170754Sdelphij	  flagging = true;
247170754Sdelphij	  incompat++;
248170754Sdelphij	  break;
249170754Sdelphij	case 'x':
250170754Sdelphij	  overlap_only = true;
251170754Sdelphij	  incompat++;
252170754Sdelphij	  break;
253170754Sdelphij	case '3':
254170754Sdelphij	  simple_only = true;
255170754Sdelphij	  incompat++;
256170754Sdelphij	  break;
257170754Sdelphij	case 'i':
258170754Sdelphij	  finalwrite = true;
259170754Sdelphij	  break;
260170754Sdelphij	case 'm':
261170754Sdelphij	  merge = true;
262170754Sdelphij	  break;
263170754Sdelphij	case 'X':
264170754Sdelphij	  overlap_only = true;
265170754Sdelphij	  /* Fall through.  */
266170754Sdelphij	case 'E':
267170754Sdelphij	  flagging = true;
268170754Sdelphij	  /* Fall through.  */
269170754Sdelphij	case 'e':
270170754Sdelphij	  incompat++;
271170754Sdelphij	  break;
272170754Sdelphij	case 'T':
273170754Sdelphij	  initial_tab = true;
274170754Sdelphij	  break;
275170754Sdelphij	case STRIP_TRAILING_CR_OPTION:
276170754Sdelphij	  strip_trailing_cr = true;
277170754Sdelphij	  break;
278170754Sdelphij	case 'v':
279170754Sdelphij	  version_etc (stdout, "diff3", PACKAGE_NAME, PACKAGE_VERSION,
280170754Sdelphij		       "Randy Smith", (char *) 0);
281170754Sdelphij	  check_stdout ();
282170754Sdelphij	  return EXIT_SUCCESS;
283170754Sdelphij	case DIFF_PROGRAM_OPTION:
284170754Sdelphij	  diff_program = optarg;
285170754Sdelphij	  break;
286170754Sdelphij	case HELP_OPTION:
287170754Sdelphij	  usage ();
288170754Sdelphij	  check_stdout ();
289170754Sdelphij	  return EXIT_SUCCESS;
290170754Sdelphij	case 'L':
291170754Sdelphij	  /* Handle up to three -L options.  */
292170754Sdelphij	  if (tag_count < 3)
293170754Sdelphij	    {
294170754Sdelphij	      tag_strings[tag_count++] = optarg;
295170754Sdelphij	      break;
296170754Sdelphij	    }
297170754Sdelphij	  try_help ("too many file label options", 0);
298170754Sdelphij	default:
299170754Sdelphij	  try_help (0, 0);
300170754Sdelphij	}
301170754Sdelphij    }
302170754Sdelphij
303170754Sdelphij  edscript = incompat & ~merge;  /* -AeExX3 without -m implies ed script.  */
304170754Sdelphij  show_2nd |= ~incompat & merge;  /* -m without -AeExX3 implies -A.  */
305170754Sdelphij  flagging |= ~incompat & merge;
306170754Sdelphij
307170754Sdelphij  if (incompat > 1  /* Ensure at most one of -AeExX3.  */
308170754Sdelphij      || finalwrite & merge /* -i -m would rewrite input file.  */
309170754Sdelphij      || (tag_count && ! flagging)) /* -L requires one of -AEX.  */
310170754Sdelphij    try_help ("incompatible options", 0);
311170754Sdelphij
312170754Sdelphij  if (argc - optind != 3)
313170754Sdelphij    {
314170754Sdelphij      if (argc - optind < 3)
315170754Sdelphij	try_help ("missing operand after `%s'", argv[argc - 1]);
316170754Sdelphij      else
317170754Sdelphij	try_help ("extra operand `%s'", argv[optind + 3]);
318170754Sdelphij    }
319170754Sdelphij
320170754Sdelphij  file = &argv[optind];
321170754Sdelphij
322170754Sdelphij  for (i = tag_count; i < 3; i++)
323170754Sdelphij    tag_strings[i] = file[i];
324170754Sdelphij
325170754Sdelphij  /* Always compare file1 to file2, even if file2 is "-".
326170754Sdelphij     This is needed for -mAeExX3.  Using the file0 as
327170754Sdelphij     the common file would produce wrong results, because if the
328170754Sdelphij     file0-file1 diffs didn't line up with the file0-file2 diffs
329170754Sdelphij     (which is entirely possible since we don't use diff's -n option),
330170754Sdelphij     diff3 might report phantom changes from file1 to file2.
331170754Sdelphij
332170754Sdelphij     Also, try to compare file0 to file1, because this is where
333170754Sdelphij     changes are expected to come from.  Diffing between these pairs
334170754Sdelphij     of files is more likely to avoid phantom changes from file0 to file1.
335170754Sdelphij
336170754Sdelphij     Historically, the default common file was file2, so some older
337170754Sdelphij     applications (e.g. Emacs ediff) used file2 as the ancestor.  So,
338170754Sdelphij     for compatibility, if this is a 3-way diff (not a merge or
339170754Sdelphij     edscript), prefer file2 as the common file.  */
340170754Sdelphij
341170754Sdelphij  common = 2 - (edscript | merge);
342170754Sdelphij
343170754Sdelphij  if (strcmp (file[common], "-") == 0)
344170754Sdelphij    {
345170754Sdelphij      /* Sigh.  We've got standard input as the common file.  We can't
346170754Sdelphij	 call diff twice on stdin.  Use the other arg as the common
347170754Sdelphij	 file instead.  */
348170754Sdelphij      common = 3 - common;
349170754Sdelphij      if (strcmp (file[0], "-") == 0 || strcmp (file[common], "-") == 0)
350170754Sdelphij	fatal ("`-' specified for more than one input file");
351170754Sdelphij    }
352170754Sdelphij
353170754Sdelphij  mapping[0] = 0;
354170754Sdelphij  mapping[1] = 3 - common;
355170754Sdelphij  mapping[2] = common;
356170754Sdelphij
357170754Sdelphij  for (i = 0; i < 3; i++)
358170754Sdelphij    rev_mapping[mapping[i]] = i;
359170754Sdelphij
360170754Sdelphij  for (i = 0; i < 3; i++)
361170754Sdelphij    if (strcmp (file[i], "-") != 0)
362170754Sdelphij      {
363170754Sdelphij	if (stat (file[i], &statb) < 0)
364170754Sdelphij	  perror_with_exit (file[i]);
365170754Sdelphij	else if (S_ISDIR (statb.st_mode))
366170754Sdelphij	  error (EXIT_TROUBLE, EISDIR, "%s", file[i]);
367170754Sdelphij      }
368170754Sdelphij
369170754Sdelphij#ifdef SIGCHLD
370170754Sdelphij  /* System V fork+wait does not work if SIGCHLD is ignored.  */
371170754Sdelphij  signal (SIGCHLD, SIG_DFL);
372170754Sdelphij#endif
373170754Sdelphij
374170754Sdelphij  /* Invoke diff twice on two pairs of input files, combine the two
375170754Sdelphij     diffs, and output them.  */
376170754Sdelphij
377170754Sdelphij  commonname = file[rev_mapping[FILEC]];
378170754Sdelphij  thread1 = process_diff (file[rev_mapping[FILE1]], commonname, &last_block);
379170754Sdelphij  thread0 = process_diff (file[rev_mapping[FILE0]], commonname, &last_block);
380170754Sdelphij  diff3 = make_3way_diff (thread0, thread1);
381170754Sdelphij  if (edscript)
382170754Sdelphij    conflicts_found
383170754Sdelphij      = output_diff3_edscript (stdout, diff3, mapping, rev_mapping,
384170754Sdelphij			       tag_strings[0], tag_strings[1], tag_strings[2]);
385170754Sdelphij  else if (merge)
386170754Sdelphij    {
387170754Sdelphij      if (! freopen (file[rev_mapping[FILE0]], "r", stdin))
388170754Sdelphij	perror_with_exit (file[rev_mapping[FILE0]]);
389170754Sdelphij      conflicts_found
390170754Sdelphij	= output_diff3_merge (stdin, stdout, diff3, mapping, rev_mapping,
391170754Sdelphij			      tag_strings[0], tag_strings[1], tag_strings[2]);
392170754Sdelphij      if (ferror (stdin))
393170754Sdelphij	fatal ("read failed");
394170754Sdelphij    }
395170754Sdelphij  else
396170754Sdelphij    {
397170754Sdelphij      output_diff3 (stdout, diff3, mapping, rev_mapping);
398170754Sdelphij      conflicts_found = false;
399170754Sdelphij    }
400170754Sdelphij
401170754Sdelphij  check_stdout ();
402170754Sdelphij  exit (conflicts_found);
403170754Sdelphij  return conflicts_found;
404170754Sdelphij}
405170754Sdelphij
406170754Sdelphijstatic void
407170754Sdelphijtry_help (char const *reason_msgid, char const *operand)
408170754Sdelphij{
409170754Sdelphij  if (reason_msgid)
410170754Sdelphij    error (0, 0, _(reason_msgid), operand);
411170754Sdelphij  error (EXIT_TROUBLE, 0,
412170754Sdelphij	 _("Try `%s --help' for more information."), program_name);
413170754Sdelphij  abort ();
414170754Sdelphij}
415170754Sdelphij
416170754Sdelphijstatic void
417170754Sdelphijcheck_stdout (void)
418170754Sdelphij{
419170754Sdelphij  if (ferror (stdout))
420170754Sdelphij    fatal ("write failed");
421170754Sdelphij  else if (fclose (stdout) != 0)
422170754Sdelphij    perror_with_exit (_("standard output"));
423170754Sdelphij}
424170754Sdelphij
425170754Sdelphijstatic char const * const option_help_msgid[] = {
426170754Sdelphij  N_("-e  --ed  Output unmerged changes from OLDFILE to YOURFILE into MYFILE."),
427170754Sdelphij  N_("-E  --show-overlap  Output unmerged changes, bracketing conflicts."),
428170754Sdelphij  N_("-A  --show-all  Output all changes, bracketing conflicts."),
429170754Sdelphij  N_("-x  --overlap-only  Output overlapping changes."),
430170754Sdelphij  N_("-X  Output overlapping changes, bracketing them."),
431170754Sdelphij  N_("-3  --easy-only  Output unmerged nonoverlapping changes."),
432170754Sdelphij  "",
433170754Sdelphij  N_("-m  --merge  Output merged file instead of ed script (default -A)."),
434170754Sdelphij  N_("-L LABEL  --label=LABEL  Use LABEL instead of file name."),
435170754Sdelphij  N_("-i  Append `w' and `q' commands to ed scripts."),
436170754Sdelphij  N_("-a  --text  Treat all files as text."),
437170754Sdelphij  N_("--strip-trailing-cr  Strip trailing carriage return on input."),
438170754Sdelphij  N_("-T  --initial-tab  Make tabs line up by prepending a tab."),
439170754Sdelphij  N_("--diff-program=PROGRAM  Use PROGRAM to compare files."),
440170754Sdelphij  "",
441170754Sdelphij  N_("-v  --version  Output version info."),
442170754Sdelphij  N_("--help  Output this help."),
443170754Sdelphij  0
444170754Sdelphij};
445170754Sdelphij
446170754Sdelphijstatic void
447170754Sdelphijusage (void)
448170754Sdelphij{
449170754Sdelphij  char const * const *p;
450170754Sdelphij
451170754Sdelphij  printf (_("Usage: %s [OPTION]... MYFILE OLDFILE YOURFILE\n"),
452170754Sdelphij	  program_name);
453170754Sdelphij  printf ("%s\n\n", _("Compare three files line by line."));
454170754Sdelphij  for (p = option_help_msgid;  *p;  p++)
455170754Sdelphij    if (**p)
456170754Sdelphij      printf ("  %s\n", _(*p));
457170754Sdelphij    else
458170754Sdelphij      putchar ('\n');
459170754Sdelphij  printf ("\n%s\n%s\n\n%s\n",
460170754Sdelphij	  _("If a FILE is `-', read standard input."),
461170754Sdelphij	  _("Exit status is 0 if successful, 1 if conflicts, 2 if trouble."),
462170754Sdelphij	  _("Report bugs to <bug-gnu-utils@gnu.org>."));
463170754Sdelphij}
464170754Sdelphij
465170754Sdelphij/* Combine the two diffs together into one.
466170754Sdelphij   Here is the algorithm:
467170754Sdelphij
468170754Sdelphij     File2 is shared in common between the two diffs.
469170754Sdelphij     Diff02 is the diff between 0 and 2.
470170754Sdelphij     Diff12 is the diff between 1 and 2.
471170754Sdelphij
472170754Sdelphij	1) Find the range for the first block in File2.
473170754Sdelphij	    a) Take the lowest of the two ranges (in File2) in the two
474170754Sdelphij	       current blocks (one from each diff) as being the low
475170754Sdelphij	       water mark.  Assign the upper end of this block as
476170754Sdelphij	       being the high water mark and move the current block up
477170754Sdelphij	       one.  Mark the block just moved over as to be used.
478170754Sdelphij	    b) Check the next block in the diff that the high water
479170754Sdelphij	       mark is *not* from.
480170754Sdelphij
481170754Sdelphij	       *If* the high water mark is above
482170754Sdelphij	       the low end of the range in that block,
483170754Sdelphij
484170754Sdelphij		   mark that block as to be used and move the current
485170754Sdelphij		   block up.  Set the high water mark to the max of
486170754Sdelphij		   the high end of this block and the current.  Repeat b.
487170754Sdelphij
488170754Sdelphij	 2) Find the corresponding ranges in File0 (from the blocks
489170754Sdelphij	    in diff02; line per line outside of diffs) and in File1.
490170754Sdelphij	    Create a diff3_block, reserving space as indicated by the ranges.
491170754Sdelphij
492170754Sdelphij	 3) Copy all of the pointers for file2 in.  At least for now,
493170754Sdelphij	    do memcmp's between corresponding strings in the two diffs.
494170754Sdelphij
495170754Sdelphij	 4) Copy all of the pointers for file0 and 1 in.  Get what is
496170754Sdelphij	    needed from file2 (when there isn't a diff block, it's
497170754Sdelphij	    identical to file2 within the range between diff blocks).
498170754Sdelphij
499170754Sdelphij	 5) If the diff blocks used came from only one of the two
500170754Sdelphij	    strings of diffs, then that file (i.e. the one other than
501170754Sdelphij	    the common file in that diff) is the odd person out.  If
502170754Sdelphij	    diff blocks are used from both sets, check to see if files
503170754Sdelphij	    0 and 1 match:
504170754Sdelphij
505170754Sdelphij		Same number of lines?  If so, do a set of memcmp's (if
506170754Sdelphij	    a memcmp matches; copy the pointer over; it'll be easier
507170754Sdelphij	    later during comparisons).  If they match, 0 & 1 are the
508170754Sdelphij	    same.  If not, all three different.
509170754Sdelphij
510170754Sdelphij     Then do it again, until the blocks are exhausted.  */
511170754Sdelphij
512170754Sdelphij
513170754Sdelphij/* Make a three way diff (chain of diff3_block's) from two two way
514170754Sdelphij   diffs (chains of diff_block's).  Assume that each of the two diffs
515170754Sdelphij   passed are onto the same file (i.e. that each of the diffs were
516170754Sdelphij   made "to" the same file).  Return a three way diff pointer with
517170754Sdelphij   numbering FILE0 = the other file in diff02, FILE1 = the other file
518170754Sdelphij   in diff12, and FILEC = the common file.  */
519170754Sdelphij
520170754Sdelphijstatic struct diff3_block *
521170754Sdelphijmake_3way_diff (struct diff_block *thread0, struct diff_block *thread1)
522170754Sdelphij{
523170754Sdelphij  /* Work on the two diffs passed to it as threads.  Thread number 0
524170754Sdelphij     is diff02, thread number 1 is diff12.  USING is the base of the
525170754Sdelphij     list of blocks to be used to construct each block of the three
526170754Sdelphij     way diff; if no blocks from a particular thread are to be used,
527170754Sdelphij     that element of USING is 0.  LAST_USING contains the last
528170754Sdelphij     elements on each of the using lists.
529170754Sdelphij
530170754Sdelphij     HIGH_WATER_MARK is the highest line number in the common file
531170754Sdelphij     described in any of the diffs in either of the USING lists.
532170754Sdelphij     HIGH_WATER_THREAD names the thread.  Similarly BASE_WATER_MARK
533170754Sdelphij     and BASE_WATER_THREAD describe the lowest line number in the
534170754Sdelphij     common file described in any of the diffs in either of the USING
535170754Sdelphij     lists.  HIGH_WATER_DIFF is the diff from which the
536170754Sdelphij     HIGH_WATER_MARK was taken.
537170754Sdelphij
538170754Sdelphij     HIGH_WATER_DIFF should always be equal to
539170754Sdelphij     LAST_USING[HIGH_WATER_THREAD].  OTHER_DIFF is the next diff to
540170754Sdelphij     check for higher water, and should always be equal to
541170754Sdelphij     CURRENT[HIGH_WATER_THREAD ^ 1].  OTHER_THREAD is the thread in
542170754Sdelphij     which the OTHER_DIFF is, and hence should always be equal to
543170754Sdelphij     HIGH_WATER_THREAD ^ 1.
544170754Sdelphij
545170754Sdelphij     LAST_DIFF is the last diff block produced by this routine, for
546170754Sdelphij     line correspondence purposes between that diff and the one
547170754Sdelphij     currently being worked on.  It is ZERO_DIFF before any blocks
548170754Sdelphij     have been created.  */
549170754Sdelphij
550170754Sdelphij  struct diff_block *using[2];
551170754Sdelphij  struct diff_block *last_using[2];
552170754Sdelphij  struct diff_block *current[2];
553170754Sdelphij
554170754Sdelphij  lin high_water_mark;
555170754Sdelphij
556170754Sdelphij  int high_water_thread;
557170754Sdelphij  int base_water_thread;
558170754Sdelphij  int other_thread;
559170754Sdelphij
560170754Sdelphij  struct diff_block *high_water_diff;
561170754Sdelphij  struct diff_block *other_diff;
562170754Sdelphij
563170754Sdelphij  struct diff3_block *result;
564170754Sdelphij  struct diff3_block *tmpblock;
565170754Sdelphij  struct diff3_block **result_end;
566170754Sdelphij
567170754Sdelphij  struct diff3_block const *last_diff3;
568170754Sdelphij
569170754Sdelphij  static struct diff3_block const zero_diff3;
570170754Sdelphij
571170754Sdelphij  /* Initialization */
572170754Sdelphij  result = 0;
573170754Sdelphij  result_end = &result;
574170754Sdelphij  current[0] = thread0; current[1] = thread1;
575170754Sdelphij  last_diff3 = &zero_diff3;
576170754Sdelphij
577170754Sdelphij  /* Sniff up the threads until we reach the end */
578170754Sdelphij
579170754Sdelphij  while (current[0] || current[1])
580170754Sdelphij    {
581170754Sdelphij      using[0] = using[1] = last_using[0] = last_using[1] = 0;
582170754Sdelphij
583170754Sdelphij      /* Setup low and high water threads, diffs, and marks.  */
584170754Sdelphij      if (!current[0])
585170754Sdelphij	base_water_thread = 1;
586170754Sdelphij      else if (!current[1])
587170754Sdelphij	base_water_thread = 0;
588170754Sdelphij      else
589170754Sdelphij	base_water_thread =
590170754Sdelphij	  (D_LOWLINE (current[0], FC) > D_LOWLINE (current[1], FC));
591170754Sdelphij
592170754Sdelphij      high_water_thread = base_water_thread;
593170754Sdelphij
594170754Sdelphij      high_water_diff = current[high_water_thread];
595170754Sdelphij
596170754Sdelphij      high_water_mark = D_HIGHLINE (high_water_diff, FC);
597170754Sdelphij
598170754Sdelphij      /* Make the diff you just got info from into the using class */
599170754Sdelphij      using[high_water_thread]
600170754Sdelphij	= last_using[high_water_thread]
601170754Sdelphij	= high_water_diff;
602170754Sdelphij      current[high_water_thread] = high_water_diff->next;
603170754Sdelphij      last_using[high_water_thread]->next = 0;
604170754Sdelphij
605170754Sdelphij      /* And mark the other diff */
606170754Sdelphij      other_thread = high_water_thread ^ 0x1;
607170754Sdelphij      other_diff = current[other_thread];
608170754Sdelphij
609170754Sdelphij      /* Shuffle up the ladder, checking the other diff to see if it
610170754Sdelphij	 needs to be incorporated.  */
611170754Sdelphij      while (other_diff
612170754Sdelphij	     && D_LOWLINE (other_diff, FC) <= high_water_mark + 1)
613170754Sdelphij	{
614170754Sdelphij
615170754Sdelphij	  /* Incorporate this diff into the using list.  Note that
616170754Sdelphij	     this doesn't take it off the current list */
617170754Sdelphij	  if (using[other_thread])
618170754Sdelphij	    last_using[other_thread]->next = other_diff;
619170754Sdelphij	  else
620170754Sdelphij	    using[other_thread] = other_diff;
621170754Sdelphij	  last_using[other_thread] = other_diff;
622170754Sdelphij
623170754Sdelphij	  /* Take it off the current list.  Note that this following
624170754Sdelphij	     code assumes that other_diff enters it equal to
625170754Sdelphij	     current[high_water_thread ^ 0x1] */
626170754Sdelphij	  current[other_thread] = current[other_thread]->next;
627170754Sdelphij	  other_diff->next = 0;
628170754Sdelphij
629170754Sdelphij	  /* Set the high_water stuff
630170754Sdelphij	     If this comparison is equal, then this is the last pass
631170754Sdelphij	     through this loop; since diff blocks within a given
632170754Sdelphij	     thread cannot overlap, the high_water_mark will be
633170754Sdelphij	     *below* the range_start of either of the next diffs.  */
634170754Sdelphij
635170754Sdelphij	  if (high_water_mark < D_HIGHLINE (other_diff, FC))
636170754Sdelphij	    {
637170754Sdelphij	      high_water_thread ^= 1;
638170754Sdelphij	      high_water_diff = other_diff;
639170754Sdelphij	      high_water_mark = D_HIGHLINE (other_diff, FC);
640170754Sdelphij	    }
641170754Sdelphij
642170754Sdelphij	  /* Set the other diff */
643170754Sdelphij	  other_thread = high_water_thread ^ 0x1;
644170754Sdelphij	  other_diff = current[other_thread];
645170754Sdelphij	}
646170754Sdelphij
647170754Sdelphij      /* The using lists contain a list of all of the blocks to be
648170754Sdelphij	 included in this diff3_block.  Create it.  */
649170754Sdelphij
650170754Sdelphij      tmpblock = using_to_diff3_block (using, last_using,
651170754Sdelphij				       base_water_thread, high_water_thread,
652170754Sdelphij				       last_diff3);
653170754Sdelphij
654170754Sdelphij      if (!tmpblock)
655170754Sdelphij	fatal ("internal error: screwup in format of diff blocks");
656170754Sdelphij
657170754Sdelphij      /* Put it on the list.  */
658170754Sdelphij      *result_end = tmpblock;
659170754Sdelphij      result_end = &tmpblock->next;
660170754Sdelphij
661170754Sdelphij      /* Set up corresponding lines correctly.  */
662170754Sdelphij      last_diff3 = tmpblock;
663170754Sdelphij    }
664170754Sdelphij  return result;
665170754Sdelphij}
666170754Sdelphij
667170754Sdelphij/* Take two lists of blocks (from two separate diff threads) and put
668170754Sdelphij   them together into one diff3 block.  Return a pointer to this diff3
669170754Sdelphij   block or 0 for failure.
670170754Sdelphij
671170754Sdelphij   All arguments besides using are for the convenience of the routine;
672170754Sdelphij   they could be derived from the using array.  LAST_USING is a pair
673170754Sdelphij   of pointers to the last blocks in the using structure.  LOW_THREAD
674170754Sdelphij   and HIGH_THREAD tell which threads contain the lowest and highest
675170754Sdelphij   line numbers for File0.  LAST_DIFF3 contains the last diff produced
676170754Sdelphij   in the calling routine.  This is used for lines mappings that
677170754Sdelphij   would still be identical to the state that diff ended in.
678170754Sdelphij
679170754Sdelphij   A distinction should be made in this routine between the two diffs
680170754Sdelphij   that are part of a normal two diff block, and the three diffs that
681170754Sdelphij   are part of a diff3_block.  */
682170754Sdelphij
683170754Sdelphijstatic struct diff3_block *
684170754Sdelphijusing_to_diff3_block (struct diff_block *using[2],
685170754Sdelphij		      struct diff_block *last_using[2],
686170754Sdelphij		      int low_thread, int high_thread,
687170754Sdelphij		      struct diff3_block const *last_diff3)
688170754Sdelphij{
689170754Sdelphij  lin low[2], high[2];
690170754Sdelphij  struct diff3_block *result;
691170754Sdelphij  struct diff_block *ptr;
692170754Sdelphij  int d;
693170754Sdelphij  lin i;
694170754Sdelphij
695170754Sdelphij  /* Find the range in the common file.  */
696170754Sdelphij  lin lowc = D_LOWLINE (using[low_thread], FC);
697170754Sdelphij  lin highc = D_HIGHLINE (last_using[high_thread], FC);
698170754Sdelphij
699170754Sdelphij  /* Find the ranges in the other files.
700170754Sdelphij     If using[d] is null, that means that the file to which that diff
701170754Sdelphij     refers is equivalent to the common file over this range.  */
702170754Sdelphij
703170754Sdelphij  for (d = 0; d < 2; d++)
704170754Sdelphij    if (using[d])
705170754Sdelphij      {
706170754Sdelphij	low[d] = D_LOW_MAPLINE (using[d], FC, FO, lowc);
707170754Sdelphij	high[d] = D_HIGH_MAPLINE (last_using[d], FC, FO, highc);
708170754Sdelphij      }
709170754Sdelphij    else
710170754Sdelphij      {
711170754Sdelphij	low[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, lowc);
712170754Sdelphij	high[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, highc);
713170754Sdelphij      }
714170754Sdelphij
715170754Sdelphij  /* Create a block with the appropriate sizes */
716170754Sdelphij  result = create_diff3_block (low[0], high[0], low[1], high[1], lowc, highc);
717170754Sdelphij
718170754Sdelphij  /* Copy information for the common file.
719170754Sdelphij     Return with a zero if any of the compares failed.  */
720170754Sdelphij
721170754Sdelphij  for (d = 0; d < 2; d++)
722170754Sdelphij    for (ptr = using[d]; ptr; ptr = D_NEXT (ptr))
723170754Sdelphij      {
724170754Sdelphij	lin result_offset = D_LOWLINE (ptr, FC) - lowc;
725170754Sdelphij
726170754Sdelphij	if (!copy_stringlist (D_LINEARRAY (ptr, FC),
727170754Sdelphij			      D_LENARRAY (ptr, FC),
728170754Sdelphij			      D_LINEARRAY (result, FILEC) + result_offset,
729170754Sdelphij			      D_LENARRAY (result, FILEC) + result_offset,
730170754Sdelphij			      D_NUMLINES (ptr, FC)))
731170754Sdelphij	  return 0;
732170754Sdelphij      }
733170754Sdelphij
734170754Sdelphij  /* Copy information for file d.  First deal with anything that might be
735170754Sdelphij     before the first diff.  */
736170754Sdelphij
737170754Sdelphij  for (d = 0; d < 2; d++)
738170754Sdelphij    {
739170754Sdelphij      struct diff_block *u = using[d];
740170754Sdelphij      lin lo = low[d], hi = high[d];
741170754Sdelphij
742170754Sdelphij      for (i = 0;
743170754Sdelphij	   i + lo < (u ? D_LOWLINE (u, FO) : hi + 1);
744170754Sdelphij	   i++)
745170754Sdelphij	{
746170754Sdelphij	  D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, i);
747170754Sdelphij	  D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, i);
748170754Sdelphij	}
749170754Sdelphij
750170754Sdelphij      for (ptr = u; ptr; ptr = D_NEXT (ptr))
751170754Sdelphij	{
752170754Sdelphij	  lin result_offset = D_LOWLINE (ptr, FO) - lo;
753170754Sdelphij	  lin linec;
754170754Sdelphij
755170754Sdelphij	  if (!copy_stringlist (D_LINEARRAY (ptr, FO),
756170754Sdelphij				D_LENARRAY (ptr, FO),
757170754Sdelphij				D_LINEARRAY (result, FILE0 + d) + result_offset,
758170754Sdelphij				D_LENARRAY (result, FILE0 + d) + result_offset,
759170754Sdelphij				D_NUMLINES (ptr, FO)))
760170754Sdelphij	    return 0;
761170754Sdelphij
762170754Sdelphij	  /* Catch the lines between here and the next diff */
763170754Sdelphij	  linec = D_HIGHLINE (ptr, FC) + 1 - lowc;
764170754Sdelphij	  for (i = D_HIGHLINE (ptr, FO) + 1 - lo;
765170754Sdelphij	       i < (D_NEXT (ptr) ? D_LOWLINE (D_NEXT (ptr), FO) : hi + 1) - lo;
766170754Sdelphij	       i++)
767170754Sdelphij	    {
768170754Sdelphij	      D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, linec);
769170754Sdelphij	      D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, linec);
770170754Sdelphij	      linec++;
771170754Sdelphij	    }
772170754Sdelphij	}
773170754Sdelphij    }
774170754Sdelphij
775170754Sdelphij  /* Set correspond */
776170754Sdelphij  if (!using[0])
777170754Sdelphij    D3_TYPE (result) = DIFF_2ND;
778170754Sdelphij  else if (!using[1])
779170754Sdelphij    D3_TYPE (result) = DIFF_1ST;
780170754Sdelphij  else
781170754Sdelphij    {
782170754Sdelphij      lin nl0 = D_NUMLINES (result, FILE0);
783170754Sdelphij      lin nl1 = D_NUMLINES (result, FILE1);
784170754Sdelphij
785170754Sdelphij      if (nl0 != nl1
786170754Sdelphij	  || !compare_line_list (D_LINEARRAY (result, FILE0),
787170754Sdelphij				 D_LENARRAY (result, FILE0),
788170754Sdelphij				 D_LINEARRAY (result, FILE1),
789170754Sdelphij				 D_LENARRAY (result, FILE1),
790170754Sdelphij				 nl0))
791170754Sdelphij	D3_TYPE (result) = DIFF_ALL;
792170754Sdelphij      else
793170754Sdelphij	D3_TYPE (result) = DIFF_3RD;
794170754Sdelphij    }
795170754Sdelphij
796170754Sdelphij  return result;
797170754Sdelphij}
798170754Sdelphij
799170754Sdelphij/* Copy pointers from a list of strings to a different list of
800170754Sdelphij   strings.  If a spot in the second list is already filled, make sure
801170754Sdelphij   that it is filled with the same string; if not, return false, the copy
802170754Sdelphij   incomplete.  Upon successful completion of the copy, return true.  */
803170754Sdelphij
804170754Sdelphijstatic bool
805170754Sdelphijcopy_stringlist (char * const fromptrs[], size_t const fromlengths[],
806170754Sdelphij		 char *toptrs[], size_t tolengths[],
807170754Sdelphij		 lin copynum)
808170754Sdelphij{
809170754Sdelphij  register char * const *f = fromptrs;
810170754Sdelphij  register char **t = toptrs;
811170754Sdelphij  register size_t const *fl = fromlengths;
812170754Sdelphij  register size_t *tl = tolengths;
813170754Sdelphij
814170754Sdelphij  while (copynum--)
815170754Sdelphij    {
816170754Sdelphij      if (*t)
817170754Sdelphij	{
818170754Sdelphij	  if (*fl != *tl || memcmp (*f, *t, *fl) != 0)
819170754Sdelphij	    return false;
820170754Sdelphij	}
821170754Sdelphij      else
822170754Sdelphij	{
823170754Sdelphij	  *t = *f;
824170754Sdelphij	  *tl = *fl;
825170754Sdelphij	}
826170754Sdelphij
827170754Sdelphij      t++; f++; tl++; fl++;
828170754Sdelphij    }
829170754Sdelphij
830170754Sdelphij  return true;
831170754Sdelphij}
832170754Sdelphij
833170754Sdelphij/* Create a diff3_block, with ranges as specified in the arguments.
834170754Sdelphij   Allocate the arrays for the various pointers (and zero them) based
835170754Sdelphij   on the arguments passed.  Return the block as a result.  */
836170754Sdelphij
837170754Sdelphijstatic struct diff3_block *
838170754Sdelphijcreate_diff3_block (lin low0, lin high0,
839170754Sdelphij		    lin low1, lin high1,
840170754Sdelphij		    lin low2, lin high2)
841170754Sdelphij{
842170754Sdelphij  struct diff3_block *result = xmalloc (sizeof *result);
843170754Sdelphij  lin numlines;
844170754Sdelphij
845170754Sdelphij  D3_TYPE (result) = ERROR;
846170754Sdelphij  D_NEXT (result) = 0;
847170754Sdelphij
848170754Sdelphij  /* Assign ranges */
849170754Sdelphij  D_LOWLINE (result, FILE0) = low0;
850170754Sdelphij  D_HIGHLINE (result, FILE0) = high0;
851170754Sdelphij  D_LOWLINE (result, FILE1) = low1;
852170754Sdelphij  D_HIGHLINE (result, FILE1) = high1;
853170754Sdelphij  D_LOWLINE (result, FILE2) = low2;
854170754Sdelphij  D_HIGHLINE (result, FILE2) = high2;
855170754Sdelphij
856170754Sdelphij  /* Allocate and zero space */
857170754Sdelphij  numlines = D_NUMLINES (result, FILE0);
858170754Sdelphij  if (numlines)
859170754Sdelphij    {
860170754Sdelphij      D_LINEARRAY (result, FILE0) = xcalloc (numlines, sizeof (char *));
861170754Sdelphij      D_LENARRAY (result, FILE0) = xcalloc (numlines, sizeof (size_t));
862170754Sdelphij    }
863170754Sdelphij  else
864170754Sdelphij    {
865170754Sdelphij      D_LINEARRAY (result, FILE0) = 0;
866170754Sdelphij      D_LENARRAY (result, FILE0) = 0;
867170754Sdelphij    }
868170754Sdelphij
869170754Sdelphij  numlines = D_NUMLINES (result, FILE1);
870170754Sdelphij  if (numlines)
871170754Sdelphij    {
872170754Sdelphij      D_LINEARRAY (result, FILE1) = xcalloc (numlines, sizeof (char *));
873170754Sdelphij      D_LENARRAY (result, FILE1) = xcalloc (numlines, sizeof (size_t));
874170754Sdelphij    }
875170754Sdelphij  else
876170754Sdelphij    {
877170754Sdelphij      D_LINEARRAY (result, FILE1) = 0;
878170754Sdelphij      D_LENARRAY (result, FILE1) = 0;
879170754Sdelphij    }
880170754Sdelphij
881170754Sdelphij  numlines = D_NUMLINES (result, FILE2);
882170754Sdelphij  if (numlines)
883170754Sdelphij    {
884170754Sdelphij      D_LINEARRAY (result, FILE2) = xcalloc (numlines, sizeof (char *));
885170754Sdelphij      D_LENARRAY (result, FILE2) = xcalloc (numlines, sizeof (size_t));
886170754Sdelphij    }
887170754Sdelphij  else
888170754Sdelphij    {
889170754Sdelphij      D_LINEARRAY (result, FILE2) = 0;
890170754Sdelphij      D_LENARRAY (result, FILE2) = 0;
891170754Sdelphij    }
892170754Sdelphij
893170754Sdelphij  /* Return */
894170754Sdelphij  return result;
895170754Sdelphij}
896170754Sdelphij
897170754Sdelphij/* Compare two lists of lines of text.
898170754Sdelphij   Return 1 if they are equivalent, 0 if not.  */
899170754Sdelphij
900170754Sdelphijstatic bool
901170754Sdelphijcompare_line_list (char * const list1[], size_t const lengths1[],
902170754Sdelphij		   char * const list2[], size_t const lengths2[],
903170754Sdelphij		   lin nl)
904170754Sdelphij{
905170754Sdelphij  char * const *l1 = list1;
906170754Sdelphij  char * const *l2 = list2;
907170754Sdelphij  size_t const *lgths1 = lengths1;
908170754Sdelphij  size_t const *lgths2 = lengths2;
909170754Sdelphij
910170754Sdelphij  while (nl--)
911170754Sdelphij    if (!*l1 || !*l2 || *lgths1 != *lgths2++
912170754Sdelphij	|| memcmp (*l1++, *l2++, *lgths1++) != 0)
913170754Sdelphij      return false;
914170754Sdelphij  return true;
915170754Sdelphij}
916170754Sdelphij
917170754Sdelphij/* Input and parse two way diffs.  */
918170754Sdelphij
919170754Sdelphijstatic struct diff_block *
920170754Sdelphijprocess_diff (char const *filea,
921170754Sdelphij	      char const *fileb,
922170754Sdelphij	      struct diff_block **last_block)
923170754Sdelphij{
924170754Sdelphij  char *diff_contents;
925170754Sdelphij  char *diff_limit;
926170754Sdelphij  char *scan_diff;
927170754Sdelphij  enum diff_type dt;
928170754Sdelphij  lin i;
929170754Sdelphij  struct diff_block *block_list, **block_list_end, *bptr;
930170754Sdelphij  size_t too_many_lines = (PTRDIFF_MAX
931170754Sdelphij			   / MIN (sizeof *bptr->lines[1],
932170754Sdelphij				  sizeof *bptr->lengths[1]));
933170754Sdelphij
934170754Sdelphij  diff_limit = read_diff (filea, fileb, &diff_contents);
935170754Sdelphij  scan_diff = diff_contents;
936170754Sdelphij  block_list_end = &block_list;
937170754Sdelphij  bptr = 0; /* Pacify `gcc -W'.  */
938170754Sdelphij
939170754Sdelphij  while (scan_diff < diff_limit)
940170754Sdelphij    {
941170754Sdelphij      bptr = xmalloc (sizeof *bptr);
942170754Sdelphij      bptr->lines[0] = bptr->lines[1] = 0;
943170754Sdelphij      bptr->lengths[0] = bptr->lengths[1] = 0;
944170754Sdelphij
945170754Sdelphij      dt = process_diff_control (&scan_diff, bptr);
946170754Sdelphij      if (dt == ERROR || *scan_diff != '\n')
947170754Sdelphij	{
948170754Sdelphij	  fprintf (stderr, _("%s: diff failed: "), program_name);
949170754Sdelphij	  do
950170754Sdelphij	    {
951170754Sdelphij	      putc (*scan_diff, stderr);
952170754Sdelphij	    }
953170754Sdelphij	  while (*scan_diff++ != '\n');
954170754Sdelphij	  exit (EXIT_TROUBLE);
955170754Sdelphij	}
956170754Sdelphij      scan_diff++;
957170754Sdelphij
958170754Sdelphij      /* Force appropriate ranges to be null, if necessary */
959170754Sdelphij      switch (dt)
960170754Sdelphij	{
961170754Sdelphij	case ADD:
962170754Sdelphij	  bptr->ranges[0][0]++;
963170754Sdelphij	  break;
964170754Sdelphij	case DELETE:
965170754Sdelphij	  bptr->ranges[1][0]++;
966170754Sdelphij	  break;
967170754Sdelphij	case CHANGE:
968170754Sdelphij	  break;
969170754Sdelphij	default:
970170754Sdelphij	  fatal ("internal error: invalid diff type in process_diff");
971170754Sdelphij	  break;
972170754Sdelphij	}
973170754Sdelphij
974170754Sdelphij      /* Allocate space for the pointers for the lines from filea, and
975170754Sdelphij	 parcel them out among these pointers */
976170754Sdelphij      if (dt != ADD)
977170754Sdelphij	{
978170754Sdelphij	  lin numlines = D_NUMLINES (bptr, 0);
979170754Sdelphij	  if (too_many_lines <= numlines)
980170754Sdelphij	    xalloc_die ();
981170754Sdelphij	  bptr->lines[0] = xmalloc (numlines * sizeof *bptr->lines[0]);
982170754Sdelphij	  bptr->lengths[0] = xmalloc (numlines * sizeof *bptr->lengths[0]);
983170754Sdelphij	  for (i = 0; i < numlines; i++)
984170754Sdelphij	    scan_diff = scan_diff_line (scan_diff,
985170754Sdelphij					&(bptr->lines[0][i]),
986170754Sdelphij					&(bptr->lengths[0][i]),
987170754Sdelphij					diff_limit,
988170754Sdelphij					'<');
989170754Sdelphij	}
990170754Sdelphij
991170754Sdelphij      /* Get past the separator for changes */
992170754Sdelphij      if (dt == CHANGE)
993170754Sdelphij	{
994170754Sdelphij	  if (strncmp (scan_diff, "---\n", 4))
995170754Sdelphij	    fatal ("invalid diff format; invalid change separator");
996170754Sdelphij	  scan_diff += 4;
997170754Sdelphij	}
998170754Sdelphij
999170754Sdelphij      /* Allocate space for the pointers for the lines from fileb, and
1000170754Sdelphij	 parcel them out among these pointers */
1001170754Sdelphij      if (dt != DELETE)
1002170754Sdelphij	{
1003170754Sdelphij	  lin numlines = D_NUMLINES (bptr, 1);
1004170754Sdelphij	  if (too_many_lines <= numlines)
1005170754Sdelphij	    xalloc_die ();
1006170754Sdelphij	  bptr->lines[1] = xmalloc (numlines * sizeof *bptr->lines[1]);
1007170754Sdelphij	  bptr->lengths[1] = xmalloc (numlines * sizeof *bptr->lengths[1]);
1008170754Sdelphij	  for (i = 0; i < numlines; i++)
1009170754Sdelphij	    scan_diff = scan_diff_line (scan_diff,
1010170754Sdelphij					&(bptr->lines[1][i]),
1011170754Sdelphij					&(bptr->lengths[1][i]),
1012170754Sdelphij					diff_limit,
1013170754Sdelphij					'>');
1014170754Sdelphij	}
1015170754Sdelphij
1016170754Sdelphij      /* Place this block on the blocklist.  */
1017170754Sdelphij      *block_list_end = bptr;
1018170754Sdelphij      block_list_end = &bptr->next;
1019170754Sdelphij    }
1020170754Sdelphij
1021170754Sdelphij  *block_list_end = 0;
1022170754Sdelphij  *last_block = bptr;
1023170754Sdelphij  return block_list;
1024170754Sdelphij}
1025170754Sdelphij
1026170754Sdelphij/* Skip tabs and spaces, and return the first character after them.  */
1027170754Sdelphij
1028170754Sdelphijstatic char *
1029170754Sdelphijskipwhite (char *s)
1030170754Sdelphij{
1031170754Sdelphij  while (*s == ' ' || *s == '\t')
1032170754Sdelphij    s++;
1033170754Sdelphij  return s;
1034170754Sdelphij}
1035170754Sdelphij
1036170754Sdelphij/* Read a nonnegative line number from S, returning the address of the
1037170754Sdelphij   first character after the line number, and storing the number into
1038170754Sdelphij   *PNUM.  Return 0 if S does not point to a valid line number.  */
1039170754Sdelphij
1040170754Sdelphijstatic char *
1041170754Sdelphijreadnum (char *s, lin *pnum)
1042170754Sdelphij{
1043170754Sdelphij  unsigned char c = *s;
1044170754Sdelphij  lin num = 0;
1045170754Sdelphij
1046170754Sdelphij  if (! ISDIGIT (c))
1047170754Sdelphij    return 0;
1048170754Sdelphij
1049170754Sdelphij  do
1050170754Sdelphij    {
1051170754Sdelphij      num = c - '0' + num * 10;
1052170754Sdelphij      c = *++s;
1053170754Sdelphij    }
1054170754Sdelphij  while (ISDIGIT (c));
1055170754Sdelphij
1056170754Sdelphij  *pnum = num;
1057170754Sdelphij  return s;
1058170754Sdelphij}
1059170754Sdelphij
1060170754Sdelphij/* Parse a normal format diff control string.  Return the type of the
1061170754Sdelphij   diff (ERROR if the format is bad).  All of the other important
1062170754Sdelphij   information is filled into to the structure pointed to by db, and
1063170754Sdelphij   the string pointer (whose location is passed to this routine) is
1064170754Sdelphij   updated to point beyond the end of the string parsed.  Note that
1065170754Sdelphij   only the ranges in the diff_block will be set by this routine.
1066170754Sdelphij
1067170754Sdelphij   If some specific pair of numbers has been reduced to a single
1068170754Sdelphij   number, then both corresponding numbers in the diff block are set
1069170754Sdelphij   to that number.  In general these numbers are interpreted as ranges
1070170754Sdelphij   inclusive, unless being used by the ADD or DELETE commands.  It is
1071170754Sdelphij   assumed that these will be special cased in a superior routine.   */
1072170754Sdelphij
1073170754Sdelphijstatic enum diff_type
1074170754Sdelphijprocess_diff_control (char **string, struct diff_block *db)
1075170754Sdelphij{
1076170754Sdelphij  char *s = *string;
1077170754Sdelphij  enum diff_type type;
1078170754Sdelphij
1079170754Sdelphij  /* Read first set of digits */
1080170754Sdelphij  s = readnum (skipwhite (s), &db->ranges[0][RANGE_START]);
1081170754Sdelphij  if (! s)
1082170754Sdelphij    return ERROR;
1083170754Sdelphij
1084170754Sdelphij  /* Was that the only digit? */
1085170754Sdelphij  s = skipwhite (s);
1086170754Sdelphij  if (*s == ',')
1087170754Sdelphij    {
1088170754Sdelphij      s = readnum (s + 1, &db->ranges[0][RANGE_END]);
1089170754Sdelphij      if (! s)
1090170754Sdelphij	return ERROR;
1091170754Sdelphij    }
1092170754Sdelphij  else
1093170754Sdelphij    db->ranges[0][RANGE_END] = db->ranges[0][RANGE_START];
1094170754Sdelphij
1095170754Sdelphij  /* Get the letter */
1096170754Sdelphij  s = skipwhite (s);
1097170754Sdelphij  switch (*s)
1098170754Sdelphij    {
1099170754Sdelphij    case 'a':
1100170754Sdelphij      type = ADD;
1101170754Sdelphij      break;
1102170754Sdelphij    case 'c':
1103170754Sdelphij      type = CHANGE;
1104170754Sdelphij      break;
1105170754Sdelphij    case 'd':
1106170754Sdelphij      type = DELETE;
1107170754Sdelphij      break;
1108170754Sdelphij    default:
1109170754Sdelphij      return ERROR;			/* Bad format */
1110170754Sdelphij    }
1111170754Sdelphij  s++;				/* Past letter */
1112170754Sdelphij
1113170754Sdelphij  /* Read second set of digits */
1114170754Sdelphij  s = readnum (skipwhite (s), &db->ranges[1][RANGE_START]);
1115170754Sdelphij  if (! s)
1116170754Sdelphij    return ERROR;
1117170754Sdelphij
1118170754Sdelphij  /* Was that the only digit? */
1119170754Sdelphij  s = skipwhite (s);
1120170754Sdelphij  if (*s == ',')
1121170754Sdelphij    {
1122170754Sdelphij      s = readnum (s + 1, &db->ranges[1][RANGE_END]);
1123170754Sdelphij      if (! s)
1124170754Sdelphij	return ERROR;
1125170754Sdelphij      s = skipwhite (s);		/* To move to end */
1126170754Sdelphij    }
1127170754Sdelphij  else
1128170754Sdelphij    db->ranges[1][RANGE_END] = db->ranges[1][RANGE_START];
1129170754Sdelphij
1130170754Sdelphij  *string = s;
1131170754Sdelphij  return type;
1132170754Sdelphij}
1133170754Sdelphij
1134170754Sdelphijstatic char *
1135170754Sdelphijread_diff (char const *filea,
1136170754Sdelphij	   char const *fileb,
1137170754Sdelphij	   char **output_placement)
1138170754Sdelphij{
1139170754Sdelphij  char *diff_result;
1140170754Sdelphij  size_t current_chunk_size, total;
1141170754Sdelphij  int fd, wstatus, status;
1142170754Sdelphij  int werrno = 0;
1143170754Sdelphij  struct stat pipestat;
1144170754Sdelphij
1145170754Sdelphij#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
1146170754Sdelphij
1147170754Sdelphij  char const *argv[9];
1148170754Sdelphij  char const **ap;
1149170754Sdelphij  int fds[2];
1150170754Sdelphij  pid_t pid;
1151170754Sdelphij
1152170754Sdelphij  ap = argv;
1153170754Sdelphij  *ap++ = diff_program;
1154170754Sdelphij  if (text)
1155170754Sdelphij    *ap++ = "-a";
1156170754Sdelphij  if (strip_trailing_cr)
1157170754Sdelphij    *ap++ = "--strip-trailing-cr";
1158170754Sdelphij  *ap++ = "--horizon-lines=100";
1159170754Sdelphij  *ap++ = "--";
1160170754Sdelphij  *ap++ = filea;
1161170754Sdelphij  *ap++ = fileb;
1162170754Sdelphij  *ap = 0;
1163170754Sdelphij
1164170754Sdelphij  if (pipe (fds) != 0)
1165170754Sdelphij    perror_with_exit ("pipe");
1166170754Sdelphij
1167170754Sdelphij  pid = vfork ();
1168170754Sdelphij  if (pid == 0)
1169170754Sdelphij    {
1170170754Sdelphij      /* Child */
1171170754Sdelphij      close (fds[0]);
1172170754Sdelphij      if (fds[1] != STDOUT_FILENO)
1173170754Sdelphij	{
1174170754Sdelphij	  dup2 (fds[1], STDOUT_FILENO);
1175170754Sdelphij	  close (fds[1]);
1176170754Sdelphij	}
1177170754Sdelphij
1178170754Sdelphij      /* The cast to (char **) is needed for portability to older
1179170754Sdelphij	 hosts with a nonstandard prototype for execvp.  */
1180170754Sdelphij      execvp (diff_program, (char **) argv);
1181170754Sdelphij
1182170754Sdelphij      _exit (errno == ENOENT ? 127 : 126);
1183170754Sdelphij    }
1184170754Sdelphij
1185170754Sdelphij  if (pid == -1)
1186170754Sdelphij    perror_with_exit ("fork");
1187170754Sdelphij
1188170754Sdelphij  close (fds[1]);		/* Prevent erroneous lack of EOF */
1189170754Sdelphij  fd = fds[0];
1190170754Sdelphij
1191170754Sdelphij#else
1192170754Sdelphij
1193170754Sdelphij  FILE *fpipe;
1194170754Sdelphij  char const args[] = " --horizon-lines=100 -- ";
1195170754Sdelphij  char *command = xmalloc (quote_system_arg (0, diff_program)
1196170754Sdelphij			   + sizeof "-a"
1197170754Sdelphij			   + sizeof "--strip-trailing-cr"
1198170754Sdelphij			   + sizeof args - 1
1199170754Sdelphij			   + quote_system_arg (0, filea) + 1
1200170754Sdelphij			   + quote_system_arg (0, fileb) + 1);
1201170754Sdelphij  char *p = command;
1202170754Sdelphij  p += quote_system_arg (p, diff_program);
1203170754Sdelphij  if (text)
1204170754Sdelphij    {
1205170754Sdelphij      strcpy (p, " -a");
1206170754Sdelphij      p += 3;
1207170754Sdelphij    }
1208170754Sdelphij  if (strip_trailing_cr)
1209170754Sdelphij    {
1210170754Sdelphij      strcpy (p, " --strip-trailing-cr");
1211170754Sdelphij      p += 20;
1212170754Sdelphij    }
1213170754Sdelphij  strcpy (p, args);
1214170754Sdelphij  p += sizeof args - 1;
1215170754Sdelphij  p += quote_system_arg (p, filea);
1216170754Sdelphij  *p++ = ' ';
1217170754Sdelphij  p += quote_system_arg (p, fileb);
1218170754Sdelphij  *p = 0;
1219170754Sdelphij  errno = 0;
1220170754Sdelphij  fpipe = popen (command, "r");
1221170754Sdelphij  if (!fpipe)
1222170754Sdelphij    perror_with_exit (command);
1223170754Sdelphij  free (command);
1224170754Sdelphij  fd = fileno (fpipe);
1225170754Sdelphij
1226170754Sdelphij#endif
1227170754Sdelphij
1228170754Sdelphij  if (fstat (fd, &pipestat) != 0)
1229170754Sdelphij    perror_with_exit ("fstat");
1230170754Sdelphij  current_chunk_size = MAX (1, STAT_BLOCKSIZE (pipestat));
1231170754Sdelphij  diff_result = xmalloc (current_chunk_size);
1232170754Sdelphij  total = 0;
1233170754Sdelphij
1234170754Sdelphij  for (;;)
1235170754Sdelphij    {
1236170754Sdelphij      size_t bytes_to_read = current_chunk_size - total;
1237170754Sdelphij      size_t bytes = block_read (fd, diff_result + total, bytes_to_read);
1238170754Sdelphij      total += bytes;
1239170754Sdelphij      if (bytes != bytes_to_read)
1240170754Sdelphij	{
1241170754Sdelphij	  if (bytes == SIZE_MAX)
1242170754Sdelphij	    perror_with_exit (_("read failed"));
1243170754Sdelphij	  break;
1244170754Sdelphij	}
1245170754Sdelphij      if (PTRDIFF_MAX / 2 <= current_chunk_size)
1246170754Sdelphij	xalloc_die ();
1247170754Sdelphij      current_chunk_size *= 2;
1248170754Sdelphij      diff_result = xrealloc (diff_result, current_chunk_size);
1249170754Sdelphij    }
1250170754Sdelphij
1251170754Sdelphij  if (total != 0 && diff_result[total-1] != '\n')
1252170754Sdelphij    fatal ("invalid diff format; incomplete last line");
1253170754Sdelphij
1254170754Sdelphij  *output_placement = diff_result;
1255170754Sdelphij
1256170754Sdelphij#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
1257170754Sdelphij
1258170754Sdelphij  wstatus = pclose (fpipe);
1259170754Sdelphij  if (wstatus == -1)
1260170754Sdelphij    werrno = errno;
1261170754Sdelphij
1262170754Sdelphij#else
1263170754Sdelphij
1264170754Sdelphij  if (close (fd) != 0)
1265170754Sdelphij    perror_with_exit ("close");
1266170754Sdelphij  if (waitpid (pid, &wstatus, 0) < 0)
1267170754Sdelphij    perror_with_exit ("waitpid");
1268170754Sdelphij
1269170754Sdelphij#endif
1270170754Sdelphij
1271170754Sdelphij  status = ! werrno && WIFEXITED (wstatus) ? WEXITSTATUS (wstatus) : INT_MAX;
1272170754Sdelphij
1273170754Sdelphij  if (EXIT_TROUBLE <= status)
1274170754Sdelphij    error (EXIT_TROUBLE, werrno,
1275170754Sdelphij	   _(status == 126
1276170754Sdelphij	     ? "subsidiary program `%s' could not be invoked"
1277170754Sdelphij	     : status == 127
1278170754Sdelphij	     ? "subsidiary program `%s' not found"
1279170754Sdelphij	     : status == INT_MAX
1280170754Sdelphij	     ? "subsidiary program `%s' failed"
1281170754Sdelphij	     : "subsidiary program `%s' failed (exit status %d)"),
1282170754Sdelphij	   diff_program, status);
1283170754Sdelphij
1284170754Sdelphij  return diff_result + total;
1285170754Sdelphij}
1286170754Sdelphij
1287170754Sdelphij
1288170754Sdelphij/* Scan a regular diff line (consisting of > or <, followed by a
1289170754Sdelphij   space, followed by text (including nulls) up to a newline.
1290170754Sdelphij
1291170754Sdelphij   This next routine began life as a macro and many parameters in it
1292170754Sdelphij   are used as call-by-reference values.  */
1293170754Sdelphijstatic char *
1294170754Sdelphijscan_diff_line (char *scan_ptr, char **set_start, size_t *set_length,
1295170754Sdelphij		char *limit, char leadingchar)
1296170754Sdelphij{
1297170754Sdelphij  char *line_ptr;
1298170754Sdelphij
1299170754Sdelphij  if (!(scan_ptr[0] == leadingchar
1300170754Sdelphij	&& scan_ptr[1] == ' '))
1301170754Sdelphij    fatal ("invalid diff format; incorrect leading line chars");
1302170754Sdelphij
1303170754Sdelphij  *set_start = line_ptr = scan_ptr + 2;
1304170754Sdelphij  while (*line_ptr++ != '\n')
1305170754Sdelphij    continue;
1306170754Sdelphij
1307170754Sdelphij  /* Include newline if the original line ended in a newline,
1308170754Sdelphij     or if an edit script is being generated.
1309170754Sdelphij     Copy any missing newline message to stderr if an edit script is being
1310170754Sdelphij     generated, because edit scripts cannot handle missing newlines.
1311170754Sdelphij     Return the beginning of the next line.  */
1312170754Sdelphij  *set_length = line_ptr - *set_start;
1313170754Sdelphij  if (line_ptr < limit && *line_ptr == '\\')
1314170754Sdelphij    {
1315170754Sdelphij      if (edscript)
1316170754Sdelphij	fprintf (stderr, "%s:", program_name);
1317170754Sdelphij      else
1318170754Sdelphij	--*set_length;
1319170754Sdelphij      line_ptr++;
1320170754Sdelphij      do
1321170754Sdelphij	{
1322170754Sdelphij	  if (edscript)
1323170754Sdelphij	    putc (*line_ptr, stderr);
1324170754Sdelphij	}
1325170754Sdelphij      while (*line_ptr++ != '\n');
1326170754Sdelphij    }
1327170754Sdelphij
1328170754Sdelphij  return line_ptr;
1329170754Sdelphij}
1330170754Sdelphij
1331170754Sdelphij/* Output a three way diff passed as a list of diff3_block's.  The
1332170754Sdelphij   argument MAPPING is indexed by external file number (in the
1333170754Sdelphij   argument list) and contains the internal file number (from the diff
1334170754Sdelphij   passed).  This is important because the user expects outputs in
1335170754Sdelphij   terms of the argument list number, and the diff passed may have
1336170754Sdelphij   been done slightly differently (if the last argument was "-", for
1337170754Sdelphij   example).  REV_MAPPING is the inverse of MAPPING.  */
1338170754Sdelphij
1339170754Sdelphijstatic void
1340170754Sdelphijoutput_diff3 (FILE *outputfile, struct diff3_block *diff,
1341170754Sdelphij	      int const mapping[3], int const rev_mapping[3])
1342170754Sdelphij{
1343170754Sdelphij  int i;
1344170754Sdelphij  int oddoneout;
1345170754Sdelphij  char *cp;
1346170754Sdelphij  struct diff3_block *ptr;
1347170754Sdelphij  lin line;
1348170754Sdelphij  size_t length;
1349170754Sdelphij  int dontprint;
1350170754Sdelphij  static int skew_increment[3] = { 2, 3, 1 }; /* 0==>2==>1==>3 */
1351170754Sdelphij  char const *line_prefix = initial_tab ? "\t" : "  ";
1352170754Sdelphij
1353170754Sdelphij  for (ptr = diff; ptr; ptr = D_NEXT (ptr))
1354170754Sdelphij    {
1355170754Sdelphij      char x[2];
1356170754Sdelphij
1357170754Sdelphij      switch (ptr->correspond)
1358170754Sdelphij	{
1359170754Sdelphij	case DIFF_ALL:
1360170754Sdelphij	  x[0] = 0;
1361170754Sdelphij	  dontprint = 3;	/* Print them all */
1362170754Sdelphij	  oddoneout = 3;	/* Nobody's odder than anyone else */
1363170754Sdelphij	  break;
1364170754Sdelphij	case DIFF_1ST:
1365170754Sdelphij	case DIFF_2ND:
1366170754Sdelphij	case DIFF_3RD:
1367170754Sdelphij	  oddoneout = rev_mapping[ptr->correspond - DIFF_1ST];
1368170754Sdelphij
1369170754Sdelphij	  x[0] = oddoneout + '1';
1370170754Sdelphij	  x[1] = 0;
1371170754Sdelphij	  dontprint = oddoneout == 0;
1372170754Sdelphij	  break;
1373170754Sdelphij	default:
1374170754Sdelphij	  fatal ("internal error: invalid diff type passed to output");
1375170754Sdelphij	}
1376170754Sdelphij      fprintf (outputfile, "====%s\n", x);
1377170754Sdelphij
1378170754Sdelphij      /* Go 0, 2, 1 if the first and third outputs are equivalent.  */
1379170754Sdelphij      for (i = 0; i < 3;
1380170754Sdelphij	   i = (oddoneout == 1 ? skew_increment[i] : i + 1))
1381170754Sdelphij	{
1382170754Sdelphij	  int realfile = mapping[i];
1383170754Sdelphij	  lin lowt = D_LOWLINE (ptr, realfile);
1384170754Sdelphij	  lin hight = D_HIGHLINE (ptr, realfile);
1385170754Sdelphij	  long int llowt = lowt;
1386170754Sdelphij	  long int lhight = hight;
1387170754Sdelphij
1388170754Sdelphij	  fprintf (outputfile, "%d:", i + 1);
1389170754Sdelphij	  switch (lowt - hight)
1390170754Sdelphij	    {
1391170754Sdelphij	    case 1:
1392170754Sdelphij	      fprintf (outputfile, "%lda\n", llowt - 1);
1393170754Sdelphij	      break;
1394170754Sdelphij	    case 0:
1395170754Sdelphij	      fprintf (outputfile, "%ldc\n", llowt);
1396170754Sdelphij	      break;
1397170754Sdelphij	    default:
1398170754Sdelphij	      fprintf (outputfile, "%ld,%ldc\n", llowt, lhight);
1399170754Sdelphij	      break;
1400170754Sdelphij	    }
1401170754Sdelphij
1402170754Sdelphij	  if (i == dontprint) continue;
1403170754Sdelphij
1404170754Sdelphij	  if (lowt <= hight)
1405170754Sdelphij	    {
1406170754Sdelphij	      line = 0;
1407170754Sdelphij	      do
1408170754Sdelphij		{
1409170754Sdelphij		  fprintf (outputfile, line_prefix);
1410170754Sdelphij		  cp = D_RELNUM (ptr, realfile, line);
1411170754Sdelphij		  length = D_RELLEN (ptr, realfile, line);
1412170754Sdelphij		  fwrite (cp, sizeof (char), length, outputfile);
1413170754Sdelphij		}
1414170754Sdelphij	      while (++line < hight - lowt + 1);
1415170754Sdelphij	      if (cp[length - 1] != '\n')
1416170754Sdelphij		fprintf (outputfile, "\n\\ %s\n",
1417170754Sdelphij			 _("No newline at end of file"));
1418170754Sdelphij	    }
1419170754Sdelphij	}
1420170754Sdelphij    }
1421170754Sdelphij}
1422170754Sdelphij
1423170754Sdelphij
1424170754Sdelphij/* Output to OUTPUTFILE the lines of B taken from FILENUM.  Double any
1425170754Sdelphij   initial '.'s; yield nonzero if any initial '.'s were doubled.  */
1426170754Sdelphij
1427170754Sdelphijstatic bool
1428170754Sdelphijdotlines (FILE *outputfile, struct diff3_block *b, int filenum)
1429170754Sdelphij{
1430170754Sdelphij  lin i;
1431170754Sdelphij  bool leading_dot = false;
1432170754Sdelphij
1433170754Sdelphij  for (i = 0;
1434170754Sdelphij       i < D_NUMLINES (b, filenum);
1435170754Sdelphij       i++)
1436170754Sdelphij    {
1437170754Sdelphij      char *line = D_RELNUM (b, filenum, i);
1438170754Sdelphij      if (line[0] == '.')
1439170754Sdelphij	{
1440170754Sdelphij	  leading_dot = true;
1441170754Sdelphij	  fprintf (outputfile, ".");
1442170754Sdelphij	}
1443170754Sdelphij      fwrite (line, sizeof (char),
1444170754Sdelphij	      D_RELLEN (b, filenum, i), outputfile);
1445170754Sdelphij    }
1446170754Sdelphij
1447170754Sdelphij  return leading_dot;
1448170754Sdelphij}
1449170754Sdelphij
1450170754Sdelphij/* Output to OUTPUTFILE a '.' line.  If LEADING_DOT is true, also
1451170754Sdelphij   output a command that removes initial '.'s starting with line START
1452170754Sdelphij   and continuing for NUM lines.  (START is long int, not lin, for
1453170754Sdelphij   convenience with printf %ld formats.)  */
1454170754Sdelphij
1455170754Sdelphijstatic void
1456170754Sdelphijundotlines (FILE *outputfile, bool leading_dot, long int start, lin num)
1457170754Sdelphij{
1458170754Sdelphij  fprintf (outputfile, ".\n");
1459170754Sdelphij  if (leading_dot)
1460170754Sdelphij    {
1461170754Sdelphij      if (num == 1)
1462170754Sdelphij	fprintf (outputfile, "%lds/^\\.//\n", start);
1463170754Sdelphij      else
1464170754Sdelphij	fprintf (outputfile, "%ld,%lds/^\\.//\n", start, start + num - 1);
1465170754Sdelphij    }
1466170754Sdelphij}
1467170754Sdelphij
1468170754Sdelphij/* Output a diff3 set of blocks as an ed script.  This script applies
1469170754Sdelphij   the changes between file's 2 & 3 to file 1.  Take the precise
1470170754Sdelphij   format of the ed script to be output from global variables set
1471170754Sdelphij   during options processing.  Reverse the order of
1472170754Sdelphij   the set of diff3 blocks in DIFF; this gets
1473170754Sdelphij   around the problems involved with changing line numbers in an ed
1474170754Sdelphij   script.
1475170754Sdelphij
1476170754Sdelphij   As in `output_diff3', the variable MAPPING maps from file number
1477170754Sdelphij   according to the argument list to file number according to the diff
1478170754Sdelphij   passed.  All files listed below are in terms of the argument list.
1479170754Sdelphij   REV_MAPPING is the inverse of MAPPING.
1480170754Sdelphij
1481170754Sdelphij   FILE0, FILE1 and FILE2 are the strings to print as the names of the
1482170754Sdelphij   three files.  These may be the actual names, or may be the
1483170754Sdelphij   arguments specified with -L.
1484170754Sdelphij
1485170754Sdelphij   Return 1 if conflicts were found.  */
1486170754Sdelphij
1487170754Sdelphijstatic bool
1488170754Sdelphijoutput_diff3_edscript (FILE *outputfile, struct diff3_block *diff,
1489170754Sdelphij		       int const mapping[3], int const rev_mapping[3],
1490170754Sdelphij		       char const *file0, char const *file1, char const *file2)
1491170754Sdelphij{
1492170754Sdelphij  bool leading_dot;
1493170754Sdelphij  bool conflicts_found = false;
1494170754Sdelphij  bool conflict;
1495170754Sdelphij  struct diff3_block *b;
1496170754Sdelphij
1497170754Sdelphij  for (b = reverse_diff3_blocklist (diff); b; b = b->next)
1498170754Sdelphij    {
1499170754Sdelphij      /* Must do mapping correctly.  */
1500170754Sdelphij      enum diff_type type
1501170754Sdelphij	= (b->correspond == DIFF_ALL
1502170754Sdelphij	   ? DIFF_ALL
1503170754Sdelphij	   : DIFF_1ST + rev_mapping[b->correspond - DIFF_1ST]);
1504170754Sdelphij
1505170754Sdelphij      long int low0, high0;
1506170754Sdelphij
1507170754Sdelphij      /* If we aren't supposed to do this output block, skip it.  */
1508170754Sdelphij      switch (type)
1509170754Sdelphij	{
1510170754Sdelphij	default: continue;
1511170754Sdelphij	case DIFF_2ND: if (!show_2nd) continue; conflict = true; break;
1512170754Sdelphij	case DIFF_3RD: if (overlap_only) continue; conflict = false; break;
1513170754Sdelphij	case DIFF_ALL: if (simple_only) continue; conflict = flagging; break;
1514170754Sdelphij	}
1515170754Sdelphij
1516170754Sdelphij      low0 = D_LOWLINE (b, mapping[FILE0]);
1517170754Sdelphij      high0 = D_HIGHLINE (b, mapping[FILE0]);
1518170754Sdelphij
1519170754Sdelphij      if (conflict)
1520170754Sdelphij	{
1521170754Sdelphij	  conflicts_found = true;
1522170754Sdelphij
1523170754Sdelphij
1524170754Sdelphij	  /* Mark end of conflict.  */
1525170754Sdelphij
1526170754Sdelphij	  fprintf (outputfile, "%lda\n", high0);
1527170754Sdelphij	  leading_dot = false;
1528170754Sdelphij	  if (type == DIFF_ALL)
1529170754Sdelphij	    {
1530170754Sdelphij	      if (show_2nd)
1531170754Sdelphij		{
1532170754Sdelphij		  /* Append lines from FILE1.  */
1533170754Sdelphij		  fprintf (outputfile, "||||||| %s\n", file1);
1534170754Sdelphij		  leading_dot = dotlines (outputfile, b, mapping[FILE1]);
1535170754Sdelphij		}
1536170754Sdelphij	      /* Append lines from FILE2.  */
1537170754Sdelphij	      fprintf (outputfile, "=======\n");
1538170754Sdelphij	      leading_dot |= dotlines (outputfile, b, mapping[FILE2]);
1539170754Sdelphij	    }
1540170754Sdelphij	  fprintf (outputfile, ">>>>>>> %s\n", file2);
1541170754Sdelphij	  undotlines (outputfile, leading_dot, high0 + 2,
1542170754Sdelphij		      (D_NUMLINES (b, mapping[FILE1])
1543170754Sdelphij		       + D_NUMLINES (b, mapping[FILE2]) + 1));
1544170754Sdelphij
1545170754Sdelphij
1546170754Sdelphij	  /* Mark start of conflict.  */
1547170754Sdelphij
1548170754Sdelphij	  fprintf (outputfile, "%lda\n<<<<<<< %s\n", low0 - 1,
1549170754Sdelphij		   type == DIFF_ALL ? file0 : file1);
1550170754Sdelphij	  leading_dot = false;
1551170754Sdelphij	  if (type == DIFF_2ND)
1552170754Sdelphij	    {
1553170754Sdelphij	      /* Prepend lines from FILE1.  */
1554170754Sdelphij	      leading_dot = dotlines (outputfile, b, mapping[FILE1]);
1555170754Sdelphij	      fprintf (outputfile, "=======\n");
1556170754Sdelphij	    }
1557170754Sdelphij	  undotlines (outputfile, leading_dot, low0 + 1,
1558170754Sdelphij		      D_NUMLINES (b, mapping[FILE1]));
1559170754Sdelphij	}
1560170754Sdelphij      else if (D_NUMLINES (b, mapping[FILE2]) == 0)
1561170754Sdelphij	/* Write out a delete */
1562170754Sdelphij	{
1563170754Sdelphij	  if (low0 == high0)
1564170754Sdelphij	    fprintf (outputfile, "%ldd\n", low0);
1565170754Sdelphij	  else
1566170754Sdelphij	    fprintf (outputfile, "%ld,%ldd\n", low0, high0);
1567170754Sdelphij	}
1568170754Sdelphij      else
1569170754Sdelphij	/* Write out an add or change */
1570170754Sdelphij	{
1571170754Sdelphij	  switch (high0 - low0)
1572170754Sdelphij	    {
1573170754Sdelphij	    case -1:
1574170754Sdelphij	      fprintf (outputfile, "%lda\n", high0);
1575170754Sdelphij	      break;
1576170754Sdelphij	    case 0:
1577170754Sdelphij	      fprintf (outputfile, "%ldc\n", high0);
1578170754Sdelphij	      break;
1579170754Sdelphij	    default:
1580170754Sdelphij	      fprintf (outputfile, "%ld,%ldc\n", low0, high0);
1581170754Sdelphij	      break;
1582170754Sdelphij	    }
1583170754Sdelphij
1584170754Sdelphij	  undotlines (outputfile, dotlines (outputfile, b, mapping[FILE2]),
1585170754Sdelphij		      low0, D_NUMLINES (b, mapping[FILE2]));
1586170754Sdelphij	}
1587170754Sdelphij    }
1588170754Sdelphij  if (finalwrite) fprintf (outputfile, "w\nq\n");
1589170754Sdelphij  return conflicts_found;
1590170754Sdelphij}
1591170754Sdelphij
1592170754Sdelphij/* Read from INFILE and output to OUTPUTFILE a set of diff3_blocks
1593170754Sdelphij   DIFF as a merged file.  This acts like 'ed file0
1594170754Sdelphij   <[output_diff3_edscript]', except that it works even for binary
1595170754Sdelphij   data or incomplete lines.
1596170754Sdelphij
1597170754Sdelphij   As before, MAPPING maps from arg list file number to diff file
1598170754Sdelphij   number, REV_MAPPING is its inverse, and FILE0, FILE1, and FILE2 are
1599170754Sdelphij   the names of the files.
1600170754Sdelphij
1601170754Sdelphij   Return 1 if conflicts were found.  */
1602170754Sdelphij
1603170754Sdelphijstatic bool
1604170754Sdelphijoutput_diff3_merge (FILE *infile, FILE *outputfile, struct diff3_block *diff,
1605170754Sdelphij		    int const mapping[3], int const rev_mapping[3],
1606170754Sdelphij		    char const *file0, char const *file1, char const *file2)
1607170754Sdelphij{
1608170754Sdelphij  int c;
1609170754Sdelphij  lin i;
1610170754Sdelphij  bool conflicts_found = false;
1611170754Sdelphij  bool conflict;
1612170754Sdelphij  struct diff3_block *b;
1613170754Sdelphij  lin linesread = 0;
1614170754Sdelphij
1615170754Sdelphij  for (b = diff; b; b = b->next)
1616170754Sdelphij    {
1617170754Sdelphij      /* Must do mapping correctly.  */
1618170754Sdelphij      enum diff_type type
1619170754Sdelphij	= ((b->correspond == DIFF_ALL)
1620170754Sdelphij	   ? DIFF_ALL
1621170754Sdelphij	   : DIFF_1ST + rev_mapping[b->correspond - DIFF_1ST]);
1622170754Sdelphij      char const *format_2nd = "<<<<<<< %s\n";
1623170754Sdelphij
1624170754Sdelphij      /* If we aren't supposed to do this output block, skip it.  */
1625170754Sdelphij      switch (type)
1626170754Sdelphij	{
1627170754Sdelphij	default: continue;
1628170754Sdelphij	case DIFF_2ND: if (!show_2nd) continue; conflict = true; break;
1629170754Sdelphij	case DIFF_3RD: if (overlap_only) continue; conflict = false; break;
1630170754Sdelphij	case DIFF_ALL: if (simple_only) continue; conflict = flagging;
1631170754Sdelphij	  format_2nd = "||||||| %s\n";
1632170754Sdelphij	  break;
1633170754Sdelphij	}
1634170754Sdelphij
1635170754Sdelphij      /* Copy I lines from file 0.  */
1636170754Sdelphij      i = D_LOWLINE (b, FILE0) - linesread - 1;
1637170754Sdelphij      linesread += i;
1638170754Sdelphij      while (0 <= --i)
1639170754Sdelphij	do
1640170754Sdelphij	  {
1641170754Sdelphij	    c = getc (infile);
1642170754Sdelphij	    if (c == EOF)
1643170754Sdelphij	      {
1644170754Sdelphij		if (ferror (infile))
1645170754Sdelphij		  perror_with_exit (_("read failed"));
1646170754Sdelphij		else if (feof (infile))
1647170754Sdelphij		  fatal ("input file shrank");
1648170754Sdelphij	      }
1649170754Sdelphij	    putc (c, outputfile);
1650170754Sdelphij	  }
1651170754Sdelphij	while (c != '\n');
1652170754Sdelphij
1653170754Sdelphij      if (conflict)
1654170754Sdelphij	{
1655170754Sdelphij	  conflicts_found = true;
1656170754Sdelphij
1657170754Sdelphij	  if (type == DIFF_ALL)
1658170754Sdelphij	    {
1659170754Sdelphij	      /* Put in lines from FILE0 with bracket.  */
1660170754Sdelphij	      fprintf (outputfile, "<<<<<<< %s\n", file0);
1661170754Sdelphij	      for (i = 0;
1662170754Sdelphij		   i < D_NUMLINES (b, mapping[FILE0]);
1663170754Sdelphij		   i++)
1664170754Sdelphij		fwrite (D_RELNUM (b, mapping[FILE0], i), sizeof (char),
1665170754Sdelphij			D_RELLEN (b, mapping[FILE0], i), outputfile);
1666170754Sdelphij	    }
1667170754Sdelphij
1668170754Sdelphij	  if (show_2nd)
1669170754Sdelphij	    {
1670170754Sdelphij	      /* Put in lines from FILE1 with bracket.  */
1671170754Sdelphij	      fprintf (outputfile, format_2nd, file1);
1672170754Sdelphij	      for (i = 0;
1673170754Sdelphij		   i < D_NUMLINES (b, mapping[FILE1]);
1674170754Sdelphij		   i++)
1675170754Sdelphij		fwrite (D_RELNUM (b, mapping[FILE1], i), sizeof (char),
1676170754Sdelphij			D_RELLEN (b, mapping[FILE1], i), outputfile);
1677170754Sdelphij	    }
1678170754Sdelphij
1679170754Sdelphij	  fprintf (outputfile, "=======\n");
1680170754Sdelphij	}
1681170754Sdelphij
1682170754Sdelphij      /* Put in lines from FILE2.  */
1683170754Sdelphij      for (i = 0;
1684170754Sdelphij	   i < D_NUMLINES (b, mapping[FILE2]);
1685170754Sdelphij	   i++)
1686170754Sdelphij	fwrite (D_RELNUM (b, mapping[FILE2], i), sizeof (char),
1687170754Sdelphij		D_RELLEN (b, mapping[FILE2], i), outputfile);
1688170754Sdelphij
1689170754Sdelphij      if (conflict)
1690170754Sdelphij	fprintf (outputfile, ">>>>>>> %s\n", file2);
1691170754Sdelphij
1692170754Sdelphij      /* Skip I lines in file 0.  */
1693170754Sdelphij      i = D_NUMLINES (b, FILE0);
1694170754Sdelphij      linesread += i;
1695170754Sdelphij      while (0 <= --i)
1696170754Sdelphij	while ((c = getc (infile)) != '\n')
1697170754Sdelphij	  if (c == EOF)
1698170754Sdelphij	    {
1699170754Sdelphij	      if (ferror (infile))
1700170754Sdelphij		perror_with_exit (_("read failed"));
1701170754Sdelphij	      else if (feof (infile))
1702170754Sdelphij		{
1703170754Sdelphij		  if (i || b->next)
1704170754Sdelphij		    fatal ("input file shrank");
1705170754Sdelphij		  return conflicts_found;
1706170754Sdelphij		}
1707170754Sdelphij	    }
1708170754Sdelphij    }
1709170754Sdelphij  /* Copy rest of common file.  */
1710170754Sdelphij  while ((c = getc (infile)) != EOF || !(ferror (infile) | feof (infile)))
1711170754Sdelphij    putc (c, outputfile);
1712170754Sdelphij  return conflicts_found;
1713170754Sdelphij}
1714170754Sdelphij
1715170754Sdelphij/* Reverse the order of the list of diff3 blocks.  */
1716170754Sdelphij
1717170754Sdelphijstatic struct diff3_block *
1718170754Sdelphijreverse_diff3_blocklist (struct diff3_block *diff)
1719170754Sdelphij{
1720170754Sdelphij  register struct diff3_block *tmp, *next, *prev;
1721170754Sdelphij
1722170754Sdelphij  for (tmp = diff, prev = 0;  tmp;  tmp = next)
1723170754Sdelphij    {
1724170754Sdelphij      next = tmp->next;
1725170754Sdelphij      tmp->next = prev;
1726170754Sdelphij      prev = tmp;
1727170754Sdelphij    }
1728170754Sdelphij
1729170754Sdelphij  return prev;
1730170754Sdelphij}
1731170754Sdelphij
1732170754Sdelphijstatic void
1733170754Sdelphijfatal (char const *msgid)
1734170754Sdelphij{
1735170754Sdelphij  error (EXIT_TROUBLE, 0, "%s", _(msgid));
1736170754Sdelphij  abort ();
1737170754Sdelphij}
1738170754Sdelphij
1739170754Sdelphijstatic void
1740170754Sdelphijperror_with_exit (char const *string)
1741170754Sdelphij{
1742170754Sdelphij  error (EXIT_TROUBLE, errno, "%s", string);
1743170754Sdelphij  abort ();
1744170754Sdelphij}
1745