xref: /plan9/sys/src/ape/cmd/diff/sdiff.c (revision 0b459c2cb92b7c9d88818e9a2f72e678e5bc4553)
1 /* SDIFF -- interactive merge front end to diff
2    Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
3 
4 This file is part of GNU DIFF.
5 
6 GNU DIFF is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU DIFF is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU DIFF; see the file COPYING.  If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
19 
20 /* GNU SDIFF was written by Thomas Lord. */
21 
22 #include <sys/cdefs.h>
23 __FBSDID("$FreeBSD: src/contrib/diff/sdiff.c,v 1.1.1.1.12.1 2002/01/28 01:26:35 nectar Exp $");
24 
25 #include "system.h"
26 #include <stdio.h>
27 #include <signal.h>
28 #include "getopt.h"
29 
30 /* Size of chunks read from files which must be parsed into lines. */
31 #define SDIFF_BUFSIZE ((size_t) 65536)
32 
33 /* Default name of the diff program */
34 #ifndef DIFF_PROGRAM
35 #define DIFF_PROGRAM "/usr/bin/diff"
36 #endif
37 
38 /* Users' editor of nonchoice */
39 #ifndef DEFAULT_EDITOR_PROGRAM
40 #define DEFAULT_EDITOR_PROGRAM "ed"
41 #endif
42 
43 extern char version_string[];
44 static char const *program_name;
45 static char const *diffbin = DIFF_PROGRAM;
46 static char const *edbin = DEFAULT_EDITOR_PROGRAM;
47 static char const **diffargv;
48 
49 static char *tmpname;
50 static int volatile tmpmade;
51 
52 #if HAVE_FORK
53 static pid_t volatile diffpid;
54 #endif
55 
56 struct line_filter;
57 
58 static FILE *ck_fopen PARAMS((char const *, char const *));
59 static RETSIGTYPE catchsig PARAMS((int));
60 static VOID *xmalloc PARAMS((size_t));
61 static char const *expand_name PARAMS((char *, int, char const *));
62 static int edit PARAMS((struct line_filter *, int, struct line_filter *, int, FILE*));
63 static int interact PARAMS((struct line_filter *, struct line_filter *, struct line_filter *, FILE*));
64 static int lf_snarf PARAMS((struct line_filter *, char *, size_t));
65 static int skip_white PARAMS((void));
66 static size_t ck_fread PARAMS((char *, size_t, FILE *));
67 static size_t lf_refill PARAMS((struct line_filter *));
68 static void checksigs PARAMS((void));
69 static void ck_fclose PARAMS((FILE *));
70 static void ck_fflush PARAMS((FILE *));
71 static void ck_fwrite PARAMS((char const *, size_t, FILE *));
72 static void cleanup PARAMS((void));
73 static void diffarg PARAMS((char const *));
74 static void execdiff PARAMS((void));
75 static void exiterr PARAMS((void));
76 static void fatal PARAMS((char const *));
77 static void flush_line PARAMS((void));
78 static void give_help PARAMS((void));
79 static void lf_copy PARAMS((struct line_filter *, int, FILE *));
80 static void lf_init PARAMS((struct line_filter *, FILE *));
81 static void lf_skip PARAMS((struct line_filter *, int));
82 static void perror_fatal PARAMS((char const *));
83 static void trapsigs PARAMS((void));
84 static void try_help PARAMS((char const *));
85 static void untrapsig PARAMS((int));
86 static void usage PARAMS((void));
87 
88 static int diraccess PARAMS((char const *));
89 
90 /* Options: */
91 
92 /* name of output file if -o spec'd */
93 static char *out_file;
94 
95 /* do not print common lines if true, set by -s option */
96 static int suppress_common_flag;
97 
98 static struct option const longopts[] =
99 {
100   {"ignore-blank-lines", 0, 0, 'B'},
101   {"speed-large-files", 0, 0, 'H'},
102   {"ignore-matching-lines", 1, 0, 'I'},
103   {"ignore-all-space", 0, 0, 'W'}, /* swap W and w for historical reasons */
104   {"text", 0, 0, 'a'},
105   {"ignore-space-change", 0, 0, 'b'},
106   {"minimal", 0, 0, 'd'},
107   {"ignore-case", 0, 0, 'i'},
108   {"left-column", 0, 0, 'l'},
109   {"output", 1, 0, 'o'},
110   {"suppress-common-lines", 0, 0, 's'},
111   {"expand-tabs", 0, 0, 't'},
112   {"width", 1, 0, 'w'},
113   {"version", 0, 0, 'v'},
114   {"help", 0, 0, 129},
115   {0, 0, 0, 0}
116 };
117 
118 static void
try_help(reason)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
usage()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
cleanup()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
exiterr()169 exiterr ()
170 {
171   cleanup ();
172   untrapsig (0);
173   checksigs ();
174   exit (2);
175 }
176 
177 static void
fatal(msg)178 fatal (msg)
179      char const *msg;
180 {
181   fprintf (stderr, "%s: %s\n", program_name, msg);
182   exiterr ();
183 }
184 
185 static void
perror_fatal(msg)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 *
xmalloc(size)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 *
ck_fopen(fname,type)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
ck_fclose(f)220 ck_fclose (f)
221      FILE *f;
222 {
223   if (fclose (f))
224     perror_fatal ("input/output error");
225 }
226 
227 static size_t
ck_fread(buf,size,f)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
ck_fwrite(buf,size,f)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
ck_fflush(f)250 ck_fflush (f)
251      FILE *f;
252 {
253   if (fflush (f) != 0)
254     perror_fatal ("output error");
255 }
256 
257 static char const *
expand_name(name,is_dir,other_name)258 expand_name (name, is_dir, other_name)
259      char *name;
260      int is_dir;
261      char const *other_name;
262 {
263   if (strcmp (name, "-") == 0)
264     fatal ("cannot interactively merge standard input");
265   if (!is_dir)
266     return name;
267   else
268     {
269       /* Yield NAME/BASE, where BASE is OTHER_NAME's basename.  */
270       char const *p = filename_lastdirchar (other_name);
271       char const *base = p ? p+1 : other_name;
272       size_t namelen = strlen (name), baselen = strlen (base);
273       char *r = xmalloc (namelen + baselen + 2);
274       memcpy (r, name, namelen);
275       r[namelen] = '/';
276       memcpy (r + namelen + 1, base, baselen + 1);
277       return r;
278     }
279 }
280 
281 
282 
283 struct line_filter {
284   FILE *infile;
285   char *bufpos;
286   char *buffer;
287   char *buflim;
288 };
289 
290 static void
lf_init(lf,infile)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
lf_refill(lf)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
lf_copy(lf,lines,outfile)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
lf_skip(lf,lines)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
lf_snarf(lf,buffer,bufsize)366 lf_snarf (lf, buffer, bufsize)
367      struct line_filter *lf;
368      char *buffer;
369      size_t bufsize;
370 {
371   char *start = lf->bufpos;
372 
373   for (;;)
374     {
375       char *next = (char *) memchr (start, '\n', lf->buflim + 1 - start);
376       size_t s = next - start;
377       if (bufsize <= s)
378 	return 0;
379       memcpy (buffer, start, s);
380       if (next < lf->buflim)
381 	{
382 	  buffer[s] = 0;
383 	  lf->bufpos = next + 1;
384 	  return 1;
385 	}
386       if (! lf_refill (lf))
387 	return s ? 0 : EOF;
388       buffer += s;
389       bufsize -= s;
390       start = next;
391     }
392 }
393 
394 
395 
396 int
main(argc,argv)397 main (argc, argv)
398      int argc;
399      char *argv[];
400 {
401   int opt;
402   char *editor;
403   char *differ;
404 
405   initialize_main (&argc, &argv);
406   program_name = argv[0];
407 
408   editor = getenv ("EDITOR");
409   if (editor)
410     edbin = editor;
411   differ = getenv ("DIFF");
412   if (differ)
413     diffbin = differ;
414 
415   diffarg ("diff");
416 
417   /* parse command line args */
418   while ((opt = getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, 0))
419 	 != EOF)
420     {
421       switch (opt)
422 	{
423 	case 'a':
424 	  diffarg ("-a");
425 	  break;
426 
427 	case 'b':
428 	  diffarg ("-b");
429 	  break;
430 
431 	case 'B':
432 	  diffarg ("-B");
433 	  break;
434 
435 	case 'd':
436 	  diffarg ("-d");
437 	  break;
438 
439 	case 'H':
440 	  diffarg ("-H");
441 	  break;
442 
443 	case 'i':
444 	  diffarg ("-i");
445 	  break;
446 
447 	case 'I':
448 	  diffarg ("-I");
449 	  diffarg (optarg);
450 	  break;
451 
452 	case 'l':
453 	  diffarg ("--left-column");
454 	  break;
455 
456 	case 'o':
457 	  out_file = optarg;
458 	  break;
459 
460 	case 's':
461 	  suppress_common_flag = 1;
462 	  break;
463 
464 	case 't':
465 	  diffarg ("-t");
466 	  break;
467 
468 	case 'v':
469 	  printf ("sdiff - GNU diffutils version %s\n", version_string);
470 	  exit (0);
471 
472 	case 'w':
473 	  diffarg ("-W");
474 	  diffarg (optarg);
475 	  break;
476 
477 	case 'W':
478 	  diffarg ("-w");
479 	  break;
480 
481 	case 129:
482 	  usage ();
483 	  if (ferror (stdout) || fclose (stdout) != 0)
484 	    fatal ("write error");
485 	  exit (0);
486 
487 	default:
488 	  try_help (0);
489 	}
490     }
491 
492   if (argc - optind != 2)
493     try_help (argc - optind < 2 ? "missing operand" : "extra operand");
494 
495   if (! out_file)
496     {
497       /* easy case: diff does everything for us */
498       if (suppress_common_flag)
499 	diffarg ("--suppress-common-lines");
500       diffarg ("-y");
501       diffarg ("--");
502       diffarg (argv[optind]);
503       diffarg (argv[optind + 1]);
504       diffarg (0);
505       execdiff ();
506     }
507   else
508     {
509       FILE *left, *right, *out, *diffout;
510       int interact_ok;
511       struct line_filter lfilt;
512       struct line_filter rfilt;
513       struct line_filter diff_filt;
514       int leftdir = diraccess (argv[optind]);
515       int rightdir = diraccess (argv[optind + 1]);
516 
517       if (leftdir && rightdir)
518 	fatal ("both files to be compared are directories");
519 
520       left = ck_fopen (expand_name (argv[optind], leftdir, argv[optind + 1]), "r");
521       ;
522       right = ck_fopen (expand_name (argv[optind + 1], rightdir, argv[optind]), "r");
523       out = ck_fopen (out_file, "w");
524 
525       diffarg ("--sdiff-merge-assist");
526       diffarg ("--");
527       diffarg (argv[optind]);
528       diffarg (argv[optind + 1]);
529       diffarg (0);
530 
531       trapsigs ();
532 
533 #if ! HAVE_FORK
534       {
535 	size_t cmdsize = 1;
536 	char *p, *command;
537 	int i;
538 
539 	for (i = 0;  diffargv[i];  i++)
540 	  cmdsize += 4 * strlen (diffargv[i]) + 3;
541 	command = p = xmalloc (cmdsize);
542 	for (i = 0;  diffargv[i];  i++)
543 	  {
544 	    char const *a = diffargv[i];
545 	    SYSTEM_QUOTE_ARG (p, a);
546 	    *p++ = ' ';
547 	  }
548 	p[-1] = '\0';
549 	diffout = popen (command, "r");
550 	if (!diffout)
551 	  perror_fatal (command);
552 	free (command);
553       }
554 #else /* HAVE_FORK */
555       {
556 	int diff_fds[2];
557 
558 	if (pipe (diff_fds) != 0)
559 	  perror_fatal ("pipe");
560 
561 	diffpid = fork ();
562 	if (diffpid < 0)
563 	  perror_fatal ("fork failed");
564 	if (!diffpid)
565 	  {
566 	    signal (SIGINT, SIG_IGN);  /* in case user interrupts editor */
567 	    signal (SIGPIPE, SIG_DFL);
568 
569 	    close (diff_fds[0]);
570 	    if (diff_fds[1] != STDOUT_FILENO)
571 	      {
572 		dup2 (diff_fds[1], STDOUT_FILENO);
573 		close (diff_fds[1]);
574 	      }
575 
576 	    execdiff ();
577 	  }
578 
579 	close (diff_fds[1]);
580 	diffout = fdopen (diff_fds[0], "r");
581 	if (!diffout)
582 	  perror_fatal ("fdopen");
583       }
584 #endif /* HAVE_FORK */
585 
586       lf_init (&diff_filt, diffout);
587       lf_init (&lfilt, left);
588       lf_init (&rfilt, right);
589 
590       interact_ok = interact (&diff_filt, &lfilt, &rfilt, out);
591 
592       ck_fclose (left);
593       ck_fclose (right);
594       ck_fclose (out);
595 
596       {
597 	int wstatus;
598 
599 #if ! HAVE_FORK
600 	wstatus = pclose (diffout);
601 #else
602 	ck_fclose (diffout);
603 	while (waitpid (diffpid, &wstatus, 0) < 0)
604 	  if (errno == EINTR)
605 	    checksigs ();
606 	  else
607 	    perror_fatal ("wait failed");
608 	diffpid = 0;
609 #endif
610 
611 	if (tmpmade)
612 	  {
613 	    unlink (tmpname);
614 	    tmpmade = 0;
615 	  }
616 
617 	if (! interact_ok)
618 	  exiterr ();
619 
620 	if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2))
621 	  fatal ("Subsidiary diff failed");
622 
623 	untrapsig (0);
624 	checksigs ();
625 	exit (WEXITSTATUS (wstatus));
626       }
627     }
628   return 0;			/* Fool -Wall . . . */
629 }
630 
631 static void
diffarg(a)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
execdiff()654 execdiff ()
655 {
656   execvp (diffbin, (char **) diffargv);
657   write (STDERR_FILENO, diffbin, strlen (diffbin));
658   write (STDERR_FILENO, ": not found\n", 12);
659   _exit (2);
660 }
661 
662 
663 
664 
665 /* Signal handling */
666 
667 #define NUM_SIGS (sizeof (sigs) / sizeof (*sigs))
668 static int const sigs[] = {
669 #ifdef SIGHUP
670        SIGHUP,
671 #endif
672 #ifdef SIGQUIT
673        SIGQUIT,
674 #endif
675 #ifdef SIGTERM
676        SIGTERM,
677 #endif
678 #ifdef SIGXCPU
679        SIGXCPU,
680 #endif
681 #ifdef SIGXFSZ
682        SIGXFSZ,
683 #endif
684        SIGINT,
685        SIGPIPE
686 };
687 
688 /* Prefer `sigaction' if it is available, since `signal' can lose signals.  */
689 #if HAVE_SIGACTION
690 static struct sigaction initial_action[NUM_SIGS];
691 #define initial_handler(i) (initial_action[i].sa_handler)
692 #else
693 static RETSIGTYPE (*initial_action[NUM_SIGS]) ();
694 #define initial_handler(i) (initial_action[i])
695 #endif
696 
697 static int volatile ignore_SIGINT;
698 static int volatile signal_received;
699 static int sigs_trapped;
700 
701 static RETSIGTYPE
catchsig(s)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
trapsigs()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
untrapsig(s)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
checksigs()776 checksigs ()
777 {
778   int s = signal_received;
779   if (s)
780     {
781       cleanup ();
782 
783       /* Yield an exit status indicating that a signal was received.  */
784       untrapsig (s);
785       kill (getpid (), s);
786 
787       /* That didn't work, so exit with error status.  */
788       exit (2);
789     }
790 }
791 
792 
793 
794 static void
give_help()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
skip_white()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
flush_line()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
edit(left,lenl,right,lenr,outfile)837 edit (left, lenl, right, lenr, outfile)
838      struct line_filter *left;
839      int lenl;
840      struct line_filter *right;
841      int lenr;
842      FILE *outfile;
843 {
844   for (;;)
845     {
846       int cmd0, cmd1;
847       int gotcmd = 0;
848 
849       cmd1 = 0; /* Pacify `gcc -W'.  */
850 
851       while (!gotcmd)
852 	{
853 	  if (putchar ('%') != '%')
854 	    perror_fatal ("output error");
855 	  ck_fflush (stdout);
856 
857 	  cmd0 = skip_white ();
858 	  switch (cmd0)
859 	    {
860 	    case 'l': case 'r': case 's': case 'v': case 'q':
861 	      if (skip_white () != '\n')
862 		{
863 		  give_help ();
864 		  flush_line ();
865 		  continue;
866 		}
867 	      gotcmd = 1;
868 	      break;
869 
870 	    case 'e':
871 	      cmd1 = skip_white ();
872 	      switch (cmd1)
873 		{
874 		case 'l': case 'r': case 'b':
875 		  if (skip_white () != '\n')
876 		    {
877 		      give_help ();
878 		      flush_line ();
879 		      continue;
880 		    }
881 		  gotcmd = 1;
882 		  break;
883 		case '\n':
884 		  gotcmd = 1;
885 		  break;
886 		default:
887 		  give_help ();
888 		  flush_line ();
889 		  continue;
890 		}
891 	      break;
892 	    case EOF:
893 	      if (feof (stdin))
894 		{
895 		  gotcmd = 1;
896 		  cmd0 = 'q';
897 		  break;
898 		}
899 	      /* falls through */
900 	    default:
901 	      flush_line ();
902 	      /* falls through */
903 	    case '\n':
904 	      give_help ();
905 	      continue;
906 	    }
907 	}
908 
909       switch (cmd0)
910 	{
911 	case 'l':
912 	  lf_copy (left, lenl, outfile);
913 	  lf_skip (right, lenr);
914 	  return 1;
915 	case 'r':
916 	  lf_copy (right, lenr, outfile);
917 	  lf_skip (left, lenl);
918 	  return 1;
919 	case 's':
920 	  suppress_common_flag = 1;
921 	  break;
922 	case 'v':
923 	  suppress_common_flag = 0;
924 	  break;
925 	case 'q':
926 	  return 0;
927 	case 'e':
928 	  {
929 	    int tfd;
930 	    FILE *tmp;
931 
932 	    if (tmpmade)
933 	      {
934 	        unlink (tmpname);
935 	        tmpmade = 0;
936 		free (tmpname);
937 	      }
938 
939 	    asprintf (&tmpname, "%s/sdiff.XXXXXX",
940 	      getenv("TMPDIR") ?: P_tmpdir);
941 	    if (tmpname == NULL)
942 	      perror_fatal ("temporary file name");
943 	    tfd = mkstemp(tmpname);
944 	    if (tfd == -1)
945 	      perror_fatal ("temporary file name");
946 	    tmp = fdopen (tfd, "w+");
947 	    if (tmp == NULL)
948 	      perror_fatal ("temporary file name");
949 
950 	    tmpmade = 1;
951 
952 	    if (cmd1 == 'l' || cmd1 == 'b')
953 	      lf_copy (left, lenl, tmp);
954 	    else
955 	      lf_skip (left, lenl);
956 
957 	    if (cmd1 == 'r' || cmd1 == 'b')
958 	      lf_copy (right, lenr, tmp);
959 	    else
960 	      lf_skip (right, lenr);
961 
962 	    ck_fflush (tmp);
963 
964 	    {
965 	      int wstatus;
966 #if ! HAVE_FORK
967 	      char *command = xmalloc (strlen (edbin) + strlen (tmpname) + 2);
968 	      sprintf (command, "%s %s", edbin, tmpname);
969 	      wstatus = system (command);
970 	      free (command);
971 #else /* HAVE_FORK */
972 	      pid_t pid;
973 
974 	      ignore_SIGINT = 1;
975 	      checksigs ();
976 
977 	      pid = fork ();
978 	      if (pid == 0)
979 		{
980 		  char const *argv[3];
981 		  int i = 0;
982 
983 		  argv[i++] = edbin;
984 		  argv[i++] = tmpname;
985 		  argv[i++] = 0;
986 
987 		  execvp (edbin, (char **) argv);
988 		  write (STDERR_FILENO, edbin, strlen (edbin));
989 		  write (STDERR_FILENO, ": not found\n", 12);
990 		  _exit (1);
991 		}
992 
993 	      if (pid < 0)
994 		perror_fatal ("fork failed");
995 
996 	      while (waitpid (pid, &wstatus, 0) < 0)
997 		if (errno == EINTR)
998 		  checksigs ();
999 		else
1000 		  perror_fatal ("wait failed");
1001 
1002 	      ignore_SIGINT = 0;
1003 #endif /* HAVE_FORK */
1004 
1005 	      if (wstatus != 0)
1006 		fatal ("Subsidiary editor failed");
1007 	    }
1008 
1009 	    if (fseek (tmp, 0L, SEEK_SET) != 0)
1010 	      perror_fatal ("fseek");
1011 	    {
1012 	      /* SDIFF_BUFSIZE is too big for a local var
1013 		 in some compilers, so we allocate it dynamically.  */
1014 	      char *buf = xmalloc (SDIFF_BUFSIZE);
1015 	      size_t size;
1016 
1017 	      while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
1018 		{
1019 		  checksigs ();
1020 		  ck_fwrite (buf, size, outfile);
1021 		}
1022 	      ck_fclose (tmp);
1023 
1024 	      free (buf);
1025 	    }
1026 	    return 1;
1027 	  }
1028 	default:
1029 	  give_help ();
1030 	  break;
1031 	}
1032     }
1033 }
1034 
1035 
1036 
1037 /* Alternately reveal bursts of diff output and handle user commands.  */
1038 static int
interact(diff,left,right,outfile)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
diraccess(dir)1104 diraccess (dir)
1105      char const *dir;
1106 {
1107   struct stat buf;
1108   return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
1109 }
1110