1220Snever/* sdiff - side-by-side merge of file differences 28346Smhaupt 3220Snever Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 2001, 2002 Free 4220Snever Software Foundation, Inc. 5220Snever 6220Snever This file is part of GNU DIFF. 7220Snever 8220Snever GNU DIFF is free software; you can redistribute it and/or modify 9220Snever it under the terms of the GNU General Public License as published by 10220Snever the Free Software Foundation; either version 2, or (at your option) 11220Snever any later version. 12220Snever 13220Snever GNU DIFF is distributed in the hope that it will be useful, 14220Snever but WITHOUT ANY WARRANTY; without even the implied warranty of 15220Snever MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 16220Snever See the GNU General Public License for more details. 17220Snever 18220Snever You should have received a copy of the GNU General Public License 191472Strims along with this program; see the file COPYING. 201472Strims If not, write to the Free Software Foundation, 211472Strims 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 22220Snever 23220Snever#include "system.h" 24220Snever 25220Snever#include <c-stack.h> 26220Snever#include <dirname.h> 27220Snever#include <error.h> 28220Snever#include <exitfail.h> 29220Snever#include <freesoft.h> 30220Snever#include <getopt.h> 31220Snever#include <quotesys.h> 32220Snever#include <stdio.h> 33220Snever#include <xalloc.h> 34220Snever 35220Sneverstatic char const authorship_msgid[] = N_("Written by Thomas Lord."); 36220Snever 37220Sneverstatic char const copyright_string[] = 38220Snever "Copyright (C) 2002 Free Software Foundation, Inc."; 39220Snever 40220Sneverextern char const version_string[]; 41220Snever 428346Smhaupt/* Size of chunks read from files which must be parsed into lines. */ 438346Smhaupt#define SDIFF_BUFSIZE ((size_t) 65536) 44220Snever 45char *program_name; 46 47static char const *editor_program = DEFAULT_EDITOR_PROGRAM; 48static char const **diffargv; 49 50static char * volatile tmpname; 51static FILE *tmp; 52 53#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK 54static pid_t volatile diffpid; 55#endif 56 57struct line_filter; 58 59static RETSIGTYPE catchsig (int); 60static bool edit (struct line_filter *, char const *, lin, lin, struct line_filter *, char const *, lin, lin, FILE *); 61static bool interact (struct line_filter *, struct line_filter *, char const *, struct line_filter *, char const *, FILE *); 62static void checksigs (void); 63static void diffarg (char const *); 64static void fatal (char const *) __attribute__((noreturn)); 65static void perror_fatal (char const *) __attribute__((noreturn)); 66static void trapsigs (void); 67static void untrapsig (int); 68 69#define NUM_SIGS (sizeof sigs / sizeof *sigs) 70static int const sigs[] = { 71#ifdef SIGHUP 72 SIGHUP, 73#endif 74#ifdef SIGQUIT 75 SIGQUIT, 76#endif 77#ifdef SIGTERM 78 SIGTERM, 79#endif 80#ifdef SIGXCPU 81 SIGXCPU, 82#endif 83#ifdef SIGXFSZ 84 SIGXFSZ, 85#endif 86 SIGINT, 87 SIGPIPE 88}; 89#define handler_index_of_SIGINT (NUM_SIGS - 2) 90#define handler_index_of_SIGPIPE (NUM_SIGS - 1) 91 92#if HAVE_SIGACTION 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)); 97#else 98 static RETSIGTYPE (*initial_action[NUM_SIGS]) (); 99# define initial_handler(i) (initial_action[i]) 100# define signal_handler(sig, handler) signal (sig, handler) 101#endif 102 103#if ! HAVE_SIGPROCMASK 104# define sigset_t int 105# define sigemptyset(s) (*(s) = 0) 106# ifndef sigmask 107# define sigmask(sig) (1 << ((sig) - 1)) 108# endif 109# define sigaddset(s, sig) (*(s) |= sigmask (sig)) 110# ifndef SIG_BLOCK 111# define SIG_BLOCK 0 112# endif 113# ifndef SIG_SETMASK 114# define SIG_SETMASK (! SIG_BLOCK) 115# endif 116# define sigprocmask(how, n, o) \ 117 ((how) == SIG_BLOCK ? *(o) = sigblock (*(n)) : sigsetmask (*(n))) 118#endif 119 120static bool diraccess (char const *); 121static int temporary_file (void); 122 123/* Options: */ 124 125/* Name of output file if -o specified. */ 126static char const *output; 127 128/* Do not print common lines. */ 129static bool suppress_common_lines; 130 131/* Value for the long option that does not have single-letter equivalents. */ 132enum 133{ 134 DIFF_PROGRAM_OPTION = CHAR_MAX + 1, 135 HELP_OPTION, 136 STRIP_TRAILING_CR_OPTION 137}; 138 139static struct option const longopts[] = 140{ 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'}, 156 {"text", 0, 0, 'a'}, 157 {"version", 0, 0, 'v'}, 158 {"width", 1, 0, 'w'}, 159 {0, 0, 0, 0} 160}; 161 162static void try_help (char const *, char const *) __attribute__((noreturn)); 163static void 164try_help (char const *reason_msgid, char const *operand) 165{ 166 if (reason_msgid) 167 error (0, 0, _(reason_msgid), operand); 168 error (EXIT_TROUBLE, 0, _("Try `%s --help' for more information."), 169 program_name); 170 abort (); 171} 172 173static void 174check_stdout (void) 175{ 176 if (ferror (stdout)) 177 fatal ("write failed"); 178 else if (fclose (stdout) != 0) 179 perror_fatal (_("standard output")); 180} 181 182static char const * const option_help_msgid[] = { 183 N_("-o FILE --output=FILE Operate interactively, sending output to FILE."), 184 "", 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."), 193 "", 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."), 197 "", 198 N_("-t --expand-tabs Expand tabs to spaces in output."), 199 "", 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."), 203 "", 204 N_("-v --version Output version info."), 205 N_("--help Output this help."), 206 0 207}; 208 209static void 210usage (void) 211{ 212 char const * const *p; 213 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++) 217 if (**p) 218 printf (" %s\n", _(*p)); 219 else 220 putchar ('\n'); 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>.")); 224} 225 226static void 227cleanup (void) 228{ 229#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK 230 if (0 < diffpid) 231 kill (diffpid, SIGPIPE); 232#endif 233 if (tmpname) 234 unlink (tmpname); 235} 236 237static void exiterr (void) __attribute__((noreturn)); 238static void 239exiterr (void) 240{ 241 cleanup (); 242 untrapsig (0); 243 checksigs (); 244 exit (EXIT_TROUBLE); 245} 246 247static void 248fatal (char const *msgid) 249{ 250 error (0, 0, "%s", _(msgid)); 251 exiterr (); 252} 253 254static void 255perror_fatal (char const *msg) 256{ 257 int e = errno; 258 checksigs (); 259 error (0, e, "%s", msg); 260 exiterr (); 261} 262 263static void 264ck_editor_status (int errnum, int status) 265{ 266 if (errnum | status) 267 { 268 char const *failure_msgid = N_("subsidiary program `%s' failed"); 269 if (! errnum && WIFEXITED (status)) 270 switch (WEXITSTATUS (status)) 271 { 272 case 126: 273 failure_msgid = N_("subsidiary program `%s' not executable"); 274 break; 275 case 127: 276 failure_msgid = N_("subsidiary program `%s' not found"); 277 break; 278 } 279 error (0, errnum, _(failure_msgid), editor_program); 280 exiterr (); 281 } 282} 283 284static FILE * 285ck_fopen (char const *fname, char const *type) 286{ 287 FILE *r = fopen (fname, type); 288 if (! r) 289 perror_fatal (fname); 290 return r; 291} 292 293static void 294ck_fclose (FILE *f) 295{ 296 if (fclose (f)) 297 perror_fatal ("fclose"); 298} 299 300static size_t 301ck_fread (char *buf, size_t size, FILE *f) 302{ 303 size_t r = fread (buf, sizeof (char), size, f); 304 if (r == 0 && ferror (f)) 305 perror_fatal (_("read failed")); 306 return r; 307} 308 309static void 310ck_fwrite (char const *buf, size_t size, FILE *f) 311{ 312 if (fwrite (buf, sizeof (char), size, f) != size) 313 perror_fatal (_("write failed")); 314} 315 316static void 317ck_fflush (FILE *f) 318{ 319 if (fflush (f) != 0) 320 perror_fatal (_("write failed")); 321} 322 323static char const * 324expand_name (char *name, bool is_dir, char const *other_name) 325{ 326 if (strcmp (name, "-") == 0) 327 fatal ("cannot interactively merge standard input"); 328 if (! is_dir) 329 return name; 330 else 331 { 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); 338 r[namelen] = '/'; 339 memcpy (r + namelen + insert_slash, base, baselen + 1); 340 return r; 341 } 342} 343 344 345 346struct line_filter { 347 FILE *infile; 348 char *bufpos; 349 char *buffer; 350 char *buflim; 351}; 352 353static void 354lf_init (struct line_filter *lf, FILE *infile) 355{ 356 lf->infile = infile; 357 lf->bufpos = lf->buffer = lf->buflim = xmalloc (SDIFF_BUFSIZE + 1); 358 lf->buflim[0] = '\n'; 359} 360 361/* Fill an exhausted line_filter buffer from its INFILE */ 362static size_t 363lf_refill (struct line_filter *lf) 364{ 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'; 369 checksigs (); 370 return s; 371} 372 373/* Advance LINES on LF's infile, copying lines to OUTFILE */ 374static void 375lf_copy (struct line_filter *lf, lin lines, FILE *outfile) 376{ 377 char *start = lf->bufpos; 378 379 while (lines) 380 { 381 lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos); 382 if (! lf->bufpos) 383 { 384 ck_fwrite (start, lf->buflim - start, outfile); 385 if (! lf_refill (lf)) 386 return; 387 start = lf->bufpos; 388 } 389 else 390 { 391 --lines; 392 ++lf->bufpos; 393 } 394 } 395 396 ck_fwrite (start, lf->bufpos - start, outfile); 397} 398 399/* Advance LINES on LF's infile without doing output */ 400static void 401lf_skip (struct line_filter *lf, lin lines) 402{ 403 while (lines) 404 { 405 lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos); 406 if (! lf->bufpos) 407 { 408 if (! lf_refill (lf)) 409 break; 410 } 411 else 412 { 413 --lines; 414 ++lf->bufpos; 415 } 416 } 417} 418 419/* Snarf a line into a buffer. Return EOF if EOF, 0 if error, 1 if OK. */ 420static int 421lf_snarf (struct line_filter *lf, char *buffer, size_t bufsize) 422{ 423 for (;;) 424 { 425 char *start = lf->bufpos; 426 char *next = (char *) memchr (start, '\n', lf->buflim + 1 - start); 427 size_t s = next - start; 428 if (bufsize <= s) 429 return 0; 430 memcpy (buffer, start, s); 431 if (next < lf->buflim) 432 { 433 buffer[s] = 0; 434 lf->bufpos = next + 1; 435 return 1; 436 } 437 if (! lf_refill (lf)) 438 return s ? 0 : EOF; 439 buffer += s; 440 bufsize -= s; 441 } 442} 443 444 445 446int 447main (int argc, char *argv[]) 448{ 449 int opt; 450 char const *prog; 451 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); 459 460 prog = getenv ("EDITOR"); 461 if (prog) 462 editor_program = prog; 463 464 diffarg (DEFAULT_DIFF_PROGRAM); 465 466 /* parse command line args */ 467 while ((opt = getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, 0)) 468 != -1) 469 { 470 switch (opt) 471 { 472 case 'a': 473 diffarg ("-a"); 474 break; 475 476 case 'b': 477 diffarg ("-b"); 478 break; 479 480 case 'B': 481 diffarg ("-B"); 482 break; 483 484 case 'd': 485 diffarg ("-d"); 486 break; 487 488 case 'E': 489 diffarg ("-E"); 490 break; 491 492 case 'H': 493 diffarg ("-H"); 494 break; 495 496 case 'i': 497 diffarg ("-i"); 498 break; 499 500 case 'I': 501 diffarg ("-I"); 502 diffarg (optarg); 503 break; 504 505 case 'l': 506 diffarg ("--left-column"); 507 break; 508 509 case 'o': 510 output = optarg; 511 break; 512 513 case 's': 514 suppress_common_lines = 1; 515 break; 516 517 case 't': 518 diffarg ("-t"); 519 break; 520 521 case 'v': 522 printf ("sdiff %s\n%s\n\n%s\n\n%s\n", 523 version_string, copyright_string, 524 _(free_software_msgid), _(authorship_msgid)); 525 check_stdout (); 526 return EXIT_SUCCESS; 527 528 case 'w': 529 diffarg ("-W"); 530 diffarg (optarg); 531 break; 532 533 case 'W': 534 diffarg ("-w"); 535 break; 536 537 case DIFF_PROGRAM_OPTION: 538 diffargv[0] = optarg; 539 break; 540 541 case HELP_OPTION: 542 usage (); 543 check_stdout (); 544 return EXIT_SUCCESS; 545 546 case STRIP_TRAILING_CR_OPTION: 547 diffarg ("--strip-trailing-cr"); 548 break; 549 550 default: 551 try_help (0, 0); 552 } 553 } 554 555 if (argc - optind != 2) 556 { 557 if (argc - optind < 2) 558 try_help ("missing operand after `%s'", argv[argc - 1]); 559 else 560 try_help ("extra operand `%s'", argv[optind + 2]); 561 } 562 563 if (! output) 564 { 565 /* easy case: diff does everything for us */ 566 if (suppress_common_lines) 567 diffarg ("--suppress-common-lines"); 568 diffarg ("-y"); 569 diffarg ("--"); 570 diffarg (argv[optind]); 571 diffarg (argv[optind + 1]); 572 diffarg (0); 573 execvp (diffargv[0], (char **) diffargv); 574 perror_fatal (diffargv[0]); 575 } 576 else 577 { 578 char const *lname, *rname; 579 FILE *left, *right, *out, *diffout; 580 bool interact_ok; 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]); 586 587 if (leftdir & rightdir) 588 fatal ("both files to be compared are directories"); 589 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"); 595 596 diffarg ("--sdiff-merge-assist"); 597 diffarg ("--"); 598 diffarg (argv[optind]); 599 diffarg (argv[optind + 1]); 600 diffarg (0); 601 602 trapsigs (); 603 604#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK) 605 { 606 size_t cmdsize = 1; 607 char *p, *command; 608 int i; 609 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++) 614 { 615 p += quote_system_arg (p, diffargv[i]); 616 *p++ = ' '; 617 } 618 p[-1] = 0; 619 errno = 0; 620 diffout = popen (command, "r"); 621 if (! diffout) 622 perror_fatal (command); 623 free (command); 624 } 625#else 626 { 627 int diff_fds[2]; 628# if HAVE_WORKING_VFORK 629 sigset_t procmask; 630 sigset_t blocked; 631# endif 632 633 if (pipe (diff_fds) != 0) 634 perror_fatal ("pipe"); 635 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); 642# endif 643 diffpid = vfork (); 644 if (diffpid < 0) 645 perror_fatal ("fork"); 646 if (! diffpid) 647 { 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); 658# endif 659 close (diff_fds[0]); 660 if (diff_fds[1] != STDOUT_FILENO) 661 { 662 dup2 (diff_fds[1], STDOUT_FILENO); 663 close (diff_fds[1]); 664 } 665 666 execvp (diffargv[0], (char **) diffargv); 667 _exit (errno == ENOEXEC ? 126 : 127); 668 } 669 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); 676 else 677 signal_handler (SIGPIPE, SIG_IGN); 678 679 /* Stop blocking SIGINT and SIGPIPE in the parent. */ 680 sigprocmask (SIG_SETMASK, &procmask, 0); 681# endif 682 683 close (diff_fds[1]); 684 diffout = fdopen (diff_fds[0], "r"); 685 if (! diffout) 686 perror_fatal ("fdopen"); 687 } 688#endif 689 690 lf_init (&diff_filt, diffout); 691 lf_init (&lfilt, left); 692 lf_init (&rfilt, right); 693 694 interact_ok = interact (&diff_filt, &lfilt, lname, &rfilt, rname, out); 695 696 ck_fclose (left); 697 ck_fclose (right); 698 ck_fclose (out); 699 700 { 701 int wstatus; 702 int werrno = 0; 703 704#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK) 705 wstatus = pclose (diffout); 706 if (wstatus == -1) 707 werrno = errno; 708#else 709 ck_fclose (diffout); 710 while (waitpid (diffpid, &wstatus, 0) < 0) 711 if (errno == EINTR) 712 checksigs (); 713 else 714 perror_fatal ("waitpid"); 715 diffpid = 0; 716#endif 717 718 if (tmpname) 719 { 720 unlink (tmpname); 721 tmpname = 0; 722 } 723 724 if (! interact_ok) 725 exiterr (); 726 727 ck_editor_status (werrno, wstatus); 728 untrapsig (0); 729 checksigs (); 730 exit (WEXITSTATUS (wstatus)); 731 } 732 } 733 return EXIT_SUCCESS; /* Fool `-Wall'. */ 734} 735 736static void 737diffarg (char const *a) 738{ 739 static size_t diffargs, diffarglim; 740 741 if (diffargs == diffarglim) 742 { 743 if (! diffarglim) 744 diffarglim = 16; 745 else if (PTRDIFF_MAX / (2 * sizeof *diffargv) <= diffarglim) 746 xalloc_die (); 747 else 748 diffarglim *= 2; 749 diffargv = xrealloc (diffargv, diffarglim * sizeof *diffargv); 750 } 751 diffargv[diffargs++] = a; 752} 753 754 755 756 757/* Signal handling */ 758 759static bool volatile ignore_SIGINT; 760static int volatile signal_received; 761static bool sigs_trapped; 762 763static RETSIGTYPE 764catchsig (int s) 765{ 766#if ! HAVE_SIGACTION 767 signal (s, SIG_IGN); 768#endif 769 if (! (s == SIGINT && ignore_SIGINT)) 770 signal_received = s; 771} 772 773#if HAVE_SIGACTION 774static struct sigaction catchaction; 775 776static void 777signal_handler (int sig, RETSIGTYPE (*handler) (int)) 778{ 779 catchaction.sa_handler = handler; 780 sigaction (sig, &catchaction, 0); 781} 782#endif 783 784static void 785trapsigs (void) 786{ 787 int i; 788 789#if HAVE_SIGACTION 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]); 794#endif 795 796 for (i = 0; i < NUM_SIGS; i++) 797 { 798#if HAVE_SIGACTION 799 sigaction (sigs[i], 0, &initial_action[i]); 800#else 801 initial_action[i] = signal (sigs[i], SIG_IGN); 802#endif 803 if (initial_handler (i) != SIG_IGN) 804 signal_handler (sigs[i], catchsig); 805 } 806 807#ifdef SIGCHLD 808 /* System V fork+wait does not work if SIGCHLD is ignored. */ 809 signal (SIGCHLD, SIG_DFL); 810#endif 811 812 sigs_trapped = 1; 813} 814 815/* Untrap signal S, or all trapped signals if S is zero. */ 816static void 817untrapsig (int s) 818{ 819 int i; 820 821 if (sigs_trapped) 822 for (i = 0; i < NUM_SIGS; i++) 823 if ((! s || sigs[i] == s) && initial_handler (i) != SIG_IGN) 824#if HAVE_SIGACTION 825 sigaction (sigs[i], &initial_action[i], 0); 826#else 827 signal (sigs[i], initial_action[i]); 828#endif 829} 830 831/* Exit if a signal has been received. */ 832static void 833checksigs (void) 834{ 835 int s = signal_received; 836 if (s) 837 { 838 cleanup (); 839 840 /* Yield an exit status indicating that a signal was received. */ 841 untrapsig (s); 842 kill (getpid (), s); 843 844 /* That didn't work, so exit with error status. */ 845 exit (EXIT_TROUBLE); 846 } 847} 848 849 850static void 851give_help (void) 852{ 853 fprintf (stderr, "%s", _("\ 854ed:\tEdit then use both versions, each decorated with a header.\n\ 855eb:\tEdit then use both versions.\n\ 856el:\tEdit then use the left version.\n\ 857er:\tEdit then use the right version.\n\ 858e:\tEdit a new version.\n\ 859l:\tUse the left version.\n\ 860r:\tUse the right version.\n\ 861s:\tSilently include common lines.\n\ 862v:\tVerbosely include common lines.\n\ 863q:\tQuit.\n\ 864")); 865} 866 867static int 868skip_white (void) 869{ 870 int c; 871 for (;;) 872 { 873 c = getchar (); 874 if (! ISSPACE (c) || c == '\n') 875 break; 876 checksigs (); 877 } 878 if (ferror (stdin)) 879 perror_fatal (_("read failed")); 880 return c; 881} 882 883static void 884flush_line (void) 885{ 886 int c; 887 while ((c = getchar ()) != '\n' && c != EOF) 888 continue; 889 if (ferror (stdin)) 890 perror_fatal (_("read failed")); 891} 892 893 894/* interpret an edit command */ 895static bool 896edit (struct line_filter *left, char const *lname, lin lline, lin llen, 897 struct line_filter *right, char const *rname, lin rline, lin rlen, 898 FILE *outfile) 899{ 900 for (;;) 901 { 902 int cmd0, cmd1; 903 bool gotcmd = 0; 904 905 cmd1 = 0; /* Pacify `gcc -W'. */ 906 907 while (! gotcmd) 908 { 909 if (putchar ('%') != '%') 910 perror_fatal (_("write failed")); 911 ck_fflush (stdout); 912 913 cmd0 = skip_white (); 914 switch (cmd0) 915 { 916 case 'l': case 'r': case 's': case 'v': case 'q': 917 if (skip_white () != '\n') 918 { 919 give_help (); 920 flush_line (); 921 continue; 922 } 923 gotcmd = 1; 924 break; 925 926 case 'e': 927 cmd1 = skip_white (); 928 switch (cmd1) 929 { 930 case 'b': case 'd': case 'l': case 'r': 931 if (skip_white () != '\n') 932 { 933 give_help (); 934 flush_line (); 935 continue; 936 } 937 gotcmd = 1; 938 break; 939 case '\n': 940 gotcmd = 1; 941 break; 942 default: 943 give_help (); 944 flush_line (); 945 continue; 946 } 947 break; 948 949 case EOF: 950 if (feof (stdin)) 951 { 952 gotcmd = 1; 953 cmd0 = 'q'; 954 break; 955 } 956 /* Fall through. */ 957 default: 958 flush_line (); 959 /* Fall through. */ 960 case '\n': 961 give_help (); 962 continue; 963 } 964 } 965 966 switch (cmd0) 967 { 968 case 'l': 969 lf_copy (left, llen, outfile); 970 lf_skip (right, rlen); 971 return 1; 972 case 'r': 973 lf_copy (right, rlen, outfile); 974 lf_skip (left, llen); 975 return 1; 976 case 's': 977 suppress_common_lines = 1; 978 break; 979 case 'v': 980 suppress_common_lines = 0; 981 break; 982 case 'q': 983 return 0; 984 case 'e': 985 { 986 int fd; 987 988 if (tmpname) 989 tmp = fopen (tmpname, "w"); 990 else 991 { 992 if ((fd = temporary_file ()) < 0) 993 perror_fatal ("mkstemp"); 994 tmp = fdopen (fd, "w"); 995 } 996 997 if (! tmp) 998 perror_fatal (tmpname); 999 1000 switch (cmd1) 1001 { 1002 case 'd': 1003 if (llen) 1004 { 1005 if (llen == 1) 1006 fprintf (tmp, "--- %s %ld\n", lname, (long) lline); 1007 else 1008 fprintf (tmp, "--- %s %ld,%ld\n", lname, 1009 (long) lline, (long) (lline + llen - 1)); 1010 } 1011 /* Fall through. */ 1012 case 'b': case 'l': 1013 lf_copy (left, llen, tmp); 1014 break; 1015 1016 default: 1017 lf_skip (left, llen); 1018 break; 1019 } 1020 1021 switch (cmd1) 1022 { 1023 case 'd': 1024 if (rlen) 1025 { 1026 if (rlen == 1) 1027 fprintf (tmp, "+++ %s %ld\n", rname, (long) rline); 1028 else 1029 fprintf (tmp, "+++ %s %ld,%ld\n", rname, 1030 (long) rline, (long) (rline + rlen - 1)); 1031 } 1032 /* Fall through. */ 1033 case 'b': case 'r': 1034 lf_copy (right, rlen, tmp); 1035 break; 1036 1037 default: 1038 lf_skip (right, rlen); 1039 break; 1040 } 1041 1042 ck_fclose (tmp); 1043 1044 { 1045 int wstatus; 1046 int werrno = 0; 1047 ignore_SIGINT = 1; 1048 checksigs (); 1049 1050 { 1051#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK) 1052 char *command = 1053 xmalloc (quote_system_arg (0, editor_program) 1054 + 1 + strlen (tmpname) + 1); 1055 sprintf (command + quote_system_arg (command, editor_program), 1056 " %s", tmpname); 1057 wstatus = system (command); 1058 if (wstatus == -1) 1059 werrno = errno; 1060 free (command); 1061#else 1062 pid_t pid; 1063 1064 pid = vfork (); 1065 if (pid == 0) 1066 { 1067 char const *argv[3]; 1068 int i = 0; 1069 1070 argv[i++] = editor_program; 1071 argv[i++] = tmpname; 1072 argv[i] = 0; 1073 1074 execvp (editor_program, (char **) argv); 1075 _exit (errno == ENOEXEC ? 126 : 127); 1076 } 1077 1078 if (pid < 0) 1079 perror_fatal ("fork"); 1080 1081 while (waitpid (pid, &wstatus, 0) < 0) 1082 if (errno == EINTR) 1083 checksigs (); 1084 else 1085 perror_fatal ("waitpid"); 1086#endif 1087 } 1088 1089 ignore_SIGINT = 0; 1090 ck_editor_status (werrno, wstatus); 1091 } 1092 1093 { 1094 char buf[SDIFF_BUFSIZE]; 1095 size_t size; 1096 tmp = ck_fopen (tmpname, "r"); 1097 while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0) 1098 { 1099 checksigs (); 1100 ck_fwrite (buf, size, outfile); 1101 } 1102 ck_fclose (tmp); 1103 } 1104 return 1; 1105 } 1106 default: 1107 give_help (); 1108 break; 1109 } 1110 } 1111} 1112 1113 1114 1115/* Alternately reveal bursts of diff output and handle user commands. */ 1116static bool 1117interact (struct line_filter *diff, 1118 struct line_filter *left, char const *lname, 1119 struct line_filter *right, char const *rname, 1120 FILE *outfile) 1121{ 1122 lin lline = 1, rline = 1; 1123 1124 for (;;) 1125 { 1126 char diff_help[256]; 1127 int snarfed = lf_snarf (diff, diff_help, sizeof diff_help); 1128 1129 if (snarfed <= 0) 1130 return snarfed != 0; 1131 1132 checksigs (); 1133 1134 if (diff_help[0] == ' ') 1135 puts (diff_help + 1); 1136 else 1137 { 1138 char *numend; 1139 uintmax_t val; 1140 lin llen, rlen, lenmax; 1141 errno = 0; 1142 llen = val = strtoumax (diff_help + 1, &numend, 10); 1143 if (llen < 0 || llen != val || errno || *numend != ',') 1144 fatal (diff_help); 1145 rlen = val = strtoumax (numend + 1, &numend, 10); 1146 if (rlen < 0 || rlen != val || errno || *numend) 1147 fatal (diff_help); 1148 1149 lenmax = MAX (llen, rlen); 1150 1151 switch (diff_help[0]) 1152 { 1153 case 'i': 1154 if (suppress_common_lines) 1155 lf_skip (diff, lenmax); 1156 else 1157 lf_copy (diff, lenmax, stdout); 1158 1159 lf_copy (left, llen, outfile); 1160 lf_skip (right, rlen); 1161 break; 1162 1163 case 'c': 1164 lf_copy (diff, lenmax, stdout); 1165 if (! edit (left, lname, lline, llen, 1166 right, rname, rline, rlen, 1167 outfile)) 1168 return 0; 1169 break; 1170 1171 default: 1172 fatal (diff_help); 1173 } 1174 1175 lline += llen; 1176 rline += rlen; 1177 } 1178 } 1179} 1180 1181/* Return nonzero if DIR is an existing directory. */ 1182static bool 1183diraccess (char const *dir) 1184{ 1185 struct stat buf; 1186 return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode); 1187} 1188 1189#ifndef P_tmpdir 1190# define P_tmpdir "/tmp" 1191#endif 1192#ifndef TMPDIR_ENV 1193# define TMPDIR_ENV "TMPDIR" 1194#endif 1195 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". */ 1199static int 1200temporary_file (void) 1201{ 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); 1205 int fd; 1206 int e; 1207 sigset_t procmask; 1208 sigset_t blocked; 1209 sprintf (buf, "%s/sdiffXXXXXX", dir); 1210 sigemptyset (&blocked); 1211 sigaddset (&blocked, SIGINT); 1212 sigprocmask (SIG_BLOCK, &blocked, &procmask); 1213 fd = mkstemp (buf); 1214 e = errno; 1215 if (0 <= fd) 1216 tmpname = buf; 1217 sigprocmask (SIG_SETMASK, &procmask, 0); 1218 errno = e; 1219 return fd; 1220} 1221