]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/diff/sdiff.c
This commit was generated by cvs2svn to compensate for changes in r104834,
[FreeBSD/FreeBSD.git] / contrib / diff / sdiff.c
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 \f
22 #include <sys/cdefs.h>
23 __FBSDID("$FreeBSD$");
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 \f
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 \f
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 = vfork ();
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 \f
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 \f
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 = vfork ();
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 \f
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 }