| 1 | /* diff - compare files line by line | 
|---|
| 2 |  | 
|---|
| 3 | Copyright (C) 1988, 1989, 1992, 1993, 1994, 1996, 1998, 2001, 2002 | 
|---|
| 4 | Free Software Foundation, Inc. | 
|---|
| 5 |  | 
|---|
| 6 | This file is part of GNU DIFF. | 
|---|
| 7 |  | 
|---|
| 8 | GNU DIFF is free software; you can redistribute it and/or modify | 
|---|
| 9 | it under the terms of the GNU General Public License as published by | 
|---|
| 10 | the Free Software Foundation; either version 2, or (at your option) | 
|---|
| 11 | any later version. | 
|---|
| 12 |  | 
|---|
| 13 | GNU DIFF is distributed in the hope that it will be useful, | 
|---|
| 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
| 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | 
|---|
| 16 | See the GNU General Public License for more details. | 
|---|
| 17 |  | 
|---|
| 18 | You should have received a copy of the GNU General Public License | 
|---|
| 19 | along with GNU DIFF; see the file COPYING. | 
|---|
| 20 | If not, write to the Free Software Foundation, | 
|---|
| 21 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */ | 
|---|
| 22 |  | 
|---|
| 23 | #define GDIFF_MAIN | 
|---|
| 24 | #include "diff.h" | 
|---|
| 25 | #include <c-stack.h> | 
|---|
| 26 | #include <dirname.h> | 
|---|
| 27 | #include <error.h> | 
|---|
| 28 | #include <exclude.h> | 
|---|
| 29 | #include <exitfail.h> | 
|---|
| 30 | #include <fnmatch.h> | 
|---|
| 31 | #include <freesoft.h> | 
|---|
| 32 | #include <getopt.h> | 
|---|
| 33 | #include <hard-locale.h> | 
|---|
| 34 | #include <prepargs.h> | 
|---|
| 35 | #include <quotesys.h> | 
|---|
| 36 | #include <regex.h> | 
|---|
| 37 | #include <setmode.h> | 
|---|
| 38 | #include <xalloc.h> | 
|---|
| 39 |  | 
|---|
| 40 | static char const authorship_msgid[] = | 
|---|
| 41 | N_("Written by Paul Eggert, Mike Haertel, David Hayes,\n\ | 
|---|
| 42 | Richard Stallman, and Len Tower."); | 
|---|
| 43 |  | 
|---|
| 44 | static char const copyright_string[] = | 
|---|
| 45 | "Copyright (C) 2002 Free Software Foundation, Inc."; | 
|---|
| 46 |  | 
|---|
| 47 | #ifndef GUTTER_WIDTH_MINIMUM | 
|---|
| 48 | # define GUTTER_WIDTH_MINIMUM 3 | 
|---|
| 49 | #endif | 
|---|
| 50 |  | 
|---|
| 51 | struct regexp_list | 
|---|
| 52 | { | 
|---|
| 53 | char *regexps;        /* chars representing disjunction of the regexps */ | 
|---|
| 54 | size_t len;           /* chars used in `regexps' */ | 
|---|
| 55 | size_t size;          /* size malloc'ed for `regexps'; 0 if not malloc'ed */ | 
|---|
| 56 | bool multiple_regexps;/* Does `regexps' represent a disjunction?  */ | 
|---|
| 57 | struct re_pattern_buffer *buf; | 
|---|
| 58 | }; | 
|---|
| 59 |  | 
|---|
| 60 | static int compare_files (struct comparison const *, char const *, char const *); | 
|---|
| 61 | static void add_regexp (struct regexp_list *, char const *); | 
|---|
| 62 | static void summarize_regexp_list (struct regexp_list *); | 
|---|
| 63 | static void specify_style (enum output_style); | 
|---|
| 64 | static void specify_value (char const **, char const *, char const *); | 
|---|
| 65 | static void try_help (char const *, char const *) __attribute__((noreturn)); | 
|---|
| 66 | static void check_stdout (void); | 
|---|
| 67 | static void usage (void); | 
|---|
| 68 |  | 
|---|
| 69 | /* If comparing directories, compare their common subdirectories | 
|---|
| 70 | recursively.  */ | 
|---|
| 71 | static bool recursive; | 
|---|
| 72 |  | 
|---|
| 73 | /* In context diffs, show previous lines that match these regexps.  */ | 
|---|
| 74 | static struct regexp_list function_regexp_list; | 
|---|
| 75 |  | 
|---|
| 76 | /* Ignore changes affecting only lines that match these regexps.  */ | 
|---|
| 77 | static struct regexp_list ignore_regexp_list; | 
|---|
| 78 |  | 
|---|
| 79 | #if HAVE_SETMODE_DOS | 
|---|
| 80 | /* Use binary I/O when reading and writing data (--binary). | 
|---|
| 81 | On POSIX hosts, this has no effect.  */ | 
|---|
| 82 | static bool binary; | 
|---|
| 83 | #endif | 
|---|
| 84 |  | 
|---|
| 85 | /* When comparing directories, if a file appears only in one | 
|---|
| 86 | directory, treat it as present but empty in the other (-N). | 
|---|
| 87 | Then `patch' would create the file with appropriate contents.  */ | 
|---|
| 88 | static bool new_file; | 
|---|
| 89 |  | 
|---|
| 90 | /* When comparing directories, if a file appears only in the second | 
|---|
| 91 | directory of the two, treat it as present but empty in the other | 
|---|
| 92 | (--unidirectional-new-file). | 
|---|
| 93 | Then `patch' would create the file with appropriate contents.  */ | 
|---|
| 94 | static bool unidirectional_new_file; | 
|---|
| 95 |  | 
|---|
| 96 | /* Report files compared that are the same (-s). | 
|---|
| 97 | Normally nothing is output when that happens.  */ | 
|---|
| 98 | static bool report_identical_files; | 
|---|
| 99 |  | 
|---|
| 100 |  | 
|---|
| 101 | /* Return a string containing the command options with which diff was invoked. | 
|---|
| 102 | Spaces appear between what were separate ARGV-elements. | 
|---|
| 103 | There is a space at the beginning but none at the end. | 
|---|
| 104 | If there were no options, the result is an empty string. | 
|---|
| 105 |  | 
|---|
| 106 | Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT, | 
|---|
| 107 | the length of that vector.  */ | 
|---|
| 108 |  | 
|---|
| 109 | static char * | 
|---|
| 110 | option_list (char **optionvec, int count) | 
|---|
| 111 | { | 
|---|
| 112 | int i; | 
|---|
| 113 | size_t size = 1; | 
|---|
| 114 | char *result; | 
|---|
| 115 | char *p; | 
|---|
| 116 |  | 
|---|
| 117 | for (i = 0; i < count; i++) | 
|---|
| 118 | size += 1 + quote_system_arg ((char *) 0, optionvec[i]); | 
|---|
| 119 |  | 
|---|
| 120 | p = result = xmalloc (size); | 
|---|
| 121 |  | 
|---|
| 122 | for (i = 0; i < count; i++) | 
|---|
| 123 | { | 
|---|
| 124 | *p++ = ' '; | 
|---|
| 125 | p += quote_system_arg (p, optionvec[i]); | 
|---|
| 126 | } | 
|---|
| 127 |  | 
|---|
| 128 | *p = 0; | 
|---|
| 129 | return result; | 
|---|
| 130 | } | 
|---|
| 131 |  | 
|---|
| 132 |  | 
|---|
| 133 | /* Return an option value suitable for add_exclude.  */ | 
|---|
| 134 |  | 
|---|
| 135 | static int | 
|---|
| 136 | exclude_options (void) | 
|---|
| 137 | { | 
|---|
| 138 | return EXCLUDE_WILDCARDS | (ignore_file_name_case ? FNM_CASEFOLD : 0); | 
|---|
| 139 | } | 
|---|
| 140 |  | 
|---|
| 141 |  | 
|---|
| 142 | static char const shortopts[] = | 
|---|
| 143 | "0123456789abBcC:dD:eEfF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:y"; | 
|---|
| 144 |  | 
|---|
| 145 | /* Values for long options that do not have single-letter equivalents.  */ | 
|---|
| 146 | enum | 
|---|
| 147 | { | 
|---|
| 148 | BINARY_OPTION = CHAR_MAX + 1, | 
|---|
| 149 | FROM_FILE_OPTION, | 
|---|
| 150 | HELP_OPTION, | 
|---|
| 151 | HORIZON_LINES_OPTION, | 
|---|
| 152 | IGNORE_FILE_NAME_CASE_OPTION, | 
|---|
| 153 | INHIBIT_HUNK_MERGE_OPTION, | 
|---|
| 154 | LEFT_COLUMN_OPTION, | 
|---|
| 155 | LINE_FORMAT_OPTION, | 
|---|
| 156 | NO_IGNORE_FILE_NAME_CASE_OPTION, | 
|---|
| 157 | NORMAL_OPTION, | 
|---|
| 158 | SDIFF_MERGE_ASSIST_OPTION, | 
|---|
| 159 | STRIP_TRAILING_CR_OPTION, | 
|---|
| 160 | SUPPRESS_COMMON_LINES_OPTION, | 
|---|
| 161 | TO_FILE_OPTION, | 
|---|
| 162 |  | 
|---|
| 163 | /* These options must be in sequence.  */ | 
|---|
| 164 | UNCHANGED_LINE_FORMAT_OPTION, | 
|---|
| 165 | OLD_LINE_FORMAT_OPTION, | 
|---|
| 166 | NEW_LINE_FORMAT_OPTION, | 
|---|
| 167 |  | 
|---|
| 168 | /* These options must be in sequence.  */ | 
|---|
| 169 | UNCHANGED_GROUP_FORMAT_OPTION, | 
|---|
| 170 | OLD_GROUP_FORMAT_OPTION, | 
|---|
| 171 | NEW_GROUP_FORMAT_OPTION, | 
|---|
| 172 | CHANGED_GROUP_FORMAT_OPTION | 
|---|
| 173 | }; | 
|---|
| 174 |  | 
|---|
| 175 | static char const group_format_option[][sizeof "--unchanged-group-format"] = | 
|---|
| 176 | { | 
|---|
| 177 | "--unchanged-group-format", | 
|---|
| 178 | "--old-group-format", | 
|---|
| 179 | "--new-group-format", | 
|---|
| 180 | "--changed-group-format" | 
|---|
| 181 | }; | 
|---|
| 182 |  | 
|---|
| 183 | static char const line_format_option[][sizeof "--unchanged-line-format"] = | 
|---|
| 184 | { | 
|---|
| 185 | "--unchanged-line-format", | 
|---|
| 186 | "--old-line-format", | 
|---|
| 187 | "--new-line-format" | 
|---|
| 188 | }; | 
|---|
| 189 |  | 
|---|
| 190 | static struct option const longopts[] = | 
|---|
| 191 | { | 
|---|
| 192 | {"binary", 0, 0, BINARY_OPTION}, | 
|---|
| 193 | {"brief", 0, 0, 'q'}, | 
|---|
| 194 | {"changed-group-format", 1, 0, CHANGED_GROUP_FORMAT_OPTION}, | 
|---|
| 195 | {"context", 2, 0, 'C'}, | 
|---|
| 196 | {"ed", 0, 0, 'e'}, | 
|---|
| 197 | {"exclude", 1, 0, 'x'}, | 
|---|
| 198 | {"exclude-from", 1, 0, 'X'}, | 
|---|
| 199 | {"expand-tabs", 0, 0, 't'}, | 
|---|
| 200 | {"forward-ed", 0, 0, 'f'}, | 
|---|
| 201 | {"from-file", 1, 0, FROM_FILE_OPTION}, | 
|---|
| 202 | {"help", 0, 0, HELP_OPTION}, | 
|---|
| 203 | {"horizon-lines", 1, 0, HORIZON_LINES_OPTION}, | 
|---|
| 204 | {"ifdef", 1, 0, 'D'}, | 
|---|
| 205 | {"ignore-all-space", 0, 0, 'w'}, | 
|---|
| 206 | {"ignore-blank-lines", 0, 0, 'B'}, | 
|---|
| 207 | {"ignore-case", 0, 0, 'i'}, | 
|---|
| 208 | {"ignore-file-name-case", 0, 0, IGNORE_FILE_NAME_CASE_OPTION}, | 
|---|
| 209 | {"ignore-matching-lines", 1, 0, 'I'}, | 
|---|
| 210 | {"ignore-space-change", 0, 0, 'b'}, | 
|---|
| 211 | {"ignore-tab-expansion", 0, 0, 'E'}, | 
|---|
| 212 | {"inhibit-hunk-merge", 0, 0, INHIBIT_HUNK_MERGE_OPTION}, | 
|---|
| 213 | {"initial-tab", 0, 0, 'T'}, | 
|---|
| 214 | {"label", 1, 0, 'L'}, | 
|---|
| 215 | {"left-column", 0, 0, LEFT_COLUMN_OPTION}, | 
|---|
| 216 | {"line-format", 1, 0, LINE_FORMAT_OPTION}, | 
|---|
| 217 | {"minimal", 0, 0, 'd'}, | 
|---|
| 218 | {"new-file", 0, 0, 'N'}, | 
|---|
| 219 | {"new-group-format", 1, 0, NEW_GROUP_FORMAT_OPTION}, | 
|---|
| 220 | {"new-line-format", 1, 0, NEW_LINE_FORMAT_OPTION}, | 
|---|
| 221 | {"no-ignore-file-name-case", 0, 0, NO_IGNORE_FILE_NAME_CASE_OPTION}, | 
|---|
| 222 | {"normal", 0, 0, NORMAL_OPTION}, | 
|---|
| 223 | {"old-group-format", 1, 0, OLD_GROUP_FORMAT_OPTION}, | 
|---|
| 224 | {"old-line-format", 1, 0, OLD_LINE_FORMAT_OPTION}, | 
|---|
| 225 | {"paginate", 0, 0, 'l'}, | 
|---|
| 226 | {"rcs", 0, 0, 'n'}, | 
|---|
| 227 | {"recursive", 0, 0, 'r'}, | 
|---|
| 228 | {"report-identical-files", 0, 0, 's'}, | 
|---|
| 229 | {"sdiff-merge-assist", 0, 0, SDIFF_MERGE_ASSIST_OPTION}, | 
|---|
| 230 | {"show-c-function", 0, 0, 'p'}, | 
|---|
| 231 | {"show-function-line", 1, 0, 'F'}, | 
|---|
| 232 | {"side-by-side", 0, 0, 'y'}, | 
|---|
| 233 | {"speed-large-files", 0, 0, 'H'}, | 
|---|
| 234 | {"starting-file", 1, 0, 'S'}, | 
|---|
| 235 | {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION}, | 
|---|
| 236 | {"suppress-common-lines", 0, 0, SUPPRESS_COMMON_LINES_OPTION}, | 
|---|
| 237 | {"text", 0, 0, 'a'}, | 
|---|
| 238 | {"to-file", 1, 0, TO_FILE_OPTION}, | 
|---|
| 239 | {"unchanged-group-format", 1, 0, UNCHANGED_GROUP_FORMAT_OPTION}, | 
|---|
| 240 | {"unchanged-line-format", 1, 0, UNCHANGED_LINE_FORMAT_OPTION}, | 
|---|
| 241 | {"unidirectional-new-file", 0, 0, 'P'}, | 
|---|
| 242 | {"unified", 2, 0, 'U'}, | 
|---|
| 243 | {"version", 0, 0, 'v'}, | 
|---|
| 244 | {"width", 1, 0, 'W'}, | 
|---|
| 245 | {0, 0, 0, 0} | 
|---|
| 246 | }; | 
|---|
| 247 |  | 
|---|
| 248 | int | 
|---|
| 249 | main (int argc, char **argv) | 
|---|
| 250 | { | 
|---|
| 251 | int exit_status = EXIT_SUCCESS; | 
|---|
| 252 | int c; | 
|---|
| 253 | int i; | 
|---|
| 254 | int prev = -1; | 
|---|
| 255 | lin ocontext = -1; | 
|---|
| 256 | bool explicit_context = 0; | 
|---|
| 257 | int width = 0; | 
|---|
| 258 | bool show_c_function = 0; | 
|---|
| 259 | char const *from_file = 0; | 
|---|
| 260 | char const *to_file = 0; | 
|---|
| 261 | uintmax_t numval; | 
|---|
| 262 | char *numend; | 
|---|
| 263 |  | 
|---|
| 264 | /* Do our initializations.  */ | 
|---|
| 265 | exit_failure = 2; | 
|---|
| 266 | initialize_main (&argc, &argv); | 
|---|
| 267 | program_name = argv[0]; | 
|---|
| 268 | setlocale (LC_ALL, ""); | 
|---|
| 269 | bindtextdomain (PACKAGE, LOCALEDIR); | 
|---|
| 270 | textdomain (PACKAGE); | 
|---|
| 271 | c_stack_action (c_stack_die); | 
|---|
| 272 | function_regexp_list.buf = &function_regexp; | 
|---|
| 273 | ignore_regexp_list.buf = &ignore_regexp; | 
|---|
| 274 | re_set_syntax (RE_SYNTAX_GREP | RE_NO_POSIX_BACKTRACKING); | 
|---|
| 275 | excluded = new_exclude (); | 
|---|
| 276 |  | 
|---|
| 277 | /* Decode the options.  */ | 
|---|
| 278 |  | 
|---|
| 279 | while ((c = getopt_long (argc, argv, shortopts, longopts, 0)) != -1) | 
|---|
| 280 | { | 
|---|
| 281 | switch (c) | 
|---|
| 282 | { | 
|---|
| 283 | case 0: | 
|---|
| 284 | break; | 
|---|
| 285 |  | 
|---|
| 286 | case '0': | 
|---|
| 287 | case '1': | 
|---|
| 288 | case '2': | 
|---|
| 289 | case '3': | 
|---|
| 290 | case '4': | 
|---|
| 291 | case '5': | 
|---|
| 292 | case '6': | 
|---|
| 293 | case '7': | 
|---|
| 294 | case '8': | 
|---|
| 295 | case '9': | 
|---|
| 296 | if (! ISDIGIT (prev)) | 
|---|
| 297 | ocontext = c - '0'; | 
|---|
| 298 | else if (LIN_MAX / 10 < ocontext | 
|---|
| 299 | || ((ocontext = 10 * ocontext + c - '0') < 0)) | 
|---|
| 300 | ocontext = LIN_MAX; | 
|---|
| 301 | break; | 
|---|
| 302 |  | 
|---|
| 303 | case 'a': | 
|---|
| 304 | text = 1; | 
|---|
| 305 | break; | 
|---|
| 306 |  | 
|---|
| 307 | case 'b': | 
|---|
| 308 | if (ignore_white_space < IGNORE_SPACE_CHANGE) | 
|---|
| 309 | ignore_white_space = IGNORE_SPACE_CHANGE; | 
|---|
| 310 | break; | 
|---|
| 311 |  | 
|---|
| 312 | case 'B': | 
|---|
| 313 | ignore_blank_lines = 1; | 
|---|
| 314 | break; | 
|---|
| 315 |  | 
|---|
| 316 | case 'C':               /* +context[=lines] */ | 
|---|
| 317 | case 'U':               /* +unified[=lines] */ | 
|---|
| 318 | { | 
|---|
| 319 | if (optarg) | 
|---|
| 320 | { | 
|---|
| 321 | numval = strtoumax (optarg, &numend, 10); | 
|---|
| 322 | if (*numend) | 
|---|
| 323 | try_help ("invalid context length `%s'", optarg); | 
|---|
| 324 | if (LIN_MAX < numval) | 
|---|
| 325 | numval = LIN_MAX; | 
|---|
| 326 | } | 
|---|
| 327 | else | 
|---|
| 328 | numval = 3; | 
|---|
| 329 |  | 
|---|
| 330 | specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT); | 
|---|
| 331 | if (context < numval) | 
|---|
| 332 | context = numval; | 
|---|
| 333 | explicit_context = 1; | 
|---|
| 334 | } | 
|---|
| 335 | break; | 
|---|
| 336 |  | 
|---|
| 337 | case 'c': | 
|---|
| 338 | specify_style (OUTPUT_CONTEXT); | 
|---|
| 339 | if (context < 3) | 
|---|
| 340 | context = 3; | 
|---|
| 341 | break; | 
|---|
| 342 |  | 
|---|
| 343 | case 'd': | 
|---|
| 344 | minimal = 1; | 
|---|
| 345 | break; | 
|---|
| 346 |  | 
|---|
| 347 | case 'D': | 
|---|
| 348 | specify_style (OUTPUT_IFDEF); | 
|---|
| 349 | { | 
|---|
| 350 | static char const C_ifdef_group_formats[] = | 
|---|
| 351 | "%%=%c#ifndef %s\n%%<#endif /* ! %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n"; | 
|---|
| 352 | char *b = xmalloc (sizeof C_ifdef_group_formats | 
|---|
| 353 | + 7 * strlen (optarg) - 14 /* 7*"%s" */ | 
|---|
| 354 | - 8 /* 5*"%%" + 3*"%c" */); | 
|---|
| 355 | sprintf (b, C_ifdef_group_formats, | 
|---|
| 356 | 0, | 
|---|
| 357 | optarg, optarg, 0, | 
|---|
| 358 | optarg, optarg, 0, | 
|---|
| 359 | optarg, optarg, optarg); | 
|---|
| 360 | for (i = 0; i < sizeof group_format / sizeof *group_format; i++) | 
|---|
| 361 | { | 
|---|
| 362 | specify_value (&group_format[i], b, "-D"); | 
|---|
| 363 | b += strlen (b) + 1; | 
|---|
| 364 | } | 
|---|
| 365 | } | 
|---|
| 366 | break; | 
|---|
| 367 |  | 
|---|
| 368 | case 'e': | 
|---|
| 369 | specify_style (OUTPUT_ED); | 
|---|
| 370 | break; | 
|---|
| 371 |  | 
|---|
| 372 | case 'E': | 
|---|
| 373 | if (ignore_white_space < IGNORE_TAB_EXPANSION) | 
|---|
| 374 | ignore_white_space = IGNORE_TAB_EXPANSION; | 
|---|
| 375 | break; | 
|---|
| 376 |  | 
|---|
| 377 | case 'f': | 
|---|
| 378 | specify_style (OUTPUT_FORWARD_ED); | 
|---|
| 379 | break; | 
|---|
| 380 |  | 
|---|
| 381 | case 'F': | 
|---|
| 382 | add_regexp (&function_regexp_list, optarg); | 
|---|
| 383 | break; | 
|---|
| 384 |  | 
|---|
| 385 | case 'h': | 
|---|
| 386 | /* Split the files into chunks for faster processing. | 
|---|
| 387 | Usually does not change the result. | 
|---|
| 388 |  | 
|---|
| 389 | This currently has no effect.  */ | 
|---|
| 390 | break; | 
|---|
| 391 |  | 
|---|
| 392 | case 'H': | 
|---|
| 393 | speed_large_files = 1; | 
|---|
| 394 | break; | 
|---|
| 395 |  | 
|---|
| 396 | case 'i': | 
|---|
| 397 | ignore_case = 1; | 
|---|
| 398 | break; | 
|---|
| 399 |  | 
|---|
| 400 | case 'I': | 
|---|
| 401 | add_regexp (&ignore_regexp_list, optarg); | 
|---|
| 402 | break; | 
|---|
| 403 |  | 
|---|
| 404 | case 'l': | 
|---|
| 405 | if (!pr_program[0]) | 
|---|
| 406 | try_help ("pagination not supported on this host", 0); | 
|---|
| 407 | paginate = 1; | 
|---|
| 408 | #ifdef SIGCHLD | 
|---|
| 409 | /* Pagination requires forking and waiting, and | 
|---|
| 410 | System V fork+wait does not work if SIGCHLD is ignored.  */ | 
|---|
| 411 | signal (SIGCHLD, SIG_DFL); | 
|---|
| 412 | #endif | 
|---|
| 413 | break; | 
|---|
| 414 |  | 
|---|
| 415 | case 'L': | 
|---|
| 416 | if (!file_label[0]) | 
|---|
| 417 | file_label[0] = optarg; | 
|---|
| 418 | else if (!file_label[1]) | 
|---|
| 419 | file_label[1] = optarg; | 
|---|
| 420 | else | 
|---|
| 421 | fatal ("too many file label options"); | 
|---|
| 422 | break; | 
|---|
| 423 |  | 
|---|
| 424 | case 'n': | 
|---|
| 425 | specify_style (OUTPUT_RCS); | 
|---|
| 426 | break; | 
|---|
| 427 |  | 
|---|
| 428 | case 'N': | 
|---|
| 429 | new_file = 1; | 
|---|
| 430 | break; | 
|---|
| 431 |  | 
|---|
| 432 | case 'p': | 
|---|
| 433 | show_c_function = 1; | 
|---|
| 434 | add_regexp (&function_regexp_list, "^[[:alpha:]$_]"); | 
|---|
| 435 | break; | 
|---|
| 436 |  | 
|---|
| 437 | case 'P': | 
|---|
| 438 | unidirectional_new_file = 1; | 
|---|
| 439 | break; | 
|---|
| 440 |  | 
|---|
| 441 | case 'q': | 
|---|
| 442 | brief = 1; | 
|---|
| 443 | break; | 
|---|
| 444 |  | 
|---|
| 445 | case 'r': | 
|---|
| 446 | recursive = 1; | 
|---|
| 447 | break; | 
|---|
| 448 |  | 
|---|
| 449 | case 's': | 
|---|
| 450 | report_identical_files = 1; | 
|---|
| 451 | break; | 
|---|
| 452 |  | 
|---|
| 453 | case 'S': | 
|---|
| 454 | specify_value (&starting_file, optarg, "-S"); | 
|---|
| 455 | break; | 
|---|
| 456 |  | 
|---|
| 457 | case 't': | 
|---|
| 458 | expand_tabs = 1; | 
|---|
| 459 | break; | 
|---|
| 460 |  | 
|---|
| 461 | case 'T': | 
|---|
| 462 | initial_tab = 1; | 
|---|
| 463 | break; | 
|---|
| 464 |  | 
|---|
| 465 | case 'u': | 
|---|
| 466 | specify_style (OUTPUT_UNIFIED); | 
|---|
| 467 | if (context < 3) | 
|---|
| 468 | context = 3; | 
|---|
| 469 | break; | 
|---|
| 470 |  | 
|---|
| 471 | case 'v': | 
|---|
| 472 | printf ("diff %s\n%s\n\n%s\n\n%s\n", | 
|---|
| 473 | version_string, copyright_string, | 
|---|
| 474 | _(free_software_msgid), _(authorship_msgid)); | 
|---|
| 475 | check_stdout (); | 
|---|
| 476 | return EXIT_SUCCESS; | 
|---|
| 477 |  | 
|---|
| 478 | case 'w': | 
|---|
| 479 | ignore_white_space = IGNORE_ALL_SPACE; | 
|---|
| 480 | break; | 
|---|
| 481 |  | 
|---|
| 482 | case 'x': | 
|---|
| 483 | add_exclude (excluded, optarg, exclude_options ()); | 
|---|
| 484 | break; | 
|---|
| 485 |  | 
|---|
| 486 | case 'X': | 
|---|
| 487 | if (add_exclude_file (add_exclude, excluded, optarg, | 
|---|
| 488 | exclude_options (), '\n')) | 
|---|
| 489 | pfatal_with_name (optarg); | 
|---|
| 490 | break; | 
|---|
| 491 |  | 
|---|
| 492 | case 'y': | 
|---|
| 493 | specify_style (OUTPUT_SDIFF); | 
|---|
| 494 | break; | 
|---|
| 495 |  | 
|---|
| 496 | case 'W': | 
|---|
| 497 | numval = strtoumax (optarg, &numend, 10); | 
|---|
| 498 | if (! (0 < numval && numval <= INT_MAX) || *numend) | 
|---|
| 499 | try_help ("invalid width `%s'", optarg); | 
|---|
| 500 | if (width != numval) | 
|---|
| 501 | { | 
|---|
| 502 | if (width) | 
|---|
| 503 | fatal ("conflicting width options"); | 
|---|
| 504 | width = numval; | 
|---|
| 505 | } | 
|---|
| 506 | break; | 
|---|
| 507 |  | 
|---|
| 508 | case BINARY_OPTION: | 
|---|
| 509 | #if HAVE_SETMODE_DOS | 
|---|
| 510 | binary = 1; | 
|---|
| 511 | set_binary_mode (STDOUT_FILENO, 1); | 
|---|
| 512 | #endif | 
|---|
| 513 | break; | 
|---|
| 514 |  | 
|---|
| 515 | case FROM_FILE_OPTION: | 
|---|
| 516 | specify_value (&from_file, optarg, "--from-file"); | 
|---|
| 517 | break; | 
|---|
| 518 |  | 
|---|
| 519 | case HELP_OPTION: | 
|---|
| 520 | usage (); | 
|---|
| 521 | check_stdout (); | 
|---|
| 522 | return EXIT_SUCCESS; | 
|---|
| 523 |  | 
|---|
| 524 | case HORIZON_LINES_OPTION: | 
|---|
| 525 | numval = strtoumax (optarg, &numend, 10); | 
|---|
| 526 | if (*numend) | 
|---|
| 527 | try_help ("invalid horizon length `%s'", optarg); | 
|---|
| 528 | horizon_lines = MAX (horizon_lines, MIN (numval, LIN_MAX)); | 
|---|
| 529 | break; | 
|---|
| 530 |  | 
|---|
| 531 | case IGNORE_FILE_NAME_CASE_OPTION: | 
|---|
| 532 | ignore_file_name_case = 1; | 
|---|
| 533 | break; | 
|---|
| 534 |  | 
|---|
| 535 | case INHIBIT_HUNK_MERGE_OPTION: | 
|---|
| 536 | /* This option is obsolete, but accept it for backward | 
|---|
| 537 | compatibility.  */ | 
|---|
| 538 | break; | 
|---|
| 539 |  | 
|---|
| 540 | case LEFT_COLUMN_OPTION: | 
|---|
| 541 | left_column = 1; | 
|---|
| 542 | break; | 
|---|
| 543 |  | 
|---|
| 544 | case LINE_FORMAT_OPTION: | 
|---|
| 545 | specify_style (OUTPUT_IFDEF); | 
|---|
| 546 | for (i = 0; i < sizeof line_format / sizeof *line_format; i++) | 
|---|
| 547 | specify_value (&line_format[i], optarg, "--line-format"); | 
|---|
| 548 | break; | 
|---|
| 549 |  | 
|---|
| 550 | case NO_IGNORE_FILE_NAME_CASE_OPTION: | 
|---|
| 551 | ignore_file_name_case = 0; | 
|---|
| 552 | break; | 
|---|
| 553 |  | 
|---|
| 554 | case NORMAL_OPTION: | 
|---|
| 555 | specify_style (OUTPUT_NORMAL); | 
|---|
| 556 | break; | 
|---|
| 557 |  | 
|---|
| 558 | case SDIFF_MERGE_ASSIST_OPTION: | 
|---|
| 559 | specify_style (OUTPUT_SDIFF); | 
|---|
| 560 | sdiff_merge_assist = 1; | 
|---|
| 561 | break; | 
|---|
| 562 |  | 
|---|
| 563 | case STRIP_TRAILING_CR_OPTION: | 
|---|
| 564 | strip_trailing_cr = 1; | 
|---|
| 565 | break; | 
|---|
| 566 |  | 
|---|
| 567 | case SUPPRESS_COMMON_LINES_OPTION: | 
|---|
| 568 | suppress_common_lines = 1; | 
|---|
| 569 | break; | 
|---|
| 570 |  | 
|---|
| 571 | case TO_FILE_OPTION: | 
|---|
| 572 | specify_value (&to_file, optarg, "--to-file"); | 
|---|
| 573 | break; | 
|---|
| 574 |  | 
|---|
| 575 | case UNCHANGED_LINE_FORMAT_OPTION: | 
|---|
| 576 | case OLD_LINE_FORMAT_OPTION: | 
|---|
| 577 | case NEW_LINE_FORMAT_OPTION: | 
|---|
| 578 | specify_style (OUTPUT_IFDEF); | 
|---|
| 579 | c -= UNCHANGED_LINE_FORMAT_OPTION; | 
|---|
| 580 | specify_value (&line_format[c], optarg, line_format_option[c]); | 
|---|
| 581 | break; | 
|---|
| 582 |  | 
|---|
| 583 | case UNCHANGED_GROUP_FORMAT_OPTION: | 
|---|
| 584 | case OLD_GROUP_FORMAT_OPTION: | 
|---|
| 585 | case NEW_GROUP_FORMAT_OPTION: | 
|---|
| 586 | case CHANGED_GROUP_FORMAT_OPTION: | 
|---|
| 587 | specify_style (OUTPUT_IFDEF); | 
|---|
| 588 | c -= UNCHANGED_GROUP_FORMAT_OPTION; | 
|---|
| 589 | specify_value (&group_format[c], optarg, group_format_option[c]); | 
|---|
| 590 | break; | 
|---|
| 591 |  | 
|---|
| 592 | default: | 
|---|
| 593 | try_help (0, 0); | 
|---|
| 594 | } | 
|---|
| 595 | prev = c; | 
|---|
| 596 | } | 
|---|
| 597 |  | 
|---|
| 598 | if (output_style == OUTPUT_UNSPECIFIED) | 
|---|
| 599 | { | 
|---|
| 600 | if (show_c_function) | 
|---|
| 601 | { | 
|---|
| 602 | specify_style (OUTPUT_CONTEXT); | 
|---|
| 603 | if (ocontext < 0) | 
|---|
| 604 | context = 3; | 
|---|
| 605 | } | 
|---|
| 606 | else | 
|---|
| 607 | specify_style (OUTPUT_NORMAL); | 
|---|
| 608 | } | 
|---|
| 609 |  | 
|---|
| 610 | if (output_style != OUTPUT_CONTEXT || hard_locale (LC_TIME)) | 
|---|
| 611 | time_format = "%Y-%m-%d %H:%M:%S.%N %z"; | 
|---|
| 612 | else | 
|---|
| 613 | { | 
|---|
| 614 | /* See POSIX 1003.1-2001 for this format.  */ | 
|---|
| 615 | time_format = "%a %b %e %T %Y"; | 
|---|
| 616 | } | 
|---|
| 617 |  | 
|---|
| 618 | if (0 <= ocontext) | 
|---|
| 619 | { | 
|---|
| 620 | bool modern_usage = 200112 <= posix2_version (); | 
|---|
| 621 |  | 
|---|
| 622 | if ((output_style == OUTPUT_CONTEXT | 
|---|
| 623 | || output_style == OUTPUT_UNIFIED) | 
|---|
| 624 | && (context < ocontext | 
|---|
| 625 | || (ocontext < context && ! explicit_context))) | 
|---|
| 626 | { | 
|---|
| 627 | if (modern_usage) | 
|---|
| 628 | { | 
|---|
| 629 | error (0, 0, | 
|---|
| 630 | _("`-%ld' option is obsolete; use `-%c %ld'"), | 
|---|
| 631 | (long) ocontext, | 
|---|
| 632 | output_style == OUTPUT_CONTEXT ? 'C' : 'U', | 
|---|
| 633 | (long) ocontext); | 
|---|
| 634 | try_help (0, 0); | 
|---|
| 635 | } | 
|---|
| 636 | context = ocontext; | 
|---|
| 637 | } | 
|---|
| 638 | else | 
|---|
| 639 | { | 
|---|
| 640 | if (modern_usage) | 
|---|
| 641 | { | 
|---|
| 642 | error (0, 0, _("`-%ld' option is obsolete; omit it"), | 
|---|
| 643 | (long) ocontext); | 
|---|
| 644 | try_help (0, 0); | 
|---|
| 645 | } | 
|---|
| 646 | } | 
|---|
| 647 | } | 
|---|
| 648 |  | 
|---|
| 649 | { | 
|---|
| 650 | /* | 
|---|
| 651 | *  We maximize first the half line width, and then the gutter width, | 
|---|
| 652 | *  according to the following constraints: | 
|---|
| 653 | *  1.  Two half lines plus a gutter must fit in a line. | 
|---|
| 654 | *  2.  If the half line width is nonzero: | 
|---|
| 655 | *      a.  The gutter width is at least GUTTER_WIDTH_MINIMUM. | 
|---|
| 656 | *      b.  If tabs are not expanded to spaces, | 
|---|
| 657 | *          a half line plus a gutter is an integral number of tabs, | 
|---|
| 658 | *          so that tabs in the right column line up. | 
|---|
| 659 | */ | 
|---|
| 660 | unsigned int t = expand_tabs ? 1 : TAB_WIDTH; | 
|---|
| 661 | int w = width ? width : 130; | 
|---|
| 662 | int off = (w + t + GUTTER_WIDTH_MINIMUM) / (2 * t)  *  t; | 
|---|
| 663 | sdiff_half_width = MAX (0, MIN (off - GUTTER_WIDTH_MINIMUM, w - off)), | 
|---|
| 664 | sdiff_column2_offset = sdiff_half_width ? off : w; | 
|---|
| 665 | } | 
|---|
| 666 |  | 
|---|
| 667 | /* Make the horizon at least as large as the context, so that | 
|---|
| 668 | shift_boundaries has more freedom to shift the first and last hunks.  */ | 
|---|
| 669 | if (horizon_lines < context) | 
|---|
| 670 | horizon_lines = context; | 
|---|
| 671 |  | 
|---|
| 672 | summarize_regexp_list (&function_regexp_list); | 
|---|
| 673 | summarize_regexp_list (&ignore_regexp_list); | 
|---|
| 674 |  | 
|---|
| 675 | if (output_style == OUTPUT_IFDEF) | 
|---|
| 676 | { | 
|---|
| 677 | for (i = 0; i < sizeof line_format / sizeof *line_format; i++) | 
|---|
| 678 | if (!line_format[i]) | 
|---|
| 679 | line_format[i] = "%l\n"; | 
|---|
| 680 | if (!group_format[OLD]) | 
|---|
| 681 | group_format[OLD] | 
|---|
| 682 | = group_format[CHANGED] ? group_format[CHANGED] : "%<"; | 
|---|
| 683 | if (!group_format[NEW]) | 
|---|
| 684 | group_format[NEW] | 
|---|
| 685 | = group_format[CHANGED] ? group_format[CHANGED] : "%>"; | 
|---|
| 686 | if (!group_format[UNCHANGED]) | 
|---|
| 687 | group_format[UNCHANGED] = "%="; | 
|---|
| 688 | if (!group_format[CHANGED]) | 
|---|
| 689 | group_format[CHANGED] = concat (group_format[OLD], | 
|---|
| 690 | group_format[NEW], ""); | 
|---|
| 691 | } | 
|---|
| 692 |  | 
|---|
| 693 | no_diff_means_no_output = | 
|---|
| 694 | (output_style == OUTPUT_IFDEF ? | 
|---|
| 695 | (!*group_format[UNCHANGED] | 
|---|
| 696 | || (strcmp (group_format[UNCHANGED], "%=") == 0 | 
|---|
| 697 | && !*line_format[UNCHANGED])) | 
|---|
| 698 | : (output_style != OUTPUT_SDIFF) | suppress_common_lines); | 
|---|
| 699 |  | 
|---|
| 700 | files_can_be_treated_as_binary = | 
|---|
| 701 | (brief | 
|---|
| 702 | & ~ (ignore_blank_lines | ignore_case | strip_trailing_cr | 
|---|
| 703 | | (ignore_regexp_list.regexps || ignore_white_space))); | 
|---|
| 704 |  | 
|---|
| 705 | switch_string = option_list (argv + 1, optind - 1); | 
|---|
| 706 |  | 
|---|
| 707 | if (from_file) | 
|---|
| 708 | { | 
|---|
| 709 | if (to_file) | 
|---|
| 710 | fatal ("--from-file and --to-file both specified"); | 
|---|
| 711 | else | 
|---|
| 712 | for (; optind < argc; optind++) | 
|---|
| 713 | { | 
|---|
| 714 | int status = compare_files ((struct comparison *) 0, | 
|---|
| 715 | from_file, argv[optind]); | 
|---|
| 716 | if (exit_status < status) | 
|---|
| 717 | exit_status = status; | 
|---|
| 718 | } | 
|---|
| 719 | } | 
|---|
| 720 | else | 
|---|
| 721 | { | 
|---|
| 722 | if (to_file) | 
|---|
| 723 | for (; optind < argc; optind++) | 
|---|
| 724 | { | 
|---|
| 725 | int status = compare_files ((struct comparison *) 0, | 
|---|
| 726 | argv[optind], to_file); | 
|---|
| 727 | if (exit_status < status) | 
|---|
| 728 | exit_status = status; | 
|---|
| 729 | } | 
|---|
| 730 | else | 
|---|
| 731 | { | 
|---|
| 732 | if (argc - optind != 2) | 
|---|
| 733 | { | 
|---|
| 734 | if (argc - optind < 2) | 
|---|
| 735 | try_help ("missing operand after `%s'", argv[argc - 1]); | 
|---|
| 736 | else | 
|---|
| 737 | try_help ("extra operand `%s'", argv[optind + 2]); | 
|---|
| 738 | } | 
|---|
| 739 |  | 
|---|
| 740 | exit_status = compare_files ((struct comparison *) 0, | 
|---|
| 741 | argv[optind], argv[optind + 1]); | 
|---|
| 742 | } | 
|---|
| 743 | } | 
|---|
| 744 |  | 
|---|
| 745 | /* Print any messages that were saved up for last.  */ | 
|---|
| 746 | print_message_queue (); | 
|---|
| 747 |  | 
|---|
| 748 | check_stdout (); | 
|---|
| 749 | exit (exit_status); | 
|---|
| 750 | return exit_status; | 
|---|
| 751 | } | 
|---|
| 752 |  | 
|---|
| 753 | /* Append to REGLIST the regexp PATTERN.  */ | 
|---|
| 754 |  | 
|---|
| 755 | static void | 
|---|
| 756 | add_regexp (struct regexp_list *reglist, char const *pattern) | 
|---|
| 757 | { | 
|---|
| 758 | size_t patlen = strlen (pattern); | 
|---|
| 759 | char const *m = re_compile_pattern (pattern, patlen, reglist->buf); | 
|---|
| 760 |  | 
|---|
| 761 | if (m != 0) | 
|---|
| 762 | error (0, 0, "%s: %s", pattern, m); | 
|---|
| 763 | else | 
|---|
| 764 | { | 
|---|
| 765 | char *regexps = reglist->regexps; | 
|---|
| 766 | size_t len = reglist->len; | 
|---|
| 767 | bool multiple_regexps = reglist->multiple_regexps = regexps != 0; | 
|---|
| 768 | size_t newlen = reglist->len = len + 2 * multiple_regexps + patlen; | 
|---|
| 769 | size_t size = reglist->size; | 
|---|
| 770 |  | 
|---|
| 771 | if (size <= newlen) | 
|---|
| 772 | { | 
|---|
| 773 | if (!size) | 
|---|
| 774 | size = 1; | 
|---|
| 775 |  | 
|---|
| 776 | do size *= 2; | 
|---|
| 777 | while (size <= newlen); | 
|---|
| 778 |  | 
|---|
| 779 | reglist->size = size; | 
|---|
| 780 | reglist->regexps = regexps = xrealloc (regexps, size); | 
|---|
| 781 | } | 
|---|
| 782 | if (multiple_regexps) | 
|---|
| 783 | { | 
|---|
| 784 | regexps[len++] = '\\'; | 
|---|
| 785 | regexps[len++] = '|'; | 
|---|
| 786 | } | 
|---|
| 787 | memcpy (regexps + len, pattern, patlen + 1); | 
|---|
| 788 | } | 
|---|
| 789 | } | 
|---|
| 790 |  | 
|---|
| 791 | /* Ensure that REGLIST represents the disjunction of its regexps. | 
|---|
| 792 | This is done here, rather than earlier, to avoid O(N^2) behavior.  */ | 
|---|
| 793 |  | 
|---|
| 794 | static void | 
|---|
| 795 | summarize_regexp_list (struct regexp_list *reglist) | 
|---|
| 796 | { | 
|---|
| 797 | if (reglist->regexps) | 
|---|
| 798 | { | 
|---|
| 799 | /* At least one regexp was specified.  Allocate a fastmap for it.  */ | 
|---|
| 800 | reglist->buf->fastmap = xmalloc (1 << CHAR_BIT); | 
|---|
| 801 | if (reglist->multiple_regexps) | 
|---|
| 802 | { | 
|---|
| 803 | /* Compile the disjunction of the regexps. | 
|---|
| 804 | (If just one regexp was specified, it is already compiled.)  */ | 
|---|
| 805 | char const *m = re_compile_pattern (reglist->regexps, reglist->len, | 
|---|
| 806 | reglist->buf); | 
|---|
| 807 | if (m != 0) | 
|---|
| 808 | error (EXIT_TROUBLE, 0, "%s: %s", reglist->regexps, m); | 
|---|
| 809 | } | 
|---|
| 810 | } | 
|---|
| 811 | } | 
|---|
| 812 |  | 
|---|
| 813 | static void | 
|---|
| 814 | try_help (char const *reason_msgid, char const *operand) | 
|---|
| 815 | { | 
|---|
| 816 | if (reason_msgid) | 
|---|
| 817 | error (0, 0, _(reason_msgid), operand); | 
|---|
| 818 | error (EXIT_TROUBLE, 0, _("Try `%s --help' for more information."), | 
|---|
| 819 | program_name); | 
|---|
| 820 | abort (); | 
|---|
| 821 | } | 
|---|
| 822 |  | 
|---|
| 823 | static void | 
|---|
| 824 | check_stdout (void) | 
|---|
| 825 | { | 
|---|
| 826 | if (ferror (stdout)) | 
|---|
| 827 | fatal ("write failed"); | 
|---|
| 828 | else if (fclose (stdout) != 0) | 
|---|
| 829 | pfatal_with_name (_("standard output")); | 
|---|
| 830 | } | 
|---|
| 831 |  | 
|---|
| 832 | static char const * const option_help_msgid[] = { | 
|---|
| 833 | N_("Compare files line by line."), | 
|---|
| 834 | "", | 
|---|
| 835 | N_("-i  --ignore-case  Ignore case differences in file contents."), | 
|---|
| 836 | N_("--ignore-file-name-case  Ignore case when comparing file names."), | 
|---|
| 837 | N_("--no-ignore-file-name-case  Consider case when comparing file names."), | 
|---|
| 838 | N_("-E  --ignore-tab-expansion  Ignore changes due to tab expansion."), | 
|---|
| 839 | N_("-b  --ignore-space-change  Ignore changes in the amount of white space."), | 
|---|
| 840 | N_("-w  --ignore-all-space  Ignore all white space."), | 
|---|
| 841 | N_("-B  --ignore-blank-lines  Ignore changes whose lines are all blank."), | 
|---|
| 842 | N_("-I RE  --ignore-matching-lines=RE  Ignore changes whose lines all match RE."), | 
|---|
| 843 | N_("--strip-trailing-cr  Strip trailing carriage return on input."), | 
|---|
| 844 | #if HAVE_SETMODE_DOS | 
|---|
| 845 | N_("--binary  Read and write data in binary mode."), | 
|---|
| 846 | #endif | 
|---|
| 847 | N_("-a  --text  Treat all files as text."), | 
|---|
| 848 | "", | 
|---|
| 849 | N_("-c  -C NUM  --context[=NUM]  Output NUM (default 3) lines of copied context.\n\ | 
|---|
| 850 | -u  -U NUM  --unified[=NUM]  Output NUM (default 3) lines of unified context.\n\ | 
|---|
| 851 | --label LABEL  Use LABEL instead of file name.\n\ | 
|---|
| 852 | -p  --show-c-function  Show which C function each change is in.\n\ | 
|---|
| 853 | -F RE  --show-function-line=RE  Show the most recent line matching RE."), | 
|---|
| 854 | N_("-q  --brief  Output only whether files differ."), | 
|---|
| 855 | N_("-e  --ed  Output an ed script."), | 
|---|
| 856 | N_("--normal  Output a normal diff."), | 
|---|
| 857 | N_("-n  --rcs  Output an RCS format diff."), | 
|---|
| 858 | N_("-y  --side-by-side  Output in two columns.\n\ | 
|---|
| 859 | -W NUM  --width=NUM  Output at most NUM (default 130) print columns.\n\ | 
|---|
| 860 | --left-column  Output only the left column of common lines.\n\ | 
|---|
| 861 | --suppress-common-lines  Do not output common lines."), | 
|---|
| 862 | N_("-D NAME  --ifdef=NAME  Output merged file to show `#ifdef NAME' diffs."), | 
|---|
| 863 | N_("--GTYPE-group-format=GFMT  Similar, but format GTYPE input groups with GFMT."), | 
|---|
| 864 | N_("--line-format=LFMT  Similar, but format all input lines with LFMT."), | 
|---|
| 865 | N_("--LTYPE-line-format=LFMT  Similar, but format LTYPE input lines with LFMT."), | 
|---|
| 866 | N_("  LTYPE is `old', `new', or `unchanged'.  GTYPE is LTYPE or `changed'."), | 
|---|
| 867 | N_("  GFMT may contain:\n\ | 
|---|
| 868 | %<  lines from FILE1\n\ | 
|---|
| 869 | %>  lines from FILE2\n\ | 
|---|
| 870 | %=  lines common to FILE1 and FILE2\n\ | 
|---|
| 871 | %[-][WIDTH][.[PREC]]{doxX}LETTER  printf-style spec for LETTER\n\ | 
|---|
| 872 | LETTERs are as follows for new group, lower case for old group:\n\ | 
|---|
| 873 | F  first line number\n\ | 
|---|
| 874 | L  last line number\n\ | 
|---|
| 875 | N  number of lines = L-F+1\n\ | 
|---|
| 876 | E  F-1\n\ | 
|---|
| 877 | M  L+1"), | 
|---|
| 878 | N_("  LFMT may contain:\n\ | 
|---|
| 879 | %L  contents of line\n\ | 
|---|
| 880 | %l  contents of line, excluding any trailing newline\n\ | 
|---|
| 881 | %[-][WIDTH][.[PREC]]{doxX}n  printf-style spec for input line number"), | 
|---|
| 882 | N_("  Either GFMT or LFMT may contain:\n\ | 
|---|
| 883 | %%  %\n\ | 
|---|
| 884 | %c'C'  the single character C\n\ | 
|---|
| 885 | %c'\\OOO'  the character with octal code OOO"), | 
|---|
| 886 | "", | 
|---|
| 887 | N_("-l  --paginate  Pass the output through `pr' to paginate it."), | 
|---|
| 888 | N_("-t  --expand-tabs  Expand tabs to spaces in output."), | 
|---|
| 889 | N_("-T  --initial-tab  Make tabs line up by prepending a tab."), | 
|---|
| 890 | "", | 
|---|
| 891 | N_("-r  --recursive  Recursively compare any subdirectories found."), | 
|---|
| 892 | N_("-N  --new-file  Treat absent files as empty."), | 
|---|
| 893 | N_("--unidirectional-new-file  Treat absent first files as empty."), | 
|---|
| 894 | N_("-s  --report-identical-files  Report when two files are the same."), | 
|---|
| 895 | N_("-x PAT  --exclude=PAT  Exclude files that match PAT."), | 
|---|
| 896 | N_("-X FILE  --exclude-from=FILE  Exclude files that match any pattern in FILE."), | 
|---|
| 897 | N_("-S FILE  --starting-file=FILE  Start with FILE when comparing directories."), | 
|---|
| 898 | N_("--from-file=FILE1  Compare FILE1 to all operands.  FILE1 can be a directory."), | 
|---|
| 899 | N_("--to-file=FILE2  Compare all operands to FILE2.  FILE2 can be a directory."), | 
|---|
| 900 | "", | 
|---|
| 901 | N_("--horizon-lines=NUM  Keep NUM lines of the common prefix and suffix."), | 
|---|
| 902 | N_("-d  --minimal  Try hard to find a smaller set of changes."), | 
|---|
| 903 | N_("--speed-large-files  Assume large files and many scattered small changes."), | 
|---|
| 904 | "", | 
|---|
| 905 | N_("-v  --version  Output version info."), | 
|---|
| 906 | N_("--help  Output this help."), | 
|---|
| 907 | "", | 
|---|
| 908 | N_("FILES are `FILE1 FILE2' or `DIR1 DIR2' or `DIR FILE...' or `FILE... DIR'."), | 
|---|
| 909 | N_("If --from-file or --to-file is given, there are no restrictions on FILES."), | 
|---|
| 910 | N_("If a FILE is `-', read standard input."), | 
|---|
| 911 | "", | 
|---|
| 912 | N_("Report bugs to <bug-gnu-utils@gnu.org>."), | 
|---|
| 913 | 0 | 
|---|
| 914 | }; | 
|---|
| 915 |  | 
|---|
| 916 | static void | 
|---|
| 917 | usage (void) | 
|---|
| 918 | { | 
|---|
| 919 | char const * const *p; | 
|---|
| 920 |  | 
|---|
| 921 | printf (_("Usage: %s [OPTION]... FILES\n"), program_name); | 
|---|
| 922 |  | 
|---|
| 923 | for (p = option_help_msgid;  *p;  p++) | 
|---|
| 924 | { | 
|---|
| 925 | if (!**p) | 
|---|
| 926 | putchar ('\n'); | 
|---|
| 927 | else | 
|---|
| 928 | { | 
|---|
| 929 | char const *msg = _(*p); | 
|---|
| 930 | char const *nl; | 
|---|
| 931 | while ((nl = strchr (msg, '\n'))) | 
|---|
| 932 | { | 
|---|
| 933 | int msglen = nl + 1 - msg; | 
|---|
| 934 | printf ("  %.*s", msglen, msg); | 
|---|
| 935 | msg = nl + 1; | 
|---|
| 936 | } | 
|---|
| 937 |  | 
|---|
| 938 | printf ("  %s\n" + 2 * (*msg != ' ' && *msg != '-'), msg); | 
|---|
| 939 | } | 
|---|
| 940 | } | 
|---|
| 941 | } | 
|---|
| 942 |  | 
|---|
| 943 | /* Set VAR to VALUE, reporting an OPTION error if this is a | 
|---|
| 944 | conflict.  */ | 
|---|
| 945 | static void | 
|---|
| 946 | specify_value (char const **var, char const *value, char const *option) | 
|---|
| 947 | { | 
|---|
| 948 | if (*var && strcmp (*var, value) != 0) | 
|---|
| 949 | { | 
|---|
| 950 | error (0, 0, _("conflicting %s option value `%s'"), option, value); | 
|---|
| 951 | try_help (0, 0); | 
|---|
| 952 | } | 
|---|
| 953 | *var = value; | 
|---|
| 954 | } | 
|---|
| 955 |  | 
|---|
| 956 | /* Set the output style to STYLE, diagnosing conflicts.  */ | 
|---|
| 957 | static void | 
|---|
| 958 | specify_style (enum output_style style) | 
|---|
| 959 | { | 
|---|
| 960 | if (output_style != style) | 
|---|
| 961 | { | 
|---|
| 962 | if (output_style != OUTPUT_UNSPECIFIED) | 
|---|
| 963 | try_help ("conflicting output style options", 0); | 
|---|
| 964 | output_style = style; | 
|---|
| 965 | } | 
|---|
| 966 | } | 
|---|
| 967 |  | 
|---|
| 968 |  | 
|---|
| 969 | static char const * | 
|---|
| 970 | filetype (struct stat const *st) | 
|---|
| 971 | { | 
|---|
| 972 | /* See POSIX 1003.1-2001 for these formats. | 
|---|
| 973 |  | 
|---|
| 974 | To keep diagnostics grammatical in English, the returned string | 
|---|
| 975 | must start with a consonant.  */ | 
|---|
| 976 |  | 
|---|
| 977 | if (S_ISREG (st->st_mode)) | 
|---|
| 978 | return st->st_size == 0 ? _("regular empty file") : _("regular file"); | 
|---|
| 979 |  | 
|---|
| 980 | if (S_ISDIR (st->st_mode)) return _("directory"); | 
|---|
| 981 |  | 
|---|
| 982 | #ifdef S_ISBLK | 
|---|
| 983 | if (S_ISBLK (st->st_mode)) return _("block special file"); | 
|---|
| 984 | #endif | 
|---|
| 985 | #ifdef S_ISCHR | 
|---|
| 986 | if (S_ISCHR (st->st_mode)) return _("character special file"); | 
|---|
| 987 | #endif | 
|---|
| 988 | #ifdef S_ISFIFO | 
|---|
| 989 | if (S_ISFIFO (st->st_mode)) return _("fifo"); | 
|---|
| 990 | #endif | 
|---|
| 991 | /* S_ISLNK is impossible with `fstat' and `stat'.  */ | 
|---|
| 992 | #ifdef S_ISSOCK | 
|---|
| 993 | if (S_ISSOCK (st->st_mode)) return _("socket"); | 
|---|
| 994 | #endif | 
|---|
| 995 | #ifdef S_TYPEISMQ | 
|---|
| 996 | if (S_TYPEISMQ (st)) return _("message queue"); | 
|---|
| 997 | #endif | 
|---|
| 998 | #ifdef S_TYPEISSEM | 
|---|
| 999 | if (S_TYPEISSEM (st)) return _("semaphore"); | 
|---|
| 1000 | #endif | 
|---|
| 1001 | #ifdef S_TYPEISSHM | 
|---|
| 1002 | if (S_TYPEISSHM (st)) return _("shared memory object"); | 
|---|
| 1003 | #endif | 
|---|
| 1004 | #ifdef S_TYPEISTMO | 
|---|
| 1005 | if (S_TYPEISTMO (st)) return _("typed memory object"); | 
|---|
| 1006 | #endif | 
|---|
| 1007 |  | 
|---|
| 1008 | return _("weird file"); | 
|---|
| 1009 | } | 
|---|
| 1010 |  | 
|---|
| 1011 |  | 
|---|
| 1012 | /* Set the last-modified time of *ST to be the current time.  */ | 
|---|
| 1013 |  | 
|---|
| 1014 | static void | 
|---|
| 1015 | set_mtime_to_now (struct stat *st) | 
|---|
| 1016 | { | 
|---|
| 1017 | #ifdef ST_MTIM_NSEC | 
|---|
| 1018 |  | 
|---|
| 1019 | # if HAVE_CLOCK_GETTIME && defined CLOCK_REALTIME | 
|---|
| 1020 | if (clock_gettime (CLOCK_REALTIME, &st->st_mtim) == 0) | 
|---|
| 1021 | return; | 
|---|
| 1022 | # endif | 
|---|
| 1023 |  | 
|---|
| 1024 | # if HAVE_GETTIMEOFDAY | 
|---|
| 1025 | { | 
|---|
| 1026 | struct timeval timeval; | 
|---|
| 1027 | if (gettimeofday (&timeval, NULL) == 0) | 
|---|
| 1028 | { | 
|---|
| 1029 | st->st_mtime = timeval.tv_sec; | 
|---|
| 1030 | st->st_mtim.ST_MTIM_NSEC = timeval.tv_usec * 1000; | 
|---|
| 1031 | return; | 
|---|
| 1032 | } | 
|---|
| 1033 | } | 
|---|
| 1034 | # endif | 
|---|
| 1035 |  | 
|---|
| 1036 | #endif /* ST_MTIM_NSEC */ | 
|---|
| 1037 |  | 
|---|
| 1038 | time (&st->st_mtime); | 
|---|
| 1039 | } | 
|---|
| 1040 |  | 
|---|
| 1041 |  | 
|---|
| 1042 | /* Compare two files (or dirs) with parent comparison PARENT | 
|---|
| 1043 | and names NAME0 and NAME1. | 
|---|
| 1044 | (If PARENT is 0, then the first name is just NAME0, etc.) | 
|---|
| 1045 | This is self-contained; it opens the files and closes them. | 
|---|
| 1046 |  | 
|---|
| 1047 | Value is EXIT_SUCCESS if files are the same, EXIT_FAILURE if | 
|---|
| 1048 | different, EXIT_TROUBLE if there is a problem opening them.  */ | 
|---|
| 1049 |  | 
|---|
| 1050 | static int | 
|---|
| 1051 | compare_files (struct comparison const *parent, | 
|---|
| 1052 | char const *name0, | 
|---|
| 1053 | char const *name1) | 
|---|
| 1054 | { | 
|---|
| 1055 | struct comparison cmp; | 
|---|
| 1056 | #define DIR_P(f) (S_ISDIR (cmp.file[f].stat.st_mode) != 0) | 
|---|
| 1057 | register int f; | 
|---|
| 1058 | int status = EXIT_SUCCESS; | 
|---|
| 1059 | bool same_files; | 
|---|
| 1060 | char *free0, *free1; | 
|---|
| 1061 |  | 
|---|
| 1062 | /* If this is directory comparison, perhaps we have a file | 
|---|
| 1063 | that exists only in one of the directories. | 
|---|
| 1064 | If so, just print a message to that effect.  */ | 
|---|
| 1065 |  | 
|---|
| 1066 | if (! ((name0 && name1) | 
|---|
| 1067 | || (unidirectional_new_file && name1) | 
|---|
| 1068 | || new_file)) | 
|---|
| 1069 | { | 
|---|
| 1070 | char const *name = name0 == 0 ? name1 : name0; | 
|---|
| 1071 | char const *dir = parent->file[name0 == 0].name; | 
|---|
| 1072 |  | 
|---|
| 1073 | /* See POSIX 1003.1-2001 for this format.  */ | 
|---|
| 1074 | message ("Only in %s: %s\n", dir, name); | 
|---|
| 1075 |  | 
|---|
| 1076 | /* Return EXIT_FAILURE so that diff_dirs will return | 
|---|
| 1077 | EXIT_FAILURE ("some files differ").  */ | 
|---|
| 1078 | return EXIT_FAILURE; | 
|---|
| 1079 | } | 
|---|
| 1080 |  | 
|---|
| 1081 | memset (cmp.file, 0, sizeof cmp.file); | 
|---|
| 1082 | cmp.parent = parent; | 
|---|
| 1083 |  | 
|---|
| 1084 | /* cmp.file[f].desc markers */ | 
|---|
| 1085 | #define NONEXISTENT (-1) /* nonexistent file */ | 
|---|
| 1086 | #define UNOPENED (-2) /* unopened file (e.g. directory) */ | 
|---|
| 1087 | #define ERRNO_ENCODE(errno) (-3 - (errno)) /* encoded errno value */ | 
|---|
| 1088 |  | 
|---|
| 1089 | #define ERRNO_DECODE(desc) (-3 - (desc)) /* inverse of ERRNO_ENCODE */ | 
|---|
| 1090 |  | 
|---|
| 1091 | cmp.file[0].desc = name0 == 0 ? NONEXISTENT : UNOPENED; | 
|---|
| 1092 | cmp.file[1].desc = name1 == 0 ? NONEXISTENT : UNOPENED; | 
|---|
| 1093 |  | 
|---|
| 1094 | /* Now record the full name of each file, including nonexistent ones.  */ | 
|---|
| 1095 |  | 
|---|
| 1096 | if (name0 == 0) | 
|---|
| 1097 | name0 = name1; | 
|---|
| 1098 | if (name1 == 0) | 
|---|
| 1099 | name1 = name0; | 
|---|
| 1100 |  | 
|---|
| 1101 | if (!parent) | 
|---|
| 1102 | { | 
|---|
| 1103 | free0 = 0; | 
|---|
| 1104 | free1 = 0; | 
|---|
| 1105 | cmp.file[0].name = name0; | 
|---|
| 1106 | cmp.file[1].name = name1; | 
|---|
| 1107 | } | 
|---|
| 1108 | else | 
|---|
| 1109 | { | 
|---|
| 1110 | cmp.file[0].name = free0 | 
|---|
| 1111 | = dir_file_pathname (parent->file[0].name, name0); | 
|---|
| 1112 | cmp.file[1].name = free1 | 
|---|
| 1113 | = dir_file_pathname (parent->file[1].name, name1); | 
|---|
| 1114 | } | 
|---|
| 1115 |  | 
|---|
| 1116 | /* Stat the files.  */ | 
|---|
| 1117 |  | 
|---|
| 1118 | for (f = 0; f < 2; f++) | 
|---|
| 1119 | { | 
|---|
| 1120 | if (cmp.file[f].desc != NONEXISTENT) | 
|---|
| 1121 | { | 
|---|
| 1122 | if (f && file_name_cmp (cmp.file[f].name, cmp.file[0].name) == 0) | 
|---|
| 1123 | { | 
|---|
| 1124 | cmp.file[f].desc = cmp.file[0].desc; | 
|---|
| 1125 | cmp.file[f].stat = cmp.file[0].stat; | 
|---|
| 1126 | } | 
|---|
| 1127 | else if (strcmp (cmp.file[f].name, "-") == 0) | 
|---|
| 1128 | { | 
|---|
| 1129 | cmp.file[f].desc = STDIN_FILENO; | 
|---|
| 1130 | if (fstat (STDIN_FILENO, &cmp.file[f].stat) != 0) | 
|---|
| 1131 | cmp.file[f].desc = ERRNO_ENCODE (errno); | 
|---|
| 1132 | else | 
|---|
| 1133 | { | 
|---|
| 1134 | if (S_ISREG (cmp.file[f].stat.st_mode)) | 
|---|
| 1135 | { | 
|---|
| 1136 | off_t pos = lseek (STDIN_FILENO, (off_t) 0, SEEK_CUR); | 
|---|
| 1137 | if (pos < 0) | 
|---|
| 1138 | cmp.file[f].desc = ERRNO_ENCODE (errno); | 
|---|
| 1139 | else | 
|---|
| 1140 | cmp.file[f].stat.st_size = | 
|---|
| 1141 | MAX (0, cmp.file[f].stat.st_size - pos); | 
|---|
| 1142 | } | 
|---|
| 1143 |  | 
|---|
| 1144 | /* POSIX 1003.1-2001 requires current time for | 
|---|
| 1145 | stdin.  */ | 
|---|
| 1146 | set_mtime_to_now (&cmp.file[f].stat); | 
|---|
| 1147 | } | 
|---|
| 1148 | } | 
|---|
| 1149 | else if (stat (cmp.file[f].name, &cmp.file[f].stat) != 0) | 
|---|
| 1150 | cmp.file[f].desc = ERRNO_ENCODE (errno); | 
|---|
| 1151 | } | 
|---|
| 1152 | } | 
|---|
| 1153 |  | 
|---|
| 1154 | /* Mark files as nonexistent at the top level as needed for -N and | 
|---|
| 1155 | --unidirectional-new-file.  */ | 
|---|
| 1156 | if (! parent) | 
|---|
| 1157 | { | 
|---|
| 1158 | if ((new_file | unidirectional_new_file) | 
|---|
| 1159 | && cmp.file[0].desc == ERRNO_ENCODE (ENOENT) | 
|---|
| 1160 | && cmp.file[1].desc == UNOPENED) | 
|---|
| 1161 | cmp.file[0].desc = NONEXISTENT; | 
|---|
| 1162 |  | 
|---|
| 1163 | if (new_file | 
|---|
| 1164 | && cmp.file[0].desc == UNOPENED | 
|---|
| 1165 | && cmp.file[1].desc == ERRNO_ENCODE (ENOENT)) | 
|---|
| 1166 | cmp.file[1].desc = NONEXISTENT; | 
|---|
| 1167 | } | 
|---|
| 1168 |  | 
|---|
| 1169 | for (f = 0; f < 2; f++) | 
|---|
| 1170 | if (cmp.file[f].desc == NONEXISTENT) | 
|---|
| 1171 | cmp.file[f].stat.st_mode = cmp.file[1 - f].stat.st_mode; | 
|---|
| 1172 |  | 
|---|
| 1173 | for (f = 0; f < 2; f++) | 
|---|
| 1174 | { | 
|---|
| 1175 | int e = ERRNO_DECODE (cmp.file[f].desc); | 
|---|
| 1176 | if (0 <= e) | 
|---|
| 1177 | { | 
|---|
| 1178 | errno = e; | 
|---|
| 1179 | perror_with_name (cmp.file[f].name); | 
|---|
| 1180 | status = EXIT_TROUBLE; | 
|---|
| 1181 | } | 
|---|
| 1182 | } | 
|---|
| 1183 |  | 
|---|
| 1184 | if (status == EXIT_SUCCESS && ! parent && DIR_P (0) != DIR_P (1)) | 
|---|
| 1185 | { | 
|---|
| 1186 | /* If one is a directory, and it was specified in the command line, | 
|---|
| 1187 | use the file in that dir with the other file's basename.  */ | 
|---|
| 1188 |  | 
|---|
| 1189 | int fnm_arg = DIR_P (0); | 
|---|
| 1190 | int dir_arg = 1 - fnm_arg; | 
|---|
| 1191 | char const *fnm = cmp.file[fnm_arg].name; | 
|---|
| 1192 | char const *dir = cmp.file[dir_arg].name; | 
|---|
| 1193 | char const *filename = cmp.file[dir_arg].name = free0 | 
|---|
| 1194 | = dir_file_pathname (dir, base_name (fnm)); | 
|---|
| 1195 |  | 
|---|
| 1196 | if (strcmp (fnm, "-") == 0) | 
|---|
| 1197 | fatal ("cannot compare `-' to a directory"); | 
|---|
| 1198 |  | 
|---|
| 1199 | if (stat (filename, &cmp.file[dir_arg].stat) != 0) | 
|---|
| 1200 | { | 
|---|
| 1201 | perror_with_name (filename); | 
|---|
| 1202 | status = EXIT_TROUBLE; | 
|---|
| 1203 | } | 
|---|
| 1204 | } | 
|---|
| 1205 |  | 
|---|
| 1206 | if (status != EXIT_SUCCESS) | 
|---|
| 1207 | { | 
|---|
| 1208 | /* One of the files should exist but does not.  */ | 
|---|
| 1209 | } | 
|---|
| 1210 | else if ((same_files | 
|---|
| 1211 | = (cmp.file[0].desc != NONEXISTENT | 
|---|
| 1212 | && cmp.file[1].desc != NONEXISTENT | 
|---|
| 1213 | && 0 < same_file (&cmp.file[0].stat, &cmp.file[1].stat) | 
|---|
| 1214 | && same_file_attributes (&cmp.file[0].stat, | 
|---|
| 1215 | &cmp.file[1].stat))) | 
|---|
| 1216 | && no_diff_means_no_output) | 
|---|
| 1217 | { | 
|---|
| 1218 | /* The two named files are actually the same physical file. | 
|---|
| 1219 | We know they are identical without actually reading them.  */ | 
|---|
| 1220 | } | 
|---|
| 1221 | else if (DIR_P (0) & DIR_P (1)) | 
|---|
| 1222 | { | 
|---|
| 1223 | if (output_style == OUTPUT_IFDEF) | 
|---|
| 1224 | fatal ("-D option not supported with directories"); | 
|---|
| 1225 |  | 
|---|
| 1226 | /* If both are directories, compare the files in them.  */ | 
|---|
| 1227 |  | 
|---|
| 1228 | if (parent && !recursive) | 
|---|
| 1229 | { | 
|---|
| 1230 | /* But don't compare dir contents one level down | 
|---|
| 1231 | unless -r was specified. | 
|---|
| 1232 | See POSIX 1003.1-2001 for this format.  */ | 
|---|
| 1233 | message ("Common subdirectories: %s and %s\n", | 
|---|
| 1234 | cmp.file[0].name, cmp.file[1].name); | 
|---|
| 1235 | } | 
|---|
| 1236 | else | 
|---|
| 1237 | status = diff_dirs (&cmp, compare_files); | 
|---|
| 1238 | } | 
|---|
| 1239 | else if ((DIR_P (0) | DIR_P (1)) | 
|---|
| 1240 | || (parent | 
|---|
| 1241 | && (! S_ISREG (cmp.file[0].stat.st_mode) | 
|---|
| 1242 | || ! S_ISREG (cmp.file[1].stat.st_mode)))) | 
|---|
| 1243 | { | 
|---|
| 1244 | if (cmp.file[0].desc == NONEXISTENT || cmp.file[1].desc == NONEXISTENT) | 
|---|
| 1245 | { | 
|---|
| 1246 | /* We have a subdirectory that exists only in one directory.  */ | 
|---|
| 1247 |  | 
|---|
| 1248 | if ((DIR_P (0) | DIR_P (1)) | 
|---|
| 1249 | && recursive | 
|---|
| 1250 | && (new_file | 
|---|
| 1251 | || (unidirectional_new_file | 
|---|
| 1252 | && cmp.file[0].desc == NONEXISTENT))) | 
|---|
| 1253 | status = diff_dirs (&cmp, compare_files); | 
|---|
| 1254 | else | 
|---|
| 1255 | { | 
|---|
| 1256 | char const *dir | 
|---|
| 1257 | = parent->file[cmp.file[0].desc == NONEXISTENT].name; | 
|---|
| 1258 |  | 
|---|
| 1259 | /* See POSIX 1003.1-2001 for this format.  */ | 
|---|
| 1260 | message ("Only in %s: %s\n", dir, name0); | 
|---|
| 1261 |  | 
|---|
| 1262 | status = EXIT_FAILURE; | 
|---|
| 1263 | } | 
|---|
| 1264 | } | 
|---|
| 1265 | else | 
|---|
| 1266 | { | 
|---|
| 1267 | /* We have two files that are not to be compared.  */ | 
|---|
| 1268 |  | 
|---|
| 1269 | /* See POSIX 1003.1-2001 for this format.  */ | 
|---|
| 1270 | message5 ("File %s is a %s while file %s is a %s\n", | 
|---|
| 1271 | file_label[0] ? file_label[0] : cmp.file[0].name, | 
|---|
| 1272 | filetype (&cmp.file[0].stat), | 
|---|
| 1273 | file_label[1] ? file_label[1] : cmp.file[1].name, | 
|---|
| 1274 | filetype (&cmp.file[1].stat)); | 
|---|
| 1275 |  | 
|---|
| 1276 | /* This is a difference.  */ | 
|---|
| 1277 | status = EXIT_FAILURE; | 
|---|
| 1278 | } | 
|---|
| 1279 | } | 
|---|
| 1280 | else if (files_can_be_treated_as_binary | 
|---|
| 1281 | && cmp.file[0].stat.st_size != cmp.file[1].stat.st_size | 
|---|
| 1282 | && (cmp.file[0].desc == NONEXISTENT | 
|---|
| 1283 | || S_ISREG (cmp.file[0].stat.st_mode)) | 
|---|
| 1284 | && (cmp.file[1].desc == NONEXISTENT | 
|---|
| 1285 | || S_ISREG (cmp.file[1].stat.st_mode))) | 
|---|
| 1286 | { | 
|---|
| 1287 | message ("Files %s and %s differ\n", | 
|---|
| 1288 | file_label[0] ? file_label[0] : cmp.file[0].name, | 
|---|
| 1289 | file_label[1] ? file_label[1] : cmp.file[1].name); | 
|---|
| 1290 | status = EXIT_FAILURE; | 
|---|
| 1291 | } | 
|---|
| 1292 | else | 
|---|
| 1293 | { | 
|---|
| 1294 | /* Both exist and neither is a directory.  */ | 
|---|
| 1295 |  | 
|---|
| 1296 | /* Open the files and record their descriptors.  */ | 
|---|
| 1297 |  | 
|---|
| 1298 | if (cmp.file[0].desc == UNOPENED) | 
|---|
| 1299 | if ((cmp.file[0].desc = open (cmp.file[0].name, O_RDONLY, 0)) < 0) | 
|---|
| 1300 | { | 
|---|
| 1301 | perror_with_name (cmp.file[0].name); | 
|---|
| 1302 | status = EXIT_TROUBLE; | 
|---|
| 1303 | } | 
|---|
| 1304 | if (cmp.file[1].desc == UNOPENED) | 
|---|
| 1305 | { | 
|---|
| 1306 | if (same_files) | 
|---|
| 1307 | cmp.file[1].desc = cmp.file[0].desc; | 
|---|
| 1308 | else if ((cmp.file[1].desc = open (cmp.file[1].name, O_RDONLY, 0)) | 
|---|
| 1309 | < 0) | 
|---|
| 1310 | { | 
|---|
| 1311 | perror_with_name (cmp.file[1].name); | 
|---|
| 1312 | status = EXIT_TROUBLE; | 
|---|
| 1313 | } | 
|---|
| 1314 | } | 
|---|
| 1315 |  | 
|---|
| 1316 | #if HAVE_SETMODE_DOS | 
|---|
| 1317 | if (binary) | 
|---|
| 1318 | for (f = 0; f < 2; f++) | 
|---|
| 1319 | if (0 <= cmp.file[f].desc) | 
|---|
| 1320 | set_binary_mode (cmp.file[f].desc, 1); | 
|---|
| 1321 | #endif | 
|---|
| 1322 |  | 
|---|
| 1323 | /* Compare the files, if no error was found.  */ | 
|---|
| 1324 |  | 
|---|
| 1325 | if (status == EXIT_SUCCESS) | 
|---|
| 1326 | status = diff_2_files (&cmp); | 
|---|
| 1327 |  | 
|---|
| 1328 | /* Close the file descriptors.  */ | 
|---|
| 1329 |  | 
|---|
| 1330 | if (0 <= cmp.file[0].desc && close (cmp.file[0].desc) != 0) | 
|---|
| 1331 | { | 
|---|
| 1332 | perror_with_name (cmp.file[0].name); | 
|---|
| 1333 | status = EXIT_TROUBLE; | 
|---|
| 1334 | } | 
|---|
| 1335 | if (0 <= cmp.file[1].desc && cmp.file[0].desc != cmp.file[1].desc | 
|---|
| 1336 | && close (cmp.file[1].desc) != 0) | 
|---|
| 1337 | { | 
|---|
| 1338 | perror_with_name (cmp.file[1].name); | 
|---|
| 1339 | status = EXIT_TROUBLE; | 
|---|
| 1340 | } | 
|---|
| 1341 | } | 
|---|
| 1342 |  | 
|---|
| 1343 | /* Now the comparison has been done, if no error prevented it, | 
|---|
| 1344 | and STATUS is the value this function will return.  */ | 
|---|
| 1345 |  | 
|---|
| 1346 | if (status == EXIT_SUCCESS) | 
|---|
| 1347 | { | 
|---|
| 1348 | if (report_identical_files && !DIR_P (0)) | 
|---|
| 1349 | message ("Files %s and %s are identical\n", | 
|---|
| 1350 | file_label[0] ? file_label[0] : cmp.file[0].name, | 
|---|
| 1351 | file_label[1] ? file_label[1] : cmp.file[1].name); | 
|---|
| 1352 | } | 
|---|
| 1353 | else | 
|---|
| 1354 | { | 
|---|
| 1355 | /* Flush stdout so that the user sees differences immediately. | 
|---|
| 1356 | This can hurt performance, unfortunately.  */ | 
|---|
| 1357 | if (fflush (stdout) != 0) | 
|---|
| 1358 | pfatal_with_name (_("standard output")); | 
|---|
| 1359 | } | 
|---|
| 1360 |  | 
|---|
| 1361 | if (free0) | 
|---|
| 1362 | free (free0); | 
|---|
| 1363 | if (free1) | 
|---|
| 1364 | free (free1); | 
|---|
| 1365 |  | 
|---|
| 1366 | return status; | 
|---|
| 1367 | } | 
|---|