diff options
author | peter <peter@FreeBSD.org> | 1995-10-28 18:51:33 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 1995-10-28 18:51:33 +0000 |
commit | 27378b5694b4941bb63a62097f7ee523da0a35bb (patch) | |
tree | 9372a0168d8ef1138d4ec4ef2051198caed461de /gnu/usr.bin/diff/diff.c | |
parent | 31fbfe9bebb8e48eaf39efc88875c743cf238ced (diff) | |
download | FreeBSD-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.c | 507 |
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; } |