bootloader: rebase onto upstream 1.0.0, sync firmware-loader version
Bootloader fork rebase:
- Base changed from 0.1.0 pre-patched archive to upstream 1.0.0 tag (c7eeb9f)
- Applied 0001-redbear-local-forks.patch (Cargo.toml crate path redirects)
- Applied fix-uefi-alloc-panic.patch equivalents (4 panic!() -> graceful
error handling in src/main.rs)
- Applied P5-live-preload-cap-1gib.patch (1 GiB cap on live image preload)
- Skipped: P0 GPT partition scan (requires new module + integration),
P1 timeout/default-resolution, P2 live preload guard (subsumed by
panic fixes + cap), P3 live image safe read, P4 large ISO boot,
redox.patch — to be applied in dedicated rebase session.
firmware-loader/Cargo.toml: version 0.1.0 -> 0.3.0 (sync with other
Red Bear custom crates which are at 0.3.0).
fork-upstream-map.toml: bootloader back from PENDING_REBASE to 1.0.0
since the partial rebase matches upstream 1.0.0 content.
fork-upstream-map.toml: base restored to 'main' tracked (was correctly
tracked by build-redbear.sh).
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Automakefile for GNU diffutils programs.
|
||||
|
||||
# Copyright (C) 2001-2002, 2006, 2009-2013, 2015-2017 Free Software Foundation,
|
||||
# Copyright (C) 2001-2002, 2006, 2009-2013, 2015-2025 Free Software Foundation,
|
||||
# Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -26,31 +26,33 @@ AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS)
|
||||
LDADD = \
|
||||
libver.a \
|
||||
../lib/libdiffutils.a \
|
||||
$(CLOCK_TIME_LIB) \
|
||||
$(HARD_LOCALE_LIB) \
|
||||
$(LIBTHREAD) \
|
||||
$(LIBCSTACK) \
|
||||
$(LIBINTL) \
|
||||
$(LIBICONV) \
|
||||
$(LIBSIGSEGV) \
|
||||
$(LIB_CLOCK_GETTIME)
|
||||
$(LIBUNISTRING) \
|
||||
$(MBRTOWC_LIB) \
|
||||
$(LIBC32CONV) \
|
||||
$(SETLOCALE_NULL_LIB)
|
||||
|
||||
diff_LDADD = $(LDADD)
|
||||
cmp_LDADD = $(LDADD)
|
||||
sdiff_LDADD = $(LDADD)
|
||||
sdiff_LDADD = $(LDADD) $(GETRANDOM_LIB)
|
||||
diff3_LDADD = $(LDADD)
|
||||
|
||||
cmp_SOURCES = cmp.c
|
||||
diff3_SOURCES = diff3.c
|
||||
sdiff_SOURCES = sdiff.c
|
||||
cmp_SOURCES = cmp.c system.c
|
||||
diff3_SOURCES = diff3.c system.c
|
||||
sdiff_SOURCES = sdiff.c system.c
|
||||
diff_SOURCES = \
|
||||
analyze.c context.c diff.c dir.c ed.c ifdef.c io.c \
|
||||
normal.c side.c util.c
|
||||
noinst_HEADERS = \
|
||||
die.h \
|
||||
diff.h \
|
||||
system.h
|
||||
normal.c side.c system.c util.c
|
||||
noinst_HEADERS = diff.h system.h
|
||||
|
||||
MOSTLYCLEANFILES = paths.h paths.ht
|
||||
|
||||
cmp.$(OBJEXT) diff3.$(OBJEXT) diff.$(OBJEXT) sdiff.$(OBJEXT): paths.h
|
||||
cmp.$(OBJEXT) diff3.$(OBJEXT) diff.$(OBJEXT) sdiff.$(OBJEXT): paths.h version.h
|
||||
|
||||
gdiff = `echo diff|sed '$(transform)'`
|
||||
BUILT_SOURCES = paths.h
|
||||
@@ -65,6 +67,7 @@ BUILT_SOURCES += version.c
|
||||
version.c: Makefile
|
||||
$(AM_V_GEN)rm -f $@
|
||||
$(AM_V_at)printf '#include <config.h>\n' > $@t
|
||||
$(AM_V_at)printf '#include <version.h>\n' >> $@t
|
||||
$(AM_V_at)printf 'char const *Version = "$(PACKAGE_VERSION)";\n' >> $@t
|
||||
$(AM_V_at)chmod a-w $@t
|
||||
$(AM_V_at)mv $@t $@
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
/* Context-format output routines for GNU DIFF.
|
||||
|
||||
Copyright (C) 1988-1989, 1991-1995, 1998, 2001-2002, 2004, 2006, 2009-2013,
|
||||
2015-2017 Free Software Foundation, Inc.
|
||||
2015-2025 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "diff.h"
|
||||
#include "c-ctype.h"
|
||||
#include <c-ctype.h>
|
||||
#include <stat-time.h>
|
||||
#include <strftime.h>
|
||||
|
||||
static char const *find_function (char const * const *, lin);
|
||||
static char const *find_function (char const *const *, lin);
|
||||
static struct change *find_hunk (struct change *);
|
||||
static void mark_ignorable (struct change *);
|
||||
static void pr_context_hunk (struct change *);
|
||||
@@ -39,40 +39,54 @@ static lin find_function_last_match;
|
||||
|
||||
static void
|
||||
print_context_label (char const *mark,
|
||||
struct file_data *inf,
|
||||
char const *name,
|
||||
char const *label)
|
||||
struct file_data *inf,
|
||||
char const *name,
|
||||
char const *label)
|
||||
{
|
||||
set_color_context (HEADER_CONTEXT);
|
||||
if (label)
|
||||
fprintf (outfile, "%s %s\n", mark, label);
|
||||
fprintf (outfile, "%s %s", mark, label);
|
||||
else
|
||||
{
|
||||
char buf[MAX (INT_STRLEN_BOUND (int) + 32,
|
||||
INT_STRLEN_BOUND (time_t) + 11)];
|
||||
struct tm const *tm = localtime (&inf->stat.st_mtime);
|
||||
int nsec = get_stat_mtime_ns (&inf->stat);
|
||||
if (! (tm && nstrftime (buf, sizeof buf, time_format, tm, 0, nsec)))
|
||||
/* POSIX requires current time for stdin. */
|
||||
struct timespec ts;
|
||||
if (inf->desc == STDIN_FILENO)
|
||||
{
|
||||
verify (TYPE_IS_INTEGER (time_t));
|
||||
if (LONG_MIN <= TYPE_MINIMUM (time_t)
|
||||
&& TYPE_MAXIMUM (time_t) <= LONG_MAX)
|
||||
{
|
||||
long int sec = inf->stat.st_mtime;
|
||||
sprintf (buf, "%ld.%.9d", sec, nsec);
|
||||
}
|
||||
else if (TYPE_MAXIMUM (time_t) <= INTMAX_MAX)
|
||||
{
|
||||
intmax_t sec = inf->stat.st_mtime;
|
||||
sprintf (buf, "%"PRIdMAX".%.9d", sec, nsec);
|
||||
}
|
||||
else
|
||||
{
|
||||
uintmax_t sec = inf->stat.st_mtime;
|
||||
sprintf (buf, "%"PRIuMAX".%.9d", sec, nsec);
|
||||
}
|
||||
static struct timespec now;
|
||||
if (!now.tv_sec)
|
||||
timespec_get (&now, TIME_UTC);
|
||||
ts = now;
|
||||
}
|
||||
else
|
||||
ts = get_stat_mtime (&inf->stat);
|
||||
|
||||
/* Buffer for nstftime output, big enough to handle any
|
||||
timestamp formatted according to time_format.
|
||||
Its size is an upper bound for the format "%Y-%m-%d %H:%M:%S.%N %z",
|
||||
with an int for year and a time_t for time zone hour.
|
||||
The format "%Y-%m-%d %H:%M:%S %z" generates fewer bytes,
|
||||
and although the format "%a %b %e %T %Y" could in theory
|
||||
generate more bytes in practice it never does. */
|
||||
char buf[INT_STRLEN_BOUND (int) + INT_STRLEN_BOUND (time_t)
|
||||
+ sizeof "-%m-%d %H:%M:%S.000000000 +00"];
|
||||
|
||||
struct tm const *tm = localtime (&ts.tv_sec);
|
||||
int nsec = ts.tv_nsec;
|
||||
if (tm && nstrftime (buf, sizeof buf, time_format, tm, localtz, nsec))
|
||||
fprintf (outfile, "%s %s\t%s", mark, name, buf);
|
||||
else if (TYPE_SIGNED (time_t))
|
||||
{
|
||||
intmax_t sec = inf->stat.st_mtime;
|
||||
fprintf (outfile, "%s %s\t%"PRIdMAX".%.9d", mark, name, sec, nsec);
|
||||
}
|
||||
else
|
||||
{
|
||||
uintmax_t sec = inf->stat.st_mtime;
|
||||
fprintf (outfile, "%s %s\t%"PRIuMAX".%.9d", mark, name, sec, nsec);
|
||||
}
|
||||
fprintf (outfile, "%s %s\t%s\n", mark, name, buf);
|
||||
}
|
||||
set_color_context (RESET_CONTEXT);
|
||||
putc ('\n', outfile);
|
||||
}
|
||||
|
||||
/* Print a header for a context diff, with the file names and dates. */
|
||||
@@ -80,7 +94,6 @@ print_context_label (char const *mark,
|
||||
void
|
||||
print_context_header (struct file_data inf[], char const *const *names, bool unidiff)
|
||||
{
|
||||
set_color_context (HEADER_CONTEXT);
|
||||
if (unidiff)
|
||||
{
|
||||
print_context_label ("---", &inf[0], names[0], file_label[0]);
|
||||
@@ -91,7 +104,6 @@ print_context_header (struct file_data inf[], char const *const *names, bool uni
|
||||
print_context_label ("***", &inf[0], names[0], file_label[0]);
|
||||
print_context_label ("---", &inf[1], names[1], file_label[1]);
|
||||
}
|
||||
set_color_context (RESET_CONTEXT);
|
||||
}
|
||||
|
||||
/* Print an edit script in context format. */
|
||||
@@ -102,13 +114,10 @@ print_context_script (struct change *script, bool unidiff)
|
||||
if (ignore_blank_lines || ignore_regexp.fastmap)
|
||||
mark_ignorable (script);
|
||||
else
|
||||
{
|
||||
struct change *e;
|
||||
for (e = script; e; e = e->link)
|
||||
e->ignore = false;
|
||||
}
|
||||
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)
|
||||
@@ -126,7 +135,7 @@ print_context_script (struct change *script, bool unidiff)
|
||||
static void
|
||||
print_context_number_range (struct file_data const *file, lin a, lin b)
|
||||
{
|
||||
printint trans_a, trans_b;
|
||||
lin trans_a, trans_b;
|
||||
translate_range (file, a, b, &trans_a, &trans_b);
|
||||
|
||||
/* We can have B <= A in the case of a range of no lines.
|
||||
@@ -169,38 +178,33 @@ print_context_function (FILE *out, char const *function)
|
||||
static void
|
||||
pr_context_hunk (struct change *hunk)
|
||||
{
|
||||
lin first0, last0, first1, last1, i;
|
||||
char const *prefix;
|
||||
char const *function;
|
||||
FILE *out;
|
||||
|
||||
/* Determine range of line numbers involved in each file. */
|
||||
|
||||
lin first0, last0, first1, last1;
|
||||
enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
|
||||
if (! changes)
|
||||
return;
|
||||
|
||||
/* Include a context's width before and after. */
|
||||
|
||||
i = - files[0].prefix_lines;
|
||||
first0 = MAX (first0 - context, i);
|
||||
first1 = MAX (first1 - context, i);
|
||||
if (last0 < files[0].valid_lines - context)
|
||||
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 < 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. */
|
||||
function = NULL;
|
||||
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 ();
|
||||
out = outfile;
|
||||
FILE *out = outfile;
|
||||
|
||||
fputs ("***************", out);
|
||||
|
||||
@@ -210,7 +214,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);
|
||||
@@ -219,38 +223,36 @@ pr_context_hunk (struct change *hunk)
|
||||
{
|
||||
struct change *next = hunk;
|
||||
|
||||
if (first0 <= last0)
|
||||
set_color_context (DELETE_CONTEXT);
|
||||
for (lin i = first0; i <= last0; i++)
|
||||
{
|
||||
set_color_context (DELETE_CONTEXT);
|
||||
|
||||
for (i = first0; i <= last0; i++)
|
||||
{
|
||||
/* Skip past changes that apply (in file 0)
|
||||
only to lines before line I. */
|
||||
/* Skip past changes that apply (in file 0)
|
||||
only to lines before line I. */
|
||||
|
||||
while (next && next->line0 + next->deleted <= i)
|
||||
next = next->link;
|
||||
while (next && next->line0 + next->deleted <= i)
|
||||
next = next->link;
|
||||
|
||||
/* Compute the marking for line I. */
|
||||
/* Compute the marking for line I. */
|
||||
|
||||
prefix = " ";
|
||||
if (next && next->line0 <= i)
|
||||
char const *prefix = " ";
|
||||
if (next && next->line0 <= i)
|
||||
{
|
||||
/* The change NEXT covers this line.
|
||||
If lines were inserted here in file 1, this is "changed".
|
||||
Otherwise it is "deleted". */
|
||||
prefix = (next->inserted > 0 ? "!" : "-");
|
||||
}
|
||||
print_1_line_nl (prefix, &files[0].linbuf[i], true);
|
||||
if (i == last0)
|
||||
set_color_context (RESET_CONTEXT);
|
||||
if (files[0].linbuf[i + 1][-1] == '\n')
|
||||
print_1_line_nl (prefix, &curr.file[0].linbuf[i], true);
|
||||
set_color_context (RESET_CONTEXT);
|
||||
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);
|
||||
@@ -259,33 +261,31 @@ pr_context_hunk (struct change *hunk)
|
||||
{
|
||||
struct change *next = hunk;
|
||||
|
||||
if (first1 <= last1)
|
||||
set_color_context (ADD_CONTEXT);
|
||||
for (lin i = first1; i <= last1; i++)
|
||||
{
|
||||
set_color_context (ADD_CONTEXT);
|
||||
|
||||
for (i = first1; i <= last1; i++)
|
||||
{
|
||||
/* Skip past changes that apply (in file 1)
|
||||
only to lines before line I. */
|
||||
/* Skip past changes that apply (in file 1)
|
||||
only to lines before line I. */
|
||||
|
||||
while (next && next->line1 + next->inserted <= i)
|
||||
next = next->link;
|
||||
while (next && next->line1 + next->inserted <= i)
|
||||
next = next->link;
|
||||
|
||||
/* Compute the marking for line I. */
|
||||
/* Compute the marking for line I. */
|
||||
|
||||
prefix = " ";
|
||||
if (next && next->line1 <= i)
|
||||
char const *prefix = " ";
|
||||
if (next && next->line1 <= i)
|
||||
{
|
||||
/* The change NEXT covers this line.
|
||||
If lines were deleted here in file 0, this is "changed".
|
||||
Otherwise it is "inserted". */
|
||||
prefix = (next->deleted > 0 ? "!" : "+");
|
||||
}
|
||||
print_1_line_nl (prefix, &files[1].linbuf[i], true);
|
||||
if (i == last1)
|
||||
set_color_context (RESET_CONTEXT);
|
||||
if (files[1].linbuf[i + 1][-1] == '\n')
|
||||
print_1_line_nl (prefix, &curr.file[1].linbuf[i], true);
|
||||
set_color_context (RESET_CONTEXT);
|
||||
if (curr.file[1].linbuf[i + 1][-1] == '\n')
|
||||
putc ('\n', out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,7 +299,7 @@ pr_context_hunk (struct change *hunk)
|
||||
static void
|
||||
print_unidiff_number_range (struct file_data const *file, lin a, lin b)
|
||||
{
|
||||
printint trans_a, trans_b;
|
||||
lin trans_a, trans_b;
|
||||
translate_range (file, a, b, &trans_a, &trans_b);
|
||||
|
||||
/* We can have B < A in the case of a range of no lines.
|
||||
@@ -322,44 +322,38 @@ print_unidiff_number_range (struct file_data const *file, lin a, lin b)
|
||||
static void
|
||||
pr_unidiff_hunk (struct change *hunk)
|
||||
{
|
||||
lin first0, last0, first1, last1;
|
||||
lin i, j, k;
|
||||
struct change *next;
|
||||
char const *function;
|
||||
FILE *out;
|
||||
|
||||
/* Determine range of line numbers involved in each file. */
|
||||
|
||||
lin first0, last0, first1, last1;
|
||||
if (! analyze_hunk (hunk, &first0, &last0, &first1, &last1))
|
||||
return;
|
||||
|
||||
/* Include a context's width before and after. */
|
||||
|
||||
i = - files[0].prefix_lines;
|
||||
first0 = MAX (first0 - context, i);
|
||||
first1 = MAX (first1 - context, i);
|
||||
if (last0 < files[0].valid_lines - context)
|
||||
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 < 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. */
|
||||
function = NULL;
|
||||
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 ();
|
||||
out = outfile;
|
||||
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);
|
||||
|
||||
@@ -368,9 +362,9 @@ pr_unidiff_hunk (struct change *hunk)
|
||||
|
||||
putc ('\n', out);
|
||||
|
||||
next = hunk;
|
||||
i = first0;
|
||||
j = first1;
|
||||
struct change *next = hunk;
|
||||
lin i = first0;
|
||||
lin j = first1;
|
||||
|
||||
while (i <= last0 || j <= last1)
|
||||
{
|
||||
@@ -378,61 +372,57 @@ pr_unidiff_hunk (struct change *hunk)
|
||||
/* If the line isn't a difference, output the context from file 0. */
|
||||
|
||||
if (!next || i < next->line0)
|
||||
{
|
||||
char const *const *line = &files[0].linbuf[i++];
|
||||
if (! (suppress_blank_empty && **line == '\n'))
|
||||
putc (initial_tab ? '\t' : ' ', out);
|
||||
print_1_line (NULL, line);
|
||||
j++;
|
||||
}
|
||||
{
|
||||
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);
|
||||
j++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* For each difference, first output the deleted part. */
|
||||
{
|
||||
/* For each difference, first output the deleted part. */
|
||||
|
||||
k = next->deleted;
|
||||
if (k)
|
||||
set_color_context (DELETE_CONTEXT);
|
||||
|
||||
while (k--)
|
||||
{
|
||||
char const * const *line = &files[0].linbuf[i++];
|
||||
putc ('-', out);
|
||||
if (initial_tab && ! (suppress_blank_empty && **line == '\n'))
|
||||
putc ('\t', out);
|
||||
print_1_line_nl (NULL, line, true);
|
||||
|
||||
if (!k)
|
||||
set_color_context (RESET_CONTEXT);
|
||||
|
||||
if (line[1][-1] == '\n')
|
||||
putc ('\n', out);
|
||||
}
|
||||
|
||||
/* Then output the inserted part. */
|
||||
|
||||
k = next->inserted;
|
||||
if (k)
|
||||
set_color_context (ADD_CONTEXT);
|
||||
lin k = next->deleted;
|
||||
|
||||
while (k--)
|
||||
{
|
||||
char const * const *line = &files[1].linbuf[j++];
|
||||
putc ('+', out);
|
||||
if (initial_tab && ! (suppress_blank_empty && **line == '\n'))
|
||||
putc ('\t', out);
|
||||
print_1_line_nl (NULL, line, true);
|
||||
{
|
||||
char const *const *line = &curr.file[0].linbuf[i++];
|
||||
set_color_context (DELETE_CONTEXT);
|
||||
putc ('-', out);
|
||||
if (initial_tab && ! (suppress_blank_empty && **line == '\n'))
|
||||
putc ('\t', out);
|
||||
print_1_line_nl (nullptr, line, true);
|
||||
|
||||
if (!k)
|
||||
set_color_context (RESET_CONTEXT);
|
||||
set_color_context (RESET_CONTEXT);
|
||||
|
||||
if (line[1][-1] == '\n')
|
||||
putc ('\n', out);
|
||||
}
|
||||
}
|
||||
|
||||
/* We're done with this hunk, so on to the next! */
|
||||
/* Then output the inserted part. */
|
||||
|
||||
next = next->link;
|
||||
}
|
||||
k = next->inserted;
|
||||
|
||||
while (k--)
|
||||
{
|
||||
char const *const *line = &curr.file[1].linbuf[j++];
|
||||
set_color_context (ADD_CONTEXT);
|
||||
putc ('+', out);
|
||||
if (initial_tab && ! (suppress_blank_empty && **line == '\n'))
|
||||
putc ('\t', out);
|
||||
print_1_line_nl (nullptr, line, true);
|
||||
|
||||
set_color_context (RESET_CONTEXT);
|
||||
|
||||
if (line[1][-1] == '\n')
|
||||
putc ('\n', out);
|
||||
}
|
||||
|
||||
/* We're done with this hunk, so on to the next! */
|
||||
|
||||
next = next->link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,39 +430,33 @@ pr_unidiff_hunk (struct change *hunk)
|
||||
2*CONTEXT unchanged lines appear, and return a pointer
|
||||
to the 'struct change' for the last change before those lines. */
|
||||
|
||||
static struct change * _GL_ATTRIBUTE_PURE
|
||||
find_hunk (struct change *start)
|
||||
static struct change * ATTRIBUTE_PURE
|
||||
find_hunk (struct change *script)
|
||||
{
|
||||
struct change *prev;
|
||||
lin top0, top1;
|
||||
lin thresh;
|
||||
|
||||
/* Threshold distance is CONTEXT if the second change is ignorable,
|
||||
2 * CONTEXT + 1 otherwise. Integer overflow can't happen, due
|
||||
to CONTEXT_LIM. */
|
||||
min (2 * CONTEXT + 1, LIN_MAX) otherwise. */
|
||||
lin ignorable_threshold = context;
|
||||
lin non_ignorable_threshold = 2 * context + 1;
|
||||
lin non_ignorable_threshold = (ckd_mul (&non_ignorable_threshold, context, 2)
|
||||
? LIN_MAX
|
||||
: non_ignorable_threshold + 1);
|
||||
|
||||
do
|
||||
for (struct change *next; ; script = next)
|
||||
{
|
||||
next = script->link;
|
||||
/* Compute number of first line in each file beyond this changed. */
|
||||
top0 = start->line0 + start->deleted;
|
||||
top1 = start->line1 + start->inserted;
|
||||
prev = start;
|
||||
start = start->link;
|
||||
thresh = (start && start->ignore
|
||||
? ignorable_threshold
|
||||
: non_ignorable_threshold);
|
||||
/* It is not supposed to matter which file we check in the end-test.
|
||||
If it would matter, crash. */
|
||||
if (start && start->line0 - top0 != start->line1 - top1)
|
||||
abort ();
|
||||
} while (start
|
||||
/* Keep going if less than THRESH lines
|
||||
elapse before the affected line. */
|
||||
&& start->line0 - top0 < thresh);
|
||||
lin top0 = script->line0 + script->deleted;
|
||||
lin top1 = script->line1 + script->inserted;
|
||||
lin thresh = (next && next->ignore
|
||||
? ignorable_threshold
|
||||
: non_ignorable_threshold);
|
||||
/* It is not supposed to matter which file we check in the end-test. */
|
||||
dassert (!next || next->line0 - top0 == next->line1 - top1);
|
||||
|
||||
return prev;
|
||||
/* Keep going if less than THRESH lines elapse
|
||||
before the affected line. */
|
||||
if (!next || thresh <= next->line0 - top0)
|
||||
return script;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the 'ignore' flag properly in each change in SCRIPT.
|
||||
@@ -485,14 +469,14 @@ mark_ignorable (struct change *script)
|
||||
while (script)
|
||||
{
|
||||
struct change *next = script->link;
|
||||
lin first0, last0, first1, last1;
|
||||
|
||||
/* Turn this change into a hunk: detach it from the others. */
|
||||
script->link = NULL;
|
||||
script->link = nullptr;
|
||||
|
||||
/* Determine whether this change is ignorable. */
|
||||
lin first0, last0, first1, last1;
|
||||
script->ignore = ! analyze_hunk (script,
|
||||
&first0, &last0, &first1, &last1);
|
||||
&first0, &last0, &first1, &last1);
|
||||
|
||||
/* Reconnect the chain as before. */
|
||||
script->link = next;
|
||||
@@ -504,10 +488,10 @@ mark_ignorable (struct change *script)
|
||||
|
||||
/* Find the last function-header line in LINBUF prior to line number LINENUM.
|
||||
This is a line containing a match for the regexp in 'function_regexp'.
|
||||
Return the address of the text, or NULL if no function-header is found. */
|
||||
Return the address of the text, or null if no function-header is found. */
|
||||
|
||||
static char const *
|
||||
find_function (char const * const *linbuf, lin linenum)
|
||||
find_function (char const *const *linbuf, lin linenum)
|
||||
{
|
||||
lin i = linenum;
|
||||
lin last = find_function_last_search;
|
||||
@@ -517,21 +501,22 @@ find_function (char const * const *linbuf, lin linenum)
|
||||
{
|
||||
/* See if this line is what we want. */
|
||||
char const *line = linbuf[i];
|
||||
size_t linelen = linbuf[i + 1] - line - 1;
|
||||
idx_t linelen = linbuf[i + 1] - line - 1;
|
||||
|
||||
/* FIXME: re_search's size args should be size_t, not int. */
|
||||
int len = MIN (linelen, INT_MAX);
|
||||
/* This line is for documentation; in practice it's equivalent
|
||||
to LEN = LINELEN and no machine code is generated. */
|
||||
regoff_t len = MIN (linelen, TYPE_MAXIMUM (regoff_t));
|
||||
|
||||
if (0 <= re_search (&function_regexp, line, len, 0, len, NULL))
|
||||
{
|
||||
find_function_last_match = i;
|
||||
return line;
|
||||
}
|
||||
if (0 <= re_search (&function_regexp, line, len, 0, len, nullptr))
|
||||
{
|
||||
find_function_last_match = i;
|
||||
return line;
|
||||
}
|
||||
}
|
||||
/* If we search back to where we started searching the previous time,
|
||||
find the line we found last time. */
|
||||
if (find_function_last_match != LIN_MAX)
|
||||
return linbuf[find_function_last_match];
|
||||
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/* Report an error and exit.
|
||||
Copyright 2016-2017 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
|
||||
02110-1301, USA. */
|
||||
|
||||
#ifndef DIE_H
|
||||
# define DIE_H
|
||||
|
||||
# include <error.h>
|
||||
# include <stdbool.h>
|
||||
# include <verify.h>
|
||||
|
||||
/* Like 'error (STATUS, ...)', except STATUS must be a nonzero constant.
|
||||
This may pacify the compiler or help it generate better code. */
|
||||
# define die(status, ...) \
|
||||
verify_expr (status, (error (status, __VA_ARGS__), assume (false)))
|
||||
|
||||
#endif /* DIE_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
/* Shared definitions for GNU DIFF
|
||||
|
||||
Copyright (C) 1988-1989, 1991-1995, 1998, 2001-2002, 2004, 2009-2013,
|
||||
2015-2017 Free Software Foundation, Inc.
|
||||
2015-2025 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
|
||||
@@ -23,6 +23,12 @@
|
||||
#include <stdio.h>
|
||||
#include <unlocked-io.h>
|
||||
|
||||
_GL_INLINE_HEADER_BEGIN
|
||||
|
||||
#ifndef DIFF_INLINE
|
||||
# define DIFF_INLINE _GL_INLINE
|
||||
#endif
|
||||
|
||||
/* What kind of changes a hunk contains. */
|
||||
enum changes
|
||||
{
|
||||
@@ -54,12 +60,6 @@ enum colors_style
|
||||
|
||||
/* Variables for command line options */
|
||||
|
||||
#ifndef GDIFF_MAIN
|
||||
# define XTERN extern
|
||||
#else
|
||||
# define XTERN
|
||||
#endif
|
||||
|
||||
enum output_style
|
||||
{
|
||||
/* No output style specified. */
|
||||
@@ -91,27 +91,31 @@ enum output_style
|
||||
};
|
||||
|
||||
/* True for output styles that are robust,
|
||||
i.e. can handle a file that ends in a non-newline. */
|
||||
#define ROBUST_OUTPUT_STYLE(S) ((S) != OUTPUT_ED && (S) != OUTPUT_FORWARD_ED)
|
||||
i.e. can handle a file that ends in a non-newline.
|
||||
This is indented unusually to pacify 'make syntax-check'. */
|
||||
DIFF_INLINE bool robust_output_style (enum output_style s)
|
||||
{
|
||||
return s != OUTPUT_ED && s != OUTPUT_FORWARD_ED;
|
||||
}
|
||||
|
||||
XTERN enum output_style output_style;
|
||||
extern enum output_style output_style;
|
||||
|
||||
/* Define the current color context used to print a line. */
|
||||
XTERN enum colors_style colors_style;
|
||||
extern enum colors_style colors_style;
|
||||
|
||||
/* Nonzero if output cannot be generated for identical files. */
|
||||
XTERN bool no_diff_means_no_output;
|
||||
extern bool no_diff_means_no_output;
|
||||
|
||||
/* Number of lines of context to show in each set of diffs.
|
||||
This is zero when context is not to be shown. */
|
||||
XTERN lin context;
|
||||
extern lin context;
|
||||
|
||||
/* Consider all files as text files (-a).
|
||||
Don't interpret codes over 0177 as implying a "binary file". */
|
||||
XTERN bool text;
|
||||
extern bool text;
|
||||
|
||||
/* Number of lines to keep in identical prefix and suffix. */
|
||||
XTERN lin horizon_lines;
|
||||
extern lin horizon_lines;
|
||||
|
||||
/* The significance of white space during comparisons. */
|
||||
enum DIFF_white_space
|
||||
@@ -136,100 +140,107 @@ enum DIFF_white_space
|
||||
/* Ignore all horizontal white space (-w). */
|
||||
IGNORE_ALL_SPACE
|
||||
};
|
||||
XTERN enum DIFF_white_space ignore_white_space;
|
||||
extern enum DIFF_white_space ignore_white_space;
|
||||
|
||||
/* Ignore changes that affect only blank lines (-B). */
|
||||
XTERN bool ignore_blank_lines;
|
||||
extern bool ignore_blank_lines;
|
||||
|
||||
/* Files can be compared byte-by-byte, as if they were binary.
|
||||
This depends on various options. */
|
||||
XTERN bool files_can_be_treated_as_binary;
|
||||
extern bool files_can_be_treated_as_binary;
|
||||
|
||||
/* Ignore differences in case of letters (-i). */
|
||||
XTERN bool ignore_case;
|
||||
extern bool ignore_case;
|
||||
|
||||
/* Ignore differences in case of letters in file names. */
|
||||
XTERN bool ignore_file_name_case;
|
||||
extern bool ignore_file_name_case;
|
||||
|
||||
/* Act on symbolic links themselves rather than on their target
|
||||
(--no-dereference). */
|
||||
XTERN bool no_dereference_symlinks;
|
||||
extern bool no_dereference_symlinks;
|
||||
|
||||
/* Local timezone for 'c' output headers, if needed. */
|
||||
#if HAVE_TM_GMTOFF
|
||||
# define localtz 0 /* Placeholder since localtz is never needed. */
|
||||
#else
|
||||
extern timezone_t localtz;
|
||||
#endif
|
||||
|
||||
/* File labels for '-c' output headers (--label). */
|
||||
XTERN char *file_label[2];
|
||||
extern char *file_label[2];
|
||||
|
||||
/* Regexp to identify function-header lines (-F). */
|
||||
XTERN struct re_pattern_buffer function_regexp;
|
||||
extern struct re_pattern_buffer function_regexp;
|
||||
|
||||
/* Ignore changes that affect only lines matching this regexp (-I). */
|
||||
XTERN struct re_pattern_buffer ignore_regexp;
|
||||
extern struct re_pattern_buffer ignore_regexp;
|
||||
|
||||
/* Say only whether files differ, not how (-q). */
|
||||
XTERN bool brief;
|
||||
extern bool brief;
|
||||
|
||||
/* Expand tabs in the output so the text lines up properly
|
||||
despite the characters added to the front of each line (-t). */
|
||||
XTERN bool expand_tabs;
|
||||
extern bool expand_tabs;
|
||||
|
||||
/* Number of columns between tab stops. */
|
||||
XTERN size_t tabsize;
|
||||
extern intmax_t tabsize;
|
||||
|
||||
/* Use a tab in the output, rather than a space, before the text of an
|
||||
input line, so as to keep the proper alignment in the input line
|
||||
without changing the characters in it (-T). */
|
||||
XTERN bool initial_tab;
|
||||
extern bool initial_tab;
|
||||
|
||||
/* Do not output an initial space or tab before the text of an empty line. */
|
||||
XTERN bool suppress_blank_empty;
|
||||
extern bool suppress_blank_empty;
|
||||
|
||||
/* Remove trailing carriage returns from input. */
|
||||
XTERN bool strip_trailing_cr;
|
||||
extern bool strip_trailing_cr;
|
||||
|
||||
/* In directory comparison, specify file to start with (-S).
|
||||
This is used for resuming an aborted comparison.
|
||||
All file names less than this name are ignored. */
|
||||
XTERN char const *starting_file;
|
||||
extern char const *starting_file;
|
||||
|
||||
/* Pipe each file's output through pr (-l). */
|
||||
XTERN bool paginate;
|
||||
extern bool paginate;
|
||||
|
||||
/* Line group formats for unchanged, old, new, and changed groups. */
|
||||
XTERN char const *group_format[CHANGED + 1];
|
||||
extern char const *group_format[CHANGED + 1];
|
||||
|
||||
/* Line formats for unchanged, old, and new lines. */
|
||||
XTERN char const *line_format[NEW + 1];
|
||||
extern char const *line_format[NEW + 1];
|
||||
|
||||
/* If using OUTPUT_SDIFF print extra information to help the sdiff filter. */
|
||||
XTERN bool sdiff_merge_assist;
|
||||
extern bool sdiff_merge_assist;
|
||||
|
||||
/* Tell OUTPUT_SDIFF to show only the left version of common lines. */
|
||||
XTERN bool left_column;
|
||||
extern bool left_column;
|
||||
|
||||
/* Tell OUTPUT_SDIFF to not show common lines. */
|
||||
XTERN bool suppress_common_lines;
|
||||
extern bool suppress_common_lines;
|
||||
|
||||
/* The half line width and column 2 offset for OUTPUT_SDIFF. */
|
||||
XTERN size_t sdiff_half_width;
|
||||
XTERN size_t sdiff_column2_offset;
|
||||
extern intmax_t sdiff_half_width;
|
||||
extern intmax_t sdiff_column2_offset;
|
||||
|
||||
/* String containing all the command options diff received,
|
||||
with spaces between and at the beginning but none at the end.
|
||||
If there were no options given, this string is empty. */
|
||||
XTERN char *switch_string;
|
||||
extern char *switch_string;
|
||||
|
||||
/* Use heuristics for better speed with large files with a small
|
||||
density of changes. */
|
||||
XTERN bool speed_large_files;
|
||||
extern bool speed_large_files;
|
||||
|
||||
/* Patterns that match file names to be excluded. */
|
||||
XTERN struct exclude *excluded;
|
||||
extern struct exclude *excluded;
|
||||
|
||||
/* Don't discard lines. This makes things slower (sometimes much
|
||||
slower) but will find a guaranteed minimal set of changes. */
|
||||
XTERN bool minimal;
|
||||
extern bool minimal;
|
||||
|
||||
/* The strftime format to use for time strings. */
|
||||
XTERN char const *time_format;
|
||||
extern char const *time_format;
|
||||
|
||||
/* The result of comparison is an "edit script": a chain of 'struct change'.
|
||||
Each 'struct change' represents one place where some lines are deleted
|
||||
@@ -254,30 +265,57 @@ struct change
|
||||
|
||||
/* Structures that describe the input files. */
|
||||
|
||||
/* Directory entry types. Like dirent DT_* macros, but portable and
|
||||
safe to use as 'char'. Use the same values as GNU/Linux, as this
|
||||
may help compilers on that platform. */
|
||||
enum detype
|
||||
{
|
||||
DE_UNKNOWN,
|
||||
DE_FIFO,
|
||||
DE_CHR,
|
||||
DE_DIR = 4,
|
||||
DE_BLK = 6,
|
||||
DE_REG = 8,
|
||||
DE_LNK = 10,
|
||||
DE_SOCK = 12,
|
||||
DE_WHT = 14,
|
||||
|
||||
/* This one is not in GNU/Linux; it means the directory entry
|
||||
type has been determined but is none of the above. */
|
||||
DE_OTHER
|
||||
};
|
||||
|
||||
/* Data on one input file being compared. */
|
||||
|
||||
struct file_data {
|
||||
int desc; /* File descriptor */
|
||||
int openerr; /* openat errno, or 0 */
|
||||
int err; /* openat or fstatat or fstat errno, or 0 */
|
||||
char const *name; /* File name */
|
||||
char const *filetype; /* file type as untranslated string */
|
||||
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;
|
||||
|
||||
/* Allocated size of buffer, in bytes. Always a multiple of
|
||||
sizeof *buffer. */
|
||||
size_t bufsize;
|
||||
idx_t bufsize;
|
||||
|
||||
/* Number of valid bytes now in the buffer. */
|
||||
size_t buffered;
|
||||
idx_t buffered;
|
||||
|
||||
/* Array of pointers to lines in the file. */
|
||||
char const **linbuf;
|
||||
|
||||
/* linbuf_base <= buffered_lines <= valid_lines <= alloc_lines.
|
||||
linebuf[linbuf_base ... buffered_lines - 1] are possibly differing.
|
||||
linebuf[linbuf_base ... valid_lines - 1] contain valid data.
|
||||
linebuf[linbuf_base ... alloc_lines - 1] are allocated. */
|
||||
/* linbuf_base <= 0 <= buffered_lines <= valid_lines <= alloc_lines.
|
||||
linbuf[0 ... buffered_lines - 1] are possibly differing.
|
||||
linbuf[linbuf_base ... valid_lines - 1] contain valid data.
|
||||
linbuf[linbuf_base ... alloc_lines - 1] are allocated. */
|
||||
lin linbuf_base, buffered_lines, valid_lines, alloc_lines;
|
||||
|
||||
/* Pointer to end of prefix of this file to ignore when hashing. */
|
||||
@@ -307,9 +345,9 @@ struct file_data {
|
||||
lin nondiscarded_lines;
|
||||
|
||||
/* Vector, indexed by real origin-0 line number,
|
||||
containing 1 for a line that is an insertion or a deletion.
|
||||
containing true for a line that is an insertion or a deletion.
|
||||
The results of comparison are stored here. */
|
||||
char *changed;
|
||||
bool *changed;
|
||||
|
||||
/* 1 if file ends in a line with no final newline. */
|
||||
bool missing_newline;
|
||||
@@ -322,25 +360,38 @@ struct file_data {
|
||||
lin equiv_max;
|
||||
};
|
||||
|
||||
/* The file buffer, considered as an array of bytes rather than
|
||||
as an array of words. */
|
||||
#define FILE_BUFFER(f) ((char *) (f)->buffer)
|
||||
/* struct file_data.desc markers.
|
||||
A top level parent directory desc can be AT_FDCWD;
|
||||
it is OK if AT_FDCWD is one of these other values. */
|
||||
enum { OPEN_FAILED = -1 }; /* open was attempted but failed */
|
||||
enum { NONEXISTENT = -2 }; /* nonexistent file */
|
||||
enum { UNOPENED = -3 }; /* unopened file (e.g., file type mismatch) */
|
||||
|
||||
/* Data on two input files being compared. */
|
||||
|
||||
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];
|
||||
extern 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'. */
|
||||
|
||||
extern struct comparison noparent;
|
||||
|
||||
/* Stdio stream to output diffs to. */
|
||||
|
||||
XTERN FILE *outfile;
|
||||
extern FILE *outfile;
|
||||
|
||||
/* Declare various functions. */
|
||||
|
||||
@@ -348,14 +399,20 @@ XTERN FILE *outfile;
|
||||
extern int diff_2_files (struct comparison *);
|
||||
|
||||
/* context.c */
|
||||
extern void print_context_header (struct file_data[], char const * const *, bool);
|
||||
extern void print_context_header (struct file_data[],
|
||||
char const *const *, bool);
|
||||
extern void print_context_script (struct change *, bool);
|
||||
|
||||
/* diff.c */
|
||||
extern int compare_files (struct comparison const *, enum detype const[2],
|
||||
char const *, char const *);
|
||||
|
||||
/* dir.c */
|
||||
extern int diff_dirs (struct comparison const *,
|
||||
int (*) (struct comparison const *,
|
||||
char const *, char const *));
|
||||
extern char *find_dir_file_pathname (char const *, char const *);
|
||||
extern int diff_dirs (struct comparison *);
|
||||
extern char *find_dir_file_pathname (struct file_data *, char const *,
|
||||
enum detype *)
|
||||
ATTRIBUTE_MALLOC ATTRIBUTE_DEALLOC_FREE
|
||||
ATTRIBUTE_RETURNS_NONNULL;
|
||||
|
||||
/* ed.c */
|
||||
extern void print_ed_script (struct change *);
|
||||
@@ -365,7 +422,7 @@ extern void pr_forward_ed_script (struct change *);
|
||||
extern void print_ifdef_script (struct change *);
|
||||
|
||||
/* io.c */
|
||||
extern void file_block_read (struct file_data *, size_t);
|
||||
extern void file_block_read (struct file_data *, idx_t);
|
||||
extern bool read_files (struct file_data[], bool);
|
||||
|
||||
/* normal.c */
|
||||
@@ -380,33 +437,28 @@ extern void print_sdiff_script (struct change *);
|
||||
/* util.c */
|
||||
extern char const change_letter[4];
|
||||
extern char const pr_program[];
|
||||
extern char *concat (char const *, char const *, char const *);
|
||||
extern bool lines_differ (char const *, char const *) _GL_ATTRIBUTE_PURE;
|
||||
extern lin translate_line_number (struct file_data const *, lin);
|
||||
extern struct change *find_change (struct change *);
|
||||
extern struct change *find_reverse_change (struct change *);
|
||||
extern void *zalloc (size_t);
|
||||
extern lin translate_line_number (struct file_data const *, lin)
|
||||
ATTRIBUTE_PURE;
|
||||
extern struct change *find_change (struct change *) ATTRIBUTE_CONST;
|
||||
extern enum changes analyze_hunk (struct change *, lin *, lin *, lin *, lin *);
|
||||
extern void begin_output (void);
|
||||
extern void cleanup_signal_handlers (void);
|
||||
extern void debug_script (struct change *);
|
||||
extern void fatal (char const *) __attribute__((noreturn));
|
||||
extern _Noreturn void fatal (char const *);
|
||||
extern void finish_output (void);
|
||||
extern void message (char const *, char const *, char const *);
|
||||
extern void message5 (char const *, char const *, char const *,
|
||||
char const *, char const *);
|
||||
extern void message (char const *, ...) ATTRIBUTE_FORMAT ((printf, 1, 2));
|
||||
extern void output_1_line (char const *, char const *, char const *,
|
||||
char const *);
|
||||
extern void perror_with_name (char const *);
|
||||
extern void pfatal_with_name (char const *) __attribute__((noreturn));
|
||||
extern void print_1_line (char const *, char const * const *);
|
||||
extern void print_1_line_nl (char const *, char const * const *, bool);
|
||||
extern _Noreturn void pfatal_with_name (char const *);
|
||||
extern void print_1_line (char const *, char const *const *);
|
||||
extern void print_1_line_nl (char const *, char const *const *, bool);
|
||||
extern void print_message_queue (void);
|
||||
extern void print_number_range (char, struct file_data *, lin, lin);
|
||||
extern void print_script (struct change *, struct change * (*) (struct change *),
|
||||
void (*) (struct change *));
|
||||
extern void setup_output (char const *, char const *, bool);
|
||||
extern void translate_range (struct file_data const *, lin, lin,
|
||||
printint *, printint *);
|
||||
extern void translate_range (struct file_data const *, lin, lin, lin *, lin *);
|
||||
|
||||
enum color_context
|
||||
{
|
||||
@@ -417,7 +469,9 @@ enum color_context
|
||||
LINE_NUMBER_CONTEXT,
|
||||
};
|
||||
|
||||
XTERN bool presume_output_tty;
|
||||
extern bool presume_output_tty;
|
||||
|
||||
extern void set_color_context (enum color_context color_context);
|
||||
extern void set_color_palette (char const *palette);
|
||||
extern void set_color_palette (char *palette);
|
||||
|
||||
_GL_INLINE_HEADER_END
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
/* Read, sort and compare two directories. Used for GNU DIFF.
|
||||
|
||||
Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006-2007,
|
||||
2009-2013, 2015-2017 Free Software Foundation, Inc.
|
||||
2009-2013, 2015-2025 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
|
||||
@@ -19,20 +19,28 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "diff.h"
|
||||
|
||||
#include <diagnose.h>
|
||||
#include <dirname.h>
|
||||
#include <error.h>
|
||||
#include <exclude.h>
|
||||
#include <filenamecat.h>
|
||||
#include <mcel.h>
|
||||
#include <quote.h>
|
||||
#include <setjmp.h>
|
||||
#include <xalloc.h>
|
||||
|
||||
/* Read the directory named by DIR and store into DIRDATA a sorted vector
|
||||
of filenames for its contents. DIR->desc == -1 means this directory is
|
||||
known to be nonexistent, so set DIRDATA to an empty vector.
|
||||
Return -1 (setting errno) if error, 0 otherwise. */
|
||||
#ifndef HAVE_STRUCT_DIRENT_D_TYPE
|
||||
# define HAVE_STRUCT_DIRENT_D_TYPE false
|
||||
#endif
|
||||
|
||||
/* A sorted vector of file names obtained by reading a directory.
|
||||
If HAVE_STRUCT_DIRENT_D_TYPE, each name is preceded by a byte
|
||||
giving the file type as an enum detype value. */
|
||||
|
||||
struct dirdata
|
||||
{
|
||||
size_t nnames; /* Number of names. */
|
||||
idx_t nnames; /* Number of names. */
|
||||
char const **names; /* Sorted names of files in dir, followed by 0. */
|
||||
char *data; /* Allocated storage for file names. */
|
||||
};
|
||||
@@ -44,98 +52,132 @@ static bool locale_specific_sorting;
|
||||
/* Where to go if locale-specific sorting fails. */
|
||||
static jmp_buf failed_locale_specific_sorting;
|
||||
|
||||
static int compare_names (char const *, char const *);
|
||||
static bool dir_loop (struct comparison const *, int);
|
||||
|
||||
|
||||
/* Read a directory and get its vector of names. */
|
||||
/* 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.
|
||||
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.
|
||||
If STARTFILE, ignore directory entries less than STARTFILE, and if
|
||||
STARTFILE_ONLY, also ignore directory entries greater than STARTFILE.
|
||||
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,
|
||||
char const *startfile, bool startfile_only)
|
||||
{
|
||||
register struct dirent *next;
|
||||
register size_t i;
|
||||
|
||||
/* Address of block containing the files that are described. */
|
||||
char const **names;
|
||||
|
||||
/* Number of files in directory. */
|
||||
size_t nnames;
|
||||
idx_t nnames = 0;
|
||||
|
||||
/* Allocated and used storage for file name data. */
|
||||
char *data;
|
||||
size_t data_alloc, data_used;
|
||||
|
||||
dirdata->names = 0;
|
||||
dirdata->data = 0;
|
||||
nnames = 0;
|
||||
data = 0;
|
||||
dirdata->names = nullptr;
|
||||
dirdata->data = nullptr;
|
||||
|
||||
if (dir->desc != -1)
|
||||
if (dir->desc != NONEXISTENT)
|
||||
{
|
||||
/* Open the directory and check for errors. */
|
||||
register 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_CLOEXEC | O_DIRECTORY
|
||||
| (no_dereference_symlinks ? O_NOFOLLOW : 0)));
|
||||
if (dirfd < 0)
|
||||
return false;
|
||||
dir->desc = dirfd;
|
||||
}
|
||||
DIR *reading = fdopendir (dirfd);
|
||||
if (!reading)
|
||||
return false;
|
||||
return false;
|
||||
dir->dirstream = reading;
|
||||
|
||||
/* Initialize the table of filenames. */
|
||||
|
||||
data_alloc = 512;
|
||||
data_used = 0;
|
||||
dirdata->data = data = xmalloc (data_alloc);
|
||||
idx_t data_alloc = 512;
|
||||
idx_t data_used = 0;
|
||||
dirdata->data = data = ximalloc (data_alloc);
|
||||
|
||||
/* Read the directory entries, and insert the subfiles
|
||||
into the 'data' table. */
|
||||
into the 'data' table. */
|
||||
|
||||
while ((errno = 0, (next = readdir (reading)) != 0))
|
||||
{
|
||||
char *d_name = next->d_name;
|
||||
size_t d_size = _D_EXACT_NAMLEN (next) + 1;
|
||||
while (true)
|
||||
{
|
||||
errno = 0;
|
||||
struct dirent *next = readdir (reading);
|
||||
if (!next)
|
||||
break;
|
||||
|
||||
/* Ignore "." and "..". */
|
||||
if (d_name[0] == '.'
|
||||
&& (d_name[1] == 0 || (d_name[1] == '.' && d_name[2] == 0)))
|
||||
continue;
|
||||
char *d_name = next->d_name;
|
||||
|
||||
if (excluded_file_name (excluded, d_name))
|
||||
continue;
|
||||
/* Ignore "." and "..". */
|
||||
if (d_name[0] == '.'
|
||||
&& (d_name[1] == 0 || (d_name[1] == '.' && d_name[2] == 0)))
|
||||
continue;
|
||||
|
||||
while (data_alloc < data_used + d_size)
|
||||
if (startfile)
|
||||
{
|
||||
if (PTRDIFF_MAX / 2 <= data_alloc)
|
||||
xalloc_die ();
|
||||
dirdata->data = data = xrealloc (data, data_alloc *= 2);
|
||||
int cmp = compare_names (d_name, startfile);
|
||||
if (cmp < 0 || (startfile_only && !!cmp))
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy (data + data_used, d_name, d_size);
|
||||
data_used += d_size;
|
||||
nnames++;
|
||||
}
|
||||
if (errno)
|
||||
{
|
||||
int e = errno;
|
||||
closedir (reading);
|
||||
errno = e;
|
||||
return false;
|
||||
}
|
||||
#if CLOSEDIR_VOID
|
||||
closedir (reading);
|
||||
#else
|
||||
if (closedir (reading) != 0)
|
||||
return false;
|
||||
if (excluded_file_name (excluded, d_name))
|
||||
continue;
|
||||
|
||||
idx_t d_size = HAVE_STRUCT_DIRENT_D_TYPE + _D_EXACT_NAMLEN (next) + 1;
|
||||
if (data_alloc - data_used < d_size)
|
||||
dirdata->data = data
|
||||
= xpalloc (data, &data_alloc,
|
||||
d_size - (data_alloc - data_used), -1, 1);
|
||||
#if HAVE_STRUCT_DIRENT_D_TYPE
|
||||
char detype;
|
||||
switch (next->d_type)
|
||||
{
|
||||
case DT_BLK: detype = DE_BLK; break;
|
||||
case DT_CHR: detype = DE_CHR; break;
|
||||
case DT_DIR: detype = DE_DIR; break;
|
||||
case DT_FIFO: detype = DE_FIFO; break;
|
||||
case DT_LNK: detype = DE_LNK; break;
|
||||
case DT_REG: detype = DE_REG; break;
|
||||
case DT_SOCK: detype = DE_SOCK; break;
|
||||
# ifdef DT_WHT
|
||||
case DT_WHT: detype = DE_WHT; break;
|
||||
# endif
|
||||
case DT_UNKNOWN: detype = DE_UNKNOWN; break;
|
||||
default: detype = DE_OTHER; break;
|
||||
}
|
||||
data[data_used++] = detype;
|
||||
d_size--;
|
||||
#endif
|
||||
memcpy (data + data_used, d_name, d_size);
|
||||
data_used += d_size;
|
||||
nnames++;
|
||||
}
|
||||
|
||||
if (errno)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Create the 'names' table from the 'data' table. */
|
||||
if (PTRDIFF_MAX / sizeof *names - 1 <= nnames)
|
||||
xalloc_die ();
|
||||
dirdata->names = names = xmalloc ((nnames + 1) * sizeof *names);
|
||||
char const **names = xinmalloc (nnames + 1, sizeof *names);
|
||||
dirdata->names = names;
|
||||
dirdata->nnames = nnames;
|
||||
for (i = 0; i < nnames; i++)
|
||||
for (idx_t i = 0; i < nnames; i++)
|
||||
{
|
||||
data += HAVE_STRUCT_DIRENT_D_TYPE;
|
||||
names[i] = data;
|
||||
data += strlen (data) + 1;
|
||||
}
|
||||
names[nnames] = 0;
|
||||
names[nnames] = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -145,16 +187,12 @@ dir_read (struct file_data const *dir, struct dirdata *dirdata)
|
||||
static int
|
||||
compare_collated (char const *name1, char const *name2)
|
||||
{
|
||||
int r;
|
||||
errno = 0;
|
||||
if (ignore_file_name_case)
|
||||
r = strcasecoll (name1, name2);
|
||||
else
|
||||
r = strcoll (name1, name2);
|
||||
int r = strcoll (name1, name2);
|
||||
if (errno)
|
||||
{
|
||||
error (0, errno, _("cannot compare file names '%s' and '%s'"),
|
||||
name1, name2);
|
||||
error (0, errno, _("cannot compare file names %s and %s"),
|
||||
quote_n (0, name1), quote_n (1, name2));
|
||||
longjmp (failed_locale_specific_sorting, 1);
|
||||
}
|
||||
return r;
|
||||
@@ -165,11 +203,14 @@ compare_collated (char const *name1, char const *name2)
|
||||
static int
|
||||
compare_names (char const *name1, char const *name2)
|
||||
{
|
||||
if (ignore_file_name_case)
|
||||
return mbscasecmp (name1, name2); /* Best we can do. */
|
||||
|
||||
if (locale_specific_sorting)
|
||||
{
|
||||
int diff = compare_collated (name1, name2);
|
||||
if (diff || ignore_file_name_case)
|
||||
return diff;
|
||||
if (diff)
|
||||
return diff;
|
||||
}
|
||||
return file_name_cmp (name1, name2);
|
||||
}
|
||||
@@ -182,138 +223,108 @@ compare_names_for_qsort (void const *file1, void const *file2)
|
||||
{
|
||||
char const *const *f1 = file1;
|
||||
char const *const *f2 = file2;
|
||||
char const *name1 = *f1;
|
||||
char const *name2 = *f2;
|
||||
if (locale_specific_sorting)
|
||||
{
|
||||
int diff = compare_collated (name1, name2);
|
||||
if (diff)
|
||||
return diff;
|
||||
}
|
||||
return file_name_cmp (name1, name2);
|
||||
return compare_names (*f1, *f2);
|
||||
}
|
||||
|
||||
/* Compare the contents of two directories named in CMP.
|
||||
This is a top-level routine; it does everything necessary for diff
|
||||
on two directories.
|
||||
|
||||
CMP->file[0].desc == -1 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.
|
||||
These names are relative to the original working directory.
|
||||
|
||||
For a file that appears in only one of the dirs, one of the name-args
|
||||
to HANDLE_FILE is zero.
|
||||
|
||||
Returns the maximum of all the values returned by HANDLE_FILE,
|
||||
Returns the maximum of all the values returned by compare_files,
|
||||
or EXIT_TROUBLE if trouble is encountered in opening files. */
|
||||
|
||||
int
|
||||
diff_dirs (struct comparison const *cmp,
|
||||
int (*handle_file) (struct comparison const *,
|
||||
char const *, char const *))
|
||||
diff_dirs (struct comparison *cmp)
|
||||
{
|
||||
struct dirdata dirdata[2];
|
||||
int volatile val = EXIT_SUCCESS;
|
||||
int i;
|
||||
|
||||
if ((cmp->file[0].desc == -1 || dir_loop (cmp, 0))
|
||||
&& (cmp->file[1].desc == -1 || dir_loop (cmp, 1)))
|
||||
if ((cmp->file[0].desc == NONEXISTENT || dir_loop (cmp, 0))
|
||||
&& (cmp->file[1].desc == NONEXISTENT || dir_loop (cmp, 1)))
|
||||
{
|
||||
error (0, 0, _("%s: recursive directory loop"),
|
||||
cmp->file[cmp->file[0].desc == -1].name);
|
||||
squote (0, cmp->file[cmp->file[0].desc == NONEXISTENT].name));
|
||||
return EXIT_TROUBLE;
|
||||
}
|
||||
|
||||
/* Get contents of both dirs. */
|
||||
for (i = 0; i < 2; i++)
|
||||
if (! dir_read (&cmp->file[i], &dirdata[i]))
|
||||
struct dirdata dirdata[2];
|
||||
int val = EXIT_SUCCESS;
|
||||
for (int i = 0; i < 2; i++)
|
||||
if (! dir_read (cmp->parent->file[i].desc, &cmp->file[i], &dirdata[i],
|
||||
cmp->parent == &noparent ? starting_file : nullptr, false))
|
||||
{
|
||||
perror_with_name (cmp->file[i].name);
|
||||
val = EXIT_TROUBLE;
|
||||
perror_with_name (cmp->file[i].name);
|
||||
val = EXIT_TROUBLE;
|
||||
}
|
||||
|
||||
if (val == EXIT_SUCCESS)
|
||||
{
|
||||
char const **volatile names[2];
|
||||
names[0] = dirdata[0].names;
|
||||
names[1] = dirdata[1].names;
|
||||
|
||||
/* Use locale-specific sorting if possible, else native byte order. */
|
||||
locale_specific_sorting = true;
|
||||
if (setjmp (failed_locale_specific_sorting))
|
||||
locale_specific_sorting = false;
|
||||
if (! ignore_file_name_case)
|
||||
if (setjmp (failed_locale_specific_sorting))
|
||||
locale_specific_sorting = false;
|
||||
|
||||
/* Sort the directories. */
|
||||
for (i = 0; i < 2; i++)
|
||||
qsort (names[i], dirdata[i].nnames, sizeof *dirdata[i].names,
|
||||
compare_names_for_qsort);
|
||||
|
||||
/* If '-S name' was given, and this is the topmost level of comparison,
|
||||
ignore all file names less than the specified starting name. */
|
||||
|
||||
if (starting_file && ! cmp->parent)
|
||||
{
|
||||
while (*names[0] && compare_names (*names[0], starting_file) < 0)
|
||||
names[0]++;
|
||||
while (*names[1] && compare_names (*names[1], starting_file) < 0)
|
||||
names[1]++;
|
||||
}
|
||||
for (int i = 0; i < 2; i++)
|
||||
qsort (dirdata[i].names, dirdata[i].nnames, sizeof *dirdata[i].names,
|
||||
compare_names_for_qsort);
|
||||
|
||||
/* Loop while files remain in one or both dirs. */
|
||||
while (*names[0] || *names[1])
|
||||
{
|
||||
/* Compare next name in dir 0 with next name in dir 1.
|
||||
At the end of a dir,
|
||||
pretend the "next name" in that dir is very large. */
|
||||
int nameorder = (!*names[0] ? 1 : !*names[1] ? -1
|
||||
: compare_names (*names[0], *names[1]));
|
||||
char const **n0 = dirdata[0].names;
|
||||
char const **n1 = dirdata[1].names;
|
||||
while (*n0 || *n1)
|
||||
{
|
||||
/* Compare next name in dir 0 with next name in dir 1.
|
||||
At the end of a dir,
|
||||
pretend the "next name" in that dir is very large. */
|
||||
int nameorder = !*n0 ? 1 : !*n1 ? -1 : compare_names (*n0, *n1);
|
||||
|
||||
/* Prefer a file_name_cmp match if available. This algorithm is
|
||||
O(N**2), where N is the number of names in a directory
|
||||
that compare_names says are all equal, but in practice N
|
||||
is so small it's not worth tuning. */
|
||||
if (nameorder == 0 && ignore_file_name_case)
|
||||
{
|
||||
int raw_order = file_name_cmp (*names[0], *names[1]);
|
||||
if (raw_order != 0)
|
||||
{
|
||||
int greater_side = raw_order < 0;
|
||||
int lesser_side = 1 - greater_side;
|
||||
char const **lesser = names[lesser_side];
|
||||
char const *greater_name = *names[greater_side];
|
||||
char const **p;
|
||||
/* Prefer a file_name_cmp match if available. This algorithm is
|
||||
O(N**2), where N is the number of names in a directory
|
||||
that compare_names says are all equal, but in practice N
|
||||
is so small it's not worth tuning. */
|
||||
if (nameorder == 0 && ignore_file_name_case)
|
||||
{
|
||||
int raw_order = file_name_cmp (*n0, *n1);
|
||||
if (raw_order != 0)
|
||||
{
|
||||
char const **lesser = raw_order < 0 ? n0 : n1;
|
||||
char const *greater_name = *(raw_order < 0 ? n1 : n0);
|
||||
|
||||
for (p = lesser + 1;
|
||||
*p && compare_names (*p, greater_name) == 0;
|
||||
p++)
|
||||
{
|
||||
int c = file_name_cmp (*p, greater_name);
|
||||
if (0 <= c)
|
||||
{
|
||||
if (c == 0)
|
||||
{
|
||||
memmove (lesser + 1, lesser,
|
||||
(char *) p - (char *) lesser);
|
||||
*lesser = greater_name;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (char const **p = lesser + 1;
|
||||
*p && compare_names (*p, greater_name) == 0;
|
||||
p++)
|
||||
{
|
||||
int c = file_name_cmp (*p, greater_name);
|
||||
if (0 <= c)
|
||||
{
|
||||
if (c == 0)
|
||||
{
|
||||
memmove (lesser + 1, lesser,
|
||||
(char *) p - (char *) lesser);
|
||||
*lesser = greater_name;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int v1 = (*handle_file) (cmp,
|
||||
0 < nameorder ? 0 : *names[0]++,
|
||||
nameorder < 0 ? 0 : *names[1]++);
|
||||
if (val < v1)
|
||||
val = v1;
|
||||
}
|
||||
enum detype detypes[]
|
||||
= { HAVE_STRUCT_DIRENT_D_TYPE && *n0 ? (*n0)[-1] : DE_UNKNOWN,
|
||||
HAVE_STRUCT_DIRENT_D_TYPE && *n1 ? (*n1)[-1] : DE_UNKNOWN };
|
||||
int v1 = compare_files (cmp, detypes,
|
||||
0 < nameorder ? nullptr : *n0++,
|
||||
nameorder < 0 ? nullptr : *n1++);
|
||||
if (val < v1)
|
||||
val = v1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
free (dirdata[i].names);
|
||||
free (dirdata[i].data);
|
||||
@@ -324,12 +335,11 @@ diff_dirs (struct comparison const *cmp,
|
||||
|
||||
/* Return nonzero if CMP is looping recursively in argument I. */
|
||||
|
||||
static bool _GL_ATTRIBUTE_PURE
|
||||
static bool ATTRIBUTE_PURE
|
||||
dir_loop (struct comparison const *cmp, int i)
|
||||
{
|
||||
struct comparison const *p = cmp;
|
||||
while ((p = p->parent))
|
||||
if (0 < same_file (&p->file[i].stat, &cmp->file[i].stat))
|
||||
for (struct comparison const *p = cmp; (p = p->parent) != &noparent; )
|
||||
if (same_file (&p->file[i].stat, &cmp->file[i].stat))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -337,48 +347,29 @@ 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,
|
||||
enum detype *detype)
|
||||
{
|
||||
/* The 'IF_LINT (volatile)' works around what appears to be a bug in
|
||||
gcc 4.8.0 20120825; see
|
||||
<http://lists.gnu.org/archive/html/bug-diffutils/2012-08/msg00007.html>.
|
||||
*/
|
||||
char const * IF_LINT (volatile) match = file;
|
||||
char const *match = file;
|
||||
|
||||
char *val;
|
||||
struct dirdata dirdata;
|
||||
dirdata.names = NULL;
|
||||
dirdata.data = NULL;
|
||||
dirdata.names = nullptr;
|
||||
dirdata.data = nullptr;
|
||||
|
||||
if (ignore_file_name_case)
|
||||
{
|
||||
struct file_data filedata;
|
||||
filedata.name = dir;
|
||||
filedata.desc = 0;
|
||||
if (ignore_file_name_case && dir_read (AT_FDCWD, dir, &dirdata, file, true))
|
||||
for (char const **p = dirdata.names; *p; p++)
|
||||
{
|
||||
if (file_name_cmp (*p, file) == 0)
|
||||
{
|
||||
match = *p;
|
||||
break;
|
||||
}
|
||||
if (match == file)
|
||||
match = *p;
|
||||
}
|
||||
|
||||
if (dir_read (&filedata, &dirdata))
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (file_name_cmp (*p, file) == 0)
|
||||
{
|
||||
match = *p;
|
||||
break;
|
||||
}
|
||||
if (match == file)
|
||||
match = *p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val = file_name_concat (dir, match, NULL);
|
||||
*detype = HAVE_STRUCT_DIRENT_D_TYPE && match != file ? match[-1] : DE_UNKNOWN;
|
||||
char *val = file_name_concat (dir->name, match, nullptr);
|
||||
free (dirdata.names);
|
||||
free (dirdata.data);
|
||||
return val;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* Output routines for ed-script format.
|
||||
|
||||
Copyright (C) 1988-1989, 1991-1993, 1995, 1998, 2001, 2004, 2006, 2009-2013,
|
||||
2015-2017 Free Software Foundation, Inc.
|
||||
2015-2025 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
|
||||
@@ -29,7 +29,8 @@ static void pr_forward_ed_hunk (struct change *);
|
||||
void
|
||||
print_ed_script (struct change *script)
|
||||
{
|
||||
print_script (script, find_reverse_change, print_ed_hunk);
|
||||
/* The script is reversed, so plain find_change suffices. */
|
||||
print_script (script, find_change, print_ed_hunk);
|
||||
}
|
||||
|
||||
/* Print a hunk of an ed diff */
|
||||
@@ -37,52 +38,50 @@ print_ed_script (struct change *script)
|
||||
static void
|
||||
print_ed_hunk (struct change *hunk)
|
||||
{
|
||||
lin f0, l0, f1, l1;
|
||||
enum changes changes;
|
||||
|
||||
#ifdef DEBUG
|
||||
debug_script (hunk);
|
||||
#endif
|
||||
|
||||
/* Determine range of line numbers involved in each file. */
|
||||
changes = analyze_hunk (hunk, &f0, &l0, &f1, &l1);
|
||||
lin f0, l0, f1, l1;
|
||||
enum changes changes = analyze_hunk (hunk, &f0, &l0, &f1, &l1);
|
||||
if (!changes)
|
||||
return;
|
||||
|
||||
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);
|
||||
|
||||
/* Print new/changed lines from second file, if needed */
|
||||
if (changes != OLD)
|
||||
{
|
||||
lin i;
|
||||
bool insert_mode = true;
|
||||
|
||||
for (i = f1; i <= l1; i++)
|
||||
{
|
||||
if (!insert_mode)
|
||||
{
|
||||
fputs ("a\n", outfile);
|
||||
insert_mode = true;
|
||||
}
|
||||
if (files[1].linbuf[i][0] == '.' && files[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
|
||||
insert mode and remove the extra dot. */
|
||||
fputs ("..\n.\ns/.//\n", outfile);
|
||||
insert_mode = false;
|
||||
}
|
||||
else
|
||||
print_1_line ("", &files[1].linbuf[i]);
|
||||
}
|
||||
for (lin i = f1; i <= l1; i++)
|
||||
{
|
||||
if (!insert_mode)
|
||||
{
|
||||
fputs ("a\n", outfile);
|
||||
insert_mode = true;
|
||||
}
|
||||
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
|
||||
insert mode and remove the extra dot. */
|
||||
fputs ("..\n.\ns/.//\n", outfile);
|
||||
insert_mode = false;
|
||||
}
|
||||
else
|
||||
print_1_line ("", &curr.file[1].linbuf[i]);
|
||||
}
|
||||
|
||||
if (insert_mode)
|
||||
fputs (".\n", outfile);
|
||||
fputs (".\n", outfile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,9 +100,8 @@ pr_forward_ed_script (struct change *script)
|
||||
static void
|
||||
pr_forward_ed_hunk (struct change *hunk)
|
||||
{
|
||||
lin i, f0, l0, f1, l1;
|
||||
|
||||
/* Determine range of line numbers involved in each file. */
|
||||
lin f0, l0, f1, l1;
|
||||
enum changes changes = analyze_hunk (hunk, &f0, &l0, &f1, &l1);
|
||||
if (!changes)
|
||||
return;
|
||||
@@ -111,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. */
|
||||
@@ -122,8 +120,8 @@ pr_forward_ed_hunk (struct change *hunk)
|
||||
/* For insertion (with or without deletion), print the number range
|
||||
and the lines from file 2. */
|
||||
|
||||
for (i = f1; i <= l1; i++)
|
||||
print_1_line ("", &files[1].linbuf[i]);
|
||||
for (lin i = f1; i <= l1; i++)
|
||||
print_1_line ("", &curr.file[1].linbuf[i]);
|
||||
|
||||
fputs (".\n", outfile);
|
||||
}
|
||||
@@ -143,35 +141,34 @@ print_rcs_script (struct change *script)
|
||||
static void
|
||||
print_rcs_hunk (struct change *hunk)
|
||||
{
|
||||
lin i, f0, l0, f1, l1;
|
||||
printint tf0, tl0, tf1, tl1;
|
||||
|
||||
/* Determine range of line numbers involved in each file. */
|
||||
lin f0, l0, f1, l1;
|
||||
enum changes changes = analyze_hunk (hunk, &f0, &l0, &f1, &l1);
|
||||
if (!changes)
|
||||
return;
|
||||
|
||||
begin_output ();
|
||||
|
||||
translate_range (&files[0], f0, l0, &tf0, &tl0);
|
||||
lin tf0, tl0, tf1, tl1;
|
||||
translate_range (&curr.file[0], f0, l0, &tf0, &tl0);
|
||||
|
||||
if (changes & OLD)
|
||||
{
|
||||
/* For deletion, print just the starting line number from file 0
|
||||
and the number of lines deleted. */
|
||||
and the number of lines deleted. */
|
||||
fprintf (outfile, "d%"pI"d %"pI"d\n", tf0,
|
||||
tf0 <= tl0 ? tl0 - tf0 + 1 : 1);
|
||||
tf0 <= tl0 ? tl0 - tf0 + 1 : 1);
|
||||
}
|
||||
|
||||
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);
|
||||
tf1 <= tl1 ? tl1 - tf1 + 1 : 1);
|
||||
|
||||
/* Print the inserted lines. */
|
||||
for (i = f1; i <= l1; i++)
|
||||
print_1_line ("", &files[1].linbuf[i]);
|
||||
for (lin i = f1; i <= l1; i++)
|
||||
print_1_line ("", &curr.file[1].linbuf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* #ifdef-format output routines for GNU DIFF.
|
||||
|
||||
Copyright (C) 1989, 1991-1994, 2001-2002, 2004, 2006, 2009-2013, 2015-2017
|
||||
Copyright (C) 1989, 1991-1994, 2001-2002, 2004, 2006, 2009-2013, 2015-2025
|
||||
Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
@@ -22,7 +22,8 @@
|
||||
|
||||
#include "diff.h"
|
||||
|
||||
#include <xalloc.h>
|
||||
#include <c-ctype.h>
|
||||
#include <xmalloca.h>
|
||||
|
||||
struct group
|
||||
{
|
||||
@@ -31,10 +32,10 @@ struct group
|
||||
};
|
||||
|
||||
static char const *format_group (FILE *, char const *, char,
|
||||
struct group const *);
|
||||
struct group const *);
|
||||
static char const *do_printf_spec (FILE *, char const *,
|
||||
struct file_data const *, lin,
|
||||
struct group const *);
|
||||
struct file_data const *, lin,
|
||||
struct group const *);
|
||||
static char const *scan_char_literal (char const *, char *);
|
||||
static lin groups_letter_value (struct group const *, char);
|
||||
static void format_ifdef (char const *, lin, lin, lin, lin);
|
||||
@@ -49,15 +50,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,9 +69,8 @@ print_ifdef_script (struct change *script)
|
||||
static void
|
||||
print_ifdef_hunk (struct change *hunk)
|
||||
{
|
||||
lin first0, last0, first1, last1;
|
||||
|
||||
/* Determine range of line numbers involved in each file. */
|
||||
lin first0, last0, first1, last1;
|
||||
enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
|
||||
if (!changes)
|
||||
return;
|
||||
@@ -80,15 +80,15 @@ print_ifdef_hunk (struct change *hunk)
|
||||
/* Print lines up to this change. */
|
||||
if (next_line0 < first0 || next_line1 < first1)
|
||||
format_ifdef (group_format[UNCHANGED],
|
||||
next_line0, first0,
|
||||
next_line1, first1);
|
||||
next_line0, first0,
|
||||
next_line1, first1);
|
||||
|
||||
/* Print this change. */
|
||||
next_line0 = last0 + 1;
|
||||
next_line1 = last1 + 1;
|
||||
format_ifdef (group_format[changes],
|
||||
first0, next_line0,
|
||||
first1, next_line1);
|
||||
first0, next_line0,
|
||||
first1, next_line1);
|
||||
}
|
||||
|
||||
/* Print a set of lines according to FORMAT.
|
||||
@@ -98,15 +98,10 @@ print_ifdef_hunk (struct change *hunk)
|
||||
static void
|
||||
format_ifdef (char const *format, lin beg0, lin end0, lin beg1, lin end1)
|
||||
{
|
||||
struct group groups[2];
|
||||
|
||||
groups[0].file = &files[0];
|
||||
groups[0].from = beg0;
|
||||
groups[0].upto = end0;
|
||||
groups[1].file = &files[1];
|
||||
groups[1].from = beg1;
|
||||
groups[1].upto = end1;
|
||||
format_group (outfile, format, 0, groups);
|
||||
format_group (outfile, format, '\0',
|
||||
((struct group const[])
|
||||
{{.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.
|
||||
@@ -116,91 +111,91 @@ format_ifdef (char const *format, lin beg0, lin end0, lin beg1, lin end1)
|
||||
If OUT is zero, do not actually print anything; just scan the format. */
|
||||
|
||||
static char const *
|
||||
format_group (register FILE *out, char const *format, char endchar,
|
||||
struct group const *groups)
|
||||
format_group (FILE *out, char const *format, char endchar,
|
||||
struct group const *groups)
|
||||
{
|
||||
register char c;
|
||||
register char const *f = format;
|
||||
char const *f = format;
|
||||
|
||||
while ((c = *f) != endchar && c != 0)
|
||||
for (char c; (c = *f) != endchar && c; )
|
||||
{
|
||||
char const *f1 = ++f;
|
||||
if (c == '%')
|
||||
switch ((c = *f++))
|
||||
{
|
||||
case '%':
|
||||
break;
|
||||
{
|
||||
c = *f++;
|
||||
switch (c)
|
||||
{
|
||||
case '%':
|
||||
break;
|
||||
|
||||
case '(':
|
||||
/* Print if-then-else format e.g. '%(n=1?thenpart:elsepart)'. */
|
||||
{
|
||||
int i;
|
||||
uintmax_t value[2];
|
||||
FILE *thenout, *elseout;
|
||||
case '(':
|
||||
/* Print if-then-else format e.g. '%(n=1?thenpart:elsepart)'. */
|
||||
{
|
||||
intmax_t value[2];
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
if (ISDIGIT (*f))
|
||||
{
|
||||
char *fend;
|
||||
errno = 0;
|
||||
value[i] = strtoumax (f, &fend, 10);
|
||||
if (errno)
|
||||
goto bad_format;
|
||||
f = fend;
|
||||
}
|
||||
else
|
||||
{
|
||||
value[i] = groups_letter_value (groups, *f);
|
||||
if (value[i] == -1)
|
||||
goto bad_format;
|
||||
f++;
|
||||
}
|
||||
if (*f++ != "=?"[i])
|
||||
goto bad_format;
|
||||
}
|
||||
if (value[0] == value[1])
|
||||
thenout = out, elseout = 0;
|
||||
else
|
||||
thenout = 0, elseout = out;
|
||||
f = format_group (thenout, f, ':', groups);
|
||||
if (*f)
|
||||
{
|
||||
f = format_group (elseout, f + 1, ')', groups);
|
||||
if (*f)
|
||||
f++;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (c_isdigit (*f))
|
||||
{
|
||||
char *fend;
|
||||
errno = 0;
|
||||
value[i] = strtoimax (f, &fend, 10);
|
||||
if (errno)
|
||||
goto bad_format;
|
||||
f = fend;
|
||||
}
|
||||
else
|
||||
{
|
||||
value[i] = groups_letter_value (groups, *f);
|
||||
if (value[i] < 0)
|
||||
goto bad_format;
|
||||
f++;
|
||||
}
|
||||
if (*f++ != "=?"[i])
|
||||
goto bad_format;
|
||||
}
|
||||
|
||||
case '<':
|
||||
/* Print lines deleted from first file. */
|
||||
print_ifdef_lines (out, line_format[OLD], &groups[0]);
|
||||
continue;
|
||||
bool equal_values = value[0] == value[1];
|
||||
FILE *thenout = equal_values ? out : nullptr;
|
||||
FILE *elseout = equal_values ? nullptr : out;
|
||||
f = format_group (thenout, f, ':', groups);
|
||||
if (*f)
|
||||
{
|
||||
f = format_group (elseout, f + 1, ')', groups);
|
||||
if (*f)
|
||||
f++;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
|
||||
case '=':
|
||||
/* Print common lines. */
|
||||
print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]);
|
||||
continue;
|
||||
case '<':
|
||||
/* Print lines deleted from first file. */
|
||||
print_ifdef_lines (out, line_format[OLD], &groups[0]);
|
||||
continue;
|
||||
|
||||
case '>':
|
||||
/* Print lines inserted from second file. */
|
||||
print_ifdef_lines (out, line_format[NEW], &groups[1]);
|
||||
continue;
|
||||
case '=':
|
||||
/* Print common lines. */
|
||||
print_ifdef_lines (out, line_format[UNCHANGED], &groups[0]);
|
||||
continue;
|
||||
|
||||
default:
|
||||
f = do_printf_spec (out, f - 2, 0, 0, groups);
|
||||
if (f)
|
||||
continue;
|
||||
/* Fall through. */
|
||||
bad_format:
|
||||
c = '%';
|
||||
f = f1;
|
||||
break;
|
||||
}
|
||||
case '>':
|
||||
/* Print lines inserted from second file. */
|
||||
print_ifdef_lines (out, line_format[NEW], &groups[1]);
|
||||
continue;
|
||||
|
||||
default:
|
||||
f = do_printf_spec (out, f - 2, nullptr, 0, groups);
|
||||
if (f)
|
||||
continue;
|
||||
/* Fall through. */
|
||||
bad_format:
|
||||
c = '%';
|
||||
f = f1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (out)
|
||||
putc (c, out);
|
||||
putc (c, out);
|
||||
}
|
||||
|
||||
return f;
|
||||
@@ -234,154 +229,149 @@ groups_letter_value (struct group const *g, char letter)
|
||||
/* Print to file OUT, using FORMAT to print the line group GROUP.
|
||||
But do nothing if OUT is zero. */
|
||||
static void
|
||||
print_ifdef_lines (register FILE *out, char const *format,
|
||||
struct group const *group)
|
||||
print_ifdef_lines (FILE *out, char const *format,
|
||||
struct group const *group)
|
||||
{
|
||||
struct file_data const *file = group->file;
|
||||
char const * const *linbuf = file->linbuf;
|
||||
lin from = group->from, upto = group->upto;
|
||||
|
||||
if (!out)
|
||||
return;
|
||||
|
||||
struct file_data const *file = group->file;
|
||||
char const *const *linbuf = file->linbuf;
|
||||
lin from = group->from, upto = group->upto;
|
||||
|
||||
/* If possible, use a single fwrite; it's faster. */
|
||||
if (!expand_tabs && format[0] == '%')
|
||||
{
|
||||
if (format[1] == 'l' && format[2] == '\n' && !format[3] && from < upto)
|
||||
{
|
||||
fwrite (linbuf[from], sizeof (char),
|
||||
linbuf[upto] + (linbuf[upto][-1] != '\n') - linbuf[from],
|
||||
out);
|
||||
return;
|
||||
}
|
||||
{
|
||||
fwrite (linbuf[from], sizeof (char),
|
||||
linbuf[upto] + (linbuf[upto][-1] != '\n') - linbuf[from],
|
||||
out);
|
||||
return;
|
||||
}
|
||||
if (format[1] == 'L' && !format[2])
|
||||
{
|
||||
fwrite (linbuf[from], sizeof (char),
|
||||
linbuf[upto] - linbuf[from], out);
|
||||
return;
|
||||
}
|
||||
{
|
||||
fwrite (linbuf[from], sizeof (char),
|
||||
linbuf[upto] - linbuf[from], out);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (; from < upto; from++)
|
||||
{
|
||||
register char c;
|
||||
register char const *f = format;
|
||||
char c;
|
||||
char const *f = format;
|
||||
|
||||
while ((c = *f++) != 0)
|
||||
{
|
||||
char const *f1 = f;
|
||||
if (c == '%')
|
||||
switch ((c = *f++))
|
||||
{
|
||||
case '%':
|
||||
break;
|
||||
while ((c = *f++))
|
||||
{
|
||||
char const *f1 = f;
|
||||
if (c == '%')
|
||||
{
|
||||
c = *f++;
|
||||
switch (c)
|
||||
{
|
||||
case '%':
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
output_1_line (linbuf[from],
|
||||
(linbuf[from + 1]
|
||||
- (linbuf[from + 1][-1] == '\n')),
|
||||
0, 0);
|
||||
continue;
|
||||
case 'l':
|
||||
output_1_line (linbuf[from],
|
||||
(linbuf[from + 1]
|
||||
- (linbuf[from + 1][-1] == '\n')),
|
||||
nullptr, nullptr);
|
||||
continue;
|
||||
|
||||
case 'L':
|
||||
output_1_line (linbuf[from], linbuf[from + 1], 0, 0);
|
||||
continue;
|
||||
case 'L':
|
||||
output_1_line (linbuf[from], linbuf[from + 1],
|
||||
nullptr, nullptr);
|
||||
continue;
|
||||
|
||||
default:
|
||||
f = do_printf_spec (out, f - 2, file, from, 0);
|
||||
if (f)
|
||||
continue;
|
||||
c = '%';
|
||||
f = f1;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
f = do_printf_spec (out, f - 2, file, from, nullptr);
|
||||
if (f)
|
||||
continue;
|
||||
c = '%';
|
||||
f = f1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
putc (c, out);
|
||||
}
|
||||
putc (c, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char const *
|
||||
do_printf_spec (FILE *out, char const *spec,
|
||||
struct file_data const *file, lin n,
|
||||
struct group const *groups)
|
||||
struct file_data const *file, lin n,
|
||||
struct group const *groups)
|
||||
{
|
||||
char const *f = spec;
|
||||
char c;
|
||||
char c1;
|
||||
|
||||
/* Scan printf-style SPEC of the form %[-'0]*[0-9]*(.[0-9]*)?[cdoxX]. */
|
||||
/* assert (*f == '%'); */
|
||||
dassert (*f == '%');
|
||||
f++;
|
||||
while ((c = *f++) == '-' || c == '\'' || c == '0')
|
||||
continue;
|
||||
while (ISDIGIT (c))
|
||||
while (c_isdigit (c))
|
||||
c = *f++;
|
||||
if (c == '.')
|
||||
while (ISDIGIT (c = *f++))
|
||||
while (c_isdigit (c = *f++))
|
||||
continue;
|
||||
c1 = *f++;
|
||||
char c1 = *f++;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 'c':
|
||||
if (c1 != '\'')
|
||||
return 0;
|
||||
return nullptr;
|
||||
else
|
||||
{
|
||||
char value IF_LINT (= 0);
|
||||
f = scan_char_literal (f, &value);
|
||||
if (!f)
|
||||
return 0;
|
||||
if (out)
|
||||
putc (value, out);
|
||||
}
|
||||
{
|
||||
char value;
|
||||
f = scan_char_literal (f, &value);
|
||||
if (!f)
|
||||
return nullptr;
|
||||
if (out)
|
||||
putc (value, out);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'd': case 'o': case 'x': case 'X':
|
||||
{
|
||||
lin value;
|
||||
lin value;
|
||||
|
||||
if (file)
|
||||
{
|
||||
if (c1 != 'n')
|
||||
return 0;
|
||||
value = translate_line_number (file, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = groups_letter_value (groups, c1);
|
||||
if (value < 0)
|
||||
return 0;
|
||||
}
|
||||
if (file)
|
||||
{
|
||||
if (c1 != 'n')
|
||||
return nullptr;
|
||||
value = translate_line_number (file, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = groups_letter_value (groups, c1);
|
||||
if (value < 0)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (out)
|
||||
{
|
||||
/* For example, if the spec is "%3xn" and pI is "l", use the printf
|
||||
format spec "%3lx". Here the spec prefix is "%3". */
|
||||
printint print_value = value;
|
||||
size_t spec_prefix_len = f - spec - 2;
|
||||
size_t pI_len = sizeof pI - 1;
|
||||
#if 0
|
||||
char format[spec_prefix_len + pI_len + 2];
|
||||
#else
|
||||
char *format = xmalloc (spec_prefix_len + pI_len + 2);
|
||||
#endif
|
||||
char *p = format + spec_prefix_len + pI_len;
|
||||
memcpy (format, spec, spec_prefix_len);
|
||||
memcpy (format + spec_prefix_len, pI, pI_len);
|
||||
*p++ = c;
|
||||
*p = '\0';
|
||||
fprintf (out, format, print_value);
|
||||
#if ! HAVE_C_VARARRAYS
|
||||
free (format);
|
||||
#endif
|
||||
}
|
||||
if (out)
|
||||
{
|
||||
/* For example, if the spec is "%3xn" and pI is "l", use the printf
|
||||
format spec "%3lx". Here the spec prefix is "%3". */
|
||||
idx_t spec_prefix_len = f - spec - 2;
|
||||
idx_t pI_len = sizeof pI - 1;
|
||||
char *format = xmalloca (spec_prefix_len + pI_len + 2);
|
||||
char *p = mempcpy (format, spec, spec_prefix_len);
|
||||
p = stpcpy (p, pI);
|
||||
*p++ = c;
|
||||
*p = '\0';
|
||||
fprintf (out, format, value);
|
||||
freea (format);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return f;
|
||||
@@ -394,36 +384,35 @@ do_printf_spec (FILE *out, char const *spec,
|
||||
static char const *
|
||||
scan_char_literal (char const *lit, char *valptr)
|
||||
{
|
||||
register char const *p = lit;
|
||||
char const *p = lit;
|
||||
char value;
|
||||
ptrdiff_t digits;
|
||||
char c = *p++;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 0:
|
||||
case '\'':
|
||||
return NULL;
|
||||
return nullptr;
|
||||
|
||||
case '\\':
|
||||
value = 0;
|
||||
while ((c = *p++) != '\'')
|
||||
{
|
||||
unsigned int digit = c - '0';
|
||||
if (8 <= digit)
|
||||
return NULL;
|
||||
value = 8 * value + digit;
|
||||
}
|
||||
digits = p - lit - 2;
|
||||
if (! (1 <= digits && digits <= 3))
|
||||
return NULL;
|
||||
break;
|
||||
value = '\0';
|
||||
while ((c = *p++) != '\'')
|
||||
{
|
||||
unsigned int digit = c - '0';
|
||||
if (8 <= digit)
|
||||
return nullptr;
|
||||
value = 8 * value + digit;
|
||||
}
|
||||
ptrdiff_t digits = p - lit - 2;
|
||||
if (! (1 <= digits && digits <= 3))
|
||||
return nullptr;
|
||||
break;
|
||||
|
||||
default:
|
||||
value = c;
|
||||
if (*p++ != '\'')
|
||||
return NULL;
|
||||
break;
|
||||
value = c;
|
||||
if (*p++ != '\'')
|
||||
return nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
*valptr = value;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/* Normal-format output routines for GNU DIFF.
|
||||
|
||||
Copyright (C) 1988-1989, 1993, 1995, 1998, 2001, 2006, 2009-2013, 2015-2017
|
||||
Copyright (C) 1988-1989, 1993, 1995, 1998, 2001, 2006, 2009-2013, 2015-2025
|
||||
Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
@@ -38,10 +38,8 @@ print_normal_script (struct change *script)
|
||||
static void
|
||||
print_normal_hunk (struct change *hunk)
|
||||
{
|
||||
lin first0, last0, first1, last1;
|
||||
register lin i;
|
||||
|
||||
/* Determine range of line numbers involved in each file. */
|
||||
lin first0, last0, first1, last1;
|
||||
enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
|
||||
if (!changes)
|
||||
return;
|
||||
@@ -50,23 +48,21 @@ 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);
|
||||
|
||||
/* Print the lines that the first file has. */
|
||||
if (changes & OLD)
|
||||
{
|
||||
if (first0 <= last0)
|
||||
set_color_context (DELETE_CONTEXT);
|
||||
for (i = first0; i <= last0; i++)
|
||||
for (lin i = first0; i <= last0; i++)
|
||||
{
|
||||
print_1_line_nl ("<", &files[0].linbuf[i], true);
|
||||
if (i == last0)
|
||||
set_color_context (RESET_CONTEXT);
|
||||
if (files[0].linbuf[i + 1][-1] == '\n')
|
||||
set_color_context (DELETE_CONTEXT);
|
||||
print_1_line_nl ("<", &curr.file[0].linbuf[i], true);
|
||||
set_color_context (RESET_CONTEXT);
|
||||
if (curr.file[0].linbuf[i + 1][-1] == '\n')
|
||||
putc ('\n', outfile);
|
||||
}
|
||||
}
|
||||
@@ -77,14 +73,12 @@ print_normal_hunk (struct change *hunk)
|
||||
/* Print the lines that the second file has. */
|
||||
if (changes & NEW)
|
||||
{
|
||||
if (first1 <= last1)
|
||||
set_color_context (ADD_CONTEXT);
|
||||
for (i = first1; i <= last1; i++)
|
||||
for (lin i = first1; i <= last1; i++)
|
||||
{
|
||||
print_1_line_nl (">", &files[1].linbuf[i], true);
|
||||
if (i == last1)
|
||||
set_color_context (RESET_CONTEXT);
|
||||
if (files[1].linbuf[i + 1][-1] == '\n')
|
||||
set_color_context (ADD_CONTEXT);
|
||||
print_1_line_nl (">", &curr.file[1].linbuf[i], true);
|
||||
set_color_context (RESET_CONTEXT);
|
||||
if (curr.file[1].linbuf[i + 1][-1] == '\n')
|
||||
putc ('\n', outfile);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/* sdiff-format output routines for GNU DIFF.
|
||||
|
||||
Copyright (C) 1991-1993, 1998, 2001-2002, 2004, 2009-2013, 2015-2017 Free
|
||||
Copyright (C) 1991-1993, 1998, 2001-2002, 2004, 2009-2013, 2015-2025 Free
|
||||
Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
#include "diff.h"
|
||||
|
||||
#include <wchar.h>
|
||||
#include <mcel.h>
|
||||
|
||||
static void print_sdiff_common_lines (lin, lin);
|
||||
static void print_sdiff_hunk (struct change *);
|
||||
@@ -37,157 +37,169 @@ 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. */
|
||||
|
||||
static size_t
|
||||
tab_from_to (size_t from, size_t to)
|
||||
static intmax_t
|
||||
tab_from_to (intmax_t from, intmax_t to)
|
||||
{
|
||||
FILE *out = outfile;
|
||||
size_t tab;
|
||||
size_t tab_size = tabsize;
|
||||
|
||||
if (!expand_tabs)
|
||||
for (tab = from + tab_size - from % tab_size; tab <= to; tab += tab_size)
|
||||
{
|
||||
putc ('\t', out);
|
||||
from = tab;
|
||||
}
|
||||
{
|
||||
intmax_t tab_size = tabsize;
|
||||
for (intmax_t tab = from + tab_size - from % tab_size;
|
||||
tab <= to; tab += tab_size)
|
||||
{
|
||||
putc ('\t', out);
|
||||
from = tab;
|
||||
}
|
||||
}
|
||||
while (from++ < to)
|
||||
putc (' ', out);
|
||||
return to;
|
||||
}
|
||||
|
||||
/* Print the text for half an sdiff line. This means truncate to
|
||||
width observing tabs, and trim a trailing newline. Return the
|
||||
last column written (not the number of chars). */
|
||||
OUT_BOUND columns, observing tabs, and trim a trailing newline.
|
||||
Return the presumed column position on the output device after
|
||||
the write (not the number of chars). */
|
||||
|
||||
static size_t
|
||||
print_half_line (char const *const *line, size_t indent, size_t out_bound)
|
||||
static intmax_t
|
||||
print_half_line (char const *const *line, intmax_t indent, intmax_t out_bound)
|
||||
{
|
||||
FILE *out = outfile;
|
||||
register size_t in_position = 0;
|
||||
register size_t out_position = 0;
|
||||
register char const *text_pointer = line[0];
|
||||
register char const *text_limit = line[1];
|
||||
mbstate_t mbstate = { 0 };
|
||||
/* IN_POSITION is the current column position if we were outputting the
|
||||
entire line, i.e. ignoring OUT_BOUND. */
|
||||
intmax_t in_position = 0;
|
||||
/* OUT_POSITION is the current column position. It stays <= OUT_BOUND
|
||||
at any moment. */
|
||||
intmax_t out_position = 0;
|
||||
char const *text_pointer = line[0];
|
||||
char const *text_limit = line[1];
|
||||
|
||||
while (text_pointer < text_limit)
|
||||
{
|
||||
char const *tp0 = text_pointer;
|
||||
register char c = *text_pointer++;
|
||||
char c = *text_pointer++;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '\t':
|
||||
{
|
||||
size_t spaces = tabsize - in_position % tabsize;
|
||||
if (in_position == out_position)
|
||||
{
|
||||
case '\t':
|
||||
{
|
||||
intmax_t spaces = tabsize - in_position % tabsize;
|
||||
intmax_t tabstop;
|
||||
if (ckd_add (&tabstop, in_position, spaces))
|
||||
return out_position;
|
||||
if (in_position == out_position)
|
||||
{
|
||||
if (expand_tabs)
|
||||
{
|
||||
if (out_bound < tabstop)
|
||||
tabstop = out_bound;
|
||||
for (; out_position < tabstop; out_position++)
|
||||
putc (' ', out);
|
||||
}
|
||||
else
|
||||
if (tabstop < out_bound)
|
||||
{
|
||||
out_position = tabstop;
|
||||
putc (c, out);
|
||||
}
|
||||
}
|
||||
in_position = tabstop;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\r':
|
||||
{
|
||||
putc (c, out);
|
||||
tab_from_to (0, indent);
|
||||
in_position = out_position = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\b':
|
||||
if (in_position != 0 && --in_position < out_bound)
|
||||
{
|
||||
if (out_position <= in_position)
|
||||
/* Add spaces to make up for suppressed tab past out_bound. */
|
||||
for (; out_position < in_position; out_position++)
|
||||
putc (' ', out);
|
||||
else
|
||||
{
|
||||
out_position = in_position;
|
||||
putc (c, out);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
/* A byte that might start a multibyte character.
|
||||
Increase TEXT_POINTER, counting columns.
|
||||
Assume encoding errors have print width 1. */
|
||||
mcel_t g = mcel_scan (tp0, text_limit);
|
||||
int width = g.err ? 1 : c32width (g.ch);
|
||||
if (0 < width && ckd_add (&in_position, in_position, width))
|
||||
return out_position;
|
||||
|
||||
/* If there is room, output the bytes since TP0. */
|
||||
if (in_position <= out_bound)
|
||||
{
|
||||
size_t tabstop = out_position + spaces;
|
||||
if (expand_tabs)
|
||||
{
|
||||
if (out_bound < tabstop)
|
||||
tabstop = out_bound;
|
||||
for (; out_position < tabstop; out_position++)
|
||||
putc (' ', out);
|
||||
}
|
||||
else
|
||||
if (tabstop < out_bound)
|
||||
{
|
||||
out_position = tabstop;
|
||||
putc (c, out);
|
||||
}
|
||||
out_position = in_position;
|
||||
fwrite (tp0, 1, g.len, out);
|
||||
}
|
||||
in_position += spaces;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\r':
|
||||
{
|
||||
putc (c, out);
|
||||
tab_from_to (0, indent);
|
||||
in_position = out_position = 0;
|
||||
}
|
||||
break;
|
||||
text_pointer = tp0 + g.len;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\b':
|
||||
if (in_position != 0 && --in_position < out_bound)
|
||||
{
|
||||
if (out_position <= in_position)
|
||||
/* Add spaces to make up for suppressed tab past out_bound. */
|
||||
for (; out_position < in_position; out_position++)
|
||||
putc (' ', out);
|
||||
else
|
||||
{
|
||||
out_position = in_position;
|
||||
putc (c, out);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
wchar_t wc;
|
||||
size_t bytes = mbrtowc (&wc, tp0, text_limit - tp0, &mbstate);
|
||||
|
||||
if (0 < bytes && bytes < (size_t) -2)
|
||||
{
|
||||
int width = wcwidth (wc);
|
||||
if (0 < width)
|
||||
in_position += width;
|
||||
if (in_position <= out_bound)
|
||||
{
|
||||
out_position = in_position;
|
||||
fwrite (tp0, 1, bytes, stdout);
|
||||
}
|
||||
text_pointer = tp0 + bytes;
|
||||
break;
|
||||
}
|
||||
}
|
||||
FALLTHROUGH;
|
||||
case '\f':
|
||||
case '\v':
|
||||
if (in_position < out_bound)
|
||||
putc (c, out);
|
||||
break;
|
||||
|
||||
case ' ': case '!': case '"': case '#': case '%':
|
||||
case '&': case '\'': case '(': case ')': case '*':
|
||||
case '+': case ',': case '-': case '.': case '/':
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
case ':': case ';': case '<': case '=': case '>':
|
||||
case '?':
|
||||
case 'A': case 'B': case 'C': case 'D': case 'E':
|
||||
case 'F': case 'G': case 'H': case 'I': case 'J':
|
||||
case 'K': case 'L': case 'M': case 'N': case 'O':
|
||||
case 'P': case 'Q': case 'R': case 'S': case 'T':
|
||||
case 'U': case 'V': case 'W': case 'X': case 'Y':
|
||||
case 'Z':
|
||||
case '[': case '\\': case ']': case '^': case '_':
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e':
|
||||
case 'f': case 'g': case 'h': case 'i': case 'j':
|
||||
case 'k': case 'l': case 'm': case 'n': case 'o':
|
||||
case 'p': case 'q': case 'r': case 's': case 't':
|
||||
case 'u': case 'v': case 'w': case 'x': case 'y':
|
||||
case 'z': case '{': case '|': case '}': case '~':
|
||||
/* These characters are printable ASCII characters. */
|
||||
if (in_position++ < out_bound)
|
||||
/* Print width 1. */
|
||||
case ' ': case '!': case '"': case '#': case '$': case '%':
|
||||
case '&': case '\'': case '(': case ')': case '*':
|
||||
case '+': case ',': case '-': case '.': case '/':
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
case ':': case ';': case '<': case '=': case '>':
|
||||
case '?': case '@':
|
||||
case 'A': case 'B': case 'C': case 'D': case 'E':
|
||||
case 'F': case 'G': case 'H': case 'I': case 'J':
|
||||
case 'K': case 'L': case 'M': case 'N': case 'O':
|
||||
case 'P': case 'Q': case 'R': case 'S': case 'T':
|
||||
case 'U': case 'V': case 'W': case 'X': case 'Y':
|
||||
case 'Z':
|
||||
case '[': case '\\': case ']': case '^': case '_': case '`':
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e':
|
||||
case 'f': case 'g': case 'h': case 'i': case 'j':
|
||||
case 'k': case 'l': case 'm': case 'n': case 'o':
|
||||
case 'p': case 'q': case 'r': case 's': case 't':
|
||||
case 'u': case 'v': case 'w': case 'x': case 'y':
|
||||
case 'z': case '{': case '|': case '}': case '~':
|
||||
if (ckd_add (&in_position, in_position, 1))
|
||||
return out_position;
|
||||
if (in_position <= out_bound)
|
||||
{
|
||||
out_position = in_position;
|
||||
putc (c, out);
|
||||
}
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
return out_position;
|
||||
}
|
||||
/* Print width 0. */
|
||||
case '\0': case '\a': case '\f': case '\v':
|
||||
if (in_position <= out_bound)
|
||||
putc (c, out);
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
return out_position;
|
||||
}
|
||||
}
|
||||
|
||||
return out_position;
|
||||
@@ -199,12 +211,12 @@ print_half_line (char const *const *line, size_t indent, size_t out_bound)
|
||||
|
||||
static void
|
||||
print_1sdiff_line (char const *const *left, char sep,
|
||||
char const *const *right)
|
||||
char const *const *right)
|
||||
{
|
||||
FILE *out = outfile;
|
||||
size_t hw = sdiff_half_width;
|
||||
size_t c2o = sdiff_column2_offset;
|
||||
size_t col = 0;
|
||||
intmax_t hw = sdiff_half_width;
|
||||
intmax_t c2o = sdiff_column2_offset;
|
||||
intmax_t col = 0;
|
||||
bool put_newline = false;
|
||||
bool color_to_reset = false;
|
||||
|
||||
@@ -227,9 +239,9 @@ print_1sdiff_line (char const *const *left, char sep,
|
||||
|
||||
if (sep != ' ')
|
||||
{
|
||||
col = tab_from_to (col, (hw + c2o - 1) / 2) + 1;
|
||||
col = tab_from_to (col, (hw + c2o - 1) >> 1) + 1;
|
||||
if (sep == '|' && put_newline != (right[1][-1] == '\n'))
|
||||
sep = put_newline ? '/' : '\\';
|
||||
sep = put_newline ? '/' : '\\';
|
||||
putc (sep, out);
|
||||
}
|
||||
|
||||
@@ -237,10 +249,10 @@ print_1sdiff_line (char const *const *left, char sep,
|
||||
{
|
||||
put_newline |= right[1][-1] == '\n';
|
||||
if (**right != '\n')
|
||||
{
|
||||
col = tab_from_to (col, c2o);
|
||||
print_half_line (right, col, hw);
|
||||
}
|
||||
{
|
||||
col = tab_from_to (col, c2o);
|
||||
print_half_line (right, col, hw);
|
||||
}
|
||||
}
|
||||
|
||||
if (put_newline)
|
||||
@@ -259,22 +271,18 @@ print_sdiff_common_lines (lin limit0, lin limit1)
|
||||
if (!suppress_common_lines && (i0 != limit0 || i1 != limit1))
|
||||
{
|
||||
if (sdiff_merge_assist)
|
||||
{
|
||||
printint len0 = limit0 - i0;
|
||||
printint len1 = limit1 - i1;
|
||||
fprintf (outfile, "i%"pI"d,%"pI"d\n", len0, len1);
|
||||
}
|
||||
fprintf (outfile, "i%"pI"d,%"pI"d\n", limit0 - i0, limit1 - i1);
|
||||
|
||||
if (!left_column)
|
||||
{
|
||||
while (i0 != limit0 && i1 != limit1)
|
||||
print_1sdiff_line (&files[0].linbuf[i0++], ' ',
|
||||
&files[1].linbuf[i1++]);
|
||||
while (i1 != limit1)
|
||||
print_1sdiff_line (0, ')', &files[1].linbuf[i1++]);
|
||||
}
|
||||
{
|
||||
while (i0 != limit0 && i1 != limit1)
|
||||
print_1sdiff_line (&curr.file[0].linbuf[i0++], ' ',
|
||||
&curr.file[1].linbuf[i1++]);
|
||||
while (i1 != limit1)
|
||||
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;
|
||||
@@ -288,10 +296,8 @@ print_sdiff_common_lines (lin limit0, lin limit1)
|
||||
static void
|
||||
print_sdiff_hunk (struct change *hunk)
|
||||
{
|
||||
lin first0, last0, first1, last1;
|
||||
register lin i, j;
|
||||
|
||||
/* Determine range of line numbers involved in each file. */
|
||||
lin first0, last0, first1, last1;
|
||||
enum changes changes =
|
||||
analyze_hunk (hunk, &first0, &last0, &first1, &last1);
|
||||
if (!changes)
|
||||
@@ -301,17 +307,17 @@ print_sdiff_hunk (struct change *hunk)
|
||||
print_sdiff_common_lines (first0, first1);
|
||||
|
||||
if (sdiff_merge_assist)
|
||||
{
|
||||
printint len0 = last0 - first0 + 1;
|
||||
printint len1 = last1 - first1 + 1;
|
||||
fprintf (outfile, "c%"pI"d,%"pI"d\n", len0, len1);
|
||||
}
|
||||
fprintf (outfile, "c%"pI"d,%"pI"d\n",
|
||||
last0 - first0 + 1,
|
||||
last1 - first1 + 1);
|
||||
|
||||
/* Print "xxx | xxx " lines. */
|
||||
if (changes == CHANGED)
|
||||
{
|
||||
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;
|
||||
@@ -320,16 +326,18 @@ print_sdiff_hunk (struct change *hunk)
|
||||
/* Print " > xxx " lines. */
|
||||
if (changes & NEW)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/* Print "xxx < " lines. */
|
||||
if (changes & OLD)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
/* System dependent declarations.
|
||||
|
||||
Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006, 2009-2013,
|
||||
2015-2025 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#define SYSTEM_INLINE _GL_EXTERN_INLINE
|
||||
#include "system.h"
|
||||
|
||||
/* Do struct stat *S, *T describe the same file? */
|
||||
bool
|
||||
same_file (struct stat const *s, struct stat const *t)
|
||||
{
|
||||
if (! SAME_INODE (*s, *t))
|
||||
{
|
||||
# if HAVE_STRUCT_STAT_ST_RDEV
|
||||
/* Two character special files describe the same device if st_rdev
|
||||
is the same, and likewise for block special devices.
|
||||
They have the same contents, so treat them as the same. */
|
||||
if (((S_ISCHR (s->st_mode) && S_ISCHR (t->st_mode))
|
||||
|| (S_ISBLK (s->st_mode) && S_ISBLK (t->st_mode)))
|
||||
&& s->st_rdev == t->st_rdev)
|
||||
return true;
|
||||
# endif
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Although POSIX says that two files are identical if st_ino and st_dev
|
||||
are the same, all too many file systems incorrectly assign the same
|
||||
(device, inode) pair to two distinct files, including:
|
||||
|
||||
- GNU/Linux NFS servers that export all local file systems as a
|
||||
single NFS file system, if a local (device, inode) pair collides
|
||||
with another one after hashing.
|
||||
|
||||
- GNU/Linux NFS servers that export Btrfs file systems with subvolumes,
|
||||
if the Btrfs (subvolume, inode) hashing function collides.
|
||||
See <https://lwn.net/Articles/866709/>.
|
||||
|
||||
- Qemu virtio-fs before Qemu 5.2 (2020); see
|
||||
<https://bugzilla.redhat.com/show_bug.cgi?id=1795362>.
|
||||
|
||||
- Network Appliance NFS servers in snapshot directories; see
|
||||
Network Appliance bug #195.
|
||||
|
||||
- ClearCase MVFS; see bug id ATRia04618.
|
||||
|
||||
Check whether two files that purport to be the same have the same
|
||||
attributes, to work around instances of this common bug.
|
||||
|
||||
Birthtime is special as st_birthtime is not portable, but when
|
||||
either birthtime is available comparing them should be definitive. */
|
||||
|
||||
#if (defined HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC \
|
||||
|| defined HAVE_STRUCT_STAT_ST_BIRTHTIM_TV_NSEC \
|
||||
|| defined HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC \
|
||||
|| (defined _WIN32 && ! defined __CYGWIN__))
|
||||
/* If either file has a birth time, comparing them is definitive. */
|
||||
struct timespec sbirth = get_stat_birthtime (s);
|
||||
struct timespec tbirth = get_stat_birthtime (t);
|
||||
if (0 <= sbirth.tv_nsec || 0 <= tbirth.tv_nsec)
|
||||
return timespec_cmp (sbirth, tbirth) == 0;
|
||||
#endif
|
||||
|
||||
/* Fall back on comparing other easily-obtainable attributes.
|
||||
Do not inspect all attributes, only attributes useful in checking
|
||||
for the bug. Check attributes most likely to differ first.
|
||||
|
||||
It's possible for two distinct files on a buggy file system to have
|
||||
the same attributes, but it's not worth slowing down all
|
||||
implementations (or complicating the configuration) to cater to
|
||||
these rare cases in buggy implementations.
|
||||
|
||||
It's also possible for the same file to appear to be two different
|
||||
files, e.g., because its permissions were changed between the two
|
||||
stat calls. In that case cmp and diff will do extra work
|
||||
to determine that the file contents are the same. */
|
||||
|
||||
return (get_stat_ctime_ns (s) == get_stat_ctime_ns (t)
|
||||
&& get_stat_mtime_ns (s) == get_stat_mtime_ns (t)
|
||||
&& s->st_ctime == t->st_ctime
|
||||
&& s->st_mtime == t->st_mtime
|
||||
&& s->st_size == t->st_size
|
||||
&& s->st_mode == t->st_mode
|
||||
&& s->st_uid == t->st_uid
|
||||
&& s->st_gid == t->st_gid
|
||||
&& s->st_nlink == t->st_nlink);
|
||||
}
|
||||
|
||||
/* Use this for code that could be used if diff ever cares about
|
||||
st_size for symlinks, which it doesn't now. */
|
||||
#define care_about_symlink_size false
|
||||
|
||||
/* Return the number of bytes in the file described by *S,
|
||||
or -1 if this cannot be determined reliably. */
|
||||
off_t
|
||||
stat_size (struct stat const *s)
|
||||
{
|
||||
mode_t mode = s->st_mode;
|
||||
off_t size = s->st_size;
|
||||
if (size < 0)
|
||||
return -1;
|
||||
if (! (S_ISREG (mode) || (care_about_symlink_size && S_ISLNK (mode))
|
||||
|| S_TYPEISSHM (s) || S_TYPEISTMO (s)))
|
||||
return -1;
|
||||
|
||||
#if (defined __linux__ || defined __CYGWIN__ || defined __FreeBSD__ \
|
||||
|| defined __NetBSD__ || defined _AIX)
|
||||
/* On some systems, /proc files with size zero are suspect. */
|
||||
if (size == 0)
|
||||
{
|
||||
static dev_t proc_dev;
|
||||
if (!proc_dev)
|
||||
{
|
||||
struct stat st;
|
||||
st.st_dev = 0;
|
||||
lstat ("/proc/self", &st);
|
||||
proc_dev = st.st_dev;
|
||||
}
|
||||
if (proc_dev && s->st_dev == proc_dev)
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
#if care_about_symlink_size && (defined __linux__ || defined __ANDROID__)
|
||||
/* Symlinks have suspect sizes on Linux kernels before 5.15,
|
||||
due to bugs in fscrypt. */
|
||||
if (S_ISLNK (mode))
|
||||
{
|
||||
static signed char symlink_size_ok;
|
||||
if (! symlink_size_ok)
|
||||
{
|
||||
struct utsname name;
|
||||
uname (&name);
|
||||
char *p = name.release;
|
||||
symlink_size_ok = ((p[1] != '.' || '5' < p[0]
|
||||
|| (p[0] == '5'
|
||||
&& ('1' <= p[2] && p[2] <= '9')
|
||||
&& ('0' <= p[3] && p[3] <= '9')
|
||||
&& ('5' <= p[3]
|
||||
|| ('0' <= p[4] && p[4] <= '9'))))
|
||||
? 1 : -1);
|
||||
}
|
||||
if (symlink_size_ok < 0)
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return size;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/* System dependent declarations.
|
||||
|
||||
Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006, 2009-2013,
|
||||
2015-2017 Free Software Foundation, Inc.
|
||||
2015-2025 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU DIFF.
|
||||
|
||||
@@ -20,33 +20,15 @@
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* Use this to suppress gcc's "...may be used before initialized" warnings. */
|
||||
#ifdef lint
|
||||
# define IF_LINT(Code) Code
|
||||
#else
|
||||
# define IF_LINT(Code) /* empty */
|
||||
#endif
|
||||
|
||||
/* Define '__attribute__' and 'volatile' first
|
||||
so that they're used consistently in all system includes. */
|
||||
#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 6) || __STRICT_ANSI__
|
||||
# define __attribute__(x)
|
||||
#endif
|
||||
|
||||
#include <verify.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include "stat-macros.h"
|
||||
|
||||
#ifndef STAT_BLOCKSIZE
|
||||
# if HAVE_STRUCT_STAT_ST_BLKSIZE
|
||||
# define STAT_BLOCKSIZE(s) ((s).st_blksize)
|
||||
# else
|
||||
# define STAT_BLOCKSIZE(s) (8 * 1024)
|
||||
# endif
|
||||
#endif
|
||||
#include <stat-macros.h>
|
||||
#include <stat-size.h>
|
||||
#include <stat-time.h>
|
||||
#include <timespec.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -61,24 +43,14 @@
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#define EXIT_TROUBLE 2
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <locale.h>
|
||||
#include <stdbit.h>
|
||||
#include <stdckdint.h>
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <string.h>
|
||||
#if ! HAVE_STRCASECOLL
|
||||
# if HAVE_STRICOLL || defined stricoll
|
||||
# define strcasecoll(a, b) stricoll (a, b)
|
||||
# else
|
||||
# define strcasecoll(a, b) strcasecmp (a, b) /* best we can do */
|
||||
# endif
|
||||
#endif
|
||||
#if ! (HAVE_STRCASECMP || defined strcasecmp)
|
||||
int strcasecmp (char const *, char const *);
|
||||
#endif
|
||||
|
||||
#include <gettext.h>
|
||||
#if ! ENABLE_NLS
|
||||
@@ -91,17 +63,6 @@ int strcasecmp (char const *, char const *);
|
||||
#define _(msgid) gettext (msgid)
|
||||
#define N_(msgid) msgid
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
/* ISDIGIT differs from isdigit, as follows:
|
||||
- Its arg may be any int or unsigned int; it need not be an unsigned char.
|
||||
- It's guaranteed to evaluate its argument exactly once.
|
||||
- It's typically faster.
|
||||
POSIX 1003.1-2001 says that only '0' through '9' are digits.
|
||||
Prefer ISDIGIT to isdigit unless it's important to use the locale's
|
||||
definition of 'digit' even when the host does not conform to POSIX. */
|
||||
#define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <signal.h>
|
||||
@@ -109,55 +70,50 @@ int strcasecmp (char const *, char const *);
|
||||
# define SIGCHLD SIGCLD
|
||||
#endif
|
||||
|
||||
#undef MIN
|
||||
#undef MAX
|
||||
#define MIN(a, b) ((a) <= (b) ? (a) : (b))
|
||||
#define MAX(a, b) ((a) >= (b) ? (a) : (b))
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <attribute.h>
|
||||
#include <idx.h>
|
||||
#include <intprops.h>
|
||||
#include "propername.h"
|
||||
#include <minmax.h>
|
||||
#include <propername.h>
|
||||
#include <same-inode.h>
|
||||
|
||||
#include "version.h"
|
||||
|
||||
/* Evaluate an assertion E that is guaranteed to be true.
|
||||
E should not crash, loop forever, or have side effects. */
|
||||
#if defined DDEBUG && !defined NDEBUG
|
||||
/* Abort the program if E is false. */
|
||||
# include <assert.h>
|
||||
# define dassert(e) assert (e)
|
||||
#else
|
||||
/* The compiler can assume E, as behavior is undefined otherwise. */
|
||||
# define dassert(e) assume (e)
|
||||
#endif
|
||||
|
||||
#ifndef SYSTEM_INLINE
|
||||
# define SYSTEM_INLINE _GL_INLINE
|
||||
#endif
|
||||
|
||||
_GL_INLINE_HEADER_BEGIN
|
||||
|
||||
/* Type used for fast comparison of several bytes at a time.
|
||||
This used to be uintmax_t, but changing it to size_t
|
||||
The type is a pointer to an incomplete struct,
|
||||
so that its values are less likely to be misused.
|
||||
This used to be uintmax_t, but changing it to the size of a pointer
|
||||
made plain 'cmp' 90% faster (GCC 4.8.1, x86). */
|
||||
|
||||
#ifndef word
|
||||
# define word size_t
|
||||
typedef struct incomplete *word;
|
||||
#endif
|
||||
|
||||
/* The signed integer type of a line number. Since files are read
|
||||
into main memory, ptrdiff_t should be wide enough. */
|
||||
into main memory, ptrdiff_t should be wide enough. pI is for
|
||||
printing line numbers. */
|
||||
|
||||
typedef ptrdiff_t lin;
|
||||
#define LIN_MAX PTRDIFF_MAX
|
||||
|
||||
/* The signed integer type for printing line numbers, and its printf
|
||||
length modifier. This is not simply ptrdiff_t, to cater to older
|
||||
and/or nonstandard C libraries where "l" works but "ll" and "t" do
|
||||
not, or where 'long' is too narrow and "ll" works but "t" does not. */
|
||||
|
||||
#if LIN_MAX <= LONG_MAX
|
||||
typedef long int printint;
|
||||
# define pI "l"
|
||||
#elif LIN_MAX <= LLONG_MAX
|
||||
typedef long long int printint;
|
||||
# define pI "ll"
|
||||
#else
|
||||
typedef ptrdiff_t printint;
|
||||
# define pI "t"
|
||||
#endif
|
||||
|
||||
verify (TYPE_SIGNED (lin));
|
||||
verify (TYPE_SIGNED (printint));
|
||||
verify (LIN_MAX == TYPE_MAXIMUM (lin));
|
||||
verify (LIN_MAX <= TYPE_MAXIMUM (printint));
|
||||
|
||||
/* Limit so that 2 * CONTEXT + 1 does not overflow. */
|
||||
|
||||
#define CONTEXT_MAX ((LIN_MAX - 1) / 2)
|
||||
|
||||
#define pI "t"
|
||||
static_assert (LIN_MAX == IDX_MAX);
|
||||
|
||||
/* This section contains POSIX-compliant defaults for macros
|
||||
that are meant to be overridden by hand in config.h as needed. */
|
||||
@@ -174,67 +130,20 @@ verify (LIN_MAX <= TYPE_MAXIMUM (printint));
|
||||
# define NULL_DEVICE "/dev/null"
|
||||
#endif
|
||||
|
||||
/* Do struct stat *S, *T describe the same special file? */
|
||||
#ifndef same_special_file
|
||||
# if HAVE_STRUCT_STAT_ST_RDEV && defined S_ISBLK && defined S_ISCHR
|
||||
# define same_special_file(s, t) \
|
||||
(((S_ISBLK ((s)->st_mode) && S_ISBLK ((t)->st_mode)) \
|
||||
|| (S_ISCHR ((s)->st_mode) && S_ISCHR ((t)->st_mode))) \
|
||||
&& (s)->st_rdev == (t)->st_rdev)
|
||||
# else
|
||||
# define same_special_file(s, t) 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Do struct stat *S, *T describe the same file? Answer -1 if unknown. */
|
||||
#ifndef same_file
|
||||
# define same_file(s, t) \
|
||||
((((s)->st_ino == (t)->st_ino) && ((s)->st_dev == (t)->st_dev)) \
|
||||
|| same_special_file (s, t))
|
||||
#endif
|
||||
|
||||
/* Do struct stat *S, *T have the same file attributes?
|
||||
|
||||
POSIX says that two files are identical if st_ino and st_dev are
|
||||
the same, but many file systems incorrectly assign the same (device,
|
||||
inode) pair to two distinct files, including:
|
||||
|
||||
- GNU/Linux NFS servers that export all local file systems as a
|
||||
single NFS file system, if a local device number (st_dev) exceeds
|
||||
255, or if a local inode number (st_ino) exceeds 16777215.
|
||||
|
||||
- Network Appliance NFS servers in snapshot directories; see
|
||||
Network Appliance bug #195.
|
||||
|
||||
- ClearCase MVFS; see bug id ATRia04618.
|
||||
|
||||
Check whether two files that purport to be the same have the same
|
||||
attributes, to work around instances of this common bug. Do not
|
||||
inspect all attributes, only attributes useful in checking for this
|
||||
bug.
|
||||
|
||||
It's possible for two distinct files on a buggy file system to have
|
||||
the same attributes, but it's not worth slowing down all
|
||||
implementations (or complicating the configuration) to cater to
|
||||
these rare cases in buggy implementations. */
|
||||
|
||||
#ifndef same_file_attributes
|
||||
# define same_file_attributes(s, t) \
|
||||
((s)->st_mode == (t)->st_mode \
|
||||
&& (s)->st_nlink == (t)->st_nlink \
|
||||
&& (s)->st_uid == (t)->st_uid \
|
||||
&& (s)->st_gid == (t)->st_gid \
|
||||
&& (s)->st_size == (t)->st_size \
|
||||
&& (s)->st_mtime == (t)->st_mtime \
|
||||
&& (s)->st_ctime == (t)->st_ctime)
|
||||
#endif
|
||||
|
||||
#define STREQ(a, b) (strcmp (a, b) == 0)
|
||||
|
||||
#ifndef FALLTHROUGH
|
||||
# if __GNUC__ < 7
|
||||
# define FALLTHROUGH ((void) 0)
|
||||
# else
|
||||
# define FALLTHROUGH __attribute__ ((__fallthrough__))
|
||||
# endif
|
||||
#endif
|
||||
/* Return the floor of the log base 2 of N. Return -1 if N is zero. */
|
||||
SYSTEM_INLINE int floor_log2 (idx_t n)
|
||||
{
|
||||
static_assert (IDX_MAX <= SIZE_MAX);
|
||||
size_t s = n;
|
||||
int w = stdc_bit_width (s);
|
||||
return w - 1;
|
||||
}
|
||||
|
||||
_GL_INLINE_HEADER_END
|
||||
|
||||
extern bool same_file (struct stat const *, struct stat const *)
|
||||
ATTRIBUTE_PURE;
|
||||
extern off_t stat_size (struct stat const *)
|
||||
ATTRIBUTE_PURE;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user