source: trunk/third/diffutils/sdiff.c @ 16149

Revision 16149, 24.8 KB checked in by rbasch, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r16148, which included commits to RCS files with non-trunk default branches.
Line 
1/* SDIFF -- interactive merge front end to diff
2   Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
3
4This file is part of GNU DIFF.
5
6GNU DIFF is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11GNU DIFF is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU DIFF; see the file COPYING.  If not, write to
18the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
19
20/* GNU SDIFF was written by Thomas Lord. */
21
22#include "system.h"
23#include <stdio.h>
24#include <signal.h>
25#include "getopt.h"
26
27/* Size of chunks read from files which must be parsed into lines. */
28#define SDIFF_BUFSIZE ((size_t) 65536)
29
30/* Default name of the diff program */
31#ifndef DIFF_PROGRAM
32#define DIFF_PROGRAM "/usr/bin/diff"
33#endif
34
35/* Users' editor of nonchoice */
36#ifndef DEFAULT_EDITOR_PROGRAM
37#define DEFAULT_EDITOR_PROGRAM "ed"
38#endif
39
40extern char version_string[];
41static char const *program_name;
42static char const *diffbin = DIFF_PROGRAM;
43static char const *edbin = DEFAULT_EDITOR_PROGRAM;
44static char const **diffargv;
45
46static char *tmpname;
47static int volatile tmpmade;
48
49#if HAVE_FORK
50static pid_t volatile diffpid;
51#endif
52
53struct line_filter;
54
55static FILE *ck_fopen PARAMS((char const *, char const *));
56static RETSIGTYPE catchsig PARAMS((int));
57static VOID *xmalloc PARAMS((size_t));
58static char const *expand_name PARAMS((char *, int, char const *));
59static int edit PARAMS((struct line_filter *, int, struct line_filter *, int, FILE*));
60static int interact PARAMS((struct line_filter *, struct line_filter *, struct line_filter *, FILE*));
61static int lf_snarf PARAMS((struct line_filter *, char *, size_t));
62static int skip_white PARAMS((void));
63static size_t ck_fread PARAMS((char *, size_t, FILE *));
64static size_t lf_refill PARAMS((struct line_filter *));
65static void checksigs PARAMS((void));
66static void ck_fclose PARAMS((FILE *));
67static void ck_fflush PARAMS((FILE *));
68static void ck_fwrite PARAMS((char const *, size_t, FILE *));
69static void cleanup PARAMS((void));
70static void diffarg PARAMS((char const *));
71static void execdiff PARAMS((void));
72static void exiterr PARAMS((void));
73static void fatal PARAMS((char const *));
74static void flush_line PARAMS((void));
75static void give_help PARAMS((void));
76static void lf_copy PARAMS((struct line_filter *, int, FILE *));
77static void lf_init PARAMS((struct line_filter *, FILE *));
78static void lf_skip PARAMS((struct line_filter *, int));
79static void perror_fatal PARAMS((char const *));
80static void trapsigs PARAMS((void));
81static void try_help PARAMS((char const *));
82static void untrapsig PARAMS((int));
83static void usage PARAMS((void));
84
85/* this lossage until the gnu libc conquers the universe */
86#if HAVE_TMPNAM
87#define private_tempnam() tmpnam ((char *) 0)
88#else
89#ifndef PVT_tmpdir
90#define PVT_tmpdir "/tmp"
91#endif
92#ifndef TMPDIR_ENV
93#define TMPDIR_ENV "TMPDIR"
94#endif
95static char *private_tempnam PARAMS((void));
96static int exists PARAMS((char const *));
97#endif
98static int diraccess PARAMS((char const *));
99
100/* Options: */
101
102/* name of output file if -o spec'd */
103static char *out_file;
104
105/* do not print common lines if true, set by -s option */
106static int suppress_common_flag;
107
108static struct option const longopts[] =
109{
110  {"ignore-blank-lines", 0, 0, 'B'},
111  {"speed-large-files", 0, 0, 'H'},
112  {"ignore-matching-lines", 1, 0, 'I'},
113  {"ignore-all-space", 0, 0, 'W'}, /* swap W and w for historical reasons */
114  {"text", 0, 0, 'a'},
115  {"ignore-space-change", 0, 0, 'b'},
116  {"minimal", 0, 0, 'd'},
117  {"ignore-case", 0, 0, 'i'},
118  {"left-column", 0, 0, 'l'},
119  {"output", 1, 0, 'o'},
120  {"suppress-common-lines", 0, 0, 's'},
121  {"expand-tabs", 0, 0, 't'},
122  {"width", 1, 0, 'w'},
123  {"version", 0, 0, 'v'},
124  {"help", 0, 0, 129},
125  {0, 0, 0, 0}
126};
127
128static void
129try_help (reason)
130     char const *reason;
131{
132  if (reason)
133    fprintf (stderr, "%s: %s\n", program_name, reason);
134  fprintf (stderr, "%s: Try `%s --help' for more information.\n",
135           program_name, program_name);
136  exit (2);
137}
138
139static void
140usage ()
141{
142  printf ("Usage: %s [OPTIONS]... FILE1 FILE2\n\n", program_name);
143  printf ("%s", "\
144  -o FILE  --output=FILE  Operate interactively, sending output to FILE.\n\n");
145  printf ("%s", "\
146  -i  --ignore-case  Consider upper- and lower-case to be the same.\n\
147  -W  --ignore-all-space  Ignore all white space.\n\
148  -b  --ignore-space-change  Ignore changes in the amount of white space.\n\
149  -B  --ignore-blank-lines  Ignore changes whose lines are all blank.\n\
150  -I RE  --ignore-matching-lines=RE  Ignore changes whose lines all match RE.\n\
151  -a  --text  Treat all files as text.\n\n");
152  printf ("%s", "\
153  -w NUM  --width=NUM  Output at most NUM (default 130) characters per line.\n\
154  -l  --left-column  Output only the left column of common lines.\n\
155  -s  --suppress-common-lines  Do not output common lines.\n\n");
156  printf ("\
157  -t  --expand-tabs  Expand tabs to spaces in output.\n\n");
158  printf ("%s", "\
159  -d  --minimal  Try hard to find a smaller set of changes.\n\
160  -H  --speed-large-files  Assume large files and many scattered small changes.\n\n");
161 printf ("%s", "\
162  -v  --version  Output version info.\n\
163  --help  Output this help.\n\n\
164If FILE1 or FILE2 is `-', read standard input.\n");
165}
166
167static void
168cleanup ()
169{
170#if HAVE_FORK
171  if (0 < diffpid)
172    kill (diffpid, SIGPIPE);
173#endif
174  if (tmpmade)
175    unlink (tmpname);
176}
177
178static void
179exiterr ()
180{
181  cleanup ();
182  untrapsig (0);
183  checksigs ();
184  exit (2);
185}
186
187static void
188fatal (msg)
189     char const *msg;
190{
191  fprintf (stderr, "%s: %s\n", program_name, msg);
192  exiterr ();
193}
194
195static void
196perror_fatal (msg)
197     char const *msg;
198{
199  int e = errno;
200  checksigs ();
201  fprintf (stderr, "%s: ", program_name);
202  errno = e;
203  perror (msg);
204  exiterr ();
205}
206
207
208/* malloc freely or DIE! */
209static VOID *
210xmalloc (size)
211     size_t size;
212{
213  VOID *r = (VOID *) malloc (size);
214  if (!r)
215    fatal ("memory exhausted");
216  return r;
217}
218
219static FILE *
220ck_fopen (fname, type)
221     char const *fname, *type;
222{
223  FILE *r = fopen (fname, type);
224  if (!r)
225    perror_fatal (fname);
226  return r;
227}
228
229static void
230ck_fclose (f)
231     FILE *f;
232{
233  if (fclose (f))
234    perror_fatal ("input/output error");
235}
236
237static size_t
238ck_fread (buf, size, f)
239     char *buf;
240     size_t size;
241     FILE *f;
242{
243  size_t r = fread (buf, sizeof (char), size, f);
244  if (r == 0 && ferror (f))
245    perror_fatal ("input error");
246  return r;
247}
248
249static void
250ck_fwrite (buf, size, f)
251     char const *buf;
252     size_t size;
253     FILE *f;
254{
255  if (fwrite (buf, sizeof (char), size, f) != size)
256    perror_fatal ("output error");
257}
258
259static void
260ck_fflush (f)
261     FILE *f;
262{
263  if (fflush (f) != 0)
264    perror_fatal ("output error");
265}
266
267static char const *
268expand_name (name, is_dir, other_name)
269     char *name;
270     int is_dir;
271     char const *other_name;
272{
273  if (strcmp (name, "-") == 0)
274    fatal ("cannot interactively merge standard input");
275  if (!is_dir)
276    return name;
277  else
278    {
279      /* Yield NAME/BASE, where BASE is OTHER_NAME's basename.  */
280      char const *p = filename_lastdirchar (other_name);
281      char const *base = p ? p+1 : other_name;
282      size_t namelen = strlen (name), baselen = strlen (base);
283      char *r = xmalloc (namelen + baselen + 2);
284      memcpy (r, name, namelen);
285      r[namelen] = '/';
286      memcpy (r + namelen + 1, base, baselen + 1);
287      return r;
288    }
289}
290
291
292
293struct line_filter {
294  FILE *infile;
295  char *bufpos;
296  char *buffer;
297  char *buflim;
298};
299
300static void
301lf_init (lf, infile)
302     struct line_filter *lf;
303     FILE *infile;
304{
305  lf->infile = infile;
306  lf->bufpos = lf->buffer = lf->buflim = xmalloc (SDIFF_BUFSIZE + 1);
307  lf->buflim[0] = '\n';
308}
309
310/* Fill an exhausted line_filter buffer from its INFILE */
311static size_t
312lf_refill (lf)
313     struct line_filter *lf;
314{
315  size_t s = ck_fread (lf->buffer, SDIFF_BUFSIZE, lf->infile);
316  lf->bufpos = lf->buffer;
317  lf->buflim = lf->buffer + s;
318  lf->buflim[0] = '\n';
319  checksigs ();
320  return s;
321}
322
323/* Advance LINES on LF's infile, copying lines to OUTFILE */
324static void
325lf_copy (lf, lines, outfile)
326     struct line_filter *lf;
327     int lines;
328     FILE *outfile;
329{
330  char *start = lf->bufpos;
331
332  while (lines)
333    {
334      lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
335      if (! lf->bufpos)
336        {
337          ck_fwrite (start, lf->buflim - start, outfile);
338          if (! lf_refill (lf))
339            return;
340          start = lf->bufpos;
341        }
342      else
343        {
344          --lines;
345          ++lf->bufpos;
346        }
347    }
348
349  ck_fwrite (start, lf->bufpos - start, outfile);
350}
351
352/* Advance LINES on LF's infile without doing output */
353static void
354lf_skip (lf, lines)
355     struct line_filter *lf;
356     int lines;
357{
358  while (lines)
359    {
360      lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
361      if (! lf->bufpos)
362        {
363          if (! lf_refill (lf))
364            break;
365        }
366      else
367        {
368          --lines;
369          ++lf->bufpos;
370        }
371    }
372}
373
374/* Snarf a line into a buffer.  Return EOF if EOF, 0 if error, 1 if OK.  */
375static int
376lf_snarf (lf, buffer, bufsize)
377     struct line_filter *lf;
378     char *buffer;
379     size_t bufsize;
380{
381  char *start = lf->bufpos;
382
383  for (;;)
384    {
385      char *next = (char *) memchr (start, '\n', lf->buflim + 1 - start);
386      size_t s = next - start;
387      if (bufsize <= s)
388        return 0;
389      memcpy (buffer, start, s);
390      if (next < lf->buflim)
391        {
392          buffer[s] = 0;
393          lf->bufpos = next + 1;
394          return 1;
395        }
396      if (! lf_refill (lf))
397        return s ? 0 : EOF;
398      buffer += s;
399      bufsize -= s;
400      start = next;
401    }
402}
403
404
405
406int
407main (argc, argv)
408     int argc;
409     char *argv[];
410{
411  int opt;
412  char *editor;
413  char *differ;
414
415  initialize_main (&argc, &argv);
416  program_name = argv[0];
417
418  editor = getenv ("EDITOR");
419  if (editor)
420    edbin = editor;
421  differ = getenv ("DIFF");
422  if (differ)
423    diffbin = differ;
424
425  diffarg ("diff");
426
427  /* parse command line args */
428  while ((opt = getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, 0))
429         != EOF)
430    {
431      switch (opt)
432        {
433        case 'a':
434          diffarg ("-a");
435          break;
436
437        case 'b':
438          diffarg ("-b");
439          break;
440
441        case 'B':
442          diffarg ("-B");
443          break;
444
445        case 'd':
446          diffarg ("-d");
447          break;
448
449        case 'H':
450          diffarg ("-H");
451          break;
452
453        case 'i':
454          diffarg ("-i");
455          break;
456
457        case 'I':
458          diffarg ("-I");
459          diffarg (optarg);
460          break;
461
462        case 'l':
463          diffarg ("--left-column");
464          break;
465
466        case 'o':
467          out_file = optarg;
468          break;
469
470        case 's':
471          suppress_common_flag = 1;
472          break;
473
474        case 't':
475          diffarg ("-t");
476          break;
477
478        case 'v':
479          printf ("sdiff - GNU diffutils version %s\n", version_string);
480          exit (0);
481
482        case 'w':
483          diffarg ("-W");
484          diffarg (optarg);
485          break;
486
487        case 'W':
488          diffarg ("-w");
489          break;
490
491        case 129:
492          usage ();
493          if (ferror (stdout) || fclose (stdout) != 0)
494            fatal ("write error");
495          exit (0);
496
497        default:
498          try_help (0);
499        }
500    }
501
502  if (argc - optind != 2)
503    try_help (argc - optind < 2 ? "missing operand" : "extra operand");
504
505  if (! out_file)
506    {
507      /* easy case: diff does everything for us */
508      if (suppress_common_flag)
509        diffarg ("--suppress-common-lines");
510      diffarg ("-y");
511      diffarg ("--");
512      diffarg (argv[optind]);
513      diffarg (argv[optind + 1]);
514      diffarg (0);
515      execdiff ();
516    }
517  else
518    {
519      FILE *left, *right, *out, *diffout;
520      int interact_ok;
521      struct line_filter lfilt;
522      struct line_filter rfilt;
523      struct line_filter diff_filt;
524      int leftdir = diraccess (argv[optind]);
525      int rightdir = diraccess (argv[optind + 1]);
526
527      if (leftdir && rightdir)
528        fatal ("both files to be compared are directories");
529
530      left = ck_fopen (expand_name (argv[optind], leftdir, argv[optind + 1]), "r");
531      ;
532      right = ck_fopen (expand_name (argv[optind + 1], rightdir, argv[optind]), "r");
533      out = ck_fopen (out_file, "w");
534
535      diffarg ("--sdiff-merge-assist");
536      diffarg ("--");
537      diffarg (argv[optind]);
538      diffarg (argv[optind + 1]);
539      diffarg (0);
540
541      trapsigs ();
542
543#if ! HAVE_FORK
544      {
545        size_t cmdsize = 1;
546        char *p, *command;
547        int i;
548
549        for (i = 0;  diffargv[i];  i++)
550          cmdsize += 4 * strlen (diffargv[i]) + 3;
551        command = p = xmalloc (cmdsize);
552        for (i = 0;  diffargv[i];  i++)
553          {
554            char const *a = diffargv[i];
555            SYSTEM_QUOTE_ARG (p, a);
556            *p++ = ' ';
557          }
558        p[-1] = '\0';
559        diffout = popen (command, "r");
560        if (!diffout)
561          perror_fatal (command);
562        free (command);
563      }
564#else /* HAVE_FORK */
565      {
566        int diff_fds[2];
567
568        if (pipe (diff_fds) != 0)
569          perror_fatal ("pipe");
570
571        diffpid = vfork ();
572        if (diffpid < 0)
573          perror_fatal ("fork failed");
574        if (!diffpid)
575          {
576            signal (SIGINT, SIG_IGN);  /* in case user interrupts editor */
577            signal (SIGPIPE, SIG_DFL);
578
579            close (diff_fds[0]);
580            if (diff_fds[1] != STDOUT_FILENO)
581              {
582                dup2 (diff_fds[1], STDOUT_FILENO);
583                close (diff_fds[1]);
584              }
585
586            execdiff ();
587          }
588
589        close (diff_fds[1]);
590        diffout = fdopen (diff_fds[0], "r");
591        if (!diffout)
592          perror_fatal ("fdopen");
593      }
594#endif /* HAVE_FORK */
595
596      lf_init (&diff_filt, diffout);
597      lf_init (&lfilt, left);
598      lf_init (&rfilt, right);
599
600      interact_ok = interact (&diff_filt, &lfilt, &rfilt, out);
601
602      ck_fclose (left);
603      ck_fclose (right);
604      ck_fclose (out);
605
606      {
607        int wstatus;
608
609#if ! HAVE_FORK
610        wstatus = pclose (diffout);
611#else
612        ck_fclose (diffout);
613        while (waitpid (diffpid, &wstatus, 0) < 0)
614          if (errno == EINTR)
615            checksigs ();
616          else
617            perror_fatal ("wait failed");
618        diffpid = 0;
619#endif
620
621        if (tmpmade)
622          {
623            unlink (tmpname);
624            tmpmade = 0;
625          }
626
627        if (! interact_ok)
628          exiterr ();
629
630        if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2))
631          fatal ("Subsidiary diff failed");
632
633        untrapsig (0);
634        checksigs ();
635        exit (WEXITSTATUS (wstatus));
636      }
637    }
638  return 0;                     /* Fool -Wall . . . */
639}
640
641static void
642diffarg (a)
643     char const *a;
644{
645  static unsigned diffargs, diffargsmax;
646
647  if (diffargs == diffargsmax)
648    {
649      if (! diffargsmax)
650        {
651          diffargv = (char const **) xmalloc (sizeof (char));
652          diffargsmax = 8;
653        }
654      diffargsmax *= 2;
655      diffargv = (char const **) realloc (diffargv,
656                                          diffargsmax * sizeof (char const *));
657      if (! diffargv)
658        fatal ("out of memory");
659    }
660  diffargv[diffargs++] = a;
661}
662
663static void
664execdiff ()
665{
666  execvp (diffbin, (char **) diffargv);
667  write (STDERR_FILENO, diffbin, strlen (diffbin));
668  write (STDERR_FILENO, ": not found\n", 12);
669  _exit (2);
670}
671
672
673
674
675/* Signal handling */
676
677#define NUM_SIGS (sizeof (sigs) / sizeof (*sigs))
678static int const sigs[] = {
679#ifdef SIGHUP
680       SIGHUP,
681#endif
682#ifdef SIGQUIT
683       SIGQUIT,
684#endif
685#ifdef SIGTERM
686       SIGTERM,
687#endif
688#ifdef SIGXCPU
689       SIGXCPU,
690#endif
691#ifdef SIGXFSZ
692       SIGXFSZ,
693#endif
694       SIGINT,
695       SIGPIPE
696};
697
698/* Prefer `sigaction' if it is available, since `signal' can lose signals.  */
699#if HAVE_SIGACTION
700static struct sigaction initial_action[NUM_SIGS];
701#define initial_handler(i) (initial_action[i].sa_handler)
702#else
703static RETSIGTYPE (*initial_action[NUM_SIGS]) ();
704#define initial_handler(i) (initial_action[i])
705#endif
706
707static int volatile ignore_SIGINT;
708static int volatile signal_received;
709static int sigs_trapped;
710
711static RETSIGTYPE
712catchsig (s)
713     int s;
714{
715#if ! HAVE_SIGACTION
716  signal (s, SIG_IGN);
717#endif
718  if (! (s == SIGINT && ignore_SIGINT))
719    signal_received = s;
720}
721
722static void
723trapsigs ()
724{
725  int i;
726
727#if HAVE_SIGACTION
728  struct sigaction catchaction;
729  bzero (&catchaction, sizeof (catchaction));
730  catchaction.sa_handler = catchsig;
731#ifdef SA_INTERRUPT
732  /* Non-Posix BSD-style systems like SunOS 4.1.x need this
733     so that `read' calls are interrupted properly.  */
734  catchaction.sa_flags = SA_INTERRUPT;
735#endif
736  sigemptyset (&catchaction.sa_mask);
737  for (i = 0;  i < NUM_SIGS;  i++)
738    sigaddset (&catchaction.sa_mask, sigs[i]);
739  for (i = 0;  i < NUM_SIGS;  i++)
740    {
741      sigaction (sigs[i], 0, &initial_action[i]);
742      if (initial_handler (i) != SIG_IGN
743          && sigaction (sigs[i], &catchaction, 0) != 0)
744        fatal ("signal error");
745    }
746#else /* ! HAVE_SIGACTION */
747  for (i = 0;  i < NUM_SIGS;  i++)
748    {
749      initial_action[i] = signal (sigs[i], SIG_IGN);
750      if (initial_handler (i) != SIG_IGN
751          && signal (sigs[i], catchsig) != SIG_IGN)
752        fatal ("signal error");
753    }
754#endif /* ! HAVE_SIGACTION */
755
756#if !defined(SIGCHLD) && defined(SIGCLD)
757#define SIGCHLD SIGCLD
758#endif
759#ifdef SIGCHLD
760  /* System V fork+wait does not work if SIGCHLD is ignored.  */
761  signal (SIGCHLD, SIG_DFL);
762#endif
763
764  sigs_trapped = 1;
765}
766
767/* Untrap signal S, or all trapped signals if S is zero.  */
768static void
769untrapsig (s)
770     int s;
771{
772  int i;
773
774  if (sigs_trapped)
775    for (i = 0;  i < NUM_SIGS;  i++)
776      if ((!s || sigs[i] == s)  &&  initial_handler (i) != SIG_IGN)
777#if HAVE_SIGACTION
778          sigaction (sigs[i], &initial_action[i], 0);
779#else
780          signal (sigs[i], initial_action[i]);
781#endif
782}
783
784/* Exit if a signal has been received.  */
785static void
786checksigs ()
787{
788  int s = signal_received;
789  if (s)
790    {
791      cleanup ();
792
793      /* Yield an exit status indicating that a signal was received.  */
794      untrapsig (s);
795      kill (getpid (), s);
796
797      /* That didn't work, so exit with error status.  */
798      exit (2);
799    }
800}
801
802
803
804static void
805give_help ()
806{
807  fprintf (stderr,"l:\tuse the left version\n");
808  fprintf (stderr,"r:\tuse the right version\n");
809  fprintf (stderr,"e l:\tedit then use the left version\n");
810  fprintf (stderr,"e r:\tedit then use the right version\n");
811  fprintf (stderr,"e b:\tedit then use the left and right versions concatenated\n");
812  fprintf (stderr,"e:\tedit a new version\n");
813  fprintf (stderr,"s:\tsilently include common lines\n");
814  fprintf (stderr,"v:\tverbosely include common lines\n");
815  fprintf (stderr,"q:\tquit\n");
816}
817
818static int
819skip_white ()
820{
821  int c;
822  for (;;)
823    {
824      c = getchar ();
825      if (!ISSPACE (c) || c == '\n')
826        break;
827      checksigs ();
828    }
829  if (ferror (stdin))
830    perror_fatal ("input error");
831  return c;
832}
833
834static void
835flush_line ()
836{
837  int c;
838  while ((c = getchar ()) != '\n' && c != EOF)
839    ;
840  if (ferror (stdin))
841    perror_fatal ("input error");
842}
843
844
845/* interpret an edit command */
846static int
847edit (left, lenl, right, lenr, outfile)
848     struct line_filter *left;
849     int lenl;
850     struct line_filter *right;
851     int lenr;
852     FILE *outfile;
853{
854  for (;;)
855    {
856      int cmd0, cmd1;
857      int gotcmd = 0;
858
859      cmd1 = 0; /* Pacify `gcc -W'.  */
860
861      while (!gotcmd)
862        {
863          if (putchar ('%') != '%')
864            perror_fatal ("output error");
865          ck_fflush (stdout);
866
867          cmd0 = skip_white ();
868          switch (cmd0)
869            {
870            case 'l': case 'r': case 's': case 'v': case 'q':
871              if (skip_white () != '\n')
872                {
873                  give_help ();
874                  flush_line ();
875                  continue;
876                }
877              gotcmd = 1;
878              break;
879
880            case 'e':
881              cmd1 = skip_white ();
882              switch (cmd1)
883                {
884                case 'l': case 'r': case 'b':
885                  if (skip_white () != '\n')
886                    {
887                      give_help ();
888                      flush_line ();
889                      continue;
890                    }
891                  gotcmd = 1;
892                  break;
893                case '\n':
894                  gotcmd = 1;
895                  break;
896                default:
897                  give_help ();
898                  flush_line ();
899                  continue;
900                }
901              break;
902            case EOF:
903              if (feof (stdin))
904                {
905                  gotcmd = 1;
906                  cmd0 = 'q';
907                  break;
908                }
909              /* falls through */
910            default:
911              flush_line ();
912              /* falls through */
913            case '\n':
914              give_help ();
915              continue;
916            }
917        }
918
919      switch (cmd0)
920        {
921        case 'l':
922          lf_copy (left, lenl, outfile);
923          lf_skip (right, lenr);
924          return 1;
925        case 'r':
926          lf_copy (right, lenr, outfile);
927          lf_skip (left, lenl);
928          return 1;
929        case 's':
930          suppress_common_flag = 1;
931          break;
932        case 'v':
933          suppress_common_flag = 0;
934          break;
935        case 'q':
936          return 0;
937        case 'e':
938          if (! tmpname && ! (tmpname = private_tempnam ()))
939            perror_fatal ("temporary file name");
940
941          tmpmade = 1;
942
943          {
944            FILE *tmp = ck_fopen (tmpname, "w+");
945
946            if (cmd1 == 'l' || cmd1 == 'b')
947              lf_copy (left, lenl, tmp);
948            else
949              lf_skip (left, lenl);
950
951            if (cmd1 == 'r' || cmd1 == 'b')
952              lf_copy (right, lenr, tmp);
953            else
954              lf_skip (right, lenr);
955
956            ck_fflush (tmp);
957
958            {
959              int wstatus;
960#if ! HAVE_FORK
961              char *command = xmalloc (strlen (edbin) + strlen (tmpname) + 2);
962              sprintf (command, "%s %s", edbin, tmpname);
963              wstatus = system (command);
964              free (command);
965#else /* HAVE_FORK */
966              pid_t pid;
967
968              ignore_SIGINT = 1;
969              checksigs ();
970
971              pid = vfork ();
972              if (pid == 0)
973                {
974                  char const *argv[3];
975                  int i = 0;
976
977                  argv[i++] = edbin;
978                  argv[i++] = tmpname;
979                  argv[i++] = 0;
980
981                  execvp (edbin, (char **) argv);
982                  write (STDERR_FILENO, edbin, strlen (edbin));
983                  write (STDERR_FILENO, ": not found\n", 12);
984                  _exit (1);
985                }
986
987              if (pid < 0)
988                perror_fatal ("fork failed");
989
990              while (waitpid (pid, &wstatus, 0) < 0)
991                if (errno == EINTR)
992                  checksigs ();
993                else
994                  perror_fatal ("wait failed");
995
996              ignore_SIGINT = 0;
997#endif /* HAVE_FORK */
998
999              if (wstatus != 0)
1000                fatal ("Subsidiary editor failed");
1001            }
1002
1003            if (fseek (tmp, 0L, SEEK_SET) != 0)
1004              perror_fatal ("fseek");
1005            {
1006              /* SDIFF_BUFSIZE is too big for a local var
1007                 in some compilers, so we allocate it dynamically.  */
1008              char *buf = xmalloc (SDIFF_BUFSIZE);
1009              size_t size;
1010
1011              while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
1012                {
1013                  checksigs ();
1014                  ck_fwrite (buf, size, outfile);
1015                }
1016              ck_fclose (tmp);
1017
1018              free (buf);
1019            }
1020            return 1;
1021          }
1022        default:
1023          give_help ();
1024          break;
1025        }
1026    }
1027}
1028
1029
1030
1031/* Alternately reveal bursts of diff output and handle user commands.  */
1032static int
1033interact (diff, left, right, outfile)
1034     struct line_filter *diff;
1035     struct line_filter *left;
1036     struct line_filter *right;
1037     FILE *outfile;
1038{
1039  for (;;)
1040    {
1041      char diff_help[256];
1042      int snarfed = lf_snarf (diff, diff_help, sizeof (diff_help));
1043
1044      if (snarfed <= 0)
1045        return snarfed;
1046
1047      checksigs ();
1048
1049      switch (diff_help[0])
1050        {
1051        case ' ':
1052          puts (diff_help + 1);
1053          break;
1054        case 'i':
1055          {
1056            int lenl = atoi (diff_help + 1), lenr, lenmax;
1057            char *p = strchr (diff_help, ',');
1058
1059            if (!p)
1060              fatal (diff_help);
1061            lenr = atoi (p + 1);
1062            lenmax = max (lenl, lenr);
1063
1064            if (suppress_common_flag)
1065              lf_skip (diff, lenmax);
1066            else
1067              lf_copy (diff, lenmax, stdout);
1068
1069            lf_copy (left, lenl, outfile);
1070            lf_skip (right, lenr);
1071            break;
1072          }
1073        case 'c':
1074          {
1075            int lenl = atoi (diff_help + 1), lenr;
1076            char *p = strchr (diff_help, ',');
1077
1078            if (!p)
1079              fatal (diff_help);
1080            lenr = atoi (p + 1);
1081            lf_copy (diff, max (lenl, lenr), stdout);
1082            if (! edit (left, lenl, right, lenr, outfile))
1083              return 0;
1084            break;
1085          }
1086        default:
1087          fatal (diff_help);
1088          break;
1089        }
1090    }
1091}
1092
1093
1094
1095/* temporary lossage: this is torn from gnu libc */
1096/* Return nonzero if DIR is an existing directory.  */
1097static int
1098diraccess (dir)
1099     char const *dir;
1100{
1101  struct stat buf;
1102  return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
1103}
1104
1105#if ! HAVE_TMPNAM
1106
1107/* Return zero if we know that FILE does not exist.  */
1108static int
1109exists (file)
1110     char const *file;
1111{
1112  struct stat buf;
1113  return stat (file, &buf) == 0 || errno != ENOENT;
1114}
1115
1116/* These are the characters used in temporary filenames.  */
1117static char const letters[] =
1118  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
1119
1120/* Generate a temporary filename and return it (in a newly allocated buffer).
1121   Use the prefix "dif" as in tempnam.
1122   This goes through a cyclic pattern of all possible
1123   filenames consisting of five decimal digits of the current pid and three
1124   of the characters in `letters'.  Each potential filename is
1125   tested for an already-existing file of the same name, and no name of an
1126   existing file will be returned.  When the cycle reaches its end
1127   return 0.  */
1128static char *
1129private_tempnam ()
1130{
1131  char const *dir = getenv (TMPDIR_ENV);
1132  static char const tmpdir[] = PVT_tmpdir;
1133  size_t index;
1134  char *buf;
1135  pid_t pid = getpid ();
1136  size_t dlen;
1137
1138  if (!dir)
1139    dir = tmpdir;
1140
1141  dlen = strlen (dir);
1142
1143  /* Remove trailing slashes from the directory name.  */
1144  while (dlen && dir[dlen - 1] == '/')
1145    --dlen;
1146
1147  buf = xmalloc (dlen + 1 + 3 + 5 + 1 + 3 + 1);
1148
1149  sprintf (buf, "%.*s/.", (int) dlen, dir);
1150  if (diraccess (buf))
1151    {
1152      for (index = 0;
1153           index < ((sizeof (letters) - 1) * (sizeof (letters) - 1)
1154                    * (sizeof (letters) - 1));
1155           ++index)
1156        {
1157          /* Construct a file name and see if it already exists.
1158
1159             We use a single counter in INDEX to cycle each of three
1160             character positions through each of 62 possible letters.  */
1161
1162          sprintf (buf, "%.*s/dif%.5lu.%c%c%c", (int) dlen, dir,
1163                   (unsigned long) pid % 100000,
1164                   letters[index % (sizeof (letters) - 1)],
1165                   letters[(index / (sizeof (letters) - 1))
1166                           % (sizeof (letters) - 1)],
1167                   letters[index / ((sizeof (letters) - 1) *
1168                                     (sizeof (letters) - 1))]);
1169
1170          if (!exists (buf))
1171            return buf;
1172        }
1173      errno = EEXIST;
1174    }
1175
1176  /* Don't free buf; `free' might change errno.  We'll exit soon anyway.  */
1177  return 0;
1178}
1179
1180#endif /* ! HAVE_TMPNAM */
Note: See TracBrowser for help on using the repository browser.