resolve_asterisk_precision converted the i64 argument with
usize::try_from(..).ok(). On a 32-bit target a value above u32::MAX does
not fit, so it became None, which means "no precision", and
check_precision never saw it: `printf '%.*s' 9223372036854775807 x`
printed `x`, and `%.*d` and `%.*f` ignored the precision the same way.
Compare the i64 with i32::MAX before converting and return the
InvalidPrecision error from there. Every pointer width now rejects the
same values and reports the number that was given. The %s arm needs no
check of its own any more.
The %s arm resolved a `*` precision without the check_precision call
the integer and float arms make, so a value above i32::MAX was used as
is instead of failing with "invalid precision". Check it there, for a
`*` precision only; a precision written in the format is still accepted.
Closes#15663
with a bare mode like -w, only the operand itself was checked for bits the umask kept. Entries found while recursing were changed silently. Chmod also exited with 0. Moved the check into a helper function and call it safe_chmod_file()
The source was checked with is_dir(), which follows symlinks, so a
symlink to a directory removed an empty destination directory and was
then moved into its place. Check the source entry itself, without its
trailing slashes, which would also make lstat follow the link.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GNU quotes `{` and `}` when they are the whole name in the shell
quoting styles, while braces inside a longer name stay bare. This
affects `printf %q` and `ls --quoting-style=shell*`.
GNU accepts a flag or option given more than once, and the last value
wins. These utilities rejected that with "cannot be used multiple
times". Set args_override_self on their commands, as done for chown and
chgrp in #15669.
numfmt keeps the case GNU still rejects: a repeated --field fails with
"multiple field specifications".
--ignore-missing dropped every file whose open failed, not only the ones
that were absent, so a file that exists but cannot be read was skipped
without a word and the run exited 0.
Classify the error in get_file_to_check: only NotFound stays on the
missing path, anything else is printed, counted through failed_open and
returned as CantOpenFile. Drop the matching !ignore_missing guard on the
final failed_open_file check, which ignored missing files never reach.
Base58 text is one big number, but the decoder flushed every 1024
digits, so longer input decoded to the wrong bytes and basenc could not
read back its own output.
Fixes#15776
* uniq: only treat blanks as field separators for -f
skip_fields_offset used is_ascii_whitespace, which also ends a field at
\n, \r and \f. Only space and tab separate fields, so check for those.
Closes#15659
* tests: uniq: cover -z with a newline inside a record
* uniq: keep newline as a field separator with -z
GNU uniq 9.12 still ends a field at a newline with -z; only \r and \f
stop being separators.
With -D, the destination was removed before the same-file check ran, so
`install -D a a` deleted `a`. Run the check before the removal.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
write_new_line() recorded a blank line as already printed for every
newline, including one that only finishes a line begun in an earlier
file or read. The real blank line right after it was then squeezed away.
Only a newline at the start of a line counts as a blank line now.
Closes#15655
A line too short to have the join field was ordered before a line whose
join field is empty, while `sort -k` gives both the same empty key. So
`join --check-order` rejected input that `sort -k` had just sorted, and
such lines never paired with each other.
Closes#15741
Passwd::locate(&str) falls back to getpwuid() when no account has the
given name and it parses as a number, so `groups 1000` or `groups +0`
answered for that uid while GNU reports "no such user". Add a name-only
locate_name() to uucore::entries, keep locate(&str) built on it with the
existing numeric fallback for id, chown and others, and use it in
groups and pinky.
Fixes#15813
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The message formatted the path with Path::display(), so control
characters in a symlink name were written to stderr unescaped. Use
quote() like the other rm diagnostics and drop the literal quotes from
the en-US and fr-FR strings.
Fixes#15811
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
--sort overrides earlier occurrences of itself in clap, so ModeFlags
only sees the last --sort=<WORD> value. Passing conflicting modes such
as --sort=general-numeric --sort=human-numeric, or -h followed by
--sort=general-numeric --sort=human-numeric, drops the earlier value
and skips the incompatibility check.
Append --sort values in clap and collect every --sort occurrence in
ModeFlags::from_matches. Repeated identical --sort values remain
accepted, and conflicting modes now fail with "options '-...' are
incompatible".
Closes#15596
A file operand named `-d=` after `--` was rewritten to `--delimiter==`
before clap parsing, so cut opened the wrong file. Stop the rewrite once
`--` has been seen.
Fixes#15806
In a UTF-8 locale the whitespace matcher splits fields on Unicode blanks
such as U+3000, but -w=trimmed only stripped SPACE and TAB from the ends
of a line. A leading Unicode blank then emptied field 1 and shifted every
word, a trailing one added an empty last field, and -s kept one-word
lines. Trim with the same matcher that splits the fields, and update the
help text, which still said Space/Tab only.
Fixes#15819