1/* $NetBSD: sdiff.c,v 1.2 2016/01/13 03:39:28 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 37static char const authorship_msgid[] = N_("Written by Thomas Lord."); 38 39static char const copyright_string[] = 40 "Copyright (C) 2002 Free Software Foundation, Inc."; 41 42extern 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 47char *program_name; 48 49static char const *editor_program = DEFAULT_EDITOR_PROGRAM; 50static char const **diffargv; 51 52static char * volatile tmpname; 53static FILE *tmp; 54 55#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK 56static pid_t volatile diffpid; 57#endif 58 59struct line_filter; 60 61static RETSIGTYPE catchsig (int); 62static bool edit (struct line_filter *, char const *, lin, lin, struct line_filter *, char const *, lin, lin, FILE *); 63static bool interact (struct line_filter *, struct line_filter *, char const *, struct line_filter *, char const *, FILE *); 64static void checksigs (void); 65static void diffarg (char const *); 66static void fatal (char const *) __attribute__((noreturn)); 67static void perror_fatal (char const *) __attribute__((noreturn)); 68static void trapsigs (void); 69static void untrapsig (int); 70 71#define NUM_SIGS (sizeof sigs / sizeof *sigs) 72static 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 122static bool diraccess (char const *); 123static int temporary_file (void); 124 125/* Options: */ 126 127/* Name of output file if -o specified. */ 128static char const *output; 129 130/* Do not print common lines. */ 131static bool suppress_common_lines; 132 133/* Value for the long option that does not have single-letter equivalents. */ 134enum 135{ 136 DIFF_PROGRAM_OPTION = CHAR_MAX + 1, 137 HELP_OPTION, 138 STRIP_TRAILING_CR_OPTION 139}; 140 141static 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 164static void try_help (char const *, char const *) __attribute__((noreturn)); 165static void 166try_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 175static void 176check_stdout (void) 177{ 178 if (ferror (stdout)) 179 fatal ("write failed"); 180 else if (fclose (stdout) != 0) 181 perror_fatal (_("standard output")); 182} 183 184static 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 211static void 212usage (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 228static void 229cleanup (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 239static void exiterr (void) __attribute__((noreturn)); 240static void 241exiterr (void) 242{ 243 cleanup (); 244 untrapsig (0); 245 checksigs (); 246 exit (EXIT_TROUBLE); 247} 248 249static void 250fatal (char const *msgid) 251{ 252 error (0, 0, "%s", _(msgid)); 253 exiterr (); 254} 255 256static void 257perror_fatal (char const *msg) 258{ 259 int e = errno; 260 checksigs (); 261 error (0, e, "%s", msg); 262 exiterr (); 263} 264 265static void 266check_child_status (int werrno, int wstatus, int max_ok_status, 267 char const *subsidiary_program) 268{ 269 int status = (! werrno && WIFEXITED (wstatus) 270 ? WEXITSTATUS (wstatus) 271 : INT_MAX); 272 273 if (max_ok_status < status) 274 { 275 error (0, werrno, 276 _(status == 126 277 ? "subsidiary program `%s' could not be invoked" 278 : status == 127 279 ? "subsidiary program `%s' not found" 280 : status == INT_MAX 281 ? "subsidiary program `%s' failed" 282 : "subsidiary program `%s' failed (exit status %d)"), 283 subsidiary_program, status); 284 exiterr (); 285 } 286} 287 288static FILE * 289ck_fopen (char const *fname, char const *type) 290{ 291 FILE *r = fopen (fname, type); 292 if (! r) 293 perror_fatal (fname); 294 return r; 295} 296 297static void 298ck_fclose (FILE *f) 299{ 300 if (fclose (f)) 301 perror_fatal ("fclose"); 302} 303 304static size_t 305ck_fread (char *buf, size_t size, FILE *f) 306{ 307 size_t r = fread (buf, sizeof (char), size, f); 308 if (r == 0 && ferror (f)) 309 perror_fatal (_("read failed")); 310 return r; 311} 312 313static void 314ck_fwrite (char const *buf, size_t size, FILE *f) 315{ 316 if (fwrite (buf, sizeof (char), size, f) != size) 317 perror_fatal (_("write failed")); 318} 319 320static void 321ck_fflush (FILE *f) 322{ 323 if (fflush (f) != 0) 324 perror_fatal (_("write failed")); 325} 326 327static char const * 328expand_name (char *name, bool is_dir, char const *other_name) 329{ 330 if (strcmp (name, "-") == 0) 331 fatal ("cannot interactively merge standard input"); 332 if (! is_dir) 333 return name; 334 else 335 { 336 /* Yield NAME/BASE, where BASE is OTHER_NAME's basename. */ 337 char const *base = base_name (other_name); 338 size_t namelen = strlen (name), baselen = strlen (base); 339 bool insert_slash = *base_name (name) && name[namelen - 1] != '/'; 340 char *r = xmalloc (namelen + insert_slash + baselen + 1); 341 memcpy (r, name, namelen); 342 r[namelen] = '/'; 343 memcpy (r + namelen + insert_slash, base, baselen + 1); 344 return r; 345 } 346} 347 348 349 350struct line_filter { 351 FILE *infile; 352 char *bufpos; 353 char *buffer; 354 char *buflim; 355}; 356 357static void 358lf_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 */ 366static size_t 367lf_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 */ 378static void 379lf_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 */ 404static void 405lf_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. */ 424static int 425lf_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 449 450int 451main (int argc, char *argv[]) 452{ 453 int opt; 454 char const *prog; 455 456 exit_failure = EXIT_TROUBLE; 457 initialize_main (&argc, &argv); 458 program_name = argv[0]; 459 setlocale (LC_ALL, ""); 460 bindtextdomain (PACKAGE, LOCALEDIR); 461 textdomain (PACKAGE); 462 c_stack_action (c_stack_die); 463 464 prog = getenv ("EDITOR"); 465 if (prog) 466 editor_program = prog; 467 468 diffarg (DEFAULT_DIFF_PROGRAM); 469 470 /* parse command line args */ 471 while ((opt = getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, 0)) 472 != -1) 473 { 474 switch (opt) 475 { 476 case 'a': 477 diffarg ("-a"); 478 break; 479 480 case 'b': 481 diffarg ("-b"); 482 break; 483 484 case 'B': 485 diffarg ("-B"); 486 break; 487 488 case 'd': 489 diffarg ("-d"); 490 break; 491 492 case 'E': 493 diffarg ("-E"); 494 break; 495 496 case 'H': 497 diffarg ("-H"); 498 break; 499 500 case 'i': 501 diffarg ("-i"); 502 break; 503 504 case 'I': 505 diffarg ("-I"); 506 diffarg (optarg); 507 break; 508 509 case 'l': 510 diffarg ("--left-column"); 511 break; 512 513 case 'o': 514 output = optarg; 515 break; 516 517 case 's': 518 suppress_common_lines = 1; 519 break; 520 521 case 't': 522 diffarg ("-t"); 523 break; 524 525 case 'v': 526 printf ("sdiff %s\n%s\n\n%s\n\n%s\n", 527 version_string, copyright_string, 528 _(free_software_msgid), _(authorship_msgid)); 529 check_stdout (); 530 return EXIT_SUCCESS; 531 532 case 'w': 533 diffarg ("-W"); 534 diffarg (optarg); 535 break; 536 537 case 'W': 538 diffarg ("-w"); 539 break; 540 541 case DIFF_PROGRAM_OPTION: 542 diffargv[0] = optarg; 543 break; 544 545 case HELP_OPTION: 546 usage (); 547 check_stdout (); 548 return EXIT_SUCCESS; 549 550 case STRIP_TRAILING_CR_OPTION: 551 diffarg ("--strip-trailing-cr"); 552 break; 553 554 default: 555 try_help (0, 0); 556 } 557 } 558 559 if (argc - optind != 2) 560 { 561 if (argc - optind < 2) 562 try_help ("missing operand after `%s'", argv[argc - 1]); 563 else 564 try_help ("extra operand `%s'", argv[optind + 2]); 565 } 566 567 if (! output) 568 { 569 /* easy case: diff does everything for us */ 570 if (suppress_common_lines) 571 diffarg ("--suppress-common-lines"); 572 diffarg ("-y"); 573 diffarg ("--"); 574 diffarg (argv[optind]); 575 diffarg (argv[optind + 1]); 576 diffarg (0); 577 execvp (diffargv[0], (char **) diffargv); 578 perror_fatal (diffargv[0]); 579 } 580 else 581 { 582 char const *lname, *rname; 583 FILE *left, *right, *out, *diffout; 584 bool interact_ok; 585 struct line_filter lfilt; 586 struct line_filter rfilt; 587 struct line_filter diff_filt; 588 bool leftdir = diraccess (argv[optind]); 589 bool rightdir = diraccess (argv[optind + 1]); 590 591 if (leftdir & rightdir) 592 fatal ("both files to be compared are directories"); 593 594 lname = expand_name (argv[optind], leftdir, argv[optind + 1]); 595 left = ck_fopen (lname, "r"); 596 rname = expand_name (argv[optind + 1], rightdir, argv[optind]); 597 right = ck_fopen (rname, "r"); 598 out = ck_fopen (output, "w"); 599 600 diffarg ("--sdiff-merge-assist"); 601 diffarg ("--"); 602 diffarg (argv[optind]); 603 diffarg (argv[optind + 1]); 604 diffarg (0); 605 606 trapsigs (); 607 608#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK) 609 { 610 size_t cmdsize = 1; 611 char *p, *command; 612 int i; 613 614 for (i = 0; diffargv[i]; i++) 615 cmdsize += quote_system_arg (0, diffargv[i]) + 1; 616 command = p = xmalloc (cmdsize); 617 for (i = 0; diffargv[i]; i++) 618 { 619 p += quote_system_arg (p, diffargv[i]); 620 *p++ = ' '; 621 } 622 p[-1] = 0; 623 errno = 0; 624 diffout = popen (command, "r"); 625 if (! diffout) 626 perror_fatal (command); 627 free (command); 628 } 629#else 630 { 631 int diff_fds[2]; 632# if HAVE_WORKING_VFORK 633 sigset_t procmask; 634 sigset_t blocked; 635# endif 636 637 if (pipe (diff_fds) != 0) 638 perror_fatal ("pipe"); 639 640# if HAVE_WORKING_VFORK 641 /* Block SIGINT and SIGPIPE. */ 642 sigemptyset (&blocked); 643 sigaddset (&blocked, SIGINT); 644 sigaddset (&blocked, SIGPIPE); 645 sigprocmask (SIG_BLOCK, &blocked, &procmask); 646# endif 647 diffpid = vfork (); 648 if (diffpid < 0) 649 perror_fatal ("fork"); 650 if (! diffpid) 651 { 652 /* Alter the child's SIGINT and SIGPIPE handlers; 653 this may munge the parent. 654 The child ignores SIGINT in case the user interrupts the editor. 655 The child does not ignore SIGPIPE, even if the parent does. */ 656 if (initial_handler (handler_index_of_SIGINT) != SIG_IGN) 657 signal_handler (SIGINT, SIG_IGN); 658 signal_handler (SIGPIPE, SIG_DFL); 659# if HAVE_WORKING_VFORK 660 /* Stop blocking SIGINT and SIGPIPE in the child. */ 661 sigprocmask (SIG_SETMASK, &procmask, 0); 662# endif 663 close (diff_fds[0]); 664 if (diff_fds[1] != STDOUT_FILENO) 665 { 666 dup2 (diff_fds[1], STDOUT_FILENO); 667 close (diff_fds[1]); 668 } 669 670 execvp (diffargv[0], (char **) diffargv); 671 _exit (errno == ENOEXEC ? 126 : 127); 672 } 673 674# if HAVE_WORKING_VFORK 675 /* Restore the parent's SIGINT and SIGPIPE behavior. */ 676 if (initial_handler (handler_index_of_SIGINT) != SIG_IGN) 677 signal_handler (SIGINT, catchsig); 678 if (initial_handler (handler_index_of_SIGPIPE) != SIG_IGN) 679 signal_handler (SIGPIPE, catchsig); 680 else 681 signal_handler (SIGPIPE, SIG_IGN); 682 683 /* Stop blocking SIGINT and SIGPIPE in the parent. */ 684 sigprocmask (SIG_SETMASK, &procmask, 0); 685# endif 686 687 close (diff_fds[1]); 688 diffout = fdopen (diff_fds[0], "r"); 689 if (! diffout) 690 perror_fatal ("fdopen"); 691 } 692#endif 693 694 lf_init (&diff_filt, diffout); 695 lf_init (&lfilt, left); 696 lf_init (&rfilt, right); 697 698 interact_ok = interact (&diff_filt, &lfilt, lname, &rfilt, rname, out); 699 700 ck_fclose (left); 701 ck_fclose (right); 702 ck_fclose (out); 703 704 { 705 int wstatus; 706 int werrno = 0; 707 708#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK) 709 wstatus = pclose (diffout); 710 if (wstatus == -1) 711 werrno = errno; 712#else 713 ck_fclose (diffout); 714 while (waitpid (diffpid, &wstatus, 0) < 0) 715 if (errno == EINTR) 716 checksigs (); 717 else 718 perror_fatal ("waitpid"); 719 diffpid = 0; 720#endif 721 722 if (tmpname) 723 { 724 unlink (tmpname); 725 tmpname = 0; 726 } 727 728 if (! interact_ok) 729 exiterr (); 730 731 check_child_status (werrno, wstatus, EXIT_FAILURE, diffargv[0]); 732 untrapsig (0); 733 checksigs (); 734 exit (WEXITSTATUS (wstatus)); 735 } 736 } 737 return EXIT_SUCCESS; /* Fool `-Wall'. */ 738} 739 740static void 741diffarg (char const *a) 742{ 743 static size_t diffargs, diffarglim; 744 745 if (diffargs == diffarglim) 746 { 747 if (! diffarglim) 748 diffarglim = 16; 749 else if (PTRDIFF_MAX / (2 * sizeof *diffargv) <= diffarglim) 750 xalloc_die (); 751 else 752 diffarglim *= 2; 753 diffargv = xrealloc (diffargv, diffarglim * sizeof *diffargv); 754 } 755 diffargv[diffargs++] = a; 756} 757 758 759 760 761/* Signal handling */ 762 763static bool volatile ignore_SIGINT; 764static int volatile signal_received; 765static bool sigs_trapped; 766 767static RETSIGTYPE 768catchsig (int s) 769{ 770#if ! HAVE_SIGACTION 771 signal (s, SIG_IGN); 772#endif 773 if (! (s == SIGINT && ignore_SIGINT)) 774 signal_received = s; 775} 776 777#if HAVE_SIGACTION 778static struct sigaction catchaction; 779 780static void 781signal_handler (int sig, RETSIGTYPE (*handler) (int)) 782{ 783 catchaction.sa_handler = handler; 784 sigaction (sig, &catchaction, 0); 785} 786#endif 787 788static void 789trapsigs (void) 790{ 791 int i; 792 793#if HAVE_SIGACTION 794 catchaction.sa_flags = SA_RESTART; 795 sigemptyset (&catchaction.sa_mask); 796 for (i = 0; i < NUM_SIGS; i++) 797 sigaddset (&catchaction.sa_mask, sigs[i]); 798#endif 799 800 for (i = 0; i < NUM_SIGS; i++) 801 { 802#if HAVE_SIGACTION 803 sigaction (sigs[i], 0, &initial_action[i]); 804#else 805 initial_action[i] = signal (sigs[i], SIG_IGN); 806#endif 807 if (initial_handler (i) != SIG_IGN) 808 signal_handler (sigs[i], catchsig); 809 } 810 811#ifdef SIGCHLD 812 /* System V fork+wait does not work if SIGCHLD is ignored. */ 813 signal (SIGCHLD, SIG_DFL); 814#endif 815 816 sigs_trapped = 1; 817} 818 819/* Untrap signal S, or all trapped signals if S is zero. */ 820static void 821untrapsig (int s) 822{ 823 int i; 824 825 if (sigs_trapped) 826 for (i = 0; i < NUM_SIGS; i++) 827 if ((! s || sigs[i] == s) && initial_handler (i) != SIG_IGN) 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/* Exit if a signal has been received. */ 836static void 837checksigs (void) 838{ 839 int s = signal_received; 840 if (s) 841 { 842 cleanup (); 843 844 /* Yield an exit status indicating that a signal was received. */ 845 untrapsig (s); 846 kill (getpid (), s); 847 848 /* That didn't work, so exit with error status. */ 849 exit (EXIT_TROUBLE); 850 } 851} 852 853 854static void 855give_help (void) 856{ 857 fprintf (stderr, "%s", _("\ 858ed:\tEdit then use both versions, each decorated with a header.\n\ 859eb:\tEdit then use both versions.\n\ 860el:\tEdit then use the left version.\n\ 861er:\tEdit then use the right version.\n\ 862e:\tEdit a new version.\n\ 863l:\tUse the left version.\n\ 864r:\tUse the right version.\n\ 865s:\tSilently include common lines.\n\ 866v:\tVerbosely include common lines.\n\ 867q:\tQuit.\n\ 868")); 869} 870 871static int 872skip_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 887static void 888flush_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 */ 899static bool 900edit (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 = 0; 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 'l': case 'r': case 's': case 'v': case 'q': 921 if (skip_white () != '\n') 922 { 923 give_help (); 924 flush_line (); 925 continue; 926 } 927 gotcmd = 1; 928 break; 929 930 case 'e': 931 cmd1 = skip_white (); 932 switch (cmd1) 933 { 934 case 'b': case 'd': case 'l': case 'r': 935 if (skip_white () != '\n') 936 { 937 give_help (); 938 flush_line (); 939 continue; 940 } 941 gotcmd = 1; 942 break; 943 case '\n': 944 gotcmd = 1; 945 break; 946 default: 947 give_help (); 948 flush_line (); 949 continue; 950 } 951 break; 952 953 case EOF: 954 if (feof (stdin)) 955 { 956 gotcmd = 1; 957 cmd0 = 'q'; 958 break; 959 } 960 /* Fall through. */ 961 default: 962 flush_line (); 963 /* Fall through. */ 964 case '\n': 965 give_help (); 966 continue; 967 } 968 } 969 970 switch (cmd0) 971 { 972 case 'l': 973 lf_copy (left, llen, outfile); 974 lf_skip (right, rlen); 975 return 1; 976 case 'r': 977 lf_copy (right, rlen, outfile); 978 lf_skip (left, llen); 979 return 1; 980 case 's': 981 suppress_common_lines = 1; 982 break; 983 case 'v': 984 suppress_common_lines = 0; 985 break; 986 case 'q': 987 return 0; 988 case 'e': 989 { 990 int fd; 991 992 if (tmpname) 993 tmp = fopen (tmpname, "w"); 994 else 995 { 996 if ((fd = temporary_file ()) < 0) 997 perror_fatal ("mkstemp"); 998 tmp = fdopen (fd, "w"); 999 } 1000 1001 if (! tmp) 1002 perror_fatal (tmpname); 1003 1004 switch (cmd1) 1005 { 1006 case 'd': 1007 if (llen) 1008 { 1009 if (llen == 1) 1010 fprintf (tmp, "--- %s %ld\n", lname, (long) lline); 1011 else 1012 fprintf (tmp, "--- %s %ld,%ld\n", lname, 1013 (long) lline, (long) (lline + llen - 1)); 1014 } 1015 /* Fall through. */ 1016 case 'b': case 'l': 1017 lf_copy (left, llen, tmp); 1018 break; 1019 1020 default: 1021 lf_skip (left, llen); 1022 break; 1023 } 1024 1025 switch (cmd1) 1026 { 1027 case 'd': 1028 if (rlen) 1029 { 1030 if (rlen == 1) 1031 fprintf (tmp, "+++ %s %ld\n", rname, (long) rline); 1032 else 1033 fprintf (tmp, "+++ %s %ld,%ld\n", rname, 1034 (long) rline, (long) (rline + rlen - 1)); 1035 } 1036 /* Fall through. */ 1037 case 'b': case 'r': 1038 lf_copy (right, rlen, tmp); 1039 break; 1040 1041 default: 1042 lf_skip (right, rlen); 1043 break; 1044 } 1045 1046 ck_fclose (tmp); 1047 1048 { 1049 int wstatus; 1050 int werrno = 0; 1051 ignore_SIGINT = 1; 1052 checksigs (); 1053 1054 { 1055#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK) 1056 char *command = 1057 xmalloc (quote_system_arg (0, editor_program) 1058 + 1 + strlen (tmpname) + 1); 1059 sprintf (command + quote_system_arg (command, editor_program), 1060 " %s", tmpname); 1061 wstatus = system (command); 1062 if (wstatus == -1) 1063 werrno = errno; 1064 free (command); 1065#else 1066 pid_t pid; 1067 1068 pid = vfork (); 1069 if (pid == 0) 1070 { 1071 char const *argv[3]; 1072 int i = 0; 1073 1074 argv[i++] = editor_program; 1075 argv[i++] = tmpname; 1076 argv[i] = 0; 1077 1078 execvp (editor_program, (char **) argv); 1079 _exit (errno == ENOEXEC ? 126 : 127); 1080 } 1081 1082 if (pid < 0) 1083 perror_fatal ("fork"); 1084 1085 while (waitpid (pid, &wstatus, 0) < 0) 1086 if (errno == EINTR) 1087 checksigs (); 1088 else 1089 perror_fatal ("waitpid"); 1090#endif 1091 } 1092 1093 ignore_SIGINT = 0; 1094 check_child_status (werrno, wstatus, EXIT_SUCCESS, 1095 editor_program); 1096 } 1097 1098 { 1099 char buf[SDIFF_BUFSIZE]; 1100 size_t size; 1101 tmp = ck_fopen (tmpname, "r"); 1102 while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0) 1103 { 1104 checksigs (); 1105 ck_fwrite (buf, size, outfile); 1106 } 1107 ck_fclose (tmp); 1108 } 1109 return 1; 1110 } 1111 default: 1112 give_help (); 1113 break; 1114 } 1115 } 1116} 1117 1118 1119 1120/* Alternately reveal bursts of diff output and handle user commands. */ 1121static bool 1122interact (struct line_filter *diff, 1123 struct line_filter *left, char const *lname, 1124 struct line_filter *right, char const *rname, 1125 FILE *outfile) 1126{ 1127 lin lline = 1, rline = 1; 1128 1129 for (;;) 1130 { 1131 char diff_help[256]; 1132 int snarfed = lf_snarf (diff, diff_help, sizeof diff_help); 1133 1134 if (snarfed <= 0) 1135 return snarfed != 0; 1136 1137 checksigs (); 1138 1139 if (diff_help[0] == ' ') 1140 puts (diff_help + 1); 1141 else 1142 { 1143 char *numend; 1144 uintmax_t val; 1145 lin llen, rlen, lenmax; 1146 errno = 0; 1147 llen = val = strtoumax (diff_help + 1, &numend, 10); 1148 if (llen < 0 || llen != val || errno || *numend != ',') 1149 fatal (diff_help); 1150 rlen = val = strtoumax (numend + 1, &numend, 10); 1151 if (rlen < 0 || rlen != val || errno || *numend) 1152 fatal (diff_help); 1153 1154 lenmax = MAX (llen, rlen); 1155 1156 switch (diff_help[0]) 1157 { 1158 case 'i': 1159 if (suppress_common_lines) 1160 lf_skip (diff, lenmax); 1161 else 1162 lf_copy (diff, lenmax, stdout); 1163 1164 lf_copy (left, llen, outfile); 1165 lf_skip (right, rlen); 1166 break; 1167 1168 case 'c': 1169 lf_copy (diff, lenmax, stdout); 1170 if (! edit (left, lname, lline, llen, 1171 right, rname, rline, rlen, 1172 outfile)) 1173 return 0; 1174 break; 1175 1176 default: 1177 fatal (diff_help); 1178 } 1179 1180 lline += llen; 1181 rline += rlen; 1182 } 1183 } 1184} 1185 1186/* Return nonzero if DIR is an existing directory. */ 1187static bool 1188diraccess (char const *dir) 1189{ 1190 struct stat buf; 1191 return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode); 1192} 1193 1194#ifndef P_tmpdir 1195# define P_tmpdir "/tmp" 1196#endif 1197#ifndef TMPDIR_ENV 1198# define TMPDIR_ENV "TMPDIR" 1199#endif 1200 1201/* Open a temporary file and return its file descriptor. Put into 1202 tmpname the address of a newly allocated buffer that holds the 1203 file's name. Use the prefix "sdiff". */ 1204static int 1205temporary_file (void) 1206{ 1207 char const *tmpdir = getenv (TMPDIR_ENV); 1208 char const *dir = tmpdir ? tmpdir : P_tmpdir; 1209 char *buf = xmalloc (strlen (dir) + 1 + 5 + 6 + 1); 1210 int fd; 1211 int e; 1212 sigset_t procmask; 1213 sigset_t blocked; 1214 sprintf (buf, "%s/sdiffXXXXXX", dir); 1215 sigemptyset (&blocked); 1216 sigaddset (&blocked, SIGINT); 1217 sigprocmask (SIG_BLOCK, &blocked, &procmask); 1218 fd = mkstemp (buf); 1219 e = errno; 1220 if (0 <= fd) 1221 tmpname = buf; 1222 sigprocmask (SIG_SETMASK, &procmask, 0); 1223 errno = e; 1224 return fd; 1225} 1226