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:
2026-07-11 09:47:59 +03:00
parent 9bbc38fe60
commit 068a1ca63e
1609 changed files with 256532 additions and 299729 deletions
@@ -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
+182 -197
View File
@@ -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
+135 -81
View File
@@ -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
+202 -211
View File
@@ -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;
+39 -42
View File
@@ -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]);
}
}
+203 -214
View File
@@ -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
+160 -152
View File
@@ -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;
}
+54 -145
View File
@@ -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