Merge branch 'vendor/DHCPCD'
[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   textdomain (PACKAGE);
456   c_stack_action (cleanup);
457
458   prog = getenv ("EDITOR");
459   if (prog)
460     editor_program = prog;
461
462   diffarg (DEFAULT_DIFF_PROGRAM);
463
464   /* parse command line args */
465   while ((opt = getopt_long (argc, argv, "abBdEHiI:lo:stvw:WZ", longopts, 0))
466          != -1)
467     {
468       switch (opt)
469         {
470         case 'a':
471           diffarg ("-a");
472           break;
473
474         case 'b':
475           diffarg ("-b");
476           break;
477
478         case 'B':
479           diffarg ("-B");
480           break;
481
482         case 'd':
483           diffarg ("-d");
484           break;
485
486         case 'E':
487           diffarg ("-E");
488           break;
489
490         case 'H':
491           diffarg ("-H");
492           break;
493
494         case 'i':
495           diffarg ("-i");
496           break;
497
498         case 'I':
499           diffarg ("-I");
500           diffarg (optarg);
501           break;
502
503         case 'l':
504           diffarg ("--left-column");
505           break;
506
507         case 'o':
508           output = optarg;
509           break;
510
511         case 's':
512           suppress_common_lines = true;
513           break;
514
515         case 't':
516           diffarg ("-t");
517           break;
518
519         case 'v':
520           version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version,
521                        AUTHORS, (char *) NULL);
522           check_stdout ();
523           return EXIT_SUCCESS;
524
525         case 'w':
526           diffarg ("-W");
527           diffarg (optarg);
528           break;
529
530         case 'W':
531           diffarg ("-w");
532           break;
533
534         case 'Z':
535           diffarg ("-Z");
536           break;
537
538         case DIFF_PROGRAM_OPTION:
539           diffargv[0] = optarg;
540           break;
541
542         case HELP_OPTION:
543           usage ();
544           check_stdout ();
545           return EXIT_SUCCESS;
546
547         case STRIP_TRAILING_CR_OPTION:
548           diffarg ("--strip-trailing-cr");
549           break;
550
551         case TABSIZE_OPTION:
552           diffarg ("--tabsize");
553           diffarg (optarg);
554           break;
555
556         default:
557           try_help (0, 0);
558         }
559     }
560
561   if (argc - optind != 2)
562     {
563       if (argc - optind < 2)
564         try_help ("missing operand after '%s'", argv[argc - 1]);
565       else
566         try_help ("extra operand '%s'", argv[optind + 2]);
567     }
568
569   if (! output)
570     {
571       /* easy case: diff does everything for us */
572       if (suppress_common_lines)
573         diffarg ("--suppress-common-lines");
574       diffarg ("-y");
575       diffarg ("--");
576       diffarg (argv[optind]);
577       diffarg (argv[optind + 1]);
578       diffarg (0);
579       execvp (diffargv[0], (char **) diffargv);
580       perror_fatal (diffargv[0]);
581     }
582   else
583     {
584       char const *lname, *rname;
585       FILE *left, *right, *out, *diffout;
586       bool interact_ok;
587       struct line_filter lfilt;
588       struct line_filter rfilt;
589       struct line_filter diff_filt;
590       bool leftdir = diraccess (argv[optind]);
591       bool rightdir = diraccess (argv[optind + 1]);
592
593       if (leftdir & rightdir)
594         fatal ("both files to be compared are directories");
595
596       lname = expand_name (argv[optind], leftdir, argv[optind + 1]);
597       left = ck_fopen (lname, "r");
598       rname = expand_name (argv[optind + 1], rightdir, argv[optind]);
599       right = ck_fopen (rname, "r");
600       out = ck_fopen (output, "w");
601
602       diffarg ("--sdiff-merge-assist");
603       diffarg ("--");
604       diffarg (argv[optind]);
605       diffarg (argv[optind + 1]);
606       diffarg (0);
607
608       trapsigs ();
609
610 #if ! HAVE_WORKING_FORK
611       {
612         char *command = system_quote_argv (SCI_SYSTEM, (char **) diffargv);
613         errno = 0;
614         diffout = popen (command, "r");
615         if (! diffout)
616           perror_fatal (command);
617         free (command);
618       }
619 #else
620       {
621         int diff_fds[2];
622
623         if (pipe (diff_fds) != 0)
624           perror_fatal ("pipe");
625
626         diffpid = fork ();
627         if (diffpid < 0)
628           perror_fatal ("fork");
629         if (! diffpid)
630           {
631             /* Alter the child's SIGINT and SIGPIPE handlers;
632                this may munge the parent.
633                The child ignores SIGINT in case the user interrupts the editor.
634                The child does not ignore SIGPIPE, even if the parent does.  */
635             if (initial_handler (handler_index_of_SIGINT) != SIG_IGN)
636               signal_handler (SIGINT, SIG_IGN);
637             signal_handler (SIGPIPE, SIG_DFL);
638             close (diff_fds[0]);
639             if (diff_fds[1] != STDOUT_FILENO)
640               {
641                 dup2 (diff_fds[1], STDOUT_FILENO);
642                 close (diff_fds[1]);
643               }
644
645             execvp (diffargv[0], (char **) diffargv);
646             _exit (errno == ENOENT ? 127 : 126);
647           }
648
649         close (diff_fds[1]);
650         diffout = fdopen (diff_fds[0], "r");
651         if (! diffout)
652           perror_fatal ("fdopen");
653       }
654 #endif
655
656       lf_init (&diff_filt, diffout);
657       lf_init (&lfilt, left);
658       lf_init (&rfilt, right);
659
660       interact_ok = interact (&diff_filt, &lfilt, lname, &rfilt, rname, out);
661
662       ck_fclose (left);
663       ck_fclose (right);
664       ck_fclose (out);
665
666       {
667         int wstatus;
668         int werrno = 0;
669
670 #if ! HAVE_WORKING_FORK
671         wstatus = pclose (diffout);
672         if (wstatus == -1)
673           werrno = errno;
674 #else
675         ck_fclose (diffout);
676         while (waitpid (diffpid, &wstatus, 0) < 0)
677           if (errno == EINTR)
678             checksigs ();
679           else
680             perror_fatal ("waitpid");
681         diffpid = 0;
682 #endif
683
684         if (tmpname)
685           {
686             unlink (tmpname);
687             tmpname = 0;
688           }
689
690         if (! interact_ok)
691           exiterr ();
692
693         check_child_status (werrno, wstatus, EXIT_FAILURE, diffargv[0]);
694         untrapsig (0);
695         checksigs ();
696         exit (WEXITSTATUS (wstatus));
697       }
698     }
699   return EXIT_SUCCESS;                  /* Fool '-Wall'.  */
700 }
701
702 static void
703 diffarg (char const *a)
704 {
705   static size_t diffargs, diffarglim;
706
707   if (diffargs == diffarglim)
708     {
709       if (! diffarglim)
710         diffarglim = 16;
711       else if (PTRDIFF_MAX / (2 * sizeof *diffargv) <= diffarglim)
712         xalloc_die ();
713       else
714         diffarglim *= 2;
715       diffargv = xrealloc (diffargv, diffarglim * sizeof *diffargv);
716     }
717   diffargv[diffargs++] = a;
718 }
719 \f
720 /* Signal handling */
721
722 static bool volatile ignore_SIGINT;
723 static int volatile signal_received;
724 static bool sigs_trapped;
725
726 static void
727 catchsig (int s)
728 {
729 #if ! HAVE_SIGACTION
730   signal (s, SIG_IGN);
731 #endif
732   if (! (s == SIGINT && ignore_SIGINT))
733     signal_received = s;
734 }
735
736 #if HAVE_SIGACTION
737 static struct sigaction catchaction;
738
739 static void
740 signal_handler (int sig, void (*handler) (int))
741 {
742   catchaction.sa_handler = handler;
743   sigaction (sig, &catchaction, 0);
744 }
745 #endif
746
747 static void
748 trapsigs (void)
749 {
750   int i;
751
752 #if HAVE_SIGACTION
753   catchaction.sa_flags = SA_RESTART;
754   sigemptyset (&catchaction.sa_mask);
755   for (i = 0;  i < NUM_SIGS;  i++)
756     sigaddset (&catchaction.sa_mask, sigs[i]);
757 #endif
758
759   for (i = 0;  i < NUM_SIGS;  i++)
760     {
761 #if HAVE_SIGACTION
762       sigaction (sigs[i], 0, &initial_action[i]);
763 #else
764       initial_action[i] = signal (sigs[i], SIG_IGN);
765 #endif
766       if (initial_handler (i) != SIG_IGN)
767         signal_handler (sigs[i], catchsig);
768     }
769
770 #ifdef SIGCHLD
771   /* System V fork+wait does not work if SIGCHLD is ignored.  */
772   signal (SIGCHLD, SIG_DFL);
773 #endif
774
775   sigs_trapped = true;
776 }
777
778 /* Untrap signal S, or all trapped signals if S is zero.  */
779 static void
780 untrapsig (int s)
781 {
782   int i;
783
784   if (sigs_trapped)
785     for (i = 0;  i < NUM_SIGS;  i++)
786       if ((! s || sigs[i] == s)  &&  initial_handler (i) != SIG_IGN)
787         {
788 #if HAVE_SIGACTION
789           sigaction (sigs[i], &initial_action[i], 0);
790 #else
791           signal (sigs[i], initial_action[i]);
792 #endif
793         }
794 }
795
796 /* Exit if a signal has been received.  */
797 static void
798 checksigs (void)
799 {
800   int s = signal_received;
801   if (s)
802     {
803       cleanup (0);
804
805       /* Yield an exit status indicating that a signal was received.  */
806       untrapsig (s);
807       kill (getpid (), s);
808
809       /* That didn't work, so exit with error status.  */
810       exit (EXIT_TROUBLE);
811     }
812 }
813 \f
814 static void
815 give_help (void)
816 {
817   fprintf (stderr, "%s", _("\
818 ed:\tEdit then use both versions, each decorated with a header.\n\
819 eb:\tEdit then use both versions.\n\
820 el or e1:\tEdit then use the left version.\n\
821 er or e2:\tEdit then use the right version.\n\
822 e:\tDiscard both versions then edit a new one.\n\
823 l or 1:\tUse the left version.\n\
824 r or 2:\tUse the right version.\n\
825 s:\tSilently include common lines.\n\
826 v:\tVerbosely include common lines.\n\
827 q:\tQuit.\n\
828 "));
829 }
830
831 static int
832 skip_white (void)
833 {
834   int c;
835   for (;;)
836     {
837       c = getchar ();
838       if (! isspace (c) || c == '\n')
839         break;
840       checksigs ();
841     }
842   if (ferror (stdin))
843     perror_fatal (_("read failed"));
844   return c;
845 }
846
847 static void
848 flush_line (void)
849 {
850   int c;
851   while ((c = getchar ()) != '\n' && c != EOF)
852     continue;
853   if (ferror (stdin))
854     perror_fatal (_("read failed"));
855 }
856
857
858 /* interpret an edit command */
859 static bool
860 edit (struct line_filter *left, char const *lname, lin lline, lin llen,
861       struct line_filter *right, char const *rname, lin rline, lin rlen,
862       FILE *outfile)
863 {
864   for (;;)
865     {
866       int cmd0 IF_LINT (= 0);
867       int cmd1 IF_LINT (= 0);
868       bool gotcmd = false;
869
870       while (! gotcmd)
871         {
872           if (putchar ('%') != '%')
873             perror_fatal (_("write failed"));
874           ck_fflush (stdout);
875
876           cmd0 = skip_white ();
877           switch (cmd0)
878             {
879             case '1': case '2': case 'l': case 'r':
880             case 's': case 'v': case 'q':
881               if (skip_white () != '\n')
882                 {
883                   give_help ();
884                   flush_line ();
885                   continue;
886                 }
887               gotcmd = true;
888               break;
889
890             case 'e':
891               cmd1 = skip_white ();
892               switch (cmd1)
893                 {
894                 case '1': case '2': case 'b': case 'd': case 'l': case 'r':
895                   if (skip_white () != '\n')
896                     {
897                       give_help ();
898                       flush_line ();
899                       continue;
900                     }
901                   gotcmd = true;
902                   break;
903                 case '\n':
904                   gotcmd = true;
905                   break;
906                 default:
907                   give_help ();
908                   flush_line ();
909                   continue;
910                 }
911               break;
912
913             case EOF:
914               if (feof (stdin))
915                 {
916                   gotcmd = true;
917                   cmd0 = 'q';
918                   break;
919                 }
920               /* Fall through.  */
921             default:
922               flush_line ();
923               /* Fall through.  */
924             case '\n':
925               give_help ();
926               continue;
927             }
928         }
929
930       switch (cmd0)
931         {
932         case '1': case 'l':
933           lf_copy (left, llen, outfile);
934           lf_skip (right, rlen);
935           return true;
936         case '2': case 'r':
937           lf_copy (right, rlen, outfile);
938           lf_skip (left, llen);
939           return true;
940         case 's':
941           suppress_common_lines = true;
942           break;
943         case 'v':
944           suppress_common_lines = false;
945           break;
946         case 'q':
947           return false;
948         case 'e':
949           {
950             int fd;
951
952             if (tmpname)
953               tmp = fopen (tmpname, "w");
954             else
955               {
956                 if ((fd = temporary_file ()) < 0)
957                   perror_fatal ("mkstemp");
958                 tmp = fdopen (fd, "w");
959               }
960
961             if (! tmp)
962               perror_fatal (tmpname);
963
964             switch (cmd1)
965               {
966               case 'd':
967                 if (llen)
968                   {
969                     if (llen == 1)
970                       fprintf (tmp, "--- %s %ld\n", lname, (long int) lline);
971                     else
972                       fprintf (tmp, "--- %s %ld,%ld\n", lname,
973                                (long int) lline,
974                                (long int) (lline + llen - 1));
975                   }
976                 /* Fall through.  */
977               case '1': case 'b': case 'l':
978                 lf_copy (left, llen, tmp);
979                 break;
980
981               default:
982                 lf_skip (left, llen);
983                 break;
984               }
985
986             switch (cmd1)
987               {
988               case 'd':
989                 if (rlen)
990                   {
991                     if (rlen == 1)
992                       fprintf (tmp, "+++ %s %ld\n", rname, (long int) rline);
993                     else
994                       fprintf (tmp, "+++ %s %ld,%ld\n", rname,
995                                (long int) rline,
996                                (long int) (rline + rlen - 1));
997                   }
998                 /* Fall through.  */
999               case '2': case 'b': case 'r':
1000                 lf_copy (right, rlen, tmp);
1001                 break;
1002
1003               default:
1004                 lf_skip (right, rlen);
1005                 break;
1006               }
1007
1008             ck_fclose (tmp);
1009
1010             {
1011               int wstatus;
1012               int werrno = 0;
1013               char const *argv[3];
1014
1015               ignore_SIGINT = true;
1016               checksigs ();
1017               argv[0] = editor_program;
1018               argv[1] = tmpname;
1019               argv[2] = 0;
1020
1021               {
1022 #if ! HAVE_WORKING_FORK
1023                 char *command = system_quote_argv (SCI_SYSTEM, (char **) argv);
1024                 wstatus = system (command);
1025                 if (wstatus == -1)
1026                   werrno = errno;
1027                 free (command);
1028 #else
1029                 pid_t pid;
1030
1031                 pid = fork ();
1032                 if (pid == 0)
1033                   {
1034                     execvp (editor_program, (char **) argv);
1035                     _exit (errno == ENOENT ? 127 : 126);
1036                   }
1037
1038                 if (pid < 0)
1039                   perror_fatal ("fork");
1040
1041                 while (waitpid (pid, &wstatus, 0) < 0)
1042                   if (errno == EINTR)
1043                     checksigs ();
1044                   else
1045                     perror_fatal ("waitpid");
1046 #endif
1047               }
1048
1049               ignore_SIGINT = false;
1050               check_child_status (werrno, wstatus, EXIT_SUCCESS,
1051                                   editor_program);
1052             }
1053
1054             {
1055               char buf[SDIFF_BUFSIZE];
1056               size_t size;
1057               tmp = ck_fopen (tmpname, "r");
1058               while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
1059                 {
1060                   checksigs ();
1061                   ck_fwrite (buf, size, outfile);
1062                 }
1063               ck_fclose (tmp);
1064             }
1065             return true;
1066           }
1067         default:
1068           give_help ();
1069           break;
1070         }
1071     }
1072 }
1073 \f
1074 /* Alternately reveal bursts of diff output and handle user commands.  */
1075 static bool
1076 interact (struct line_filter *diff,
1077           struct line_filter *left, char const *lname,
1078           struct line_filter *right, char const *rname,
1079           FILE *outfile)
1080 {
1081   lin lline = 1, rline = 1;
1082
1083   for (;;)
1084     {
1085       char diff_help[256];
1086       int snarfed = lf_snarf (diff, diff_help, sizeof diff_help);
1087
1088       if (snarfed <= 0)
1089         return snarfed != 0;
1090
1091       checksigs ();
1092
1093       if (diff_help[0] == ' ')
1094         puts (diff_help + 1);
1095       else
1096         {
1097           char *numend;
1098           uintmax_t val;
1099           lin llen, rlen, lenmax;
1100           errno = 0;
1101           llen = val = strtoumax (diff_help + 1, &numend, 10);
1102           if (llen < 0 || llen != val || errno || *numend != ',')
1103             fatal (diff_help);
1104           rlen = val = strtoumax (numend + 1, &numend, 10);
1105           if (rlen < 0 || rlen != val || errno || *numend)
1106             fatal (diff_help);
1107
1108           lenmax = MAX (llen, rlen);
1109
1110           switch (diff_help[0])
1111             {
1112             case 'i':
1113               if (suppress_common_lines)
1114                 lf_skip (diff, lenmax);
1115               else
1116                 lf_copy (diff, lenmax, stdout);
1117
1118               lf_copy (left, llen, outfile);
1119               lf_skip (right, rlen);
1120               break;
1121
1122             case 'c':
1123               lf_copy (diff, lenmax, stdout);
1124               if (! edit (left, lname, lline, llen,
1125                           right, rname, rline, rlen,
1126                           outfile))
1127                 return false;
1128               break;
1129
1130             default:
1131               fatal (diff_help);
1132             }
1133
1134           lline += llen;
1135           rline += rlen;
1136         }
1137     }
1138 }
1139
1140 /* Return true if DIR is an existing directory.  */
1141 static bool
1142 diraccess (char const *dir)
1143 {
1144   struct stat buf;
1145   return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
1146 }
1147
1148 #ifndef P_tmpdir
1149 # define P_tmpdir "/tmp"
1150 #endif
1151 #ifndef TMPDIR_ENV
1152 # define TMPDIR_ENV "TMPDIR"
1153 #endif
1154
1155 /* Open a temporary file and return its file descriptor.  Put into
1156    tmpname the address of a newly allocated buffer that holds the
1157    file's name.  Use the prefix "sdiff".  */
1158 static int
1159 temporary_file (void)
1160 {
1161   char const *tmpdir = getenv (TMPDIR_ENV);
1162   char const *dir = tmpdir ? tmpdir : P_tmpdir;
1163   char *buf = xmalloc (strlen (dir) + 1 + 5 + 6 + 1);
1164   int fd;
1165   sprintf (buf, "%s/sdiffXXXXXX", dir);
1166   fd = mkstemp (buf);
1167   if (0 <= fd)
1168     tmpname = buf;
1169   return fd;
1170 }