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