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