Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/diff/src/diff3.c
39530 views
1
/* diff3 - compare three files line by line
2
3
Copyright (C) 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1998, 2001,
4
2002, 2004 Free Software Foundation, Inc.
5
6
This program 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
This program 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.
14
See the GNU General Public License for more details.
15
16
You should have received a copy of the GNU General Public License
17
along with this program; see the file COPYING.
18
If not, write to the Free Software Foundation,
19
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20
21
#include "system.h"
22
#include "paths.h"
23
24
#include <stdio.h>
25
#include <unlocked-io.h>
26
27
#include <c-stack.h>
28
#include <cmpbuf.h>
29
#include <error.h>
30
#include <exitfail.h>
31
#include <file-type.h>
32
#include <getopt.h>
33
#include <inttostr.h>
34
#include <quotesys.h>
35
#include <version-etc.h>
36
#include <xalloc.h>
37
38
/* Internal data structures and macros for the diff3 program; includes
39
data structures for both diff3 diffs and normal diffs. */
40
41
/* Different files within a three way diff. */
42
#define FILE0 0
43
#define FILE1 1
44
#define FILE2 2
45
46
/* A three way diff is built from two two-way diffs; the file which
47
the two two-way diffs share is: */
48
#define FILEC FILE2
49
50
/* Different files within a two way diff.
51
FC is the common file, FO the other file. */
52
#define FO 0
53
#define FC 1
54
55
/* The ranges are indexed by */
56
#define RANGE_START 0
57
#define RANGE_END 1
58
59
enum diff_type {
60
ERROR, /* Should not be used */
61
ADD, /* Two way diff add */
62
CHANGE, /* Two way diff change */
63
DELETE, /* Two way diff delete */
64
DIFF_ALL, /* All three are different */
65
DIFF_1ST, /* Only the first is different */
66
DIFF_2ND, /* Only the second */
67
DIFF_3RD /* Only the third */
68
};
69
70
/* Two way diff */
71
struct diff_block {
72
lin ranges[2][2]; /* Ranges are inclusive */
73
char **lines[2]; /* The actual lines (may contain nulls) */
74
size_t *lengths[2]; /* Line lengths (including newlines, if any) */
75
struct diff_block *next;
76
};
77
78
/* Three way diff */
79
80
struct diff3_block {
81
enum diff_type correspond; /* Type of diff */
82
lin ranges[3][2]; /* Ranges are inclusive */
83
char **lines[3]; /* The actual lines (may contain nulls) */
84
size_t *lengths[3]; /* Line lengths (including newlines, if any) */
85
struct diff3_block *next;
86
};
87
88
/* Access the ranges on a diff block. */
89
#define D_LOWLINE(diff, filenum) \
90
((diff)->ranges[filenum][RANGE_START])
91
#define D_HIGHLINE(diff, filenum) \
92
((diff)->ranges[filenum][RANGE_END])
93
#define D_NUMLINES(diff, filenum) \
94
(D_HIGHLINE (diff, filenum) - D_LOWLINE (diff, filenum) + 1)
95
96
/* Access the line numbers in a file in a diff by relative line
97
numbers (i.e. line number within the diff itself). Note that these
98
are lvalues and can be used for assignment. */
99
#define D_RELNUM(diff, filenum, linenum) \
100
((diff)->lines[filenum][linenum])
101
#define D_RELLEN(diff, filenum, linenum) \
102
((diff)->lengths[filenum][linenum])
103
104
/* And get at them directly, when that should be necessary. */
105
#define D_LINEARRAY(diff, filenum) \
106
((diff)->lines[filenum])
107
#define D_LENARRAY(diff, filenum) \
108
((diff)->lengths[filenum])
109
110
/* Next block. */
111
#define D_NEXT(diff) ((diff)->next)
112
113
/* Access the type of a diff3 block. */
114
#define D3_TYPE(diff) ((diff)->correspond)
115
116
/* Line mappings based on diffs. The first maps off the top of the
117
diff, the second off of the bottom. */
118
#define D_HIGH_MAPLINE(diff, fromfile, tofile, linenum) \
119
((linenum) \
120
- D_HIGHLINE ((diff), (fromfile)) \
121
+ D_HIGHLINE ((diff), (tofile)))
122
123
#define D_LOW_MAPLINE(diff, fromfile, tofile, linenum) \
124
((linenum) \
125
- D_LOWLINE ((diff), (fromfile)) \
126
+ D_LOWLINE ((diff), (tofile)))
127
128
/* Options variables for flags set on command line. */
129
130
/* If nonzero, treat all files as text files, never as binary. */
131
static bool text;
132
133
/* Remove trailing carriage returns from input. */
134
static bool strip_trailing_cr;
135
136
/* If nonzero, write out an ed script instead of the standard diff3 format. */
137
static bool edscript;
138
139
/* If nonzero, in the case of overlapping diffs (type DIFF_ALL),
140
preserve the lines which would normally be deleted from
141
file 1 with a special flagging mechanism. */
142
static bool flagging;
143
144
/* Use a tab to align output lines (-T). */
145
static bool initial_tab;
146
147
/* If nonzero, do not output information for overlapping diffs. */
148
static bool simple_only;
149
150
/* If nonzero, do not output information for non-overlapping diffs. */
151
static bool overlap_only;
152
153
/* If nonzero, show information for DIFF_2ND diffs. */
154
static bool show_2nd;
155
156
/* If nonzero, include `:wq' at the end of the script
157
to write out the file being edited. */
158
static bool finalwrite;
159
160
/* If nonzero, output a merged file. */
161
static bool merge;
162
163
char *program_name;
164
165
static char *read_diff (char const *, char const *, char **);
166
static char *scan_diff_line (char *, char **, size_t *, char *, char);
167
static enum diff_type process_diff_control (char **, struct diff_block *);
168
static bool compare_line_list (char * const[], size_t const[], char * const[], size_t const[], lin);
169
static bool copy_stringlist (char * const[], size_t const[], char *[], size_t[], lin);
170
static bool output_diff3_edscript (FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *);
171
static bool output_diff3_merge (FILE *, FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *);
172
static struct diff3_block *create_diff3_block (lin, lin, lin, lin, lin, lin);
173
static struct diff3_block *make_3way_diff (struct diff_block *, struct diff_block *);
174
static struct diff3_block *reverse_diff3_blocklist (struct diff3_block *);
175
static struct diff3_block *using_to_diff3_block (struct diff_block *[2], struct diff_block *[2], int, int, struct diff3_block const *);
176
static struct diff_block *process_diff (char const *, char const *, struct diff_block **);
177
static void check_stdout (void);
178
static void fatal (char const *) __attribute__((noreturn));
179
static void output_diff3 (FILE *, struct diff3_block *, int const[3], int const[3]);
180
static void perror_with_exit (char const *) __attribute__((noreturn));
181
static void try_help (char const *, char const *) __attribute__((noreturn));
182
static void usage (void);
183
184
static char const *diff_program = DEFAULT_DIFF_PROGRAM;
185
186
/* Values for long options that do not have single-letter equivalents. */
187
enum
188
{
189
DIFF_PROGRAM_OPTION = CHAR_MAX + 1,
190
HELP_OPTION,
191
STRIP_TRAILING_CR_OPTION
192
};
193
194
static struct option const longopts[] =
195
{
196
{"diff-program", 1, 0, DIFF_PROGRAM_OPTION},
197
{"easy-only", 0, 0, '3'},
198
{"ed", 0, 0, 'e'},
199
{"help", 0, 0, HELP_OPTION},
200
{"initial-tab", 0, 0, 'T'},
201
{"label", 1, 0, 'L'},
202
{"merge", 0, 0, 'm'},
203
{"overlap-only", 0, 0, 'x'},
204
{"show-all", 0, 0, 'A'},
205
{"show-overlap", 0, 0, 'E'},
206
{"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
207
{"text", 0, 0, 'a'},
208
{"version", 0, 0, 'v'},
209
{0, 0, 0, 0}
210
};
211
212
int
213
main (int argc, char **argv)
214
{
215
int c, i;
216
int common;
217
int mapping[3];
218
int rev_mapping[3];
219
int incompat = 0;
220
bool conflicts_found;
221
struct diff_block *thread0, *thread1, *last_block;
222
struct diff3_block *diff3;
223
int tag_count = 0;
224
char *tag_strings[3];
225
char *commonname;
226
char **file;
227
struct stat statb;
228
229
exit_failure = 2;
230
initialize_main (&argc, &argv);
231
program_name = argv[0];
232
setlocale (LC_ALL, "");
233
textdomain (PACKAGE);
234
c_stack_action (0);
235
236
while ((c = getopt_long (argc, argv, "aeimvx3AEL:TX", longopts, 0)) != -1)
237
{
238
switch (c)
239
{
240
case 'a':
241
text = true;
242
break;
243
case 'A':
244
show_2nd = true;
245
flagging = true;
246
incompat++;
247
break;
248
case 'x':
249
overlap_only = true;
250
incompat++;
251
break;
252
case '3':
253
simple_only = true;
254
incompat++;
255
break;
256
case 'i':
257
finalwrite = true;
258
break;
259
case 'm':
260
merge = true;
261
break;
262
case 'X':
263
overlap_only = true;
264
/* Fall through. */
265
case 'E':
266
flagging = true;
267
/* Fall through. */
268
case 'e':
269
incompat++;
270
break;
271
case 'T':
272
initial_tab = true;
273
break;
274
case STRIP_TRAILING_CR_OPTION:
275
strip_trailing_cr = true;
276
break;
277
case 'v':
278
version_etc (stdout, "diff3", PACKAGE_NAME, PACKAGE_VERSION,
279
"Randy Smith", (char *) 0);
280
check_stdout ();
281
return EXIT_SUCCESS;
282
case DIFF_PROGRAM_OPTION:
283
diff_program = optarg;
284
break;
285
case HELP_OPTION:
286
usage ();
287
check_stdout ();
288
return EXIT_SUCCESS;
289
case 'L':
290
/* Handle up to three -L options. */
291
if (tag_count < 3)
292
{
293
tag_strings[tag_count++] = optarg;
294
break;
295
}
296
try_help ("too many file label options", 0);
297
default:
298
try_help (0, 0);
299
}
300
}
301
302
edscript = incompat & ~merge; /* -AeExX3 without -m implies ed script. */
303
show_2nd |= ~incompat & merge; /* -m without -AeExX3 implies -A. */
304
flagging |= ~incompat & merge;
305
306
if (incompat > 1 /* Ensure at most one of -AeExX3. */
307
|| finalwrite & merge /* -i -m would rewrite input file. */
308
|| (tag_count && ! flagging)) /* -L requires one of -AEX. */
309
try_help ("incompatible options", 0);
310
311
if (argc - optind != 3)
312
{
313
if (argc - optind < 3)
314
try_help ("missing operand after `%s'", argv[argc - 1]);
315
else
316
try_help ("extra operand `%s'", argv[optind + 3]);
317
}
318
319
file = &argv[optind];
320
321
for (i = tag_count; i < 3; i++)
322
tag_strings[i] = file[i];
323
324
/* Always compare file1 to file2, even if file2 is "-".
325
This is needed for -mAeExX3. Using the file0 as
326
the common file would produce wrong results, because if the
327
file0-file1 diffs didn't line up with the file0-file2 diffs
328
(which is entirely possible since we don't use diff's -n option),
329
diff3 might report phantom changes from file1 to file2.
330
331
Also, try to compare file0 to file1, because this is where
332
changes are expected to come from. Diffing between these pairs
333
of files is more likely to avoid phantom changes from file0 to file1.
334
335
Historically, the default common file was file2, so some older
336
applications (e.g. Emacs ediff) used file2 as the ancestor. So,
337
for compatibility, if this is a 3-way diff (not a merge or
338
edscript), prefer file2 as the common file. */
339
340
common = 2 - (edscript | merge);
341
342
if (strcmp (file[common], "-") == 0)
343
{
344
/* Sigh. We've got standard input as the common file. We can't
345
call diff twice on stdin. Use the other arg as the common
346
file instead. */
347
common = 3 - common;
348
if (strcmp (file[0], "-") == 0 || strcmp (file[common], "-") == 0)
349
fatal ("`-' specified for more than one input file");
350
}
351
352
mapping[0] = 0;
353
mapping[1] = 3 - common;
354
mapping[2] = common;
355
356
for (i = 0; i < 3; i++)
357
rev_mapping[mapping[i]] = i;
358
359
for (i = 0; i < 3; i++)
360
if (strcmp (file[i], "-") != 0)
361
{
362
if (stat (file[i], &statb) < 0)
363
perror_with_exit (file[i]);
364
else if (S_ISDIR (statb.st_mode))
365
error (EXIT_TROUBLE, EISDIR, "%s", file[i]);
366
}
367
368
#ifdef SIGCHLD
369
/* System V fork+wait does not work if SIGCHLD is ignored. */
370
signal (SIGCHLD, SIG_DFL);
371
#endif
372
373
/* Invoke diff twice on two pairs of input files, combine the two
374
diffs, and output them. */
375
376
commonname = file[rev_mapping[FILEC]];
377
thread1 = process_diff (file[rev_mapping[FILE1]], commonname, &last_block);
378
thread0 = process_diff (file[rev_mapping[FILE0]], commonname, &last_block);
379
diff3 = make_3way_diff (thread0, thread1);
380
if (edscript)
381
conflicts_found
382
= output_diff3_edscript (stdout, diff3, mapping, rev_mapping,
383
tag_strings[0], tag_strings[1], tag_strings[2]);
384
else if (merge)
385
{
386
if (! freopen (file[rev_mapping[FILE0]], "r", stdin))
387
perror_with_exit (file[rev_mapping[FILE0]]);
388
conflicts_found
389
= output_diff3_merge (stdin, stdout, diff3, mapping, rev_mapping,
390
tag_strings[0], tag_strings[1], tag_strings[2]);
391
if (ferror (stdin))
392
fatal ("read failed");
393
}
394
else
395
{
396
output_diff3 (stdout, diff3, mapping, rev_mapping);
397
conflicts_found = false;
398
}
399
400
check_stdout ();
401
exit (conflicts_found);
402
return conflicts_found;
403
}
404
405
static void
406
try_help (char const *reason_msgid, char const *operand)
407
{
408
if (reason_msgid)
409
error (0, 0, _(reason_msgid), operand);
410
error (EXIT_TROUBLE, 0,
411
_("Try `%s --help' for more information."), program_name);
412
abort ();
413
}
414
415
static void
416
check_stdout (void)
417
{
418
if (ferror (stdout))
419
fatal ("write failed");
420
else if (fclose (stdout) != 0)
421
perror_with_exit (_("standard output"));
422
}
423
424
static char const * const option_help_msgid[] = {
425
N_("-e --ed Output unmerged changes from OLDFILE to YOURFILE into MYFILE."),
426
N_("-E --show-overlap Output unmerged changes, bracketing conflicts."),
427
N_("-A --show-all Output all changes, bracketing conflicts."),
428
N_("-x --overlap-only Output overlapping changes."),
429
N_("-X Output overlapping changes, bracketing them."),
430
N_("-3 --easy-only Output unmerged nonoverlapping changes."),
431
"",
432
N_("-m --merge Output merged file instead of ed script (default -A)."),
433
N_("-L LABEL --label=LABEL Use LABEL instead of file name."),
434
N_("-i Append `w' and `q' commands to ed scripts."),
435
N_("-a --text Treat all files as text."),
436
N_("--strip-trailing-cr Strip trailing carriage return on input."),
437
N_("-T --initial-tab Make tabs line up by prepending a tab."),
438
N_("--diff-program=PROGRAM Use PROGRAM to compare files."),
439
"",
440
N_("-v --version Output version info."),
441
N_("--help Output this help."),
442
0
443
};
444
445
static void
446
usage (void)
447
{
448
char const * const *p;
449
450
printf (_("Usage: %s [OPTION]... MYFILE OLDFILE YOURFILE\n"),
451
program_name);
452
printf ("%s\n\n", _("Compare three files line by line."));
453
for (p = option_help_msgid; *p; p++)
454
if (**p)
455
printf (" %s\n", _(*p));
456
else
457
putchar ('\n');
458
printf ("\n%s\n%s\n\n%s\n",
459
_("If a FILE is `-', read standard input."),
460
_("Exit status is 0 if successful, 1 if conflicts, 2 if trouble."),
461
_("Report bugs to <[email protected]>."));
462
}
463
464
/* Combine the two diffs together into one.
465
Here is the algorithm:
466
467
File2 is shared in common between the two diffs.
468
Diff02 is the diff between 0 and 2.
469
Diff12 is the diff between 1 and 2.
470
471
1) Find the range for the first block in File2.
472
a) Take the lowest of the two ranges (in File2) in the two
473
current blocks (one from each diff) as being the low
474
water mark. Assign the upper end of this block as
475
being the high water mark and move the current block up
476
one. Mark the block just moved over as to be used.
477
b) Check the next block in the diff that the high water
478
mark is *not* from.
479
480
*If* the high water mark is above
481
the low end of the range in that block,
482
483
mark that block as to be used and move the current
484
block up. Set the high water mark to the max of
485
the high end of this block and the current. Repeat b.
486
487
2) Find the corresponding ranges in File0 (from the blocks
488
in diff02; line per line outside of diffs) and in File1.
489
Create a diff3_block, reserving space as indicated by the ranges.
490
491
3) Copy all of the pointers for file2 in. At least for now,
492
do memcmp's between corresponding strings in the two diffs.
493
494
4) Copy all of the pointers for file0 and 1 in. Get what is
495
needed from file2 (when there isn't a diff block, it's
496
identical to file2 within the range between diff blocks).
497
498
5) If the diff blocks used came from only one of the two
499
strings of diffs, then that file (i.e. the one other than
500
the common file in that diff) is the odd person out. If
501
diff blocks are used from both sets, check to see if files
502
0 and 1 match:
503
504
Same number of lines? If so, do a set of memcmp's (if
505
a memcmp matches; copy the pointer over; it'll be easier
506
later during comparisons). If they match, 0 & 1 are the
507
same. If not, all three different.
508
509
Then do it again, until the blocks are exhausted. */
510
511
512
/* Make a three way diff (chain of diff3_block's) from two two way
513
diffs (chains of diff_block's). Assume that each of the two diffs
514
passed are onto the same file (i.e. that each of the diffs were
515
made "to" the same file). Return a three way diff pointer with
516
numbering FILE0 = the other file in diff02, FILE1 = the other file
517
in diff12, and FILEC = the common file. */
518
519
static struct diff3_block *
520
make_3way_diff (struct diff_block *thread0, struct diff_block *thread1)
521
{
522
/* Work on the two diffs passed to it as threads. Thread number 0
523
is diff02, thread number 1 is diff12. USING is the base of the
524
list of blocks to be used to construct each block of the three
525
way diff; if no blocks from a particular thread are to be used,
526
that element of USING is 0. LAST_USING contains the last
527
elements on each of the using lists.
528
529
HIGH_WATER_MARK is the highest line number in the common file
530
described in any of the diffs in either of the USING lists.
531
HIGH_WATER_THREAD names the thread. Similarly BASE_WATER_MARK
532
and BASE_WATER_THREAD describe the lowest line number in the
533
common file described in any of the diffs in either of the USING
534
lists. HIGH_WATER_DIFF is the diff from which the
535
HIGH_WATER_MARK was taken.
536
537
HIGH_WATER_DIFF should always be equal to
538
LAST_USING[HIGH_WATER_THREAD]. OTHER_DIFF is the next diff to
539
check for higher water, and should always be equal to
540
CURRENT[HIGH_WATER_THREAD ^ 1]. OTHER_THREAD is the thread in
541
which the OTHER_DIFF is, and hence should always be equal to
542
HIGH_WATER_THREAD ^ 1.
543
544
LAST_DIFF is the last diff block produced by this routine, for
545
line correspondence purposes between that diff and the one
546
currently being worked on. It is ZERO_DIFF before any blocks
547
have been created. */
548
549
struct diff_block *using[2];
550
struct diff_block *last_using[2];
551
struct diff_block *current[2];
552
553
lin high_water_mark;
554
555
int high_water_thread;
556
int base_water_thread;
557
int other_thread;
558
559
struct diff_block *high_water_diff;
560
struct diff_block *other_diff;
561
562
struct diff3_block *result;
563
struct diff3_block *tmpblock;
564
struct diff3_block **result_end;
565
566
struct diff3_block const *last_diff3;
567
568
static struct diff3_block const zero_diff3;
569
570
/* Initialization */
571
result = 0;
572
result_end = &result;
573
current[0] = thread0; current[1] = thread1;
574
last_diff3 = &zero_diff3;
575
576
/* Sniff up the threads until we reach the end */
577
578
while (current[0] || current[1])
579
{
580
using[0] = using[1] = last_using[0] = last_using[1] = 0;
581
582
/* Setup low and high water threads, diffs, and marks. */
583
if (!current[0])
584
base_water_thread = 1;
585
else if (!current[1])
586
base_water_thread = 0;
587
else
588
base_water_thread =
589
(D_LOWLINE (current[0], FC) > D_LOWLINE (current[1], FC));
590
591
high_water_thread = base_water_thread;
592
593
high_water_diff = current[high_water_thread];
594
595
high_water_mark = D_HIGHLINE (high_water_diff, FC);
596
597
/* Make the diff you just got info from into the using class */
598
using[high_water_thread]
599
= last_using[high_water_thread]
600
= high_water_diff;
601
current[high_water_thread] = high_water_diff->next;
602
last_using[high_water_thread]->next = 0;
603
604
/* And mark the other diff */
605
other_thread = high_water_thread ^ 0x1;
606
other_diff = current[other_thread];
607
608
/* Shuffle up the ladder, checking the other diff to see if it
609
needs to be incorporated. */
610
while (other_diff
611
&& D_LOWLINE (other_diff, FC) <= high_water_mark + 1)
612
{
613
614
/* Incorporate this diff into the using list. Note that
615
this doesn't take it off the current list */
616
if (using[other_thread])
617
last_using[other_thread]->next = other_diff;
618
else
619
using[other_thread] = other_diff;
620
last_using[other_thread] = other_diff;
621
622
/* Take it off the current list. Note that this following
623
code assumes that other_diff enters it equal to
624
current[high_water_thread ^ 0x1] */
625
current[other_thread] = current[other_thread]->next;
626
other_diff->next = 0;
627
628
/* Set the high_water stuff
629
If this comparison is equal, then this is the last pass
630
through this loop; since diff blocks within a given
631
thread cannot overlap, the high_water_mark will be
632
*below* the range_start of either of the next diffs. */
633
634
if (high_water_mark < D_HIGHLINE (other_diff, FC))
635
{
636
high_water_thread ^= 1;
637
high_water_diff = other_diff;
638
high_water_mark = D_HIGHLINE (other_diff, FC);
639
}
640
641
/* Set the other diff */
642
other_thread = high_water_thread ^ 0x1;
643
other_diff = current[other_thread];
644
}
645
646
/* The using lists contain a list of all of the blocks to be
647
included in this diff3_block. Create it. */
648
649
tmpblock = using_to_diff3_block (using, last_using,
650
base_water_thread, high_water_thread,
651
last_diff3);
652
653
if (!tmpblock)
654
fatal ("internal error: screwup in format of diff blocks");
655
656
/* Put it on the list. */
657
*result_end = tmpblock;
658
result_end = &tmpblock->next;
659
660
/* Set up corresponding lines correctly. */
661
last_diff3 = tmpblock;
662
}
663
return result;
664
}
665
666
/* Take two lists of blocks (from two separate diff threads) and put
667
them together into one diff3 block. Return a pointer to this diff3
668
block or 0 for failure.
669
670
All arguments besides using are for the convenience of the routine;
671
they could be derived from the using array. LAST_USING is a pair
672
of pointers to the last blocks in the using structure. LOW_THREAD
673
and HIGH_THREAD tell which threads contain the lowest and highest
674
line numbers for File0. LAST_DIFF3 contains the last diff produced
675
in the calling routine. This is used for lines mappings that
676
would still be identical to the state that diff ended in.
677
678
A distinction should be made in this routine between the two diffs
679
that are part of a normal two diff block, and the three diffs that
680
are part of a diff3_block. */
681
682
static struct diff3_block *
683
using_to_diff3_block (struct diff_block *using[2],
684
struct diff_block *last_using[2],
685
int low_thread, int high_thread,
686
struct diff3_block const *last_diff3)
687
{
688
lin low[2], high[2];
689
struct diff3_block *result;
690
struct diff_block *ptr;
691
int d;
692
lin i;
693
694
/* Find the range in the common file. */
695
lin lowc = D_LOWLINE (using[low_thread], FC);
696
lin highc = D_HIGHLINE (last_using[high_thread], FC);
697
698
/* Find the ranges in the other files.
699
If using[d] is null, that means that the file to which that diff
700
refers is equivalent to the common file over this range. */
701
702
for (d = 0; d < 2; d++)
703
if (using[d])
704
{
705
low[d] = D_LOW_MAPLINE (using[d], FC, FO, lowc);
706
high[d] = D_HIGH_MAPLINE (last_using[d], FC, FO, highc);
707
}
708
else
709
{
710
low[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, lowc);
711
high[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, highc);
712
}
713
714
/* Create a block with the appropriate sizes */
715
result = create_diff3_block (low[0], high[0], low[1], high[1], lowc, highc);
716
717
/* Copy information for the common file.
718
Return with a zero if any of the compares failed. */
719
720
for (d = 0; d < 2; d++)
721
for (ptr = using[d]; ptr; ptr = D_NEXT (ptr))
722
{
723
lin result_offset = D_LOWLINE (ptr, FC) - lowc;
724
725
if (!copy_stringlist (D_LINEARRAY (ptr, FC),
726
D_LENARRAY (ptr, FC),
727
D_LINEARRAY (result, FILEC) + result_offset,
728
D_LENARRAY (result, FILEC) + result_offset,
729
D_NUMLINES (ptr, FC)))
730
return 0;
731
}
732
733
/* Copy information for file d. First deal with anything that might be
734
before the first diff. */
735
736
for (d = 0; d < 2; d++)
737
{
738
struct diff_block *u = using[d];
739
lin lo = low[d], hi = high[d];
740
741
for (i = 0;
742
i + lo < (u ? D_LOWLINE (u, FO) : hi + 1);
743
i++)
744
{
745
D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, i);
746
D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, i);
747
}
748
749
for (ptr = u; ptr; ptr = D_NEXT (ptr))
750
{
751
lin result_offset = D_LOWLINE (ptr, FO) - lo;
752
lin linec;
753
754
if (!copy_stringlist (D_LINEARRAY (ptr, FO),
755
D_LENARRAY (ptr, FO),
756
D_LINEARRAY (result, FILE0 + d) + result_offset,
757
D_LENARRAY (result, FILE0 + d) + result_offset,
758
D_NUMLINES (ptr, FO)))
759
return 0;
760
761
/* Catch the lines between here and the next diff */
762
linec = D_HIGHLINE (ptr, FC) + 1 - lowc;
763
for (i = D_HIGHLINE (ptr, FO) + 1 - lo;
764
i < (D_NEXT (ptr) ? D_LOWLINE (D_NEXT (ptr), FO) : hi + 1) - lo;
765
i++)
766
{
767
D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, linec);
768
D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, linec);
769
linec++;
770
}
771
}
772
}
773
774
/* Set correspond */
775
if (!using[0])
776
D3_TYPE (result) = DIFF_2ND;
777
else if (!using[1])
778
D3_TYPE (result) = DIFF_1ST;
779
else
780
{
781
lin nl0 = D_NUMLINES (result, FILE0);
782
lin nl1 = D_NUMLINES (result, FILE1);
783
784
if (nl0 != nl1
785
|| !compare_line_list (D_LINEARRAY (result, FILE0),
786
D_LENARRAY (result, FILE0),
787
D_LINEARRAY (result, FILE1),
788
D_LENARRAY (result, FILE1),
789
nl0))
790
D3_TYPE (result) = DIFF_ALL;
791
else
792
D3_TYPE (result) = DIFF_3RD;
793
}
794
795
return result;
796
}
797
798
/* Copy pointers from a list of strings to a different list of
799
strings. If a spot in the second list is already filled, make sure
800
that it is filled with the same string; if not, return false, the copy
801
incomplete. Upon successful completion of the copy, return true. */
802
803
static bool
804
copy_stringlist (char * const fromptrs[], size_t const fromlengths[],
805
char *toptrs[], size_t tolengths[],
806
lin copynum)
807
{
808
register char * const *f = fromptrs;
809
register char **t = toptrs;
810
register size_t const *fl = fromlengths;
811
register size_t *tl = tolengths;
812
813
while (copynum--)
814
{
815
if (*t)
816
{
817
if (*fl != *tl || memcmp (*f, *t, *fl) != 0)
818
return false;
819
}
820
else
821
{
822
*t = *f;
823
*tl = *fl;
824
}
825
826
t++; f++; tl++; fl++;
827
}
828
829
return true;
830
}
831
832
/* Create a diff3_block, with ranges as specified in the arguments.
833
Allocate the arrays for the various pointers (and zero them) based
834
on the arguments passed. Return the block as a result. */
835
836
static struct diff3_block *
837
create_diff3_block (lin low0, lin high0,
838
lin low1, lin high1,
839
lin low2, lin high2)
840
{
841
struct diff3_block *result = xmalloc (sizeof *result);
842
lin numlines;
843
844
D3_TYPE (result) = ERROR;
845
D_NEXT (result) = 0;
846
847
/* Assign ranges */
848
D_LOWLINE (result, FILE0) = low0;
849
D_HIGHLINE (result, FILE0) = high0;
850
D_LOWLINE (result, FILE1) = low1;
851
D_HIGHLINE (result, FILE1) = high1;
852
D_LOWLINE (result, FILE2) = low2;
853
D_HIGHLINE (result, FILE2) = high2;
854
855
/* Allocate and zero space */
856
numlines = D_NUMLINES (result, FILE0);
857
if (numlines)
858
{
859
D_LINEARRAY (result, FILE0) = xcalloc (numlines, sizeof (char *));
860
D_LENARRAY (result, FILE0) = xcalloc (numlines, sizeof (size_t));
861
}
862
else
863
{
864
D_LINEARRAY (result, FILE0) = 0;
865
D_LENARRAY (result, FILE0) = 0;
866
}
867
868
numlines = D_NUMLINES (result, FILE1);
869
if (numlines)
870
{
871
D_LINEARRAY (result, FILE1) = xcalloc (numlines, sizeof (char *));
872
D_LENARRAY (result, FILE1) = xcalloc (numlines, sizeof (size_t));
873
}
874
else
875
{
876
D_LINEARRAY (result, FILE1) = 0;
877
D_LENARRAY (result, FILE1) = 0;
878
}
879
880
numlines = D_NUMLINES (result, FILE2);
881
if (numlines)
882
{
883
D_LINEARRAY (result, FILE2) = xcalloc (numlines, sizeof (char *));
884
D_LENARRAY (result, FILE2) = xcalloc (numlines, sizeof (size_t));
885
}
886
else
887
{
888
D_LINEARRAY (result, FILE2) = 0;
889
D_LENARRAY (result, FILE2) = 0;
890
}
891
892
/* Return */
893
return result;
894
}
895
896
/* Compare two lists of lines of text.
897
Return 1 if they are equivalent, 0 if not. */
898
899
static bool
900
compare_line_list (char * const list1[], size_t const lengths1[],
901
char * const list2[], size_t const lengths2[],
902
lin nl)
903
{
904
char * const *l1 = list1;
905
char * const *l2 = list2;
906
size_t const *lgths1 = lengths1;
907
size_t const *lgths2 = lengths2;
908
909
while (nl--)
910
if (!*l1 || !*l2 || *lgths1 != *lgths2++
911
|| memcmp (*l1++, *l2++, *lgths1++) != 0)
912
return false;
913
return true;
914
}
915
916
/* Input and parse two way diffs. */
917
918
static struct diff_block *
919
process_diff (char const *filea,
920
char const *fileb,
921
struct diff_block **last_block)
922
{
923
char *diff_contents;
924
char *diff_limit;
925
char *scan_diff;
926
enum diff_type dt;
927
lin i;
928
struct diff_block *block_list, **block_list_end, *bptr;
929
size_t too_many_lines = (PTRDIFF_MAX
930
/ MIN (sizeof *bptr->lines[1],
931
sizeof *bptr->lengths[1]));
932
933
diff_limit = read_diff (filea, fileb, &diff_contents);
934
scan_diff = diff_contents;
935
block_list_end = &block_list;
936
bptr = 0; /* Pacify `gcc -W'. */
937
938
while (scan_diff < diff_limit)
939
{
940
bptr = xmalloc (sizeof *bptr);
941
bptr->lines[0] = bptr->lines[1] = 0;
942
bptr->lengths[0] = bptr->lengths[1] = 0;
943
944
dt = process_diff_control (&scan_diff, bptr);
945
if (dt == ERROR || *scan_diff != '\n')
946
{
947
fprintf (stderr, _("%s: diff failed: "), program_name);
948
do
949
{
950
putc (*scan_diff, stderr);
951
}
952
while (*scan_diff++ != '\n');
953
exit (EXIT_TROUBLE);
954
}
955
scan_diff++;
956
957
/* Force appropriate ranges to be null, if necessary */
958
switch (dt)
959
{
960
case ADD:
961
bptr->ranges[0][0]++;
962
break;
963
case DELETE:
964
bptr->ranges[1][0]++;
965
break;
966
case CHANGE:
967
break;
968
default:
969
fatal ("internal error: invalid diff type in process_diff");
970
break;
971
}
972
973
/* Allocate space for the pointers for the lines from filea, and
974
parcel them out among these pointers */
975
if (dt != ADD)
976
{
977
lin numlines = D_NUMLINES (bptr, 0);
978
if (too_many_lines <= numlines)
979
xalloc_die ();
980
bptr->lines[0] = xmalloc (numlines * sizeof *bptr->lines[0]);
981
bptr->lengths[0] = xmalloc (numlines * sizeof *bptr->lengths[0]);
982
for (i = 0; i < numlines; i++)
983
scan_diff = scan_diff_line (scan_diff,
984
&(bptr->lines[0][i]),
985
&(bptr->lengths[0][i]),
986
diff_limit,
987
'<');
988
}
989
990
/* Get past the separator for changes */
991
if (dt == CHANGE)
992
{
993
if (strncmp (scan_diff, "---\n", 4))
994
fatal ("invalid diff format; invalid change separator");
995
scan_diff += 4;
996
}
997
998
/* Allocate space for the pointers for the lines from fileb, and
999
parcel them out among these pointers */
1000
if (dt != DELETE)
1001
{
1002
lin numlines = D_NUMLINES (bptr, 1);
1003
if (too_many_lines <= numlines)
1004
xalloc_die ();
1005
bptr->lines[1] = xmalloc (numlines * sizeof *bptr->lines[1]);
1006
bptr->lengths[1] = xmalloc (numlines * sizeof *bptr->lengths[1]);
1007
for (i = 0; i < numlines; i++)
1008
scan_diff = scan_diff_line (scan_diff,
1009
&(bptr->lines[1][i]),
1010
&(bptr->lengths[1][i]),
1011
diff_limit,
1012
'>');
1013
}
1014
1015
/* Place this block on the blocklist. */
1016
*block_list_end = bptr;
1017
block_list_end = &bptr->next;
1018
}
1019
1020
*block_list_end = 0;
1021
*last_block = bptr;
1022
return block_list;
1023
}
1024
1025
/* Skip tabs and spaces, and return the first character after them. */
1026
1027
static char *
1028
skipwhite (char *s)
1029
{
1030
while (*s == ' ' || *s == '\t')
1031
s++;
1032
return s;
1033
}
1034
1035
/* Read a nonnegative line number from S, returning the address of the
1036
first character after the line number, and storing the number into
1037
*PNUM. Return 0 if S does not point to a valid line number. */
1038
1039
static char *
1040
readnum (char *s, lin *pnum)
1041
{
1042
unsigned char c = *s;
1043
lin num = 0;
1044
1045
if (! ISDIGIT (c))
1046
return 0;
1047
1048
do
1049
{
1050
num = c - '0' + num * 10;
1051
c = *++s;
1052
}
1053
while (ISDIGIT (c));
1054
1055
*pnum = num;
1056
return s;
1057
}
1058
1059
/* Parse a normal format diff control string. Return the type of the
1060
diff (ERROR if the format is bad). All of the other important
1061
information is filled into to the structure pointed to by db, and
1062
the string pointer (whose location is passed to this routine) is
1063
updated to point beyond the end of the string parsed. Note that
1064
only the ranges in the diff_block will be set by this routine.
1065
1066
If some specific pair of numbers has been reduced to a single
1067
number, then both corresponding numbers in the diff block are set
1068
to that number. In general these numbers are interpreted as ranges
1069
inclusive, unless being used by the ADD or DELETE commands. It is
1070
assumed that these will be special cased in a superior routine. */
1071
1072
static enum diff_type
1073
process_diff_control (char **string, struct diff_block *db)
1074
{
1075
char *s = *string;
1076
enum diff_type type;
1077
1078
/* Read first set of digits */
1079
s = readnum (skipwhite (s), &db->ranges[0][RANGE_START]);
1080
if (! s)
1081
return ERROR;
1082
1083
/* Was that the only digit? */
1084
s = skipwhite (s);
1085
if (*s == ',')
1086
{
1087
s = readnum (s + 1, &db->ranges[0][RANGE_END]);
1088
if (! s)
1089
return ERROR;
1090
}
1091
else
1092
db->ranges[0][RANGE_END] = db->ranges[0][RANGE_START];
1093
1094
/* Get the letter */
1095
s = skipwhite (s);
1096
switch (*s)
1097
{
1098
case 'a':
1099
type = ADD;
1100
break;
1101
case 'c':
1102
type = CHANGE;
1103
break;
1104
case 'd':
1105
type = DELETE;
1106
break;
1107
default:
1108
return ERROR; /* Bad format */
1109
}
1110
s++; /* Past letter */
1111
1112
/* Read second set of digits */
1113
s = readnum (skipwhite (s), &db->ranges[1][RANGE_START]);
1114
if (! s)
1115
return ERROR;
1116
1117
/* Was that the only digit? */
1118
s = skipwhite (s);
1119
if (*s == ',')
1120
{
1121
s = readnum (s + 1, &db->ranges[1][RANGE_END]);
1122
if (! s)
1123
return ERROR;
1124
s = skipwhite (s); /* To move to end */
1125
}
1126
else
1127
db->ranges[1][RANGE_END] = db->ranges[1][RANGE_START];
1128
1129
*string = s;
1130
return type;
1131
}
1132
1133
static char *
1134
read_diff (char const *filea,
1135
char const *fileb,
1136
char **output_placement)
1137
{
1138
char *diff_result;
1139
size_t current_chunk_size, total;
1140
int fd, wstatus, status;
1141
int werrno = 0;
1142
struct stat pipestat;
1143
1144
#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
1145
1146
char const *argv[9];
1147
char const **ap;
1148
int fds[2];
1149
pid_t pid;
1150
1151
ap = argv;
1152
*ap++ = diff_program;
1153
if (text)
1154
*ap++ = "-a";
1155
if (strip_trailing_cr)
1156
*ap++ = "--strip-trailing-cr";
1157
*ap++ = "--horizon-lines=100";
1158
*ap++ = "--";
1159
*ap++ = filea;
1160
*ap++ = fileb;
1161
*ap = 0;
1162
1163
if (pipe (fds) != 0)
1164
perror_with_exit ("pipe");
1165
1166
pid = vfork ();
1167
if (pid == 0)
1168
{
1169
/* Child */
1170
close (fds[0]);
1171
if (fds[1] != STDOUT_FILENO)
1172
{
1173
dup2 (fds[1], STDOUT_FILENO);
1174
close (fds[1]);
1175
}
1176
1177
/* The cast to (char **) is needed for portability to older
1178
hosts with a nonstandard prototype for execvp. */
1179
execvp (diff_program, (char **) argv);
1180
1181
_exit (errno == ENOENT ? 127 : 126);
1182
}
1183
1184
if (pid == -1)
1185
perror_with_exit ("fork");
1186
1187
close (fds[1]); /* Prevent erroneous lack of EOF */
1188
fd = fds[0];
1189
1190
#else
1191
1192
FILE *fpipe;
1193
char const args[] = " --horizon-lines=100 -- ";
1194
char *command = xmalloc (quote_system_arg (0, diff_program)
1195
+ sizeof "-a"
1196
+ sizeof "--strip-trailing-cr"
1197
+ sizeof args - 1
1198
+ quote_system_arg (0, filea) + 1
1199
+ quote_system_arg (0, fileb) + 1);
1200
char *p = command;
1201
p += quote_system_arg (p, diff_program);
1202
if (text)
1203
{
1204
strcpy (p, " -a");
1205
p += 3;
1206
}
1207
if (strip_trailing_cr)
1208
{
1209
strcpy (p, " --strip-trailing-cr");
1210
p += 20;
1211
}
1212
strcpy (p, args);
1213
p += sizeof args - 1;
1214
p += quote_system_arg (p, filea);
1215
*p++ = ' ';
1216
p += quote_system_arg (p, fileb);
1217
*p = 0;
1218
errno = 0;
1219
fpipe = popen (command, "r");
1220
if (!fpipe)
1221
perror_with_exit (command);
1222
free (command);
1223
fd = fileno (fpipe);
1224
1225
#endif
1226
1227
if (fstat (fd, &pipestat) != 0)
1228
perror_with_exit ("fstat");
1229
current_chunk_size = MAX (1, STAT_BLOCKSIZE (pipestat));
1230
diff_result = xmalloc (current_chunk_size);
1231
total = 0;
1232
1233
for (;;)
1234
{
1235
size_t bytes_to_read = current_chunk_size - total;
1236
size_t bytes = block_read (fd, diff_result + total, bytes_to_read);
1237
total += bytes;
1238
if (bytes != bytes_to_read)
1239
{
1240
if (bytes == SIZE_MAX)
1241
perror_with_exit (_("read failed"));
1242
break;
1243
}
1244
if (PTRDIFF_MAX / 2 <= current_chunk_size)
1245
xalloc_die ();
1246
current_chunk_size *= 2;
1247
diff_result = xrealloc (diff_result, current_chunk_size);
1248
}
1249
1250
if (total != 0 && diff_result[total-1] != '\n')
1251
fatal ("invalid diff format; incomplete last line");
1252
1253
*output_placement = diff_result;
1254
1255
#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
1256
1257
wstatus = pclose (fpipe);
1258
if (wstatus == -1)
1259
werrno = errno;
1260
1261
#else
1262
1263
if (close (fd) != 0)
1264
perror_with_exit ("close");
1265
if (waitpid (pid, &wstatus, 0) < 0)
1266
perror_with_exit ("waitpid");
1267
1268
#endif
1269
1270
status = ! werrno && WIFEXITED (wstatus) ? WEXITSTATUS (wstatus) : INT_MAX;
1271
1272
if (EXIT_TROUBLE <= status)
1273
error (EXIT_TROUBLE, werrno,
1274
_(status == 126
1275
? "subsidiary program `%s' could not be invoked"
1276
: status == 127
1277
? "subsidiary program `%s' not found"
1278
: status == INT_MAX
1279
? "subsidiary program `%s' failed"
1280
: "subsidiary program `%s' failed (exit status %d)"),
1281
diff_program, status);
1282
1283
return diff_result + total;
1284
}
1285
1286
1287
/* Scan a regular diff line (consisting of > or <, followed by a
1288
space, followed by text (including nulls) up to a newline.
1289
1290
This next routine began life as a macro and many parameters in it
1291
are used as call-by-reference values. */
1292
static char *
1293
scan_diff_line (char *scan_ptr, char **set_start, size_t *set_length,
1294
char *limit, char leadingchar)
1295
{
1296
char *line_ptr;
1297
1298
if (!(scan_ptr[0] == leadingchar
1299
&& scan_ptr[1] == ' '))
1300
fatal ("invalid diff format; incorrect leading line chars");
1301
1302
*set_start = line_ptr = scan_ptr + 2;
1303
while (*line_ptr++ != '\n')
1304
continue;
1305
1306
/* Include newline if the original line ended in a newline,
1307
or if an edit script is being generated.
1308
Copy any missing newline message to stderr if an edit script is being
1309
generated, because edit scripts cannot handle missing newlines.
1310
Return the beginning of the next line. */
1311
*set_length = line_ptr - *set_start;
1312
if (line_ptr < limit && *line_ptr == '\\')
1313
{
1314
if (edscript)
1315
fprintf (stderr, "%s:", program_name);
1316
else
1317
--*set_length;
1318
line_ptr++;
1319
do
1320
{
1321
if (edscript)
1322
putc (*line_ptr, stderr);
1323
}
1324
while (*line_ptr++ != '\n');
1325
}
1326
1327
return line_ptr;
1328
}
1329
1330
/* Output a three way diff passed as a list of diff3_block's. The
1331
argument MAPPING is indexed by external file number (in the
1332
argument list) and contains the internal file number (from the diff
1333
passed). This is important because the user expects outputs in
1334
terms of the argument list number, and the diff passed may have
1335
been done slightly differently (if the last argument was "-", for
1336
example). REV_MAPPING is the inverse of MAPPING. */
1337
1338
static void
1339
output_diff3 (FILE *outputfile, struct diff3_block *diff,
1340
int const mapping[3], int const rev_mapping[3])
1341
{
1342
int i;
1343
int oddoneout;
1344
char *cp;
1345
struct diff3_block *ptr;
1346
lin line;
1347
size_t length;
1348
int dontprint;
1349
static int skew_increment[3] = { 2, 3, 1 }; /* 0==>2==>1==>3 */
1350
char const *line_prefix = initial_tab ? "\t" : " ";
1351
1352
for (ptr = diff; ptr; ptr = D_NEXT (ptr))
1353
{
1354
char x[2];
1355
1356
switch (ptr->correspond)
1357
{
1358
case DIFF_ALL:
1359
x[0] = 0;
1360
dontprint = 3; /* Print them all */
1361
oddoneout = 3; /* Nobody's odder than anyone else */
1362
break;
1363
case DIFF_1ST:
1364
case DIFF_2ND:
1365
case DIFF_3RD:
1366
oddoneout = rev_mapping[ptr->correspond - DIFF_1ST];
1367
1368
x[0] = oddoneout + '1';
1369
x[1] = 0;
1370
dontprint = oddoneout == 0;
1371
break;
1372
default:
1373
fatal ("internal error: invalid diff type passed to output");
1374
}
1375
fprintf (outputfile, "====%s\n", x);
1376
1377
/* Go 0, 2, 1 if the first and third outputs are equivalent. */
1378
for (i = 0; i < 3;
1379
i = (oddoneout == 1 ? skew_increment[i] : i + 1))
1380
{
1381
int realfile = mapping[i];
1382
lin lowt = D_LOWLINE (ptr, realfile);
1383
lin hight = D_HIGHLINE (ptr, realfile);
1384
long int llowt = lowt;
1385
long int lhight = hight;
1386
1387
fprintf (outputfile, "%d:", i + 1);
1388
switch (lowt - hight)
1389
{
1390
case 1:
1391
fprintf (outputfile, "%lda\n", llowt - 1);
1392
break;
1393
case 0:
1394
fprintf (outputfile, "%ldc\n", llowt);
1395
break;
1396
default:
1397
fprintf (outputfile, "%ld,%ldc\n", llowt, lhight);
1398
break;
1399
}
1400
1401
if (i == dontprint) continue;
1402
1403
if (lowt <= hight)
1404
{
1405
line = 0;
1406
do
1407
{
1408
fprintf (outputfile, line_prefix);
1409
cp = D_RELNUM (ptr, realfile, line);
1410
length = D_RELLEN (ptr, realfile, line);
1411
fwrite (cp, sizeof (char), length, outputfile);
1412
}
1413
while (++line < hight - lowt + 1);
1414
if (cp[length - 1] != '\n')
1415
fprintf (outputfile, "\n\\ %s\n",
1416
_("No newline at end of file"));
1417
}
1418
}
1419
}
1420
}
1421
1422
1423
/* Output to OUTPUTFILE the lines of B taken from FILENUM. Double any
1424
initial '.'s; yield nonzero if any initial '.'s were doubled. */
1425
1426
static bool
1427
dotlines (FILE *outputfile, struct diff3_block *b, int filenum)
1428
{
1429
lin i;
1430
bool leading_dot = false;
1431
1432
for (i = 0;
1433
i < D_NUMLINES (b, filenum);
1434
i++)
1435
{
1436
char *line = D_RELNUM (b, filenum, i);
1437
if (line[0] == '.')
1438
{
1439
leading_dot = true;
1440
fprintf (outputfile, ".");
1441
}
1442
fwrite (line, sizeof (char),
1443
D_RELLEN (b, filenum, i), outputfile);
1444
}
1445
1446
return leading_dot;
1447
}
1448
1449
/* Output to OUTPUTFILE a '.' line. If LEADING_DOT is true, also
1450
output a command that removes initial '.'s starting with line START
1451
and continuing for NUM lines. (START is long int, not lin, for
1452
convenience with printf %ld formats.) */
1453
1454
static void
1455
undotlines (FILE *outputfile, bool leading_dot, long int start, lin num)
1456
{
1457
fprintf (outputfile, ".\n");
1458
if (leading_dot)
1459
{
1460
if (num == 1)
1461
fprintf (outputfile, "%lds/^\\.//\n", start);
1462
else
1463
fprintf (outputfile, "%ld,%lds/^\\.//\n", start, start + num - 1);
1464
}
1465
}
1466
1467
/* Output a diff3 set of blocks as an ed script. This script applies
1468
the changes between file's 2 & 3 to file 1. Take the precise
1469
format of the ed script to be output from global variables set
1470
during options processing. Reverse the order of
1471
the set of diff3 blocks in DIFF; this gets
1472
around the problems involved with changing line numbers in an ed
1473
script.
1474
1475
As in `output_diff3', the variable MAPPING maps from file number
1476
according to the argument list to file number according to the diff
1477
passed. All files listed below are in terms of the argument list.
1478
REV_MAPPING is the inverse of MAPPING.
1479
1480
FILE0, FILE1 and FILE2 are the strings to print as the names of the
1481
three files. These may be the actual names, or may be the
1482
arguments specified with -L.
1483
1484
Return 1 if conflicts were found. */
1485
1486
static bool
1487
output_diff3_edscript (FILE *outputfile, struct diff3_block *diff,
1488
int const mapping[3], int const rev_mapping[3],
1489
char const *file0, char const *file1, char const *file2)
1490
{
1491
bool leading_dot;
1492
bool conflicts_found = false;
1493
bool conflict;
1494
struct diff3_block *b;
1495
1496
for (b = reverse_diff3_blocklist (diff); b; b = b->next)
1497
{
1498
/* Must do mapping correctly. */
1499
enum diff_type type
1500
= (b->correspond == DIFF_ALL
1501
? DIFF_ALL
1502
: DIFF_1ST + rev_mapping[b->correspond - DIFF_1ST]);
1503
1504
long int low0, high0;
1505
1506
/* If we aren't supposed to do this output block, skip it. */
1507
switch (type)
1508
{
1509
default: continue;
1510
case DIFF_2ND: if (!show_2nd) continue; conflict = true; break;
1511
case DIFF_3RD: if (overlap_only) continue; conflict = false; break;
1512
case DIFF_ALL: if (simple_only) continue; conflict = flagging; break;
1513
}
1514
1515
low0 = D_LOWLINE (b, mapping[FILE0]);
1516
high0 = D_HIGHLINE (b, mapping[FILE0]);
1517
1518
if (conflict)
1519
{
1520
conflicts_found = true;
1521
1522
1523
/* Mark end of conflict. */
1524
1525
fprintf (outputfile, "%lda\n", high0);
1526
leading_dot = false;
1527
if (type == DIFF_ALL)
1528
{
1529
if (show_2nd)
1530
{
1531
/* Append lines from FILE1. */
1532
fprintf (outputfile, "||||||| %s\n", file1);
1533
leading_dot = dotlines (outputfile, b, mapping[FILE1]);
1534
}
1535
/* Append lines from FILE2. */
1536
fprintf (outputfile, "=======\n");
1537
leading_dot |= dotlines (outputfile, b, mapping[FILE2]);
1538
}
1539
fprintf (outputfile, ">>>>>>> %s\n", file2);
1540
undotlines (outputfile, leading_dot, high0 + 2,
1541
(D_NUMLINES (b, mapping[FILE1])
1542
+ D_NUMLINES (b, mapping[FILE2]) + 1));
1543
1544
1545
/* Mark start of conflict. */
1546
1547
fprintf (outputfile, "%lda\n<<<<<<< %s\n", low0 - 1,
1548
type == DIFF_ALL ? file0 : file1);
1549
leading_dot = false;
1550
if (type == DIFF_2ND)
1551
{
1552
/* Prepend lines from FILE1. */
1553
leading_dot = dotlines (outputfile, b, mapping[FILE1]);
1554
fprintf (outputfile, "=======\n");
1555
}
1556
undotlines (outputfile, leading_dot, low0 + 1,
1557
D_NUMLINES (b, mapping[FILE1]));
1558
}
1559
else if (D_NUMLINES (b, mapping[FILE2]) == 0)
1560
/* Write out a delete */
1561
{
1562
if (low0 == high0)
1563
fprintf (outputfile, "%ldd\n", low0);
1564
else
1565
fprintf (outputfile, "%ld,%ldd\n", low0, high0);
1566
}
1567
else
1568
/* Write out an add or change */
1569
{
1570
switch (high0 - low0)
1571
{
1572
case -1:
1573
fprintf (outputfile, "%lda\n", high0);
1574
break;
1575
case 0:
1576
fprintf (outputfile, "%ldc\n", high0);
1577
break;
1578
default:
1579
fprintf (outputfile, "%ld,%ldc\n", low0, high0);
1580
break;
1581
}
1582
1583
undotlines (outputfile, dotlines (outputfile, b, mapping[FILE2]),
1584
low0, D_NUMLINES (b, mapping[FILE2]));
1585
}
1586
}
1587
if (finalwrite) fprintf (outputfile, "w\nq\n");
1588
return conflicts_found;
1589
}
1590
1591
/* Read from INFILE and output to OUTPUTFILE a set of diff3_blocks
1592
DIFF as a merged file. This acts like 'ed file0
1593
<[output_diff3_edscript]', except that it works even for binary
1594
data or incomplete lines.
1595
1596
As before, MAPPING maps from arg list file number to diff file
1597
number, REV_MAPPING is its inverse, and FILE0, FILE1, and FILE2 are
1598
the names of the files.
1599
1600
Return 1 if conflicts were found. */
1601
1602
static bool
1603
output_diff3_merge (FILE *infile, FILE *outputfile, struct diff3_block *diff,
1604
int const mapping[3], int const rev_mapping[3],
1605
char const *file0, char const *file1, char const *file2)
1606
{
1607
int c;
1608
lin i;
1609
bool conflicts_found = false;
1610
bool conflict;
1611
struct diff3_block *b;
1612
lin linesread = 0;
1613
1614
for (b = diff; b; b = b->next)
1615
{
1616
/* Must do mapping correctly. */
1617
enum diff_type type
1618
= ((b->correspond == DIFF_ALL)
1619
? DIFF_ALL
1620
: DIFF_1ST + rev_mapping[b->correspond - DIFF_1ST]);
1621
char const *format_2nd = "<<<<<<< %s\n";
1622
1623
/* If we aren't supposed to do this output block, skip it. */
1624
switch (type)
1625
{
1626
default: continue;
1627
case DIFF_2ND: if (!show_2nd) continue; conflict = true; break;
1628
case DIFF_3RD: if (overlap_only) continue; conflict = false; break;
1629
case DIFF_ALL: if (simple_only) continue; conflict = flagging;
1630
format_2nd = "||||||| %s\n";
1631
break;
1632
}
1633
1634
/* Copy I lines from file 0. */
1635
i = D_LOWLINE (b, FILE0) - linesread - 1;
1636
linesread += i;
1637
while (0 <= --i)
1638
do
1639
{
1640
c = getc (infile);
1641
if (c == EOF)
1642
{
1643
if (ferror (infile))
1644
perror_with_exit (_("read failed"));
1645
else if (feof (infile))
1646
fatal ("input file shrank");
1647
}
1648
putc (c, outputfile);
1649
}
1650
while (c != '\n');
1651
1652
if (conflict)
1653
{
1654
conflicts_found = true;
1655
1656
if (type == DIFF_ALL)
1657
{
1658
/* Put in lines from FILE0 with bracket. */
1659
fprintf (outputfile, "<<<<<<< %s\n", file0);
1660
for (i = 0;
1661
i < D_NUMLINES (b, mapping[FILE0]);
1662
i++)
1663
fwrite (D_RELNUM (b, mapping[FILE0], i), sizeof (char),
1664
D_RELLEN (b, mapping[FILE0], i), outputfile);
1665
}
1666
1667
if (show_2nd)
1668
{
1669
/* Put in lines from FILE1 with bracket. */
1670
fprintf (outputfile, format_2nd, file1);
1671
for (i = 0;
1672
i < D_NUMLINES (b, mapping[FILE1]);
1673
i++)
1674
fwrite (D_RELNUM (b, mapping[FILE1], i), sizeof (char),
1675
D_RELLEN (b, mapping[FILE1], i), outputfile);
1676
}
1677
1678
fprintf (outputfile, "=======\n");
1679
}
1680
1681
/* Put in lines from FILE2. */
1682
for (i = 0;
1683
i < D_NUMLINES (b, mapping[FILE2]);
1684
i++)
1685
fwrite (D_RELNUM (b, mapping[FILE2], i), sizeof (char),
1686
D_RELLEN (b, mapping[FILE2], i), outputfile);
1687
1688
if (conflict)
1689
fprintf (outputfile, ">>>>>>> %s\n", file2);
1690
1691
/* Skip I lines in file 0. */
1692
i = D_NUMLINES (b, FILE0);
1693
linesread += i;
1694
while (0 <= --i)
1695
while ((c = getc (infile)) != '\n')
1696
if (c == EOF)
1697
{
1698
if (ferror (infile))
1699
perror_with_exit (_("read failed"));
1700
else if (feof (infile))
1701
{
1702
if (i || b->next)
1703
fatal ("input file shrank");
1704
return conflicts_found;
1705
}
1706
}
1707
}
1708
/* Copy rest of common file. */
1709
while ((c = getc (infile)) != EOF || !(ferror (infile) | feof (infile)))
1710
putc (c, outputfile);
1711
return conflicts_found;
1712
}
1713
1714
/* Reverse the order of the list of diff3 blocks. */
1715
1716
static struct diff3_block *
1717
reverse_diff3_blocklist (struct diff3_block *diff)
1718
{
1719
register struct diff3_block *tmp, *next, *prev;
1720
1721
for (tmp = diff, prev = 0; tmp; tmp = next)
1722
{
1723
next = tmp->next;
1724
tmp->next = prev;
1725
prev = tmp;
1726
}
1727
1728
return prev;
1729
}
1730
1731
static void
1732
fatal (char const *msgid)
1733
{
1734
error (EXIT_TROUBLE, 0, "%s", _(msgid));
1735
abort ();
1736
}
1737
1738
static void
1739
perror_with_exit (char const *string)
1740
{
1741
error (EXIT_TROUBLE, errno, "%s", string);
1742
abort ();
1743
}
1744
1745