diff -u -d -x '*.[oea]*' -x '[.M]*' diffutils-3.0/src/analyze.c diffutils-3.0.KKK/src/analyze.c --- diffutils-3.0/src/analyze.c 2010-04-15 13:58:08.000000000 +0100 +++ diffutils-3.0.KKK/src/analyze.c 2010-09-27 11:59:38.265625000 +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.KKK/src/context.c --- diffutils-3.0/src/context.c 2010-04-15 20:53:08.000000000 +0100 +++ diffutils-3.0.KKK/src/context.c 2010-09-27 11:59:38.265625000 +0100 @@ -21,6 +21,9 @@ #include "diff.h" #include "c-ctype.h" #include +#include +#include +#include #include #include @@ -36,59 +39,120 @@ /* 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 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, inf->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; } } diff -u -d -x '*.[oea]*' -x '[.M]*' diffutils-3.0/src/diff.c diffutils-3.0.KKK/src/diff.c --- diffutils-3.0/src/diff.c 2010-04-15 20:53:08.000000000 +0100 +++ diffutils-3.0.KKK/src/diff.c 2010-09-27 11:59:38.265625000 +0100 @@ -28,9 +28,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -72,10 +74,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; @@ -112,9 +110,11 @@ enum { BINARY_OPTION = CHAR_MAX + 1, + COMPARE_OPTION, FROM_FILE_OPTION, HELP_OPTION, HORIZON_LINES_OPTION, + IF_DIFFERENT_OPTION, IGNORE_FILE_NAME_CASE_OPTION, INHIBIT_HUNK_MERGE_OPTION, LEFT_COLUMN_OPTION, @@ -160,6 +160,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'}, @@ -170,6 +171,7 @@ {"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'}, @@ -522,6 +524,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 +578,10 @@ horizon_lines = MAX (horizon_lines, MIN (numval, LIN_MAX)); break; + case IF_DIFFERENT_OPTION: + if_different = true; + break; + case IGNORE_FILE_NAME_CASE_OPTION: ignore_file_name_case = true; break; @@ -659,6 +703,15 @@ tabsize = 8; if (! width) width = 130; + /* 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, @@ -852,6 +905,15 @@ 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."), + 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."), #if O_BINARY N_("--binary Read and write data in binary mode."), #endif @@ -996,6 +1058,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 +1252,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 +1317,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 +1395,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 +1447,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 +1496,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); + } } diff -u -d -x '*.[oea]*' -x '[.M]*' diffutils-3.0/src/diff.h diffutils-3.0.KKK/src/diff.h --- diffutils-3.0/src/diff.h 2010-04-15 20:53:08.000000000 +0100 +++ diffutils-3.0.KKK/src/diff.h 2010-09-27 11:59:38.281250000 +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; @@ -135,6 +175,10 @@ /* Ignore changes that affect only lines matching this regexp (-I). */ XTERN struct re_pattern_buffer ignore_regexp; +/* If comparing directories, compare their common subdirectories + recursively. */ +XTERN bool recursive; + /* Say only whether files differ, not how (-q). */ XTERN bool brief; @@ -316,10 +360,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 */ @@ -358,9 +403,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 +417,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/util.c diffutils-3.0.KKK/src/util.c --- diffutils-3.0/src/util.c 2010-04-15 20:53:08.000000000 +0100 +++ diffutils-3.0.KKK/src/util.c 2010-09-27 11:59:38.281250000 +0100 @@ -83,6 +83,13 @@ } void +message2 (char const *format_msgid, char const *arg1, char const *arg2, + char const *arg3, char const *arg4) +{ + 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 +158,16 @@ 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; + show_title = cmp->parent != 0 || + ((compare & OBJECTS) && (S_ISDIR (cmp->file[0].stat.st_mode) !=0 + || S_ISDIR (cmp->file[1].stat.st_mode) !=0)); outfile = 0; } @@ -247,26 +256,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. @@ -786,3 +784,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; +}