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