📄 diff.c
字号:
%% %\n\ %c'C' the single character C\n\ %c'\\OOO' the character with octal code OOO"), "", N_("-l --paginate Pass the output through `pr' to paginate it."), N_("-t --expand-tabs Expand tabs to spaces in output."), N_("-T --initial-tab Make tabs line up by prepending a tab."), N_("--tabsize=NUM Tab stops are every NUM (default 8) print columns."), "", N_("-r --recursive Recursively compare any subdirectories found."), N_("-N --new-file Treat absent files as empty."), N_("--unidirectional-new-file Treat absent first files as empty."), N_("-s --report-identical-files Report when two files are the same."), N_("-x PAT --exclude=PAT Exclude files that match PAT."), N_("-X FILE --exclude-from=FILE Exclude files that match any pattern in FILE."), N_("-S FILE --starting-file=FILE Start with FILE when comparing directories."), N_("--from-file=FILE1 Compare FILE1 to all operands. FILE1 can be a directory."), N_("--to-file=FILE2 Compare all operands to FILE2. FILE2 can be a directory."), "", N_("--horizon-lines=NUM Keep NUM lines of the common prefix and suffix."), N_("-d --minimal Try hard to find a smaller set of changes."), N_("--speed-large-files Assume large files and many scattered small changes."), "", N_("-v --version Output version info."), N_("--help Output this help."), "", N_("FILES are `FILE1 FILE2' or `DIR1 DIR2' or `DIR FILE...' or `FILE... DIR'."), N_("If --from-file or --to-file is given, there are no restrictions on FILES."), N_("If a FILE is `-', read standard input."), N_("Exit status is 0 if inputs are the same, 1 if different, 2 if trouble."), "", N_("Report bugs to <bug-gnu-utils@gnu.org>."), 0};static voidusage (void){ char const * const *p; printf (_("Usage: %s [OPTION]... FILES\n"), program_name); for (p = option_help_msgid; *p; p++) { if (!**p) putchar ('\n'); else { char const *msg = _(*p); char const *nl; while ((nl = strchr (msg, '\n'))) { int msglen = nl + 1 - msg; printf (" %.*s", msglen, msg); msg = nl + 1; } printf (" %s\n" + 2 * (*msg != ' ' && *msg != '-'), msg); } }}/* Set VAR to VALUE, reporting an OPTION error if this is a conflict. */static voidspecify_value (char const **var, char const *value, char const *option){ if (*var && strcmp (*var, value) != 0) { error (0, 0, _("conflicting %s option value `%s'"), option, value); try_help (0, 0); } *var = value;}/* Set the output style to STYLE, diagnosing conflicts. */static voidspecify_style (enum output_style style){ if (output_style != style) { if (output_style != OUTPUT_UNSPECIFIED) try_help ("conflicting output style options", 0); output_style = style; }}/* Set the last-modified time of *ST to be the current time. */static voidset_mtime_to_now (struct stat *st){#ifdef ST_MTIM_NSEC# if HAVE_CLOCK_GETTIME && defined CLOCK_REALTIME if (clock_gettime (CLOCK_REALTIME, &st->st_mtim) == 0) return;# endif# if HAVE_GETTIMEOFDAY { struct timeval timeval; if (gettimeofday (&timeval, 0) == 0) { st->st_mtime = timeval.tv_sec; st->st_mtim.ST_MTIM_NSEC = timeval.tv_usec * 1000; return; } }# endif#endif /* ST_MTIM_NSEC */ time (&st->st_mtime);}/* Compare two files (or dirs) with parent comparison PARENT and names NAME0 and NAME1. (If PARENT is 0, then the first name is just NAME0, etc.) This is self-contained; it opens the files and closes them. Value is EXIT_SUCCESS if files are the same, EXIT_FAILURE if different, EXIT_TROUBLE if there is a problem opening them. */static intcompare_files (struct comparison const *parent, char const *name0, char const *name1){ struct comparison cmp;#define DIR_P(f) (S_ISDIR (cmp.file[f].stat.st_mode) != 0) register int f; int status = EXIT_SUCCESS; bool same_files; char *free0, *free1; /* If this is directory comparison, perhaps we have a file that exists only in one of the directories. If so, just print a message to that effect. */ if (! ((name0 && name1) || (unidirectional_new_file && name1) || new_file)) { char const *name = name0 == 0 ? name1 : name0; char const *dir = parent->file[name0 == 0].name; /* See POSIX 1003.1-2001 for this format. */ message ("Only in %s: %s\n", dir, name); /* Return EXIT_FAILURE so that diff_dirs will return EXIT_FAILURE ("some files differ"). */ return EXIT_FAILURE; } memset (cmp.file, 0, sizeof cmp.file); cmp.parent = parent; /* cmp.file[f].desc markers */#define NONEXISTENT (-1) /* nonexistent file */#define UNOPENED (-2) /* unopened file (e.g. directory) */#define ERRNO_ENCODE(errno) (-3 - (errno)) /* encoded errno value */#define ERRNO_DECODE(desc) (-3 - (desc)) /* inverse of ERRNO_ENCODE */ cmp.file[0].desc = name0 == 0 ? NONEXISTENT : UNOPENED; cmp.file[1].desc = name1 == 0 ? NONEXISTENT : UNOPENED; /* Now record the full name of each file, including nonexistent ones. */ if (name0 == 0) name0 = name1; if (name1 == 0) name1 = name0; if (!parent) { free0 = 0; free1 = 0; cmp.file[0].name = name0; cmp.file[1].name = name1; } else { cmp.file[0].name = free0 = dir_file_pathname (parent->file[0].name, name0); cmp.file[1].name = free1 = dir_file_pathname (parent->file[1].name, name1); } /* Stat the files. */ for (f = 0; f < 2; f++) { if (cmp.file[f].desc != NONEXISTENT) { if (f && file_name_cmp (cmp.file[f].name, cmp.file[0].name) == 0) { cmp.file[f].desc = cmp.file[0].desc; cmp.file[f].stat = cmp.file[0].stat; } else if (strcmp (cmp.file[f].name, "-") == 0) { cmp.file[f].desc = STDIN_FILENO; if (fstat (STDIN_FILENO, &cmp.file[f].stat) != 0) cmp.file[f].desc = ERRNO_ENCODE (errno); else { if (S_ISREG (cmp.file[f].stat.st_mode)) { off_t pos = lseek (STDIN_FILENO, (off_t) 0, SEEK_CUR); if (pos < 0) cmp.file[f].desc = ERRNO_ENCODE (errno); else cmp.file[f].stat.st_size = MAX (0, cmp.file[f].stat.st_size - pos); } /* POSIX 1003.1-2001 requires current time for stdin. */ set_mtime_to_now (&cmp.file[f].stat); } } else if (stat (cmp.file[f].name, &cmp.file[f].stat) != 0) cmp.file[f].desc = ERRNO_ENCODE (errno); } } /* Mark files as nonexistent as needed for -N and -P, if they are inaccessible empty regular files (the kind of files that 'patch' creates to indicate nonexistent backups), or if they are top-level files that do not exist but their counterparts do exist. */ for (f = 0; f < 2; f++) if ((new_file || (f == 0 && unidirectional_new_file)) && (cmp.file[f].desc == UNOPENED ? (S_ISREG (cmp.file[f].stat.st_mode) && ! (cmp.file[f].stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) && cmp.file[f].stat.st_size == 0) : (cmp.file[f].desc == ERRNO_ENCODE (ENOENT) && ! parent && cmp.file[1 - f].desc == UNOPENED))) cmp.file[f].desc = NONEXISTENT; for (f = 0; f < 2; f++) if (cmp.file[f].desc == NONEXISTENT) { memset (&cmp.file[f].stat, 0, sizeof cmp.file[f].stat); cmp.file[f].stat.st_mode = cmp.file[1 - f].stat.st_mode; } for (f = 0; f < 2; f++) { int e = ERRNO_DECODE (cmp.file[f].desc); if (0 <= e) { errno = e; perror_with_name (cmp.file[f].name); status = EXIT_TROUBLE; } } if (status == EXIT_SUCCESS && ! parent && DIR_P (0) != DIR_P (1)) { /* If one is a directory, and it was specified in the command line, use the file in that dir with the other file's basename. */ int fnm_arg = DIR_P (0); int dir_arg = 1 - fnm_arg; char const *fnm = cmp.file[fnm_arg].name; char const *dir = cmp.file[dir_arg].name; char const *filename = cmp.file[dir_arg].name = free0 = dir_file_pathname (dir, base_name (fnm)); if (strcmp (fnm, "-") == 0) fatal ("cannot compare `-' to a directory"); if (stat (filename, &cmp.file[dir_arg].stat) != 0) { perror_with_name (filename); status = EXIT_TROUBLE; } } if (status != EXIT_SUCCESS) { /* One of the files should exist but does not. */ } else if (cmp.file[0].desc == NONEXISTENT && cmp.file[1].desc == NONEXISTENT) { /* Neither file "exists", so there's nothing to compare. */ } else if ((same_files = (cmp.file[0].desc != NONEXISTENT && cmp.file[1].desc != NONEXISTENT && 0 < same_file (&cmp.file[0].stat, &cmp.file[1].stat) && same_file_attributes (&cmp.file[0].stat, &cmp.file[1].stat))) && no_diff_means_no_output) { /* The two named files are actually the same physical file. We know they are identical without actually reading them. */ } else if (DIR_P (0) & DIR_P (1)) { if (output_style == OUTPUT_IFDEF) fatal ("-D option not supported with directories"); /* If both are directories, compare the files in them. */ if (parent && !recursive) { /* But don't compare dir contents one level down unless -r was specified. See POSIX 1003.1-2001 for this format. */ message ("Common subdirectories: %s and %s\n", cmp.file[0].name, cmp.file[1].name); } else status = diff_dirs (&cmp, compare_files); } else if ((DIR_P (0) | DIR_P (1)) || (parent && (! S_ISREG (cmp.file[0].stat.st_mode) || ! S_ISREG (cmp.file[1].stat.st_mode)))) { if (cmp.file[0].desc == NONEXISTENT || cmp.file[1].desc == NONEXISTENT) { /* We have a subdirectory that exists only in one directory. */ if ((DIR_P (0) | DIR_P (1)) && recursive && (new_file || (unidirectional_new_file && cmp.file[0].desc == NONEXISTENT))) status = diff_dirs (&cmp, compare_files); else { char const *dir = parent->file[cmp.file[0].desc == NONEXISTENT].name; /* See POSIX 1003.1-2001 for this format. */ message ("Only in %s: %s\n", dir, name0); status = EXIT_FAILURE; } } else { /* We have two files that are not to be compared. */ /* See POSIX 1003.1-2001 for this format. */ message5 ("File %s is a %s while file %s is a %s\n", file_label[0] ? file_label[0] : cmp.file[0].name, file_type (&cmp.file[0].stat), file_label[1] ? file_label[1] : cmp.file[1].name, file_type (&cmp.file[1].stat)); /* This is a difference. */ status = EXIT_FAILURE; } } else if (files_can_be_treated_as_binary && S_ISREG (cmp.file[0].stat.st_mode) && S_ISREG (cmp.file[1].stat.st_mode) && cmp.file[0].stat.st_size != cmp.file[1].stat.st_size) { message ("Files %s and %s differ\n", file_label[0] ? file_label[0] : cmp.file[0].name, file_label[1] ? file_label[1] : cmp.file[1].name); status = EXIT_FAILURE; } else { /* Both exist and neither is a directory. */ /* Open the files and record their descriptors. */ if (cmp.file[0].desc == UNOPENED) if ((cmp.file[0].desc = open (cmp.file[0].name, O_RDONLY, 0)) < 0) { perror_with_name (cmp.file[0].name); status = EXIT_TROUBLE; } if (cmp.file[1].desc == UNOPENED) { if (same_files) cmp.file[1].desc = cmp.file[0].desc; else if ((cmp.file[1].desc = open (cmp.file[1].name, O_RDONLY, 0)) < 0) { perror_with_name (cmp.file[1].name); status = EXIT_TROUBLE; } }#if HAVE_SETMODE_DOS if (binary) for (f = 0; f < 2; f++) if (0 <= cmp.file[f].desc) set_binary_mode (cmp.file[f].desc, true);#endif /* Compare the files, if no error was found. */ if (status == EXIT_SUCCESS) status = diff_2_files (&cmp); /* Close the file descriptors. */ if (0 <= cmp.file[0].desc && close (cmp.file[0].desc) != 0) { perror_with_name (cmp.file[0].name); status = EXIT_TROUBLE; } if (0 <= cmp.file[1].desc && cmp.file[0].desc != cmp.file[1].desc && close (cmp.file[1].desc) != 0) { perror_with_name (cmp.file[1].name); status = EXIT_TROUBLE; } } /* Now the comparison has been done, if no error prevented it, and STATUS is the value this function will return. */ if (status == EXIT_SUCCESS) { if (report_identical_files && !DIR_P (0)) message ("Files %s and %s are identical\n", file_label[0] ? file_label[0] : cmp.file[0].name, file_label[1] ? file_label[1] : cmp.file[1].name); } else { /* Flush stdout so that the user sees differences immediately. This can hurt performance, unfortunately. */ if (fflush (stdout) != 0) pfatal_with_name (_("standard output")); } if (free0) free (free0); if (free1) free (free1); return status;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -