Blame src/sdiff.c.covscan

Packit 05ea35
/* sdiff - side-by-side merge of file differences
Packit 05ea35
Packit 05ea35
   Copyright (C) 1992-1996, 1998, 2001-2002, 2004, 2006-2007, 2009-2013,
Packit 05ea35
   2015-2017 Free Software Foundation, Inc.
Packit 05ea35
Packit 05ea35
   This file is part of GNU DIFF.
Packit 05ea35
Packit 05ea35
   This program is free software: you can redistribute it and/or modify
Packit 05ea35
   it under the terms of the GNU General Public License as published by
Packit 05ea35
   the Free Software Foundation, either version 3 of the License, or
Packit 05ea35
   (at your option) any later version.
Packit 05ea35
Packit 05ea35
   This program is distributed in the hope that it will be useful,
Packit 05ea35
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 05ea35
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 05ea35
   GNU General Public License for more details.
Packit 05ea35
Packit 05ea35
   You should have received a copy of the GNU General Public License
Packit 05ea35
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 05ea35
Packit 05ea35
#include "system.h"
Packit 05ea35
#include "paths.h"
Packit 05ea35
Packit 05ea35
#include <stdio.h>
Packit 05ea35
#include <unlocked-io.h>
Packit 05ea35
Packit 05ea35
#include <c-stack.h>
Packit 05ea35
#include <dirname.h>
Packit 05ea35
#include "die.h"
Packit 05ea35
#include <error.h>
Packit 05ea35
#include <exitfail.h>
Packit 05ea35
#include <file-type.h>
Packit 05ea35
#include <getopt.h>
Packit 05ea35
#include <progname.h>
Packit 05ea35
#include <system-quote.h>
Packit 05ea35
#include <version-etc.h>
Packit 05ea35
#include <xalloc.h>
Packit 05ea35
Packit 05ea35
/* The official name of this program (e.g., no 'g' prefix).  */
Packit 05ea35
#define PROGRAM_NAME "sdiff"
Packit 05ea35
Packit 05ea35
#define AUTHORS \
Packit 05ea35
  proper_name ("Thomas Lord")
Packit 05ea35
Packit 05ea35
/* Size of chunks read from files which must be parsed into lines.  */
Packit 05ea35
#define SDIFF_BUFSIZE ((size_t) 65536)
Packit 05ea35
Packit 05ea35
static char const *editor_program = DEFAULT_EDITOR_PROGRAM;
Packit 05ea35
static char const **diffargv;
Packit 05ea35
Packit 05ea35
static char * volatile tmpname;
Packit 05ea35
static FILE *tmp;
Packit 05ea35
Packit 05ea35
#if HAVE_WORKING_FORK
Packit 05ea35
static pid_t volatile diffpid;
Packit 05ea35
#endif
Packit 05ea35
Packit 05ea35
struct line_filter;
Packit 05ea35
Packit 05ea35
static void catchsig (int);
Packit 05ea35
static bool edit (struct line_filter *, char const *, lin, lin, struct line_filter *, char const *, lin, lin, FILE *);
Packit 05ea35
static bool interact (struct line_filter *, struct line_filter *, char const *, struct line_filter *, char const *, FILE *);
Packit 05ea35
static void checksigs (void);
Packit 05ea35
static void diffarg (char const *);
Packit 05ea35
static void fatal (char const *) __attribute__((noreturn));
Packit 05ea35
static void perror_fatal (char const *) __attribute__((noreturn));
Packit 05ea35
static void trapsigs (void);
Packit 05ea35
static void untrapsig (int);
Packit 05ea35
Packit 05ea35
static int const sigs[] = {
Packit 05ea35
#ifdef SIGHUP
Packit 05ea35
       SIGHUP,
Packit 05ea35
#endif
Packit 05ea35
#ifdef SIGQUIT
Packit 05ea35
       SIGQUIT,
Packit 05ea35
#endif
Packit 05ea35
#ifdef SIGTERM
Packit 05ea35
       SIGTERM,
Packit 05ea35
#endif
Packit 05ea35
#ifdef SIGXCPU
Packit 05ea35
       SIGXCPU,
Packit 05ea35
#endif
Packit 05ea35
#ifdef SIGXFSZ
Packit 05ea35
       SIGXFSZ,
Packit 05ea35
#endif
Packit 05ea35
#ifdef SIGPIPE
Packit 05ea35
       SIGPIPE,
Packit 05ea35
#endif
Packit 05ea35
       SIGINT
Packit 05ea35
};
Packit 05ea35
enum
Packit 05ea35
  {
Packit 05ea35
    NUM_SIGS = sizeof sigs / sizeof *sigs,
Packit 05ea35
    handler_index_of_SIGINT = NUM_SIGS - 1
Packit 05ea35
  };
Packit 05ea35
Packit 05ea35
#if HAVE_SIGACTION
Packit 05ea35
  /* Prefer 'sigaction' if available, since 'signal' can lose signals.  */
Packit 05ea35
  static struct sigaction initial_action[NUM_SIGS];
Packit 05ea35
# define initial_handler(i) (initial_action[i].sa_handler)
Packit 05ea35
  static void signal_handler (int, void (*) (int));
Packit 05ea35
#else
Packit 05ea35
  static void (*initial_action[NUM_SIGS]) ();
Packit 05ea35
# define initial_handler(i) (initial_action[i])
Packit 05ea35
# define signal_handler(sig, handler) signal (sig, handler)
Packit 05ea35
#endif
Packit 05ea35
Packit 05ea35
static bool diraccess (char const *);
Packit 05ea35
static int temporary_file (void);
Packit 05ea35
Packit 05ea35
/* Options: */
Packit 05ea35
Packit 05ea35
/* Name of output file if -o specified.  */
Packit 05ea35
static char const *output;
Packit 05ea35
Packit 05ea35
/* Do not print common lines.  */
Packit 05ea35
static bool suppress_common_lines;
Packit 05ea35
Packit 05ea35
/* Value for the long option that does not have single-letter equivalents.  */
Packit 05ea35
enum
Packit 05ea35
{
Packit 05ea35
  DIFF_PROGRAM_OPTION = CHAR_MAX + 1,
Packit 05ea35
  HELP_OPTION,
Packit 05ea35
  STRIP_TRAILING_CR_OPTION,
Packit 05ea35
  TABSIZE_OPTION
Packit 05ea35
};
Packit 05ea35
Packit 05ea35
static struct option const longopts[] =
Packit 05ea35
{
Packit 05ea35
  {"diff-program", 1, 0, DIFF_PROGRAM_OPTION},
Packit 05ea35
  {"expand-tabs", 0, 0, 't'},
Packit 05ea35
  {"help", 0, 0, HELP_OPTION},
Packit 05ea35
  {"ignore-all-space", 0, 0, 'W'}, /* swap W and w for historical reasons */
Packit 05ea35
  {"ignore-blank-lines", 0, 0, 'B'},
Packit 05ea35
  {"ignore-case", 0, 0, 'i'},
Packit 05ea35
  {"ignore-matching-lines", 1, 0, 'I'},
Packit 05ea35
  {"ignore-space-change", 0, 0, 'b'},
Packit 05ea35
  {"ignore-tab-expansion", 0, 0, 'E'},
Packit 05ea35
  {"ignore-trailing-space", 0, 0, 'Z'},
Packit 05ea35
  {"left-column", 0, 0, 'l'},
Packit 05ea35
  {"minimal", 0, 0, 'd'},
Packit 05ea35
  {"output", 1, 0, 'o'},
Packit 05ea35
  {"speed-large-files", 0, 0, 'H'},
Packit 05ea35
  {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
Packit 05ea35
  {"suppress-common-lines", 0, 0, 's'},
Packit 05ea35
  {"tabsize", 1, 0, TABSIZE_OPTION},
Packit 05ea35
  {"text", 0, 0, 'a'},
Packit 05ea35
  {"version", 0, 0, 'v'},
Packit 05ea35
  {"width", 1, 0, 'w'},
Packit 05ea35
  {0, 0, 0, 0}
Packit 05ea35
};
Packit 05ea35
Packit 05ea35
static void try_help (char const *, char const *) __attribute__((noreturn));
Packit 05ea35
static void
Packit 05ea35
try_help (char const *reason_msgid, char const *operand)
Packit 05ea35
{
Packit 05ea35
  if (reason_msgid)
Packit 05ea35
    error (0, 0, _(reason_msgid), operand);
Packit 05ea35
  die (EXIT_TROUBLE, 0, _("Try '%s --help' for more information."),
Packit 05ea35
	 program_name);
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
static void
Packit 05ea35
check_stdout (void)
Packit 05ea35
{
Packit 05ea35
  if (ferror (stdout))
Packit 05ea35
    fatal ("write failed");
Packit 05ea35
  else if (fclose (stdout) != 0)
Packit 05ea35
    perror_fatal (_("standard output"));
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
static char const * const option_help_msgid[] = {
Packit 05ea35
  N_("-o, --output=FILE            operate interactively, sending output to FILE"),
Packit 05ea35
  "",
Packit 05ea35
  N_("-i, --ignore-case            consider upper- and lower-case to be the same"),
Packit 05ea35
  N_("-E, --ignore-tab-expansion   ignore changes due to tab expansion"),
Packit 05ea35
  N_("-Z, --ignore-trailing-space  ignore white space at line end"),
Packit 05ea35
  N_("-b, --ignore-space-change    ignore changes in the amount of white space"),
Packit 05ea35
  N_("-W, --ignore-all-space       ignore all white space"),
Packit 05ea35
  N_("-B, --ignore-blank-lines     ignore changes whose lines are all blank"),
Packit 05ea35
  N_("-I, --ignore-matching-lines=RE  ignore changes all whose lines match RE"),
Packit 05ea35
  N_("    --strip-trailing-cr      strip trailing carriage return on input"),
Packit 05ea35
  N_("-a, --text                   treat all files as text"),
Packit 05ea35
  "",
Packit 05ea35
  N_("-w, --width=NUM              output at most NUM (default 130) print columns"),
Packit 05ea35
  N_("-l, --left-column            output only the left column of common lines"),
Packit 05ea35
  N_("-s, --suppress-common-lines  do not output common lines"),
Packit 05ea35
  "",
Packit 05ea35
  N_("-t, --expand-tabs            expand tabs to spaces in output"),
Packit 05ea35
  N_("    --tabsize=NUM            tab stops at every NUM (default 8) print columns"),
Packit 05ea35
  "",
Packit 05ea35
  N_("-d, --minimal                try hard to find a smaller set of changes"),
Packit 05ea35
  N_("-H, --speed-large-files      assume large files, many scattered small changes"),
Packit 05ea35
  N_("    --diff-program=PROGRAM   use PROGRAM to compare files"),
Packit 05ea35
  "",
Packit 05ea35
  N_("    --help                   display this help and exit"),
Packit 05ea35
  N_("-v, --version                output version information and exit"),
Packit 05ea35
  0
Packit 05ea35
};
Packit 05ea35
Packit 05ea35
static void
Packit 05ea35
usage (void)
Packit 05ea35
{
Packit 05ea35
  char const * const *p;
Packit 05ea35
Packit 05ea35
  printf (_("Usage: %s [OPTION]... FILE1 FILE2\n"), program_name);
Packit 05ea35
  printf ("%s\n\n",
Packit 05ea35
          _("Side-by-side merge of differences between FILE1 and FILE2."));
Packit 05ea35
Packit 05ea35
  fputs (_("\
Packit 05ea35
Mandatory arguments to long options are mandatory for short options too.\n\
Packit 05ea35
"), stdout);
Packit 05ea35
  for (p = option_help_msgid;  *p;  p++)
Packit 05ea35
    if (**p)
Packit 05ea35
      printf ("  %s\n", _(*p));
Packit 05ea35
    else
Packit 05ea35
      putchar ('\n');
Packit 05ea35
  printf ("\n%s\n%s\n",
Packit 05ea35
	  _("If a FILE is '-', read standard input."),
Packit 05ea35
	  _("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."));
Packit 05ea35
  emit_bug_reporting_address ();
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
/* Clean up after a signal or other failure.  This function is
Packit 05ea35
   async-signal-safe.  */
Packit 05ea35
static void
Packit 05ea35
cleanup (int signo __attribute__((unused)))
Packit 05ea35
{
Packit 05ea35
#if HAVE_WORKING_FORK
Packit 05ea35
  if (0 < diffpid)
Packit 05ea35
    kill (diffpid, SIGPIPE);
Packit 05ea35
#endif
Packit 05ea35
  if (tmpname)
Packit 05ea35
    unlink (tmpname);
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
static void exiterr (void) __attribute__((noreturn));
Packit 05ea35
static void
Packit 05ea35
exiterr (void)
Packit 05ea35
{
Packit 05ea35
  cleanup (0);
Packit 05ea35
  untrapsig (0);
Packit 05ea35
  checksigs ();
Packit 05ea35
  exit (EXIT_TROUBLE);
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
static void
Packit 05ea35
fatal (char const *msgid)
Packit 05ea35
{
Packit 05ea35
  error (0, 0, "%s", _(msgid));
Packit 05ea35
  exiterr ();
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
static void
Packit 05ea35
perror_fatal (char const *msg)
Packit 05ea35
{
Packit 05ea35
  int e = errno;
Packit 05ea35
  checksigs ();
Packit 05ea35
  error (0, e, "%s", msg);
Packit 05ea35
  exiterr ();
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
static void
Packit 05ea35
check_child_status (int werrno, int wstatus, int max_ok_status,
Packit 05ea35
		    char const *subsidiary_program)
Packit 05ea35
{
Packit 05ea35
  int status = (! werrno && WIFEXITED (wstatus)
Packit 05ea35
		? WEXITSTATUS (wstatus)
Packit 05ea35
		: INT_MAX);
Packit 05ea35
Packit 05ea35
  if (max_ok_status < status)
Packit 05ea35
    {
Packit 05ea35
      error (0, werrno,
Packit 05ea35
	     _(status == 126
Packit 05ea35
	       ? "subsidiary program '%s' could not be invoked"
Packit 05ea35
	       : status == 127
Packit 05ea35
	       ? "subsidiary program '%s' not found"
Packit 05ea35
	       : status == INT_MAX
Packit 05ea35
	       ? "subsidiary program '%s' failed"
Packit 05ea35
	       : "subsidiary program '%s' failed (exit status %d)"),
Packit 05ea35
	     subsidiary_program, status);
Packit 05ea35
      exiterr ();
Packit 05ea35
    }
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
static FILE *
Packit 05ea35
ck_fopen (char const *fname, char const *type)
Packit 05ea35
{
Packit 05ea35
  FILE *r = fopen (fname, type);
Packit 05ea35
  if (! r)
Packit 05ea35
    perror_fatal (fname);
Packit 05ea35
  return r;
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
static void
Packit 05ea35
ck_fclose (FILE *f)
Packit 05ea35
{
Packit 05ea35
  if (fclose (f))
Packit 05ea35
    perror_fatal ("fclose");
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
static size_t
Packit 05ea35
ck_fread (char *buf, size_t size, FILE *f)
Packit 05ea35
{
Packit 05ea35
  size_t r = fread (buf, sizeof (char), size, f);
Packit 05ea35
  if (r == 0 && ferror (f))
Packit 05ea35
    perror_fatal (_("read failed"));
Packit 05ea35
  return r;
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
static void
Packit 05ea35
ck_fwrite (char const *buf, size_t size, FILE *f)
Packit 05ea35
{
Packit 05ea35
  if (fwrite (buf, sizeof (char), size, f) != size)
Packit 05ea35
    perror_fatal (_("write failed"));
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
static void
Packit 05ea35
ck_fflush (FILE *f)
Packit 05ea35
{
Packit 05ea35
  if (fflush (f) != 0)
Packit 05ea35
    perror_fatal (_("write failed"));
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
static char const *
Packit 05ea35
expand_name (char *name, bool is_dir, char const *other_name)
Packit 05ea35
{
Packit 05ea35
  if (STREQ (name, "-"))
Packit 05ea35
    fatal ("cannot interactively merge standard input");
Packit 05ea35
  if (! is_dir)
Packit 05ea35
    return name;
Packit 05ea35
  else
Packit 05ea35
    {
Packit 05ea35
      /* Yield NAME/BASE, where BASE is OTHER_NAME's basename.  */
Packit 05ea35
      char const *base = last_component (other_name);
Packit 05ea35
      size_t namelen = strlen (name), baselen = base_len (base);
Packit 05ea35
      bool insert_slash = *last_component (name) && name[namelen - 1] != '/';
Packit 05ea35
      char *r = xmalloc (namelen + insert_slash + baselen + 1);
Packit 05ea35
      memcpy (r, name, namelen);
Packit 05ea35
      r[namelen] = '/';
Packit 05ea35
      memcpy (r + namelen + insert_slash, base, baselen);
Packit 05ea35
      r[namelen + insert_slash + baselen] = '\0';
Packit 05ea35
      return r;
Packit 05ea35
    }
Packit 05ea35
}
Packit 05ea35

Packit 05ea35
struct line_filter {
Packit 05ea35
  FILE *infile;
Packit 05ea35
  char *bufpos;
Packit 05ea35
  char *buffer;
Packit 05ea35
  char *buflim;
Packit 05ea35
};
Packit 05ea35
Packit 05ea35
static void
Packit 05ea35
lf_init (struct line_filter *lf, FILE *infile)
Packit 05ea35
{
Packit 05ea35
  lf->infile = infile;
Packit 05ea35
  lf->bufpos = lf->buffer = lf->buflim = xmalloc (SDIFF_BUFSIZE + 1);
Packit 05ea35
  lf->buflim[0] = '\n';
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
/* Fill an exhausted line_filter buffer from its INFILE */
Packit 05ea35
static size_t
Packit 05ea35
lf_refill (struct line_filter *lf)
Packit 05ea35
{
Packit 05ea35
  size_t s = ck_fread (lf->buffer, SDIFF_BUFSIZE, lf->infile);
Packit 05ea35
  lf->bufpos = lf->buffer;
Packit 05ea35
  lf->buflim = lf->buffer + s;
Packit 05ea35
  lf->buflim[0] = '\n';
Packit 05ea35
  checksigs ();
Packit 05ea35
  return s;
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
/* Advance LINES on LF's infile, copying lines to OUTFILE */
Packit 05ea35
static void
Packit 05ea35
lf_copy (struct line_filter *lf, lin lines, FILE *outfile)
Packit 05ea35
{
Packit 05ea35
  char *start = lf->bufpos;
Packit 05ea35
Packit 05ea35
  while (lines)
Packit 05ea35
    {
Packit 05ea35
      lf->bufpos = rawmemchr (lf->bufpos, '\n');
Packit 05ea35
      if (lf->bufpos == lf->buflim)
Packit 05ea35
	{
Packit 05ea35
	  ck_fwrite (start, lf->buflim - start, outfile);
Packit 05ea35
	  if (! lf_refill (lf))
Packit 05ea35
	    return;
Packit 05ea35
	  start = lf->bufpos;
Packit 05ea35
	}
Packit 05ea35
      else
Packit 05ea35
	{
Packit 05ea35
	  --lines;
Packit 05ea35
	  ++lf->bufpos;
Packit 05ea35
	}
Packit 05ea35
    }
Packit 05ea35
Packit 05ea35
  ck_fwrite (start, lf->bufpos - start, outfile);
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
/* Advance LINES on LF's infile without doing output */
Packit 05ea35
static void
Packit 05ea35
lf_skip (struct line_filter *lf, lin lines)
Packit 05ea35
{
Packit 05ea35
  while (lines)
Packit 05ea35
    {
Packit 05ea35
      lf->bufpos = rawmemchr (lf->bufpos, '\n');
Packit 05ea35
      if (lf->bufpos == lf->buflim)
Packit 05ea35
	{
Packit 05ea35
	  if (! lf_refill (lf))
Packit 05ea35
	    break;
Packit 05ea35
	}
Packit 05ea35
      else
Packit 05ea35
	{
Packit 05ea35
	  --lines;
Packit 05ea35
	  ++lf->bufpos;
Packit 05ea35
	}
Packit 05ea35
    }
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
/* Snarf a line into a buffer.  Return EOF if EOF, 0 if error, 1 if OK.  */
Packit 05ea35
static int
Packit 05ea35
lf_snarf (struct line_filter *lf, char *buffer, size_t bufsize)
Packit 05ea35
{
Packit 05ea35
  for (;;)
Packit 05ea35
    {
Packit 05ea35
      char *start = lf->bufpos;
Packit 05ea35
      char *next = rawmemchr (start, '\n');
Packit 05ea35
      size_t s = next - start;
Packit 05ea35
      if (bufsize <= s)
Packit 05ea35
	return 0;
Packit 05ea35
      memcpy (buffer, start, s);
Packit 05ea35
      if (next < lf->buflim)
Packit 05ea35
	{
Packit 05ea35
	  buffer[s] = 0;
Packit 05ea35
	  lf->bufpos = next + 1;
Packit 05ea35
	  return 1;
Packit 05ea35
	}
Packit 05ea35
      if (! lf_refill (lf))
Packit 05ea35
	return s ? 0 : EOF;
Packit 05ea35
      buffer += s;
Packit 05ea35
      bufsize -= s;
Packit 05ea35
    }
Packit 05ea35
}
Packit 05ea35

Packit 05ea35
int
Packit 05ea35
main (int argc, char *argv[])
Packit 05ea35
{
Packit 05ea35
  int opt;
Packit 05ea35
  char const *prog;
Packit 05ea35
Packit 05ea35
  exit_failure = EXIT_TROUBLE;
Packit 05ea35
  initialize_main (&argc, &argv);
Packit 05ea35
  set_program_name (argv[0]);
Packit 05ea35
  setlocale (LC_ALL, "");
Packit 05ea35
  bindtextdomain (PACKAGE, LOCALEDIR);
Packit 05ea35
  textdomain (PACKAGE);
Packit 05ea35
  c_stack_action (cleanup);
Packit 05ea35
Packit 05ea35
  prog = getenv ("EDITOR");
Packit 05ea35
  if (prog)
Packit 05ea35
    editor_program = prog;
Packit 05ea35
Packit 05ea35
  diffarg (DEFAULT_DIFF_PROGRAM);
Packit 05ea35
Packit 05ea35
  /* parse command line args */
Packit 05ea35
  while ((opt = getopt_long (argc, argv, "abBdEHiI:lo:stvw:WZ", longopts, 0))
Packit 05ea35
	 != -1)
Packit 05ea35
    {
Packit 05ea35
      switch (opt)
Packit 05ea35
	{
Packit 05ea35
	case 'a':
Packit 05ea35
	  diffarg ("-a");
Packit 05ea35
	  break;
Packit 05ea35
Packit 05ea35
	case 'b':
Packit 05ea35
	  diffarg ("-b");
Packit 05ea35
	  break;
Packit 05ea35
Packit 05ea35
	case 'B':
Packit 05ea35
	  diffarg ("-B");
Packit 05ea35
	  break;
Packit 05ea35
Packit 05ea35
	case 'd':
Packit 05ea35
	  diffarg ("-d");
Packit 05ea35
	  break;
Packit 05ea35
Packit 05ea35
	case 'E':
Packit 05ea35
	  diffarg ("-E");
Packit 05ea35
	  break;
Packit 05ea35
Packit 05ea35
	case 'H':
Packit 05ea35
	  diffarg ("-H");
Packit 05ea35
	  break;
Packit 05ea35
Packit 05ea35
	case 'i':
Packit 05ea35
	  diffarg ("-i");
Packit 05ea35
	  break;
Packit 05ea35
Packit 05ea35
	case 'I':
Packit 05ea35
	  diffarg ("-I");
Packit 05ea35
	  diffarg (optarg);
Packit 05ea35
	  break;
Packit 05ea35
Packit 05ea35
	case 'l':
Packit 05ea35
	  diffarg ("--left-column");
Packit 05ea35
	  break;
Packit 05ea35
Packit 05ea35
	case 'o':
Packit 05ea35
	  output = optarg;
Packit 05ea35
	  break;
Packit 05ea35
Packit 05ea35
	case 's':
Packit 05ea35
	  suppress_common_lines = true;
Packit 05ea35
	  break;
Packit 05ea35
Packit 05ea35
	case 't':
Packit 05ea35
	  diffarg ("-t");
Packit 05ea35
	  break;
Packit 05ea35
Packit 05ea35
	case 'v':
Packit 05ea35
	  version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version,
Packit 05ea35
		       AUTHORS, (char *) NULL);
Packit 05ea35
	  check_stdout ();
Packit 05ea35
	  return EXIT_SUCCESS;
Packit 05ea35
Packit 05ea35
	case 'w':
Packit 05ea35
	  diffarg ("-W");
Packit 05ea35
	  diffarg (optarg);
Packit 05ea35
	  break;
Packit 05ea35
Packit 05ea35
	case 'W':
Packit 05ea35
	  diffarg ("-w");
Packit 05ea35
	  break;
Packit 05ea35
Packit 05ea35
	case 'Z':
Packit 05ea35
	  diffarg ("-Z");
Packit 05ea35
	  break;
Packit 05ea35
Packit 05ea35
	case DIFF_PROGRAM_OPTION:
Packit 05ea35
	  diffargv[0] = optarg;
Packit 05ea35
	  break;
Packit 05ea35
Packit 05ea35
	case HELP_OPTION:
Packit 05ea35
	  usage ();
Packit 05ea35
	  check_stdout ();
Packit 05ea35
	  return EXIT_SUCCESS;
Packit 05ea35
Packit 05ea35
	case STRIP_TRAILING_CR_OPTION:
Packit 05ea35
	  diffarg ("--strip-trailing-cr");
Packit 05ea35
	  break;
Packit 05ea35
Packit 05ea35
	case TABSIZE_OPTION:
Packit 05ea35
	  diffarg ("--tabsize");
Packit 05ea35
	  diffarg (optarg);
Packit 05ea35
	  break;
Packit 05ea35
Packit 05ea35
	default:
Packit 05ea35
	  try_help (0, 0);
Packit 05ea35
	}
Packit 05ea35
    }
Packit 05ea35
Packit 05ea35
  if (argc - optind != 2)
Packit 05ea35
    {
Packit 05ea35
      if (argc - optind < 2)
Packit 05ea35
	try_help ("missing operand after '%s'", argv[argc - 1]);
Packit 05ea35
      else
Packit 05ea35
	try_help ("extra operand '%s'", argv[optind + 2]);
Packit 05ea35
    }
Packit 05ea35
Packit 05ea35
  if (! output)
Packit 05ea35
    {
Packit 05ea35
      /* easy case: diff does everything for us */
Packit 05ea35
      if (suppress_common_lines)
Packit 05ea35
	diffarg ("--suppress-common-lines");
Packit 05ea35
      diffarg ("-y");
Packit 05ea35
      diffarg ("--");
Packit 05ea35
      diffarg (argv[optind]);
Packit 05ea35
      diffarg (argv[optind + 1]);
Packit 05ea35
      diffarg (0);
Packit 05ea35
      execvp (diffargv[0], (char **) diffargv);
Packit 05ea35
      perror_fatal (diffargv[0]);
Packit 05ea35
    }
Packit 05ea35
  else
Packit 05ea35
    {
Packit 05ea35
      char const *lname, *rname;
Packit 05ea35
      FILE *left, *right, *out, *diffout;
Packit 05ea35
      bool interact_ok;
Packit 05ea35
      struct line_filter lfilt;
Packit 05ea35
      struct line_filter rfilt;
Packit 05ea35
      struct line_filter diff_filt;
Packit 05ea35
      bool leftdir = diraccess (argv[optind]);
Packit 05ea35
      bool rightdir = diraccess (argv[optind + 1]);
Packit 05ea35
Packit 05ea35
      if (leftdir & rightdir)
Packit 05ea35
	fatal ("both files to be compared are directories");
Packit 05ea35
Packit 05ea35
      lname = expand_name (argv[optind], leftdir, argv[optind + 1]);
Packit 05ea35
      left = ck_fopen (lname, "r");
Packit 05ea35
      rname = expand_name (argv[optind + 1], rightdir, argv[optind]);
Packit 05ea35
      right = ck_fopen (rname, "r");
Packit 05ea35
      out = ck_fopen (output, "w");
Packit 05ea35
Packit 05ea35
      diffarg ("--sdiff-merge-assist");
Packit 05ea35
      diffarg ("--");
Packit 05ea35
      diffarg (argv[optind]);
Packit 05ea35
      diffarg (argv[optind + 1]);
Packit 05ea35
      diffarg (0);
Packit 05ea35
Packit 05ea35
      trapsigs ();
Packit 05ea35
Packit 05ea35
#if ! HAVE_WORKING_FORK
Packit 05ea35
      {
Packit 05ea35
	char *command = system_quote_argv (SCI_SYSTEM, (char **) diffargv);
Packit 05ea35
	errno = 0;
Packit 05ea35
	diffout = popen (command, "r");
Packit 05ea35
	if (! diffout)
Packit 05ea35
	  perror_fatal (command);
Packit 05ea35
	free (command);
Packit 05ea35
      }
Packit 05ea35
#else
Packit 05ea35
      {
Packit 05ea35
	int diff_fds[2];
Packit 05ea35
Packit 05ea35
	if (pipe (diff_fds) != 0)
Packit 05ea35
	  perror_fatal ("pipe");
Packit 05ea35
Packit 05ea35
	diffpid = fork ();
Packit 05ea35
	if (diffpid < 0)
Packit 05ea35
	  perror_fatal ("fork");
Packit 05ea35
	if (! diffpid)
Packit 05ea35
	  {
Packit 05ea35
	    /* Alter the child's SIGINT and SIGPIPE handlers;
Packit 05ea35
	       this may munge the parent.
Packit 05ea35
	       The child ignores SIGINT in case the user interrupts the editor.
Packit 05ea35
	       The child does not ignore SIGPIPE, even if the parent does.  */
Packit 05ea35
	    if (initial_handler (handler_index_of_SIGINT) != SIG_IGN)
Packit 05ea35
	      signal_handler (SIGINT, SIG_IGN);
Packit 05ea35
	    signal_handler (SIGPIPE, SIG_DFL);
Packit 05ea35
	    close (diff_fds[0]);
Packit 05ea35
	    if (diff_fds[1] != STDOUT_FILENO)
Packit 05ea35
	      {
Packit 05ea35
		dup2 (diff_fds[1], STDOUT_FILENO);
Packit 05ea35
		close (diff_fds[1]);
Packit 05ea35
	      }
Packit 05ea35
Packit 05ea35
	    execvp (diffargv[0], (char **) diffargv);
Packit 05ea35
	    _exit (errno == ENOENT ? 127 : 126);
Packit 05ea35
	  }
Packit 05ea35
Packit 05ea35
	close (diff_fds[1]);
Packit 05ea35
	diffout = fdopen (diff_fds[0], "r");
Packit 05ea35
	if (! diffout)
Packit 05ea35
	  perror_fatal ("fdopen");
Packit 05ea35
      }
Packit 05ea35
#endif
Packit 05ea35
Packit 05ea35
      lf_init (&diff_filt, diffout);
Packit 05ea35
      lf_init (&lfilt, left);
Packit 05ea35
      lf_init (&rfilt, right);
Packit 05ea35
Packit 05ea35
      interact_ok = interact (&diff_filt, &lfilt, lname, &rfilt, rname, out);
Packit 05ea35
Packit 05ea35
      ck_fclose (left);
Packit 05ea35
      ck_fclose (right);
Packit 05ea35
      ck_fclose (out);
Packit 05ea35
Packit 05ea35
      {
Packit 05ea35
	int wstatus;
Packit 05ea35
	int werrno = 0;
Packit 05ea35
Packit 05ea35
#if ! HAVE_WORKING_FORK
Packit 05ea35
	wstatus = pclose (diffout);
Packit 05ea35
	if (wstatus == -1)
Packit 05ea35
	  werrno = errno;
Packit 05ea35
#else
Packit 05ea35
	ck_fclose (diffout);
Packit 05ea35
	while (waitpid (diffpid, &wstatus, 0) < 0)
Packit 05ea35
	  if (errno == EINTR)
Packit 05ea35
	    checksigs ();
Packit 05ea35
	  else
Packit 05ea35
	    perror_fatal ("waitpid");
Packit 05ea35
	diffpid = 0;
Packit 05ea35
#endif
Packit 05ea35
Packit 05ea35
	if (tmpname)
Packit 05ea35
	  {
Packit 05ea35
	    unlink (tmpname);
Packit 05ea35
	    tmpname = 0;
Packit 05ea35
	  }
Packit 05ea35
Packit 05ea35
	if (! interact_ok)
Packit 05ea35
	  exiterr ();
Packit 05ea35
Packit 05ea35
	check_child_status (werrno, wstatus, EXIT_FAILURE, diffargv[0]);
Packit 05ea35
	untrapsig (0);
Packit 05ea35
	checksigs ();
Packit 05ea35
	exit (WEXITSTATUS (wstatus));
Packit 05ea35
      }
Packit 05ea35
    }
Packit 05ea35
  return EXIT_SUCCESS;			/* Fool '-Wall'.  */
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
static void
Packit 05ea35
diffarg (char const *a)
Packit 05ea35
{
Packit 05ea35
  static size_t diffargs, diffarglim;
Packit 05ea35
Packit 05ea35
  if (diffargs == diffarglim)
Packit 05ea35
    {
Packit 05ea35
      if (! diffarglim)
Packit 05ea35
	diffarglim = 16;
Packit 05ea35
      else if (PTRDIFF_MAX / (2 * sizeof *diffargv) <= diffarglim)
Packit 05ea35
	xalloc_die ();
Packit 05ea35
      else
Packit 05ea35
	diffarglim *= 2;
Packit 05ea35
      diffargv = xrealloc (diffargv, diffarglim * sizeof *diffargv);
Packit 05ea35
    }
Packit 05ea35
  diffargv[diffargs++] = a;
Packit 05ea35
}
Packit 05ea35

Packit 05ea35
/* Signal handling */
Packit 05ea35
Packit 05ea35
static bool volatile ignore_SIGINT;
Packit 05ea35
static int volatile signal_received;
Packit 05ea35
static bool sigs_trapped;
Packit 05ea35
Packit 05ea35
static void
Packit 05ea35
catchsig (int s)
Packit 05ea35
{
Packit 05ea35
#if ! HAVE_SIGACTION
Packit 05ea35
  signal (s, SIG_IGN);
Packit 05ea35
#endif
Packit 05ea35
  if (! (s == SIGINT && ignore_SIGINT))
Packit 05ea35
    signal_received = s;
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
#if HAVE_SIGACTION
Packit 05ea35
static struct sigaction catchaction;
Packit 05ea35
Packit 05ea35
static void
Packit 05ea35
signal_handler (int sig, void (*handler) (int))
Packit 05ea35
{
Packit 05ea35
  catchaction.sa_handler = handler;
Packit 05ea35
  sigaction (sig, &catchaction, 0);
Packit 05ea35
}
Packit 05ea35
#endif
Packit 05ea35
Packit 05ea35
static void
Packit 05ea35
trapsigs (void)
Packit 05ea35
{
Packit 05ea35
  int i;
Packit 05ea35
Packit 05ea35
#if HAVE_SIGACTION
Packit 05ea35
  catchaction.sa_flags = SA_RESTART;
Packit 05ea35
  sigemptyset (&catchaction.sa_mask);
Packit 05ea35
  for (i = 0;  i < NUM_SIGS;  i++)
Packit 05ea35
    sigaddset (&catchaction.sa_mask, sigs[i]);
Packit 05ea35
#endif
Packit 05ea35
Packit 05ea35
  for (i = 0;  i < NUM_SIGS;  i++)
Packit 05ea35
    {
Packit 05ea35
#if HAVE_SIGACTION
Packit 05ea35
      sigaction (sigs[i], 0, &initial_action[i]);
Packit 05ea35
#else
Packit 05ea35
      initial_action[i] = signal (sigs[i], SIG_IGN);
Packit 05ea35
#endif
Packit 05ea35
      if (initial_handler (i) != SIG_IGN)
Packit 05ea35
	signal_handler (sigs[i], catchsig);
Packit 05ea35
    }
Packit 05ea35
Packit 05ea35
#ifdef SIGCHLD
Packit 05ea35
  /* System V fork+wait does not work if SIGCHLD is ignored.  */
Packit 05ea35
  signal (SIGCHLD, SIG_DFL);
Packit 05ea35
#endif
Packit 05ea35
Packit 05ea35
  sigs_trapped = true;
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
/* Untrap signal S, or all trapped signals if S is zero.  */
Packit 05ea35
static void
Packit 05ea35
untrapsig (int s)
Packit 05ea35
{
Packit 05ea35
  int i;
Packit 05ea35
Packit 05ea35
  if (sigs_trapped)
Packit 05ea35
    for (i = 0;  i < NUM_SIGS;  i++)
Packit 05ea35
      if ((! s || sigs[i] == s)  &&  initial_handler (i) != SIG_IGN)
Packit 05ea35
	{
Packit 05ea35
#if HAVE_SIGACTION
Packit 05ea35
	  sigaction (sigs[i], &initial_action[i], 0);
Packit 05ea35
#else
Packit 05ea35
	  signal (sigs[i], initial_action[i]);
Packit 05ea35
#endif
Packit 05ea35
	}
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
/* Exit if a signal has been received.  */
Packit 05ea35
static void
Packit 05ea35
checksigs (void)
Packit 05ea35
{
Packit 05ea35
  int s = signal_received;
Packit 05ea35
  if (s)
Packit 05ea35
    {
Packit 05ea35
      cleanup (0);
Packit 05ea35
Packit 05ea35
      /* Yield an exit status indicating that a signal was received.  */
Packit 05ea35
      untrapsig (s);
Packit 05ea35
      kill (getpid (), s);
Packit 05ea35
Packit 05ea35
      /* That didn't work, so exit with error status.  */
Packit 05ea35
      exit (EXIT_TROUBLE);
Packit 05ea35
    }
Packit 05ea35
}
Packit 05ea35

Packit 05ea35
static void
Packit 05ea35
give_help (void)
Packit 05ea35
{
Packit 05ea35
  fprintf (stderr, "%s", _("\
Packit 05ea35
ed:\tEdit then use both versions, each decorated with a header.\n\
Packit 05ea35
eb:\tEdit then use both versions.\n\
Packit 05ea35
el or e1:\tEdit then use the left version.\n\
Packit 05ea35
er or e2:\tEdit then use the right version.\n\
Packit 05ea35
e:\tDiscard both versions then edit a new one.\n\
Packit 05ea35
l or 1:\tUse the left version.\n\
Packit 05ea35
r or 2:\tUse the right version.\n\
Packit 05ea35
s:\tSilently include common lines.\n\
Packit 05ea35
v:\tVerbosely include common lines.\n\
Packit 05ea35
q:\tQuit.\n\
Packit 05ea35
"));
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
static int
Packit 05ea35
skip_white (void)
Packit 05ea35
{
Packit 05ea35
  int c;
Packit 05ea35
  for (;;)
Packit 05ea35
    {
Packit 05ea35
      c = getchar ();
Packit 05ea35
      if (! isspace (c) || c == '\n')
Packit 05ea35
	break;
Packit 05ea35
      checksigs ();
Packit 05ea35
    }
Packit 05ea35
  if (ferror (stdin))
Packit 05ea35
    perror_fatal (_("read failed"));
Packit 05ea35
  return c;
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
static void
Packit 05ea35
flush_line (void)
Packit 05ea35
{
Packit 05ea35
  int c;
Packit 05ea35
  while ((c = getchar ()) != '\n' && c != EOF)
Packit 05ea35
    continue;
Packit 05ea35
  if (ferror (stdin))
Packit 05ea35
    perror_fatal (_("read failed"));
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
Packit 05ea35
/* interpret an edit command */
Packit 05ea35
static bool
Packit 05ea35
edit (struct line_filter *left, char const *lname, lin lline, lin llen,
Packit 05ea35
      struct line_filter *right, char const *rname, lin rline, lin rlen,
Packit 05ea35
      FILE *outfile)
Packit 05ea35
{
Packit 05ea35
  for (;;)
Packit 05ea35
    {
Packit 05ea35
      int cmd0 IF_LINT (= 0);
Packit 05ea35
      int cmd1 IF_LINT (= 0);
Packit 05ea35
      bool gotcmd = false;
Packit 05ea35
Packit 05ea35
      while (! gotcmd)
Packit 05ea35
	{
Packit 05ea35
	  if (putchar ('%') != '%')
Packit 05ea35
	    perror_fatal (_("write failed"));
Packit 05ea35
	  ck_fflush (stdout);
Packit 05ea35
Packit 05ea35
	  cmd0 = skip_white ();
Packit 05ea35
	  switch (cmd0)
Packit 05ea35
	    {
Packit 05ea35
	    case '1': case '2': case 'l': case 'r':
Packit 05ea35
	    case 's': case 'v': case 'q':
Packit 05ea35
	      if (skip_white () != '\n')
Packit 05ea35
		{
Packit 05ea35
		  give_help ();
Packit 05ea35
		  flush_line ();
Packit 05ea35
		  continue;
Packit 05ea35
		}
Packit 05ea35
	      gotcmd = true;
Packit 05ea35
	      break;
Packit 05ea35
Packit 05ea35
	    case 'e':
Packit 05ea35
	      cmd1 = skip_white ();
Packit 05ea35
	      switch (cmd1)
Packit 05ea35
		{
Packit 05ea35
		case '1': case '2': case 'b': case 'd': case 'l': case 'r':
Packit 05ea35
		  if (skip_white () != '\n')
Packit 05ea35
		    {
Packit 05ea35
		      give_help ();
Packit 05ea35
		      flush_line ();
Packit 05ea35
		      continue;
Packit 05ea35
		    }
Packit 05ea35
		  gotcmd = true;
Packit 05ea35
		  break;
Packit 05ea35
		case '\n':
Packit 05ea35
		  gotcmd = true;
Packit 05ea35
		  break;
Packit 05ea35
		default:
Packit 05ea35
		  give_help ();
Packit 05ea35
		  flush_line ();
Packit 05ea35
		  continue;
Packit 05ea35
		}
Packit 05ea35
	      break;
Packit 05ea35
Packit 05ea35
	    case EOF:
Packit 05ea35
	      if (feof (stdin))
Packit 05ea35
		{
Packit 05ea35
		  gotcmd = true;
Packit 05ea35
		  cmd0 = 'q';
Packit 05ea35
		  break;
Packit 05ea35
		}
Packit 05ea35
	      FALLTHROUGH;
Packit 05ea35
	    default:
Packit 05ea35
	      flush_line ();
Packit 05ea35
	      FALLTHROUGH;
Packit 05ea35
	    case '\n':
Packit 05ea35
	      give_help ();
Packit 05ea35
	      continue;
Packit 05ea35
	    }
Packit 05ea35
	}
Packit 05ea35
Packit 05ea35
      switch (cmd0)
Packit 05ea35
	{
Packit 05ea35
	case '1': case 'l':
Packit 05ea35
	  lf_copy (left, llen, outfile);
Packit 05ea35
	  lf_skip (right, rlen);
Packit 05ea35
	  return true;
Packit 05ea35
	case '2': case 'r':
Packit 05ea35
	  lf_copy (right, rlen, outfile);
Packit 05ea35
	  lf_skip (left, llen);
Packit 05ea35
	  return true;
Packit 05ea35
	case 's':
Packit 05ea35
	  suppress_common_lines = true;
Packit 05ea35
	  break;
Packit 05ea35
	case 'v':
Packit 05ea35
	  suppress_common_lines = false;
Packit 05ea35
	  break;
Packit 05ea35
	case 'q':
Packit 05ea35
	  return false;
Packit 05ea35
	case 'e':
Packit 05ea35
	  {
Packit 05ea35
	    int fd;
Packit 05ea35
Packit 05ea35
	    if (tmpname)
Packit 05ea35
	      tmp = fopen (tmpname, "w");
Packit 05ea35
	    else
Packit 05ea35
	      {
Packit 05ea35
		if ((fd = temporary_file ()) < 0)
Packit 05ea35
		  perror_fatal ("mkstemp");
Packit 05ea35
		tmp = fdopen (fd, "w");
Packit 05ea35
	      }
Packit 05ea35
Packit 05ea35
	    if (! tmp)
Packit 05ea35
	      perror_fatal (tmpname);
Packit 05ea35
Packit 05ea35
	    switch (cmd1)
Packit 05ea35
	      {
Packit 05ea35
	      case 'd':
Packit 05ea35
		if (llen)
Packit 05ea35
		  {
Packit 05ea35
		    printint l1 = lline;
Packit 05ea35
		    printint l2 = lline + llen - 1;
Packit 05ea35
		    if (llen == 1)
Packit 05ea35
		      fprintf (tmp, "--- %s %"pI"d\n", lname, l1);
Packit 05ea35
		    else
Packit 05ea35
		      fprintf (tmp, "--- %s %"pI"d,%"pI"d\n", lname, l1, l2);
Packit 05ea35
		  }
Packit 05ea35
		FALLTHROUGH;
Packit 05ea35
	      case '1': case 'b': case 'l':
Packit 05ea35
		lf_copy (left, llen, tmp);
Packit 05ea35
		break;
Packit 05ea35
Packit 05ea35
	      default:
Packit 05ea35
		lf_skip (left, llen);
Packit 05ea35
		break;
Packit 05ea35
	      }
Packit 05ea35
Packit 05ea35
	    switch (cmd1)
Packit 05ea35
	      {
Packit 05ea35
	      case 'd':
Packit 05ea35
		if (rlen)
Packit 05ea35
		  {
Packit 05ea35
		    printint l1 = rline;
Packit 05ea35
		    printint l2 = rline + rlen - 1;
Packit 05ea35
		    if (rlen == 1)
Packit 05ea35
		      fprintf (tmp, "+++ %s %"pI"d\n", rname, l1);
Packit 05ea35
		    else
Packit 05ea35
		      fprintf (tmp, "+++ %s %"pI"d,%"pI"d\n", rname, l1, l2);
Packit 05ea35
		  }
Packit 05ea35
		FALLTHROUGH;
Packit 05ea35
	      case '2': case 'b': case 'r':
Packit 05ea35
		lf_copy (right, rlen, tmp);
Packit 05ea35
		break;
Packit 05ea35
Packit 05ea35
	      default:
Packit 05ea35
		lf_skip (right, rlen);
Packit 05ea35
		break;
Packit 05ea35
	      }
Packit 05ea35
Packit 05ea35
	    ck_fclose (tmp);
Packit 05ea35
Packit 05ea35
	    {
Packit 05ea35
	      int wstatus;
Packit 05ea35
	      int werrno = 0;
Packit 05ea35
	      char const *argv[3];
Packit 05ea35
Packit 05ea35
	      ignore_SIGINT = true;
Packit 05ea35
	      checksigs ();
Packit 05ea35
	      argv[0] = editor_program;
Packit 05ea35
	      argv[1] = tmpname;
Packit 05ea35
	      argv[2] = 0;
Packit 05ea35
Packit 05ea35
	      {
Packit 05ea35
#if ! HAVE_WORKING_FORK
Packit 05ea35
		char *command = system_quote_argv (SCI_SYSTEM, (char **) argv);
Packit 05ea35
		wstatus = system (command);
Packit 05ea35
		if (wstatus == -1)
Packit 05ea35
		  werrno = errno;
Packit 05ea35
		free (command);
Packit 05ea35
#else
Packit 05ea35
		pid_t pid;
Packit 05ea35
Packit 05ea35
		pid = fork ();
Packit 05ea35
		if (pid == 0)
Packit 05ea35
		  {
Packit 05ea35
		    execvp (editor_program, (char **) argv);
Packit 05ea35
		    _exit (errno == ENOENT ? 127 : 126);
Packit 05ea35
		  }
Packit 05ea35
Packit 05ea35
		if (pid < 0)
Packit 05ea35
		  perror_fatal ("fork");
Packit 05ea35
Packit 05ea35
		while (waitpid (pid, &wstatus, 0) < 0)
Packit 05ea35
		  if (errno == EINTR)
Packit 05ea35
		    checksigs ();
Packit 05ea35
		  else
Packit 05ea35
		    perror_fatal ("waitpid");
Packit 05ea35
#endif
Packit 05ea35
	      }
Packit 05ea35
Packit 05ea35
	      ignore_SIGINT = false;
Packit 05ea35
	      check_child_status (werrno, wstatus, EXIT_SUCCESS,
Packit 05ea35
				  editor_program);
Packit 05ea35
	    }
Packit 05ea35
Packit 05ea35
	    {
Packit 05ea35
	      char buf[SDIFF_BUFSIZE];
Packit 05ea35
	      size_t size;
Packit 05ea35
	      tmp = ck_fopen (tmpname, "r");
Packit 05ea35
	      while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
Packit 05ea35
		{
Packit 05ea35
		  checksigs ();
Packit 05ea35
		  ck_fwrite (buf, size, outfile);
Packit 05ea35
		}
Packit 05ea35
	      ck_fclose (tmp);
Packit 05ea35
	    }
Packit 05ea35
	    return true;
Packit 05ea35
	  }
Packit 05ea35
	default:
Packit 05ea35
	  give_help ();
Packit 05ea35
	  break;
Packit 05ea35
	}
Packit 05ea35
    }
Packit 05ea35
}
Packit 05ea35

Packit 05ea35
/* Alternately reveal bursts of diff output and handle user commands.  */
Packit 05ea35
static bool
Packit 05ea35
interact (struct line_filter *diff,
Packit 05ea35
	  struct line_filter *left, char const *lname,
Packit 05ea35
	  struct line_filter *right, char const *rname,
Packit 05ea35
	  FILE *outfile)
Packit 05ea35
{
Packit 05ea35
  lin lline = 1, rline = 1;
Packit 05ea35
Packit 05ea35
  for (;;)
Packit 05ea35
    {
Packit 05ea35
      char diff_help[256];
Packit 05ea35
      int snarfed = lf_snarf (diff, diff_help, sizeof diff_help);
Packit 05ea35
Packit 05ea35
      if (snarfed <= 0)
Packit 05ea35
	return snarfed != 0;
Packit 05ea35
Packit 05ea35
      checksigs ();
Packit 05ea35
Packit 05ea35
      if (diff_help[0] == ' ')
Packit 05ea35
	puts (diff_help + 1);
Packit 05ea35
      else
Packit 05ea35
	{
Packit 05ea35
	  char *numend;
Packit 05ea35
	  uintmax_t val;
Packit 05ea35
	  lin llen, rlen, lenmax;
Packit 05ea35
	  errno = 0;
Packit 05ea35
	  val = strtoumax (diff_help + 1, &numend, 10);
Packit 05ea35
	  if (LIN_MAX < val || errno || *numend != ',')
Packit 05ea35
	    fatal (diff_help);
Packit 05ea35
	  llen = val;
Packit 05ea35
	  val = strtoumax (numend + 1, &numend, 10);
Packit 05ea35
	  if (LIN_MAX < val || errno || *numend)
Packit 05ea35
	    fatal (diff_help);
Packit 05ea35
	  rlen = val;
Packit 05ea35
Packit 05ea35
	  lenmax = MAX (llen, rlen);
Packit 05ea35
Packit 05ea35
	  switch (diff_help[0])
Packit 05ea35
	    {
Packit 05ea35
	    case 'i':
Packit 05ea35
	      if (suppress_common_lines)
Packit 05ea35
		lf_skip (diff, lenmax);
Packit 05ea35
	      else
Packit 05ea35
		lf_copy (diff, lenmax, stdout);
Packit 05ea35
Packit 05ea35
	      lf_copy (left, llen, outfile);
Packit 05ea35
	      lf_skip (right, rlen);
Packit 05ea35
	      break;
Packit 05ea35
Packit 05ea35
	    case 'c':
Packit 05ea35
	      lf_copy (diff, lenmax, stdout);
Packit 05ea35
	      if (! edit (left, lname, lline, llen,
Packit 05ea35
			  right, rname, rline, rlen,
Packit 05ea35
			  outfile))
Packit 05ea35
		return false;
Packit 05ea35
	      break;
Packit 05ea35
Packit 05ea35
	    default:
Packit 05ea35
	      fatal (diff_help);
Packit 05ea35
	    }
Packit 05ea35
Packit 05ea35
	  lline += llen;
Packit 05ea35
	  rline += rlen;
Packit 05ea35
	}
Packit 05ea35
    }
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
/* Return true if DIR is an existing directory.  */
Packit 05ea35
static bool
Packit 05ea35
diraccess (char const *dir)
Packit 05ea35
{
Packit 05ea35
  struct stat buf;
Packit 05ea35
  return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
Packit 05ea35
}
Packit 05ea35
Packit 05ea35
#ifndef P_tmpdir
Packit 05ea35
# define P_tmpdir "/tmp"
Packit 05ea35
#endif
Packit 05ea35
#ifndef TMPDIR_ENV
Packit 05ea35
# define TMPDIR_ENV "TMPDIR"
Packit 05ea35
#endif
Packit 05ea35
Packit 05ea35
/* Open a temporary file and return its file descriptor.  Put into
Packit 05ea35
   tmpname the address of a newly allocated buffer that holds the
Packit 05ea35
   file's name.  Use the prefix "sdiff".  */
Packit 05ea35
static int
Packit 05ea35
temporary_file (void)
Packit 05ea35
{
Packit 05ea35
  char const *tmpdir = getenv (TMPDIR_ENV);
Packit 05ea35
  char const *dir = tmpdir ? tmpdir : P_tmpdir;
Packit 05ea35
  char *buf = xmalloc (strlen (dir) + 1 + 5 + 6 + 1);
Packit 05ea35
  int fd;
Packit 05ea35
  sprintf (buf, "%s/sdiffXXXXXX", dir);
Packit 05ea35
  fd = mkstemp (buf);
Packit 05ea35
  if (0 <= fd)
Packit 05ea35
    tmpname = buf;
Packit 05ea35
  return fd;
Packit 05ea35
}