Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
 
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
 
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 = 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
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
 
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
 
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 = 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
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
}