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