Compare commits

...

310 Commits

Author SHA1 Message Date
Daniel Black
d161c9a9db Skip read-only check when output file specified
Only check if input files are read-only when overwriting them; skip
that check when the output goes to a different file.

* src/patch.c (main): Set read_only_behavior to RO_IGNORE when an output
file has been specified.
* tests/read-only-files: Add test case.
2025-05-01 13:40:20 +02:00
Andreas Gruenbacher
4c302306a8 Reject empty filenames
* src/safe.c (safe_xstat): Reject empty pathnames.
* tests/bad-filenames: Add a new test.
* src/patch.c (main): Don't check if the input file is writable when
we're not going to modify it.
2025-04-05 17:26:56 +02:00
Andreas Gruenbacher
aea66268c9 Add some missing filename quoting
* src/patch.c (main): Quote outname.
2025-04-05 17:25:45 +02:00
Andreas Gruenbacher
77c27209f6 Fix 'make release' for proper releases
(Proper releases have a signed git tag.)
2025-03-29 22:40:26 +01:00
Andreas Gruenbacher
48ceda8200 Version 2.8
* NEWS: Update.
2025-03-29 17:17:03 +01:00
Petr Vaněk
b3d0c93338 Regression in commit abe92e8010ab affecting MariaDB tests
I have disovered a regression in commit abe92e8010ab ("Prefer idx_t,
ptrdiff_t to lin") while I was running MariaDB tests. The regression is
related to a diff file [1], where the patch fails to apply it with
following error:

  patch: **** '---' expected at line 2 of patch

To illustrate the issue, I have attached a git patch containing a
testcase with simplified reproducer.

[1] https://github.com/MariaDB/server/blob/mariadb-10.6.21/mysql-test/suite/innodb/r/innodb-wl5522%2Cstrict_crc32.rdiff file
2025-02-26 16:46:07 -08:00
Paul Eggert
29fced6251 Count traditional diff pattern lines correctly
This fixes a bug I introduced on Thu Sep 5 16:37:50 2024 -0700.
Problem reported by Petr Vaněk in:
https://lists.gnu.org/archive/html/bug-patch/2025-02/msg00017.html
* src/pch.c (another_hunk): Fix method for counting number
of lines in a traditional diff hunk.
2025-02-26 16:46:07 -08:00
Paul Eggert
b5d2124e2e patch: fix --no-backup-if-mismatch regression
Problem reported by Sam James in:
https://lists.gnu.org/archive/html/bug-patch/2025-02/msg00014.html
https://bugs.gentoo.org/show_bug.cgi?id=949834
* src/patch.c (backup_if_mismatch_specified): New static var.
(get_some_switches): Set it.
(main): Default backup_if_mismatch only if not set on command line.
* tests/no-backup: New file.
* tests/Makefile.am (TESTS): Add it.
2025-02-24 23:29:59 -08:00
Sam James
86ac7e2d7b Fix dodgy assert with side-effects in insert_cached_dirfd
Michał Górny <mgorny@gentoo.org> reported that patch was running out of
FDs and that the `deep-directories` test was failing. This turns out
to be because `hash_insert` isn't called at all with `-DNDEBUG` because
`insert_cached_dirfd` only calls it in one case inside of an `assert`.

See https://github.com/conda-forge/patch-feedstock/issues/11.

This regressed in 025a54b789bd88ed15430f8633514e296826983e.

* src/safe.c (insert_cached_dirfd): Don't use 'assert' for 'hash_insert'
  call with side-effects.
2025-02-06 21:13:20 +01:00
Bruno Haible
7d876522e9 Declare an expected test failure on Haiku.
* bootstrap.conf (gnulib_modules): Add test-xfail.
* tests/preserve-mode-and-timestamp: Add a comment regarding Haiku.
* tests/Makefile.am (XFAIL_TESTS): On Haiku, add preserve-mode-and-timestamp.
2025-02-06 21:03:10 +01:00
Andreas Gruenbacher
86baf979f4 build: update gnulib submodule to latest 2025-02-06 21:02:41 +01:00
Bruno Haible
1ba2c1bdf8 Fix two test failures on Haiku.
On Haiku, all error numbers are negative, see
<https://www.gnu.org/software/gnulib/manual/html_node/errno_002eh.html>.

Bisected by Eli Schwartz <eschwartz@gentoo.org>.

This partially reverts commit 043355371a76de8ea7d06f79a69fde905af7cc45.

* src/inp.c (get_input_file):
* src/patch.c (main):
* src/safe.c (read_symlink):
* src/util.c (move_file):
Don't assume that all system-defined errno values are positive.
2025-02-05 13:29:32 -08:00
Paul Eggert
1da6bf84db Check for newlines only when creating a file name
Also, check only the last file name component.
In other words, mimic operating systems that follow POSIX.1-2024’s
encouragement to fail with EILSEQ when openat etc. create a file name.
This is more conservative than the previous patch to prohibit
newlines in file names.
* src/patch.c (main, backup_file_name_option, get_some_switches):
* src/util.c (parse_c_string, make_tempfile):
Don’t check for newlines in a file name unless we are definitely
creating a file, as it’s harmless to read and stat file with
newlines in their names if the OS allows that.
* src/safe.c (traverse_another_path, traverse_path): New arg
REJECT_NL.  If set, reject any file name whose last component
contains a newline.  Also, do not do traversal if unsafe.  All
callers changed to pass true if they are creating the file name,
false otherwise, and to not bother checking whether we are unsafe.
(safe_open): Special case for when O_CREAT is set but O_EXCL is not.
* src/util.c (pfatal): Report "Invalid byte sequence" for EILSEQ.
This POSIX wording is less confusing than glibc's "Invalid or
incomplete multibyte or wide character".  Also, this lets
the test cases check for this wording.
* tests/bad-filenames: Adjust to new diagnostic wording.
2025-01-08 09:53:45 -08:00
Paul Eggert
30ee6103a5 Gnulib renamed some modules
* bootstrap.conf (gnulib_modules):
Adjust to recent module renaming in Gnulib.
2025-01-05 13:33:00 -08:00
Paul Eggert
6dbc381055 maint: update bootstrap from gnulib 2025-01-05 13:33:00 -08:00
Paul Eggert
05ac924fef build: update gnulib submodule to latest 2025-01-05 13:33:00 -08:00
Paul Eggert
95e0092f97 maint: make update-copyright 2025-01-05 13:33:00 -08:00
Andreas Gruenbacher
5bac274507 Set --no-backup-if-mismatch when in --posix mode
When in POSIX mode, the --no-backup-if-mismatch option should be
enabled.  However, this is only true when the POSIXLY_CORRECT
environmant variable is set but not when the --posix command line
option is given.  Fix that by setting backup_if_mismatch after
evaluating the command line arguments.
2024-11-20 23:54:48 +01:00
Andreas Gruenbacher
910fecf695 Add missing feature tests to the test suite
Check for chmod, hardlink, symlink, and special character support to
prevent test suite failures in feature constrained environments.

Thanks to Bruno Haible and Nelson H. F. Beebe for their testing and
analysis.

* tests/test-lib.sh: Add new feature tests.
* tests/hardlinks: Split this hardlinks related test off from
tests/remember-backup-files.
* tests/Makefile.am (TESTS): Add new hardlinks test here.
* tests/file-create-modes, tests/file-modes, tests/read-only-files,
tests/preserve-mode-and-timestamp, tests/no-mode-change-git-diff: These
tests require chmod support.
* tests/hardlinks, tests/unmodified-files: These tests require hardlink
support.
* tests/symlinks: This test requires symlink support.
* tests/quoted-filenames: This test requires special character support
in filenames.
2024-11-11 22:21:27 +01:00
Andreas Gruenbacher
be8b3c68b0 Disable release-prep
* cfg.mk: Disable release-prep by overriding the release-prep-hook for
now.  With that, "make release" succeeds for alpha releases.
2024-10-11 13:56:15 +02:00
Eli Schwartz
c61485bd05 Fix "make release" to handle alpha releases
news-check-regexp to scan for unreleased changes, has to be set
conditional on the release type. It has to be defined in cfg.mk, not
Makefile.am, so key off of the RELEASE_TYPE as automake conditionals are
not available.

Signed-off-by: Eli Schwartz <eschwartz@gentoo.org>
2024-10-11 13:53:07 +02:00
Andreas Gruenbacher
499916fdd8 Add announce-gen module for "make release"
* bootstrap.conf (gnulib_modules): Add announce-gen.
2024-10-11 11:43:59 +02:00
Paul Eggert
adb1ebce7f Pacify gcc -fsanitize=address
Some of this merely pacifies -fsanitize=address by pointing
to storage rather than freeing it when we are about to exit anyway.
Other parts of it keep track of storage more carefully so that it
can be freed rather than leak.
* src/common.h (struct outfile): New member ‘alloc’.
* src/patch.c (files_to_output_root) [SANITIZE_ADDRESS]:
New externally visible variable, to pacify -fsanitize=address.
(main): Use new functions described below to remove
files and free memory.
(delete_files): Do not free the list as we go, as we are about
to exit and -fsanitize=address doesn’t care about this storage.
(output_file_later): Set and use new member ‘alloc’ to avoid
memory leaks.
(output_files): WIth -fsanitize=address, record the list head
in files_to_output_root that the address sanitizer can see,
so that it won’t complain when we don’t free storage just before exit.
Free only when not exiting, and free using ‘alloc’ rather than ‘name’.
(perfile_cleanup_remove): New function.
(cleanup_remove): Rename from ‘cleanup’.  All uses changed.
Reimplement in terms of perfile_cleanup_remove.
(free_outfile_name, perfile_cleanup_free): New functions.
* src/util.c (make_tempfile): Set new member ‘alloc’.
2024-09-20 00:20:06 -07:00
Paul Eggert
6bdae94eb3 Fix memory leak when malformed unidiff patch
* src/pch.c (another_hunk): Fix memory leak when scanning a
unidiff patch malformed due to a line that does not begin with
‘ ’, ‘-’, ‘+’, or ‘=’.
2024-09-20 00:20:06 -07:00
Paul Eggert
72a146cfaf Port to clang address sanitizer
* src/patch.c (FREE_BEFORE_EXIT): Port to clang, which
uses __has_feature (address_sanitizer) instead of
defined __SANITIZE_ADDRESS__.  Also, rename this to
SANITIZE_ADDRESS since it is really about -fsanitize=address
rather than freeing before exit and as subsequent patches
will show there are simpler ways to pacify -fsanitize=address.
All uses changed.
2024-09-20 00:20:06 -07:00
Paul Eggert
e2e6820f71 Refactor argc+argv processing
* src/patch.c (Argc, Argv): Remove these confusing static variables.
They date back to before the code used getopt_long,
and are no longer needed.  All uses changed.
(get_some_switches): New args argc+argv, which are now used
instead of the static vars.  All uses changed.
2024-09-20 00:20:06 -07:00
Paul Eggert
606c09176c Omit needless get_some_switches code
* src/patch.c (get_some_switches): Remove unnecessary
initialization and testing.
2024-09-20 00:20:06 -07:00
Andreas Gruenbacher
ee3cc4016d Revert "Remove obsolete require_gnu_diff function"
Oops, function require_gnu_diff is still needed in two places.

This reverts commit 8cae4fc2213649e36e8f9a4cf21c28a82de3705c.
2024-09-18 14:51:33 +02:00
Andreas Gruenbacher
8cae4fc221 Remove obsolete require_gnu_diff function
* tests/test-lib.sh (require_gnu_diff): Remove.
2024-09-18 14:42:49 +02:00
Paul Eggert
164b529f53 savebuf can return a null pointer
* src/util.h (savebuf): Do not declare with ATTRIBUTE_RETURNS_NONNULL.
Bug caught by gcc -fsanitize=undefined.
2024-09-17 23:46:23 -07:00
Paul Eggert
91c1e4f071 Spelling fixes 2024-09-17 22:51:04 -07:00
Paul Eggert
a03e1bac4a Port other reject-format test to non-GNU diff
* tests/reject-format: Also don’t assume GNU diff
for the ab.diff file.  Problem discovered on Solaris 11.4.
2024-09-17 22:28:45 -07:00
Paul Eggert
9ba5eb00b3 Don’t be fooled by NUL bytes in diff directives
* src/pch.c (get_line, pget_line): New arg ALLOW_NUL.
It is true when getting data lines, which can contain NUL,
but false when getting ‘diff’ directives, which cannot.
All uses changed.
* tests/bad-filenames: Check that ‘patch’ rejects
directives containing NUL.
2024-09-17 17:43:23 -07:00
Paul Eggert
79dd5e762c Don’t be fooled by "\000" in file name
* src/util.c (parse_c_string): Don’t be tricked by perverse
C-style quoted strings containing backslash, '0', '0', '0'.
* tests/quoted-filenames: Test this.
2024-09-17 17:43:23 -07:00
Paul Eggert
8492a6a212 Port to quasi-GNU diff
Problem reported by Jim Meyering: ‘diff’ acted like GNU diff,
and generated correct output, but the output differed slightly
from what the test wanted.  As the output of ‘diff’ is not
completely determined from its input, it’s better to put
the desired ‘diff’ output directly in the test when the test
depends on the exact output.
* tests/preserve-c-function-names, tests/reject-format:
Do not require GNU diff or use ‘diff’.
Instead, cat the desired ‘diff’ output.
2024-09-17 17:43:23 -07:00
Paul Eggert
f6f2c6f31a In previous patch, make w_q static 2024-09-17 08:32:25 -07:00
Paul Eggert
0525681eb5 Pacify -Wunterminated-string-initialization
Problem reported by Jim Meyering.
* src/pch.c (do_ed_script): Pacify bleeding-edge GCC
-Wunterminated-string-initialization.
2024-09-17 08:27:49 -07:00
Andreas Gruenbacher
301411d927 Spelling fixes
Prefer 'timestamp' over 'time stamp'.
2024-09-17 10:59:25 +02:00
Andreas Gruenbacher
461520693e Remove double semicolon
* src/safe.c (remove_cached_dirfd): Remove a stray double semicolon.
2024-09-17 10:59:25 +02:00
Andreas Gruenbacher
923e0ef280 Prefer angle bracket headers
* src/util.h: Prefer angle brackets for gnulib header files.
2024-09-17 10:59:25 +02:00
Andreas Gruenbacher
5d17ca0e5f Update more old copyright notices
* configure.ac: Replace the old copyright notice with the current
version from COPYING.
* ChangeLog-2011: Likewise.
2024-09-17 10:59:15 +02:00
Paul Eggert
5f4edd389e Simplify memory allocation of files to delete
This pacifies ‘make sc_cast_of_argument_to_free’, which otherwise
complains about the ‘free ((void *) elt)’ in dispose_file_to_delete.
Rather than worry about pacifying that ‘make’ rule,
simplify memory allocation by doing the linked list by hand,
with a ‘next’ member the way our grandfathers did it.
This reduces the number of source code lines by 23,
removes the need for Gnulib’s linked-list and xlist modules,
and makes the code type-safer (as opposed to going through void *).
* bootstrap.conf (gnulib_modules): Remove linked-list, xlist.
* src/patch.c: Do not include gl_linked_list.h, gl_xlist.h.
(struct file_to_delete): New member ‘next’.
(files_to_delete): Now struct file_to_delete *, not gl_list_t.
(dispose_file_to_delete, init_files_to_delete):
Remove; no longer needed.
(files_to_delete_tail): New static var.
(delete_file_later): Append the new file by hand.
(delete_files): Iterate and free by hand.
2024-09-16 22:58:28 -07:00
Paul Eggert
802511c09b Report patch read errors more immediately
* src/pch.c (open_patch_file): Cache patch file descriptor.
When reading a patch, report read errors right away rather
than possibly waiting until end of input.
2024-09-16 17:39:10 -07:00
Paul Eggert
a93b50de71 Port fflush usage to OpenBSD 7.5
* src/inp.c (scan_input): Accept file descriptor, not stream.
All callers changed.
* src/patch.c (main): Do not obtain a stream for the patch
file descriptor, as scan_input merely needs a file descriptor.
This removes the need to call Fclose, which calls fflush,
which fails on OpenBSD 7.5 which (contra POSIX) does not
let you fflush an input stream.
2024-09-16 17:39:10 -07:00
Andreas Gruenbacher
55e224bd73 Update old copyright notices
* NEWS: Replace the old copyright notice with the current version from
COPYING.
* m4/setmode.m4: Likewise.
2024-09-17 00:07:22 +02:00
Paul Eggert
33a7fd89ec Fix gl_gcc_warnings typo in ‘configure’
* configure.ac (gl_gcc_warnings): Always set before using,
fixing a typo I introduced Sat Aug 24 08:28:18 2024 -0700.
2024-09-16 14:11:43 -07:00
Paul Eggert
2313b37f6a build: update gnulib submodule to latest 2024-09-16 13:35:24 -07:00
Paul Eggert
37fec39c58 Remove some dependencies no longer needed
* bootstrap.conf (gnulib_modules): Remove clock-time,
nstrftime, stdc_bit_ceil, time.
* src/inp.c: Do not include stdbit.h.
2024-09-16 13:34:01 -07:00
Paul Eggert
cc87173e46 Update NEWS as per recent changes 2024-09-15 22:50:38 -07:00
Paul Eggert
7887622b77 Update POSIX citations 2024-09-15 22:50:38 -07:00
Paul Eggert
18f4dd67ad Use “Gruenbacher” in international contexts
* patch.man: “Grünbacher” → “Gruenbacher”,
as per 2024-08-30 email from Andreas.
2024-09-15 22:50:38 -07:00
Paul Eggert
638675c19a Adjust libs to match recent Gnulib
* src/Makefile.am (patch_LDADD): Add $(CLOCK_TIME_LIB) (replacing
$(LIB_CLOCK_GETTIME)), $(EUIDACCESS_LIBGEN) (replacing
$(LIB_EACCESS), $(GETRANDOM_LIB), $(HARD_LOCALE_LIB), $(LIBINTL),
$(MBRTOWC_LIB), $(SETLOCALE_NULL_LIB).  This matches recent
Gnulib and is needed for AIX 7.1 which requires linking
with -lpthread.
2024-09-15 22:50:38 -07:00
Paul Eggert
53400a17ed Pacify clang -Wbitwise-conditional-parentheses
* src/util.c (copy_attr): Pacify clang 18.1.3 (1ubuntu1).
2024-09-15 22:50:38 -07:00
Paul Eggert
e8e1bcb7a0 Remove “support” for nested critical sections
It was a bit racy, and it’s no longer needed now that
we have shrunk critical sections.
* src/util.c (signals_are_deferred): Rename from signal_deferring_level.
Now effectively a boolean, instead of being a counter.
All uses changed.
2024-09-15 22:50:38 -07:00
Paul Eggert
b963510dcd Simplify critical section code in util fns
* src/util.c (move_file, create_file, copy_file):
Do not worry about whether the file is temporary
when deciding whether to make a section of code critical.
Just do it.  There’s no need for optimization now
that we don’t need syscalls around critical sections.
2024-09-15 22:50:38 -07:00
Paul Eggert
85949fb4ac Shrink critical sections
* src/patch.c (main):
Do not put calls to output_files in critical sections,
as it can now deal with signals.
(struct file_to_output): ‘from’ member is now struct outfile, not
char volatile *volatile.  All uses changed.
(output_file_later): Append new structure to list in a critical section.
(output_files): Redo so that calls need not be in a critical section.
Instead, put critical sections around small subsidiary parts.
2024-09-15 22:50:38 -07:00
Paul Eggert
448ff9bc72 Defer signals by hand with sigatomic_t
Refactor by using a sig_atomic_t variable instead of a sigprocmask
call to defer signals.  This should be good enough for a
single-thread app where we write all the code that needs critical
sections, and where the signal handler merely cleans up and exits.
The resulting code should have the same behavior (including
signal-handling races!) as the original.
* bootstrap.conf (gnulib_modules): Remove sigaction, sigprocmask.
Instead, use sigaction only if it’s supported natively,
as the Gnulib emulation of sigaction drags in code we no longer need.
* configure.ac: Check for sigaction, sigfillset.
* src/patch.c (fatal_cleanup): New async-signal-safe function,
which does the cleanup that the old fatal_exit (SIG) did when SIG
was nonzero.
(fatal_exit): Do what the old fatal_exit (SIG) did when SIG was zero.
Omit SIG arg.  All callers changed.  This function is no longer
called from signal handlers, and so no longer needs to be
async-signal-safe under some circumstances.  However, it now
defers signals.
* src/util.c (signal_received): New static var.
(signal_deferring_level): Now sig_atomic_t.
(fatal_cleanup_and_terminate, handle_signal): New functions.
(defer_signals, undefer_signals): Reimplement by
using sigatomic_t volatile vars, not by using sigprocmask.
(init_signals): Don’t assume SIGPIPE since we don’t use the
Gnulib sigpipe module.  Use simple sigfillset signal mask
so that we needn’t use sigprocmask to inquire about the
current signal mask.  Have a fallback for old platforms
that lack sigaction and sigfillset, since we no longer use
Gnulib’s sigaction module.
(exit_with_signal): Remove; no longer needed.
2024-09-15 22:50:38 -07:00
Paul Eggert
b95a603908 output_file_later avoid a malloc+free pair
* src/patch.c (output_file_later, output_files):
Combine two malloc calls into one, and likewise for free.
Rename local to ‘f’ to make the code easier to follow.
2024-09-15 22:50:38 -07:00
Paul Eggert
2663228379 Defend against closed stdin/stdout/stderr
Various parts of ‘patch’ can misbehave when stdin, stdout, stderr
are closed.  For example, ‘dup’ can yield 0, 1, or 2 and the
resulting file descriptor will be misused.  Although POSIX
requires that ‘patch’ be invoked with stdin/stdout/stderror open,
it’s better to defend against the possbility that they’re not
open.  Use Gnulib’s xstdopen module to defend against this.
* bootstrap.conf (gnulib_modules): Add xstdopen.
* src/patch.c: Include xstdopen.h.
(main): Call xstdopen before doing I/O.
2024-09-15 22:50:38 -07:00
Paul Eggert
fc779640b0 Stop using Gnulib ‘execute’ module
This is motivated by wanting to remove dependencies on
Gnulib’s sigprocmask etc. modules, in later patches.
* bootstrap.conf (gnulib_modules): Remove execute.
* src/pch.c: Don’t include execute.h.
(do_ed_script): Use ‘quote_system_arg’ and ‘systemic’, not
‘execute’, to run the editor command while avoiding
quoting vulnerabilities.
* src/util.c (quote_system_arg): Now extern.
2024-09-15 22:50:38 -07:00
Paul Eggert
470699c8f2 Move defer_signals up
* src/util.c (signal_deferring_level, defer_signals, undefer_signals):
Refactor by moving up.  This should simplify later patches.
2024-09-15 22:50:38 -07:00
Paul Eggert
f696e4ef12 Make sigs, NUM_SIGS local
* src/util.c (sigs, NUM_SIGS): Refactor by moving these static
constants into the only function that uses them.
2024-09-15 22:50:38 -07:00
Paul Eggert
4d3a4ab09a Rename block_signals
This refactoring prepares for a new algorithm that does not use
SIG_BLOCK, and where the name ‘block_signals’ would be misleading.
* src/util.c (signal_deferring_level, defer_signals, undefer_signals):
Rename from signal_blocking_level, block_signals, unblock_signals.
All uses changed.
2024-09-15 22:50:38 -07:00
Paul Eggert
b3bb925cee Trade a bit of space for time in parse_c_string
* src/util.c (parse_c_string): Omit unnecessary realloc.
2024-09-15 22:50:38 -07:00
Paul Eggert
070d859e07 Allocate first patchbuf statically
This way we avoid the need to call malloc first thing.
Usually there will no need to call malloc at all, for patchbuf.
* src/common.h (patchbuf, patchbufsize): Move extern decls from here ...
* src/pch.h: ... to here.
* src/patch.c (patchbuf, patchbufsize): Move defns from here ...
* src/pch.c: ... to here, and initialize patchbuf statically.
* src/patch.c (main): No need to initialize patchbuf.
* src/pch.c (initial_patchbuf): New static vbar.
(grow_patchbuf): New function.
(pget_line): Use grow_patchbuf rather than growing by hand.
* src/util.c: Include pch.h, for patchbuf.
(ask): Return patchbuf, not void, so that callers need not
include pch.h merely to access patchbuf.  All callers changed.
2024-09-15 22:50:38 -07:00
Paul Eggert
f46a90f137 Use bigger buffer size by default
This idea is taken from GNU coreutils.
* src/patch.c (patchbufsize): Initialize statically rather than
dynamically.
* src/util.c (IO_MAX): Remove.  All uses replaced by IO_BUFSIZE.
* src/util.h (IO_BUFSIZE): New constant, taken from coreutils.
2024-09-15 22:50:38 -07:00
Paul Eggert
2e64cfd98c Preinitialize fatal_act.sa_hander
* src/util.c (fatal_act): Initialize .sa_handler statically ...
(init_signals): ... rather than dynamically.
2024-09-15 22:50:38 -07:00
Paul Eggert
45de0d99c8 Prefer EXIT_SUCCESS etc. to literal integers
* src/patch.c (main, usage, get_some_switches, fatal_exit):
* src/pch.c (do_ed_script):
* src/util.c (exit_with_signal):
Prefer EXIT_SUCCESS, EXIT_FAILURE, EXIT_TROUBLE to 0, 1, 2.
* src/util.h (EXIT_TROUBLE): New constant.
2024-09-15 22:50:38 -07:00
Paul Eggert
82c4940a2e Fix some signal handling races
Also, when a signal arrives, clean up temporary files that were
not put in /tmp.
* src/patch.c (main): Block signals around every call to output_files
and to remove_if_needed.
(struct file_to_output): ‘from’ and ‘to’ are now volatile,
as they are accessed in signal handler.
(files_to_output): Now volatile, and done by hand instead of
using gl_list_t so that it can be volatile.  All uses changed.
(files_to_output_tail): New var, which points to pointer to last
object in list (if any).  Volatile so it can be accessed by
signal handler.  All updates of list changed.
(dispose_file_to_output, init_files_to_output, gl_list_clear):
Remove; no longer used now that we are doing this list by hand.
(output_files): 2nd arg is now int, not bool; -1 means we
are in a signal handler.  When exiting due to a signal handler,
merely unlink (not safe_unlink) the source, and do not use
‘free’; this way, the code is async-signal-safe.
(fatal_exit): Arrange for signals to be blocked, when not
invoked as a signal handler.  Call output_files even when
signaled, as it will now do something sensible by deleting
temp files without renaming them.
2024-09-15 22:50:38 -07:00
Paul Eggert
eceea619a6 Fix --set-utc TZ setting
* src/patch.c (main): Set TZ to "UTC0", which older POSIX requires
support for, as opposed to "UTC", which even POSIX.1-2024 arguably
does not require support for.  Also, check for setenv failure.
2024-09-15 22:50:38 -07:00
Paul Eggert
9c55d3f541 Improve logic for when rename removes source
* src/util.c (move_file): Don’t test outfrom when it’s already
known to be non-null.  Don’t worry about destination link count
if we created the source.
2024-09-15 22:50:38 -07:00
Paul Eggert
61c72f07b3 Fix signal race when renaming file
* src/util.c (move_file): Fix race if a signal arives between
the time we rename a file and we mark the source as not existing.
2024-09-15 22:50:38 -07:00
Paul Eggert
705c9bcdc1 Simplify traverse_another_path via last_component
* src/safe.c: Include basename-lgpl.h.
(traverse_another_path): Simplify by using last_component.
2024-09-15 22:50:38 -07:00
Paul Eggert
77f21a6068 Stop using Gnulib dirname module
We don’t need its dir_name and base_name functions, since we
merely copy the strings elsewhere.  Instead, use some of the
modules that dirname uses.
* bootstrap.conf (gnulib_modules): Add basename-lgpl, filename.
Remove dirname.
* src/pch.c, src/util.c:
Include basename-lgpl.h and filename.h instead of dirname.h.
* src/pch.c (best_name):
* src/util.c (version_controller):
Use last_component instead of the dirname module’s functions.
* src/safe.c: Include filename.h instead of dirname.h.
* src/util.c (make_tempfile): No need to break the file name
into directory and basename; just use the whole file name.
2024-09-15 22:50:38 -07:00
Paul Eggert
fe8ffd9ba4 Fix implausible overflow when reading symlinks
* src/safe.c (read_symlink): Check for integer overflow
in bufferi size calculation.
2024-09-15 22:50:38 -07:00
Paul Eggert
5e84bda3ff Access checks should use effective, not real
* src/safe.c (safe_access): Use effective user and group ID,
not real user and group ID.
2024-09-15 22:50:38 -07:00
Paul Eggert
8d4ca493ed Don’t assume AT_FDCWD != -1
* src/safe.h (DIRFD_INVALID): New constant.
* src/safe.c (traverse_another_path, traverse_path, safe_xstat)
(safe_open, safe_rename, safe_mkdir, safe_rmdir, safe_unlink)
(safe_symlink, safe_chmod, safe_lchown, safe_lutimens)
(safe_readlink, safe_access):
Use it to port to perverse platforms where AT_FDCWD == -1.
2024-09-15 22:50:38 -07:00
Paul Eggert
fe5d4a06f7 Copy input to output attributes via fd if possible
* src/inp.c (scan_input): New arg ifp.  It is now the
caller’s responsibility to open and close the input.
Caller changed.
* src/patch.c (main): Open the input file for scan_input, and use
its file descriptor, if available, to avoid some races while
setting output file attributes.
2024-09-15 22:50:38 -07:00
Paul Eggert
ca4c431f8a Simplify timestamp epoch checking
* src/util.c (fetchname): Simplify timestamp calculations.
Also, add a comment saying why they are buggy in obscure
cases that have little practical implication.
2024-09-15 22:50:38 -07:00
Paul Eggert
0ad4347277 Check for ftello failures
Also, use Gnulib modules fseeko, ftello, and rely on off_t,
fseeko, ftello as they are safe to use nowadays.
* bootstrap.conf (gnulib_modules): Add fseeko, ftello.
This is needed only for very old platforms, plus MSVC.
* src/common.h (file_offset, file_seek, file_tell): Remove.
All uses replaced with off_t, fseek, ftell.
* src/util.c (Fseeko): Rename from Fseek.  All uses changed.
(Ftello): New function.  All ftello callers that rely on
nonnegative results changed to Ftello.
2024-09-15 22:50:38 -07:00
Paul Eggert
c49a16d0ca Remove format_linenum
* src/util.c (format_linenum):
* src/util.h (LINENUM_LENGTH_BOUND):
Remove.  They are no longer needed now that line numbers can be
printed with %td.  All uses replaced by printf with %td.
2024-09-15 22:50:38 -07:00
Paul Eggert
22efdeebaa Fix "with multiple words" line number
* src/pch.c (intuit_diff_type): Fix typo in diagnostic,
which output a file offset as if it were a line number.
2024-09-15 22:50:38 -07:00
Paul Eggert
5a70a1b6b9 Drop Plan B
‘patch’ dates back to when porting to 16-bit machines still
mattered, and where it was therefore useful to support files that
did not fit in RAM.  So ‘patch’ had two operating modes, Plan A
and Plan B.  In Plan A the input was simply read into memory, but
if memory was exhausted ‘patch’ fell back onto Plan B where input
was transformed into a temporary file that holds the input lines.
The idea was to not use any malloc calls during the main part of
the ‘patch’ run, so that ‘patch’ could not exhaust memory if Plan
A succeeded.  Over the years, though, that approach has not always
been observed, as malloc is called for sundry reasons and ‘patch’
immediately fails when malloc fails other than during the Plan A
initial phase.  In practice this misbehavior has not been a
problem, as ‘patch’ now invariably runs on machines where source
file contents fit into RAM so Plan B is never used.  The GNU
Coding Standards says not to worry about supporting machines too
small to read file contents, and now’s a good time to remove the
Plan B code, as it is making further maintenance a pain.
* bootstrap.conf (gnulib_modules): Remove ialloc.
All uses of ialloc.h and its API removed, and replaced by
xalloc.h API as needed.
* src/common.h (lin, LINENUM_MIN, LINENUM_MAX): Remove.
All uses of ‘lin’ replaced by idx_t if known to be nonnegative,
ptrdiff_t otherwise.  All uses of LINENUM_MAX replaced by IDX_MAX.
LINENUM_MIN was not used.
* src/inp.c (tibufsize, TIBUFSIZE_MINIMUM, tifd, tibuf, tiline)
(lines_per_buf, tireclen, last_line_size, too_many_lines)
(lines_too_long, plan_a, plan_b): Remove.  All uses removed.
(scan_input): Do just what plan_a used to do, except report a fatal
error on memory exhaustion.
Do not worry about file types other than regular file or symlink
as they are not possible.  All uses changed.
(ifetch): Omit WHICHBUF arg, which is no longer needed now that
we always use Plan A.  All uses changed.
* src/patch.c (tmpin): Remove.  All uses removed.
* src/pch.c (grow_hunkmax, pget_line): Use xpalloc rather than
doing the equivalent by hand.
(grow_hunkmax): Always succeed.  All uses changed.
(another_hunk): Return bool not signed char, since -1 is
no longer possible.  All uses changed.
Use ximemdup0 instead of savestr when that is more convenient.
(get_line, pget_line): Return idx_t, not ptrdiff_t, since -1
is no longer possible.  All uses changed.
* src/util.c (savebuf): Always succeed.  All callers changed.
Simplify.
(Write): Now static.
2024-09-15 22:50:38 -07:00
Paul Eggert
c0d465f21b Prefer ximemdup0 to xmemdup0
This is natural, as the args are all nonnegative ptrdiff_t, not size_t.
It also removes the need for Gnulib’s xmemdup0 module.
* bootstrap.conf (gnulib_modules): Remove xmemdup0.
* src/pch.c, src/util.c: Do not include xmemdup0.h.
All calls to xmemdup0 replaced by ximemdup0.
2024-09-15 22:50:38 -07:00
Paul Eggert
b91aab2c88 Refactor ifetch API
* src/inp.c (ifetch): Return struct iline, instead of
returning a pointer and storing through a pointer.
All callers changed.
* src/inp.h (struct iline): New type.
2024-09-15 22:50:38 -07:00
Paul Eggert
55c8a5c494 Do not attempt huge I/Os
Also, simplify I/O error checking by moving some of it into
new functions Read and Write.
* bootstrap.conf (gnulib_modules): Remove full-write.
* src/inp.c (plan_a, plan_b, ifetch):
* src/util.c (move_file, copy_to_fd):
Use Read or Write instead of checking for I/O errors by hand.
* src/util.c: Do not include full-write.h.
(IO_MAX): New constant.
(ifetch): Diagnose temp file shrinkage.
(ask): Do not attempt a read of more than IO_MAX bytes.
Use xpalloc to reallocate, instead of doing it by hand.
(Read, Write): New functions.
2024-09-15 22:50:38 -07:00
Paul Eggert
e0e121efe5 Use outfd when setting file attributes
* src/patch.c (main): Check for output error when closing outfd.
Use outfd when setting file attributes, to fix some races.
This means delaying closing until after setting file attributes.
(spew_output): Don’t close output stream; that is now the
caller’s responsibility.  Caller changed.
2024-09-15 22:50:38 -07:00
Paul Eggert
241e57ea5b Don’t assume Linux-like S_IFREG
* boostrap.conf (gnulib_modules): Add assert-h.
* src/pch.c (fetchmode): Convert Git type to local file type.
2024-09-15 22:50:38 -07:00
Paul Eggert
0f8c62835f Use STDOUT_FILENO etc
* src/patch.c (open_outfile):
* src/pch.c (do_ed_script): Prefer macros like STDOUT_FILENO to
expressions like 1 or fileno (stdout).
2024-09-15 22:50:38 -07:00
Paul Eggert
f2c3676393 Fix unlikely glitch with ed diffs
* src/patch.c (main): If tmpout can’t be created, don’t
continue when diff_type == ED_DIFF.
2024-09-15 22:50:38 -07:00
Paul Eggert
cec6407930 Use fds to copy attrs in create_backup_copy
* src/util.c (create_backup_copy): Let the new copy_file set file
attributes, as this can be done more straightforwardly via
file descriptors.
(copy_to_fd): Return the source file descriptor instead of closing
it, so that the caller can use it before closing.  All callers changed.
(copy_file): New arg ATTR.  Set the destination’s attributes
accordingly.  All callers changed.
2024-09-15 22:50:38 -07:00
Paul Eggert
6b7b01b89b Be more careful about (time_t) -1
* src/patch.c (main):
* src/pch.c (intuit_diff_type):
* src/util.c (fetchname):
Mark an invalid timespec with both .tv_sec = (time_t) -1
and with .tv_nsec = -1.  This is more reliable in case
time_t is unsigned and narrower than int, in which case
(time_t) -1 != -1.  It’s also more reliable in the unusual case
where (time_t) -1 is a valid timestamp.  All uses changed.
2024-09-15 22:50:38 -07:00
Paul Eggert
043355371a X == -1 → X < 0
* src/inp.c (get_input_file, plan_b, ifetch):
* src/patch.c (main, get_some_switches, open_outfile)
(init_reject, output_file_now):
* src/pch.c (open_patch_file, there_is_another_patch)
(another_hunk, do_ed_script):
* src/safe.c (read_symlink, traverse_another_path):
* src/util.c (move_file):
Prefer X < 0 to X == -1 when either comparison will do.
This lets us focus better on oddball cases like uid_t and time_t
when converted from -1.
2024-09-15 22:50:38 -07:00
Paul Eggert
0a66dee829 Let set_file_attributes use fds not names
Although this ability is currently not used, so this commit is
merely refactoring, the patch should help ‘patch’ avoid some race
conditions in followup commits.
* bootstrap.conf (gnulib_modules): Add futimens.
* src/util.c: Include utimens.h.
(lacks_appropriate_privileges, copy_fdattr_error): New functions.
(copy_attr): New args src_fd, dst_fd.  All uses changed.
(set_file_attributes): New args tofd, fromfd.  All uses changed.
2024-09-15 22:50:38 -07:00
Paul Eggert
36ff2c9296 Port to narrow unsigned uid_t
* src/util.c (set_file_attributes): Work even if uid_t or gid_t
is unsigned and narrower than int, so that (uid_t) -1 != -1.
2024-09-15 22:50:38 -07:00
Paul Eggert
3951496739 Check for output errors more systematically
* bootstrap.conf (gnulib_modules): Add closeout.
* src/merge.c (print_linerange, merge_result, merge_hunk):
* src/patch.c (main, usage, get_some_switches)
(print_unidiff_range, abort_hunk_unified, abort_hunk_context)
(apply_hunk, copy_till, spew_output):
* src/pch.c (open_patch_file, there_is_another_patch)
(another_hunk, pch_write_line, do_ed_script):
* src/safe.c (traverse_another_path):
* src/util.c (putline, vsay, ask, systemic):
* src/version.c (version):
Check for output errors more systematically.
* src/patch.c: Include closeout.h.
(main): Invoke close_stdout at exit.
* src/util.c (fatal): Use fputc, not putc; no need for speed here.
(pfatal): Use fprintf, not putline, to avoid recursion loop
on write error.
(Fclose, Fflush, Fprintf, Fputc, Fputs, Fwrite):
New functions, to go with Fseek.
* src/version.c: Include util.h, for Fprintf.
2024-09-15 22:50:38 -07:00
Paul Eggert
6429630c0c Report input error right away
* src/inp.c (plan_b):
* src/pch.c (incomplete_line):
When getc fails due to an input error, report the error right away.

Simplify EOF testing
2024-09-15 22:50:38 -07:00
Paul Eggert
03cb187db6 Simplify EOF testing
* src/inp.c (plan_b):
* src/patch.c (apply_hunk, copy_till):
* src/pch.c (skip_to, pget_line, incomplete_line, do_ed_script):
Just check whether a value is negative, rather than exactly EOF.
This simplifies the code a bit, and speeds it up very slightly.
2024-09-15 22:50:38 -07:00
Paul Eggert
cc7cde7a2c Prefer other types to ‘int’
* src/merge.c (merge_result, merge_hunk):
Hunk number is intmax_t, not int, fixing an unlikely overflow.
* src/patch.c (invc): Now signed char, not int.
(numeric_string): Use bool, not int, for sign.
* src/pch.c (p_says_nonexistent, sha1_says_nonexistent)
(pch_says_nonexistent): Now char, not int.  All uses changed.
(p_rfc834_nesting, pget_line): Use idx_t, not int,
fixing an unlikely overflow.  All uses changed.
(another_hunk): Now signed char, not int.  All uses changed.
* src/util.c (format_linenum): Remove unnecessary casts to int.
2024-09-15 22:50:38 -07:00
Paul Eggert
4c6650b507 Detect unlikely integer overflow in size calcs
* src/pch.c (set_hunkmax, grow_hunkmax): Check for unlikely
integer overflow in size calculations, by using ireallocarray
rather than realloc and similarly for xireallocarray vs xmalloc.
(grow_hunkmax): Rely on C89 guarantee that when realloc fails, the
old storage is still available.  We need not worry any more about
ancient hosts where that was not true.  Grow by a factor of
1.5, as per xpalloc, instead of by a factor of 2.
2024-09-15 22:50:38 -07:00
Paul Eggert
abe92e8010 Prefer idx_t, ptrdiff_t to lin
Prefer idx_t and ptrdiff_t to lin when counting lines in main
memory.  This is mostly for clarity, though it should help
efficiency slightly in obsolescent 32-bit platforms.
* src/inp.c (ifetch):
* src/merge.c (locate_merge, merge_hunk, count_context_lines)
(context_matches_file, compute_changes):
* src/patch.c (main, locate_hunk, mangled_patch)
(print_unidiff_range, abort_hunk_unified, abort_hunk_context)
(apply_hunk, patch_match):
* src/pch.c (p_ptrn_lines, p_repl_lines, p_end, p_max)
(p_prefix_content, p_suffix_content, hunkmax, p_efake, p_bfake)
(another_hunk, pch_swap, pch_ptrn_lines, pch_repl_lines)
(pch_end, pch_prefix_context, pch_suffix_context, pch_line_len)
(pch_char, pfetch, pch_write_line, pch_normalize):
Prefer idx_t to lin for object sizes.
Prefer ptrdiff_t to lin for pointer differences.
All uses changed.
* src/merge.c (compute_changes): Check for integer overflow
when combining file with memory counts, when the result
must fit into memory.
2024-09-15 22:50:38 -07:00
Paul Eggert
30449e294b Fix compatibility issue with blanks in patches
* src/util.c (remove_prefix): Remove; no longer used.
* src/pch.c (intuit_diff_type, scan_linenum, another_hunk):
Allow a nonempty sequence of blanks in places where POSIX requires
support for these sequences.
(another_hunk): Parse the "0,0" instead of comparing it literally,
since there can be blanks around the comma.
* tests/Makefile.am (TESTS): Add unusual-blanks.
* tests/unusual-blanks: New file.
2024-09-15 22:50:38 -07:00
Paul Eggert
9228a8cf28 pch_swap return type cleanup
* src/pch.c (pch_swap): Return void not bool, since it always
returns true if it returns at all.  All uses changed.
2024-09-15 22:50:38 -07:00
Paul Eggert
c1c438d3a8 Fix unlikely int overflow in hunk counts
* src/patch.c (main): Don’t assume hunk counts fit in int.
2024-09-15 22:50:38 -07:00
Paul Eggert
5d3f41f6fc Use char for char in plan_a
* src/inp.c (plan_a): Use char, not int, for local.
2024-09-15 22:50:38 -07:00
Paul Eggert
fb056f23ac Cache cwd_is_root dev, ino
* src/util.c (cwd_is_root): Cache stat results.
2024-09-15 22:50:38 -07:00
Paul Eggert
84b5f34f2a Avoid ‘unsigned’ in safe.c
* src/safe.c (MAX_SAFE_COMPONENTS): Now an enum instead of unsigned.
(dirfd_cache_misses): Now intmax_t instead of unsigned.
All uses changed.
(count_path_components): Return idx_t, not unsigned, to
avoid issues with file names with more than INT_MAX components.
All uses changed.
2024-09-15 22:50:38 -07:00
Paul Eggert
d2e113e9a4 Simplify get_sha1
* src/pch.c (get_sha1): Return the new string instead of storing
through a pointer.  All uses changed.  Use xmemdup0 instead of
duplicating its logic.
2024-09-15 22:50:38 -07:00
Paul Eggert
f73718b634 Avoid casts in patch.c
* src/patch.c (main, get_some_switches, abort_hunk_context):
Redo to avoid casts.
(main): Use %#o rather than 0%o for octal output, to avoid
unnecessary leading 0.
2024-09-15 22:50:38 -07:00
Paul Eggert
79eef3e014 Prefer idx_t in util.c
* src/util.c (file_id_hasher): Avoid unlikely signed integer
overflow when adding e->ino and e->dev.
(create_backup, copy_to_fd, quote_system_arg, version_controller)
(savebuf, remove_prefix, removedirs): Prefer idx_t to size_t.
2024-09-15 22:50:38 -07:00
Paul Eggert
11588d0fa2 Prefer idx_t in pch.c
* src/pch.c (p_len, p_indent, open_patch_file, intuit_diff_type)
(prefix_components, best_name, another_hunk, get_line, pget_line)
(pch_swap, pch_line_len):
Prefer idx_t or ptrdiff_t to size_t.  All uses changed.
(intuit_diff_type): Rename local distance_from_minimum to
above_minimum, and make it 1 or 0 which is all that is needed.
This avoids integer overflow when the distance exceeds INT_MAX.
2024-09-15 22:50:38 -07:00
Paul Eggert
e16037d11f Prefer idx_t in patch.c
* src/patch.c (patchbufsize, main, similar):
Prefer idx_t to size_t.  All uses changed.
* src/pch.c (pget_line):
* src/util.c: Include ialloc.h.
(ask): Avoid bad behavior on unlikely size overflow.
When converting from size_t to idx_t,
prefer reallocation growth by 50% not 100%, to match xpalloc.
2024-09-15 22:50:38 -07:00
Paul Eggert
388926ffa7 Prefer idx_t in list.h
* src/list.h (list_entry): Prefer idx_t to size_t.
2024-09-15 22:50:38 -07:00
Paul Eggert
3582fdb953 Prefer idx_t in inp.c
Prefer signed to unsigned types for object sizes,
as they have better checking (e.g., gcc -fsanitize=undefined).
* bootstrap.conf (gnulib_modules): Add idx (already being used
indirectly) and stdc_bit_ceil.
* src/common.h: Include idx.h.
* src/inp.c: Include stdbit.h.
(tibufsize, tireclen, last_line_size, plan_a, ifetch):
Prefer idx_t to size_t for object sizes.  All uses changed.
(lines_per_buf): Prefer idx_t to lin when it's talking about
object sizes.
(plan_b): Check for idx_t overflow too.  Use stdc_bit_ceil
instead of doing it by hand.
2024-09-15 22:50:38 -07:00
Paul Eggert
4a47c002fa build: update gnulib submodule to latest 2024-09-15 22:50:38 -07:00
Paul Eggert
4f8c4b8842 Don’t limit strip counts etc. to INT_MAX
* src/patch.c (debug): Now unsigned short int, not int, since
it is used as a mask and only the bottom 9 bits matter.
(patch_get, strippath, maxfuzz): Now intmax_t, not int.
(numeric_string): Return intmax_t, not int.
On overflow return an extremum rather than reporting an error,
since the resulting values are effectively infinity anyway.
All uses changed.
* src/util.c (success, fetchname, parse_name): Accept intmax_t, not int.
2024-09-15 22:50:38 -07:00
Paul Eggert
0a810b6e27 Fix unlikely integer overflows in patch.c
* src/patch.c (numeric_string): Use stdckdint instead of
by-hand overflow checks that might not work.
2024-09-15 22:50:38 -07:00
Paul Eggert
bac3b6d06a Fix unlikely integer overflows in pch.c
* src/pch.c (scan_linenum): Use stdckdint instead of by-hand
overflow checks that might not work.  Fix already-existing
use of ckd_add that wasn’t done quite correctly.
2024-09-15 22:50:38 -07:00
Paul Eggert
8fb784b7d0 Fix unlikely integer overflows in inp.c
* bootstrap.conf (gnulib_modules): Add ialloc.
* src/inp.c: Include ialloc.h.
(plan_a, plan_b): Prefer idx_t and ssize_t to size_t.
Use stdckdint instead of by-hand overflow checks that might not work.
2024-09-15 22:50:38 -07:00
Paul Eggert
e10f3ca472 Promote minmax.h to common.h
* src/common.h: Include minmax.h here ...
* src/merge.c, src/patch.c, src/safe.c: ... instead of here.
2024-09-15 22:50:38 -07:00
Paul Eggert
57e2165767 Avoid some memory allocation by not using ‘const’
* src/common.h (struct outfile.exists):
* src/safe.c (struct symlink.path):
Member is no longer pointer to const.  All uses changed.
* src/inp.c (get_input_file, plan_a, plan_b):
* src/patch.c (delete_file_later):
* src/pch.c (do_ed_script):
* src/safe.c (safe_xstat, safe_stat, safe_lstat, safe_open)
(safe_rename, safe_mkdir, safe_rmdir, safe_unlink, safe_symlink)
(safe_chmod, safe_lchown, safe_lutimens, safe_readlink)
(safe_access):
* src/util.c (volatilize set_file_attributes, create_backup_copy)
(create_backup, copy_to_fd, copy_file, append_to_file, trystat)
(version_get, stat_file):
Arg no longer points to const.  All callers changed.
* src/safe.c (openat_cached):
Make a copy of arg, to simplify API.
Arg now points to const, since it no longer needs write access.
(traverse_next, traverse_another_path, traverse_path):
Arg points to pointer that is no longer pointer to const.
Temporarily change argument string instead of duplicating it and
changing the copy.  All uses changed.
* src/util.c (volatilize): Result is no longer pointer to const.
2024-09-15 22:50:38 -07:00
Paul Eggert
eb18b39cc7 Remove unnecessary char * casts in inp.c
* src/inp.c (plan_a): Remove unnecessary casts to char *.
2024-09-15 22:50:38 -07:00
Paul Eggert
d60cb724bd Pacify gcc -Wunused-parameter when !USE_XATTR
* src/common.h (struct outfile.exists):
Now char const volatile *volatile, not bool volatile,
so that we follow the C standard more strictly.
All uses changed.
* src/util.c (UTIL_INLINE): New macro.
(volatilize): New function.
* src/util.h: Use _GL_INLINE_HEADER_BEGIN, UTIL_INLINE.
(devolatilize): New function.
2024-09-15 22:50:38 -07:00
Paul Eggert
05ef886471 Pacify gcc -Wunused-parameter when !USE_XATTR
* src/util.c (copy_attr) [!USE_XATTR]: Mark args with MAYBE_UNUSED.
2024-09-15 22:50:38 -07:00
Paul Eggert
1f8d192897 maint: stop using alloca
It means unbounded allocation on the stack, which is trouble
on some platforms.  Also, gcc-12 on Pop!_OS 22.04 LTS complains.
* bootstrap.conf (gnulib_modules): Remove alloca.
* src/pch.c, src/safe.c: Don’t include alloca.h.
* src/pch.c (do_ed_script):
Use designated initializer instead of fixed-size alloca.
* src/safe.c (new_cached_dirfd, openat_cached):
Name arg is now allocated on heap by caller, and is now char * not
char const *.  All callers changed.
(openat_cached, traverse_next): Redo to avoid gotos.
(traverse_next, traverse_another_path):
Use ximemdup0 instead of alloca, to allocate on the heap rather than
unboundedly on the stack.
2024-09-15 22:50:38 -07:00
Paul Eggert
ba92722539 Don’t assume O_RDONLY == 0
* src/safe.c (O_PATHSEARCH): New constant.
(openat_cached): Use it, so as to not assume that
O_RDONLY == 0, and so that the open works even if
the directory is unreadable (except on ancient hosts).
2024-09-15 22:50:38 -07:00
Paul Eggert
0f98e033f2 Avoid syscall when nested signal block
* src/util.c (block_signals): Don’t call sigprocmask
when nesting, as the signals are already blocked.
2024-09-15 22:50:38 -07:00
Paul Eggert
1235ccc55b Add signal comment
* src/patch.c (main): Comment why signals are blocked here,
and add a FIXME.
2024-09-15 22:50:38 -07:00
Paul Eggert
43ee674679 build: update gnulib submodule to latest 2024-08-28 23:13:44 -07:00
Paul Eggert
e3819470dc Update NEWS, README-prereq 2024-08-28 23:13:44 -07:00
Paul Eggert
2c2a83b77a Omit _Noreturn when easy
* src/inp.c (too_many_lines, lines_too_long):
* src/patch.c (mangled_patch):
* src/pch.c (malformed):
Omit _Noreturn for functions where it is automatically deduced in
a default build by gcc (GCC) 14.2.1 20240801 (Red Hat 14.2.1-1) x86-64.
2024-08-28 23:13:44 -07:00
Paul Eggert
a13c2eaf16 Replace __attribute__ with attribute.h
* src/common.h (__attribute__): Remove.
All uses replaced by _Noreturn or ATTRIBUTE_FORMAT.
2024-08-28 23:13:44 -07:00
Paul Eggert
6eb2d138e6 Switch from ctype.h to c-ctype.h
* bootstrap.conf (gnulib_modules): Add c-ctype.
It’s alreay being used indirectly.
* src/common.h: Include c-ctype.h instead of ctype.h.
All uses of isspace replaced by c_isspace; this is equivalent
since we do not call setlocale.  All uses of c==' ' || c=='\t'
replaced by c_isblank.
Sort includes, system includes last.
(ISDIGIT): Remove.  All uses replaced by c_isdigit.
2024-08-28 23:13:44 -07:00
Paul Eggert
d1a6847368 Simplify warning configuration
* configure.ac (WARN_CFLAGS): Simplify configuration
by not bothering to suppress warnings that don’t
need to be suppressed with GCC 14.2.1.
2024-08-28 23:13:44 -07:00
Paul Eggert
d6631b3125 Prefer ATTRIBUTE_* to _GL_ATTRIBUTE_*
When attribute.h defines a shorthand macro, use it
to avoid _GL_ prefixes.  This affects _GL_ATTRIBUTE_FORMAT
and _GL_ATTRIBUTE_PURE.
2024-08-28 23:13:44 -07:00
Paul Eggert
bc6899dc6f Pacify gcc -Wno-unused-parameter
* bootstrap.conf (gnulib_modules): Add ‘attribute’.
It’s already being used indirectly.
* configure.ac (WARN_CFLAGS): Omit -Wno-unused-parameter.
* src/common.h: Include attribute.h.
(FALLTHROUGH): Remove, as attribute.h does this now.
* src/util.c (copy_attr_error, copy_attr_quote, copy_attr_free):
Use MAYBE_UNUSED.
2024-08-28 23:13:44 -07:00
Paul Eggert
da259855ca Improve ‘git diff’ output if desired
* .gitattributes: New file.
2024-08-28 23:13:44 -07:00
Paul Eggert
248ef134f8 Prefer strerror to perror
* src/patch.c (putline): Move from here ...
* src/util.c: ... to here, and make it extern.
* src/pch.c (there_is_another_patch):
* src/util.c (pfatal):
Use putline with strerror rather than attempting to work
around old perror bugs.  This also works better in the
unlikely case where the program name length does not fit
in int.
2024-08-28 23:13:44 -07:00
Paul Eggert
6cb321aff4 A bit more long-string fixing
* src/util.c (fatal, pfatal): Avoid unlikely int overflow
with very long program names.
2024-08-28 23:13:44 -07:00
Paul Eggert
47bc09dc6a Prefer nullptr to NULL
C23-style nullptr has some minor static-checking advantages over
C89-style NULL, and we’re already using Gnulib’s nullptr module.
* src/inp.c, src/patch.c, src/pch.c, src/safe.c, src/util.c:
Prefer nullptr to NULL.
2024-08-28 23:13:44 -07:00
Paul Eggert
7608746040 More fixing of printing of very long strings
* bootstrap.conf (gnulib_modules): Add nullptr.
* src/patch.c (if_defined, not_defined):
Now merely strings, not printf formats.  All uses changed.
(putline): New static function.
(print_header_line, abort_hunk_unified, abort_hunk_context)
(apply_hunk): Use it to remove assumptions that string
lengths fit in int.  Also, prefer fputs to printf with plain %s.
(print_header_line): TAG arg now is assumed to have an appended ' ',
to save us the trouble of outputting ' ' separately.  All uses changed.
2024-08-28 23:13:44 -07:00
Paul Eggert
323da0da04 Don’t assume string sizes fit in int when printing
* src/common.h: Move stdckdint.h include here ...
* src/util.c: ... from here.
* src/pch.c (scan_linenum):
* src/safe.c (traverse_another_path):
Don’t assume pointer differences fit in int when calling printf.
2024-08-28 23:13:44 -07:00
Paul Eggert
53d10143f2 Avoid fprintf INT_MAX overflow when merging
* src/merge.c (merge_hunk): Don’t assume patch line lengths
fit in int.
2024-08-28 23:13:44 -07:00
Paul Eggert
59681c8e9a Avoid sprintf INT_MAX overflow
The bug is extremely unlikely, but it’s easy to fix.
* bootstrap.conf (gnulib_modules): Add stpcpy.
* src/util.c (SCCSPREFIX): Now a macro, so that it can be concatenated.
(try1, try2): Remove these macros.  Replace all uses with ...
(trystat): ... this new function.
* src/util.c (version_controller, make_tempfile):
Avoid issues with sprintf result exceeding INT_MAX.
2024-08-28 23:13:44 -07:00
Paul Eggert
4278b91942 Reject output file names containing '\n'
This is encouraged by POSIX.1-2004.
* bootstrap.conf (gnulib_modules): Add mempcpy.
* src/patch.c (main, get_some_switches):
* src/util.c (parse_c_string):
Reject output file names containing newlines.
(backup_file_name_option): New function, to help with that.
(make_tempfile): Reject TMPDIR values containing newlines.
Do not silently screw up if TMPDIR length exceeds INT_MAX.
* tests/bad-filenames: Test for file names containing '\n'.
2024-08-28 23:13:44 -07:00
Paul Eggert
34b45bc78b Update man page a bit.
* patch.man: Fix minor formatting glitches.
Remove obsolete references to news articles, as Larry Wall’s
once-famous ‘rn’ program is no longer used.
Mention Git.  Update Autoconf mention.
Remove discussion of differences between current patch
and patch 2.1 and earlier, as 2.1 is so old I can’t
easily find out its release date.
2024-08-28 23:13:44 -07:00
Paul Eggert
d18c05d5bd Update copyright notices
Switch to single intervals for FSF notices,
and consistently put them first.
Update copyright notices for 2024.
* cfg.mk (update-copyright-env): Use UPDATE_COPYRIGHT_FORCE=1,
UPDATE_COPYRIGHT_USE_INTERVALS=2.
* patch.man: Always use \(co, so that update-copyright
updates these dates.
* src/version.c: Correct Larry Wall copyright years.
2024-08-28 23:13:44 -07:00
Paul Eggert
af828e563c Fix some races involving signals
* src/common.h (struct outfile.exists): Now volatile.
2024-08-28 23:13:44 -07:00
Paul Eggert
b3a6c95537 Don’t attempt to remove files we didn’t create
Fix part of a race condition when a signal arrives, which can
cause ‘patch’ to remove the wrong file.
* src/common.h (struct outfile.temporary): New member.
* src/patch.c (tmped, tmpin, tmppat, tmpout): Initialize it.
(outrej): New static var, replacing the old rejname, but now
of type struct outfile rather than char *.  All uses changed.
* src/patch.c (create_output_file):
* src/util.c (create_file, copy_file):
Accept struct outfile * instead of char *.  All uses changed.
* src/patch.c (open_outfile, output_file_now, output_file):
* src/util.c (create_backup_copy, move_file):
Accept char * instead of char const *, so that the pointer can be
copied into a struct outfile.  All uses changed.
* src/util.c (create_file, copy_file, try_safe_open):
Block signals for temporary files, so that signal handlers always
see an ‘exists’ member consistent with whether we created the file.
(unblock_signals): Preserve errno so that caller need not do that.
(struct try_safe_open_args.out): New member.  All uses changed.
(try_safe_open, make_tempfile): Rely on try_tempname to set
out->exists, rather than having make_tempfile do it (which
would mean a much longer critical section).
2024-08-28 23:13:44 -07:00
Paul Eggert
9abc949979 Omit goto in try_safe_open
* src/util.c (try_safe_open): Rewrite to avoid goto.
2024-08-28 23:13:44 -07:00
Paul Eggert
90e62d5195 Pacify clang re obsolete O_CREAT test
* src/util.c (create_file): Remove obsolete test O_CREAT && O_TRUNC.
clang compares about it, and we don’t need to worry about ancient
platforms lacking O_CREAT or O_TRUNC.
2024-08-28 23:13:44 -07:00
Paul Eggert
2b87c1eb7f Allow nested block/unblock of signals
* src/util.c (initial_signal_mask): Now auto, not static.
(init_signals): Do not block a signal that is already blocked.
(signal_blocking_level): New static var.
(block_signals, unblock_signals): Support nested block/unblock calls.
2024-08-28 23:13:44 -07:00
Paul Eggert
7aa1c3bc32 Adjust to new Gnulib bootstrap post imports
* bootstrap.conf (bootstrap_post_import_hook): New function,
replacing the old inline code.  Remove unnecessary gettext comments.
2024-08-28 23:13:44 -07:00
Paul Eggert
99c0c0b7b8 maint: remove generated file lib/Makefile.am 2024-08-28 23:13:44 -07:00
Paul Eggert
1c087d6fcb Rely on Gnulib inttypes module
* bootstrap.conf (gnulib_modules): Add inttypes.  Remove size_max.
* configure.ac: Do not call gl_SIZE_MAX.
* src/common.h: Include inttypes.h unconditionally.
2024-08-28 23:13:44 -07:00
Paul Eggert
7214f8d44b Update main locals more consistently
* src/patch.c (main): Be a bit more disciplined about updating
local vars.  No need to set outfd = -1 if outstate.ofp is
non-null, since it must be -1 in that case.
2024-08-28 23:13:44 -07:00
Paul Eggert
6785b2c42c Use struct outfile * in function args
* src/patch.c (output_file_later, output_file_now, output_file)
(remove_if_needed):
* src/pch.c (do_ed_script):
* src/util.c (move_file, make_tempfile):
Accept struct outfile * instead of one or two args.
All callers changed.
* src/pch.c (pch_name): Return char *, not char const *.
2024-08-28 23:13:44 -07:00
Paul Eggert
72d7ed09bc Refactor temp names into struct
* src/common.h (struct outfile): New type.
* src/patch.c (tmped, tmpin, tmpout, tmppat, tmppat): New vars,
using this type.  They replace TMPEDNAME, TMPEDNAME_needs_removal,
TMPINNAME, TMPINNAME_needs_removal, TMPOUTNAME,
TMPOUTNAME_needs_removal, TMPPATNAME, TMPPATNAME_needs_removal,
TMPREJNAME, TMPREJNAME_needs_removal.  All uses changed.
(tmprej): Now static.
* util.c (make_tempfile): First arg is now char **,
not char const **.  All callers changed.
2024-08-28 23:13:44 -07:00
Paul Eggert
abf6fb176b Simplify by using Gnulib sigaction
* bootstrap.conf (gnulib_modules): Add raise, sigaction, signal-h,
sigprocmask.  Remove signal.
* configure.ac: Do not check for raise, sigaction, sigprocmask,
sigsetmask.
* src/patch.c (main): Do not block signals during dry runs; there's
no need.
* src/util.c (SIGCHLD, raise, sigset_t, sigemptyset, sigmask)
(sigaddset, sigismember, sigprocmask, sigblock, sigsetmask):
Remove substitutes; not needed now that we have Gnulib.
(sigs): Don’t worry about whether SIGHUP and SIGPIPE are present.
(NUM_SIGS): Now a constant, not a macro.
(signals_to_block): Remove.  All uses changed to fatal_act.sa_mask.
(fatal_exit_handler) [!HAVE_SIGACTION]: Remove.  All uses removed.
(init_signals): Rename from set_signals.  All uses changed.
Only do the true part; the false part is now done by unblock_signals.
(block_signals): Rename from ignore_signals, since it now blocks
instead of ignoring on all platforms.  All uses changed.
(init_signals, block_signals): Simplify by assuming
HAVE_SIGACTION, HAVE_SIGPROCMASK, HAVE_SIGSETMASK.
(setup_handler): Remove macro.
(unblock_signals): New function.
2024-08-25 18:14:30 -07:00
Paul Eggert
d3816ac315 Avoid unnecessary freeing in output_files
If we’re about to exit, calling ‘free’ just slows us down
and is more likely to trigger a bug somewhere.
* src/patch.c (FREE_BEFORE_EXIT): Now a boolean, not
defined-or-not-defined.  All uses changed.
(output_files): New arg EXITING.  All uses changed.
2024-08-25 18:14:30 -07:00
Paul Eggert
346d3aca37 Clean up cleanup
* src/patch.c (cleanup): Do not call output_files,
since this function can be called from a signal handler
and output_files is not async-signal-safe.  All callers changed.
No need for the ‘already_cleaning_up’ static.
2024-08-25 18:14:30 -07:00
Paul Eggert
ff2317b2cb Port better to GNU/Hurd
It lacks PATH_MAX, so don’t use PATH_MAX.
* bootstrap.conf (gnulib_modules): Add stdckdint.
* src/util.c: Include stdckdint.h.  Omit duplicate stdarg.h include.
(move_file, copy_file): Don’t limit symlink contents to PATH_MAX.
Check for symlink or file contents that unexpectedly grew.
(copy_file): New arg FROMST.  All callers changed.
2024-08-25 18:14:30 -07:00
Paul Eggert
c2d9792e92 Don’t say empty backups are unreadable
* patch.man: Remove text that became obsolete in 2011.
2024-08-25 18:14:30 -07:00
Paul Eggert
8c27a03b2e Spelling fixes 2024-08-25 18:14:30 -07:00
Paul Eggert
d46d729c0c Change manywarnings usage to be more like coreutils
* configure.ac: Treat --enable-gcc-warnings more like coreutils does.
This mostly just migrates coreutils changes into this file.
2024-08-25 18:14:30 -07:00
Paul Eggert
924698bfd6 Pacify clang, which dislikes n + "y"
* src/merge.c (merge_hunk):
* src/patch.c (main): Do not add an integer to a string literal,
as Clang unhelpfully warns that you’re using C, not C++.
2024-08-25 18:14:30 -07:00
Paul Eggert
8939519c06 Pacify -Wstrict-overflow in pch.c
* src/pch.c (fetchmode): Avoid undefined behavior in the unlikely
event that str happens to be next to the end of memory.
2024-08-25 18:14:30 -07:00
Paul Eggert
531cc2b9c6 Pacify -Wsuggest-attribute=format in util.c
* src/util.c (copy_attr_error): Mark with printf attribute.
(copy_attr): Ignore -Wsuggest-attribute=format.
2024-08-25 18:14:30 -07:00
Paul Eggert
ff13fea205 Port to non-VLA C compilers
The C standard does not require support for VLAs.
* src/util.c (cwd_is_root): Don’t use VLA.
2024-08-25 18:14:30 -07:00
Paul Eggert
3d5c0d1fd9 Rename vars to pacify gcc -Wshadow
* src/patch.c (noreverse_flag, reverse_flag, patchbuf, patchbufsize):
Rename from noreverse, reverse, buf, bufsize.
All uses changed.
* src/pch.c (do_ed_script): Rename locals.
2024-08-25 18:14:30 -07:00
Paul Eggert
56788ce462 Stop including stdbool.h
With current Gnulib it’s not needed.
* src/common.h, src/list.h, src/safe.h: Don’t include stdbool.h
2024-08-25 18:14:30 -07:00
Paul Eggert
c10da772ef Recommend 64-bit time_t on 32-bit platforms
* bootstrap.conf (gnulib_modules): Add year2038-recommended.
2024-08-25 18:14:30 -07:00
Paul Eggert
1e217677df Remove pch_sha1
* src/pch.c (pch_sha1): Remove; unused.
2024-08-25 18:14:30 -07:00
Paul Eggert
39005cfce9 Move skip_spaces
* src/pch.c (skip_spaces): Move here ...
* src/util.h: ... from here, since it is used only in pch.c.
No need for it to be declared inline.
2024-08-25 18:14:30 -07:00
Paul Eggert
755712d85e Remove pch_timestamp function
* src/pch.h (pch_timestamp): Remove.  All uses removed.
It wasn’t worth the aggravation to keep it.
2024-08-25 18:14:30 -07:00
Paul Eggert
04f0eeb438 Prefer extern inline to static inline for list.h
* src/list.h: Use _GL_INLINE_HEADER_BEGIN, _GL_INLINE_HEADER_END.
(LIST_INLINE): New macro, if not already defined.
(INIT_LIST_HEAD, list_add, list_del, list_del_init, list_empty)
(list_entry): Now LIST_INLINE, not static inline.
* src/safe.c (LIST_INLINE): New macro.
2024-08-25 18:14:30 -07:00
Paul Eggert
f06c1230d7 maint: pacify gcc 14 -Wcast-align
* src/list.h (list_entry): Now a 2-arg function rather than
a 3-arg macro.  All uses changed.
2024-08-25 18:14:30 -07:00
Paul Eggert
aab6e7bc8a maint: pacify -Wanalyzer-null-argument
* src/pch.c (do_ed_script): Simplify slightly to pacify
GCC 14 -Wanalyzer-null-argument.
2024-08-25 18:14:30 -07:00
Paul Eggert
d1d32c9747 maint: work around GCC bug 109839
* src/patch.c, src/pch.c: Ignore -Wanalyzer-fd-leak.
2024-08-25 18:14:30 -07:00
Paul Eggert
7575694ee8 maint: pacify gcc -Wmissing-variable-declarations
* src/common.h, src/inp.c, src/inp.h, src/merge.c, src/patch.c:
* src/pch.c, src/pch.h, src/safe.c, src/util.c, src/util.h:
* src/version.c:
Stop using XTERN, as that trick doesn’t work with gcc
-Wmissing-variable-declarations.  Instead, use the vanilla
approach of extern declarations in .h files, and definitions
without ‘extern’ in .c files.
2024-08-25 18:14:30 -07:00
Paul Eggert
8f78b098c2 maint: pacify gcc -Winline
* configure.ac (nw): Add -Winline.
2024-08-25 18:14:30 -07:00
Paul Eggert
bb841fd7b0 maint: port _FORTIFY_SOURCE to Ubuntu
* configure.ac (_FORTIFY_SOURCE): Define only if not already defined,
so as to not collide with the builtin definition on Ubuntu 24.04 LTS.
2024-08-25 18:14:30 -07:00
Paul Eggert
4887683ab0 maint: assume STDC_HEADERS
We don’t need to worry about pre-C89 any more.
* src/common.h (CTYPE_DOMAIN, ISSPACE): Remove.
All uses of ISSPACE replaced by isspace.
(errno): Remove decl.
2024-08-25 18:14:30 -07:00
Paul Eggert
5b8ecde1a2 maint: spruce up our .m4 files a bit
* m4/setmode.m4 (AC_FUNC_SETMODE_DOS):
Use AC_CHECK_HEADERS_ONCE instead of AC_CHECK_HEADERS.
* m4/xattr.m4: Bump serial.
2024-08-25 18:14:30 -07:00
Paul Eggert
009a424a74 maint: omit obsolete macro calls
* configure.ac: Remove gl_USE_SYSTEM_EXTENSIONS.
It is no longer needed, since Gnulib does this for us.
2024-08-25 18:14:30 -07:00
Paul Eggert
299167f059 maint: simplify .gitignore
* .gitignore: Update for current Gnulib.
Merge m4/.gitignore, tests/.gitignore into this.
* m4/.gitignore, tests/.gitignore: Remove.
2024-08-25 18:14:30 -07:00
Paul Eggert
3ec44a42c4 build: update gnulib submodule to latest 2024-08-25 18:14:30 -07:00
Andreas Gruenbacher
68cb5293f7 build: update gnulib submodule to latest
* bootstrap, bootstrap.conf: Update.
* configure.ac: Remove obsolete AC_PROG_CC_STDC macro (AC_PROG_CC is
already present). Remove obsolescent AC_HEADER_STDC macro.
* m4/xattr.m4: Replace obsolete AC_HELP_STRING by AS_HELP_STRING.
* m4/setmode.m4: Replace obsolete AC_TRY_LINK by AC_LINK_IFELSE.
* Makefile.am (EXTRA_DIST): Add missing m4/gnulib-cache.m4.
* lib/Makefile.am: Regenerate.
2024-08-23 22:37:40 +02:00
Collin Funk
f144b35425 build: Enable the 'subdir-objects' Automake option.
* configure.ac (AM_INIT_AUTOMAKE): Add 'subdir-objects'.
* src/safe.c (remove_cached_dirfd): Use hash_remove instead of the
deprecated hash_delete.
2024-03-02 00:17:51 +01:00
Collin Funk
faafc79f8d build: update gnulib submodule to latest
The current version of gnulib causes build failures which are fixed by
the following commit:
<https://git.savannah.gnu.org/cgit/gnulib.git/commit/?id=8002ca7b56acb46b42eeac4a343e112a8ee283cf>
* src/pch.c (do_ed_script): Update arguments to gnulib's execute
function to match the addition of the directory argument. No
functional changes.
2024-03-02 00:17:51 +01:00
Takashi Iwai
c835ecc67b Pass the correct stat to backup files
The last case to call output_file() in the main loop is
    output_file (outname, NULL, &tmpoutst, NULL, NULL,
                 file_type | 0, backup);
and this essentially means to create a backup file (where to=NULL)
only if backup=true, and does nothing else.

And, in the current code, the passed file stat (&tmpoutst) is a file
stat of the temporary file that has been processed, not the original
file (outname) to be backed up.  When the backup is performed
immediately, this is no big problem.  However, output_file() may
schedule the deferred handling, and the given file may be backed up at
a later point.  The problem is that create_backup() tries to avoid the
backup of the same file twice, and it checks the given stat i-node
number in the hash list.  Since it's a stat of a temporary file, the
same i-node number may be reused once a temp file is deleted and
another is created.  This results in a false-positive detection of the
already existing file, eventually missing a backup file.

This patch attempts to address the issue:
- Modify the condition for better understanding, clearly indicating
  that the code there is for creating a backup file
- Pass the stat of the original file instead of a temporary file

BugLink: https://bugzilla.opensuse.org/show_bug.cgi?id=1198106
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jean Delvare <jdelvare@suse.de>
2022-05-10 10:53:04 +02:00
Paul Eggert
24f81beb27 maint: modernize README-{hacking,prereq} 2021-10-30 16:33:05 -07:00
Kerin Millar
7623b2dc0d Fix test for presence of BASH_LINENO[0]
eval is not some sort of magical sandbox for executing code that might cause
the shell's parser to take exception. Render the test resilient by carrying
it out within a subshell. While at it, position the redirection so that
STDERR is, in fact, muted.

Signed-off-by: Kerin Millar <kfm@plushkava.net>
Reported-by: Paolo Pedroni <paolo.pedroni@iol.it>
Closes: https://bugs.gentoo.org/738810
2021-01-08 10:15:31 +01:00
Andreas Gruenbacher
0993940034 gnulib: update to latest
* bootstrap: Update.
* bootstrap.conf (gnulib_modules): Replace getdate with parse-datetime, malloc
with malloc-gnu, and realloc with realloc-gnu.
* src/patch.c (main): Function find_backup_file_name has gained a new dir_fd
argument.
* src/util.c (create_backup): Likewise.
(fetchname): Function get_date has been renamed to parse_datetime.
2020-05-14 14:01:13 +02:00
Andreas Gruenbacher
78ed9decdf Add missing-section tests to context-format test case
* tests/context-format: Add tests with a missing pattern and a missing
replacement section in a hunk.  Patch should fill in the missing
sections from the existing sections.
2019-12-24 00:26:49 +01:00
Andreas Gruenbacher
76e775847f Fix failed assertion 'outstate->after_newline'
The assertion triggers when the -o FILE option is used, more than one output
file is written into FILE, and one of those files (except the last one) ends in
the middle of a line.
* src/patch.c (main): Fix the case described above.
2019-07-16 01:16:28 +02:00
Andreas Gruenbacher
15b158db3a Avoid invalid memory access in context format diffs
* src/pch.c (another_hunk): Avoid invalid memory access in context format
diffs.
2019-07-15 19:53:50 +02:00
Andreas Gruenbacher
dce4683cbb Don't follow symlinks unless --follow-symlinks is given
* src/inp.c (plan_a, plan_b), src/util.c (copy_to_fd, copy_file,
append_to_file): Unless the --follow-symlinks option is given, open files with
the O_NOFOLLOW flag to avoid following symlinks.  So far, we were only doing
that consistently for input files.
* src/util.c (create_backup): When creating empty backup files, (re)create them
with O_CREAT | O_EXCL to avoid following symlinks in that case as well.
2019-07-15 19:53:27 +02:00
Andreas Gruenbacher
61d7788b83 Don't crash when RLIMIT_NOFILE is set to RLIM_INFINITY
* src/safe.c (min_cached_fds): Define minimum number of cached dir file
descriptors.
(max_cached_fds): Change type to rlim_t to allow storing RLIM_INFINITY.
(init_dirfd_cache): Set max_cached_fds to RLIM_INFINITY when RLIMIT_NOFILE is
RLIM_INFINITY.  Set the initial hash table size to min_cached_fds, independent
of RLIMIT_NOFILE: patches commonly only affect one or a few files, so a small
hash table will usually suffice; if needed, the hash table will grow.
(insert_cached_dirfd): Don't shrink the cache when max_cached_fds is
RLIM_INFINITY.
2019-06-28 11:20:31 +02:00
Andreas Gruenbacher
b7b028a77b Abort when cleaning up fails
When a fatal error triggers during cleanup, another attempt will be made to
clean up, which will likely lead to the same fatal error.  So instead, bail out
when that happens.
src/patch.c (cleanup): Bail out when called recursively.
(main): There is no need to call output_files() before cleanup() as cleanup()
already does that.
2019-06-28 00:46:06 +02:00
Andreas Gruenbacher
a5b442ce01 Skip "ed" test when the ed utility is not installed
* tests/ed-style: Require ed.
2019-06-27 11:09:31 +02:00
Andreas Gruenbacher
2b584aec9e Improve support for memory leak detection
When building with the address sanitizer on, free some more resources before
exiting.  (This is unnecessary when not looking for memory leaks.)
* src/patch.c (init_files_to_delete): Add dispose function for freeing
filenames.
2019-06-27 11:07:01 +02:00
Andreas Gruenbacher
9c986353e4 Fix swapping fake lines in pch_swap
* src/pch.c (pch_swap): Fix swapping p_bfake and p_efake when there is a
blank line in the middle of a context-diff hunk: that empty line stays
in the middle of the hunk and isn't swapped.

Fixes: https://savannah.gnu.org/bugs/index.php?53133
2018-08-17 16:27:11 +02:00
Andreas Gruenbacher
ff81775f4e Make the (debug & 2) output more useful
* src/pch.c (another_hunk): In the (debug & 2) output, fix how empty
lines that are not part of the patch context are printed.  Also, add
newlines to lines that are missing them to keep the output readable.
2018-08-17 13:13:54 +02:00
Jean Delvare
369dcccdfa Don't leak temporary file on failed multi-file ed-style patch
The previous fix worked fine with single-file ed-style patches, but
would still leak temporary files in the case of multi-file ed-style
patch. Fix that case as well, and extend the test case to check for
it.

* src/patch.c (main): Unlink TMPEDNAME if needed before moving to
  the next file in a patch.

This closes bug #53820:
https://savannah.gnu.org/bugs/index.php?53820

Fixes: 123eaff0d5d1 ("Fix arbitrary command execution in ed-style patches (CVE-2018-1000156)")
Fixes: 19599883ffb6 ("Don't leak temporary file on failed ed-style patch")
2018-05-07 15:15:33 +02:00
Jean Delvare
19599883ff Don't leak temporary file on failed ed-style patch
Now that we write ed-style patches to a temporary file before we
apply them, we need to ensure that the temporary file is removed
before we leave, even on fatal error.

* src/pch.c (do_ed_script): Use global TMPEDNAME instead of local
  tmpname. Don't unlink the file directly, instead tag it for removal
  at exit time.
* src/patch.c (cleanup): Unlink TMPEDNAME at exit.

This closes bug #53820:
https://savannah.gnu.org/bugs/index.php?53820

Fixes: 123eaff0d5d1 ("Fix arbitrary command execution in ed-style patches (CVE-2018-1000156)")
2018-05-03 14:34:48 +02:00
Bruno Haible
f322a7e0e5 Request 'alloca' module from gnulib.
* bootstrap.conf (gnulib_modules): Add 'alloca'.
2018-04-07 16:41:14 +02:00
Bruno Haible
458ac51a05 Fix 'ed-style' test failure.
* tests/ed-style: Remove '?' line from expected output.
2018-04-07 16:41:14 +02:00
Bruno Haible
1e9104c180 Fix check of return value of fwrite().
* src/patch.c (copy_till): Consider incomplete fwrite() write as an error.
* src/pch.c (pch_write_line, do_ed_script): Likewise.
2018-04-07 16:41:14 +02:00
Jim Meyering
ae81be0024 maint: avoid warnings from GCC8
Hi Andreas,

I configured with --enable-gcc-warnings and bleeding-edge gcc
(version 8.0.1 20180406) and hit some warning-escalated-to-errors.
This fixes them:

>From a71ddb200dbe7ac0f9258796b5a51979b2740e88 Mon Sep 17 00:00:00 2001
From: Jim Meyering <meyering@fb.com>
Date: Fri, 6 Apr 2018 16:47:00 -0700
Subject: [PATCH] maint: avoid warnings from GCC8

* src/common.h (FALLTHROUGH): Define.
* src/patch.c (abort_hunk_context): Use FALLTHROUGH macro in place of
a comment.  This avoids a warning from -Wimplicit-fallthrough=.
* src/pch.c (do_ed_script): Add otherwise unnecessary initialization
to avoid warning from -Wmaybe-uninitialized.
(another_hunk): Use FALLTHROUGH macro here, too, twice.
2018-04-07 09:58:05 +02:00
Andreas Gruenbacher
2a32bf09f5 Minor cleanups in do_ed_script
* src/pch.c (do_ed_script): Minor cleanups.
2018-04-06 21:29:56 +02:00
Andreas Gruenbacher
ff1d3a67da Use gnulib execute module
* bootstrap.conf (gnulib_modules): Add execute.
* src/pch.c (do_ed_script): Switch from fork + execlp to execute.
2018-04-06 21:29:19 +02:00
Andreas Gruenbacher
3fcd042d26 Invoke ed directly instead of using the shell
* src/pch.c (do_ed_script): Invoke ed directly instead of using a shell
command to avoid quoting vulnerabilities.
2018-04-06 20:50:06 +02:00
Andreas Gruenbacher
123eaff0d5 Fix arbitrary command execution in ed-style patches (CVE-2018-1000156)
* src/pch.c (do_ed_script): Write ed script to a temporary file instead
of piping it to ed: this will cause ed to abort on invalid commands
instead of rejecting them and carrying on.
* tests/ed-style: New test case.
* tests/Makefile.am (TESTS): Add test case.
2018-04-06 12:23:02 +02:00
Andreas Gruenbacher
b5a91a01e5 Allow input files to be missing for ed-style patches
* src/pch.c (do_ed_script): Allow input files to be missing so that new
files will be created as with non-ed-style patches.
2018-04-06 12:23:02 +02:00
Andreas Gruenbacher
f290f48a62 Fix segfault with mangled rename patch
http://savannah.gnu.org/bugs/?53132
* src/pch.c (intuit_diff_type): Ensure that two filenames are specified
for renames and copies (fix the existing check).
2018-02-12 16:48:24 +01:00
Andreas Gruenbacher
074e2395f8 Test suite: fix Korn shell incompatibility
tests/merge: In a Korn shell, shift apparently fails when $# is 0.
2018-02-07 17:05:00 +01:00
Andreas Gruenbacher
f6bc5b14bd Test suite compatibility fixes
* tests/crlf-handling, tests/git-cleanup, tests/test-lib.sh: Use printf
instead of echo -e / echo -n for compatibility with systems that don't
support these echo options.
* tests/merge: Minor other cleanups.
2018-02-07 13:19:24 +01:00
Andreas Gruenbacher
3bbebbb29f Avoid set_file_attributes sign conversion warnings
* src/util.c (set_file_attributes): Avoid sign conversion warnings when
assigning -1 to uid_t / gid_t.
2018-02-07 12:01:22 +01:00
Andreas Gruenbacher
40b387de08 Version 2.7.6
* NEWS: Update.
2018-02-03 19:43:57 +01:00
Andreas Gruenbacher
66f9fa2690 Fix typo in README
* README: Fix typo.
2018-02-03 19:43:57 +01:00
Andreas Gruenbacher
107e1cecfc Switch to gnupload module
* bootstrap.conf: use gnupload module.
* (RELEASE_TYPE): Define, as maint.mk expects it to be defined.
(tell-upload): Remove obsolete target.
2018-02-03 14:46:17 +01:00
Andreas Gruenbacher
e6da0b81bf Remove stale reference to m4/utimbuf.m4
* Makefile.am (EXTRA_DIST): Remove stale reference to m4/utimbuf.m4.
2018-02-03 14:44:53 +01:00
Andreas Gruenbacher
9ab866d562 maint: update bootstrap and gnulib submodule
* bootstrap: Update from gnulib.
2018-02-03 13:26:55 +01:00
Andreas Gruenbacher
6a737fca57 Clarify the error messages for malformed normal diff patches
* src/pch.c (another_hunk): Clarify the error messages for malformed
normal diff patches.
2018-01-23 11:01:47 +01:00
Thomas Orgis
592e1f9163 Create git diff files with indicated mode
* src/patch.c (main): Create git diff files with indicated mode.
* tests/file-create-modes: New test case.
* tests/Makefile.am (TESTS): Add test case.

This fixes building current Linux 4.14.x from the signed tarball and
patch file, where the patch creates a script with the executable bit
set.
2018-01-23 10:56:27 +01:00
Andreas Gruenbacher
15cc7d44d4 gnulib: update to latest 2018-01-23 10:41:04 +01:00
Bruno Haible
5bf48c87ca Don't use an undocumented Autoconf macro
* configure.ac: Use AC_CONFIG_HEADERS instead of AC_CONFIG_HEADER.
2018-01-23 10:40:56 +01:00
Andreas Gruenbacher
00947f4111 Don't allow hunks to overlap
* src/patch.c (locate_hunk): Don't allow a hunk to overlap with the
previous one.
* tests/false-match: Add regression test.
2017-09-06 18:35:44 +02:00
Andreas Gruenbacher
61b6ec8857 patch.man: Clarify --forward description 2017-09-06 17:57:29 +02:00
Andreas Gruenbacher
a2b6fb1672 gnulib: update to latest 2017-09-04 13:56:25 +02:00
Tim Waugh
817d7d1767 Rename canonicalize global variable
* src/common.h, src/merge.c (context_matches_file), src/patch.c
(get_some_switches, patch_match), src/pch.c(another_hunk): Rename the
global variable 'canonicalize' to 'canonicalize_ws'.

Patch uses a global variable 'canonicalize' as part of its
implementation of the --ignore-whitespace flag.

In glibc there is a function canonicalize():
https://www.gnu.org/software/libc/manual/html_node/FP-Bit-Twiddling.html#index-canonicalize

Rename the global variable so that it will not conflict.

Original bug report:
https://bugzilla.redhat.com/show_bug.cgi?id=1422463
2017-02-22 11:03:43 +01:00
Hanno Boeck
49705d1d8d Add a missing NULL check in parse_name
* src/util.c (parse_name): parse_c_string() can fail and return NULL.
Check for that so that we won't access the NULL pointer here.
2016-08-10 09:25:29 +02:00
Hanno Boeck
a0d7fe4589 Fix out-of-bounds access to lines in a patch
This bug can trigger with malformed patches.
* src/pch.c (pch_write_line): Avoid out-of-bounds access to
p_line[line][p_len[line] - 1] when p_len[line] is 0.
2016-08-10 00:13:25 +02:00
Jim Meyering
4c43a0b1cb tests: use $PATCH rather than hard-coded path
* tests/git-cleanup: Don't hard-code program name.
2016-07-31 10:17:52 +02:00
Jim Meyering
d874c38335 maint: placate a "make syntax-check" rule
* src/pch.c (set_hunkmax): Don't cast return value of xmalloc.
(grow_hunkmax): Likewise for two uses of realloc that the syntax-check
rule did not detect.
2016-07-31 10:17:52 +02:00
Jim Meyering
b6c4780f73 maint: avoid new warning-errors from gcc-6.1
When configured with --enable-gcc-warnings and gcc-6.1, ...
* src/safe.c (count_path_components): Use _GL_ATTRIBUTE_PURE,
to avoid an error from -Werror=suggest-attribute=pure.
* src/util.h (filename_is_safe): Likewise.
* src/patch.c (main): Placate -Werror=format= by casting
pch_mode's mode_t return type to the "unsigned int" required
to match the %o format string.
* src/patch.c (delete_files): Correct indentation, to avoid
this error from -Werror=misleading-indentation:

  patch.c: In function 'delete_files':
  patch.c:1816:4: error: this 'if' clause does not guard...
      if (verbosity == VERBOSE)
      ^~
  patch.c:1820:6: note: ...this statement, but the latter is
    misleadingly indented as if it is guarded by the 'if'
	move_file (0, 0, 0, file_to_delete->name, mode,
	^~~~~~~~~
2016-07-31 10:17:52 +02:00
Jim Meyering
9ca38fa1e6 gnulib: update to latest 2016-07-31 10:17:52 +02:00
Andreas Gruenbacher
66fdcf0e7c Fix broken git-style patch behavior
When a git-syle patch is applied, all file modifications are done to
temporary files which are put in place when the patch ends.  When a
patch fails, GNU patch was trying to "roll back" to the start.  A bug in
that code that lead to accidental file deletion was recently discovered
by Richard Weinberger <richard@nod.at>.  Even worse though, GNU patch
should not exhibit this "rollback" behavior in the first place; that's
not what people expect. Instead, the files modified so far should be put
in place.
* src/patch.c (cleanup): Put output files processed successfully
in place instead of trying to "roll back" to the start.
(forget_output_files): Remove obsolete (and broken) function.
* tests/git-cleanup: New broken git-style patch test case that exercises
the cleanup path.
* tests/Makefile.am (TESTS): Add new test case.
2016-07-27 15:47:47 +02:00
Andreas Gruenbacher
83a3ed012c Fix inname test case
* src/safe.h (unsafe): New flag to allow turning off safe file
operations.
* src/safe.c (safe_xstat, safe_open, safe_rename, safe_mkdir,
safe_rmdir, safe_unlink, safe_symlink, safe_chmod, safe_lchown,
safe_lutimens, safe_readlink, safe_access): When safe file operations
are turned off, skip safe path traversal.  Any symlink checks of the
last path component are still done though.
* src/patch.c (main): When the file to patch is specified on the command
line, turn off safe file operations.
* tests/inname: Fix typo in test.
2016-07-27 15:47:47 +02:00
Andreas Gruenbacher
70532e21a8 Add context-format test to XFAIL_TESTS for now
* tests/Makefile.am (XFAIL_TESTS): Add context-format test until someone
gets to fixing it.
2016-07-27 10:58:03 +02:00
Andreas Gruenbacher
4f4fd1b41d Add broken context-format test cases
* tests/context-format: Add context-format test cases from
Mattias Andrée <maandree@member.fsf.org> that patch doesn't parse
correctly.
2016-03-18 10:12:31 +01:00
Andreas Gruenbacher
3270d221a9 Don't use a zero-size array in struct symlink
* src/safe.c (struct symlink): Get rid of the zero-size array which is a gcc
extension.
(read_symlink): Adapt to this struct symlink change.
2015-10-03 15:25:49 +02:00
Andreas Gruenbacher
98b86e970a Input file outside current directory: add test case
Patch currently makes sure that input / output files are inside the current
working directory even when the input files are explicitly specified on the
command line (see http://savannah.gnu.org/bugs/?45581).
* tests/inname: Add a test case for this bug.
2015-07-20 11:18:12 +02:00
Tobias Stoeckmann
68049159f2 Terminate readlink string
The function readlink does not nul terminate its result string.  safe_readlink
is a wrapper for readlinkat, which has the same behaviour.
* src/util.c (copy_file): Therefore, explicitly set '\0' and reserve one byte
for it.  (agruen: Reserve PATH_MAX + 1 bytes instead of only reading PATH_MAX -
1 characters.)
2015-07-12 15:38:23 +02:00
Tobias Stoeckmann
871f87824a Use xmalloc in bestmatch
* src/bestmatch.h (bestmatch): Use xmalloc instead of malloc to handle
out-of-memory situations.
2015-07-08 20:55:51 +02:00
Andreas Gruenbacher
5c6625bf62 Don't require traditional patch header after "git --diff"
Reported by Tim Waugh <twaugh@redhat.com>.
* src/pch.c (intuit_diff_type): Don't require a traditional patch header
("--- old\n+++ new/n") after a "git --diff" header; the "git --diff" header
gives us enough information for being able to process subsequent hunks.  This
deals with corrupted patches more gracefully.
* tests/corrupt-patch: New test case.
* tests/Makefile.am (TESTS): Add test case.
2015-03-09 15:29:15 -04:00
Andreas Gruenbacher
6a55099e7a Use gnulib size_max module
* bootstrap.conf (gnulib_modules): Add size_max.
* configure.ac: Call gl_SIZE_MAX.
2015-03-08 14:55:48 -04:00
Andreas Gruenbacher
3b698ab6a1 Version 2.7.5
* NEWS: Update.
2015-03-07 01:32:27 +01:00
Andreas Gruenbacher
3cd040cf4c build: update gnulib submodule to latest 2015-03-07 01:32:21 +01:00
Andreas Gruenbacher
7a77ae9f81 Allow absolute symlinks that lead back into the working directory
* src/safe.c (cwd_stat_errno, cwd_stat): stat() result of ".".
(read_symlink): When a symlink is absolute, check if it leads back into the
working directory.  If it does, strip off the prefix above the working
directory. If the symlink points to the working directory, return an empty
path.
(traverse_another_path): Recognize empty paths from read_symlink().
* tests/symlinks: Absolute symlink test cases.
2015-03-07 01:23:29 +01:00
Andreas Gruenbacher
274c66c775 Describe better how the dirfd cache works 2015-03-05 23:14:57 +01:00
Andreas Gruenbacher
99d3b514e9 Use overflow safe arithmetic for counting cache misses
* src/safe.c: We don't need a long counter if we use overflow-safe arithmetic
here.
2015-03-05 23:14:25 +01:00
Andreas Gruenbacher
a6615bcb83 Also cache resolved symlinks
When resolving a symlink in a pathname, we traverse each path component in the
symlink and cache all of them.  At the end, add an additional cache entry for
the symlink itself so that we don't have to resolve the symlink again (even
though this will usually be cached).  Skip that if the symlink's parent isn't
in the cache anymore, though.
* src/safe.c (free_cached_dirfd): Remove from parent here instead of in
callers. Move close() to remove_cached_dirfd() instead.
(insert_cached_dirfd): Only insert if the entry's parent still exists; entries
without parent are invalid (see compare_cached_dirfds()); "top-level" entries
have cwd as their parent.
(new_cached_dirfd): New function split off from openat_cached().
(openat_cached): Use new_cached_dirfd() here.
(traverse_another_path): When starting to resolve a symlink, create an unhashed
dirfd cache entry for the symlink lookup result. When the symlink is completely
resolved, add that entry to the cache.
2015-03-05 22:57:44 +01:00
Andreas Gruenbacher
914d06b7c3 Invalidate child dirfd cache entries when their parent goes away
If we don't do that, a directory could be removed from the cache, a new
directory with the same dirfd could be created, and the entries from the old
directory would appear in the new directory.
* src/safe.c (struct cached_dirfd): Keep track of the children of each dirfd
cache entry.
(remove_cached_dirfd): Remove all the entry's children from the lookup hash,
take them off the list of children, and initialize the children's
children_link.  Then, remove the entry itself from its parent. This has no
effect if the entry doesn't have a parent because then, children_link is empty.
(openat_cached): Add new dirfd cache entries to their parent's list of children
and initialize the entry's list of children.
(traverse_another_path): Also initialize cwd's list of children.
2015-03-05 22:57:44 +01:00
Andreas Gruenbacher
c5705fd476 Convert lru list into a list_head list
* src/safe.c (struct cached_dirfd): Replace prev and next with a lru_link
list_head.
(lru_list): Turn into a list_head.
(lru_list_add, lru_list_del, lru_list_del_init): Replace by list_add(),
list_del(), list_del_init().
(insert_cached_dirfd): Get to the list entry from the embedded list_head with
the list_entry() macro.
2015-03-05 22:57:42 +01:00
Andreas Gruenbacher
ca9df22fe6 Add list_head based double linked list
* src/list.h: New data structure.
src/Makefile.am (patch_SOURCES): Add list.h.
2015-03-05 22:57:07 +01:00
Andreas Gruenbacher
0d3df382d6 Invalidate dirfd less aggressively
src/safe.c (safe_rename, safe_rmdir): Only invalidate cache entries when the
underlying sycall succeeds and the entry actually goes away.  This keeps the
cache filled upon speculative rmdir when the directory may not be empty, for
example.
2015-03-05 22:57:07 +01:00
Tim Waugh
9f92e52c9f Add more path traversal test cases
* tests/symlinks: Add more path traversal test cases.
2015-03-05 22:57:07 +01:00
Andreas Gruenbacher
6fbdcefe7d Move path traversal error reporting into main()
* src/safe.c (traverse_another_path): Don't report errors here.
* src/patch.c (main): Instead, recognize and report them here. Detect when an
output file name is invalid; it doesn't make sense to try creating a
reject file based on the same outbut file name in that case.
2015-03-05 22:57:07 +01:00
Andreas Gruenbacher
a025a51ca5 Limit the number of path components
src/safe.c (MAX_PATH_COMPONENTS): The maximum number of path components
allowed.
(count_path_components): New function.
(traverse_another_path): Fail if the number of path components gets too high.
2015-03-05 22:57:07 +01:00
Andreas Gruenbacher
ef609c26b2 Follow directory symlinks within the working directory
* src/safe.c (struct symlink): A symlink to resolve.
(push_symlink, pop_symlink): New functions.
(read_symlink): Create a new symlink stack entry.
(traverse_next): Follow ".." components within the working directory. When
hitting symlinks, "follow" them by reading and returning them.
(traverse_another_path): Recursively traverse symlinks.
2015-03-05 22:57:04 +01:00
Andreas Gruenbacher
3c58eb50ce Keep track of the directory hierarchy
* src/safe.c (struct cached_dirfd): Add parent pointer. Now that we know our
parent, we no longer need to duplicate its directory file descriptor.
(lookup_cached_dirfd): Don't update the lru list here.
(insert_cached_dirfd): The lru list may now be empty even if the cache is not.
(put_path): New function to put a path back into the lru list.
(openat_cached): Take cached entried off the lru list.  They are added back
in put_path().
(traverse_another_path): Put lookup result back into the lru list with
put_path().
2015-03-05 21:49:55 +01:00
Andreas Gruenbacher
e1f0aa0a9d Refactor traverse_another_path() and helpers
Prepare for keeping track of the directory hierarchy:
* src/safe.c (traverse_another_path): Pass struct cached_dirfd to
traverse_next().
(traverse_next, openat_cached): Pass through struct cached_dirfd.
2015-02-28 21:10:34 +01:00
Andreas Gruenbacher
c5da382c0b Move error reporting out of make_tempfile()
* src/util.c (make_tempfile): Remove error reporting here.
* src/inp.c (plan_b): Readd error reporting here.
* src/patch.c (main): Likewise.
* src/pch.c (open_patch_file): Likewise.
2015-02-28 21:10:34 +01:00
Andreas Gruenbacher
d55ab5b941 Minor cosmetic changes
* src/safe.c: Minor cosmetic changes
2015-02-28 21:10:22 +01:00
Andreas Gruenbacher
db9f39507e Fix handling of renamed files
When a file has already been renamed, make sure it is not renamed back to its
old name.  Reported by Guido Draheim.
* src/patch.c (main): Make sure we never rename a file back to its previous
name. Report when a file was renamed already.
* tests/copy-rename: Add "already renamed" test cases.
2015-02-22 18:50:16 +01:00
Andreas Gruenbacher
8d12036047 Fix symlinks test case on some architectures
* src/safe.c: Include util.h for say(). Define EFTYPE if it isn't defined
already.
(traverse_another_path): When openat fails, also check for EMLINK, EFTYPE, and
ENOTDIR. Change the error message to "file ... is not a directory" and only
skip the rest of the patch instead of aborting.
* tests/symlinks: Update.
2015-02-10 22:31:56 +01:00
Andreas Gruenbacher
7297352e16 Test suite portability fixes
Reported and fixed (mostly) by Christian Weisgerber <naddy@mips.inka.de>:
* tests/deep-directories: Avoid the bash >& redirection operator.
* tests/no-mode-change-git-diff: Instead of "stat -c", use "ls -l  sed".
* tests/read-only-files: A redirection failure for a special built-in causes
some shells (FreeBSD sh, OpenBSD sh (pdksh), some bash --posix) to exit, and
the colon command is a special built-in. Perform the redirection in a subshell.
2015-02-04 10:54:03 +01:00
Andreas Gruenbacher
69434de2d5 Switch from gen_tempname() to try_tempname()
* Update gnulib submodule to latest.
* src/util.c (try_safe_open_args, try_safe_open): Arguments and callback for
try_tempname().
(make_tempfile): Switch from gen_tempname() to try_tempname().
2015-02-04 10:53:56 +01:00
Andreas Gruenbacher
ddf16e1209 Check the result of the --follow-symlinks option
* tests/symlinks: Check the result of treating a symlink as a file with
--follow-symlinks.
2015-02-02 15:43:56 +01:00
Andreas Gruenbacher
8a9f5432d4 Link patch with LIB_EACCESS where needed
* src/Makefile.am (patch_LDADD): Add LIB_EACCESS here. At least on Solaris,
faccessat() is implemented through eaccess() which is in the "gen" library.
2015-02-01 17:09:59 +01:00
Andreas Gruenbacher
b73c1f95cb Fix minor signedness warning
* src/pch.c (intuit_diff_type): Don't assign signed dummy value to unsigned
variable.
2015-02-01 15:47:16 +01:00
Andreas Gruenbacher
709458d9d9 Use gnulib faccessat module
* bootstrap.conf (gnulib_modules): Add faccessat.
2015-02-01 15:45:24 +01:00
Andreas Gruenbacher
de89dde09e Upate NEWS 2015-01-31 22:14:01 +01:00
Andreas Gruenbacher
38d87ecb9e Fix indentation heuristic for context diffs
Diffs can be indented by a variable number of spaces, tabs, or X characters.
Make sure that intuit_diff_type() only accepts context diffs where the first
and second line are indented identically, or else another_hunk() will fail.
* src/pch.c (intuit_diff_type): Remember the indentation of the last line. Only
recognize context diff hunks with the same amount of indentation on the first
and second line.
* tests/garbage: New test case.
* tests/Makefile.am (TESTS): Add test case.
2015-01-31 22:14:01 +01:00
Quentin Casasnovas
82b800c955 patch: git-diff mode: do not change permissions if there isn't an explicit mode change.
Signed-off-by: Quentin Casasnovas <quentin.casasnovas@oracle.com>
2015-01-31 22:14:01 +01:00
Quentin Casasnovas
68d39ff29f tests: add a test case for unwanted mode changes.
Signed-off-by: Quentin Casasnovas <quentin.casasnovas@oracle.com>
2015-01-31 22:14:01 +01:00
Quentin Casasnovas
c80b1a06a9 test-lib.sh: factorize require_* functions
Since the code is identical when just checking if a utility is present on
the system or not, we can factorize it.

Signed-off-by: Quentin Casasnovas <quentin.casasnovas@oracle.com>
2015-01-31 22:14:01 +01:00
Andreas Gruenbacher
8d621e8819 Add test case for patch behind symlink
* tests/symlinks: Add a test case where the patch file itself is in a path that
follows a symbolic link; we want to continue allowing that.
2015-01-31 22:14:01 +01:00
Tim Waugh
290ffcb488 Allow arbitrary symlink targets again
* src/util.c (symlink_target_is_valid): Remove.
(move_file): Remove symlink target checking.
* tests/symlinks: Update test case.
2015-01-31 22:14:01 +01:00
Andreas Gruenbacher
b72e3be5c8 Update list of gnulib modules used
* bootstrap.conf (gnulib_modules): Remove lchmod, lstat, mkdir, readlink,
rename, mkdir, symlink, unlink, utimens. Add fchownat, fchmodat, fstatat,
mkdirat, openat, readlinkat, renameat, symlinkat, unlinkat, utimensat.
* src/util.h: Don't include <utimens.h> anymore.
2015-01-31 22:14:01 +01:00
Andreas Gruenbacher
71a3172c7e Use symlink-safe system call replacements
Use the symlink-safe replacements for system calls in many places throughout
the code: In some places this makes patch safe against path traversal attacks;
in other places, it saves the kernel from having to re-traverse the pathnames.
* src/inp.c (plan_b): Use safe_open() + fdopen() instead of fopen().
* src/util.c (copy_attr): Document why we are safe here.
(create_backup): Use safe_open() instead of creat().
2015-01-31 22:14:00 +01:00
Tim Waugh
025a54b789 Add symlink-safe system call replacements
Add wrappers around system calls that traverse relative pathnames without
following symlinks. Written by Tim Waugh <twaugh@redhat.com> and Andreas
Gruenbacher <agruenba@redhat.com>.
* src/safe.h: Declare functions here.
* src/safe.c: Implement safe_* system call replacements that do not follow
symlinks along pathnames. Pathname components are resolved with openat().
Lookup results are cached to keep the overhead reasonably low.
* tests/deep-directories: New path traversal cache test.
* src/Makefile.am (patch_SOURCES): Add safe.[ch].
* tests/Makefile.am (TESTS): Add new test.
2015-01-31 22:13:44 +01:00
Andreas Gruenbacher
6a56d401d2 build: update gnulib submodule to latest 2015-01-31 18:25:15 +01:00
Tim Waugh
75fcb4b560 Avoid closing file descriptor twice
* src/patch.c (main): Make sure we don't close() outfd after passing it on to
fdopen(): the file descriptor might have been reused in the meantime.
2015-01-31 18:25:12 +01:00
Andreas Gruenbacher
3e7113a6b5 Remove unused variable
* src/pch.c (name_is_valid): Remove unused variable.
2015-01-29 19:31:53 +01:00
Andreas Gruenbacher
41688ad8ef Fix the fix for CVE-2015-1196
* src/util.c (filename_is_safe): New function split off from name_is_valid().
(symlink_target_is_valid): Explain why we cannot have absolute symlinks or
symlinks with ".." components for now.
(move_file): Move absolute filename check here and explain.
* tests/symlinks: Put test case with ".." symlink in comments for now.
* NEWS: Add CVE number.
2015-01-22 21:51:51 +01:00
Andreas Gruenbacher
17953b5893 For renames and copies, make sure that both file names are valid
* src/patch.c (main): Allow there_is_another_patch() to set the
skip_rest_of_patch flag.
* src/pch.c (intuit_diff_type): For renames and copies, also check the "other"
file name.
(pch_copy, pch_rename): Now that both names are checked in intuit_diff_type(),
we know they are defined here.
2015-01-21 16:32:41 +01:00
Andreas Gruenbacher
0c08d7a902 Fail when out of memory in set_hunkmax()
src/pch.c (another_hunk): Call set_hunkmax() from here to make sure it is
called even when falling back from plan A to plan B.
(open_patch_file): No need to call set_hunkmax() anymore.
src/pch.c (set_hunkmax): Fail when out of memory. Make static.
src/pch.h: Remove set_hunkmax() prototype.
2015-01-20 12:43:57 +01:00
Andreas Gruenbacher
19285e563a Don't try applying hunks at offsets that can't work
* src/patch.c (locate_hunk): Start trying to apply the hunk at the minimum
offset which puts the hunk in the valid range of lines. This will often still
be offset 0.
2015-01-20 12:00:24 +01:00
Andreas Gruenbacher
ae88d1c270 Move symlink_target_is_valid() and cwd_is_root()
* src/util.c: Move symlink_target_is_valid() and cwd_is_root() here from
src/pch.c.
2015-01-20 10:10:10 +01:00
Andreas Gruenbacher
4e9269a5fc Make sure symlinks don't point outside working directory (CVE-2015-119)
When creating symlinks from git-style patches, make sure the symlinks don't
point above the current working directory.  Otherwise, a subsequent patch could
use the symlink to write outside the working directory.

* src/pch.c (symlink_target_is_valid): New function to check for valid symlink
targets.
* src/util.c (move_file): Use symlink_target_is_valid() here.
* tests/symlinks: Add valid and invalid symlink test cases.
2015-01-19 23:18:30 +01:00
Andreas Gruenbacher
44a987e02f Add line number overflow checking
* bootstrap.conf: use intprops module.
* src/common.h: Define LINENUM_MIN and LINENUM_MAX macros.
* src/pch.c (another_hunk): Add line number overflow checking.  Based on Robert
C. Seacord's INT32-C document for integer overflow checking and Tobias
Stoeckmann's "integer overflows and oob memory access" patch for FreeBSD.
2014-11-30 20:56:46 +01:00
Andreas Gruenbacher
f22e47d873 More savebuf/savestr error handling
* bootstrap.conf: use xmemdup0 module.
* src/pch.c (there_is_another_patch): Use xmemdup0 instead of savebuf when we
cannot recover from out-of-memory situations.
(intuit_diff_type): Likewise, use xstrdup instead of savestr.
(another_hunk): Handle the case when savestr returns NULL.
* src/util.c (fetchname, parse_name): Use xmemdup0 instead of savebuf when we
cannot recover from out-of-memory situations.

Bugs pointed out by Tobias Stoeckmann <tobias@stoeckmann.org>.
2014-11-30 15:52:42 +01:00
Tobias Stoeckmann
e4c6511f46 savebuf/savestr error handling
* src/patch.c (get_some_switches): The function savebuf (and therefore savestr)
copies strings using malloc.  If malloc fails, NULL is returned.  This is
intentional behavior so that in case of failure during "plan a" patching, "plan
b" can step in.  The return value has to be properly checked for NULL.  If the
return value must not be NULL, use xstrdup instead.
2014-11-30 15:35:44 +01:00
Andreas Gruenbacher
3fd4144ae9 build: update gnulib submodule to latest
* src/merge.c (compute_changes): The TOO_EXPENSIVE heuristic in diffseq has
been removed, including compareseq's find_minimal parameter and the context's
too_expensive limit.  Adjust.
2014-11-30 15:35:31 +01:00
Jean Delvare
65193f1cc1 Drop useless test in another_hunk()
src/pch.c (another_hunk): This test will always succeed.
2014-11-10 11:43:36 +01:00
Tobias Stoeckmann
e25e622dec Buffer overflow on malicious input file
There is a hard to reach but possible buffer overflow when using
patch with a very large (modified) input file.  I doubt you will ever
see this with a 64 bit system, but it's possible with 32 bit:

$ echo hello > file1
$ echo world > file2
$ diff -Nau file1 file2 > file.diff

Nothing fancy so far.  Adjust file1 so it contains at least one line that
is 2 GB in size.  Larger is fine too, but stay below 4 GB.

$ tr '\0' c < /dev/zero | dd bs=1K count=2097152 of=file1

Now try to patch it.

$ patch -Np0 -i file.diff
Segmentation fault

The issue is in patch's "plan b" strategy  (If your system would still
want to use "plan a", force patch to use "plan b" through debug flag).

Plan b writes lines into a temporary file, with equally long lines, so
it can use a buffer mechanism to access them in a kind of randomly
fassion.  In order to do that, it retrieves the longest line.

In this example, it will encounter the 2 GB line and stores that as the
longest one.  Afterwards it will adjust the tibufsize variable to be
large enough:

  for (tibufsize = TIBUFSIZE_MINIMUM;  tibufsize < maxlen;  tibufsize <<= 1)
    /* do nothing */ ;

Due to maxlen's size (2 GB), tibufsize will be SIZE_T_MAX, i.e. 4 GB.
A few lines later it allocates space for the tibuf buffers:

  tibuf[0] = xmalloc (2 * tibufsize);
  tibuf[1] = tibuf[0] + tibufsize;

This will allocate 0 bytes because tibufsize overflowed.  The next
time patch writes into the buffer, a segmentation fault will occur...
Depends on your system how long it takes until that happens. ;)

The fix is simple:  Bail out on lines that are too long.  Patch already
does that for files that have too many lines.
2014-10-30 22:47:40 +01:00
Andreas Gruenbacher
f926295f4f Improve error message when refusing to delete file
* src/patch.c: Improve error message.
* tests/create-delete: Update the test case.
2014-08-13 01:16:44 +02:00
Andreas Gruenbacher
dc63883f08 Correct the --help text of option --merge
* src/patch.c (option_help): The --merge option does not have a short
form; update the help text.
2013-12-09 09:26:16 +01:00
Steven Rostedt
a2f4bfe0f3 Preserve function names in reject files
* src/patch.c (main): Preserve function names in reject files.
* tests/reject-format: Update the test case.
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Andreas Gruenbacher <agruen@linbit.com>
2013-08-19 22:44:46 +02:00
Andreas Gruenbacher
89dbba7229 Test case for the dry-run fix
* tests/create-directory: Add test case here.
2013-07-30 15:28:25 +02:00
Andreas Gruenbacher
afdfa9ec8e In dry-run mode, create temporary files in a temporary directory
* src/util.c (make_tempfile): Do not create temporary files in the final output
directory when in dry-run mode: the path may be read-only.  In addition, we do
not want to leave intermediary empty output directories around.
2013-07-30 12:52:42 +02:00
Eric S. Raymond
aac14e3187 Fix some formatting problems in the manpage
* patch.1: Use higher-level markup that translates better into HTML and other
formats.  (With changes by Andreas Gruenbacher.)
2013-06-18 09:48:32 +02:00
Stefano Lattarini
60c9d4838f build: don't use -Werror in AM_INIT_AUTOMAKE
Doing so prevents bootstrapping with bleeding-edge autotools,
because of harmless deprecation warnings (that are not planned
to become hard errors for at least a few years to come).  And
unfortunately, options in AM_INIT_AUTOMAKE take precedence over
those given on the command line (this is a long-time wart of
automake).

* configure.ac (AM_INIT_AUTOMAKE): Drop '-Werror' option.

Copyright-paperwork-exempt: yes
Signed-off-by: Stefano Lattarini <stefano.lattarini@gmail.com>
2013-05-02 06:03:31 +02:00
Andreas Gruenbacher
2f40ef66be Fix removing empty directories
Reported by Thomas Moschny <thomas.moschny@gmx.de>:
src/patch.c (main): Temporary output files are created in the same directory as
the output file.  Make sure to remove them before removing empty files and
their empty ancestor directories; else the directories won't be empty.
tests/remove-directories: Add directory removal test case.
tests/Makefile.am (TESTS): Add new test case.
2013-03-10 19:02:54 +01:00
Andreas Gruenbacher
082baa326a Clarify the description of option --forward
* patch.man: Clarify the description of option --forward.
2013-01-03 22:19:20 +01:00
Andreas Gruenbacher
291ec17581 Initialize data structures early enough
* src/patch.c (main): Initialize data structures early enough, before error
paths can access them.
* tests/bad-usage: Test bad command line usage.
* tests/Makefile.am (TESTS): Add bad-usage here.
2012-10-04 12:43:05 +02:00
Andreas Gruenbacher
47191c287d Don't fail test suite if printf '\0' is broken
* tests/create-delete: Skip binary diff test if printf '\0' is broken.
2012-09-30 13:06:35 +02:00
85 changed files with 6279 additions and 3922 deletions

7
.gitattributes vendored Normal file
View File

@ -0,0 +1,7 @@
# Run this to make 'git diff' on texinfo files give nicer hunk context:
# git config diff.texinfo.funcname '^@node[ ][ ]*\\([^,][^,]*\\)'
*.texi* diff=texinfo
# Run this to make 'git diff' on .m4 files give nicer hunk context:
# git config diff.m4.xfuncname '^((AC_DEFUN|m4_define)[^,)]*)'
*.m4 diff=m4

23
.gitignore vendored
View File

@ -1,5 +1,7 @@
*.log
*.o
-/lib/Makefile.am
*.trs
*~
.deps/
/.pc/
/.version
@ -8,24 +10,25 @@
/ID
/INSTALL
/VERSION
/[0-9]*.patch
/aclocal.m4
/autom4te.cache/
/build-aux
/build-aux/
/config.cache
/config.h
/config.hin
/config.log
/config.status
/configure
/doc
/lib
/doc/
/lib/
!/lib/Makefile.am
/m4/*.m4
/m4/.gitignore
!/m4/setmode.m4
!/m4/xattr.m4
/maint.mk
/patch-*.tar.bz2
/patch-*.tar.bz2.sig
/patch-*.tar.gz
/patch-*.tar.gz.sig
/patch-*.tar.xz
/patch-*.tar.xz.sig
/patch-*.tar.*
/src/patch
/stamp-h1
Makefile

View File

@ -1039,7 +1039,7 @@
* tests/asymmetric-hunks: New test case.
* Makefile.in (TESTS): Add test case.
* util.c (move_file): Create backup files of nonexisting files with
* util.c (move_file): Create backup files of nonexistent files with
the default mode instead of mode 0: files with mode 0 cause too many
problems with applications which do not expect unreadable files.
* tests/create-delete: Test for this.
@ -2030,7 +2030,7 @@
* util.c (move_file): Don't assume that ENOENT is reported when both
ENOENT and EXDEV apply; this isn't true with DJGPP, and
Posix doesn't require it.
POSIX doesn't require it.
* pch.c (there_is_another_patch):
Suggest -p when we can't intuit a file.
@ -2185,7 +2185,7 @@
verbosity.
(fetchname): Change argument head_says_nonexistent to pstamp, and
store header timestamp into *pstamp.
If -T or -Z option is given, match time stamps more precisely.
If -T or -Z option is given, match timestamps more precisely.
(ask): Remove unnecessary close of ttyfd.
When there is no terminal at all, output a newline to make the
output look nicer. After reporting EOF, flush stdout;
@ -3658,23 +3658,20 @@ Sun Dec 17 17:29:48 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu)
* patch.c: Initial revision
Copyright (C) 1984, 1985, 1986, 1987, 1988 Larry Wall.
Copyright (C) 1989-1993, 1997-2002, 2009-2012 Free Software Foundation, Inc.
Copyright 1989-2025 Free Software Foundation, Inc.
Copyright 1984-1988 Larry Wall.
This file is part of GNU Patch.
This program is free software; you can redistribute it and/or modify
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.
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 they will be useful,
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; see the file COPYING. If not, write to
the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
along with this program. If not, see <http://www.gnu.org/licenses/>.

View File

@ -1,5 +1,4 @@
# Copyright (C) 1989-1993, 1997-1999, 2002-2003, 2006, 2009-2012 Free Software
# Foundation, Inc.
# Copyright 1989-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
@ -23,17 +22,19 @@ dist_man1_MANS = patch.man
EXTRA_DIST = \
ChangeLog-2011 \
cfg.mk \
m4/gnulib-cache.m4 \
m4/mkdir.m4 \
m4/setmode.m4 \
m4/utimbuf.m4 \
bootstrap \
pc
if ALPHA_VERSION
EXTRA_DIST += README-alpha
GNU_SERVER = alpha.gnu.org
RELEASE_TYPE = alpha
else
GNU_SERVER = ftp.gnu.org
RELEASE_TYPE = stable
endif
EXTRA_DIST += $(top_srcdir)/.version
@ -52,14 +53,3 @@ gen-ChangeLog:
rm -f $(distdir)/ChangeLog; \
mv $(distdir)/cl-t $(distdir)/ChangeLog; \
fi
tell-upload:
echo; \
(echo "Upload with: "; \
for archive in $(DIST_ARCHIVES); do \
echo "gnupload --to $(GNU_SERVER):patch $$archive"; \
done) | \
sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'; \
echo
upload: dist tell-upload
uploadcheck: distcheck tell-upload

66
NEWS
View File

@ -1,3 +1,50 @@
Unreleased changes:
Changes in version 2.8:
* The --follow-symlinks option now applies to output files as well as input.
* 'patch' now supports file timestamps after 2038 even on traditional
GNU/Linux platforms where time_t defaults to 32 bits.
* 'patch' no longer creates files with names containing newlines,
as encouraged by POSIX.1-2024.
* Patches can no longer contain NUL ('\0') bytes in diff directive lines.
These bytes would otherwise cause unpredictable behavior.
* Patches can now contain sequences of spaces and tabs around line numbers
and in other places where POSIX requires support for these sequences.
* --enable-gcc-warnings no longer uses expensive static checking.
Use --enable-gcc-warnings=expensive if you still want it.
* Fix undefined or ill-defined behavior in unusual cases, such as very
large sizes, possible stack overflow, I/O errors, memory exhaustion,
races with other processes, and signals arriving at inopportune moments.
* Remove old "Plan B" code, designed for machines with 16-bit pointers.
* Assume C99 or later; previously it assumed C89 or later.
* Port to current GCC, Autoconf, Gnulib, etc.
Changes in version 2.7.6:
* Files specified on the command line are no longer verified to be inside the
current working directory, so commands like "patch -i foo.diff ../foo" will
work again.
* Various fixes.
Changes in version 2.7.5:
* There are users which expect patch to follow symbolic links in the working
directory, so patch now again follows symbolic links as long as they do not
leave the working directory.
Changes until version 2.7.4:
* When a file isn't being deleted because the file contents don't match the
patch, the resulting message is now "Not deleting file ... as content
differs from patch" instead of "File ... is not empty after patch; not
deleting".
* Function names in hunks (from diff -p) are now preserved in reject files.
* Patch no longer follows symbolic links to input and output files. This
ensures that symbolic links created by git-style patches cannot cause
patch to write outside the working directory (CVE-2015-1196).
* Various fixes.
Changes in version 2.7.1:
* Two critical bug fixes in the "diff --git" format support.
@ -84,7 +131,7 @@ Changes in version 2.6:
line endings in patches. This allows to preserve CRs even in mangled
patches, or in patches generated on non-POSIX systems and without the
--binary option.
* Backup files for nonexisting files are now created with default
* Backup files for nonexistent files are now created with default
permissions rather than with mode 0: backup files with mode 0 were
causing problems with applications which do not expect unreadable
files.
@ -110,7 +157,7 @@ Changes in version 2.5.6:
* File names in context patches may now contain spaces, so long
as the context patch headers use a tab to separate the file name
from the time stamp.
from the timestamp.
* Perforce is now supported.
* Patch lines beginning with "#" are comments and are ignored.
@ -317,22 +364,19 @@ Changes in version 2.0.12g8:
Copyright (C) 1992-1993, 1997-2003, 2006, 2009, 2011-2012 Free Software
Foundation, Inc.
Copyright 1992-2025 Free Software Foundation, Inc.
This file is part of GNU Patch.
This program is free software; you can redistribute it and/or modify
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 2, or (at your option)
any later version.
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 they will be useful,
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; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
along with this program. If not, see <http://www.gnu.org/licenses/>.

13
README
View File

@ -16,7 +16,7 @@ README-hacking file. If this file came to you as part of a tar archive,
then see the file INSTALL for compilation and installation instructions.
See the file NEWS for a list of major changes in the current release.
A more detailed descripiton of all changes can be found in the file ChangeLog
A more detailed description of all changes can be found in the file ChangeLog
in tar archives, and with "git log" which shows the version control history.
Tutorial-style documentation for patch is included in the GNU
@ -41,12 +41,15 @@ too busy working on other things, like Perl. He has graciously agreed
to let GNU `patch' be distributed under the terms of the GNU General
Public License.
For any copyright year range specified as YYYY-ZZZZ in this package
note that the range specifies every single year in that closed interval.
Please see the file COPYING for copying conditions.
------
Copyright (C) 1984, 1985, 1986, 1987, 1988 Larry Wall
Copyright (C) 1989-1993, 1997, 1999, 2002, 2009, 2011-2012 Free Software
Foundation, Inc.
Copyright 1989-2025 Free Software Foundation, Inc.
Copyright 1984-1988 Larry Wall
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,40 +1,47 @@
-*- outline -*-
Building from a Git repository -*- outline -*-
These notes intend to help people working on the checked-out sources.
These requirements do not apply when building from a distribution tarball.
If this package has a file HACKING, please also read that file for
more detailed contribution guidelines.
* Requirements
We've opted to keep only the highest-level sources in the GIT repository.
This eases our maintenance burden, (fewer merges etc.), but imposes more
We've opted to keep only the highest-level sources in the Git repository.
This eases our maintenance burden (fewer merges etc.), but imposes more
requirements on anyone wishing to build from the just-checked-out sources.
For example, you have to use the latest stable versions of the maintainer
tools we depend upon, including:
- Automake <http://www.gnu.org/software/automake/>
- Autoconf <http://www.gnu.org/software/autoconf/>
- Gettext <http://www.gnu.org/software/gettext/>
- Gzip <http://www.gnu.org/software/gzip/>
- M4 <http://www.gnu.org/software/m4/>
- Tar <http://www.gnu.org/software/tar/>
- Wget <http://www.gnu.org/software/wget/>
(The requirements to build from a release are much less and are just
the requirements of the standard './configure && make' procedure.)
Specific development tools and versions will be checked for and listed by
the bootstrap script. See README-prereq for specific notes on obtaining
these prerequisite tools.
Valgrind <http://valgrind.org/> is also highly recommended, if
Valgrind supports your architecture.
Valgrind supports your architecture. See also README-valgrind
(if present).
While building from a just-cloned source tree may require installing a
few prerequisites, later, a plain `git pull && make' should be sufficient.
few prerequisites, later, a plain 'git pull && make' typically suffices.
* First GIT checkout
* First Git checkout
You can get a copy of the source repository like this:
$ git clone git://git.sv.gnu.org/patch
$ cd patch
$ git clone git://git.sv.gnu.org/<packagename>
$ cd <packagename>
As an optional step, if you already have a copy of the gnulib git
repository on your hard drive, then you can use it as a reference to
reduce download time and disk space requirements:
where '<packagename>' stands for 'coreutils' or whatever other package
you are building.
To use the most-recent Gnulib (as opposed to the Gnulib version that
the package last synchronized to), do this next:
$ git submodule foreach git pull origin master
$ git commit -m 'build: update gnulib submodule to latest' gnulib
As an optional step, if you already have a copy of the Gnulib Git
repository, then you can use it as a reference to reduce download
time and file system space requirements:
$ export GNULIB_SRCDIR=/path/to/gnulib
@ -43,20 +50,14 @@ which are extracted from other source packages:
$ ./bootstrap
To use the most-recent gnulib (as opposed to the gnulib version that
the package last synchronized to), do this next:
$ git submodule foreach git pull origin master
$ git commit -m 'build: update gnulib submodule to latest' gnulib
And there you are! Just
$ ./configure --quiet
$ ./configure --quiet #[--disable-gcc-warnings] [*]
$ make
$ make check
At this point, there should be no difference between your local copy,
and the GIT master copy:
and the Git master copy:
$ git diff
@ -64,22 +65,34 @@ should output no difference.
Enjoy!
[*] By default GCC warnings are enabled when building from Git.
If you get warnings with recent GCC and Glibc with default
configure-time options, please report the warnings to the bug
reporting address of this package instead of to bug-gnulib,
even if the problem seems to originate in a Gnulib-provided file.
If you get warnings with other configurations, you can run
'./configure --disable-gcc-warnings' or 'make WERROR_CFLAGS='
to build quietly or verbosely, respectively.
-----
* Submitting patches
If you develop a fix or a new feature, please send it to the
appropriate bug-reporting address as reported by the --help option of
each program. One way to do this is to use vc-dwim
<http://www.gnu.org/software/vc-dwim/>), as follows.
<https://www.gnu.org/software/vc-dwim/>), as follows.
Run the command "vc-dwim --help", copy its definition of the
"git-changelog-symlink-init" function into your shell, and then run
this function at the top-level directory of the package.
Run the command "vc-dwim --initialize" from the top-level directory
of this package's git-cloned hierarchy.
Edit the ChangeLog file that this command creates, creating a
Edit the (empty) ChangeLog file that this command creates, creating a
properly-formatted entry according to the GNU coding standards
<http://www.gnu.org/prep/standards/html_node/Change-Logs.html>.
<https://www.gnu.org/prep/standards/html_node/Change-Logs.html>.
Run the command "vc-dwim" and make sure its output looks good.
Make your changes.
Run the command "vc-dwim" and make sure its output (the diff of all
your changes) looks good.
Run "vc-dwim --commit".
@ -88,7 +101,7 @@ each program. One way to do this is to use vc-dwim
-----
Copyright (C) 2002-2007, 2009-2012 Free Software Foundation, Inc.
Copyright 2002-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
@ -101,4 +114,4 @@ 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/>.
along with this program. If not, see <https://www.gnu.org/licenses/>.

36
README-prereq Normal file
View File

@ -0,0 +1,36 @@
This gives some notes on obtaining the tools required for development.
These tools can be used by the 'bootstrap' and 'configure' scripts,
as well as by 'make'. They include:
- Autoconf <https://www.gnu.org/software/autoconf/>
- Automake <https://www.gnu.org/software/automake/>
- Git <https://git-scm.com/>
- Gzip <https://www.gnu.org/software/gzip/>
- M4 <https://www.gnu.org/software/m4/>
- Make <https://www.gnu.org/software/make/>
- Tar <https://www.gnu.org/software/tar/>
- Texinfo <https://www.gnu.org/software/texinfo/>
- Wget <http://www.gnu.org/software/wget/>
- XZ Utils <https://tukaani.org/xz/>
It is generally better to use official packages for your system.
If a package is not officially available you can build it from source
and install it into a directory that you can then use to build this
package. If some packages are available but are too old, install the
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.it; 72
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.72.tar.gz
gzip -d <autoconf-2.72.tar.gz | tar xf -
cd autoconf-2.72
./configure --prefix=$prefix
make install
Once the prerequisites are installed, you can build this package as
described in README-hacking.

1770
bootstrap

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,10 @@
# Bootstrap configuration.
# Bootstrap configuration. -*- sh -*-
# Copyright (C) 2006-2007, 2009-2012 Free Software Foundation, Inc.
# Copyright 2006-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 of the License, or
# 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,
@ -13,103 +13,92 @@
# 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/>.
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# gnulib modules used by this package.
gnulib_modules="
announce-gen
argmatch
assert-h
attribute
backupfile
clock-time
basename-lgpl
bool
c-ctype
closeout
diffseq
dirname
dup2
errno
errno-h
exitfail
extensions
faccessat
fchownat
fchmodat
fcntl-h
full-write
getdate
filename
fseeko
fstatat
ftello
futimens
getopt-gnu
gettime
gitlog-to-changelog
git-version-gen
gnupload
hash
idx
ignore-value
intprops
inttypes-h
largefile
lchmod
linked-list
lstat
maintainer-makefile
malloc
malloc-gnu
manywarnings
memchr
mempcpy
minmax
mkdir
mkdirat
nullptr
openat
parse-datetime
progname
quotearg
readlink
realloc
rename
rmdir
raise
readlinkat
realloc-posix
renameat
setenv
signal
signal-h
ssize_t
stat-time
stdbool
stdlib
symlink
sys_stat
stdckdint-h
stdlib-h
stpcpy
symlinkat
sys_stat-h
tempname
time
unistd
unlink
test-xfail
unistd-h
unlinkat
update-copyright
utimens
utimensat
verror
xalloc
xlist
xstdopen
year2038-recommended
"
gnulib_tool_option_extras='--symlink --makefile-name=gnulib.mk'
# Build prerequisites
buildreq="\
autoconf 2.59
automake 1.9.6
git 1.5.5
tar -
"
# # Additional xgettext options to use. Use "\\\newline" to break lines.
# XGETTEXT_OPTIONS=$XGETTEXT_OPTIONS'\\\
# --from-code=UTF-8\\\
# --flag=asprintf:2:c-format --flag=vasprintf:2:c-format\\\
# --flag=asnprintf:3:c-format --flag=vasnprintf:3:c-format\\\
# --flag=wrapf:1:c-format\\\
# '
#
# # If "AM_GNU_GETTEXT(external" or "AM_GNU_GETTEXT([external]"
# # appears in configure.ac, exclude some unnecessary files.
# # Without grep's -E option (not portable enough, pre-configure),
# # the following test is ugly. Also, this depends on the existence
# # of configure.ac, not the obsolescent-named configure.in. But if
# # you're using this infrastructure, you should care about such things.
#
# gettext_external=0
# grep '^[ ]*AM_GNU_GETTEXT(external\>' configure.ac > /dev/null &&
# gettext_external=1
# grep '^[ ]*AM_GNU_GETTEXT(\[external\]' configure.ac > /dev/null &&
# gettext_external=1
#
# if test $gettext_external = 1; then
# # Gettext supplies these files, but we don't need them since
# # we don't have an intl subdirectory.
# excluded_files='
# m4/glibc2.m4
# m4/intdiv0.m4
# m4/lcmessage.m4
# m4/lock.m4
# m4/printf-posix.m4
# m4/size_max.m4
# m4/uintmax_t.m4
# m4/ulonglong.m4
# m4/visibility.m4
# m4/xsize.m4
# '
# fi
# Automake requires that ChangeLog exist.
touch ChangeLog || exit 1
bootstrap_post_import_hook ()
{
# Automake requires that ChangeLog exist.
touch ChangeLog || exit 1
}

13
cfg.mk
View File

@ -1,5 +1,5 @@
# Customize maint.mk -*- makefile -*-
# Copyright (C) 2011-2012 Free Software Foundation, Inc.
# Copyright 2011-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
@ -16,8 +16,17 @@
config_h_header = <(common|config)\.h>
ifeq ($(RELEASE_TYPE),alpha)
news-check-regexp = "Unreleased changes"
else
news-check-regexp = '^Changes in version $(VERSION_REGEXP):'
endif
release-prep-hook =
update-copyright-env = \
UPDATE_COPYRIGHT_USE_INTERVALS=1 \
UPDATE_COPYRIGHT_FORCE=1 \
UPDATE_COPYRIGHT_USE_INTERVALS=2 \
UPDATE_COPYRIGHT_MAX_LINE_LENGTH=79
local-checks-to-skip = \

View File

@ -1,10 +1,9 @@
# Copyright (C) 1993, 1997-1999, 2002-2003, 2006, 2009, 2011-2012 Free Software
# Foundation, Inc.
# Copyright 1993-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# 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 2, or (at your option)
# any later version.
# 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
@ -12,9 +11,7 @@
# 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.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
AC_PREREQ([2.65])
@ -33,116 +30,87 @@ AM_CONDITIONAL([ENABLE_MERGE], [test "$enableval" != no])
AM_CONDITIONAL([ALPHA_VERSION],
[[echo "$PACKAGE_VERSION" | grep -- "-[0-9a-f][0-9a-f]*\\(-dirty\\)\\?$" >/dev/null]])
AM_INIT_AUTOMAKE([1.11.2 -Wall -Werror gnu dist-bzip2 dist-xz color-tests parallel-tests])
AM_INIT_AUTOMAKE([1.11.2 -Wall gnu dist-bzip2 dist-xz color-tests parallel-tests subdir-objects])
AM_SILENT_RULES([yes])
AC_CONFIG_HEADER([config.h:config.hin])
AC_CONFIG_HEADERS([config.h:config.hin])
AC_PROG_CC
AC_PROG_CC_STDC
gl_EARLY
gl_USE_SYSTEM_EXTENSIONS
AC_HEADER_STDC
gl_INIT
AM_PROG_AR
AC_ARG_ENABLE([gcc-warnings],
[AS_HELP_STRING([--enable-gcc-warnings],
[turn on lots of GCC warnings (for developers)])],
[AS_HELP_STRING([--enable-gcc-warnings@<:@=TYPE@:>@],
[control generation of GCC warnings. The TYPE 'no' disables
warnings (default for non-developer builds); 'yes' generates
cheap warnings if available;
'expensive' in addition generates expensive-to-compute warnings
if available.])],
[case $enableval in
yes|no) ;;
no|yes|expensive) ;;
*) AC_MSG_ERROR([bad value $enableval for gcc-warnings option]) ;;
esac
gl_gcc_warnings=$enableval],
[gl_gcc_warnings=no]
)
if test "$gl_gcc_warnings" = yes; then
if test $gl_gcc_warnings != no; then
gl_WARN_ADD([-Werror], [WERROR_CFLAGS])
AC_SUBST([WERROR_CFLAGS])
nw=
ew=
AS_IF([test $gl_gcc_warnings != expensive],
[# -fanalyzer and related options slow GCC considerably.
ew="$ew -fanalyzer -Wno-analyzer-malloc-leak"])
# This, $nw, is the list of warnings we disable.
nw="$nw -Wdeclaration-after-statement" # too useful to forbid
nw="$nw -Waggregate-return" # anachronistic
nw="$nw -Wlong-long" # C90 is anachronistic (lib/gethrxtime.h)
nw="$nw -Wc++-compat" # We don't care about C++ compilers
nw="$nw -Wundef" # Warns on '#if GNULIB_FOO' etc in gnulib
nw="$nw -Wtraditional" # Warns on #elif which we use often
nw="$nw -Wcast-qual" # Too many warnings for now
nw="$nw -Wconversion" # Too many warnings for now
nw="$nw -Wsystem-headers" # Don't let system headers trigger warnings
nw="$nw -Wsign-conversion" # Too many warnings for now
nw="$nw -Wtraditional-conversion" # Too many warnings for now
nw="$nw -Wunreachable-code" # Too many warnings for now
nw="$nw -Wpadded" # Our structs are not padded
nw="$nw -Wredundant-decls" # openat.h declares e.g., mkdirat
nw="$nw -Wlogical-op" # any use of fwrite provokes this
nw="$nw -Wformat-nonliteral" # who.c and pinky.c strftime uses
nw="$nw -Wvla" # warnings in gettext.h
nw="$nw -Wnested-externs" # use of XARGMATCH/verify_function__
nw="$nw -Wswitch-enum" # Too many warnings for now
nw="$nw -Wswitch-default" # Too many warnings for now
nw="$nw -Wstack-protector" # not worth working around
nw="$nw -Wmissing-format-attribute" # not worth working around in patch
nw="$nw -Wsuggest-attribute=format" # warns about util.c
# things to fix soon:
nw="$nw -Wshadow"
nw="$nw -Wstrict-overflow"
nw="$nw -Wunsafe-loop-optimizations"
# nw="$nw -Wfloat-equal" # sort.c, seq.c
# nw="$nw -Wmissing-format-attribute" # copy.c
# nw="$nw -Winline" # system.h's readdir_ignoring_dot_and_dotdot
# ?? -Wstrict-overflow
nw=$ew
nw="$nw -Winline" # It's OK for a compiler to not inline.
# Using -Wstrict-overflow is a pain, but the alternative is worse.
# For an example, see the code that provoked this report:
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=33498
# Code like that still infloops with gcc-4.6.0 and -O2. Scary indeed.
gl_MANYWARN_ALL_GCC([ws])
gl_MANYWARN_COMPLEMENT([ws], [$ws], [$nw])
for w in $ws; do
gl_WARN_ADD([$w])
done
gl_WARN_ADD([-Wno-missing-field-initializers]) # We need this one
gl_WARN_ADD([-Wno-sign-compare]) # Too many warnings for now
gl_WARN_ADD([-Wno-unused-parameter]) # Too many warnings for now
# In spite of excluding -Wlogical-op above, it is enabled, as of
# gcc 4.5.0 20090517, and it provokes warnings in cat.c, dd.c, truncate.c
gl_WARN_ADD([-Wno-logical-op])
gl_WARN_ADD([-fdiagnostics-show-option])
gl_WARN_ADD([-funit-at-a-time])
gl_WARN_ADD([-Wno-format-nonliteral])
AC_SUBST([WARN_CFLAGS])
AC_DEFINE([lint], [1], [Define to 1 if the compiler is checking for lint.])
AC_DEFINE([_FORTIFY_SOURCE], [2],
[enable compile-time and run-time bounds-checking, and some warnings])
AH_VERBATIM([FORTIFY_SOURCE],
[/* Enable compile-time and run-time bounds-checking, and some warnings,
without upsetting glibc 2.15+. */
#if !defined _FORTIFY_SOURCE && defined __OPTIMIZE__ && __OPTIMIZE__
# define _FORTIFY_SOURCE 2
#endif
])
AC_DEFINE([GNULIB_PORTCHECK], [1], [enable some gnulib portability checks])
# We use a slightly smaller set of warning options for lib/.
# We also use a smaller set of warning options for lib/.
# Remove the following and save the result in GNULIB_WARN_CFLAGS.
nw=
nw="$nw -Wuninitialized"
nw="$nw -Wunused-macros"
nw="$nw -Wmissing-prototypes"
nw="$nw -Wold-style-definition"
nw=$ew
gl_MANYWARN_COMPLEMENT([GNULIB_WARN_CFLAGS], [$WARN_CFLAGS], [$nw])
AC_SUBST([GNULIB_WARN_CFLAGS])
# For gnulib-tests, the set is slightly smaller still.
nw=
nw="$nw -Wstrict-prototypes"
gl_MANYWARN_COMPLEMENT([GNULIB_TEST_WARN_CFLAGS],
[$GNULIB_WARN_CFLAGS], [$nw])
AC_SUBST([GNULIB_TEST_WARN_CFLAGS])
fi
# Ensure VLAs are not used.
# Note -Wvla is implicitly added by gl_MANYWARN_ALL_GCC
AC_DEFINE([GNULIB_NO_VLA], [1], [Define to 1 to disable use of VLAs])
AC_TYPE_MODE_T
AC_TYPE_OFF_T
gl_FUNC_XATTR
AC_CHECK_FUNCS(geteuid getuid raise sigaction sigprocmask sigsetmask)
AC_CHECK_FUNCS_ONCE([geteuid getuid sigaction sigfillset])
AC_FUNC_SETMODE_DOS
AC_PATH_PROG([ED], [ed], [ed])

2
gnulib

@ -1 +1 @@
Subproject commit 77b054428b11be9cd76d1e3b0a7cd7cf86053173
Subproject commit 9829f0a93c40492875f67ee738e000611bdcf0e7

View File

@ -1,14 +0,0 @@
## Process this file with automake to produce Makefile.in.
AM_CFLAGS =
AM_CPPFLAGS =
BUILT_SOURCES =
CLEANFILES =
EXTRA_DIST =
MAINTAINERCLEANFILES =
MOSTLYCLEANDIRS =
MOSTLYCLEANFILES =
SUFFIXES =
noinst_LIBRARIES =
include gnulib.mk

250
m4/.gitignore vendored
View File

@ -1,250 +0,0 @@
/00gnulib.m4
/alloca.m4
/argmatch.m4
/backupfile.m4
/bison.m4
/canonicalize.m4
/clock_time.m4
/closedir.m4
/codeset.m4
/configmake.m4
/d-ino.m4
/dirent-safer.m4
/dirent_h.m4
/dirfd.m4
/dirname.m4
/double-slash-root.m4
/dup2.m4
/eealloc.m4
/environ.m4
/errno_h.m4
/error.m4
/exponentd.m4
/extensions.m4
/fcntl-o.m4
/fcntl.m4
/fcntl_h.m4
/float_h.m4
/fstat.m4
/getdtablesize.m4
/getopt.m4
/gettime.m4
/gettimeofday.m4
/glibc21.m4
/gnulib-cache.m4
/gnulib-common.m4
/gnulib-comp.m4
/gnulib-tool.m4
/hash.m4
/include_next.m4
/inline.m4
/intmax_t.m4
/inttypes_h.m4
/largefile.m4
/lchmod.m4
/localcharset.m4
/locale-fr.m4
/locale-ja.m4
/locale-zh.m4
/longlong.m4
/lstat.m4
/malloc.m4
/malloca.m4
/manywarnings.m4
/math_h.m4
/mbrtowc.m4
/mbsinit.m4
/mbstate_t.m4
/memchr.m4
/minmax.m4
/mkdir.m4
/mktime.m4
/mmap-anon.m4
/msvc-inval.m4
/msvc-nothrow.m4
/multiarch.m4
/nocrash.m4
/opendir.m4
/parse-datetime.m4
/pathmax.m4
/printf.m4
/quote.m4
/quotearg.m4
/raise.m4
/readdir.m4
/readlink.m4
/realloc.m4
/rename.m4
/rmdir.m4
/safe-read.m4
/safe-write.m4
/setenv.m4
/signal_h.m4
/size_max.m4
/ssize_t.m4
/stat-time.m4
/stat.m4
/stdarg.m4
/stdbool.m4
/stddef_h.m4
/stdint.m4
/stdint_h.m4
/stdio_h.m4
/stdlib_h.m4
/strerror.m4
/string_h.m4
/strndup.m4
/strnlen.m4
/symlink.m4
/sys_socket_h.m4
/sys_stat_h.m4
/sys_time_h.m4
/tempname.m4
/time_h.m4
/time_r.m4
/timespec.m4
/tm_gmtoff.m4
/unistd-safer.m4
/unistd_h.m4
/unlink.m4
/utimbuf.m4
/utimens.m4
/utimes.m4
/vasnprintf.m4
/vasprintf.m4
/warn-on-use.m4
/warnings.m4
/wchar_h.m4
/wchar_t.m4
/wctype_h.m4
/wint_t.m4
/write.m4
/xalloc.m4
/xsize.m4
/xstrndup.m4
/xvasprintf.m4
00gnulib.m4
alloca.m4
argmatch.m4
backupfile.m4
bison.m4
canonicalize.m4
clock_time.m4
codeset.m4
configmake.m4
d-ino.m4
dirent-safer.m4
dirent_h.m4
dirfd.m4
dirname.m4
dos.m4
double-slash-root.m4
dup2.m4
eealloc.m4
environ.m4
errno_h.m4
error.m4
extensions.m4
fcntl-o.m4
fcntl.m4
fcntl_h.m4
float_h.m4
fseek.m4
fseeko.m4
ftell.m4
ftello.m4
getdate.m4
getdtablesize.m4
getopt.m4
gettime.m4
gettimeofday.m4
glibc21.m4
gnulib-cache.m4
gnulib-common.m4
gnulib-comp.m4
gnulib-tool.m4
hash.m4
include_next.m4
inline.m4
intmax_t.m4
inttypes_h.m4
lchmod.m4
localcharset.m4
locale-fr.m4
locale-ja.m4
locale-zh.m4
longlong.m4
lseek.m4
lstat.m4
malloc.m4
malloca.m4
manywarnings.m4
mbrtowc.m4
mbsinit.m4
mbstate_t.m4
memchr.m4
minmax.m4
mkdir.m4
mktime.m4
mmap-anon.m4
multiarch.m4
onceonly.m4
parse-datetime.m4
pathmax.m4
printf.m4
quote.m4
quotearg.m4
readlink.m4
realloc.m4
rename.m4
rmdir.m4
safe-read.m4
safe-write.m4
setenv.m4
size_max.m4
ssize_t.m4
stat-time.m4
stat.m4
stdarg.m4
stdbool.m4
stddef_h.m4
stdint.m4
stdint_h.m4
stdio_h.m4
stdlib_h.m4
strerror.m4
string_h.m4
strndup.m4
strnlen.m4
symlink.m4
sys_stat_h.m4
sys_time_h.m4
tempname.m4
time_h.m4
time_r.m4
timespec.m4
tm_gmtoff.m4
unistd-safer.m4
unistd_h.m4
unlink.m4
utimbuf.m4
utimens.m4
utimes.m4
vasnprintf.m4
vasprintf.m4
warn-on-use.m4
warnings.m4
wchar_h.m4
wchar_t.m4
wctype_h.m4
wint_t.m4
write.m4
xalloc.m4
xsize.m4
xstrndup.m4
xvasprintf.m4
/close.m4
/gl_list.m4
/off_t.m4
/sys_types_h.m4
/extern-inline.m4

View File

@ -1,35 +1,33 @@
# Check for setmode, DOS style.
# Copyright (C) 2001-2002, 2011-2012 Free Software Foundation, Inc.
# Copyright 2001-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# 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 2, or (at your option)
# any later version.
# 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, write to the Free Software
# Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
# USA.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
AC_DEFUN([AC_FUNC_SETMODE_DOS],
[AC_CHECK_HEADERS([fcntl.h unistd.h])
[AC_CHECK_HEADERS_ONCE([fcntl.h unistd.h])
AC_CACHE_CHECK([for DOS-style setmode],
[ac_cv_func_setmode_dos],
[AC_TRY_LINK(
[#include <io.h>
[AC_LINK_IFELSE([AC_LANG_PROGRAM(
[[#include <io.h>
#if HAVE_FCNTL_H
# include <fcntl.h>
#endif
#if HAVE_UNISTD_H
# include <unistd.h>
#endif],
[int ret = setmode && setmode (1, O_BINARY);],
#endif]],
[int ret = setmode && setmode (1, O_BINARY);])],
[ac_cv_func_setmode_dos=yes],
[ac_cv_func_setmode_dos=no])])
if test $ac_cv_func_setmode_dos = yes; then

View File

@ -1,7 +1,7 @@
# xattr.m4 - check for Extended Attributes (Linux)
# serial 3
# serial 4
# Copyright (C) 2003, 2008-2012 Free Software Foundation, Inc.
# Copyright 2003-2025 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@ -12,7 +12,7 @@
AC_DEFUN([gl_FUNC_XATTR],
[
AC_ARG_ENABLE([xattr],
AC_HELP_STRING([--disable-xattr],
AS_HELP_STRING([--disable-xattr],
[do not support extended attributes]),
[use_xattr=$enableval], [use_xattr=yes])

299
patch.man
View File

@ -1,14 +1,13 @@
.\" patch man page
.de Id
.ds Dt \\$4
..
.ds = \-\^\-
.de Sp
.if t .sp .3
.if n .sp
..
.TH PATCH 1 \*(Dt GNU
.ta 3n
.ds Cw CW
.if \n(.g .if f CR .ds Cw CR
.ds fC \f(\*(Cw
.TH PATCH 1 "" GNU
.SH NAME
patch \- apply a diff file to an original
.SH SYNOPSIS
@ -60,7 +59,7 @@ editor via a pipe.
.B patch
tries to skip any leading garbage, apply the diff,
and then skip any trailing garbage.
Thus you could feed an article or message containing a
Thus you could feed an email message containing a
diff listing to
.BR patch ,
and it should work.
@ -149,8 +148,8 @@ to edit is, using the following rules.
First,
.B patch
takes an ordered list of candidate file names as follows:
.TP 3
.B " \(bu"
.RS "\w' 'u"
.IP \(bu "\w'\(bu 'u"
If the header is that of a context diff,
.B patch
takes the old and new file names in the header.
@ -162,8 +161,7 @@ option.
The name
.B /dev/null
is also ignored.
.TP
.B " \(bu"
.IP \(bu
If there is an
.B Index:\&
line in the leading garbage
@ -174,23 +172,22 @@ is conforming to \s-1POSIX\s0,
takes the name in the
.B Index:\&
line.
.TP
.B " \(bu"
.IP \(bu
For the purpose of the following rules,
the candidate file names are considered to be in the order (old, new, index),
regardless of the order that they appear in the header.
.RE
.LP
Then
.B patch
selects a file name from the candidate list as follows:
.TP 3
.B " \(bu"
.RS "\w' 'u"
.IP \(bu "\w'\(bu 'u"
If some of the named files exist,
.B patch
selects the first name if conforming to \s-1POSIX\s0,
and the best name otherwise.
.TP
.B " \(bu"
.IP \(bu
If
.B patch
is not ignoring \s-1RCS\s0, ClearCase, Perforce, and \s-1SCCS\s0 (see the
@ -202,8 +199,7 @@ but an \s-1RCS\s0, ClearCase, Perforce, or \s-1SCCS\s0 master is found,
.B patch
selects the first named file
with an \s-1RCS\s0, ClearCase, Perforce, or \s-1SCCS\s0 master.
.TP
.B " \(bu"
.IP \(bu
If no named files exist,
no \s-1RCS\s0, ClearCase, Perforce, or \s-1SCCS\s0 master was found,
some names are given,
@ -212,12 +208,12 @@ is not conforming to \s-1POSIX\s0,
and the patch appears to create a file,
.B patch
selects the best name requiring the creation of the fewest directories.
.TP
.B " \(bu"
.IP \(bu
If no file name results from the above heuristics, you are asked
for the name of the file to patch, and
.B patch
selects that name.
.RE
.LP
To determine the
.I best
@ -238,15 +234,16 @@ If not,
.B patch
asks for confirmation before proceeding.
.PP
The upshot of all this is that you should be able to say, while in a news
interface, something like the following:
The upshot of all this is that you should be able to run
something like the following shell command:
.Sp
\fB| patch \-d /usr/src/local/blurfl\fP
.RS
\fBpatch \-d /usr/src/local/blurfl\fP
.RE
.Sp
and patch a file in the
.B blurfl
directory directly from the article containing
the patch.
directory directly from a patch that is read from standard input.
.PP
If the patch file contains more than one patch,
.B patch
@ -262,9 +259,6 @@ mentioned previously.
Make backup files.
That is, when patching a file,
rename or copy the original instead of removing it.
When backing up a file that does not exist,
an empty, unreadable backup file is created
as a placeholder to represent the nonexistent file.
See the
.B \-V
or
@ -342,7 +336,7 @@ script.
Remove output files that are empty after the patches have been applied.
Normally this option is unnecessary, since
.B patch
can examine the time stamps on the header to determine whether a file
can examine the timestamps on the header to determine whether a file
should exist after patching.
However, if the input is not a context diff or if
.B patch
@ -423,21 +417,22 @@ A typical conflict will look like this:
.LP
.RS
.nf
.B <<<<<<<
.I lines from the original file
.B |||||||
.I original lines from the patch
.B =======
.I new lines from the patch
.B >>>>>>>
.ne 7
\*(fC<<<<<<<\fP
\fIlines from the original file\fP
\*(fC|||||||\fP
\fIoriginal lines from the patch\fP
\*(fC=======\fP
\fInew lines from the patch\fP
\*(fC>>>>>>>\fP
.RE
.fi
.IP "" 3
The optional argument of \fB\*=merge\fP determines the output format for
conflicts: the diff3 format shows the \fB|||||||\fP section with the original
conflicts: the diff3 format shows the \*(fC|||||||\fP section with the original
lines from the patch; in the merge format, this section is missing. The merge
format is the default.
.Sp
This option implies \fB\*=forward\fP and does not take the
\fB--fuzz\fR=\fInum\fP option into account.
.TP
@ -445,9 +440,9 @@ This option implies \fB\*=forward\fP and does not take the
Interpret the patch file as a normal diff.
.TP
\fB\-N\fP or \fB\*=forward\fP
Ignore patches that seem to be reversed or already applied. It is only checked if
the first hunk of a patch can be reversed.
See also
When a patch does not apply, patch usually checks if the patch looks like it
has been applied already by trying to reverse-apply the first hunk. The
\fB\*=forward\fP option prevents that. See also
.BR \-R .
.TP
\fB\-o\fP \fIoutfile\fP or \fB\*=output=\fP\fIoutfile\fP
@ -470,7 +465,9 @@ you keep your files in a different directory than the person who sent
out the patch.
For example, supposing the file name in the patch file was
.Sp
\fB/u/howard/src/blurfl/blurfl.c\fP
.RS
\fB/u/howard/src/blurfl/blurfl.c\fP
.RE
.Sp
setting
.B \-p0
@ -478,13 +475,17 @@ gives the entire file name unmodified,
.B \-p1
gives
.Sp
\fBu/howard/src/blurfl/blurfl.c\fP
.RS
\fBu/howard/src/blurfl/blurfl.c\fP
.RE
.Sp
without the leading slash,
.B \-p4
gives
.Sp
\fBblurfl/blurfl.c\fP
.RS
\fBblurfl/blurfl.c\fP
.RE
.Sp
and not specifying
.B \-p
@ -496,23 +497,18 @@ option.
.TP
.B \*=posix
Conform more strictly to the \s-1POSIX\s0 standard, as follows.
.RS
.TP 3
.B " \(bu"
.RS "\w' 'u"
.IP \(bu "\w'\(bu 'u"
Take the first existing file from the list (old, new, index)
when intuiting file names from diff headers.
.TP
.B " \(bu"
.IP \(bu
Do not remove files that are empty after patching.
.TP
.B " \(bu"
.IP \(bu
Do not ask whether to get files from \s-1RCS\s0, ClearCase, Perforce,
or \s-1SCCS\s0.
.TP
.B " \(bu"
.IP \(bu
Require that all options precede the files in the command line.
.TP
.B " \(bu"
.IP \(bu
Do not backup files when there is a mismatch.
.RE
.TP
@ -619,15 +615,15 @@ in the patch; and assume that patches are reversed if they look like
they are.
.TP
\fB\-T\fP or \fB\*=set\-time\fP
Set the modification and access times of patched files from time stamps
given in context diff headers. Unless specified in the time stamps,
Set the modification and access times of patched files from timestamps
given in context diff headers. Unless specified in the timestamps,
assume that the context diff headers use local time.
.Sp
Use of this option with time stamps that do not include time zones is
Use of this option with timestamps that do not include time zones is
not recommended, because patches using local time cannot easily be used
by people in other time zones, and because local time stamps are
by people in other time zones, and because local timestamps are
ambiguous when local clocks move backwards during daylight-saving time
adjustments. Make sure that time stamps include time zones, or generate
adjustments. Make sure that timestamps include time zones, or generate
patches with \s-1UTC\s0 and use the
.B \-Z
or
@ -657,7 +653,7 @@ it affects only the names of any backup files that are made.
The value of
.I method
is like the \s-1GNU\s0
Emacs `version-control' variable;
Emacs version-control variable;
.B patch
also recognizes synonyms that
are more descriptive. The valid values for
@ -754,8 +750,8 @@ is
.BR src/patch/util.c- .
.TP
\fB\-Z\fP or \fB\*=set\-utc\fP
Set the modification and access times of patched files from time stamps
given in context diff headers. Unless specified in the time stamps,
Set the modification and access times of patched files from timestamps
given in context diff headers. Unless specified in the timestamps,
assume that the context diff headers use Coordinated Universal Time
(\s-1UTC\s0, often known as \s-1GMT\s0). Also see the
.B \-T
@ -847,13 +843,16 @@ controlling terminal; used to get answers to questions asked of the user
.Sp
Marshall T. Rose and Einar A. Stefferud,
Proposed Standard for Message Encapsulation,
Internet RFC 934 <URL:ftp://ftp.isi.edu/in-notes/rfc934.txt> (1985-01).
Internet RFC 934 <https://datatracker.ietf.org/doc/html/rfc934> (1985-01).
.SH "NOTES FOR PATCH SENDERS"
There are several things you should bear in mind if you are going to
be sending out patches.
.PP
Create your patch systematically.
A good method is the command
When using a version control system this should be easy;
for example, with Git you can use
.BR "git diff" .
Otherwise, a good method is the command
.BI "diff\ \-Naur\ " "old\ new"
where
.I old
@ -865,18 +864,21 @@ The names
and
.I new
should not contain any slashes.
The
.PP
If the patch should communicate file timestamps as well as file contents, its
.B diff
command's headers should have dates
commands' headers should have dates
and times in Universal Time using traditional Unix format,
so that patch recipients can use the
.B \-Z
or
.B \*=set\-utc
option.
Here is an example command, using Bourne shell syntax:
Here is an example command to generate such headers, using Bourne shell syntax:
.Sp
\fBLC_ALL=C TZ=UTC0 diff \-Naur gcc\-2.7 gcc\-2.8\fP
.RS
\fBLC_ALL=C TZ=UTC0 diff \-Naur myprog\-2.7 myprog\-2.8\fP
.RE
.PP
Tell your recipients how to apply the patch
by telling them which directory to
@ -925,14 +927,16 @@ If the recipient is supposed to use the
.BI \-p N
option, do not send output that looks like this:
.Sp
.ft B
.RS
.ft \*(Cw
.ne 3
diff \-Naur v2.0.29/prog/README prog/README
diff \-Naur v2.0.29/prog/README prog/README
.br
\-\^\-\^\- v2.0.29/prog/README Mon Mar 10 15:13:12 1997
\-\^\-\^\- v2.0.29/prog/README Mon Mar 10 15:13:12 2024
.br
+\^+\^+ prog/README Mon Mar 17 14:58:22 1997
+\^+\^+ prog/README Mon Mar 17 14:58:22 2024
.ft
.RE
.Sp
because the two file names have different numbers of slashes,
and different versions of
@ -940,14 +944,16 @@ and different versions of
interpret the file names differently.
To avoid confusion, send output that looks like this instead:
.Sp
.ft B
.RS
.ft \*(Cw
.ne 3
diff \-Naur v2.0.29/prog/README v2.0.30/prog/README
diff \-Naur v2.0.29/prog/README v2.0.30/prog/README
.br
\-\^\-\^\- v2.0.29/prog/README Mon Mar 10 15:13:12 1997
\-\^\-\^\- v2.0.29/prog/README Mon Mar 10 15:13:12 2024
.br
+\^+\^+ v2.0.30/prog/README Mon Mar 17 14:58:22 1997
+\^+\^+ v2.0.30/prog/README Mon Mar 17 14:58:22 2024
.ft
.RE
.Sp
.PP
Avoid sending patches that compare backup file names like
@ -968,7 +974,7 @@ Try not to have your patch modify derived files
(e.g. the file
.B configure
where there is a line
.B "configure: configure.in"
.B "configure: configure.ac"
in your makefile), since the recipient should be
able to regenerate the derived files anyway.
If you must send diffs of derived files,
@ -1033,57 +1039,15 @@ applied to exactly the same version of the file that the patch was
generated from.
.SH "COMPATIBILITY ISSUES"
The \s-1POSIX\s0 standard specifies behavior that differs from
.BR patch 's
traditional behavior.
You should be aware of these differences if you must interoperate with
GNU
.BR patch .
.RS "\w' 'u"
.IP \(bu "\w'\(bu 'u"
In \s-1POSIX\s0
.B patch
versions 2.1 and earlier, which do not conform to \s-1POSIX\s0.
.TP 3
.B " \(bu"
In traditional
.BR patch ,
the
.B \-p
option's operand was optional, and a bare
.B \-p
was equivalent to
.BR \-p0.
The
.B \-p
option now requires an operand, and
.B "\-p\ 0"
is now equivalent to
.BR \-p0 .
For maximum compatibility, use options like
.B \-p0
and
.BR \-p1 .
.Sp
Also,
traditional
.B patch
simply counted slashes when stripping path prefixes;
.B patch
now counts pathname components.
That is, a sequence of one or more adjacent slashes
now counts as a single slash.
For maximum portability, avoid sending patches containing
.B //
in file names.
.TP
.B " \(bu"
In traditional
.BR patch ,
backups were enabled by default.
This behavior is now enabled with the
when
.B \-b
or
.B \*=backup
option.
.Sp
Conversely, in \s-1POSIX\s0
.BR patch ,
backups are never made, even when there is a mismatch.
is not used, backups are not made even when there is a mismatch.
In \s-1GNU\s0
.BR patch ,
this behavior is enabled with the
@ -1093,88 +1057,46 @@ option, or by conforming to \s-1POSIX\s0 with the
option or by setting the
.B POSIXLY_CORRECT
environment variable.
.Sp
The
.BI \-b "\ suffix"
option
of traditional
.IP \(bu
When intuiting the name of the file to be patched from the patch header,
.B patch
is equivalent to the
.BI "\-b\ \-z" "\ suffix"
options of \s-1GNU\s0
.BR patch .
.TP
.B " \(bu"
Traditional
.B patch
used a complicated (and incompletely documented) method
to intuit the name of the file to be patched from the patch header.
This method did not conform to \s-1POSIX\s0, and had a few gotchas.
Now
.B patch
uses a different, equally complicated (but better documented) method
that is optionally \s-1POSIX\s0-conforming; we hope it has
fewer gotchas. The two methods are compatible if the
uses a complicated method
that is optionally \s-1POSIX\s0-conforming.
The method is equivalent to \s-1POSIX\s0 if the
file names in the context diff header and the
.B Index:\&
line are all identical after prefix-stripping.
Your patch is normally compatible if each header's file names
all contain the same number of slashes.
.TP
.B " \(bu"
When traditional
.B patch
asked the user a question, it sent the question to standard error
and looked for an answer from
the first file in the following list that was a terminal:
standard error, standard output,
.BR /dev/tty ,
and standard input.
Now
.B patch
sends questions to standard output and gets answers from
.BR /dev/tty .
Defaults for some answers have been changed so that
.B patch
never goes into an infinite loop when using default answers.
.TP
.B " \(bu"
Traditional
.B patch
exited with a status value that counted the number of bad hunks,
or with status 1 if there was real trouble.
Now
.B patch
exits with status 1 if some hunks failed,
or with 2 if there was real trouble.
.TP
.B " \(bu"
.IP \(bu
Limit yourself to the following options when sending instructions
meant to be executed by anyone running \s-1GNU\s0
.BR patch ,
traditional
.BR patch ,
.B patch
or a
.B patch
that conforms to \s-1POSIX\s0.
Spaces are significant in the following list, and operands are required.
Spaces are optional in the following list.
.Sp
.nf
.in +3
.ne 11
.B \-b
.B \-c
.BI \-d " dir"
.BI \-D " define"
.B \-e
.BI \-i " patchfile"
.B \-l
.B \-n
.B \-N
.BI \-o " outfile"
.BI \-p num
.BI \-p " num"
.B \-R
.BI \-r " rejectfile"
.BI \-u
.in
.fi
.RE
.SH BUGS
Please report bugs via email to
.BR <bug-patch@gnu.org> .
@ -1194,16 +1116,9 @@ Computing how to merge a hunk is significantly harder than using the standard
fuzzy algorithm. Bigger hunks, more context, a bigger offset from the
original location, and a worse match all slow the algorithm down.
.SH COPYING
Copyright
.ie t \(co
.el (C)
1984, 1985, 1986, 1988 Larry Wall.
Copyright \(co 1989\(en2025 Free Software Foundation, Inc.
.br
Copyright
.ie t \(co
.el (C)
1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
2000, 2001, 2002, 2009 Free Software Foundation, Inc.
Copyright \(co 1984\(en1986, 1988 Larry Wall.
.PP
Permission is granted to make and distribute verbatim copies of
this manual provided the copyright notice and this permission notice
@ -1229,4 +1144,4 @@ setting file times, and deleting files;
and made it conform better to \s-1POSIX\s0.
Other contributors include Wayne Davison, who added unidiff support,
and David MacKenzie, who added configuration and backup support.
Andreas Gr\[:u]nbacher added support for merging.
Andreas Gruenbacher added support for merging.

View File

@ -1,5 +1,4 @@
# Copyright (C) 1989-1993, 1997-1999, 2002-2003, 2006, 2009-2012 Free Software
# Foundation, Inc.
# Copyright 1989-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
@ -27,14 +26,19 @@ patch_SOURCES = \
patch.c \
pch.c \
pch.h \
safe.c \
safe.h \
util.c \
util.h \
version.c \
version.h
version.h \
list.h
AM_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib
patch_LDADD = $(LDADD) $(top_builddir)/lib/libpatch.a $(LIB_CLOCK_GETTIME) \
$(LIB_XATTR)
patch_LDADD = $(LDADD) $(top_builddir)/lib/libpatch.a \
$(CLOCK_TIME_LIB) $(EUIDACCESS_LIBGEN) $(GETRANDOM_LIB) \
$(HARD_LOCALE_LIB) $(LIBINTL) $(MBRTOWC_LIB) $(SETLOCALE_NULL_LIB) \
$(LIB_XATTR)
if ENABLE_MERGE
patch_SOURCES += merge.c

View File

@ -1,6 +1,6 @@
/* Find a best match between two vectors.
Copyright (C) 2009-2012 Free Software Foundation, Inc.
Copyright 2009-2025 Free Software Foundation, Inc.
Written by Andreas Gruenbacher <agruen@gnu.org>, 2009.
This program is free software: you can redistribute it and/or modify
@ -64,7 +64,7 @@ bestmatch(OFFSET xoff, OFFSET xlim, OFFSET yoff, OFFSET ylim,
OFFSET fmid_plus_2_min, ymax = -1;
OFFSET c;
V = malloc ((2 * max + 3) * sizeof (OFFSET));
V = xmalloc ((2 * max + 3) * sizeof (OFFSET));
fd = V + max + 1 - fmid;
/*

View File

@ -1,9 +1,7 @@
/* common definitions for 'patch' */
/* Copyright (C) 1986, 1988 Larry Wall
Copyright (C) 1990-1993, 1997-1999, 2002-2003, 2006, 2009-2012 Free Software
Foundation, Inc.
/* Copyright 1990-2025 Free Software Foundation, Inc.
Copyright 1986, 1988 Larry Wall
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
@ -24,39 +22,26 @@
#include <config.h>
#include <attribute.h>
#include <c-ctype.h>
#include <idx.h>
#include <intprops.h>
#include <progname.h>
#include <minmax.h>
#include <assert.h>
#include <stdbool.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <stdckdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <sys/stat.h>
#include <limits.h>
#if HAVE_INTTYPES_H
# include <inttypes.h>
#elif HAVE_STDINT_H
# include <stdint.h>
#endif
#include <ctype.h>
/* CTYPE_DOMAIN (C) is nonzero if the unsigned char C can safely be given
as an argument to <ctype.h> macros like 'isspace'. */
#if STDC_HEADERS
#define CTYPE_DOMAIN(c) 1
#else
#define CTYPE_DOMAIN(c) ((unsigned) (c) <= 0177)
#endif
#ifndef ISSPACE
#define ISSPACE(c) (CTYPE_DOMAIN (c) && isspace (c))
#endif
#ifndef ISDIGIT
#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
#endif
#include <progname.h>
#include <unistd.h>
/* handy definitions */
@ -66,52 +51,60 @@
/* typedefs */
typedef off_t lin; /* must be signed */
/* A description of an output file. It may be temporary. */
struct outfile
{
/* Name of the file. */
char *name;
/* Equal to NAME if the file exists; otherwise a null pointer.
When non-null, the storage it points to is safe to access in
a signal handler. */
char volatile *volatile exists;
/* Equal to NAME if NAME should be freed when this structure is
freed or reused; otherwise, a null pointer. */
char *alloc;
/* Whether the file is intended to be temporary, and therefore
should be cleaned up before exit, if it exists. */
bool temporary;
};
/* globals */
XTERN char *buf; /* general purpose buffer */
XTERN size_t bufsize; /* allocated size of buf */
extern char *inname;
extern char *outfile;
extern int inerrno;
extern signed char invc;
extern struct stat instat;
extern bool dry_run;
extern bool posixly_correct;
XTERN bool using_plan_a; /* try to keep everything in memory */
extern char const *origprae;
extern char const *origbase;
extern char const *origsuff;
XTERN char *inname;
XTERN char *outfile;
XTERN int inerrno;
XTERN int invc;
XTERN struct stat instat;
XTERN bool dry_run;
XTERN bool posixly_correct;
extern struct outfile tmped;
extern struct outfile tmppat;
XTERN char const *origprae;
XTERN char const *origbase;
XTERN char const *origsuff;
XTERN char const * TMPINNAME;
XTERN char const * TMPOUTNAME;
XTERN char const * TMPPATNAME;
XTERN bool TMPINNAME_needs_removal;
XTERN bool TMPOUTNAME_needs_removal;
XTERN bool TMPPATNAME_needs_removal;
#ifdef DEBUGGING
XTERN int debug;
#if DEBUGGING
extern unsigned short int debug;
#else
# define debug 0
#endif
XTERN bool force;
XTERN bool batch;
XTERN bool noreverse;
XTERN bool reverse;
XTERN enum { DEFAULT_VERBOSITY, SILENT, VERBOSE } verbosity;
XTERN bool skip_rest_of_patch;
XTERN int strippath;
XTERN bool canonicalize;
XTERN int patch_get;
XTERN bool set_time;
XTERN bool set_utc;
XTERN bool follow_symlinks;
extern bool force;
extern bool batch;
extern bool noreverse_flag;
extern bool reverse_flag;
extern enum verbosity { DEFAULT_VERBOSITY, SILENT, VERBOSE } verbosity;
extern bool skip_rest_of_patch;
extern intmax_t strippath;
extern bool canonicalize_ws;
extern intmax_t patch_get;
extern bool set_time;
extern bool set_utc;
extern bool follow_symlinks;
enum diff
{
@ -124,42 +117,12 @@ enum diff
GIT_BINARY_DIFF
};
XTERN enum diff diff_type;
extern enum diff diff_type;
XTERN char *revision; /* prerequisite revision, if any */
extern char *revision; /* prerequisite revision, if any */
#ifndef __attribute__
/* The __attribute__ feature is available in gcc versions 2.5 and later.
The __-protected variants of the attributes 'format' and 'printf' are
accepted by gcc versions 2.6.4 (effectively 2.7) and later.
We enable __attribute__ only if these are supported too, because
gnulib and libintl do '#define printf __printf__' when they override
the 'printf' function. */
# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
# define __attribute__(Spec) /* empty */
# endif
#endif
void fatal_exit (int) __attribute__ ((noreturn));
#include <errno.h>
#if !STDC_HEADERS && !defined errno
extern int errno;
#endif
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#if HAVE_FSEEKO
typedef off_t file_offset;
# define file_seek fseeko
# define file_tell ftello
#else
typedef long file_offset;
# define file_seek fseek
# define file_tell ftell
#endif
void fatal_cleanup (void);
_Noreturn void fatal_exit (void);
#if ! (HAVE_GETEUID || defined geteuid)
# if ! (HAVE_GETUID || defined getuid)
@ -169,16 +132,14 @@ extern int errno;
# endif
#endif
#include <fcntl.h>
#ifdef HAVE_SETMODE_DOS
XTERN int binary_transput; /* O_BINARY if binary i/o is desired */
extern int binary_transput; /* O_BINARY if binary i/o is desired */
#else
# define binary_transput 0
#endif
/* Disable the CR stripping heuristic? */
XTERN bool no_strip_trailing_cr;
extern bool no_strip_trailing_cr;
#ifndef NULL_DEVICE
#define NULL_DEVICE "/dev/null"
@ -197,20 +158,20 @@ struct outstate
};
/* offset in the input and output at which the previous hunk matched */
XTERN lin in_offset;
XTERN lin out_offset;
extern ptrdiff_t in_offset;
extern ptrdiff_t out_offset;
/* how many input lines have been irretractably output */
XTERN lin last_frozen_line;
extern idx_t last_frozen_line;
bool copy_till (struct outstate *, lin);
bool similar (char const *, size_t, char const *, size_t) _GL_ATTRIBUTE_PURE;
bool copy_till (struct outstate *, idx_t);
bool similar (char const *, idx_t, char const *, idx_t) ATTRIBUTE_PURE;
#ifdef ENABLE_MERGE
enum conflict_style { MERGE_MERGE, MERGE_DIFF3 };
XTERN enum conflict_style conflict_style;
extern enum conflict_style conflict_style;
bool merge_hunk (int hunk, struct outstate *, lin where, bool *);
bool merge_hunk (intmax_t hunk, struct outstate *, idx_t where, bool *);
#else
# define merge_hunk(hunk, outstate, where, somefailed) false
#endif

321
src/inp.c
View File

@ -1,8 +1,7 @@
/* inputting files to be patched */
/* Copyright (C) 1986, 1988 Larry Wall
Copyright (C) 1991-1993, 1997-1999, 2002-2003, 2006, 2009-2012 Free Software
Foundation, Inc.
/* Copyright 1991-2025 Free Software Foundation, Inc.
Copyright 1986, 1988 Larry Wall
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
@ -17,78 +16,34 @@
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 XTERN extern
#include <common.h>
#include <quotearg.h>
#include <util.h>
#include <xalloc.h>
#undef XTERN
#define XTERN
#include <inp.h>
#include <safe.h>
/* Input-file-with-indexable-lines abstract type */
static char *i_buffer; /* plan A buffer */
static char const **i_ptr; /* pointers to lines in plan A buffer */
static char *i_buffer; /* buffer of input file lines */
static char const **i_ptr; /* pointers to lines in buffer */
idx_t input_lines; /* how long is input file in lines */
static size_t tibufsize; /* size of plan b buffers */
#ifndef TIBUFSIZE_MINIMUM
#define TIBUFSIZE_MINIMUM (8 * 1024) /* minimum value for tibufsize */
#endif
static int tifd = -1; /* plan b virtual string array */
static char *tibuf[2]; /* plan b buffers */
static lin tiline[2] = {-1, -1}; /* 1st line in each buffer */
static lin lines_per_buf; /* how many lines per buffer */
static size_t tireclen; /* length of records in tmp file */
static size_t last_line_size; /* size of last input line */
static bool plan_a (char const *); /* yield false if memory runs out */
static void plan_b (char const *);
static void report_revision (bool);
static void too_many_lines (char const *) __attribute__((noreturn));
/* New patch--prepare to edit another file. */
void
re_input (void)
{
if (using_plan_a) {
if (i_buffer)
{
free (i_buffer);
i_buffer = 0;
free (i_ptr);
}
}
else {
if (tifd >= 0)
close (tifd);
tifd = -1;
if (tibuf[0])
{
free (tibuf[0]);
tibuf[0] = 0;
}
tiline[0] = tiline[1] = -1;
tireclen = 0;
}
}
/* Construct the line index, somehow or other. */
void
scan_input (char *filename, mode_t file_type)
{
using_plan_a = ! (debug & 16) && plan_a (filename);
if (!using_plan_a)
{
if (! S_ISREG (file_type))
{
assert (S_ISLNK (file_type));
fatal ("Can't handle %s %s", "symbolic link", quotearg (filename));
}
plan_b(filename);
}
}
/* Report whether a desired revision was found. */
@ -114,23 +69,16 @@ report_revision (bool found_revision)
rev);
else
{
ask ("This file doesn't appear to be the %s version -- patch anyway? [n] ",
rev);
if (*buf != 'y')
if (*ask (("This file doesn't appear to be the %s version"
" -- patch anyway? [n] "),
rev)
!= 'y')
fatal ("aborted");
}
}
static void
too_many_lines (char const *filename)
{
fatal ("File %s has too many lines", quotearg (filename));
}
bool
get_input_file (char const *filename, char const *outname, mode_t file_type)
get_input_file (char *filename, char const *outname, mode_t file_type)
{
bool elsewhere = strcmp (filename, outname) != 0;
char const *cs;
@ -207,38 +155,29 @@ get_input_file (char const *filename, char const *outname, mode_t file_type)
}
/* Try keeping everything in memory. */
/* Read input and build its line index. */
static bool
plan_a (char const *filename)
void
scan_input (char *filename, mode_t file_type, int ifd)
{
char const *s;
char const *lim;
char const **ptr;
char *buffer;
lin iline;
size_t size = instat.st_size;
/* Fail if the file size doesn't fit in a size_t,
/* Fail if the file size doesn't fit,
or if storage isn't available. */
if (! (size == instat.st_size
&& (buffer = malloc (size ? size : (size_t) 1))))
return false;
idx_t size;
if (ckd_add (&size, instat.st_size, 0))
xalloc_die ();
char *buffer = ximalloc (size);
/* Read the input file, but don't bother reading it if it's empty.
When creating files, the files do not actually exist. */
if (size)
{
if (S_ISREG (instat.st_mode))
if (S_ISREG (file_type))
{
int ifd = open (filename, O_RDONLY|binary_transput);
size_t buffered = 0, n;
if (ifd < 0)
pfatal ("can't open file %s", quotearg (filename));
idx_t buffered = 0;
while (size - buffered != 0)
{
n = read (ifd, buffer + buffered, size - buffered);
ssize_t n = Read (ifd, buffer + buffered, size - buffered);
if (n == 0)
{
/* Some non-POSIX hosts exaggerate st_size in text mode;
@ -246,52 +185,30 @@ plan_a (char const *filename)
size = buffered;
break;
}
if (n == (size_t) -1)
{
/* Perhaps size is too large for this host. */
close (ifd);
free (buffer);
return false;
}
buffered += n;
}
if (close (ifd) != 0)
read_fatal ();
}
else if (S_ISLNK (instat.st_mode))
else
{
ssize_t n;
n = readlink (filename, buffer, size);
ssize_t n = safe_readlink (filename, buffer, size);
if (n < 0)
pfatal ("can't read %s %s", "symbolic link", quotearg (filename));
size = n;
}
else
{
free (buffer);
return false;
}
}
/* Scan the buffer and build array of pointers to lines. */
lim = buffer + size;
iline = 3; /* 1 unused, 1 for SOF, 1 for EOF if last line is incomplete */
for (s = buffer; (s = (char *) memchr (s, '\n', lim - s)); s++)
if (++iline < 0)
too_many_lines (filename);
if (! (iline == (size_t) iline
&& (size_t) iline * sizeof *ptr / sizeof *ptr == (size_t) iline
&& (ptr = (char const **) malloc ((size_t) iline * sizeof *ptr))))
{
free (buffer);
return false;
}
char const *lim = buffer + size;
idx_t iline = 3; /* 1 unused, 1 for SOF,
1 for EOF if last line is incomplete. */
for (char const *s = buffer; (s = memchr (s, '\n', lim - s)); s++)
iline++;
char const **ptr = xireallocarray (nullptr, iline, sizeof *ptr);
iline = 0;
for (s = buffer; ; s++)
for (char const *s = buffer; ; s++)
{
ptr[++iline] = s;
if (! (s = (char *) memchr (s, '\n', lim - s)))
if (! (s = memchr (s, '\n', lim - s)))
break;
}
if (size && lim[-1] != '\n')
@ -301,18 +218,18 @@ plan_a (char const *filename)
if (revision)
{
char const *rev = revision;
int rev0 = rev[0];
char rev0 = rev[0];
bool found_revision = false;
size_t revlen = strlen (rev);
idx_t revlen = strlen (rev);
if (revlen <= size)
{
char const *limrev = lim - revlen;
for (s = buffer; (s = (char *) memchr (s, rev0, limrev - s)); s++)
for (char const *s = buffer; (s = memchr (s, rev0, limrev - s)); s++)
if (memcmp (s, rev, revlen) == 0
&& (s == buffer || ISSPACE ((unsigned char) s[-1]))
&& (s + 1 == limrev || ISSPACE ((unsigned char) s[revlen])))
&& (s == buffer || c_isspace (s[-1]))
&& (s + 1 == limrev || c_isspace (s[revlen])))
{
found_revision = true;
break;
@ -322,164 +239,18 @@ plan_a (char const *filename)
report_revision (found_revision);
}
/* Plan A will work. */
i_buffer = buffer;
i_ptr = ptr;
return true;
}
/* Keep (virtually) nothing in memory. */
/* Fetch a line from the input file. */
static void
plan_b (char const *filename)
struct iline
ifetch (idx_t line)
{
FILE *ifp;
int c;
size_t len;
size_t maxlen;
bool found_revision;
size_t i;
char const *rev;
size_t revlen;
lin line = 1;
if (! (1 <= line && line <= input_lines))
return (struct iline) { .ptr = "", .size = 0 };
if (instat.st_size == 0)
filename = NULL_DEVICE;
if (! (ifp = fopen (filename, binary_transput ? "rb" : "r")))
pfatal ("Can't open file %s", quotearg (filename));
if (TMPINNAME_needs_removal)
{
/* Reopen the existing temporary file. */
tifd = create_file (TMPINNAME, O_RDWR | O_BINARY, 0, true);
}
else
{
tifd = make_tempfile (&TMPINNAME, 'i', NULL, O_RDWR | O_BINARY,
S_IRUSR | S_IWUSR);
TMPINNAME_needs_removal = true;
}
i = 0;
len = 0;
maxlen = 1;
rev = revision;
found_revision = !rev;
revlen = rev ? strlen (rev) : 0;
while ((c = getc (ifp)) != EOF)
{
len++;
if (c == '\n')
{
if (++line < 0)
too_many_lines (filename);
if (maxlen < len)
maxlen = len;
len = 0;
}
if (!found_revision)
{
if (i == revlen)
{
found_revision = ISSPACE ((unsigned char) c);
i = (size_t) -1;
}
else if (i != (size_t) -1)
i = rev[i]==c ? i + 1 : (size_t) -1;
if (i == (size_t) -1 && ISSPACE ((unsigned char) c))
i = 0;
}
}
if (revision)
report_revision (found_revision);
Fseek (ifp, 0, SEEK_SET); /* rewind file */
for (tibufsize = TIBUFSIZE_MINIMUM; tibufsize < maxlen; tibufsize <<= 1)
/* do nothing */ ;
lines_per_buf = tibufsize / maxlen;
tireclen = maxlen;
tibuf[0] = xmalloc (2 * tibufsize);
tibuf[1] = tibuf[0] + tibufsize;
for (line = 1; ; line++)
{
char *p = tibuf[0] + maxlen * (line % lines_per_buf);
char const *p0 = p;
if (! (line % lines_per_buf)) /* new block */
if (write (tifd, tibuf[0], tibufsize) != tibufsize)
write_fatal ();
if ((c = getc (ifp)) == EOF)
break;
for (;;)
{
*p++ = c;
if (c == '\n')
{
last_line_size = p - p0;
break;
}
if ((c = getc (ifp)) == EOF)
{
last_line_size = p - p0;
line++;
goto EOF_reached;
}
}
}
EOF_reached:
if (ferror (ifp) || fclose (ifp) != 0)
read_fatal ();
if (line % lines_per_buf != 0)
if (write (tifd, tibuf[0], tibufsize) != tibufsize)
write_fatal ();
input_lines = line - 1;
}
/* Fetch a line from the input file.
WHICHBUF is ignored when the file is in memory. */
char const *
ifetch (lin line, bool whichbuf, size_t *psize)
{
char const *q;
char const *p;
if (line < 1 || line > input_lines) {
*psize = 0;
return "";
}
if (using_plan_a) {
p = i_ptr[line];
*psize = i_ptr[line + 1] - p;
return p;
} else {
lin offline = line % lines_per_buf;
lin baseline = line - offline;
if (tiline[0] == baseline)
whichbuf = false;
else if (tiline[1] == baseline)
whichbuf = true;
else {
tiline[whichbuf] = baseline;
if ((lseek (tifd, baseline/lines_per_buf * tibufsize, SEEK_SET)
== -1)
|| read (tifd, tibuf[whichbuf], tibufsize) < 0)
read_fatal ();
}
p = tibuf[whichbuf] + (tireclen*offline);
if (line == input_lines)
*psize = last_line_size;
else {
for (q = p; *q++ != '\n'; )
/* do nothing */ ;
*psize = q - p;
}
return p;
}
char const *ptr = i_ptr[line];
return (struct iline) { .ptr = ptr, .size = i_ptr[line + 1] - ptr };
}

View File

@ -1,8 +1,7 @@
/* inputting files to be patched */
/* Copyright (C) 1986, 1988 Larry Wall
Copyright (C) 1991-1993, 1997-1999, 2002-2003, 2009-2012 Free Software
Foundation, Inc.
/* Copyright 1991-2025 Free Software Foundation, Inc.
Copyright 1986, 1988 Larry Wall
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
@ -17,9 +16,14 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
XTERN lin input_lines; /* how long is input file in lines */
/* Number of lines in input file. */
extern idx_t input_lines;
char const *ifetch (lin, bool, size_t *);
bool get_input_file (char const *, char const *, mode_t);
/* Description of an input line: a pointer to its start, and the
number of bytes in it (including any trailing newline). */
struct iline { char const *ptr; idx_t size; };
struct iline ifetch (idx_t) ATTRIBUTE_PURE;
bool get_input_file (char *, char const *, mode_t);
void re_input (void);
void scan_input (char *, mode_t);
void scan_input (char *, mode_t, int);

67
src/list.h Normal file
View File

@ -0,0 +1,67 @@
#ifndef __LIST_H
#define __LIST_H
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
_GL_INLINE_HEADER_BEGIN
#ifndef LIST_INLINE
# define LIST_INLINE _GL_INLINE
#endif
LIST_INLINE void
INIT_LIST_HEAD (struct list_head *list)
{
list->next = list;
list->prev = list;
}
LIST_INLINE void
list_add (struct list_head *entry, struct list_head *head)
{
struct list_head *next = head->next;
entry->prev = head;
entry->next = next;
next->prev = head->next = entry;
}
LIST_INLINE void
list_del (struct list_head *entry)
{
struct list_head *next = entry->next;
struct list_head *prev = entry->prev;
next->prev = prev;
prev->next = next;
}
LIST_INLINE void
list_del_init (struct list_head *entry)
{
list_del (entry);
INIT_LIST_HEAD (entry);
}
LIST_INLINE bool
list_empty (const struct list_head *head)
{
return head->next == head;
}
/* Return PTR - OFFSET, ignoring the type of PTR and treating OFFSET
as a byte offset. */
LIST_INLINE void *
list_entry (void *ptr, idx_t offset)
{
char *p = ptr;
return p - offset;
}
_GL_INLINE_HEADER_END
#endif /* __LIST_H */

View File

@ -1,6 +1,6 @@
/* Merge a patch
Copyright (C) 2009-2012 Free Software Foundation, Inc.
Copyright 2009-2025 Free Software Foundation, Inc.
Written by Andreas Gruenbacher <agruen@gnu.org>, 2009.
This program is free software: you can redistribute it and/or modify
@ -16,24 +16,22 @@
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 XTERN extern
#include <common.h>
#include <minmax.h>
#include <xalloc.h>
#include <inp.h>
#include <pch.h>
#include <util.h>
static lin count_context_lines (void);
static bool context_matches_file (lin, lin);
static void compute_changes (lin, lin, lin, lin, char *, char *);
static idx_t count_context_lines (void);
static bool context_matches_file (idx_t, idx_t);
static void compute_changes (idx_t, idx_t, idx_t, idx_t, char *, char *);
#define OFFSET lin
#define OFFSET ptrdiff_t
#define EQUAL_IDX(x, y) (context_matches_file (x, y))
#include "bestmatch.h"
#define XVECREF_YVECREF_EQUAL(ctxt, x, y) (context_matches_file (x, y))
#define OFFSET lin
#define OFFSET ptrdiff_t
#define EXTRA_CONTEXT_FIELDS \
char *xchar; \
char *ychar;
@ -42,21 +40,19 @@ static void compute_changes (lin, lin, lin, lin, char *, char *);
#define USE_HEURISTIC 1
#include "diffseq.h"
static lin
locate_merge (lin *matched)
static idx_t
locate_merge (idx_t *matched)
{
lin first_guess = pch_first () + in_offset;
lin pat_lines = pch_ptrn_lines ();
lin context_lines = count_context_lines ();
lin max_where = input_lines - pat_lines + context_lines + 1;
lin min_where = last_frozen_line + 1;
lin max_pos_offset = max_where - first_guess;
lin max_neg_offset = first_guess - min_where;
lin max_offset = (max_pos_offset < max_neg_offset
? max_neg_offset : max_pos_offset);
lin where = first_guess, max_matched = 0;
lin min, max;
lin offset;
idx_t first_guess = pch_first () + in_offset;
idx_t pat_lines = pch_ptrn_lines ();
idx_t context_lines = count_context_lines ();
idx_t max_where = input_lines - pat_lines + context_lines + 1;
idx_t min_where = last_frozen_line + 1;
ptrdiff_t max_pos_offset = max_where - first_guess;
ptrdiff_t max_neg_offset = first_guess - min_where;
ptrdiff_t max_offset = MAX (max_pos_offset, max_neg_offset);
idx_t where = first_guess;
idx_t max_matched = 0;
bool match_until_eof;
/* Note: we need to preserve patch's property that it applies hunks at the
@ -70,21 +66,15 @@ locate_merge (lin *matched)
/* Allow at most CONTEXT_LINES lines to be replaced (replacing counts
as insert + delete), and require the remaining MIN lines to match. */
max = 2 * context_lines;
min = pat_lines - context_lines;
idx_t max = 2 * context_lines;
idx_t min = pat_lines - context_lines;
if (debug & 1)
{
char numbuf0[LINENUM_LENGTH_BOUND + 1];
char numbuf1[LINENUM_LENGTH_BOUND + 1];
say ("locating merge: min=%s max=%s ",
format_linenum (numbuf0, min),
format_linenum (numbuf1, max));
}
say ("locating merge: min=%td max=%td ", min, max);
/* Hunks from the start or end of the file have less context. Anchor them
to the start or end, trying to make up for this disadvantage. */
offset = pch_suffix_context () - pch_prefix_context ();
ptrdiff_t offset = pch_suffix_context () - pch_prefix_context ();
if (offset > 0 && pch_first () <= 1)
max_pos_offset = 0;
match_until_eof = offset < 0;
@ -97,9 +87,7 @@ locate_merge (lin *matched)
{
if (offset <= max_pos_offset)
{
lin guess = first_guess + offset;
lin last;
lin changes;
idx_t guess = first_guess + offset, last, changes;
changes = bestmatch (1, pat_lines + 1, guess, input_lines + 1,
match_until_eof ? input_lines - guess + 1 : min,
@ -116,9 +104,7 @@ locate_merge (lin *matched)
}
if (0 < offset && offset <= max_neg_offset)
{
lin guess = first_guess - offset;
lin last;
lin changes;
idx_t guess = first_guess - offset, last, changes;
changes = bestmatch (1, pat_lines + 1, guess, input_lines + 1,
match_until_eof ? input_lines - guess + 1 : min,
@ -135,15 +121,7 @@ locate_merge (lin *matched)
}
}
if (debug & 1)
{
char numbuf0[LINENUM_LENGTH_BOUND + 1];
char numbuf1[LINENUM_LENGTH_BOUND + 1];
char numbuf2[LINENUM_LENGTH_BOUND + 1];
say ("where=%s matched=%s changes=%s\n",
format_linenum (numbuf0, where),
format_linenum (numbuf1, max_matched),
format_linenum (numbuf2, max + 1));
}
say ("where=%td matched=%td changes=%td\n", where, max_matched, max + 1);
out:
*matched = max_matched;
@ -153,72 +131,66 @@ locate_merge (lin *matched)
}
static void
print_linerange (lin from, lin to)
print_linerange (idx_t from, idx_t to)
{
char numbuf0[LINENUM_LENGTH_BOUND + 1];
char numbuf1[LINENUM_LENGTH_BOUND + 1];
if (to <= from)
printf ("%s",
format_linenum (numbuf0, from));
Fprintf (stdout, "%td", from);
else
printf ("%s-%s",
format_linenum (numbuf0, from),
format_linenum (numbuf1, to));
Fprintf (stdout, "%td-%td", from, to);
}
static void
merge_result (bool *first_result, int hunk, char const *what, lin from, lin to)
merge_result (bool *first_result, intmax_t hunk, char const *what,
idx_t from, idx_t to)
{
static char const *last_what;
if (*first_result && what)
{
printf ("Hunk #%d %s at ", hunk, what);
Fprintf (stdout, "Hunk #%jd %s at ", hunk, what);
last_what = what;
}
else if (! what)
{
if (! *first_result)
{
fputs (".\n", stdout);
fflush (stdout);
Fputs (".\n", stdout);
Fflush (stdout);
last_what = 0;
}
return;
}
else if (last_what == what)
fputs (",", stdout);
Fputc (',', stdout);
else
printf (", %s at ", what);
Fprintf (stdout, ", %s at ", what);
print_linerange (from + out_offset, to + out_offset);
*first_result = false;
}
bool
merge_hunk (int hunk, struct outstate *outstate, lin where, bool *somefailed)
merge_hunk (intmax_t hunk, struct outstate *outstate,
idx_t where, bool *somefailed)
{
bool applies_cleanly;
bool first_result = true;
bool already_applied;
FILE *fp = outstate->ofp;
lin old = 1;
lin firstold = pch_ptrn_lines ();
lin new = firstold + 1;
lin firstnew = pch_end ();
lin in;
lin firstin;
idx_t old = 1;
idx_t firstold = pch_ptrn_lines ();
idx_t new = firstold + 1;
char *oldin;
lin matched;
lin lastwhere;
idx_t lastwhere;
/* Convert '!' markers into '-' and '+' to simplify things here. */
pch_normalize (UNI_DIFF);
assert (pch_char (firstnew + 1) == '^');
assert (pch_char (pch_end () + 1) == '^');
while (pch_char (new) == '=' || pch_char (new) == '\n')
new++;
idx_t matched;
if (where)
{
applies_cleanly = true;
@ -230,7 +202,7 @@ merge_hunk (int hunk, struct outstate *outstate, lin where, bool *somefailed)
applies_cleanly = false;
}
in = firstold + 2;
idx_t in = firstold + 2;
oldin = xmalloc (in + matched + 1);
memset (oldin, ' ', in + matched);
oldin[0] = '*';
@ -241,40 +213,29 @@ merge_hunk (int hunk, struct outstate *outstate, lin where, bool *somefailed)
if (debug & 2)
{
char numbuf0[LINENUM_LENGTH_BOUND + 1];
char numbuf1[LINENUM_LENGTH_BOUND + 1];
lin n;
fputc ('\n', stderr);
for (n = 0; n <= in + matched; n++)
Fputc ('\n', stderr);
for (idx_t n = 0; n <= in + matched; n++)
{
fprintf (stderr, "%s %c",
format_linenum (numbuf0, n),
oldin[n]);
Fprintf (stderr, "%td %c", n, oldin[n]);
if (n == 0)
fprintf(stderr, " %s,%s\n",
format_linenum (numbuf0, pch_first()),
format_linenum (numbuf1, pch_ptrn_lines()));
Fprintf (stderr, " %td,%td\n", pch_first (), pch_ptrn_lines ());
else if (n <= firstold)
fprintf (stderr, " |%.*s",
(int) pch_line_len (n), pfetch (n));
{
Fputs (" |", stderr);
Fwrite (pfetch (n), 1, pch_line_len (n), stderr);
}
else if (n == in - 1)
fprintf(stderr, " %s,%s\n",
format_linenum (numbuf0, where),
format_linenum (numbuf1, matched));
Fprintf (stderr, " %td,%td\n", where, matched);
else if (n >= in && n < in + matched)
{
size_t size;
const char *line;
line = ifetch (where + n - in, false, &size);
fprintf (stderr, " |%.*s",
(int) size, line);
struct iline line = ifetch (where + n - in);
Fputs (" |", stderr);
Fwrite (line.ptr, 1, line.size, stderr);
}
else
fputc('\n', stderr);
Fputc ('\n', stderr);
}
fflush (stderr);
Fflush (stderr);
}
if (last_frozen_line < where - 1)
@ -284,13 +245,11 @@ merge_hunk (int hunk, struct outstate *outstate, lin where, bool *somefailed)
for (;;)
{
firstold = old;
firstnew = new;
firstin = in;
idx_t firstnew = new;
idx_t firstin = in;
if (pch_char (old) == '-' || pch_char (new) == '+')
{
lin lines;
while (pch_char (old) == '-')
{
if (oldin[old] == '-' || oldin[in] == '+')
@ -307,7 +266,7 @@ merge_hunk (int hunk, struct outstate *outstate, lin where, bool *somefailed)
while (pch_char (new) == '+')
new++;
lines = new - firstnew;
idx_t lines = new - firstnew;
if (verbosity == VERBOSE
|| ((verbosity != SILENT) && ! applies_cleanly))
merge_result (&first_result, hunk, "merged",
@ -432,7 +391,7 @@ merge_hunk (int hunk, struct outstate *outstate, lin where, bool *somefailed)
where, lastwhere - 1);
if (conflict_style == MERGE_DIFF3)
{
lin common_prefix = lastwhere - where;
idx_t common_prefix = lastwhere - where;
/* Forget about common prefix lines. */
firstin -= common_prefix;
@ -448,8 +407,7 @@ merge_hunk (int hunk, struct outstate *outstate, lin where, bool *somefailed)
if (! already_applied)
{
lin common_suffix = 0;
lin lines;
idx_t common_suffix = 0;
if (conflict_style == MERGE_MERGE)
{
@ -461,14 +419,14 @@ merge_hunk (int hunk, struct outstate *outstate, lin where, bool *somefailed)
/* do nothing */ ;
}
lines = 3 + (in - firstin) + (new - firstnew);
idx_t lines = 3 + (in - firstin) + (new - firstnew);
if (conflict_style == MERGE_DIFF3)
lines += 1 + (old - firstold);
merge_result (&first_result, hunk, "NOT MERGED",
where, where + lines - 1);
out_offset += lines - (in - firstin);
fputs (outstate->after_newline + "\n<<<<<<<\n", fp);
Fputs (&"\n<<<<<<<\n"[outstate->after_newline], fp);
outstate->after_newline = true;
if (firstin < in)
{
@ -479,7 +437,7 @@ merge_hunk (int hunk, struct outstate *outstate, lin where, bool *somefailed)
if (conflict_style == MERGE_DIFF3)
{
fputs (outstate->after_newline + "\n|||||||\n", fp);
Fputs (&"\n|||||||\n"[outstate->after_newline], fp);
outstate->after_newline = true;
while (firstold < old)
{
@ -488,14 +446,14 @@ merge_hunk (int hunk, struct outstate *outstate, lin where, bool *somefailed)
}
}
fputs (outstate->after_newline + "\n=======\n", fp);
Fputs (&"\n=======\n"[outstate->after_newline], fp);
outstate->after_newline = true;
while (firstnew < new)
{
outstate->after_newline = pch_write_line (firstnew, fp);
firstnew++;
}
fputs (outstate->after_newline + "\n>>>>>>>\n", fp);
Fputs (&"\n>>>>>>>\n"[outstate->after_newline], fp);
outstate->after_newline = true;
outstate->zero_output = false;
if (ferror (fp))
@ -520,53 +478,48 @@ merge_hunk (int hunk, struct outstate *outstate, lin where, bool *somefailed)
return true;
}
static lin
static idx_t
count_context_lines (void)
{
lin old;
lin lastold = pch_ptrn_lines ();
lin context;
idx_t lastold = pch_ptrn_lines ();
idx_t context = 0;
for (context = 0, old = 1; old <= lastold; old++)
for (idx_t old = 1; old <= lastold; old++)
if (pch_char (old) == ' ')
context++;
return context;
}
static bool
context_matches_file (lin old, lin where)
context_matches_file (idx_t old, idx_t where)
{
size_t size;
const char *line;
line = ifetch (where, false, &size);
return size &&
(canonicalize ?
similar (pfetch (old), pch_line_len (old), line, size) :
(size == pch_line_len (old) &&
memcmp (line, pfetch (old), size) == 0));
struct iline line = ifetch (where);
return line.size &&
(canonicalize_ws ?
similar (pfetch (old), pch_line_len (old), line.ptr, line.size) :
(line.size == pch_line_len (old) &&
memcmp (line.ptr, pfetch (old), line.size) == 0));
}
static void
compute_changes (lin xmin, lin xmax, lin ymin, lin ymax,
compute_changes (idx_t xmin, idx_t xmax, idx_t ymin, idx_t ymax,
char *xchar, char *ychar)
{
struct context ctxt;
lin diags;
ctxt.xchar = xchar - xmin;
ctxt.ychar = ychar - ymin;
diags = xmax + ymax + 3;
ctxt.fdiag = xmalloc (2 * diags * sizeof (*ctxt.fdiag));
idx_t diags;
if (ckd_add (&diags, xmax, ymax) || ckd_add (&diags, diags, 3))
xalloc_die ();
ctxt.fdiag = xinmalloc (diags, 2 * sizeof *ctxt.fdiag);
ctxt.bdiag = ctxt.fdiag + diags;
ctxt.fdiag += ymax + 1;
ctxt.bdiag += ymax + 1;
ctxt.heuristic = true;
ctxt.too_expensive = xmax + ymax;
compareseq (xmin, xmax, ymin, ymax, true, &ctxt);
compareseq (xmin, xmax, ymin, ymax, false, &ctxt);
ctxt.fdiag -= ymax + 1;
free (ctxt.fdiag);

File diff suppressed because it is too large Load Diff

1314
src/pch.c

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,7 @@
/* reading patches */
/* Copyright (C) 1986, 1987, 1988 Larry Wall
Copyright (C) 1990-1993, 1997-2003, 2009-2012 Free Software Foundation, Inc.
/* Copyright 1990-2025 Free Software Foundation, Inc.
Copyright 1986-1988 Larry Wall
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
@ -19,43 +18,39 @@
enum nametype { OLD, NEW, INDEX, NONE };
lin pch_end (void) _GL_ATTRIBUTE_PURE;
lin pch_first (void) _GL_ATTRIBUTE_PURE;
lin pch_hunk_beg (void) _GL_ATTRIBUTE_PURE;
char const *pch_c_function (void) _GL_ATTRIBUTE_PURE;
bool pch_git_diff (void) _GL_ATTRIBUTE_PURE;
char const * pch_timestr (bool which) _GL_ATTRIBUTE_PURE;
char const *pch_sha1 (bool which) _GL_ATTRIBUTE_PURE;
mode_t pch_mode (bool which) _GL_ATTRIBUTE_PURE;
lin pch_newfirst (void) _GL_ATTRIBUTE_PURE;
lin pch_prefix_context (void) _GL_ATTRIBUTE_PURE;
lin pch_ptrn_lines (void) _GL_ATTRIBUTE_PURE;
lin pch_repl_lines (void) _GL_ATTRIBUTE_PURE;
lin pch_suffix_context (void) _GL_ATTRIBUTE_PURE;
bool pch_swap (void);
bool pch_write_line (lin, FILE *);
/* General purpose buffer. */
extern char *patchbuf;
/* Allocated size of buf. It is at least IO_BUFSIZE. */
extern idx_t patchbufsize;
void grow_patchbuf (void);
idx_t pch_end (void) ATTRIBUTE_PURE;
idx_t pch_first (void) ATTRIBUTE_PURE;
idx_t pch_hunk_beg (void) ATTRIBUTE_PURE;
char const *pch_c_function (void) ATTRIBUTE_PURE;
bool pch_git_diff (void) ATTRIBUTE_PURE;
char const * pch_timestr (bool which) ATTRIBUTE_PURE;
mode_t pch_mode (bool which) ATTRIBUTE_PURE;
idx_t pch_newfirst (void) ATTRIBUTE_PURE;
idx_t pch_prefix_context (void) ATTRIBUTE_PURE;
idx_t pch_ptrn_lines (void) ATTRIBUTE_PURE;
idx_t pch_repl_lines (void) ATTRIBUTE_PURE;
idx_t pch_suffix_context (void) ATTRIBUTE_PURE;
void pch_swap (void);
bool pch_write_line (idx_t, FILE *);
bool there_is_another_patch (bool, mode_t *);
char *pfetch (lin) _GL_ATTRIBUTE_PURE;
char pch_char (lin) _GL_ATTRIBUTE_PURE;
int another_hunk (enum diff, bool);
int pch_says_nonexistent (bool) _GL_ATTRIBUTE_PURE;
size_t pch_line_len (lin) _GL_ATTRIBUTE_PURE;
const char *pch_name(enum nametype) _GL_ATTRIBUTE_PURE;
bool pch_copy (void) _GL_ATTRIBUTE_PURE;
bool pch_rename (void) _GL_ATTRIBUTE_PURE;
void do_ed_script (char const *, char const *, bool *, FILE *);
char *pfetch (idx_t) ATTRIBUTE_PURE;
char pch_char (idx_t) ATTRIBUTE_PURE;
bool another_hunk (enum diff, bool);
char pch_says_nonexistent (bool) ATTRIBUTE_PURE;
idx_t pch_line_len (idx_t) ATTRIBUTE_PURE;
char *pch_name (enum nametype) ATTRIBUTE_PURE;
bool pch_copy (void) ATTRIBUTE_PURE;
bool pch_rename (void) ATTRIBUTE_PURE;
void do_ed_script (char *, struct outfile *, FILE *);
void open_patch_file (char const *);
void re_patch (void);
void set_hunkmax (void);
void pch_normalize (enum diff);
XTERN struct timespec p_timestamp[2]; /* timestamps in patch headers */
/* Return timestamp of patch header for file WHICH (false = old, true = new),
or a timestamp with tv_sec == -1 if there was no timestamp or an error in
the timestamp. */
static inline struct timespec pch_timestamp (bool which)
{
return p_timestamp[which];
}
extern struct timespec p_timestamp[2]; /* timestamps in patch headers */

729
src/safe.c Normal file
View File

@ -0,0 +1,729 @@
/* safe path traversal functions for 'patch' */
/* Copyright 2015-2025 Free Software Foundation, Inc.
Written by Tim Waugh <twaugh@redhat.com> and
Andreas Gruenbacher <agruenba@redhat.com>.
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 <sys/types.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <safe.h>
#include <basename-lgpl.h>
#include <hash.h>
#include <filename.h>
#include <xalloc.h>
#include "common.h"
#include "util.h"
#define LIST_INLINE _GL_EXTERN_INLINE
#include "list.h"
#ifndef EFTYPE
# define EFTYPE 0
#endif
#ifdef O_PATH
enum { O_PATHSEARCH = O_PATH };
#else
enum { O_PATHSEARCH = O_SEARCH };
#endif
enum { MAX_PATH_COMPONENTS = 1024 };
/* Flag to turn the safe_* functions into their unsafe variants; files may then
lie outside the current working directory. */
bool unsafe;
/* Path lookup results are cached in a hash table + LRU list. When the
cache is full, the oldest entries are removed. */
static intmax_t dirfd_cache_misses;
struct cached_dirfd {
struct list_head lru_link;
struct list_head children_link, children;
struct cached_dirfd *parent;
char *name;
int fd;
};
static Hash_table *cached_dirfds;
static rlim_t min_cached_fds = 8;
static rlim_t max_cached_fds;
static LIST_HEAD (lru_list);
static size_t hash_cached_dirfd (const void *entry, size_t table_size)
{
const struct cached_dirfd *d = entry;
size_t strhash = hash_string (d->name, table_size);
return (strhash * 31 + d->parent->fd) % table_size;
}
static bool compare_cached_dirfds (const void *_a,
const void *_b)
{
const struct cached_dirfd *a = _a;
const struct cached_dirfd *b = _b;
return (a->parent->fd == b->parent->fd &&
!strcmp (a->name, b->name));
}
static void free_cached_dirfd (struct cached_dirfd *entry)
{
list_del (&entry->children_link);
free (entry->name);
free (entry);
}
static void init_dirfd_cache (void)
{
struct rlimit nofile;
if (getrlimit (RLIMIT_NOFILE, &nofile) == 0)
{
if (nofile.rlim_cur == RLIM_INFINITY)
max_cached_fds = RLIM_INFINITY;
else
max_cached_fds = MAX (nofile.rlim_cur / 4, min_cached_fds);
}
else
max_cached_fds = min_cached_fds;
cached_dirfds = hash_initialize (min_cached_fds, nullptr,
hash_cached_dirfd,
compare_cached_dirfds, nullptr);
if (!cached_dirfds)
xalloc_die ();
}
static struct cached_dirfd *lookup_cached_dirfd (struct cached_dirfd *dir, const char *name)
{
struct cached_dirfd *entry = nullptr;
if (cached_dirfds)
{
struct cached_dirfd key;
key.parent = dir;
key.name = (char *) name;
entry = hash_lookup (cached_dirfds, &key);
}
return entry;
}
static void remove_cached_dirfd (struct cached_dirfd *entry)
{
while (! list_empty (&entry->children))
{
struct cached_dirfd *child =
list_entry (entry->children.next,
offsetof (struct cached_dirfd, children_link));
list_del_init (&child->children_link);
/* assert (list_empty (&child->children_link)); */
hash_remove (cached_dirfds, child); /* noop when not hashed */
}
list_del (&entry->lru_link);
hash_remove (cached_dirfds, entry); /* noop when not hashed */
close (entry->fd);
free_cached_dirfd (entry);
}
static void insert_cached_dirfd (struct cached_dirfd *entry, int keepfd)
{
if (!cached_dirfds)
init_dirfd_cache ();
if (max_cached_fds != RLIM_INFINITY)
{
/* Trim off the least recently used entries */
while (hash_get_n_entries (cached_dirfds) >= max_cached_fds)
{
struct cached_dirfd *last =
list_entry (lru_list.prev,
offsetof (struct cached_dirfd, lru_link));
if (&last->lru_link == &lru_list)
break;
if (last->fd == keepfd)
{
last = list_entry (last->lru_link.prev,
offsetof (struct cached_dirfd, lru_link));
if (&last->lru_link == &lru_list)
break;
}
remove_cached_dirfd (last);
}
}
/* Only insert if the parent still exists. */
if (! list_empty (&entry->children_link))
if (hash_insert (cached_dirfds, entry) != entry)
xalloc_die ();
}
static void invalidate_cached_dirfd (int dirfd, const char *name)
{
struct cached_dirfd dir, key, *entry;
if (!cached_dirfds)
return;
dir.fd = dirfd;
key.parent = &dir;
key.name = (char *) name;
entry = hash_lookup (cached_dirfds, &key);
if (entry)
remove_cached_dirfd (entry);
}
/* Put the looked up path back onto the lru list. Return the file descriptor
of the top entry. */
static int put_path (struct cached_dirfd *entry)
{
int fd = entry->fd;
while (entry)
{
struct cached_dirfd *parent = entry->parent;
if (! parent)
break;
list_add (&entry->lru_link, &lru_list);
entry = parent;
}
return fd;
}
static struct cached_dirfd *
new_cached_dirfd (struct cached_dirfd *dir, char *name, int fd)
{
struct cached_dirfd *entry = xmalloc (sizeof (struct cached_dirfd));
INIT_LIST_HEAD (&entry->lru_link);
list_add (&entry->children_link, &dir->children);
INIT_LIST_HEAD (&entry->children);
entry->parent = dir;
entry->name = name;
entry->fd = fd;
return entry;
}
/* In DIR, look up NAME as a subdirectory.
Return the corresponding cache entry if found, a null pointer otherwise.
If KEEPFD is nonnegative, make sure that any cache entry for it is not
removed from the cache (and KEEPFD remains open). */
static struct cached_dirfd *
openat_cached (struct cached_dirfd *dir, char const *name, int keepfd)
{
int fd;
struct cached_dirfd *entry = lookup_cached_dirfd (dir, name);
if (entry)
{
list_del_init (&entry->lru_link);
/* assert (list_empty (&entry->lru_link)); */
return entry;
}
dirfd_cache_misses++;
/* Actually get the new directory file descriptor. Don't follow
symbolic links. */
fd = openat (dir->fd, name, O_PATHSEARCH | O_DIRECTORY | O_NOFOLLOW);
/* Don't cache errors. */
if (fd < 0)
return nullptr;
/* Store new cache entry */
entry = new_cached_dirfd (dir, xstrdup (name), fd);
insert_cached_dirfd (entry, keepfd);
return entry;
}
static idx_t ATTRIBUTE_PURE
count_path_components (const char *path)
{
while (ISSLASH (*path))
path++;
if (! *path)
return 1;
for (idx_t components = 0; ; components++)
{
if (!*path)
return components;
while (*path && ! ISSLASH (*path))
path++;
while (ISSLASH (*path))
path++;
}
}
/* A symlink to resolve. */
struct symlink {
struct symlink *prev;
char *path;
};
static void push_symlink (struct symlink **stack, struct symlink *symlink)
{
symlink->prev = *stack;
*stack = symlink;
}
static void pop_symlink (struct symlink **stack)
{
struct symlink *top = *stack;
*stack = top->prev;
free (top);
}
static int cwd_stat_errno = -1;
static struct stat cwd_stat;
static struct symlink *read_symlink(int dirfd, const char *name)
{
int saved_errno = errno;
struct stat st;
struct symlink *symlink;
char *buffer;
ssize_t ret;
if (fstatat (dirfd, name, &st, AT_SYMLINK_NOFOLLOW)
|| ! S_ISLNK (st.st_mode))
{
errno = saved_errno;
return nullptr;
}
idx_t symlinksize;
if (ckd_add (&symlinksize, st.st_size, 1 + sizeof *symlink))
xalloc_die ();
symlink = ximalloc (symlinksize);
buffer = (char *)(symlink + 1);
ret = readlinkat (dirfd, name, buffer, st.st_size);
if (ret <= 0)
goto fail;
buffer[ret] = 0;
symlink->path = buffer;
if (ISSLASH (*buffer))
{
char *end;
if (cwd_stat_errno == -1)
{
cwd_stat_errno = stat (".", &cwd_stat) == 0 ? 0 : errno;
if (cwd_stat_errno)
goto fail_exdev;
}
end = buffer + ret;
for (;;)
{
char slash;
int rv;
slash = *end; *end = 0;
rv = stat (symlink->path, &st);
*end = slash;
if (rv == 0
&& st.st_dev == cwd_stat.st_dev
&& st.st_ino == cwd_stat.st_ino)
{
while (ISSLASH (*end))
end++;
symlink->path = end;
return symlink;
}
end--;
if (end == symlink->path)
break;
while (end != symlink->path + 1 && ! ISSLASH (*end))
end--;
while (end != symlink->path + 1 && ISSLASH (*(end - 1)))
end--;
}
goto fail_exdev;
}
return symlink;
fail_exdev:
errno = EXDEV;
fail:
free (symlink);
return nullptr;
}
/* Resolve the next path component in PATH inside DIR. If it is a symlink,
read it and returned it in TOP. */
static struct cached_dirfd *
traverse_next (struct cached_dirfd *dir, char **path, int keepfd,
struct symlink **symlink)
{
char *p = *path;
struct cached_dirfd *entry = dir;
while (*p && ! ISSLASH (*p))
p++;
if (**path == '.' && *path + 1 == p)
;
else if (**path == '.' && *(*path + 1) == '.' && *path + 2 == p)
{
entry = dir->parent;
if (! entry)
{
/* Must not leave the working tree. */
errno = EXDEV;
}
else
{
assert (list_empty (&dir->lru_link));
list_add (&dir->lru_link, &lru_list);
}
}
else
{
char slash_or_nul = *p;
*p = '\0';
entry = openat_cached (dir, *path, keepfd);
if (! entry)
{
if (errno == ELOOP
|| errno == EMLINK /* FreeBSD 10.1: Too many links */
|| (errno == EFTYPE
/* NetBSD 6.1: Inappropriate file type or format. */)
|| errno == ENOTDIR)
{
*symlink = read_symlink (dir->fd, *path);
if (*symlink)
entry = dir;
errno = ELOOP;
}
}
*p = slash_or_nul;
}
if (entry)
while (ISSLASH (*p))
p++;
*path = p;
return entry;
}
/* Traverse PATHNAME.
If REJECT_NL, fail if the PATHNAME's last component contains a newline.
Otherwise, if unsafe or PATHNAME is absolute, just return AT_FDCWD.
Otherwise, update PATHNAME to point to the last path component and
return a file descriptor to its parent directory (which can be AT_FDCWD).
If KEEPFD is nonnegative, make sure that any cache entry for it is not
removed from the cache (and KEEPFD remains open).
When this function is not running, all cache entries are on the lru list,
and all cache entries which still have a parent are also in the hash table.
While this function is running, all cache entries on the path being looked
up are off the lru list but in the hash table.
*/
static int
traverse_another_path (char **pathname, bool reject_nl, int keepfd)
{
char *path = *pathname;
char *last = last_component (path);
if (reject_nl && strchr (last, '\n'))
{
errno = EILSEQ;
return DIRFD_INVALID;
}
if (unsafe || last == path || IS_ABSOLUTE_FILE_NAME (path))
return AT_FDCWD;
static struct cached_dirfd cwd = {
.fd = AT_FDCWD,
};
intmax_t misses = dirfd_cache_misses;
struct cached_dirfd *dir = &cwd;
struct symlink *stack = nullptr;
idx_t steps = count_path_components (path);
struct cached_dirfd *traversed_symlink = nullptr;
INIT_LIST_HEAD (&cwd.children);
if (steps > MAX_PATH_COMPONENTS)
{
errno = ELOOP;
return DIRFD_INVALID;
}
if (debug & 32)
{
idx_t full_pathlen = last - path;
int pathlen = ckd_sub (&pathlen, full_pathlen, 0) ? -1 : pathlen;
Fprintf (stdout, "Resolving path \"%.*s\"", pathlen, path);
}
while (stack || path != last)
{
struct cached_dirfd *entry;
struct symlink *symlink = nullptr;
char *prev = path;
entry = traverse_next (dir, stack ? &stack->path : &path, keepfd, &symlink);
if (! entry)
{
if (debug & 32)
{
Fputs (" (failed)\n", stdout);
Fflush (stdout);
}
goto fail;
}
dir = entry;
if (! stack && symlink)
{
const char *p = prev;
while (*p && ! ISSLASH (*p))
p++;
char *name = ximemdup0 (prev, p - prev);
traversed_symlink = new_cached_dirfd (dir, name, DIRFD_INVALID);
}
if (stack && ! *stack->path)
pop_symlink (&stack);
if (symlink && *symlink->path)
{
push_symlink (&stack, symlink);
steps += count_path_components (symlink->path);
if (steps > MAX_PATH_COMPONENTS)
{
errno = ELOOP;
goto fail;
}
}
else if (symlink)
pop_symlink (&symlink);
if (traversed_symlink && ! stack)
{
traversed_symlink->fd =
entry->fd == AT_FDCWD ? AT_FDCWD : dup (entry->fd);
if (traversed_symlink->fd < 0)
free_cached_dirfd (traversed_symlink);
else
{
insert_cached_dirfd (traversed_symlink, keepfd);
list_add (&traversed_symlink->lru_link, &lru_list);
}
traversed_symlink = nullptr;
}
}
*pathname = last;
if (debug & 32)
{
misses = dirfd_cache_misses - misses;
if (! misses)
Fprintf (stdout, " (cached)\n");
else
Fprintf (stdout, " (%jd miss%s)\n", misses, misses == 1 ? "" : "es");
Fflush (stdout);
}
return put_path (dir);
fail:
if (traversed_symlink)
free_cached_dirfd (traversed_symlink);
put_path (dir);
while (stack)
pop_symlink (&stack);
return DIRFD_INVALID;
}
/* Just traverse PATHNAME; see traverse_another_path(). */
static int
traverse_path (char **pathname, bool reject_nl)
{
return traverse_another_path (pathname, reject_nl, DIRFD_INVALID);
}
static int
safe_xstat (char *pathname, struct stat *buf, int flags)
{
int dirfd = traverse_path (&pathname, false);
if (dirfd == DIRFD_INVALID)
return -1;
if (! strcmp (pathname, ""))
return EINVAL;
return fstatat (dirfd, pathname, buf, flags);
}
/* Replacement for stat() */
int
safe_stat (char *pathname, struct stat *buf)
{
return safe_xstat (pathname, buf, 0);
}
/* Replacement for lstat() */
int
safe_lstat (char *pathname, struct stat *buf)
{
return safe_xstat (pathname, buf, AT_SYMLINK_NOFOLLOW);
}
/* Replacement for open() */
int
safe_open (char *pathname, int flags, mode_t mode)
{
int creat_excl = flags & (O_CREAT | O_EXCL);
int dirfd = traverse_path (&pathname, creat_excl == (O_CREAT | O_EXCL));
if (dirfd == DIRFD_INVALID)
return -1;
/* If O_CREAT is set but O_EXCL is not, traverse_path does not
suffice for checking file names with '\n', so check by hand. */
if (creat_excl == O_CREAT
&& strchr (last_component (pathname), '\n')
&& faccessat (dirfd, pathname, F_OK, AT_EACCESS) < 0
&& errno == ENOENT)
{
errno = EILSEQ;
return -1;
}
return openat (dirfd, pathname, flags, mode);
}
/* Replacement for rename() */
int
safe_rename (char *oldpath, char *newpath)
{
int olddirfd = traverse_path (&oldpath, false);
if (olddirfd == DIRFD_INVALID)
return -1;
int newdirfd = traverse_another_path (&newpath, true, olddirfd);
if (newdirfd == DIRFD_INVALID)
return -1;
int ret = renameat (olddirfd, oldpath, newdirfd, newpath);
if (! ret)
{
invalidate_cached_dirfd (olddirfd, oldpath);
invalidate_cached_dirfd (newdirfd, newpath);
}
return ret;
}
/* Replacement for mkdir() */
int
safe_mkdir (char *pathname, mode_t mode)
{
int dirfd = traverse_path (&pathname, true);
if (dirfd == DIRFD_INVALID)
return -1;
return mkdirat (dirfd, pathname, mode);
}
/* Replacement for rmdir() */
int
safe_rmdir (char *pathname)
{
int dirfd = traverse_path (&pathname, false);
if (dirfd == DIRFD_INVALID)
return -1;
int ret = unlinkat (dirfd, pathname, AT_REMOVEDIR);
if (! ret)
invalidate_cached_dirfd (dirfd, pathname);
return ret;
}
/* Replacement for unlink() */
int
safe_unlink (char *pathname)
{
int dirfd = traverse_path (&pathname, false);
if (dirfd == DIRFD_INVALID)
return -1;
return unlinkat (dirfd, pathname, 0);
}
/* Replacement for symlink() */
int
safe_symlink (char const *target, char *linkpath)
{
int dirfd = traverse_path (&linkpath, true);
if (dirfd == DIRFD_INVALID)
return -1;
return symlinkat (target, dirfd, linkpath);
}
/* Replacement for chmod() */
int
safe_chmod (char *pathname, mode_t mode)
{
int dirfd = traverse_path (&pathname, false);
if (dirfd == DIRFD_INVALID)
return -1;
return fchmodat (dirfd, pathname, mode, 0);
}
/* Replacement for lchown() */
int
safe_lchown (char *pathname, uid_t owner, gid_t group)
{
int dirfd = traverse_path (&pathname, false);
if (dirfd == DIRFD_INVALID)
return -1;
return fchownat (dirfd, pathname, owner, group, AT_SYMLINK_NOFOLLOW);
}
/* Replacement for lutimens() */
int
safe_lutimens (char *pathname, struct timespec const times[2])
{
int dirfd = traverse_path (&pathname, false);
if (dirfd == DIRFD_INVALID)
return -1;
return utimensat (dirfd, pathname, times, AT_SYMLINK_NOFOLLOW);
}
/* Replacement for readlink() */
ssize_t
safe_readlink (char *pathname, char *buf, size_t bufsiz)
{
int dirfd = traverse_path (&pathname, false);
if (dirfd == DIRFD_INVALID)
return -1;
return readlinkat (dirfd, pathname, buf, bufsiz);
}
/* Replacement for access() */
int
safe_access (char *pathname, int mode)
{
int dirfd = traverse_path (&pathname, false);
if (dirfd == DIRFD_INVALID)
return -1;
return faccessat (dirfd, pathname, mode, AT_EACCESS);
}

42
src/safe.h Normal file
View File

@ -0,0 +1,42 @@
/* safe path traversal functions for 'patch' */
/* Copyright 2015-2025 Free Software Foundation, Inc.
Written by Tim Waugh <twaugh@redhat.com> and
Andreas Gruenbacher <agruenba@redhat.com>.
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/>. */
/* A constant that cannot be successfully passed as a directory file
descriptor to openat etc. Its value is negative but does not equal
AT_FDCWD. Normally the value is -1, but it is -2 on perverse
platforms where AT_FDCWD == -1. */
enum { DIRFD_INVALID = -1 - (AT_FDCWD == -1) };
extern bool unsafe;
int safe_stat (char *pathname, struct stat *buf);
int safe_lstat (char *pathname, struct stat *buf);
int safe_open (char *pathname, int flags, mode_t mode);
int safe_rename (char *oldpath, char *newpath);
int safe_mkdir (char *pathname, mode_t mode);
int safe_rmdir (char *pathname);
int safe_unlink (char *pathname);
int safe_symlink (const char *target, char *linkpath);
int safe_chmod (char *pathname, mode_t mode);
int safe_lchown (char *pathname, uid_t owner, gid_t group);
int safe_lutimens (char *pathname, struct timespec const times[2]);
ssize_t safe_readlink(char *pathname, char *buf, size_t bufsiz);
int safe_access(char *pathname, int mode);

1244
src/util.c

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,7 @@
/* utility functions for 'patch' */
/* Copyright (C) 1986 Larry Wall
Copyright (C) 1992-1993, 1997-1999, 2001-2003, 2009-2012 Free Software
Foundation, Inc.
/* Copyright 1992-2025 Free Software Foundation, Inc.
Copyright 1986 Larry Wall
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
@ -18,58 +16,12 @@
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 <utimens.h>
#include <timespec.h>
#include <stat-time.h>
#include <backupfile.h>
/* An upper bound on the print length of a signed decimal line number.
Add one for the sign. */
#define LINENUM_LENGTH_BOUND (sizeof (lin) * CHAR_BIT / 3 + 1)
enum file_id_type { UNKNOWN, CREATED, DELETE_LATER, OVERWRITTEN };
XTERN enum backup_type backup_type;
bool ok_to_reverse (char const *, ...) __attribute__ ((format (printf, 1, 2)));
void ask (char const *, ...) __attribute__ ((format (printf, 1, 2)));
void say (char const *, ...) __attribute__ ((format (printf, 1, 2)));
void fatal (char const *, ...)
__attribute__ ((noreturn, format (printf, 1, 2)));
void pfatal (char const *, ...)
__attribute__ ((noreturn, format (printf, 1, 2)));
void fetchname (char const *, int, char **, char **, struct timespec *);
char *parse_name (char const *, int, char const **);
char *savebuf (char const *, size_t);
char *savestr (char const *);
char const *version_controller (char const *, bool, struct stat const *, char **, char **);
bool version_get (char const *, char const *, bool, bool, char const *, struct stat *);
int create_file (char const *, int, mode_t, bool);
int systemic (char const *);
char *format_linenum (char[LINENUM_LENGTH_BOUND + 1], lin);
void Fseek (FILE *, file_offset, int);
void copy_file (char const *, char const *, struct stat *, int, mode_t, bool);
void append_to_file (char const *, char const *);
void exit_with_signal (int) __attribute__ ((noreturn));
void ignore_signals (void);
void init_backup_hash_table (void);
void init_time (void);
void xalloc_die (void) __attribute__ ((noreturn));
void create_backup (char const *, const struct stat *, bool);
void move_file (char const *, bool *, struct stat const *, char const *, mode_t, bool);
void read_fatal (void) __attribute__ ((noreturn));
void remove_prefix (char *, size_t);
void removedirs (char const *);
void set_signals (bool);
void write_fatal (void) __attribute__ ((noreturn));
void insert_file_id (struct stat const *, enum file_id_type);
enum file_id_type lookup_file_id (struct stat const *);
void set_queued_output (struct stat const *, bool);
bool has_queued_output (struct stat const *);
int stat_file (char const *, struct stat *);
enum file_attributes {
FA_TIMES = 1,
FA_IDS = 2,
@ -77,15 +29,89 @@ enum file_attributes {
FA_XATTRS = 8
};
void set_file_attributes (char const *, enum file_attributes, char const *,
const struct stat *, mode_t, struct timespec *);
/* Exit status for trouble, such as write failure. */
enum { EXIT_TROUBLE = 2 };
static inline char const * _GL_ATTRIBUTE_PURE
skip_spaces (char const *str)
/* Read or write at most IO_BUFSIZE bytes at a time.
In 2024 256 KiB was determined to be the best blocksize
to minimize system call overhead across most systems
when copying files. See coreutils/src/ioblksize.h. */
enum { IO_BUFSIZE = 256 * 1024 };
/* POSIX says behavior is implementation-defined for I/O requests
larger than SSIZE_MAX. IO_BUFSIZE is OK on all known platforms.
Check it to be sure. */
static_assert (IO_BUFSIZE <= SSIZE_MAX);
extern enum backup_type backup_type;
_GL_INLINE_HEADER_BEGIN
#ifndef UTIL_INLINE
# define UTIL_INLINE _GL_INLINE
#endif
char volatile *volatilize (char *);
/* Convert S to a pointer to non-volatile. This is the inverse of volatilize.
Ss contents must not be updated by a signal handler. */
UTIL_INLINE char *
devolatilize (char volatile *s)
{
while (ISSPACE ((unsigned char) *str))
str++;
return str;
return (char *) s;
}
int make_tempfile(char const **, char, char const *, int, mode_t);
bool ok_to_reverse (char const *, ...) ATTRIBUTE_FORMAT ((printf, 1, 2));
char *ask (char const *, ...) ATTRIBUTE_FORMAT ((printf, 1, 2));
void say (char const *, ...) ATTRIBUTE_FORMAT ((printf, 1, 2));
_Noreturn void fatal (char const *, ...) ATTRIBUTE_FORMAT ((printf, 1, 2));
_Noreturn void pfatal (char const *, ...) ATTRIBUTE_FORMAT ((printf, 1, 2));
void fetchname (char const *, intmax_t, char **, char **, struct timespec *);
char *parse_name (char const *, intmax_t, char const **);
char *savebuf (char const *, idx_t)
ATTRIBUTE_MALLOC ATTRIBUTE_DEALLOC_FREE ATTRIBUTE_ALLOC_SIZE ((2));
char const *version_controller (char const *, bool, struct stat const *, char **, char **);
bool version_get (char *, char const *, bool, bool, char const *, struct stat *);
int create_file (struct outfile *, int, mode_t, bool);
int systemic (char const *);
void Fclose (FILE *);
void Fflush (FILE *);
void Fprintf (FILE *, char const *, ...) ATTRIBUTE_FORMAT ((printf, 2, 3));
void Fputc (int, FILE *);
void Fputs (char const *restrict, FILE *restrict);
void Fseeko (FILE *, off_t, int);
off_t Ftello (FILE *);
void Fwrite (void const *restrict, size_t, size_t, FILE *restrict);
idx_t Read (int, void *, idx_t);
void copy_file (char *, struct stat const *, struct outfile *, struct stat *,
int, mode_t, enum file_attributes, bool);
void append_to_file (char *, char *);
idx_t quote_system_arg (char *, char const *);
void init_signals (void);
void defer_signals (void);
void undefer_signals (void);
void init_backup_hash_table (void);
void init_time (void);
_Noreturn void xalloc_die (void);
void create_backup (char *, const struct stat *, bool);
void move_file (struct outfile *, struct stat const *,
char *, mode_t, bool);
_Noreturn void read_fatal (void);
void removedirs (char const *);
_Noreturn void write_fatal (void);
void putline (FILE *, ...);
void insert_file_id (struct stat const *, enum file_id_type);
enum file_id_type lookup_file_id (struct stat const *);
void set_queued_output (struct stat const *, bool);
bool has_queued_output (struct stat const *);
int stat_file (char *, struct stat *);
bool filename_is_safe (char const *) ATTRIBUTE_PURE;
bool cwd_is_root (char const *);
void set_file_attributes (char *, int, enum file_attributes, char const *, int,
const struct stat *, mode_t, struct timespec *);
int make_tempfile (struct outfile *, char, char const *, int, mode_t);
_GL_INLINE_HEADER_END

View File

@ -1,12 +1,12 @@
/* Print the version number. */
#define XTERN extern
#include <common.h>
#include <version.h>
#include <util.h>
static char const copyright_string[] = "\
Copyright (C) 2003, 2009-2012 Free Software Foundation, Inc.\n\
Copyright (C) 1988 Larry Wall";
Copyright 1989-2025 Free Software Foundation, Inc.\n\
Copyright 1984-1988 Larry Wall";
static char const free_software_msgid[] = "\
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n\
@ -19,6 +19,6 @@ Written by Larry Wall and Paul Eggert";
void
version (void)
{
printf ("%s %s\n%s\n\n%s\n\n%s\n", PACKAGE_NAME, PACKAGE_VERSION,
copyright_string, free_software_msgid, authorship_msgid);
Fprintf (stdout, "%s %s\n%s\n\n%s\n\n%s\n", PACKAGE_NAME, PACKAGE_VERSION,
copyright_string, free_software_msgid, authorship_msgid);
}

2
tests/.gitignore vendored
View File

@ -1,2 +0,0 @@
*.log
*.trs

View File

@ -1,5 +1,4 @@
# Copyright (C) 1989-1993, 1997-1999, 2002-2003, 2006, 2009-2012 Free Software
# Foundation, Inc.
# Copyright 1989-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
@ -20,20 +19,30 @@ TESTS = \
asymmetric-hunks \
backup-prefix-suffix \
bad-filenames \
bad-usage \
concat-git-diff \
context-format \
copy-rename \
corrupt-patch \
corrupt-reject-files \
create-delete \
create-directory \
criss-cross \
crlf-handling \
dash-o-append \
deep-directories \
ed-style \
empty-files \
false-match \
fifo \
file-create-modes \
file-modes \
filename-choice \
git-binary-diff \
git-cleanup \
garbage \
global-reject-files \
hardlinks \
inname \
line-numbers \
merge \
@ -41,19 +50,29 @@ TESTS = \
mixed-patch-types \
munged-context-format \
need-filename \
no-backup \
no-mode-change-git-diff \
no-newline-triggers-assert \
preserve-c-function-names \
preserve-mode-and-timestamp \
quoted-filenames \
read-only-files \
regression-abe92e8010ab \
reject-format \
remember-backup-files \
remember-reject-files \
remove-directories \
symlinks \
unmodified-files
unmodified-files \
unusual-blanks
XFAIL_TESTS = \
dash-o-append
dash-o-append \
context-format
if OS_IS_HAIKU
XFAIL_TESTS += \
preserve-mode-and-timestamp
endif
EXTRA_DIST = \
$(TESTS) \

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -6,7 +6,7 @@
. $srcdir/test-lib.sh
require_cat
require cat
use_local_patch
use_tmpdir

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -6,7 +6,7 @@
. $srcdir/test-lib.sh
require_cat
require cat
use_local_patch
use_tmpdir

View File

@ -1,4 +1,4 @@
# Copyright (C) 2011-2012 Free Software Foundation, Inc.
# Copyright 2011-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -126,6 +126,57 @@ check 'patch -f -p1 --dry-run < d.diff || echo status: $?' <<EOF
checking file g
EOF
fixup='s/ file ab\.[^ ]*/ file ab.*/'
check_ignoring_temp_file_name() {
pat=\'$1\'
unset arg1; eval ${2+"arg1=\\''$2'\\'"}
unset arg2; eval ${3+"arg2=\\''$3'\\'"}
check '
emit_patch '"$pat"' | patch '"$arg1"' '"$arg2"' >patch.out 2>&1
status=$?
sed "$fixup" patch.out
echo status: $status
'
}
nl='
'
nlname="a${nl}b"
quoted_nlname='"a\nb"'
check_ignoring_temp_file_name "$quoted_nlname" <<EOF
patching file 'a
b'
$PATCH: **** Can't rename file ab.* to 'a
b' : Invalid byte sequence
status: 2
EOF
check_ignoring_temp_file_name foo "$nlname" <<EOF
patching file 'a
b'
$PATCH: **** Can't rename file ab.* to 'a
b' : Invalid byte sequence
status: 2
EOF
check_ignoring_temp_file_name foo -o "$nlname" <<EOF
$PATCH: **** Can't create file 'a
b' : Invalid byte sequence
status: 2
EOF
emit_patch_with_NUL()
{
printf '%s\n' '--- /dev/null'
printf '+++ a\0b\n'
printf '%s\n' '@@ -0,0 +1 @@' '+x'
}
check 'emit_patch_with_NUL | patch; echo status: $?' <<EOF
$PATCH: **** patch line 2 contains NUL byte
status: 2
EOF
mkdir d
cd d
cat > d.diff <<EOF
@ -149,3 +200,11 @@ No file to patch. Skipping patch.
1 out of 1 hunk ignored
status: 1
EOF
# Empty filenames are not allowed:
check 'emit_patch f | patch -r- "" || echo status: $?' <<EOF
File '' is not a regular file -- refusing to patch
1 out of 1 hunk ignored
status: 1
EOF

18
tests/bad-usage Normal file
View File

@ -0,0 +1,18 @@
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
# notice and this notice are preserved.
. $srcdir/test-lib.sh
require cat
use_local_patch
use_tmpdir
# ==============================================================
check 'patch -px || echo "status: $?"' <<EOF
$PATCH: **** strip count x is not a number
status: 2
EOF

View File

@ -1,4 +1,4 @@
# Copyright (C) 2010-2012 Free Software Foundation, Inc.
# Copyright 2010-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -6,7 +6,7 @@
. $srcdir/test-lib.sh
require_cat
require cat
use_local_patch
use_tmpdir

122
tests/context-format Normal file
View File

@ -0,0 +1,122 @@
# Copyright 2016-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
# notice and this notice are preserved.
. $srcdir/test-lib.sh
use_local_patch
use_tmpdir
# ==============================================================
printf "%s\n" 1 2 4 5 > a
cat > ab.diff <<EOF
*** a
--- b
***************
*** 1,4 ****
--- 1,5 ----
1
2
+ 3
4
5
EOF
check 'patch < ab.diff' <<EOF
patching file a
EOF
printf "%s\n" 1 2 3 4 5 > a
cat > ab.diff <<EOF
*** a
--- b
***************
*** 1,5 ****
1
2
- 3
4
5
--- 1,4 ----
EOF
check 'patch < ab.diff' <<EOF
patching file a
EOF
# --------------------------------------------------------------
printf "%s\n" a a a a a b a a a a a > a
cat > ab.diff <<EOF
*** a
--- b
***************
*** 6 ****
- b
--- 5 ----
EOF
check 'patch < ab.diff' <<EOF
patching file a
EOF
check 'echo `cat a`' <<EOF
a a a a a a a a a a
EOF
cat > ba.diff <<EOF
*** b
--- a
***************
*** 5 ****
--- 6 ----
+ b
EOF
check 'patch < ba.diff' <<EOF
patching file a
EOF
check 'echo `cat a`' <<EOF
a a a a a b a a a a a
EOF
printf "%s\n" a a a a a a a a a a b > a
cat > ab.diff <<EOF
*** a
--- b
***************
*** 11 ****
- b
--- 10 ----
EOF
check 'patch < ab.diff' <<EOF
patching file a
EOF
check 'echo `cat a`' <<EOF
a a a a a a a a a a
EOF
cat > ba.diff <<EOF
*** b
--- a
***************
*** 10 ****
--- 11 ----
+ b
EOF
check 'patch < ba.diff' <<EOF
patching file a
EOF
check 'echo `cat a`' <<EOF
a a a a a a a a a a b
EOF

View File

@ -1,4 +1,4 @@
# Copyright (C) 2010-2012 Free Software Foundation, Inc.
# Copyright 2010-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -8,7 +8,7 @@
. $srcdir/test-lib.sh
require_cat
require cat
use_local_patch
use_tmpdir
@ -63,6 +63,30 @@ check 'cat h' <<EOF
new
EOF
echo old > h
check 'patch -p1 < rename.diff || echo "Status: $?"' <<EOF
patching file h (already renamed from f)
EOF
ncheck 'test ! -e f'
check 'cat h' <<EOF
new
EOF
mv h f
check 'patch -p1 -R < rename.diff || echo "Status: $?"' <<EOF
patching file f (already renamed from h)
EOF
ncheck 'test ! -e h'
check 'cat f' <<EOF
old
EOF
# --------------------------------------------------------------
# Patches with no hunks

33
tests/corrupt-patch Normal file
View File

@ -0,0 +1,33 @@
# Copyright 2015-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
# notice and this notice are preserved.
. $srcdir/test-lib.sh
require cat
use_local_patch
use_tmpdir
# ==============================================================
# Patch missing the traditional "--- old\n+++ new\n" header
cat > test.diff <<EOF
diff --git a/test b/test
index 8e139fc..4c03766 100644
@@ -1 +1 @@
-a
+b
EOF
echo a > test
check 'patch -p1 -i test.diff' <<EOF
patching file test
EOF
check 'cat test' <<EOF
b
EOF

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -6,7 +6,7 @@
. $srcdir/test-lib.sh
require_cat
require cat
use_local_patch
use_tmpdir

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -6,8 +6,8 @@
. $srcdir/test-lib.sh
require_cat
require_sed
require cat
require sed
use_local_patch
use_tmpdir
@ -182,7 +182,7 @@ EOF
check 'patch -p0 < p.diff || echo status: $?' <<EOF
patching file target
File target is not empty after patch; not deleting
Not deleting file target as content differs from patch
status: 1
EOF
@ -202,7 +202,7 @@ EOF
check 'patch -p0 < p.diff || echo status: $?' <<EOF
patching file target
File target is not empty after patch; not deleting
Not deleting file target as content differs from patch
status: 1
EOF
@ -212,23 +212,27 @@ EOF
# --------------------------------------------------------------
printf '\0' > target
cat > p.diff <<EOF
if ! test -s target; then
echo "Failed to printf a null character; skipping this test"
else
cat > p.diff <<EOF
diff --git a/target b/target
deleted file mode 100644
index f76dd23..0000000
Binary files a/target and /dev/null differ
EOF
check 'patch -p1 -b < p.diff || echo status: $?' <<EOF
check 'patch -p1 -b < p.diff || echo status: $?' <<EOF
patching file target
File target is not empty after patch; not deleting
Not deleting file target as content differs from patch
status: 1
EOF
ncheck 'test -e target'
ncheck 'test -e target'
# Patch creates a backup file even when the original file remains unchanged:
ncheck 'test -e target.orig'
ncheck 'test -e target.orig'
fi
# ================================================================
# File not expected to become empty does become empty

View File

@ -1,18 +1,17 @@
# Copyright (C) 2010-2012 Free Software Foundation, Inc.
# Copyright 2010-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
# notice and this notice are preserved.
# Check whether patch correctly creates directories before creating
# new files.
. $srcdir/test-lib.sh
use_local_patch
use_tmpdir
# ==============================================================
# Check whether patch correctly creates directories before creating
# new files.
cat > f.diff <<EOF
--- /dev/null
@ -25,3 +24,20 @@ EOF
check 'patch -p0 < f.diff' <<EOF
patching file b/newfile
EOF
# ==============================================================
# Make sure it doesn't create files or directories in the target
# location in --dry-run mode
mkdir d
cat > f.diff <<EOF
--- /dev/null
+++ d/e/f
@@ -0,0 +1 @@
+f
EOF
chmod u-w d
check 'patch -p0 --dry-run < f.diff' <<EOF
checking file d/e/f
EOF
chmod u+w d

View File

@ -1,4 +1,4 @@
# Copyright (C) 2010-2012 Free Software Foundation, Inc.
# Copyright 2010-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -8,7 +8,7 @@
. $srcdir/test-lib.sh
require_cat
require cat
use_local_patch
use_tmpdir

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -8,13 +8,13 @@
. $srcdir/test-lib.sh
require_gnu_diff
require_sed
require gnu_diff
require sed
use_local_patch
use_tmpdir
lf2crlf() {
while read l; do echo -e "$l\r"; done
while read l; do printf "%s\r\n" "$l"; done
}
echo 1 > a

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -6,7 +6,7 @@
. $srcdir/test-lib.sh
require_cat
require cat
use_local_patch
use_tmpdir

28
tests/deep-directories Executable file
View File

@ -0,0 +1,28 @@
# Copyright 2015-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
# notice and this notice are preserved.
. $srcdir/test-lib.sh
require cat
use_local_patch
use_tmpdir
# ==============================================================
# Exercise the directory file descriptor cache
# Artificially limit to 8 cache entries
ulimit -n 32 > /dev/null 2>&1 || exit 77
cat > ab.diff <<EOF
--- /dev/null
+++ b/1/2/3/4/5/6/7/8/9/foo
@@ -0,0 +1 @@
+foo
EOF
check 'patch -p1 < ab.diff || echo Status: $?' <<EOF
patching file 1/2/3/4/5/6/7/8/9/foo
EOF

72
tests/ed-style Normal file
View File

@ -0,0 +1,72 @@
# Copyright 2018-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
# notice and this notice are preserved.
. $srcdir/test-lib.sh
require cat
require ed
use_local_patch
use_tmpdir
# ==============================================================
cat > ed1.diff <<EOF
0a
foo
.
EOF
check 'patch -e foo -i ed1.diff' <<EOF
EOF
check 'cat foo' <<EOF
foo
EOF
cat > ed2.diff <<EOF
1337a
r !echo bar
,p
EOF
check 'patch -e foo -i ed2.diff > /dev/null 2> /dev/null || echo "Status: $?"' <<EOF
Status: 2
EOF
check 'cat foo' <<EOF
foo
EOF
# Test the case where one ed-style patch modifies several files
cat > ed3.diff <<EOF
--- foo
+++ foo
1c
bar
.
--- baz
+++ baz
0a
baz
.
EOF
# Apparently we can't create a file with such a patch, while it works fine
# when the file name is provided on the command line
cat > baz <<EOF
EOF
check 'patch -e -i ed3.diff' <<EOF
EOF
check 'cat foo' <<EOF
bar
EOF
check 'cat baz' <<EOF
baz
EOF

View File

@ -1,4 +1,4 @@
# Copyright (C) 2010-2012 Free Software Foundation, Inc.
# Copyright 2010-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -8,7 +8,7 @@
. $srcdir/test-lib.sh
require_cat
require cat
use_local_patch
use_tmpdir
umask 022

55
tests/false-match Normal file
View File

@ -0,0 +1,55 @@
# Copyright 2017-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
# notice and this notice are preserved.
. $srcdir/test-lib.sh
require cat
use_local_patch
use_tmpdir
# ==============================================================
# Check if patch incorrectly applies overlapping hunks: after the
# first hunk is applied, the second hunk should not apply
# anymore. Older versions of patch didn't recognize that and did
# apply the second hunk on top of the first one, leading to the
# following incorrect result, with no information about hunk 2:
#
# Hunk #1 succeeded at 7 (offset 6 lines).
#
# printf '%s\n' x x x x x x 1 2 3 a a 4 5 6
cat > a.diff <<EOF
--- a
+++ a
@@ -1,6 +1,7 @@
1
2
3
+a
4
5
6
@@ -7,6 +8,7 @@
1
2
3
+a
4
5
6
EOF
printf "%s\n" x x x x x x > a
seq 1 6 >> a
check 'patch < a.diff || echo status: $?' <<EOF
patching file a
Hunk #1 succeeded at 7 (offset 6 lines).
Hunk #2 FAILED at 8.
1 out of 2 hunks FAILED -- saving rejects to file a.rej
status: 1
EOF

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -8,7 +8,7 @@
. $srcdir/test-lib.sh
require_cat
require cat
use_local_patch
use_tmpdir

52
tests/file-create-modes Normal file
View File

@ -0,0 +1,52 @@
# Copyright 2010-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
# notice and this notice are preserved.
# Test if git diffs can create files with proper modes
. $srcdir/test-lib.sh
require cat
require sed
require chmod
use_local_patch
use_tmpdir
# ==============================================================
cat > f.diff <<EOF
diff --git a/f b/f
new file mode 100710
index 0000000..3e75765
--- /dev/null
+++ b/f
@@ -0,0 +1 @@
+new
diff --git a/g b/g
new file mode 100654
index 0000000..3e75765
--- /dev/null
+++ b/g
@@ -0,0 +1 @@
+new
EOF
check 'patch -p1 < f.diff' <<EOF
patching file f
patching file g
EOF
check 'ls -l f g | sed "s,\(..........\).*,\1,"' <<EOF
-rwx--x---
-rw-r-xr--
EOF
check 'patch -p1 -R < f.diff' <<EOF
patching file f
patching file g
EOF
ncheck '! test -e f -o -e g || echo fail'

View File

@ -1,4 +1,4 @@
# Copyright (C) 2010-2012 Free Software Foundation, Inc.
# Copyright 2010-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -8,8 +8,9 @@
. $srcdir/test-lib.sh
require_cat
require_sed
require cat
require sed
require chmod
use_local_patch
use_tmpdir

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -8,7 +8,7 @@
. $srcdir/test-lib.sh
require_cat
require cat
use_local_patch
use_tmpdir

25
tests/garbage Normal file
View File

@ -0,0 +1,25 @@
# Copyright 2015-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
# notice and this notice are preserved.
. $srcdir/test-lib.sh
require cat
use_local_patch
use_tmpdir
# ==============================================================
cat > bad.diff <<EOF
***************
*** 0 ****
--- 1 ----
+ foo
EOF
check 'patch foo -i bad.diff || echo "Status: $?"' <<EOF
$PATCH: **** Only garbage was found in the patch input.
Status: 2
EOF

View File

@ -1,4 +1,4 @@
# Copyright (C) 2010-2012 Free Software Foundation, Inc.
# Copyright 2010-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -6,7 +6,7 @@
. $srcdir/test-lib.sh
require_cat
require cat
use_local_patch
use_tmpdir

75
tests/git-cleanup Normal file
View File

@ -0,0 +1,75 @@
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
# notice and this notice are preserved.
. $srcdir/test-lib.sh
require cat
use_local_patch
use_tmpdir
# ==============================================================
cat > 1.diff <<EOF
diff --git a/f b/f
--- f
+++ f
@@ -1 +1 @@
-1
+1a
@@ -5 +5 @@
-out
+in
diff --git a/g b/g
--- g
+++ g
@@ -1 +1 @@
-out
+in
diff --git a/h b/h
--- h
+++ h
@@ -1 +1 @@
BAD PATCH
EOF
echo 1 > f
printf '' > g
printf '' > h
check 'patch -f -i 1.diff || echo status: $?' <<EOF
patching file f
Hunk #2 FAILED at 5.
1 out of 2 hunks FAILED -- saving rejects to file f.rej
patching file g
Hunk #1 FAILED at 1.
1 out of 1 hunk FAILED -- saving rejects to file g.rej
patching file h
$PATCH: **** malformed patch at line 20: BAD PATCH
status: 2
EOF
check 'cat f' <<EOF
1a
EOF
check 'cat f.rej' <<EOF
--- f
+++ f
@@ -5 +5 @@
-out
+in
EOF
ncheck 'cat g'
check 'cat g.rej' <<EOF
--- g
+++ g
@@ -1 +1 @@
-out
+in
EOF

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -8,7 +8,7 @@
. $srcdir/test-lib.sh
require_cat
require cat
use_local_patch
use_tmpdir

50
tests/hardlinks Normal file
View File

@ -0,0 +1,50 @@
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
# notice and this notice are preserved.
# Patch must not overwrite backup files it has created itself.
# (Backup file tests for symlinks are in tests/symlinks.)
. $srcdir/test-lib.sh
require cat
require hardlinks
use_local_patch
use_tmpdir
# ==============================================================
# Hardlinks between source files
echo one > f
ln f g
cat > fg.diff <<EOF
--- f.orig
+++ f
@@ -2 +2 @@
-one
+two
--- g.orig
+++ g
@@ -2 +2 @@
-one
+two
EOF
check 'patch -p0 < fg.diff' <<EOF
patching file f
Hunk #1 succeeded at 1 (offset -1 lines).
patching file g
Hunk #1 succeeded at 1 (offset -1 lines).
EOF
check 'cat f.orig' <<EOF
one
EOF
check 'cat g.orig' <<EOF
one
EOF

View File

@ -1,14 +1,12 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
# notice and this notice are preserved.
# Don't recognize hunks before a filename has been specified/seen
. $srcdir/test-lib.sh
require_cat
require cat
use_local_patch
use_tmpdir
@ -37,3 +35,21 @@ EOF
check 'cat x' <<EOF
three
EOF
# --------------------------------------------------------------
echo aa > file1
mkdir 1
cd 1
cat > g.diff <<EOF
--- file1
+++ file2
@@ -1 +1 @@
-aa
+bb
EOF
check 'patch ../file1 < g.diff' <<EOF
patching file ../file1
EOF

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -6,8 +6,8 @@
. $srcdir/test-lib.sh
require_cat
require_sed
require cat
require sed
use_local_patch
use_tmpdir

View File

@ -1,4 +1,4 @@
# Copyright (C) 2011-2012 Free Software Foundation, Inc.
# Copyright 2011-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -6,7 +6,7 @@
. $srcdir/test-lib.sh
require_cat
require cat
use_local_patch
use_tmpdir

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -8,9 +8,9 @@
. $srcdir/test-lib.sh
require_cat
require_gnu_diff
require_sed
require cat
require gnu_diff
require sed
use_local_patch
use_tmpdir
@ -30,30 +30,28 @@ x2() {
while test $# -gt 0 && test "$1" != -- ; do
echo "$1"
shift
done > a.sed
echo "$body" | sed -f a.sed > b
shift
done > b.sed
echo "$body" | sed -f b.sed > b
test $# -eq 0 || shift
while test $# -gt 0 ; do
echo "$1"
shift
done > b.sed
echo "$body" | sed -f b.sed > c
rm -f a.sed b.sed
done > c.sed
echo "$body" | sed -f c.sed > c
rm -f b.sed c.sed
output=`diff -u a b | patch $ARGS -f c`
status=$?
echo "$output" | sed -e '/^$/d' -e '/^patching file c$/d'
cat c
test $status == 0 || echo "Status: $status"
test $status = 0 || echo "Status: $status"
}
x() {
ARGS="$ARGS --merge" x2 "$@"
ARGS="--merge" x2 "$@"
echo
ARGS="$ARGS --merge=diff3" x2 "$@"
ARGS="--merge=diff3" x2 "$@"
}
unset ARGS
# ==============================================================
check 'x 3' <<EOF

View File

@ -1,4 +1,4 @@
# Copyright (C) 2010-2012 Free Software Foundation, Inc.
# Copyright 2010-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -6,7 +6,7 @@
. $srcdir/test-lib.sh
require_cat
require cat
use_local_patch
use_tmpdir
umask 022

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -8,8 +8,8 @@
. $srcdir/test-lib.sh
require_cat
require_sed
require cat
require sed
use_local_patch
use_tmpdir

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -8,8 +8,8 @@
. $srcdir/test-lib.sh
require_cat
require_sed
require cat
require sed
use_local_patch
use_tmpdir

56
tests/no-backup Normal file
View File

@ -0,0 +1,56 @@
# Copyright 2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
# notice and this notice are preserved.
# Test the --no-backup-if-mismatch option
. $srcdir/test-lib.sh
require cat
use_local_patch
use_tmpdir
# ==============================================================
cat >my_file <<'EOF'
/* ... */
void baz();
void baz() {
/* ... */
}
int main() {
int foo;
int bar;
/* ... */
baz();
}
EOF
cat >my_file.patch <<'EOF'
--- my_file 2025-02-16 11:22:12.881765792 +0000
+++ my_file_new 2025-02-16 11:22:12.881796732 +0000
@@ -2,7 +2,7 @@
void baz();
void baz() {
- /* ... */
+ // ...
}
int main() {
EOF
unset POSIXLY_CORRECT
check 'patch -N --no-backup-if-mismatch <my_file.patch || echo "Status: $?"' <<'EOF'
patching file my_file
Hunk #1 succeeded at 3 with fuzz 1 (offset 1 line).
EOF
ncheck 'test ! -f my_file.orig'

View File

@ -0,0 +1,35 @@
# Copyright 2010-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
# notice and this notice are preserved.
. $srcdir/test-lib.sh
require cat
require chmod
require stat
require chmod
use_local_patch
use_tmpdir
echo 'ksplice' > f
chmod 755 f
cat > simple.diff <<EOF
diff --git a/f b/f
index 422a422a..736b6c7063690a 100644
--- a/f
+++ b/f
@@ -1 +1 @@
-ksplice
+ksplice rocks!
EOF
check 'patch -p1 < simple.diff || echo "Status: $?"' <<EOF
patching file f
EOF
check 'ls -l f | sed "s,\(..........\).*,\1,"' <<EOF
-rwxr-xr-x
EOF

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -8,7 +8,7 @@
. $srcdir/test-lib.sh
require_cat
require cat
use_local_patch
use_tmpdir

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -8,8 +8,7 @@
. $srcdir/test-lib.sh
require_cat
require_gnu_diff
require cat
use_local_patch
use_tmpdir
@ -33,7 +32,23 @@ EOF
preserve_trailing_blank=
diff -p -c -L a -L b a b > ab.diff
cat > ab.diff <<EOF
*** a
--- b
*************** int foo()
*** 2,6 ****
{
/* waste a line */
$preserve_trailing_blank
! return 1;
}
--- 2,6 ----
{
/* waste a line */
$preserve_trailing_blank
! return 2;
}
EOF
touch c
check 'patch c < ab.diff || cat c.rej' <<EOF
patching file c

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -6,8 +6,12 @@
. $srcdir/test-lib.sh
require_cat
require_sed
# Note: This test fails on Haiku. This is normal; it is caused by
# Haiku bug <https://dev.haiku-os.org/ticket/19213>.
require cat
require sed
require chmod
use_local_patch
use_tmpdir
@ -52,7 +56,7 @@ EOF
# Some non-GNU versions of date can't show a file's timestamp
timestamp2_touch=`echo "$timestamp2" | sed -e "$sed_expr"`
TZ=UTC touch -t $timestamp2_touch f.compare
ncheck 'test ! \( f -ot f.compare -o f -nt f.compare \) || echo "timstamp differs"'
ncheck 'test ! \( f -ot f.compare -o f -nt f.compare \) || echo "timestamp differs"'
# POSIX allows a byte like '+' or '.' in position 11 to indicate the
# presence of extended permissions like ACLs.
@ -118,4 +122,4 @@ check 'patch -p1 --backup --set-utc < f.diff' <<EOF
patching file f
EOF
ncheck 'test ! \( f -ot f.compare -o f -nt f.compare \) || echo "timstamp differs"'
ncheck 'test ! \( f -ot f.compare -o f -nt f.compare \) || echo "timestamp differs"'

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -8,14 +8,16 @@
. $srcdir/test-lib.sh
require_cat
require cat
require special_characters
use_local_patch
use_tmpdir
# ==============================================================
# Forbidden characters in Windows filenames:
# \ / : * ? " < > |
# \ / : * ? " < > | TAB
# Filenames with trailing space characters
cat > d.diff <<EOF
--- "\\t \\040"
@ -31,3 +33,25 @@ ncheck 'patch -s -p0 < d.diff'
check 'cat " "' <<EOF
two
EOF
cat > e.diff <<\EOF
*** /dev/null Sat Sep 14 08:47:53 2024
--- "f\000g" Tue Sep 17 10:21:17 2024
***************
*** 0 ****
--- 1 ----
+
EOF
check 'patch -f < e.diff; echo status: $?' <<EOF
can't find file to patch at input line 3
Perhaps you should have used the -p or --strip option?
The text leading up to this was:
--------------------------
|*** /dev/null Sat Sep 14 08:47:53 2024
|--- "f\000g" Tue Sep 17 10:21:17 2024
--------------------------
No file to patch. Skipping patch.
1 out of 1 hunk ignored
status: 1
EOF

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -8,7 +8,8 @@
. $srcdir/test-lib.sh
require_cat
require cat
require chmod
use_local_patch
use_tmpdir
@ -16,7 +17,7 @@ use_tmpdir
: > read-only
chmod a-w read-only
if : 2> /dev/null > read-only; then
if ( : 2> /dev/null > read-only ); then
echo "Files with read-only permissions are writable" \
"(probably running as superuser)" >&2
exit 77
@ -66,3 +67,8 @@ check 'patch -f -p0 --read-only=ignore < f.diff || echo "Status: $?"' <<EOF
patching file f
patching file f
EOF
check 'patch -o - f /dev/null || echo "Status: $?"' <<EOF
patching file - (read from f)
three
EOF

View File

@ -0,0 +1,33 @@
. $srcdir/test-lib.sh
require cat
use_local_patch
use_tmpdir
cat > f <<EOF
1
2
test:
a
4
EOF
cat > f.diff <<EOF
2,3c2
< test:
< a
---
> 3
EOF
check 'patch f < f.diff' <<EOF
patching file f
Hunk #1 succeeded at 3 (offset 1 line).
EOF
check 'cat f' <<EOF
1
2
3
4
EOF

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -8,12 +8,13 @@
. $srcdir/test-lib.sh
require_cat
require_sed
require_gnu_diff
require cat
require sed
use_local_patch
use_tmpdir
preserve_trailing_blank=
# ==============================================================
cat > f.orig <<EOF
@ -27,7 +28,26 @@ a() {
EOF
sed -e 's/5/5a/' f.orig > f
diff -p -c -L f.orig -L f f.orig f > f.diff
cat > f.diff <<EOF
*** f.orig
--- f
*************** a() {
*** 2,7 ****
2
3
$preserve_trailing_blank
! 5
6
}
--- 2,7 ----
2
3
$preserve_trailing_blank
! 5a
6
}
EOF
check 'patch -f -F0 --no-backup-if-mismatch --dry-run f < f.diff || echo "Status: $?"' <<EOF
checking file f
@ -43,8 +63,6 @@ Hunk #1 FAILED at 2.
Status: 1
EOF
preserve_trailing_blank=
check 'cat f.rej' <<EOF
*** f.orig
--- f
@ -67,8 +85,20 @@ EOF
# --------------------------------------------------------------
( echo "Index: f"; diff -p -u -L f.orig -L f f.orig f ) > f.diff
check 'patch -f -F0 --no-backup-if-mismatch f < f.diff || echo "Status: $?"' <<EOF
cat > f1.diff <<EOF
Index: f
--- f.orig
+++ f
@@ -2,6 +2,6 @@ a() {
2
3
$preserve_trailing_blank
-5
+5a
6
}
EOF
check 'patch -f -F0 --no-backup-if-mismatch f < f1.diff || echo "Status: $?"' <<EOF
patching file f
Hunk #1 FAILED at 2.
1 out of 1 hunk FAILED -- saving rejects to file f.rej
@ -79,7 +109,7 @@ check 'cat f.rej' <<EOF
Index: f
--- f.orig
+++ f
@@ -2,6 +2,6 @@
@@ -2,6 +2,6 @@ a() {
2
3
$preserve_trailing_blank
@ -91,7 +121,6 @@ EOF
# --------------------------------------------------------------
diff -p -c -L f.orig -L f f.orig f > f.diff
check 'patch -f -F0 --no-backup-if-mismatch --reject-format=unified f < f.diff || echo "Status: $?"' <<EOF
patching file f
Hunk #1 FAILED at 2.
@ -102,7 +131,7 @@ EOF
check 'cat f.rej' <<EOF
--- f.orig
+++ f
@@ -2,6 +2,6 @@
@@ -2,6 +2,6 @@ a() {
2
3
$preserve_trailing_blank
@ -114,8 +143,7 @@ EOF
# --------------------------------------------------------------
( echo "Index: f"; diff -p -u -L f.orig -L f f.orig f ) > f.diff
check 'patch -f -F0 --no-backup-if-mismatch --reject-format=context f < f.diff || echo "Status: $?"' <<EOF
check 'patch -f -F0 --no-backup-if-mismatch --reject-format=context f < f1.diff || echo "Status: $?"' <<EOF
patching file f
Hunk #1 FAILED at 2.
1 out of 1 hunk FAILED -- saving rejects to file f.rej
@ -147,7 +175,13 @@ EOF
echo one > a
echo two > b
diff -u -L "a label of a" -L "b label of b" a b > ab.diff
cat > ab.diff <<EOF
--- a label of a
+++ b label of b
@@ -1 +1 @@
-one
+two
EOF
mv b a
check 'patch -f < ab.diff || echo "Status: $?"' <<EOF

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -9,7 +9,7 @@
. $srcdir/test-lib.sh
require_cat
require cat
use_local_patch
use_tmpdir
@ -50,41 +50,6 @@ EOF
# ==============================================================
# Hardlinks between source files
echo one > f
ln f g
cat > fg.diff <<EOF
--- f.orig
+++ f
@@ -2 +2 @@
-one
+two
--- g.orig
+++ g
@@ -2 +2 @@
-one
+two
EOF
check 'patch -p0 < fg.diff' <<EOF
patching file f
Hunk #1 succeeded at 1 (offset -1 lines).
patching file g
Hunk #1 succeeded at 1 (offset -1 lines).
EOF
check 'cat f.orig' <<EOF
one
EOF
check 'cat g.orig' <<EOF
one
EOF
# ==============================================================
echo one > f
cat > f.diff <<EOF

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -6,7 +6,7 @@
. $srcdir/test-lib.sh
require_cat
require cat
use_local_patch
use_tmpdir

29
tests/remove-directories Normal file
View File

@ -0,0 +1,29 @@
# Copyright 2013-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
# notice and this notice are preserved.
. $srcdir/test-lib.sh
require cat
use_local_patch
use_tmpdir
# ==============================================================
# Remove empty parent directories when removing a file.
mkdir dir
echo foobar > dir/file
cat > apatch <<EOF
--- dir/file
+++ /dev/null
@@ -1 +0,0 @@
-foobar
EOF
check 'patch -p0 -E < apatch' <<EOF
patching file dir/file
EOF
ncheck '! test -e dir'

View File

@ -1,4 +1,4 @@
# Copyright (C) 2010-2012 Free Software Foundation, Inc.
# Copyright 2010-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -8,7 +8,8 @@
. $srcdir/test-lib.sh
require_cat
require cat
require symlinks
use_local_patch
use_tmpdir
@ -59,6 +60,14 @@ check 'patch --follow-symlinks < modify.diff || echo "Status: $?"' <<EOF
patching file l
EOF
check 'cat f' <<EOF
one
EOF
check 'cat l' <<EOF
two
EOF
# --------------------------------------------------------------
rm -f f l
@ -146,6 +155,234 @@ ncheck 'test ! -L symlink'
# --------------------------------------------------------------
# Recursive symlinks
ln -s l1 l2
ln -s l2 l1
cat > f.diff <<EOF
--- l1/f
+++ l1/f
@@ -0,0 +1 @@
+new
EOF
check 'patch -p0 < f.diff || echo "Status: $?"' <<EOF
Invalid file name l1/f -- skipping patch
Status: 1
EOF
rm -f l1 l2
# --------------------------------------------------------------
cat > retraverse.diff <<EOF
--- abc/def/ghi/jkl
+++ abc/def/ghi/jkl
@@ -0,0 +1 @@
+Parent directory traversal
EOF
ncheck 'mkdir abc'
ncheck 'mkdir abc/def'
ln -sf ../../abc/def abc/def/ghi
check 'patch -p0 < retraverse.diff || echo "Status: $?"' << EOF
patching file abc/def/ghi/jkl
EOF
# --------------------------------------------------------------
# Patch should not create symlinks which point outside the working directory.
mkdir d
echo one > d/f
ln -s d ld
cat > ld.diff <<EOF
--- ld/f
+++ ld/f
@@ -1 +1 @@
-one
+two
EOF
check 'patch -p0 < ld.diff' <<EOF
patching file ld/f
EOF
mkdir e
ln -s ../d e/ld
cat > eld.diff <<EOF
--- e/ld/f
+++ e/ld/f
@@ -1 +1 @@
-two
+three
EOF
check 'patch -p0 < eld.diff' <<EOF
patching file e/ld/f
EOF
rm -f e/ld
ln -sf ../ld e/ld
check 'patch -p0 -R < eld.diff' <<EOF
patching file e/ld/f
EOF
mkdir g
ln -sf ../../z g/bad-rel
ln -sf .. bad-rel-step2
ln -sf ../bad-rel-step2/z g/bad-rel-step1
ln -sf /z g/bad-abs
cat > follow-bad-symlink.diff <<EOF
--- g/bad-rel/x
+++ g/bad-rel/x
@@ -0,0 +1 @@
+relative
--- g/bad-rel-step1/x
+++ g/bad-rel-step1/x
@@ -0,0 +1 @@
+relative, 2 steps
--- g/bad-abs/x
+++ g/bad-abs/x
@@ -0,0 +1 @@
+absolute
EOF
check 'patch -p0 < follow-bad-symlink.diff || echo "Status: $?"' <<EOF
Invalid file name g/bad-rel/x -- skipping patch
Invalid file name g/bad-rel-step1/x -- skipping patch
Invalid file name g/bad-abs/x -- skipping patch
Status: 1
EOF
rm -rf ld d e g
cat > symlink-target.diff <<EOF
diff --git a/dir/foo b/dir/foo
new file mode 120000
index 0000000..cad2309
--- /dev/null
+++ b/dir/foo
@@ -0,0 +1 @@
+../foo
\ No newline at end of file
EOF
check 'patch -p1 < symlink-target.diff || echo "Status: $?"' <<EOF
patching symbolic link dir/foo
EOF
rm -f dir/foo
cat > follow-symlink.diff <<EOF
diff --git a/dir/foo b/dir/foo
new file mode 120000
index 0000000..cad2309
--- /dev/null
+++ b/dir/foo
@@ -0,0 +1 @@
+..
\ No newline at end of file
diff --git a/dir/foo/bar b/dir/foo/bar
new file mode 100644
index 0000000..2ab772d
--- /dev/null
+++ b/dir/foo/bar
@@ -0,0 +1 @@
+created in ..
diff --git a/dir/bad b/dir/bad
new file mode 120000
index 0000000..cad2309
--- /dev/null
+++ b/dir/bad
@@ -0,0 +1 @@
+../..
\ No newline at end of file
diff --git a/dir/bad/baz b/dir/bad/baz
new file mode 100644
index 0000000..2ab772d
--- /dev/null
+++ b/dir/bad/baz
@@ -0,0 +1 @@
+created in ../..
EOF
check 'patch -f -p1 < follow-symlink.diff || echo "Status: $?"' <<EOF
patching symbolic link dir/foo
patching file dir/foo/bar
patching symbolic link dir/bad
Invalid file name dir/bad/baz -- skipping patch
Status: 1
EOF
check 'cat bar' <<EOF
created in ..
EOF
rm -f bar
cat > bad-symlink-target1.diff <<EOF
diff --git a/bar b/bar
new file mode 120000
index 0000000..cad2309
--- /dev/null
+++ b/bar
@@ -0,0 +1 @@
+/bar
\ No newline at end of file
EOF
check 'patch -p1 < bad-symlink-target1.diff || echo "Status: $?"' <<EOF
patching symbolic link bar
EOF
cat > bad-symlink-target2.diff <<EOF
diff --git a/baz b/baz
new file mode 120000
index 0000000..cad2309
--- /dev/null
+++ b/baz
@@ -0,0 +1 @@
+../baz
\ No newline at end of file
EOF
check 'patch -p1 < bad-symlink-target2.diff || echo "Status: $?"' <<EOF
patching symbolic link baz
EOF
# --------------------------------------------------------------
# Absolute symlinks that point back into the working directory
mkdir d
ln -s $PWD here
ln -s $PWD/here/d here_d
cat > good-absolute.diff <<EOF
--- /dev/null
+++ here/d/foo
@@ -0,0 +1 @@
+foo
--- /dev/null
+++ here_d/bar
@@ -0,0 +1 @@
+bar
EOF
check 'patch -p0 < good-absolute.diff' <<EOF
patching file here/d/foo
patching file here_d/bar
EOF
rm -rf d
# --------------------------------------------------------------
# The backup file of a new symlink is an empty regular file.
check 'patch -p1 --backup < create-symlink.diff || echo "Status: $?"' <<EOF
@ -181,3 +418,20 @@ check 'echo b > symlink.orig && cat target2' <<EOF
b
EOF
rm -f target2
# --------------------------------------------------------------
# Make sure we do follow symlinks to patch files.
ncheck 'mkdir d'
cat > d/ab.diff <<EOF
--- /dev/null
+++ b/foo
@@ -0,0 +1 @@
+foo
EOF
ncheck 'ln -sf d l'
check 'patch -p1 -i l/ab.diff' <<EOF
patching file foo
EOF

View File

@ -1,5 +1,5 @@
# Library for simple test scripts
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -7,14 +7,7 @@
# FIXME: Requires a version of diff that understands "-u".
require_cat() {
if ! type cat > /dev/null 2> /dev/null; then
echo "This test requires the cat utility" >&2
exit 77
fi
}
require_gnu_diff() {
_require_gnu_diff() {
case "`diff --version 2> /dev/null`" in
*GNU*)
;;
@ -24,9 +17,57 @@ require_gnu_diff() {
esac
}
require_sed() {
if ! type sed > /dev/null 2> /dev/null; then
echo "This test requires the sed utility" >&2
_try_chmod() {
chmod "$1" "$2" && test "`ls -l "$2" | cut -b2-10`" = "$3"
}
_require_chmod() {
tmp=`mktemp working-chmod.XXXXXXXX`
if ! _try_chmod 644 "$tmp" "rw-r--r--" || \
! _try_chmod 600 "$tmp" "rw-------"; then
rm -f "$tmp"
echo "This test requires chmod support" >&2
exit 77
fi
rm -f "$tmp"
}
_require_hardlinks() {
tmpdir=`mktemp -d hardlinks.XXXXXXXX`
if ! touch "$tmpdir/f" ||
! ln "$tmpdir/f" "$tmpdir/g"; then
rm -rf "$tmpdir"
echo "This test requires hardlink support" >&2
exit 77
fi
rm -rf "$tmpdir"
}
_require_symlinks() {
tmpdir=`mktemp -d hardlinks.XXXXXXXX`
if ! touch "$tmpdir/f" ||
! ln -s "f" "$tmpdir/g"; then
rm -rf "$tmpdir"
echo "This test requires symlink support" >&2
exit 77
fi
rm -rf "$tmpdir"
}
_require_special_characters() {
if ! tmp=`mktemp ' '.XXXXXXXX`; then
echo "This test requires special character support in filenames" >&2
exit 77
fi
rm -f "$tmp"
}
require() {
utility="$1"
if type _require_${utility} > /dev/null 2> /dev/null; then
_require_${utility}
elif ! type "${utility}" > /dev/null 2> /dev/null; then
echo "This test requires the ${utility} utility" >&2
exit 77
fi
}
@ -45,7 +86,7 @@ use_local_patch() {
eval 'patch() {
if test -n "$GDB" ; then
echo -e "\n" >&3
printf "\n\n" >&3
gdbserver localhost:53153 $PATCH "$@" 2>&3
else
$PATCH "$@"
@ -117,22 +158,15 @@ cleanup() {
exit $status
}
if test -z "`echo -n`"; then
if eval 'test -n "${BASH_LINENO[0]}" 2>/dev/null'; then
eval '
_start_test() {
echo -n "[${BASH_LINENO[2]}] $* -- "
}'
else
eval '
_start_test() {
echo -n "* $* -- "
}'
fi
if ( eval 'test -n "${BASH_LINENO[0]}"' 2>/dev/null ); then
eval '
_start_test() {
printf "[${BASH_LINENO[2]}] %s -- " "$*"
}'
else
eval '
_start_test() {
echo "* $*"
printf "* %s -- " "$*"
}'
fi
@ -163,7 +197,7 @@ if ! type seq > /dev/null 2> /dev/null; then
)}
fi
require_cat
require cat
clean_env
checks_succeeded=0

View File

@ -1,4 +1,4 @@
# Copyright (C) 2009, 2011-2012 Free Software Foundation, Inc.
# Copyright 2009-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
@ -6,7 +6,8 @@
. $srcdir/test-lib.sh
require_cat
require cat
require hardlinks
use_local_patch
use_tmpdir

45
tests/unusual-blanks Normal file
View File

@ -0,0 +1,45 @@
# Copyright 2024-2025 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
# notice and this notice are preserved.
. $srcdir/test-lib.sh
require cat
use_local_patch
use_tmpdir
# ==============================================================
# Patch missing the traditional "--- old\n+++ new\n" header
cat > test <<\EOF
m
n
o
EOF
cat > test.diff <<\EOF
*** a/test Tue Sep 3 17:39:19 2024
--- b/test Tue Sep 3 17:39:26 2024
***************
*** 1 , 3 ****
m
! n
o
--- 1 , 3 ----
m
! o
o
EOF
check 'patch -p1 -i test.diff' <<\EOF
patching file test
EOF
check 'cat test' <<\EOF
m
o
o
EOF