1 /* SDIFF -- interactive merge front end to diff
2 Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
4 This file is part of GNU DIFF.
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)
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.
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. */
20 /* GNU SDIFF was written by Thomas Lord. */
22 #include <sys/cdefs.h>
23 __FBSDID("$FreeBSD$");
30 /* Size of chunks read from files which must be parsed into lines. */
31 #define SDIFF_BUFSIZE ((size_t) 65536)
33 /* Default name of the diff program */
35 #define DIFF_PROGRAM "/usr/bin/diff"
38 /* Users' editor of nonchoice */
39 #ifndef DEFAULT_EDITOR_PROGRAM
40 #define DEFAULT_EDITOR_PROGRAM "ed"
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;
50 static int volatile tmpmade;
53 static pid_t volatile diffpid;
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));
88 static int diraccess PARAMS((char const *));
92 /* name of output file if -o spec'd */
93 static char *out_file;
95 /* do not print common lines if true, set by -s option */
96 static int suppress_common_flag;
98 static struct option const longopts[] =
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 */
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'},
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);
132 printf ("Usage: %s [OPTIONS]... FILE1 FILE2\n\n", program_name);
134 -o FILE --output=FILE Operate interactively, sending output to FILE.\n\n");
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");
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");
147 -t --expand-tabs Expand tabs to spaces in output.\n\n");
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");
152 -v --version Output version info.\n\
153 --help Output this help.\n\n\
154 If FILE1 or FILE2 is `-', read standard input.\n");
162 kill (diffpid, SIGPIPE);
181 fprintf (stderr, "%s: %s\n", program_name, msg);
191 fprintf (stderr, "%s: ", program_name);
198 /* malloc freely or DIE! */
203 VOID *r = (VOID *) malloc (size);
205 fatal ("memory exhausted");
210 ck_fopen (fname, type)
211 char const *fname, *type;
213 FILE *r = fopen (fname, type);
215 perror_fatal (fname);
224 perror_fatal ("input/output error");
228 ck_fread (buf, size, f)
233 size_t r = fread (buf, sizeof (char), size, f);
234 if (r == 0 && ferror (f))
235 perror_fatal ("input error");
240 ck_fwrite (buf, size, f)
245 if (fwrite (buf, sizeof (char), size, f) != size)
246 perror_fatal ("output error");
254 perror_fatal ("output error");
258 expand_name (name, is_dir, other_name)
261 char const *other_name;
263 if (strcmp (name, "-") == 0)
264 fatal ("cannot interactively merge standard input");
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);
276 memcpy (r + namelen + 1, base, baselen + 1);
292 struct line_filter *lf;
296 lf->bufpos = lf->buffer = lf->buflim = xmalloc (SDIFF_BUFSIZE + 1);
297 lf->buflim[0] = '\n';
300 /* Fill an exhausted line_filter buffer from its INFILE */
303 struct line_filter *lf;
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';
313 /* Advance LINES on LF's infile, copying lines to OUTFILE */
315 lf_copy (lf, lines, outfile)
316 struct line_filter *lf;
320 char *start = lf->bufpos;
324 lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
327 ck_fwrite (start, lf->buflim - start, outfile);
328 if (! lf_refill (lf))
339 ck_fwrite (start, lf->bufpos - start, outfile);
342 /* Advance LINES on LF's infile without doing output */
345 struct line_filter *lf;
350 lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
353 if (! lf_refill (lf))
364 /* Snarf a line into a buffer. Return EOF if EOF, 0 if error, 1 if OK. */
366 lf_snarf (lf, buffer, bufsize)
367 struct line_filter *lf;
371 char *start = lf->bufpos;
375 char *next = (char *) memchr (start, '\n', lf->buflim + 1 - start);
376 size_t s = next - start;
379 memcpy (buffer, start, s);
380 if (next < lf->buflim)
383 lf->bufpos = next + 1;
386 if (! lf_refill (lf))
405 initialize_main (&argc, &argv);
406 program_name = argv[0];
408 editor = getenv ("EDITOR");
411 differ = getenv ("DIFF");
417 /* parse command line args */
418 while ((opt = getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, 0))
453 diffarg ("--left-column");
461 suppress_common_flag = 1;
469 printf ("sdiff - GNU diffutils version %s\n", version_string);
483 if (ferror (stdout) || fclose (stdout) != 0)
484 fatal ("write error");
492 if (argc - optind != 2)
493 try_help (argc - optind < 2 ? "missing operand" : "extra operand");
497 /* easy case: diff does everything for us */
498 if (suppress_common_flag)
499 diffarg ("--suppress-common-lines");
502 diffarg (argv[optind]);
503 diffarg (argv[optind + 1]);
509 FILE *left, *right, *out, *diffout;
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]);
517 if (leftdir && rightdir)
518 fatal ("both files to be compared are directories");
520 left = ck_fopen (expand_name (argv[optind], leftdir, argv[optind + 1]), "r");
522 right = ck_fopen (expand_name (argv[optind + 1], rightdir, argv[optind]), "r");
523 out = ck_fopen (out_file, "w");
525 diffarg ("--sdiff-merge-assist");
527 diffarg (argv[optind]);
528 diffarg (argv[optind + 1]);
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++)
544 char const *a = diffargv[i];
545 SYSTEM_QUOTE_ARG (p, a);
549 diffout = popen (command, "r");
551 perror_fatal (command);
554 #else /* HAVE_FORK */
558 if (pipe (diff_fds) != 0)
559 perror_fatal ("pipe");
563 perror_fatal ("fork failed");
566 signal (SIGINT, SIG_IGN); /* in case user interrupts editor */
567 signal (SIGPIPE, SIG_DFL);
570 if (diff_fds[1] != STDOUT_FILENO)
572 dup2 (diff_fds[1], STDOUT_FILENO);
580 diffout = fdopen (diff_fds[0], "r");
582 perror_fatal ("fdopen");
584 #endif /* HAVE_FORK */
586 lf_init (&diff_filt, diffout);
587 lf_init (&lfilt, left);
588 lf_init (&rfilt, right);
590 interact_ok = interact (&diff_filt, &lfilt, &rfilt, out);
600 wstatus = pclose (diffout);
603 while (waitpid (diffpid, &wstatus, 0) < 0)
607 perror_fatal ("wait failed");
620 if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2))
621 fatal ("Subsidiary diff failed");
625 exit (WEXITSTATUS (wstatus));
628 return 0; /* Fool -Wall . . . */
635 static unsigned diffargs, diffargsmax;
637 if (diffargs == diffargsmax)
641 diffargv = (char const **) xmalloc (sizeof (char));
645 diffargv = (char const **) realloc (diffargv,
646 diffargsmax * sizeof (char const *));
648 fatal ("out of memory");
650 diffargv[diffargs++] = a;
656 execvp (diffbin, (char **) diffargv);
657 write (STDERR_FILENO, diffbin, strlen (diffbin));
658 write (STDERR_FILENO, ": not found\n", 12);
665 /* Signal handling */
667 #define NUM_SIGS (sizeof (sigs) / sizeof (*sigs))
668 static int const sigs[] = {
688 /* Prefer `sigaction' if it is available, since `signal' can lose signals. */
690 static struct sigaction initial_action[NUM_SIGS];
691 #define initial_handler(i) (initial_action[i].sa_handler)
693 static RETSIGTYPE (*initial_action[NUM_SIGS]) ();
694 #define initial_handler(i) (initial_action[i])
697 static int volatile ignore_SIGINT;
698 static int volatile signal_received;
699 static int sigs_trapped;
708 if (! (s == SIGINT && ignore_SIGINT))
718 struct sigaction catchaction;
719 bzero (&catchaction, sizeof (catchaction));
720 catchaction.sa_handler = catchsig;
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;
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++)
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");
736 #else /* ! HAVE_SIGACTION */
737 for (i = 0; i < NUM_SIGS; i++)
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");
744 #endif /* ! HAVE_SIGACTION */
746 #if !defined(SIGCHLD) && defined(SIGCLD)
747 #define SIGCHLD SIGCLD
750 /* System V fork+wait does not work if SIGCHLD is ignored. */
751 signal (SIGCHLD, SIG_DFL);
757 /* Untrap signal S, or all trapped signals if S is zero. */
765 for (i = 0; i < NUM_SIGS; i++)
766 if ((!s || sigs[i] == s) && initial_handler (i) != SIG_IGN)
768 sigaction (sigs[i], &initial_action[i], 0);
770 signal (sigs[i], initial_action[i]);
774 /* Exit if a signal has been received. */
778 int s = signal_received;
783 /* Yield an exit status indicating that a signal was received. */
787 /* That didn't work, so exit with error status. */
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");
815 if (!ISSPACE (c) || c == '\n')
820 perror_fatal ("input error");
828 while ((c = getchar ()) != '\n' && c != EOF)
831 perror_fatal ("input error");
835 /* interpret an edit command */
837 edit (left, lenl, right, lenr, outfile)
838 struct line_filter *left;
840 struct line_filter *right;
849 cmd1 = 0; /* Pacify `gcc -W'. */
853 if (putchar ('%') != '%')
854 perror_fatal ("output error");
857 cmd0 = skip_white ();
860 case 'l': case 'r': case 's': case 'v': case 'q':
861 if (skip_white () != '\n')
871 cmd1 = skip_white ();
874 case 'l': case 'r': case 'b':
875 if (skip_white () != '\n')
912 lf_copy (left, lenl, outfile);
913 lf_skip (right, lenr);
916 lf_copy (right, lenr, outfile);
917 lf_skip (left, lenl);
920 suppress_common_flag = 1;
923 suppress_common_flag = 0;
939 asprintf (&tmpname, "%s/sdiff.XXXXXX",
940 getenv("TMPDIR") ?: P_tmpdir);
942 perror_fatal ("temporary file name");
943 tfd = mkstemp(tmpname);
945 perror_fatal ("temporary file name");
946 tmp = fdopen (tfd, "w+");
948 perror_fatal ("temporary file name");
952 if (cmd1 == 'l' || cmd1 == 'b')
953 lf_copy (left, lenl, tmp);
955 lf_skip (left, lenl);
957 if (cmd1 == 'r' || cmd1 == 'b')
958 lf_copy (right, lenr, tmp);
960 lf_skip (right, lenr);
967 char *command = xmalloc (strlen (edbin) + strlen (tmpname) + 2);
968 sprintf (command, "%s %s", edbin, tmpname);
969 wstatus = system (command);
971 #else /* HAVE_FORK */
987 execvp (edbin, (char **) argv);
988 write (STDERR_FILENO, edbin, strlen (edbin));
989 write (STDERR_FILENO, ": not found\n", 12);
994 perror_fatal ("fork failed");
996 while (waitpid (pid, &wstatus, 0) < 0)
1000 perror_fatal ("wait failed");
1003 #endif /* HAVE_FORK */
1006 fatal ("Subsidiary editor failed");
1009 if (fseek (tmp, 0L, SEEK_SET) != 0)
1010 perror_fatal ("fseek");
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);
1017 while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
1020 ck_fwrite (buf, size, outfile);
1037 /* Alternately reveal bursts of diff output and handle user commands. */
1039 interact (diff, left, right, outfile)
1040 struct line_filter *diff;
1041 struct line_filter *left;
1042 struct line_filter *right;
1047 char diff_help[256];
1048 int snarfed = lf_snarf (diff, diff_help, sizeof (diff_help));
1055 switch (diff_help[0])
1058 puts (diff_help + 1);
1062 int lenl = atoi (diff_help + 1), lenr, lenmax;
1063 char *p = strchr (diff_help, ',');
1067 lenr = atoi (p + 1);
1068 lenmax = max (lenl, lenr);
1070 if (suppress_common_flag)
1071 lf_skip (diff, lenmax);
1073 lf_copy (diff, lenmax, stdout);
1075 lf_copy (left, lenl, outfile);
1076 lf_skip (right, lenr);
1081 int lenl = atoi (diff_help + 1), lenr;
1082 char *p = strchr (diff_help, ',');
1086 lenr = atoi (p + 1);
1087 lf_copy (diff, max (lenl, lenr), stdout);
1088 if (! edit (left, lenl, right, lenr, outfile))
1101 /* temporary lossage: this is torn from gnu libc */
1102 /* Return nonzero if DIR is an existing directory. */
1108 return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);