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