A bare suffix with no digits at all (e.g. head -c kiB) must still
parse as 1 of that unit. Only a suffix that remains after stripping
leading zeros (e.g. 0K) means zero.
Closes#13703
0K, 00K and similar inputs were being parsed as 1KiB because the
leading zeros were stripped before suffix interpretation, leaving a
bare suffix that parse_size treats as 1 unit. The leading zeros mean
the value is zero, so a bare suffix must still produce 0.
GNU coreutils prints nothing for head -c 0K and tail -c 0K.
Closes#13703
* chmod: report the real error when a target's metadata is inaccessible
chmod used Path::exists() to decide whether a target exists, but exists() returns false on any metadata error, so a file whose parent directory lacks search permission was reported as "No such file or directory" instead of "Permission denied". Use try_exists() to distinguish a genuine ENOENT from a permission error, matching GNU. Fixes#9789.
* tests/chmod: use digit separators in octal mode literal
Satisfy clippy::unreadable_literal (0o100644 -> 0o100_644) so the
Style and Lint job passes.
The character path walked the line one character at a time through the
encoding decoder and looked the locale encoding up per character, which
dominated the per-line cost on mixed ASCII and multi-byte text.
Take runs of ASCII bytes a machine word at a time. They are single-byte
characters in every encoding handled here, so byte offset and character
position move together across a run, and only the bytes above 0x7F need
the decoder. Bundle the parts that are fixed for the whole run -- the
ranges, the output delimiter and whether it was given, the position mode
and the encoding -- into a CharCut built once by cut_chars, and make the
line body a method on it, so the per-line call passes one pointer rather
than seven arguments.
In uucore, MbEncoding becomes the public Encoding and locale_encoding()
returns it by value, so a caller decoding many characters resolves the
locale once and keeps it in a register instead of reaching through a
OnceLock per character. is_multibyte_locale() had no callers left.
Instruction counts against the parent commit (cachegrind, LC_ALL=C.UTF-8):
-c 5-30, 100k mixed short lines 30.44M -> 28.95M
-c 20-70, 20k long multibyte lines 44.38M -> 41.02M
That is roughly 1.1x in wall clock on both shapes; the machine was too
loaded to quote a tighter figure. The single-byte path (LC_ALL=C) and
field mode are unchanged.
Selecting a range of characters still costs more than the same range of
bytes, and always will: -c used to be an alias for -b, and characters
have to be decoded to be counted.
Tests cover advance directly -- character counting for -c, byte counting
for -b -n, and the word boundary crossings -- plus a cut -c case over
mixed ASCII and multi-byte lines.
wasmtime's argument marshaling requires valid UTF-8, so a raw non-UTF-8
byte delimiter can't be passed through to the WASI binary the way it
can natively on Linux.
integers() parsed both operands with i128, so an operand needing more
digits than that range holds was rejected as an invalid integer with exit
code 2. GNU compares integers of arbitrary width and returns 0 or 1
instead of erroring.
Parse each operand into a sign and its decimal digits and compare those
directly: sign first, then digit count, then bytewise over the remaining
digits. This lifts the width limit from all six integer operators
(-eq, -ne, -lt, -le, -gt, -ge) without adding a dependency, and leaves the
set of accepted operands unchanged.
Fixes#12874
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>