~ubuntu-branches/ubuntu/gutsy/diffutils/gutsy

« back to all changes in this revision

Viewing changes to src/sdiff.c

  • Committer: Bazaar Package Importer
  • Author(s): Santiago Vila
  • Date: 2005-02-15 22:45:18 UTC
  • Revision ID: james.westby@ubuntu.com-20050215224518-dw9ti3me00twpcmt
Tags: upstream-2.8.1
ImportĀ upstreamĀ versionĀ 2.8.1

Show diffs side-by-side

added added

removed removed

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