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