summaryrefslogtreecommitdiffstats
path: root/gnu/usr.bin/diff/diff.c
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>1995-10-28 18:51:33 +0000
committerpeter <peter@FreeBSD.org>1995-10-28 18:51:33 +0000
commit27378b5694b4941bb63a62097f7ee523da0a35bb (patch)
tree9372a0168d8ef1138d4ec4ef2051198caed461de /gnu/usr.bin/diff/diff.c
parent31fbfe9bebb8e48eaf39efc88875c743cf238ced (diff)
downloadFreeBSD-src-27378b5694b4941bb63a62097f7ee523da0a35bb.zip
FreeBSD-src-27378b5694b4941bb63a62097f7ee523da0a35bb.tar.gz
Import GNU diffutils 2.7
Note, this is going to be messy.. 2.3 was vendor-branch imported, while 2.6 was done as a delta. Sigh. I'm importing this on a vendor branch so that it will be easier to deal with next time.. (cvs-1.6 wants rcs-5.7, and rcs-5.7 suggests diffutils-2.7)
Diffstat (limited to 'gnu/usr.bin/diff/diff.c')
-rw-r--r--gnu/usr.bin/diff/diff.c507
1 files changed, 343 insertions, 164 deletions
diff --git a/gnu/usr.bin/diff/diff.c b/gnu/usr.bin/diff/diff.c
index e4eb66f..ab1549b 100644
--- a/gnu/usr.bin/diff/diff.c
+++ b/gnu/usr.bin/diff/diff.c
@@ -1,5 +1,5 @@
/* GNU DIFF main routine.
- Copyright (C) 1988, 1989, 1992 Free Software Foundation, Inc.
+ Copyright (C) 1988, 1989, 1992, 1993, 1994 Free Software Foundation, Inc.
This file is part of GNU DIFF.
@@ -22,6 +22,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#define GDIFF_MAIN
#include "diff.h"
+#include <signal.h>
#include "getopt.h"
#include "fnmatch.h"
@@ -33,14 +34,18 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#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 ();
+static char const *filetype PARAMS((struct stat const *));
+static char *option_list PARAMS((char **, int));
+static int add_exclude_file PARAMS((char const *));
+static int ck_atoi PARAMS((char const *, int *));
+static int compare_files PARAMS((char const *, char const *, char const *, char const *, int));
+static int specify_format PARAMS((char **, char *));
+static void add_exclude PARAMS((char const *));
+static void add_regexp PARAMS((struct regexp_list **, char const *));
+static void specify_style PARAMS((enum output_style));
+static void try_help PARAMS((char const *));
+static void check_stdout PARAMS((void));
+static void usage PARAMS((void));
/* Nonzero for -r: if comparing two directories,
compare their common subdirectories recursively. */
@@ -51,6 +56,11 @@ static int recursive;
int no_discards;
+#if HAVE_SETMODE
+/* I/O mode: nonzero only if using binary input/output. */
+static int binary_I_O;
+#endif
+
/* Return a string containing the command options with which diff was invoked.
Spaces appear between what were separate ARGV-elements.
There is a space at the beginning but none at the end.
@@ -65,13 +75,13 @@ option_list (optionvec, count)
int count;
{
int i;
- int length = 0;
+ size_t length = 0;
char *result;
for (i = 0; i < count; i++)
length += strlen (optionvec[i]) + 1;
- result = (char *) xmalloc (length + 1);
+ result = xmalloc (length + 1);
result[0] = 0;
for (i = 0; i < count; i++)
@@ -83,14 +93,14 @@ option_list (optionvec, count)
return result;
}
-/* Convert STR to a positive integer, storing the result in *OUT.
+/* 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;
+ char const *str;
int *out;
{
- char *p;
+ char const *p;
for (p = str; *p; p++)
if (*p < '0' || *p > '9')
return -1;
@@ -101,12 +111,12 @@ ck_atoi (str, out)
/* Keep track of excluded file name patterns. */
-static const char **exclude;
+static char const **exclude;
static int exclude_alloc, exclude_count;
int
excluded_filename (f)
- const char *f;
+ char const *f;
{
int i;
for (i = 0; i < exclude_count; i++)
@@ -117,10 +127,10 @@ excluded_filename (f)
static void
add_exclude (pattern)
- const char *pattern;
+ char const *pattern;
{
if (exclude_alloc <= exclude_count)
- exclude = (const char **)
+ exclude = (char const **)
(exclude_alloc == 0
? xmalloc ((exclude_alloc = 64) * sizeof (*exclude))
: xrealloc (exclude, (exclude_alloc *= 2) * sizeof (*exclude)));
@@ -130,13 +140,15 @@ add_exclude (pattern)
static int
add_exclude_file (name)
- const char *name;
+ char const *name;
{
struct file_data f;
char *p, *q, *lim;
f.name = optarg;
- f.desc = strcmp (optarg, "-") == 0 ? 0 : open (optarg, O_RDONLY, 0);
+ f.desc = (strcmp (optarg, "-") == 0
+ ? STDIN_FILENO
+ : open (optarg, O_RDONLY, 0));
if (f.desc < 0 || fstat (f.desc, &f.stat) != 0)
return -1;
@@ -145,7 +157,7 @@ add_exclude_file (name)
for (p = f.buffer, lim = p + f.buffered_chars; p < lim; p = q)
{
- q = memchr (p, '\n', lim - p);
+ q = (char *) memchr (p, '\n', lim - p);
if (!q)
q = lim;
*q++ = 0;
@@ -158,7 +170,7 @@ add_exclude_file (name)
/* 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[] =
+static struct option const longopts[] =
{
{"ignore-blank-lines", 0, 0, 'B'},
{"context", 2, 0, 'C'},
@@ -185,7 +197,6 @@ static struct option longopts[] =
{"print", 0, 0, 'l'}, /* An alias, no longer recommended */
{"rcs", 0, 0, 'n'},
{"show-c-function", 0, 0, 'p'},
- {"binary", 0, 0, 'q'}, /* An alias, no longer recommended */
{"brief", 0, 0, 'q'},
{"recursive", 0, 0, 'r'},
{"report-identical-files", 0, 0, 's'},
@@ -202,11 +213,14 @@ static struct option longopts[] =
{"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},
+ {"line-format", 1, 0, 135},
+ {"old-group-format", 1, 0, 136},
+ {"new-group-format", 1, 0, 137},
+ {"unchanged-group-format", 1, 0, 138},
+ {"changed-group-format", 1, 0, 139},
+ {"horizon-lines", 1, 0, 140},
+ {"help", 0, 0, 141},
+ {"binary", 0, 0, 142},
{0, 0, 0, 0}
};
@@ -218,42 +232,20 @@ main (argc, argv)
int val;
int c;
int prev = -1;
- extern char *version_string;
int width = DEFAULT_WIDTH;
+ int show_c_function = 0;
- program = argv[0];
-
- /* Do our initializations. */
+ /* Do our initializations. */
+ initialize_main (&argc, &argv);
+ program_name = argv[0];
output_style = OUTPUT_NORMAL;
- always_text_flag = FALSE;
- ignore_space_change_flag = FALSE;
- ignore_all_space_flag = FALSE;
- length_varies = FALSE;
- ignore_case_flag = FALSE;
- ignore_blank_lines_flag = FALSE;
- 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';
- tab_align_flag = FALSE;
- tab_expand_flag = FALSE;
- recursive = FALSE;
- paginate_flag = FALSE;
- heuristic = FALSE;
- dir_start_file = NULL;
- msg_chain = NULL;
- msg_chain_end = NULL;
- no_discards = 0;
/* Decode the options. */
while ((c = getopt_long (argc, argv,
"0123456789abBcC:dD:efF:hHiI:lL:nNpPqrsS:tTuU:vwW:x:X:y",
- longopts, (int *)0)) != EOF)
+ longopts, 0)) != EOF)
{
switch (c)
{
@@ -285,14 +277,16 @@ main (argc, argv)
break;
case 'b':
- /* Ignore changes in amount of whitespace. */
+ /* Ignore changes in amount of white space. */
ignore_space_change_flag = 1;
- length_varies = 1;
+ ignore_some_changes = 1;
+ ignore_some_line_changes = 1;
break;
case 'B':
/* Ignore changes affecting only blank lines. */
ignore_blank_lines_flag = 1;
+ ignore_some_changes = 1;
break;
case 'C': /* +context[=lines] */
@@ -323,7 +317,7 @@ main (argc, argv)
specify_style (OUTPUT_IFDEF);
{
int i, err = 0;
- static const char C_ifdef_group_formats[] =
+ static char const 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" */
@@ -376,17 +370,28 @@ main (argc, argv)
case 'i':
/* Ignore changes in case. */
ignore_case_flag = 1;
+ ignore_some_changes = 1;
+ ignore_some_line_changes = 1;
break;
case 'I':
/* Ignore changes affecting only lines that match the
specified regexp. */
add_regexp (&ignore_regexp_list, optarg);
+ ignore_some_changes = 1;
break;
case 'l':
/* Pass the output through `pr' to paginate it. */
paginate_flag = 1;
+#if !defined(SIGCHLD) && defined(SIGCLD)
+#define SIGCHLD SIGCLD
+#endif
+#ifdef SIGCHLD
+ /* Pagination requires forking and waiting, and
+ System V fork+wait does not work if SIGCHLD is ignored. */
+ signal (SIGCHLD, SIG_DFL);
+#endif
break;
case 'L':
@@ -398,7 +403,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. */
@@ -413,7 +418,7 @@ main (argc, argv)
case 'p':
/* Make context-style output and show name of last C function. */
- specify_style (OUTPUT_CONTEXT);
+ show_c_function = 1;
add_regexp (&function_regexp_list, "^[_a-zA-Z$]");
break;
@@ -429,7 +434,7 @@ main (argc, argv)
break;
case 'r':
- /* When comparing directories,
+ /* When comparing directories,
recursively compare any subdirectories found. */
recursive = 1;
break;
@@ -464,13 +469,14 @@ main (argc, argv)
break;
case 'v':
- fprintf (stderr, "GNU diff version %s\n", version_string);
- break;
+ printf ("diff - GNU diffutils version %s\n", version_string);
+ exit (0);
case 'w':
- /* Ignore horizontal whitespace when comparing lines. */
+ /* Ignore horizontal white space when comparing lines. */
ignore_all_space_flag = 1;
- length_varies = 1;
+ ignore_some_changes = 1;
+ ignore_some_line_changes = 1;
break;
case 'x':
@@ -492,15 +498,15 @@ main (argc, argv)
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);
@@ -511,40 +517,57 @@ main (argc, argv)
case 133:
case 134:
specify_style (OUTPUT_IFDEF);
+ if (specify_format (&line_format[c - 132], optarg) != 0)
+ error ("conflicting line format", 0, 0);
+ break;
+
+ case 135:
+ specify_style (OUTPUT_IFDEF);
{
- const char **form = &line_format[c - 132];
- if (*form && strcmp (*form, optarg) != 0)
+ int i, err = 0;
+ for (i = 0; i < sizeof (line_format) / sizeof (*line_format); i++)
+ err |= specify_format (&line_format[i], optarg);
+ if (err)
error ("conflicting line format", 0, 0);
- *form = optarg;
}
break;
- case 135:
case 136:
case 137:
case 138:
+ case 139:
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;
- }
+ if (specify_format (&group_format[c - 136], optarg) != 0)
+ error ("conflicting group format", 0, 0);
break;
- case 139:
+ case 140:
if (ck_atoi (optarg, &horizon_lines) || horizon_lines < 0)
fatal ("horizon must be a nonnegative integer");
break;
- default:
+ case 141:
usage ();
+ check_stdout ();
+ exit (0);
+
+ case 142:
+ /* Use binary I/O when reading and writing data.
+ On Posix hosts, this has no effect. */
+#if HAVE_SETMODE
+ binary_I_O = 1;
+ setmode (STDOUT_FILENO, O_BINARY);
+#endif
+ break;
+
+ default:
+ try_help (0);
}
prev = c;
}
- if (optind != argc - 2)
- usage ();
+ if (argc - optind != 2)
+ try_help (argc - optind < 2 ? "missing operand" : "extra operand");
{
@@ -564,14 +587,21 @@ main (argc, argv)
sdiff_column2_offset = sdiff_half_width ? off : width;
}
+ if (show_c_function && output_style != OUTPUT_UNIFIED)
+ specify_style (OUTPUT_CONTEXT);
+
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)
{
+ /* Format arrays are char *, not char const *,
+ because integer formats are temporarily modified.
+ But it is safe to assign a constant like "%=" to a format array,
+ since "%=" does not format any integers. */
int i;
for (i = 0; i < sizeof (line_format) / sizeof (*line_format); i++)
if (!line_format[i])
@@ -598,13 +628,12 @@ main (argc, argv)
switch_string = option_list (argv + 1, optind - 1);
- val = compare_files (NULL, argv[optind], NULL, argv[optind + 1], 0);
+ val = compare_files (0, argv[optind], 0, argv[optind + 1], 0);
/* Print any messages that were saved up for last. */
print_message_queue ();
- if (ferror (stdout) || fclose (stdout) != 0)
- fatal ("write error");
+ check_stdout ();
exit (val);
return val;
}
@@ -614,14 +643,14 @@ main (argc, argv)
static void
add_regexp (reglist, pattern)
struct regexp_list **reglist;
- char *pattern;
+ char const *pattern;
{
struct regexp_list *r;
- const char *m;
+ char const *m;
r = (struct regexp_list *) xmalloc (sizeof (*r));
bzero (r, sizeof (*r));
- r->buf.fastmap = (char *) xmalloc (256);
+ r->buf.fastmap = xmalloc (256);
m = re_compile_pattern (pattern, strlen (pattern), &r->buf);
if (m != 0)
error ("%s: %s", pattern, m);
@@ -632,37 +661,102 @@ add_regexp (reglist, pattern)
}
static void
-usage ()
+try_help (reason)
+ char const *reason;
{
- 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, "\
- [--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, "\
- [--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");
+ if (reason)
+ error ("%s", reason, 0);
+ error ("Try `%s --help' for more information.", program_name, 0);
exit (2);
-}
+}
+
+static void
+check_stdout ()
+{
+ if (ferror (stdout) || fclose (stdout) != 0)
+ fatal ("write error");
+}
+
+static char const * const option_help[] = {
+"-i --ignore-case Consider upper- and lower-case to be the same.",
+"-w --ignore-all-space Ignore all white space.",
+"-b --ignore-space-change Ignore changes in the amount of white space.",
+"-B --ignore-blank-lines Ignore changes whose lines are all blank.",
+"-I RE --ignore-matching-lines=RE Ignore changes whose lines all match RE.",
+#if HAVE_SETMODE
+"--binary Read and write data in binary mode.",
+#endif
+"-a --text Treat all files as text.\n",
+"-c -C NUM --context[=NUM] Output NUM (default 2) lines of copied context.",
+"-u -U NUM --unified[=NUM] Output NUM (default 2) lines of unified context.",
+" -NUM Use NUM context lines.",
+" -L LABEL --label LABEL Use LABEL instead of file name.",
+" -p --show-c-function Show which C function each change is in.",
+" -F RE --show-function-line=RE Show the most recent line matching RE.",
+"-q --brief Output only whether files differ.",
+"-e --ed Output an ed script.",
+"-n --rcs Output an RCS format diff.",
+"-y --side-by-side Output in two columns.",
+" -w NUM --width=NUM Output at most NUM (default 130) characters per line.",
+" --left-column Output only the left column of common lines.",
+" --suppress-common-lines Do not output common lines.",
+"-DNAME --ifdef=NAME Output merged file to show `#ifdef NAME' diffs.",
+"--GTYPE-group-format=GFMT Similar, but format GTYPE input groups with GFMT.",
+"--line-format=LFMT Similar, but format all input lines with LFMT.",
+"--LTYPE-line-format=LFMT Similar, but format LTYPE input lines with LFMT.",
+" LTYPE is `old', `new', or `unchanged'. GTYPE is LTYPE or `changed'.",
+" GFMT may contain:",
+" %< lines from FILE1",
+" %> lines from FILE2",
+" %= lines common to FILE1 and FILE2",
+" %[-][WIDTH][.[PREC]]{doxX}LETTER printf-style spec for LETTER",
+" LETTERs are as follows for new group, lower case for old group:",
+" F first line number",
+" L last line number",
+" N number of lines = L-F+1",
+" E F-1",
+" M L+1",
+" LFMT may contain:",
+" %L contents of line",
+" %l contents of line, excluding any trailing newline",
+" %[-][WIDTH][.[PREC]]{doxX}n printf-style spec for input line number",
+" Either GFMT or LFMT may contain:",
+" %% %",
+" %c'C' the single character C",
+" %c'\\OOO' the character with octal code OOO\n",
+"-l --paginate Pass the output through `pr' to paginate it.",
+"-t --expand-tabs Expand tabs to spaces in output.",
+"-T --initial-tab Make tabs line up by prepending a tab.\n",
+"-r --recursive Recursively compare any subdirectories found.",
+"-N --new-file Treat absent files as empty.",
+"-P --unidirectional-new-file Treat absent first files as empty.",
+"-s --report-identical-files Report when two files are the same.",
+"-x PAT --exclude=PAT Exclude files that match PAT.",
+"-X FILE --exclude-from=FILE Exclude files that match any pattern in FILE.",
+"-S FILE --starting-file=FILE Start with FILE when comparing directories.\n",
+"--horizon-lines=NUM Keep NUM lines of the common prefix and suffix.",
+"-d --minimal Try hard to find a smaller set of changes.",
+"-H --speed-large-files Assume large files and many scattered small changes.\n",
+"-v --version Output version info.",
+"--help Output this help.",
+0
+};
+
+static void
+usage ()
+{
+ char const * const *p;
+
+ printf ("Usage: %s [OPTION]... FILE1 FILE2\n\n", program_name);
+ for (p = option_help; *p; p++)
+ printf (" %s\n", *p);
+ printf ("\nIf FILE1 or FILE2 is `-', read standard input.\n");
+}
static int
specify_format (var, value)
- const char **var;
- const char *value;
+ char **var;
+ char *value;
{
int err = *var ? strcmp (*var, value) : 0;
*var = value;
@@ -679,6 +773,57 @@ specify_style (style)
output_style = style;
}
+static char const *
+filetype (st)
+ struct stat const *st;
+{
+ /* See Posix.2 section 4.17.6.1.1 and Table 5-1 for these formats.
+ To keep diagnostics grammatical, the returned string must start
+ with a consonant. */
+
+ if (S_ISREG (st->st_mode))
+ {
+ if (st->st_size == 0)
+ return "regular empty file";
+ /* Posix.2 section 5.14.2 seems to suggest that we must read the file
+ and guess whether it's C, Fortran, etc., but this is somewhat useless
+ and doesn't reflect historical practice. We're allowed to guess
+ wrong, so we don't bother to read the file. */
+ return "regular file";
+ }
+ if (S_ISDIR (st->st_mode)) return "directory";
+
+ /* other Posix.1 file types */
+#ifdef S_ISBLK
+ if (S_ISBLK (st->st_mode)) return "block special file";
+#endif
+#ifdef S_ISCHR
+ if (S_ISCHR (st->st_mode)) return "character special file";
+#endif
+#ifdef S_ISFIFO
+ if (S_ISFIFO (st->st_mode)) return "fifo";
+#endif
+
+ /* other Posix.1b file types */
+#ifdef S_TYPEISMQ
+ if (S_TYPEISMQ (st)) return "message queue";
+#endif
+#ifdef S_TYPEISSEM
+ if (S_TYPEISSEM (st)) return "semaphore";
+#endif
+#ifdef S_TYPEISSHM
+ if (S_TYPEISSHM (st)) return "shared memory object";
+#endif
+
+ /* other popular file types */
+ /* S_ISLNK is impossible with `fstat' and `stat'. */
+#ifdef S_ISSOCK
+ if (S_ISSOCK (st->st_mode)) return "socket";
+#endif
+
+ return "weird file";
+}
+
/* Compare two files (or dirs) with specified names
DIR0/NAME0 and DIR1/NAME1, at level DEPTH in directory recursion.
(if DIR0 is 0, then the name is just NAME0, etc.)
@@ -689,15 +834,16 @@ specify_style (style)
static int
compare_files (dir0, name0, dir1, name1, depth)
- char *dir0, *dir1;
- char *name0, *name1;
+ char const *dir0, *dir1;
+ char const *name0, *name1;
int depth;
{
struct file_data inf[2];
register int i;
int val;
int same_files;
- int errorcount = 0;
+ int failed = 0;
+ char *free0 = 0, *free1 = 0;
/* If this is directory comparison, perhaps we have a file
that exists only in one of the directories.
@@ -707,15 +853,17 @@ compare_files (dir0, name0, dir1, name1, depth)
|| (unidirectional_new_file_flag && name1 != 0)
|| entire_new_file_flag))
{
- char *name = name0 == 0 ? name1 : name0;
- char *dir = name0 == 0 ? dir1 : dir0;
+ char const *name = name0 == 0 ? name1 : name0;
+ char const *dir = name0 == 0 ? dir1 : dir0;
message ("Only in %s: %s\n", dir, name);
/* Return 1 so that diff_dirs will return 1 ("some files differ"). */
return 1;
}
+ bzero (inf, sizeof (inf));
+
/* Mark any nonexistent file with -1 in the desc field. */
- /* Mark unopened files (i.e. directories) with -2. */
+ /* Mark unopened files (e.g. directories) with -2. */
inf[0].desc = name0 == 0 ? -1 : -2;
inf[1].desc = name1 == 0 ? -1 : -2;
@@ -727,25 +875,41 @@ compare_files (dir0, name0, dir1, name1, depth)
if (name1 == 0)
name1 = name0;
- inf[0].name = dir0 == 0 ? name0 : concat (dir0, "/", name0);
- inf[1].name = dir1 == 0 ? name1 : concat (dir1, "/", name1);
+ inf[0].name = dir0 == 0 ? name0 : (free0 = dir_file_pathname (dir0, name0));
+ inf[1].name = dir1 == 0 ? name1 : (free1 = dir_file_pathname (dir1, name1));
/* Stat the files. Record whether they are directories. */
for (i = 0; i <= 1; i++)
{
- bzero (&inf[i].stat, sizeof (struct stat));
- inf[i].dir_p = 0;
-
if (inf[i].desc != -1)
{
int stat_result;
- if (strcmp (inf[i].name, "-") == 0)
+ if (i && filename_cmp (inf[i].name, inf[0].name) == 0)
{
- inf[i].desc = 0;
- inf[i].name = "Standard Input";
- stat_result = fstat (0, &inf[i].stat);
+ inf[i].stat = inf[0].stat;
+ stat_result = 0;
+ }
+ else if (strcmp (inf[i].name, "-") == 0)
+ {
+ inf[i].desc = STDIN_FILENO;
+ stat_result = fstat (STDIN_FILENO, &inf[i].stat);
+ if (stat_result == 0 && S_ISREG (inf[i].stat.st_mode))
+ {
+ off_t pos = lseek (STDIN_FILENO, (off_t) 0, SEEK_CUR);
+ if (pos == -1)
+ stat_result = -1;
+ else
+ {
+ if (pos <= inf[i].stat.st_size)
+ inf[i].stat.st_size -= pos;
+ else
+ inf[i].stat.st_size = 0;
+ /* Posix.2 4.17.6.1.4 requires current time for stdin. */
+ time (&inf[i].stat.st_mtime);
+ }
+ }
}
else
stat_result = stat (inf[i].name, &inf[i].stat);
@@ -753,42 +917,46 @@ compare_files (dir0, name0, dir1, name1, depth)
if (stat_result != 0)
{
perror_with_name (inf[i].name);
- errorcount = 1;
+ failed = 1;
}
else
- inf[i].dir_p = S_ISDIR (inf[i].stat.st_mode) && inf[i].desc != 0;
+ {
+ inf[i].dir_p = S_ISDIR (inf[i].stat.st_mode) && inf[i].desc != 0;
+ if (inf[1 - i].desc == -1)
+ {
+ inf[1 - i].dir_p = inf[i].dir_p;
+ inf[1 - i].stat.st_mode = inf[i].stat.st_mode;
+ }
+ }
}
}
- if (name0 == 0)
- inf[0].dir_p = inf[1].dir_p;
- if (name1 == 0)
- inf[1].dir_p = inf[0].dir_p;
-
- if (errorcount == 0 && depth == 0 && inf[0].dir_p != inf[1].dir_p)
+ if (! failed && depth == 0 && inf[0].dir_p != inf[1].dir_p)
{
/* If one is a directory, and it was specified in the command line,
use the file in that dir with the other file's basename. */
int fnm_arg = 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));
+ char const *fnm = inf[fnm_arg].name;
+ char const *dir = inf[dir_arg].name;
+ char const *p = filename_lastdirchar (fnm);
+ char const *filename = inf[dir_arg].name
+ = dir_file_pathname (dir, p ? p + 1 : fnm);
- if (inf[fnm_arg].desc == 0)
+ if (strcmp (fnm, "-") == 0)
fatal ("can't compare - to a directory");
if (stat (filename, &inf[dir_arg].stat) != 0)
{
perror_with_name (filename);
- errorcount = 1;
+ failed = 1;
}
else
inf[dir_arg].dir_p = S_ISDIR (inf[dir_arg].stat.st_mode);
}
- if (errorcount)
+ if (failed)
{
/* If either file should exist but does not, return 2. */
@@ -796,10 +964,8 @@ compare_files (dir0, name0, dir1, name1, depth)
val = 2;
}
- 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)
+ else if ((same_files = inf[0].desc != -1 && inf[1].desc != -1
+ && 0 < same_file (&inf[0].stat, &inf[1].stat))
&& no_diff_means_no_output)
{
/* The two named files are actually the same physical file.
@@ -828,37 +994,43 @@ compare_files (dir0, name0, dir1, name1, depth)
}
}
- else if (inf[0].dir_p | inf[1].dir_p)
+ else if ((inf[0].dir_p | inf[1].dir_p)
+ || (depth > 0
+ && (! S_ISREG (inf[0].stat.st_mode)
+ || ! S_ISREG (inf[1].stat.st_mode))))
{
/* 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 (recursive
+ if ((inf[0].dir_p | inf[1].dir_p)
+ && 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;
+ char const *dir = (inf[0].desc == -1) ? dir1 : dir0;
+ /* See Posix.2 section 4.17.6.1.1 for this format. */
message ("Only in %s: %s\n", dir, name0);
val = 1;
}
}
else
{
- /* We have a subdirectory in one directory
- and a file in the other. */
+ /* We have two files that are not to be compared. */
- message ("%s is a directory but %s is not\n",
- inf[1 - inf[0].dir_p].name, inf[inf[0].dir_p].name);
+ /* See Posix.2 section 4.17.6.1.1 for this format. */
+ message5 ("File %s is a %s while file %s is a %s\n",
+ inf[0].name, filetype (&inf[0].stat),
+ inf[1].name, filetype (&inf[1].stat));
/* This is a difference. */
val = 1;
}
}
- else if (no_details_flag
+ else if ((no_details_flag & ~ignore_some_changes)
&& 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)))
@@ -876,7 +1048,7 @@ compare_files (dir0, name0, dir1, name1, depth)
if ((inf[0].desc = open (inf[0].name, O_RDONLY, 0)) < 0)
{
perror_with_name (inf[0].name);
- errorcount = 1;
+ failed = 1;
}
if (inf[1].desc == -2)
if (same_files)
@@ -884,12 +1056,19 @@ compare_files (dir0, name0, dir1, name1, depth)
else if ((inf[1].desc = open (inf[1].name, O_RDONLY, 0)) < 0)
{
perror_with_name (inf[1].name);
- errorcount = 1;
+ failed = 1;
}
-
+
+#if HAVE_SETMODE
+ if (binary_I_O)
+ for (i = 0; i <= 1; i++)
+ if (0 <= inf[i].desc)
+ setmode (inf[i].desc, O_BINARY);
+#endif
+
/* Compare the files, if no error was found. */
- val = errorcount ? 2 : diff_2_files (inf, depth);
+ val = failed ? 2 : diff_2_files (inf, depth);
/* Close the file descriptors. */
@@ -918,10 +1097,10 @@ compare_files (dir0, name0, dir1, name1, depth)
else
fflush (stdout);
- if (dir0 != 0)
- free (inf[0].name);
- if (dir1 != 0)
- free (inf[1].name);
+ if (free0)
+ free (free0);
+ if (free1)
+ free (free1);
return val;
}
OpenPOWER on IntegriCloud