Compare commits

...

39 Commits

Author SHA1 Message Date
Jim Meyering
bbc1bdb771 maint: update copyright dates 2026-01-02 16:52:26 -08:00
Jim Meyering
301eca636b build: update gnulib to latest, and update bootstrap 2026-01-02 16:51:54 -08:00
Paul Eggert
33ce4260b8 diff: tweak -q, -s doc
Problem reported by Dan Jacobsen (bug#79959).
* src/diff.c (option_help_msgid): Tweak.
2025-12-07 09:41:44 -08:00
Paul Eggert
6ad4a619d4 diff: tweak signal initialization
* src/syncsig.c (syncsig_install): No need to initialize
all of signal_count; just the catchable signals.
2025-12-03 09:40:35 -08:00
Paul Eggert
916b50fe96 diff: no need to avoid localcharset
This fixes builds on macOS 12–14.
Problem reported by Bruno Haible (Bug#79887).
* bootstrap.conf (avoided_gnulib_modules): Remove localcharset.
2025-12-03 09:40:35 -08:00
Paul Eggert
a1432a6216 build: update gnulib submodule to latest
* src/context.c (print_contenxt_label):
Adjust to recent Gnulib changes to nstrftime API.
2025-11-02 13:09:16 -08:00
Paul Eggert
a709a0a2bd diff: simplify sigaction configuration
* configure.ac: Remove checks for sigaction, sigprocmask, sigblock.
The sigprocmask and sigblock checks were not being used anyway.
* src/sdiff.c: Use (defined SA_NOCLDSTOP) instead of HAVE_SIGACTION.
This is what other modules do.
2025-11-02 13:09:16 -08:00
Paul Eggert
b4524ba9a6 diff: refactor and improve signal handling
This is so we can improve sdiff signal handling.
* bootstrap.conf: Add stdcountof-h.
* src/Makefile.am (diff_SOURCES): Add syncsig.c.
(noinst_HEADERS): Add syncsig.h.
* src/diff.c, src/util.c: Include syncsig.h.
* src/util.c: Move signal-related stuff from here ...
* src/syncsig.c: ... to here.
(syncsig_install, syncsig_cleanup):
Rename from install_signal_handlers, cleanup_signal_handlers.
All uses changed.  Handle some more signals.
Add an option to not handle stop-related signals.
(syncsig_poll, syncsig_deliver):
New functions, which are like the old process_signals
but in two pieces not one.  All uses changed.
(syncsig_install): New args FUN and ARG.
Return int on failure.  All callers changed.
* src/syncsig.h: New file.
2025-11-02 13:09:16 -08:00
Paul Eggert
6b9c726076 maint: use our textdomain for Gnulib
Support diffutils’ traditional way of getting translations,
by telling Gnulib to use diffutils’ message catalog.
* configure.ac (GNULIB_TEXT_DOMAIN): New macro.
* src/cmp.c, src/diff.c, src/diff3.c, src/sdiff.c (main):
Don’t call bindtextdomain ("gnulib", GNULIB_LOCALEDIR)
as the existing bindtextdomain (PACKAGE, LOCALEDIR) call suffices.
2025-09-09 10:23:01 -07:00
Paul Eggert
3ccfcd8cd7 build: update gnulib submodule to latest 2025-09-09 10:23:01 -07:00
Paul Eggert
096a3b29b5 maint: remove po/en.po
* po/en.po: Remove.  It wasn’t being used, and we’re now doing its
intent in a less-hassly way.
2025-09-09 10:23:01 -07:00
Paul Eggert
dc6dc9147f cmp: improve LC_MESSAGES test
* src/cmp.c (hard_locale_LC_MESSAGES): Use setlocale, not gettext,
to decide whether the messages might not be those of the C or
POSIX locale.  This is a more reliable way to test whether
the locale is something like en_US.utf8, a locale that does
not have a translation catalog but is not the C locale.
2025-09-09 10:23:01 -07:00
Paul Eggert
cf5648869a maint: use need-formatstring-macros
* configure.ac: Pass need-formatstring-macros, not merely
need-ngettext, to AM_GNU_GETTEXT.  This is mostly for show, as
diffutils has used format string macros for years and since nobody
uses ancient gettext any more nobody has noticed a problem.
2025-09-09 10:23:01 -07:00
Paul Eggert
e124541148 maint: reduce Gnulib module usage
Recentish changes to Gnulib have pulled in more dependencies
on multithreading, locking, and whatnot.  Revamp to remove
these unwanted dependencies.
* bootstrap.conf: Also avoid hard-locale, localcharset,
localename-unsafe, localename-unsafe-limited.
Stop avoiding localename.
(avoided_gnulib_tests): New var.  Avoid these tests too.
(gnulib-modules): Remove hard-locale, nstrftime.
Add nstrftime-limited.
* configure.ac (gl_cv_func_mbrtowc_C_locale_sans_EILSEQ)
(gl_cv_func_mbrtoc32_C_locale_sans_EILSEQ):
New vars, so that we do not worry about multibyte C locales.
(gl_THREADLIB_DEFAULT_NO): New macro.
Not sure how much it helps, but it can’t hurt.
(SUPPORT_NON_GREG_CALENDARS_IN_STRFTIME): New macro.
* src/cmp.c: Do not include hard-locale.h.
(hard_locale_LC_MESSAGES): Assume that LC_MESSAGES is hard
if and only if "(C)" gets translated.  This drags in fewer
dependencies than calling hard_locale.
* src/diff.c: Include strftime.h instead of hard-locale.h.
(hard_locale_LC_TIME): New function, that uses nstrftime
to infer whether the time locale is hard.
(main): Use it instead of hard_locale.

maint: default Gnulib to no multithreading

* configure.ac: Define gl_THREADLIB_DEFAULT_NO
so that Gnulib defaults to no multithreading.
2025-09-05 14:47:08 -07:00
Paul Eggert
42083759b6 build: update gnulib submodule to latest 2025-09-05 14:47:08 -07:00
Paul Eggert
c08704493d sdiff: simplify give_help slightly
* src/sdiff.c (give_help): Simplify by using fputs instead of fprintf.
2025-09-05 14:47:08 -07:00
Paul Eggert
f3f02235de diff: simplify usage slightly
* src/diff.c (usage): Simplify by omitting a need to call putchar.
2025-09-05 14:47:08 -07:00
Bruno Haible
0cba18b965 build: Update after gnulib changed
* gnulib-tests/Makefile.am: Initialize AM_CFLAGS.
2025-09-05 14:47:08 -07:00
Paul Eggert
2591992037 diff: reduce nstrftime dependency
* configure.ac (REQUIRE_GNUISH_STRFTIME_AM_PM):
Define to 0, since we don’t use AM or PM indicators.
2025-04-27 23:42:49 -07:00
Paul Eggert
c191460adf sdiff: refactor simplification
* src/sdiff.c (skip_white): Simplify a loop.
(edit): Redo nested switches to avoid duplicate code.
2025-04-27 23:42:49 -07:00
Paul Eggert
686357a40c sdiff: port back to C17-
* src/sdiff.c (edit): Do not use a label just before a statement.
Problem reported by Bruno Haible (Bug#78032).
2025-04-27 23:42:49 -07:00
Paul Eggert
49760e9f50 doc: update Autoconf version 2025-04-27 23:42:49 -07:00
Paul Eggert
33ee12e587 build: use system help2man
Instead of shipping an old ‘help2man’ that is not properly maintained,
use the system’s ‘help2man’.  It was already listed as a build
prerequisite, and being configured via AM_MISSING_PROG.
Problem for FreeBSD and NetBSD reported by Bruno Haible
<https://bugs.gnu.org/72235>.
* man/Makefile.am (EXTRA_DIST): Remove help2man.
($(dist_man1_MANS)): Do not depend on help2man.
Use $(HELP2MAN) instead.
* man/help2man: Remove.
2025-04-27 23:42:49 -07:00
Paul Eggert
45a4762bf3 sdiff: pacify gcc -flto -Wmaybe-uninitialized
* src/sdiff.c (edit): Portmanteauize two locals into one, which
arguably makes the code clearer, and anyway pacifies gcc -flto
-Wmaybe-uninitialized with gcc (GCC) 15.0.1 20250329 (Red Hat
15.0.1-0) x86-64 (Bug#78019).
2025-04-23 11:46:37 -07:00
Paul Eggert
80053ab7b5 sdiff: continue → break
* src/sdiff.c (edit): Don’t use ‘continue’ when a simple
break from the switch will do.
2025-04-23 11:46:37 -07:00
Jim Meyering
d05765a8f1 maint: post-release administrivia
* NEWS: Add header line for next release.
* .prev-version: Record previous version.
* cfg.mk (old_NEWS_hash): Auto-update.
2025-04-08 19:41:07 -07:00
Jim Meyering
16681a3cbc version 3.12
* NEWS: Record release date.
2025-04-08 19:37:19 -07:00
Jim Meyering
6c92d5fb8f tests: cmp: increase timeout to avoid failure on a very busy-IO system
* tests/cmp: Increase timeout from 0.1 to 0.4s, to avoid false-failure
on a system with lots of IO congestion. Reported by Nelson Beebe
in https://lists.gnu.org/r/diffutils-devel/2025-04/msg00007.html
2025-04-08 13:44:18 -07:00
Jim Meyering
7e53977313 build: update gnulib to latest; and update bootstrap 2025-04-04 14:23:49 -07:00
Jim Meyering
53ba74998b maint: ensure that new "make syntax-check"-run sc_codespell passes
* cfg.mk (codespell_ignore_words_list): Ignore false-positive "words".
2025-03-28 08:11:20 -07:00
Jim Meyering
f180b5f4d4 maint: rather than exempt "hight" as a spelling false-positive...
* src/diff3.c (output_diff3): Rename lowt,hight to low_t,high_t.
2025-03-27 21:34:25 -07:00
Paul Eggert
362a759cfb tests: test for diff -y crash
* tests/side-by-side-seq: New test.
* tests/Makefile.am (TESTS): Add it.
2025-02-28 23:14:35 -08:00
Paul Eggert
e9f8e6a439 diff: fix allocation typo leading to crashes
But reported by Nick Smallbone, with one-line fix by
Collin Funk <https://bugs.gnu.org/76613>.
* src/io.c (find_and_hash_each_line): Fix size computation.
2025-02-28 23:14:35 -08:00
Paul Eggert
58e734dedd tests: make seq replacement more available
This refactoring should let other future tests use ‘seq’.
* tests/diff3 (seq): Move from here ...
* tests/init.cfg: ... to here.
2025-02-28 23:14:35 -08:00
Paul Eggert
706116c651 maint: mention bug#76452 in NEWS 2025-02-21 23:10:06 -07:00
Collin Funk
6395d51a01 diff: add a test case for the recent empty file bug
* tests/empty-file: New file.
* tests/Makefile.am (TESTS): Add the test.
2025-02-21 23:03:38 -07:00
Paul Eggert
6ce0ebd033 diff: don't treat empty files as a different file type
Reported by Kate Deplaix <kit-ty-kate@outlook.com> in
<https://lists.gnu.org/r/bug-diffutils/2025-02/msg00005.html>.

* src/diff.c (compare_prepped_files): Don't rely on string
file type, as that might not agree with our idea of a file type.
2025-02-21 23:03:38 -07:00
Simon Josefsson
82cbd61848 cmp, diff, diff3, sdiff: support gnulib-l10n
* src/cmp.c (main): Call bindtextdomain for gnulib-l10n.
* src/diff.c (main): Likewise.
* src/diff3.c (main): Likewise.
* src/sdiff.c (main): Likewise.
2025-02-03 15:10:37 -08:00
Jim Meyering
41034e8398 maint: post-release administrivia
* NEWS: Add header line for next release.
* .prev-version: Record previous version.
* cfg.mk (old_NEWS_hash): Auto-update.
2025-02-02 20:52:15 -08:00
54 changed files with 713 additions and 1233 deletions

View File

@ -1 +1 @@
3.10
3.12

View File

@ -33,7 +33,7 @@ Patrick D'Cruze
Eli Zaretskii
Copyright (C) 2001, 2006, 2009-2013, 2015-2025 Free Software Foundation, Inc.
Copyright (C) 2001, 2006, 2009-2013, 2015-2026 Free Software Foundation, Inc.
This file is part of GNU diffutils.

View File

@ -4265,7 +4265,7 @@ Thu Nov 3 16:30:24 1988 Randall Smith (randy at gluteus.ai.mit.edu)
-----
Copyright (C) 1988-1994, 1997-2002, 2004, 2006, 2009-2013, 2015-2025
Copyright (C) 1988-1994, 1997-2002, 2004, 2006, 2009-2013, 2015-2026
Free Software Foundation, Inc.
Copying and distribution of this file, with or without

View File

@ -568,7 +568,7 @@ Then just open the index.html file (in the generated lcov-html directory)
in your favorite web browser.
========================================================================
Copyright (C) 2009-2013, 2015-2025 Free Software Foundation, Inc.
Copyright (C) 2009-2013, 2015-2026 Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or

View File

@ -1,6 +1,6 @@
# Main Automakefile for GNU diffutils.
# Copyright (C) 2001-2002, 2004, 2006, 2009-2013, 2015-2025 Free Software
# Copyright (C) 2001-2002, 2004, 2006, 2009-2013, 2015-2026 Free Software
# Foundation, Inc.
# This program is free software: you can redistribute it and/or modify

17
NEWS
View File

@ -1,5 +1,20 @@
GNU diffutils NEWS -*- outline -*-
* Noteworthy changes in release ?.? (????-??-??) [?]
* Noteworthy changes in release 3.12 (2025-04-08) [stable]
** Bug fixes
diff -r no longer merely summarizes when comparing an empty regular
file to a nonempty regular file.
[bug#76452 introduced in 3.11]
diff -y no longer crashes when given nontrivial differences.
[bug#76613 introduced in 3.11]
* Noteworthy changes in release 3.11 (2025-02-02) [stable]
** Improvements
@ -533,7 +548,7 @@ User-visible changes in version 2.0:
Copyright (C) 1993-1994, 1998, 2001-2002, 2004, 2006, 2009-2013, 2015-2025 Free
Copyright (C) 1993-1994, 1998, 2001-2002, 2004, 2006, 2009-2013, 2015-2026 Free
Software Foundation, Inc.
This file is part of GNU Diffutils.

15
README
View File

@ -30,19 +30,12 @@ installation. If you have an older version of libiconv, please
upgrade to the latest one; see <ftp://ftp.gnu.org/gnu/libiconv/>. If
the problem seems isolated to diffutils, though, please report a bug.
This program requires a Standard C compiler (C89 or later). If you
This program requires a Standard C compiler (C99 or later). If you
have a nonstandard compiler, please install GCC first.
If you make changes to the source code, you may need appropriate
versions of GNU build tools to regenerate the intermediate files. The
following versions were used to generate the intermediate files in
this distribution:
* Autoconf 2.59 <ftp://ftp.gnu.org/gnu/autoconf/autoconf-2.59.tar.gz>
* Automake 1.8.3 <ftp://ftp.gnu.org/gnu/automake/automake-1.8.3.tar.gz>
* gettext 0.14.1 <ftp://ftp.gnu.org/gnu/gettext/gettext-0.14.1.tar.gz>
* help2man 1.33 <ftp://ftp.gnu.org/gnu/help2man/help2man-1.33.1.tar.gz>
* Texinfo 4.7 <ftp://ftp.gnu.org/gnu/texinfo/texinfo-4.7.tar.gz>
versions of GNU build tools to regenerate the intermediate files.
See README-prereq for details.
For any copyright year range specified as YYYY-ZZZZ in this package
note that the range specifies every single year in that closed interval.
@ -51,7 +44,7 @@ Please report bugs to <bug-diffutils@gnu.org>.
-----
Copyright (C) 1992, 1998, 2001-2002, 2004, 2009-2013, 2015-2025 Free Software
Copyright (C) 1992, 1998, 2001-2002, 2004, 2009-2013, 2015-2026 Free Software
Foundation, Inc.
This file is part of GNU Diffutils.

View File

@ -119,7 +119,7 @@ diffutils:
-----
Copyright (C) 2002-2025 Free Software Foundation, Inc.
Copyright (C) 2002-2026 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

View File

@ -24,14 +24,14 @@ too-old versions first as they may be needed to build newer versions.
Here is an example of how to build a program from source. This
example is for Autoconf; a similar approach should work for the other
developer prerequisites. This example assumes Autoconf 2.71; it
developer prerequisites. This example assumes Autoconf 2.72; it
should be OK to use a later version of Autoconf, if available.
prefix=$HOME/prefix # (or wherever else you choose)
export PATH=$prefix/bin:$PATH
wget https://ftp.gnu.org/pub/gnu/autoconf/autoconf-2.71.tar.gz
gzip -d <autoconf-2.71.tar.gz | tar xf -
cd autoconf-2.71
wget https://ftp.gnu.org/pub/gnu/autoconf/autoconf-2.72.tar.gz
gzip -d <autoconf-2.72.tar.gz | tar xf -
cd autoconf-2.72
./configure --prefix=$prefix
make install

View File

@ -3,9 +3,9 @@
# Bootstrap this package from checked-out sources.
scriptversion=2024-07-04.10; # UTC
scriptversion=2025-06-10.02; # UTC
# Copyright (C) 2003-2025 Free Software Foundation, Inc.
# Copyright (C) 2003-2026 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
@ -37,9 +37,9 @@ medir=`dirname "$me"`
# A library of shell functions for autopull.sh, autogen.sh, and bootstrap.
scriptlibversion=2025-01-26.03; # UTC
scriptlibversion=2025-12-04.19; # UTC
# Copyright (C) 2003-2025 Free Software Foundation, Inc.
# Copyright (C) 2003-2026 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
@ -72,7 +72,7 @@ export LC_ALL
# Honor $PERL, but work even if there is none.
PERL="${PERL-perl}"
default_gnulib_url=https://git.savannah.gnu.org/git/gnulib.git
default_gnulib_url=https://https.git.savannah.gnu.org/git/gnulib.git
# Copyright year, for the --version output.
copyright_year=`echo "$scriptlibversion" | sed -e 's/[^0-9].*//'`
@ -580,17 +580,11 @@ prepare_GNULIB_SRCDIR ()
|| cleanup_gnulib
else
# GNULIB_REFDIR is not set or not usable. Ignore it.
shallow=
shallow='--depth 2'
if test -z "$GNULIB_REVISION"; then
if git clone -h 2>&1 | grep -- --depth > /dev/null; then
shallow='--depth 2'
fi
git clone $shallow "$gnulib_url" "$gnulib_path" \
|| cleanup_gnulib
else
if git fetch -h 2>&1 | grep -- --depth > /dev/null; then
shallow='--depth 2'
fi
# Only want a shallow checkout of $GNULIB_REVISION, but git does not
# support cloning by commit hash. So attempt a shallow fetch by
# commit hash to minimize the amount of data downloaded and changes
@ -601,7 +595,10 @@ prepare_GNULIB_SRCDIR ()
# to fetching all commits.
# $GNULIB_REVISION can be a commit id, a tag name, or a branch name.
mkdir -p "$gnulib_path"
git -C "$gnulib_path" init
# Use a -c option to silence an annoying message
# "hint: Using 'master' as the name for the initial branch."
# (cf. <https://stackoverflow.com/questions/65524512/>).
git -C "$gnulib_path" -c init.defaultBranch=master init
git -C "$gnulib_path" remote add origin "$gnulib_url"
if git -C "$gnulib_path" fetch $shallow origin "$GNULIB_REVISION"
then
@ -864,7 +861,8 @@ autopull()
elif check_exists git-merge-changelog; then
echo "$0: initializing git-merge-changelog driver"
git config merge.merge-changelog.name 'GNU-style ChangeLog merge driver'
git config merge.merge-changelog.driver 'git-merge-changelog %O %A %B'
git config merge.merge-changelog.driver \
'git-merge-changelog %O %A %B "%Y"'
else
echo "$0: consider installing git-merge-changelog from gnulib"
fi
@ -1340,7 +1338,7 @@ autogen()
# Invoke autoreconf with --force --install to ensure upgrades of tools
# such as ylwrap.
AUTORECONFFLAGS="--verbose --install --force -I $m4_base $ACLOCAL_FLAGS"
AUTORECONFFLAGS="--verbose --install --force $ACLOCAL_FLAGS"
AUTORECONFFLAGS="$AUTORECONFFLAGS --no-recursive"
# Tell autoreconf not to invoke autopoint or libtoolize; they were run above.
@ -1416,7 +1414,7 @@ autogen()
# Local Variables:
# eval: (add-hook 'before-save-hook 'time-stamp nil t)
# time-stamp-start: "scriptlibversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-format: "%Y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:
@ -1627,7 +1625,7 @@ fi
# Local Variables:
# eval: (add-hook 'before-save-hook 'time-stamp nil t)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-format: "%Y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

View File

@ -1,6 +1,6 @@
# Bootstrap configuration.
# Copyright (C) 2006-2013, 2015-2025 Free Software Foundation, Inc.
# Copyright (C) 2006-2013, 2015-2026 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
@ -16,12 +16,31 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
avoided_gnulib_modules='
--avoid=localename
--avoid=hard-locale
--avoid=localename-unsafe
--avoid=localename-unsafe-limited
--avoid=lock-tests
--avoid=mbuiter
--avoid=mbuiterf
--avoid=setlocale
'
# These tests depend on localcharset, which we avoid.
avoided_gnulib_tests='
--avoid=c32width-tests
--avoid=quotearg-simple-tests
--avoid=regex-tests
--avoid=wcwidth-tests
'
# These test whether all bytes are valid in the C locale,
# which we don't care about.
avoided_gnulib_tests=$avoided_gnulib_tests'
--avoid=btoc32-tests
--avoid=btowc-tests
--avoid=mbrtoc32-tests
--avoid=mbrtowc-tests
--avoid=mbsrtoc32s-tests
--avoid=mbsrtowcs-tests
'
# gnulib modules used by this package.
gnulib_modules='
@ -66,7 +85,6 @@ gnu-make
gnu-web-doc-update
gnumakefile
gnupload
hard-locale
ialloc
idx
intprops
@ -81,7 +99,7 @@ mempcpy
minmax
mkstemp
mktime
nstrftime
nstrftime-limited
nullptr
openat
pclose
@ -105,8 +123,9 @@ stat
stat-macros
stat-size
stat-time
stdckdint-h
stdc_bit_width
stdckdint-h
stdcountof-h
stdint-h
stpcpy
strcase
@ -149,6 +168,7 @@ gnulib_tool_option_extras="--tests-base=gnulib-tests
--symlink
--makefile-name=gnulib.mk
$avoided_gnulib_modules
$avoided_gnulib_tests
"
# Build prerequisites

6
cfg.mk
View File

@ -1,5 +1,5 @@
# Customize maint.mk -*- makefile -*-
# Copyright (C) 2003-2013, 2015-2025 Free Software Foundation, Inc.
# Copyright (C) 2003-2013, 2015-2026 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
@ -36,7 +36,7 @@ announcement_Cc_ = $(translation_project_), $(PACKAGE)-devel@gnu.org
# Now that we have better tests, make this the default.
export VERBOSE = yes
old_NEWS_hash = d6a8096805f9b3ee162842058477f0bc
old_NEWS_hash = cfdcec044a361bf0e7b4e860bacc3d53
# Tell maint.mk's syntax-check rules that diff gets config.h directly or
# via diff.h or system.h.
@ -85,3 +85,5 @@ exclude_file_name_regexp--sc_prohibit_strcmp = ^gl/lib/
# Tell gnulib's tight_scope rule that we mark extern inlines with
# DIFF_INLINE and SYSTEM_INLINE.
export _gl_TS_extern = extern|DIFF_INLINE|SYSTEM_INLINE
codespell_ignore_words_list = FO,ND,debbugs

View File

@ -1,6 +1,6 @@
# Configure template for GNU Diffutils.
# Copyright (C) 1994-1995, 1998, 2001-2002, 2004, 2006, 2009-2013, 2015-2025
# Copyright (C) 1994-1995, 1998, 2001-2002, 2004, 2006, 2009-2013, 2015-2026
# Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
@ -37,6 +37,18 @@ AC_PROG_AWK
AC_PROG_CC
AM_MISSING_PROG([HELP2MAN], [help2man])
AC_PROG_RANLIB
# Do not worry about multibyte C locales.
# This removes dependencies on hard-locale etc.
gl_cv_func_mbrtowc_C_locale_sans_EILSEQ="guessing yes"
gl_cv_func_mbrtoc32_C_locale_sans_EILSEQ="guessing yes"
# Default Gnulib configuration to no multithreading,
# since diffutils is single-threaded.
# Although the builder can override this with the --enable-threads option
# of 'configure', diffutils does not support that on all platforms.
m4_define([gl_THREADLIB_DEFAULT_NO])
gl_EARLY
gl_USE_SYSTEM_EXTENSIONS
gl_INIT
@ -59,6 +71,15 @@ AC_DEFINE([GNULIB_MBRTOC32_REGULAR], [1],
[Do not worry about rare encodings like CP864, EBCDIC, Johab, and Shift JIS
that glibc does not support.])
AC_DEFINE([REQUIRE_GNUISH_STRFTIME_AM_PM], [0],
[Do not worry about GNU strftime behavior for AM and PM indicators.])
AC_DEFINE([SUPPORT_NON_GREG_CALENDARS_IN_STRFTIME], [false],
[Do not worry about GNU strftime behavior for non-Gregorian calendars.])
# Diffutils translates Gnulib's msgids too.
AC_DEFINE([GNULIB_TEXT_DOMAIN], [PACKAGE],
[Textdomain to use when translating Gnulib's msgids.])
AC_C_INLINE
AC_CHECK_MEMBERS([struct stat.st_rdev])
@ -66,10 +87,6 @@ AC_HEADER_DIRENT
AC_HEADER_SYS_WAIT
AC_TYPE_PID_T
AC_CHECK_FUNCS_ONCE([sigaction sigprocmask])
if test $ac_cv_func_sigprocmask = no; then
AC_CHECK_FUNCS([sigblock])
fi
AC_FUNC_FORK
dnl O_PATH exists since Linux 2.6.39, but is supported with fstat() only since
@ -184,8 +201,8 @@ AC_DEFINE_UNQUOTED([PR_PROGRAM], ["$PR_PROGRAM"], [Name of "pr" program.])
# When .tarball-version exists, we're building from a tarball
# and must not make man/*.1 files depend on the generated src/version.c,
# because that would induce a requirement to run the help2man perl script.
# We are not yet prepared to make perl a build-from-tarball requirement.
# because that would induce a requirement to run help2man.
# We are not yet prepared to make help2man a build-from-tarball requirement.
# Hence, here we detect .tarball-version existence. When not present,
# we define a variable to be used in man/Makefile.am to induce the
# proper dependency (so that man/*.1 will be rebuilt upon any version change),
@ -195,7 +212,7 @@ test -f $srcdir/.tarball-version \
&& SRC_VERSION_C= \
|| SRC_VERSION_C=../src/version.c
AM_GNU_GETTEXT([external], [need-ngettext])
AM_GNU_GETTEXT([external], [need-formatstring-macros])
AM_GNU_GETTEXT_VERSION([0.19.2])
XGETTEXT="AWK='$AWK' \$(SHELL) \$(top_srcdir)/exgettext $XGETTEXT"

View File

@ -1,6 +1,6 @@
# Makefile for GNU diffutils documentation.
# Copyright (C) 2001-2002, 2009-2013, 2015-2025 Free Software Foundation, Inc.
# Copyright (C) 2001-2002, 2009-2013, 2015-2026 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

View File

@ -17,7 +17,7 @@ and documents the GNU @command{diff}, @command{diff3},
differences between files and the GNU @command{patch} command for
using their output to update files.
Copyright @copyright{} 1992--1994, 1998, 2001--2002, 2004, 2006, 2009--2025
Copyright @copyright{} 1992--1994, 1998, 2001--2002, 2004, 2006, 2009--2026
Free Software Foundation, Inc.
@quotation
@ -4016,7 +4016,7 @@ found. @xref{Comparing Directories}.
@item -s
@itemx --report-identical-files
Report when two files are the same. @xref{Comparing Directories}.
Also report when two files are the same. @xref{Comparing Directories}.
@item -S @var{file}
@itemx --starting-file=@var{file}

View File

@ -5,7 +5,7 @@
@c hence no sectioning command or @node.
@display
Copyright @copyright{} 2000--2002, 2007--2008, 2022--2025 Free Software
Copyright @copyright{} 2000--2002, 2007--2008, 2022--2026 Free Software
Foundation, Inc.
@uref{https://fsf.org/}

View File

@ -1,7 +1,7 @@
#! /bin/sh
# Wrapper around gettext for programs using the msgid convention.
# Copyright (C) 1998, 2001, 2004, 2009-2013, 2015-2025 Free Software
# Copyright (C) 1998, 2001, 2004, 2009-2013, 2015-2026 Free Software
# Foundation, Inc.
# Written by Paul Eggert <eggert@twinsun.com>.

2
gnulib

@ -1 +1 @@
Subproject commit 553ab924d2b68d930fae5d3c6396502a57852d23
Subproject commit 4f6ac2c3c689cd7312b5f9da97791b14bbc2ee53

View File

@ -1 +1,3 @@
AM_CFLAGS =
include gnulib.mk

View File

@ -1,6 +1,6 @@
# Automakefile for GNU Diffutils library.
# Copyright (C) 2001-2002, 2004, 2006, 2009-2013, 2015-2025 Free Software
# Copyright (C) 2001-2002, 2004, 2006, 2009-2013, 2015-2026 Free Software
# Foundation, Inc.
# This program is free software: you can redistribute it and/or modify

View File

@ -1,6 +1,6 @@
/* Buffer primitives for comparison operations.
Copyright (C) 1993, 1995, 1998, 2001-2002, 2006, 2009-2013, 2015-2025 Free
Copyright (C) 1993, 1995, 1998, 2001-2002, 2006, 2009-2013, 2015-2026 Free
Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify

View File

@ -1,6 +1,6 @@
/* Buffer primitives for comparison operations.
Copyright (C) 2002, 2009-2013, 2015-2025 Free Software Foundation, Inc.
Copyright (C) 2002, 2009-2013, 2015-2026 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

View File

@ -1,6 +1,6 @@
/* Diagnostics for GNU diffutils
Copyright 2023-2025 Free Software Foundation, Inc.
Copyright 2023-2026 Free Software Foundation, Inc.
This file is part of GNU DIFF.

View File

@ -1,6 +1,6 @@
# Automakefile for GNU diffutils man pages
# Copyright (C) 2002, 2009-2013, 2015-2025 Free Software Foundation, Inc.
# Copyright (C) 2002, 2009-2013, 2015-2026 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
@ -16,7 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
dist_man1_MANS = cmp.1 diff.1 diff3.1 sdiff.1
EXTRA_DIST = $(dist_man1_MANS:%.1=%.x) help2man
EXTRA_DIST = $(dist_man1_MANS:%.1=%.x)
MAINTAINERCLEANFILES = $(dist_man1_MANS)
S = $(top_srcdir)/src
@ -29,13 +29,13 @@ sdiff.1: $S/sdiff.c sdiff.x
# to ensure help2man invokes them via the use of PATH below.
bin_dir = ../src
# Depend on the former to get version number changes.
$(dist_man1_MANS): $(SRC_VERSION_C) help2man
# Depend on $(SRC_VERSION_C) to get version number changes.
$(dist_man1_MANS): $(SRC_VERSION_C)
$(AM_V_GEN)base=`expr $@ : '\(.*\).1'` \
&& test -x $(bin_dir)/$$base \
&& (echo '[NAME]' \
&& sed 's@/\* *@@; s/-/\\-/;s/^GNU //; q' $S/$$base.c) \
| PATH="$(bin_dir)$(PATH_SEPARATOR)$$PATH" \
$(srcdir)/help2man -i - -i $(srcdir)/$$base.x \
$(HELP2MAN) -i - -i $(srcdir)/$$base.x \
-S '$(PACKAGE) $(VERSION)' $$base > $$base.1-t \
&& mv $$base.1-t $(srcdir)/$$base.1

View File

@ -1,801 +0,0 @@
#!/usr/bin/env perl
# Generate a short man page from --help and --version output.
# Copyright (C) 1997-2005, 2009-2017, 2020-2021, 2025 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, see <https://www.gnu.org/licenses/>.
# Written by Brendan O'Dea <bod@debian.org>
# Available from https://ftp.gnu.org/gnu/help2man/
use 5.008;
use strict;
use warnings;
use Getopt::Long;
use Text::ParseWords qw(shellwords);
use Text::Tabs qw(expand);
use POSIX qw(strftime setlocale LC_ALL);
my $this_program = 'help2man';
my $this_version = '1.48.5';
sub _ { $_[0] }
sub configure_locale
{
my $locale = shift;
die "$this_program: no locale support (Locale::gettext required)\n"
unless $locale eq 'C';
}
sub dec { $_[0] }
sub enc { $_[0] }
sub enc_user { $_[0] }
sub kark { die +(sprintf shift, @_), "\n" }
sub N_ { $_[0] }
sub program_basename;
sub get_option_value;
sub convert_option;
sub fix_italic_spacing;
my $version_info = enc_user sprintf _(<<'EOT'), $this_program, $this_version;
GNU %s %s
Copyright (C) 1997-2005, 2009-2017, 2020-2021, 2025 Free Software Foundation,
Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Brendan O'Dea <bod@debian.org>
EOT
my $help_info = enc_user sprintf _(<<'EOT'), $this_program, $this_program;
`%s' generates a man page out of `--help' and `--version' output.
Usage: %s [OPTION]... EXECUTABLE
-n, --name=STRING description for the NAME paragraph
-s, --section=SECTION section number for manual page (1, 6, 8)
-m, --manual=TEXT name of manual (User Commands, ...)
-S, --source=TEXT source of program (FSF, Debian, ...)
-L, --locale=STRING select locale (default "C")
-i, --include=FILE include material from `FILE'
-I, --opt-include=FILE include material from `FILE' if it exists
-o, --output=FILE send output to `FILE'
-p, --info-page=TEXT name of Texinfo manual
-N, --no-info suppress pointer to Texinfo manual
-l, --libtool exclude the `lt-' from the program name
-b, --bold-refs apply bold style to references
--help print this help, then exit
--version print version number, then exit
EXECUTABLE should accept `--help' and `--version' options and produce output on
stdout although alternatives may be specified using:
-h, --help-option=STRING help option string
-v, --version-option=STRING version option string
--version-string=STRING version string
--no-discard-stderr include stderr when parsing option output
Report bugs to <bug-help2man@gnu.org>.
EOT
my $section = 1;
my $manual = '';
my $source = '';
my $help_option = '--help';
my $version_option = '--version';
my $discard_stderr = 1;
my ($opt_name, @opt_include, $opt_output, $opt_info, $opt_no_info, $opt_libtool,
$opt_bold_refs, $version_text);
my %opt_def = (
'n|name=s' => \$opt_name,
's|section=s' => \$section,
'm|manual=s' => \$manual,
'S|source=s' => \$source,
'L|locale=s' => sub { configure_locale pop },
'i|include=s' => sub { push @opt_include, [ pop, 1 ] },
'I|opt-include=s' => sub { push @opt_include, [ pop, 0 ] },
'o|output=s' => \$opt_output,
'p|info-page=s' => \$opt_info,
'N|no-info' => \$opt_no_info,
'l|libtool' => \$opt_libtool,
'b|bold-refs' => \$opt_bold_refs,
'help' => sub { print $help_info; exit },
'version' => sub { print $version_info; exit },
'h|help-option=s' => \$help_option,
'v|version-option=s' => \$version_option,
'version-string=s' => \$version_text,
'discard-stderr!' => \$discard_stderr,
);
# Parse options.
Getopt::Long::config('bundling');
die $help_info unless GetOptions %opt_def and @ARGV == 1;
my %include = ();
my %replace = ();
my %append = ();
my %append_match = ();
my @sections = (); # retain order of include file or in-line *section*s
# Process include file (if given). Format is:
#
# Optional initial text, ignored. May include lines starting with `-'
# which are processed as options.
#
# [section]
# Verbatim text to be included in the named section. By default at
# the start, but in the case of `name' and `synopsis' the content
# will replace the autogenerated contents.
#
# [<section]
# Verbatim text to be inserted at the start of the named section.
#
# [=section]
# Verbatim text to replace the named section.
#
# [>section]
# Verbatim text to be appended to the end of the named section.
#
# /pattern/
# Verbatim text for inclusion below a paragraph matching `pattern'.
#
while (@opt_include)
{
my ($inc, $required) = @{shift @opt_include};
next unless -f $inc or $required;
kark N_("%s: can't open `%s' (%s)"), $this_program, $inc, $!
unless open INC, $inc;
my $key;
my $hash;
while (<INC>)
{
# Convert input to internal Perl format, so that multibyte
# sequences are treated as single characters.
$_ = dec $_;
# [section]
if (/^\[([^]]+)\]\s*$/)
{
$key = uc $1;
$key =~ s/^\s+//;
$key =~ s/\s+$//;
$hash = \%include;
# Handle explicit [<section], [=section] and [>section]
if ($key =~ s/^([<>=])\s*//)
{
if ($1 eq '>') { $hash = \%append; }
elsif ($1 eq '=') { $hash = \%replace; }
}
# NAME/SYNOPSIS replace by default
elsif ($key eq _('NAME') or $key eq _('SYNOPSIS'))
{
$hash = \%replace;
}
else
{
$hash = \%include;
}
push @sections, $key;
next;
}
# /pattern/
if (m!^/(.*)/([ims]*)\s*$!)
{
my $pat = $2 ? "(?$2)$1" : $1;
# Check pattern.
eval { $key = qr($pat) };
if ($@)
{
$@ =~ s/ at .*? line \d.*//;
die "$inc:$.:$@";
}
$hash = \%append_match;
next;
}
# Check for options before the first section--anything else is
# silently ignored, allowing the first for comments and
# revision info.
unless ($key)
{
# handle options
if (/^-/)
{
local @ARGV = shellwords $_;
GetOptions %opt_def;
}
next;
}
$hash->{$key} .= $_;
}
close INC;
kark N_("%s: no valid information found in `%s'"), $this_program, $inc
unless $key;
}
# Compress trailing blank lines.
for my $hash (\(%include, %replace, %append, %append_match))
{
for (keys %$hash) { $hash->{$_} =~ s/\n+$/\n/ }
}
# Grab help and version info from executable.
my $help_text = get_option_value $ARGV[0], $help_option;
$version_text ||= get_option_value $ARGV[0], $version_option;
# By default the generated manual pages will include the current date. This may
# however be overridden by setting the environment variable $SOURCE_DATE_EPOCH
# to an integer value of the seconds since the UNIX epoch. This is primarily
# intended to support reproducible builds (wiki.debian.org/ReproducibleBuilds)
# and will additionally ensure that the output date string is UTC.
my $epoch_secs = time;
if (exists $ENV{SOURCE_DATE_EPOCH} and $ENV{SOURCE_DATE_EPOCH} =~ /^(\d+)$/)
{
$epoch_secs = $1;
$ENV{TZ} = 'UTC0';
}
# Translators: the following message is a strftime(3) format string, which in
# the English version expands to the month as a word and the full year. It
# is used on the footer of the generated manual pages. If in doubt, you may
# just use %x as the value (which should be the full locale-specific date).
my $date = enc strftime _("%B %Y"), localtime $epoch_secs;
my $program = program_basename $ARGV[0];
my $package = $program;
my $version;
if ($opt_output)
{
unlink $opt_output or kark N_("%s: can't unlink %s (%s)"),
$this_program, $opt_output, $! if -e $opt_output;
open STDOUT, ">$opt_output"
or kark N_("%s: can't create %s (%s)"), $this_program, $opt_output, $!;
}
# The first line of the --version information is assumed to be in one
# of the following formats:
#
# <version>
# <program> <version>
# {GNU,Free} <program> <version>
# <program> ({GNU,Free,} <package>) <version>
# <program> - {GNU,Free,} <package> <version>
# <program> - {GNU,Free,} <package> - <version>
#
# and separated from any copyright/author details by a blank line.
($_, $version_text) = ((split /\n+/, $version_text, 2), '');
if (/^(\S+) +\(((?:(?:GNU|Free) +)?[^)]+)\) +(\S.*)$/ or
/^(\S+) +- +((?:(?:GNU|Free) +)?\S.*) +- +(\S.*)$/ or
/^(\S+) +- +((?:(?:GNU|Free) +)?\S+) +(\S.*)$/)
{
$program = program_basename $1;
$package = $2;
$version = $3;
}
elsif (/^((?:GNU|Free) +)?(\S+) +(\S.*)$/)
{
$program = program_basename $2;
$package = $1 ? "$1$program" : $program;
$version = $3;
}
else
{
$version = $_;
}
# No info for `info' itself.
$opt_no_info = 1 if $program eq 'info';
if ($opt_name)
{
# --name overrides --include contents.
$replace{_('NAME')} = "$program \\- $opt_name\n";
}
# Translators: "NAME", "SYNOPSIS" and other one or two word strings in all
# upper case are manual page section headings. The man(1) manual page in your
# language, if available should provide the conventional translations.
for ($replace{_('NAME')} || ($include{_('NAME')} ||= ''))
{
if ($_) # Use first name given as $program
{
$program = $1 if /^([^\s,]+)(?:,?\s*[^\s,\\-]+)*\s+\\?-/;
}
else # Set a default (useless) NAME paragraph.
{
$_ = sprintf _("%s \\- manual page for %s %s") . "\n", $program,
$program, $version;
}
}
# Man pages traditionally have the page title in caps.
my $PROGRAM = uc $program;
# Set default page head/footers
$source ||= "$package $version";
unless ($manual)
{
for ($section)
{
if (/^(1[Mm]|8)/) { $manual = enc _('System Administration Utilities') }
elsif (/^6/) { $manual = enc _('Games') }
else { $manual = enc _('User Commands') }
}
}
# Extract usage clause(s) [if any] for SYNOPSIS.
# Translators: "Usage" and "or" here are patterns (regular expressions) which
# are used to match the usage synopsis in program output. An example from cp
# (GNU coreutils) which contains both strings:
# Usage: cp [OPTION]... [-T] SOURCE DEST
# or: cp [OPTION]... SOURCE... DIRECTORY
# or: cp [OPTION]... -t DIRECTORY SOURCE...
my $PAT_USAGE = _('Usage');
my $PAT_USAGE_CONT = _('or');
if ($help_text =~ s/^($PAT_USAGE):( +(\S+))(.*)((?:\n(?: {6}\1| *($PAT_USAGE_CONT): +\S).*)*)//om)
{
my @syn = $3 . $4;
if ($_ = $5)
{
s/^\n//;
for (split /\n/) { s/^ *(($PAT_USAGE_CONT): +)?//o; push @syn, $_ }
}
my $synopsis = '';
for (@syn)
{
$synopsis .= ".br\n" if $synopsis;
s!^\S*/!!;
s/^lt-// if $opt_libtool;
s/^(\S+) *//;
$synopsis .= ".B $1\n";
s/\s+$//;
s/(([][]|\.\.+)+)/\\fR$1\\fI/g;
s/^/\\fI/ unless s/^\\fR//;
$_ .= '\fR';
s/(\\fI)( *)/$2$1/g;
s/\\fI\\fR//g;
s/^\\fR//;
s/\\fI$//;
s/^\./\\&./;
$_ = fix_italic_spacing $_;
$synopsis .= "$_\n";
}
$include{_('SYNOPSIS')} .= $synopsis;
}
# Process text, initial section is DESCRIPTION.
my $sect = _('DESCRIPTION');
$_ = "$help_text\n\n$version_text";
# Normalise paragraph breaks.
s/^\n+//;
s/\n*$/\n/;
s/\n\n+/\n\n/g;
# Join hyphenated lines.
s/([A-Za-z])-\n *([A-Za-z])/$1$2/g;
# Temporarily exchange leading dots, apostrophes and backslashes for
# tokens.
s/^\./\x80/mg;
s/^'/\x81/mg;
s/\\/\x82/g;
# Translators: patterns are used to match common program output. In the source
# these strings are all of the form of "my $PAT_something = _('...');" and are
# regular expressions. If there is more than one commonly used string, you
# may separate alternatives with "|". Spaces in these expressions are written
# as " +" to indicate that more than one space may be matched. The string
# "(?:[\\w-]+ +)?" in the bug reporting pattern is used to indicate an
# optional word, so that either "Report bugs" or "Report _program_ bugs" will
# be matched.
my $PAT_BUGS = _('Report +(?:[\w-]+ +)?bugs|' .
'Email +bug +reports +to|' .
'.* +online +help:');
my $PAT_AUTHOR = _('Written +by');
my $PAT_OPTIONS = _('Options');
my $PAT_ENVIRONMENT = _('Environment');
my $PAT_FILES = _('Files');
my $PAT_EXAMPLES = _('Examples');
my $PAT_FREE_SOFTWARE = _('This +is +free +software');
my $PAT_SEE_ALSO = _('Full +documentation');
# Start a new paragraph (if required) for these.
s/([^\n])\n($PAT_BUGS|$PAT_AUTHOR|$PAT_SEE_ALSO) /$1\n\n$2 /og;
# Convert iso-8859-1 copyright symbol or (c) to nroff
# character.
s/^Copyright +(?:\xa9|\([Cc]\))/Copyright \\(co/mg;
while (length)
{
# Convert some standard paragraph names.
if (s/^($PAT_OPTIONS): *\n+//o)
{
$sect = _('OPTIONS');
next;
}
if (s/^($PAT_ENVIRONMENT): *\n+//o)
{
$sect = _('ENVIRONMENT');
next;
}
if (s/^($PAT_FILES): *\n+//o)
{
$sect = _('FILES');
next;
}
elsif (s/^($PAT_EXAMPLES): *\n+//o)
{
$sect = _('EXAMPLES');
next;
}
# Custom section indicated by a line containing "*Section Name*".
if (s/^\*(\w(.*\w)?)\* *\n+//)
{
$sect = uc $1;
$sect =~ tr/*/ /; # also accept *Section*Name*
push @sections, $sect;
next;
}
# Copyright section.
if (/^Copyright /)
{
$sect = _('COPYRIGHT');
}
# Bug reporting section.
elsif (/^($PAT_BUGS) /o)
{
$sect = _('REPORTING BUGS');
}
# Author section.
elsif (/^($PAT_AUTHOR)/o)
{
$sect = _('AUTHOR');
}
elsif (/^($PAT_SEE_ALSO)/o)
{
$sect = _('SEE ALSO');
$opt_no_info = 1;
}
# Examples, indicated by an indented leading $, % or > are
# rendered in a constant width font.
if (/^( +)([\$\%>] )\S/)
{
my $indent = $1;
my $prefix = $2;
my $break = '.IP';
while (s/^$indent\Q$prefix\E(\S.*)\n*//)
{
$include{$sect} .= "$break\n\\f(CW$prefix$1\\fR\n";
$break = '.br';
}
next;
}
my $matched = '';
# Sub-sections have a trailing colon and the second line indented.
if (s/^(\S.*:) *\n / /)
{
$matched .= $& if %append_match;
$include{$sect} .= qq(.SS "$1"\n);
}
my $indent = 0;
my $content = '';
# Option with description.
if (s/^( {1,10}([+-]\S.*?))(?:( +(?!-))|\n( {20,}))(\S.*)\n//)
{
$matched .= $& if %append_match;
$indent = length ($4 || "$1$3");
$content = ".TP\n\x84$2\n\x84$5\n";
unless ($4)
{
# Indent may be different on second line.
$indent = length $& if /^ {20,}/;
}
}
# Option without description.
elsif (s/^ {1,10}([+-]\S.*)\n//)
{
$matched .= $& if %append_match;
$content = ".HP\n\x84$1\n";
$indent = 80; # not continued
}
# Indented paragraph with tag.
elsif (s/^( +(\S.*?) +)(\S.*)\n//)
{
$matched .= $& if %append_match;
$indent = length $1;
$content = ".TP\n\x84$2\n\x84$3\n";
}
# Indented paragraph.
elsif (s/^( +)(\S.*)\n//)
{
$matched .= $& if %append_match;
$indent = length $1;
$content = ".IP\n\x84$2\n";
}
# Left justified paragraph.
else
{
s/(.*)\n//;
$matched .= $& if %append_match;
$content = ".PP\n" if $include{$sect};
$content .= "$1\n";
}
# Append continuations.
while ($indent ? s/^ {$indent}(\S.*)\n// : s/^(\S.*)\n//)
{
$matched .= $& if %append_match;
$content .= "\x84$1\n";
}
# Move to next paragraph.
s/^\n+//;
for ($content)
{
# Leading dot and apostrophe protection.
s/\x84\./\x80/g;
s/\x84'/\x81/g;
s/\x84//g;
# Examples should be verbatim.
unless ($sect eq _('EXAMPLES'))
{
# Convert options.
s/(^|[ (])(-[][\w=-]+)/$1 . convert_option $2/mge;
# Italicise filenames: /a/b, $VAR/c/d, ~/e/f
s!
(^|[ (]) # space/punctuation before
(
(?:\$\w+|~)? # leading variable, or tilde
(?:/\w(?:[\w.-]*\w)?)+ # path components
)
($|[ ,;.)]) # space/punctuation after
!$1\\fI$2\\fP$3!xmg;
$_ = fix_italic_spacing $_;
}
# Escape remaining hyphens.
s/-/\x83/g;
if ($sect eq _('COPYRIGHT'))
{
# Insert line breaks before additional copyright messages
# and the disclaimer.
s/\n(Copyright |$PAT_FREE_SOFTWARE)/\n.br\n$1/og;
}
elsif ($sect eq _('REPORTING BUGS'))
{
# Handle multi-line bug reporting sections of the form:
#
# Report <program> bugs to <addr>
# GNU <package> home page: <url>
# ...
s/\n([[:upper:]])/\n.br\n$1/g;
}
elsif ($sect eq _('SEE ALSO'))
{
# Handle external references of the form:
#
# GNU <package> online resources: <addr>
# Full documentation at: <addr>
# or available locally via: info ...
#
s/\'/\\(aq/g; # shell quotes for info command
s/\n(.)/\n.br\n$1/g; # separate lines for each item
}
}
# Check if matched paragraph contains /pat/.
if (%append_match)
{
for my $pat (keys %append_match)
{
if ($matched =~ $pat)
{
$content .= ".PP\n" unless $append_match{$pat} =~ /^\./;
$content .= $append_match{$pat};
}
}
}
$include{$sect} .= $content;
}
# Refer to the real documentation.
unless ($opt_no_info)
{
my $info_page = $opt_info || $program;
$sect = _('SEE ALSO');
$include{$sect} .= ".PP\n" if $include{$sect};
$include{$sect} .= sprintf _(<<'EOT'), $program, $program, $info_page;
The full documentation for
.B %s
is maintained as a Texinfo manual. If the
.B info
and
.B %s
programs are properly installed at your site, the command
.IP
.B info %s
.PP
should give you access to the complete manual.
EOT
}
# Append additional text.
while (my ($sect, $text) = each %append)
{
$include{$sect} .= $append{$sect};
}
# Replace sections.
while (my ($sect, $text) = each %replace)
{
$include{$sect} = $replace{$sect};
}
# Output header.
print <<EOT;
.\\" DO NOT MODIFY THIS FILE! It was generated by $this_program $this_version.
.TH $PROGRAM "$section" "$date" "$source" "$manual"
EOT
# Section ordering.
my @pre = (_('NAME'), _('SYNOPSIS'), _('DESCRIPTION'), _('OPTIONS'),
_('EXAMPLES'));
my @post = (_('ENVIRONMENT'), _('FILES'), _('AUTHOR'),
_('REPORTING BUGS'), _('COPYRIGHT'), _('SEE ALSO'));
my %filter = map { $_ => 1 } @pre, @post;
# Output content.
my %done;
for my $sect (@pre, (grep !$filter{$_}, @sections), @post)
{
next if $done{$sect}++; # ignore duplicates
next unless $include{$sect};
if ($include{$sect})
{
my $quote = $sect =~ /\W/ ? '"' : '';
print enc ".SH $quote$sect$quote\n";
for ($include{$sect})
{
# Add bold style around referenced pages.
if ($opt_bold_refs)
{
# This will ignore entries already marked up (with \)
s/(^|\s|,)([\[\w\x83]+)\(([1-9][[:lower:]]?)\)/$1\\fB$2\\fP($3)/g;
}
# Replace leading dot, apostrophe, backslash and hyphen
# tokens.
s/\x80/\\&./g;
s/\x81/\\&'/g;
s/\x82/\\e/g;
s/\x83/\\-/g;
# Convert some latin1 chars to troff equivalents
s/\xa0/\\ /g; # non-breaking space
print enc $_;
}
}
}
close STDOUT or kark N_("%s: error writing to %s (%s)"), $this_program,
$opt_output || 'stdout', $!;
exit;
# Get program basename, and strip libtool "lt-" prefix if required.
sub program_basename
{
local $_ = shift;
s!.*/!!;
s/^lt-// if $opt_libtool;
$_;
}
# Call program with given option and return results.
sub get_option_value
{
my ($prog, $opt) = @_;
my $stderr = $discard_stderr ? '/dev/null' : '&1';
my $value = join '',
map { s/ +$//; expand $_ }
map { dec $_ }
`$prog $opt 2>$stderr`;
unless ($value)
{
my $err = N_("%s: can't get `%s' info from %s%s");
my $extra = $discard_stderr
? "\n" . N_("Try `--no-discard-stderr' if option outputs to stderr")
: '';
kark $err, $this_program, $opt, $prog, $extra;
}
$value;
}
# Convert option dashes to \- to stop nroff from hyphenating 'em, and
# embolden. Option arguments get italicised.
sub convert_option
{
local $_ = '\fB' . shift;
s/-/\x83/g;
unless (s/\[=(.*)\]$/\\fR[=\\fI$1\\fR]/)
{
s/=(.)/\\fR=\\fI$1/;
s/ (.)/ \\fI$1/;
$_ .= '\fR';
}
$_;
}
# Insert spacing escape characters \, and \/ before and after italic text. See
# https://www.gnu.org/software/groff/manual/html_node/Ligatures-and-Kerning.html
sub fix_italic_spacing
{
local $_ = shift;
s!\\fI(.*?)\\f([BRP])!\\fI\\,$1\\/\\f$2!g;
return $_;
}

View File

@ -1,6 +1,6 @@
# List of files that contain translatable strings.
# Copyright (C) 2001-2002, 2009-2013, 2015-2025 Free Software Foundation, Inc.
# Copyright (C) 2001-2002, 2009-2013, 2015-2026 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

View File

@ -1,30 +0,0 @@
# English messages for GNU diffutils
# Copyright 1998, 2001-2003, 2009-2013, 2015-2025 Free Software Foundation,
# Inc.
# Paul Eggert <eggert@twinsun.com>, 1998
#
msgid ""
msgstr ""
"Project-Id-Version: GNU diffutils 3.2\n"
"POT-Creation-Date: 2002-06-16 23:44-0700\n"
"PO-Revision-Date: 2012-01-25 23:11-0700\n"
"Last-Translator: Paul Eggert <eggert@cs.ucla.edu>\n"
"Language-Team: English <en@translate.freefriends.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#. TRANSLATORS: Please translate "(C)" to the C-in-a-circle symbol
#. (U+00A9, COPYRIGHT SIGN) if possible, as this has some minor
#. technical advantages in international copyright law. If the
#. copyright symbol is not available, please leave it as "(C)".
#: lib/version-etc.c:50
msgid "(C)"
msgstr "©"
#. TRANSLATORS: Please translate the second "o" in "Torbjorn Granlund"
#. to an o-with-umlaut (U+00F6, LATIN SMALL LETTER O WITH DIAERESIS)
#. if possible.
#: src/cmp.c:47
msgid "Written by Torbjorn Granlund and David MacKenzie."
msgstr "Written by Torbjörn Granlund and David MacKenzie."

View File

@ -1,6 +1,6 @@
# Automakefile for GNU diffutils programs.
# Copyright (C) 2001-2002, 2006, 2009-2013, 2015-2025 Free Software Foundation,
# Copyright (C) 2001-2002, 2006, 2009-2013, 2015-2026 Free Software Foundation,
# Inc.
# This program is free software: you can redistribute it and/or modify
@ -47,8 +47,8 @@ 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 system.c util.c
noinst_HEADERS = diff.h system.h
normal.c side.c syncsig.c system.c util.c
noinst_HEADERS = diff.h syncsig.h system.h
MOSTLYCLEANFILES = paths.h paths.ht

View File

@ -1,7 +1,7 @@
/* Analyze file differences for GNU DIFF.
Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006-2007,
2009-2013, 2015-2025 Free Software Foundation, Inc.
2009-2013, 2015-2026 Free Software Foundation, Inc.
This file is part of GNU DIFF.

View File

@ -1,7 +1,7 @@
/* GNU cmp - compare two files byte by byte
Copyright (C) 1990-1996, 1998, 2001-2002, 2004, 2006-2007, 2009-2013,
2015-2025 Free Software Foundation, Inc.
2015-2026 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
@ -28,7 +28,6 @@
#include <exitfail.h>
#include <file-type.h>
#include <getopt.h>
#include <hard-locale.h>
#include <progname.h>
#include <quote.h>
#include <unlocked-io.h>
@ -46,11 +45,15 @@ static char const PROGRAM_NAME[] = "cmp";
proper_name_lite ("Torbjorn Granlund", "Torbj\303\266rn Granlund"), \
_("David MacKenzie")
/* Return true if the locale's messages might not be those of C or POSIX. */
static bool
hard_locale_LC_MESSAGES (void)
{
#if defined LC_MESSAGES && ENABLE_NLS
return hard_locale (LC_MESSAGES);
#if ENABLE_NLS
/* GNU diff defines ENABLE_NLS only if gettext is preinstalled, and
on these platforms setlocale (LC_MESSAGES, nullptr) never returns nullptr
and always returns "C" when in the C or POSIX locales. */
return !STREQ (setlocale (LC_MESSAGES, nullptr), "C");
#else
return false;
#endif

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-2025 Free Software Foundation, Inc.
2015-2026 Free Software Foundation, Inc.
This file is part of GNU DIFF.
@ -72,7 +72,8 @@ print_context_label (char const *mark,
struct tm const *tm = localtime (&ts.tv_sec);
int nsec = ts.tv_nsec;
if (tm && nstrftime (buf, sizeof buf, time_format, tm, localtz, nsec))
if (tm
&& 0 <= nstrftime (buf, sizeof buf, time_format, tm, localtz, nsec))
fprintf (outfile, "%s %s\t%s", mark, name, buf);
else if (TYPE_SIGNED (time_t))
{

View File

@ -1,7 +1,7 @@
/* GNU diff - compare files line by line
Copyright (C) 1988-1989, 1992-1994, 1996, 1998, 2001-2002, 2004, 2006-2007,
2009-2013, 2015-2025 Free Software Foundation, Inc.
2009-2013, 2015-2026 Free Software Foundation, Inc.
This file is part of GNU DIFF.
@ -35,7 +35,7 @@
#include <filenamecat.h>
#include <fnmatch.h>
#include <getopt.h>
#include <hard-locale.h>
#include <strftime.h>
#include <progname.h>
#include <quote.h>
#include <sh-quote.h>
@ -293,6 +293,21 @@ exclude_options (void)
{
return EXCLUDE_WILDCARDS | (ignore_file_name_case ? FNM_CASEFOLD : 0);
}
/* Return true if in a hard LC_TIME locale. */
static bool
hard_locale_LC_TIME (void)
{
/* Use the heuristic that %c has its usual POSIX meaning.
This is good enough in practice. */
static struct tm const tm
= { .tm_year = 1970 - 1900, .tm_mon = 0, .tm_mday = 1,
.tm_hour = 23, .tm_min = 59, .tm_sec = 59 };
static char const expected[] = "Thu Jan 1 23:59:59 1970";
char buf[sizeof expected];
return (nstrftime (buf, sizeof buf, "%c", &tm, 0, 0) == sizeof buf - 1
&& memcmp (buf, expected, sizeof buf) == 0);
}
int
main (int argc, char **argv)
@ -746,7 +761,7 @@ main (int argc, char **argv)
specify_style (OUTPUT_NORMAL);
}
if (output_style != OUTPUT_CONTEXT || hard_locale (LC_TIME))
if (output_style != OUTPUT_CONTEXT || hard_locale_LC_TIME ())
{
#if defined STAT_TIMESPEC || defined STAT_TIMESPEC_NS
time_format = "%Y-%m-%d %H:%M:%S.%N %z";
@ -965,8 +980,8 @@ check_stdout (void)
static char const *const option_help_msgid[] = {
N_(" --normal output a normal diff (the default)"),
N_("-q, --brief report only when files differ"),
N_("-s, --report-identical-files report when two files are the same"),
N_("-q, --brief report only whether files differ"),
N_("-s, --report-identical-files also report if two files are the same"),
N_("-c, -C NUM, --context[=NUM] output NUM (default 3) lines of copied context"),
N_("-u, -U NUM, --unified[=NUM] output NUM (default 3) lines of unified context"),
N_("-e, --ed output an ed script"),
@ -1074,11 +1089,10 @@ Mandatory arguments to long options are mandatory for short options too.\n\
for (char const *const *p = option_help_msgid; *p; p++)
{
if (!**p)
putchar ('\n');
else
char const *msg = *p;
if (*msg)
{
char const *msg = _(*p);
msg = gettext (msg);
for (char const *nl; (nl = strchr (msg, '\n')); msg = nl + 1)
{
fputs (" ", stdout);
@ -1087,8 +1101,8 @@ Mandatory arguments to long options are mandatory for short options too.\n\
if (*msg == ' ' || *msg == '-')
fputs (" ", stdout);
puts (msg);
}
puts (msg);
}
emit_bug_reporting_address ();
}
@ -1222,14 +1236,14 @@ compare_prepped_files (struct comparison const *parent,
the type is unusual, then simply report their type.
However, at the top level do this only if one file is a symlink
and the other is not. */
if (toplevel
? (!S_ISLNK (cmp->file[0].stat.st_mode)
!= !S_ISLNK (cmp->file[1].stat.st_mode))
: (cmp->file[0].filetype != cmp->file[1].filetype
|| ! (S_ISREG (cmp->file[0].stat.st_mode)
|| S_ISLNK (cmp->file[0].stat.st_mode)
|| S_ISCHR (cmp->file[0].stat.st_mode)
|| S_ISBLK (cmp->file[0].stat.st_mode))))
mode_t mode0 = cmp->file[0].stat.st_mode;
mode_t mode1 = cmp->file[1].stat.st_mode;
if (toplevel ? !S_ISLNK (mode0) != !S_ISLNK (mode1)
: S_ISREG (mode0) ? !S_ISREG (mode1)
: S_ISLNK (mode0) ? !S_ISLNK (mode1)
: S_ISCHR (mode0) ? !S_ISCHR (mode1)
: S_ISBLK (mode0) ? !S_ISBLK (mode1)
: true)
{
/* POSIX 1003.1-2017 says any message will do, so long as it
contains the file names. */
@ -1243,7 +1257,7 @@ compare_prepped_files (struct comparison const *parent,
}
/* If both files are symlinks, compare symlink contents. */
if (S_ISLNK (cmp->file[0].stat.st_mode))
if (S_ISLNK (mode0))
{
/* We get here only if we are not dereferencing symlinks. */
dassert (no_dereference_symlinks);
@ -1294,7 +1308,7 @@ compare_prepped_files (struct comparison const *parent,
and report file types of all other non-regular files.
POSIX 1003.1-2017 says any message will do,
so long as it contains the file names. */
if (!toplevel && !S_ISREG (cmp->file[0].stat.st_mode))
if (!toplevel && !S_ISREG (mode0))
{
if (cmp->file[0].stat.st_rdev == cmp->file[1].stat.st_rdev)
return EXIT_SUCCESS;
@ -1310,7 +1324,7 @@ compare_prepped_files (struct comparison const *parent,
for (int i = 0; i < n_num; i++)
sprintf (numbuf[i], "%"PRIdMAX, num[i]);
message ((S_ISCHR (cmp->file[0].stat.st_mode)
message ((S_ISCHR (mode0)
? ("Character special files %s (%s, %s)"
" and %s (%s, %s) differ\n")
: ("Block special files %s (%s, %s)"
@ -1322,8 +1336,8 @@ compare_prepped_files (struct comparison const *parent,
}
if (files_can_be_treated_as_binary
&& S_ISREG (cmp->file[0].stat.st_mode)
&& S_ISREG (cmp->file[1].stat.st_mode)
&& S_ISREG (mode0)
&& S_ISREG (mode1)
&& cmp->file[0].stat.st_size != cmp->file[1].stat.st_size
&& 0 <= cmp->file[0].stat.st_size
&& 0 <= cmp->file[1].stat.st_size)

View File

@ -1,7 +1,7 @@
/* Shared definitions for GNU DIFF
Copyright (C) 1988-1989, 1991-1995, 1998, 2001-2002, 2004, 2009-2013,
2015-2025 Free Software Foundation, Inc.
2015-2026 Free Software Foundation, Inc.
This file is part of GNU DIFF.

View File

@ -1,7 +1,7 @@
/* GNU diff3 - compare three files line by line
Copyright (C) 1988-1989, 1992-1996, 1998, 2001-2002, 2004, 2006, 2009-2013,
2015-2025 Free Software Foundation, Inc.
2015-2026 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
@ -1314,33 +1314,33 @@ output_diff3 (FILE *outputfile, struct diff3_block *diff,
i = (oddoneout == 1 ? skew_increment[i] : i + 1))
{
int realfile = mapping[i];
lin lowt = D_LOWLINE (ptr, realfile);
lin hight = D_HIGHLINE (ptr, realfile);
lin low_t = D_LOWLINE (ptr, realfile);
lin high_t = D_HIGHLINE (ptr, realfile);
fprintf (outputfile, "%d:", i + 1);
switch (lowt - hight)
switch (low_t - high_t)
{
case 1:
fprintf (outputfile, "%"pI"da\n", lowt - 1);
fprintf (outputfile, "%"pI"da\n", low_t - 1);
break;
case 0:
fprintf (outputfile, "%"pI"dc\n", lowt);
fprintf (outputfile, "%"pI"dc\n", low_t);
break;
default:
fprintf (outputfile, "%"pI"d,%"pI"dc\n", lowt, hight);
fprintf (outputfile, "%"pI"d,%"pI"dc\n", low_t, high_t);
break;
}
if (i == dontprint) continue;
if (lowt <= hight)
if (low_t <= high_t)
for (lin line = 0; ; line++)
{
fputs (line_prefix, outputfile);
char *cp = D_RELNUM (ptr, realfile, line);
idx_t length = D_RELLEN (ptr, realfile, line);
fwrite (cp, sizeof (char), length, outputfile);
if (hight - lowt <= line)
if (high_t - low_t <= line)
{
if (cp[length - 1] != '\n')
fprintf (outputfile, "\n\\ %s\n",

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-2025 Free Software Foundation, Inc.
2009-2013, 2015-2026 Free Software Foundation, Inc.
This file is part of GNU DIFF.

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-2025 Free Software Foundation, Inc.
2015-2026 Free Software Foundation, Inc.
This file is part of GNU DIFF.

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-2025
Copyright (C) 1989, 1991-1994, 2001-2002, 2004, 2006, 2009-2013, 2015-2026
Free Software Foundation, Inc.
This file is part of GNU DIFF.

View File

@ -1,7 +1,7 @@
/* File I/O for GNU DIFF.
Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006, 2009-2013,
2015-2025 Free Software Foundation, Inc.
2015-2026 Free Software Foundation, Inc.
This file is part of GNU DIFF.
@ -1012,7 +1012,7 @@ find_and_hash_each_line (struct file_data *current)
linbuf += linbuf_base;
linbuf = xpalloc (linbuf, &n, 1, -1, sizeof *linbuf);
linbuf -= linbuf_base;
alloc_lines = n - linbuf_base;
alloc_lines = linbuf_base + n;
}
linbuf[line] = p;

View File

@ -1,6 +1,6 @@
/* Normal-format output routines for GNU DIFF.
Copyright (C) 1988-1989, 1993, 1995, 1998, 2001, 2006, 2009-2013, 2015-2025
Copyright (C) 1988-1989, 1993, 1995, 1998, 2001, 2006, 2009-2013, 2015-2026
Free Software Foundation, Inc.
This file is part of GNU DIFF.

View File

@ -1,7 +1,7 @@
/* GNU sdiff - side-by-side merge of file differences
Copyright (C) 1992-1996, 1998, 2001-2002, 2004, 2006-2007, 2009-2013,
2015-2025 Free Software Foundation, Inc.
2015-2026 Free Software Foundation, Inc.
This file is part of GNU DIFF.
@ -100,7 +100,9 @@ enum
typedef void (*sighandler) (int);
static void signal_handler (int, sighandler);
#if HAVE_SIGACTION
/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
present. */
#ifdef SA_NOCLDSTOP
/* Prefer 'sigaction' if available, since 'signal' can lose signals. */
static struct sigaction initial_action[NUM_SIGS];
static sighandler
@ -714,21 +716,21 @@ static bool sigs_trapped;
static void
catchsig (int s)
{
#if ! HAVE_SIGACTION
#ifndef SA_NOCLDSTOP
signal (s, SIG_IGN);
#endif
if (! (s == SIGINT && ignore_SIGINT))
signal_received = s;
}
#if HAVE_SIGACTION
#ifdef SA_NOCLDSTOP
static struct sigaction catchaction;
#endif
static void
signal_handler (int sig, sighandler handler)
{
#if HAVE_SIGACTION
#ifdef SA_NOCLDSTOP
catchaction.sa_handler = handler;
sigaction (sig, &catchaction, 0);
#else
@ -739,7 +741,7 @@ signal_handler (int sig, sighandler handler)
static void
trapsigs (void)
{
#if HAVE_SIGACTION
#ifdef SA_NOCLDSTOP
catchaction.sa_flags = SA_RESTART;
sigemptyset (&catchaction.sa_mask);
for (int i = 0; i < NUM_SIGS; i++)
@ -748,7 +750,7 @@ trapsigs (void)
for (int i = 0; i < NUM_SIGS; i++)
{
#if HAVE_SIGACTION
#ifdef SA_NOCLDSTOP
sigaction (sigs[i], nullptr, &initial_action[i]);
#else
initial_action[i] = signal (sigs[i], SIG_IGN);
@ -773,7 +775,7 @@ untrapsig (int s)
for (int i = 0; i < NUM_SIGS; i++)
if ((! s || sigs[i] == s) && initial_handler (i) != SIG_IGN)
{
#if HAVE_SIGACTION
#ifdef SA_NOCLDSTOP
sigaction (sigs[i], &initial_action[i], nullptr);
#else
signal (sigs[i], initial_action[i]);
@ -802,7 +804,7 @@ checksigs (void)
static void
give_help (void)
{
fprintf (stderr, "%s", _("\
fputs (_("\
ed:\tEdit then use both versions, each decorated with a header.\n\
eb:\tEdit then use both versions.\n\
el or e1:\tEdit then use the left version.\n\
@ -813,20 +815,15 @@ r or 2:\tUse the right version.\n\
s:\tSilently include common lines.\n\
v:\tVerbosely include common lines.\n\
q:\tQuit.\n\
"));
"), stderr);
}
static int
skip_white (void)
{
int c;
for (;;)
{
c = getchar ();
if (! c_isspace (c) || c == '\n')
break;
checksigs ();
}
while ((c = getchar ()) != '\n' && c_isspace (c))
checksigs ();
if (ferror (stdin))
perror_fatal (_("read failed"));
return c;
@ -856,8 +853,7 @@ edit (struct line_filter *left, char const *lname, lin lline, lin llen,
{
for (;;)
{
int cmd0;
int cmd1;
int cmd;
bool gotcmd = false;
while (! gotcmd)
@ -866,48 +862,41 @@ edit (struct line_filter *left, char const *lname, lin lline, lin llen,
perror_fatal (_("write failed"));
ck_fflush (stdout);
cmd0 = skip_white ();
switch (cmd0)
cmd = skip_white ();
switch (cmd)
{
case '1': case '2': case 'l': case 'r':
case 's': case 'v': case 'q':
if (skip_white () != '\n')
{
give_help ();
flush_line ();
continue;
}
gotcmd = true;
break;
case 'e':
cmd1 = skip_white ();
case 'e':;
int cmd1 = skip_white ();
switch (cmd1)
{
case '1': case '2': case 'b': case 'd': case 'l': case 'r':
if (skip_white () != '\n')
{
give_help ();
flush_line ();
continue;
}
gotcmd = true;
break;
case '\n':
gotcmd = true;
break;
continue;
default:
give_help ();
flush_line ();
continue;
continue;
case '1': case '2': case 'b': case 'd': case 'l': case 'r':
cmd |= cmd1 << UCHAR_WIDTH;
break;
}
FALLTHROUGH;
case '1': case '2': case 'l': case 'r':
case 's': case 'v': case 'q':
if (skip_white () == '\n')
gotcmd = true;
else
{
give_help ();
flush_line ();
}
break;
case EOF:
if (feof (stdin))
{
gotcmd = true;
cmd0 = 'q';
cmd = 'q';
break;
}
FALLTHROUGH;
@ -916,11 +905,11 @@ edit (struct line_filter *left, char const *lname, lin lline, lin llen,
FALLTHROUGH;
case '\n':
give_help ();
continue;
break;
}
}
switch (cmd0)
switch (cmd & UCHAR_MAX)
{
case '1': case 'l':
lf_copy (left, llen, outfile);
@ -953,6 +942,7 @@ edit (struct line_filter *left, char const *lname, lin lline, lin llen,
if (! tmp)
perror_fatal (squote (0, tmpname));
int cmd1 = cmd >> UCHAR_WIDTH;
switch (cmd1)
{
case 'd':

View File

@ -1,6 +1,6 @@
/* sdiff-format output routines for GNU DIFF.
Copyright (C) 1991-1993, 1998, 2001-2002, 2004, 2009-2013, 2015-2025 Free
Copyright (C) 1991-1993, 1998, 2001-2002, 2004, 2009-2013, 2015-2026 Free
Software Foundation, Inc.
This file is part of GNU DIFF.

350
src/syncsig.c Normal file
View File

@ -0,0 +1,350 @@
/* Synchronous signal handling
Copyright 2025-2026 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/>. */
#include <config.h>
#include "syncsig.h"
#include "minmax.h"
#include <signal.h>
#include <stdcountof.h>
/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
present. */
#ifndef SA_NOCLDSTOP
# define SA_NOCLDSTOP 0
# define sigprocmask(how, set, oset) 0
# if ! HAVE_SIGINTERRUPT
# define siginterrupt(sig, flag) 0
# endif
#endif
#ifndef SA_RESTART
# define SA_RESTART 0
#endif
#ifndef SIGSTOP
# define SIGSTOP 0
#endif
/* The set of signals that are caught. */
static sigset_t caught_signals;
/* The signals we can catch.
This includes all catchable GNU/Linux or POSIX signals that by
default are ignored, or that stop or terminate the process.
It also includes SIGQUIT since that can come from the terminal.
It excludes other signals that normally come from program failure.
If you modify this table, also modify signal_count's initializer. */
static int const catchable[] =
{
/* SIGABRT is normally from program failure. */
SIGALRM,
/* SIGBUS is normally from program failure. */
#ifdef SIGCHLD
SIGCHLD,
#else
# define SIGCHLD 0
#endif
/* SIGCLD (older platforms) is an alias for SIGCHLD. */
#ifdef SIGCONT
SIGCONT,
#else
# define SIGCONT 0
#endif
/* SIGEMT is normally from program failure. */
/* SIGFPE is normally from program failure. */
SIGHUP,
/* SIGILL is normally from program failure. */
/* SIGINFO is an alias for SIGPWR on Linux. */
SIGINT,
/* SIGIO is an alias for SIGPOLL. */
/* SIGKILL can't be caught. */
#ifdef SIGLOST
SIGLOST,
#else
# define SIGLOST 0
#endif
SIGPIPE,
#ifdef SIGPOLL /* Removed from POSIX.1-2024; still in Linux. */
SIGPOLL,
#else
# define SIGPOLL 0
#endif
#ifdef SIGPROF /* Removed from POSIX.1-2024; still in Linux. */
SIGPROF,
#else
# define SIGPROF 0
#endif
#ifdef SIGPWR
SIGPWR,
#else
# define SIGPWR 0
#endif
SIGQUIT,
/* SIGSEGV is normally from program failure. */
/* Linux SIGSTKFLT is unused. */
/* SIGSTOP can't be caught. */
/* SIGSYS is normally from program failure. */
SIGTERM,
/* SIGTRAP is normally from program failure. */
#ifdef SIGTSTP
SIGTSTP,
#else
# define SIGTSTP 0
#endif
#ifdef SIGTTIN
SIGTTIN,
#else
# define SIGTTIN 0
#endif
#ifdef SIGTTOU
SIGTTOU,
#else
# define SIGTTOU 0
#endif
#ifdef SIGURG
SIGURG,
#else
# define SIGURG 0
#endif
#ifdef SIGUSR1
SIGUSR1,
#else
# define SIGUSR1 0
#endif
#ifdef SIGUSR2
SIGUSR2,
#else
# define SIGUSR2 0
#endif
#ifdef SIGVTALRM
SIGVTALRM,
#else
# define SIGVTALRM 0
#endif
#ifdef SIGWINCH
SIGWINCH,
#else
# define SIGWINCH 0
#endif
#ifdef SIGXCPU
SIGXCPU,
#else
# define SIGXCPU 0
#endif
#ifdef SIGXFSZ
SIGXFSZ,
#else
# define SIGXFSZ 0
#endif
};
/* Number of pending signals received, for each signal type. */
static sig_atomic_t volatile signal_count[] =
{
/* Explicitly initialize, so that the table is large enough. */
[SIGALRM] = 0,
[SIGCHLD] = 0,
[SIGCONT] = 0,
[SIGHUP] = 0,
[SIGINT] = 0,
[SIGLOST] = 0,
[SIGPIPE] = 0,
[SIGPOLL] = 0,
[SIGPROF] = 0,
[SIGPWR] = 0,
[SIGQUIT] = 0,
[SIGTERM] = 0,
[SIGTSTP] = 0,
[SIGTTIN] = 0,
[SIGTTOU] = 0,
[SIGURG] = 0,
[SIGUSR1] = 0,
[SIGUSR2] = 0,
[SIGVTALRM] = 0,
[SIGWINCH] = 0,
[SIGXCPU] = 0,
[SIGXFSZ] = 0,
};
/* This acts as bool though its type is sig_atomic_t.
If true, signal_count might contain nonzero entries.
If false, signal_count is all zero.
This is to speed up syncsig_poll when it returns 0. */
static sig_atomic_t volatile possible_signal_count;
/* Actions before syncsig_install was called. */
#if SA_NOCLDSTOP
static struct sigaction oldact[countof (signal_count)];
#else
static void (*oldact[countof (signal_count)]) (int);
#endif
/* Record an asynchronous signal. This function is async-signal-safe. */
static void
sighandler (int sig)
{
#if !SA_NOCLDSTOP
/* An unavoidable race here: the default action might mistakenly
be taken before 'signal' is called. */
signal (sig, sighandler);
#endif
possible_signal_count = true;
signal_count[sig]++;
}
void
syncsig_install (int flags)
{
for (int i = 0; i < countof (catchable); i++)
signal_count[catchable[i]] = 0;
possible_signal_count = false;
sigemptyset (&caught_signals);
for (int i = 0; i < countof (catchable); i++)
{
int sig = catchable[i];
if (((sig == SIGTSTP) & !(flags & SYNCSIG_TSTP))
| ((sig == SIGTTIN) & !(flags & SYNCSIG_TTIN))
| ((sig == SIGTTOU) & !(flags & SYNCSIG_TTOU)))
continue;
#if SA_NOCLDSTOP
sigaction (sig, nullptr, &oldact[sig]);
bool catching_sig = oldact[sig].sa_handler != SIG_IGN;
#else
oldact[i] = signal (sig, SIG_IGN);
bool catching_sig = oldact[i] != SIG_IGN;
if (catching_sig)
{
/* An unavoidable race here: SIG might be mistakenly ignored
before 'signal' is called. */
signal (sig, sighandler);
siginterrupt (sig, 0);
}
#endif
if (catching_sig)
sigaddset (&caught_signals, sig);
}
#if SA_NOCLDSTOP
struct sigaction act;
act.sa_handler = sighandler;
act.sa_mask = caught_signals;
act.sa_flags = SA_RESTART;
for (int i = 0; i < countof (catchable); i++)
{
int sig = catchable[i];
if (sigismember (&caught_signals, sig))
sigaction (sig, &act, nullptr);
}
#endif
}
void
syncsig_uninstall (void)
{
for (int i = 0; i < countof (catchable); i++)
{
int sig = catchable[i];
if (sigismember (&caught_signals, sig))
{
#if SA_NOCLDSTOP
sigaction (sig, &oldact[sig], nullptr);
#else
signal (sig, oldact[sig]);
#endif
}
}
}
int
syncsig_poll (void)
{
int sig = 0;
if (possible_signal_count)
{
/* This module uses static rather than thread-local storage,
so it is useful only in single-threaded programs,
and it uses sigprocmask rather than pthread_sigmask. */
sigset_t oldset;
sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
for (int i = 0; ; i++)
{
if (i == countof (catchable))
{
possible_signal_count = false;
break;
}
int s = catchable[i];
int c = signal_count[s];
if (c)
{
signal_count[s] = c - 1;
sig = s;
break;
}
}
sigprocmask (SIG_SETMASK, &oldset, nullptr);
}
return sig;
}
void
syncsig_deliver (int sig)
{
#if SA_NOCLDSTOP
struct sigaction act;
#else
void (*act) (int);
#endif
if (sig == SIGTSTP)
sig = SIGSTOP;
else
{
#if SA_NOCLDSTOP
sigaction (sig, &oldact[sig], &act);
#else
act = signal (sig, oldact[sig]);
#endif
}
raise (sig);
if (sig != SIGSTOP)
{
/* The program did not exit due to the raised signal, so continue. */
#if SA_NOCLDSTOP
sigaction (sig, &act, nullptr);
#else
signal (sig, act);
#endif
}
}

60
src/syncsig.h Normal file
View File

@ -0,0 +1,60 @@
/* Synchronous signal handling
Copyright 2025-2026 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/>. */
/* Written by Paul Eggert. */
/* Flags for syncsig_install. */
enum
{
/* Also catch SIGTSTP, SIGTTIN, SIGTTOU, which by default stop the process.
These flags have no effect on platforms lacking these signals. */
SYNCSIG_TSTP = (1 << 0),
SYNCSIG_TTIN = (1 << 1),
SYNCSIG_TTOU = (1 << 2),
};
/* Set up asynchronous signal handling according to FLAGS.
syncsig_install fails only on unusual platforms where
valid calls to functions like sigaction can fail;
if it fails, signal handling is in a weird state
and neither syncsig_process nor syncsig_uninstall should be called. */
extern void syncsig_install (int flags);
/* Return a signal number if a signal has arrived, zero otherwise.
After syncsig_install, there should not be an unbounded amount of
time between calls to this function, and its result should be dealt
with promptly. */
extern int syncsig_poll (void);
/* Do the action for SIG that would have been done
had syncsig_install not been called.
SIG should have recently been returned by syncsig_poll.
For example, if SIG is SIGTSTP stop the process and return after
SIGCONT is delivered. Another example: kill the process if SIG is
SIGINT and SIGINT handling is the default.
This function should be called only after syncsig_install. */
extern void syncsig_deliver (int sig);
/* Stop doing asynchronous signal handling, undoing syncsig_install.
This function should be called only after syncsig_install.
To deal with signals arriving just before calling this function,
call syncsig_poll afterwards. */
extern void syncsig_uninstall (void);

View File

@ -1,7 +1,7 @@
/* System dependent declarations.
Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006, 2009-2013,
2015-2025 Free Software Foundation, Inc.
2015-2026 Free Software Foundation, Inc.
This file is part of GNU DIFF.

View File

@ -1,7 +1,7 @@
/* System dependent declarations.
Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006, 2009-2013,
2015-2025 Free Software Foundation, Inc.
2015-2026 Free Software Foundation, Inc.
This file is part of GNU DIFF.

View File

@ -1,7 +1,7 @@
/* Support routines for GNU DIFF.
Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006, 2009-2013,
2015-2025 Free Software Foundation, Inc.
2015-2026 Free Software Foundation, Inc.
This file is part of GNU DIFF.
@ -27,31 +27,11 @@
#include <flexmember.h>
#include <mcel.h>
#include <quotearg.h>
#include <syncsig.h>
#include <system-quote.h>
#include <xalloc.h>
#include <stdarg.h>
#include <signal.h>
/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
present. */
#ifndef SA_NOCLDSTOP
# define SA_NOCLDSTOP 0
# define sigprocmask(How, Set, Oset) 0
# if ! HAVE_SIGINTERRUPT
# define siginterrupt(sig, flag) 0
# endif
#endif
#ifndef SA_RESTART
# define SA_RESTART 0
#endif
#ifndef SIGSTOP
# define SIGSTOP 0
#endif
#ifndef SIGTSTP
# define SIGTSTP 0
#endif
char const pr_program[] = PR_PROGRAM;
@ -184,78 +164,9 @@ print_message_queue (void)
m = next;
}
}
/* Signal handling, needed for restoring default colors. */
static void
xsigaddset (sigset_t *set, int sig)
{
if (sigaddset (set, sig) != 0)
pfatal_with_name ("sigaddset");
}
static bool
xsigismember (sigset_t const *set, int sig)
{
int mem = sigismember (set, sig);
if (mem < 0)
pfatal_with_name ("sigismember");
assume (mem <= 1);
return mem;
}
typedef void (*signal_handler) (int);
static signal_handler
xsignal (int sig, signal_handler func)
{
signal_handler h = signal (sig, func);
if (h == SIG_ERR)
pfatal_with_name ("signal");
return h;
}
static void
xsigprocmask (int how, sigset_t const *restrict set, sigset_t *restrict oset)
{
if (sigprocmask (how, set, oset) != 0)
pfatal_with_name ("sigprocmask");
}
/* If true, some signals are caught. This is separate from
'caught_signals' because POSIX doesn't require an all-zero sigset_t
to be valid. */
static bool some_signals_caught;
/* The set of signals that are caught. */
static sigset_t caught_signals;
/* If nonzero, the value of the pending fatal signal. */
static sig_atomic_t volatile interrupt_signal;
/* A count of the number of pending stop signals that have been received. */
static sig_atomic_t volatile stop_signal_count;
/* An ordinary signal was received; arrange for the program to exit. */
static void
sighandler (int sig)
{
if (! SA_NOCLDSTOP)
signal (sig, SIG_IGN);
if (! interrupt_signal)
interrupt_signal = sig;
}
/* A SIGTSTP was received; arrange for the program to suspend itself. */
static void
stophandler (int sig)
{
if (! SA_NOCLDSTOP)
signal (sig, stophandler);
if (! interrupt_signal)
stop_signal_count++;
}
/* Process any pending signals. If signals are caught, this function
should be called periodically. Ideally there should never be an
unbounded amount of time when signals are not being processed.
@ -265,131 +176,18 @@ stophandler (int sig)
static void
process_signals (void)
{
while (interrupt_signal | stop_signal_count)
for (int sig; (sig = syncsig_poll ()); )
{
set_color_context (RESET_CONTEXT);
fflush (stdout);
sigset_t oldset;
xsigprocmask (SIG_BLOCK, &caught_signals, &oldset);
/* Reload stop_signal_count and (if needed) interrupt_signal, in
case a new signal was handled before sigprocmask took effect. */
int stops = stop_signal_count, sig;
/* SIGTSTP is special, since the application can receive that signal
more than once. In this case, don't set the signal handler to the
default. Instead, just raise the uncatchable SIGSTOP. */
if (stops)
{
stop_signal_count = stops - 1;
sig = SIGSTOP;
}
else
{
sig = interrupt_signal;
xsignal (sig, SIG_DFL);
}
/* Exit or suspend the program. */
if (raise (sig) != 0)
pfatal_with_name ("raise");
xsigprocmask (SIG_SETMASK, &oldset, nullptr);
/* If execution reaches here, then the program has been
continued (after being suspended). */
syncsig_deliver (sig);
}
}
/* The signals that can be caught, the number of such signals,
and which of them are actually caught. */
static int const sig[] =
{
#if SIGTSTP
/* This one is handled specially; see is_tstp_index. */
SIGTSTP,
#endif
/* The usual suspects. */
#ifdef SIGALRM
SIGALRM,
#endif
#ifdef SIGHUP
SIGHUP,
#endif
SIGINT,
#ifdef SIGPIPE
SIGPIPE,
#endif
#ifdef SIGQUIT
SIGQUIT,
#endif
SIGTERM,
#ifdef SIGPOLL
SIGPOLL,
#endif
#ifdef SIGPROF
SIGPROF,
#endif
#ifdef SIGVTALRM
SIGVTALRM,
#endif
#ifdef SIGXCPU
SIGXCPU,
#endif
#ifdef SIGXFSZ
SIGXFSZ,
#endif
};
enum { nsigs = sizeof (sig) / sizeof *(sig) };
/* True if sig[j] == SIGTSTP. */
static bool
is_tstp_index (int j)
{
return SIGTSTP && j == 0;
}
static void
install_signal_handlers (void)
{
if (sigemptyset (&caught_signals) != 0)
pfatal_with_name ("sigemptyset");
#if SA_NOCLDSTOP
for (int j = 0; j < nsigs; j++)
{
struct sigaction actj;
if (sigaction (sig[j], nullptr, &actj) == 0 && actj.sa_handler != SIG_IGN)
xsigaddset (&caught_signals, sig[j]);
}
struct sigaction act;
act.sa_mask = caught_signals;
act.sa_flags = SA_RESTART;
for (int j = 0; j < nsigs; j++)
if (xsigismember (&caught_signals, sig[j]))
{
act.sa_handler = is_tstp_index (j) ? stophandler : sighandler;
if (sigaction (sig[j], &act, nullptr) != 0)
pfatal_with_name ("sigaction");
some_signals_caught = true;
}
#else
for (int j = 0; j < nsigs; j++)
{
signal_handler h = signal (sig[j], SIG_IGN);
if (h != SIG_IGN && h != SIG_ERR)
{
xsigaddset (&caught_signals, sig[j]);
xsignal (sig[j], is_tstp_index (j) ? stophandler : sighandler);
some_signals_caught = true;
if (siginterrupt (sig[j], 0) != 0)
pfatal_with_name ("siginterrupt");
}
}
#endif
syncsig_install (SYNCSIG_TSTP);
}
/* Clean up signal handlers just before exiting the program. Do this
@ -398,14 +196,11 @@ install_signal_handlers (void)
void
cleanup_signal_handlers (void)
{
if (some_signals_caught)
{
for (int j = 0; j < nsigs; j++)
if (xsigismember (&caught_signals, sig[j]))
xsignal (sig[j], SIG_DFL);
process_signals ();
}
syncsig_uninstall ();
process_signals ();
}
/* Color handling. */
static char const *current_name[2];
static bool currently_recursive;

View File

@ -9,6 +9,7 @@ TESTS = \
cmp \
colliding-file-names \
diff3 \
empty-file \
excess-slash \
expand-tabs \
help-version \
@ -24,6 +25,7 @@ TESTS = \
no-dereference \
no-newline-at-eof \
side-by-side \
side-by-side-seq \
starting-file \
stdin \
strcoll-0-names \

View File

@ -1,7 +1,7 @@
#!/bin/sh
# Test 'cmp'.
# Copyright 2017-2025 Free Software Foundation, Inc.
# Copyright 2017-2026 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
@ -247,7 +247,7 @@ cmp -i 1000 -n $big j1 j2 || fail=1
rm -f a b
if timeout 0.1 true && truncate -s 14T a && truncate -s 15T b; then
returns_ 1 timeout 0.1 cmp a b >/dev/null || fail=1
returns_ 1 timeout 0.4 cmp a b >/dev/null || fail=1
fi
rm -f a b

View File

@ -3,18 +3,6 @@
. "${srcdir=.}/init.sh"; path_prepend_ ../src
# Some systems lack seq.
# A limited replacement for seq: handle 1 or 2 args; increment must be 1
seq()
{
case $# in
1) start=1 final=$1;;
2) start=$1 final=$2;;
*) echo you lose 1>&2; exit 1;;
esac
awk 'BEGIN{for(i='$start';i<='$final';i++) print i}' < /dev/null
}
echo a > a || framework_failure_
echo b > b || framework_failure_
echo c > c || framework_failure_

23
tests/empty-file Executable file
View File

@ -0,0 +1,23 @@
#!/bin/sh
# Test empty files
. "${srcdir=.}/init.sh"; path_prepend_ ../src
fail=0
mkdir a b
touch a/test
echo 'content' > b/test
returns_ 1 diff -Naur a b >out || fail=1
cat <<EOF > expected || framework_failure_
diff -Naur a/test b/test
--- a/test
+++ b/test
@@ -0,0 +1 @@
+content
EOF
# Remove date and time.
sed -e 's/^\([-+*][-+*][-+*] [^ ]*\) .*/\1/' out > k; mv k out
compare expected out || fail=1
Exit $fail

View File

@ -1,7 +1,7 @@
# -*- sh -*-
# Check environment variables for sane values while testing.
# Copyright (C) 2000-2025 Free Software Foundation, Inc.
# Copyright (C) 2000-2026 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

View File

@ -2,7 +2,7 @@
# Make sure all these programs work properly
# when invoked with --help or --version.
# Copyright (C) 2000-2013, 2015-2025 Free Software Foundation, Inc.
# Copyright (C) 2000-2013, 2015-2026 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

View File

@ -1,6 +1,6 @@
# This file is sourced by init.sh, *before* its initialization.
# Copyright (C) 2010-2025 Free Software Foundation, Inc.
# Copyright (C) 2010-2026 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
@ -120,4 +120,16 @@ require_utf8_locale_()
test $found_working_tr = 1 || skip_ "failed to find a working tr program"
}
# Some systems lack seq.
# A limited replacement for seq: handle 1 or 2 args; increment must be 1
seq()
{
case $# in
1) start=1 final=$1;;
2) start=$1 final=$2;;
*) echo you lose 1>&2; exit 1;;
esac
awk 'BEGIN{for(i='$start';i<='$final';i++) print i}' < /dev/null
}
sanitize_path_

26
tests/side-by-side-seq Executable file
View File

@ -0,0 +1,26 @@
#!/bin/sh
# Test side-by-side output on sequences.
. "${srcdir=.}/init.sh"; path_prepend_ ../src
fail=0
seq 1 100 >in1 || framework_failure_
(seq 1 49 && seq 51 100) >in2 || framework_failure_
awk '
BEGIN {
for (i = 1; i <= 100; i++) {
if (i == 50) {
print "50 <"
} else {
printf "%d\t%d\n", i, i
}
}
}
' </dev/null >exp || framework_failure_
returns_ 1 diff -yW 11 in1 in2 >out 2>err || fail=1
compare exp out || fail=1
compare /dev/null err || fail=1
Exit $fail