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