1170754Sdelphij/* sdiff - side-by-side merge of file differences
2170754Sdelphij
3170754Sdelphij   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 2001, 2002, 2004
4170754Sdelphij   Free Software Foundation, Inc.
5170754Sdelphij
6170754Sdelphij   This file is part of GNU DIFF.
7170754Sdelphij
8170754Sdelphij   GNU DIFF is free software; you can redistribute it and/or modify
9170754Sdelphij   it under the terms of the GNU General Public License as published by
10170754Sdelphij   the Free Software Foundation; either version 2, or (at your option)
11170754Sdelphij   any later version.
12170754Sdelphij
13170754Sdelphij   GNU DIFF is distributed in the hope that it will be useful,
14170754Sdelphij   but WITHOUT ANY WARRANTY; without even the implied warranty of
15170754Sdelphij   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16170754Sdelphij   See the GNU General Public License for more details.
17170754Sdelphij
18170754Sdelphij   You should have received a copy of the GNU General Public License
19170754Sdelphij   along with this program; see the file COPYING.
20170754Sdelphij   If not, write to the Free Software Foundation,
21170754Sdelphij   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
22170754Sdelphij
23170754Sdelphij#include "system.h"
24170754Sdelphij#include "paths.h"
25170754Sdelphij
26170754Sdelphij#include <stdio.h>
27170754Sdelphij#include <unlocked-io.h>
28170754Sdelphij
29170754Sdelphij#include <c-stack.h>
30170754Sdelphij#include <dirname.h>
31170754Sdelphij#include <error.h>
32170754Sdelphij#include <exit.h>
33170754Sdelphij#include <exitfail.h>
34170754Sdelphij#include <file-type.h>
35170754Sdelphij#include <getopt.h>
36170754Sdelphij#include <quotesys.h>
37170754Sdelphij#include <version-etc.h>
38170754Sdelphij#include <xalloc.h>
39170754Sdelphij
40170754Sdelphij/* Size of chunks read from files which must be parsed into lines.  */
41170754Sdelphij#define SDIFF_BUFSIZE ((size_t) 65536)
42170754Sdelphij
43170754Sdelphijchar *program_name;
44170754Sdelphij
45170754Sdelphijstatic char const *editor_program = DEFAULT_EDITOR_PROGRAM;
46170754Sdelphijstatic char const **diffargv;
47170754Sdelphij
48170754Sdelphijstatic char * volatile tmpname;
49170754Sdelphijstatic FILE *tmp;
50170754Sdelphij
51170754Sdelphij#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
52170754Sdelphijstatic pid_t volatile diffpid;
53170754Sdelphij#endif
54170754Sdelphij
55170754Sdelphijstruct line_filter;
56170754Sdelphij
57170754Sdelphijstatic void catchsig (int);
58170754Sdelphijstatic bool edit (struct line_filter *, char const *, lin, lin, struct line_filter *, char const *, lin, lin, FILE *);
59170754Sdelphijstatic bool interact (struct line_filter *, struct line_filter *, char const *, struct line_filter *, char const *, FILE *);
60170754Sdelphijstatic void checksigs (void);
61170754Sdelphijstatic void diffarg (char const *);
62170754Sdelphijstatic void fatal (char const *) __attribute__((noreturn));
63170754Sdelphijstatic void perror_fatal (char const *) __attribute__((noreturn));
64170754Sdelphijstatic void trapsigs (void);
65170754Sdelphijstatic void untrapsig (int);
66170754Sdelphij
67170754Sdelphij#define NUM_SIGS (sizeof sigs / sizeof *sigs)
68170754Sdelphijstatic int const sigs[] = {
69170754Sdelphij#ifdef SIGHUP
70170754Sdelphij       SIGHUP,
71170754Sdelphij#endif
72170754Sdelphij#ifdef SIGQUIT
73170754Sdelphij       SIGQUIT,
74170754Sdelphij#endif
75170754Sdelphij#ifdef SIGTERM
76170754Sdelphij       SIGTERM,
77170754Sdelphij#endif
78170754Sdelphij#ifdef SIGXCPU
79170754Sdelphij       SIGXCPU,
80170754Sdelphij#endif
81170754Sdelphij#ifdef SIGXFSZ
82170754Sdelphij       SIGXFSZ,
83170754Sdelphij#endif
84170754Sdelphij       SIGINT,
85170754Sdelphij       SIGPIPE
86170754Sdelphij};
87170754Sdelphij#define handler_index_of_SIGINT (NUM_SIGS - 2)
88170754Sdelphij#define handler_index_of_SIGPIPE (NUM_SIGS - 1)
89170754Sdelphij
90170754Sdelphij#if HAVE_SIGACTION
91170754Sdelphij  /* Prefer `sigaction' if available, since `signal' can lose signals.  */
92170754Sdelphij  static struct sigaction initial_action[NUM_SIGS];
93170754Sdelphij# define initial_handler(i) (initial_action[i].sa_handler)
94170754Sdelphij  static void signal_handler (int, void (*) (int));
95170754Sdelphij#else
96170754Sdelphij  static void (*initial_action[NUM_SIGS]) ();
97170754Sdelphij# define initial_handler(i) (initial_action[i])
98170754Sdelphij# define signal_handler(sig, handler) signal (sig, handler)
99170754Sdelphij#endif
100170754Sdelphij
101170754Sdelphij#if ! HAVE_SIGPROCMASK
102170754Sdelphij# define sigset_t int
103170754Sdelphij# define sigemptyset(s) (*(s) = 0)
104170754Sdelphij# ifndef sigmask
105170754Sdelphij#  define sigmask(sig) (1 << ((sig) - 1))
106170754Sdelphij# endif
107170754Sdelphij# define sigaddset(s, sig) (*(s) |= sigmask (sig))
108170754Sdelphij# ifndef SIG_BLOCK
109170754Sdelphij#  define SIG_BLOCK 0
110170754Sdelphij# endif
111170754Sdelphij# ifndef SIG_SETMASK
112170754Sdelphij#  define SIG_SETMASK (! SIG_BLOCK)
113170754Sdelphij# endif
114170754Sdelphij# define sigprocmask(how, n, o) \
115170754Sdelphij    ((how) == SIG_BLOCK ? *(o) = sigblock (*(n)) : sigsetmask (*(n)))
116170754Sdelphij#endif
117170754Sdelphij
118170754Sdelphijstatic bool diraccess (char const *);
119170754Sdelphijstatic int temporary_file (void);
120170754Sdelphij
121170754Sdelphij/* Options: */
122170754Sdelphij
123170754Sdelphij/* Name of output file if -o specified.  */
124170754Sdelphijstatic char const *output;
125170754Sdelphij
126170754Sdelphij/* Do not print common lines.  */
127170754Sdelphijstatic bool suppress_common_lines;
128170754Sdelphij
129170754Sdelphij/* Value for the long option that does not have single-letter equivalents.  */
130170754Sdelphijenum
131170754Sdelphij{
132170754Sdelphij  DIFF_PROGRAM_OPTION = CHAR_MAX + 1,
133170754Sdelphij  HELP_OPTION,
134170754Sdelphij  STRIP_TRAILING_CR_OPTION,
135170754Sdelphij  TABSIZE_OPTION
136170754Sdelphij};
137170754Sdelphij
138170754Sdelphijstatic struct option const longopts[] =
139170754Sdelphij{
140170754Sdelphij  {"diff-program", 1, 0, DIFF_PROGRAM_OPTION},
141170754Sdelphij  {"expand-tabs", 0, 0, 't'},
142170754Sdelphij  {"help", 0, 0, HELP_OPTION},
143170754Sdelphij  {"ignore-all-space", 0, 0, 'W'}, /* swap W and w for historical reasons */
144170754Sdelphij  {"ignore-blank-lines", 0, 0, 'B'},
145170754Sdelphij  {"ignore-case", 0, 0, 'i'},
146170754Sdelphij  {"ignore-matching-lines", 1, 0, 'I'},
147170754Sdelphij  {"ignore-space-change", 0, 0, 'b'},
148170754Sdelphij  {"ignore-tab-expansion", 0, 0, 'E'},
149170754Sdelphij  {"left-column", 0, 0, 'l'},
150170754Sdelphij  {"minimal", 0, 0, 'd'},
151170754Sdelphij  {"output", 1, 0, 'o'},
152170754Sdelphij  {"speed-large-files", 0, 0, 'H'},
153170754Sdelphij  {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
154170754Sdelphij  {"suppress-common-lines", 0, 0, 's'},
155170754Sdelphij  {"tabsize", 1, 0, TABSIZE_OPTION},
156170754Sdelphij  {"text", 0, 0, 'a'},
157170754Sdelphij  {"version", 0, 0, 'v'},
158170754Sdelphij  {"width", 1, 0, 'w'},
159170754Sdelphij  {0, 0, 0, 0}
160170754Sdelphij};
161170754Sdelphij
162170754Sdelphijstatic void try_help (char const *, char const *) __attribute__((noreturn));
163170754Sdelphijstatic void
164170754Sdelphijtry_help (char const *reason_msgid, char const *operand)
165170754Sdelphij{
166170754Sdelphij  if (reason_msgid)
167170754Sdelphij    error (0, 0, _(reason_msgid), operand);
168170754Sdelphij  error (EXIT_TROUBLE, 0, _("Try `%s --help' for more information."),
169170754Sdelphij	 program_name);
170170754Sdelphij  abort ();
171170754Sdelphij}
172170754Sdelphij
173170754Sdelphijstatic void
174170754Sdelphijcheck_stdout (void)
175170754Sdelphij{
176170754Sdelphij  if (ferror (stdout))
177170754Sdelphij    fatal ("write failed");
178170754Sdelphij  else if (fclose (stdout) != 0)
179170754Sdelphij    perror_fatal (_("standard output"));
180170754Sdelphij}
181170754Sdelphij
182170754Sdelphijstatic char const * const option_help_msgid[] = {
183170754Sdelphij  N_("-o FILE  --output=FILE  Operate interactively, sending output to FILE."),
184170754Sdelphij  "",
185170754Sdelphij  N_("-i  --ignore-case  Consider upper- and lower-case to be the same."),
186170754Sdelphij  N_("-E  --ignore-tab-expansion  Ignore changes due to tab expansion."),
187170754Sdelphij  N_("-b  --ignore-space-change  Ignore changes in the amount of white space."),
188170754Sdelphij  N_("-W  --ignore-all-space  Ignore all white space."),
189170754Sdelphij  N_("-B  --ignore-blank-lines  Ignore changes whose lines are all blank."),
190170754Sdelphij  N_("-I RE  --ignore-matching-lines=RE  Ignore changes whose lines all match RE."),
191170754Sdelphij  N_("--strip-trailing-cr  Strip trailing carriage return on input."),
192170754Sdelphij  N_("-a  --text  Treat all files as text."),
193170754Sdelphij  "",
194170754Sdelphij  N_("-w NUM  --width=NUM  Output at most NUM (default 130) print columns."),
195170754Sdelphij  N_("-l  --left-column  Output only the left column of common lines."),
196170754Sdelphij  N_("-s  --suppress-common-lines  Do not output common lines."),
197170754Sdelphij  "",
198170754Sdelphij  N_("-t  --expand-tabs  Expand tabs to spaces in output."),
199170754Sdelphij  N_("--tabsize=NUM  Tab stops are every NUM (default 8) print columns."),
200170754Sdelphij  "",
201170754Sdelphij  N_("-d  --minimal  Try hard to find a smaller set of changes."),
202170754Sdelphij  N_("-H  --speed-large-files  Assume large files and many scattered small changes."),
203170754Sdelphij  N_("--diff-program=PROGRAM  Use PROGRAM to compare files."),
204170754Sdelphij  "",
205170754Sdelphij  N_("-v  --version  Output version info."),
206170754Sdelphij  N_("--help  Output this help."),
207170754Sdelphij  0
208170754Sdelphij};
209170754Sdelphij
210170754Sdelphijstatic void
211170754Sdelphijusage (void)
212170754Sdelphij{
213170754Sdelphij  char const * const *p;
214170754Sdelphij
215170754Sdelphij  printf (_("Usage: %s [OPTION]... FILE1 FILE2\n"), program_name);
216170754Sdelphij  printf ("%s\n\n", _("Side-by-side merge of file differences."));
217170754Sdelphij  for (p = option_help_msgid;  *p;  p++)
218170754Sdelphij    if (**p)
219170754Sdelphij      printf ("  %s\n", _(*p));
220170754Sdelphij    else
221170754Sdelphij      putchar ('\n');
222170754Sdelphij  printf ("\n%s\n%s\n\n%s\n",
223170754Sdelphij	  _("If a FILE is `-', read standard input."),
224170754Sdelphij	  _("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."),
225170754Sdelphij	  _("Report bugs to <bug-gnu-utils@gnu.org>."));
226170754Sdelphij}
227170754Sdelphij
228170754Sdelphij/* Clean up after a signal or other failure.  This function is
229170754Sdelphij   async-signal-safe.  */
230170754Sdelphijstatic void
231170754Sdelphijcleanup (int signo __attribute__((unused)))
232170754Sdelphij{
233170754Sdelphij#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
234170754Sdelphij  if (0 < diffpid)
235170754Sdelphij    kill (diffpid, SIGPIPE);
236170754Sdelphij#endif
237170754Sdelphij  if (tmpname)
238170754Sdelphij    unlink (tmpname);
239170754Sdelphij}
240170754Sdelphij
241170754Sdelphijstatic void exiterr (void) __attribute__((noreturn));
242170754Sdelphijstatic void
243170754Sdelphijexiterr (void)
244170754Sdelphij{
245170754Sdelphij  cleanup (0);
246170754Sdelphij  untrapsig (0);
247170754Sdelphij  checksigs ();
248170754Sdelphij  exit (EXIT_TROUBLE);
249170754Sdelphij}
250170754Sdelphij
251170754Sdelphijstatic void
252170754Sdelphijfatal (char const *msgid)
253170754Sdelphij{
254170754Sdelphij  error (0, 0, "%s", _(msgid));
255170754Sdelphij  exiterr ();
256170754Sdelphij}
257170754Sdelphij
258170754Sdelphijstatic void
259170754Sdelphijperror_fatal (char const *msg)
260170754Sdelphij{
261170754Sdelphij  int e = errno;
262170754Sdelphij  checksigs ();
263170754Sdelphij  error (0, e, "%s", msg);
264170754Sdelphij  exiterr ();
265170754Sdelphij}
266170754Sdelphij
267170754Sdelphijstatic void
268170754Sdelphijcheck_child_status (int werrno, int wstatus, int max_ok_status,
269170754Sdelphij		    char const *subsidiary_program)
270170754Sdelphij{
271170754Sdelphij  int status = (! werrno && WIFEXITED (wstatus)
272170754Sdelphij		? WEXITSTATUS (wstatus)
273170754Sdelphij		: INT_MAX);
274170754Sdelphij
275170754Sdelphij  if (max_ok_status < status)
276170754Sdelphij    {
277170754Sdelphij      error (0, werrno,
278170754Sdelphij	     _(status == 126
279170754Sdelphij	       ? "subsidiary program `%s' could not be invoked"
280170754Sdelphij	       : status == 127
281170754Sdelphij	       ? "subsidiary program `%s' not found"
282170754Sdelphij	       : status == INT_MAX
283170754Sdelphij	       ? "subsidiary program `%s' failed"
284170754Sdelphij	       : "subsidiary program `%s' failed (exit status %d)"),
285170754Sdelphij	     subsidiary_program, status);
286170754Sdelphij      exiterr ();
287170754Sdelphij    }
288170754Sdelphij}
289170754Sdelphij
290170754Sdelphijstatic FILE *
291170754Sdelphijck_fopen (char const *fname, char const *type)
292170754Sdelphij{
293170754Sdelphij  FILE *r = fopen (fname, type);
294170754Sdelphij  if (! r)
295170754Sdelphij    perror_fatal (fname);
296170754Sdelphij  return r;
297170754Sdelphij}
298170754Sdelphij
299170754Sdelphijstatic void
300170754Sdelphijck_fclose (FILE *f)
301170754Sdelphij{
302170754Sdelphij  if (fclose (f))
303170754Sdelphij    perror_fatal ("fclose");
304170754Sdelphij}
305170754Sdelphij
306170754Sdelphijstatic size_t
307170754Sdelphijck_fread (char *buf, size_t size, FILE *f)
308170754Sdelphij{
309170754Sdelphij  size_t r = fread (buf, sizeof (char), size, f);
310170754Sdelphij  if (r == 0 && ferror (f))
311170754Sdelphij    perror_fatal (_("read failed"));
312170754Sdelphij  return r;
313170754Sdelphij}
314170754Sdelphij
315170754Sdelphijstatic void
316170754Sdelphijck_fwrite (char const *buf, size_t size, FILE *f)
317170754Sdelphij{
318170754Sdelphij  if (fwrite (buf, sizeof (char), size, f) != size)
319170754Sdelphij    perror_fatal (_("write failed"));
320170754Sdelphij}
321170754Sdelphij
322170754Sdelphijstatic void
323170754Sdelphijck_fflush (FILE *f)
324170754Sdelphij{
325170754Sdelphij  if (fflush (f) != 0)
326170754Sdelphij    perror_fatal (_("write failed"));
327170754Sdelphij}
328170754Sdelphij
329170754Sdelphijstatic char const *
330170754Sdelphijexpand_name (char *name, bool is_dir, char const *other_name)
331170754Sdelphij{
332170754Sdelphij  if (strcmp (name, "-") == 0)
333170754Sdelphij    fatal ("cannot interactively merge standard input");
334170754Sdelphij  if (! is_dir)
335170754Sdelphij    return name;
336170754Sdelphij  else
337170754Sdelphij    {
338170754Sdelphij      /* Yield NAME/BASE, where BASE is OTHER_NAME's basename.  */
339170754Sdelphij      char const *base = base_name (other_name);
340170754Sdelphij      size_t namelen = strlen (name), baselen = strlen (base);
341170754Sdelphij      bool insert_slash = *base_name (name) && name[namelen - 1] != '/';
342170754Sdelphij      char *r = xmalloc (namelen + insert_slash + baselen + 1);
343170754Sdelphij      memcpy (r, name, namelen);
344170754Sdelphij      r[namelen] = '/';
345170754Sdelphij      memcpy (r + namelen + insert_slash, base, baselen + 1);
346170754Sdelphij      return r;
347170754Sdelphij    }
348170754Sdelphij}
349170754Sdelphij
350170754Sdelphijstruct line_filter {
351170754Sdelphij  FILE *infile;
352170754Sdelphij  char *bufpos;
353170754Sdelphij  char *buffer;
354170754Sdelphij  char *buflim;
355170754Sdelphij};
356170754Sdelphij
357170754Sdelphijstatic void
358170754Sdelphijlf_init (struct line_filter *lf, FILE *infile)
359170754Sdelphij{
360170754Sdelphij  lf->infile = infile;
361170754Sdelphij  lf->bufpos = lf->buffer = lf->buflim = xmalloc (SDIFF_BUFSIZE + 1);
362170754Sdelphij  lf->buflim[0] = '\n';
363170754Sdelphij}
364170754Sdelphij
365170754Sdelphij/* Fill an exhausted line_filter buffer from its INFILE */
366170754Sdelphijstatic size_t
367170754Sdelphijlf_refill (struct line_filter *lf)
368170754Sdelphij{
369170754Sdelphij  size_t s = ck_fread (lf->buffer, SDIFF_BUFSIZE, lf->infile);
370170754Sdelphij  lf->bufpos = lf->buffer;
371170754Sdelphij  lf->buflim = lf->buffer + s;
372170754Sdelphij  lf->buflim[0] = '\n';
373170754Sdelphij  checksigs ();
374170754Sdelphij  return s;
375170754Sdelphij}
376170754Sdelphij
377170754Sdelphij/* Advance LINES on LF's infile, copying lines to OUTFILE */
378170754Sdelphijstatic void
379170754Sdelphijlf_copy (struct line_filter *lf, lin lines, FILE *outfile)
380170754Sdelphij{
381170754Sdelphij  char *start = lf->bufpos;
382170754Sdelphij
383170754Sdelphij  while (lines)
384170754Sdelphij    {
385170754Sdelphij      lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
386170754Sdelphij      if (! lf->bufpos)
387170754Sdelphij	{
388170754Sdelphij	  ck_fwrite (start, lf->buflim - start, outfile);
389170754Sdelphij	  if (! lf_refill (lf))
390170754Sdelphij	    return;
391170754Sdelphij	  start = lf->bufpos;
392170754Sdelphij	}
393170754Sdelphij      else
394170754Sdelphij	{
395170754Sdelphij	  --lines;
396170754Sdelphij	  ++lf->bufpos;
397170754Sdelphij	}
398170754Sdelphij    }
399170754Sdelphij
400170754Sdelphij  ck_fwrite (start, lf->bufpos - start, outfile);
401170754Sdelphij}
402170754Sdelphij
403170754Sdelphij/* Advance LINES on LF's infile without doing output */
404170754Sdelphijstatic void
405170754Sdelphijlf_skip (struct line_filter *lf, lin lines)
406170754Sdelphij{
407170754Sdelphij  while (lines)
408170754Sdelphij    {
409170754Sdelphij      lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
410170754Sdelphij      if (! lf->bufpos)
411170754Sdelphij	{
412170754Sdelphij	  if (! lf_refill (lf))
413170754Sdelphij	    break;
414170754Sdelphij	}
415170754Sdelphij      else
416170754Sdelphij	{
417170754Sdelphij	  --lines;
418170754Sdelphij	  ++lf->bufpos;
419170754Sdelphij	}
420170754Sdelphij    }
421170754Sdelphij}
422170754Sdelphij
423170754Sdelphij/* Snarf a line into a buffer.  Return EOF if EOF, 0 if error, 1 if OK.  */
424170754Sdelphijstatic int
425170754Sdelphijlf_snarf (struct line_filter *lf, char *buffer, size_t bufsize)
426170754Sdelphij{
427170754Sdelphij  for (;;)
428170754Sdelphij    {
429170754Sdelphij      char *start = lf->bufpos;
430170754Sdelphij      char *next = (char *) memchr (start, '\n', lf->buflim + 1 - start);
431170754Sdelphij      size_t s = next - start;
432170754Sdelphij      if (bufsize <= s)
433170754Sdelphij	return 0;
434170754Sdelphij      memcpy (buffer, start, s);
435170754Sdelphij      if (next < lf->buflim)
436170754Sdelphij	{
437170754Sdelphij	  buffer[s] = 0;
438170754Sdelphij	  lf->bufpos = next + 1;
439170754Sdelphij	  return 1;
440170754Sdelphij	}
441170754Sdelphij      if (! lf_refill (lf))
442170754Sdelphij	return s ? 0 : EOF;
443170754Sdelphij      buffer += s;
444170754Sdelphij      bufsize -= s;
445170754Sdelphij    }
446170754Sdelphij}
447170754Sdelphij
448170754Sdelphijint
449170754Sdelphijmain (int argc, char *argv[])
450170754Sdelphij{
451170754Sdelphij  int opt;
452170754Sdelphij  char const *prog;
453170754Sdelphij
454170754Sdelphij  exit_failure = EXIT_TROUBLE;
455170754Sdelphij  initialize_main (&argc, &argv);
456170754Sdelphij  program_name = argv[0];
457170754Sdelphij  setlocale (LC_ALL, "");
458170754Sdelphij  bindtextdomain (PACKAGE, LOCALEDIR);
459170754Sdelphij  textdomain (PACKAGE);
460170754Sdelphij  c_stack_action (cleanup);
461170754Sdelphij
462170754Sdelphij  prog = getenv ("EDITOR");
463170754Sdelphij  if (prog)
464170754Sdelphij    editor_program = prog;
465170754Sdelphij
466170754Sdelphij  diffarg (DEFAULT_DIFF_PROGRAM);
467170754Sdelphij
468170754Sdelphij  /* parse command line args */
469170754Sdelphij  while ((opt = getopt_long (argc, argv, "abBdEHiI:lo:stvw:W", longopts, 0))
470170754Sdelphij	 != -1)
471170754Sdelphij    {
472170754Sdelphij      switch (opt)
473170754Sdelphij	{
474170754Sdelphij	case 'a':
475170754Sdelphij	  diffarg ("-a");
476170754Sdelphij	  break;
477170754Sdelphij
478170754Sdelphij	case 'b':
479170754Sdelphij	  diffarg ("-b");
480170754Sdelphij	  break;
481170754Sdelphij
482170754Sdelphij	case 'B':
483170754Sdelphij	  diffarg ("-B");
484170754Sdelphij	  break;
485170754Sdelphij
486170754Sdelphij	case 'd':
487170754Sdelphij	  diffarg ("-d");
488170754Sdelphij	  break;
489170754Sdelphij
490170754Sdelphij	case 'E':
491170754Sdelphij	  diffarg ("-E");
492170754Sdelphij	  break;
493170754Sdelphij
494170754Sdelphij	case 'H':
495170754Sdelphij	  diffarg ("-H");
496170754Sdelphij	  break;
497170754Sdelphij
498170754Sdelphij	case 'i':
499170754Sdelphij	  diffarg ("-i");
500170754Sdelphij	  break;
501170754Sdelphij
502170754Sdelphij	case 'I':
503170754Sdelphij	  diffarg ("-I");
504170754Sdelphij	  diffarg (optarg);
505170754Sdelphij	  break;
506170754Sdelphij
507170754Sdelphij	case 'l':
508170754Sdelphij	  diffarg ("--left-column");
509170754Sdelphij	  break;
510170754Sdelphij
511170754Sdelphij	case 'o':
512170754Sdelphij	  output = optarg;
513170754Sdelphij	  break;
514170754Sdelphij
515170754Sdelphij	case 's':
516170754Sdelphij	  suppress_common_lines = true;
517170754Sdelphij	  break;
518170754Sdelphij
519170754Sdelphij	case 't':
520170754Sdelphij	  diffarg ("-t");
521170754Sdelphij	  break;
522170754Sdelphij
523170754Sdelphij	case 'v':
524170754Sdelphij	  version_etc (stdout, "sdiff", PACKAGE_NAME, PACKAGE_VERSION,
525170754Sdelphij		       "Thomas Lord", (char *) 0);
526170754Sdelphij	  check_stdout ();
527170754Sdelphij	  return EXIT_SUCCESS;
528170754Sdelphij
529170754Sdelphij	case 'w':
530170754Sdelphij	  diffarg ("-W");
531170754Sdelphij	  diffarg (optarg);
532170754Sdelphij	  break;
533170754Sdelphij
534170754Sdelphij	case 'W':
535170754Sdelphij	  diffarg ("-w");
536170754Sdelphij	  break;
537170754Sdelphij
538170754Sdelphij	case DIFF_PROGRAM_OPTION:
539170754Sdelphij	  diffargv[0] = optarg;
540170754Sdelphij	  break;
541170754Sdelphij
542170754Sdelphij	case HELP_OPTION:
543170754Sdelphij	  usage ();
544170754Sdelphij	  check_stdout ();
545170754Sdelphij	  return EXIT_SUCCESS;
546170754Sdelphij
547170754Sdelphij	case STRIP_TRAILING_CR_OPTION:
548170754Sdelphij	  diffarg ("--strip-trailing-cr");
549170754Sdelphij	  break;
550170754Sdelphij
551170754Sdelphij	case TABSIZE_OPTION:
552170754Sdelphij	  diffarg ("--tabsize");
553170754Sdelphij	  diffarg (optarg);
554170754Sdelphij	  break;
555170754Sdelphij
556170754Sdelphij	default:
557170754Sdelphij	  try_help (0, 0);
558170754Sdelphij	}
559170754Sdelphij    }
560170754Sdelphij
561170754Sdelphij  if (argc - optind != 2)
562170754Sdelphij    {
563170754Sdelphij      if (argc - optind < 2)
564170754Sdelphij	try_help ("missing operand after `%s'", argv[argc - 1]);
565170754Sdelphij      else
566170754Sdelphij	try_help ("extra operand `%s'", argv[optind + 2]);
567170754Sdelphij    }
568170754Sdelphij
569170754Sdelphij  if (! output)
570170754Sdelphij    {
571170754Sdelphij      /* easy case: diff does everything for us */
572170754Sdelphij      if (suppress_common_lines)
573170754Sdelphij	diffarg ("--suppress-common-lines");
574170754Sdelphij      diffarg ("-y");
575170754Sdelphij      diffarg ("--");
576170754Sdelphij      diffarg (argv[optind]);
577170754Sdelphij      diffarg (argv[optind + 1]);
578170754Sdelphij      diffarg (0);
579170754Sdelphij      execvp (diffargv[0], (char **) diffargv);
580170754Sdelphij      perror_fatal (diffargv[0]);
581170754Sdelphij    }
582170754Sdelphij  else
583170754Sdelphij    {
584170754Sdelphij      char const *lname, *rname;
585170754Sdelphij      FILE *left, *right, *out, *diffout;
586170754Sdelphij      bool interact_ok;
587170754Sdelphij      struct line_filter lfilt;
588170754Sdelphij      struct line_filter rfilt;
589170754Sdelphij      struct line_filter diff_filt;
590170754Sdelphij      bool leftdir = diraccess (argv[optind]);
591170754Sdelphij      bool rightdir = diraccess (argv[optind + 1]);
592170754Sdelphij
593170754Sdelphij      if (leftdir & rightdir)
594170754Sdelphij	fatal ("both files to be compared are directories");
595170754Sdelphij
596170754Sdelphij      lname = expand_name (argv[optind], leftdir, argv[optind + 1]);
597170754Sdelphij      left = ck_fopen (lname, "r");
598170754Sdelphij      rname = expand_name (argv[optind + 1], rightdir, argv[optind]);
599170754Sdelphij      right = ck_fopen (rname, "r");
600170754Sdelphij      out = ck_fopen (output, "w");
601170754Sdelphij
602170754Sdelphij      diffarg ("--sdiff-merge-assist");
603170754Sdelphij      diffarg ("--");
604170754Sdelphij      diffarg (argv[optind]);
605170754Sdelphij      diffarg (argv[optind + 1]);
606170754Sdelphij      diffarg (0);
607170754Sdelphij
608170754Sdelphij      trapsigs ();
609170754Sdelphij
610170754Sdelphij#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
611170754Sdelphij      {
612170754Sdelphij	size_t cmdsize = 1;
613170754Sdelphij	char *p, *command;
614170754Sdelphij	int i;
615170754Sdelphij
616170754Sdelphij	for (i = 0;  diffargv[i];  i++)
617170754Sdelphij	  cmdsize += quote_system_arg (0, diffargv[i]) + 1;
618170754Sdelphij	command = p = xmalloc (cmdsize);
619170754Sdelphij	for (i = 0;  diffargv[i];  i++)
620170754Sdelphij	  {
621170754Sdelphij	    p += quote_system_arg (p, diffargv[i]);
622170754Sdelphij	    *p++ = ' ';
623170754Sdelphij	  }
624170754Sdelphij	p[-1] = 0;
625170754Sdelphij	errno = 0;
626170754Sdelphij	diffout = popen (command, "r");
627170754Sdelphij	if (! diffout)
628170754Sdelphij	  perror_fatal (command);
629170754Sdelphij	free (command);
630170754Sdelphij      }
631170754Sdelphij#else
632170754Sdelphij      {
633170754Sdelphij	int diff_fds[2];
634170754Sdelphij# if HAVE_WORKING_VFORK
635170754Sdelphij	sigset_t procmask;
636170754Sdelphij	sigset_t blocked;
637170754Sdelphij# endif
638170754Sdelphij
639170754Sdelphij	if (pipe (diff_fds) != 0)
640170754Sdelphij	  perror_fatal ("pipe");
641170754Sdelphij
642170754Sdelphij# if HAVE_WORKING_VFORK
643170754Sdelphij	/* Block SIGINT and SIGPIPE.  */
644170754Sdelphij	sigemptyset (&blocked);
645170754Sdelphij	sigaddset (&blocked, SIGINT);
646170754Sdelphij	sigaddset (&blocked, SIGPIPE);
647170754Sdelphij	sigprocmask (SIG_BLOCK, &blocked, &procmask);
648170754Sdelphij# endif
649170754Sdelphij	diffpid = vfork ();
650170754Sdelphij	if (diffpid < 0)
651170754Sdelphij	  perror_fatal ("fork");
652170754Sdelphij	if (! diffpid)
653170754Sdelphij	  {
654170754Sdelphij	    /* Alter the child's SIGINT and SIGPIPE handlers;
655170754Sdelphij	       this may munge the parent.
656170754Sdelphij	       The child ignores SIGINT in case the user interrupts the editor.
657170754Sdelphij	       The child does not ignore SIGPIPE, even if the parent does.  */
658170754Sdelphij	    if (initial_handler (handler_index_of_SIGINT) != SIG_IGN)
659170754Sdelphij	      signal_handler (SIGINT, SIG_IGN);
660170754Sdelphij	    signal_handler (SIGPIPE, SIG_DFL);
661170754Sdelphij# if HAVE_WORKING_VFORK
662170754Sdelphij	    /* Stop blocking SIGINT and SIGPIPE in the child.  */
663170754Sdelphij	    sigprocmask (SIG_SETMASK, &procmask, 0);
664170754Sdelphij# endif
665170754Sdelphij	    close (diff_fds[0]);
666170754Sdelphij	    if (diff_fds[1] != STDOUT_FILENO)
667170754Sdelphij	      {
668170754Sdelphij		dup2 (diff_fds[1], STDOUT_FILENO);
669170754Sdelphij		close (diff_fds[1]);
670170754Sdelphij	      }
671170754Sdelphij
672170754Sdelphij	    execvp (diffargv[0], (char **) diffargv);
673170754Sdelphij	    _exit (errno == ENOENT ? 127 : 126);
674170754Sdelphij	  }
675170754Sdelphij
676170754Sdelphij# if HAVE_WORKING_VFORK
677170754Sdelphij	/* Restore the parent's SIGINT and SIGPIPE behavior.  */
678170754Sdelphij	if (initial_handler (handler_index_of_SIGINT) != SIG_IGN)
679170754Sdelphij	  signal_handler (SIGINT, catchsig);
680170754Sdelphij	if (initial_handler (handler_index_of_SIGPIPE) != SIG_IGN)
681170754Sdelphij	  signal_handler (SIGPIPE, catchsig);
682170754Sdelphij	else
683170754Sdelphij	  signal_handler (SIGPIPE, SIG_IGN);
684170754Sdelphij
685170754Sdelphij	/* Stop blocking SIGINT and SIGPIPE in the parent.  */
686170754Sdelphij	sigprocmask (SIG_SETMASK, &procmask, 0);
687170754Sdelphij# endif
688170754Sdelphij
689170754Sdelphij	close (diff_fds[1]);
690170754Sdelphij	diffout = fdopen (diff_fds[0], "r");
691170754Sdelphij	if (! diffout)
692170754Sdelphij	  perror_fatal ("fdopen");
693170754Sdelphij      }
694170754Sdelphij#endif
695170754Sdelphij
696170754Sdelphij      lf_init (&diff_filt, diffout);
697170754Sdelphij      lf_init (&lfilt, left);
698170754Sdelphij      lf_init (&rfilt, right);
699170754Sdelphij
700170754Sdelphij      interact_ok = interact (&diff_filt, &lfilt, lname, &rfilt, rname, out);
701170754Sdelphij
702170754Sdelphij      ck_fclose (left);
703170754Sdelphij      ck_fclose (right);
704170754Sdelphij      ck_fclose (out);
705170754Sdelphij
706170754Sdelphij      {
707170754Sdelphij	int wstatus;
708170754Sdelphij	int werrno = 0;
709170754Sdelphij
710170754Sdelphij#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
711170754Sdelphij	wstatus = pclose (diffout);
712170754Sdelphij	if (wstatus == -1)
713170754Sdelphij	  werrno = errno;
714170754Sdelphij#else
715170754Sdelphij	ck_fclose (diffout);
716170754Sdelphij	while (waitpid (diffpid, &wstatus, 0) < 0)
717170754Sdelphij	  if (errno == EINTR)
718170754Sdelphij	    checksigs ();
719170754Sdelphij	  else
720170754Sdelphij	    perror_fatal ("waitpid");
721170754Sdelphij	diffpid = 0;
722170754Sdelphij#endif
723170754Sdelphij
724170754Sdelphij	if (tmpname)
725170754Sdelphij	  {
726170754Sdelphij	    unlink (tmpname);
727170754Sdelphij	    tmpname = 0;
728170754Sdelphij	  }
729170754Sdelphij
730170754Sdelphij	if (! interact_ok)
731170754Sdelphij	  exiterr ();
732170754Sdelphij
733170754Sdelphij	check_child_status (werrno, wstatus, EXIT_FAILURE, diffargv[0]);
734170754Sdelphij	untrapsig (0);
735170754Sdelphij	checksigs ();
736170754Sdelphij	exit (WEXITSTATUS (wstatus));
737170754Sdelphij      }
738170754Sdelphij    }
739170754Sdelphij  return EXIT_SUCCESS;			/* Fool `-Wall'.  */
740170754Sdelphij}
741170754Sdelphij
742170754Sdelphijstatic void
743170754Sdelphijdiffarg (char const *a)
744170754Sdelphij{
745170754Sdelphij  static size_t diffargs, diffarglim;
746170754Sdelphij
747170754Sdelphij  if (diffargs == diffarglim)
748170754Sdelphij    {
749170754Sdelphij      if (! diffarglim)
750170754Sdelphij	diffarglim = 16;
751170754Sdelphij      else if (PTRDIFF_MAX / (2 * sizeof *diffargv) <= diffarglim)
752170754Sdelphij	xalloc_die ();
753170754Sdelphij      else
754170754Sdelphij	diffarglim *= 2;
755170754Sdelphij      diffargv = xrealloc (diffargv, diffarglim * sizeof *diffargv);
756170754Sdelphij    }
757170754Sdelphij  diffargv[diffargs++] = a;
758170754Sdelphij}
759170754Sdelphij
760170754Sdelphij/* Signal handling */
761170754Sdelphij
762170754Sdelphijstatic bool volatile ignore_SIGINT;
763170754Sdelphijstatic int volatile signal_received;
764170754Sdelphijstatic bool sigs_trapped;
765170754Sdelphij
766170754Sdelphijstatic void
767170754Sdelphijcatchsig (int s)
768170754Sdelphij{
769170754Sdelphij#if ! HAVE_SIGACTION
770170754Sdelphij  signal (s, SIG_IGN);
771170754Sdelphij#endif
772170754Sdelphij  if (! (s == SIGINT && ignore_SIGINT))
773170754Sdelphij    signal_received = s;
774170754Sdelphij}
775170754Sdelphij
776170754Sdelphij#if HAVE_SIGACTION
777170754Sdelphijstatic struct sigaction catchaction;
778170754Sdelphij
779170754Sdelphijstatic void
780170754Sdelphijsignal_handler (int sig, void (*handler) (int))
781170754Sdelphij{
782170754Sdelphij  catchaction.sa_handler = handler;
783170754Sdelphij  sigaction (sig, &catchaction, 0);
784170754Sdelphij}
785170754Sdelphij#endif
786170754Sdelphij
787170754Sdelphijstatic void
788170754Sdelphijtrapsigs (void)
789170754Sdelphij{
790170754Sdelphij  int i;
791170754Sdelphij
792170754Sdelphij#if HAVE_SIGACTION
793170754Sdelphij  catchaction.sa_flags = SA_RESTART;
794170754Sdelphij  sigemptyset (&catchaction.sa_mask);
795170754Sdelphij  for (i = 0;  i < NUM_SIGS;  i++)
796170754Sdelphij    sigaddset (&catchaction.sa_mask, sigs[i]);
797170754Sdelphij#endif
798170754Sdelphij
799170754Sdelphij  for (i = 0;  i < NUM_SIGS;  i++)
800170754Sdelphij    {
801170754Sdelphij#if HAVE_SIGACTION
802170754Sdelphij      sigaction (sigs[i], 0, &initial_action[i]);
803170754Sdelphij#else
804170754Sdelphij      initial_action[i] = signal (sigs[i], SIG_IGN);
805170754Sdelphij#endif
806170754Sdelphij      if (initial_handler (i) != SIG_IGN)
807170754Sdelphij	signal_handler (sigs[i], catchsig);
808170754Sdelphij    }
809170754Sdelphij
810170754Sdelphij#ifdef SIGCHLD
811170754Sdelphij  /* System V fork+wait does not work if SIGCHLD is ignored.  */
812170754Sdelphij  signal (SIGCHLD, SIG_DFL);
813170754Sdelphij#endif
814170754Sdelphij
815170754Sdelphij  sigs_trapped = true;
816170754Sdelphij}
817170754Sdelphij
818170754Sdelphij/* Untrap signal S, or all trapped signals if S is zero.  */
819170754Sdelphijstatic void
820170754Sdelphijuntrapsig (int s)
821170754Sdelphij{
822170754Sdelphij  int i;
823170754Sdelphij
824170754Sdelphij  if (sigs_trapped)
825170754Sdelphij    for (i = 0;  i < NUM_SIGS;  i++)
826170754Sdelphij      if ((! s || sigs[i] == s)  &&  initial_handler (i) != SIG_IGN)
827170754Sdelphij	{
828170754Sdelphij#if HAVE_SIGACTION
829170754Sdelphij	  sigaction (sigs[i], &initial_action[i], 0);
830170754Sdelphij#else
831170754Sdelphij	  signal (sigs[i], initial_action[i]);
832170754Sdelphij#endif
833170754Sdelphij	}
834170754Sdelphij}
835170754Sdelphij
836170754Sdelphij/* Exit if a signal has been received.  */
837170754Sdelphijstatic void
838170754Sdelphijchecksigs (void)
839170754Sdelphij{
840170754Sdelphij  int s = signal_received;
841170754Sdelphij  if (s)
842170754Sdelphij    {
843170754Sdelphij      cleanup (0);
844170754Sdelphij
845170754Sdelphij      /* Yield an exit status indicating that a signal was received.  */
846170754Sdelphij      untrapsig (s);
847170754Sdelphij      kill (getpid (), s);
848170754Sdelphij
849170754Sdelphij      /* That didn't work, so exit with error status.  */
850170754Sdelphij      exit (EXIT_TROUBLE);
851170754Sdelphij    }
852170754Sdelphij}
853170754Sdelphij
854170754Sdelphijstatic void
855170754Sdelphijgive_help (void)
856170754Sdelphij{
857170754Sdelphij  fprintf (stderr, "%s", _("\
858170754Sdelphijed:\tEdit then use both versions, each decorated with a header.\n\
859170754Sdelphijeb:\tEdit then use both versions.\n\
860170764Sdelphijel or e1:\tEdit then use the left version.\n\
861170764Sdelphijer or e2:\tEdit then use the right version.\n\
862170764Sdelphije:\tDiscard both versions then edit a new one.\n\
863170764Sdelphijl or 1:\tUse the left version.\n\
864170764Sdelphijr or 2:\tUse the right version.\n\
865170754Sdelphijs:\tSilently include common lines.\n\
866170754Sdelphijv:\tVerbosely include common lines.\n\
867170754Sdelphijq:\tQuit.\n\
868170754Sdelphij"));
869170754Sdelphij}
870170754Sdelphij
871170754Sdelphijstatic int
872170754Sdelphijskip_white (void)
873170754Sdelphij{
874170754Sdelphij  int c;
875170754Sdelphij  for (;;)
876170754Sdelphij    {
877170754Sdelphij      c = getchar ();
878170754Sdelphij      if (! isspace (c) || c == '\n')
879170754Sdelphij	break;
880170754Sdelphij      checksigs ();
881170754Sdelphij    }
882170754Sdelphij  if (ferror (stdin))
883170754Sdelphij    perror_fatal (_("read failed"));
884170754Sdelphij  return c;
885170754Sdelphij}
886170754Sdelphij
887170754Sdelphijstatic void
888170754Sdelphijflush_line (void)
889170754Sdelphij{
890170754Sdelphij  int c;
891170754Sdelphij  while ((c = getchar ()) != '\n' && c != EOF)
892170754Sdelphij    continue;
893170754Sdelphij  if (ferror (stdin))
894170754Sdelphij    perror_fatal (_("read failed"));
895170754Sdelphij}
896170754Sdelphij
897170754Sdelphij
898170754Sdelphij/* interpret an edit command */
899170754Sdelphijstatic bool
900170754Sdelphijedit (struct line_filter *left, char const *lname, lin lline, lin llen,
901170754Sdelphij      struct line_filter *right, char const *rname, lin rline, lin rlen,
902170754Sdelphij      FILE *outfile)
903170754Sdelphij{
904170754Sdelphij  for (;;)
905170754Sdelphij    {
906170754Sdelphij      int cmd0, cmd1;
907170754Sdelphij      bool gotcmd = false;
908170754Sdelphij
909170754Sdelphij      cmd1 = 0; /* Pacify `gcc -W'.  */
910170754Sdelphij
911170754Sdelphij      while (! gotcmd)
912170754Sdelphij	{
913170754Sdelphij	  if (putchar ('%') != '%')
914170754Sdelphij	    perror_fatal (_("write failed"));
915170754Sdelphij	  ck_fflush (stdout);
916170754Sdelphij
917170754Sdelphij	  cmd0 = skip_white ();
918170754Sdelphij	  switch (cmd0)
919170754Sdelphij	    {
920170764Sdelphij	    case '1': case '2': case 'l': case 'r':
921170764Sdelphij	    case 's': case 'v': case 'q':
922170754Sdelphij	      if (skip_white () != '\n')
923170754Sdelphij		{
924170754Sdelphij		  give_help ();
925170754Sdelphij		  flush_line ();
926170754Sdelphij		  continue;
927170754Sdelphij		}
928170754Sdelphij	      gotcmd = true;
929170754Sdelphij	      break;
930170754Sdelphij
931170754Sdelphij	    case 'e':
932170754Sdelphij	      cmd1 = skip_white ();
933170754Sdelphij	      switch (cmd1)
934170754Sdelphij		{
935170764Sdelphij		case '1': case '2': case 'b': case 'd': case 'l': case 'r':
936170754Sdelphij		  if (skip_white () != '\n')
937170754Sdelphij		    {
938170754Sdelphij		      give_help ();
939170754Sdelphij		      flush_line ();
940170754Sdelphij		      continue;
941170754Sdelphij		    }
942170754Sdelphij		  gotcmd = true;
943170754Sdelphij		  break;
944170754Sdelphij		case '\n':
945170754Sdelphij		  gotcmd = true;
946170754Sdelphij		  break;
947170754Sdelphij		default:
948170754Sdelphij		  give_help ();
949170754Sdelphij		  flush_line ();
950170754Sdelphij		  continue;
951170754Sdelphij		}
952170754Sdelphij	      break;
953170754Sdelphij
954170754Sdelphij	    case EOF:
955170754Sdelphij	      if (feof (stdin))
956170754Sdelphij		{
957170754Sdelphij		  gotcmd = true;
958170754Sdelphij		  cmd0 = 'q';
959170754Sdelphij		  break;
960170754Sdelphij		}
961170754Sdelphij	      /* Fall through.  */
962170754Sdelphij	    default:
963170754Sdelphij	      flush_line ();
964170754Sdelphij	      /* Fall through.  */
965170754Sdelphij	    case '\n':
966170754Sdelphij	      give_help ();
967170754Sdelphij	      continue;
968170754Sdelphij	    }
969170754Sdelphij	}
970170754Sdelphij
971170754Sdelphij      switch (cmd0)
972170754Sdelphij	{
973170764Sdelphij	case '1': case 'l':
974170754Sdelphij	  lf_copy (left, llen, outfile);
975170754Sdelphij	  lf_skip (right, rlen);
976170754Sdelphij	  return true;
977170764Sdelphij	case '2': case 'r':
978170754Sdelphij	  lf_copy (right, rlen, outfile);
979170754Sdelphij	  lf_skip (left, llen);
980170754Sdelphij	  return true;
981170754Sdelphij	case 's':
982170754Sdelphij	  suppress_common_lines = true;
983170754Sdelphij	  break;
984170754Sdelphij	case 'v':
985170754Sdelphij	  suppress_common_lines = false;
986170754Sdelphij	  break;
987170754Sdelphij	case 'q':
988170754Sdelphij	  return false;
989170754Sdelphij	case 'e':
990170754Sdelphij	  {
991170754Sdelphij	    int fd;
992170754Sdelphij
993170754Sdelphij	    if (tmpname)
994170754Sdelphij	      tmp = fopen (tmpname, "w");
995170754Sdelphij	    else
996170754Sdelphij	      {
997170754Sdelphij		if ((fd = temporary_file ()) < 0)
998170754Sdelphij		  perror_fatal ("mkstemp");
999170754Sdelphij		tmp = fdopen (fd, "w");
1000170754Sdelphij	      }
1001170754Sdelphij
1002170754Sdelphij	    if (! tmp)
1003170754Sdelphij	      perror_fatal (tmpname);
1004170754Sdelphij
1005170754Sdelphij	    switch (cmd1)
1006170754Sdelphij	      {
1007170754Sdelphij	      case 'd':
1008170754Sdelphij		if (llen)
1009170754Sdelphij		  {
1010170754Sdelphij		    if (llen == 1)
1011170754Sdelphij		      fprintf (tmp, "--- %s %ld\n", lname, (long int) lline);
1012170754Sdelphij		    else
1013170754Sdelphij		      fprintf (tmp, "--- %s %ld,%ld\n", lname,
1014170754Sdelphij			       (long int) lline,
1015170754Sdelphij			       (long int) (lline + llen - 1));
1016170754Sdelphij		  }
1017170754Sdelphij		/* Fall through.  */
1018170764Sdelphij	      case '1': case 'b': case 'l':
1019170754Sdelphij		lf_copy (left, llen, tmp);
1020170754Sdelphij		break;
1021170754Sdelphij
1022170754Sdelphij	      default:
1023170754Sdelphij		lf_skip (left, llen);
1024170754Sdelphij		break;
1025170754Sdelphij	      }
1026170754Sdelphij
1027170754Sdelphij	    switch (cmd1)
1028170754Sdelphij	      {
1029170754Sdelphij	      case 'd':
1030170754Sdelphij		if (rlen)
1031170754Sdelphij		  {
1032170754Sdelphij		    if (rlen == 1)
1033170754Sdelphij		      fprintf (tmp, "+++ %s %ld\n", rname, (long int) rline);
1034170754Sdelphij		    else
1035170754Sdelphij		      fprintf (tmp, "+++ %s %ld,%ld\n", rname,
1036170754Sdelphij			       (long int) rline,
1037170754Sdelphij			       (long int) (rline + rlen - 1));
1038170754Sdelphij		  }
1039170754Sdelphij		/* Fall through.  */
1040170764Sdelphij	      case '2': case 'b': case 'r':
1041170754Sdelphij		lf_copy (right, rlen, tmp);
1042170754Sdelphij		break;
1043170754Sdelphij
1044170754Sdelphij	      default:
1045170754Sdelphij		lf_skip (right, rlen);
1046170754Sdelphij		break;
1047170754Sdelphij	      }
1048170754Sdelphij
1049170754Sdelphij	    ck_fclose (tmp);
1050170754Sdelphij
1051170754Sdelphij	    {
1052170754Sdelphij	      int wstatus;
1053170754Sdelphij	      int werrno = 0;
1054170754Sdelphij	      ignore_SIGINT = true;
1055170754Sdelphij	      checksigs ();
1056170754Sdelphij
1057170754Sdelphij	      {
1058170754Sdelphij#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
1059170754Sdelphij		char *command =
1060170754Sdelphij		  xmalloc (quote_system_arg (0, editor_program)
1061170754Sdelphij			   + 1 + strlen (tmpname) + 1);
1062170754Sdelphij		sprintf (command + quote_system_arg (command, editor_program),
1063170754Sdelphij			 " %s", tmpname);
1064170754Sdelphij		wstatus = system (command);
1065170754Sdelphij		if (wstatus == -1)
1066170754Sdelphij		  werrno = errno;
1067170754Sdelphij		free (command);
1068170754Sdelphij#else
1069170754Sdelphij		pid_t pid;
1070170754Sdelphij
1071170754Sdelphij		pid = vfork ();
1072170754Sdelphij		if (pid == 0)
1073170754Sdelphij		  {
1074170754Sdelphij		    char const *argv[3];
1075170754Sdelphij		    int i = 0;
1076170754Sdelphij
1077170754Sdelphij		    argv[i++] = editor_program;
1078170754Sdelphij		    argv[i++] = tmpname;
1079170754Sdelphij		    argv[i] = 0;
1080170754Sdelphij
1081170754Sdelphij		    execvp (editor_program, (char **) argv);
1082170754Sdelphij		    _exit (errno == ENOENT ? 127 : 126);
1083170754Sdelphij		  }
1084170754Sdelphij
1085170754Sdelphij		if (pid < 0)
1086170754Sdelphij		  perror_fatal ("fork");
1087170754Sdelphij
1088170754Sdelphij		while (waitpid (pid, &wstatus, 0) < 0)
1089170754Sdelphij		  if (errno == EINTR)
1090170754Sdelphij		    checksigs ();
1091170754Sdelphij		  else
1092170754Sdelphij		    perror_fatal ("waitpid");
1093170754Sdelphij#endif
1094170754Sdelphij	      }
1095170754Sdelphij
1096170754Sdelphij	      ignore_SIGINT = false;
1097170754Sdelphij	      check_child_status (werrno, wstatus, EXIT_SUCCESS,
1098170754Sdelphij				  editor_program);
1099170754Sdelphij	    }
1100170754Sdelphij
1101170754Sdelphij	    {
1102170754Sdelphij	      char buf[SDIFF_BUFSIZE];
1103170754Sdelphij	      size_t size;
1104170754Sdelphij	      tmp = ck_fopen (tmpname, "r");
1105170754Sdelphij	      while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
1106170754Sdelphij		{
1107170754Sdelphij		  checksigs ();
1108170754Sdelphij		  ck_fwrite (buf, size, outfile);
1109170754Sdelphij		}
1110170754Sdelphij	      ck_fclose (tmp);
1111170754Sdelphij	    }
1112170754Sdelphij	    return true;
1113170754Sdelphij	  }
1114170754Sdelphij	default:
1115170754Sdelphij	  give_help ();
1116170754Sdelphij	  break;
1117170754Sdelphij	}
1118170754Sdelphij    }
1119170754Sdelphij}
1120170754Sdelphij
1121170754Sdelphij/* Alternately reveal bursts of diff output and handle user commands.  */
1122170754Sdelphijstatic bool
1123170754Sdelphijinteract (struct line_filter *diff,
1124170754Sdelphij	  struct line_filter *left, char const *lname,
1125170754Sdelphij	  struct line_filter *right, char const *rname,
1126170754Sdelphij	  FILE *outfile)
1127170754Sdelphij{
1128170754Sdelphij  lin lline = 1, rline = 1;
1129170754Sdelphij
1130170754Sdelphij  for (;;)
1131170754Sdelphij    {
1132170754Sdelphij      char diff_help[256];
1133170754Sdelphij      int snarfed = lf_snarf (diff, diff_help, sizeof diff_help);
1134170754Sdelphij
1135170754Sdelphij      if (snarfed <= 0)
1136170754Sdelphij	return snarfed != 0;
1137170754Sdelphij
1138170754Sdelphij      checksigs ();
1139170754Sdelphij
1140170754Sdelphij      if (diff_help[0] == ' ')
1141170754Sdelphij	puts (diff_help + 1);
1142170754Sdelphij      else
1143170754Sdelphij	{
1144170754Sdelphij	  char *numend;
1145170754Sdelphij	  uintmax_t val;
1146170754Sdelphij	  lin llen, rlen, lenmax;
1147170754Sdelphij	  errno = 0;
1148170754Sdelphij	  llen = val = strtoumax (diff_help + 1, &numend, 10);
1149170754Sdelphij	  if (llen < 0 || llen != val || errno || *numend != ',')
1150170754Sdelphij	    fatal (diff_help);
1151170754Sdelphij	  rlen = val = strtoumax (numend + 1, &numend, 10);
1152170754Sdelphij	  if (rlen < 0 || rlen != val || errno || *numend)
1153170754Sdelphij	    fatal (diff_help);
1154170754Sdelphij
1155170754Sdelphij	  lenmax = MAX (llen, rlen);
1156170754Sdelphij
1157170754Sdelphij	  switch (diff_help[0])
1158170754Sdelphij	    {
1159170754Sdelphij	    case 'i':
1160170754Sdelphij	      if (suppress_common_lines)
1161170754Sdelphij		lf_skip (diff, lenmax);
1162170754Sdelphij	      else
1163170754Sdelphij		lf_copy (diff, lenmax, stdout);
1164170754Sdelphij
1165170754Sdelphij	      lf_copy (left, llen, outfile);
1166170754Sdelphij	      lf_skip (right, rlen);
1167170754Sdelphij	      break;
1168170754Sdelphij
1169170754Sdelphij	    case 'c':
1170170754Sdelphij	      lf_copy (diff, lenmax, stdout);
1171170754Sdelphij	      if (! edit (left, lname, lline, llen,
1172170754Sdelphij			  right, rname, rline, rlen,
1173170754Sdelphij			  outfile))
1174170754Sdelphij		return false;
1175170754Sdelphij	      break;
1176170754Sdelphij
1177170754Sdelphij	    default:
1178170754Sdelphij	      fatal (diff_help);
1179170754Sdelphij	    }
1180170754Sdelphij
1181170754Sdelphij	  lline += llen;
1182170754Sdelphij	  rline += rlen;
1183170754Sdelphij	}
1184170754Sdelphij    }
1185170754Sdelphij}
1186170754Sdelphij
1187170754Sdelphij/* Return true if DIR is an existing directory.  */
1188170754Sdelphijstatic bool
1189170754Sdelphijdiraccess (char const *dir)
1190170754Sdelphij{
1191170754Sdelphij  struct stat buf;
1192170754Sdelphij  return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
1193170754Sdelphij}
1194170754Sdelphij
1195170754Sdelphij#ifndef P_tmpdir
1196170754Sdelphij# define P_tmpdir "/tmp"
1197170754Sdelphij#endif
1198170754Sdelphij#ifndef TMPDIR_ENV
1199170754Sdelphij# define TMPDIR_ENV "TMPDIR"
1200170754Sdelphij#endif
1201170754Sdelphij
1202170754Sdelphij/* Open a temporary file and return its file descriptor.  Put into
1203170754Sdelphij   tmpname the address of a newly allocated buffer that holds the
1204170754Sdelphij   file's name.  Use the prefix "sdiff".  */
1205170754Sdelphijstatic int
1206170754Sdelphijtemporary_file (void)
1207170754Sdelphij{
1208170754Sdelphij  char const *tmpdir = getenv (TMPDIR_ENV);
1209170754Sdelphij  char const *dir = tmpdir ? tmpdir : P_tmpdir;
1210170754Sdelphij  char *buf = xmalloc (strlen (dir) + 1 + 5 + 6 + 1);
1211170754Sdelphij  int fd;
1212170754Sdelphij  int e;
1213170754Sdelphij  sigset_t procmask;
1214170754Sdelphij  sigset_t blocked;
1215170754Sdelphij  sprintf (buf, "%s/sdiffXXXXXX", dir);
1216170754Sdelphij  sigemptyset (&blocked);
1217170754Sdelphij  sigaddset (&blocked, SIGINT);
1218170754Sdelphij  sigprocmask (SIG_BLOCK, &blocked, &procmask);
1219170754Sdelphij  fd = mkstemp (buf);
1220170754Sdelphij  e = errno;
1221170754Sdelphij  if (0 <= fd)
1222170754Sdelphij    tmpname = buf;
1223170754Sdelphij  sigprocmask (SIG_SETMASK, &procmask, 0);
1224170754Sdelphij  errno = e;
1225170754Sdelphij  return fd;
1226170754Sdelphij}
1227