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