xref: /freebsd/contrib/diff/src/sdiff.c (revision db062bd2e6fda34503bf8adb3170eb192495ed96)
118fd37a7SXin LI /* sdiff - side-by-side merge of file differences
218fd37a7SXin LI 
318fd37a7SXin LI    Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 2001, 2002, 2004
418fd37a7SXin LI    Free Software Foundation, Inc.
518fd37a7SXin LI 
618fd37a7SXin LI    This file is part of GNU DIFF.
718fd37a7SXin LI 
818fd37a7SXin LI    GNU DIFF is free software; you can redistribute it and/or modify
918fd37a7SXin LI    it under the terms of the GNU General Public License as published by
1018fd37a7SXin LI    the Free Software Foundation; either version 2, or (at your option)
1118fd37a7SXin LI    any later version.
1218fd37a7SXin LI 
1318fd37a7SXin LI    GNU DIFF is distributed in the hope that it will be useful,
1418fd37a7SXin LI    but WITHOUT ANY WARRANTY; without even the implied warranty of
1518fd37a7SXin LI    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1618fd37a7SXin LI    See the GNU General Public License for more details.
1718fd37a7SXin LI 
1818fd37a7SXin LI    You should have received a copy of the GNU General Public License
1918fd37a7SXin LI    along with this program; see the file COPYING.
2018fd37a7SXin LI    If not, write to the Free Software Foundation,
2118fd37a7SXin LI    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
2218fd37a7SXin LI 
2318fd37a7SXin LI #include "system.h"
2418fd37a7SXin LI #include "paths.h"
2518fd37a7SXin LI 
2618fd37a7SXin LI #include <stdio.h>
2718fd37a7SXin LI #include <unlocked-io.h>
2818fd37a7SXin LI 
2918fd37a7SXin LI #include <c-stack.h>
3018fd37a7SXin LI #include <dirname.h>
3118fd37a7SXin LI #include <error.h>
3218fd37a7SXin LI #include <exit.h>
3318fd37a7SXin LI #include <exitfail.h>
3418fd37a7SXin LI #include <file-type.h>
3518fd37a7SXin LI #include <getopt.h>
3618fd37a7SXin LI #include <quotesys.h>
3718fd37a7SXin LI #include <version-etc.h>
3818fd37a7SXin LI #include <xalloc.h>
3918fd37a7SXin LI 
4018fd37a7SXin LI /* Size of chunks read from files which must be parsed into lines.  */
4118fd37a7SXin LI #define SDIFF_BUFSIZE ((size_t) 65536)
4218fd37a7SXin LI 
4318fd37a7SXin LI char *program_name;
4418fd37a7SXin LI 
4518fd37a7SXin LI static char const *editor_program = DEFAULT_EDITOR_PROGRAM;
4618fd37a7SXin LI static char const **diffargv;
4718fd37a7SXin LI 
4818fd37a7SXin LI static char * volatile tmpname;
4918fd37a7SXin LI static FILE *tmp;
5018fd37a7SXin LI 
5118fd37a7SXin LI #if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
5218fd37a7SXin LI static pid_t volatile diffpid;
5318fd37a7SXin LI #endif
5418fd37a7SXin LI 
5518fd37a7SXin LI struct line_filter;
5618fd37a7SXin LI 
5718fd37a7SXin LI static void catchsig (int);
5818fd37a7SXin LI static bool edit (struct line_filter *, char const *, lin, lin, struct line_filter *, char const *, lin, lin, FILE *);
5918fd37a7SXin LI static bool interact (struct line_filter *, struct line_filter *, char const *, struct line_filter *, char const *, FILE *);
6018fd37a7SXin LI static void checksigs (void);
6118fd37a7SXin LI static void diffarg (char const *);
6218fd37a7SXin LI static void fatal (char const *) __attribute__((noreturn));
6318fd37a7SXin LI static void perror_fatal (char const *) __attribute__((noreturn));
6418fd37a7SXin LI static void trapsigs (void);
6518fd37a7SXin LI static void untrapsig (int);
6618fd37a7SXin LI 
6718fd37a7SXin LI #define NUM_SIGS (sizeof sigs / sizeof *sigs)
6818fd37a7SXin LI static int const sigs[] = {
6918fd37a7SXin LI #ifdef SIGHUP
7018fd37a7SXin LI        SIGHUP,
7118fd37a7SXin LI #endif
7218fd37a7SXin LI #ifdef SIGQUIT
7318fd37a7SXin LI        SIGQUIT,
7418fd37a7SXin LI #endif
7518fd37a7SXin LI #ifdef SIGTERM
7618fd37a7SXin LI        SIGTERM,
7718fd37a7SXin LI #endif
7818fd37a7SXin LI #ifdef SIGXCPU
7918fd37a7SXin LI        SIGXCPU,
8018fd37a7SXin LI #endif
8118fd37a7SXin LI #ifdef SIGXFSZ
8218fd37a7SXin LI        SIGXFSZ,
8318fd37a7SXin LI #endif
8418fd37a7SXin LI        SIGINT,
8518fd37a7SXin LI        SIGPIPE
8618fd37a7SXin LI };
8718fd37a7SXin LI #define handler_index_of_SIGINT (NUM_SIGS - 2)
8818fd37a7SXin LI #define handler_index_of_SIGPIPE (NUM_SIGS - 1)
8918fd37a7SXin LI 
9018fd37a7SXin LI #if HAVE_SIGACTION
9118fd37a7SXin LI   /* Prefer `sigaction' if available, since `signal' can lose signals.  */
9218fd37a7SXin LI   static struct sigaction initial_action[NUM_SIGS];
9318fd37a7SXin LI # define initial_handler(i) (initial_action[i].sa_handler)
9418fd37a7SXin LI   static void signal_handler (int, void (*) (int));
9518fd37a7SXin LI #else
9618fd37a7SXin LI   static void (*initial_action[NUM_SIGS]) ();
9718fd37a7SXin LI # define initial_handler(i) (initial_action[i])
9818fd37a7SXin LI # define signal_handler(sig, handler) signal (sig, handler)
9918fd37a7SXin LI #endif
10018fd37a7SXin LI 
10118fd37a7SXin LI #if ! HAVE_SIGPROCMASK
10218fd37a7SXin LI # define sigset_t int
10318fd37a7SXin LI # define sigemptyset(s) (*(s) = 0)
10418fd37a7SXin LI # ifndef sigmask
10518fd37a7SXin LI #  define sigmask(sig) (1 << ((sig) - 1))
10618fd37a7SXin LI # endif
10718fd37a7SXin LI # define sigaddset(s, sig) (*(s) |= sigmask (sig))
10818fd37a7SXin LI # ifndef SIG_BLOCK
10918fd37a7SXin LI #  define SIG_BLOCK 0
11018fd37a7SXin LI # endif
11118fd37a7SXin LI # ifndef SIG_SETMASK
11218fd37a7SXin LI #  define SIG_SETMASK (! SIG_BLOCK)
11318fd37a7SXin LI # endif
11418fd37a7SXin LI # define sigprocmask(how, n, o) \
11518fd37a7SXin LI     ((how) == SIG_BLOCK ? *(o) = sigblock (*(n)) : sigsetmask (*(n)))
11618fd37a7SXin LI #endif
11718fd37a7SXin LI 
11818fd37a7SXin LI static bool diraccess (char const *);
11918fd37a7SXin LI static int temporary_file (void);
12018fd37a7SXin LI 
12118fd37a7SXin LI /* Options: */
12218fd37a7SXin LI 
12318fd37a7SXin LI /* Name of output file if -o specified.  */
12418fd37a7SXin LI static char const *output;
12518fd37a7SXin LI 
12618fd37a7SXin LI /* Do not print common lines.  */
12718fd37a7SXin LI static bool suppress_common_lines;
12818fd37a7SXin LI 
12918fd37a7SXin LI /* Value for the long option that does not have single-letter equivalents.  */
13018fd37a7SXin LI enum
13118fd37a7SXin LI {
13218fd37a7SXin LI   DIFF_PROGRAM_OPTION = CHAR_MAX + 1,
13318fd37a7SXin LI   HELP_OPTION,
13418fd37a7SXin LI   STRIP_TRAILING_CR_OPTION,
13518fd37a7SXin LI   TABSIZE_OPTION
13618fd37a7SXin LI };
13718fd37a7SXin LI 
13818fd37a7SXin LI static struct option const longopts[] =
13918fd37a7SXin LI {
14018fd37a7SXin LI   {"diff-program", 1, 0, DIFF_PROGRAM_OPTION},
14118fd37a7SXin LI   {"expand-tabs", 0, 0, 't'},
14218fd37a7SXin LI   {"help", 0, 0, HELP_OPTION},
14318fd37a7SXin LI   {"ignore-all-space", 0, 0, 'W'}, /* swap W and w for historical reasons */
14418fd37a7SXin LI   {"ignore-blank-lines", 0, 0, 'B'},
14518fd37a7SXin LI   {"ignore-case", 0, 0, 'i'},
14618fd37a7SXin LI   {"ignore-matching-lines", 1, 0, 'I'},
14718fd37a7SXin LI   {"ignore-space-change", 0, 0, 'b'},
14818fd37a7SXin LI   {"ignore-tab-expansion", 0, 0, 'E'},
14918fd37a7SXin LI   {"left-column", 0, 0, 'l'},
15018fd37a7SXin LI   {"minimal", 0, 0, 'd'},
15118fd37a7SXin LI   {"output", 1, 0, 'o'},
15218fd37a7SXin LI   {"speed-large-files", 0, 0, 'H'},
15318fd37a7SXin LI   {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
15418fd37a7SXin LI   {"suppress-common-lines", 0, 0, 's'},
15518fd37a7SXin LI   {"tabsize", 1, 0, TABSIZE_OPTION},
15618fd37a7SXin LI   {"text", 0, 0, 'a'},
15718fd37a7SXin LI   {"version", 0, 0, 'v'},
15818fd37a7SXin LI   {"width", 1, 0, 'w'},
15918fd37a7SXin LI   {0, 0, 0, 0}
16018fd37a7SXin LI };
16118fd37a7SXin LI 
16218fd37a7SXin LI static void try_help (char const *, char const *) __attribute__((noreturn));
16318fd37a7SXin LI static void
try_help(char const * reason_msgid,char const * operand)16418fd37a7SXin LI try_help (char const *reason_msgid, char const *operand)
16518fd37a7SXin LI {
16618fd37a7SXin LI   if (reason_msgid)
16718fd37a7SXin LI     error (0, 0, _(reason_msgid), operand);
16818fd37a7SXin LI   error (EXIT_TROUBLE, 0, _("Try `%s --help' for more information."),
16918fd37a7SXin LI 	 program_name);
17018fd37a7SXin LI   abort ();
17118fd37a7SXin LI }
17218fd37a7SXin LI 
17318fd37a7SXin LI static void
check_stdout(void)17418fd37a7SXin LI check_stdout (void)
17518fd37a7SXin LI {
17618fd37a7SXin LI   if (ferror (stdout))
17718fd37a7SXin LI     fatal ("write failed");
17818fd37a7SXin LI   else if (fclose (stdout) != 0)
17918fd37a7SXin LI     perror_fatal (_("standard output"));
18018fd37a7SXin LI }
18118fd37a7SXin LI 
18218fd37a7SXin LI static char const * const option_help_msgid[] = {
18318fd37a7SXin LI   N_("-o FILE  --output=FILE  Operate interactively, sending output to FILE."),
18418fd37a7SXin LI   "",
18518fd37a7SXin LI   N_("-i  --ignore-case  Consider upper- and lower-case to be the same."),
18618fd37a7SXin LI   N_("-E  --ignore-tab-expansion  Ignore changes due to tab expansion."),
18718fd37a7SXin LI   N_("-b  --ignore-space-change  Ignore changes in the amount of white space."),
18818fd37a7SXin LI   N_("-W  --ignore-all-space  Ignore all white space."),
18918fd37a7SXin LI   N_("-B  --ignore-blank-lines  Ignore changes whose lines are all blank."),
19018fd37a7SXin LI   N_("-I RE  --ignore-matching-lines=RE  Ignore changes whose lines all match RE."),
19118fd37a7SXin LI   N_("--strip-trailing-cr  Strip trailing carriage return on input."),
19218fd37a7SXin LI   N_("-a  --text  Treat all files as text."),
19318fd37a7SXin LI   "",
19418fd37a7SXin LI   N_("-w NUM  --width=NUM  Output at most NUM (default 130) print columns."),
19518fd37a7SXin LI   N_("-l  --left-column  Output only the left column of common lines."),
19618fd37a7SXin LI   N_("-s  --suppress-common-lines  Do not output common lines."),
19718fd37a7SXin LI   "",
19818fd37a7SXin LI   N_("-t  --expand-tabs  Expand tabs to spaces in output."),
19918fd37a7SXin LI   N_("--tabsize=NUM  Tab stops are every NUM (default 8) print columns."),
20018fd37a7SXin LI   "",
20118fd37a7SXin LI   N_("-d  --minimal  Try hard to find a smaller set of changes."),
20218fd37a7SXin LI   N_("-H  --speed-large-files  Assume large files and many scattered small changes."),
20318fd37a7SXin LI   N_("--diff-program=PROGRAM  Use PROGRAM to compare files."),
20418fd37a7SXin LI   "",
20518fd37a7SXin LI   N_("-v  --version  Output version info."),
20618fd37a7SXin LI   N_("--help  Output this help."),
20718fd37a7SXin LI   0
20818fd37a7SXin LI };
20918fd37a7SXin LI 
21018fd37a7SXin LI static void
usage(void)21118fd37a7SXin LI usage (void)
21218fd37a7SXin LI {
21318fd37a7SXin LI   char const * const *p;
21418fd37a7SXin LI 
21518fd37a7SXin LI   printf (_("Usage: %s [OPTION]... FILE1 FILE2\n"), program_name);
21618fd37a7SXin LI   printf ("%s\n\n", _("Side-by-side merge of file differences."));
21718fd37a7SXin LI   for (p = option_help_msgid;  *p;  p++)
21818fd37a7SXin LI     if (**p)
21918fd37a7SXin LI       printf ("  %s\n", _(*p));
22018fd37a7SXin LI     else
22118fd37a7SXin LI       putchar ('\n');
22218fd37a7SXin LI   printf ("\n%s\n%s\n\n%s\n",
22318fd37a7SXin LI 	  _("If a FILE is `-', read standard input."),
22418fd37a7SXin LI 	  _("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."),
22518fd37a7SXin LI 	  _("Report bugs to <bug-gnu-utils@gnu.org>."));
22618fd37a7SXin LI }
22718fd37a7SXin LI 
22818fd37a7SXin LI /* Clean up after a signal or other failure.  This function is
22918fd37a7SXin LI    async-signal-safe.  */
23018fd37a7SXin LI static void
cleanup(int signo)23118fd37a7SXin LI cleanup (int signo __attribute__((unused)))
23218fd37a7SXin LI {
23318fd37a7SXin LI #if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
23418fd37a7SXin LI   if (0 < diffpid)
23518fd37a7SXin LI     kill (diffpid, SIGPIPE);
23618fd37a7SXin LI #endif
23718fd37a7SXin LI   if (tmpname)
23818fd37a7SXin LI     unlink (tmpname);
23918fd37a7SXin LI }
24018fd37a7SXin LI 
24118fd37a7SXin LI static void exiterr (void) __attribute__((noreturn));
24218fd37a7SXin LI static void
exiterr(void)24318fd37a7SXin LI exiterr (void)
24418fd37a7SXin LI {
24518fd37a7SXin LI   cleanup (0);
24618fd37a7SXin LI   untrapsig (0);
24718fd37a7SXin LI   checksigs ();
24818fd37a7SXin LI   exit (EXIT_TROUBLE);
24918fd37a7SXin LI }
25018fd37a7SXin LI 
25118fd37a7SXin LI static void
fatal(char const * msgid)25218fd37a7SXin LI fatal (char const *msgid)
25318fd37a7SXin LI {
25418fd37a7SXin LI   error (0, 0, "%s", _(msgid));
25518fd37a7SXin LI   exiterr ();
25618fd37a7SXin LI }
25718fd37a7SXin LI 
25818fd37a7SXin LI static void
perror_fatal(char const * msg)25918fd37a7SXin LI perror_fatal (char const *msg)
26018fd37a7SXin LI {
26118fd37a7SXin LI   int e = errno;
26218fd37a7SXin LI   checksigs ();
26318fd37a7SXin LI   error (0, e, "%s", msg);
26418fd37a7SXin LI   exiterr ();
26518fd37a7SXin LI }
26618fd37a7SXin LI 
26718fd37a7SXin LI static void
check_child_status(int werrno,int wstatus,int max_ok_status,char const * subsidiary_program)26818fd37a7SXin LI check_child_status (int werrno, int wstatus, int max_ok_status,
26918fd37a7SXin LI 		    char const *subsidiary_program)
27018fd37a7SXin LI {
27118fd37a7SXin LI   int status = (! werrno && WIFEXITED (wstatus)
27218fd37a7SXin LI 		? WEXITSTATUS (wstatus)
27318fd37a7SXin LI 		: INT_MAX);
27418fd37a7SXin LI 
27518fd37a7SXin LI   if (max_ok_status < status)
27618fd37a7SXin LI     {
27718fd37a7SXin LI       error (0, werrno,
27818fd37a7SXin LI 	     _(status == 126
27918fd37a7SXin LI 	       ? "subsidiary program `%s' could not be invoked"
28018fd37a7SXin LI 	       : status == 127
28118fd37a7SXin LI 	       ? "subsidiary program `%s' not found"
28218fd37a7SXin LI 	       : status == INT_MAX
28318fd37a7SXin LI 	       ? "subsidiary program `%s' failed"
28418fd37a7SXin LI 	       : "subsidiary program `%s' failed (exit status %d)"),
28518fd37a7SXin LI 	     subsidiary_program, status);
28618fd37a7SXin LI       exiterr ();
28718fd37a7SXin LI     }
28818fd37a7SXin LI }
28918fd37a7SXin LI 
29018fd37a7SXin LI static FILE *
ck_fopen(char const * fname,char const * type)29118fd37a7SXin LI ck_fopen (char const *fname, char const *type)
29218fd37a7SXin LI {
29318fd37a7SXin LI   FILE *r = fopen (fname, type);
29418fd37a7SXin LI   if (! r)
29518fd37a7SXin LI     perror_fatal (fname);
29618fd37a7SXin LI   return r;
29718fd37a7SXin LI }
29818fd37a7SXin LI 
29918fd37a7SXin LI static void
ck_fclose(FILE * f)30018fd37a7SXin LI ck_fclose (FILE *f)
30118fd37a7SXin LI {
30218fd37a7SXin LI   if (fclose (f))
30318fd37a7SXin LI     perror_fatal ("fclose");
30418fd37a7SXin LI }
30518fd37a7SXin LI 
30618fd37a7SXin LI static size_t
ck_fread(char * buf,size_t size,FILE * f)30718fd37a7SXin LI ck_fread (char *buf, size_t size, FILE *f)
30818fd37a7SXin LI {
30918fd37a7SXin LI   size_t r = fread (buf, sizeof (char), size, f);
31018fd37a7SXin LI   if (r == 0 && ferror (f))
31118fd37a7SXin LI     perror_fatal (_("read failed"));
31218fd37a7SXin LI   return r;
31318fd37a7SXin LI }
31418fd37a7SXin LI 
31518fd37a7SXin LI static void
ck_fwrite(char const * buf,size_t size,FILE * f)31618fd37a7SXin LI ck_fwrite (char const *buf, size_t size, FILE *f)
31718fd37a7SXin LI {
31818fd37a7SXin LI   if (fwrite (buf, sizeof (char), size, f) != size)
31918fd37a7SXin LI     perror_fatal (_("write failed"));
32018fd37a7SXin LI }
32118fd37a7SXin LI 
32218fd37a7SXin LI static void
ck_fflush(FILE * f)32318fd37a7SXin LI ck_fflush (FILE *f)
32418fd37a7SXin LI {
32518fd37a7SXin LI   if (fflush (f) != 0)
32618fd37a7SXin LI     perror_fatal (_("write failed"));
32718fd37a7SXin LI }
32818fd37a7SXin LI 
32918fd37a7SXin LI static char const *
expand_name(char * name,bool is_dir,char const * other_name)33018fd37a7SXin LI expand_name (char *name, bool is_dir, char const *other_name)
33118fd37a7SXin LI {
33218fd37a7SXin LI   if (strcmp (name, "-") == 0)
33318fd37a7SXin LI     fatal ("cannot interactively merge standard input");
33418fd37a7SXin LI   if (! is_dir)
33518fd37a7SXin LI     return name;
33618fd37a7SXin LI   else
33718fd37a7SXin LI     {
33818fd37a7SXin LI       /* Yield NAME/BASE, where BASE is OTHER_NAME's basename.  */
33918fd37a7SXin LI       char const *base = base_name (other_name);
34018fd37a7SXin LI       size_t namelen = strlen (name), baselen = strlen (base);
34118fd37a7SXin LI       bool insert_slash = *base_name (name) && name[namelen - 1] != '/';
34218fd37a7SXin LI       char *r = xmalloc (namelen + insert_slash + baselen + 1);
34318fd37a7SXin LI       memcpy (r, name, namelen);
34418fd37a7SXin LI       r[namelen] = '/';
34518fd37a7SXin LI       memcpy (r + namelen + insert_slash, base, baselen + 1);
34618fd37a7SXin LI       return r;
34718fd37a7SXin LI     }
34818fd37a7SXin LI }
34918fd37a7SXin LI 
35018fd37a7SXin LI struct line_filter {
35118fd37a7SXin LI   FILE *infile;
35218fd37a7SXin LI   char *bufpos;
35318fd37a7SXin LI   char *buffer;
35418fd37a7SXin LI   char *buflim;
35518fd37a7SXin LI };
35618fd37a7SXin LI 
35718fd37a7SXin LI static void
lf_init(struct line_filter * lf,FILE * infile)35818fd37a7SXin LI lf_init (struct line_filter *lf, FILE *infile)
35918fd37a7SXin LI {
36018fd37a7SXin LI   lf->infile = infile;
36118fd37a7SXin LI   lf->bufpos = lf->buffer = lf->buflim = xmalloc (SDIFF_BUFSIZE + 1);
36218fd37a7SXin LI   lf->buflim[0] = '\n';
36318fd37a7SXin LI }
36418fd37a7SXin LI 
36518fd37a7SXin LI /* Fill an exhausted line_filter buffer from its INFILE */
36618fd37a7SXin LI static size_t
lf_refill(struct line_filter * lf)36718fd37a7SXin LI lf_refill (struct line_filter *lf)
36818fd37a7SXin LI {
36918fd37a7SXin LI   size_t s = ck_fread (lf->buffer, SDIFF_BUFSIZE, lf->infile);
37018fd37a7SXin LI   lf->bufpos = lf->buffer;
37118fd37a7SXin LI   lf->buflim = lf->buffer + s;
37218fd37a7SXin LI   lf->buflim[0] = '\n';
37318fd37a7SXin LI   checksigs ();
37418fd37a7SXin LI   return s;
37518fd37a7SXin LI }
37618fd37a7SXin LI 
37718fd37a7SXin LI /* Advance LINES on LF's infile, copying lines to OUTFILE */
37818fd37a7SXin LI static void
lf_copy(struct line_filter * lf,lin lines,FILE * outfile)37918fd37a7SXin LI lf_copy (struct line_filter *lf, lin lines, FILE *outfile)
38018fd37a7SXin LI {
38118fd37a7SXin LI   char *start = lf->bufpos;
38218fd37a7SXin LI 
38318fd37a7SXin LI   while (lines)
38418fd37a7SXin LI     {
38518fd37a7SXin LI       lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
38618fd37a7SXin LI       if (! lf->bufpos)
38718fd37a7SXin LI 	{
38818fd37a7SXin LI 	  ck_fwrite (start, lf->buflim - start, outfile);
38918fd37a7SXin LI 	  if (! lf_refill (lf))
39018fd37a7SXin LI 	    return;
39118fd37a7SXin LI 	  start = lf->bufpos;
39218fd37a7SXin LI 	}
39318fd37a7SXin LI       else
39418fd37a7SXin LI 	{
39518fd37a7SXin LI 	  --lines;
39618fd37a7SXin LI 	  ++lf->bufpos;
39718fd37a7SXin LI 	}
39818fd37a7SXin LI     }
39918fd37a7SXin LI 
40018fd37a7SXin LI   ck_fwrite (start, lf->bufpos - start, outfile);
40118fd37a7SXin LI }
40218fd37a7SXin LI 
40318fd37a7SXin LI /* Advance LINES on LF's infile without doing output */
40418fd37a7SXin LI static void
lf_skip(struct line_filter * lf,lin lines)40518fd37a7SXin LI lf_skip (struct line_filter *lf, lin lines)
40618fd37a7SXin LI {
40718fd37a7SXin LI   while (lines)
40818fd37a7SXin LI     {
40918fd37a7SXin LI       lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
41018fd37a7SXin LI       if (! lf->bufpos)
41118fd37a7SXin LI 	{
41218fd37a7SXin LI 	  if (! lf_refill (lf))
41318fd37a7SXin LI 	    break;
41418fd37a7SXin LI 	}
41518fd37a7SXin LI       else
41618fd37a7SXin LI 	{
41718fd37a7SXin LI 	  --lines;
41818fd37a7SXin LI 	  ++lf->bufpos;
41918fd37a7SXin LI 	}
42018fd37a7SXin LI     }
42118fd37a7SXin LI }
42218fd37a7SXin LI 
42318fd37a7SXin LI /* Snarf a line into a buffer.  Return EOF if EOF, 0 if error, 1 if OK.  */
42418fd37a7SXin LI static int
lf_snarf(struct line_filter * lf,char * buffer,size_t bufsize)42518fd37a7SXin LI lf_snarf (struct line_filter *lf, char *buffer, size_t bufsize)
42618fd37a7SXin LI {
42718fd37a7SXin LI   for (;;)
42818fd37a7SXin LI     {
42918fd37a7SXin LI       char *start = lf->bufpos;
43018fd37a7SXin LI       char *next = (char *) memchr (start, '\n', lf->buflim + 1 - start);
43118fd37a7SXin LI       size_t s = next - start;
43218fd37a7SXin LI       if (bufsize <= s)
43318fd37a7SXin LI 	return 0;
43418fd37a7SXin LI       memcpy (buffer, start, s);
43518fd37a7SXin LI       if (next < lf->buflim)
43618fd37a7SXin LI 	{
43718fd37a7SXin LI 	  buffer[s] = 0;
43818fd37a7SXin LI 	  lf->bufpos = next + 1;
43918fd37a7SXin LI 	  return 1;
44018fd37a7SXin LI 	}
44118fd37a7SXin LI       if (! lf_refill (lf))
44218fd37a7SXin LI 	return s ? 0 : EOF;
44318fd37a7SXin LI       buffer += s;
44418fd37a7SXin LI       bufsize -= s;
44518fd37a7SXin LI     }
44618fd37a7SXin LI }
44718fd37a7SXin LI 
44818fd37a7SXin LI int
main(int argc,char * argv[])44918fd37a7SXin LI main (int argc, char *argv[])
45018fd37a7SXin LI {
45118fd37a7SXin LI   int opt;
45218fd37a7SXin LI   char const *prog;
45318fd37a7SXin LI 
45418fd37a7SXin LI   exit_failure = EXIT_TROUBLE;
45518fd37a7SXin LI   initialize_main (&argc, &argv);
45618fd37a7SXin LI   program_name = argv[0];
45718fd37a7SXin LI   setlocale (LC_ALL, "");
45818fd37a7SXin LI   bindtextdomain (PACKAGE, LOCALEDIR);
45918fd37a7SXin LI   textdomain (PACKAGE);
46018fd37a7SXin LI   c_stack_action (cleanup);
46118fd37a7SXin LI 
46218fd37a7SXin LI   prog = getenv ("EDITOR");
46318fd37a7SXin LI   if (prog)
46418fd37a7SXin LI     editor_program = prog;
46518fd37a7SXin LI 
46618fd37a7SXin LI   diffarg (DEFAULT_DIFF_PROGRAM);
46718fd37a7SXin LI 
46818fd37a7SXin LI   /* parse command line args */
46918fd37a7SXin LI   while ((opt = getopt_long (argc, argv, "abBdEHiI:lo:stvw:W", longopts, 0))
47018fd37a7SXin LI 	 != -1)
47118fd37a7SXin LI     {
47218fd37a7SXin LI       switch (opt)
47318fd37a7SXin LI 	{
47418fd37a7SXin LI 	case 'a':
47518fd37a7SXin LI 	  diffarg ("-a");
47618fd37a7SXin LI 	  break;
47718fd37a7SXin LI 
47818fd37a7SXin LI 	case 'b':
47918fd37a7SXin LI 	  diffarg ("-b");
48018fd37a7SXin LI 	  break;
48118fd37a7SXin LI 
48218fd37a7SXin LI 	case 'B':
48318fd37a7SXin LI 	  diffarg ("-B");
48418fd37a7SXin LI 	  break;
48518fd37a7SXin LI 
48618fd37a7SXin LI 	case 'd':
48718fd37a7SXin LI 	  diffarg ("-d");
48818fd37a7SXin LI 	  break;
48918fd37a7SXin LI 
49018fd37a7SXin LI 	case 'E':
49118fd37a7SXin LI 	  diffarg ("-E");
49218fd37a7SXin LI 	  break;
49318fd37a7SXin LI 
49418fd37a7SXin LI 	case 'H':
49518fd37a7SXin LI 	  diffarg ("-H");
49618fd37a7SXin LI 	  break;
49718fd37a7SXin LI 
49818fd37a7SXin LI 	case 'i':
49918fd37a7SXin LI 	  diffarg ("-i");
50018fd37a7SXin LI 	  break;
50118fd37a7SXin LI 
50218fd37a7SXin LI 	case 'I':
50318fd37a7SXin LI 	  diffarg ("-I");
50418fd37a7SXin LI 	  diffarg (optarg);
50518fd37a7SXin LI 	  break;
50618fd37a7SXin LI 
50718fd37a7SXin LI 	case 'l':
50818fd37a7SXin LI 	  diffarg ("--left-column");
50918fd37a7SXin LI 	  break;
51018fd37a7SXin LI 
51118fd37a7SXin LI 	case 'o':
51218fd37a7SXin LI 	  output = optarg;
51318fd37a7SXin LI 	  break;
51418fd37a7SXin LI 
51518fd37a7SXin LI 	case 's':
51618fd37a7SXin LI 	  suppress_common_lines = true;
51718fd37a7SXin LI 	  break;
51818fd37a7SXin LI 
51918fd37a7SXin LI 	case 't':
52018fd37a7SXin LI 	  diffarg ("-t");
52118fd37a7SXin LI 	  break;
52218fd37a7SXin LI 
52318fd37a7SXin LI 	case 'v':
52418fd37a7SXin LI 	  version_etc (stdout, "sdiff", PACKAGE_NAME, PACKAGE_VERSION,
52518fd37a7SXin LI 		       "Thomas Lord", (char *) 0);
52618fd37a7SXin LI 	  check_stdout ();
52718fd37a7SXin LI 	  return EXIT_SUCCESS;
52818fd37a7SXin LI 
52918fd37a7SXin LI 	case 'w':
53018fd37a7SXin LI 	  diffarg ("-W");
53118fd37a7SXin LI 	  diffarg (optarg);
53218fd37a7SXin LI 	  break;
53318fd37a7SXin LI 
53418fd37a7SXin LI 	case 'W':
53518fd37a7SXin LI 	  diffarg ("-w");
53618fd37a7SXin LI 	  break;
53718fd37a7SXin LI 
53818fd37a7SXin LI 	case DIFF_PROGRAM_OPTION:
53918fd37a7SXin LI 	  diffargv[0] = optarg;
54018fd37a7SXin LI 	  break;
54118fd37a7SXin LI 
54218fd37a7SXin LI 	case HELP_OPTION:
54318fd37a7SXin LI 	  usage ();
54418fd37a7SXin LI 	  check_stdout ();
54518fd37a7SXin LI 	  return EXIT_SUCCESS;
54618fd37a7SXin LI 
54718fd37a7SXin LI 	case STRIP_TRAILING_CR_OPTION:
54818fd37a7SXin LI 	  diffarg ("--strip-trailing-cr");
54918fd37a7SXin LI 	  break;
55018fd37a7SXin LI 
55118fd37a7SXin LI 	case TABSIZE_OPTION:
55218fd37a7SXin LI 	  diffarg ("--tabsize");
55318fd37a7SXin LI 	  diffarg (optarg);
55418fd37a7SXin LI 	  break;
55518fd37a7SXin LI 
55618fd37a7SXin LI 	default:
55718fd37a7SXin LI 	  try_help (0, 0);
55818fd37a7SXin LI 	}
55918fd37a7SXin LI     }
56018fd37a7SXin LI 
56118fd37a7SXin LI   if (argc - optind != 2)
56218fd37a7SXin LI     {
56318fd37a7SXin LI       if (argc - optind < 2)
56418fd37a7SXin LI 	try_help ("missing operand after `%s'", argv[argc - 1]);
56518fd37a7SXin LI       else
56618fd37a7SXin LI 	try_help ("extra operand `%s'", argv[optind + 2]);
56718fd37a7SXin LI     }
56818fd37a7SXin LI 
56918fd37a7SXin LI   if (! output)
57018fd37a7SXin LI     {
57118fd37a7SXin LI       /* easy case: diff does everything for us */
57218fd37a7SXin LI       if (suppress_common_lines)
57318fd37a7SXin LI 	diffarg ("--suppress-common-lines");
57418fd37a7SXin LI       diffarg ("-y");
57518fd37a7SXin LI       diffarg ("--");
57618fd37a7SXin LI       diffarg (argv[optind]);
57718fd37a7SXin LI       diffarg (argv[optind + 1]);
57818fd37a7SXin LI       diffarg (0);
57918fd37a7SXin LI       execvp (diffargv[0], (char **) diffargv);
58018fd37a7SXin LI       perror_fatal (diffargv[0]);
58118fd37a7SXin LI     }
58218fd37a7SXin LI   else
58318fd37a7SXin LI     {
58418fd37a7SXin LI       char const *lname, *rname;
58518fd37a7SXin LI       FILE *left, *right, *out, *diffout;
58618fd37a7SXin LI       bool interact_ok;
58718fd37a7SXin LI       struct line_filter lfilt;
58818fd37a7SXin LI       struct line_filter rfilt;
58918fd37a7SXin LI       struct line_filter diff_filt;
59018fd37a7SXin LI       bool leftdir = diraccess (argv[optind]);
59118fd37a7SXin LI       bool rightdir = diraccess (argv[optind + 1]);
59218fd37a7SXin LI 
59318fd37a7SXin LI       if (leftdir & rightdir)
59418fd37a7SXin LI 	fatal ("both files to be compared are directories");
59518fd37a7SXin LI 
59618fd37a7SXin LI       lname = expand_name (argv[optind], leftdir, argv[optind + 1]);
59718fd37a7SXin LI       left = ck_fopen (lname, "r");
59818fd37a7SXin LI       rname = expand_name (argv[optind + 1], rightdir, argv[optind]);
59918fd37a7SXin LI       right = ck_fopen (rname, "r");
60018fd37a7SXin LI       out = ck_fopen (output, "w");
60118fd37a7SXin LI 
60218fd37a7SXin LI       diffarg ("--sdiff-merge-assist");
60318fd37a7SXin LI       diffarg ("--");
60418fd37a7SXin LI       diffarg (argv[optind]);
60518fd37a7SXin LI       diffarg (argv[optind + 1]);
60618fd37a7SXin LI       diffarg (0);
60718fd37a7SXin LI 
60818fd37a7SXin LI       trapsigs ();
60918fd37a7SXin LI 
61018fd37a7SXin LI #if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
61118fd37a7SXin LI       {
61218fd37a7SXin LI 	size_t cmdsize = 1;
61318fd37a7SXin LI 	char *p, *command;
61418fd37a7SXin LI 	int i;
61518fd37a7SXin LI 
61618fd37a7SXin LI 	for (i = 0;  diffargv[i];  i++)
61718fd37a7SXin LI 	  cmdsize += quote_system_arg (0, diffargv[i]) + 1;
61818fd37a7SXin LI 	command = p = xmalloc (cmdsize);
61918fd37a7SXin LI 	for (i = 0;  diffargv[i];  i++)
62018fd37a7SXin LI 	  {
62118fd37a7SXin LI 	    p += quote_system_arg (p, diffargv[i]);
62218fd37a7SXin LI 	    *p++ = ' ';
62318fd37a7SXin LI 	  }
62418fd37a7SXin LI 	p[-1] = 0;
62518fd37a7SXin LI 	errno = 0;
62618fd37a7SXin LI 	diffout = popen (command, "r");
62718fd37a7SXin LI 	if (! diffout)
62818fd37a7SXin LI 	  perror_fatal (command);
62918fd37a7SXin LI 	free (command);
63018fd37a7SXin LI       }
63118fd37a7SXin LI #else
63218fd37a7SXin LI       {
63318fd37a7SXin LI 	int diff_fds[2];
63418fd37a7SXin LI # if HAVE_WORKING_VFORK
63518fd37a7SXin LI 	sigset_t procmask;
63618fd37a7SXin LI 	sigset_t blocked;
63718fd37a7SXin LI # endif
63818fd37a7SXin LI 
63918fd37a7SXin LI 	if (pipe (diff_fds) != 0)
64018fd37a7SXin LI 	  perror_fatal ("pipe");
64118fd37a7SXin LI 
64218fd37a7SXin LI # if HAVE_WORKING_VFORK
64318fd37a7SXin LI 	/* Block SIGINT and SIGPIPE.  */
64418fd37a7SXin LI 	sigemptyset (&blocked);
64518fd37a7SXin LI 	sigaddset (&blocked, SIGINT);
64618fd37a7SXin LI 	sigaddset (&blocked, SIGPIPE);
64718fd37a7SXin LI 	sigprocmask (SIG_BLOCK, &blocked, &procmask);
64818fd37a7SXin LI # endif
64918fd37a7SXin LI 	diffpid = vfork ();
65018fd37a7SXin LI 	if (diffpid < 0)
65118fd37a7SXin LI 	  perror_fatal ("fork");
65218fd37a7SXin LI 	if (! diffpid)
65318fd37a7SXin LI 	  {
65418fd37a7SXin LI 	    /* Alter the child's SIGINT and SIGPIPE handlers;
65518fd37a7SXin LI 	       this may munge the parent.
65618fd37a7SXin LI 	       The child ignores SIGINT in case the user interrupts the editor.
65718fd37a7SXin LI 	       The child does not ignore SIGPIPE, even if the parent does.  */
65818fd37a7SXin LI 	    if (initial_handler (handler_index_of_SIGINT) != SIG_IGN)
65918fd37a7SXin LI 	      signal_handler (SIGINT, SIG_IGN);
66018fd37a7SXin LI 	    signal_handler (SIGPIPE, SIG_DFL);
66118fd37a7SXin LI # if HAVE_WORKING_VFORK
66218fd37a7SXin LI 	    /* Stop blocking SIGINT and SIGPIPE in the child.  */
66318fd37a7SXin LI 	    sigprocmask (SIG_SETMASK, &procmask, 0);
66418fd37a7SXin LI # endif
66518fd37a7SXin LI 	    close (diff_fds[0]);
66618fd37a7SXin LI 	    if (diff_fds[1] != STDOUT_FILENO)
66718fd37a7SXin LI 	      {
66818fd37a7SXin LI 		dup2 (diff_fds[1], STDOUT_FILENO);
66918fd37a7SXin LI 		close (diff_fds[1]);
67018fd37a7SXin LI 	      }
67118fd37a7SXin LI 
67218fd37a7SXin LI 	    execvp (diffargv[0], (char **) diffargv);
67318fd37a7SXin LI 	    _exit (errno == ENOENT ? 127 : 126);
67418fd37a7SXin LI 	  }
67518fd37a7SXin LI 
67618fd37a7SXin LI # if HAVE_WORKING_VFORK
67718fd37a7SXin LI 	/* Restore the parent's SIGINT and SIGPIPE behavior.  */
67818fd37a7SXin LI 	if (initial_handler (handler_index_of_SIGINT) != SIG_IGN)
67918fd37a7SXin LI 	  signal_handler (SIGINT, catchsig);
68018fd37a7SXin LI 	if (initial_handler (handler_index_of_SIGPIPE) != SIG_IGN)
68118fd37a7SXin LI 	  signal_handler (SIGPIPE, catchsig);
68218fd37a7SXin LI 	else
68318fd37a7SXin LI 	  signal_handler (SIGPIPE, SIG_IGN);
68418fd37a7SXin LI 
68518fd37a7SXin LI 	/* Stop blocking SIGINT and SIGPIPE in the parent.  */
68618fd37a7SXin LI 	sigprocmask (SIG_SETMASK, &procmask, 0);
68718fd37a7SXin LI # endif
68818fd37a7SXin LI 
68918fd37a7SXin LI 	close (diff_fds[1]);
69018fd37a7SXin LI 	diffout = fdopen (diff_fds[0], "r");
69118fd37a7SXin LI 	if (! diffout)
69218fd37a7SXin LI 	  perror_fatal ("fdopen");
69318fd37a7SXin LI       }
69418fd37a7SXin LI #endif
69518fd37a7SXin LI 
69618fd37a7SXin LI       lf_init (&diff_filt, diffout);
69718fd37a7SXin LI       lf_init (&lfilt, left);
69818fd37a7SXin LI       lf_init (&rfilt, right);
69918fd37a7SXin LI 
70018fd37a7SXin LI       interact_ok = interact (&diff_filt, &lfilt, lname, &rfilt, rname, out);
70118fd37a7SXin LI 
70218fd37a7SXin LI       ck_fclose (left);
70318fd37a7SXin LI       ck_fclose (right);
70418fd37a7SXin LI       ck_fclose (out);
70518fd37a7SXin LI 
70618fd37a7SXin LI       {
70718fd37a7SXin LI 	int wstatus;
70818fd37a7SXin LI 	int werrno = 0;
70918fd37a7SXin LI 
71018fd37a7SXin LI #if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
71118fd37a7SXin LI 	wstatus = pclose (diffout);
71218fd37a7SXin LI 	if (wstatus == -1)
71318fd37a7SXin LI 	  werrno = errno;
71418fd37a7SXin LI #else
71518fd37a7SXin LI 	ck_fclose (diffout);
71618fd37a7SXin LI 	while (waitpid (diffpid, &wstatus, 0) < 0)
71718fd37a7SXin LI 	  if (errno == EINTR)
71818fd37a7SXin LI 	    checksigs ();
71918fd37a7SXin LI 	  else
72018fd37a7SXin LI 	    perror_fatal ("waitpid");
72118fd37a7SXin LI 	diffpid = 0;
72218fd37a7SXin LI #endif
72318fd37a7SXin LI 
72418fd37a7SXin LI 	if (tmpname)
72518fd37a7SXin LI 	  {
72618fd37a7SXin LI 	    unlink (tmpname);
72718fd37a7SXin LI 	    tmpname = 0;
72818fd37a7SXin LI 	  }
72918fd37a7SXin LI 
73018fd37a7SXin LI 	if (! interact_ok)
73118fd37a7SXin LI 	  exiterr ();
73218fd37a7SXin LI 
73318fd37a7SXin LI 	check_child_status (werrno, wstatus, EXIT_FAILURE, diffargv[0]);
73418fd37a7SXin LI 	untrapsig (0);
73518fd37a7SXin LI 	checksigs ();
73618fd37a7SXin LI 	exit (WEXITSTATUS (wstatus));
73718fd37a7SXin LI       }
73818fd37a7SXin LI     }
73918fd37a7SXin LI   return EXIT_SUCCESS;			/* Fool `-Wall'.  */
74018fd37a7SXin LI }
74118fd37a7SXin LI 
74218fd37a7SXin LI static void
diffarg(char const * a)74318fd37a7SXin LI diffarg (char const *a)
74418fd37a7SXin LI {
74518fd37a7SXin LI   static size_t diffargs, diffarglim;
74618fd37a7SXin LI 
74718fd37a7SXin LI   if (diffargs == diffarglim)
74818fd37a7SXin LI     {
74918fd37a7SXin LI       if (! diffarglim)
75018fd37a7SXin LI 	diffarglim = 16;
75118fd37a7SXin LI       else if (PTRDIFF_MAX / (2 * sizeof *diffargv) <= diffarglim)
75218fd37a7SXin LI 	xalloc_die ();
75318fd37a7SXin LI       else
75418fd37a7SXin LI 	diffarglim *= 2;
75518fd37a7SXin LI       diffargv = xrealloc (diffargv, diffarglim * sizeof *diffargv);
75618fd37a7SXin LI     }
75718fd37a7SXin LI   diffargv[diffargs++] = a;
75818fd37a7SXin LI }
75918fd37a7SXin LI 
76018fd37a7SXin LI /* Signal handling */
76118fd37a7SXin LI 
76218fd37a7SXin LI static bool volatile ignore_SIGINT;
76318fd37a7SXin LI static int volatile signal_received;
76418fd37a7SXin LI static bool sigs_trapped;
76518fd37a7SXin LI 
76618fd37a7SXin LI static void
catchsig(int s)76718fd37a7SXin LI catchsig (int s)
76818fd37a7SXin LI {
76918fd37a7SXin LI #if ! HAVE_SIGACTION
77018fd37a7SXin LI   signal (s, SIG_IGN);
77118fd37a7SXin LI #endif
77218fd37a7SXin LI   if (! (s == SIGINT && ignore_SIGINT))
77318fd37a7SXin LI     signal_received = s;
77418fd37a7SXin LI }
77518fd37a7SXin LI 
77618fd37a7SXin LI #if HAVE_SIGACTION
77718fd37a7SXin LI static struct sigaction catchaction;
77818fd37a7SXin LI 
77918fd37a7SXin LI static void
signal_handler(int sig,void (* handler)(int))78018fd37a7SXin LI signal_handler (int sig, void (*handler) (int))
78118fd37a7SXin LI {
78218fd37a7SXin LI   catchaction.sa_handler = handler;
78318fd37a7SXin LI   sigaction (sig, &catchaction, 0);
78418fd37a7SXin LI }
78518fd37a7SXin LI #endif
78618fd37a7SXin LI 
78718fd37a7SXin LI static void
trapsigs(void)78818fd37a7SXin LI trapsigs (void)
78918fd37a7SXin LI {
79018fd37a7SXin LI   int i;
79118fd37a7SXin LI 
79218fd37a7SXin LI #if HAVE_SIGACTION
79318fd37a7SXin LI   catchaction.sa_flags = SA_RESTART;
79418fd37a7SXin LI   sigemptyset (&catchaction.sa_mask);
79518fd37a7SXin LI   for (i = 0;  i < NUM_SIGS;  i++)
79618fd37a7SXin LI     sigaddset (&catchaction.sa_mask, sigs[i]);
79718fd37a7SXin LI #endif
79818fd37a7SXin LI 
79918fd37a7SXin LI   for (i = 0;  i < NUM_SIGS;  i++)
80018fd37a7SXin LI     {
80118fd37a7SXin LI #if HAVE_SIGACTION
80218fd37a7SXin LI       sigaction (sigs[i], 0, &initial_action[i]);
80318fd37a7SXin LI #else
80418fd37a7SXin LI       initial_action[i] = signal (sigs[i], SIG_IGN);
80518fd37a7SXin LI #endif
80618fd37a7SXin LI       if (initial_handler (i) != SIG_IGN)
80718fd37a7SXin LI 	signal_handler (sigs[i], catchsig);
80818fd37a7SXin LI     }
80918fd37a7SXin LI 
81018fd37a7SXin LI #ifdef SIGCHLD
81118fd37a7SXin LI   /* System V fork+wait does not work if SIGCHLD is ignored.  */
81218fd37a7SXin LI   signal (SIGCHLD, SIG_DFL);
81318fd37a7SXin LI #endif
81418fd37a7SXin LI 
81518fd37a7SXin LI   sigs_trapped = true;
81618fd37a7SXin LI }
81718fd37a7SXin LI 
81818fd37a7SXin LI /* Untrap signal S, or all trapped signals if S is zero.  */
81918fd37a7SXin LI static void
untrapsig(int s)82018fd37a7SXin LI untrapsig (int s)
82118fd37a7SXin LI {
82218fd37a7SXin LI   int i;
82318fd37a7SXin LI 
82418fd37a7SXin LI   if (sigs_trapped)
82518fd37a7SXin LI     for (i = 0;  i < NUM_SIGS;  i++)
82618fd37a7SXin LI       if ((! s || sigs[i] == s)  &&  initial_handler (i) != SIG_IGN)
82718fd37a7SXin LI 	{
82818fd37a7SXin LI #if HAVE_SIGACTION
82918fd37a7SXin LI 	  sigaction (sigs[i], &initial_action[i], 0);
83018fd37a7SXin LI #else
83118fd37a7SXin LI 	  signal (sigs[i], initial_action[i]);
83218fd37a7SXin LI #endif
83318fd37a7SXin LI 	}
83418fd37a7SXin LI }
83518fd37a7SXin LI 
83618fd37a7SXin LI /* Exit if a signal has been received.  */
83718fd37a7SXin LI static void
checksigs(void)83818fd37a7SXin LI checksigs (void)
83918fd37a7SXin LI {
84018fd37a7SXin LI   int s = signal_received;
84118fd37a7SXin LI   if (s)
84218fd37a7SXin LI     {
84318fd37a7SXin LI       cleanup (0);
84418fd37a7SXin LI 
84518fd37a7SXin LI       /* Yield an exit status indicating that a signal was received.  */
84618fd37a7SXin LI       untrapsig (s);
84718fd37a7SXin LI       kill (getpid (), s);
84818fd37a7SXin LI 
84918fd37a7SXin LI       /* That didn't work, so exit with error status.  */
85018fd37a7SXin LI       exit (EXIT_TROUBLE);
85118fd37a7SXin LI     }
85218fd37a7SXin LI }
85318fd37a7SXin LI 
85418fd37a7SXin LI static void
give_help(void)85518fd37a7SXin LI give_help (void)
85618fd37a7SXin LI {
85718fd37a7SXin LI   fprintf (stderr, "%s", _("\
85818fd37a7SXin LI ed:\tEdit then use both versions, each decorated with a header.\n\
85918fd37a7SXin LI eb:\tEdit then use both versions.\n\
860b61bbb70SXin LI el or e1:\tEdit then use the left version.\n\
861b61bbb70SXin LI er or e2:\tEdit then use the right version.\n\
862b61bbb70SXin LI e:\tDiscard both versions then edit a new one.\n\
863b61bbb70SXin LI l or 1:\tUse the left version.\n\
864b61bbb70SXin LI r or 2:\tUse the right version.\n\
86518fd37a7SXin LI s:\tSilently include common lines.\n\
86618fd37a7SXin LI v:\tVerbosely include common lines.\n\
86718fd37a7SXin LI q:\tQuit.\n\
86818fd37a7SXin LI "));
86918fd37a7SXin LI }
87018fd37a7SXin LI 
87118fd37a7SXin LI static int
skip_white(void)87218fd37a7SXin LI skip_white (void)
87318fd37a7SXin LI {
87418fd37a7SXin LI   int c;
87518fd37a7SXin LI   for (;;)
87618fd37a7SXin LI     {
87718fd37a7SXin LI       c = getchar ();
87818fd37a7SXin LI       if (! isspace (c) || c == '\n')
87918fd37a7SXin LI 	break;
88018fd37a7SXin LI       checksigs ();
88118fd37a7SXin LI     }
88218fd37a7SXin LI   if (ferror (stdin))
88318fd37a7SXin LI     perror_fatal (_("read failed"));
88418fd37a7SXin LI   return c;
88518fd37a7SXin LI }
88618fd37a7SXin LI 
88718fd37a7SXin LI static void
flush_line(void)88818fd37a7SXin LI flush_line (void)
88918fd37a7SXin LI {
89018fd37a7SXin LI   int c;
89118fd37a7SXin LI   while ((c = getchar ()) != '\n' && c != EOF)
89218fd37a7SXin LI     continue;
89318fd37a7SXin LI   if (ferror (stdin))
89418fd37a7SXin LI     perror_fatal (_("read failed"));
89518fd37a7SXin LI }
89618fd37a7SXin LI 
89718fd37a7SXin LI 
89818fd37a7SXin LI /* interpret an edit command */
89918fd37a7SXin LI static bool
edit(struct line_filter * left,char const * lname,lin lline,lin llen,struct line_filter * right,char const * rname,lin rline,lin rlen,FILE * outfile)90018fd37a7SXin LI edit (struct line_filter *left, char const *lname, lin lline, lin llen,
90118fd37a7SXin LI       struct line_filter *right, char const *rname, lin rline, lin rlen,
90218fd37a7SXin LI       FILE *outfile)
90318fd37a7SXin LI {
90418fd37a7SXin LI   for (;;)
90518fd37a7SXin LI     {
90618fd37a7SXin LI       int cmd0, cmd1;
90718fd37a7SXin LI       bool gotcmd = false;
90818fd37a7SXin LI 
90918fd37a7SXin LI       cmd1 = 0; /* Pacify `gcc -W'.  */
91018fd37a7SXin LI 
91118fd37a7SXin LI       while (! gotcmd)
91218fd37a7SXin LI 	{
91318fd37a7SXin LI 	  if (putchar ('%') != '%')
91418fd37a7SXin LI 	    perror_fatal (_("write failed"));
91518fd37a7SXin LI 	  ck_fflush (stdout);
91618fd37a7SXin LI 
91718fd37a7SXin LI 	  cmd0 = skip_white ();
91818fd37a7SXin LI 	  switch (cmd0)
91918fd37a7SXin LI 	    {
920b61bbb70SXin LI 	    case '1': case '2': case 'l': case 'r':
921b61bbb70SXin LI 	    case 's': case 'v': case 'q':
92218fd37a7SXin LI 	      if (skip_white () != '\n')
92318fd37a7SXin LI 		{
92418fd37a7SXin LI 		  give_help ();
92518fd37a7SXin LI 		  flush_line ();
92618fd37a7SXin LI 		  continue;
92718fd37a7SXin LI 		}
92818fd37a7SXin LI 	      gotcmd = true;
92918fd37a7SXin LI 	      break;
93018fd37a7SXin LI 
93118fd37a7SXin LI 	    case 'e':
93218fd37a7SXin LI 	      cmd1 = skip_white ();
93318fd37a7SXin LI 	      switch (cmd1)
93418fd37a7SXin LI 		{
935b61bbb70SXin LI 		case '1': case '2': case 'b': case 'd': case 'l': case 'r':
93618fd37a7SXin LI 		  if (skip_white () != '\n')
93718fd37a7SXin LI 		    {
93818fd37a7SXin LI 		      give_help ();
93918fd37a7SXin LI 		      flush_line ();
94018fd37a7SXin LI 		      continue;
94118fd37a7SXin LI 		    }
94218fd37a7SXin LI 		  gotcmd = true;
94318fd37a7SXin LI 		  break;
94418fd37a7SXin LI 		case '\n':
94518fd37a7SXin LI 		  gotcmd = true;
94618fd37a7SXin LI 		  break;
94718fd37a7SXin LI 		default:
94818fd37a7SXin LI 		  give_help ();
94918fd37a7SXin LI 		  flush_line ();
95018fd37a7SXin LI 		  continue;
95118fd37a7SXin LI 		}
95218fd37a7SXin LI 	      break;
95318fd37a7SXin LI 
95418fd37a7SXin LI 	    case EOF:
95518fd37a7SXin LI 	      if (feof (stdin))
95618fd37a7SXin LI 		{
95718fd37a7SXin LI 		  gotcmd = true;
95818fd37a7SXin LI 		  cmd0 = 'q';
95918fd37a7SXin LI 		  break;
96018fd37a7SXin LI 		}
96118fd37a7SXin LI 	      /* Fall through.  */
96218fd37a7SXin LI 	    default:
96318fd37a7SXin LI 	      flush_line ();
96418fd37a7SXin LI 	      /* Fall through.  */
96518fd37a7SXin LI 	    case '\n':
96618fd37a7SXin LI 	      give_help ();
96718fd37a7SXin LI 	      continue;
96818fd37a7SXin LI 	    }
96918fd37a7SXin LI 	}
97018fd37a7SXin LI 
97118fd37a7SXin LI       switch (cmd0)
97218fd37a7SXin LI 	{
973b61bbb70SXin LI 	case '1': case 'l':
97418fd37a7SXin LI 	  lf_copy (left, llen, outfile);
97518fd37a7SXin LI 	  lf_skip (right, rlen);
97618fd37a7SXin LI 	  return true;
977b61bbb70SXin LI 	case '2': case 'r':
97818fd37a7SXin LI 	  lf_copy (right, rlen, outfile);
97918fd37a7SXin LI 	  lf_skip (left, llen);
98018fd37a7SXin LI 	  return true;
98118fd37a7SXin LI 	case 's':
98218fd37a7SXin LI 	  suppress_common_lines = true;
98318fd37a7SXin LI 	  break;
98418fd37a7SXin LI 	case 'v':
98518fd37a7SXin LI 	  suppress_common_lines = false;
98618fd37a7SXin LI 	  break;
98718fd37a7SXin LI 	case 'q':
98818fd37a7SXin LI 	  return false;
98918fd37a7SXin LI 	case 'e':
99018fd37a7SXin LI 	  {
99118fd37a7SXin LI 	    int fd;
99218fd37a7SXin LI 
99318fd37a7SXin LI 	    if (tmpname)
99418fd37a7SXin LI 	      tmp = fopen (tmpname, "w");
99518fd37a7SXin LI 	    else
99618fd37a7SXin LI 	      {
99718fd37a7SXin LI 		if ((fd = temporary_file ()) < 0)
99818fd37a7SXin LI 		  perror_fatal ("mkstemp");
99918fd37a7SXin LI 		tmp = fdopen (fd, "w");
100018fd37a7SXin LI 	      }
100118fd37a7SXin LI 
100218fd37a7SXin LI 	    if (! tmp)
100318fd37a7SXin LI 	      perror_fatal (tmpname);
100418fd37a7SXin LI 
100518fd37a7SXin LI 	    switch (cmd1)
100618fd37a7SXin LI 	      {
100718fd37a7SXin LI 	      case 'd':
100818fd37a7SXin LI 		if (llen)
100918fd37a7SXin LI 		  {
101018fd37a7SXin LI 		    if (llen == 1)
101118fd37a7SXin LI 		      fprintf (tmp, "--- %s %ld\n", lname, (long int) lline);
101218fd37a7SXin LI 		    else
101318fd37a7SXin LI 		      fprintf (tmp, "--- %s %ld,%ld\n", lname,
101418fd37a7SXin LI 			       (long int) lline,
101518fd37a7SXin LI 			       (long int) (lline + llen - 1));
101618fd37a7SXin LI 		  }
101718fd37a7SXin LI 		/* Fall through.  */
1018b61bbb70SXin LI 	      case '1': case 'b': case 'l':
101918fd37a7SXin LI 		lf_copy (left, llen, tmp);
102018fd37a7SXin LI 		break;
102118fd37a7SXin LI 
102218fd37a7SXin LI 	      default:
102318fd37a7SXin LI 		lf_skip (left, llen);
102418fd37a7SXin LI 		break;
102518fd37a7SXin LI 	      }
102618fd37a7SXin LI 
102718fd37a7SXin LI 	    switch (cmd1)
102818fd37a7SXin LI 	      {
102918fd37a7SXin LI 	      case 'd':
103018fd37a7SXin LI 		if (rlen)
103118fd37a7SXin LI 		  {
103218fd37a7SXin LI 		    if (rlen == 1)
103318fd37a7SXin LI 		      fprintf (tmp, "+++ %s %ld\n", rname, (long int) rline);
103418fd37a7SXin LI 		    else
103518fd37a7SXin LI 		      fprintf (tmp, "+++ %s %ld,%ld\n", rname,
103618fd37a7SXin LI 			       (long int) rline,
103718fd37a7SXin LI 			       (long int) (rline + rlen - 1));
103818fd37a7SXin LI 		  }
103918fd37a7SXin LI 		/* Fall through.  */
1040b61bbb70SXin LI 	      case '2': case 'b': case 'r':
104118fd37a7SXin LI 		lf_copy (right, rlen, tmp);
104218fd37a7SXin LI 		break;
104318fd37a7SXin LI 
104418fd37a7SXin LI 	      default:
104518fd37a7SXin LI 		lf_skip (right, rlen);
104618fd37a7SXin LI 		break;
104718fd37a7SXin LI 	      }
104818fd37a7SXin LI 
104918fd37a7SXin LI 	    ck_fclose (tmp);
105018fd37a7SXin LI 
105118fd37a7SXin LI 	    {
105218fd37a7SXin LI 	      int wstatus;
105318fd37a7SXin LI 	      int werrno = 0;
105418fd37a7SXin LI 	      ignore_SIGINT = true;
105518fd37a7SXin LI 	      checksigs ();
105618fd37a7SXin LI 
105718fd37a7SXin LI 	      {
105818fd37a7SXin LI #if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
105918fd37a7SXin LI 		char *command =
106018fd37a7SXin LI 		  xmalloc (quote_system_arg (0, editor_program)
106118fd37a7SXin LI 			   + 1 + strlen (tmpname) + 1);
106218fd37a7SXin LI 		sprintf (command + quote_system_arg (command, editor_program),
106318fd37a7SXin LI 			 " %s", tmpname);
106418fd37a7SXin LI 		wstatus = system (command);
106518fd37a7SXin LI 		if (wstatus == -1)
106618fd37a7SXin LI 		  werrno = errno;
106718fd37a7SXin LI 		free (command);
106818fd37a7SXin LI #else
106918fd37a7SXin LI 		pid_t pid;
107018fd37a7SXin LI 
107118fd37a7SXin LI 		pid = vfork ();
107218fd37a7SXin LI 		if (pid == 0)
107318fd37a7SXin LI 		  {
107418fd37a7SXin LI 		    char const *argv[3];
107518fd37a7SXin LI 		    int i = 0;
107618fd37a7SXin LI 
107718fd37a7SXin LI 		    argv[i++] = editor_program;
107818fd37a7SXin LI 		    argv[i++] = tmpname;
107918fd37a7SXin LI 		    argv[i] = 0;
108018fd37a7SXin LI 
108118fd37a7SXin LI 		    execvp (editor_program, (char **) argv);
108218fd37a7SXin LI 		    _exit (errno == ENOENT ? 127 : 126);
108318fd37a7SXin LI 		  }
108418fd37a7SXin LI 
108518fd37a7SXin LI 		if (pid < 0)
108618fd37a7SXin LI 		  perror_fatal ("fork");
108718fd37a7SXin LI 
108818fd37a7SXin LI 		while (waitpid (pid, &wstatus, 0) < 0)
108918fd37a7SXin LI 		  if (errno == EINTR)
109018fd37a7SXin LI 		    checksigs ();
109118fd37a7SXin LI 		  else
109218fd37a7SXin LI 		    perror_fatal ("waitpid");
109318fd37a7SXin LI #endif
109418fd37a7SXin LI 	      }
109518fd37a7SXin LI 
109618fd37a7SXin LI 	      ignore_SIGINT = false;
109718fd37a7SXin LI 	      check_child_status (werrno, wstatus, EXIT_SUCCESS,
109818fd37a7SXin LI 				  editor_program);
109918fd37a7SXin LI 	    }
110018fd37a7SXin LI 
110118fd37a7SXin LI 	    {
110218fd37a7SXin LI 	      char buf[SDIFF_BUFSIZE];
110318fd37a7SXin LI 	      size_t size;
110418fd37a7SXin LI 	      tmp = ck_fopen (tmpname, "r");
110518fd37a7SXin LI 	      while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
110618fd37a7SXin LI 		{
110718fd37a7SXin LI 		  checksigs ();
110818fd37a7SXin LI 		  ck_fwrite (buf, size, outfile);
110918fd37a7SXin LI 		}
111018fd37a7SXin LI 	      ck_fclose (tmp);
111118fd37a7SXin LI 	    }
111218fd37a7SXin LI 	    return true;
111318fd37a7SXin LI 	  }
111418fd37a7SXin LI 	default:
111518fd37a7SXin LI 	  give_help ();
111618fd37a7SXin LI 	  break;
111718fd37a7SXin LI 	}
111818fd37a7SXin LI     }
111918fd37a7SXin LI }
112018fd37a7SXin LI 
112118fd37a7SXin LI /* Alternately reveal bursts of diff output and handle user commands.  */
112218fd37a7SXin LI static bool
interact(struct line_filter * diff,struct line_filter * left,char const * lname,struct line_filter * right,char const * rname,FILE * outfile)112318fd37a7SXin LI interact (struct line_filter *diff,
112418fd37a7SXin LI 	  struct line_filter *left, char const *lname,
112518fd37a7SXin LI 	  struct line_filter *right, char const *rname,
112618fd37a7SXin LI 	  FILE *outfile)
112718fd37a7SXin LI {
112818fd37a7SXin LI   lin lline = 1, rline = 1;
112918fd37a7SXin LI 
113018fd37a7SXin LI   for (;;)
113118fd37a7SXin LI     {
113218fd37a7SXin LI       char diff_help[256];
113318fd37a7SXin LI       int snarfed = lf_snarf (diff, diff_help, sizeof diff_help);
113418fd37a7SXin LI 
113518fd37a7SXin LI       if (snarfed <= 0)
113618fd37a7SXin LI 	return snarfed != 0;
113718fd37a7SXin LI 
113818fd37a7SXin LI       checksigs ();
113918fd37a7SXin LI 
114018fd37a7SXin LI       if (diff_help[0] == ' ')
114118fd37a7SXin LI 	puts (diff_help + 1);
114218fd37a7SXin LI       else
114318fd37a7SXin LI 	{
114418fd37a7SXin LI 	  char *numend;
114518fd37a7SXin LI 	  uintmax_t val;
114618fd37a7SXin LI 	  lin llen, rlen, lenmax;
114718fd37a7SXin LI 	  errno = 0;
114818fd37a7SXin LI 	  llen = val = strtoumax (diff_help + 1, &numend, 10);
114918fd37a7SXin LI 	  if (llen < 0 || llen != val || errno || *numend != ',')
115018fd37a7SXin LI 	    fatal (diff_help);
115118fd37a7SXin LI 	  rlen = val = strtoumax (numend + 1, &numend, 10);
115218fd37a7SXin LI 	  if (rlen < 0 || rlen != val || errno || *numend)
115318fd37a7SXin LI 	    fatal (diff_help);
115418fd37a7SXin LI 
115518fd37a7SXin LI 	  lenmax = MAX (llen, rlen);
115618fd37a7SXin LI 
115718fd37a7SXin LI 	  switch (diff_help[0])
115818fd37a7SXin LI 	    {
115918fd37a7SXin LI 	    case 'i':
116018fd37a7SXin LI 	      if (suppress_common_lines)
116118fd37a7SXin LI 		lf_skip (diff, lenmax);
116218fd37a7SXin LI 	      else
116318fd37a7SXin LI 		lf_copy (diff, lenmax, stdout);
116418fd37a7SXin LI 
116518fd37a7SXin LI 	      lf_copy (left, llen, outfile);
116618fd37a7SXin LI 	      lf_skip (right, rlen);
116718fd37a7SXin LI 	      break;
116818fd37a7SXin LI 
116918fd37a7SXin LI 	    case 'c':
117018fd37a7SXin LI 	      lf_copy (diff, lenmax, stdout);
117118fd37a7SXin LI 	      if (! edit (left, lname, lline, llen,
117218fd37a7SXin LI 			  right, rname, rline, rlen,
117318fd37a7SXin LI 			  outfile))
117418fd37a7SXin LI 		return false;
117518fd37a7SXin LI 	      break;
117618fd37a7SXin LI 
117718fd37a7SXin LI 	    default:
117818fd37a7SXin LI 	      fatal (diff_help);
117918fd37a7SXin LI 	    }
118018fd37a7SXin LI 
118118fd37a7SXin LI 	  lline += llen;
118218fd37a7SXin LI 	  rline += rlen;
118318fd37a7SXin LI 	}
118418fd37a7SXin LI     }
118518fd37a7SXin LI }
118618fd37a7SXin LI 
118718fd37a7SXin LI /* Return true if DIR is an existing directory.  */
118818fd37a7SXin LI static bool
diraccess(char const * dir)118918fd37a7SXin LI diraccess (char const *dir)
119018fd37a7SXin LI {
119118fd37a7SXin LI   struct stat buf;
119218fd37a7SXin LI   return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
119318fd37a7SXin LI }
119418fd37a7SXin LI 
119518fd37a7SXin LI #ifndef P_tmpdir
119618fd37a7SXin LI # define P_tmpdir "/tmp"
119718fd37a7SXin LI #endif
119818fd37a7SXin LI #ifndef TMPDIR_ENV
119918fd37a7SXin LI # define TMPDIR_ENV "TMPDIR"
120018fd37a7SXin LI #endif
120118fd37a7SXin LI 
120218fd37a7SXin LI /* Open a temporary file and return its file descriptor.  Put into
120318fd37a7SXin LI    tmpname the address of a newly allocated buffer that holds the
120418fd37a7SXin LI    file's name.  Use the prefix "sdiff".  */
120518fd37a7SXin LI static int
temporary_file(void)120618fd37a7SXin LI temporary_file (void)
120718fd37a7SXin LI {
120818fd37a7SXin LI   char const *tmpdir = getenv (TMPDIR_ENV);
120918fd37a7SXin LI   char const *dir = tmpdir ? tmpdir : P_tmpdir;
121018fd37a7SXin LI   char *buf = xmalloc (strlen (dir) + 1 + 5 + 6 + 1);
121118fd37a7SXin LI   int fd;
121218fd37a7SXin LI   int e;
121318fd37a7SXin LI   sigset_t procmask;
121418fd37a7SXin LI   sigset_t blocked;
121518fd37a7SXin LI   sprintf (buf, "%s/sdiffXXXXXX", dir);
121618fd37a7SXin LI   sigemptyset (&blocked);
121718fd37a7SXin LI   sigaddset (&blocked, SIGINT);
121818fd37a7SXin LI   sigprocmask (SIG_BLOCK, &blocked, &procmask);
121918fd37a7SXin LI   fd = mkstemp (buf);
122018fd37a7SXin LI   e = errno;
122118fd37a7SXin LI   if (0 <= fd)
122218fd37a7SXin LI     tmpname = buf;
122318fd37a7SXin LI   sigprocmask (SIG_SETMASK, &procmask, 0);
122418fd37a7SXin LI   errno = e;
122518fd37a7SXin LI   return fd;
122618fd37a7SXin LI }
1227