1/* sdiff - side-by-side merge of file differences 2 3 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 2001, 2002 Free 4 Software Foundation, Inc. 5 6 This file is part of GNU DIFF. 7 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) 11 any later version. 12 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. 17 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. */ 22 23#include "system.h" 24 25#include <c-stack.h> 26#include <dirname.h> 27#include <error.h> 28#include <exitfail.h> 29#include <freesoft.h> 30#include <getopt.h> 31#include <quotesys.h> 32#include <stdio.h> 33#include <xalloc.h> 34 35static char const authorship_msgid[] = N_("Written by Thomas Lord."); 36 37static char const copyright_string[] = 38 "Copyright (C) 2002 Free Software Foundation, Inc."; 39 40extern char const version_string[]; 41 42/* Size of chunks read from files which must be parsed into lines. */ 43#define SDIFF_BUFSIZE ((size_t) 65536) 44 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