diff: use openat, fstatat when recursive

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.
This commit is contained in:
Paul Eggert 2023-07-18 08:28:10 -07:00
parent 1bb156e927
commit 6bf2c33ea4
14 changed files with 217 additions and 184 deletions

1
.gitignore vendored
View File

@ -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

4
NEWS
View File

@ -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]

View File

@ -45,6 +45,7 @@ exitfail
extensions
extern-inline
fcntl
fdopendir
file-type
filenamecat
flexmember

View File

@ -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 <diffseq.h>
@ -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)
{

View File

@ -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'))

View File

@ -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. */

View File

@ -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;

View File

@ -20,6 +20,7 @@
#include "diff.h"
#include <error.h>
#include <dirname.h>
#include <exclude.h>
#include <filenamecat.h>
#include <setjmp.h>
@ -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;

View File

@ -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]);
}
}

View File

@ -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.

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -5,7 +5,9 @@
fail=0
cat <<EOF > 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 <<EOF > 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 <d >out 2>err || fail=1
compare /dev/null out || fail=1
compare /dev/null err || fail=1
Exit $fail