source: vendor/diffutils/2.8.1/src/diff3.c

Last change on this file was 2556, checked in by bird, 19 years ago

diffutils 2.8.1

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