1
/* sdiff - side-by-side merge of file differences
3
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 2001, 2002 Free
4
Software Foundation, Inc.
6
This file is part of GNU DIFF.
8
GNU DIFF is free software; you can redistribute it and/or modify
9
it under the terms of the GNU General Public License as published by
10
the Free Software Foundation; either version 2, or (at your option)
13
GNU DIFF is distributed in the hope that it will be useful,
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
See the GNU General Public License for more details.
18
You should have received a copy of the GNU General Public License
19
along with this program; see the file COPYING.
20
If not, write to the Free Software Foundation,
21
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
35
static char const authorship_msgid[] = N_("Written by Thomas Lord.");
37
static char const copyright_string[] =
38
"Copyright (C) 2002 Free Software Foundation, Inc.";
40
extern char const version_string[];
42
/* Size of chunks read from files which must be parsed into lines. */
43
#define SDIFF_BUFSIZE ((size_t) 65536)
47
static char const *editor_program = DEFAULT_EDITOR_PROGRAM;
48
static char const **diffargv;
50
static char * volatile tmpname;
53
#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
54
static pid_t volatile diffpid;
59
static RETSIGTYPE catchsig (int);
60
static bool edit (struct line_filter *, char const *, lin, lin, struct line_filter *, char const *, lin, lin, FILE *);
61
static bool interact (struct line_filter *, struct line_filter *, char const *, struct line_filter *, char const *, FILE *);
62
static void checksigs (void);
63
static void diffarg (char const *);
64
static void fatal (char const *) __attribute__((noreturn));
65
static void perror_fatal (char const *) __attribute__((noreturn));
66
static void trapsigs (void);
67
static void untrapsig (int);
69
#define NUM_SIGS (sizeof sigs / sizeof *sigs)
70
static int const sigs[] = {
89
#define handler_index_of_SIGINT (NUM_SIGS - 2)
90
#define handler_index_of_SIGPIPE (NUM_SIGS - 1)
93
/* Prefer `sigaction' if available, since `signal' can lose signals. */
94
static struct sigaction initial_action[NUM_SIGS];
95
# define initial_handler(i) (initial_action[i].sa_handler)
96
static void signal_handler (int, RETSIGTYPE (*) (int));
98
static RETSIGTYPE (*initial_action[NUM_SIGS]) ();
99
# define initial_handler(i) (initial_action[i])
100
# define signal_handler(sig, handler) signal (sig, handler)
103
#if ! HAVE_SIGPROCMASK
104
# define sigset_t int
105
# define sigemptyset(s) (*(s) = 0)
107
# define sigmask(sig) (1 << ((sig) - 1))
109
# define sigaddset(s, sig) (*(s) |= sigmask (sig))
114
# define SIG_SETMASK (! SIG_BLOCK)
116
# define sigprocmask(how, n, o) \
117
((how) == SIG_BLOCK ? *(o) = sigblock (*(n)) : sigsetmask (*(n)))
120
static bool diraccess (char const *);
121
static int temporary_file (void);
125
/* Name of output file if -o specified. */
126
static char const *output;
128
/* Do not print common lines. */
129
static bool suppress_common_lines;
131
/* Value for the long option that does not have single-letter equivalents. */
134
DIFF_PROGRAM_OPTION = CHAR_MAX + 1,
136
STRIP_TRAILING_CR_OPTION
139
static struct option const longopts[] =
141
{"diff-program", 1, 0, DIFF_PROGRAM_OPTION},
142
{"expand-tabs", 0, 0, 't'},
143
{"help", 0, 0, HELP_OPTION},
144
{"ignore-all-space", 0, 0, 'W'}, /* swap W and w for historical reasons */
145
{"ignore-blank-lines", 0, 0, 'B'},
146
{"ignore-case", 0, 0, 'i'},
147
{"ignore-matching-lines", 1, 0, 'I'},
148
{"ignore-space-change", 0, 0, 'b'},
149
{"ignore-tab-expansion", 0, 0, 'E'},
150
{"left-column", 0, 0, 'l'},
151
{"minimal", 0, 0, 'd'},
152
{"output", 1, 0, 'o'},
153
{"speed-large-files", 0, 0, 'H'},
154
{"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
155
{"suppress-common-lines", 0, 0, 's'},
157
{"version", 0, 0, 'v'},
158
{"width", 1, 0, 'w'},
162
static void try_help (char const *, char const *) __attribute__((noreturn));
164
try_help (char const *reason_msgid, char const *operand)
167
error (0, 0, _(reason_msgid), operand);
168
error (EXIT_TROUBLE, 0, _("Try `%s --help' for more information."),
177
fatal ("write failed");
178
else if (fclose (stdout) != 0)
179
perror_fatal (_("standard output"));
182
static char const * const option_help_msgid[] = {
183
N_("-o FILE --output=FILE Operate interactively, sending output to FILE."),
185
N_("-i --ignore-case Consider upper- and lower-case to be the same."),
186
N_("-E --ignore-tab-expansion Ignore changes due to tab expansion."),
187
N_("-b --ignore-space-change Ignore changes in the amount of white space."),
188
N_("-W --ignore-all-space Ignore all white space."),
189
N_("-B --ignore-blank-lines Ignore changes whose lines are all blank."),
190
N_("-I RE --ignore-matching-lines=RE Ignore changes whose lines all match RE."),
191
N_("--strip-trailing-cr Strip trailing carriage return on input."),
192
N_("-a --text Treat all files as text."),
194
N_("-w NUM --width=NUM Output at most NUM (default 130) columns per line."),
195
N_("-l --left-column Output only the left column of common lines."),
196
N_("-s --suppress-common-lines Do not output common lines."),
198
N_("-t --expand-tabs Expand tabs to spaces in output."),
200
N_("-d --minimal Try hard to find a smaller set of changes."),
201
N_("-H --speed-large-files Assume large files and many scattered small changes."),
202
N_("--diff-program=PROGRAM Use PROGRAM to compare files."),
204
N_("-v --version Output version info."),
205
N_("--help Output this help."),
212
char const * const *p;
214
printf (_("Usage: %s [OPTION]... FILE1 FILE2\n"), program_name);
215
printf ("%s\n\n", _("Side-by-side merge of file differences."));
216
for (p = option_help_msgid; *p; p++)
218
printf (" %s\n", _(*p));
221
printf ("\n%s\n\n%s\n",
222
_("If a FILE is `-', read standard input."),
223
_("Report bugs to <bug-gnu-utils@gnu.org>."));
229
#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
231
kill (diffpid, SIGPIPE);
237
static void exiterr (void) __attribute__((noreturn));
248
fatal (char const *msgid)
250
error (0, 0, "%s", _(msgid));
255
perror_fatal (char const *msg)
259
error (0, e, "%s", msg);
264
ck_editor_status (int errnum, int status)
268
char const *failure_msgid = N_("subsidiary program `%s' failed");
269
if (! errnum && WIFEXITED (status))
270
switch (WEXITSTATUS (status))
273
failure_msgid = N_("subsidiary program `%s' not executable");
276
failure_msgid = N_("subsidiary program `%s' not found");
279
error (0, errnum, _(failure_msgid), editor_program);
285
ck_fopen (char const *fname, char const *type)
287
FILE *r = fopen (fname, type);
289
perror_fatal (fname);
297
perror_fatal ("fclose");
301
ck_fread (char *buf, size_t size, FILE *f)
303
size_t r = fread (buf, sizeof (char), size, f);
304
if (r == 0 && ferror (f))
305
perror_fatal (_("read failed"));
310
ck_fwrite (char const *buf, size_t size, FILE *f)
312
if (fwrite (buf, sizeof (char), size, f) != size)
313
perror_fatal (_("write failed"));
320
perror_fatal (_("write failed"));
324
expand_name (char *name, bool is_dir, char const *other_name)
326
if (strcmp (name, "-") == 0)
327
fatal ("cannot interactively merge standard input");
332
/* Yield NAME/BASE, where BASE is OTHER_NAME's basename. */
333
char const *base = base_name (other_name);
334
size_t namelen = strlen (name), baselen = strlen (base);
335
bool insert_slash = *base_name (name) && name[namelen - 1] != '/';
336
char *r = xmalloc (namelen + insert_slash + baselen + 1);
337
memcpy (r, name, namelen);
339
memcpy (r + namelen + insert_slash, base, baselen + 1);
354
lf_init (struct line_filter *lf, FILE *infile)
357
lf->bufpos = lf->buffer = lf->buflim = xmalloc (SDIFF_BUFSIZE + 1);
358
lf->buflim[0] = '\n';
361
/* Fill an exhausted line_filter buffer from its INFILE */
363
lf_refill (struct line_filter *lf)
365
size_t s = ck_fread (lf->buffer, SDIFF_BUFSIZE, lf->infile);
366
lf->bufpos = lf->buffer;
367
lf->buflim = lf->buffer + s;
368
lf->buflim[0] = '\n';
373
/* Advance LINES on LF's infile, copying lines to OUTFILE */
375
lf_copy (struct line_filter *lf, lin lines, FILE *outfile)
377
char *start = lf->bufpos;
381
lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
384
ck_fwrite (start, lf->buflim - start, outfile);
385
if (! lf_refill (lf))
396
ck_fwrite (start, lf->bufpos - start, outfile);
399
/* Advance LINES on LF's infile without doing output */
401
lf_skip (struct line_filter *lf, lin lines)
405
lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
408
if (! lf_refill (lf))
419
/* Snarf a line into a buffer. Return EOF if EOF, 0 if error, 1 if OK. */
421
lf_snarf (struct line_filter *lf, char *buffer, size_t bufsize)
425
char *start = lf->bufpos;
426
char *next = (char *) memchr (start, '\n', lf->buflim + 1 - start);
427
size_t s = next - start;
430
memcpy (buffer, start, s);
431
if (next < lf->buflim)
434
lf->bufpos = next + 1;
437
if (! lf_refill (lf))
447
main (int argc, char *argv[])
452
exit_failure = EXIT_TROUBLE;
453
initialize_main (&argc, &argv);
454
program_name = argv[0];
455
setlocale (LC_ALL, "");
456
bindtextdomain (PACKAGE, LOCALEDIR);
457
textdomain (PACKAGE);
458
c_stack_action (c_stack_die);
460
prog = getenv ("EDITOR");
462
editor_program = prog;
464
diffarg (DEFAULT_DIFF_PROGRAM);
466
/* parse command line args */
467
while ((opt = getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, 0))
506
diffarg ("--left-column");
514
suppress_common_lines = 1;
522
printf ("sdiff %s\n%s\n\n%s\n\n%s\n",
523
version_string, copyright_string,
524
_(free_software_msgid), _(authorship_msgid));
537
case DIFF_PROGRAM_OPTION:
538
diffargv[0] = optarg;
546
case STRIP_TRAILING_CR_OPTION:
547
diffarg ("--strip-trailing-cr");
555
if (argc - optind != 2)
557
if (argc - optind < 2)
558
try_help ("missing operand after `%s'", argv[argc - 1]);
560
try_help ("extra operand `%s'", argv[optind + 2]);
565
/* easy case: diff does everything for us */
566
if (suppress_common_lines)
567
diffarg ("--suppress-common-lines");
570
diffarg (argv[optind]);
571
diffarg (argv[optind + 1]);
573
execvp (diffargv[0], (char **) diffargv);
574
perror_fatal (diffargv[0]);
578
char const *lname, *rname;
579
FILE *left, *right, *out, *diffout;
581
struct line_filter lfilt;
582
struct line_filter rfilt;
583
struct line_filter diff_filt;
584
bool leftdir = diraccess (argv[optind]);
585
bool rightdir = diraccess (argv[optind + 1]);
587
if (leftdir & rightdir)
588
fatal ("both files to be compared are directories");
590
lname = expand_name (argv[optind], leftdir, argv[optind + 1]);
591
left = ck_fopen (lname, "r");
592
rname = expand_name (argv[optind + 1], rightdir, argv[optind]);
593
right = ck_fopen (rname, "r");
594
out = ck_fopen (output, "w");
596
diffarg ("--sdiff-merge-assist");
598
diffarg (argv[optind]);
599
diffarg (argv[optind + 1]);
604
#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
610
for (i = 0; diffargv[i]; i++)
611
cmdsize += quote_system_arg (0, diffargv[i]) + 1;
612
command = p = xmalloc (cmdsize);
613
for (i = 0; diffargv[i]; i++)
615
p += quote_system_arg (p, diffargv[i]);
620
diffout = popen (command, "r");
622
perror_fatal (command);
628
# if HAVE_WORKING_VFORK
633
if (pipe (diff_fds) != 0)
634
perror_fatal ("pipe");
636
# if HAVE_WORKING_VFORK
637
/* Block SIGINT and SIGPIPE. */
638
sigemptyset (&blocked);
639
sigaddset (&blocked, SIGINT);
640
sigaddset (&blocked, SIGPIPE);
641
sigprocmask (SIG_BLOCK, &blocked, &procmask);
645
perror_fatal ("fork");
648
/* Alter the child's SIGINT and SIGPIPE handlers;
649
this may munge the parent.
650
The child ignores SIGINT in case the user interrupts the editor.
651
The child does not ignore SIGPIPE, even if the parent does. */
652
if (initial_handler (handler_index_of_SIGINT) != SIG_IGN)
653
signal_handler (SIGINT, SIG_IGN);
654
signal_handler (SIGPIPE, SIG_DFL);
655
# if HAVE_WORKING_VFORK
656
/* Stop blocking SIGINT and SIGPIPE in the child. */
657
sigprocmask (SIG_SETMASK, &procmask, 0);
660
if (diff_fds[1] != STDOUT_FILENO)
662
dup2 (diff_fds[1], STDOUT_FILENO);
666
execvp (diffargv[0], (char **) diffargv);
667
_exit (errno == ENOEXEC ? 126 : 127);
670
# if HAVE_WORKING_VFORK
671
/* Restore the parent's SIGINT and SIGPIPE behavior. */
672
if (initial_handler (handler_index_of_SIGINT) != SIG_IGN)
673
signal_handler (SIGINT, catchsig);
674
if (initial_handler (handler_index_of_SIGPIPE) != SIG_IGN)
675
signal_handler (SIGPIPE, catchsig);
677
signal_handler (SIGPIPE, SIG_IGN);
679
/* Stop blocking SIGINT and SIGPIPE in the parent. */
680
sigprocmask (SIG_SETMASK, &procmask, 0);
684
diffout = fdopen (diff_fds[0], "r");
686
perror_fatal ("fdopen");
690
lf_init (&diff_filt, diffout);
691
lf_init (&lfilt, left);
692
lf_init (&rfilt, right);
694
interact_ok = interact (&diff_filt, &lfilt, lname, &rfilt, rname, out);
704
#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
705
wstatus = pclose (diffout);
710
while (waitpid (diffpid, &wstatus, 0) < 0)
714
perror_fatal ("waitpid");
727
ck_editor_status (werrno, wstatus);
730
exit (WEXITSTATUS (wstatus));
733
return EXIT_SUCCESS; /* Fool `-Wall'. */
737
diffarg (char const *a)
739
static size_t diffargs, diffarglim;
741
if (diffargs == diffarglim)
745
else if (PTRDIFF_MAX / (2 * sizeof *diffargv) <= diffarglim)
749
diffargv = xrealloc (diffargv, diffarglim * sizeof *diffargv);
751
diffargv[diffargs++] = a;
757
/* Signal handling */
759
static bool volatile ignore_SIGINT;
760
static int volatile signal_received;
761
static bool sigs_trapped;
769
if (! (s == SIGINT && ignore_SIGINT))
774
static struct sigaction catchaction;
777
signal_handler (int sig, RETSIGTYPE (*handler) (int))
779
catchaction.sa_handler = handler;
780
sigaction (sig, &catchaction, 0);
790
catchaction.sa_flags = SA_RESTART;
791
sigemptyset (&catchaction.sa_mask);
792
for (i = 0; i < NUM_SIGS; i++)
793
sigaddset (&catchaction.sa_mask, sigs[i]);
796
for (i = 0; i < NUM_SIGS; i++)
799
sigaction (sigs[i], 0, &initial_action[i]);
801
initial_action[i] = signal (sigs[i], SIG_IGN);
803
if (initial_handler (i) != SIG_IGN)
804
signal_handler (sigs[i], catchsig);
808
/* System V fork+wait does not work if SIGCHLD is ignored. */
809
signal (SIGCHLD, SIG_DFL);
815
/* Untrap signal S, or all trapped signals if S is zero. */
822
for (i = 0; i < NUM_SIGS; i++)
823
if ((! s || sigs[i] == s) && initial_handler (i) != SIG_IGN)
825
sigaction (sigs[i], &initial_action[i], 0);
827
signal (sigs[i], initial_action[i]);
831
/* Exit if a signal has been received. */
835
int s = signal_received;
840
/* Yield an exit status indicating that a signal was received. */
844
/* That didn't work, so exit with error status. */
853
fprintf (stderr, "%s", _("\
854
ed:\tEdit then use both versions, each decorated with a header.\n\
855
eb:\tEdit then use both versions.\n\
856
el:\tEdit then use the left version.\n\
857
er:\tEdit then use the right version.\n\
858
e:\tEdit a new version.\n\
859
l:\tUse the left version.\n\
860
r:\tUse the right version.\n\
861
s:\tSilently include common lines.\n\
862
v:\tVerbosely include common lines.\n\
874
if (! ISSPACE (c) || c == '\n')
879
perror_fatal (_("read failed"));
887
while ((c = getchar ()) != '\n' && c != EOF)
890
perror_fatal (_("read failed"));
894
/* interpret an edit command */
896
edit (struct line_filter *left, char const *lname, lin lline, lin llen,
897
struct line_filter *right, char const *rname, lin rline, lin rlen,
905
cmd1 = 0; /* Pacify `gcc -W'. */
909
if (putchar ('%') != '%')
910
perror_fatal (_("write failed"));
913
cmd0 = skip_white ();
916
case 'l': case 'r': case 's': case 'v': case 'q':
917
if (skip_white () != '\n')
927
cmd1 = skip_white ();
930
case 'b': case 'd': case 'l': case 'r':
931
if (skip_white () != '\n')
969
lf_copy (left, llen, outfile);
970
lf_skip (right, rlen);
973
lf_copy (right, rlen, outfile);
974
lf_skip (left, llen);
977
suppress_common_lines = 1;
980
suppress_common_lines = 0;
989
tmp = fopen (tmpname, "w");
992
if ((fd = temporary_file ()) < 0)
993
perror_fatal ("mkstemp");
994
tmp = fdopen (fd, "w");
998
perror_fatal (tmpname);
1006
fprintf (tmp, "--- %s %ld\n", lname, (long) lline);
1008
fprintf (tmp, "--- %s %ld,%ld\n", lname,
1009
(long) lline, (long) (lline + llen - 1));
1013
lf_copy (left, llen, tmp);
1017
lf_skip (left, llen);
1027
fprintf (tmp, "+++ %s %ld\n", rname, (long) rline);
1029
fprintf (tmp, "+++ %s %ld,%ld\n", rname,
1030
(long) rline, (long) (rline + rlen - 1));
1034
lf_copy (right, rlen, tmp);
1038
lf_skip (right, rlen);
1051
#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
1053
xmalloc (quote_system_arg (0, editor_program)
1054
+ 1 + strlen (tmpname) + 1);
1055
sprintf (command + quote_system_arg (command, editor_program),
1057
wstatus = system (command);
1067
char const *argv[3];
1070
argv[i++] = editor_program;
1071
argv[i++] = tmpname;
1074
execvp (editor_program, (char **) argv);
1075
_exit (errno == ENOEXEC ? 126 : 127);
1079
perror_fatal ("fork");
1081
while (waitpid (pid, &wstatus, 0) < 0)
1085
perror_fatal ("waitpid");
1090
ck_editor_status (werrno, wstatus);
1094
char buf[SDIFF_BUFSIZE];
1096
tmp = ck_fopen (tmpname, "r");
1097
while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
1100
ck_fwrite (buf, size, outfile);
1115
/* Alternately reveal bursts of diff output and handle user commands. */
1117
interact (struct line_filter *diff,
1118
struct line_filter *left, char const *lname,
1119
struct line_filter *right, char const *rname,
1122
lin lline = 1, rline = 1;
1126
char diff_help[256];
1127
int snarfed = lf_snarf (diff, diff_help, sizeof diff_help);
1130
return snarfed != 0;
1134
if (diff_help[0] == ' ')
1135
puts (diff_help + 1);
1140
lin llen, rlen, lenmax;
1142
llen = val = strtoumax (diff_help + 1, &numend, 10);
1143
if (llen < 0 || llen != val || errno || *numend != ',')
1145
rlen = val = strtoumax (numend + 1, &numend, 10);
1146
if (rlen < 0 || rlen != val || errno || *numend)
1149
lenmax = MAX (llen, rlen);
1151
switch (diff_help[0])
1154
if (suppress_common_lines)
1155
lf_skip (diff, lenmax);
1157
lf_copy (diff, lenmax, stdout);
1159
lf_copy (left, llen, outfile);
1160
lf_skip (right, rlen);
1164
lf_copy (diff, lenmax, stdout);
1165
if (! edit (left, lname, lline, llen,
1166
right, rname, rline, rlen,
1181
/* Return nonzero if DIR is an existing directory. */
1183
diraccess (char const *dir)
1186
return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
1190
# define P_tmpdir "/tmp"
1193
# define TMPDIR_ENV "TMPDIR"
1196
/* Open a temporary file and return its file descriptor. Put into
1197
tmpname the address of a newly allocated buffer that holds the
1198
file's name. Use the prefix "sdiff". */
1200
temporary_file (void)
1202
char const *tmpdir = getenv (TMPDIR_ENV);
1203
char const *dir = tmpdir ? tmpdir : P_tmpdir;
1204
char *buf = xmalloc (strlen (dir) + 1 + 5 + 6 + 1);
1209
sprintf (buf, "%s/sdiffXXXXXX", dir);
1210
sigemptyset (&blocked);
1211
sigaddset (&blocked, SIGINT);
1212
sigprocmask (SIG_BLOCK, &blocked, &procmask);
1217
sigprocmask (SIG_SETMASK, &procmask, 0);