diff options
| author | jtc <jtc@NetBSD.org> | 1993-07-08 20:13:23 +0000 |
|---|---|---|
| committer | jtc <jtc@NetBSD.org> | 1993-07-08 20:13:23 +0000 |
| commit | d996bc247f59fdf10cf4b6618131b5062e478999 (patch) | |
| tree | 4a74e275e3f737762cfe0e24e497cdb45ea14347 /gnu/usr.bin/diff/diff.c | |
| parent | a18c6474da95632c233f93fa4ecd3de9a5949ad2 (diff) | |
Update to GNU diff 2.3
Diffstat (limited to 'gnu/usr.bin/diff/diff.c')
| -rw-r--r-- | gnu/usr.bin/diff/diff.c | 643 |
1 files changed, 439 insertions, 204 deletions
diff --git a/gnu/usr.bin/diff/diff.c b/gnu/usr.bin/diff/diff.c index 5d4dffdca39..e4eb66f82dd 100644 --- a/gnu/usr.bin/diff/diff.c +++ b/gnu/usr.bin/diff/diff.c @@ -1,11 +1,11 @@ /* GNU DIFF main routine. - Copyright (C) 1988, 1989 Free Software Foundation, Inc. + Copyright (C) 1988, 1989, 1992 Free Software Foundation, Inc. This file is part of GNU DIFF. GNU DIFF is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) +the Free Software Foundation; either version 2, or (at your option) any later version. GNU DIFF is distributed in the hope that it will be useful, @@ -18,18 +18,34 @@ along with GNU DIFF; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* GNU DIFF was written by Mike Haertel, David Hayes, - Richard Stallman and Len Tower. */ + Richard Stallman, Len Tower, and Paul Eggert. */ #define GDIFF_MAIN -#include "regex.h" #include "diff.h" #include "getopt.h" +#include "fnmatch.h" +#ifndef DEFAULT_WIDTH +#define DEFAULT_WIDTH 130 +#endif + +#ifndef GUTTER_WIDTH_MINIMUM +#define GUTTER_WIDTH_MINIMUM 3 +#endif + +int diff_dirs (); +int diff_2_files (); + +static int compare_files (); +static int specify_format (); +static void add_regexp(); +static void specify_style (); +static void usage (); /* Nonzero for -r: if comparing two directories, compare their common subdirectories recursively. */ -int recursive; +static int recursive; /* For debugging: don't do discard_confusing_lines. */ @@ -67,45 +83,134 @@ option_list (optionvec, count) return result; } -/* The numbers 129 and 130 that appear in the fourth element - for the context and unidiff entries are used as a way of - telling the big switch in `main' how to process those options. */ +/* Convert STR to a positive integer, storing the result in *OUT. + If STR is not a valid integer, return -1 (otherwise 0). */ +static int +ck_atoi (str, out) + char *str; + int *out; +{ + char *p; + for (p = str; *p; p++) + if (*p < '0' || *p > '9') + return -1; + + *out = atoi (optarg); + return 0; +} + +/* Keep track of excluded file name patterns. */ + +static const char **exclude; +static int exclude_alloc, exclude_count; + +int +excluded_filename (f) + const char *f; +{ + int i; + for (i = 0; i < exclude_count; i++) + if (fnmatch (exclude[i], f, 0) == 0) + return 1; + return 0; +} + +static void +add_exclude (pattern) + const char *pattern; +{ + if (exclude_alloc <= exclude_count) + exclude = (const char **) + (exclude_alloc == 0 + ? xmalloc ((exclude_alloc = 64) * sizeof (*exclude)) + : xrealloc (exclude, (exclude_alloc *= 2) * sizeof (*exclude))); + + exclude[exclude_count++] = pattern; +} + +static int +add_exclude_file (name) + const char *name; +{ + struct file_data f; + char *p, *q, *lim; + + f.name = optarg; + f.desc = strcmp (optarg, "-") == 0 ? 0 : open (optarg, O_RDONLY, 0); + if (f.desc < 0 || fstat (f.desc, &f.stat) != 0) + return -1; + + sip (&f, 1); + slurp (&f); + + for (p = f.buffer, lim = p + f.buffered_chars; p < lim; p = q) + { + q = memchr (p, '\n', lim - p); + if (!q) + q = lim; + *q++ = 0; + add_exclude (p); + } + + return close (f.desc); +} + +/* The numbers 129- that appear in the fourth element of some entries + tell the big switch in `main' how to process those options. */ static struct option longopts[] = { {"ignore-blank-lines", 0, 0, 'B'}, - {"context", 2, 0, 129}, + {"context", 2, 0, 'C'}, {"ifdef", 1, 0, 'D'}, {"show-function-line", 1, 0, 'F'}, {"speed-large-files", 0, 0, 'H'}, {"ignore-matching-lines", 1, 0, 'I'}, - {"file-label", 1, 0, 'L'}, - {"entire-new-files", 0, 0, 'N'}, - {"new-files", 0, 0, 'N'}, + {"label", 1, 0, 'L'}, + {"file-label", 1, 0, 'L'}, /* An alias, no longer recommended */ + {"new-file", 0, 0, 'N'}, + {"entire-new-file", 0, 0, 'N'}, /* An alias, no longer recommended */ + {"unidirectional-new-file", 0, 0, 'P'}, {"starting-file", 1, 0, 'S'}, {"initial-tab", 0, 0, 'T'}, + {"width", 1, 0, 'W'}, {"text", 0, 0, 'a'}, - {"all-text", 0, 0, 'a'}, - {"ascii", 0, 0, 'a'}, + {"ascii", 0, 0, 'a'}, /* An alias, no longer recommended */ {"ignore-space-change", 0, 0, 'b'}, {"minimal", 0, 0, 'd'}, {"ed", 0, 0, 'e'}, - {"reversed-ed", 0, 0, 'f'}, + {"forward-ed", 0, 0, 'f'}, {"ignore-case", 0, 0, 'i'}, - {"print", 0, 0, 'l'}, + {"paginate", 0, 0, 'l'}, + {"print", 0, 0, 'l'}, /* An alias, no longer recommended */ {"rcs", 0, 0, 'n'}, {"show-c-function", 0, 0, 'p'}, - {"binary", 0, 0, 'q'}, + {"binary", 0, 0, 'q'}, /* An alias, no longer recommended */ {"brief", 0, 0, 'q'}, {"recursive", 0, 0, 'r'}, {"report-identical-files", 0, 0, 's'}, {"expand-tabs", 0, 0, 't'}, - {"ignore-all-space", 0, 0, 'w'}, - {"unified", 2, 0, 130}, {"version", 0, 0, 'v'}, + {"ignore-all-space", 0, 0, 'w'}, + {"exclude", 1, 0, 'x'}, + {"exclude-from", 1, 0, 'X'}, + {"side-by-side", 0, 0, 'y'}, + {"unified", 2, 0, 'U'}, + {"left-column", 0, 0, 129}, + {"suppress-common-lines", 0, 0, 130}, + {"sdiff-merge-assist", 0, 0, 131}, + {"old-line-format", 1, 0, 132}, + {"new-line-format", 1, 0, 133}, + {"unchanged-line-format", 1, 0, 134}, + {"old-group-format", 1, 0, 135}, + {"new-group-format", 1, 0, 136}, + {"unchanged-group-format", 1, 0, 137}, + {"changed-group-format", 1, 0, 138}, + {"horizon-lines", 1, 0, 139}, {0, 0, 0, 0} }; +int main (argc, argv) int argc; char *argv[]; @@ -113,8 +218,8 @@ main (argc, argv) int val; int c; int prev = -1; - int longind; extern char *version_string; + int width = DEFAULT_WIDTH; program = argv[0]; @@ -126,10 +231,11 @@ main (argc, argv) length_varies = FALSE; ignore_case_flag = FALSE; ignore_blank_lines_flag = FALSE; - ignore_regexp = 0; - function_regexp = 0; + ignore_regexp_list = NULL; + function_regexp_list = NULL; print_file_same_flag = FALSE; entire_new_file_flag = FALSE; + unidirectional_new_file_flag = FALSE; no_details_flag = FALSE; context = -1; line_end_char = '\n'; @@ -137,7 +243,6 @@ main (argc, argv) tab_expand_flag = FALSE; recursive = FALSE; paginate_flag = FALSE; - ifdef_string = NULL; heuristic = FALSE; dir_start_file = NULL; msg_chain = NULL; @@ -147,11 +252,9 @@ main (argc, argv) /* Decode the options. */ while ((c = getopt_long (argc, argv, - "0123456789abBcC:dD:efF:hHiI:lL:nNpqrsS:tTuvw", - longopts, &longind)) != EOF) + "0123456789abBcC:dD:efF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:y", + longopts, (int *)0)) != EOF) { - if (c == 0) /* Long option. */ - c = longopts[longind].val; switch (c) { /* All digits combine in decimal to specify the context-size. */ @@ -192,26 +295,21 @@ main (argc, argv) ignore_blank_lines_flag = 1; break; - case 'C': - case 129: /* +context[=lines] */ - case 130: /* +unified[=lines] */ + case 'C': /* +context[=lines] */ + case 'U': /* +unified[=lines] */ if (optarg) { if (context >= 0) fatal ("context length specified twice"); - { - char *p; - for (p = optarg; *p; p++) - if (*p < '0' || *p > '9') - fatal ("invalid context length argument"); - } - context = atoi (optarg); + + if (ck_atoi (optarg, &context)) + fatal ("invalid context length argument"); } /* Falls through. */ case 'c': /* Make context-style output. */ - specify_style (c == 130 ? OUTPUT_UNIFIED : OUTPUT_CONTEXT); + specify_style (c == 'U' ? OUTPUT_UNIFIED : OUTPUT_CONTEXT); break; case 'd': @@ -223,7 +321,25 @@ main (argc, argv) case 'D': /* Make merged #ifdef output. */ specify_style (OUTPUT_IFDEF); - ifdef_string = optarg; + { + int i, err = 0; + static const char C_ifdef_group_formats[] = + "#ifndef %s\n%%<#endif /* not %s */\n%c#ifdef %s\n%%>#endif /* %s */\n%c%%=%c#ifndef %s\n%%<#else /* %s */\n%%>#endif /* %s */\n"; + char *b = xmalloc (sizeof (C_ifdef_group_formats) + + 7 * strlen(optarg) - 14 /* 7*"%s" */ + - 8 /* 5*"%%" + 3*"%c" */); + sprintf (b, C_ifdef_group_formats, + optarg, optarg, 0, + optarg, optarg, 0, 0, + optarg, optarg, optarg); + for (i = 0; i < 4; i++) + { + err |= specify_format (&group_format[i], b); + b += strlen (b) + 1; + } + if (err) + error ("conflicting #ifdef formats", 0, 0); + } break; case 'e': @@ -241,7 +357,7 @@ main (argc, argv) /* Show, for each set of changes, the previous line that matches the specified regexp. Currently affects only context-style output. */ - function_regexp = optarg; + add_regexp (&function_regexp_list, optarg); break; case 'h': @@ -265,7 +381,7 @@ main (argc, argv) case 'I': /* Ignore changes affecting only lines that match the specified regexp. */ - ignore_regexp = optarg; + add_regexp (&ignore_regexp_list, optarg); break; case 'l': @@ -282,7 +398,7 @@ main (argc, argv) else fatal ("too many file label options"); break; - + case 'n': /* Output RCS-style diffs, like `-f' except that each command specifies the number of lines affected. */ @@ -298,7 +414,14 @@ main (argc, argv) case 'p': /* Make context-style output and show name of last C function. */ specify_style (OUTPUT_CONTEXT); - function_regexp = "^[_a-zA-Z]"; + add_regexp (&function_regexp_list, "^[_a-zA-Z$]"); + break; + + case 'P': + /* When comparing directories, if a file appears only in + the second directory of the two, + treat it as present but empty in the other. */ + unidirectional_new_file_flag = 1; break; case 'q': @@ -335,21 +458,85 @@ main (argc, argv) tab_align_flag = 1; break; - case 'v': - printf ("GNU diff version %s\n", version_string); - break; - case 'u': /* Output the context diff in unidiff format. */ specify_style (OUTPUT_UNIFIED); break; + case 'v': + fprintf (stderr, "GNU diff version %s\n", version_string); + break; + case 'w': /* Ignore horizontal whitespace when comparing lines. */ ignore_all_space_flag = 1; length_varies = 1; break; + case 'x': + add_exclude (optarg); + break; + + case 'X': + if (add_exclude_file (optarg) != 0) + pfatal_with_name (optarg); + break; + + case 'y': + /* Use side-by-side (sdiff-style) columnar output. */ + specify_style (OUTPUT_SDIFF); + break; + + case 'W': + /* Set the line width for OUTPUT_SDIFF. */ + if (ck_atoi (optarg, &width) || width <= 0) + fatal ("column width must be a positive integer"); + break; + + case 129: + sdiff_left_only = 1; + break; + + case 130: + sdiff_skip_common_lines = 1; + break; + + case 131: + /* sdiff-style columns output. */ + specify_style (OUTPUT_SDIFF); + sdiff_help_sdiff = 1; + break; + + case 132: + case 133: + case 134: + specify_style (OUTPUT_IFDEF); + { + const char **form = &line_format[c - 132]; + if (*form && strcmp (*form, optarg) != 0) + error ("conflicting line format", 0, 0); + *form = optarg; + } + break; + + case 135: + case 136: + case 137: + case 138: + specify_style (OUTPUT_IFDEF); + { + const char **form = &group_format[c - 135]; + if (*form && strcmp (*form, optarg) != 0) + error ("conflicting group format", 0, 0); + *form = optarg; + } + break; + + case 139: + if (ck_atoi (optarg, &horizon_lines) || horizon_lines < 0) + fatal ("horizon must be a nonnegative integer"); + break; + default: usage (); } @@ -359,37 +546,59 @@ main (argc, argv) if (optind != argc - 2) usage (); - if (ignore_regexp) - { - char *val; - bzero (&ignore_regexp_compiled, sizeof ignore_regexp_compiled); - val = re_compile_pattern (ignore_regexp, strlen (ignore_regexp), - &ignore_regexp_compiled); - if (val != 0) - error ("%s: %s", ignore_regexp, val); - ignore_regexp_compiled.fastmap = (char *) xmalloc (256); - } - if (function_regexp) - { - char *val; - bzero (&function_regexp_compiled, sizeof function_regexp_compiled); - val = re_compile_pattern (function_regexp, strlen (function_regexp), - &function_regexp_compiled); - if (val != 0) - error ("%s: %s", function_regexp, val); - function_regexp_compiled.fastmap = (char *) xmalloc (256); - } + { + /* + * We maximize first the half line width, and then the gutter width, + * according to the following constraints: + * 1. Two half lines plus a gutter must fit in a line. + * 2. If the half line width is nonzero: + * a. The gutter width is at least GUTTER_WIDTH_MINIMUM. + * b. If tabs are not expanded to spaces, + * a half line plus a gutter is an integral number of tabs, + * so that tabs in the right column line up. + */ + int t = tab_expand_flag ? 1 : TAB_WIDTH; + int off = (width + t + GUTTER_WIDTH_MINIMUM) / (2*t) * t; + sdiff_half_width = max (0, min (off - GUTTER_WIDTH_MINIMUM, width - off)), + sdiff_column2_offset = sdiff_half_width ? off : width; + } if (output_style != OUTPUT_CONTEXT && output_style != OUTPUT_UNIFIED) context = 0; else if (context == -1) /* Default amount of context for -c. */ context = 3; + + if (output_style == OUTPUT_IFDEF) + { + int i; + for (i = 0; i < sizeof (line_format) / sizeof (*line_format); i++) + if (!line_format[i]) + line_format[i] = "%l\n"; + if (!group_format[OLD]) + group_format[OLD] + = group_format[UNCHANGED] ? group_format[UNCHANGED] : "%<"; + if (!group_format[NEW]) + group_format[NEW] + = group_format[UNCHANGED] ? group_format[UNCHANGED] : "%>"; + if (!group_format[UNCHANGED]) + group_format[UNCHANGED] = "%="; + if (!group_format[CHANGED]) + group_format[CHANGED] = concat (group_format[OLD], + group_format[NEW], ""); + } + + no_diff_means_no_output = + (output_style == OUTPUT_IFDEF ? + (!*group_format[UNCHANGED] + || (strcmp (group_format[UNCHANGED], "%=") == 0 + && !*line_format[UNCHANGED])) + : output_style == OUTPUT_SDIFF ? sdiff_skip_common_lines : 1); switch_string = option_list (argv + 1, optind - 1); - val = compare_files (0, argv[optind], 0, argv[optind + 1], 0); + val = compare_files (NULL, argv[optind], NULL, argv[optind + 1], 0); /* Print any messages that were saved up for last. */ print_message_queue (); @@ -397,32 +606,76 @@ main (argc, argv) if (ferror (stdout) || fclose (stdout) != 0) fatal ("write error"); exit (val); + return val; } +/* Add the compiled form of regexp PATTERN to REGLIST. */ + +static void +add_regexp (reglist, pattern) + struct regexp_list **reglist; + char *pattern; +{ + struct regexp_list *r; + const char *m; + + r = (struct regexp_list *) xmalloc (sizeof (*r)); + bzero (r, sizeof (*r)); + r->buf.fastmap = (char *) xmalloc (256); + m = re_compile_pattern (pattern, strlen (pattern), &r->buf); + if (m != 0) + error ("%s: %s", pattern, m); + + /* Add to the start of the list, since it's easier than the end. */ + r->next = *reglist; + *reglist = r; +} + +static void usage () { + fprintf (stderr, "Usage: %s [options] from-file to-file\n", program); + fprintf (stderr, "Options:\n\ + [-abBcdefhHilnNpPqrstTuvwy] [-C lines] [-D name] [-F regexp]\n\ + [-I regexp] [-L from-label [-L to-label]] [-S starting-file] [-U lines]\n\ + [-W columns] [-x pattern] [-X pattern-file] [--exclude=pattern]\n\ + [--exclude-from=pattern-file] [--ignore-blank-lines] [--context[=lines]]\n\ + [--ifdef=name] [--show-function-line=regexp] [--speed-large-files]\n\ + [--label=from-label [--label=to-label]] [--new-file]\n"); fprintf (stderr, "\ -Usage: diff [-#] [-abBcdefhHilnNprstTuvw] [-C lines] [-F regexp] [-I regexp]\n\ - [-L label [-L label]] [-S file] [-D symbol] [+ignore-blank-lines]\n\ - [+context[=lines]] [+unified[=lines]] [+ifdef=symbol]\n\ - [+show-function-line=regexp]\n"); + [--ignore-matching-lines=regexp] [--unidirectional-new-file]\n\ + [--starting-file=starting-file] [--initial-tab] [--width=columns]\n\ + [--text] [--ignore-space-change] [--minimal] [--ed] [--forward-ed]\n\ + [--ignore-case] [--paginate] [--rcs] [--show-c-function] [--brief]\n\ + [--recursive] [--report-identical-files] [--expand-tabs] [--version]\n"); fprintf (stderr, "\ - [+speed-large-files] [+ignore-matching-lines=regexp] [+new-file]\n\ - [+initial-tab] [+starting-file=file] [+text] [+all-text] [+ascii]\n\ - [+minimal] [+ignore-space-change] [+ed] [+reversed-ed] [+ignore-case]\n"); - fprintf (stderr, "\ - [+print] [+rcs] [+show-c-function] [+binary] [+brief] [+recursive]\n\ - [+report-identical-files] [+expand-tabs] [+ignore-all-space]\n\ - [+file-label=label [+file-label=label]] [+version] path1 path2\n"); + [--ignore-all-space] [--side-by-side] [--unified[=lines]]\n\ + [--left-column] [--suppress-common-lines] [--sdiff-merge-assist]\n\ + [--old-line-format=format] [--new-line-format=format]\n\ + [--unchanged-line-format=format]\n\ + [--old-group-format=format] [--new-group-format=format]\n\ + [--unchanged-group-format=format] [--changed-group-format=format]\n\ + [--horizon-lines=lines]\n"); exit (2); } +static int +specify_format (var, value) + const char **var; + const char *value; +{ + int err = *var ? strcmp (*var, value) : 0; + *var = value; + return err; +} + +static void specify_style (style) enum output_style style; { if (output_style != OUTPUT_NORMAL && output_style != style) - error ("conflicting specifications of output style"); + error ("conflicting specifications of output style", 0, 0); output_style = style; } @@ -431,27 +684,28 @@ specify_style (style) (if DIR0 is 0, then the name is just NAME0, etc.) This is self-contained; it opens the files and closes them. - Value is 0 if files are identical, 1 if different, + Value is 0 if files are the same, 1 if different, 2 if there is a problem opening them. */ -int +static int compare_files (dir0, name0, dir1, name1, depth) char *dir0, *dir1; char *name0, *name1; int depth; { - static char Standard_Input[] = "Standard Input"; struct file_data inf[2]; register int i; int val; + int same_files; int errorcount = 0; - int stat_result[2]; /* 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 (! entire_new_file_flag && (name0 == 0 || name1 == 0)) + if (! ((name0 != 0 && name1 != 0) + || (unidirectional_new_file_flag && name1 != 0) + || entire_new_file_flag)) { char *name = name0 == 0 ? name1 : name0; char *dir = name0 == 0 ? dir1 : dir0; @@ -476,88 +730,84 @@ compare_files (dir0, name0, dir1, name1, depth) inf[0].name = dir0 == 0 ? name0 : concat (dir0, "/", name0); inf[1].name = dir1 == 0 ? name1 : concat (dir1, "/", name1); - /* Stat the files. Record whether they are directories. - Record in stat_result whether stat fails. */ + /* Stat the files. Record whether they are directories. */ for (i = 0; i <= 1; i++) { - bzero (&inf[i].stat, sizeof(struct stat)); + bzero (&inf[i].stat, sizeof (struct stat)); inf[i].dir_p = 0; - stat_result[i] = 0; if (inf[i].desc != -1) { - char *filename = inf[i].name; - - stat_result[i] = - strcmp (filename, "-") - ? stat (filename, &inf[i].stat) - : fstat (0, &inf[i].stat); - - if (stat_result[i] < 0) + int stat_result; + + if (strcmp (inf[i].name, "-") == 0) { - perror_with_name (filename); + inf[i].desc = 0; + inf[i].name = "Standard Input"; + stat_result = fstat (0, &inf[i].stat); + } + else + stat_result = stat (inf[i].name, &inf[i].stat); + + if (stat_result != 0) + { + perror_with_name (inf[i].name); errorcount = 1; } else - inf[i].dir_p = - S_IFDIR == (inf[i].stat.st_mode & S_IFMT) - && strcmp (filename, "-"); + inf[i].dir_p = S_ISDIR (inf[i].stat.st_mode) && inf[i].desc != 0; } } - /* See if the two named files are actually the same physical file. - If so, we know they are identical without actually reading them. */ - - if (output_style != OUTPUT_IFDEF - && inf[0].stat.st_ino == inf[1].stat.st_ino - && inf[0].stat.st_dev == inf[1].stat.st_dev - && stat_result[0] == 0 - && stat_result[1] == 0) - { - val = 0; - goto done; - } - if (name0 == 0) inf[0].dir_p = inf[1].dir_p; if (name1 == 0) inf[1].dir_p = inf[0].dir_p; - /* Open the files and record their descriptors. */ - - for (i = 0; i <= 1; i++) + if (errorcount == 0 && depth == 0 && inf[0].dir_p != inf[1].dir_p) { - if (inf[i].desc == -1) - ; - else if (!strcmp (inf[i].name, "-")) - { - inf[i].desc = 0; - inf[i].name = Standard_Input; - } - /* Don't bother opening if stat already failed. */ - else if (stat_result[i] == 0 && ! inf[i].dir_p) - { - char *filename = inf[i].name; + /* 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. */ - inf[i].desc = open (filename, O_RDONLY, 0); - if (0 > inf[i].desc) - { - perror_with_name (filename); - errorcount = 1; - } + int fnm_arg = inf[0].dir_p; + int dir_arg = 1 - fnm_arg; + char *p = rindex (inf[fnm_arg].name, '/'); + char *filename = inf[dir_arg].name + = concat (inf[dir_arg].name, "/", (p ? p+1 : inf[fnm_arg].name)); + + if (inf[fnm_arg].desc == 0) + fatal ("can't compare - to a directory"); + + if (stat (filename, &inf[dir_arg].stat) != 0) + { + perror_with_name (filename); + errorcount = 1; } + else + inf[dir_arg].dir_p = S_ISDIR (inf[dir_arg].stat.st_mode); } if (errorcount) { - /* If either file should exist but fails to be opened, return 2. */ + /* If either file should exist but does not, return 2. */ val = 2; } - else if (inf[0].dir_p && inf[1].dir_p) + else if ((same_files = inf[0].stat.st_ino == inf[1].stat.st_ino + && inf[0].stat.st_dev == inf[1].stat.st_dev + && inf[0].desc != -1 + && inf[1].desc != -1) + && no_diff_means_no_output) + { + /* The two named files are actually the same physical file. + We know they are identical without actually reading them. */ + + val = 0; + } + else if (inf[0].dir_p & inf[1].dir_p) { if (output_style == OUTPUT_IFDEF) fatal ("-D option not supported with directories"); @@ -574,66 +824,21 @@ compare_files (dir0, name0, dir1, name1, depth) } else { - val = diff_dirs (inf[0].name, inf[1].name, - compare_files, depth, 0, 0); - } - - } - else if (depth == 0 && (inf[0].dir_p || inf[1].dir_p)) - { - - /* If only one is a directory, and it was specified in the command line, - use the file in that dir whose basename matches the other file. */ - - int dir_arg = (inf[0].dir_p ? 0 : 1); - int fnm_arg = (inf[0].dir_p ? 1 : 0); - char *p = rindex (inf[fnm_arg].name, '/'); - char *filename = concat (inf[dir_arg].name, "/", - (p ? p+1 : inf[fnm_arg].name)); - - if (inf[fnm_arg].name == Standard_Input) - fatal ("can't compare - to a directory"); - - inf[dir_arg].desc = open (filename, O_RDONLY, 0); - - if (0 > inf[dir_arg].desc) - { - perror_with_name (filename); - val = 2; - } - else - { - /* JF: patch from the net to check and make sure we can really free - this. If it's from argv[], freeing it is a *really* bad idea */ - if (0 != (dir_arg ? dir1 : dir0)) - free (inf[dir_arg].name); - inf[dir_arg].name = filename; - if (fstat (inf[dir_arg].desc, &inf[dir_arg].stat) < 0) - pfatal_with_name (inf[dir_arg].name); - - inf[dir_arg].dir_p - = (S_IFDIR == (inf[dir_arg].stat.st_mode & S_IFMT)); - if (inf[dir_arg].dir_p) - { - error ("%s is a directory but %s is not", - inf[dir_arg].name, inf[fnm_arg].name); - val = 1; - } - else - val = diff_2_files (inf, depth); + val = diff_dirs (inf, compare_files, depth); } } - else if (depth > 0 && (inf[0].dir_p || inf[1].dir_p)) + else if (inf[0].dir_p | inf[1].dir_p) { /* Perhaps we have a subdirectory that exists only in one directory. If so, just print a message to that effect. */ if (inf[0].desc == -1 || inf[1].desc == -1) { - if (entire_new_file_flag && recursive) - val = diff_dirs (inf[0].name, inf[1].name, compare_files, depth, - inf[0].desc == -1, inf[1].desc == -1); + if (recursive + && (entire_new_file_flag + || (unidirectional_new_file_flag && inf[0].desc == -1))) + val = diff_dirs (inf, compare_files, depth); else { char *dir = (inf[0].desc == -1) ? dir1 : dir0; @@ -646,34 +851,64 @@ compare_files (dir0, name0, dir1, name1, depth) /* We have a subdirectory in one directory and a file in the other. */ - if (inf[0].dir_p) - message ("%s is a directory but %s is not\n", - inf[0].name, inf[1].name); - else - message ("%s is a directory but %s is not\n", - inf[1].name, inf[0].name); + message ("%s is a directory but %s is not\n", + inf[1 - inf[0].dir_p].name, inf[inf[0].dir_p].name); + /* This is a difference. */ val = 1; } } + else if (no_details_flag + && inf[0].stat.st_size != inf[1].stat.st_size + && (inf[0].desc == -1 || S_ISREG (inf[0].stat.st_mode)) + && (inf[1].desc == -1 || S_ISREG (inf[1].stat.st_mode))) + { + message ("Files %s and %s differ\n", inf[0].name, inf[1].name); + val = 1; + } else { - - /* Both exist and both are ordinary files. */ - - val = diff_2_files (inf, depth); - + /* Both exist and neither is a directory. */ + + /* Open the files and record their descriptors. */ + + if (inf[0].desc == -2) + if ((inf[0].desc = open (inf[0].name, O_RDONLY, 0)) < 0) + { + perror_with_name (inf[0].name); + errorcount = 1; + } + if (inf[1].desc == -2) + if (same_files) + inf[1].desc = inf[0].desc; + else if ((inf[1].desc = open (inf[1].name, O_RDONLY, 0)) < 0) + { + perror_with_name (inf[1].name); + errorcount = 1; + } + + /* Compare the files, if no error was found. */ + + val = errorcount ? 2 : diff_2_files (inf, depth); + + /* Close the file descriptors. */ + + if (inf[0].desc >= 0 && close (inf[0].desc) != 0) + { + perror_with_name (inf[0].name); + val = 2; + } + if (inf[1].desc >= 0 && inf[0].desc != inf[1].desc + && close (inf[1].desc) != 0) + { + perror_with_name (inf[1].name); + val = 2; + } } /* Now the comparison has been done, if no error prevented it, and VAL is the value this function will return. */ - if (inf[0].desc >= 0) - close (inf[0].desc); - if (inf[1].desc >= 0) - close (inf[1].desc); - - done: if (val == 0 && !inf[0].dir_p) { if (print_file_same_flag) |
