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