1 /* SDIFF -- interactive merge front end to diff 2 Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc. 3 4 This file is part of GNU DIFF. 5 6 GNU DIFF is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU DIFF is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU DIFF; see the file COPYING. If not, write to 18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ 19 20 /* GNU SDIFF was written by Thomas Lord. */ 21 22 #include <sys/cdefs.h> 23 __FBSDID("$FreeBSD: src/contrib/diff/sdiff.c,v 1.1.1.1.12.1 2002/01/28 01:26:35 nectar Exp $"); 24 25 #include "system.h" 26 #include <stdio.h> 27 #include <signal.h> 28 #include "getopt.h" 29 30 /* Size of chunks read from files which must be parsed into lines. */ 31 #define SDIFF_BUFSIZE ((size_t) 65536) 32 33 /* Default name of the diff program */ 34 #ifndef DIFF_PROGRAM 35 #define DIFF_PROGRAM "/usr/bin/diff" 36 #endif 37 38 /* Users' editor of nonchoice */ 39 #ifndef DEFAULT_EDITOR_PROGRAM 40 #define DEFAULT_EDITOR_PROGRAM "ed" 41 #endif 42 43 extern char version_string[]; 44 static char const *program_name; 45 static char const *diffbin = DIFF_PROGRAM; 46 static char const *edbin = DEFAULT_EDITOR_PROGRAM; 47 static char const **diffargv; 48 49 static char *tmpname; 50 static int volatile tmpmade; 51 52 #if HAVE_FORK 53 static pid_t volatile diffpid; 54 #endif 55 56 struct line_filter; 57 58 static FILE *ck_fopen PARAMS((char const *, char const *)); 59 static RETSIGTYPE catchsig PARAMS((int)); 60 static VOID *xmalloc PARAMS((size_t)); 61 static char const *expand_name PARAMS((char *, int, char const *)); 62 static int edit PARAMS((struct line_filter *, int, struct line_filter *, int, FILE*)); 63 static int interact PARAMS((struct line_filter *, struct line_filter *, struct line_filter *, FILE*)); 64 static int lf_snarf PARAMS((struct line_filter *, char *, size_t)); 65 static int skip_white PARAMS((void)); 66 static size_t ck_fread PARAMS((char *, size_t, FILE *)); 67 static size_t lf_refill PARAMS((struct line_filter *)); 68 static void checksigs PARAMS((void)); 69 static void ck_fclose PARAMS((FILE *)); 70 static void ck_fflush PARAMS((FILE *)); 71 static void ck_fwrite PARAMS((char const *, size_t, FILE *)); 72 static void cleanup PARAMS((void)); 73 static void diffarg PARAMS((char const *)); 74 static void execdiff PARAMS((void)); 75 static void exiterr PARAMS((void)); 76 static void fatal PARAMS((char const *)); 77 static void flush_line PARAMS((void)); 78 static void give_help PARAMS((void)); 79 static void lf_copy PARAMS((struct line_filter *, int, FILE *)); 80 static void lf_init PARAMS((struct line_filter *, FILE *)); 81 static void lf_skip PARAMS((struct line_filter *, int)); 82 static void perror_fatal PARAMS((char const *)); 83 static void trapsigs PARAMS((void)); 84 static void try_help PARAMS((char const *)); 85 static void untrapsig PARAMS((int)); 86 static void usage PARAMS((void)); 87 88 static int diraccess PARAMS((char const *)); 89 90 /* Options: */ 91 92 /* name of output file if -o spec'd */ 93 static char *out_file; 94 95 /* do not print common lines if true, set by -s option */ 96 static int suppress_common_flag; 97 98 static struct option const longopts[] = 99 { 100 {"ignore-blank-lines", 0, 0, 'B'}, 101 {"speed-large-files", 0, 0, 'H'}, 102 {"ignore-matching-lines", 1, 0, 'I'}, 103 {"ignore-all-space", 0, 0, 'W'}, /* swap W and w for historical reasons */ 104 {"text", 0, 0, 'a'}, 105 {"ignore-space-change", 0, 0, 'b'}, 106 {"minimal", 0, 0, 'd'}, 107 {"ignore-case", 0, 0, 'i'}, 108 {"left-column", 0, 0, 'l'}, 109 {"output", 1, 0, 'o'}, 110 {"suppress-common-lines", 0, 0, 's'}, 111 {"expand-tabs", 0, 0, 't'}, 112 {"width", 1, 0, 'w'}, 113 {"version", 0, 0, 'v'}, 114 {"help", 0, 0, 129}, 115 {0, 0, 0, 0} 116 }; 117 118 static void 119 try_help (reason) 120 char const *reason; 121 { 122 if (reason) 123 fprintf (stderr, "%s: %s\n", program_name, reason); 124 fprintf (stderr, "%s: Try `%s --help' for more information.\n", 125 program_name, program_name); 126 exit (2); 127 } 128 129 static void 130 usage () 131 { 132 printf ("Usage: %s [OPTIONS]... FILE1 FILE2\n\n", program_name); 133 printf ("%s", "\ 134 -o FILE --output=FILE Operate interactively, sending output to FILE.\n\n"); 135 printf ("%s", "\ 136 -i --ignore-case Consider upper- and lower-case to be the same.\n\ 137 -W --ignore-all-space Ignore all white space.\n\ 138 -b --ignore-space-change Ignore changes in the amount of white space.\n\ 139 -B --ignore-blank-lines Ignore changes whose lines are all blank.\n\ 140 -I RE --ignore-matching-lines=RE Ignore changes whose lines all match RE.\n\ 141 -a --text Treat all files as text.\n\n"); 142 printf ("%s", "\ 143 -w NUM --width=NUM Output at most NUM (default 130) characters per line.\n\ 144 -l --left-column Output only the left column of common lines.\n\ 145 -s --suppress-common-lines Do not output common lines.\n\n"); 146 printf ("\ 147 -t --expand-tabs Expand tabs to spaces in output.\n\n"); 148 printf ("%s", "\ 149 -d --minimal Try hard to find a smaller set of changes.\n\ 150 -H --speed-large-files Assume large files and many scattered small changes.\n\n"); 151 printf ("%s", "\ 152 -v --version Output version info.\n\ 153 --help Output this help.\n\n\ 154 If FILE1 or FILE2 is `-', read standard input.\n"); 155 } 156 157 static void 158 cleanup () 159 { 160 #if HAVE_FORK 161 if (0 < diffpid) 162 kill (diffpid, SIGPIPE); 163 #endif 164 if (tmpmade) 165 unlink (tmpname); 166 } 167 168 static void 169 exiterr () 170 { 171 cleanup (); 172 untrapsig (0); 173 checksigs (); 174 exit (2); 175 } 176 177 static void 178 fatal (msg) 179 char const *msg; 180 { 181 fprintf (stderr, "%s: %s\n", program_name, msg); 182 exiterr (); 183 } 184 185 static void 186 perror_fatal (msg) 187 char const *msg; 188 { 189 int e = errno; 190 checksigs (); 191 fprintf (stderr, "%s: ", program_name); 192 errno = e; 193 perror (msg); 194 exiterr (); 195 } 196 197 198 /* malloc freely or DIE! */ 199 static VOID * 200 xmalloc (size) 201 size_t size; 202 { 203 VOID *r = (VOID *) malloc (size); 204 if (!r) 205 fatal ("memory exhausted"); 206 return r; 207 } 208 209 static FILE * 210 ck_fopen (fname, type) 211 char const *fname, *type; 212 { 213 FILE *r = fopen (fname, type); 214 if (!r) 215 perror_fatal (fname); 216 return r; 217 } 218 219 static void 220 ck_fclose (f) 221 FILE *f; 222 { 223 if (fclose (f)) 224 perror_fatal ("input/output error"); 225 } 226 227 static size_t 228 ck_fread (buf, size, f) 229 char *buf; 230 size_t size; 231 FILE *f; 232 { 233 size_t r = fread (buf, sizeof (char), size, f); 234 if (r == 0 && ferror (f)) 235 perror_fatal ("input error"); 236 return r; 237 } 238 239 static void 240 ck_fwrite (buf, size, f) 241 char const *buf; 242 size_t size; 243 FILE *f; 244 { 245 if (fwrite (buf, sizeof (char), size, f) != size) 246 perror_fatal ("output error"); 247 } 248 249 static void 250 ck_fflush (f) 251 FILE *f; 252 { 253 if (fflush (f) != 0) 254 perror_fatal ("output error"); 255 } 256 257 static char const * 258 expand_name (name, is_dir, other_name) 259 char *name; 260 int is_dir; 261 char const *other_name; 262 { 263 if (strcmp (name, "-") == 0) 264 fatal ("cannot interactively merge standard input"); 265 if (!is_dir) 266 return name; 267 else 268 { 269 /* Yield NAME/BASE, where BASE is OTHER_NAME's basename. */ 270 char const *p = filename_lastdirchar (other_name); 271 char const *base = p ? p+1 : other_name; 272 size_t namelen = strlen (name), baselen = strlen (base); 273 char *r = xmalloc (namelen + baselen + 2); 274 memcpy (r, name, namelen); 275 r[namelen] = '/'; 276 memcpy (r + namelen + 1, base, baselen + 1); 277 return r; 278 } 279 } 280 281 282 283 struct line_filter { 284 FILE *infile; 285 char *bufpos; 286 char *buffer; 287 char *buflim; 288 }; 289 290 static void 291 lf_init (lf, infile) 292 struct line_filter *lf; 293 FILE *infile; 294 { 295 lf->infile = infile; 296 lf->bufpos = lf->buffer = lf->buflim = xmalloc (SDIFF_BUFSIZE + 1); 297 lf->buflim[0] = '\n'; 298 } 299 300 /* Fill an exhausted line_filter buffer from its INFILE */ 301 static size_t 302 lf_refill (lf) 303 struct line_filter *lf; 304 { 305 size_t s = ck_fread (lf->buffer, SDIFF_BUFSIZE, lf->infile); 306 lf->bufpos = lf->buffer; 307 lf->buflim = lf->buffer + s; 308 lf->buflim[0] = '\n'; 309 checksigs (); 310 return s; 311 } 312 313 /* Advance LINES on LF's infile, copying lines to OUTFILE */ 314 static void 315 lf_copy (lf, lines, outfile) 316 struct line_filter *lf; 317 int lines; 318 FILE *outfile; 319 { 320 char *start = lf->bufpos; 321 322 while (lines) 323 { 324 lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos); 325 if (! lf->bufpos) 326 { 327 ck_fwrite (start, lf->buflim - start, outfile); 328 if (! lf_refill (lf)) 329 return; 330 start = lf->bufpos; 331 } 332 else 333 { 334 --lines; 335 ++lf->bufpos; 336 } 337 } 338 339 ck_fwrite (start, lf->bufpos - start, outfile); 340 } 341 342 /* Advance LINES on LF's infile without doing output */ 343 static void 344 lf_skip (lf, lines) 345 struct line_filter *lf; 346 int lines; 347 { 348 while (lines) 349 { 350 lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos); 351 if (! lf->bufpos) 352 { 353 if (! lf_refill (lf)) 354 break; 355 } 356 else 357 { 358 --lines; 359 ++lf->bufpos; 360 } 361 } 362 } 363 364 /* Snarf a line into a buffer. Return EOF if EOF, 0 if error, 1 if OK. */ 365 static int 366 lf_snarf (lf, buffer, bufsize) 367 struct line_filter *lf; 368 char *buffer; 369 size_t bufsize; 370 { 371 char *start = lf->bufpos; 372 373 for (;;) 374 { 375 char *next = (char *) memchr (start, '\n', lf->buflim + 1 - start); 376 size_t s = next - start; 377 if (bufsize <= s) 378 return 0; 379 memcpy (buffer, start, s); 380 if (next < lf->buflim) 381 { 382 buffer[s] = 0; 383 lf->bufpos = next + 1; 384 return 1; 385 } 386 if (! lf_refill (lf)) 387 return s ? 0 : EOF; 388 buffer += s; 389 bufsize -= s; 390 start = next; 391 } 392 } 393 394 395 396 int 397 main (argc, argv) 398 int argc; 399 char *argv[]; 400 { 401 int opt; 402 char *editor; 403 char *differ; 404 405 initialize_main (&argc, &argv); 406 program_name = argv[0]; 407 408 editor = getenv ("EDITOR"); 409 if (editor) 410 edbin = editor; 411 differ = getenv ("DIFF"); 412 if (differ) 413 diffbin = differ; 414 415 diffarg ("diff"); 416 417 /* parse command line args */ 418 while ((opt = getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, 0)) 419 != EOF) 420 { 421 switch (opt) 422 { 423 case 'a': 424 diffarg ("-a"); 425 break; 426 427 case 'b': 428 diffarg ("-b"); 429 break; 430 431 case 'B': 432 diffarg ("-B"); 433 break; 434 435 case 'd': 436 diffarg ("-d"); 437 break; 438 439 case 'H': 440 diffarg ("-H"); 441 break; 442 443 case 'i': 444 diffarg ("-i"); 445 break; 446 447 case 'I': 448 diffarg ("-I"); 449 diffarg (optarg); 450 break; 451 452 case 'l': 453 diffarg ("--left-column"); 454 break; 455 456 case 'o': 457 out_file = optarg; 458 break; 459 460 case 's': 461 suppress_common_flag = 1; 462 break; 463 464 case 't': 465 diffarg ("-t"); 466 break; 467 468 case 'v': 469 printf ("sdiff - GNU diffutils version %s\n", version_string); 470 exit (0); 471 472 case 'w': 473 diffarg ("-W"); 474 diffarg (optarg); 475 break; 476 477 case 'W': 478 diffarg ("-w"); 479 break; 480 481 case 129: 482 usage (); 483 if (ferror (stdout) || fclose (stdout) != 0) 484 fatal ("write error"); 485 exit (0); 486 487 default: 488 try_help (0); 489 } 490 } 491 492 if (argc - optind != 2) 493 try_help (argc - optind < 2 ? "missing operand" : "extra operand"); 494 495 if (! out_file) 496 { 497 /* easy case: diff does everything for us */ 498 if (suppress_common_flag) 499 diffarg ("--suppress-common-lines"); 500 diffarg ("-y"); 501 diffarg ("--"); 502 diffarg (argv[optind]); 503 diffarg (argv[optind + 1]); 504 diffarg (0); 505 execdiff (); 506 } 507 else 508 { 509 FILE *left, *right, *out, *diffout; 510 int interact_ok; 511 struct line_filter lfilt; 512 struct line_filter rfilt; 513 struct line_filter diff_filt; 514 int leftdir = diraccess (argv[optind]); 515 int rightdir = diraccess (argv[optind + 1]); 516 517 if (leftdir && rightdir) 518 fatal ("both files to be compared are directories"); 519 520 left = ck_fopen (expand_name (argv[optind], leftdir, argv[optind + 1]), "r"); 521 ; 522 right = ck_fopen (expand_name (argv[optind + 1], rightdir, argv[optind]), "r"); 523 out = ck_fopen (out_file, "w"); 524 525 diffarg ("--sdiff-merge-assist"); 526 diffarg ("--"); 527 diffarg (argv[optind]); 528 diffarg (argv[optind + 1]); 529 diffarg (0); 530 531 trapsigs (); 532 533 #if ! HAVE_FORK 534 { 535 size_t cmdsize = 1; 536 char *p, *command; 537 int i; 538 539 for (i = 0; diffargv[i]; i++) 540 cmdsize += 4 * strlen (diffargv[i]) + 3; 541 command = p = xmalloc (cmdsize); 542 for (i = 0; diffargv[i]; i++) 543 { 544 char const *a = diffargv[i]; 545 SYSTEM_QUOTE_ARG (p, a); 546 *p++ = ' '; 547 } 548 p[-1] = '\0'; 549 diffout = popen (command, "r"); 550 if (!diffout) 551 perror_fatal (command); 552 free (command); 553 } 554 #else /* HAVE_FORK */ 555 { 556 int diff_fds[2]; 557 558 if (pipe (diff_fds) != 0) 559 perror_fatal ("pipe"); 560 561 diffpid = fork (); 562 if (diffpid < 0) 563 perror_fatal ("fork failed"); 564 if (!diffpid) 565 { 566 signal (SIGINT, SIG_IGN); /* in case user interrupts editor */ 567 signal (SIGPIPE, SIG_DFL); 568 569 close (diff_fds[0]); 570 if (diff_fds[1] != STDOUT_FILENO) 571 { 572 dup2 (diff_fds[1], STDOUT_FILENO); 573 close (diff_fds[1]); 574 } 575 576 execdiff (); 577 } 578 579 close (diff_fds[1]); 580 diffout = fdopen (diff_fds[0], "r"); 581 if (!diffout) 582 perror_fatal ("fdopen"); 583 } 584 #endif /* HAVE_FORK */ 585 586 lf_init (&diff_filt, diffout); 587 lf_init (&lfilt, left); 588 lf_init (&rfilt, right); 589 590 interact_ok = interact (&diff_filt, &lfilt, &rfilt, out); 591 592 ck_fclose (left); 593 ck_fclose (right); 594 ck_fclose (out); 595 596 { 597 int wstatus; 598 599 #if ! HAVE_FORK 600 wstatus = pclose (diffout); 601 #else 602 ck_fclose (diffout); 603 while (waitpid (diffpid, &wstatus, 0) < 0) 604 if (errno == EINTR) 605 checksigs (); 606 else 607 perror_fatal ("wait failed"); 608 diffpid = 0; 609 #endif 610 611 if (tmpmade) 612 { 613 unlink (tmpname); 614 tmpmade = 0; 615 } 616 617 if (! interact_ok) 618 exiterr (); 619 620 if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2)) 621 fatal ("Subsidiary diff failed"); 622 623 untrapsig (0); 624 checksigs (); 625 exit (WEXITSTATUS (wstatus)); 626 } 627 } 628 return 0; /* Fool -Wall . . . */ 629 } 630 631 static void 632 diffarg (a) 633 char const *a; 634 { 635 static unsigned diffargs, diffargsmax; 636 637 if (diffargs == diffargsmax) 638 { 639 if (! diffargsmax) 640 { 641 diffargv = (char const **) xmalloc (sizeof (char)); 642 diffargsmax = 8; 643 } 644 diffargsmax *= 2; 645 diffargv = (char const **) realloc (diffargv, 646 diffargsmax * sizeof (char const *)); 647 if (! diffargv) 648 fatal ("out of memory"); 649 } 650 diffargv[diffargs++] = a; 651 } 652 653 static void 654 execdiff () 655 { 656 execvp (diffbin, (char **) diffargv); 657 write (STDERR_FILENO, diffbin, strlen (diffbin)); 658 write (STDERR_FILENO, ": not found\n", 12); 659 _exit (2); 660 } 661 662 663 664 665 /* Signal handling */ 666 667 #define NUM_SIGS (sizeof (sigs) / sizeof (*sigs)) 668 static int const sigs[] = { 669 #ifdef SIGHUP 670 SIGHUP, 671 #endif 672 #ifdef SIGQUIT 673 SIGQUIT, 674 #endif 675 #ifdef SIGTERM 676 SIGTERM, 677 #endif 678 #ifdef SIGXCPU 679 SIGXCPU, 680 #endif 681 #ifdef SIGXFSZ 682 SIGXFSZ, 683 #endif 684 SIGINT, 685 SIGPIPE 686 }; 687 688 /* Prefer `sigaction' if it is available, since `signal' can lose signals. */ 689 #if HAVE_SIGACTION 690 static struct sigaction initial_action[NUM_SIGS]; 691 #define initial_handler(i) (initial_action[i].sa_handler) 692 #else 693 static RETSIGTYPE (*initial_action[NUM_SIGS]) (); 694 #define initial_handler(i) (initial_action[i]) 695 #endif 696 697 static int volatile ignore_SIGINT; 698 static int volatile signal_received; 699 static int sigs_trapped; 700 701 static RETSIGTYPE 702 catchsig (s) 703 int s; 704 { 705 #if ! HAVE_SIGACTION 706 signal (s, SIG_IGN); 707 #endif 708 if (! (s == SIGINT && ignore_SIGINT)) 709 signal_received = s; 710 } 711 712 static void 713 trapsigs () 714 { 715 int i; 716 717 #if HAVE_SIGACTION 718 struct sigaction catchaction; 719 bzero (&catchaction, sizeof (catchaction)); 720 catchaction.sa_handler = catchsig; 721 #ifdef SA_INTERRUPT 722 /* Non-Posix BSD-style systems like SunOS 4.1.x need this 723 so that `read' calls are interrupted properly. */ 724 catchaction.sa_flags = SA_INTERRUPT; 725 #endif 726 sigemptyset (&catchaction.sa_mask); 727 for (i = 0; i < NUM_SIGS; i++) 728 sigaddset (&catchaction.sa_mask, sigs[i]); 729 for (i = 0; i < NUM_SIGS; i++) 730 { 731 sigaction (sigs[i], 0, &initial_action[i]); 732 if (initial_handler (i) != SIG_IGN 733 && sigaction (sigs[i], &catchaction, 0) != 0) 734 fatal ("signal error"); 735 } 736 #else /* ! HAVE_SIGACTION */ 737 for (i = 0; i < NUM_SIGS; i++) 738 { 739 initial_action[i] = signal (sigs[i], SIG_IGN); 740 if (initial_handler (i) != SIG_IGN 741 && signal (sigs[i], catchsig) != SIG_IGN) 742 fatal ("signal error"); 743 } 744 #endif /* ! HAVE_SIGACTION */ 745 746 #if !defined(SIGCHLD) && defined(SIGCLD) 747 #define SIGCHLD SIGCLD 748 #endif 749 #ifdef SIGCHLD 750 /* System V fork+wait does not work if SIGCHLD is ignored. */ 751 signal (SIGCHLD, SIG_DFL); 752 #endif 753 754 sigs_trapped = 1; 755 } 756 757 /* Untrap signal S, or all trapped signals if S is zero. */ 758 static void 759 untrapsig (s) 760 int s; 761 { 762 int i; 763 764 if (sigs_trapped) 765 for (i = 0; i < NUM_SIGS; i++) 766 if ((!s || sigs[i] == s) && initial_handler (i) != SIG_IGN) 767 #if HAVE_SIGACTION 768 sigaction (sigs[i], &initial_action[i], 0); 769 #else 770 signal (sigs[i], initial_action[i]); 771 #endif 772 } 773 774 /* Exit if a signal has been received. */ 775 static void 776 checksigs () 777 { 778 int s = signal_received; 779 if (s) 780 { 781 cleanup (); 782 783 /* Yield an exit status indicating that a signal was received. */ 784 untrapsig (s); 785 kill (getpid (), s); 786 787 /* That didn't work, so exit with error status. */ 788 exit (2); 789 } 790 } 791 792 793 794 static void 795 give_help () 796 { 797 fprintf (stderr,"l:\tuse the left version\n"); 798 fprintf (stderr,"r:\tuse the right version\n"); 799 fprintf (stderr,"e l:\tedit then use the left version\n"); 800 fprintf (stderr,"e r:\tedit then use the right version\n"); 801 fprintf (stderr,"e b:\tedit then use the left and right versions concatenated\n"); 802 fprintf (stderr,"e:\tedit a new version\n"); 803 fprintf (stderr,"s:\tsilently include common lines\n"); 804 fprintf (stderr,"v:\tverbosely include common lines\n"); 805 fprintf (stderr,"q:\tquit\n"); 806 } 807 808 static int 809 skip_white () 810 { 811 int c; 812 for (;;) 813 { 814 c = getchar (); 815 if (!ISSPACE (c) || c == '\n') 816 break; 817 checksigs (); 818 } 819 if (ferror (stdin)) 820 perror_fatal ("input error"); 821 return c; 822 } 823 824 static void 825 flush_line () 826 { 827 int c; 828 while ((c = getchar ()) != '\n' && c != EOF) 829 ; 830 if (ferror (stdin)) 831 perror_fatal ("input error"); 832 } 833 834 835 /* interpret an edit command */ 836 static int 837 edit (left, lenl, right, lenr, outfile) 838 struct line_filter *left; 839 int lenl; 840 struct line_filter *right; 841 int lenr; 842 FILE *outfile; 843 { 844 for (;;) 845 { 846 int cmd0, cmd1; 847 int gotcmd = 0; 848 849 cmd1 = 0; /* Pacify `gcc -W'. */ 850 851 while (!gotcmd) 852 { 853 if (putchar ('%') != '%') 854 perror_fatal ("output error"); 855 ck_fflush (stdout); 856 857 cmd0 = skip_white (); 858 switch (cmd0) 859 { 860 case 'l': case 'r': case 's': case 'v': case 'q': 861 if (skip_white () != '\n') 862 { 863 give_help (); 864 flush_line (); 865 continue; 866 } 867 gotcmd = 1; 868 break; 869 870 case 'e': 871 cmd1 = skip_white (); 872 switch (cmd1) 873 { 874 case 'l': case 'r': case 'b': 875 if (skip_white () != '\n') 876 { 877 give_help (); 878 flush_line (); 879 continue; 880 } 881 gotcmd = 1; 882 break; 883 case '\n': 884 gotcmd = 1; 885 break; 886 default: 887 give_help (); 888 flush_line (); 889 continue; 890 } 891 break; 892 case EOF: 893 if (feof (stdin)) 894 { 895 gotcmd = 1; 896 cmd0 = 'q'; 897 break; 898 } 899 /* falls through */ 900 default: 901 flush_line (); 902 /* falls through */ 903 case '\n': 904 give_help (); 905 continue; 906 } 907 } 908 909 switch (cmd0) 910 { 911 case 'l': 912 lf_copy (left, lenl, outfile); 913 lf_skip (right, lenr); 914 return 1; 915 case 'r': 916 lf_copy (right, lenr, outfile); 917 lf_skip (left, lenl); 918 return 1; 919 case 's': 920 suppress_common_flag = 1; 921 break; 922 case 'v': 923 suppress_common_flag = 0; 924 break; 925 case 'q': 926 return 0; 927 case 'e': 928 { 929 int tfd; 930 FILE *tmp; 931 932 if (tmpmade) 933 { 934 unlink (tmpname); 935 tmpmade = 0; 936 free (tmpname); 937 } 938 939 asprintf (&tmpname, "%s/sdiff.XXXXXX", 940 getenv("TMPDIR") ?: P_tmpdir); 941 if (tmpname == NULL) 942 perror_fatal ("temporary file name"); 943 tfd = mkstemp(tmpname); 944 if (tfd == -1) 945 perror_fatal ("temporary file name"); 946 tmp = fdopen (tfd, "w+"); 947 if (tmp == NULL) 948 perror_fatal ("temporary file name"); 949 950 tmpmade = 1; 951 952 if (cmd1 == 'l' || cmd1 == 'b') 953 lf_copy (left, lenl, tmp); 954 else 955 lf_skip (left, lenl); 956 957 if (cmd1 == 'r' || cmd1 == 'b') 958 lf_copy (right, lenr, tmp); 959 else 960 lf_skip (right, lenr); 961 962 ck_fflush (tmp); 963 964 { 965 int wstatus; 966 #if ! HAVE_FORK 967 char *command = xmalloc (strlen (edbin) + strlen (tmpname) + 2); 968 sprintf (command, "%s %s", edbin, tmpname); 969 wstatus = system (command); 970 free (command); 971 #else /* HAVE_FORK */ 972 pid_t pid; 973 974 ignore_SIGINT = 1; 975 checksigs (); 976 977 pid = fork (); 978 if (pid == 0) 979 { 980 char const *argv[3]; 981 int i = 0; 982 983 argv[i++] = edbin; 984 argv[i++] = tmpname; 985 argv[i++] = 0; 986 987 execvp (edbin, (char **) argv); 988 write (STDERR_FILENO, edbin, strlen (edbin)); 989 write (STDERR_FILENO, ": not found\n", 12); 990 _exit (1); 991 } 992 993 if (pid < 0) 994 perror_fatal ("fork failed"); 995 996 while (waitpid (pid, &wstatus, 0) < 0) 997 if (errno == EINTR) 998 checksigs (); 999 else 1000 perror_fatal ("wait failed"); 1001 1002 ignore_SIGINT = 0; 1003 #endif /* HAVE_FORK */ 1004 1005 if (wstatus != 0) 1006 fatal ("Subsidiary editor failed"); 1007 } 1008 1009 if (fseek (tmp, 0L, SEEK_SET) != 0) 1010 perror_fatal ("fseek"); 1011 { 1012 /* SDIFF_BUFSIZE is too big for a local var 1013 in some compilers, so we allocate it dynamically. */ 1014 char *buf = xmalloc (SDIFF_BUFSIZE); 1015 size_t size; 1016 1017 while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0) 1018 { 1019 checksigs (); 1020 ck_fwrite (buf, size, outfile); 1021 } 1022 ck_fclose (tmp); 1023 1024 free (buf); 1025 } 1026 return 1; 1027 } 1028 default: 1029 give_help (); 1030 break; 1031 } 1032 } 1033 } 1034 1035 1036 1037 /* Alternately reveal bursts of diff output and handle user commands. */ 1038 static int 1039 interact (diff, left, right, outfile) 1040 struct line_filter *diff; 1041 struct line_filter *left; 1042 struct line_filter *right; 1043 FILE *outfile; 1044 { 1045 for (;;) 1046 { 1047 char diff_help[256]; 1048 int snarfed = lf_snarf (diff, diff_help, sizeof (diff_help)); 1049 1050 if (snarfed <= 0) 1051 return snarfed; 1052 1053 checksigs (); 1054 1055 switch (diff_help[0]) 1056 { 1057 case ' ': 1058 puts (diff_help + 1); 1059 break; 1060 case 'i': 1061 { 1062 int lenl = atoi (diff_help + 1), lenr, lenmax; 1063 char *p = strchr (diff_help, ','); 1064 1065 if (!p) 1066 fatal (diff_help); 1067 lenr = atoi (p + 1); 1068 lenmax = max (lenl, lenr); 1069 1070 if (suppress_common_flag) 1071 lf_skip (diff, lenmax); 1072 else 1073 lf_copy (diff, lenmax, stdout); 1074 1075 lf_copy (left, lenl, outfile); 1076 lf_skip (right, lenr); 1077 break; 1078 } 1079 case 'c': 1080 { 1081 int lenl = atoi (diff_help + 1), lenr; 1082 char *p = strchr (diff_help, ','); 1083 1084 if (!p) 1085 fatal (diff_help); 1086 lenr = atoi (p + 1); 1087 lf_copy (diff, max (lenl, lenr), stdout); 1088 if (! edit (left, lenl, right, lenr, outfile)) 1089 return 0; 1090 break; 1091 } 1092 default: 1093 fatal (diff_help); 1094 break; 1095 } 1096 } 1097 } 1098 1099 1100 1101 /* temporary lossage: this is torn from gnu libc */ 1102 /* Return nonzero if DIR is an existing directory. */ 1103 static int 1104 diraccess (dir) 1105 char const *dir; 1106 { 1107 struct stat buf; 1108 return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode); 1109 } 1110