1170754Sdelphij/* diff3 - compare three files line by line 2170754Sdelphij 3170754Sdelphij Copyright (C) 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1998, 2001, 4170754Sdelphij 2002, 2004 Free Software Foundation, Inc. 5170754Sdelphij 6170754Sdelphij This program is free software; you can redistribute it and/or modify 7170754Sdelphij it under the terms of the GNU General Public License as published by 8170754Sdelphij the Free Software Foundation; either version 2, or (at your option) 9170754Sdelphij any later version. 10170754Sdelphij 11170754Sdelphij This program is distributed in the hope that it will be useful, 12170754Sdelphij but WITHOUT ANY WARRANTY; without even the implied warranty of 13170754Sdelphij MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14170754Sdelphij See the GNU General Public License for more details. 15170754Sdelphij 16170754Sdelphij You should have received a copy of the GNU General Public License 17170754Sdelphij along with this program; see the file COPYING. 18170754Sdelphij If not, write to the Free Software Foundation, 19170754Sdelphij 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 20170754Sdelphij 21170754Sdelphij#include "system.h" 22170754Sdelphij#include "paths.h" 23170754Sdelphij 24170754Sdelphij#include <stdio.h> 25170754Sdelphij#include <unlocked-io.h> 26170754Sdelphij 27170754Sdelphij#include <c-stack.h> 28170754Sdelphij#include <cmpbuf.h> 29170754Sdelphij#include <error.h> 30170754Sdelphij#include <exitfail.h> 31170754Sdelphij#include <file-type.h> 32170754Sdelphij#include <getopt.h> 33170754Sdelphij#include <inttostr.h> 34170754Sdelphij#include <quotesys.h> 35170754Sdelphij#include <version-etc.h> 36170754Sdelphij#include <xalloc.h> 37170754Sdelphij 38170754Sdelphij/* Internal data structures and macros for the diff3 program; includes 39170754Sdelphij data structures for both diff3 diffs and normal diffs. */ 40170754Sdelphij 41170754Sdelphij/* Different files within a three way diff. */ 42170754Sdelphij#define FILE0 0 43170754Sdelphij#define FILE1 1 44170754Sdelphij#define FILE2 2 45170754Sdelphij 46170754Sdelphij/* A three way diff is built from two two-way diffs; the file which 47170754Sdelphij the two two-way diffs share is: */ 48170754Sdelphij#define FILEC FILE2 49170754Sdelphij 50170754Sdelphij/* Different files within a two way diff. 51170754Sdelphij FC is the common file, FO the other file. */ 52170754Sdelphij#define FO 0 53170754Sdelphij#define FC 1 54170754Sdelphij 55170754Sdelphij/* The ranges are indexed by */ 56170754Sdelphij#define RANGE_START 0 57170754Sdelphij#define RANGE_END 1 58170754Sdelphij 59170754Sdelphijenum diff_type { 60170754Sdelphij ERROR, /* Should not be used */ 61170754Sdelphij ADD, /* Two way diff add */ 62170754Sdelphij CHANGE, /* Two way diff change */ 63170754Sdelphij DELETE, /* Two way diff delete */ 64170754Sdelphij DIFF_ALL, /* All three are different */ 65170754Sdelphij DIFF_1ST, /* Only the first is different */ 66170754Sdelphij DIFF_2ND, /* Only the second */ 67170754Sdelphij DIFF_3RD /* Only the third */ 68170754Sdelphij}; 69170754Sdelphij 70170754Sdelphij/* Two way diff */ 71170754Sdelphijstruct diff_block { 72170754Sdelphij lin ranges[2][2]; /* Ranges are inclusive */ 73170754Sdelphij char **lines[2]; /* The actual lines (may contain nulls) */ 74170754Sdelphij size_t *lengths[2]; /* Line lengths (including newlines, if any) */ 75170754Sdelphij struct diff_block *next; 76170754Sdelphij}; 77170754Sdelphij 78170754Sdelphij/* Three way diff */ 79170754Sdelphij 80170754Sdelphijstruct diff3_block { 81170754Sdelphij enum diff_type correspond; /* Type of diff */ 82170754Sdelphij lin ranges[3][2]; /* Ranges are inclusive */ 83170754Sdelphij char **lines[3]; /* The actual lines (may contain nulls) */ 84170754Sdelphij size_t *lengths[3]; /* Line lengths (including newlines, if any) */ 85170754Sdelphij struct diff3_block *next; 86170754Sdelphij}; 87170754Sdelphij 88170754Sdelphij/* Access the ranges on a diff block. */ 89170754Sdelphij#define D_LOWLINE(diff, filenum) \ 90170754Sdelphij ((diff)->ranges[filenum][RANGE_START]) 91170754Sdelphij#define D_HIGHLINE(diff, filenum) \ 92170754Sdelphij ((diff)->ranges[filenum][RANGE_END]) 93170754Sdelphij#define D_NUMLINES(diff, filenum) \ 94170754Sdelphij (D_HIGHLINE (diff, filenum) - D_LOWLINE (diff, filenum) + 1) 95170754Sdelphij 96170754Sdelphij/* Access the line numbers in a file in a diff by relative line 97170754Sdelphij numbers (i.e. line number within the diff itself). Note that these 98170754Sdelphij are lvalues and can be used for assignment. */ 99170754Sdelphij#define D_RELNUM(diff, filenum, linenum) \ 100170754Sdelphij ((diff)->lines[filenum][linenum]) 101170754Sdelphij#define D_RELLEN(diff, filenum, linenum) \ 102170754Sdelphij ((diff)->lengths[filenum][linenum]) 103170754Sdelphij 104170754Sdelphij/* And get at them directly, when that should be necessary. */ 105170754Sdelphij#define D_LINEARRAY(diff, filenum) \ 106170754Sdelphij ((diff)->lines[filenum]) 107170754Sdelphij#define D_LENARRAY(diff, filenum) \ 108170754Sdelphij ((diff)->lengths[filenum]) 109170754Sdelphij 110170754Sdelphij/* Next block. */ 111170754Sdelphij#define D_NEXT(diff) ((diff)->next) 112170754Sdelphij 113170754Sdelphij/* Access the type of a diff3 block. */ 114170754Sdelphij#define D3_TYPE(diff) ((diff)->correspond) 115170754Sdelphij 116170754Sdelphij/* Line mappings based on diffs. The first maps off the top of the 117170754Sdelphij diff, the second off of the bottom. */ 118170754Sdelphij#define D_HIGH_MAPLINE(diff, fromfile, tofile, linenum) \ 119170754Sdelphij ((linenum) \ 120170754Sdelphij - D_HIGHLINE ((diff), (fromfile)) \ 121170754Sdelphij + D_HIGHLINE ((diff), (tofile))) 122170754Sdelphij 123170754Sdelphij#define D_LOW_MAPLINE(diff, fromfile, tofile, linenum) \ 124170754Sdelphij ((linenum) \ 125170754Sdelphij - D_LOWLINE ((diff), (fromfile)) \ 126170754Sdelphij + D_LOWLINE ((diff), (tofile))) 127170754Sdelphij 128170754Sdelphij/* Options variables for flags set on command line. */ 129170754Sdelphij 130170754Sdelphij/* If nonzero, treat all files as text files, never as binary. */ 131170754Sdelphijstatic bool text; 132170754Sdelphij 133170754Sdelphij/* Remove trailing carriage returns from input. */ 134170754Sdelphijstatic bool strip_trailing_cr; 135170754Sdelphij 136170754Sdelphij/* If nonzero, write out an ed script instead of the standard diff3 format. */ 137170754Sdelphijstatic bool edscript; 138170754Sdelphij 139170754Sdelphij/* If nonzero, in the case of overlapping diffs (type DIFF_ALL), 140170754Sdelphij preserve the lines which would normally be deleted from 141170754Sdelphij file 1 with a special flagging mechanism. */ 142170754Sdelphijstatic bool flagging; 143170754Sdelphij 144170754Sdelphij/* Use a tab to align output lines (-T). */ 145170754Sdelphijstatic bool initial_tab; 146170754Sdelphij 147170754Sdelphij/* If nonzero, do not output information for overlapping diffs. */ 148170754Sdelphijstatic bool simple_only; 149170754Sdelphij 150170754Sdelphij/* If nonzero, do not output information for non-overlapping diffs. */ 151170754Sdelphijstatic bool overlap_only; 152170754Sdelphij 153170754Sdelphij/* If nonzero, show information for DIFF_2ND diffs. */ 154170754Sdelphijstatic bool show_2nd; 155170754Sdelphij 156170754Sdelphij/* If nonzero, include `:wq' at the end of the script 157170754Sdelphij to write out the file being edited. */ 158170754Sdelphijstatic bool finalwrite; 159170754Sdelphij 160170754Sdelphij/* If nonzero, output a merged file. */ 161170754Sdelphijstatic bool merge; 162170754Sdelphij 163170754Sdelphijchar *program_name; 164170754Sdelphij 165170754Sdelphijstatic char *read_diff (char const *, char const *, char **); 166170754Sdelphijstatic char *scan_diff_line (char *, char **, size_t *, char *, char); 167170754Sdelphijstatic enum diff_type process_diff_control (char **, struct diff_block *); 168170754Sdelphijstatic bool compare_line_list (char * const[], size_t const[], char * const[], size_t const[], lin); 169170754Sdelphijstatic bool copy_stringlist (char * const[], size_t const[], char *[], size_t[], lin); 170170754Sdelphijstatic bool output_diff3_edscript (FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *); 171170754Sdelphijstatic bool output_diff3_merge (FILE *, FILE *, struct diff3_block *, int const[3], int const[3], char const *, char const *, char const *); 172170754Sdelphijstatic struct diff3_block *create_diff3_block (lin, lin, lin, lin, lin, lin); 173170754Sdelphijstatic struct diff3_block *make_3way_diff (struct diff_block *, struct diff_block *); 174170754Sdelphijstatic struct diff3_block *reverse_diff3_blocklist (struct diff3_block *); 175170754Sdelphijstatic struct diff3_block *using_to_diff3_block (struct diff_block *[2], struct diff_block *[2], int, int, struct diff3_block const *); 176170754Sdelphijstatic struct diff_block *process_diff (char const *, char const *, struct diff_block **); 177170754Sdelphijstatic void check_stdout (void); 178170754Sdelphijstatic void fatal (char const *) __attribute__((noreturn)); 179170754Sdelphijstatic void output_diff3 (FILE *, struct diff3_block *, int const[3], int const[3]); 180170754Sdelphijstatic void perror_with_exit (char const *) __attribute__((noreturn)); 181170754Sdelphijstatic void try_help (char const *, char const *) __attribute__((noreturn)); 182170754Sdelphijstatic void usage (void); 183170754Sdelphij 184170754Sdelphijstatic char const *diff_program = DEFAULT_DIFF_PROGRAM; 185170754Sdelphij 186170754Sdelphij/* Values for long options that do not have single-letter equivalents. */ 187170754Sdelphijenum 188170754Sdelphij{ 189170754Sdelphij DIFF_PROGRAM_OPTION = CHAR_MAX + 1, 190170754Sdelphij HELP_OPTION, 191170754Sdelphij STRIP_TRAILING_CR_OPTION 192170754Sdelphij}; 193170754Sdelphij 194170754Sdelphijstatic struct option const longopts[] = 195170754Sdelphij{ 196170754Sdelphij {"diff-program", 1, 0, DIFF_PROGRAM_OPTION}, 197170754Sdelphij {"easy-only", 0, 0, '3'}, 198170754Sdelphij {"ed", 0, 0, 'e'}, 199170754Sdelphij {"help", 0, 0, HELP_OPTION}, 200170754Sdelphij {"initial-tab", 0, 0, 'T'}, 201170754Sdelphij {"label", 1, 0, 'L'}, 202170754Sdelphij {"merge", 0, 0, 'm'}, 203170754Sdelphij {"overlap-only", 0, 0, 'x'}, 204170754Sdelphij {"show-all", 0, 0, 'A'}, 205170754Sdelphij {"show-overlap", 0, 0, 'E'}, 206170754Sdelphij {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION}, 207170754Sdelphij {"text", 0, 0, 'a'}, 208170754Sdelphij {"version", 0, 0, 'v'}, 209170754Sdelphij {0, 0, 0, 0} 210170754Sdelphij}; 211170754Sdelphij 212170754Sdelphijint 213170754Sdelphijmain (int argc, char **argv) 214170754Sdelphij{ 215170754Sdelphij int c, i; 216170754Sdelphij int common; 217170754Sdelphij int mapping[3]; 218170754Sdelphij int rev_mapping[3]; 219170754Sdelphij int incompat = 0; 220170754Sdelphij bool conflicts_found; 221170754Sdelphij struct diff_block *thread0, *thread1, *last_block; 222170754Sdelphij struct diff3_block *diff3; 223170754Sdelphij int tag_count = 0; 224170754Sdelphij char *tag_strings[3]; 225170754Sdelphij char *commonname; 226170754Sdelphij char **file; 227170754Sdelphij struct stat statb; 228170754Sdelphij 229170754Sdelphij exit_failure = 2; 230170754Sdelphij initialize_main (&argc, &argv); 231170754Sdelphij program_name = argv[0]; 232170754Sdelphij setlocale (LC_ALL, ""); 233170754Sdelphij bindtextdomain (PACKAGE, LOCALEDIR); 234170754Sdelphij textdomain (PACKAGE); 235170754Sdelphij c_stack_action (0); 236170754Sdelphij 237170754Sdelphij while ((c = getopt_long (argc, argv, "aeimvx3AEL:TX", longopts, 0)) != -1) 238170754Sdelphij { 239170754Sdelphij switch (c) 240170754Sdelphij { 241170754Sdelphij case 'a': 242170754Sdelphij text = true; 243170754Sdelphij break; 244170754Sdelphij case 'A': 245170754Sdelphij show_2nd = true; 246170754Sdelphij flagging = true; 247170754Sdelphij incompat++; 248170754Sdelphij break; 249170754Sdelphij case 'x': 250170754Sdelphij overlap_only = true; 251170754Sdelphij incompat++; 252170754Sdelphij break; 253170754Sdelphij case '3': 254170754Sdelphij simple_only = true; 255170754Sdelphij incompat++; 256170754Sdelphij break; 257170754Sdelphij case 'i': 258170754Sdelphij finalwrite = true; 259170754Sdelphij break; 260170754Sdelphij case 'm': 261170754Sdelphij merge = true; 262170754Sdelphij break; 263170754Sdelphij case 'X': 264170754Sdelphij overlap_only = true; 265170754Sdelphij /* Fall through. */ 266170754Sdelphij case 'E': 267170754Sdelphij flagging = true; 268170754Sdelphij /* Fall through. */ 269170754Sdelphij case 'e': 270170754Sdelphij incompat++; 271170754Sdelphij break; 272170754Sdelphij case 'T': 273170754Sdelphij initial_tab = true; 274170754Sdelphij break; 275170754Sdelphij case STRIP_TRAILING_CR_OPTION: 276170754Sdelphij strip_trailing_cr = true; 277170754Sdelphij break; 278170754Sdelphij case 'v': 279170754Sdelphij version_etc (stdout, "diff3", PACKAGE_NAME, PACKAGE_VERSION, 280170754Sdelphij "Randy Smith", (char *) 0); 281170754Sdelphij check_stdout (); 282170754Sdelphij return EXIT_SUCCESS; 283170754Sdelphij case DIFF_PROGRAM_OPTION: 284170754Sdelphij diff_program = optarg; 285170754Sdelphij break; 286170754Sdelphij case HELP_OPTION: 287170754Sdelphij usage (); 288170754Sdelphij check_stdout (); 289170754Sdelphij return EXIT_SUCCESS; 290170754Sdelphij case 'L': 291170754Sdelphij /* Handle up to three -L options. */ 292170754Sdelphij if (tag_count < 3) 293170754Sdelphij { 294170754Sdelphij tag_strings[tag_count++] = optarg; 295170754Sdelphij break; 296170754Sdelphij } 297170754Sdelphij try_help ("too many file label options", 0); 298170754Sdelphij default: 299170754Sdelphij try_help (0, 0); 300170754Sdelphij } 301170754Sdelphij } 302170754Sdelphij 303170754Sdelphij edscript = incompat & ~merge; /* -AeExX3 without -m implies ed script. */ 304170754Sdelphij show_2nd |= ~incompat & merge; /* -m without -AeExX3 implies -A. */ 305170754Sdelphij flagging |= ~incompat & merge; 306170754Sdelphij 307170754Sdelphij if (incompat > 1 /* Ensure at most one of -AeExX3. */ 308170754Sdelphij || finalwrite & merge /* -i -m would rewrite input file. */ 309170754Sdelphij || (tag_count && ! flagging)) /* -L requires one of -AEX. */ 310170754Sdelphij try_help ("incompatible options", 0); 311170754Sdelphij 312170754Sdelphij if (argc - optind != 3) 313170754Sdelphij { 314170754Sdelphij if (argc - optind < 3) 315170754Sdelphij try_help ("missing operand after `%s'", argv[argc - 1]); 316170754Sdelphij else 317170754Sdelphij try_help ("extra operand `%s'", argv[optind + 3]); 318170754Sdelphij } 319170754Sdelphij 320170754Sdelphij file = &argv[optind]; 321170754Sdelphij 322170754Sdelphij for (i = tag_count; i < 3; i++) 323170754Sdelphij tag_strings[i] = file[i]; 324170754Sdelphij 325170754Sdelphij /* Always compare file1 to file2, even if file2 is "-". 326170754Sdelphij This is needed for -mAeExX3. Using the file0 as 327170754Sdelphij the common file would produce wrong results, because if the 328170754Sdelphij file0-file1 diffs didn't line up with the file0-file2 diffs 329170754Sdelphij (which is entirely possible since we don't use diff's -n option), 330170754Sdelphij diff3 might report phantom changes from file1 to file2. 331170754Sdelphij 332170754Sdelphij Also, try to compare file0 to file1, because this is where 333170754Sdelphij changes are expected to come from. Diffing between these pairs 334170754Sdelphij of files is more likely to avoid phantom changes from file0 to file1. 335170754Sdelphij 336170754Sdelphij Historically, the default common file was file2, so some older 337170754Sdelphij applications (e.g. Emacs ediff) used file2 as the ancestor. So, 338170754Sdelphij for compatibility, if this is a 3-way diff (not a merge or 339170754Sdelphij edscript), prefer file2 as the common file. */ 340170754Sdelphij 341170754Sdelphij common = 2 - (edscript | merge); 342170754Sdelphij 343170754Sdelphij if (strcmp (file[common], "-") == 0) 344170754Sdelphij { 345170754Sdelphij /* Sigh. We've got standard input as the common file. We can't 346170754Sdelphij call diff twice on stdin. Use the other arg as the common 347170754Sdelphij file instead. */ 348170754Sdelphij common = 3 - common; 349170754Sdelphij if (strcmp (file[0], "-") == 0 || strcmp (file[common], "-") == 0) 350170754Sdelphij fatal ("`-' specified for more than one input file"); 351170754Sdelphij } 352170754Sdelphij 353170754Sdelphij mapping[0] = 0; 354170754Sdelphij mapping[1] = 3 - common; 355170754Sdelphij mapping[2] = common; 356170754Sdelphij 357170754Sdelphij for (i = 0; i < 3; i++) 358170754Sdelphij rev_mapping[mapping[i]] = i; 359170754Sdelphij 360170754Sdelphij for (i = 0; i < 3; i++) 361170754Sdelphij if (strcmp (file[i], "-") != 0) 362170754Sdelphij { 363170754Sdelphij if (stat (file[i], &statb) < 0) 364170754Sdelphij perror_with_exit (file[i]); 365170754Sdelphij else if (S_ISDIR (statb.st_mode)) 366170754Sdelphij error (EXIT_TROUBLE, EISDIR, "%s", file[i]); 367170754Sdelphij } 368170754Sdelphij 369170754Sdelphij#ifdef SIGCHLD 370170754Sdelphij /* System V fork+wait does not work if SIGCHLD is ignored. */ 371170754Sdelphij signal (SIGCHLD, SIG_DFL); 372170754Sdelphij#endif 373170754Sdelphij 374170754Sdelphij /* Invoke diff twice on two pairs of input files, combine the two 375170754Sdelphij diffs, and output them. */ 376170754Sdelphij 377170754Sdelphij commonname = file[rev_mapping[FILEC]]; 378170754Sdelphij thread1 = process_diff (file[rev_mapping[FILE1]], commonname, &last_block); 379170754Sdelphij thread0 = process_diff (file[rev_mapping[FILE0]], commonname, &last_block); 380170754Sdelphij diff3 = make_3way_diff (thread0, thread1); 381170754Sdelphij if (edscript) 382170754Sdelphij conflicts_found 383170754Sdelphij = output_diff3_edscript (stdout, diff3, mapping, rev_mapping, 384170754Sdelphij tag_strings[0], tag_strings[1], tag_strings[2]); 385170754Sdelphij else if (merge) 386170754Sdelphij { 387170754Sdelphij if (! freopen (file[rev_mapping[FILE0]], "r", stdin)) 388170754Sdelphij perror_with_exit (file[rev_mapping[FILE0]]); 389170754Sdelphij conflicts_found 390170754Sdelphij = output_diff3_merge (stdin, stdout, diff3, mapping, rev_mapping, 391170754Sdelphij tag_strings[0], tag_strings[1], tag_strings[2]); 392170754Sdelphij if (ferror (stdin)) 393170754Sdelphij fatal ("read failed"); 394170754Sdelphij } 395170754Sdelphij else 396170754Sdelphij { 397170754Sdelphij output_diff3 (stdout, diff3, mapping, rev_mapping); 398170754Sdelphij conflicts_found = false; 399170754Sdelphij } 400170754Sdelphij 401170754Sdelphij check_stdout (); 402170754Sdelphij exit (conflicts_found); 403170754Sdelphij return conflicts_found; 404170754Sdelphij} 405170754Sdelphij 406170754Sdelphijstatic void 407170754Sdelphijtry_help (char const *reason_msgid, char const *operand) 408170754Sdelphij{ 409170754Sdelphij if (reason_msgid) 410170754Sdelphij error (0, 0, _(reason_msgid), operand); 411170754Sdelphij error (EXIT_TROUBLE, 0, 412170754Sdelphij _("Try `%s --help' for more information."), program_name); 413170754Sdelphij abort (); 414170754Sdelphij} 415170754Sdelphij 416170754Sdelphijstatic void 417170754Sdelphijcheck_stdout (void) 418170754Sdelphij{ 419170754Sdelphij if (ferror (stdout)) 420170754Sdelphij fatal ("write failed"); 421170754Sdelphij else if (fclose (stdout) != 0) 422170754Sdelphij perror_with_exit (_("standard output")); 423170754Sdelphij} 424170754Sdelphij 425170754Sdelphijstatic char const * const option_help_msgid[] = { 426170754Sdelphij N_("-e --ed Output unmerged changes from OLDFILE to YOURFILE into MYFILE."), 427170754Sdelphij N_("-E --show-overlap Output unmerged changes, bracketing conflicts."), 428170754Sdelphij N_("-A --show-all Output all changes, bracketing conflicts."), 429170754Sdelphij N_("-x --overlap-only Output overlapping changes."), 430170754Sdelphij N_("-X Output overlapping changes, bracketing them."), 431170754Sdelphij N_("-3 --easy-only Output unmerged nonoverlapping changes."), 432170754Sdelphij "", 433170754Sdelphij N_("-m --merge Output merged file instead of ed script (default -A)."), 434170754Sdelphij N_("-L LABEL --label=LABEL Use LABEL instead of file name."), 435170754Sdelphij N_("-i Append `w' and `q' commands to ed scripts."), 436170754Sdelphij N_("-a --text Treat all files as text."), 437170754Sdelphij N_("--strip-trailing-cr Strip trailing carriage return on input."), 438170754Sdelphij N_("-T --initial-tab Make tabs line up by prepending a tab."), 439170754Sdelphij N_("--diff-program=PROGRAM Use PROGRAM to compare files."), 440170754Sdelphij "", 441170754Sdelphij N_("-v --version Output version info."), 442170754Sdelphij N_("--help Output this help."), 443170754Sdelphij 0 444170754Sdelphij}; 445170754Sdelphij 446170754Sdelphijstatic void 447170754Sdelphijusage (void) 448170754Sdelphij{ 449170754Sdelphij char const * const *p; 450170754Sdelphij 451170754Sdelphij printf (_("Usage: %s [OPTION]... MYFILE OLDFILE YOURFILE\n"), 452170754Sdelphij program_name); 453170754Sdelphij printf ("%s\n\n", _("Compare three files line by line.")); 454170754Sdelphij for (p = option_help_msgid; *p; p++) 455170754Sdelphij if (**p) 456170754Sdelphij printf (" %s\n", _(*p)); 457170754Sdelphij else 458170754Sdelphij putchar ('\n'); 459170754Sdelphij printf ("\n%s\n%s\n\n%s\n", 460170754Sdelphij _("If a FILE is `-', read standard input."), 461170754Sdelphij _("Exit status is 0 if successful, 1 if conflicts, 2 if trouble."), 462170754Sdelphij _("Report bugs to <bug-gnu-utils@gnu.org>.")); 463170754Sdelphij} 464170754Sdelphij 465170754Sdelphij/* Combine the two diffs together into one. 466170754Sdelphij Here is the algorithm: 467170754Sdelphij 468170754Sdelphij File2 is shared in common between the two diffs. 469170754Sdelphij Diff02 is the diff between 0 and 2. 470170754Sdelphij Diff12 is the diff between 1 and 2. 471170754Sdelphij 472170754Sdelphij 1) Find the range for the first block in File2. 473170754Sdelphij a) Take the lowest of the two ranges (in File2) in the two 474170754Sdelphij current blocks (one from each diff) as being the low 475170754Sdelphij water mark. Assign the upper end of this block as 476170754Sdelphij being the high water mark and move the current block up 477170754Sdelphij one. Mark the block just moved over as to be used. 478170754Sdelphij b) Check the next block in the diff that the high water 479170754Sdelphij mark is *not* from. 480170754Sdelphij 481170754Sdelphij *If* the high water mark is above 482170754Sdelphij the low end of the range in that block, 483170754Sdelphij 484170754Sdelphij mark that block as to be used and move the current 485170754Sdelphij block up. Set the high water mark to the max of 486170754Sdelphij the high end of this block and the current. Repeat b. 487170754Sdelphij 488170754Sdelphij 2) Find the corresponding ranges in File0 (from the blocks 489170754Sdelphij in diff02; line per line outside of diffs) and in File1. 490170754Sdelphij Create a diff3_block, reserving space as indicated by the ranges. 491170754Sdelphij 492170754Sdelphij 3) Copy all of the pointers for file2 in. At least for now, 493170754Sdelphij do memcmp's between corresponding strings in the two diffs. 494170754Sdelphij 495170754Sdelphij 4) Copy all of the pointers for file0 and 1 in. Get what is 496170754Sdelphij needed from file2 (when there isn't a diff block, it's 497170754Sdelphij identical to file2 within the range between diff blocks). 498170754Sdelphij 499170754Sdelphij 5) If the diff blocks used came from only one of the two 500170754Sdelphij strings of diffs, then that file (i.e. the one other than 501170754Sdelphij the common file in that diff) is the odd person out. If 502170754Sdelphij diff blocks are used from both sets, check to see if files 503170754Sdelphij 0 and 1 match: 504170754Sdelphij 505170754Sdelphij Same number of lines? If so, do a set of memcmp's (if 506170754Sdelphij a memcmp matches; copy the pointer over; it'll be easier 507170754Sdelphij later during comparisons). If they match, 0 & 1 are the 508170754Sdelphij same. If not, all three different. 509170754Sdelphij 510170754Sdelphij Then do it again, until the blocks are exhausted. */ 511170754Sdelphij 512170754Sdelphij 513170754Sdelphij/* Make a three way diff (chain of diff3_block's) from two two way 514170754Sdelphij diffs (chains of diff_block's). Assume that each of the two diffs 515170754Sdelphij passed are onto the same file (i.e. that each of the diffs were 516170754Sdelphij made "to" the same file). Return a three way diff pointer with 517170754Sdelphij numbering FILE0 = the other file in diff02, FILE1 = the other file 518170754Sdelphij in diff12, and FILEC = the common file. */ 519170754Sdelphij 520170754Sdelphijstatic struct diff3_block * 521170754Sdelphijmake_3way_diff (struct diff_block *thread0, struct diff_block *thread1) 522170754Sdelphij{ 523170754Sdelphij /* Work on the two diffs passed to it as threads. Thread number 0 524170754Sdelphij is diff02, thread number 1 is diff12. USING is the base of the 525170754Sdelphij list of blocks to be used to construct each block of the three 526170754Sdelphij way diff; if no blocks from a particular thread are to be used, 527170754Sdelphij that element of USING is 0. LAST_USING contains the last 528170754Sdelphij elements on each of the using lists. 529170754Sdelphij 530170754Sdelphij HIGH_WATER_MARK is the highest line number in the common file 531170754Sdelphij described in any of the diffs in either of the USING lists. 532170754Sdelphij HIGH_WATER_THREAD names the thread. Similarly BASE_WATER_MARK 533170754Sdelphij and BASE_WATER_THREAD describe the lowest line number in the 534170754Sdelphij common file described in any of the diffs in either of the USING 535170754Sdelphij lists. HIGH_WATER_DIFF is the diff from which the 536170754Sdelphij HIGH_WATER_MARK was taken. 537170754Sdelphij 538170754Sdelphij HIGH_WATER_DIFF should always be equal to 539170754Sdelphij LAST_USING[HIGH_WATER_THREAD]. OTHER_DIFF is the next diff to 540170754Sdelphij check for higher water, and should always be equal to 541170754Sdelphij CURRENT[HIGH_WATER_THREAD ^ 1]. OTHER_THREAD is the thread in 542170754Sdelphij which the OTHER_DIFF is, and hence should always be equal to 543170754Sdelphij HIGH_WATER_THREAD ^ 1. 544170754Sdelphij 545170754Sdelphij LAST_DIFF is the last diff block produced by this routine, for 546170754Sdelphij line correspondence purposes between that diff and the one 547170754Sdelphij currently being worked on. It is ZERO_DIFF before any blocks 548170754Sdelphij have been created. */ 549170754Sdelphij 550170754Sdelphij struct diff_block *using[2]; 551170754Sdelphij struct diff_block *last_using[2]; 552170754Sdelphij struct diff_block *current[2]; 553170754Sdelphij 554170754Sdelphij lin high_water_mark; 555170754Sdelphij 556170754Sdelphij int high_water_thread; 557170754Sdelphij int base_water_thread; 558170754Sdelphij int other_thread; 559170754Sdelphij 560170754Sdelphij struct diff_block *high_water_diff; 561170754Sdelphij struct diff_block *other_diff; 562170754Sdelphij 563170754Sdelphij struct diff3_block *result; 564170754Sdelphij struct diff3_block *tmpblock; 565170754Sdelphij struct diff3_block **result_end; 566170754Sdelphij 567170754Sdelphij struct diff3_block const *last_diff3; 568170754Sdelphij 569170754Sdelphij static struct diff3_block const zero_diff3; 570170754Sdelphij 571170754Sdelphij /* Initialization */ 572170754Sdelphij result = 0; 573170754Sdelphij result_end = &result; 574170754Sdelphij current[0] = thread0; current[1] = thread1; 575170754Sdelphij last_diff3 = &zero_diff3; 576170754Sdelphij 577170754Sdelphij /* Sniff up the threads until we reach the end */ 578170754Sdelphij 579170754Sdelphij while (current[0] || current[1]) 580170754Sdelphij { 581170754Sdelphij using[0] = using[1] = last_using[0] = last_using[1] = 0; 582170754Sdelphij 583170754Sdelphij /* Setup low and high water threads, diffs, and marks. */ 584170754Sdelphij if (!current[0]) 585170754Sdelphij base_water_thread = 1; 586170754Sdelphij else if (!current[1]) 587170754Sdelphij base_water_thread = 0; 588170754Sdelphij else 589170754Sdelphij base_water_thread = 590170754Sdelphij (D_LOWLINE (current[0], FC) > D_LOWLINE (current[1], FC)); 591170754Sdelphij 592170754Sdelphij high_water_thread = base_water_thread; 593170754Sdelphij 594170754Sdelphij high_water_diff = current[high_water_thread]; 595170754Sdelphij 596170754Sdelphij high_water_mark = D_HIGHLINE (high_water_diff, FC); 597170754Sdelphij 598170754Sdelphij /* Make the diff you just got info from into the using class */ 599170754Sdelphij using[high_water_thread] 600170754Sdelphij = last_using[high_water_thread] 601170754Sdelphij = high_water_diff; 602170754Sdelphij current[high_water_thread] = high_water_diff->next; 603170754Sdelphij last_using[high_water_thread]->next = 0; 604170754Sdelphij 605170754Sdelphij /* And mark the other diff */ 606170754Sdelphij other_thread = high_water_thread ^ 0x1; 607170754Sdelphij other_diff = current[other_thread]; 608170754Sdelphij 609170754Sdelphij /* Shuffle up the ladder, checking the other diff to see if it 610170754Sdelphij needs to be incorporated. */ 611170754Sdelphij while (other_diff 612170754Sdelphij && D_LOWLINE (other_diff, FC) <= high_water_mark + 1) 613170754Sdelphij { 614170754Sdelphij 615170754Sdelphij /* Incorporate this diff into the using list. Note that 616170754Sdelphij this doesn't take it off the current list */ 617170754Sdelphij if (using[other_thread]) 618170754Sdelphij last_using[other_thread]->next = other_diff; 619170754Sdelphij else 620170754Sdelphij using[other_thread] = other_diff; 621170754Sdelphij last_using[other_thread] = other_diff; 622170754Sdelphij 623170754Sdelphij /* Take it off the current list. Note that this following 624170754Sdelphij code assumes that other_diff enters it equal to 625170754Sdelphij current[high_water_thread ^ 0x1] */ 626170754Sdelphij current[other_thread] = current[other_thread]->next; 627170754Sdelphij other_diff->next = 0; 628170754Sdelphij 629170754Sdelphij /* Set the high_water stuff 630170754Sdelphij If this comparison is equal, then this is the last pass 631170754Sdelphij through this loop; since diff blocks within a given 632170754Sdelphij thread cannot overlap, the high_water_mark will be 633170754Sdelphij *below* the range_start of either of the next diffs. */ 634170754Sdelphij 635170754Sdelphij if (high_water_mark < D_HIGHLINE (other_diff, FC)) 636170754Sdelphij { 637170754Sdelphij high_water_thread ^= 1; 638170754Sdelphij high_water_diff = other_diff; 639170754Sdelphij high_water_mark = D_HIGHLINE (other_diff, FC); 640170754Sdelphij } 641170754Sdelphij 642170754Sdelphij /* Set the other diff */ 643170754Sdelphij other_thread = high_water_thread ^ 0x1; 644170754Sdelphij other_diff = current[other_thread]; 645170754Sdelphij } 646170754Sdelphij 647170754Sdelphij /* The using lists contain a list of all of the blocks to be 648170754Sdelphij included in this diff3_block. Create it. */ 649170754Sdelphij 650170754Sdelphij tmpblock = using_to_diff3_block (using, last_using, 651170754Sdelphij base_water_thread, high_water_thread, 652170754Sdelphij last_diff3); 653170754Sdelphij 654170754Sdelphij if (!tmpblock) 655170754Sdelphij fatal ("internal error: screwup in format of diff blocks"); 656170754Sdelphij 657170754Sdelphij /* Put it on the list. */ 658170754Sdelphij *result_end = tmpblock; 659170754Sdelphij result_end = &tmpblock->next; 660170754Sdelphij 661170754Sdelphij /* Set up corresponding lines correctly. */ 662170754Sdelphij last_diff3 = tmpblock; 663170754Sdelphij } 664170754Sdelphij return result; 665170754Sdelphij} 666170754Sdelphij 667170754Sdelphij/* Take two lists of blocks (from two separate diff threads) and put 668170754Sdelphij them together into one diff3 block. Return a pointer to this diff3 669170754Sdelphij block or 0 for failure. 670170754Sdelphij 671170754Sdelphij All arguments besides using are for the convenience of the routine; 672170754Sdelphij they could be derived from the using array. LAST_USING is a pair 673170754Sdelphij of pointers to the last blocks in the using structure. LOW_THREAD 674170754Sdelphij and HIGH_THREAD tell which threads contain the lowest and highest 675170754Sdelphij line numbers for File0. LAST_DIFF3 contains the last diff produced 676170754Sdelphij in the calling routine. This is used for lines mappings that 677170754Sdelphij would still be identical to the state that diff ended in. 678170754Sdelphij 679170754Sdelphij A distinction should be made in this routine between the two diffs 680170754Sdelphij that are part of a normal two diff block, and the three diffs that 681170754Sdelphij are part of a diff3_block. */ 682170754Sdelphij 683170754Sdelphijstatic struct diff3_block * 684170754Sdelphijusing_to_diff3_block (struct diff_block *using[2], 685170754Sdelphij struct diff_block *last_using[2], 686170754Sdelphij int low_thread, int high_thread, 687170754Sdelphij struct diff3_block const *last_diff3) 688170754Sdelphij{ 689170754Sdelphij lin low[2], high[2]; 690170754Sdelphij struct diff3_block *result; 691170754Sdelphij struct diff_block *ptr; 692170754Sdelphij int d; 693170754Sdelphij lin i; 694170754Sdelphij 695170754Sdelphij /* Find the range in the common file. */ 696170754Sdelphij lin lowc = D_LOWLINE (using[low_thread], FC); 697170754Sdelphij lin highc = D_HIGHLINE (last_using[high_thread], FC); 698170754Sdelphij 699170754Sdelphij /* Find the ranges in the other files. 700170754Sdelphij If using[d] is null, that means that the file to which that diff 701170754Sdelphij refers is equivalent to the common file over this range. */ 702170754Sdelphij 703170754Sdelphij for (d = 0; d < 2; d++) 704170754Sdelphij if (using[d]) 705170754Sdelphij { 706170754Sdelphij low[d] = D_LOW_MAPLINE (using[d], FC, FO, lowc); 707170754Sdelphij high[d] = D_HIGH_MAPLINE (last_using[d], FC, FO, highc); 708170754Sdelphij } 709170754Sdelphij else 710170754Sdelphij { 711170754Sdelphij low[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, lowc); 712170754Sdelphij high[d] = D_HIGH_MAPLINE (last_diff3, FILEC, FILE0 + d, highc); 713170754Sdelphij } 714170754Sdelphij 715170754Sdelphij /* Create a block with the appropriate sizes */ 716170754Sdelphij result = create_diff3_block (low[0], high[0], low[1], high[1], lowc, highc); 717170754Sdelphij 718170754Sdelphij /* Copy information for the common file. 719170754Sdelphij Return with a zero if any of the compares failed. */ 720170754Sdelphij 721170754Sdelphij for (d = 0; d < 2; d++) 722170754Sdelphij for (ptr = using[d]; ptr; ptr = D_NEXT (ptr)) 723170754Sdelphij { 724170754Sdelphij lin result_offset = D_LOWLINE (ptr, FC) - lowc; 725170754Sdelphij 726170754Sdelphij if (!copy_stringlist (D_LINEARRAY (ptr, FC), 727170754Sdelphij D_LENARRAY (ptr, FC), 728170754Sdelphij D_LINEARRAY (result, FILEC) + result_offset, 729170754Sdelphij D_LENARRAY (result, FILEC) + result_offset, 730170754Sdelphij D_NUMLINES (ptr, FC))) 731170754Sdelphij return 0; 732170754Sdelphij } 733170754Sdelphij 734170754Sdelphij /* Copy information for file d. First deal with anything that might be 735170754Sdelphij before the first diff. */ 736170754Sdelphij 737170754Sdelphij for (d = 0; d < 2; d++) 738170754Sdelphij { 739170754Sdelphij struct diff_block *u = using[d]; 740170754Sdelphij lin lo = low[d], hi = high[d]; 741170754Sdelphij 742170754Sdelphij for (i = 0; 743170754Sdelphij i + lo < (u ? D_LOWLINE (u, FO) : hi + 1); 744170754Sdelphij i++) 745170754Sdelphij { 746170754Sdelphij D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, i); 747170754Sdelphij D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, i); 748170754Sdelphij } 749170754Sdelphij 750170754Sdelphij for (ptr = u; ptr; ptr = D_NEXT (ptr)) 751170754Sdelphij { 752170754Sdelphij lin result_offset = D_LOWLINE (ptr, FO) - lo; 753170754Sdelphij lin linec; 754170754Sdelphij 755170754Sdelphij if (!copy_stringlist (D_LINEARRAY (ptr, FO), 756170754Sdelphij D_LENARRAY (ptr, FO), 757170754Sdelphij D_LINEARRAY (result, FILE0 + d) + result_offset, 758170754Sdelphij D_LENARRAY (result, FILE0 + d) + result_offset, 759170754Sdelphij D_NUMLINES (ptr, FO))) 760170754Sdelphij return 0; 761170754Sdelphij 762170754Sdelphij /* Catch the lines between here and the next diff */ 763170754Sdelphij linec = D_HIGHLINE (ptr, FC) + 1 - lowc; 764170754Sdelphij for (i = D_HIGHLINE (ptr, FO) + 1 - lo; 765170754Sdelphij i < (D_NEXT (ptr) ? D_LOWLINE (D_NEXT (ptr), FO) : hi + 1) - lo; 766170754Sdelphij i++) 767170754Sdelphij { 768170754Sdelphij D_RELNUM (result, FILE0 + d, i) = D_RELNUM (result, FILEC, linec); 769170754Sdelphij D_RELLEN (result, FILE0 + d, i) = D_RELLEN (result, FILEC, linec); 770170754Sdelphij linec++; 771170754Sdelphij } 772170754Sdelphij } 773170754Sdelphij } 774170754Sdelphij 775170754Sdelphij /* Set correspond */ 776170754Sdelphij if (!using[0]) 777170754Sdelphij D3_TYPE (result) = DIFF_2ND; 778170754Sdelphij else if (!using[1]) 779170754Sdelphij D3_TYPE (result) = DIFF_1ST; 780170754Sdelphij else 781170754Sdelphij { 782170754Sdelphij lin nl0 = D_NUMLINES (result, FILE0); 783170754Sdelphij lin nl1 = D_NUMLINES (result, FILE1); 784170754Sdelphij 785170754Sdelphij if (nl0 != nl1 786170754Sdelphij || !compare_line_list (D_LINEARRAY (result, FILE0), 787170754Sdelphij D_LENARRAY (result, FILE0), 788170754Sdelphij D_LINEARRAY (result, FILE1), 789170754Sdelphij D_LENARRAY (result, FILE1), 790170754Sdelphij nl0)) 791170754Sdelphij D3_TYPE (result) = DIFF_ALL; 792170754Sdelphij else 793170754Sdelphij D3_TYPE (result) = DIFF_3RD; 794170754Sdelphij } 795170754Sdelphij 796170754Sdelphij return result; 797170754Sdelphij} 798170754Sdelphij 799170754Sdelphij/* Copy pointers from a list of strings to a different list of 800170754Sdelphij strings. If a spot in the second list is already filled, make sure 801170754Sdelphij that it is filled with the same string; if not, return false, the copy 802170754Sdelphij incomplete. Upon successful completion of the copy, return true. */ 803170754Sdelphij 804170754Sdelphijstatic bool 805170754Sdelphijcopy_stringlist (char * const fromptrs[], size_t const fromlengths[], 806170754Sdelphij char *toptrs[], size_t tolengths[], 807170754Sdelphij lin copynum) 808170754Sdelphij{ 809170754Sdelphij register char * const *f = fromptrs; 810170754Sdelphij register char **t = toptrs; 811170754Sdelphij register size_t const *fl = fromlengths; 812170754Sdelphij register size_t *tl = tolengths; 813170754Sdelphij 814170754Sdelphij while (copynum--) 815170754Sdelphij { 816170754Sdelphij if (*t) 817170754Sdelphij { 818170754Sdelphij if (*fl != *tl || memcmp (*f, *t, *fl) != 0) 819170754Sdelphij return false; 820170754Sdelphij } 821170754Sdelphij else 822170754Sdelphij { 823170754Sdelphij *t = *f; 824170754Sdelphij *tl = *fl; 825170754Sdelphij } 826170754Sdelphij 827170754Sdelphij t++; f++; tl++; fl++; 828170754Sdelphij } 829170754Sdelphij 830170754Sdelphij return true; 831170754Sdelphij} 832170754Sdelphij 833170754Sdelphij/* Create a diff3_block, with ranges as specified in the arguments. 834170754Sdelphij Allocate the arrays for the various pointers (and zero them) based 835170754Sdelphij on the arguments passed. Return the block as a result. */ 836170754Sdelphij 837170754Sdelphijstatic struct diff3_block * 838170754Sdelphijcreate_diff3_block (lin low0, lin high0, 839170754Sdelphij lin low1, lin high1, 840170754Sdelphij lin low2, lin high2) 841170754Sdelphij{ 842170754Sdelphij struct diff3_block *result = xmalloc (sizeof *result); 843170754Sdelphij lin numlines; 844170754Sdelphij 845170754Sdelphij D3_TYPE (result) = ERROR; 846170754Sdelphij D_NEXT (result) = 0; 847170754Sdelphij 848170754Sdelphij /* Assign ranges */ 849170754Sdelphij D_LOWLINE (result, FILE0) = low0; 850170754Sdelphij D_HIGHLINE (result, FILE0) = high0; 851170754Sdelphij D_LOWLINE (result, FILE1) = low1; 852170754Sdelphij D_HIGHLINE (result, FILE1) = high1; 853170754Sdelphij D_LOWLINE (result, FILE2) = low2; 854170754Sdelphij D_HIGHLINE (result, FILE2) = high2; 855170754Sdelphij 856170754Sdelphij /* Allocate and zero space */ 857170754Sdelphij numlines = D_NUMLINES (result, FILE0); 858170754Sdelphij if (numlines) 859170754Sdelphij { 860170754Sdelphij D_LINEARRAY (result, FILE0) = xcalloc (numlines, sizeof (char *)); 861170754Sdelphij D_LENARRAY (result, FILE0) = xcalloc (numlines, sizeof (size_t)); 862170754Sdelphij } 863170754Sdelphij else 864170754Sdelphij { 865170754Sdelphij D_LINEARRAY (result, FILE0) = 0; 866170754Sdelphij D_LENARRAY (result, FILE0) = 0; 867170754Sdelphij } 868170754Sdelphij 869170754Sdelphij numlines = D_NUMLINES (result, FILE1); 870170754Sdelphij if (numlines) 871170754Sdelphij { 872170754Sdelphij D_LINEARRAY (result, FILE1) = xcalloc (numlines, sizeof (char *)); 873170754Sdelphij D_LENARRAY (result, FILE1) = xcalloc (numlines, sizeof (size_t)); 874170754Sdelphij } 875170754Sdelphij else 876170754Sdelphij { 877170754Sdelphij D_LINEARRAY (result, FILE1) = 0; 878170754Sdelphij D_LENARRAY (result, FILE1) = 0; 879170754Sdelphij } 880170754Sdelphij 881170754Sdelphij numlines = D_NUMLINES (result, FILE2); 882170754Sdelphij if (numlines) 883170754Sdelphij { 884170754Sdelphij D_LINEARRAY (result, FILE2) = xcalloc (numlines, sizeof (char *)); 885170754Sdelphij D_LENARRAY (result, FILE2) = xcalloc (numlines, sizeof (size_t)); 886170754Sdelphij } 887170754Sdelphij else 888170754Sdelphij { 889170754Sdelphij D_LINEARRAY (result, FILE2) = 0; 890170754Sdelphij D_LENARRAY (result, FILE2) = 0; 891170754Sdelphij } 892170754Sdelphij 893170754Sdelphij /* Return */ 894170754Sdelphij return result; 895170754Sdelphij} 896170754Sdelphij 897170754Sdelphij/* Compare two lists of lines of text. 898170754Sdelphij Return 1 if they are equivalent, 0 if not. */ 899170754Sdelphij 900170754Sdelphijstatic bool 901170754Sdelphijcompare_line_list (char * const list1[], size_t const lengths1[], 902170754Sdelphij char * const list2[], size_t const lengths2[], 903170754Sdelphij lin nl) 904170754Sdelphij{ 905170754Sdelphij char * const *l1 = list1; 906170754Sdelphij char * const *l2 = list2; 907170754Sdelphij size_t const *lgths1 = lengths1; 908170754Sdelphij size_t const *lgths2 = lengths2; 909170754Sdelphij 910170754Sdelphij while (nl--) 911170754Sdelphij if (!*l1 || !*l2 || *lgths1 != *lgths2++ 912170754Sdelphij || memcmp (*l1++, *l2++, *lgths1++) != 0) 913170754Sdelphij return false; 914170754Sdelphij return true; 915170754Sdelphij} 916170754Sdelphij 917170754Sdelphij/* Input and parse two way diffs. */ 918170754Sdelphij 919170754Sdelphijstatic struct diff_block * 920170754Sdelphijprocess_diff (char const *filea, 921170754Sdelphij char const *fileb, 922170754Sdelphij struct diff_block **last_block) 923170754Sdelphij{ 924170754Sdelphij char *diff_contents; 925170754Sdelphij char *diff_limit; 926170754Sdelphij char *scan_diff; 927170754Sdelphij enum diff_type dt; 928170754Sdelphij lin i; 929170754Sdelphij struct diff_block *block_list, **block_list_end, *bptr; 930170754Sdelphij size_t too_many_lines = (PTRDIFF_MAX 931170754Sdelphij / MIN (sizeof *bptr->lines[1], 932170754Sdelphij sizeof *bptr->lengths[1])); 933170754Sdelphij 934170754Sdelphij diff_limit = read_diff (filea, fileb, &diff_contents); 935170754Sdelphij scan_diff = diff_contents; 936170754Sdelphij block_list_end = &block_list; 937170754Sdelphij bptr = 0; /* Pacify `gcc -W'. */ 938170754Sdelphij 939170754Sdelphij while (scan_diff < diff_limit) 940170754Sdelphij { 941170754Sdelphij bptr = xmalloc (sizeof *bptr); 942170754Sdelphij bptr->lines[0] = bptr->lines[1] = 0; 943170754Sdelphij bptr->lengths[0] = bptr->lengths[1] = 0; 944170754Sdelphij 945170754Sdelphij dt = process_diff_control (&scan_diff, bptr); 946170754Sdelphij if (dt == ERROR || *scan_diff != '\n') 947170754Sdelphij { 948170754Sdelphij fprintf (stderr, _("%s: diff failed: "), program_name); 949170754Sdelphij do 950170754Sdelphij { 951170754Sdelphij putc (*scan_diff, stderr); 952170754Sdelphij } 953170754Sdelphij while (*scan_diff++ != '\n'); 954170754Sdelphij exit (EXIT_TROUBLE); 955170754Sdelphij } 956170754Sdelphij scan_diff++; 957170754Sdelphij 958170754Sdelphij /* Force appropriate ranges to be null, if necessary */ 959170754Sdelphij switch (dt) 960170754Sdelphij { 961170754Sdelphij case ADD: 962170754Sdelphij bptr->ranges[0][0]++; 963170754Sdelphij break; 964170754Sdelphij case DELETE: 965170754Sdelphij bptr->ranges[1][0]++; 966170754Sdelphij break; 967170754Sdelphij case CHANGE: 968170754Sdelphij break; 969170754Sdelphij default: 970170754Sdelphij fatal ("internal error: invalid diff type in process_diff"); 971170754Sdelphij break; 972170754Sdelphij } 973170754Sdelphij 974170754Sdelphij /* Allocate space for the pointers for the lines from filea, and 975170754Sdelphij parcel them out among these pointers */ 976170754Sdelphij if (dt != ADD) 977170754Sdelphij { 978170754Sdelphij lin numlines = D_NUMLINES (bptr, 0); 979170754Sdelphij if (too_many_lines <= numlines) 980170754Sdelphij xalloc_die (); 981170754Sdelphij bptr->lines[0] = xmalloc (numlines * sizeof *bptr->lines[0]); 982170754Sdelphij bptr->lengths[0] = xmalloc (numlines * sizeof *bptr->lengths[0]); 983170754Sdelphij for (i = 0; i < numlines; i++) 984170754Sdelphij scan_diff = scan_diff_line (scan_diff, 985170754Sdelphij &(bptr->lines[0][i]), 986170754Sdelphij &(bptr->lengths[0][i]), 987170754Sdelphij diff_limit, 988170754Sdelphij '<'); 989170754Sdelphij } 990170754Sdelphij 991170754Sdelphij /* Get past the separator for changes */ 992170754Sdelphij if (dt == CHANGE) 993170754Sdelphij { 994170754Sdelphij if (strncmp (scan_diff, "---\n", 4)) 995170754Sdelphij fatal ("invalid diff format; invalid change separator"); 996170754Sdelphij scan_diff += 4; 997170754Sdelphij } 998170754Sdelphij 999170754Sdelphij /* Allocate space for the pointers for the lines from fileb, and 1000170754Sdelphij parcel them out among these pointers */ 1001170754Sdelphij if (dt != DELETE) 1002170754Sdelphij { 1003170754Sdelphij lin numlines = D_NUMLINES (bptr, 1); 1004170754Sdelphij if (too_many_lines <= numlines) 1005170754Sdelphij xalloc_die (); 1006170754Sdelphij bptr->lines[1] = xmalloc (numlines * sizeof *bptr->lines[1]); 1007170754Sdelphij bptr->lengths[1] = xmalloc (numlines * sizeof *bptr->lengths[1]); 1008170754Sdelphij for (i = 0; i < numlines; i++) 1009170754Sdelphij scan_diff = scan_diff_line (scan_diff, 1010170754Sdelphij &(bptr->lines[1][i]), 1011170754Sdelphij &(bptr->lengths[1][i]), 1012170754Sdelphij diff_limit, 1013170754Sdelphij '>'); 1014170754Sdelphij } 1015170754Sdelphij 1016170754Sdelphij /* Place this block on the blocklist. */ 1017170754Sdelphij *block_list_end = bptr; 1018170754Sdelphij block_list_end = &bptr->next; 1019170754Sdelphij } 1020170754Sdelphij 1021170754Sdelphij *block_list_end = 0; 1022170754Sdelphij *last_block = bptr; 1023170754Sdelphij return block_list; 1024170754Sdelphij} 1025170754Sdelphij 1026170754Sdelphij/* Skip tabs and spaces, and return the first character after them. */ 1027170754Sdelphij 1028170754Sdelphijstatic char * 1029170754Sdelphijskipwhite (char *s) 1030170754Sdelphij{ 1031170754Sdelphij while (*s == ' ' || *s == '\t') 1032170754Sdelphij s++; 1033170754Sdelphij return s; 1034170754Sdelphij} 1035170754Sdelphij 1036170754Sdelphij/* Read a nonnegative line number from S, returning the address of the 1037170754Sdelphij first character after the line number, and storing the number into 1038170754Sdelphij *PNUM. Return 0 if S does not point to a valid line number. */ 1039170754Sdelphij 1040170754Sdelphijstatic char * 1041170754Sdelphijreadnum (char *s, lin *pnum) 1042170754Sdelphij{ 1043170754Sdelphij unsigned char c = *s; 1044170754Sdelphij lin num = 0; 1045170754Sdelphij 1046170754Sdelphij if (! ISDIGIT (c)) 1047170754Sdelphij return 0; 1048170754Sdelphij 1049170754Sdelphij do 1050170754Sdelphij { 1051170754Sdelphij num = c - '0' + num * 10; 1052170754Sdelphij c = *++s; 1053170754Sdelphij } 1054170754Sdelphij while (ISDIGIT (c)); 1055170754Sdelphij 1056170754Sdelphij *pnum = num; 1057170754Sdelphij return s; 1058170754Sdelphij} 1059170754Sdelphij 1060170754Sdelphij/* Parse a normal format diff control string. Return the type of the 1061170754Sdelphij diff (ERROR if the format is bad). All of the other important 1062170754Sdelphij information is filled into to the structure pointed to by db, and 1063170754Sdelphij the string pointer (whose location is passed to this routine) is 1064170754Sdelphij updated to point beyond the end of the string parsed. Note that 1065170754Sdelphij only the ranges in the diff_block will be set by this routine. 1066170754Sdelphij 1067170754Sdelphij If some specific pair of numbers has been reduced to a single 1068170754Sdelphij number, then both corresponding numbers in the diff block are set 1069170754Sdelphij to that number. In general these numbers are interpreted as ranges 1070170754Sdelphij inclusive, unless being used by the ADD or DELETE commands. It is 1071170754Sdelphij assumed that these will be special cased in a superior routine. */ 1072170754Sdelphij 1073170754Sdelphijstatic enum diff_type 1074170754Sdelphijprocess_diff_control (char **string, struct diff_block *db) 1075170754Sdelphij{ 1076170754Sdelphij char *s = *string; 1077170754Sdelphij enum diff_type type; 1078170754Sdelphij 1079170754Sdelphij /* Read first set of digits */ 1080170754Sdelphij s = readnum (skipwhite (s), &db->ranges[0][RANGE_START]); 1081170754Sdelphij if (! s) 1082170754Sdelphij return ERROR; 1083170754Sdelphij 1084170754Sdelphij /* Was that the only digit? */ 1085170754Sdelphij s = skipwhite (s); 1086170754Sdelphij if (*s == ',') 1087170754Sdelphij { 1088170754Sdelphij s = readnum (s + 1, &db->ranges[0][RANGE_END]); 1089170754Sdelphij if (! s) 1090170754Sdelphij return ERROR; 1091170754Sdelphij } 1092170754Sdelphij else 1093170754Sdelphij db->ranges[0][RANGE_END] = db->ranges[0][RANGE_START]; 1094170754Sdelphij 1095170754Sdelphij /* Get the letter */ 1096170754Sdelphij s = skipwhite (s); 1097170754Sdelphij switch (*s) 1098170754Sdelphij { 1099170754Sdelphij case 'a': 1100170754Sdelphij type = ADD; 1101170754Sdelphij break; 1102170754Sdelphij case 'c': 1103170754Sdelphij type = CHANGE; 1104170754Sdelphij break; 1105170754Sdelphij case 'd': 1106170754Sdelphij type = DELETE; 1107170754Sdelphij break; 1108170754Sdelphij default: 1109170754Sdelphij return ERROR; /* Bad format */ 1110170754Sdelphij } 1111170754Sdelphij s++; /* Past letter */ 1112170754Sdelphij 1113170754Sdelphij /* Read second set of digits */ 1114170754Sdelphij s = readnum (skipwhite (s), &db->ranges[1][RANGE_START]); 1115170754Sdelphij if (! s) 1116170754Sdelphij return ERROR; 1117170754Sdelphij 1118170754Sdelphij /* Was that the only digit? */ 1119170754Sdelphij s = skipwhite (s); 1120170754Sdelphij if (*s == ',') 1121170754Sdelphij { 1122170754Sdelphij s = readnum (s + 1, &db->ranges[1][RANGE_END]); 1123170754Sdelphij if (! s) 1124170754Sdelphij return ERROR; 1125170754Sdelphij s = skipwhite (s); /* To move to end */ 1126170754Sdelphij } 1127170754Sdelphij else 1128170754Sdelphij db->ranges[1][RANGE_END] = db->ranges[1][RANGE_START]; 1129170754Sdelphij 1130170754Sdelphij *string = s; 1131170754Sdelphij return type; 1132170754Sdelphij} 1133170754Sdelphij 1134170754Sdelphijstatic char * 1135170754Sdelphijread_diff (char const *filea, 1136170754Sdelphij char const *fileb, 1137170754Sdelphij char **output_placement) 1138170754Sdelphij{ 1139170754Sdelphij char *diff_result; 1140170754Sdelphij size_t current_chunk_size, total; 1141170754Sdelphij int fd, wstatus, status; 1142170754Sdelphij int werrno = 0; 1143170754Sdelphij struct stat pipestat; 1144170754Sdelphij 1145170754Sdelphij#if HAVE_WORKING_FORK || HAVE_WORKING_VFORK 1146170754Sdelphij 1147170754Sdelphij char const *argv[9]; 1148170754Sdelphij char const **ap; 1149170754Sdelphij int fds[2]; 1150170754Sdelphij pid_t pid; 1151170754Sdelphij 1152170754Sdelphij ap = argv; 1153170754Sdelphij *ap++ = diff_program; 1154170754Sdelphij if (text) 1155170754Sdelphij *ap++ = "-a"; 1156170754Sdelphij if (strip_trailing_cr) 1157170754Sdelphij *ap++ = "--strip-trailing-cr"; 1158170754Sdelphij *ap++ = "--horizon-lines=100"; 1159170754Sdelphij *ap++ = "--"; 1160170754Sdelphij *ap++ = filea; 1161170754Sdelphij *ap++ = fileb; 1162170754Sdelphij *ap = 0; 1163170754Sdelphij 1164170754Sdelphij if (pipe (fds) != 0) 1165170754Sdelphij perror_with_exit ("pipe"); 1166170754Sdelphij 1167170754Sdelphij pid = vfork (); 1168170754Sdelphij if (pid == 0) 1169170754Sdelphij { 1170170754Sdelphij /* Child */ 1171170754Sdelphij close (fds[0]); 1172170754Sdelphij if (fds[1] != STDOUT_FILENO) 1173170754Sdelphij { 1174170754Sdelphij dup2 (fds[1], STDOUT_FILENO); 1175170754Sdelphij close (fds[1]); 1176170754Sdelphij } 1177170754Sdelphij 1178170754Sdelphij /* The cast to (char **) is needed for portability to older 1179170754Sdelphij hosts with a nonstandard prototype for execvp. */ 1180170754Sdelphij execvp (diff_program, (char **) argv); 1181170754Sdelphij 1182170754Sdelphij _exit (errno == ENOENT ? 127 : 126); 1183170754Sdelphij } 1184170754Sdelphij 1185170754Sdelphij if (pid == -1) 1186170754Sdelphij perror_with_exit ("fork"); 1187170754Sdelphij 1188170754Sdelphij close (fds[1]); /* Prevent erroneous lack of EOF */ 1189170754Sdelphij fd = fds[0]; 1190170754Sdelphij 1191170754Sdelphij#else 1192170754Sdelphij 1193170754Sdelphij FILE *fpipe; 1194170754Sdelphij char const args[] = " --horizon-lines=100 -- "; 1195170754Sdelphij char *command = xmalloc (quote_system_arg (0, diff_program) 1196170754Sdelphij + sizeof "-a" 1197170754Sdelphij + sizeof "--strip-trailing-cr" 1198170754Sdelphij + sizeof args - 1 1199170754Sdelphij + quote_system_arg (0, filea) + 1 1200170754Sdelphij + quote_system_arg (0, fileb) + 1); 1201170754Sdelphij char *p = command; 1202170754Sdelphij p += quote_system_arg (p, diff_program); 1203170754Sdelphij if (text) 1204170754Sdelphij { 1205170754Sdelphij strcpy (p, " -a"); 1206170754Sdelphij p += 3; 1207170754Sdelphij } 1208170754Sdelphij if (strip_trailing_cr) 1209170754Sdelphij { 1210170754Sdelphij strcpy (p, " --strip-trailing-cr"); 1211170754Sdelphij p += 20; 1212170754Sdelphij } 1213170754Sdelphij strcpy (p, args); 1214170754Sdelphij p += sizeof args - 1; 1215170754Sdelphij p += quote_system_arg (p, filea); 1216170754Sdelphij *p++ = ' '; 1217170754Sdelphij p += quote_system_arg (p, fileb); 1218170754Sdelphij *p = 0; 1219170754Sdelphij errno = 0; 1220170754Sdelphij fpipe = popen (command, "r"); 1221170754Sdelphij if (!fpipe) 1222170754Sdelphij perror_with_exit (command); 1223170754Sdelphij free (command); 1224170754Sdelphij fd = fileno (fpipe); 1225170754Sdelphij 1226170754Sdelphij#endif 1227170754Sdelphij 1228170754Sdelphij if (fstat (fd, &pipestat) != 0) 1229170754Sdelphij perror_with_exit ("fstat"); 1230170754Sdelphij current_chunk_size = MAX (1, STAT_BLOCKSIZE (pipestat)); 1231170754Sdelphij diff_result = xmalloc (current_chunk_size); 1232170754Sdelphij total = 0; 1233170754Sdelphij 1234170754Sdelphij for (;;) 1235170754Sdelphij { 1236170754Sdelphij size_t bytes_to_read = current_chunk_size - total; 1237170754Sdelphij size_t bytes = block_read (fd, diff_result + total, bytes_to_read); 1238170754Sdelphij total += bytes; 1239170754Sdelphij if (bytes != bytes_to_read) 1240170754Sdelphij { 1241170754Sdelphij if (bytes == SIZE_MAX) 1242170754Sdelphij perror_with_exit (_("read failed")); 1243170754Sdelphij break; 1244170754Sdelphij } 1245170754Sdelphij if (PTRDIFF_MAX / 2 <= current_chunk_size) 1246170754Sdelphij xalloc_die (); 1247170754Sdelphij current_chunk_size *= 2; 1248170754Sdelphij diff_result = xrealloc (diff_result, current_chunk_size); 1249170754Sdelphij } 1250170754Sdelphij 1251170754Sdelphij if (total != 0 && diff_result[total-1] != '\n') 1252170754Sdelphij fatal ("invalid diff format; incomplete last line"); 1253170754Sdelphij 1254170754Sdelphij *output_placement = diff_result; 1255170754Sdelphij 1256170754Sdelphij#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK) 1257170754Sdelphij 1258170754Sdelphij wstatus = pclose (fpipe); 1259170754Sdelphij if (wstatus == -1) 1260170754Sdelphij werrno = errno; 1261170754Sdelphij 1262170754Sdelphij#else 1263170754Sdelphij 1264170754Sdelphij if (close (fd) != 0) 1265170754Sdelphij perror_with_exit ("close"); 1266170754Sdelphij if (waitpid (pid, &wstatus, 0) < 0) 1267170754Sdelphij perror_with_exit ("waitpid"); 1268170754Sdelphij 1269170754Sdelphij#endif 1270170754Sdelphij 1271170754Sdelphij status = ! werrno && WIFEXITED (wstatus) ? WEXITSTATUS (wstatus) : INT_MAX; 1272170754Sdelphij 1273170754Sdelphij if (EXIT_TROUBLE <= status) 1274170754Sdelphij error (EXIT_TROUBLE, werrno, 1275170754Sdelphij _(status == 126 1276170754Sdelphij ? "subsidiary program `%s' could not be invoked" 1277170754Sdelphij : status == 127 1278170754Sdelphij ? "subsidiary program `%s' not found" 1279170754Sdelphij : status == INT_MAX 1280170754Sdelphij ? "subsidiary program `%s' failed" 1281170754Sdelphij : "subsidiary program `%s' failed (exit status %d)"), 1282170754Sdelphij diff_program, status); 1283170754Sdelphij 1284170754Sdelphij return diff_result + total; 1285170754Sdelphij} 1286170754Sdelphij 1287170754Sdelphij 1288170754Sdelphij/* Scan a regular diff line (consisting of > or <, followed by a 1289170754Sdelphij space, followed by text (including nulls) up to a newline. 1290170754Sdelphij 1291170754Sdelphij This next routine began life as a macro and many parameters in it 1292170754Sdelphij are used as call-by-reference values. */ 1293170754Sdelphijstatic char * 1294170754Sdelphijscan_diff_line (char *scan_ptr, char **set_start, size_t *set_length, 1295170754Sdelphij char *limit, char leadingchar) 1296170754Sdelphij{ 1297170754Sdelphij char *line_ptr; 1298170754Sdelphij 1299170754Sdelphij if (!(scan_ptr[0] == leadingchar 1300170754Sdelphij && scan_ptr[1] == ' ')) 1301170754Sdelphij fatal ("invalid diff format; incorrect leading line chars"); 1302170754Sdelphij 1303170754Sdelphij *set_start = line_ptr = scan_ptr + 2; 1304170754Sdelphij while (*line_ptr++ != '\n') 1305170754Sdelphij continue; 1306170754Sdelphij 1307170754Sdelphij /* Include newline if the original line ended in a newline, 1308170754Sdelphij or if an edit script is being generated. 1309170754Sdelphij Copy any missing newline message to stderr if an edit script is being 1310170754Sdelphij generated, because edit scripts cannot handle missing newlines. 1311170754Sdelphij Return the beginning of the next line. */ 1312170754Sdelphij *set_length = line_ptr - *set_start; 1313170754Sdelphij if (line_ptr < limit && *line_ptr == '\\') 1314170754Sdelphij { 1315170754Sdelphij if (edscript) 1316170754Sdelphij fprintf (stderr, "%s:", program_name); 1317170754Sdelphij else 1318170754Sdelphij --*set_length; 1319170754Sdelphij line_ptr++; 1320170754Sdelphij do 1321170754Sdelphij { 1322170754Sdelphij if (edscript) 1323170754Sdelphij putc (*line_ptr, stderr); 1324170754Sdelphij } 1325170754Sdelphij while (*line_ptr++ != '\n'); 1326170754Sdelphij } 1327170754Sdelphij 1328170754Sdelphij return line_ptr; 1329170754Sdelphij} 1330170754Sdelphij 1331170754Sdelphij/* Output a three way diff passed as a list of diff3_block's. The 1332170754Sdelphij argument MAPPING is indexed by external file number (in the 1333170754Sdelphij argument list) and contains the internal file number (from the diff 1334170754Sdelphij passed). This is important because the user expects outputs in 1335170754Sdelphij terms of the argument list number, and the diff passed may have 1336170754Sdelphij been done slightly differently (if the last argument was "-", for 1337170754Sdelphij example). REV_MAPPING is the inverse of MAPPING. */ 1338170754Sdelphij 1339170754Sdelphijstatic void 1340170754Sdelphijoutput_diff3 (FILE *outputfile, struct diff3_block *diff, 1341170754Sdelphij int const mapping[3], int const rev_mapping[3]) 1342170754Sdelphij{ 1343170754Sdelphij int i; 1344170754Sdelphij int oddoneout; 1345170754Sdelphij char *cp; 1346170754Sdelphij struct diff3_block *ptr; 1347170754Sdelphij lin line; 1348170754Sdelphij size_t length; 1349170754Sdelphij int dontprint; 1350170754Sdelphij static int skew_increment[3] = { 2, 3, 1 }; /* 0==>2==>1==>3 */ 1351170754Sdelphij char const *line_prefix = initial_tab ? "\t" : " "; 1352170754Sdelphij 1353170754Sdelphij for (ptr = diff; ptr; ptr = D_NEXT (ptr)) 1354170754Sdelphij { 1355170754Sdelphij char x[2]; 1356170754Sdelphij 1357170754Sdelphij switch (ptr->correspond) 1358170754Sdelphij { 1359170754Sdelphij case DIFF_ALL: 1360170754Sdelphij x[0] = 0; 1361170754Sdelphij dontprint = 3; /* Print them all */ 1362170754Sdelphij oddoneout = 3; /* Nobody's odder than anyone else */ 1363170754Sdelphij break; 1364170754Sdelphij case DIFF_1ST: 1365170754Sdelphij case DIFF_2ND: 1366170754Sdelphij case DIFF_3RD: 1367170754Sdelphij oddoneout = rev_mapping[ptr->correspond - DIFF_1ST]; 1368170754Sdelphij 1369170754Sdelphij x[0] = oddoneout + '1'; 1370170754Sdelphij x[1] = 0; 1371170754Sdelphij dontprint = oddoneout == 0; 1372170754Sdelphij break; 1373170754Sdelphij default: 1374170754Sdelphij fatal ("internal error: invalid diff type passed to output"); 1375170754Sdelphij } 1376170754Sdelphij fprintf (outputfile, "====%s\n", x); 1377170754Sdelphij 1378170754Sdelphij /* Go 0, 2, 1 if the first and third outputs are equivalent. */ 1379170754Sdelphij for (i = 0; i < 3; 1380170754Sdelphij i = (oddoneout == 1 ? skew_increment[i] : i + 1)) 1381170754Sdelphij { 1382170754Sdelphij int realfile = mapping[i]; 1383170754Sdelphij lin lowt = D_LOWLINE (ptr, realfile); 1384170754Sdelphij lin hight = D_HIGHLINE (ptr, realfile); 1385170754Sdelphij long int llowt = lowt; 1386170754Sdelphij long int lhight = hight; 1387170754Sdelphij 1388170754Sdelphij fprintf (outputfile, "%d:", i + 1); 1389170754Sdelphij switch (lowt - hight) 1390170754Sdelphij { 1391170754Sdelphij case 1: 1392170754Sdelphij fprintf (outputfile, "%lda\n", llowt - 1); 1393170754Sdelphij break; 1394170754Sdelphij case 0: 1395170754Sdelphij fprintf (outputfile, "%ldc\n", llowt); 1396170754Sdelphij break; 1397170754Sdelphij default: 1398170754Sdelphij fprintf (outputfile, "%ld,%ldc\n", llowt, lhight); 1399170754Sdelphij break; 1400170754Sdelphij } 1401170754Sdelphij 1402170754Sdelphij if (i == dontprint) continue; 1403170754Sdelphij 1404170754Sdelphij if (lowt <= hight) 1405170754Sdelphij { 1406170754Sdelphij line = 0; 1407170754Sdelphij do 1408170754Sdelphij { 1409170754Sdelphij fprintf (outputfile, line_prefix); 1410170754Sdelphij cp = D_RELNUM (ptr, realfile, line); 1411170754Sdelphij length = D_RELLEN (ptr, realfile, line); 1412170754Sdelphij fwrite (cp, sizeof (char), length, outputfile); 1413170754Sdelphij } 1414170754Sdelphij while (++line < hight - lowt + 1); 1415170754Sdelphij if (cp[length - 1] != '\n') 1416170754Sdelphij fprintf (outputfile, "\n\\ %s\n", 1417170754Sdelphij _("No newline at end of file")); 1418170754Sdelphij } 1419170754Sdelphij } 1420170754Sdelphij } 1421170754Sdelphij} 1422170754Sdelphij 1423170754Sdelphij 1424170754Sdelphij/* Output to OUTPUTFILE the lines of B taken from FILENUM. Double any 1425170754Sdelphij initial '.'s; yield nonzero if any initial '.'s were doubled. */ 1426170754Sdelphij 1427170754Sdelphijstatic bool 1428170754Sdelphijdotlines (FILE *outputfile, struct diff3_block *b, int filenum) 1429170754Sdelphij{ 1430170754Sdelphij lin i; 1431170754Sdelphij bool leading_dot = false; 1432170754Sdelphij 1433170754Sdelphij for (i = 0; 1434170754Sdelphij i < D_NUMLINES (b, filenum); 1435170754Sdelphij i++) 1436170754Sdelphij { 1437170754Sdelphij char *line = D_RELNUM (b, filenum, i); 1438170754Sdelphij if (line[0] == '.') 1439170754Sdelphij { 1440170754Sdelphij leading_dot = true; 1441170754Sdelphij fprintf (outputfile, "."); 1442170754Sdelphij } 1443170754Sdelphij fwrite (line, sizeof (char), 1444170754Sdelphij D_RELLEN (b, filenum, i), outputfile); 1445170754Sdelphij } 1446170754Sdelphij 1447170754Sdelphij return leading_dot; 1448170754Sdelphij} 1449170754Sdelphij 1450170754Sdelphij/* Output to OUTPUTFILE a '.' line. If LEADING_DOT is true, also 1451170754Sdelphij output a command that removes initial '.'s starting with line START 1452170754Sdelphij and continuing for NUM lines. (START is long int, not lin, for 1453170754Sdelphij convenience with printf %ld formats.) */ 1454170754Sdelphij 1455170754Sdelphijstatic void 1456170754Sdelphijundotlines (FILE *outputfile, bool leading_dot, long int start, lin num) 1457170754Sdelphij{ 1458170754Sdelphij fprintf (outputfile, ".\n"); 1459170754Sdelphij if (leading_dot) 1460170754Sdelphij { 1461170754Sdelphij if (num == 1) 1462170754Sdelphij fprintf (outputfile, "%lds/^\\.//\n", start); 1463170754Sdelphij else 1464170754Sdelphij fprintf (outputfile, "%ld,%lds/^\\.//\n", start, start + num - 1); 1465170754Sdelphij } 1466170754Sdelphij} 1467170754Sdelphij 1468170754Sdelphij/* Output a diff3 set of blocks as an ed script. This script applies 1469170754Sdelphij the changes between file's 2 & 3 to file 1. Take the precise 1470170754Sdelphij format of the ed script to be output from global variables set 1471170754Sdelphij during options processing. Reverse the order of 1472170754Sdelphij the set of diff3 blocks in DIFF; this gets 1473170754Sdelphij around the problems involved with changing line numbers in an ed 1474170754Sdelphij script. 1475170754Sdelphij 1476170754Sdelphij As in `output_diff3', the variable MAPPING maps from file number 1477170754Sdelphij according to the argument list to file number according to the diff 1478170754Sdelphij passed. All files listed below are in terms of the argument list. 1479170754Sdelphij REV_MAPPING is the inverse of MAPPING. 1480170754Sdelphij 1481170754Sdelphij FILE0, FILE1 and FILE2 are the strings to print as the names of the 1482170754Sdelphij three files. These may be the actual names, or may be the 1483170754Sdelphij arguments specified with -L. 1484170754Sdelphij 1485170754Sdelphij Return 1 if conflicts were found. */ 1486170754Sdelphij 1487170754Sdelphijstatic bool 1488170754Sdelphijoutput_diff3_edscript (FILE *outputfile, struct diff3_block *diff, 1489170754Sdelphij int const mapping[3], int const rev_mapping[3], 1490170754Sdelphij char const *file0, char const *file1, char const *file2) 1491170754Sdelphij{ 1492170754Sdelphij bool leading_dot; 1493170754Sdelphij bool conflicts_found = false; 1494170754Sdelphij bool conflict; 1495170754Sdelphij struct diff3_block *b; 1496170754Sdelphij 1497170754Sdelphij for (b = reverse_diff3_blocklist (diff); b; b = b->next) 1498170754Sdelphij { 1499170754Sdelphij /* Must do mapping correctly. */ 1500170754Sdelphij enum diff_type type 1501170754Sdelphij = (b->correspond == DIFF_ALL 1502170754Sdelphij ? DIFF_ALL 1503170754Sdelphij : DIFF_1ST + rev_mapping[b->correspond - DIFF_1ST]); 1504170754Sdelphij 1505170754Sdelphij long int low0, high0; 1506170754Sdelphij 1507170754Sdelphij /* If we aren't supposed to do this output block, skip it. */ 1508170754Sdelphij switch (type) 1509170754Sdelphij { 1510170754Sdelphij default: continue; 1511170754Sdelphij case DIFF_2ND: if (!show_2nd) continue; conflict = true; break; 1512170754Sdelphij case DIFF_3RD: if (overlap_only) continue; conflict = false; break; 1513170754Sdelphij case DIFF_ALL: if (simple_only) continue; conflict = flagging; break; 1514170754Sdelphij } 1515170754Sdelphij 1516170754Sdelphij low0 = D_LOWLINE (b, mapping[FILE0]); 1517170754Sdelphij high0 = D_HIGHLINE (b, mapping[FILE0]); 1518170754Sdelphij 1519170754Sdelphij if (conflict) 1520170754Sdelphij { 1521170754Sdelphij conflicts_found = true; 1522170754Sdelphij 1523170754Sdelphij 1524170754Sdelphij /* Mark end of conflict. */ 1525170754Sdelphij 1526170754Sdelphij fprintf (outputfile, "%lda\n", high0); 1527170754Sdelphij leading_dot = false; 1528170754Sdelphij if (type == DIFF_ALL) 1529170754Sdelphij { 1530170754Sdelphij if (show_2nd) 1531170754Sdelphij { 1532170754Sdelphij /* Append lines from FILE1. */ 1533170754Sdelphij fprintf (outputfile, "||||||| %s\n", file1); 1534170754Sdelphij leading_dot = dotlines (outputfile, b, mapping[FILE1]); 1535170754Sdelphij } 1536170754Sdelphij /* Append lines from FILE2. */ 1537170754Sdelphij fprintf (outputfile, "=======\n"); 1538170754Sdelphij leading_dot |= dotlines (outputfile, b, mapping[FILE2]); 1539170754Sdelphij } 1540170754Sdelphij fprintf (outputfile, ">>>>>>> %s\n", file2); 1541170754Sdelphij undotlines (outputfile, leading_dot, high0 + 2, 1542170754Sdelphij (D_NUMLINES (b, mapping[FILE1]) 1543170754Sdelphij + D_NUMLINES (b, mapping[FILE2]) + 1)); 1544170754Sdelphij 1545170754Sdelphij 1546170754Sdelphij /* Mark start of conflict. */ 1547170754Sdelphij 1548170754Sdelphij fprintf (outputfile, "%lda\n<<<<<<< %s\n", low0 - 1, 1549170754Sdelphij type == DIFF_ALL ? file0 : file1); 1550170754Sdelphij leading_dot = false; 1551170754Sdelphij if (type == DIFF_2ND) 1552170754Sdelphij { 1553170754Sdelphij /* Prepend lines from FILE1. */ 1554170754Sdelphij leading_dot = dotlines (outputfile, b, mapping[FILE1]); 1555170754Sdelphij fprintf (outputfile, "=======\n"); 1556170754Sdelphij } 1557170754Sdelphij undotlines (outputfile, leading_dot, low0 + 1, 1558170754Sdelphij D_NUMLINES (b, mapping[FILE1])); 1559170754Sdelphij } 1560170754Sdelphij else if (D_NUMLINES (b, mapping[FILE2]) == 0) 1561170754Sdelphij /* Write out a delete */ 1562170754Sdelphij { 1563170754Sdelphij if (low0 == high0) 1564170754Sdelphij fprintf (outputfile, "%ldd\n", low0); 1565170754Sdelphij else 1566170754Sdelphij fprintf (outputfile, "%ld,%ldd\n", low0, high0); 1567170754Sdelphij } 1568170754Sdelphij else 1569170754Sdelphij /* Write out an add or change */ 1570170754Sdelphij { 1571170754Sdelphij switch (high0 - low0) 1572170754Sdelphij { 1573170754Sdelphij case -1: 1574170754Sdelphij fprintf (outputfile, "%lda\n", high0); 1575170754Sdelphij break; 1576170754Sdelphij case 0: 1577170754Sdelphij fprintf (outputfile, "%ldc\n", high0); 1578170754Sdelphij break; 1579170754Sdelphij default: 1580170754Sdelphij fprintf (outputfile, "%ld,%ldc\n", low0, high0); 1581170754Sdelphij break; 1582170754Sdelphij } 1583170754Sdelphij 1584170754Sdelphij undotlines (outputfile, dotlines (outputfile, b, mapping[FILE2]), 1585170754Sdelphij low0, D_NUMLINES (b, mapping[FILE2])); 1586170754Sdelphij } 1587170754Sdelphij } 1588170754Sdelphij if (finalwrite) fprintf (outputfile, "w\nq\n"); 1589170754Sdelphij return conflicts_found; 1590170754Sdelphij} 1591170754Sdelphij 1592170754Sdelphij/* Read from INFILE and output to OUTPUTFILE a set of diff3_blocks 1593170754Sdelphij DIFF as a merged file. This acts like 'ed file0 1594170754Sdelphij <[output_diff3_edscript]', except that it works even for binary 1595170754Sdelphij data or incomplete lines. 1596170754Sdelphij 1597170754Sdelphij As before, MAPPING maps from arg list file number to diff file 1598170754Sdelphij number, REV_MAPPING is its inverse, and FILE0, FILE1, and FILE2 are 1599170754Sdelphij the names of the files. 1600170754Sdelphij 1601170754Sdelphij Return 1 if conflicts were found. */ 1602170754Sdelphij 1603170754Sdelphijstatic bool 1604170754Sdelphijoutput_diff3_merge (FILE *infile, FILE *outputfile, struct diff3_block *diff, 1605170754Sdelphij int const mapping[3], int const rev_mapping[3], 1606170754Sdelphij char const *file0, char const *file1, char const *file2) 1607170754Sdelphij{ 1608170754Sdelphij int c; 1609170754Sdelphij lin i; 1610170754Sdelphij bool conflicts_found = false; 1611170754Sdelphij bool conflict; 1612170754Sdelphij struct diff3_block *b; 1613170754Sdelphij lin linesread = 0; 1614170754Sdelphij 1615170754Sdelphij for (b = diff; b; b = b->next) 1616170754Sdelphij { 1617170754Sdelphij /* Must do mapping correctly. */ 1618170754Sdelphij enum diff_type type 1619170754Sdelphij = ((b->correspond == DIFF_ALL) 1620170754Sdelphij ? DIFF_ALL 1621170754Sdelphij : DIFF_1ST + rev_mapping[b->correspond - DIFF_1ST]); 1622170754Sdelphij char const *format_2nd = "<<<<<<< %s\n"; 1623170754Sdelphij 1624170754Sdelphij /* If we aren't supposed to do this output block, skip it. */ 1625170754Sdelphij switch (type) 1626170754Sdelphij { 1627170754Sdelphij default: continue; 1628170754Sdelphij case DIFF_2ND: if (!show_2nd) continue; conflict = true; break; 1629170754Sdelphij case DIFF_3RD: if (overlap_only) continue; conflict = false; break; 1630170754Sdelphij case DIFF_ALL: if (simple_only) continue; conflict = flagging; 1631170754Sdelphij format_2nd = "||||||| %s\n"; 1632170754Sdelphij break; 1633170754Sdelphij } 1634170754Sdelphij 1635170754Sdelphij /* Copy I lines from file 0. */ 1636170754Sdelphij i = D_LOWLINE (b, FILE0) - linesread - 1; 1637170754Sdelphij linesread += i; 1638170754Sdelphij while (0 <= --i) 1639170754Sdelphij do 1640170754Sdelphij { 1641170754Sdelphij c = getc (infile); 1642170754Sdelphij if (c == EOF) 1643170754Sdelphij { 1644170754Sdelphij if (ferror (infile)) 1645170754Sdelphij perror_with_exit (_("read failed")); 1646170754Sdelphij else if (feof (infile)) 1647170754Sdelphij fatal ("input file shrank"); 1648170754Sdelphij } 1649170754Sdelphij putc (c, outputfile); 1650170754Sdelphij } 1651170754Sdelphij while (c != '\n'); 1652170754Sdelphij 1653170754Sdelphij if (conflict) 1654170754Sdelphij { 1655170754Sdelphij conflicts_found = true; 1656170754Sdelphij 1657170754Sdelphij if (type == DIFF_ALL) 1658170754Sdelphij { 1659170754Sdelphij /* Put in lines from FILE0 with bracket. */ 1660170754Sdelphij fprintf (outputfile, "<<<<<<< %s\n", file0); 1661170754Sdelphij for (i = 0; 1662170754Sdelphij i < D_NUMLINES (b, mapping[FILE0]); 1663170754Sdelphij i++) 1664170754Sdelphij fwrite (D_RELNUM (b, mapping[FILE0], i), sizeof (char), 1665170754Sdelphij D_RELLEN (b, mapping[FILE0], i), outputfile); 1666170754Sdelphij } 1667170754Sdelphij 1668170754Sdelphij if (show_2nd) 1669170754Sdelphij { 1670170754Sdelphij /* Put in lines from FILE1 with bracket. */ 1671170754Sdelphij fprintf (outputfile, format_2nd, file1); 1672170754Sdelphij for (i = 0; 1673170754Sdelphij i < D_NUMLINES (b, mapping[FILE1]); 1674170754Sdelphij i++) 1675170754Sdelphij fwrite (D_RELNUM (b, mapping[FILE1], i), sizeof (char), 1676170754Sdelphij D_RELLEN (b, mapping[FILE1], i), outputfile); 1677170754Sdelphij } 1678170754Sdelphij 1679170754Sdelphij fprintf (outputfile, "=======\n"); 1680170754Sdelphij } 1681170754Sdelphij 1682170754Sdelphij /* Put in lines from FILE2. */ 1683170754Sdelphij for (i = 0; 1684170754Sdelphij i < D_NUMLINES (b, mapping[FILE2]); 1685170754Sdelphij i++) 1686170754Sdelphij fwrite (D_RELNUM (b, mapping[FILE2], i), sizeof (char), 1687170754Sdelphij D_RELLEN (b, mapping[FILE2], i), outputfile); 1688170754Sdelphij 1689170754Sdelphij if (conflict) 1690170754Sdelphij fprintf (outputfile, ">>>>>>> %s\n", file2); 1691170754Sdelphij 1692170754Sdelphij /* Skip I lines in file 0. */ 1693170754Sdelphij i = D_NUMLINES (b, FILE0); 1694170754Sdelphij linesread += i; 1695170754Sdelphij while (0 <= --i) 1696170754Sdelphij while ((c = getc (infile)) != '\n') 1697170754Sdelphij if (c == EOF) 1698170754Sdelphij { 1699170754Sdelphij if (ferror (infile)) 1700170754Sdelphij perror_with_exit (_("read failed")); 1701170754Sdelphij else if (feof (infile)) 1702170754Sdelphij { 1703170754Sdelphij if (i || b->next) 1704170754Sdelphij fatal ("input file shrank"); 1705170754Sdelphij return conflicts_found; 1706170754Sdelphij } 1707170754Sdelphij } 1708170754Sdelphij } 1709170754Sdelphij /* Copy rest of common file. */ 1710170754Sdelphij while ((c = getc (infile)) != EOF || !(ferror (infile) | feof (infile))) 1711170754Sdelphij putc (c, outputfile); 1712170754Sdelphij return conflicts_found; 1713170754Sdelphij} 1714170754Sdelphij 1715170754Sdelphij/* Reverse the order of the list of diff3 blocks. */ 1716170754Sdelphij 1717170754Sdelphijstatic struct diff3_block * 1718170754Sdelphijreverse_diff3_blocklist (struct diff3_block *diff) 1719170754Sdelphij{ 1720170754Sdelphij register struct diff3_block *tmp, *next, *prev; 1721170754Sdelphij 1722170754Sdelphij for (tmp = diff, prev = 0; tmp; tmp = next) 1723170754Sdelphij { 1724170754Sdelphij next = tmp->next; 1725170754Sdelphij tmp->next = prev; 1726170754Sdelphij prev = tmp; 1727170754Sdelphij } 1728170754Sdelphij 1729170754Sdelphij return prev; 1730170754Sdelphij} 1731170754Sdelphij 1732170754Sdelphijstatic void 1733170754Sdelphijfatal (char const *msgid) 1734170754Sdelphij{ 1735170754Sdelphij error (EXIT_TROUBLE, 0, "%s", _(msgid)); 1736170754Sdelphij abort (); 1737170754Sdelphij} 1738170754Sdelphij 1739170754Sdelphijstatic void 1740170754Sdelphijperror_with_exit (char const *string) 1741170754Sdelphij{ 1742170754Sdelphij error (EXIT_TROUBLE, errno, "%s", string); 1743170754Sdelphij abort (); 1744170754Sdelphij} 1745