From 6bf2c33ea45c20061c13eeefebf2951eab79b61c Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Tue, 18 Jul 2023 08:28:10 -0700 Subject: [PATCH] diff: use openat, fstatat when recursive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should improve performance when doing recursive comparisons. Currently there is no attempt to avoid file descriptor exhaustion, just as previously there is no attempt to avoid file names that provoke ENAMETOOLONG. Because of this change, ‘diff - A/B’ now works correctly when standard input is a directory. * .gitignore: Add lib/dirent.h. * bootstrap.conf (gnulib_modules): Add fdopendir. * src/diff.c (main): Initialize noparent’s desc to AT_FDCWD. (compare_files): Use fstatat with parent directory’s file descriptor and relative name, instead of lstat or stat. Likewise for openat and open. * src/diff.h (struct file_data): New member ‘dirstream’. (struct comparison): The ‘parent’ member is now &noparent (instead of null) if there is no parent. All uses changed. (curr): New toplevel variable, replacing ‘files’. All uses changed. * src/dir.c: Include dirname.h, for last_component. (dir_read): New arg PARENTDIRFD. Arg DIR is no longer pointer-to-const since DIR->desc and DIR->dirstream are now updated. Use PARENTDIRFD to open the directory via opendat+fdopendir instead of via opendir. Update new dirstream component instead of closing the directory, since it’s now the caller’s responsibility to close the directory because callers now want the file descriptor. All callers changed. (diff_dirs): First arg CMP is no longer pointer-to-const since CMP->file is updated by dir_read. All callers changed. (find_dir_file_pathname): First arg is now struct file_data *, not merely a file name. All callers changed. * tests/stdin: Test new behavior when stdin is a directory. --- .gitignore | 1 + NEWS | 4 ++ bootstrap.conf | 1 + src/analyze.c | 11 +++-- src/context.c | 48 +++++++++---------- src/diff.c | 125 ++++++++++++++++++++++++++----------------------- src/diff.h | 22 +++++++-- src/dir.c | 86 ++++++++++++++++++---------------- src/ed.c | 17 +++---- src/ifdef.c | 14 +++--- src/normal.c | 12 ++--- src/side.c | 20 ++++---- src/util.c | 21 +++------ tests/stdin | 19 +++++--- 14 files changed, 217 insertions(+), 184 deletions(-) diff --git a/.gitignore b/.gitignore index 9dfbe26..0e163f8 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ lib/config.h lib/config.hin lib/configmake.h lib/ctype.h +lib/dirent.h lib/error.h lib/errno.h lib/fcntl.h diff --git a/NEWS b/NEWS index f879394..192500d 100644 --- a/NEWS +++ b/NEWS @@ -37,6 +37,10 @@ GNU diffutils NEWS -*- outline -*- diff -ly no longer mishandles non-ASCII input [bug#64461 introduced in 2.9] + diff - A/B now works correctly when standard input is a directory, + by reading a file named B in that directory. + [bug present since "the beginning"] + * Noteworthy changes in release 3.10 (2023-05-21) [stable] diff --git a/bootstrap.conf b/bootstrap.conf index d9c42a6..98f94cf 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -45,6 +45,7 @@ exitfail extensions extern-inline fcntl +fdopendir file-type filenamecat flexmember diff --git a/src/analyze.c b/src/analyze.c index ba0de04..a6a0bec 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -30,8 +30,10 @@ #define OFFSET lin #define OFFSET_MAX LIN_MAX #define EXTRA_CONTEXT_FIELDS /* none */ -#define NOTE_DELETE(c, x) (files[0].changed[files[0].realindexes[x]] = true) -#define NOTE_INSERT(c, y) (files[1].changed[files[1].realindexes[y]] = true) +#define NOTE_DELETE(c, x) \ + (curr.file[0].changed[curr.file[0].realindexes[x]] = true) +#define NOTE_INSERT(c, y) \ + (curr.file[1].changed[curr.file[1].realindexes[y]] = true) #define USE_HEURISTIC #include @@ -554,8 +556,7 @@ diff_2_files (struct comparison *cmp) lin too_expensive = (lin) 1 << ((floor_log2 (diags) >> 1) + 1); ctxt.too_expensive = MAX (4096, too_expensive); - files[0] = cmp->file[0]; - files[1] = cmp->file[1]; + curr = *cmp; compareseq (0, cmp->file[0].nondiscarded_lines, 0, cmp->file[1].nondiscarded_lines, minimal, &ctxt); @@ -613,7 +614,7 @@ diff_2_files (struct comparison *cmp) 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); + cmp->parent != &noparent); switch (output_style) { diff --git a/src/context.c b/src/context.c index 79f8271..c6d880a 100644 --- a/src/context.c +++ b/src/context.c @@ -105,7 +105,7 @@ print_context_script (struct change *script, bool unidiff) for (struct change *e = script; e; e = e->link) e->ignore = false; - find_function_last_search = - files[0].prefix_lines; + find_function_last_search = - curr.file[0].prefix_lines; find_function_last_match = LIN_MAX; if (unidiff) @@ -174,22 +174,22 @@ pr_context_hunk (struct change *hunk) /* Include a context's width before and after. */ - lin minus_prefix_lines = files[0].prefix_lines; + lin minus_prefix_lines = curr.file[0].prefix_lines; first0 = MAX (first0 - context, minus_prefix_lines); first1 = MAX (first1 - context, minus_prefix_lines); - if (last0 < files[0].valid_lines - context) + if (last0 < curr.file[0].valid_lines - context) last0 += context; else - last0 = files[0].valid_lines - 1; - if (last1 < files[1].valid_lines - context) + last0 = curr.file[0].valid_lines - 1; + if (last1 < curr.file[1].valid_lines - context) last1 += context; else - last1 = files[1].valid_lines - 1; + last1 = curr.file[1].valid_lines - 1; /* If desired, find the preceding function definition line in file 0. */ char const *function = nullptr; if (function_regexp.fastmap) - function = find_function (files[0].linbuf, first0); + function = find_function (curr.file[0].linbuf, first0); begin_output (); FILE *out = outfile; @@ -202,7 +202,7 @@ pr_context_hunk (struct change *hunk) putc ('\n', out); set_color_context (LINE_NUMBER_CONTEXT); fputs ("*** ", out); - print_context_number_range (&files[0], first0, last0); + print_context_number_range (&curr.file[0], first0, last0); fputs (" ****", out); set_color_context (RESET_CONTEXT); putc ('\n', out); @@ -231,16 +231,16 @@ pr_context_hunk (struct change *hunk) Otherwise it is "deleted". */ prefix = (next->inserted > 0 ? "!" : "-"); } - print_1_line_nl (prefix, &files[0].linbuf[i], true); + print_1_line_nl (prefix, &curr.file[0].linbuf[i], true); set_color_context (RESET_CONTEXT); - if (files[0].linbuf[i + 1][-1] == '\n') + if (curr.file[0].linbuf[i + 1][-1] == '\n') putc ('\n', out); } } set_color_context (LINE_NUMBER_CONTEXT); fputs ("--- ", out); - print_context_number_range (&files[1], first1, last1); + print_context_number_range (&curr.file[1], first1, last1); fputs (" ----", out); set_color_context (RESET_CONTEXT); putc ('\n', out); @@ -269,9 +269,9 @@ pr_context_hunk (struct change *hunk) Otherwise it is "inserted". */ prefix = (next->deleted > 0 ? "!" : "+"); } - print_1_line_nl (prefix, &files[1].linbuf[i], true); + print_1_line_nl (prefix, &curr.file[1].linbuf[i], true); set_color_context (RESET_CONTEXT); - if (files[1].linbuf[i + 1][-1] == '\n') + if (curr.file[1].linbuf[i + 1][-1] == '\n') putc ('\n', out); } } @@ -317,31 +317,31 @@ pr_unidiff_hunk (struct change *hunk) /* Include a context's width before and after. */ - lin minus_prefix_lines = - files[0].prefix_lines; + lin minus_prefix_lines = - curr.file[0].prefix_lines; first0 = MAX (first0 - context, minus_prefix_lines); first1 = MAX (first1 - context, minus_prefix_lines); - if (last0 < files[0].valid_lines - context) + if (last0 < curr.file[0].valid_lines - context) last0 += context; else - last0 = files[0].valid_lines - 1; - if (last1 < files[1].valid_lines - context) + last0 = curr.file[0].valid_lines - 1; + if (last1 < curr.file[1].valid_lines - context) last1 += context; else - last1 = files[1].valid_lines - 1; + last1 = curr.file[1].valid_lines - 1; /* If desired, find the preceding function definition line in file 0. */ char const *function = nullptr; if (function_regexp.fastmap) - function = find_function (files[0].linbuf, first0); + function = find_function (curr.file[0].linbuf, first0); begin_output (); FILE *out = outfile; set_color_context (LINE_NUMBER_CONTEXT); fputs ("@@ -", out); - print_unidiff_number_range (&files[0], first0, last0); + print_unidiff_number_range (&curr.file[0], first0, last0); fputs (" +", out); - print_unidiff_number_range (&files[1], first1, last1); + print_unidiff_number_range (&curr.file[1], first1, last1); fputs (" @@", out); set_color_context (RESET_CONTEXT); @@ -361,7 +361,7 @@ pr_unidiff_hunk (struct change *hunk) if (!next || i < next->line0) { - char const *const *line = &files[0].linbuf[i++]; + char const *const *line = &curr.file[0].linbuf[i++]; if (! (suppress_blank_empty && **line == '\n')) putc (initial_tab ? '\t' : ' ', out); print_1_line (nullptr, line); @@ -375,7 +375,7 @@ pr_unidiff_hunk (struct change *hunk) while (k--) { - char const *const *line = &files[0].linbuf[i++]; + char const *const *line = &curr.file[0].linbuf[i++]; set_color_context (DELETE_CONTEXT); putc ('-', out); if (initial_tab && ! (suppress_blank_empty && **line == '\n')) @@ -394,7 +394,7 @@ pr_unidiff_hunk (struct change *hunk) while (k--) { - char const *const *line = &files[1].linbuf[j++]; + char const *const *line = &curr.file[1].linbuf[j++]; set_color_context (ADD_CONTEXT); putc ('+', out); if (initial_tab && ! (suppress_blank_empty && **line == '\n')) diff --git a/src/diff.c b/src/diff.c index 5430461..afe9f44 100644 --- a/src/diff.c +++ b/src/diff.c @@ -823,6 +823,9 @@ main (int argc, char **argv) int exit_status = EXIT_SUCCESS; + noparent.file[0].desc = AT_FDCWD; + noparent.file[1].desc = AT_FDCWD; + if (from_file) { if (to_file) @@ -830,7 +833,7 @@ main (int argc, char **argv) else for (; optind < argc; optind++) { - int status = compare_files (nullptr, from_file, argv[optind]); + int status = compare_files (&noparent, from_file, argv[optind]); if (exit_status < status) exit_status = status; } @@ -840,7 +843,7 @@ main (int argc, char **argv) if (to_file) for (; optind < argc; optind++) { - int status = compare_files (nullptr, argv[optind], to_file); + int status = compare_files (&noparent, argv[optind], to_file); if (exit_status < status) exit_status = status; } @@ -854,7 +857,8 @@ main (int argc, char **argv) try_help ("extra operand '%s'", argv[optind + 2]); } - exit_status = compare_files (nullptr, argv[optind], argv[optind + 1]); + exit_status = compare_files (&noparent, + argv[optind], argv[optind + 1]); } } @@ -1148,7 +1152,7 @@ dir_p (struct comparison const *pcmp, int f) /* Compare two files (or dirs) with parent comparison PARENT and names NAME0 and NAME1. - (If PARENT is null, then the first name is just NAME0, etc.) + (If PARENT == &NOPARENT, then the first name is just NAME0, etc.) This is self-contained; it opens the files and closes them. Value is EXIT_SUCCESS if files are the same, EXIT_FAILURE if @@ -1192,7 +1196,7 @@ compare_files (struct comparison const *parent, char *free0; char *free1; - if (!parent) + if (parent == &noparent) { free0 = nullptr; free1 = nullptr; @@ -1242,11 +1246,17 @@ compare_files (struct comparison const *parent, set_mtime_to_now (&cmp.file[f].stat); } } - else if ((no_dereference_symlinks - ? lstat (cmp.file[f].name, &cmp.file[f].stat) - : stat (cmp.file[f].name, &cmp.file[f].stat)) - != 0) - cmp.file[f].desc = errno_encode (errno); + else + { + char const *name = cmp.file[f].name; + if (fstatat (parent->file[f].desc, + (parent->file[f].desc < 0 ? name + : last_component (name)), + &cmp.file[f].stat, + no_dereference_symlinks ? AT_SYMLINK_NOFOLLOW : 0) + < 0) + cmp.file[f].desc = errno_encode (errno); + } } } @@ -1263,7 +1273,7 @@ compare_files (struct comparison const *parent, && cmp.file[f].stat.st_size == 0) : ((cmp.file[f].desc == errno_encode (ENOENT) || cmp.file[f].desc == errno_encode (EBADF)) - && ! parent + && parent == &noparent && (cmp.file[1 - f].desc == UNOPENED || cmp.file[1 - f].desc == STDIN_FILENO)))) cmp.file[f].desc = NONEXISTENT; @@ -1288,7 +1298,7 @@ compare_files (struct comparison const *parent, } } - if (status == EXIT_SUCCESS && ! parent && !no_directory + if (status == EXIT_SUCCESS && !no_directory && parent == &noparent && dir_p (&cmp, 0) != dir_p (&cmp, 1)) { /* If one is a directory, and it was specified in the command line, @@ -1297,17 +1307,21 @@ compare_files (struct comparison const *parent, int fnm_arg = dir_p (&cmp, 0); int dir_arg = 1 - fnm_arg; char const *fnm = cmp.file[fnm_arg].name; - char const *dir = cmp.file[dir_arg].name; + char const *base_fnm = last_component (fnm); char const *filename = cmp.file[dir_arg].name = free0 - = find_dir_file_pathname (dir, last_component (fnm)); + = find_dir_file_pathname (&cmp.file[dir_arg], base_fnm); if (STREQ (fnm, "-")) fatal ("cannot compare '-' to a directory"); - if ((no_dereference_symlinks - ? lstat (filename, &cmp.file[dir_arg].stat) - : stat (filename, &cmp.file[dir_arg].stat)) - != 0) + int dirdesc = cmp.file[dir_arg].desc; + cmp.file[dir_arg].desc = UNOPENED; + noparent.file[dir_arg].desc = dirdesc < 0 ? AT_FDCWD : dirdesc; + if (fstatat (noparent.file[dir_arg].desc, + dirdesc < 0 ? filename : base_fnm, + &cmp.file[dir_arg].stat, + no_dereference_symlinks ? AT_SYMLINK_NOFOLLOW : 0) + < 0) { perror_with_name (filename); status = EXIT_TROUBLE; @@ -1343,7 +1357,7 @@ compare_files (struct comparison const *parent, /* If both are directories, compare the files in them. */ - if (parent && !recursive) + if (!recursive && parent != &noparent) { /* But don't compare dir contents one level down unless -r was specified. @@ -1355,7 +1369,7 @@ compare_files (struct comparison const *parent, status = diff_dirs (&cmp, compare_files); } else if ((dir_p (&cmp, 0) | dir_p (&cmp, 1)) - || (parent + || (parent != &noparent && !((S_ISREG (cmp.file[0].stat.st_mode) || S_ISLNK (cmp.file[0].stat.st_mode)) && (S_ISREG (cmp.file[1].stat.st_mode) @@ -1399,7 +1413,7 @@ compare_files (struct comparison const *parent, else if (S_ISLNK (cmp.file[0].stat.st_mode) || S_ISLNK (cmp.file[1].stat.st_mode)) { - /* We get here only if we use lstat(), not stat(). */ + /* We get here only if we are not dereferencing symlinks. */ dassert (no_dereference_symlinks); if (S_ISLNK (cmp.file[0].stat.st_mode) @@ -1464,52 +1478,45 @@ compare_files (struct comparison const *parent, /* Open the files and record their descriptors. */ - int oflags = O_RDONLY | (binary ? O_BINARY : 0); + int oflags = (O_RDONLY | (binary ? O_BINARY : 0) + | (no_dereference_symlinks ? O_NOFOLLOW : 0)); - if (cmp.file[0].desc == UNOPENED) - { - cmp.file[0].desc = open (cmp.file[0].name, oflags); - if (cmp.file[0].desc < 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 - { - cmp.file[1].desc = open (cmp.file[1].name, oflags); - if (cmp.file[1].desc < 0) - { - perror_with_name (cmp.file[1].name); - status = EXIT_TROUBLE; - } - } - } + for (int f = 0; f < 2; f++) + if (cmp.file[f].desc == UNOPENED) + { + if (f && same_files) + cmp.file[f].desc = cmp.file[0].desc; + else + { + int dirfd = parent->file[f].desc; + char const *name = cmp.file[f].name; + char const *nm = dirfd < 0 ? name : last_component (name); + cmp.file[f].desc = openat (dirfd, nm, oflags); + if (cmp.file[f].desc < 0) + { + perror_with_name (name); + status = EXIT_TROUBLE; + } + } + } /* 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; - } } + + /* Close any input files. */ + for (int f = 0; f < 2; f++) + if ((f == 0 || cmp.file[f].desc != cmp.file[0].desc) + && (cmp.file[f].dirstream ? closedir (cmp.file[f].dirstream) < 0 + : 0 <= cmp.file[f].desc && close (cmp.file[f].desc) < 0)) + { + perror_with_name (cmp.file[f].name); + status = EXIT_TROUBLE; + } + /* Now the comparison has been done, if no error prevented it, and STATUS is the value this function will return. */ diff --git a/src/diff.h b/src/diff.h index 663d65d..1726f21 100644 --- a/src/diff.h +++ b/src/diff.h @@ -276,6 +276,10 @@ struct file_data { char const *name; /* File name */ struct stat stat; /* File status */ + /* Directory stream corresponding to DESC, if it has been fdopendir'ed. + Null otherwise. */ + DIR *dirstream; + /* Buffer in which text of file is read. */ word *buffer; @@ -345,13 +349,23 @@ enum { UNOPENED = -2 }; /* unopened file (e.g. directory) */ struct comparison { + /* The two files. */ struct file_data file[2]; - struct comparison const *parent; /* parent, if a recursive comparison */ + + /* The parent comparison, or &noparent if at the top level. */ + struct comparison const *parent; }; /* Describe the two files currently being compared. */ -XTERN struct file_data files[2]; +XTERN struct comparison curr; + +/* A placeholder for the parent of the top level comparison. + Only the desc slots are used; although they are typically AT_FDCWD, + one might be nonnegative for a directory at the top level + for 'diff DIR FILE' or 'diff FILE DIR'. */ + +XTERN struct comparison noparent; /* Stdio stream to output diffs to. */ @@ -368,10 +382,10 @@ extern void print_context_header (struct file_data[], extern void print_context_script (struct change *, bool); /* dir.c */ -extern int diff_dirs (struct comparison const *, +extern int diff_dirs (struct comparison *, int (*) (struct comparison const *, char const *, char const *)); -extern char *find_dir_file_pathname (char const *, char const *) +extern char *find_dir_file_pathname (struct file_data *, char const *) ATTRIBUTE_MALLOC ATTRIBUTE_DEALLOC_FREE ATTRIBUTE_RETURNS_NONNULL; diff --git a/src/dir.c b/src/dir.c index c3af9b5..9cd4efd 100644 --- a/src/dir.c +++ b/src/dir.c @@ -20,6 +20,7 @@ #include "diff.h" #include +#include #include #include #include @@ -44,14 +45,17 @@ static jmp_buf failed_locale_specific_sorting; static bool dir_loop (struct comparison const *, int); -/* Read the directory named by DIR and store into DIRDATA a sorted +/* Given the parent directory PARENTDIRFD (negative for current dir), + read the directory named by DIR and store into DIRDATA a sorted vector of filenames for its contents. - DIR->desc == NONEXISTENT means this directory is known to be - nonexistent, so set DIRDATA to an empty vector. + Use DIR's basename if PARENTDIRFD is nonnegative, for efficiency. + If DIR->desc == NONEXISTENT, this directory is known to be + nonexistent so set DIRDATA to an empty vector; + otherwise, update DIR->desc and DIR->dirstream as needed. Return true if successful, false (setting errno) otherwise. */ static bool -dir_read (struct file_data const *dir, struct dirdata *dirdata) +dir_read (int parentdirfd, struct file_data *dir, struct dirdata *dirdata) { /* Number of files in directory. */ idx_t nnames = 0; @@ -65,9 +69,22 @@ dir_read (struct file_data const *dir, struct dirdata *dirdata) if (dir->desc != NONEXISTENT) { /* Open the directory and check for errors. */ - DIR *reading = opendir (dir->name); + int dirfd = dir->desc; + if (dirfd < 0) + { + dirfd = openat (parentdirfd, + (parentdirfd < 0 ? dir->name + : last_component (dir->name)), + (O_RDONLY | O_DIRECTORY + | (no_dereference_symlinks ? O_NOFOLLOW : 0))); + if (dirfd < 0) + return false; + dir->desc = dirfd; + } + DIR *reading = fdopendir (dirfd); if (!reading) return false; + dir->dirstream = reading; /* Initialize the table of filenames. */ @@ -105,13 +122,8 @@ dir_read (struct file_data const *dir, struct dirdata *dirdata) nnames++; } - int readdir_errno = errno; - if (closedir (reading) < 0 || readdir_errno) - { - if (readdir_errno) - errno = readdir_errno; - return false; - } + if (errno) + return false; } /* Create the 'names' table from the 'data' table. */ @@ -183,8 +195,9 @@ compare_names_for_qsort (void const *file1, void const *file2) This is a top-level routine; it does everything necessary for diff on two directories. - CMP->file[0].desc == NONEXISTENT says directory CMP->file[0] doesn't exist, - but pretend it is empty. Likewise for CMP->file[1]. + If CMP->file[0].desc == NONEXISTENT, directory CMP->file[0] doesn't exist + and pretend it is empty. Otherwise, update CMP->file[0].desc and + CMP->file[0].dirstream as needed. Likewise for CMP->file[1]. HANDLE_FILE is a caller-provided subroutine called to handle each file. It gets three operands: CMP, name of file in dir 0, name of file in dir 1. @@ -197,7 +210,7 @@ compare_names_for_qsort (void const *file1, void const *file2) or EXIT_TROUBLE if trouble is encountered in opening files. */ int -diff_dirs (struct comparison const *cmp, +diff_dirs (struct comparison *cmp, int (*handle_file) (struct comparison const *, char const *, char const *)) { @@ -213,7 +226,7 @@ diff_dirs (struct comparison const *cmp, struct dirdata dirdata[2]; int volatile val = EXIT_SUCCESS; for (int i = 0; i < 2; i++) - if (! dir_read (&cmp->file[i], &dirdata[i])) + if (! dir_read (cmp->parent->file[i].desc, &cmp->file[i], &dirdata[i])) { perror_with_name (cmp->file[i].name); val = EXIT_TROUBLE; @@ -317,7 +330,7 @@ dir_loop (struct comparison const *cmp, int i) /* Find a matching filename in a directory. */ char * -find_dir_file_pathname (char const *dir, char const *file) +find_dir_file_pathname (struct file_data *dir, char const *file) { /* IF_LINT due to GCC bug 21161. */ char const *IF_LINT (volatile) match = file; @@ -326,35 +339,26 @@ find_dir_file_pathname (char const *dir, char const *file) dirdata.names = nullptr; dirdata.data = nullptr; - if (ignore_file_name_case) + if (ignore_file_name_case && dir_read (AT_FDCWD, dir, &dirdata)) { - struct file_data filedata; - filedata.name = dir; - filedata.desc = 0; - - if (dir_read (&filedata, &dirdata)) - { - locale_specific_sorting = true; - if (setjmp (failed_locale_specific_sorting)) - match = file; /* longjmp may mess up MATCH. */ - else + locale_specific_sorting = true; + if (setjmp (failed_locale_specific_sorting)) + match = file; /* longjmp may mess up MATCH. */ + else + for (char const **p = dirdata.names; *p; p++) + if (compare_names (*p, file) == 0) { - for (char const **p = dirdata.names; *p; p++) - if (compare_names (*p, file) == 0) - { - if (file_name_cmp (*p, file) == 0) - { - match = *p; - break; - } - if (match == file) - match = *p; - } + if (file_name_cmp (*p, file) == 0) + { + match = *p; + break; + } + if (match == file) + match = *p; } - } } - char *val = file_name_concat (dir, match, nullptr); + char *val = file_name_concat (dir->name, match, nullptr); free (dirdata.names); free (dirdata.data); return val; diff --git a/src/ed.c b/src/ed.c index 44bf1be..b3172dc 100644 --- a/src/ed.c +++ b/src/ed.c @@ -51,7 +51,7 @@ print_ed_hunk (struct change *hunk) begin_output (); /* Print out the line number header for this hunk */ - print_number_range (',', &files[0], f0, l0); + print_number_range (',', &curr.file[0], f0, l0); fputc (change_letter[changes], outfile); fputc ('\n', outfile); @@ -67,7 +67,8 @@ print_ed_hunk (struct change *hunk) fputs ("a\n", outfile); insert_mode = true; } - if (files[1].linbuf[i][0] == '.' && files[1].linbuf[i][1] == '\n') + if (curr.file[1].linbuf[i][0] == '.' + && curr.file[1].linbuf[i][1] == '\n') { /* The file's line is just a dot, and it would exit insert mode. Precede the dot with another dot, exit @@ -76,7 +77,7 @@ print_ed_hunk (struct change *hunk) insert_mode = false; } else - print_1_line ("", &files[1].linbuf[i]); + print_1_line ("", &curr.file[1].linbuf[i]); } if (insert_mode) @@ -108,7 +109,7 @@ pr_forward_ed_hunk (struct change *hunk) begin_output (); fputc (change_letter[changes], outfile); - print_number_range (' ', files, f0, l0); + print_number_range (' ', &curr.file[0], f0, l0); fputc ('\n', outfile); /* If deletion only, print just the number range. */ @@ -120,7 +121,7 @@ pr_forward_ed_hunk (struct change *hunk) and the lines from file 2. */ for (lin i = f1; i <= l1; i++) - print_1_line ("", &files[1].linbuf[i]); + print_1_line ("", &curr.file[1].linbuf[i]); fputs (".\n", outfile); } @@ -149,7 +150,7 @@ print_rcs_hunk (struct change *hunk) begin_output (); lin tf0, tl0, tf1, tl1; - translate_range (&files[0], f0, l0, &tf0, &tl0); + translate_range (&curr.file[0], f0, l0, &tf0, &tl0); if (changes & OLD) { @@ -162,12 +163,12 @@ print_rcs_hunk (struct change *hunk) if (changes & NEW) { /* Take last-line-number from file 0 and # lines from file 1. */ - translate_range (&files[1], f1, l1, &tf1, &tl1); + translate_range (&curr.file[1], f1, l1, &tf1, &tl1); fprintf (outfile, "a%"pI"d %"pI"d\n", tl0, tf1 <= tl1 ? tl1 - tf1 + 1 : 1); /* Print the inserted lines. */ for (lin i = f1; i <= l1; i++) - print_1_line ("", &files[1].linbuf[i]); + print_1_line ("", &curr.file[1].linbuf[i]); } } diff --git a/src/ifdef.c b/src/ifdef.c index 1bc76c8..6192d55 100644 --- a/src/ifdef.c +++ b/src/ifdef.c @@ -49,15 +49,15 @@ static lin next_line1; void print_ifdef_script (struct change *script) { - next_line0 = next_line1 = - files[0].prefix_lines; + next_line0 = next_line1 = - curr.file[0].prefix_lines; print_script (script, find_change, print_ifdef_hunk); - if (next_line0 < files[0].valid_lines - || next_line1 < files[1].valid_lines) + if (next_line0 < curr.file[0].valid_lines + || next_line1 < curr.file[1].valid_lines) { begin_output (); format_ifdef (group_format[UNCHANGED], - next_line0, files[0].valid_lines, - next_line1, files[1].valid_lines); + next_line0, curr.file[0].valid_lines, + next_line1, curr.file[1].valid_lines); } } @@ -99,8 +99,8 @@ format_ifdef (char const *format, lin beg0, lin end0, lin beg1, lin end1) { format_group (outfile, format, '\0', ((struct group const[]) - {{.file = &files[0], .from = beg0, .upto = end0}, - {.file = &files[1], .from = beg1, .upto = end1}})); + {{.file = &curr.file[0], .from = beg0, .upto = end0}, + {.file = &curr.file[1], .from = beg1, .upto = end1}})); } /* Print to file OUT a set of lines according to FORMAT. diff --git a/src/normal.c b/src/normal.c index 1792fd0..7672ecd 100644 --- a/src/normal.c +++ b/src/normal.c @@ -48,9 +48,9 @@ print_normal_hunk (struct change *hunk) /* Print out the line number header for this hunk */ set_color_context (LINE_NUMBER_CONTEXT); - print_number_range (',', &files[0], first0, last0); + print_number_range (',', &curr.file[0], first0, last0); fputc (change_letter[changes], outfile); - print_number_range (',', &files[1], first1, last1); + print_number_range (',', &curr.file[1], first1, last1); set_color_context (RESET_CONTEXT); fputc ('\n', outfile); @@ -60,9 +60,9 @@ print_normal_hunk (struct change *hunk) for (lin i = first0; i <= last0; i++) { set_color_context (DELETE_CONTEXT); - print_1_line_nl ("<", &files[0].linbuf[i], true); + print_1_line_nl ("<", &curr.file[0].linbuf[i], true); set_color_context (RESET_CONTEXT); - if (files[0].linbuf[i + 1][-1] == '\n') + if (curr.file[0].linbuf[i + 1][-1] == '\n') putc ('\n', outfile); } } @@ -76,9 +76,9 @@ print_normal_hunk (struct change *hunk) for (lin i = first1; i <= last1; i++) { set_color_context (ADD_CONTEXT); - print_1_line_nl (">", &files[1].linbuf[i], true); + print_1_line_nl (">", &curr.file[1].linbuf[i], true); set_color_context (RESET_CONTEXT); - if (files[1].linbuf[i + 1][-1] == '\n') + if (curr.file[1].linbuf[i + 1][-1] == '\n') putc ('\n', outfile); } } diff --git a/src/side.c b/src/side.c index ab43ad9..a4b471e 100644 --- a/src/side.c +++ b/src/side.c @@ -37,10 +37,11 @@ print_sdiff_script (struct change *script) { begin_output (); - next0 = next1 = - files[0].prefix_lines; + next0 = next1 = - curr.file[0].prefix_lines; print_script (script, find_change, print_sdiff_hunk); - print_sdiff_common_lines (files[0].valid_lines, files[1].valid_lines); + print_sdiff_common_lines (curr.file[0].valid_lines, + curr.file[1].valid_lines); } /* Tab from column FROM to column TO, where FROM <= TO. Yield TO. */ @@ -275,13 +276,13 @@ print_sdiff_common_lines (lin limit0, lin limit1) if (!left_column) { while (i0 != limit0 && i1 != limit1) - print_1sdiff_line (&files[0].linbuf[i0++], ' ', - &files[1].linbuf[i1++]); + print_1sdiff_line (&curr.file[0].linbuf[i0++], ' ', + &curr.file[1].linbuf[i1++]); while (i1 != limit1) - print_1sdiff_line (0, ')', &files[1].linbuf[i1++]); + print_1sdiff_line (0, ')', &curr.file[1].linbuf[i1++]); } while (i0 != limit0) - print_1sdiff_line (&files[0].linbuf[i0++], '(', 0); + print_1sdiff_line (&curr.file[0].linbuf[i0++], '(', 0); } next0 = limit0; @@ -315,7 +316,8 @@ print_sdiff_hunk (struct change *hunk) { lin i, j; for (i = first0, j = first1; i <= last0 && j <= last1; i++, j++) - print_1sdiff_line (&files[0].linbuf[i], '|', &files[1].linbuf[j]); + print_1sdiff_line (&curr.file[0].linbuf[i], '|', + &curr.file[1].linbuf[j]); changes = (i <= last0 ? OLD : 0) + (j <= last1 ? NEW : 0); next0 = first0 = i; next1 = first1 = j; @@ -326,7 +328,7 @@ print_sdiff_hunk (struct change *hunk) { lin j; for (j = first1; j <= last1; ++j) - print_1sdiff_line (0, '>', &files[1].linbuf[j]); + print_1sdiff_line (0, '>', &curr.file[1].linbuf[j]); next1 = j; } @@ -335,7 +337,7 @@ print_sdiff_hunk (struct change *hunk) { lin i; for (i = first0; i <= last0; ++i) - print_1sdiff_line (&files[0].linbuf[i], '<', 0); + print_1sdiff_line (&curr.file[0].linbuf[i], '<', 0); next0 = i; } } diff --git a/src/util.c b/src/util.c index 30fb531..69b7831 100644 --- a/src/util.c +++ b/src/util.c @@ -989,19 +989,9 @@ begin_output (void) free (name); /* A special header is needed at the beginning of context output. */ - switch (output_style) - { - case OUTPUT_CONTEXT: - print_context_header (files, (char const *const *)names, false); - break; - - case OUTPUT_UNIFIED: - print_context_header (files, (char const *const *)names, true); - break; - - default: - break; - } + if (output_style == OUTPUT_CONTEXT || output_style == OUTPUT_UNIFIED) + print_context_header (curr.file, (char const *const *) names, + output_style == OUTPUT_UNIFIED); if (names[0] != current_name0) free (names[0]); @@ -1370,8 +1360,9 @@ analyze_hunk (struct change *hunk, bool skip_leading_white_space = skip_white_space && IGNORE_SPACE_CHANGE <= ignore_white_space; - char const *const *linbuf0 = files[0].linbuf; /* Help the compiler. */ - char const *const *linbuf1 = files[1].linbuf; + /* Help the compiler. */ + char const *const *linbuf0 = curr.file[0].linbuf; + char const *const *linbuf1 = curr.file[1].linbuf; lin show_from = 0, show_to = 0; diff --git a/tests/stdin b/tests/stdin index 94b7542..0fd9e7e 100755 --- a/tests/stdin +++ b/tests/stdin @@ -5,7 +5,9 @@ fail=0 -cat < exp || fail=1 +echo a > a || framework_failure_ +echo b > b || framework_failure_ +cat <<'EOF' > exp || framework_failure_ --- - +++ b @@ -1 +1 @@ @@ -13,12 +15,17 @@ cat < exp || fail=1 +b EOF -echo a > a -echo b > b - returns_ 1 diff -u - b < a > out 2> err || fail=1 # Remove date and time. -sed -e 's/^\([-+*][-+*][-+*] [^ ]*\) .*/\1/' out > k; mv k out -compare exp out || fail=1 +sed -e 's/^\([-+*][-+*][-+*] [^ ]*\) .*/\1/' out >outk || framework_failure_ +compare exp outk || fail=1 +compare /dev/null err || fail=1 + +mkdir d || framework_failure_ +echo a >d/a || framework_failure_ + +diff -u - a out 2>err || fail=1 +compare /dev/null out || fail=1 +compare /dev/null err || fail=1 Exit $fail