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