Blame src/diff3.c

Packit Service fdd496
/* diff3 - compare three files line by line
Packit Service fdd496
Packit Service fdd496
   Copyright (C) 1988-1989, 1992-1996, 1998, 2001-2002, 2004, 2006, 2009-2013,
Packit Service fdd496
   2015-2017 Free Software Foundation, Inc.
Packit Service fdd496
Packit Service fdd496
   This program is free software: you can redistribute it and/or modify
Packit Service fdd496
   it under the terms of the GNU General Public License as published by
Packit Service fdd496
   the Free Software Foundation, either version 3 of the License, or
Packit Service fdd496
   (at your option) any later version.
Packit Service fdd496
Packit Service fdd496
   This program is distributed in the hope that it will be useful,
Packit Service fdd496
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service fdd496
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service fdd496
   GNU General Public License for more details.
Packit Service fdd496
Packit Service fdd496
   You should have received a copy of the GNU General Public License
Packit Service fdd496
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit Service fdd496

Packit Service fdd496
#include "system.h"
Packit Service fdd496
#include "paths.h"
Packit Service fdd496
Packit Service fdd496
#include <stdio.h>
Packit Service fdd496
#include <unlocked-io.h>
Packit Service fdd496
Packit Service fdd496
#include <c-stack.h>
Packit Service fdd496
#include <cmpbuf.h>
Packit Service fdd496
#include "die.h"
Packit Service fdd496
#include <error.h>
Packit Service fdd496
#include <exitfail.h>
Packit Service fdd496
#include <file-type.h>
Packit Service fdd496
#include <getopt.h>
Packit Service fdd496
#include <progname.h>
Packit Service fdd496
#include <system-quote.h>
Packit Service fdd496
#include <version-etc.h>
Packit Service fdd496
#include <xalloc.h>
Packit Service fdd496
#include <xfreopen.h>
Packit Service fdd496
Packit Service fdd496
/* The official name of this program (e.g., no 'g' prefix).  */
Packit Service fdd496
#define PROGRAM_NAME "diff3"
Packit Service fdd496
Packit Service fdd496
#define AUTHORS \
Packit Service fdd496
  proper_name ("Randy Smith")
Packit Service fdd496
Packit Service fdd496
/* Internal data structures and macros for the diff3 program; includes
Packit Service fdd496
   data structures for both diff3 diffs and normal diffs.  */
Packit Service fdd496
Packit Service fdd496
/* Different files within a three way diff.  */
Packit Service fdd496
#define	FILE0	0
Packit Service fdd496
#define	FILE1	1
Packit Service fdd496
#define	FILE2	2
Packit Service fdd496
Packit Service fdd496
/* A three way diff is built from two two-way diffs; the file which
Packit Service fdd496
   the two two-way diffs share is:  */
Packit Service fdd496
#define	FILEC	FILE2
Packit Service fdd496
Packit Service fdd496
/* Different files within a two way diff.
Packit Service fdd496
   FC is the common file, FO the other file.  */
Packit Service fdd496
#define FO 0
Packit Service fdd496
#define FC 1
Packit Service fdd496
Packit Service fdd496
/* The ranges are indexed by */
Packit Service fdd496
#define	RANGE_START	0
Packit Service fdd496
#define	RANGE_END	1
Packit Service fdd496
Packit Service fdd496
enum diff_type {
Packit Service fdd496
  ERROR,			/* Should not be used */
Packit Service fdd496
  ADD,				/* Two way diff add */
Packit Service fdd496
  CHANGE,			/* Two way diff change */
Packit Service fdd496
  DELETE,			/* Two way diff delete */
Packit Service fdd496
  DIFF_ALL,			/* All three are different */
Packit Service fdd496
  DIFF_1ST,			/* Only the first is different */
Packit Service fdd496
  DIFF_2ND,			/* Only the second */
Packit Service fdd496
  DIFF_3RD			/* Only the third */
Packit Service fdd496
};
Packit Service fdd496
Packit Service fdd496
/* Two way diff */
Packit Service fdd496
struct diff_block {
Packit Service fdd496
  lin ranges[2][2];		/* Ranges are inclusive */
Packit Service fdd496
  char **lines[2];		/* The actual lines (may contain nulls) */
Packit Service fdd496
  size_t *lengths[2];		/* Line lengths (including newlines, if any) */
Packit Service fdd496
  struct diff_block *next;
Packit Service fdd496
#ifdef lint
Packit Service fdd496
  struct diff_block *n2;	/* Used only when freeing.  */
Packit Service fdd496
#endif
Packit Service fdd496
};
Packit Service fdd496
Packit Service fdd496
/* Three way diff */
Packit Service fdd496
Packit Service fdd496
struct diff3_block {
Packit Service fdd496
  enum diff_type correspond;	/* Type of diff */
Packit Service fdd496
  lin ranges[3][2];		/* Ranges are inclusive */
Packit Service fdd496
  char **lines[3];		/* The actual lines (may contain nulls) */
Packit Service fdd496
  size_t *lengths[3];		/* Line lengths (including newlines, if any) */
Packit Service fdd496
  struct diff3_block *next;
Packit Service fdd496
};
Packit Service fdd496
Packit Service fdd496
/* Access the ranges on a diff block.  */
Packit Service fdd496
#define	D_LOWLINE(diff, filenum)	\
Packit Service fdd496
  ((diff)->ranges[filenum][RANGE_START])
Packit Service fdd496
#define	D_HIGHLINE(diff, filenum)	\
Packit Service fdd496
  ((diff)->ranges[filenum][RANGE_END])
Packit Service fdd496
#define	D_NUMLINES(diff, filenum)	\
Packit Service fdd496
  (D_HIGHLINE (diff, filenum) - D_LOWLINE (diff, filenum) + 1)
Packit Service fdd496
Packit Service fdd496
/* Access the line numbers in a file in a diff by relative line
Packit Service fdd496
   numbers (i.e. line number within the diff itself).  Note that these
Packit Service fdd496
   are lvalues and can be used for assignment.  */
Packit Service fdd496
#define	D_RELNUM(diff, filenum, linenum)	\
Packit Service fdd496
  ((diff)->lines[filenum][linenum])
Packit Service fdd496
#define	D_RELLEN(diff, filenum, linenum)	\
Packit Service fdd496
  ((diff)->lengths[filenum][linenum])
Packit Service fdd496
Packit Service fdd496
/* And get at them directly, when that should be necessary.  */
Packit Service fdd496
#define	D_LINEARRAY(diff, filenum)	\
Packit Service fdd496
  ((diff)->lines[filenum])
Packit Service fdd496
#define	D_LENARRAY(diff, filenum)	\
Packit Service fdd496
  ((diff)->lengths[filenum])
Packit Service fdd496
Packit Service fdd496
/* Next block.  */
Packit Service fdd496
#define	D_NEXT(diff)	((diff)->next)
Packit Service fdd496
Packit Service fdd496
/* Access the type of a diff3 block.  */
Packit Service fdd496
#define	D3_TYPE(diff)	((diff)->correspond)
Packit Service fdd496
Packit Service fdd496
/* Line mappings based on diffs.  The first maps off the top of the
Packit Service fdd496
   diff, the second off of the bottom.  */
Packit Service fdd496
#define	D_HIGH_MAPLINE(diff, fromfile, tofile, linenum)	\
Packit Service fdd496
  ((linenum)						\
Packit Service fdd496
   - D_HIGHLINE ((diff), (fromfile))			\
Packit Service fdd496
   + D_HIGHLINE ((diff), (tofile)))
Packit Service fdd496
Packit Service fdd496
#define	D_LOW_MAPLINE(diff, fromfile, tofile, linenum)	\
Packit Service fdd496
  ((linenum)						\
Packit Service fdd496
   - D_LOWLINE ((diff), (fromfile))			\
Packit Service fdd496
   + D_LOWLINE ((diff), (tofile)))
Packit Service fdd496

Packit Service fdd496
/* Options variables for flags set on command line.  */
Packit Service fdd496
Packit Service fdd496
/* If nonzero, treat all files as text files, never as binary.  */
Packit Service fdd496
static bool text;
Packit Service fdd496
Packit Service fdd496
/* Remove trailing carriage returns from input.  */
Packit Service fdd496
static bool strip_trailing_cr;
Packit Service fdd496
Packit Service fdd496
/* If nonzero, write out an ed script instead of the standard diff3 format.  */
Packit Service fdd496
static bool edscript;
Packit Service fdd496
Packit Service fdd496
/* If nonzero, in the case of overlapping diffs (type DIFF_ALL),
Packit Service fdd496
   preserve the lines which would normally be deleted from
Packit Service fdd496
   file 1 with a special flagging mechanism.  */
Packit Service fdd496
static bool flagging;
Packit Service fdd496
Packit Service fdd496
/* Use a tab to align output lines (-T).  */
Packit Service fdd496
static bool initial_tab;
Packit Service fdd496
Packit Service fdd496
/* If nonzero, do not output information for overlapping diffs.  */
Packit Service fdd496
static bool simple_only;
Packit Service fdd496
Packit Service fdd496
/* If nonzero, do not output information for non-overlapping diffs.  */
Packit Service fdd496
static bool overlap_only;
Packit Service fdd496
Packit Service fdd496
/* If nonzero, show information for DIFF_2ND diffs.  */
Packit Service fdd496
static bool show_2nd;
Packit Service fdd496
Packit Service fdd496
/* If nonzero, include ':wq' at the end of the script
Packit Service fdd496
   to write out the file being edited.   */
Packit Service fdd496
static bool finalwrite;
Packit Service fdd496
Packit Service fdd496
/* If nonzero, output a merged file.  */
Packit Service fdd496
static bool merge;
Packit Service fdd496
Packit Service fdd496
static char *read_diff (char const *, char const *, char **);
Packit Service fdd496
static char *scan_diff_line (char *, char **, size_t *, char *, char);
Packit Service fdd496
static enum diff_type process_diff_control (char **, struct diff_block *);
Packit Service fdd496
static bool compare_line_list (char * const[], size_t const[], char * const[], size_t const[], lin);
Packit Service fdd496
static bool copy_stringlist (char * const[], size_t const[], char *[], size_t[], lin);
Packit Service fdd496
static bool output_diff3_edscript (FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *);
Packit Service fdd496
static bool output_diff3_merge (FILE *, FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *);
Packit Service fdd496
static struct diff3_block *create_diff3_block (lin, lin, lin, lin, lin, lin);
Packit Service fdd496
static struct diff3_block *make_3way_diff (struct diff_block *, struct diff_block *);
Packit Service fdd496
static struct diff3_block *reverse_diff3_blocklist (struct diff3_block *);
Packit Service fdd496
static struct diff3_block *using_to_diff3_block (struct diff_block *[2], struct diff_block *[2], int, int, struct diff3_block const *);
Packit Service fdd496
static struct diff_block *process_diff (char const *, char const *, struct diff_block **, char **);
Packit Service fdd496
static void check_stdout (void);
Packit Service fdd496
static void fatal (char const *) __attribute__((noreturn));
Packit Service fdd496
static void output_diff3 (FILE *, struct diff3_block *, int const[3], int const[3]);
Packit Service fdd496
static void perror_with_exit (char const *) __attribute__((noreturn));
Packit Service fdd496
static void try_help (char const *, char const *) __attribute__((noreturn));
Packit Service fdd496
static void usage (void);
Packit Service fdd496
Packit Service fdd496
static char const *diff_program = DEFAULT_DIFF_PROGRAM;
Packit Service fdd496
Packit Service fdd496
/* Values for long options that do not have single-letter equivalents.  */
Packit Service fdd496
enum
Packit Service fdd496
{
Packit Service fdd496
  DIFF_PROGRAM_OPTION = CHAR_MAX + 1,
Packit Service fdd496
  HELP_OPTION,
Packit Service fdd496
  STRIP_TRAILING_CR_OPTION
Packit Service fdd496
};
Packit Service fdd496
Packit Service fdd496
static struct option const longopts[] =
Packit Service fdd496
{
Packit Service fdd496
  {"diff-program", 1, 0, DIFF_PROGRAM_OPTION},
Packit Service fdd496
  {"easy-only", 0, 0, '3'},
Packit Service fdd496
  {"ed", 0, 0, 'e'},
Packit Service fdd496
  {"help", 0, 0, HELP_OPTION},
Packit Service fdd496
  {"initial-tab", 0, 0, 'T'},
Packit Service fdd496
  {"label", 1, 0, 'L'},
Packit Service fdd496
  {"merge", 0, 0, 'm'},
Packit Service fdd496
  {"overlap-only", 0, 0, 'x'},
Packit Service fdd496
  {"show-all", 0, 0, 'A'},
Packit Service fdd496
  {"show-overlap", 0, 0, 'E'},
Packit Service fdd496
  {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
Packit Service fdd496
  {"text", 0, 0, 'a'},
Packit Service fdd496
  {"version", 0, 0, 'v'},
Packit Service fdd496
  {0, 0, 0, 0}
Packit Service fdd496
};
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
free_diff_block (struct diff_block *p)
Packit Service fdd496
{
Packit Service fdd496
#ifndef lint
Packit Service fdd496
  (void)p;
Packit Service fdd496
#else
Packit Service fdd496
  while (p)
Packit Service fdd496
    {
Packit Service fdd496
      free (p->lines[0]);
Packit Service fdd496
      free (p->lines[1]);
Packit Service fdd496
      free (p->lengths[0]);
Packit Service fdd496
      free (p->lengths[1]);
Packit Service fdd496
      struct diff_block *next = p->n2;
Packit Service fdd496
      free (p);
Packit Service fdd496
      p = next;
Packit Service fdd496
    }
Packit Service fdd496
#endif
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Copy each next pointer to n2, since make_3way_diff would clobber the former,
Packit Service fdd496
   yet we will still need something to free these buffers.  */
Packit Service fdd496
static void
Packit Service fdd496
next_to_n2 (struct diff_block *p)
Packit Service fdd496
{
Packit Service fdd496
#ifndef lint
Packit Service fdd496
  (void)p;
Packit Service fdd496
#else
Packit Service fdd496
  while (p)
Packit Service fdd496
    p = p->n2 = p->next;
Packit Service fdd496
#endif
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
int
Packit Service fdd496
main (int argc, char **argv)
Packit Service fdd496
{
Packit Service fdd496
  int c, i;
Packit Service fdd496
  int common;
Packit Service fdd496
  int mapping[3];
Packit Service fdd496
  int rev_mapping[3];
Packit Service fdd496
  int incompat = 0;
Packit Service fdd496
  bool conflicts_found;
Packit Service fdd496
  struct diff_block *thread0, *thread1, *last_block;
Packit Service fdd496
  struct diff3_block *diff3;
Packit Service fdd496
  int tag_count = 0;
Packit Service fdd496
  char *tag_strings[3];
Packit Service fdd496
  char *commonname;
Packit Service fdd496
  char **file;
Packit Service fdd496
  struct stat statb;
Packit Service fdd496
Packit Service fdd496
  exit_failure = EXIT_TROUBLE;
Packit Service fdd496
  initialize_main (&argc, &argv);
Packit Service fdd496
  set_program_name (argv[0]);
Packit Service fdd496
  setlocale (LC_ALL, "");
Packit Service fdd496
  bindtextdomain (PACKAGE, LOCALEDIR);
Packit Service fdd496
  textdomain (PACKAGE);
Packit Service fdd496
  c_stack_action (0);
Packit Service fdd496
Packit Service fdd496
  while ((c = getopt_long (argc, argv, "aeimvx3AEL:TX", longopts, 0)) != -1)
Packit Service fdd496
    {
Packit Service fdd496
      switch (c)
Packit Service fdd496
	{
Packit Service fdd496
	case 'a':
Packit Service fdd496
	  text = true;
Packit Service fdd496
	  break;
Packit Service fdd496
	case 'A':
Packit Service fdd496
	  show_2nd = true;
Packit Service fdd496
	  flagging = true;
Packit Service fdd496
	  incompat++;
Packit Service fdd496
	  break;
Packit Service fdd496
	case 'x':
Packit Service fdd496
	  overlap_only = true;
Packit Service fdd496
	  incompat++;
Packit Service fdd496
	  break;
Packit Service fdd496
	case '3':
Packit Service fdd496
	  simple_only = true;
Packit Service fdd496
	  incompat++;
Packit Service fdd496
	  break;
Packit Service fdd496
	case 'i':
Packit Service fdd496
	  finalwrite = true;
Packit Service fdd496
	  break;
Packit Service fdd496
	case 'm':
Packit Service fdd496
	  merge = true;
Packit Service fdd496
	  break;
Packit Service fdd496
	case 'X':
Packit Service fdd496
	  overlap_only = true;
Packit Service fdd496
	  FALLTHROUGH;
Packit Service fdd496
	case 'E':
Packit Service fdd496
	  flagging = true;
Packit Service fdd496
	  FALLTHROUGH;
Packit Service fdd496
	case 'e':
Packit Service fdd496
	  incompat++;
Packit Service fdd496
	  break;
Packit Service fdd496
	case 'T':
Packit Service fdd496
	  initial_tab = true;
Packit Service fdd496
	  break;
Packit Service fdd496
	case STRIP_TRAILING_CR_OPTION:
Packit Service fdd496
	  strip_trailing_cr = true;
Packit Service fdd496
	  break;
Packit Service fdd496
	case 'v':
Packit Service fdd496
	  version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version,
Packit Service fdd496
		       AUTHORS, (char *) NULL);
Packit Service fdd496
	  check_stdout ();
Packit Service fdd496
	  return EXIT_SUCCESS;
Packit Service fdd496
	case DIFF_PROGRAM_OPTION:
Packit Service fdd496
	  diff_program = optarg;
Packit Service fdd496
	  break;
Packit Service fdd496
	case HELP_OPTION:
Packit Service fdd496
	  usage ();
Packit Service fdd496
	  check_stdout ();
Packit Service fdd496
	  return EXIT_SUCCESS;
Packit Service fdd496
	case 'L':
Packit Service fdd496
	  /* Handle up to three -L options.  */
Packit Service fdd496
	  if (tag_count < 3)
Packit Service fdd496
	    {
Packit Service fdd496
	      tag_strings[tag_count++] = optarg;
Packit Service fdd496
	      break;
Packit Service fdd496
	    }
Packit Service fdd496
	  try_help ("too many file label options", 0);
Packit Service fdd496
	default:
Packit Service fdd496
	  try_help (0, 0);
Packit Service fdd496
	}
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* -AeExX3 without -m implies ed script.  */
Packit Service fdd496
  edscript = incompat & ~(int) merge;
Packit Service fdd496
Packit Service fdd496
  show_2nd |= ~incompat & merge;  /* -m without -AeExX3 implies -A.  */
Packit Service fdd496
  flagging |= ~incompat & merge;
Packit Service fdd496
Packit Service fdd496
  if (incompat > 1  /* Ensure at most one of -AeExX3.  */
Packit Service fdd496
      || finalwrite & merge /* -i -m would rewrite input file.  */
Packit Service fdd496
      || (tag_count && ! flagging)) /* -L requires one of -AEX.  */
Packit Service fdd496
    try_help ("incompatible options", 0);
Packit Service fdd496
Packit Service fdd496
  if (argc - optind != 3)
Packit Service fdd496
    {
Packit Service fdd496
      if (argc - optind < 3)
Packit Service fdd496
	try_help ("missing operand after '%s'", argv[argc - 1]);
Packit Service fdd496
      else
Packit Service fdd496
	try_help ("extra operand '%s'", argv[optind + 3]);
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  file = &argv[optind];
Packit Service fdd496
Packit Service fdd496
  for (i = tag_count; i < 3; i++)
Packit Service fdd496
    tag_strings[i] = file[i];
Packit Service fdd496
Packit Service fdd496
  /* Always compare file1 to file2, even if file2 is "-".
Packit Service fdd496
     This is needed for -mAeExX3.  Using the file0 as
Packit Service fdd496
     the common file would produce wrong results, because if the
Packit Service fdd496
     file0-file1 diffs didn't line up with the file0-file2 diffs
Packit Service fdd496
     (which is entirely possible since we don't use diff's -n option),
Packit Service fdd496
     diff3 might report phantom changes from file1 to file2.
Packit Service fdd496
Packit Service fdd496
     Also, try to compare file0 to file1, because this is where
Packit Service fdd496
     changes are expected to come from.  Diffing between these pairs
Packit Service fdd496
     of files is more likely to avoid phantom changes from file0 to file1.
Packit Service fdd496
Packit Service fdd496
     Historically, the default common file was file2, so some older
Packit Service fdd496
     applications (e.g. Emacs ediff) used file2 as the ancestor.  So,
Packit Service fdd496
     for compatibility, if this is a 3-way diff (not a merge or
Packit Service fdd496
     edscript), prefer file2 as the common file.  */
Packit Service fdd496
Packit Service fdd496
  common = 2 - (edscript | merge);
Packit Service fdd496
Packit Service fdd496
  if (STREQ (file[common], "-"))
Packit Service fdd496
    {
Packit Service fdd496
      /* Sigh.  We've got standard input as the common file.  We can't
Packit Service fdd496
	 call diff twice on stdin.  Use the other arg as the common
Packit Service fdd496
	 file instead.  */
Packit Service fdd496
      common = 3 - common;
Packit Service fdd496
      if (STREQ (file[0], "-") || STREQ (file[common], "-"))
Packit Service fdd496
	fatal ("'-' specified for more than one input file");
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  mapping[0] = 0;
Packit Service fdd496
  mapping[1] = 3 - common;
Packit Service fdd496
  mapping[2] = common;
Packit Service fdd496
Packit Service fdd496
  for (i = 0; i < 3; i++)
Packit Service fdd496
    rev_mapping[mapping[i]] = i;
Packit Service fdd496
Packit Service fdd496
  for (i = 0; i < 3; i++)
Packit Service fdd496
    if (! STREQ (file[i], "-"))
Packit Service fdd496
      {
Packit Service fdd496
	if (stat (file[i], &statb) < 0)
Packit Service fdd496
	  perror_with_exit (file[i]);
Packit Service fdd496
	else if (S_ISDIR (statb.st_mode))
Packit Service fdd496
	  die (EXIT_TROUBLE, EISDIR, "%s", file[i]);
Packit Service fdd496
      }
Packit Service fdd496
Packit Service fdd496
#ifdef SIGCHLD
Packit Service fdd496
  /* System V fork+wait does not work if SIGCHLD is ignored.  */
Packit Service fdd496
  signal (SIGCHLD, SIG_DFL);
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
  /* Invoke diff twice on two pairs of input files, combine the two
Packit Service fdd496
     diffs, and output them.  */
Packit Service fdd496
Packit Service fdd496
  char *b0, *b1;
Packit Service fdd496
  commonname = file[rev_mapping[FILEC]];
Packit Service fdd496
  thread1 = process_diff (file[rev_mapping[FILE1]], commonname, &last_block, &b1;;
Packit Service fdd496
  thread0 = process_diff (file[rev_mapping[FILE0]], commonname, &last_block, &b0;;
Packit Service fdd496
Packit Service fdd496
  next_to_n2 (thread0);
Packit Service fdd496
  next_to_n2 (thread1);
Packit Service fdd496
Packit Service fdd496
  diff3 = make_3way_diff (thread0, thread1);
Packit Service fdd496
Packit Service fdd496
  free_diff_block (thread0);
Packit Service fdd496
  free_diff_block (thread1);
Packit Service fdd496
Packit Service fdd496
  if (edscript)
Packit Service fdd496
    conflicts_found
Packit Service fdd496
      = output_diff3_edscript (stdout, diff3, mapping, rev_mapping,
Packit Service fdd496
			       tag_strings[0], tag_strings[1], tag_strings[2]);
Packit Service fdd496
  else if (merge)
Packit Service fdd496
    {
Packit Service fdd496
      xfreopen (file[rev_mapping[FILE0]], "r", stdin);
Packit Service fdd496
      conflicts_found
Packit Service fdd496
	= output_diff3_merge (stdin, stdout, diff3, mapping, rev_mapping,
Packit Service fdd496
			      tag_strings[0], tag_strings[1], tag_strings[2]);
Packit Service fdd496
      if (ferror (stdin))
Packit Service fdd496
	fatal ("read failed");
Packit Service fdd496
    }
Packit Service fdd496
  else
Packit Service fdd496
    {
Packit Service fdd496
      output_diff3 (stdout, diff3, mapping, rev_mapping);
Packit Service fdd496
      conflicts_found = false;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  free (b0);
Packit Service fdd496
  free (b1);
Packit Service fdd496
  check_stdout ();
Packit Service fdd496
  exit (conflicts_found);
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
try_help (char const *reason_msgid, char const *operand)
Packit Service fdd496
{
Packit Service fdd496
  if (reason_msgid)
Packit Service fdd496
    error (0, 0, _(reason_msgid), operand);
Packit Service fdd496
  die (EXIT_TROUBLE, 0,
Packit Service fdd496
	 _("Try '%s --help' for more information."), program_name);
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
check_stdout (void)
Packit Service fdd496
{
Packit Service fdd496
  if (ferror (stdout))
Packit Service fdd496
    fatal ("write failed");
Packit Service fdd496
  else if (fclose (stdout) != 0)
Packit Service fdd496
    perror_with_exit (_("standard output"));
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
static char const * const option_help_msgid[] = {
Packit Service fdd496
  N_("-A, --show-all              output all changes, bracketing conflicts"),
Packit Service fdd496
  "",
Packit Service fdd496
  N_("-e, --ed                    output ed script incorporating changes\n"
Packit Service fdd496
     "                                from OLDFILE to YOURFILE into MYFILE"),
Packit Service fdd496
  N_("-E, --show-overlap          like -e, but bracket conflicts"),
Packit Service fdd496
  N_("-3, --easy-only             like -e, but incorporate only nonoverlapping changes"),
Packit Service fdd496
  N_("-x, --overlap-only          like -e, but incorporate only overlapping changes"),
Packit Service fdd496
  N_("-X                          like -x, but bracket conflicts"),
Packit Service fdd496
  N_("-i                          append 'w' and 'q' commands to ed scripts"),
Packit Service fdd496
  "",
Packit Service fdd496
  N_("-m, --merge                 output actual merged file, according to\n"
Packit Service fdd496
     "                                -A if no other options are given"),
Packit Service fdd496
  "",
Packit Service fdd496
  N_("-a, --text                  treat all files as text"),
Packit Service fdd496
  N_("    --strip-trailing-cr     strip trailing carriage return on input"),
Packit Service fdd496
  N_("-T, --initial-tab           make tabs line up by prepending a tab"),
Packit Service fdd496
  N_("    --diff-program=PROGRAM  use PROGRAM to compare files"),
Packit Service fdd496
  N_("-L, --label=LABEL           use LABEL instead of file name\n"
Packit Service fdd496
     "                                (can be repeated up to three times)"),
Packit Service fdd496
  "",
Packit Service fdd496
  N_("    --help                  display this help and exit"),
Packit Service fdd496
  N_("-v, --version               output version information and exit"),
Packit Service fdd496
  0
Packit Service fdd496
};
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
usage (void)
Packit Service fdd496
{
Packit Service fdd496
  char const * const *p;
Packit Service fdd496
Packit Service fdd496
  printf (_("Usage: %s [OPTION]... MYFILE OLDFILE YOURFILE\n"),
Packit Service fdd496
	  program_name);
Packit Service fdd496
  printf ("%s\n\n", _("Compare three files line by line."));
Packit Service fdd496
Packit Service fdd496
  fputs (_("\
Packit Service fdd496
Mandatory arguments to long options are mandatory for short options too.\n\
Packit Service fdd496
"), stdout);
Packit Service fdd496
  for (p = option_help_msgid;  *p;  p++)
Packit Service fdd496
    if (**p)
Packit Service fdd496
      printf ("  %s\n", _(*p));
Packit Service fdd496
    else
Packit Service fdd496
      putchar ('\n');
Packit Service fdd496
  fputs (_("\n\
Packit Service fdd496
The default output format is a somewhat human-readable representation of\n\
Packit Service fdd496
the changes.\n\
Packit Service fdd496
\n\
Packit Service fdd496
The -e, -E, -x, -X (and corresponding long) options cause an ed script\n\
Packit Service fdd496
to be output instead of the default.\n\
Packit Service fdd496
\n\
Packit Service fdd496
Finally, the -m (--merge) option causes diff3 to do the merge internally\n\
Packit Service fdd496
and output the actual merged file.  For unusual input, this is more\n\
Packit Service fdd496
robust than using ed.\n"), stdout);
Packit Service fdd496
  printf ("\n%s\n%s\n",
Packit Service fdd496
	  _("If a FILE is '-', read standard input."),
Packit Service fdd496
	  _("Exit status is 0 if successful, 1 if conflicts, 2 if trouble."));
Packit Service fdd496
  emit_bug_reporting_address ();
Packit Service fdd496
}
Packit Service fdd496

Packit Service fdd496
/* Combine the two diffs together into one.
Packit Service fdd496
   Here is the algorithm:
Packit Service fdd496
Packit Service fdd496
     File2 is shared in common between the two diffs.
Packit Service fdd496
     Diff02 is the diff between 0 and 2.
Packit Service fdd496
     Diff12 is the diff between 1 and 2.
Packit Service fdd496
Packit Service fdd496
	1) Find the range for the first block in File2.
Packit Service fdd496
	    a) Take the lowest of the two ranges (in File2) in the two
Packit Service fdd496
	       current blocks (one from each diff) as being the low
Packit Service fdd496
	       water mark.  Assign the upper end of this block as
Packit Service fdd496
	       being the high water mark and move the current block up
Packit Service fdd496
	       one.  Mark the block just moved over as to be used.
Packit Service fdd496
	    b) Check the next block in the diff that the high water
Packit Service fdd496
	       mark is *not* from.
Packit Service fdd496
Packit Service fdd496
	       *If* the high water mark is above
Packit Service fdd496
	       the low end of the range in that block,
Packit Service fdd496
Packit Service fdd496
		   mark that block as to be used and move the current
Packit Service fdd496
		   block up.  Set the high water mark to the max of
Packit Service fdd496
		   the high end of this block and the current.  Repeat b.
Packit Service fdd496
Packit Service fdd496
	 2) Find the corresponding ranges in File0 (from the blocks
Packit Service fdd496
	    in diff02; line per line outside of diffs) and in File1.
Packit Service fdd496
	    Create a diff3_block, reserving space as indicated by the ranges.
Packit Service fdd496
Packit Service fdd496
	 3) Copy all of the pointers for file2 in.  At least for now,
Packit Service fdd496
	    do memcmp's between corresponding strings in the two diffs.
Packit Service fdd496
Packit Service fdd496
	 4) Copy all of the pointers for file0 and 1 in.  Get what is
Packit Service fdd496
	    needed from file2 (when there isn't a diff block, it's
Packit Service fdd496
	    identical to file2 within the range between diff blocks).
Packit Service fdd496
Packit Service fdd496
	 5) If the diff blocks used came from only one of the two
Packit Service fdd496
	    strings of diffs, then that file (i.e. the one other than
Packit Service fdd496
	    the common file in that diff) is the odd person out.  If
Packit Service fdd496
	    diff blocks are used from both sets, check to see if files
Packit Service fdd496
	    0 and 1 match:
Packit Service fdd496
Packit Service fdd496
		Same number of lines?  If so, do a set of memcmp's (if
Packit Service fdd496
	    a memcmp matches; copy the pointer over; it'll be easier
Packit Service fdd496
	    later during comparisons).  If they match, 0 & 1 are the
Packit Service fdd496
	    same.  If not, all three different.
Packit Service fdd496
Packit Service fdd496
     Then do it again, until the blocks are exhausted.  */
Packit Service fdd496
Packit Service fdd496
Packit Service fdd496
/* Make a three way diff (chain of diff3_block's) from two two way
Packit Service fdd496
   diffs (chains of diff_block's).  Assume that each of the two diffs
Packit Service fdd496
   passed are onto the same file (i.e. that each of the diffs were
Packit Service fdd496
   made "to" the same file).  Return a three way diff pointer with
Packit Service fdd496
   numbering FILE0 = the other file in diff02, FILE1 = the other file
Packit Service fdd496
   in diff12, and FILEC = the common file.  */
Packit Service fdd496
Packit Service fdd496
static struct diff3_block *
Packit Service fdd496
make_3way_diff (struct diff_block *thread0, struct diff_block *thread1)
Packit Service fdd496
{
Packit Service fdd496
  /* Work on the two diffs passed to it as threads.  Thread number 0
Packit Service fdd496
     is diff02, thread number 1 is diff12.  USING is the base of the
Packit Service fdd496
     list of blocks to be used to construct each block of the three
Packit Service fdd496
     way diff; if no blocks from a particular thread are to be used,
Packit Service fdd496
     that element of USING is 0.  LAST_USING contains the last
Packit Service fdd496
     elements on each of the using lists.
Packit Service fdd496
Packit Service fdd496
     HIGH_WATER_MARK is the highest line number in the common file
Packit Service fdd496
     described in any of the diffs in either of the USING lists.
Packit Service fdd496
     HIGH_WATER_THREAD names the thread.  Similarly BASE_WATER_MARK
Packit Service fdd496
     and BASE_WATER_THREAD describe the lowest line number in the
Packit Service fdd496
     common file described in any of the diffs in either of the USING
Packit Service fdd496
     lists.  HIGH_WATER_DIFF is the diff from which the
Packit Service fdd496
     HIGH_WATER_MARK was taken.
Packit Service fdd496
Packit Service fdd496
     HIGH_WATER_DIFF should always be equal to
Packit Service fdd496
     LAST_USING[HIGH_WATER_THREAD].  OTHER_DIFF is the next diff to
Packit Service fdd496
     check for higher water, and should always be equal to
Packit Service fdd496
     CURRENT[HIGH_WATER_THREAD ^ 1].  OTHER_THREAD is the thread in
Packit Service fdd496
     which the OTHER_DIFF is, and hence should always be equal to
Packit Service fdd496
     HIGH_WATER_THREAD ^ 1.
Packit Service fdd496
Packit Service fdd496
     LAST_DIFF is the last diff block produced by this routine, for
Packit Service fdd496
     line correspondence purposes between that diff and the one
Packit Service fdd496
     currently being worked on.  It is ZERO_DIFF before any blocks
Packit Service fdd496
     have been created.  */
Packit Service fdd496
Packit Service fdd496
  struct diff_block *using[2];
Packit Service fdd496
  struct diff_block *last_using[2];
Packit Service fdd496
  struct diff_block *current[2];
Packit Service fdd496
Packit Service fdd496
  lin high_water_mark;
Packit Service fdd496
Packit Service fdd496
  int high_water_thread;
Packit Service fdd496
  int base_water_thread;
Packit Service fdd496
  int other_thread;
Packit Service fdd496
Packit Service fdd496
  struct diff_block *high_water_diff;
Packit Service fdd496
  struct diff_block *other_diff;
Packit Service fdd496
Packit Service fdd496
  struct diff3_block *result;
Packit Service fdd496
  struct diff3_block *tmpblock;
Packit Service fdd496
  struct diff3_block **result_end;
Packit Service fdd496
Packit Service fdd496
  struct diff3_block const *last_diff3;
Packit Service fdd496
Packit Service fdd496
  static struct diff3_block const zero_diff3;
Packit Service fdd496
Packit Service fdd496
  /* Initialization */
Packit Service fdd496
  result = 0;
Packit Service fdd496
  result_end = &result;
Packit Service fdd496
  current[0] = thread0; current[1] = thread1;
Packit Service fdd496
  last_diff3 = &zero_diff3;
Packit Service fdd496
Packit Service fdd496
  /* Sniff up the threads until we reach the end */
Packit Service fdd496
Packit Service fdd496
  while (current[0] || current[1])
Packit Service fdd496
    {
Packit Service fdd496
      using[0] = using[1] = last_using[0] = last_using[1] = 0;
Packit Service fdd496
Packit Service fdd496
      /* Setup low and high water threads, diffs, and marks.  */
Packit Service fdd496
      if (!current[0])
Packit Service fdd496
	base_water_thread = 1;
Packit Service fdd496
      else if (!current[1])
Packit Service fdd496
	base_water_thread = 0;
Packit Service fdd496
      else
Packit Service fdd496
	base_water_thread =
Packit Service fdd496
	  (D_LOWLINE (current[0], FC) > D_LOWLINE (current[1], FC));
Packit Service fdd496
Packit Service fdd496
      high_water_thread = base_water_thread;
Packit Service fdd496
Packit Service fdd496
      high_water_diff = current[high_water_thread];
Packit Service fdd496
Packit Service fdd496
      high_water_mark = D_HIGHLINE (high_water_diff, FC);
Packit Service fdd496
Packit Service fdd496
      /* Make the diff you just got info from into the using class */
Packit Service fdd496
      using[high_water_thread]
Packit Service fdd496
	= last_using[high_water_thread]
Packit Service fdd496
	= high_water_diff;
Packit Service fdd496
      current[high_water_thread] = high_water_diff->next;
Packit Service fdd496
      last_using[high_water_thread]->next = 0;
Packit Service fdd496
Packit Service fdd496
      /* And mark the other diff */
Packit Service fdd496
      other_thread = high_water_thread ^ 0x1;
Packit Service fdd496
      other_diff = current[other_thread];
Packit Service fdd496
Packit Service fdd496
      /* Shuffle up the ladder, checking the other diff to see if it
Packit Service fdd496
	 needs to be incorporated.  */
Packit Service fdd496
      while (other_diff
Packit Service fdd496
	     && D_LOWLINE (other_diff, FC) <= high_water_mark + 1)
Packit Service fdd496
	{
Packit Service fdd496
Packit Service fdd496
	  /* Incorporate this diff into the using list.  Note that
Packit Service fdd496
	     this doesn't take it off the current list */
Packit Service fdd496
	  if (using[other_thread])
Packit Service fdd496
	    last_using[other_thread]->next = other_diff;
Packit Service fdd496
	  else
Packit Service fdd496
	    using[other_thread] = other_diff;
Packit Service fdd496
	  last_using[other_thread] = other_diff;
Packit Service fdd496
Packit Service fdd496
	  /* Take it off the current list.  Note that this following
Packit Service fdd496
	     code assumes that other_diff enters it equal to
Packit Service fdd496
	     current[high_water_thread ^ 0x1] */
Packit Service fdd496
	  current[other_thread] = current[other_thread]->next;
Packit Service fdd496
	  other_diff->next = 0;
Packit Service fdd496
Packit Service fdd496
	  /* Set the high_water stuff
Packit Service fdd496
	     If this comparison is equal, then this is the last pass
Packit Service fdd496
	     through this loop; since diff blocks within a given
Packit Service fdd496
	     thread cannot overlap, the high_water_mark will be
Packit Service fdd496
	     *below* the range_start of either of the next diffs.  */
Packit Service fdd496
Packit Service fdd496
	  if (high_water_mark < D_HIGHLINE (other_diff, FC))
Packit Service fdd496
	    {
Packit Service fdd496
	      high_water_thread ^= 1;
Packit Service fdd496
	      high_water_mark = D_HIGHLINE (other_diff, FC);
Packit Service fdd496
	    }
Packit Service fdd496
Packit Service fdd496
	  /* Set the other diff */
Packit Service fdd496
	  other_thread = high_water_thread ^ 0x1;
Packit Service fdd496
	  other_diff = current[other_thread];
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      /* The using lists contain a list of all of the blocks to be
Packit Service fdd496
	 included in this diff3_block.  Create it.  */
Packit Service fdd496
Packit Service fdd496
      tmpblock = using_to_diff3_block (using, last_using,
Packit Service fdd496
				       base_water_thread, high_water_thread,
Packit Service fdd496
				       last_diff3);
Packit Service fdd496
Packit Service fdd496
      if (!tmpblock)
Packit Service fdd496
	fatal ("internal error: screwup in format of diff blocks");
Packit Service fdd496
Packit Service fdd496
      /* Put it on the list.  */
Packit Service fdd496
      *result_end = tmpblock;
Packit Service fdd496
      result_end = &tmpblock->next;
Packit Service fdd496
Packit Service fdd496
      /* Set up corresponding lines correctly.  */
Packit Service fdd496
      last_diff3 = tmpblock;
Packit Service fdd496
    }
Packit Service fdd496
  return result;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Take two lists of blocks (from two separate diff threads) and put
Packit Service fdd496
   them together into one diff3 block.  Return a pointer to this diff3
Packit Service fdd496
   block or 0 for failure.
Packit Service fdd496
Packit Service fdd496
   All arguments besides using are for the convenience of the routine;
Packit Service fdd496
   they could be derived from the using array.  LAST_USING is a pair
Packit Service fdd496
   of pointers to the last blocks in the using structure.  LOW_THREAD
Packit Service fdd496
   and HIGH_THREAD tell which threads contain the lowest and highest
Packit Service fdd496
   line numbers for File0.  LAST_DIFF3 contains the last diff produced
Packit Service fdd496
   in the calling routine.  This is used for lines mappings that
Packit Service fdd496
   would still be identical to the state that diff ended in.
Packit Service fdd496
Packit Service fdd496
   A distinction should be made in this routine between the two diffs
Packit Service fdd496
   that are part of a normal two diff block, and the three diffs that
Packit Service fdd496
   are part of a diff3_block.  */
Packit Service fdd496
Packit Service fdd496
static struct diff3_block *
Packit Service fdd496
using_to_diff3_block (struct diff_block *using[2],
Packit Service fdd496
		      struct diff_block *last_using[2],
Packit Service fdd496
		      int low_thread, int high_thread,
Packit Service fdd496
		      struct diff3_block const *last_diff3)
Packit Service fdd496
{
Packit Service fdd496
  lin low[2], high[2];
Packit Service fdd496
  struct diff3_block *result;
Packit Service fdd496
  struct diff_block *ptr;
Packit Service fdd496
  int d;
Packit Service fdd496
  lin i;
Packit Service fdd496
Packit Service fdd496
  /* Find the range in the common file.  */
Packit Service fdd496
  lin lowc = D_LOWLINE (using[low_thread], FC);
Packit Service fdd496
  lin highc = D_HIGHLINE (last_using[high_thread], FC);
Packit Service fdd496
Packit Service fdd496
  /* Find the ranges in the other files.
Packit Service fdd496
     If using[d] is null, that means that the file to which that diff
Packit Service fdd496
     refers is equivalent to the common file over this range.  */
Packit Service fdd496
Packit Service fdd496
  for (d = 0; d < 2; d++)
Packit Service fdd496
    if (using[d])
Packit Service fdd496
      {
Packit Service fdd496
	low[d] = D_LOW_MAPLINE (using[d], FC, FO, lowc);
Packit Service fdd496
	high[d] = D_HIGH_MAPLINE (last_using[d], FC, FO, highc);
Packit Service fdd496
      }
Packit Service fdd496
    else
Packit Service fdd496
      {
Packit Service fdd496
	low[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, lowc);
Packit Service fdd496
	high[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, highc);
Packit Service fdd496
      }
Packit Service fdd496
Packit Service fdd496
  /* Create a block with the appropriate sizes */
Packit Service fdd496
  result = create_diff3_block (low[0], high[0], low[1], high[1], lowc, highc);
Packit Service fdd496
Packit Service fdd496
  /* Copy information for the common file.
Packit Service fdd496
     Return with a zero if any of the compares failed.  */
Packit Service fdd496
Packit Service fdd496
  for (d = 0; d < 2; d++)
Packit Service fdd496
    for (ptr = using[d]; ptr; ptr = D_NEXT (ptr))
Packit Service fdd496
      {
Packit Service fdd496
	lin result_offset = D_LOWLINE (ptr, FC) - lowc;
Packit Service fdd496
Packit Service fdd496
	if (!copy_stringlist (D_LINEARRAY (ptr, FC),
Packit Service fdd496
			      D_LENARRAY (ptr, FC),
Packit Service fdd496
			      D_LINEARRAY (result, FILEC) + result_offset,
Packit Service fdd496
			      D_LENARRAY (result, FILEC) + result_offset,
Packit Service fdd496
			      D_NUMLINES (ptr, FC)))
Packit Service fdd496
	  return 0;
Packit Service fdd496
      }
Packit Service fdd496
Packit Service fdd496
  /* Copy information for file d.  First deal with anything that might be
Packit Service fdd496
     before the first diff.  */
Packit Service fdd496
Packit Service fdd496
  for (d = 0; d < 2; d++)
Packit Service fdd496
    {
Packit Service fdd496
      struct diff_block *u = using[d];
Packit Service fdd496
      lin lo = low[d], hi = high[d];
Packit Service fdd496
Packit Service fdd496
      for (i = 0;
Packit Service fdd496
	   i + lo < (u ? D_LOWLINE (u, FO) : hi + 1);
Packit Service fdd496
	   i++)
Packit Service fdd496
	{
Packit Service fdd496
	  D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, i);
Packit Service fdd496
	  D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, i);
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      for (ptr = u; ptr; ptr = D_NEXT (ptr))
Packit Service fdd496
	{
Packit Service fdd496
	  lin result_offset = D_LOWLINE (ptr, FO) - lo;
Packit Service fdd496
	  lin linec;
Packit Service fdd496
Packit Service fdd496
	  if (!copy_stringlist (D_LINEARRAY (ptr, FO),
Packit Service fdd496
				D_LENARRAY (ptr, FO),
Packit Service fdd496
				D_LINEARRAY (result, FILE0 + d) + result_offset,
Packit Service fdd496
				D_LENARRAY (result, FILE0 + d) + result_offset,
Packit Service fdd496
				D_NUMLINES (ptr, FO)))
Packit Service fdd496
	    return 0;
Packit Service fdd496
Packit Service fdd496
	  /* Catch the lines between here and the next diff */
Packit Service fdd496
	  linec = D_HIGHLINE (ptr, FC) + 1 - lowc;
Packit Service fdd496
	  for (i = D_HIGHLINE (ptr, FO) + 1 - lo;
Packit Service fdd496
	       i < (D_NEXT (ptr) ? D_LOWLINE (D_NEXT (ptr), FO) : hi + 1) - lo;
Packit Service fdd496
	       i++)
Packit Service fdd496
	    {
Packit Service fdd496
	      D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, linec);
Packit Service fdd496
	      D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, linec);
Packit Service fdd496
	      linec++;
Packit Service fdd496
	    }
Packit Service fdd496
	}
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* Set correspond */
Packit Service fdd496
  if (!using[0])
Packit Service fdd496
    D3_TYPE (result) = DIFF_2ND;
Packit Service fdd496
  else if (!using[1])
Packit Service fdd496
    D3_TYPE (result) = DIFF_1ST;
Packit Service fdd496
  else
Packit Service fdd496
    {
Packit Service fdd496
      lin nl0 = D_NUMLINES (result, FILE0);
Packit Service fdd496
      lin nl1 = D_NUMLINES (result, FILE1);
Packit Service fdd496
Packit Service fdd496
      if (nl0 != nl1
Packit Service fdd496
	  || !compare_line_list (D_LINEARRAY (result, FILE0),
Packit Service fdd496
				 D_LENARRAY (result, FILE0),
Packit Service fdd496
				 D_LINEARRAY (result, FILE1),
Packit Service fdd496
				 D_LENARRAY (result, FILE1),
Packit Service fdd496
				 nl0))
Packit Service fdd496
	D3_TYPE (result) = DIFF_ALL;
Packit Service fdd496
      else
Packit Service fdd496
	D3_TYPE (result) = DIFF_3RD;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  return result;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Copy pointers from a list of strings to a different list of
Packit Service fdd496
   strings.  If a spot in the second list is already filled, make sure
Packit Service fdd496
   that it is filled with the same string; if not, return false, the copy
Packit Service fdd496
   incomplete.  Upon successful completion of the copy, return true.  */
Packit Service fdd496
Packit Service fdd496
static bool
Packit Service fdd496
copy_stringlist (char * const fromptrs[], size_t const fromlengths[],
Packit Service fdd496
		 char *toptrs[], size_t tolengths[],
Packit Service fdd496
		 lin copynum)
Packit Service fdd496
{
Packit Service fdd496
  register char * const *f = fromptrs;
Packit Service fdd496
  register char **t = toptrs;
Packit Service fdd496
  register size_t const *fl = fromlengths;
Packit Service fdd496
  register size_t *tl = tolengths;
Packit Service fdd496
Packit Service fdd496
  while (copynum--)
Packit Service fdd496
    {
Packit Service fdd496
      if (*t)
Packit Service fdd496
	{
Packit Service fdd496
	  if (*fl != *tl || memcmp (*f, *t, *fl) != 0)
Packit Service fdd496
	    return false;
Packit Service fdd496
	}
Packit Service fdd496
      else
Packit Service fdd496
	{
Packit Service fdd496
	  *t = *f;
Packit Service fdd496
	  *tl = *fl;
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      t++; f++; tl++; fl++;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  return true;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Create a diff3_block, with ranges as specified in the arguments.
Packit Service fdd496
   Allocate the arrays for the various pointers (and zero them) based
Packit Service fdd496
   on the arguments passed.  Return the block as a result.  */
Packit Service fdd496
Packit Service fdd496
static struct diff3_block *
Packit Service fdd496
create_diff3_block (lin low0, lin high0,
Packit Service fdd496
		    lin low1, lin high1,
Packit Service fdd496
		    lin low2, lin high2)
Packit Service fdd496
{
Packit Service fdd496
  struct diff3_block *result = xmalloc (sizeof *result);
Packit Service fdd496
  lin numlines;
Packit Service fdd496
Packit Service fdd496
  D3_TYPE (result) = ERROR;
Packit Service fdd496
  D_NEXT (result) = 0;
Packit Service fdd496
Packit Service fdd496
  /* Assign ranges */
Packit Service fdd496
  D_LOWLINE (result, FILE0) = low0;
Packit Service fdd496
  D_HIGHLINE (result, FILE0) = high0;
Packit Service fdd496
  D_LOWLINE (result, FILE1) = low1;
Packit Service fdd496
  D_HIGHLINE (result, FILE1) = high1;
Packit Service fdd496
  D_LOWLINE (result, FILE2) = low2;
Packit Service fdd496
  D_HIGHLINE (result, FILE2) = high2;
Packit Service fdd496
Packit Service fdd496
  /* Allocate and zero space */
Packit Service fdd496
  numlines = D_NUMLINES (result, FILE0);
Packit Service fdd496
  if (numlines)
Packit Service fdd496
    {
Packit Service fdd496
      D_LINEARRAY (result, FILE0) = xcalloc (numlines, sizeof (char *));
Packit Service fdd496
      D_LENARRAY (result, FILE0) = xcalloc (numlines, sizeof (size_t));
Packit Service fdd496
    }
Packit Service fdd496
  else
Packit Service fdd496
    {
Packit Service fdd496
      D_LINEARRAY (result, FILE0) = 0;
Packit Service fdd496
      D_LENARRAY (result, FILE0) = 0;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  numlines = D_NUMLINES (result, FILE1);
Packit Service fdd496
  if (numlines)
Packit Service fdd496
    {
Packit Service fdd496
      D_LINEARRAY (result, FILE1) = xcalloc (numlines, sizeof (char *));
Packit Service fdd496
      D_LENARRAY (result, FILE1) = xcalloc (numlines, sizeof (size_t));
Packit Service fdd496
    }
Packit Service fdd496
  else
Packit Service fdd496
    {
Packit Service fdd496
      D_LINEARRAY (result, FILE1) = 0;
Packit Service fdd496
      D_LENARRAY (result, FILE1) = 0;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  numlines = D_NUMLINES (result, FILE2);
Packit Service fdd496
  if (numlines)
Packit Service fdd496
    {
Packit Service fdd496
      D_LINEARRAY (result, FILE2) = xcalloc (numlines, sizeof (char *));
Packit Service fdd496
      D_LENARRAY (result, FILE2) = xcalloc (numlines, sizeof (size_t));
Packit Service fdd496
    }
Packit Service fdd496
  else
Packit Service fdd496
    {
Packit Service fdd496
      D_LINEARRAY (result, FILE2) = 0;
Packit Service fdd496
      D_LENARRAY (result, FILE2) = 0;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  /* Return */
Packit Service fdd496
  return result;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Compare two lists of lines of text.
Packit Service fdd496
   Return 1 if they are equivalent, 0 if not.  */
Packit Service fdd496
Packit Service fdd496
static bool
Packit Service fdd496
compare_line_list (char * const list1[], size_t const lengths1[],
Packit Service fdd496
		   char * const list2[], size_t const lengths2[],
Packit Service fdd496
		   lin nl)
Packit Service fdd496
{
Packit Service fdd496
  char * const *l1 = list1;
Packit Service fdd496
  char * const *l2 = list2;
Packit Service fdd496
  size_t const *lgths1 = lengths1;
Packit Service fdd496
  size_t const *lgths2 = lengths2;
Packit Service fdd496
Packit Service fdd496
  while (nl--)
Packit Service fdd496
    if (!*l1 || !*l2 || *lgths1 != *lgths2++
Packit Service fdd496
	|| memcmp (*l1++, *l2++, *lgths1++) != 0)
Packit Service fdd496
      return false;
Packit Service fdd496
  return true;
Packit Service fdd496
}
Packit Service fdd496

Packit Service fdd496
/* Input and parse two way diffs.  */
Packit Service fdd496
Packit Service fdd496
static struct diff_block *
Packit Service fdd496
process_diff (char const *filea,
Packit Service fdd496
	      char const *fileb,
Packit Service fdd496
	      struct diff_block **last_block,
Packit Service fdd496
	      char **buf_to_free)
Packit Service fdd496
{
Packit Service fdd496
  char *diff_contents;
Packit Service fdd496
  char *diff_limit;
Packit Service fdd496
  char *scan_diff;
Packit Service fdd496
  enum diff_type dt;
Packit Service fdd496
  lin i;
Packit Service fdd496
  struct diff_block *block_list;
Packit Service fdd496
  struct diff_block **block_list_end = &block_list;
Packit Service fdd496
  struct diff_block *bptr IF_LINT (= NULL);
Packit Service fdd496
  size_t too_many_lines = (PTRDIFF_MAX
Packit Service fdd496
			   / MIN (sizeof *bptr->lines[1],
Packit Service fdd496
				  sizeof *bptr->lengths[1]));
Packit Service fdd496
Packit Service fdd496
  diff_limit = read_diff (filea, fileb, &diff_contents);
Packit Service fdd496
  *buf_to_free = diff_contents;
Packit Service fdd496
  scan_diff = diff_contents;
Packit Service fdd496
Packit Service fdd496
  while (scan_diff < diff_limit)
Packit Service fdd496
    {
Packit Service fdd496
      bptr = xmalloc (sizeof *bptr);
Packit Service fdd496
      bptr->lines[0] = bptr->lines[1] = 0;
Packit Service fdd496
      bptr->lengths[0] = bptr->lengths[1] = 0;
Packit Service fdd496
Packit Service fdd496
      dt = process_diff_control (&scan_diff, bptr);
Packit Service fdd496
      if (dt == ERROR || *scan_diff != '\n')
Packit Service fdd496
	{
Packit Service fdd496
	  fprintf (stderr, _("%s: diff failed: "), program_name);
Packit Service fdd496
	  do
Packit Service fdd496
	    {
Packit Service fdd496
	      putc (*scan_diff, stderr);
Packit Service fdd496
	    }
Packit Service fdd496
	  while (*scan_diff++ != '\n');
Packit Service fdd496
	  exit (EXIT_TROUBLE);
Packit Service fdd496
	}
Packit Service fdd496
      scan_diff++;
Packit Service fdd496
Packit Service fdd496
      /* Force appropriate ranges to be null, if necessary */
Packit Service fdd496
      switch (dt)
Packit Service fdd496
	{
Packit Service fdd496
	case ADD:
Packit Service fdd496
	  bptr->ranges[0][0]++;
Packit Service fdd496
	  break;
Packit Service fdd496
	case DELETE:
Packit Service fdd496
	  bptr->ranges[1][0]++;
Packit Service fdd496
	  break;
Packit Service fdd496
	case CHANGE:
Packit Service fdd496
	  break;
Packit Service fdd496
	default:
Packit Service fdd496
	  fatal ("internal error: invalid diff type in process_diff");
Packit Service fdd496
	  break;
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      /* Allocate space for the pointers for the lines from filea, and
Packit Service fdd496
	 parcel them out among these pointers */
Packit Service fdd496
      if (dt != ADD)
Packit Service fdd496
	{
Packit Service fdd496
	  lin numlines = D_NUMLINES (bptr, 0);
Packit Service fdd496
	  if (too_many_lines <= numlines)
Packit Service fdd496
	    xalloc_die ();
Packit Service fdd496
	  bptr->lines[0] = xmalloc (numlines * sizeof *bptr->lines[0]);
Packit Service fdd496
	  bptr->lengths[0] = xmalloc (numlines * sizeof *bptr->lengths[0]);
Packit Service fdd496
	  for (i = 0; i < numlines; i++)
Packit Service fdd496
	    scan_diff = scan_diff_line (scan_diff,
Packit Service fdd496
					&(bptr->lines[0][i]),
Packit Service fdd496
					&(bptr->lengths[0][i]),
Packit Service fdd496
					diff_limit,
Packit Service fdd496
					'<');
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      /* Get past the separator for changes */
Packit Service fdd496
      if (dt == CHANGE)
Packit Service fdd496
	{
Packit Service fdd496
	  if (strncmp (scan_diff, "---\n", 4))
Packit Service fdd496
	    fatal ("invalid diff format; invalid change separator");
Packit Service fdd496
	  scan_diff += 4;
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      /* Allocate space for the pointers for the lines from fileb, and
Packit Service fdd496
	 parcel them out among these pointers */
Packit Service fdd496
      if (dt != DELETE)
Packit Service fdd496
	{
Packit Service fdd496
	  lin numlines = D_NUMLINES (bptr, 1);
Packit Service fdd496
	  if (too_many_lines <= numlines)
Packit Service fdd496
	    xalloc_die ();
Packit Service fdd496
	  bptr->lines[1] = xmalloc (numlines * sizeof *bptr->lines[1]);
Packit Service fdd496
	  bptr->lengths[1] = xmalloc (numlines * sizeof *bptr->lengths[1]);
Packit Service fdd496
	  for (i = 0; i < numlines; i++)
Packit Service fdd496
	    scan_diff = scan_diff_line (scan_diff,
Packit Service fdd496
					&(bptr->lines[1][i]),
Packit Service fdd496
					&(bptr->lengths[1][i]),
Packit Service fdd496
					diff_limit,
Packit Service fdd496
					'>');
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      /* Place this block on the blocklist.  */
Packit Service fdd496
      *block_list_end = bptr;
Packit Service fdd496
      block_list_end = &bptr->next;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  *block_list_end = NULL;
Packit Service fdd496
  *last_block = bptr;
Packit Service fdd496
  return block_list;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Skip tabs and spaces, and return the first character after them.  */
Packit Service fdd496
Packit Service fdd496
static char * _GL_ATTRIBUTE_PURE
Packit Service fdd496
skipwhite (char *s)
Packit Service fdd496
{
Packit Service fdd496
  while (*s == ' ' || *s == '\t')
Packit Service fdd496
    s++;
Packit Service fdd496
  return s;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Read a nonnegative line number from S, returning the address of the
Packit Service fdd496
   first character after the line number, and storing the number into
Packit Service fdd496
   *PNUM.  Return 0 if S does not point to a valid line number.  */
Packit Service fdd496
Packit Service fdd496
static char *
Packit Service fdd496
readnum (char *s, lin *pnum)
Packit Service fdd496
{
Packit Service fdd496
  unsigned char c = *s;
Packit Service fdd496
  lin num = 0;
Packit Service fdd496
Packit Service fdd496
  if (! ISDIGIT (c))
Packit Service fdd496
    return 0;
Packit Service fdd496
Packit Service fdd496
  do
Packit Service fdd496
    {
Packit Service fdd496
      num = c - '0' + num * 10;
Packit Service fdd496
      c = *++s;
Packit Service fdd496
    }
Packit Service fdd496
  while (ISDIGIT (c));
Packit Service fdd496
Packit Service fdd496
  *pnum = num;
Packit Service fdd496
  return s;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Parse a normal format diff control string.  Return the type of the
Packit Service fdd496
   diff (ERROR if the format is bad).  All of the other important
Packit Service fdd496
   information is filled into to the structure pointed to by db, and
Packit Service fdd496
   the string pointer (whose location is passed to this routine) is
Packit Service fdd496
   updated to point beyond the end of the string parsed.  Note that
Packit Service fdd496
   only the ranges in the diff_block will be set by this routine.
Packit Service fdd496
Packit Service fdd496
   If some specific pair of numbers has been reduced to a single
Packit Service fdd496
   number, then both corresponding numbers in the diff block are set
Packit Service fdd496
   to that number.  In general these numbers are interpreted as ranges
Packit Service fdd496
   inclusive, unless being used by the ADD or DELETE commands.  It is
Packit Service fdd496
   assumed that these will be special cased in a superior routine.   */
Packit Service fdd496
Packit Service fdd496
static enum diff_type
Packit Service fdd496
process_diff_control (char **string, struct diff_block *db)
Packit Service fdd496
{
Packit Service fdd496
  char *s = *string;
Packit Service fdd496
  enum diff_type type;
Packit Service fdd496
Packit Service fdd496
  /* Read first set of digits */
Packit Service fdd496
  s = readnum (skipwhite (s), &db->ranges[0][RANGE_START]);
Packit Service fdd496
  if (! s)
Packit Service fdd496
    return ERROR;
Packit Service fdd496
Packit Service fdd496
  /* Was that the only digit? */
Packit Service fdd496
  s = skipwhite (s);
Packit Service fdd496
  if (*s == ',')
Packit Service fdd496
    {
Packit Service fdd496
      s = readnum (s + 1, &db->ranges[0][RANGE_END]);
Packit Service fdd496
      if (! s)
Packit Service fdd496
	return ERROR;
Packit Service fdd496
    }
Packit Service fdd496
  else
Packit Service fdd496
    db->ranges[0][RANGE_END] = db->ranges[0][RANGE_START];
Packit Service fdd496
Packit Service fdd496
  /* Get the letter */
Packit Service fdd496
  s = skipwhite (s);
Packit Service fdd496
  switch (*s)
Packit Service fdd496
    {
Packit Service fdd496
    case 'a':
Packit Service fdd496
      type = ADD;
Packit Service fdd496
      break;
Packit Service fdd496
    case 'c':
Packit Service fdd496
      type = CHANGE;
Packit Service fdd496
      break;
Packit Service fdd496
    case 'd':
Packit Service fdd496
      type = DELETE;
Packit Service fdd496
      break;
Packit Service fdd496
    default:
Packit Service fdd496
      return ERROR;			/* Bad format */
Packit Service fdd496
    }
Packit Service fdd496
  s++;				/* Past letter */
Packit Service fdd496
Packit Service fdd496
  /* Read second set of digits */
Packit Service fdd496
  s = readnum (skipwhite (s), &db->ranges[1][RANGE_START]);
Packit Service fdd496
  if (! s)
Packit Service fdd496
    return ERROR;
Packit Service fdd496
Packit Service fdd496
  /* Was that the only digit? */
Packit Service fdd496
  s = skipwhite (s);
Packit Service fdd496
  if (*s == ',')
Packit Service fdd496
    {
Packit Service fdd496
      s = readnum (s + 1, &db->ranges[1][RANGE_END]);
Packit Service fdd496
      if (! s)
Packit Service fdd496
	return ERROR;
Packit Service fdd496
      s = skipwhite (s);		/* To move to end */
Packit Service fdd496
    }
Packit Service fdd496
  else
Packit Service fdd496
    db->ranges[1][RANGE_END] = db->ranges[1][RANGE_START];
Packit Service fdd496
Packit Service fdd496
  *string = s;
Packit Service fdd496
  return type;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
static char *
Packit Service fdd496
read_diff (char const *filea,
Packit Service fdd496
	   char const *fileb,
Packit Service fdd496
	   char **output_placement)
Packit Service fdd496
{
Packit Service fdd496
  char *diff_result;
Packit Service fdd496
  size_t current_chunk_size, total;
Packit Service fdd496
  int fd, wstatus, status;
Packit Service fdd496
  int werrno = 0;
Packit Service fdd496
  struct stat pipestat;
Packit Service fdd496
  char const *argv[9];
Packit Service fdd496
  char const **ap;
Packit Service fdd496
#if HAVE_WORKING_FORK
Packit Service fdd496
  int fds[2];
Packit Service fdd496
  pid_t pid;
Packit Service fdd496
#else
Packit Service fdd496
  FILE *fpipe;
Packit Service fdd496
  char *command;
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
  ap = argv;
Packit Service fdd496
  *ap++ = diff_program;
Packit Service fdd496
  if (text)
Packit Service fdd496
    *ap++ = "-a";
Packit Service fdd496
  if (strip_trailing_cr)
Packit Service fdd496
    *ap++ = "--strip-trailing-cr";
Packit Service fdd496
  *ap++ = "--horizon-lines=100";
Packit Service fdd496
  *ap++ = "--";
Packit Service fdd496
  *ap++ = filea;
Packit Service fdd496
  *ap++ = fileb;
Packit Service fdd496
  *ap = 0;
Packit Service fdd496
Packit Service fdd496
#if HAVE_WORKING_FORK
Packit Service fdd496
Packit Service fdd496
  if (pipe (fds) != 0)
Packit Service fdd496
    perror_with_exit ("pipe");
Packit Service fdd496
Packit Service fdd496
  pid = fork ();
Packit Service fdd496
  if (pid == 0)
Packit Service fdd496
    {
Packit Service fdd496
      /* Child */
Packit Service fdd496
      close (fds[0]);
Packit Service fdd496
      if (fds[1] != STDOUT_FILENO)
Packit Service fdd496
	{
Packit Service fdd496
	  dup2 (fds[1], STDOUT_FILENO);
Packit Service fdd496
	  close (fds[1]);
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      /* The cast to (char **) is needed for portability to older
Packit Service fdd496
	 hosts with a nonstandard prototype for execvp.  */
Packit Service fdd496
      execvp (diff_program, (char **) argv);
Packit Service fdd496
Packit Service fdd496
      _exit (errno == ENOENT ? 127 : 126);
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  if (pid == -1)
Packit Service fdd496
    perror_with_exit ("fork");
Packit Service fdd496
Packit Service fdd496
  close (fds[1]);		/* Prevent erroneous lack of EOF */
Packit Service fdd496
  fd = fds[0];
Packit Service fdd496
Packit Service fdd496
#else
Packit Service fdd496
Packit Service fdd496
  command = system_quote_argv (SCI_SYSTEM, (char **) argv);
Packit Service fdd496
  errno = 0;
Packit Service fdd496
  fpipe = popen (command, "r");
Packit Service fdd496
  if (!fpipe)
Packit Service fdd496
    perror_with_exit (command);
Packit Service fdd496
  free (command);
Packit Service fdd496
  fd = fileno (fpipe);
Packit Service fdd496
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
  if (fstat (fd, &pipestat) != 0)
Packit Service fdd496
    perror_with_exit ("fstat");
Packit Service fdd496
  current_chunk_size = MAX (1, STAT_BLOCKSIZE (pipestat));
Packit Service fdd496
  diff_result = xmalloc (current_chunk_size);
Packit Service fdd496
  total = 0;
Packit Service fdd496
Packit Service fdd496
  for (;;)
Packit Service fdd496
    {
Packit Service fdd496
      size_t bytes_to_read = current_chunk_size - total;
Packit Service fdd496
      size_t bytes = block_read (fd, diff_result + total, bytes_to_read);
Packit Service fdd496
      total += bytes;
Packit Service fdd496
      if (bytes != bytes_to_read)
Packit Service fdd496
	{
Packit Service fdd496
	  if (bytes == SIZE_MAX)
Packit Service fdd496
	    perror_with_exit (_("read failed"));
Packit Service fdd496
	  break;
Packit Service fdd496
	}
Packit Service fdd496
      if (PTRDIFF_MAX / 2 <= current_chunk_size)
Packit Service fdd496
	xalloc_die ();
Packit Service fdd496
      current_chunk_size *= 2;
Packit Service fdd496
      diff_result = xrealloc (diff_result, current_chunk_size);
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  if (total != 0 && diff_result[total-1] != '\n')
Packit Service fdd496
    fatal ("invalid diff format; incomplete last line");
Packit Service fdd496
Packit Service fdd496
  *output_placement = diff_result;
Packit Service fdd496
Packit Service fdd496
#if ! HAVE_WORKING_FORK
Packit Service fdd496
Packit Service fdd496
  wstatus = pclose (fpipe);
Packit Service fdd496
  if (wstatus == -1)
Packit Service fdd496
    werrno = errno;
Packit Service fdd496
Packit Service fdd496
#else
Packit Service fdd496
Packit Service fdd496
  if (close (fd) != 0)
Packit Service fdd496
    perror_with_exit ("close");
Packit Service fdd496
  if (waitpid (pid, &wstatus, 0) < 0)
Packit Service fdd496
    perror_with_exit ("waitpid");
Packit Service fdd496
Packit Service fdd496
#endif
Packit Service fdd496
Packit Service fdd496
  status = ! werrno && WIFEXITED (wstatus) ? WEXITSTATUS (wstatus) : INT_MAX;
Packit Service fdd496
Packit Service fdd496
  if (EXIT_TROUBLE <= status)
Packit Service fdd496
    die (EXIT_TROUBLE, werrno,
Packit Service fdd496
	   _(status == 126
Packit Service fdd496
	     ? "subsidiary program '%s' could not be invoked"
Packit Service fdd496
	     : status == 127
Packit Service fdd496
	     ? "subsidiary program '%s' not found"
Packit Service fdd496
	     : status == INT_MAX
Packit Service fdd496
	     ? "subsidiary program '%s' failed"
Packit Service fdd496
	     : "subsidiary program '%s' failed (exit status %d)"),
Packit Service fdd496
	   diff_program, status);
Packit Service fdd496
Packit Service fdd496
  return diff_result + total;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
Packit Service fdd496
/* Scan a regular diff line (consisting of > or <, followed by a
Packit Service fdd496
   space, followed by text (including nulls) up to a newline.
Packit Service fdd496
Packit Service fdd496
   This next routine began life as a macro and many parameters in it
Packit Service fdd496
   are used as call-by-reference values.  */
Packit Service fdd496
static char *
Packit Service fdd496
scan_diff_line (char *scan_ptr, char **set_start, size_t *set_length,
Packit Service fdd496
		char *limit, char leadingchar)
Packit Service fdd496
{
Packit Service fdd496
  char *line_ptr;
Packit Service fdd496
Packit Service fdd496
  if (!(scan_ptr[0] == leadingchar
Packit Service fdd496
	&& scan_ptr[1] == ' '))
Packit Service fdd496
    fatal ("invalid diff format; incorrect leading line chars");
Packit Service fdd496
Packit Service fdd496
  *set_start = line_ptr = scan_ptr + 2;
Packit Service fdd496
  while (*line_ptr++ != '\n')
Packit Service fdd496
    continue;
Packit Service fdd496
Packit Service fdd496
  /* Include newline if the original line ended in a newline,
Packit Service fdd496
     or if an edit script is being generated.
Packit Service fdd496
     Copy any missing newline message to stderr if an edit script is being
Packit Service fdd496
     generated, because edit scripts cannot handle missing newlines.
Packit Service fdd496
     Return the beginning of the next line.  */
Packit Service fdd496
  *set_length = line_ptr - *set_start;
Packit Service fdd496
  if (line_ptr < limit && *line_ptr == '\\')
Packit Service fdd496
    {
Packit Service fdd496
      if (edscript)
Packit Service fdd496
	fprintf (stderr, "%s:", program_name);
Packit Service fdd496
      else
Packit Service fdd496
	--*set_length;
Packit Service fdd496
      line_ptr++;
Packit Service fdd496
      do
Packit Service fdd496
	{
Packit Service fdd496
	  if (edscript)
Packit Service fdd496
	    putc (*line_ptr, stderr);
Packit Service fdd496
	}
Packit Service fdd496
      while (*line_ptr++ != '\n');
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  return line_ptr;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Output a three way diff passed as a list of diff3_block's.  The
Packit Service fdd496
   argument MAPPING is indexed by external file number (in the
Packit Service fdd496
   argument list) and contains the internal file number (from the diff
Packit Service fdd496
   passed).  This is important because the user expects outputs in
Packit Service fdd496
   terms of the argument list number, and the diff passed may have
Packit Service fdd496
   been done slightly differently (if the last argument was "-", for
Packit Service fdd496
   example).  REV_MAPPING is the inverse of MAPPING.  */
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
output_diff3 (FILE *outputfile, struct diff3_block *diff,
Packit Service fdd496
	      int const mapping[3], int const rev_mapping[3])
Packit Service fdd496
{
Packit Service fdd496
  int i;
Packit Service fdd496
  int oddoneout;
Packit Service fdd496
  char *cp;
Packit Service fdd496
  struct diff3_block *ptr;
Packit Service fdd496
  lin line;
Packit Service fdd496
  size_t length;
Packit Service fdd496
  int dontprint;
Packit Service fdd496
  static int skew_increment[3] = { 2, 3, 1 }; /* 0==>2==>1==>3 */
Packit Service fdd496
  char const *line_prefix = initial_tab ? "\t" : "  ";
Packit Service fdd496
Packit Service fdd496
  for (ptr = diff; ptr; ptr = D_NEXT (ptr))
Packit Service fdd496
    {
Packit Service fdd496
      char x[2];
Packit Service fdd496
Packit Service fdd496
      switch (ptr->correspond)
Packit Service fdd496
	{
Packit Service fdd496
	case DIFF_ALL:
Packit Service fdd496
	  x[0] = 0;
Packit Service fdd496
	  dontprint = 3;	/* Print them all */
Packit Service fdd496
	  oddoneout = 3;	/* Nobody's odder than anyone else */
Packit Service fdd496
	  break;
Packit Service fdd496
	case DIFF_1ST:
Packit Service fdd496
	case DIFF_2ND:
Packit Service fdd496
	case DIFF_3RD:
Packit Service fdd496
	  oddoneout = rev_mapping[ptr->correspond - DIFF_1ST];
Packit Service fdd496
Packit Service fdd496
	  x[0] = oddoneout + '1';
Packit Service fdd496
	  x[1] = 0;
Packit Service fdd496
	  dontprint = oddoneout == 0;
Packit Service fdd496
	  break;
Packit Service fdd496
	default:
Packit Service fdd496
	  fatal ("internal error: invalid diff type passed to output");
Packit Service fdd496
	}
Packit Service fdd496
      fprintf (outputfile, "====%s\n", x);
Packit Service fdd496
Packit Service fdd496
      /* Go 0, 2, 1 if the first and third outputs are equivalent.  */
Packit Service fdd496
      for (i = 0; i < 3;
Packit Service fdd496
	   i = (oddoneout == 1 ? skew_increment[i] : i + 1))
Packit Service fdd496
	{
Packit Service fdd496
	  int realfile = mapping[i];
Packit Service fdd496
	  lin lowt = D_LOWLINE (ptr, realfile);
Packit Service fdd496
	  lin hight = D_HIGHLINE (ptr, realfile);
Packit Service fdd496
	  printint llowt = lowt;
Packit Service fdd496
	  printint lhight = hight;
Packit Service fdd496
Packit Service fdd496
	  fprintf (outputfile, "%d:", i + 1);
Packit Service fdd496
	  switch (lowt - hight)
Packit Service fdd496
	    {
Packit Service fdd496
	    case 1:
Packit Service fdd496
	      fprintf (outputfile, "%"pI"da\n", llowt - 1);
Packit Service fdd496
	      break;
Packit Service fdd496
	    case 0:
Packit Service fdd496
	      fprintf (outputfile, "%"pI"dc\n", llowt);
Packit Service fdd496
	      break;
Packit Service fdd496
	    default:
Packit Service fdd496
	      fprintf (outputfile, "%"pI"d,%"pI"dc\n", llowt, lhight);
Packit Service fdd496
	      break;
Packit Service fdd496
	    }
Packit Service fdd496
Packit Service fdd496
	  if (i == dontprint) continue;
Packit Service fdd496
Packit Service fdd496
	  if (lowt <= hight)
Packit Service fdd496
	    {
Packit Service fdd496
	      line = 0;
Packit Service fdd496
	      do
Packit Service fdd496
		{
Packit Service fdd496
		  fputs (line_prefix, outputfile);
Packit Service fdd496
		  cp = D_RELNUM (ptr, realfile, line);
Packit Service fdd496
		  length = D_RELLEN (ptr, realfile, line);
Packit Service fdd496
		  fwrite (cp, sizeof (char), length, outputfile);
Packit Service fdd496
		}
Packit Service fdd496
	      while (++line < hight - lowt + 1);
Packit Service fdd496
	      if (cp[length - 1] != '\n')
Packit Service fdd496
		fprintf (outputfile, "\n\\ %s\n",
Packit Service fdd496
			 _("No newline at end of file"));
Packit Service fdd496
	    }
Packit Service fdd496
	}
Packit Service fdd496
    }
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
Packit Service fdd496
/* Output to OUTPUTFILE the lines of B taken from FILENUM.  Double any
Packit Service fdd496
   initial '.'s; yield nonzero if any initial '.'s were doubled.  */
Packit Service fdd496
Packit Service fdd496
static bool
Packit Service fdd496
dotlines (FILE *outputfile, struct diff3_block *b, int filenum)
Packit Service fdd496
{
Packit Service fdd496
  lin i;
Packit Service fdd496
  bool leading_dot = false;
Packit Service fdd496
Packit Service fdd496
  for (i = 0;
Packit Service fdd496
       i < D_NUMLINES (b, filenum);
Packit Service fdd496
       i++)
Packit Service fdd496
    {
Packit Service fdd496
      char *line = D_RELNUM (b, filenum, i);
Packit Service fdd496
      if (line[0] == '.')
Packit Service fdd496
	{
Packit Service fdd496
	  leading_dot = true;
Packit Service fdd496
	  fputc ('.', outputfile);
Packit Service fdd496
	}
Packit Service fdd496
      fwrite (line, sizeof (char),
Packit Service fdd496
	      D_RELLEN (b, filenum, i), outputfile);
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  return leading_dot;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Output to OUTPUTFILE a '.' line.  If LEADING_DOT is true, also
Packit Service fdd496
   output a command that removes initial '.'s starting with line START
Packit Service fdd496
   and continuing for NUM lines.  */
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
undotlines (FILE *outputfile, bool leading_dot, printint start, printint num)
Packit Service fdd496
{
Packit Service fdd496
  fputs (".\n", outputfile);
Packit Service fdd496
  if (leading_dot)
Packit Service fdd496
    {
Packit Service fdd496
      if (num == 1)
Packit Service fdd496
	fprintf (outputfile, "%"pI"ds/^\\.//\n", start);
Packit Service fdd496
      else
Packit Service fdd496
	fprintf (outputfile, "%"pI"d,%"pI"ds/^\\.//\n", start, start + num - 1);
Packit Service fdd496
    }
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Output a diff3 set of blocks as an ed script.  This script applies
Packit Service fdd496
   the changes between file's 2 & 3 to file 1.  Take the precise
Packit Service fdd496
   format of the ed script to be output from global variables set
Packit Service fdd496
   during options processing.  Reverse the order of
Packit Service fdd496
   the set of diff3 blocks in DIFF; this gets
Packit Service fdd496
   around the problems involved with changing line numbers in an ed
Packit Service fdd496
   script.
Packit Service fdd496
Packit Service fdd496
   As in 'output_diff3', the variable MAPPING maps from file number
Packit Service fdd496
   according to the argument list to file number according to the diff
Packit Service fdd496
   passed.  All files listed below are in terms of the argument list.
Packit Service fdd496
   REV_MAPPING is the inverse of MAPPING.
Packit Service fdd496
Packit Service fdd496
   FILE0, FILE1 and FILE2 are the strings to print as the names of the
Packit Service fdd496
   three files.  These may be the actual names, or may be the
Packit Service fdd496
   arguments specified with -L.
Packit Service fdd496
Packit Service fdd496
   Return 1 if conflicts were found.  */
Packit Service fdd496
Packit Service fdd496
static bool
Packit Service fdd496
output_diff3_edscript (FILE *outputfile, struct diff3_block *diff,
Packit Service fdd496
		       int const mapping[3], int const rev_mapping[3],
Packit Service fdd496
		       char const *file0, char const *file1, char const *file2)
Packit Service fdd496
{
Packit Service fdd496
  bool leading_dot;
Packit Service fdd496
  bool conflicts_found = false;
Packit Service fdd496
  bool conflict;
Packit Service fdd496
  struct diff3_block *b;
Packit Service fdd496
Packit Service fdd496
  for (b = reverse_diff3_blocklist (diff); b; b = b->next)
Packit Service fdd496
    {
Packit Service fdd496
      /* Must do mapping correctly.  */
Packit Service fdd496
      enum diff_type type
Packit Service fdd496
	= (b->correspond == DIFF_ALL
Packit Service fdd496
	   ? DIFF_ALL
Packit Service fdd496
	   : DIFF_1ST + rev_mapping[b->correspond - DIFF_1ST]);
Packit Service fdd496
Packit Service fdd496
      printint low0, high0;
Packit Service fdd496
Packit Service fdd496
      /* If we aren't supposed to do this output block, skip it.  */
Packit Service fdd496
      switch (type)
Packit Service fdd496
	{
Packit Service fdd496
	default: continue;
Packit Service fdd496
	case DIFF_2ND: if (!show_2nd) continue; conflict = true; break;
Packit Service fdd496
	case DIFF_3RD: if (overlap_only) continue; conflict = false; break;
Packit Service fdd496
	case DIFF_ALL: if (simple_only) continue; conflict = flagging; break;
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      low0 = D_LOWLINE (b, mapping[FILE0]);
Packit Service fdd496
      high0 = D_HIGHLINE (b, mapping[FILE0]);
Packit Service fdd496
Packit Service fdd496
      if (conflict)
Packit Service fdd496
	{
Packit Service fdd496
	  conflicts_found = true;
Packit Service fdd496
Packit Service fdd496
Packit Service fdd496
	  /* Mark end of conflict.  */
Packit Service fdd496
Packit Service fdd496
	  fprintf (outputfile, "%"pI"da\n", high0);
Packit Service fdd496
	  leading_dot = false;
Packit Service fdd496
	  if (type == DIFF_ALL)
Packit Service fdd496
	    {
Packit Service fdd496
	      if (show_2nd)
Packit Service fdd496
		{
Packit Service fdd496
		  /* Append lines from FILE1.  */
Packit Service fdd496
		  fprintf (outputfile, "||||||| %s\n", file1);
Packit Service fdd496
		  leading_dot = dotlines (outputfile, b, mapping[FILE1]);
Packit Service fdd496
		}
Packit Service fdd496
	      /* Append lines from FILE2.  */
Packit Service fdd496
	      fputs ("=======\n", outputfile);
Packit Service fdd496
	      leading_dot |= dotlines (outputfile, b, mapping[FILE2]);
Packit Service fdd496
	    }
Packit Service fdd496
	  fprintf (outputfile, ">>>>>>> %s\n", file2);
Packit Service fdd496
	  undotlines (outputfile, leading_dot, high0 + 2,
Packit Service fdd496
		      (D_NUMLINES (b, mapping[FILE1])
Packit Service fdd496
		       + D_NUMLINES (b, mapping[FILE2]) + 1));
Packit Service fdd496
Packit Service fdd496
Packit Service fdd496
	  /* Mark start of conflict.  */
Packit Service fdd496
Packit Service fdd496
	  fprintf (outputfile, "%"pI"da\n<<<<<<< %s\n", low0 - 1,
Packit Service fdd496
		   type == DIFF_ALL ? file0 : file1);
Packit Service fdd496
	  leading_dot = false;
Packit Service fdd496
	  if (type == DIFF_2ND)
Packit Service fdd496
	    {
Packit Service fdd496
	      /* Prepend lines from FILE1.  */
Packit Service fdd496
	      leading_dot = dotlines (outputfile, b, mapping[FILE1]);
Packit Service fdd496
	      fputs ("=======\n", outputfile);
Packit Service fdd496
	    }
Packit Service fdd496
	  undotlines (outputfile, leading_dot, low0 + 1,
Packit Service fdd496
		      D_NUMLINES (b, mapping[FILE1]));
Packit Service fdd496
	}
Packit Service fdd496
      else if (D_NUMLINES (b, mapping[FILE2]) == 0)
Packit Service fdd496
	/* Write out a delete */
Packit Service fdd496
	{
Packit Service fdd496
	  if (low0 == high0)
Packit Service fdd496
	    fprintf (outputfile, "%"pI"dd\n", low0);
Packit Service fdd496
	  else
Packit Service fdd496
	    fprintf (outputfile, "%"pI"d,%"pI"dd\n", low0, high0);
Packit Service fdd496
	}
Packit Service fdd496
      else
Packit Service fdd496
	/* Write out an add or change */
Packit Service fdd496
	{
Packit Service fdd496
	  switch (high0 - low0)
Packit Service fdd496
	    {
Packit Service fdd496
	    case -1:
Packit Service fdd496
	      fprintf (outputfile, "%"pI"da\n", high0);
Packit Service fdd496
	      break;
Packit Service fdd496
	    case 0:
Packit Service fdd496
	      fprintf (outputfile, "%"pI"dc\n", high0);
Packit Service fdd496
	      break;
Packit Service fdd496
	    default:
Packit Service fdd496
	      fprintf (outputfile, "%"pI"d,%"pI"dc\n", low0, high0);
Packit Service fdd496
	      break;
Packit Service fdd496
	    }
Packit Service fdd496
Packit Service fdd496
	  undotlines (outputfile, dotlines (outputfile, b, mapping[FILE2]),
Packit Service fdd496
		      low0, D_NUMLINES (b, mapping[FILE2]));
Packit Service fdd496
	}
Packit Service fdd496
    }
Packit Service fdd496
  if (finalwrite)
Packit Service fdd496
    fputs ("w\nq\n", outputfile);
Packit Service fdd496
  return conflicts_found;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Read from INFILE and output to OUTPUTFILE a set of diff3_blocks
Packit Service fdd496
   DIFF as a merged file.  This acts like 'ed file0
Packit Service fdd496
   <[output_diff3_edscript]', except that it works even for binary
Packit Service fdd496
   data or incomplete lines.
Packit Service fdd496
Packit Service fdd496
   As before, MAPPING maps from arg list file number to diff file
Packit Service fdd496
   number, REV_MAPPING is its inverse, and FILE0, FILE1, and FILE2 are
Packit Service fdd496
   the names of the files.
Packit Service fdd496
Packit Service fdd496
   Return 1 if conflicts were found.  */
Packit Service fdd496
Packit Service fdd496
static bool
Packit Service fdd496
output_diff3_merge (FILE *infile, FILE *outputfile, struct diff3_block *diff,
Packit Service fdd496
		    int const mapping[3], int const rev_mapping[3],
Packit Service fdd496
		    char const *file0, char const *file1, char const *file2)
Packit Service fdd496
{
Packit Service fdd496
  int c;
Packit Service fdd496
  lin i;
Packit Service fdd496
  bool conflicts_found = false;
Packit Service fdd496
  bool conflict;
Packit Service fdd496
  struct diff3_block *b;
Packit Service fdd496
  lin linesread = 0;
Packit Service fdd496
Packit Service fdd496
  for (b = diff; b; b = b->next)
Packit Service fdd496
    {
Packit Service fdd496
      /* Must do mapping correctly.  */
Packit Service fdd496
      enum diff_type type
Packit Service fdd496
	= ((b->correspond == DIFF_ALL)
Packit Service fdd496
	   ? DIFF_ALL
Packit Service fdd496
	   : DIFF_1ST + rev_mapping[b->correspond - DIFF_1ST]);
Packit Service fdd496
      char const *format_2nd = "<<<<<<< %s\n";
Packit Service fdd496
Packit Service fdd496
      /* If we aren't supposed to do this output block, skip it.  */
Packit Service fdd496
      switch (type)
Packit Service fdd496
	{
Packit Service fdd496
	default: continue;
Packit Service fdd496
	case DIFF_2ND: if (!show_2nd) continue; conflict = true; break;
Packit Service fdd496
	case DIFF_3RD: if (overlap_only) continue; conflict = false; break;
Packit Service fdd496
	case DIFF_ALL: if (simple_only) continue; conflict = flagging;
Packit Service fdd496
	  format_2nd = "||||||| %s\n";
Packit Service fdd496
	  break;
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      /* Copy I lines from file 0.  */
Packit Service fdd496
      i = D_LOWLINE (b, FILE0) - linesread - 1;
Packit Service fdd496
      linesread += i;
Packit Service fdd496
      while (0 <= --i)
Packit Service fdd496
	do
Packit Service fdd496
	  {
Packit Service fdd496
	    c = getc (infile);
Packit Service fdd496
	    if (c == EOF)
Packit Service fdd496
	      {
Packit Service fdd496
		if (ferror (infile))
Packit Service fdd496
		  perror_with_exit (_("read failed"));
Packit Service fdd496
		else if (feof (infile))
Packit Service fdd496
		  fatal ("input file shrank");
Packit Service fdd496
	      }
Packit Service fdd496
	    putc (c, outputfile);
Packit Service fdd496
	  }
Packit Service fdd496
	while (c != '\n');
Packit Service fdd496
Packit Service fdd496
      if (conflict)
Packit Service fdd496
	{
Packit Service fdd496
	  conflicts_found = true;
Packit Service fdd496
Packit Service fdd496
	  if (type == DIFF_ALL)
Packit Service fdd496
	    {
Packit Service fdd496
	      /* Put in lines from FILE0 with bracket.  */
Packit Service fdd496
	      fprintf (outputfile, "<<<<<<< %s\n", file0);
Packit Service fdd496
	      for (i = 0;
Packit Service fdd496
		   i < D_NUMLINES (b, mapping[FILE0]);
Packit Service fdd496
		   i++)
Packit Service fdd496
		fwrite (D_RELNUM (b, mapping[FILE0], i), sizeof (char),
Packit Service fdd496
			D_RELLEN (b, mapping[FILE0], i), outputfile);
Packit Service fdd496
	    }
Packit Service fdd496
Packit Service fdd496
	  if (show_2nd)
Packit Service fdd496
	    {
Packit Service fdd496
	      /* Put in lines from FILE1 with bracket.  */
Packit Service fdd496
	      fprintf (outputfile, format_2nd, file1);
Packit Service fdd496
	      for (i = 0;
Packit Service fdd496
		   i < D_NUMLINES (b, mapping[FILE1]);
Packit Service fdd496
		   i++)
Packit Service fdd496
		fwrite (D_RELNUM (b, mapping[FILE1], i), sizeof (char),
Packit Service fdd496
			D_RELLEN (b, mapping[FILE1], i), outputfile);
Packit Service fdd496
	    }
Packit Service fdd496
Packit Service fdd496
	  fputs ("=======\n", outputfile);
Packit Service fdd496
	}
Packit Service fdd496
Packit Service fdd496
      /* Put in lines from FILE2.  */
Packit Service fdd496
      for (i = 0;
Packit Service fdd496
	   i < D_NUMLINES (b, mapping[FILE2]);
Packit Service fdd496
	   i++)
Packit Service fdd496
	fwrite (D_RELNUM (b, mapping[FILE2], i), sizeof (char),
Packit Service fdd496
		D_RELLEN (b, mapping[FILE2], i), outputfile);
Packit Service fdd496
Packit Service fdd496
      if (conflict)
Packit Service fdd496
	fprintf (outputfile, ">>>>>>> %s\n", file2);
Packit Service fdd496
Packit Service fdd496
      /* Skip I lines in file 0.  */
Packit Service fdd496
      i = D_NUMLINES (b, FILE0);
Packit Service fdd496
      linesread += i;
Packit Service fdd496
      while (0 <= --i)
Packit Service fdd496
	while ((c = getc (infile)) != '\n')
Packit Service fdd496
	  if (c == EOF)
Packit Service fdd496
	    {
Packit Service fdd496
	      if (ferror (infile))
Packit Service fdd496
		perror_with_exit (_("read failed"));
Packit Service fdd496
	      else if (feof (infile))
Packit Service fdd496
		{
Packit Service fdd496
		  if (i || b->next)
Packit Service fdd496
		    fatal ("input file shrank");
Packit Service fdd496
		  return conflicts_found;
Packit Service fdd496
		}
Packit Service fdd496
	    }
Packit Service fdd496
    }
Packit Service fdd496
  /* Copy rest of common file.  */
Packit Service fdd496
  while ((c = getc (infile)) != EOF || !(ferror (infile) | feof (infile)))
Packit Service fdd496
    putc (c, outputfile);
Packit Service fdd496
  return conflicts_found;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
/* Reverse the order of the list of diff3 blocks.  */
Packit Service fdd496
Packit Service fdd496
static struct diff3_block *
Packit Service fdd496
reverse_diff3_blocklist (struct diff3_block *diff)
Packit Service fdd496
{
Packit Service fdd496
  register struct diff3_block *tmp, *next, *prev;
Packit Service fdd496
Packit Service fdd496
  for (tmp = diff, prev = 0;  tmp;  tmp = next)
Packit Service fdd496
    {
Packit Service fdd496
      next = tmp->next;
Packit Service fdd496
      tmp->next = prev;
Packit Service fdd496
      prev = tmp;
Packit Service fdd496
    }
Packit Service fdd496
Packit Service fdd496
  return prev;
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
fatal (char const *msgid)
Packit Service fdd496
{
Packit Service fdd496
  die (EXIT_TROUBLE, 0, "%s", _(msgid));
Packit Service fdd496
}
Packit Service fdd496
Packit Service fdd496
static void
Packit Service fdd496
perror_with_exit (char const *string)
Packit Service fdd496
{
Packit Service fdd496
  die (EXIT_TROUBLE, errno, "%s", string);
Packit Service fdd496
}