118fd37a7SXin LI /* diff - compare files line by line 218fd37a7SXin LI 318fd37a7SXin LI Copyright (C) 1988, 1989, 1992, 1993, 1994, 1996, 1998, 2001, 2002, 418fd37a7SXin LI 2004 Free Software Foundation, Inc. 518fd37a7SXin LI 618fd37a7SXin LI This file is part of GNU DIFF. 718fd37a7SXin LI 818fd37a7SXin LI GNU DIFF is free software; you can redistribute it and/or modify 918fd37a7SXin LI it under the terms of the GNU General Public License as published by 1018fd37a7SXin LI the Free Software Foundation; either version 2, or (at your option) 1118fd37a7SXin LI any later version. 1218fd37a7SXin LI 1318fd37a7SXin LI GNU DIFF is distributed in the hope that it will be useful, 1418fd37a7SXin LI but WITHOUT ANY WARRANTY; without even the implied warranty of 1518fd37a7SXin LI MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 1618fd37a7SXin LI See the GNU General Public License for more details. 1718fd37a7SXin LI 1818fd37a7SXin LI You should have received a copy of the GNU General Public License 1918fd37a7SXin LI along with GNU DIFF; see the file COPYING. 2018fd37a7SXin LI If not, write to the Free Software Foundation, 2118fd37a7SXin LI 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 2218fd37a7SXin LI 2318fd37a7SXin LI #define GDIFF_MAIN 2418fd37a7SXin LI #include "diff.h" 2518fd37a7SXin LI #include "paths.h" 2618fd37a7SXin LI #include <c-stack.h> 2718fd37a7SXin LI #include <dirname.h> 2818fd37a7SXin LI #include <error.h> 2918fd37a7SXin LI #include <exclude.h> 3018fd37a7SXin LI #include <exit.h> 3118fd37a7SXin LI #include <exitfail.h> 3218fd37a7SXin LI #include <file-type.h> 3318fd37a7SXin LI #include <fnmatch.h> 3418fd37a7SXin LI #include <getopt.h> 3518fd37a7SXin LI #include <hard-locale.h> 3618fd37a7SXin LI #include <posixver.h> 3718fd37a7SXin LI #include <prepargs.h> 3818fd37a7SXin LI #include <quotesys.h> 3918fd37a7SXin LI #include <setmode.h> 4018fd37a7SXin LI #include <version-etc.h> 4118fd37a7SXin LI #include <xalloc.h> 4218fd37a7SXin LI 4318fd37a7SXin LI #ifndef GUTTER_WIDTH_MINIMUM 4418fd37a7SXin LI # define GUTTER_WIDTH_MINIMUM 3 4518fd37a7SXin LI #endif 4618fd37a7SXin LI 4718fd37a7SXin LI struct regexp_list 4818fd37a7SXin LI { 4918fd37a7SXin LI char *regexps; /* chars representing disjunction of the regexps */ 5018fd37a7SXin LI size_t len; /* chars used in `regexps' */ 5118fd37a7SXin LI size_t size; /* size malloc'ed for `regexps'; 0 if not malloc'ed */ 5218fd37a7SXin LI bool multiple_regexps;/* Does `regexps' represent a disjunction? */ 5318fd37a7SXin LI struct re_pattern_buffer *buf; 5418fd37a7SXin LI }; 5518fd37a7SXin LI 5618fd37a7SXin LI static int compare_files (struct comparison const *, char const *, char const *); 5718fd37a7SXin LI static void add_regexp (struct regexp_list *, char const *); 5818fd37a7SXin LI static void summarize_regexp_list (struct regexp_list *); 5918fd37a7SXin LI static void specify_style (enum output_style); 6018fd37a7SXin LI static void specify_value (char const **, char const *, char const *); 6118fd37a7SXin LI static void try_help (char const *, char const *) __attribute__((noreturn)); 6218fd37a7SXin LI static void check_stdout (void); 6318fd37a7SXin LI static void usage (void); 6418fd37a7SXin LI 6518fd37a7SXin LI /* If comparing directories, compare their common subdirectories 6618fd37a7SXin LI recursively. */ 6718fd37a7SXin LI static bool recursive; 6818fd37a7SXin LI 6918fd37a7SXin LI /* In context diffs, show previous lines that match these regexps. */ 7018fd37a7SXin LI static struct regexp_list function_regexp_list; 7118fd37a7SXin LI 7218fd37a7SXin LI /* Ignore changes affecting only lines that match these regexps. */ 7318fd37a7SXin LI static struct regexp_list ignore_regexp_list; 7418fd37a7SXin LI 7518fd37a7SXin LI #if HAVE_SETMODE_DOS 7618fd37a7SXin LI /* Use binary I/O when reading and writing data (--binary). 7718fd37a7SXin LI On POSIX hosts, this has no effect. */ 7818fd37a7SXin LI static bool binary; 7918fd37a7SXin LI #else 8018fd37a7SXin LI enum { binary = true }; 8118fd37a7SXin LI #endif 8218fd37a7SXin LI 8318fd37a7SXin LI /* When comparing directories, if a file appears only in one 8418fd37a7SXin LI directory, treat it as present but empty in the other (-N). 8518fd37a7SXin LI Then `patch' would create the file with appropriate contents. */ 8618fd37a7SXin LI static bool new_file; 8718fd37a7SXin LI 8818fd37a7SXin LI /* When comparing directories, if a file appears only in the second 8918fd37a7SXin LI directory of the two, treat it as present but empty in the other 9018fd37a7SXin LI (--unidirectional-new-file). 9118fd37a7SXin LI Then `patch' would create the file with appropriate contents. */ 9218fd37a7SXin LI static bool unidirectional_new_file; 9318fd37a7SXin LI 9418fd37a7SXin LI /* Report files compared that are the same (-s). 9518fd37a7SXin LI Normally nothing is output when that happens. */ 9618fd37a7SXin LI static bool report_identical_files; 9718fd37a7SXin LI 9818fd37a7SXin LI 9918fd37a7SXin LI /* Return a string containing the command options with which diff was invoked. 10018fd37a7SXin LI Spaces appear between what were separate ARGV-elements. 10118fd37a7SXin LI There is a space at the beginning but none at the end. 10218fd37a7SXin LI If there were no options, the result is an empty string. 10318fd37a7SXin LI 10418fd37a7SXin LI Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT, 10518fd37a7SXin LI the length of that vector. */ 10618fd37a7SXin LI 10718fd37a7SXin LI static char * 10818fd37a7SXin LI option_list (char **optionvec, int count) 10918fd37a7SXin LI { 11018fd37a7SXin LI int i; 11118fd37a7SXin LI size_t size = 1; 11218fd37a7SXin LI char *result; 11318fd37a7SXin LI char *p; 11418fd37a7SXin LI 11518fd37a7SXin LI for (i = 0; i < count; i++) 11618fd37a7SXin LI size += 1 + quote_system_arg ((char *) 0, optionvec[i]); 11718fd37a7SXin LI 11818fd37a7SXin LI p = result = xmalloc (size); 11918fd37a7SXin LI 12018fd37a7SXin LI for (i = 0; i < count; i++) 12118fd37a7SXin LI { 12218fd37a7SXin LI *p++ = ' '; 12318fd37a7SXin LI p += quote_system_arg (p, optionvec[i]); 12418fd37a7SXin LI } 12518fd37a7SXin LI 12618fd37a7SXin LI *p = 0; 12718fd37a7SXin LI return result; 12818fd37a7SXin LI } 12918fd37a7SXin LI 13018fd37a7SXin LI 13118fd37a7SXin LI /* Return an option value suitable for add_exclude. */ 13218fd37a7SXin LI 13318fd37a7SXin LI static int 13418fd37a7SXin LI exclude_options (void) 13518fd37a7SXin LI { 13618fd37a7SXin LI return EXCLUDE_WILDCARDS | (ignore_file_name_case ? FNM_CASEFOLD : 0); 13718fd37a7SXin LI } 13818fd37a7SXin LI 13918fd37a7SXin LI static char const shortopts[] = 14018fd37a7SXin LI "0123456789abBcC:dD:eEfF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:y"; 14118fd37a7SXin LI 14218fd37a7SXin LI /* Values for long options that do not have single-letter equivalents. */ 14318fd37a7SXin LI enum 14418fd37a7SXin LI { 14518fd37a7SXin LI BINARY_OPTION = CHAR_MAX + 1, 14618fd37a7SXin LI FROM_FILE_OPTION, 14718fd37a7SXin LI HELP_OPTION, 14818fd37a7SXin LI HORIZON_LINES_OPTION, 14918fd37a7SXin LI IGNORE_FILE_NAME_CASE_OPTION, 15018fd37a7SXin LI INHIBIT_HUNK_MERGE_OPTION, 15118fd37a7SXin LI LEFT_COLUMN_OPTION, 15218fd37a7SXin LI LINE_FORMAT_OPTION, 15318fd37a7SXin LI NO_IGNORE_FILE_NAME_CASE_OPTION, 15418fd37a7SXin LI NORMAL_OPTION, 15518fd37a7SXin LI SDIFF_MERGE_ASSIST_OPTION, 15618fd37a7SXin LI STRIP_TRAILING_CR_OPTION, 15718fd37a7SXin LI SUPPRESS_COMMON_LINES_OPTION, 15818fd37a7SXin LI TABSIZE_OPTION, 15918fd37a7SXin LI TO_FILE_OPTION, 16018fd37a7SXin LI 16118fd37a7SXin LI /* These options must be in sequence. */ 16218fd37a7SXin LI UNCHANGED_LINE_FORMAT_OPTION, 16318fd37a7SXin LI OLD_LINE_FORMAT_OPTION, 16418fd37a7SXin LI NEW_LINE_FORMAT_OPTION, 16518fd37a7SXin LI 16618fd37a7SXin LI /* These options must be in sequence. */ 16718fd37a7SXin LI UNCHANGED_GROUP_FORMAT_OPTION, 16818fd37a7SXin LI OLD_GROUP_FORMAT_OPTION, 16918fd37a7SXin LI NEW_GROUP_FORMAT_OPTION, 17018fd37a7SXin LI CHANGED_GROUP_FORMAT_OPTION 17118fd37a7SXin LI }; 17218fd37a7SXin LI 17318fd37a7SXin LI static char const group_format_option[][sizeof "--unchanged-group-format"] = 17418fd37a7SXin LI { 17518fd37a7SXin LI "--unchanged-group-format", 17618fd37a7SXin LI "--old-group-format", 17718fd37a7SXin LI "--new-group-format", 17818fd37a7SXin LI "--changed-group-format" 17918fd37a7SXin LI }; 18018fd37a7SXin LI 18118fd37a7SXin LI static char const line_format_option[][sizeof "--unchanged-line-format"] = 18218fd37a7SXin LI { 18318fd37a7SXin LI "--unchanged-line-format", 18418fd37a7SXin LI "--old-line-format", 18518fd37a7SXin LI "--new-line-format" 18618fd37a7SXin LI }; 18718fd37a7SXin LI 18818fd37a7SXin LI static struct option const longopts[] = 18918fd37a7SXin LI { 19018fd37a7SXin LI {"binary", 0, 0, BINARY_OPTION}, 19118fd37a7SXin LI {"brief", 0, 0, 'q'}, 19218fd37a7SXin LI {"changed-group-format", 1, 0, CHANGED_GROUP_FORMAT_OPTION}, 19318fd37a7SXin LI {"context", 2, 0, 'C'}, 19418fd37a7SXin LI {"ed", 0, 0, 'e'}, 19518fd37a7SXin LI {"exclude", 1, 0, 'x'}, 19618fd37a7SXin LI {"exclude-from", 1, 0, 'X'}, 19718fd37a7SXin LI {"expand-tabs", 0, 0, 't'}, 19818fd37a7SXin LI {"forward-ed", 0, 0, 'f'}, 19918fd37a7SXin LI {"from-file", 1, 0, FROM_FILE_OPTION}, 20018fd37a7SXin LI {"help", 0, 0, HELP_OPTION}, 20118fd37a7SXin LI {"horizon-lines", 1, 0, HORIZON_LINES_OPTION}, 20218fd37a7SXin LI {"ifdef", 1, 0, 'D'}, 20318fd37a7SXin LI {"ignore-all-space", 0, 0, 'w'}, 20418fd37a7SXin LI {"ignore-blank-lines", 0, 0, 'B'}, 20518fd37a7SXin LI {"ignore-case", 0, 0, 'i'}, 20618fd37a7SXin LI {"ignore-file-name-case", 0, 0, IGNORE_FILE_NAME_CASE_OPTION}, 20718fd37a7SXin LI {"ignore-matching-lines", 1, 0, 'I'}, 20818fd37a7SXin LI {"ignore-space-change", 0, 0, 'b'}, 20918fd37a7SXin LI {"ignore-tab-expansion", 0, 0, 'E'}, 21018fd37a7SXin LI {"inhibit-hunk-merge", 0, 0, INHIBIT_HUNK_MERGE_OPTION}, 21118fd37a7SXin LI {"initial-tab", 0, 0, 'T'}, 21218fd37a7SXin LI {"label", 1, 0, 'L'}, 21318fd37a7SXin LI {"left-column", 0, 0, LEFT_COLUMN_OPTION}, 21418fd37a7SXin LI {"line-format", 1, 0, LINE_FORMAT_OPTION}, 21518fd37a7SXin LI {"minimal", 0, 0, 'd'}, 21618fd37a7SXin LI {"new-file", 0, 0, 'N'}, 21718fd37a7SXin LI {"new-group-format", 1, 0, NEW_GROUP_FORMAT_OPTION}, 21818fd37a7SXin LI {"new-line-format", 1, 0, NEW_LINE_FORMAT_OPTION}, 21918fd37a7SXin LI {"no-ignore-file-name-case", 0, 0, NO_IGNORE_FILE_NAME_CASE_OPTION}, 22018fd37a7SXin LI {"normal", 0, 0, NORMAL_OPTION}, 22118fd37a7SXin LI {"old-group-format", 1, 0, OLD_GROUP_FORMAT_OPTION}, 22218fd37a7SXin LI {"old-line-format", 1, 0, OLD_LINE_FORMAT_OPTION}, 22318fd37a7SXin LI {"paginate", 0, 0, 'l'}, 22418fd37a7SXin LI {"rcs", 0, 0, 'n'}, 22518fd37a7SXin LI {"recursive", 0, 0, 'r'}, 22618fd37a7SXin LI {"report-identical-files", 0, 0, 's'}, 22718fd37a7SXin LI {"sdiff-merge-assist", 0, 0, SDIFF_MERGE_ASSIST_OPTION}, 22818fd37a7SXin LI {"show-c-function", 0, 0, 'p'}, 22918fd37a7SXin LI {"show-function-line", 1, 0, 'F'}, 23018fd37a7SXin LI {"side-by-side", 0, 0, 'y'}, 23118fd37a7SXin LI {"speed-large-files", 0, 0, 'H'}, 23218fd37a7SXin LI {"starting-file", 1, 0, 'S'}, 23318fd37a7SXin LI {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION}, 23418fd37a7SXin LI {"suppress-common-lines", 0, 0, SUPPRESS_COMMON_LINES_OPTION}, 23518fd37a7SXin LI {"tabsize", 1, 0, TABSIZE_OPTION}, 23618fd37a7SXin LI {"text", 0, 0, 'a'}, 23718fd37a7SXin LI {"to-file", 1, 0, TO_FILE_OPTION}, 23818fd37a7SXin LI {"unchanged-group-format", 1, 0, UNCHANGED_GROUP_FORMAT_OPTION}, 23918fd37a7SXin LI {"unchanged-line-format", 1, 0, UNCHANGED_LINE_FORMAT_OPTION}, 24018fd37a7SXin LI {"unidirectional-new-file", 0, 0, 'P'}, 24118fd37a7SXin LI {"unified", 2, 0, 'U'}, 24218fd37a7SXin LI {"version", 0, 0, 'v'}, 24318fd37a7SXin LI {"width", 1, 0, 'W'}, 24418fd37a7SXin LI {0, 0, 0, 0} 24518fd37a7SXin LI }; 24618fd37a7SXin LI 24718fd37a7SXin LI int 24818fd37a7SXin LI main (int argc, char **argv) 24918fd37a7SXin LI { 25018fd37a7SXin LI int exit_status = EXIT_SUCCESS; 25118fd37a7SXin LI int c; 25218fd37a7SXin LI int i; 25318fd37a7SXin LI int prev = -1; 25418fd37a7SXin LI lin ocontext = -1; 25518fd37a7SXin LI bool explicit_context = false; 25618fd37a7SXin LI size_t width = 0; 25718fd37a7SXin LI bool show_c_function = false; 25818fd37a7SXin LI char const *from_file = 0; 25918fd37a7SXin LI char const *to_file = 0; 26018fd37a7SXin LI uintmax_t numval; 26118fd37a7SXin LI char *numend; 26218fd37a7SXin LI 26318fd37a7SXin LI /* Do our initializations. */ 26418fd37a7SXin LI exit_failure = 2; 26518fd37a7SXin LI initialize_main (&argc, &argv); 26618fd37a7SXin LI program_name = argv[0]; 26718fd37a7SXin LI setlocale (LC_ALL, ""); 26818fd37a7SXin LI bindtextdomain (PACKAGE, LOCALEDIR); 26918fd37a7SXin LI textdomain (PACKAGE); 27018fd37a7SXin LI c_stack_action (0); 27118fd37a7SXin LI function_regexp_list.buf = &function_regexp; 27218fd37a7SXin LI ignore_regexp_list.buf = &ignore_regexp; 27318fd37a7SXin LI re_set_syntax (RE_SYNTAX_GREP | RE_NO_POSIX_BACKTRACKING); 27418fd37a7SXin LI excluded = new_exclude (); 27518fd37a7SXin LI 27618fd37a7SXin LI /* Decode the options. */ 27718fd37a7SXin LI 27818fd37a7SXin LI while ((c = getopt_long (argc, argv, shortopts, longopts, 0)) != -1) 27918fd37a7SXin LI { 28018fd37a7SXin LI switch (c) 28118fd37a7SXin LI { 28218fd37a7SXin LI case 0: 28318fd37a7SXin LI break; 28418fd37a7SXin LI 28518fd37a7SXin LI case '0': 28618fd37a7SXin LI case '1': 28718fd37a7SXin LI case '2': 28818fd37a7SXin LI case '3': 28918fd37a7SXin LI case '4': 29018fd37a7SXin LI case '5': 29118fd37a7SXin LI case '6': 29218fd37a7SXin LI case '7': 29318fd37a7SXin LI case '8': 29418fd37a7SXin LI case '9': 29518fd37a7SXin LI if (! ISDIGIT (prev)) 29618fd37a7SXin LI ocontext = c - '0'; 29718fd37a7SXin LI else if (LIN_MAX / 10 < ocontext 29818fd37a7SXin LI || ((ocontext = 10 * ocontext + c - '0') < 0)) 29918fd37a7SXin LI ocontext = LIN_MAX; 30018fd37a7SXin LI break; 30118fd37a7SXin LI 30218fd37a7SXin LI case 'a': 30318fd37a7SXin LI text = true; 30418fd37a7SXin LI break; 30518fd37a7SXin LI 30618fd37a7SXin LI case 'b': 30718fd37a7SXin LI if (ignore_white_space < IGNORE_SPACE_CHANGE) 30818fd37a7SXin LI ignore_white_space = IGNORE_SPACE_CHANGE; 30918fd37a7SXin LI break; 31018fd37a7SXin LI 31118fd37a7SXin LI case 'B': 31218fd37a7SXin LI ignore_blank_lines = true; 31318fd37a7SXin LI break; 31418fd37a7SXin LI 31518fd37a7SXin LI case 'C': 31618fd37a7SXin LI case 'U': 31718fd37a7SXin LI { 31818fd37a7SXin LI if (optarg) 31918fd37a7SXin LI { 32018fd37a7SXin LI numval = strtoumax (optarg, &numend, 10); 32118fd37a7SXin LI if (*numend) 32218fd37a7SXin LI try_help ("invalid context length `%s'", optarg); 32318fd37a7SXin LI if (LIN_MAX < numval) 32418fd37a7SXin LI numval = LIN_MAX; 32518fd37a7SXin LI } 32618fd37a7SXin LI else 32718fd37a7SXin LI numval = 3; 32818fd37a7SXin LI 32918fd37a7SXin LI specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT); 33018fd37a7SXin LI if (context < numval) 33118fd37a7SXin LI context = numval; 33218fd37a7SXin LI explicit_context = true; 33318fd37a7SXin LI } 33418fd37a7SXin LI break; 33518fd37a7SXin LI 33618fd37a7SXin LI case 'c': 33718fd37a7SXin LI specify_style (OUTPUT_CONTEXT); 33818fd37a7SXin LI if (context < 3) 33918fd37a7SXin LI context = 3; 34018fd37a7SXin LI break; 34118fd37a7SXin LI 34218fd37a7SXin LI case 'd': 34318fd37a7SXin LI minimal = true; 34418fd37a7SXin LI break; 34518fd37a7SXin LI 34618fd37a7SXin LI case 'D': 34718fd37a7SXin LI specify_style (OUTPUT_IFDEF); 34818fd37a7SXin LI { 34918fd37a7SXin LI static char const C_ifdef_group_formats[] = 35018fd37a7SXin LI "%%=%c#ifndef %s\n%%<#endif /* ! %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n"; 35118fd37a7SXin LI char *b = xmalloc (sizeof C_ifdef_group_formats 35218fd37a7SXin LI + 7 * strlen (optarg) - 14 /* 7*"%s" */ 35318fd37a7SXin LI - 8 /* 5*"%%" + 3*"%c" */); 35418fd37a7SXin LI sprintf (b, C_ifdef_group_formats, 35518fd37a7SXin LI 0, 35618fd37a7SXin LI optarg, optarg, 0, 35718fd37a7SXin LI optarg, optarg, 0, 35818fd37a7SXin LI optarg, optarg, optarg); 35918fd37a7SXin LI for (i = 0; i < sizeof group_format / sizeof *group_format; i++) 36018fd37a7SXin LI { 36118fd37a7SXin LI specify_value (&group_format[i], b, "-D"); 36218fd37a7SXin LI b += strlen (b) + 1; 36318fd37a7SXin LI } 36418fd37a7SXin LI } 36518fd37a7SXin LI break; 36618fd37a7SXin LI 36718fd37a7SXin LI case 'e': 36818fd37a7SXin LI specify_style (OUTPUT_ED); 36918fd37a7SXin LI break; 37018fd37a7SXin LI 37118fd37a7SXin LI case 'E': 37218fd37a7SXin LI if (ignore_white_space < IGNORE_TAB_EXPANSION) 37318fd37a7SXin LI ignore_white_space = IGNORE_TAB_EXPANSION; 37418fd37a7SXin LI break; 37518fd37a7SXin LI 37618fd37a7SXin LI case 'f': 37718fd37a7SXin LI specify_style (OUTPUT_FORWARD_ED); 37818fd37a7SXin LI break; 37918fd37a7SXin LI 38018fd37a7SXin LI case 'F': 38118fd37a7SXin LI add_regexp (&function_regexp_list, optarg); 38218fd37a7SXin LI break; 38318fd37a7SXin LI 38418fd37a7SXin LI case 'h': 38518fd37a7SXin LI /* Split the files into chunks for faster processing. 38618fd37a7SXin LI Usually does not change the result. 38718fd37a7SXin LI 38818fd37a7SXin LI This currently has no effect. */ 38918fd37a7SXin LI break; 39018fd37a7SXin LI 39118fd37a7SXin LI case 'H': 39218fd37a7SXin LI speed_large_files = true; 39318fd37a7SXin LI break; 39418fd37a7SXin LI 39518fd37a7SXin LI case 'i': 39618fd37a7SXin LI ignore_case = true; 39718fd37a7SXin LI break; 39818fd37a7SXin LI 39918fd37a7SXin LI case 'I': 40018fd37a7SXin LI add_regexp (&ignore_regexp_list, optarg); 40118fd37a7SXin LI break; 40218fd37a7SXin LI 40318fd37a7SXin LI case 'l': 40418fd37a7SXin LI if (!pr_program[0]) 40518fd37a7SXin LI try_help ("pagination not supported on this host", 0); 40618fd37a7SXin LI paginate = true; 40718fd37a7SXin LI #ifdef SIGCHLD 40818fd37a7SXin LI /* Pagination requires forking and waiting, and 40918fd37a7SXin LI System V fork+wait does not work if SIGCHLD is ignored. */ 41018fd37a7SXin LI signal (SIGCHLD, SIG_DFL); 41118fd37a7SXin LI #endif 41218fd37a7SXin LI break; 41318fd37a7SXin LI 41418fd37a7SXin LI case 'L': 41518fd37a7SXin LI if (!file_label[0]) 41618fd37a7SXin LI file_label[0] = optarg; 41718fd37a7SXin LI else if (!file_label[1]) 41818fd37a7SXin LI file_label[1] = optarg; 41918fd37a7SXin LI else 42018fd37a7SXin LI fatal ("too many file label options"); 42118fd37a7SXin LI break; 42218fd37a7SXin LI 42318fd37a7SXin LI case 'n': 42418fd37a7SXin LI specify_style (OUTPUT_RCS); 42518fd37a7SXin LI break; 42618fd37a7SXin LI 42718fd37a7SXin LI case 'N': 42818fd37a7SXin LI new_file = true; 42918fd37a7SXin LI break; 43018fd37a7SXin LI 43118fd37a7SXin LI case 'p': 43218fd37a7SXin LI show_c_function = true; 43318fd37a7SXin LI add_regexp (&function_regexp_list, "^[[:alpha:]$_]"); 43418fd37a7SXin LI break; 43518fd37a7SXin LI 43618fd37a7SXin LI case 'P': 43718fd37a7SXin LI unidirectional_new_file = true; 43818fd37a7SXin LI break; 43918fd37a7SXin LI 44018fd37a7SXin LI case 'q': 44118fd37a7SXin LI brief = true; 44218fd37a7SXin LI break; 44318fd37a7SXin LI 44418fd37a7SXin LI case 'r': 44518fd37a7SXin LI recursive = true; 44618fd37a7SXin LI break; 44718fd37a7SXin LI 44818fd37a7SXin LI case 's': 44918fd37a7SXin LI report_identical_files = true; 45018fd37a7SXin LI break; 45118fd37a7SXin LI 45218fd37a7SXin LI case 'S': 45318fd37a7SXin LI specify_value (&starting_file, optarg, "-S"); 45418fd37a7SXin LI break; 45518fd37a7SXin LI 45618fd37a7SXin LI case 't': 45718fd37a7SXin LI expand_tabs = true; 45818fd37a7SXin LI break; 45918fd37a7SXin LI 46018fd37a7SXin LI case 'T': 46118fd37a7SXin LI initial_tab = true; 46218fd37a7SXin LI break; 46318fd37a7SXin LI 46418fd37a7SXin LI case 'u': 46518fd37a7SXin LI specify_style (OUTPUT_UNIFIED); 46618fd37a7SXin LI if (context < 3) 46718fd37a7SXin LI context = 3; 46818fd37a7SXin LI break; 46918fd37a7SXin LI 47018fd37a7SXin LI case 'v': 47118fd37a7SXin LI version_etc (stdout, "diff", PACKAGE_NAME, PACKAGE_VERSION, 47218fd37a7SXin LI "Paul Eggert", "Mike Haertel", "David Hayes", 47318fd37a7SXin LI "Richard Stallman", "Len Tower", (char *) 0); 47418fd37a7SXin LI check_stdout (); 47518fd37a7SXin LI return EXIT_SUCCESS; 47618fd37a7SXin LI 47718fd37a7SXin LI case 'w': 47818fd37a7SXin LI ignore_white_space = IGNORE_ALL_SPACE; 47918fd37a7SXin LI break; 48018fd37a7SXin LI 48118fd37a7SXin LI case 'x': 48218fd37a7SXin LI add_exclude (excluded, optarg, exclude_options ()); 48318fd37a7SXin LI break; 48418fd37a7SXin LI 48518fd37a7SXin LI case 'X': 48618fd37a7SXin LI if (add_exclude_file (add_exclude, excluded, optarg, 48718fd37a7SXin LI exclude_options (), '\n')) 48818fd37a7SXin LI pfatal_with_name (optarg); 48918fd37a7SXin LI break; 49018fd37a7SXin LI 49118fd37a7SXin LI case 'y': 49218fd37a7SXin LI specify_style (OUTPUT_SDIFF); 49318fd37a7SXin LI break; 49418fd37a7SXin LI 49518fd37a7SXin LI case 'W': 49618fd37a7SXin LI numval = strtoumax (optarg, &numend, 10); 49718fd37a7SXin LI if (! (0 < numval && numval <= SIZE_MAX) || *numend) 49818fd37a7SXin LI try_help ("invalid width `%s'", optarg); 49918fd37a7SXin LI if (width != numval) 50018fd37a7SXin LI { 50118fd37a7SXin LI if (width) 50218fd37a7SXin LI fatal ("conflicting width options"); 50318fd37a7SXin LI width = numval; 50418fd37a7SXin LI } 50518fd37a7SXin LI break; 50618fd37a7SXin LI 50718fd37a7SXin LI case BINARY_OPTION: 50818fd37a7SXin LI #if HAVE_SETMODE_DOS 50918fd37a7SXin LI binary = true; 51018fd37a7SXin LI set_binary_mode (STDOUT_FILENO, true); 51118fd37a7SXin LI #endif 51218fd37a7SXin LI break; 51318fd37a7SXin LI 51418fd37a7SXin LI case FROM_FILE_OPTION: 51518fd37a7SXin LI specify_value (&from_file, optarg, "--from-file"); 51618fd37a7SXin LI break; 51718fd37a7SXin LI 51818fd37a7SXin LI case HELP_OPTION: 51918fd37a7SXin LI usage (); 52018fd37a7SXin LI check_stdout (); 52118fd37a7SXin LI return EXIT_SUCCESS; 52218fd37a7SXin LI 52318fd37a7SXin LI case HORIZON_LINES_OPTION: 52418fd37a7SXin LI numval = strtoumax (optarg, &numend, 10); 52518fd37a7SXin LI if (*numend) 52618fd37a7SXin LI try_help ("invalid horizon length `%s'", optarg); 52718fd37a7SXin LI horizon_lines = MAX (horizon_lines, MIN (numval, LIN_MAX)); 52818fd37a7SXin LI break; 52918fd37a7SXin LI 53018fd37a7SXin LI case IGNORE_FILE_NAME_CASE_OPTION: 53118fd37a7SXin LI ignore_file_name_case = true; 53218fd37a7SXin LI break; 53318fd37a7SXin LI 53418fd37a7SXin LI case INHIBIT_HUNK_MERGE_OPTION: 53518fd37a7SXin LI /* This option is obsolete, but accept it for backward 53618fd37a7SXin LI compatibility. */ 53718fd37a7SXin LI break; 53818fd37a7SXin LI 53918fd37a7SXin LI case LEFT_COLUMN_OPTION: 54018fd37a7SXin LI left_column = true; 54118fd37a7SXin LI break; 54218fd37a7SXin LI 54318fd37a7SXin LI case LINE_FORMAT_OPTION: 54418fd37a7SXin LI specify_style (OUTPUT_IFDEF); 54518fd37a7SXin LI for (i = 0; i < sizeof line_format / sizeof *line_format; i++) 54618fd37a7SXin LI specify_value (&line_format[i], optarg, "--line-format"); 54718fd37a7SXin LI break; 54818fd37a7SXin LI 54918fd37a7SXin LI case NO_IGNORE_FILE_NAME_CASE_OPTION: 55018fd37a7SXin LI ignore_file_name_case = false; 55118fd37a7SXin LI break; 55218fd37a7SXin LI 55318fd37a7SXin LI case NORMAL_OPTION: 55418fd37a7SXin LI specify_style (OUTPUT_NORMAL); 55518fd37a7SXin LI break; 55618fd37a7SXin LI 55718fd37a7SXin LI case SDIFF_MERGE_ASSIST_OPTION: 55818fd37a7SXin LI specify_style (OUTPUT_SDIFF); 55918fd37a7SXin LI sdiff_merge_assist = true; 56018fd37a7SXin LI break; 56118fd37a7SXin LI 56218fd37a7SXin LI case STRIP_TRAILING_CR_OPTION: 56318fd37a7SXin LI strip_trailing_cr = true; 56418fd37a7SXin LI break; 56518fd37a7SXin LI 56618fd37a7SXin LI case SUPPRESS_COMMON_LINES_OPTION: 56718fd37a7SXin LI suppress_common_lines = true; 56818fd37a7SXin LI break; 56918fd37a7SXin LI 57018fd37a7SXin LI case TABSIZE_OPTION: 57118fd37a7SXin LI numval = strtoumax (optarg, &numend, 10); 57218fd37a7SXin LI if (! (0 < numval && numval <= SIZE_MAX) || *numend) 57318fd37a7SXin LI try_help ("invalid tabsize `%s'", optarg); 57418fd37a7SXin LI if (tabsize != numval) 57518fd37a7SXin LI { 57618fd37a7SXin LI if (tabsize) 57718fd37a7SXin LI fatal ("conflicting tabsize options"); 57818fd37a7SXin LI tabsize = numval; 57918fd37a7SXin LI } 58018fd37a7SXin LI break; 58118fd37a7SXin LI 58218fd37a7SXin LI case TO_FILE_OPTION: 58318fd37a7SXin LI specify_value (&to_file, optarg, "--to-file"); 58418fd37a7SXin LI break; 58518fd37a7SXin LI 58618fd37a7SXin LI case UNCHANGED_LINE_FORMAT_OPTION: 58718fd37a7SXin LI case OLD_LINE_FORMAT_OPTION: 58818fd37a7SXin LI case NEW_LINE_FORMAT_OPTION: 58918fd37a7SXin LI specify_style (OUTPUT_IFDEF); 59018fd37a7SXin LI c -= UNCHANGED_LINE_FORMAT_OPTION; 59118fd37a7SXin LI specify_value (&line_format[c], optarg, line_format_option[c]); 59218fd37a7SXin LI break; 59318fd37a7SXin LI 59418fd37a7SXin LI case UNCHANGED_GROUP_FORMAT_OPTION: 59518fd37a7SXin LI case OLD_GROUP_FORMAT_OPTION: 59618fd37a7SXin LI case NEW_GROUP_FORMAT_OPTION: 59718fd37a7SXin LI case CHANGED_GROUP_FORMAT_OPTION: 59818fd37a7SXin LI specify_style (OUTPUT_IFDEF); 59918fd37a7SXin LI c -= UNCHANGED_GROUP_FORMAT_OPTION; 60018fd37a7SXin LI specify_value (&group_format[c], optarg, group_format_option[c]); 60118fd37a7SXin LI break; 60218fd37a7SXin LI 60318fd37a7SXin LI default: 60418fd37a7SXin LI try_help (0, 0); 60518fd37a7SXin LI } 60618fd37a7SXin LI prev = c; 60718fd37a7SXin LI } 60818fd37a7SXin LI 60918fd37a7SXin LI if (output_style == OUTPUT_UNSPECIFIED) 61018fd37a7SXin LI { 61118fd37a7SXin LI if (show_c_function) 61218fd37a7SXin LI { 61318fd37a7SXin LI specify_style (OUTPUT_CONTEXT); 61418fd37a7SXin LI if (ocontext < 0) 61518fd37a7SXin LI context = 3; 61618fd37a7SXin LI } 61718fd37a7SXin LI else 61818fd37a7SXin LI specify_style (OUTPUT_NORMAL); 61918fd37a7SXin LI } 62018fd37a7SXin LI 62118fd37a7SXin LI if (output_style != OUTPUT_CONTEXT || hard_locale (LC_TIME)) 62218fd37a7SXin LI { 62318fd37a7SXin LI #ifdef ST_MTIM_NSEC 62418fd37a7SXin LI time_format = "%Y-%m-%d %H:%M:%S.%N %z"; 62518fd37a7SXin LI #else 62618fd37a7SXin LI time_format = "%Y-%m-%d %H:%M:%S %z"; 62718fd37a7SXin LI #endif 62818fd37a7SXin LI } 62918fd37a7SXin LI else 63018fd37a7SXin LI { 63118fd37a7SXin LI /* See POSIX 1003.1-2001 for this format. */ 63218fd37a7SXin LI time_format = "%a %b %e %T %Y"; 63318fd37a7SXin LI } 63418fd37a7SXin LI 63518fd37a7SXin LI if (0 <= ocontext) 63618fd37a7SXin LI { 63718fd37a7SXin LI bool modern_usage = 200112 <= posix2_version (); 63818fd37a7SXin LI 63918fd37a7SXin LI if ((output_style == OUTPUT_CONTEXT 64018fd37a7SXin LI || output_style == OUTPUT_UNIFIED) 64118fd37a7SXin LI && (context < ocontext 64218fd37a7SXin LI || (ocontext < context && ! explicit_context))) 64318fd37a7SXin LI { 64418fd37a7SXin LI if (modern_usage) 64518fd37a7SXin LI { 64618fd37a7SXin LI error (0, 0, 64718fd37a7SXin LI _("`-%ld' option is obsolete; use `-%c %ld'"), 64818fd37a7SXin LI (long int) ocontext, 64918fd37a7SXin LI output_style == OUTPUT_CONTEXT ? 'C' : 'U', 65018fd37a7SXin LI (long int) ocontext); 65118fd37a7SXin LI try_help (0, 0); 65218fd37a7SXin LI } 65318fd37a7SXin LI context = ocontext; 65418fd37a7SXin LI } 65518fd37a7SXin LI else 65618fd37a7SXin LI { 65718fd37a7SXin LI if (modern_usage) 65818fd37a7SXin LI { 65918fd37a7SXin LI error (0, 0, _("`-%ld' option is obsolete; omit it"), 66018fd37a7SXin LI (long int) ocontext); 66118fd37a7SXin LI try_help (0, 0); 66218fd37a7SXin LI } 66318fd37a7SXin LI } 66418fd37a7SXin LI } 66518fd37a7SXin LI 66618fd37a7SXin LI if (! tabsize) 66718fd37a7SXin LI tabsize = 8; 66818fd37a7SXin LI if (! width) 66918fd37a7SXin LI width = 130; 67018fd37a7SXin LI 67118fd37a7SXin LI { 67218fd37a7SXin LI /* Maximize first the half line width, and then the gutter width, 67318fd37a7SXin LI according to the following constraints: 67418fd37a7SXin LI 67518fd37a7SXin LI 1. Two half lines plus a gutter must fit in a line. 67618fd37a7SXin LI 2. If the half line width is nonzero: 67718fd37a7SXin LI a. The gutter width is at least GUTTER_WIDTH_MINIMUM. 67818fd37a7SXin LI b. If tabs are not expanded to spaces, 67918fd37a7SXin LI a half line plus a gutter is an integral number of tabs, 68018fd37a7SXin LI so that tabs in the right column line up. */ 68118fd37a7SXin LI 68218fd37a7SXin LI intmax_t t = expand_tabs ? 1 : tabsize; 68318fd37a7SXin LI intmax_t w = width; 68418fd37a7SXin LI intmax_t off = (w + t + GUTTER_WIDTH_MINIMUM) / (2 * t) * t; 68518fd37a7SXin LI sdiff_half_width = MAX (0, MIN (off - GUTTER_WIDTH_MINIMUM, w - off)), 68618fd37a7SXin LI sdiff_column2_offset = sdiff_half_width ? off : w; 68718fd37a7SXin LI } 68818fd37a7SXin LI 68918fd37a7SXin LI /* Make the horizon at least as large as the context, so that 69018fd37a7SXin LI shift_boundaries has more freedom to shift the first and last hunks. */ 69118fd37a7SXin LI if (horizon_lines < context) 69218fd37a7SXin LI horizon_lines = context; 69318fd37a7SXin LI 69418fd37a7SXin LI summarize_regexp_list (&function_regexp_list); 69518fd37a7SXin LI summarize_regexp_list (&ignore_regexp_list); 69618fd37a7SXin LI 69718fd37a7SXin LI if (output_style == OUTPUT_IFDEF) 69818fd37a7SXin LI { 69918fd37a7SXin LI for (i = 0; i < sizeof line_format / sizeof *line_format; i++) 70018fd37a7SXin LI if (!line_format[i]) 70118fd37a7SXin LI line_format[i] = "%l\n"; 70218fd37a7SXin LI if (!group_format[OLD]) 70318fd37a7SXin LI group_format[OLD] 70418fd37a7SXin LI = group_format[CHANGED] ? group_format[CHANGED] : "%<"; 70518fd37a7SXin LI if (!group_format[NEW]) 70618fd37a7SXin LI group_format[NEW] 70718fd37a7SXin LI = group_format[CHANGED] ? group_format[CHANGED] : "%>"; 70818fd37a7SXin LI if (!group_format[UNCHANGED]) 70918fd37a7SXin LI group_format[UNCHANGED] = "%="; 71018fd37a7SXin LI if (!group_format[CHANGED]) 71118fd37a7SXin LI group_format[CHANGED] = concat (group_format[OLD], 71218fd37a7SXin LI group_format[NEW], ""); 71318fd37a7SXin LI } 71418fd37a7SXin LI 71518fd37a7SXin LI no_diff_means_no_output = 71618fd37a7SXin LI (output_style == OUTPUT_IFDEF ? 71718fd37a7SXin LI (!*group_format[UNCHANGED] 71818fd37a7SXin LI || (strcmp (group_format[UNCHANGED], "%=") == 0 71918fd37a7SXin LI && !*line_format[UNCHANGED])) 72018fd37a7SXin LI : (output_style != OUTPUT_SDIFF) | suppress_common_lines); 72118fd37a7SXin LI 72218fd37a7SXin LI files_can_be_treated_as_binary = 72318fd37a7SXin LI (brief & binary 72418fd37a7SXin LI & ~ (ignore_blank_lines | ignore_case | strip_trailing_cr 72518fd37a7SXin LI | (ignore_regexp_list.regexps || ignore_white_space))); 72618fd37a7SXin LI 72718fd37a7SXin LI switch_string = option_list (argv + 1, optind - 1); 72818fd37a7SXin LI 72918fd37a7SXin LI if (from_file) 73018fd37a7SXin LI { 73118fd37a7SXin LI if (to_file) 73218fd37a7SXin LI fatal ("--from-file and --to-file both specified"); 73318fd37a7SXin LI else 73418fd37a7SXin LI for (; optind < argc; optind++) 73518fd37a7SXin LI { 73618fd37a7SXin LI int status = compare_files ((struct comparison *) 0, 73718fd37a7SXin LI from_file, argv[optind]); 73818fd37a7SXin LI if (exit_status < status) 73918fd37a7SXin LI exit_status = status; 74018fd37a7SXin LI } 74118fd37a7SXin LI } 74218fd37a7SXin LI else 74318fd37a7SXin LI { 74418fd37a7SXin LI if (to_file) 74518fd37a7SXin LI for (; optind < argc; optind++) 74618fd37a7SXin LI { 74718fd37a7SXin LI int status = compare_files ((struct comparison *) 0, 74818fd37a7SXin LI argv[optind], to_file); 74918fd37a7SXin LI if (exit_status < status) 75018fd37a7SXin LI exit_status = status; 75118fd37a7SXin LI } 75218fd37a7SXin LI else 75318fd37a7SXin LI { 75418fd37a7SXin LI if (argc - optind != 2) 75518fd37a7SXin LI { 75618fd37a7SXin LI if (argc - optind < 2) 75718fd37a7SXin LI try_help ("missing operand after `%s'", argv[argc - 1]); 75818fd37a7SXin LI else 75918fd37a7SXin LI try_help ("extra operand `%s'", argv[optind + 2]); 76018fd37a7SXin LI } 76118fd37a7SXin LI 76218fd37a7SXin LI exit_status = compare_files ((struct comparison *) 0, 76318fd37a7SXin LI argv[optind], argv[optind + 1]); 76418fd37a7SXin LI } 76518fd37a7SXin LI } 76618fd37a7SXin LI 76718fd37a7SXin LI /* Print any messages that were saved up for last. */ 76818fd37a7SXin LI print_message_queue (); 76918fd37a7SXin LI 77018fd37a7SXin LI check_stdout (); 77118fd37a7SXin LI exit (exit_status); 77218fd37a7SXin LI return exit_status; 77318fd37a7SXin LI } 77418fd37a7SXin LI 77518fd37a7SXin LI /* Append to REGLIST the regexp PATTERN. */ 77618fd37a7SXin LI 77718fd37a7SXin LI static void 77818fd37a7SXin LI add_regexp (struct regexp_list *reglist, char const *pattern) 77918fd37a7SXin LI { 78018fd37a7SXin LI size_t patlen = strlen (pattern); 78118fd37a7SXin LI char const *m = re_compile_pattern (pattern, patlen, reglist->buf); 78218fd37a7SXin LI 78318fd37a7SXin LI if (m != 0) 78418fd37a7SXin LI error (0, 0, "%s: %s", pattern, m); 78518fd37a7SXin LI else 78618fd37a7SXin LI { 78718fd37a7SXin LI char *regexps = reglist->regexps; 78818fd37a7SXin LI size_t len = reglist->len; 78918fd37a7SXin LI bool multiple_regexps = reglist->multiple_regexps = regexps != 0; 79018fd37a7SXin LI size_t newlen = reglist->len = len + 2 * multiple_regexps + patlen; 79118fd37a7SXin LI size_t size = reglist->size; 79218fd37a7SXin LI 79318fd37a7SXin LI if (size <= newlen) 79418fd37a7SXin LI { 79518fd37a7SXin LI if (!size) 79618fd37a7SXin LI size = 1; 79718fd37a7SXin LI 79818fd37a7SXin LI do size *= 2; 79918fd37a7SXin LI while (size <= newlen); 80018fd37a7SXin LI 80118fd37a7SXin LI reglist->size = size; 80218fd37a7SXin LI reglist->regexps = regexps = xrealloc (regexps, size); 80318fd37a7SXin LI } 80418fd37a7SXin LI if (multiple_regexps) 80518fd37a7SXin LI { 80618fd37a7SXin LI regexps[len++] = '\\'; 80718fd37a7SXin LI regexps[len++] = '|'; 80818fd37a7SXin LI } 80918fd37a7SXin LI memcpy (regexps + len, pattern, patlen + 1); 81018fd37a7SXin LI } 81118fd37a7SXin LI } 81218fd37a7SXin LI 81318fd37a7SXin LI /* Ensure that REGLIST represents the disjunction of its regexps. 81418fd37a7SXin LI This is done here, rather than earlier, to avoid O(N^2) behavior. */ 81518fd37a7SXin LI 81618fd37a7SXin LI static void 81718fd37a7SXin LI summarize_regexp_list (struct regexp_list *reglist) 81818fd37a7SXin LI { 81918fd37a7SXin LI if (reglist->regexps) 82018fd37a7SXin LI { 82118fd37a7SXin LI /* At least one regexp was specified. Allocate a fastmap for it. */ 82218fd37a7SXin LI reglist->buf->fastmap = xmalloc (1 << CHAR_BIT); 82318fd37a7SXin LI if (reglist->multiple_regexps) 82418fd37a7SXin LI { 82518fd37a7SXin LI /* Compile the disjunction of the regexps. 82618fd37a7SXin LI (If just one regexp was specified, it is already compiled.) */ 82718fd37a7SXin LI char const *m = re_compile_pattern (reglist->regexps, reglist->len, 82818fd37a7SXin LI reglist->buf); 82918fd37a7SXin LI if (m != 0) 83018fd37a7SXin LI error (EXIT_TROUBLE, 0, "%s: %s", reglist->regexps, m); 83118fd37a7SXin LI } 83218fd37a7SXin LI } 83318fd37a7SXin LI } 83418fd37a7SXin LI 83518fd37a7SXin LI static void 83618fd37a7SXin LI try_help (char const *reason_msgid, char const *operand) 83718fd37a7SXin LI { 83818fd37a7SXin LI if (reason_msgid) 83918fd37a7SXin LI error (0, 0, _(reason_msgid), operand); 84018fd37a7SXin LI error (EXIT_TROUBLE, 0, _("Try `%s --help' for more information."), 84118fd37a7SXin LI program_name); 84218fd37a7SXin LI abort (); 84318fd37a7SXin LI } 84418fd37a7SXin LI 84518fd37a7SXin LI static void 84618fd37a7SXin LI check_stdout (void) 84718fd37a7SXin LI { 84818fd37a7SXin LI if (ferror (stdout)) 84918fd37a7SXin LI fatal ("write failed"); 85018fd37a7SXin LI else if (fclose (stdout) != 0) 85118fd37a7SXin LI pfatal_with_name (_("standard output")); 85218fd37a7SXin LI } 85318fd37a7SXin LI 85418fd37a7SXin LI static char const * const option_help_msgid[] = { 85518fd37a7SXin LI N_("Compare files line by line."), 85618fd37a7SXin LI "", 85718fd37a7SXin LI N_("-i --ignore-case Ignore case differences in file contents."), 85818fd37a7SXin LI N_("--ignore-file-name-case Ignore case when comparing file names."), 85918fd37a7SXin LI N_("--no-ignore-file-name-case Consider case when comparing file names."), 86018fd37a7SXin LI N_("-E --ignore-tab-expansion Ignore changes due to tab expansion."), 86118fd37a7SXin LI N_("-b --ignore-space-change Ignore changes in the amount of white space."), 86218fd37a7SXin LI N_("-w --ignore-all-space Ignore all white space."), 86318fd37a7SXin LI N_("-B --ignore-blank-lines Ignore changes whose lines are all blank."), 86418fd37a7SXin LI N_("-I RE --ignore-matching-lines=RE Ignore changes whose lines all match RE."), 86518fd37a7SXin LI N_("--strip-trailing-cr Strip trailing carriage return on input."), 86618fd37a7SXin LI #if HAVE_SETMODE_DOS 86718fd37a7SXin LI N_("--binary Read and write data in binary mode."), 86818fd37a7SXin LI #endif 86918fd37a7SXin LI N_("-a --text Treat all files as text."), 87018fd37a7SXin LI "", 87118fd37a7SXin LI N_("-c -C NUM --context[=NUM] Output NUM (default 3) lines of copied context.\n\ 87218fd37a7SXin LI -u -U NUM --unified[=NUM] Output NUM (default 3) lines of unified context.\n\ 87318fd37a7SXin LI --label LABEL Use LABEL instead of file name.\n\ 87418fd37a7SXin LI -p --show-c-function Show which C function each change is in.\n\ 87518fd37a7SXin LI -F RE --show-function-line=RE Show the most recent line matching RE."), 87618fd37a7SXin LI N_("-q --brief Output only whether files differ."), 87718fd37a7SXin LI N_("-e --ed Output an ed script."), 87818fd37a7SXin LI N_("--normal Output a normal diff."), 87918fd37a7SXin LI N_("-n --rcs Output an RCS format diff."), 88018fd37a7SXin LI N_("-y --side-by-side Output in two columns.\n\ 88118fd37a7SXin LI -W NUM --width=NUM Output at most NUM (default 130) print columns.\n\ 88218fd37a7SXin LI --left-column Output only the left column of common lines.\n\ 88318fd37a7SXin LI --suppress-common-lines Do not output common lines."), 88418fd37a7SXin LI N_("-D NAME --ifdef=NAME Output merged file to show `#ifdef NAME' diffs."), 88518fd37a7SXin LI N_("--GTYPE-group-format=GFMT Similar, but format GTYPE input groups with GFMT."), 88618fd37a7SXin LI N_("--line-format=LFMT Similar, but format all input lines with LFMT."), 88718fd37a7SXin LI N_("--LTYPE-line-format=LFMT Similar, but format LTYPE input lines with LFMT."), 88818fd37a7SXin LI N_(" LTYPE is `old', `new', or `unchanged'. GTYPE is LTYPE or `changed'."), 88918fd37a7SXin LI N_(" GFMT may contain:\n\ 89018fd37a7SXin LI %< lines from FILE1\n\ 89118fd37a7SXin LI %> lines from FILE2\n\ 89218fd37a7SXin LI %= lines common to FILE1 and FILE2\n\ 89318fd37a7SXin LI %[-][WIDTH][.[PREC]]{doxX}LETTER printf-style spec for LETTER\n\ 89418fd37a7SXin LI LETTERs are as follows for new group, lower case for old group:\n\ 89518fd37a7SXin LI F first line number\n\ 89618fd37a7SXin LI L last line number\n\ 89718fd37a7SXin LI N number of lines = L-F+1\n\ 89818fd37a7SXin LI E F-1\n\ 89918fd37a7SXin LI M L+1"), 90018fd37a7SXin LI N_(" LFMT may contain:\n\ 90118fd37a7SXin LI %L contents of line\n\ 90218fd37a7SXin LI %l contents of line, excluding any trailing newline\n\ 90318fd37a7SXin LI %[-][WIDTH][.[PREC]]{doxX}n printf-style spec for input line number"), 90418fd37a7SXin LI N_(" Either GFMT or LFMT may contain:\n\ 90518fd37a7SXin LI %% %\n\ 90618fd37a7SXin LI %c'C' the single character C\n\ 90718fd37a7SXin LI %c'\\OOO' the character with octal code OOO"), 90818fd37a7SXin LI "", 90918fd37a7SXin LI N_("-l --paginate Pass the output through `pr' to paginate it."), 91018fd37a7SXin LI N_("-t --expand-tabs Expand tabs to spaces in output."), 91118fd37a7SXin LI N_("-T --initial-tab Make tabs line up by prepending a tab."), 91218fd37a7SXin LI N_("--tabsize=NUM Tab stops are every NUM (default 8) print columns."), 91318fd37a7SXin LI "", 91418fd37a7SXin LI N_("-r --recursive Recursively compare any subdirectories found."), 91518fd37a7SXin LI N_("-N --new-file Treat absent files as empty."), 91618fd37a7SXin LI N_("--unidirectional-new-file Treat absent first files as empty."), 91718fd37a7SXin LI N_("-s --report-identical-files Report when two files are the same."), 91818fd37a7SXin LI N_("-x PAT --exclude=PAT Exclude files that match PAT."), 91918fd37a7SXin LI N_("-X FILE --exclude-from=FILE Exclude files that match any pattern in FILE."), 92018fd37a7SXin LI N_("-S FILE --starting-file=FILE Start with FILE when comparing directories."), 92118fd37a7SXin LI N_("--from-file=FILE1 Compare FILE1 to all operands. FILE1 can be a directory."), 92218fd37a7SXin LI N_("--to-file=FILE2 Compare all operands to FILE2. FILE2 can be a directory."), 92318fd37a7SXin LI "", 92418fd37a7SXin LI N_("--horizon-lines=NUM Keep NUM lines of the common prefix and suffix."), 92518fd37a7SXin LI N_("-d --minimal Try hard to find a smaller set of changes."), 92618fd37a7SXin LI N_("--speed-large-files Assume large files and many scattered small changes."), 92718fd37a7SXin LI "", 92818fd37a7SXin LI N_("-v --version Output version info."), 92918fd37a7SXin LI N_("--help Output this help."), 93018fd37a7SXin LI "", 93118fd37a7SXin LI N_("FILES are `FILE1 FILE2' or `DIR1 DIR2' or `DIR FILE...' or `FILE... DIR'."), 93218fd37a7SXin LI N_("If --from-file or --to-file is given, there are no restrictions on FILES."), 93318fd37a7SXin LI N_("If a FILE is `-', read standard input."), 93418fd37a7SXin LI N_("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."), 93518fd37a7SXin LI "", 93618fd37a7SXin LI N_("Report bugs to <bug-gnu-utils@gnu.org>."), 93718fd37a7SXin LI 0 93818fd37a7SXin LI }; 93918fd37a7SXin LI 94018fd37a7SXin LI static void 94118fd37a7SXin LI usage (void) 94218fd37a7SXin LI { 94318fd37a7SXin LI char const * const *p; 94418fd37a7SXin LI 94518fd37a7SXin LI printf (_("Usage: %s [OPTION]... FILES\n"), program_name); 94618fd37a7SXin LI 94718fd37a7SXin LI for (p = option_help_msgid; *p; p++) 94818fd37a7SXin LI { 94918fd37a7SXin LI if (!**p) 95018fd37a7SXin LI putchar ('\n'); 95118fd37a7SXin LI else 95218fd37a7SXin LI { 95318fd37a7SXin LI char const *msg = _(*p); 95418fd37a7SXin LI char const *nl; 95518fd37a7SXin LI while ((nl = strchr (msg, '\n'))) 95618fd37a7SXin LI { 95718fd37a7SXin LI int msglen = nl + 1 - msg; 95818fd37a7SXin LI printf (" %.*s", msglen, msg); 95918fd37a7SXin LI msg = nl + 1; 96018fd37a7SXin LI } 96118fd37a7SXin LI 96218fd37a7SXin LI printf (" %s\n" + 2 * (*msg != ' ' && *msg != '-'), msg); 96318fd37a7SXin LI } 96418fd37a7SXin LI } 96518fd37a7SXin LI } 96618fd37a7SXin LI 96718fd37a7SXin LI /* Set VAR to VALUE, reporting an OPTION error if this is a 96818fd37a7SXin LI conflict. */ 96918fd37a7SXin LI static void 97018fd37a7SXin LI specify_value (char const **var, char const *value, char const *option) 97118fd37a7SXin LI { 97218fd37a7SXin LI if (*var && strcmp (*var, value) != 0) 97318fd37a7SXin LI { 97418fd37a7SXin LI error (0, 0, _("conflicting %s option value `%s'"), option, value); 97518fd37a7SXin LI try_help (0, 0); 97618fd37a7SXin LI } 97718fd37a7SXin LI *var = value; 97818fd37a7SXin LI } 97918fd37a7SXin LI 98018fd37a7SXin LI /* Set the output style to STYLE, diagnosing conflicts. */ 98118fd37a7SXin LI static void 98218fd37a7SXin LI specify_style (enum output_style style) 98318fd37a7SXin LI { 98418fd37a7SXin LI if (output_style != style) 98518fd37a7SXin LI { 98618fd37a7SXin LI if (output_style != OUTPUT_UNSPECIFIED) 98718fd37a7SXin LI try_help ("conflicting output style options", 0); 98818fd37a7SXin LI output_style = style; 98918fd37a7SXin LI } 99018fd37a7SXin LI } 99118fd37a7SXin LI 99218fd37a7SXin LI /* Set the last-modified time of *ST to be the current time. */ 99318fd37a7SXin LI 99418fd37a7SXin LI static void 99518fd37a7SXin LI set_mtime_to_now (struct stat *st) 99618fd37a7SXin LI { 99718fd37a7SXin LI #ifdef ST_MTIM_NSEC 99818fd37a7SXin LI 99918fd37a7SXin LI # if HAVE_CLOCK_GETTIME && defined CLOCK_REALTIME 100018fd37a7SXin LI if (clock_gettime (CLOCK_REALTIME, &st->st_mtim) == 0) 100118fd37a7SXin LI return; 100218fd37a7SXin LI # endif 100318fd37a7SXin LI 100418fd37a7SXin LI # if HAVE_GETTIMEOFDAY 100518fd37a7SXin LI { 100618fd37a7SXin LI struct timeval timeval; 100718fd37a7SXin LI if (gettimeofday (&timeval, 0) == 0) 100818fd37a7SXin LI { 100918fd37a7SXin LI st->st_mtime = timeval.tv_sec; 101018fd37a7SXin LI st->st_mtim.ST_MTIM_NSEC = timeval.tv_usec * 1000; 101118fd37a7SXin LI return; 101218fd37a7SXin LI } 101318fd37a7SXin LI } 101418fd37a7SXin LI # endif 101518fd37a7SXin LI 101618fd37a7SXin LI #endif /* ST_MTIM_NSEC */ 101718fd37a7SXin LI 101818fd37a7SXin LI time (&st->st_mtime); 101918fd37a7SXin LI } 102018fd37a7SXin LI 102118fd37a7SXin LI /* Compare two files (or dirs) with parent comparison PARENT 102218fd37a7SXin LI and names NAME0 and NAME1. 102318fd37a7SXin LI (If PARENT is 0, then the first name is just NAME0, etc.) 102418fd37a7SXin LI This is self-contained; it opens the files and closes them. 102518fd37a7SXin LI 102618fd37a7SXin LI Value is EXIT_SUCCESS if files are the same, EXIT_FAILURE if 102718fd37a7SXin LI different, EXIT_TROUBLE if there is a problem opening them. */ 102818fd37a7SXin LI 102918fd37a7SXin LI static int 103018fd37a7SXin LI compare_files (struct comparison const *parent, 103118fd37a7SXin LI char const *name0, 103218fd37a7SXin LI char const *name1) 103318fd37a7SXin LI { 103418fd37a7SXin LI struct comparison cmp; 103518fd37a7SXin LI #define DIR_P(f) (S_ISDIR (cmp.file[f].stat.st_mode) != 0) 103618fd37a7SXin LI register int f; 103718fd37a7SXin LI int status = EXIT_SUCCESS; 103818fd37a7SXin LI bool same_files; 103918fd37a7SXin LI char *free0, *free1; 104018fd37a7SXin LI 104118fd37a7SXin LI /* If this is directory comparison, perhaps we have a file 104218fd37a7SXin LI that exists only in one of the directories. 104318fd37a7SXin LI If so, just print a message to that effect. */ 104418fd37a7SXin LI 104518fd37a7SXin LI if (! ((name0 && name1) 104618fd37a7SXin LI || (unidirectional_new_file && name1) 104718fd37a7SXin LI || new_file)) 104818fd37a7SXin LI { 104918fd37a7SXin LI char const *name = name0 == 0 ? name1 : name0; 105018fd37a7SXin LI char const *dir = parent->file[name0 == 0].name; 105118fd37a7SXin LI 105218fd37a7SXin LI /* See POSIX 1003.1-2001 for this format. */ 105318fd37a7SXin LI message ("Only in %s: %s\n", dir, name); 105418fd37a7SXin LI 105518fd37a7SXin LI /* Return EXIT_FAILURE so that diff_dirs will return 105618fd37a7SXin LI EXIT_FAILURE ("some files differ"). */ 105718fd37a7SXin LI return EXIT_FAILURE; 105818fd37a7SXin LI } 105918fd37a7SXin LI 106018fd37a7SXin LI memset (cmp.file, 0, sizeof cmp.file); 106118fd37a7SXin LI cmp.parent = parent; 106218fd37a7SXin LI 106318fd37a7SXin LI /* cmp.file[f].desc markers */ 106418fd37a7SXin LI #define NONEXISTENT (-1) /* nonexistent file */ 106518fd37a7SXin LI #define UNOPENED (-2) /* unopened file (e.g. directory) */ 106618fd37a7SXin LI #define ERRNO_ENCODE(errno) (-3 - (errno)) /* encoded errno value */ 106718fd37a7SXin LI 106818fd37a7SXin LI #define ERRNO_DECODE(desc) (-3 - (desc)) /* inverse of ERRNO_ENCODE */ 106918fd37a7SXin LI 107018fd37a7SXin LI cmp.file[0].desc = name0 == 0 ? NONEXISTENT : UNOPENED; 107118fd37a7SXin LI cmp.file[1].desc = name1 == 0 ? NONEXISTENT : UNOPENED; 107218fd37a7SXin LI 107318fd37a7SXin LI /* Now record the full name of each file, including nonexistent ones. */ 107418fd37a7SXin LI 107518fd37a7SXin LI if (name0 == 0) 107618fd37a7SXin LI name0 = name1; 107718fd37a7SXin LI if (name1 == 0) 107818fd37a7SXin LI name1 = name0; 107918fd37a7SXin LI 108018fd37a7SXin LI if (!parent) 108118fd37a7SXin LI { 108218fd37a7SXin LI free0 = 0; 108318fd37a7SXin LI free1 = 0; 108418fd37a7SXin LI cmp.file[0].name = name0; 108518fd37a7SXin LI cmp.file[1].name = name1; 108618fd37a7SXin LI } 108718fd37a7SXin LI else 108818fd37a7SXin LI { 108918fd37a7SXin LI cmp.file[0].name = free0 109018fd37a7SXin LI = dir_file_pathname (parent->file[0].name, name0); 109118fd37a7SXin LI cmp.file[1].name = free1 109218fd37a7SXin LI = dir_file_pathname (parent->file[1].name, name1); 109318fd37a7SXin LI } 109418fd37a7SXin LI 109518fd37a7SXin LI /* Stat the files. */ 109618fd37a7SXin LI 109718fd37a7SXin LI for (f = 0; f < 2; f++) 109818fd37a7SXin LI { 109918fd37a7SXin LI if (cmp.file[f].desc != NONEXISTENT) 110018fd37a7SXin LI { 110118fd37a7SXin LI if (f && file_name_cmp (cmp.file[f].name, cmp.file[0].name) == 0) 110218fd37a7SXin LI { 110318fd37a7SXin LI cmp.file[f].desc = cmp.file[0].desc; 110418fd37a7SXin LI cmp.file[f].stat = cmp.file[0].stat; 110518fd37a7SXin LI } 110618fd37a7SXin LI else if (strcmp (cmp.file[f].name, "-") == 0) 110718fd37a7SXin LI { 110818fd37a7SXin LI cmp.file[f].desc = STDIN_FILENO; 110918fd37a7SXin LI if (fstat (STDIN_FILENO, &cmp.file[f].stat) != 0) 111018fd37a7SXin LI cmp.file[f].desc = ERRNO_ENCODE (errno); 111118fd37a7SXin LI else 111218fd37a7SXin LI { 111318fd37a7SXin LI if (S_ISREG (cmp.file[f].stat.st_mode)) 111418fd37a7SXin LI { 111518fd37a7SXin LI off_t pos = lseek (STDIN_FILENO, (off_t) 0, SEEK_CUR); 111618fd37a7SXin LI if (pos < 0) 111718fd37a7SXin LI cmp.file[f].desc = ERRNO_ENCODE (errno); 111818fd37a7SXin LI else 111918fd37a7SXin LI cmp.file[f].stat.st_size = 112018fd37a7SXin LI MAX (0, cmp.file[f].stat.st_size - pos); 112118fd37a7SXin LI } 112218fd37a7SXin LI 112318fd37a7SXin LI /* POSIX 1003.1-2001 requires current time for 112418fd37a7SXin LI stdin. */ 112518fd37a7SXin LI set_mtime_to_now (&cmp.file[f].stat); 112618fd37a7SXin LI } 112718fd37a7SXin LI } 112818fd37a7SXin LI else if (stat (cmp.file[f].name, &cmp.file[f].stat) != 0) 112918fd37a7SXin LI cmp.file[f].desc = ERRNO_ENCODE (errno); 113018fd37a7SXin LI } 113118fd37a7SXin LI } 113218fd37a7SXin LI 113318fd37a7SXin LI /* Mark files as nonexistent as needed for -N and -P, if they are 113418fd37a7SXin LI inaccessible empty regular files (the kind of files that 'patch' 113518fd37a7SXin LI creates to indicate nonexistent backups), or if they are 113618fd37a7SXin LI top-level files that do not exist but their counterparts do 113718fd37a7SXin LI exist. */ 113818fd37a7SXin LI for (f = 0; f < 2; f++) 113918fd37a7SXin LI if ((new_file || (f == 0 && unidirectional_new_file)) 114018fd37a7SXin LI && (cmp.file[f].desc == UNOPENED 114118fd37a7SXin LI ? (S_ISREG (cmp.file[f].stat.st_mode) 114218fd37a7SXin LI && ! (cmp.file[f].stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) 114318fd37a7SXin LI && cmp.file[f].stat.st_size == 0) 114418fd37a7SXin LI : (cmp.file[f].desc == ERRNO_ENCODE (ENOENT) 114518fd37a7SXin LI && ! parent 114618fd37a7SXin LI && cmp.file[1 - f].desc == UNOPENED))) 114718fd37a7SXin LI cmp.file[f].desc = NONEXISTENT; 114818fd37a7SXin LI 114918fd37a7SXin LI for (f = 0; f < 2; f++) 115018fd37a7SXin LI if (cmp.file[f].desc == NONEXISTENT) 115118fd37a7SXin LI { 115218fd37a7SXin LI memset (&cmp.file[f].stat, 0, sizeof cmp.file[f].stat); 115318fd37a7SXin LI cmp.file[f].stat.st_mode = cmp.file[1 - f].stat.st_mode; 115418fd37a7SXin LI } 115518fd37a7SXin LI 115618fd37a7SXin LI for (f = 0; f < 2; f++) 115718fd37a7SXin LI { 115818fd37a7SXin LI int e = ERRNO_DECODE (cmp.file[f].desc); 115918fd37a7SXin LI if (0 <= e) 116018fd37a7SXin LI { 116118fd37a7SXin LI errno = e; 116218fd37a7SXin LI perror_with_name (cmp.file[f].name); 116318fd37a7SXin LI status = EXIT_TROUBLE; 116418fd37a7SXin LI } 116518fd37a7SXin LI } 116618fd37a7SXin LI 116718fd37a7SXin LI if (status == EXIT_SUCCESS && ! parent && DIR_P (0) != DIR_P (1)) 116818fd37a7SXin LI { 116918fd37a7SXin LI /* If one is a directory, and it was specified in the command line, 117018fd37a7SXin LI use the file in that dir with the other file's basename. */ 117118fd37a7SXin LI 117218fd37a7SXin LI int fnm_arg = DIR_P (0); 117318fd37a7SXin LI int dir_arg = 1 - fnm_arg; 117418fd37a7SXin LI char const *fnm = cmp.file[fnm_arg].name; 117518fd37a7SXin LI char const *dir = cmp.file[dir_arg].name; 117618fd37a7SXin LI char const *filename = cmp.file[dir_arg].name = free0 117718fd37a7SXin LI = dir_file_pathname (dir, base_name (fnm)); 117818fd37a7SXin LI 117918fd37a7SXin LI if (strcmp (fnm, "-") == 0) 118018fd37a7SXin LI fatal ("cannot compare `-' to a directory"); 118118fd37a7SXin LI 118218fd37a7SXin LI if (stat (filename, &cmp.file[dir_arg].stat) != 0) 118318fd37a7SXin LI { 118418fd37a7SXin LI perror_with_name (filename); 118518fd37a7SXin LI status = EXIT_TROUBLE; 118618fd37a7SXin LI } 118718fd37a7SXin LI } 118818fd37a7SXin LI 118918fd37a7SXin LI if (status != EXIT_SUCCESS) 119018fd37a7SXin LI { 119118fd37a7SXin LI /* One of the files should exist but does not. */ 119218fd37a7SXin LI } 119318fd37a7SXin LI else if (cmp.file[0].desc == NONEXISTENT 119418fd37a7SXin LI && cmp.file[1].desc == NONEXISTENT) 119518fd37a7SXin LI { 119618fd37a7SXin LI /* Neither file "exists", so there's nothing to compare. */ 119718fd37a7SXin LI } 119818fd37a7SXin LI else if ((same_files 119918fd37a7SXin LI = (cmp.file[0].desc != NONEXISTENT 120018fd37a7SXin LI && cmp.file[1].desc != NONEXISTENT 120118fd37a7SXin LI && 0 < same_file (&cmp.file[0].stat, &cmp.file[1].stat) 120218fd37a7SXin LI && same_file_attributes (&cmp.file[0].stat, 120318fd37a7SXin LI &cmp.file[1].stat))) 120418fd37a7SXin LI && no_diff_means_no_output) 120518fd37a7SXin LI { 120618fd37a7SXin LI /* The two named files are actually the same physical file. 120718fd37a7SXin LI We know they are identical without actually reading them. */ 120818fd37a7SXin LI } 120918fd37a7SXin LI else if (DIR_P (0) & DIR_P (1)) 121018fd37a7SXin LI { 121118fd37a7SXin LI if (output_style == OUTPUT_IFDEF) 121218fd37a7SXin LI fatal ("-D option not supported with directories"); 121318fd37a7SXin LI 121418fd37a7SXin LI /* If both are directories, compare the files in them. */ 121518fd37a7SXin LI 121618fd37a7SXin LI if (parent && !recursive) 121718fd37a7SXin LI { 121818fd37a7SXin LI /* But don't compare dir contents one level down 121918fd37a7SXin LI unless -r was specified. 122018fd37a7SXin LI See POSIX 1003.1-2001 for this format. */ 122118fd37a7SXin LI message ("Common subdirectories: %s and %s\n", 122218fd37a7SXin LI cmp.file[0].name, cmp.file[1].name); 122318fd37a7SXin LI } 122418fd37a7SXin LI else 122518fd37a7SXin LI status = diff_dirs (&cmp, compare_files); 122618fd37a7SXin LI } 122718fd37a7SXin LI else if ((DIR_P (0) | DIR_P (1)) 122818fd37a7SXin LI || (parent 122918fd37a7SXin LI && (! S_ISREG (cmp.file[0].stat.st_mode) 123018fd37a7SXin LI || ! S_ISREG (cmp.file[1].stat.st_mode)))) 123118fd37a7SXin LI { 123218fd37a7SXin LI if (cmp.file[0].desc == NONEXISTENT || cmp.file[1].desc == NONEXISTENT) 123318fd37a7SXin LI { 123418fd37a7SXin LI /* We have a subdirectory that exists only in one directory. */ 123518fd37a7SXin LI 123618fd37a7SXin LI if ((DIR_P (0) | DIR_P (1)) 123718fd37a7SXin LI && recursive 123818fd37a7SXin LI && (new_file 123918fd37a7SXin LI || (unidirectional_new_file 124018fd37a7SXin LI && cmp.file[0].desc == NONEXISTENT))) 124118fd37a7SXin LI status = diff_dirs (&cmp, compare_files); 124218fd37a7SXin LI else 124318fd37a7SXin LI { 124418fd37a7SXin LI char const *dir 124518fd37a7SXin LI = parent->file[cmp.file[0].desc == NONEXISTENT].name; 124618fd37a7SXin LI 124718fd37a7SXin LI /* See POSIX 1003.1-2001 for this format. */ 124818fd37a7SXin LI message ("Only in %s: %s\n", dir, name0); 124918fd37a7SXin LI 125018fd37a7SXin LI status = EXIT_FAILURE; 125118fd37a7SXin LI } 125218fd37a7SXin LI } 125318fd37a7SXin LI else 125418fd37a7SXin LI { 125518fd37a7SXin LI /* We have two files that are not to be compared. */ 125618fd37a7SXin LI 125718fd37a7SXin LI /* See POSIX 1003.1-2001 for this format. */ 125818fd37a7SXin LI message5 ("File %s is a %s while file %s is a %s\n", 125918fd37a7SXin LI file_label[0] ? file_label[0] : cmp.file[0].name, 126018fd37a7SXin LI file_type (&cmp.file[0].stat), 126118fd37a7SXin LI file_label[1] ? file_label[1] : cmp.file[1].name, 126218fd37a7SXin LI file_type (&cmp.file[1].stat)); 126318fd37a7SXin LI 126418fd37a7SXin LI /* This is a difference. */ 126518fd37a7SXin LI status = EXIT_FAILURE; 126618fd37a7SXin LI } 126718fd37a7SXin LI } 126818fd37a7SXin LI else if (files_can_be_treated_as_binary 126918fd37a7SXin LI && S_ISREG (cmp.file[0].stat.st_mode) 127018fd37a7SXin LI && S_ISREG (cmp.file[1].stat.st_mode) 127118fd37a7SXin LI && cmp.file[0].stat.st_size != cmp.file[1].stat.st_size) 127218fd37a7SXin LI { 127318fd37a7SXin LI message ("Files %s and %s differ\n", 127418fd37a7SXin LI file_label[0] ? file_label[0] : cmp.file[0].name, 127518fd37a7SXin LI file_label[1] ? file_label[1] : cmp.file[1].name); 127618fd37a7SXin LI status = EXIT_FAILURE; 127718fd37a7SXin LI } 127818fd37a7SXin LI else 127918fd37a7SXin LI { 128018fd37a7SXin LI /* Both exist and neither is a directory. */ 128118fd37a7SXin LI 128218fd37a7SXin LI /* Open the files and record their descriptors. */ 128318fd37a7SXin LI 128418fd37a7SXin LI if (cmp.file[0].desc == UNOPENED) 128518fd37a7SXin LI if ((cmp.file[0].desc = open (cmp.file[0].name, O_RDONLY, 0)) < 0) 128618fd37a7SXin LI { 128718fd37a7SXin LI perror_with_name (cmp.file[0].name); 128818fd37a7SXin LI status = EXIT_TROUBLE; 128918fd37a7SXin LI } 129018fd37a7SXin LI if (cmp.file[1].desc == UNOPENED) 129118fd37a7SXin LI { 129218fd37a7SXin LI if (same_files) 129318fd37a7SXin LI cmp.file[1].desc = cmp.file[0].desc; 129418fd37a7SXin LI else if ((cmp.file[1].desc = open (cmp.file[1].name, O_RDONLY, 0)) 129518fd37a7SXin LI < 0) 129618fd37a7SXin LI { 129718fd37a7SXin LI perror_with_name (cmp.file[1].name); 129818fd37a7SXin LI status = EXIT_TROUBLE; 129918fd37a7SXin LI } 130018fd37a7SXin LI } 130118fd37a7SXin LI 130218fd37a7SXin LI #if HAVE_SETMODE_DOS 130318fd37a7SXin LI if (binary) 130418fd37a7SXin LI for (f = 0; f < 2; f++) 130518fd37a7SXin LI if (0 <= cmp.file[f].desc) 130618fd37a7SXin LI set_binary_mode (cmp.file[f].desc, true); 130718fd37a7SXin LI #endif 130818fd37a7SXin LI 130918fd37a7SXin LI /* Compare the files, if no error was found. */ 131018fd37a7SXin LI 131118fd37a7SXin LI if (status == EXIT_SUCCESS) 131218fd37a7SXin LI status = diff_2_files (&cmp); 131318fd37a7SXin LI 131418fd37a7SXin LI /* Close the file descriptors. */ 131518fd37a7SXin LI 131618fd37a7SXin LI if (0 <= cmp.file[0].desc && close (cmp.file[0].desc) != 0) 131718fd37a7SXin LI { 131818fd37a7SXin LI perror_with_name (cmp.file[0].name); 131918fd37a7SXin LI status = EXIT_TROUBLE; 132018fd37a7SXin LI } 132118fd37a7SXin LI if (0 <= cmp.file[1].desc && cmp.file[0].desc != cmp.file[1].desc 132218fd37a7SXin LI && close (cmp.file[1].desc) != 0) 132318fd37a7SXin LI { 132418fd37a7SXin LI perror_with_name (cmp.file[1].name); 132518fd37a7SXin LI status = EXIT_TROUBLE; 132618fd37a7SXin LI } 132718fd37a7SXin LI } 132818fd37a7SXin LI 132918fd37a7SXin LI /* Now the comparison has been done, if no error prevented it, 133018fd37a7SXin LI and STATUS is the value this function will return. */ 133118fd37a7SXin LI 133218fd37a7SXin LI if (status == EXIT_SUCCESS) 133318fd37a7SXin LI { 133418fd37a7SXin LI if (report_identical_files && !DIR_P (0)) 133518fd37a7SXin LI message ("Files %s and %s are identical\n", 133618fd37a7SXin LI file_label[0] ? file_label[0] : cmp.file[0].name, 133718fd37a7SXin LI file_label[1] ? file_label[1] : cmp.file[1].name); 133818fd37a7SXin LI } 133918fd37a7SXin LI else 134018fd37a7SXin LI { 134118fd37a7SXin LI /* Flush stdout so that the user sees differences immediately. 134218fd37a7SXin LI This can hurt performance, unfortunately. */ 134318fd37a7SXin LI if (fflush (stdout) != 0) 134418fd37a7SXin LI pfatal_with_name (_("standard output")); 134518fd37a7SXin LI } 134618fd37a7SXin LI 134718fd37a7SXin LI if (free0) 134818fd37a7SXin LI free (free0); 134918fd37a7SXin LI if (free1) 135018fd37a7SXin LI free (free1); 135118fd37a7SXin LI 135218fd37a7SXin LI return status; 135318fd37a7SXin LI } 1354