📄 diff.c
字号:
/* diff - compare files line by line Copyright (C) 1988, 1989, 1992, 1993, 1994, 1996, 1998, 2001, 2002, 2004 Free Software Foundation, Inc. This file is part of GNU DIFF. GNU DIFF is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU DIFF is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU DIFF; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#define GDIFF_MAIN#include "diff.h"#include "paths.h"#include <c-stack.h>#include <dirname.h>#include <error.h>#include <exclude.h>#include <exit.h>#include <exitfail.h>#include <file-type.h>#include <fnmatch.h>#include <getopt.h>#include <hard-locale.h>#include <posixver.h>#include <prepargs.h>#include <quotesys.h>#include <setmode.h>#include <version-etc.h>#include <xalloc.h>#ifndef GUTTER_WIDTH_MINIMUM# define GUTTER_WIDTH_MINIMUM 3#endifstruct regexp_list{ char *regexps; /* chars representing disjunction of the regexps */ size_t len; /* chars used in `regexps' */ size_t size; /* size malloc'ed for `regexps'; 0 if not malloc'ed */ bool multiple_regexps;/* Does `regexps' represent a disjunction? */ struct re_pattern_buffer *buf;};static int compare_files (struct comparison const *, char const *, char const *);static void add_regexp (struct regexp_list *, char const *);static void summarize_regexp_list (struct regexp_list *);static void specify_style (enum output_style);static void specify_value (char const **, char const *, char const *);static void try_help (char const *, char const *) __attribute__((noreturn));static void check_stdout (void);static void usage (void);/* If comparing directories, compare their common subdirectories recursively. */static bool recursive;/* In context diffs, show previous lines that match these regexps. */static struct regexp_list function_regexp_list;/* Ignore changes affecting only lines that match these regexps. */static struct regexp_list ignore_regexp_list;#if HAVE_SETMODE_DOS/* Use binary I/O when reading and writing data (--binary). On POSIX hosts, this has no effect. */static bool binary;#elseenum { binary = true };#endif/* When comparing directories, if a file appears only in one directory, treat it as present but empty in the other (-N). Then `patch' would create the file with appropriate contents. */static bool new_file;/* When comparing directories, if a file appears only in the second directory of the two, treat it as present but empty in the other (--unidirectional-new-file). Then `patch' would create the file with appropriate contents. */static bool unidirectional_new_file;/* Report files compared that are the same (-s). Normally nothing is output when that happens. */static bool report_identical_files;/* Return a string containing the command options with which diff was invoked. Spaces appear between what were separate ARGV-elements. There is a space at the beginning but none at the end. If there were no options, the result is an empty string. Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT, the length of that vector. */static char *option_list (char **optionvec, int count){ int i; size_t size = 1; char *result; char *p; for (i = 0; i < count; i++) size += 1 + quote_system_arg ((char *) 0, optionvec[i]); p = result = xmalloc (size); for (i = 0; i < count; i++) { *p++ = ' '; p += quote_system_arg (p, optionvec[i]); } *p = 0; return result;}/* Return an option value suitable for add_exclude. */static intexclude_options (void){ return EXCLUDE_WILDCARDS | (ignore_file_name_case ? FNM_CASEFOLD : 0);}static char const shortopts[] ="0123456789abBcC:dD:eEfF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:y";/* Values for long options that do not have single-letter equivalents. */enum{ BINARY_OPTION = CHAR_MAX + 1, FROM_FILE_OPTION, HELP_OPTION, HORIZON_LINES_OPTION, IGNORE_FILE_NAME_CASE_OPTION, INHIBIT_HUNK_MERGE_OPTION, LEFT_COLUMN_OPTION, LINE_FORMAT_OPTION, NO_IGNORE_FILE_NAME_CASE_OPTION, NORMAL_OPTION, SDIFF_MERGE_ASSIST_OPTION, STRIP_TRAILING_CR_OPTION, SUPPRESS_COMMON_LINES_OPTION, TABSIZE_OPTION, TO_FILE_OPTION, /* These options must be in sequence. */ UNCHANGED_LINE_FORMAT_OPTION, OLD_LINE_FORMAT_OPTION, NEW_LINE_FORMAT_OPTION, /* These options must be in sequence. */ UNCHANGED_GROUP_FORMAT_OPTION, OLD_GROUP_FORMAT_OPTION, NEW_GROUP_FORMAT_OPTION, CHANGED_GROUP_FORMAT_OPTION};static char const group_format_option[][sizeof "--unchanged-group-format"] = { "--unchanged-group-format", "--old-group-format", "--new-group-format", "--changed-group-format" };static char const line_format_option[][sizeof "--unchanged-line-format"] = { "--unchanged-line-format", "--old-line-format", "--new-line-format" };static struct option const longopts[] ={ {"binary", 0, 0, BINARY_OPTION}, {"brief", 0, 0, 'q'}, {"changed-group-format", 1, 0, CHANGED_GROUP_FORMAT_OPTION}, {"context", 2, 0, 'C'}, {"ed", 0, 0, 'e'}, {"exclude", 1, 0, 'x'}, {"exclude-from", 1, 0, 'X'}, {"expand-tabs", 0, 0, 't'}, {"forward-ed", 0, 0, 'f'}, {"from-file", 1, 0, FROM_FILE_OPTION}, {"help", 0, 0, HELP_OPTION}, {"horizon-lines", 1, 0, HORIZON_LINES_OPTION}, {"ifdef", 1, 0, 'D'}, {"ignore-all-space", 0, 0, 'w'}, {"ignore-blank-lines", 0, 0, 'B'}, {"ignore-case", 0, 0, 'i'}, {"ignore-file-name-case", 0, 0, IGNORE_FILE_NAME_CASE_OPTION}, {"ignore-matching-lines", 1, 0, 'I'}, {"ignore-space-change", 0, 0, 'b'}, {"ignore-tab-expansion", 0, 0, 'E'}, {"inhibit-hunk-merge", 0, 0, INHIBIT_HUNK_MERGE_OPTION}, {"initial-tab", 0, 0, 'T'}, {"label", 1, 0, 'L'}, {"left-column", 0, 0, LEFT_COLUMN_OPTION}, {"line-format", 1, 0, LINE_FORMAT_OPTION}, {"minimal", 0, 0, 'd'}, {"new-file", 0, 0, 'N'}, {"new-group-format", 1, 0, NEW_GROUP_FORMAT_OPTION}, {"new-line-format", 1, 0, NEW_LINE_FORMAT_OPTION}, {"no-ignore-file-name-case", 0, 0, NO_IGNORE_FILE_NAME_CASE_OPTION}, {"normal", 0, 0, NORMAL_OPTION}, {"old-group-format", 1, 0, OLD_GROUP_FORMAT_OPTION}, {"old-line-format", 1, 0, OLD_LINE_FORMAT_OPTION}, {"paginate", 0, 0, 'l'}, {"rcs", 0, 0, 'n'}, {"recursive", 0, 0, 'r'}, {"report-identical-files", 0, 0, 's'}, {"sdiff-merge-assist", 0, 0, SDIFF_MERGE_ASSIST_OPTION}, {"show-c-function", 0, 0, 'p'}, {"show-function-line", 1, 0, 'F'}, {"side-by-side", 0, 0, 'y'}, {"speed-large-files", 0, 0, 'H'}, {"starting-file", 1, 0, 'S'}, {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION}, {"suppress-common-lines", 0, 0, SUPPRESS_COMMON_LINES_OPTION}, {"tabsize", 1, 0, TABSIZE_OPTION}, {"text", 0, 0, 'a'}, {"to-file", 1, 0, TO_FILE_OPTION}, {"unchanged-group-format", 1, 0, UNCHANGED_GROUP_FORMAT_OPTION}, {"unchanged-line-format", 1, 0, UNCHANGED_LINE_FORMAT_OPTION}, {"unidirectional-new-file", 0, 0, 'P'}, {"unified", 2, 0, 'U'}, {"version", 0, 0, 'v'}, {"width", 1, 0, 'W'}, {0, 0, 0, 0}};intmain (int argc, char **argv){ int exit_status = EXIT_SUCCESS; int c; int i; int prev = -1; lin ocontext = -1; bool explicit_context = false; size_t width = 0; bool show_c_function = false; char const *from_file = 0; char const *to_file = 0; uintmax_t numval; char *numend; /* Do our initializations. */ exit_failure = 2; initialize_main (&argc, &argv); program_name = argv[0]; setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); c_stack_action (0); function_regexp_list.buf = &function_regexp; ignore_regexp_list.buf = &ignore_regexp; re_set_syntax (RE_SYNTAX_GREP | RE_NO_POSIX_BACKTRACKING); excluded = new_exclude (); /* Decode the options. */ while ((c = getopt_long (argc, argv, shortopts, longopts, 0)) != -1) { switch (c) { case 0: break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (! ISDIGIT (prev)) ocontext = c - '0'; else if (LIN_MAX / 10 < ocontext || ((ocontext = 10 * ocontext + c - '0') < 0)) ocontext = LIN_MAX; break; case 'a': text = true; break; case 'b': if (ignore_white_space < IGNORE_SPACE_CHANGE) ignore_white_space = IGNORE_SPACE_CHANGE; break; case 'B': ignore_blank_lines = true; break; case 'C': case 'U': { if (optarg) { numval = strtoumax (optarg, &numend, 10); if (*numend) try_help ("invalid context length `%s'", optarg); if (LIN_MAX < numval) numval = LIN_MAX; } else numval = 3; specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT); if (context < numval) context = numval; explicit_context = true; } break; case 'c': specify_style (OUTPUT_CONTEXT); if (context < 3) context = 3; break; case 'd': minimal = true; break; case 'D': specify_style (OUTPUT_IFDEF); { static char const C_ifdef_group_formats[] = "%%=%c#ifndef %s\n%%<#endif /* ! %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n"; char *b = xmalloc (sizeof C_ifdef_group_formats + 7 * strlen (optarg) - 14 /* 7*"%s" */ - 8 /* 5*"%%" + 3*"%c" */); sprintf (b, C_ifdef_group_formats, 0, optarg, optarg, 0, optarg, optarg, 0, optarg, optarg, optarg); for (i = 0; i < sizeof group_format / sizeof *group_format; i++) { specify_value (&group_format[i], b, "-D"); b += strlen (b) + 1; } } break; case 'e': specify_style (OUTPUT_ED); break; case 'E': if (ignore_white_space < IGNORE_TAB_EXPANSION) ignore_white_space = IGNORE_TAB_EXPANSION; break; case 'f': specify_style (OUTPUT_FORWARD_ED); break; case 'F': add_regexp (&function_regexp_list, optarg); break; case 'h': /* Split the files into chunks for faster processing. Usually does not change the result. This currently has no effect. */ break; case 'H': speed_large_files = true; break; case 'i': ignore_case = true; break; case 'I': add_regexp (&ignore_regexp_list, optarg); break; case 'l': if (!pr_program[0]) try_help ("pagination not supported on this host", 0); paginate = true;#ifdef SIGCHLD /* Pagination requires forking and waiting, and System V fork+wait does not work if SIGCHLD is ignored. */ signal (SIGCHLD, SIG_DFL);#endif break; case 'L': if (!file_label[0]) file_label[0] = optarg; else if (!file_label[1]) file_label[1] = optarg; else fatal ("too many file label options"); break; case 'n': specify_style (OUTPUT_RCS); break; case 'N': new_file = true; break; case 'p': show_c_function = true; add_regexp (&function_regexp_list, "^[[:alpha:]$_]"); break; case 'P': unidirectional_new_file = true; break; case 'q': brief = true; break; case 'r': recursive = true; break; case 's': report_identical_files = true; break; case 'S':
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -