diff -u -d -x '*.[oea]*' -x '[.M]*' diffutils-3.0/src/analyze.c diffutils-3.0.AAA/src/analyze.c --- diffutils-3.0/src/analyze.c 2010-04-15 13:58:08.000000000 +0100 +++ diffutils-3.0.AAA/src/analyze.c 2010-09-26 12:58:17.656250000 +0100 @@ -447,21 +447,37 @@ /* If CHANGES, briefly report that two files differed. Return 2 if trouble, CHANGES otherwise. */ -static int +int briefly_report (int changes, struct file_data const filevec[]) { + if (changes == 0 && (is_different & PROPERTIES)) + changes = 1; if (changes) { char const *label0 = file_label[0] ? file_label[0] : filevec[0].name; char const *label1 = file_label[1] ? file_label[1] : filevec[1].name; - if (brief) - message ("Files %s and %s differ\n", label0, label1); + if (S_ISDIR (filevec[0].stat.st_mode) != 0) + { + if (S_ISDIR (filevec[1].stat.st_mode) != 0) + message ("Directories %s and %s differ\n", label0, label1); + else + message ("Directory %s and file %s differ\n", label0, label1); + } else { - message ("Binary files %s and %s differ\n", label0, label1); - changes = 2; + if (S_ISDIR (filevec[1].stat.st_mode) != 0) + message ("File %s and directory %s differ\n", label0, label1); + else + { + if (brief) + message ("Files %s and %s differ\n", label0, label1); + else + message ("Binary files %s and %s differ\n", label0, label1); + } } + if (! brief) + changes = 2; } return changes; @@ -536,7 +552,30 @@ } } - changes = briefly_report (changes, cmp->file); + if (brief || compare <= CONTENT) + /* Brief output option, or binary file and only comparing contents. */ + changes = briefly_report (changes, cmp->file); + else + { + /* Binary file. */ + if (changes == 1 || (is_different & PROPERTIES)) + { + files[0] = cmp->file[0]; + files[1] = cmp->file[1]; + setup_output (cmp); + begin_output (); + /* Report binary file content differences that can't be explicitly shown. */ + if (changes == 1) + message ("Contents of %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); + else + /* PROPERTIES_DIFFER == TRUE. */ + changes = 1; + } + } + if (changes == 1) + is_different |= CONTENT; } else { @@ -634,17 +673,21 @@ else changes = (script != 0); + if (changes == 1) + is_different |= CONTENT; + if (brief) changes = briefly_report (changes, cmp->file); else { + if (changes == 0 && (is_different & PROPERTIES)) + changes = 1; if (changes || !no_diff_means_no_output) { /* Record info for starting up output, to be used if and when we have some output to print. */ - setup_output (file_label[0] ? file_label[0] : cmp->file[0].name, - file_label[1] ? file_label[1] : cmp->file[1].name, - cmp->parent != 0); + setup_output (cmp); + begin_output (); switch (output_style) { @@ -686,6 +729,14 @@ finish_output (); } + /* Report a non-existent file / regular file (normally empty) + comparison that produces no 'content' differences, as having + different 'content'. */ + if (compare > 0 && !(is_different & CONTENT) && + (cmp->file[0].desc == -1 || cmp->file[1].desc == -1)) + message ("Contents of %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); } free (cmp->file[0].undiscarded); diff -u -d -x '*.[oea]*' -x '[.M]*' diffutils-3.0/src/context.c diffutils-3.0.AAA/src/context.c --- diffutils-3.0/src/context.c 2010-04-15 20:53:08.000000000 +0100 +++ diffutils-3.0.AAA/src/context.c 2010-09-26 12:58:17.656250000 +0100 @@ -21,6 +21,10 @@ #include "diff.h" #include "c-ctype.h" #include +#include +#include +#include +#include #include #include @@ -36,59 +40,122 @@ /* The value find_function returned when it started searching there. */ static lin find_function_last_match; -/* Print a label for a context diff, with a file name and date or a label. */ +/* Print a label for a context or normal diff, with a file name and date, a file + name and file properities, or a label. */ static void -print_context_label (char const *mark, +print_label (char const *mark, struct file_data *inf, - char const *label) + char const *label, + int const file_num) { + char const *quoted_name = (quote_filenames) ? + quotearg_n_style (0, shell_quoting_style, inf->name) : inf->name; + char whitespace = (compare == 0) ? '\t' : ' '; if (label) fprintf (outfile, "%s %s\n", mark, label); else { - char buf[MAX (INT_STRLEN_BOUND (int) + 32, - INT_STRLEN_BOUND (time_t) + 11)]; - struct tm const *tm = localtime (&inf->stat.st_mtime); - int nsec = get_stat_mtime_ns (&inf->stat); - if (! (tm && nstrftime (buf, sizeof buf, time_format, tm, 0, nsec))) + fprintf (outfile, "%s %s", mark, quoted_name); + if (compare > CONTENT || (compare == CONTENT && inf->desc == -1)) + for (int i=compare_width[0] * file_num;i>0;--i) + fprintf (outfile, " "); + if (inf->desc == -1 && compare > 0) + fprintf (outfile, "%cnon-existent %s\n", whitespace, + (S_ISDIR (inf->stat.st_mode) != 0) ? "directory" : "file"); + else { - verify (TYPE_IS_INTEGER (time_t)); - if (LONG_MIN <= TYPE_MINIMUM (time_t) - && TYPE_MAXIMUM (time_t) <= LONG_MAX) + if (compare == 0 || (compare & TIME)) { - long int sec = inf->stat.st_mtime; - sprintf (buf, "%ld.%.9d", sec, nsec); + char buf[MAX (INT_STRLEN_BOUND (int) + 32, + INT_STRLEN_BOUND (time_t) + 11)]; + struct tm const *tm = localtime (&inf->stat.st_mtime); + int nsec = get_stat_mtime_ns (&inf->stat); + if (! (tm && nstrftime (buf, sizeof buf, time_format, tm, 0, nsec))) + { + verify (TYPE_IS_INTEGER (time_t)); + if (LONG_MIN <= TYPE_MINIMUM (time_t) + && TYPE_MAXIMUM (time_t) <= LONG_MAX) + { + long int sec = inf->stat.st_mtime; + sprintf (buf, "%ld.%.9d", sec, nsec); + } + else if (TYPE_MAXIMUM (time_t) <= INTMAX_MAX) + { + intmax_t sec = inf->stat.st_mtime; + sprintf (buf, "%"PRIdMAX".%.9d", sec, nsec); + } + else + { + uintmax_t sec = inf->stat.st_mtime; + sprintf (buf, "%"PRIuMAX".%.9d", sec, nsec); + } + } + fprintf (outfile, "%c%s", whitespace, buf); } - else if (TYPE_MAXIMUM (time_t) <= INTMAX_MAX) + if (compare & MODE) { - intmax_t sec = inf->stat.st_mtime; - sprintf (buf, "%"PRIdMAX".%.9d", sec, nsec); + char modebuf[12]; + filemodestring (&inf->stat, modebuf); + modebuf[10]='\0'; + fprintf (outfile, " mode=%s",modebuf); } - else + if (compare & SIZE) { - uintmax_t sec = inf->stat.st_mtime; - sprintf (buf, "%"PRIuMAX".%.9d", sec, nsec); + fprintf (outfile, " size="); + for (int i=compare_width[3] * file_num;i>0;--i) + fprintf (outfile, " "); + fprintf (outfile, "%lli", inf->stat.st_size); + } + if (compare & OWNER) + { + fprintf (outfile, " owner=%s", getuser (inf->stat.st_uid)); + for (int i=compare_width[4] * file_num;i>0;--i) + fprintf (outfile, " "); } + if (compare & GROUP) + { + fprintf (outfile, " group=%s", getgroup (inf->stat.st_gid)); + for (int i=compare_width[5] * file_num;i>0;--i) + fprintf (outfile, " "); + } + if (compare & OBJECTS) + { + fprintf (outfile, " object=%s", file_type (&inf->stat)); + } + putc ('\n', outfile); } - fprintf (outfile, "%s %s\t%s\n", mark, inf->name, buf); } } -/* Print a header for a context diff, with the file names and dates. */ +/* Print a header for a context or normal diff, with the file names, + and dates or properties. */ void -print_context_header (struct file_data inf[], bool unidiff) +print_header (struct file_data inf[], enum output_style output_style) { - if (unidiff) - { - print_context_label ("---", &inf[0], file_label[0]); - print_context_label ("+++", &inf[1], file_label[1]); - } - else + switch (output_style) { - print_context_label ("***", &inf[0], file_label[0]); - print_context_label ("---", &inf[1], file_label[1]); + case OUTPUT_CONTEXT: + print_label ("***", &inf[0], file_label[0], -1); + print_label ("---", &inf[1], file_label[1], +1); + break; + + case OUTPUT_UNIFIED: + print_label ("---", &inf[0], file_label[0], -1); + print_label ("+++", &inf[1], file_label[1], +1); + break; + + case OUTPUT_NORMAL: + if (compare >= CONTENT) + { + print_label ("<<<", &inf[0], file_label[0], -1); + print_label (">>>", &inf[1], file_label[1], +1); + } + break; + + default: + break; } } @@ -150,7 +217,7 @@ putc (' ', out); for (i = 0; c_isspace ((unsigned char) function[i]) && function[i] != '\n'; i++) continue; - for (j = i; j < i + 40 && function[j] != '\n'; j++) + for (j = i; j < i + function_width && function[j] != '\n'; j++) continue; while (i < j && c_isspace ((unsigned char) function[j - 1])) j--; diff -u -d -x '*.[oea]*' -x '[.M]*' diffutils-3.0/src/diff.c diffutils-3.0.AAA/src/diff.c --- diffutils-3.0/src/diff.c 2010-04-15 20:53:08.000000000 +0100 +++ diffutils-3.0.AAA/src/diff.c 2010-09-26 12:58:17.656250000 +0100 @@ -28,9 +28,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -64,6 +66,8 @@ }; static int compare_files (struct comparison const *, char const *, char const *); +static void add_ignore_string (struct ignore_strings *, char *); +static void compile_ignore_string (struct ignore_strings *); static void add_regexp (struct regexp_list *, char const *); static void summarize_regexp_list (struct regexp_list *); static void specify_style (enum output_style); @@ -72,10 +76,6 @@ 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; @@ -106,21 +106,25 @@ static bool report_identical_files; static char const shortopts[] = -"0123456789abBcC:dD:eEfF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:y"; +"0123456789abBcC:dD:eEfF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:yz:"; /* Values for long options that do not have single-letter equivalents. */ enum { BINARY_OPTION = CHAR_MAX + 1, + COMPARE_OPTION, FROM_FILE_OPTION, + FUNCTION_WIDTH_OPTION, HELP_OPTION, HORIZON_LINES_OPTION, + IF_DIFFERENT_OPTION, IGNORE_FILE_NAME_CASE_OPTION, INHIBIT_HUNK_MERGE_OPTION, LEFT_COLUMN_OPTION, LINE_FORMAT_OPTION, NO_IGNORE_FILE_NAME_CASE_OPTION, NORMAL_OPTION, + QUOTE_FILENAMES_OPTION, SDIFF_MERGE_ASSIST_OPTION, STRIP_TRAILING_CR_OPTION, SUPPRESS_BLANK_EMPTY_OPTION, @@ -160,6 +164,7 @@ {"binary", 0, 0, BINARY_OPTION}, {"brief", 0, 0, 'q'}, {"changed-group-format", 1, 0, CHANGED_GROUP_FORMAT_OPTION}, + {"compare", 1, 0, COMPARE_OPTION}, {"context", 2, 0, 'C'}, {"ed", 0, 0, 'e'}, {"exclude", 1, 0, 'x'}, @@ -167,13 +172,16 @@ {"expand-tabs", 0, 0, 't'}, {"forward-ed", 0, 0, 'f'}, {"from-file", 1, 0, FROM_FILE_OPTION}, + {"function-width", 1, 0, FUNCTION_WIDTH_OPTION}, {"help", 0, 0, HELP_OPTION}, {"horizon-lines", 1, 0, HORIZON_LINES_OPTION}, {"ifdef", 1, 0, 'D'}, + {"if-different", 0, 0, IF_DIFFERENT_OPTION}, {"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-equivalent-strings", 1, 0, 'z'}, {"ignore-matching-lines", 1, 0, 'I'}, {"ignore-space-change", 0, 0, 'b'}, {"ignore-tab-expansion", 0, 0, 'E'}, @@ -191,6 +199,7 @@ {"old-group-format", 1, 0, OLD_GROUP_FORMAT_OPTION}, {"old-line-format", 1, 0, OLD_LINE_FORMAT_OPTION}, {"paginate", 0, 0, 'l'}, + {"quote-filenames", 0, 0, QUOTE_FILENAMES_OPTION}, {"rcs", 0, 0, 'n'}, {"recursive", 0, 0, 'r'}, {"report-identical-files", 0, 0, 's'}, @@ -281,6 +290,8 @@ c_stack_action (0); function_regexp_list.buf = &function_regexp; ignore_regexp_list.buf = &ignore_regexp; + ignore_string_list.count = 0; + ignore_string_list.alloc = 0; re_set_syntax (RE_SYNTAX_GREP | RE_NO_POSIX_BACKTRACKING); excluded = new_exclude (); @@ -514,6 +525,10 @@ } break; + case 'z': + add_ignore_string (&ignore_string_list, optarg); + break; + case BINARY_OPTION: #if O_BINARY binary = true; @@ -522,6 +537,44 @@ #endif break; + case COMPARE_OPTION: + compare = 0; + { + char *s=strdup (optarg); + char *ps=strtok (s, ","); + while (ps) + { + bool negate=(*ps=='~'); + if (negate) ++ps; + unsigned int compare_item=0; + if (strcmp (ps, "content") == 0) + compare_item = CONTENT; + else if (strcmp (ps, "time") == 0) + compare_item = TIME; + else if (strcmp (ps, "mode") == 0) + compare_item = MODE; + else if (strcmp (ps, "size") == 0) + compare_item = SIZE; + else if (strcmp (ps, "owner") == 0) + compare_item = OWNER; + else if (strcmp (ps, "group") == 0) + compare_item = GROUP; + else if (strcmp (ps, "objects") == 0) + compare_item = OBJECTS; + else if (strcmp (ps, "all") == 0) + compare_item = ~OBJECTS; + else + try_help ("invalid compare option `%s'", ps); + if (negate) + compare &= ~compare_item; + else + compare |= compare_item; + ps=strtok (NULL, ","); + } + free (s); + } + break; + case FROM_FILE_OPTION: specify_value (&from_file, optarg, "--from-file"); break; @@ -538,6 +591,22 @@ horizon_lines = MAX (horizon_lines, MIN (numval, LIN_MAX)); break; + case FUNCTION_WIDTH_OPTION: + numval = strtoumax (optarg, &numend, 10); + if (! (0 < numval && numval <= SIZE_MAX) || *numend) + try_help ("invalid 'function width' size `%s'", optarg); + if (function_width != numval) + { + if (function_width) + fatal ("conflicting 'function width' options"); + function_width = numval; + } + break; + + case IF_DIFFERENT_OPTION: + if_different = true; + break; + case IGNORE_FILE_NAME_CASE_OPTION: ignore_file_name_case = true; break; @@ -565,6 +634,10 @@ specify_style (OUTPUT_NORMAL); break; + case QUOTE_FILENAMES_OPTION: + quote_filenames = true; + break; + case SDIFF_MERGE_ASSIST_OPTION: specify_style (OUTPUT_SDIFF); sdiff_merge_assist = true; @@ -659,6 +732,18 @@ tabsize = 8; if (! width) width = 130; + if (! function_width) + function_width = 40; + + /* Side-by-side style (normally) outputs the whole files, even if they are the same, + so switch off the 'only difference if different' option. */ + if (if_different && (output_style == OUTPUT_SDIFF && !suppress_common_lines)) + if_different = false; + + /* The 'compare' option only applies to normal and both context styles. */ + if (compare > 0 && (output_style != OUTPUT_NORMAL && + output_style != OUTPUT_CONTEXT && output_style != OUTPUT_UNIFIED)) + compare = 0; { /* Maximize first the half line width, and then the gutter width, @@ -683,6 +768,7 @@ if (horizon_lines < context) horizon_lines = context; + compile_ignore_string (&ignore_string_list); summarize_regexp_list (&function_regexp_list); summarize_regexp_list (&ignore_regexp_list); @@ -714,7 +800,8 @@ files_can_be_treated_as_binary = (brief & binary & ~ (ignore_blank_lines | ignore_case | strip_trailing_cr - | (ignore_regexp_list.regexps || ignore_white_space))); + | (ignore_regexp_list.regexps || ignore_white_space || + ignore_string_list.count))); switch_string = option_list (argv + 1, optind - 1); @@ -761,6 +848,58 @@ return exit_status; } +/* Allocate space and add to IGNLIST the regexps for + ignoring strings specified by PATTERN. */ + +static void +add_ignore_string (struct ignore_strings *ignlist, char *pattern) +{ + /* Allocate space for data structures. */ + if (ignlist->count <= ignlist->alloc) + ignlist->regexp = (struct ignore_string *) x2nrealloc (ignlist->regexp, + &ignlist->alloc, sizeof (struct ignore_string)); + + size_t i = ignlist->count; + ignlist->regexp[i].regs = (struct re_registers *) + xmalloc (sizeof (struct re_registers)); + ignlist->regexp[i].buf = (struct re_pattern_buffer *) + xcalloc (1, sizeof (struct re_pattern_buffer)); + ignlist->regexp[i].buf->fastmap = xmalloc (1 << CHAR_BIT); + + /* Save the pattern, rather than compile it here, + since IGNORE_CASE may change. */ + ignlist->regexp[i].pattern = pattern; + ignlist->count++; +} + +/* Compile the regexps in IGNLIST. */ + +static void +compile_ignore_string (struct ignore_strings *ignlist) +{ + if (!ignlist->count) return; + + reg_syntax_t toggle_re_syntax = re_set_syntax (re_syntax_options | + RE_SYNTAX_EGREP | (ignore_case ? RE_ICASE : 0)); + + size_t i; + for (i = 0; i < ignlist->count; i++) + { + size_t patlen = strlen (ignlist->regexp[i].pattern); + char const *m = re_compile_pattern (ignlist->regexp[i].pattern, + patlen, ignlist->regexp[i].buf); + if (m != 0) + error (0, 0, "%s: %s", ignlist->regexp[i].pattern, m); + + /* Any regexp that can match a NULL string (e.g. -z a|) + must not update the hash value. */ + ignlist->regexp[i].update_hash = + re_search (ignlist->regexp[i].buf, "", 0, 0, 0, 0) == 0 ? false : true; + } + + toggle_re_syntax=re_set_syntax (toggle_re_syntax); +} + /* Append to REGLIST the regexp PATTERN. */ static void @@ -843,80 +982,109 @@ static char const * const option_help_msgid[] = { N_("Compare files line by line."), "", - N_("-i --ignore-case Ignore case differences in file contents."), - N_("--ignore-file-name-case Ignore case when comparing file names."), - N_("--no-ignore-file-name-case Consider case when comparing file names."), - N_("-E --ignore-tab-expansion Ignore changes due to tab expansion."), - N_("-b --ignore-space-change Ignore changes in the amount of white space."), - N_("-w --ignore-all-space Ignore all white space."), - N_("-B --ignore-blank-lines Ignore changes whose lines are all blank."), - N_("-I RE --ignore-matching-lines=RE Ignore changes whose lines all match RE."), - N_("--strip-trailing-cr Strip trailing carriage return on input."), -#if O_BINARY - N_("--binary Read and write data in binary mode."), -#endif - N_("-a --text Treat all files as text."), + N_("What to ignore or compare:"), + N_("-i, --ignore-case Ignore case differences in file contents"), + N_(" --ignore-file-name-case Ignore case when comparing file names"), + N_(" --no-ignore-file-name-case"), + N_(" Consider case when comparing file names"), + N_("-E, --ignore-tab-expansion Ignore changes due to tab expansion"), + N_("-b, --ignore-space-change Ignore changes in the amount of white space"), + N_("-w, --ignore-all-space Ignore all white space"), + N_("-z ERE, --ignore-equivalent-strings=ERE"), + N_(" Ignore equivalent strings (strings"), + N_(" matching ERE in both files)"), + N_("-B, --ignore-blank-lines Ignore changes whose lines are all blank"), + N_("-I RE, --ignore-matching-lines=RE"), + N_(" Ignore changes whose lines all match RE"), + N_(" --strip-trailing-cr Strip trailing carriage return on input"), + N_(" --if-different Compare file contents only if the file"), + N_(" sizes or timestamps are different"), + N_(" --compare=PROPERTY[,PROPERTY...]"), + N_(" Properties to compare; PROPERTY may be any"), + N_(" of `content', `time', `mode', `size',"), + N_(" `owner', `group', `all' (for all of these),"), + N_(" or `objects'; properties are applied"), + N_(" sequentially, a `~' prefix switching that"), + N_(" property off."), "", - N_("-c -C NUM --context[=NUM] Output NUM (default 3) lines of copied context.\n\ --u -U NUM --unified[=NUM] Output NUM (default 3) lines of unified context.\n\ - --label LABEL Use LABEL instead of file name.\n\ - -p --show-c-function Show which C function each change is in.\n\ - -F RE --show-function-line=RE Show the most recent line matching RE."), - N_("-q --brief Output only whether files differ."), - N_("-e --ed Output an ed script."), - N_("--normal Output a normal diff."), - N_("-n --rcs Output an RCS format diff."), - N_("-y --side-by-side Output in two columns.\n\ - -W NUM --width=NUM Output at most NUM (default 130) print columns.\n\ - --left-column Output only the left column of common lines.\n\ - --suppress-common-lines Do not output common lines."), - N_("-D NAME --ifdef=NAME Output merged file to show `#ifdef NAME' diffs."), - N_("--GTYPE-group-format=GFMT Similar, but format GTYPE input groups with GFMT."), - N_("--line-format=LFMT Similar, but format all input lines with LFMT."), - N_("--LTYPE-line-format=LFMT Similar, but format LTYPE input lines with LFMT."), - N_(" LTYPE is `old', `new', or `unchanged'. GTYPE is LTYPE or `changed'."), - N_(" GFMT may contain:\n\ - %< lines from FILE1\n\ - %> lines from FILE2\n\ - %= lines common to FILE1 and FILE2\n\ - %[-][WIDTH][.[PREC]]{doxX}LETTER printf-style spec for LETTER\n\ - LETTERs are as follows for new group, lower case for old group:\n\ - F first line number\n\ - L last line number\n\ - N number of lines = L-F+1\n\ - E F-1\n\ - M L+1"), - N_(" LFMT may contain:\n\ - %L contents of line\n\ - %l contents of line, excluding any trailing newline\n\ - %[-][WIDTH][.[PREC]]{doxX}n printf-style spec for input line number"), - N_(" Either GFMT or LFMT may contain:\n\ - %% %\n\ - %c'C' the single character C\n\ - %c'\\OOO' the character with octal code OOO"), + N_("Output format control:"), + N_("-c, -C NUM, --context[=NUM] Output NUM (default 3) lines of copied context"), + N_("-u, -U NUM, --unified[=NUM] Output NUM (default 3) lines of unified context"), + N_(" -p, --show-c-function Show which C function each change is in"), + N_(" -F RE, --show-function-line=RE Show the most recent line matching RE"), + N_(" --function-width=NUM Max. columns of function line (default 40)"), + N_(" --label LABEL Use LABEL instead of file name"), + N_("-q, --brief Output only whether files differ"), + N_("-e, --ed Output an ed script"), + N_(" --normal Output a normal diff"), + N_("-n, --rcs Output an RCS format diff"), + N_("-y, --side-by-side Output in two columns"), + N_(" -W NUM, --width=NUM Output at most NUM (default 130) columns"), + N_(" --left-column Output only the left side of common lines"), + N_(" --suppress-common-lines Do not output common lines"), + N_("-D NAME, --ifdef=NAME Output merged file to show `#ifdef NAME' diffs"), + N_(" --GTYPE-group-format=GFMT"), + N_(" Similar, but format GTYPE input groups with GFMT"), + N_(" --line-format=LFMT"), + N_(" Similar, but format all input lines with LFMT"), + N_(" --LTYPE-line-format=LFMT"), + N_(" Similar, but format LTYPE input lines with LFMT"), + N_(" LTYPE is `old', `new', or `unchanged'. GTYPE is LTYPE or `changed'."), + N_(" GFMT may contain:"), + N_(" %< lines from FILE1"), + N_(" %> lines from FILE2"), + N_(" %= lines common to FILE1 and FILE2"), + N_(" %[-][WIDTH][.[PREC]]{doxX}LETTER printf-style spec for LETTER"), + N_(" LETTERs are as follows for new group, lower case for old group:"), + N_(" F first line number"), + N_(" L last line number"), + N_(" N number of lines = L-F+1"), + N_(" E F-1"), + N_(" M L+1"), + N_(" LFMT may contain:"), + N_(" %L contents of line"), + N_(" %l contents of line, excluding any trailing newline"), + N_(" %[-][WIDTH][.[PREC]]{doxX}n printf-style spec for input line number"), + N_(" Either GFMT or LFMT may contain:"), + N_(" %% %"), + 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_("--suppress-blank-empty Suppress space or tab before empty output lines."), + N_("Output modification:"), + 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_(" --suppress-blank-empty Suppress space or tab before empty output lines"), + N_(" --quote-filenames Quote filenames on output"), + N_("-s, --report-identical-files"), + N_(" Report when two files are the same"), "", - 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_("Recursive/directory control:"), + N_("-r, --recursive Recursively compare any subdirectories found"), + N_("-N, --new-file Treat absent files as empty"), + N_(" --unidirectional-new-file"), + N_(" Treat absent first files as empty"), + 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;"), + N_(" FILE1 can be a directory."), + N_(" --to-file=FILE2 Compare all operands to FILE2;"), + N_(" 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_("Algorithm control:"), + 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_("Miscellaneuoes:"), +#if O_BINARY + N_(" --binary Read and write data in binary mode"), +#endif + N_("-a, --text Treat all files as text"), + 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."), @@ -996,6 +1164,156 @@ # endif #endif } + +/* cmp.file[f].desc markers */ +#define NONEXISTENT (-1) /* nonexistent file */ +#define UNOPENED (-2) /* unopened file (e.g. directory) */ +#define TEMPNONEXIST (-99) /* temporarily treat file object as nonexistent */ +#define ERRNO_ENCODE(errno) (-3 - (errno)) /* encoded errno value */ +#define ERRNO_DECODE(desc) (-3 - (desc)) /* inverse of ERRNO_ENCODE */ + +/* Compare the file properties (except content). */ +static void +compare_properties (struct file_data const file[]) { + if (compare == 0) return; + + is_different = 0; + bool file0_exists = file[0].desc != NONEXISTENT; + bool file1_exists = file[1].desc != NONEXISTENT; + bool only_one_file_exists = !file0_exists || !file1_exists; + compare_width[0] = compare_width[3] = compare_width[4] = compare_width[5] = 0; + + if (only_one_file_exists) + is_different |= PROPERTIES; + + if ((compare & TIME) && (only_one_file_exists || + different_time (&file[0].stat,&file[1].stat))) + is_different |= TIME; + + if (compare & MODE) + { + char modebuf0[12],modebuf1[12]; + if (file0_exists) + filemodestring (&file[0].stat, modebuf0); + if (file1_exists) + filemodestring (&file[1].stat, modebuf1); + if (only_one_file_exists || strcmp(modebuf0,modebuf1) != 0) + is_different |= MODE; + } + + if ((compare & SIZE) && (only_one_file_exists || + file[0].stat.st_size != file[1].stat.st_size )) + { + is_different |= SIZE; + compare_width[3] = 0; + off_t q1 = file0_exists ? file[0].stat.st_size : 0; + off_t q2 = file1_exists ? file[1].stat.st_size : 0; + do { + if (q1 /= 10) ++compare_width[3]; + if (q2 /= 10) --compare_width[3]; + } while ((q1 | q2) != 0); + } + + if ((compare & OWNER) && (only_one_file_exists || + file[0].stat.st_uid != file[1].stat.st_uid)) + { + is_different |= OWNER; + compare_width[4] = strlen (file0_exists ? getuser (file[0].stat.st_uid) : "") - + strlen (file1_exists ? getuser (file[1].stat.st_uid) : ""); + } + + if ((compare & GROUP) && (only_one_file_exists || + file[0].stat.st_gid != file[1].stat.st_gid)) + { + is_different |= GROUP; + compare_width[5] = strlen (file0_exists ? getgroup (file[0].stat.st_gid) : "") - + strlen (file1_exists ? getgroup (file[1].stat.st_gid) : ""); + } + + if ((compare & OBJECTS) && (only_one_file_exists || + file_type (&file[0].stat)!=file_type (&file[1].stat))) + is_different |= OBJECTS; + + if ((is_different & PROPERTIES) || compare & CONTENT) + compare_width[0] = strlen (file[0].name) - strlen (file[1].name); + + return; +} + +/* Show the properties of two files (one of which can be nonexistent). + Value is EXIT_SUCCESS if the properties are the same, EXIT_FAILURE if + different, EXIT_TROUBLE if there is a problem opening them. */ +static int +show_properties (struct comparison const *cmp) { + if (brief) + return briefly_report (0, cmp->file); + else + { + files[0] = cmp->file[0]; + files[1] = cmp->file[1]; + setup_output (cmp); + begin_output (); + /* This is a difference. */ + return EXIT_FAILURE; + } +} + +/* Compare the contents of two files (one of which can be nonexistent). + Value is EXIT_SUCCESS if the contents are the same, EXIT_FAILURE if + different, EXIT_TROUBLE if there is a problem opening them. */ +static int +compare_contents_and_properties (struct comparison *cmp,const bool same_files) { + + int f; + int status = EXIT_SUCCESS; + + /* 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; + } + return status; +} /* Compare two files (or dirs) with parent comparison PARENT and names NAME0 and NAME1. @@ -1040,13 +1358,6 @@ 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 ? UNOPENED : NONEXISTENT; cmp.file[1].desc = name1 ? UNOPENED : NONEXISTENT; @@ -1112,11 +1423,11 @@ } } - /* 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. */ + /* Mark files as nonexistent as needed for --new-file (-N) and + --unidirectional-new-file, 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 @@ -1190,21 +1501,48 @@ } else if (DIR_P (0) & DIR_P (1)) { + /* Both objects are directories (D). One could be nonexistent (N), + if --new-file or --unidirectional-new-file were requested. + DD DN. */ + if (output_style == OUTPUT_IFDEF) fatal ("-D option not supported with directories"); - /* If both are directories, compare the files in them. */ + bool reported = false; + if (compare & OBJECTS) + { + compare_properties (cmp.file); + if ((is_different & PROPERTIES)) + { + status = show_properties (&cmp); + reported = true; + } + + if (report_identical_files && status == EXIT_SUCCESS) + { + message ("Directories %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); + reported = true; + } + } + + /* 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); + if (!reported) + message ("Common subdirectories: %s and %s\n", + cmp.file[0].name, cmp.file[1].name); } else - status = diff_dirs (&cmp, compare_files); + { + int new_status = diff_dirs (&cmp, compare_files); + status = MAX (status, new_status); + } } else if ((DIR_P (0) | DIR_P (1)) || (parent @@ -1215,14 +1553,41 @@ { /* We have a subdirectory that exists only in one directory. */ + /* FIXME - the previous comment is wrong? */ + /* The objects are a special file and nonexistent file. + SN. */ + bool done = false; + if ((compare & OBJECTS) + && (new_file || (unidirectional_new_file + && cmp.file[0].desc == NONEXISTENT))) + { + compare_properties (cmp.file); + if (is_different & PROPERTIES) + { + /* The objects are a special file and nonexistent file, + with --new-file or unidirectional-new-file. + SN. */ + status = show_properties (&cmp); + done = true; + } + } + 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 { + /* FIXME - can we ever get here? */ +printf("XXXX Let Duncan Moore know if you see this message XXXX\n"); // !! + int new_status = diff_dirs (&cmp, compare_files); + status = MAX (status, new_status); + } + else if (!done) + { + /* The objects are a special file and nonexistent file, + with no --new-file or unidirectional-new-file. + SN. */ char const *dir; /* PARENT must be non-NULL here. */ @@ -1237,71 +1602,121 @@ } else { - /* We have two files that are not to be compared. */ + /* The objects are regular files (R), directories (D) or + special files (S). None of them are nonexistent. + DR SR DS SS. */ + if (compare & OBJECTS) + { + compare_properties (cmp.file); + if (S_ISREG (cmp.file[0].stat.st_mode) + || S_ISREG (cmp.file[1].stat.st_mode)) + { + /* DR SR. */ + if (!(compare == 0 || (compare & CONTENT))) + /* The file contents are not being compared. + The properties must be different. */ + status = show_properties (&cmp); + else + { + int nonreg_file = S_ISREG (cmp.file[0].stat.st_mode) ? 1 : 0; + int temp_desc = cmp.file[nonreg_file].desc; + cmp.file[nonreg_file].desc = TEMPNONEXIST; + status = compare_contents_and_properties(&cmp,same_files); + cmp.file[nonreg_file].desc = temp_desc; + } + } + else + /* DS SS. */ + status = show_properties (&cmp); - /* 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)); + /* Report (normally) empty regular files (E), directories and + special files that produce no 'content' differences as having + different 'content', since this won't have been shown. */ + bool report = true; + if ((is_different & CONTENT) && + (S_ISREG (cmp.file[0].stat.st_mode) || + S_ISREG (cmp.file[1].stat.st_mode))) + report = false; - /* This is a difference. */ - status = EXIT_FAILURE; + if (report && ((DIR_P (0) | DIR_P (1)) || + (!(is_different & CONTENT) && + (S_ISREG (cmp.file[0].stat.st_mode) || + S_ISREG (cmp.file[1].stat.st_mode))))) + { + /* DE SE DS. */ + if (compare == 0 || (compare & CONTENT)) + { + status = MAX (status, EXIT_FAILURE); + if (!brief) + message ("Contents of %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); + } + } + + if (DIR_P (0) | DIR_P (1)) + { + /* DS DR. */ + if (recursive && (new_file + || (unidirectional_new_file && DIR_P (1)))) + { + cmp.file[DIR_P (0)].desc = NONEXISTENT; + int new_status = diff_dirs (&cmp, compare_files); + status = MAX (status, new_status); + } + } + } + else + { + /* DS DR SR SS. */ + /* We have two files that are not to be compared. */ + + /* See POSIX 1003.1-2001 for this format. */ + message2 ("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. */ + /* Both objects 'exist' and neither is a directory. + One file may be marked nonexistent - it exists only + for the purposes of --new-file or --unidirectional-new-file. + RR RN. */ - int oflags = O_RDONLY | (binary ? O_BINARY : 0); + compare_properties (cmp.file); - if (cmp.file[0].desc == UNOPENED) - if ((cmp.file[0].desc = open (cmp.file[0].name, oflags, 0)) < 0) - { - perror_with_name (cmp.file[0].name); - status = EXIT_TROUBLE; - } - if (cmp.file[1].desc == UNOPENED) + if ( !(compare == 0 || (compare & CONTENT) ) || + ( if_different && + !different_time_or_size (&cmp.file[0].stat, &cmp.file[1].stat) ) ) { - if (same_files) - cmp.file[1].desc = cmp.file[0].desc; - else if ((cmp.file[1].desc = open (cmp.file[1].name, oflags, 0)) < 0) - { - perror_with_name (cmp.file[1].name); - status = EXIT_TROUBLE; - } + /* The file contents are not being compared. */ + if (is_different & PROPERTIES) + status = show_properties (&cmp); } - - /* 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) + 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) { - perror_with_name (cmp.file[0].name); - status = EXIT_TROUBLE; + /* The file contents are being compared and can be treated as binary. */ + 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; } - if (0 <= cmp.file[1].desc && cmp.file[0].desc != cmp.file[1].desc - && close (cmp.file[1].desc) != 0) + else { - perror_with_name (cmp.file[1].name); - status = EXIT_TROUBLE; + /* The file contents are being compared, and may or may not be binary. */ + + status = compare_contents_and_properties(&cmp, same_files); + } } @@ -1310,7 +1725,7 @@ if (status == EXIT_SUCCESS) { - if (report_identical_files && !DIR_P (0)) + if (report_identical_files && !DIR_P (0) && !DIR_P (1)) 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); diff -u -d -x '*.[oea]*' -x '[.M]*' diffutils-3.0/src/diff.h diffutils-3.0.AAA/src/diff.h --- diffutils-3.0/src/diff.h 2010-04-15 20:53:08.000000000 +0100 +++ diffutils-3.0.AAA/src/diff.h 2010-09-26 12:58:17.656250000 +0100 @@ -83,6 +83,46 @@ XTERN enum output_style output_style; +/* A bit-field of file properties to compare and output for --compare. + All bits 0, means do a standard diff comparison. */ +enum +{ + /* Compare file contents. */ + CONTENT = 1 << 0, + + /* Compare file timestamps. */ + TIME = 1 << 1, + + /* Compare file modes. */ + MODE = 1 << 2, + + /* Compare file sizes. */ + SIZE = 1 << 3, + + /* Compare file owners. */ + OWNER = 1 << 4, + + /* Compare file groups. */ + GROUP = 1 << 5, + + /* Compare file types. */ + OBJECTS = 1 << 6, + + /* Mask for all properties except content. */ + PROPERTIES = ~CONTENT +}; +/* The properties to compare. */ +XTERN unsigned int compare; +/* True if the property is different. */ +XTERN unsigned int is_different; + +/* The difference in output width of the --compare properties of + the two files. */ +XTERN int compare_width[7]; + +/* Only compare file contents if the sizes or timestamps are different. */ +XTERN bool if_different; + /* Nonzero if output cannot be generated for identical files. */ XTERN bool no_diff_means_no_output; @@ -129,15 +169,46 @@ /* File labels for `-c' output headers (--label). */ XTERN char *file_label[2]; +/* Maximum number of columns to show for function-header lines + (--function-width). */ +XTERN size_t function_width; + /* Regexp to identify function-header lines (-F). */ XTERN struct re_pattern_buffer function_regexp; /* Ignore changes that affect only lines matching this regexp (-I). */ XTERN struct re_pattern_buffer ignore_regexp; +/* Ignore strings that match a regexp in both files (-z). */ +struct ignore_string +{ + struct re_pattern_buffer *buf; + struct re_registers *regs; + char *pattern; + int begin1; + int begin2; + int size1; + int size2; + bool update_hash; +}; +struct ignore_strings +{ + struct ignore_string *regexp; + size_t alloc; + size_t count; +}; +XTERN struct ignore_strings ignore_string_list; + +/* If comparing directories, compare their common subdirectories + recursively. */ +XTERN bool recursive; + /* Say only whether files differ, not how (-q). */ XTERN bool brief; +/* Quote printed filenames, to make them unambiguous (--quote-filenames). */ +XTERN bool quote_filenames; + /* Expand tabs in the output so the text lines up properly despite the characters added to the front of each line (-t). */ XTERN bool expand_tabs; @@ -316,10 +387,11 @@ /* Declare various functions. */ /* analyze.c */ +int briefly_report (int, struct file_data const []); int diff_2_files (struct comparison *); /* context.c */ -void print_context_header (struct file_data[], bool); +void print_header (struct file_data[], enum output_style); void print_context_script (struct change *, bool); /* dir.c */ @@ -350,7 +422,7 @@ extern char const pr_program[]; char *concat (char const *, char const *, char const *); char *dir_file_pathname (char const *, char const *); -bool lines_differ (char const *, char const *); +bool lines_differ (char const *, size_t, char const *, size_t); lin translate_line_number (struct file_data const *, lin); struct change *find_change (struct change *); struct change *find_reverse_change (struct change *); @@ -358,9 +430,12 @@ enum changes analyze_hunk (struct change *, lin *, lin *, lin *, lin *); void begin_output (void); void debug_script (struct change *); +bool different_time (const struct stat *, const struct stat *); +bool different_time_or_size (const struct stat *, const struct stat *); void fatal (char const *) __attribute__((noreturn)); void finish_output (void); void message (char const *, char const *, char const *); +void message2 (char const *, char const *, char const *, char const *, char const *); void message5 (char const *, char const *, char const *, char const *, char const *); void output_1_line (char const *, char const *, char const *, char const *); void perror_with_name (char const *); @@ -369,5 +444,5 @@ void print_message_queue (void); void print_number_range (char, struct file_data *, lin, lin); void print_script (struct change *, struct change * (*) (struct change *), void (*) (struct change *)); -void setup_output (char const *, char const *, bool); +void setup_output (struct comparison const *); void translate_range (struct file_data const *, lin, lin, long int *, long int *); diff -u -d -x '*.[oea]*' -x '[.M]*' diffutils-3.0/src/io.c diffutils-3.0.AAA/src/io.c --- diffutils-3.0/src/io.c 2010-04-17 07:15:46.000000000 +0100 +++ diffutils-3.0.AAA/src/io.c 2010-09-26 12:58:17.656250000 +0100 @@ -216,7 +216,7 @@ char const *suffix_begin = current->suffix_begin; char const *bufend = FILE_BUFFER (current) + current->buffered; bool diff_length_compare_anyway = - ignore_white_space != IGNORE_NO_WHITE_SPACE; + ignore_white_space != IGNORE_NO_WHITE_SPACE || ignore_string_list.count; bool same_length_diff_contents_compare_anyway = diff_length_compare_anyway | ignore_case; @@ -227,6 +227,7 @@ h = 0; /* Hash this line until we find a newline. */ + if (!ignore_string_list.count) if (ignore_case) switch (ignore_white_space) { @@ -263,20 +264,10 @@ switch (c) { - case '\b': - column -= 0 < column; - break; - case '\t': c = ' '; repetitions = tabsize - column % tabsize; - column = (column + repetitions < column - ? 0 - : column + repetitions); - break; - - case '\r': - column = 0; + column += repetitions; break; default: @@ -333,20 +324,10 @@ switch (c) { - case '\b': - column -= 0 < column; - break; - case '\t': c = ' '; repetitions = tabsize - column % tabsize; - column = (column + repetitions < column - ? 0 - : column + repetitions); - break; - - case '\r': - column = 0; + column += repetitions; break; default: @@ -366,6 +347,116 @@ h = HASH (h, c); break; } + else + { + size_t len = (size_t) strchr (p,'\n') - (size_t) p; + size_t column = 0; + size_t i; + for (i = 0 ; i < ignore_string_list.count; i++) + ignore_string_list.regexp[i].begin1 = -1; + + while (1) + { + start_loop : + for (i = 0; i < ignore_string_list.count; i++) + { + struct ignore_string *ign = &ignore_string_list.regexp[i]; + if (ign->begin1 == -2) continue; + if (ip + ign->begin1 < p) + { + if ((ign->begin1 = re_search (ign->buf, ip, len, p - ip, len, ign->regs)) < 0) + ign->begin1 = -2; + else + ign->size1 = ign->regs->end[0] - ign->regs->start[0]; + } + if (ip + ign->begin1 == p) + { + ign->begin1 = -1; + if (ign->size1 == 0) + continue; /* Proceed to the next regexp. */ + /* Update the hash value if the regexps + can only match non-NULL strings. */ + if (ignore_string_list.regexp[i].update_hash) + { + char* pp; + for (pp = ign->pattern; *pp; pp++) + h = HASH (h, *pp); + } + p += ign->size1; + column += ign->size1; + goto start_loop; + } + } + + c = *p++; + char const *pp = p; + + switch (ignore_white_space) + { + case IGNORE_ALL_SPACE: + /* For -w, just skip past any white space. */ + while (isspace (c) && c != '\n') + c = *p++; + break; + + case IGNORE_SPACE_CHANGE: + /* For -b, advance past any sequence of white space + and consider it just one space, or nothing at + all if it is at the end of the line. */ + if (isspace (c)) + { + while (c != '\n') + { + c = *p++; + if (! isspace (c)) + { + h = HASH (h, ' '); + break; + } + } + } + break; + + case IGNORE_TAB_EXPANSION: + { + if (c == ' ' || c == '\t') + { + for (;; c = *p++) + { + if (c == ' ') + column++; + else if (c == '\t') + column += tabsize - column % tabsize; + else + break; + } + /* With the -z option, we may need different 'lengths' of + white space to realign to the same column. Therefore we cannot + hash the whole length - so just use one space. */ + h = HASH (h, ' '); + } + } + break; + + case IGNORE_NO_WHITE_SPACE: + break; + } + + if (p != pp) + { + --p; + continue; + } + + if (c != '\n') + { + column++; + h = HASH (h, ignore_case ? tolower (c) : c); + } + else + break; + } + } hashing_done:; @@ -423,7 +514,7 @@ else if (!diff_length_compare_anyway) continue; - if (! lines_differ (eqline, ip)) + if (! lines_differ (eqline, eqs[i].length, ip, length)) break; } diff -u -d -x '*.[oea]*' -x '[.M]*' diffutils-3.0/src/util.c diffutils-3.0.AAA/src/util.c --- diffutils-3.0/src/util.c 2010-04-15 20:53:08.000000000 +0100 +++ diffutils-3.0.AAA/src/util.c 2010-09-26 12:58:17.671875000 +0100 @@ -21,6 +21,7 @@ #include "diff.h" #include #include +#include #include #include @@ -49,6 +50,8 @@ void perror_with_name (char const *name) { + if (quote_filenames) + name = quotearg_n_style (0, shell_quoting_style, name); error (0, errno, "%s", name); } @@ -79,10 +82,27 @@ void message (char const *format_msgid, char const *arg1, char const *arg2) { + if (quote_filenames) + { + arg1 = quotearg_n_style (0, shell_quoting_style, arg1); + arg2 = quotearg_n_style (1, shell_quoting_style, arg2); + } message5 (format_msgid, arg1, arg2, 0, 0); } void +message2 (char const *format_msgid, char const *arg1, char const *arg2, + char const *arg3, char const *arg4) +{ + if (quote_filenames) + { + arg1 = quotearg_n_style (0, shell_quoting_style, arg1); + arg3 = quotearg_n_style (1, shell_quoting_style, arg2); + } + message5 (format_msgid, arg1, arg2, arg3, arg4); +} + +void message5 (char const *format_msgid, char const *arg1, char const *arg2, char const *arg3, char const *arg4) { @@ -151,14 +171,21 @@ static char const *current_name0; static char const *current_name1; -static bool currently_recursive; +static bool show_title; void -setup_output (char const *name0, char const *name1, bool recursive) +setup_output (struct comparison const *cmp) { - current_name0 = name0; - current_name1 = name1; - currently_recursive = recursive; + current_name0 = file_label[0] ? file_label[0] : cmp->file[0].name; + current_name1 = file_label[1] ? file_label[1] : cmp->file[1].name; + if (quote_filenames) + { + current_name0 = quotearg_n_style (0, shell_quoting_style, current_name0); + current_name1 = quotearg_n_style (1, shell_quoting_style, current_name1); + } + show_title = cmp->parent != 0 || recursive || + ((compare & OBJECTS) && (S_ISDIR (cmp->file[0].stat.st_mode) !=0 + || S_ISDIR (cmp->file[1].stat.st_mode) !=0)); outfile = 0; } @@ -247,26 +274,15 @@ /* If handling multiple files (because scanning a directory), print which files the following output is about. */ - if (currently_recursive) + if (show_title) printf ("%s\n", name); } free (name); - /* A special header is needed at the beginning of context output. */ - switch (output_style) - { - case OUTPUT_CONTEXT: - print_context_header (files, false); - break; - - case OUTPUT_UNIFIED: - print_context_header (files, true); - break; - - default: - break; - } + /* A special header is needed at the beginning of context output, + and normal output with --compare. */ + print_header (files, output_style); } /* Call after the end of output of diffs for one file. @@ -317,16 +333,61 @@ Return nonzero if the lines differ. */ bool -lines_differ (char const *s1, char const *s2) +lines_differ (char const *s1, size_t len1, char const *s2, size_t len2) { register char const *t1 = s1; register char const *t2 = s2; - size_t column = 0; + size_t i; + for (i = 0; i < ignore_string_list.count; i++) { + ignore_string_list.regexp[i].begin1 = -1; + ignore_string_list.regexp[i].begin2 = -1; + } + size_t column1 = 0; + size_t column2 = 0; while (1) { + start_loop : + for (i = 0; i < ignore_string_list.count; i++) + { + struct ignore_string *ign = &ignore_string_list.regexp[i]; + if (ign->begin1 == -2 || ign->begin2 == -2) continue; + + if (s1 + ign->begin1 < t1) + { + if ((ign->begin1 = re_search (ign->buf, s1, len1, t1 - s1, len1, ign->regs)) < 0) + ign->begin1 = -2; + else + ign->size1 = ign->regs->end[0] - ign->regs->start[0]; + } + if (s1 + ign->begin1 == t1) + ign->begin1 = -1; + + if (s2 + ign->begin2 < t2) + { + if ((ign->begin2 = re_search (ign->buf, s2, len2, t2 - s2, len2, ign->regs)) <0 ) + ign->begin2 = -2; + else + ign->size2 = ign->regs->end[0] - ign->regs->start[0]; + } + if (s2 + ign->begin2 == t2) + ign->begin2 = -1; + + if (ign->begin1 == -1 && ign->begin2 == -1) + { + if (ign->size1 + ign->size2 == 0) + continue; /* Proceed to the next regexp, if matched strings are both empty. */ + t1 += ign->size1; + t2 += ign->size2; + column1 += ign->size1; + column2 += ign->size2; + goto start_loop; + } + } register unsigned char c1 = *t1++; register unsigned char c2 = *t2++; + char const *tt1 = t1; + char const *tt2 = t2; /* Test for exact char equality first, since it's a common case. */ if (c1 != c2) @@ -399,13 +460,12 @@ if ((c1 == ' ' && c2 == '\t') || (c1 == '\t' && c2 == ' ')) { - size_t column2 = column; for (;; c1 = *t1++) { if (c1 == ' ') - column++; + column1++; else if (c1 == '\t') - column += tabsize - column % tabsize; + column1 += tabsize - column1 % tabsize; else break; } @@ -418,7 +478,7 @@ else break; } - if (column != column2) + if (column1 != column2) return true; } break; @@ -426,6 +486,10 @@ case IGNORE_NO_WHITE_SPACE: break; } + if (ignore_string_list.count&&(t1!=tt1||t2!=tt2)) { + --t1;--t2; + continue; + } /* Lowercase all letters if -i is specified. */ @@ -441,7 +505,8 @@ if (c1 == '\n') return false; - column += c1 == '\t' ? tabsize - column % tabsize : 1; + column1 += c1 == '\t' ? tabsize - column1 % tabsize : 1; + column2 += c2 == '\t' ? tabsize - column2 % tabsize : 1; } return true; @@ -786,3 +851,30 @@ fflush (stderr); } + +/* Return 1 if timestamp of *ST1 and *ST2 is different, + and 0 otherwise. */ + +bool +different_time (const struct stat *st1, const struct stat *st2) +{ + return ( st1->st_mtime != st2->st_mtime +#ifdef ST_MTIM_NSEC + || st1->st_mtim.ST_MTIM_NSEC != st2->st_mtim.ST_MTIM_NSEC +#endif + ) ? 1 : 0; +} + +/* Return 1 if timestamp or size of *ST1 and *ST2 are different, + and 0 otherwise. */ + +bool +different_time_or_size (const struct stat *st1, const struct stat *st2) +{ + return ( st1->st_size != st2->st_size + || st1->st_mtime != st2->st_mtime +#ifdef ST_MTIM_NSEC + || st1->st_mtim.ST_MTIM_NSEC != st2->st_mtim.ST_MTIM_NSEC +#endif + ) ? 1 : 0; +}