`test_mkdir_environment_expansion` set TEST_VAR in the test process, so
mkdir never saw it; pass it to the child with `.env()` instead. The
POSIXLY_CORRECT part of df's `test_default_block_size` is already
covered by the test_df integration tests, so remove it.
Signed-off-by: Zhiwei Liang <zhiwei.liang@zliang.me>
On the Android emulator, the link's ctime did not change between its
creation and `chown -h`, because the file system clock had not ticked.
Sleep 100 ms before `chown`, as test_ls_order_time does.
Signed-off-by: Zhiwei Liang <zhiwei.liang@zliang.me>
`-a 3` and `-v x` were rejected by clap, as "invalid value '3' for
'-a <FILENUM>'", before the parser that says "invalid file number:
'3'" could see them. A missing operand came out as clap's "the
following required arguments were not provided", an extra one as
"unexpected argument 'c' found".
Let any value reach the file number parser, and count the operands
after parsing: "missing operand", "missing operand after 'a'" and
"extra operand 'c'", each followed by the help hint, exit status 1.
`cksum --check` accepted lines such as `CRC (f) = 3015617425` and read
the decimal digest as hex. The legacy algorithms have no tagged format,
so report these lines as improperly formatted.
Signed-off-by: Zhiwei Liang <zhiwei.liang@zliang.me>
GNU prints the `cksum --debug` hardware lines with the program name,
for example "cksum: using avx2 hardware support". Print them with
show_error!, as tail --debug does.
Signed-off-by: Zhiwei Liang <zhiwei.liang@zliang.me>
On aarch64, `cksum --debug` used the ASIMD check for the vmull line,
but the vmull path needs PMULL. Check PMULL at runtime, as GNU does.
Signed-off-by: Zhiwei Liang <zhiwei.liang@zliang.me>
The only thing keeping it off them was the stat time fields, which they
expose as timespecs (st_atim and so on). Read those there, and gate
safe_traversal and its users on Redox alone, so chmod, du, install and
rm walk trees through descriptors there too. The path-based chmod
helpers that only AIX still used are gone, as are the target-specific
safe-traversal features of chmod, chown and install, which already get
it through perms.
Redox stays excluded: nix has none of the *at calls there.
date implements get_clock_resolution via libc::clock_getres (rustix's
excludes WASI) and set_system_datetime for WASI, the latter reporting
"not supported" since the sandbox has no wall-clock-set syscall.
tee duplicates its input through two pipes on Linux. When splicing from
one of them to an output failed part-way, for example with ENOSPC or
EFBIG, the bytes the output did not take stayed in the pipe, and the
remaining outputs got them in front of their next chunk.
Replace the pipe with an empty one after a failed output. The old pipe
is closed first, so that this also works at the open file limit. A new
pipe gets the larger size only if the 2nd pipe got it at the start, so
that the 2nd is never smaller than the 1st. If no new pipe can be made,
tee fails with the error.
With --to=<scale>, a "%.0f" precision took the div_round path, which keeps
one fractional digit below the next base and then rounds that to nearest,
so 1234567 printed 1M instead of GNU's 2M under the default from-zero
rounding. An explicitly given precision now rounds the scaled value with
the selected method.
Error construction is infrequently executed compared with the normal success paths. Mark these functions with `#[cold]` so the compiler can treat them as unlikely code paths and keep cold code separate from hot code.
Use `#[inline(never)]` to prevent error-handling code from being inlined into their callers, avoiding unnecessary code-size growth and keeping the normal execution paths smaller.
Add UCommand::ignore_sigxfsz, which ignores SIGXFSZ in the child's
pre_exec, so a test can push a utility past an Fsize limit and see the
failed write.
test_dd's two Fsize tests did this with a guard that changed SIGXFSZ for
the whole test process. Under cargo test they run in parallel in one
binary, and one test restoring the default could get the other's dd
killed before it printed its stats. Use the new method instead.
Switching utilities rebuilds bundles, and the on-disk and WASI paths
leaked a freshly parsed resource each time. Cache all of them per source
instead, so each file is parsed and leaked at most once.
Overwrite the localizer only after the new one was built, instead of
clearing it first: a failed setup now keeps the previous utility's
strings, matching LOCALIZED_UTIL. This also drops the "already
initialized" error, which can no longer happen.
Replace the thread-based test, which also passed before this change,
with one checking that bundles reuse cached resources.
The help template sets up localization with the invoked name, e.g. dir,
vdir or a prefixed binary name. Those have no strings of their own, so
switching to them replaced the utility's strings with nothing.
setup_localization() only ever kept the first utility: the per-thread
localizer could not be replaced and the embedded utility resource was a
single process-wide OnceLock. Hosts that run several utilities, such as
nushell, then printed raw message ids for every utility after the first.
Make the thread-local localizer replaceable when a different utility is
set up, and cache embedded utility resources per utility.
GNU accepts any blanks, or none, on either side of the '=' of a
tagged line, with or without a space before the '(': "MD5(f) = D",
"MD5 (f)= D", "MD5 (f) =D" and "MD5 (f)<TAB>=<TAB>D" are all
checked. uutils only accepted "MD5 (f) = D" and "MD5(f)= D", and
reported the other lines as improperly formatted.
End the file name at the last ')' and skip the blanks around the
'='. If the text before the '(' is all hex digits, keep the exact
separators: "<HEX DIGEST> (a)= b" is an untagged line with a single
space, and must still be read as one.
Update the parser tests that expected such lines to be rejected,
and add tests for these lines, for lines that must stay rejected
as in GNU (a form feed or carriage return next to the '=', two
spaces or a tab before the '('), and for the hex digest case.
COMMAND and its arguments were read as String, so argument parsing
failed when any value was not valid UTF-8. Read them as OsString so
they reach the child unchanged, as they do with GNU timeout.
DURATION, --signal and --kill-after remain text values. A non-UTF-8
value there still exits 125.
After creating a hard link, cp -l still ran the final chmod meant for newly
copied files on the destination. Since the link shares the source's inode,
this changed the mode of the source file: under umask 077 a 755 source became
700, and with -f over an existing file the source took the old destination's
mode. GNU cp leaves the linked file untouched. Skip that chmod in link mode.
When reading a regular file backwards for `tail -n`, the block closest
to the start of the file was sized as `size % BLOCK_SIZE`, which is 0
when the file size is an exact multiple of 65536 bytes. That block was
then never scanned: a 65536-byte file gave no output for `tail -n 1`,
and larger files lost their first 64 KiB whenever the requested lines
reached into it.
Size the first block as whatever remains after the full blocks, so a
zero remainder means a full block.
Rust maps both EACCES and EPERM to ErrorKind::PermissionDenied, so
UIoError always said "Permission denied". GNU uses "Operation not
permitted" for EPERM (e.g. touch on a writable file owned by someone
else). On unix, prefer the raw errno when it is EPERM.
Add a uucore unit test, an integration test for `touch -d` on a
root-owned file, and accept "Operation not permitted" in the unlink
directory test for macOS/BSD.
Fixes#15020
Stdout is line buffered: every chunk was searched for its last newline,
and the bytes after it cost an extra write. Write to the file
descriptor directly, as cat and tee do.
Keep write_output out of line: once inlined into the per-byte loop of
translate_input, it made the translator state go to memory on every
byte, doubling the time of tr -s.
Give test_broken_pipe_no_error more than a pipe buffer to write: it
only hit the broken pipe when the output came late, which was already
racy on main (1 failure in 60 runs) and became frequent here.
Mean of 30 paired runs on a pinned core, 100 MB input:
before after
tr -d '\n' (text) 36.23 ms 32.64 ms
tr -d '\r\n' (CSV) 35.66 ms 31.94 ms
When deleting or replacing a single byte, a chunk without it was still
copied to the output buffer before being written. ChunkProcessor now
returns the bytes to write, so such a chunk is written from the input.
Mean of 30 paired runs on a pinned core, 100 MB of text without CR:
before after
tr -d '\r' 8.54 ms 7.67 ms
tr '\r' '\n' 8.84 ms 7.97 ms
The single byte to delete or replace was looked up in the whole table,
with a Vec allocation, on every read. Do it once when building the
operation.
About 1,300 fewer instructions per 32 KiB read: up to 3% faster when
nothing is deleted, 7% fewer instructions on the single byte replace
benchmark, noise otherwise.
Deleting filtered bytes with a branch per byte, which mispredicts on
real data. Store every byte in a small block and only advance past the
kept ones. After a block that kept almost nothing, go back to the
branch: it is then well predicted and stores less. When the single
byte to delete is rare, copy the runs between its occurrences instead.
Add a benchmark for deleting a single byte.
Mean of 40 runs in ms, 100 MB input unless noted (Ryzen 9 9950X,
GNU 9.12, pinned core, hyperfine -N):
GNU trunk opti
tr -d , (CSV) 90.1 87.0 33.7
tr -d ' ' 118.0 109.2 33.8
tr -d '\n' 47.8 57.8 37.0
tr -d '\r' (CRLF, 16 MB) 7.4 8.9 5.7
tr -d aeiou 272.4 261.0 32.9
tr -cd 'a-z\n' 130.5 137.9 32.9
tr -cd '[:print:]' (binary) 332.0 317.6 35.0