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