diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ef7519b88..cfcddbb14 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,3 +5,8 @@ updates: schedule: interval: "daily" open-pull-requests-limit: 5 + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 5 diff --git a/.github/workflows/GnuTests.yml b/.github/workflows/GnuTests.yml index 3a152be22..c8d3c8b94 100644 --- a/.github/workflows/GnuTests.yml +++ b/.github/workflows/GnuTests.yml @@ -8,6 +8,10 @@ on: [push, pull_request] jobs: gnu: + permissions: + actions: read # for dawidd6/action-download-artifact to query and download artifacts + contents: read # for actions/checkout to fetch code + pull-requests: read # for dawidd6/action-download-artifact to query commit hash name: Run GNU tests runs-on: ubuntu-latest steps: diff --git a/Cargo.lock b/Cargo.lock index 72f7af984..1079f8a65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -256,9 +256,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.1.12" +version = "3.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c167e37342afc5f33fd87bbc870cedd020d2a6dffa05d45ccd9241fbdd146db" +checksum = "85a35a599b11c089a7f49105658d089b8f2cf0882993c17daf6de15285c2c35d" dependencies = [ "atty", "bitflags", @@ -277,14 +277,14 @@ version = "3.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7ca9141e27e6ebc52e3c378b0c07f3cea52db46ed1cc5861735fb697b56356" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", ] [[package]] name = "clap_lex" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "189ddd3b5d32a70b35e7686054371742a937b0d99128e76dde6340210e966669" +checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" dependencies = [ "os_str_bytes", ] @@ -316,7 +316,7 @@ version = "0.0.13" dependencies = [ "atty", "chrono", - "clap 3.1.12", + "clap 3.1.15", "clap_complete", "conv", "filetime", @@ -1202,9 +1202,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", ] @@ -2067,7 +2067,7 @@ checksum = "7cf7d77f457ef8dfa11e4cd5933c5ddb5dc52a94664071951219a97710f0a32b" name = "uu_arch" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "platform-info", "uucore", ] @@ -2076,7 +2076,7 @@ dependencies = [ name = "uu_base32" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2092,7 +2092,7 @@ dependencies = [ name = "uu_basename" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2100,7 +2100,7 @@ dependencies = [ name = "uu_basenc" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uu_base32", "uucore", ] @@ -2110,7 +2110,7 @@ name = "uu_cat" version = "0.0.13" dependencies = [ "atty", - "clap 3.1.12", + "clap 3.1.15", "nix", "thiserror", "unix_socket", @@ -2121,7 +2121,7 @@ dependencies = [ name = "uu_chcon" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "fts-sys", "libc", "selinux", @@ -2133,7 +2133,7 @@ dependencies = [ name = "uu_chgrp" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2141,7 +2141,7 @@ dependencies = [ name = "uu_chmod" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "libc", "uucore", ] @@ -2150,7 +2150,7 @@ dependencies = [ name = "uu_chown" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2158,7 +2158,7 @@ dependencies = [ name = "uu_chroot" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2166,7 +2166,7 @@ dependencies = [ name = "uu_cksum" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2174,7 +2174,7 @@ dependencies = [ name = "uu_comm" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2182,7 +2182,7 @@ dependencies = [ name = "uu_cp" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "exacl", "filetime", "ioctl-sys", @@ -2199,7 +2199,7 @@ dependencies = [ name = "uu_csplit" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "regex", "thiserror", "uucore", @@ -2211,7 +2211,7 @@ version = "0.0.13" dependencies = [ "atty", "bstr", - "clap 3.1.12", + "clap 3.1.15", "memchr 2.5.0", "uucore", ] @@ -2221,7 +2221,7 @@ name = "uu_date" version = "0.0.13" dependencies = [ "chrono", - "clap 3.1.12", + "clap 3.1.15", "libc", "uucore", "winapi 0.3.9", @@ -2232,7 +2232,7 @@ name = "uu_dd" version = "0.0.13" dependencies = [ "byte-unit", - "clap 3.1.12", + "clap 3.1.15", "gcd", "libc", "signal-hook", @@ -2243,7 +2243,7 @@ dependencies = [ name = "uu_df" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "number_prefix", "unicode-width", "uucore", @@ -2253,7 +2253,7 @@ dependencies = [ name = "uu_dir" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "selinux", "uu_ls", "uucore", @@ -2263,7 +2263,7 @@ dependencies = [ name = "uu_dircolors" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "glob", "uucore", ] @@ -2272,7 +2272,7 @@ dependencies = [ name = "uu_dirname" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2281,7 +2281,7 @@ name = "uu_du" version = "0.0.13" dependencies = [ "chrono", - "clap 3.1.12", + "clap 3.1.15", "glob", "uucore", "winapi 0.3.9", @@ -2291,7 +2291,7 @@ dependencies = [ name = "uu_echo" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2299,7 +2299,7 @@ dependencies = [ name = "uu_env" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "rust-ini", "uucore", ] @@ -2308,7 +2308,7 @@ dependencies = [ name = "uu_expand" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "unicode-width", "uucore", ] @@ -2317,7 +2317,7 @@ dependencies = [ name = "uu_expr" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "num-bigint", "num-traits", "onig", @@ -2328,7 +2328,7 @@ dependencies = [ name = "uu_factor" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "coz", "num-traits", "paste", @@ -2342,7 +2342,7 @@ dependencies = [ name = "uu_false" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2350,7 +2350,7 @@ dependencies = [ name = "uu_fmt" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "unicode-width", "uucore", ] @@ -2359,7 +2359,7 @@ dependencies = [ name = "uu_fold" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2367,7 +2367,7 @@ dependencies = [ name = "uu_groups" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2377,7 +2377,7 @@ version = "0.0.13" dependencies = [ "blake2b_simd", "blake3", - "clap 3.1.12", + "clap 3.1.15", "digest", "hex", "md-5", @@ -2393,7 +2393,7 @@ dependencies = [ name = "uu_head" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "memchr 2.5.0", "uucore", ] @@ -2402,7 +2402,7 @@ dependencies = [ name = "uu_hostid" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "libc", "uucore", ] @@ -2411,7 +2411,7 @@ dependencies = [ name = "uu_hostname" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "hostname", "uucore", "winapi 0.3.9", @@ -2421,7 +2421,7 @@ dependencies = [ name = "uu_id" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "selinux", "uucore", ] @@ -2430,7 +2430,7 @@ dependencies = [ name = "uu_install" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "file_diff", "filetime", "libc", @@ -2442,7 +2442,7 @@ dependencies = [ name = "uu_join" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "memchr 2.5.0", "uucore", ] @@ -2451,7 +2451,7 @@ dependencies = [ name = "uu_kill" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "libc", "uucore", ] @@ -2460,7 +2460,7 @@ dependencies = [ name = "uu_link" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2468,7 +2468,7 @@ dependencies = [ name = "uu_ln" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2476,7 +2476,7 @@ dependencies = [ name = "uu_logname" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "libc", "uucore", ] @@ -2487,7 +2487,7 @@ version = "0.0.13" dependencies = [ "atty", "chrono", - "clap 3.1.12", + "clap 3.1.15", "glob", "lazy_static", "lscolors", @@ -2504,7 +2504,7 @@ dependencies = [ name = "uu_mkdir" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2512,7 +2512,7 @@ dependencies = [ name = "uu_mkfifo" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "libc", "uucore", ] @@ -2521,7 +2521,7 @@ dependencies = [ name = "uu_mknod" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "libc", "uucore", ] @@ -2530,7 +2530,7 @@ dependencies = [ name = "uu_mktemp" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "rand", "tempfile", "uucore", @@ -2541,7 +2541,7 @@ name = "uu_more" version = "0.0.13" dependencies = [ "atty", - "clap 3.1.12", + "clap 3.1.15", "crossterm", "nix", "unicode-segmentation", @@ -2553,7 +2553,7 @@ dependencies = [ name = "uu_mv" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "fs_extra", "uucore", ] @@ -2562,7 +2562,7 @@ dependencies = [ name = "uu_nice" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "libc", "nix", "uucore", @@ -2572,7 +2572,7 @@ dependencies = [ name = "uu_nl" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "regex", "uucore", ] @@ -2582,7 +2582,7 @@ name = "uu_nohup" version = "0.0.13" dependencies = [ "atty", - "clap 3.1.12", + "clap 3.1.15", "libc", "uucore", ] @@ -2591,7 +2591,7 @@ dependencies = [ name = "uu_nproc" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "libc", "num_cpus", "uucore", @@ -2601,7 +2601,7 @@ dependencies = [ name = "uu_numfmt" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2610,7 +2610,7 @@ name = "uu_od" version = "0.0.13" dependencies = [ "byteorder", - "clap 3.1.12", + "clap 3.1.15", "half", "uucore", ] @@ -2619,7 +2619,7 @@ dependencies = [ name = "uu_paste" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2627,7 +2627,7 @@ dependencies = [ name = "uu_pathchk" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "libc", "uucore", ] @@ -2636,7 +2636,7 @@ dependencies = [ name = "uu_pinky" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2645,7 +2645,7 @@ name = "uu_pr" version = "0.0.13" dependencies = [ "chrono", - "clap 3.1.12", + "clap 3.1.15", "itertools", "quick-error", "regex", @@ -2656,7 +2656,7 @@ dependencies = [ name = "uu_printenv" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2664,7 +2664,7 @@ dependencies = [ name = "uu_printf" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2672,7 +2672,7 @@ dependencies = [ name = "uu_ptx" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "regex", "uucore", ] @@ -2681,7 +2681,7 @@ dependencies = [ name = "uu_pwd" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2689,7 +2689,7 @@ dependencies = [ name = "uu_readlink" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2697,7 +2697,7 @@ dependencies = [ name = "uu_realpath" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2705,7 +2705,7 @@ dependencies = [ name = "uu_relpath" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2713,7 +2713,7 @@ dependencies = [ name = "uu_rm" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "remove_dir_all 0.7.0", "uucore", "walkdir", @@ -2724,7 +2724,7 @@ dependencies = [ name = "uu_rmdir" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "libc", "uucore", ] @@ -2733,7 +2733,7 @@ dependencies = [ name = "uu_runcon" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "libc", "selinux", "thiserror", @@ -2745,7 +2745,7 @@ name = "uu_seq" version = "0.0.13" dependencies = [ "bigdecimal", - "clap 3.1.12", + "clap 3.1.15", "num-bigint", "num-traits", "uucore", @@ -2755,7 +2755,7 @@ dependencies = [ name = "uu_shred" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "rand", "uucore", ] @@ -2764,7 +2764,7 @@ dependencies = [ name = "uu_shuf" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "rand", "rand_core", "uucore", @@ -2774,7 +2774,7 @@ dependencies = [ name = "uu_sleep" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2783,7 +2783,7 @@ name = "uu_sort" version = "0.0.13" dependencies = [ "binary-heap-plus", - "clap 3.1.12", + "clap 3.1.15", "compare", "ctrlc", "fnv", @@ -2801,7 +2801,7 @@ dependencies = [ name = "uu_split" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "memchr 2.5.0", "uucore", ] @@ -2810,7 +2810,7 @@ dependencies = [ name = "uu_stat" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2818,7 +2818,7 @@ dependencies = [ name = "uu_stdbuf" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "tempfile", "uu_stdbuf_libstdbuf", "uucore", @@ -2838,7 +2838,7 @@ dependencies = [ name = "uu_sum" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2846,7 +2846,7 @@ dependencies = [ name = "uu_sync" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "libc", "uucore", "winapi 0.3.9", @@ -2856,7 +2856,7 @@ dependencies = [ name = "uu_tac" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "memchr 2.5.0", "memmap2", "regex", @@ -2867,7 +2867,7 @@ dependencies = [ name = "uu_tail" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "libc", "nix", "uucore", @@ -2878,7 +2878,7 @@ dependencies = [ name = "uu_tee" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "libc", "retain_mut", "uucore", @@ -2888,7 +2888,7 @@ dependencies = [ name = "uu_test" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "libc", "redox_syscall", "uucore", @@ -2898,7 +2898,7 @@ dependencies = [ name = "uu_timeout" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "libc", "nix", "uucore", @@ -2908,7 +2908,7 @@ dependencies = [ name = "uu_touch" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "filetime", "time 0.3.9", "uucore", @@ -2919,7 +2919,7 @@ dependencies = [ name = "uu_tr" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "nom", "uucore", ] @@ -2928,7 +2928,7 @@ dependencies = [ name = "uu_true" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2936,7 +2936,7 @@ dependencies = [ name = "uu_truncate" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2944,7 +2944,7 @@ dependencies = [ name = "uu_tsort" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2953,7 +2953,7 @@ name = "uu_tty" version = "0.0.13" dependencies = [ "atty", - "clap 3.1.12", + "clap 3.1.15", "libc", "uucore", ] @@ -2962,7 +2962,7 @@ dependencies = [ name = "uu_uname" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "platform-info", "uucore", ] @@ -2971,7 +2971,7 @@ dependencies = [ name = "uu_unexpand" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "unicode-width", "uucore", ] @@ -2980,7 +2980,7 @@ dependencies = [ name = "uu_uniq" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "strum", "strum_macros", "uucore", @@ -2990,7 +2990,7 @@ dependencies = [ name = "uu_unlink" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -2999,7 +2999,7 @@ name = "uu_uptime" version = "0.0.13" dependencies = [ "chrono", - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -3007,7 +3007,7 @@ dependencies = [ name = "uu_users" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -3015,7 +3015,7 @@ dependencies = [ name = "uu_vdir" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "selinux", "uu_ls", "uucore", @@ -3026,7 +3026,7 @@ name = "uu_wc" version = "0.0.13" dependencies = [ "bytecount", - "clap 3.1.12", + "clap 3.1.15", "libc", "nix", "unicode-width", @@ -3038,7 +3038,7 @@ dependencies = [ name = "uu_who" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "uucore", ] @@ -3046,7 +3046,7 @@ dependencies = [ name = "uu_whoami" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "libc", "uucore", "winapi 0.3.9", @@ -3056,7 +3056,7 @@ dependencies = [ name = "uu_yes" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "nix", "uucore", ] @@ -3065,7 +3065,7 @@ dependencies = [ name = "uucore" version = "0.0.13" dependencies = [ - "clap 3.1.12", + "clap 3.1.15", "data-encoding", "data-encoding-macro", "dns-lookup", @@ -3243,9 +3243,9 @@ checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" [[package]] name = "xattr" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" dependencies = [ "libc", ] diff --git a/docs/src/index.md b/docs/src/index.md index 3ea5d913a..c51fbb198 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -5,7 +5,7 @@ utilities in [Rust](https://www.rust-lang.org). It is available for Linux, Windows, Mac and other platforms. The API reference for `uucore`, the library of functions shared between -various utils, is hosted at at +various utils, is hosted at [docs.rs](https://docs.rs/uucore/latest/uucore/). uutils is licensed under the [MIT License](https://github.com/uutils/coreutils/blob/main/LICENSE). @@ -17,4 +17,4 @@ uutils is licensed under the [MIT License](https://github.com/uutils/coreutils/b * [Discord](https://discord.gg/wQVJbvJ) > Note: This manual is automatically generated from the source code and is -> a work in progress. \ No newline at end of file +> a work in progress. diff --git a/src/uu/base32/src/base_common.rs b/src/uu/base32/src/base_common.rs index 100c85b1f..148bad2f8 100644 --- a/src/uu/base32/src/base_common.rs +++ b/src/uu/base32/src/base_common.rs @@ -90,7 +90,7 @@ pub fn parse_base_cmd_args(args: impl uucore::Args, about: &str, usage: &str) -> let arg_list = args .collect_str(InvalidEncodingHandling::ConvertLossy) .accept_any(); - Config::from(&command.get_matches_from(arg_list)) + Config::from(&command.try_get_matches_from(arg_list)?) } pub fn base_app<'a>(about: &'a str, usage: &'a str) -> Command<'a> { diff --git a/src/uu/cat/src/cat.rs b/src/uu/cat/src/cat.rs index edba1b8d0..67de917f7 100644 --- a/src/uu/cat/src/cat.rs +++ b/src/uu/cat/src/cat.rs @@ -188,7 +188,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { .collect_str(InvalidEncodingHandling::Ignore) .accept_any(); - let matches = uu_app().get_matches_from(args); + let matches = uu_app().try_get_matches_from(args)?; let number_mode = if matches.is_present(options::NUMBER_NONBLANK) { NumberingMode::NonEmpty diff --git a/src/uu/cp/Cargo.toml b/src/uu/cp/Cargo.toml index f9036101a..3f3c2e317 100644 --- a/src/uu/cp/Cargo.toml +++ b/src/uu/cp/Cargo.toml @@ -34,7 +34,7 @@ ioctl-sys = "0.8" winapi = { version="0.3", features=["fileapi"] } [target.'cfg(unix)'.dependencies] -xattr="0.2.1" +xattr="0.2.3" exacl= { version = "0.8.0", optional=true } [[bin]] diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index 913cf2769..1b47b7828 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -57,7 +57,7 @@ use std::path::{Path, PathBuf, StripPrefixError}; use std::str::FromStr; use std::string::ToString; use uucore::backup_control::{self, BackupMode}; -use uucore::error::{set_exit_code, ExitCode, UError, UResult}; +use uucore::error::{set_exit_code, ExitCode, UClapError, UError, UResult}; use uucore::fs::{canonicalize, MissingHandling, ResolveMode}; use walkdir::WalkDir; @@ -485,7 +485,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { app.print_help()?; } clap::ErrorKind::DisplayVersion => println!("{}", app.render_version()), - _ => return Err(Box::new(e)), + _ => return Err(Box::new(e.with_exit_code(1))), }; } else if let Ok(matches) = matches { let options = Options::from_matches(&matches)?; @@ -992,7 +992,9 @@ fn copy_directory( } // if no-dereference is enabled and this is a symlink, copy it as a file - if !options.dereference && fs::symlink_metadata(root).unwrap().file_type().is_symlink() { + if !options.dereference && fs::symlink_metadata(root).unwrap().file_type().is_symlink() + // replace by is_symlink in rust>=1.58 + { return copy_file(root, target, options, symlinked_files); } @@ -1036,6 +1038,7 @@ fn copy_directory( { let p = or_continue!(path); let is_symlink = fs::symlink_metadata(p.path())?.file_type().is_symlink(); + // replace by is_symlink in rust >=1.58 let path = current_dir.join(&p.path()); let local_to_root_parent = match root_parent { @@ -1288,7 +1291,7 @@ fn copy_file( // Fail if dest is a dangling symlink or a symlink this program created previously if fs::symlink_metadata(dest) - .map(|m| m.file_type().is_symlink()) + .map(|m| m.file_type().is_symlink()) // replace by is_symlink in rust>=1.58 .unwrap_or(false) { if FileInformation::from_path(dest, false) @@ -1301,7 +1304,7 @@ fn copy_file( dest.display() ))); } - if !dest.exists() { + if options.dereference && !dest.exists() { return Err(Error::Error(format!( "not writing through dangling symlink '{}'", dest.display() @@ -1535,7 +1538,7 @@ fn copy_link( } else { // we always need to remove the file to be able to create a symlink, // even if it is writeable. - if dest.exists() { + if dest.is_file() { fs::remove_file(dest)?; } dest.into() diff --git a/src/uu/df/src/blocks.rs b/src/uu/df/src/blocks.rs index 7783e5636..e964a208a 100644 --- a/src/uu/df/src/blocks.rs +++ b/src/uu/df/src/blocks.rs @@ -3,7 +3,7 @@ // * For the full copyright and license information, please view the LICENSE // * file that was distributed with this source code. //! Types for representing and displaying block sizes. -use crate::{OPT_BLOCKSIZE, OPT_HUMAN_READABLE_BINARY, OPT_HUMAN_READABLE_DECIMAL}; +use crate::OPT_BLOCKSIZE; use clap::ArgMatches; use std::fmt; @@ -26,6 +26,22 @@ const IEC_BASES: [u128; 10] = [ /// Suffixes for the first nine multi-byte unit suffixes. const SUFFIXES: [char; 9] = ['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; +const SI_BASES: [u128; 10] = [ + 1, + 1_000, + 1_000_000, + 1_000_000_000, + 1_000_000_000_000, + 1_000_000_000_000_000, + 1_000_000_000_000_000_000, + 1_000_000_000_000_000_000_000, + 1_000_000_000_000_000_000_000_000, + 1_000_000_000_000_000_000_000_000_000, +]; + +// we use "kB" instead of "KB" because of GNU df +const SI_SUFFIXES: [&str; 9] = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + /// Convert a multiple of 1024 into a string like "12K" or "34M". /// /// # Examples @@ -57,28 +73,90 @@ fn to_magnitude_and_suffix_1024(n: u128) -> Result { Err(()) } +/// Convert a number into a string like "12kB" or "34MB". +/// +/// Powers of 1000 become "1kB", "1MB", "1GB", etc. +/// +/// The returned string has a maximum length of 5 chars, for example: "1.1kB", "999kB", "1MB". +fn to_magnitude_and_suffix_not_powers_of_1024(n: u128) -> Result { + let mut i = 0; + + while SI_BASES[i + 1] - SI_BASES[i] < n && i < SI_SUFFIXES.len() { + i += 1; + } + + let quot = n / SI_BASES[i]; + let rem = n % SI_BASES[i]; + let suffix = SI_SUFFIXES[i]; + + if rem == 0 { + Ok(format!("{}{}", quot, suffix)) + } else { + let tenths_place = rem / (SI_BASES[i] / 10); + + if rem % (SI_BASES[i] / 10) == 0 { + Ok(format!("{}.{}{}", quot, tenths_place, suffix)) + } else if tenths_place + 1 == 10 { + Ok(format!("{}{}", quot + 1, suffix)) + } else { + Ok(format!("{}.{}{}", quot, tenths_place + 1, suffix)) + } + } +} + /// Convert a number into a magnitude and a multi-byte unit suffix. /// /// # Errors /// /// If the number is too large to represent. fn to_magnitude_and_suffix(n: u128) -> Result { - if n % 1024 == 0 { + if n % 1024 == 0 && n % 1000 != 0 { to_magnitude_and_suffix_1024(n) } else { - // TODO Implement this, probably using code from `numfmt`. - Ok("1kB".into()) + to_magnitude_and_suffix_not_powers_of_1024(n) } } +/// A mode to use in condensing the display of a large number of bytes. +pub(crate) enum SizeFormat { + HumanReadable(HumanReadable), + StaticBlockSize, +} + +impl Default for SizeFormat { + fn default() -> Self { + Self::StaticBlockSize + } +} + +/// A mode to use in condensing the human readable display of a large number +/// of bytes. +/// +/// The [`HumanReadable::Decimal`] and[`HumanReadable::Binary`] variants +/// represent dynamic block sizes: as the number of bytes increases, the +/// divisor increases as well (for example, from 1 to 1,000 to 1,000,000 +/// and so on in the case of [`HumanReadable::Decimal`]). +#[derive(Clone, Copy)] +pub(crate) enum HumanReadable { + /// Use the largest divisor corresponding to a unit, like B, K, M, G, etc. + /// + /// This variant represents powers of 1,000. Contrast with + /// [`HumanReadable::Binary`], which represents powers of + /// 1,024. + Decimal, + + /// Use the largest divisor corresponding to a unit, like B, K, M, G, etc. + /// + /// This variant represents powers of 1,024. Contrast with + /// [`HumanReadable::Decimal`], which represents powers + /// of 1,000. + Binary, +} + /// A block size to use in condensing the display of a large number of bytes. /// /// The [`BlockSize::Bytes`] variant represents a static block -/// size. The [`BlockSize::HumanReadableDecimal`] and -/// [`BlockSize::HumanReadableBinary`] variants represent dynamic -/// block sizes: as the number of bytes increases, the divisor -/// increases as well (for example, from 1 to 1,000 to 1,000,000 and -/// so on in the case of [`BlockSize::HumanReadableDecimal`]). +/// size. /// /// The default variant is `Bytes(1024)`. pub(crate) enum BlockSize { @@ -86,20 +164,6 @@ pub(crate) enum BlockSize { /// /// The number must be positive. Bytes(u64), - - /// Use the largest divisor corresponding to a unit, like B, K, M, G, etc. - /// - /// This variant represents powers of 1,000. Contrast with - /// [`BlockSize::HumanReadableBinary`], which represents powers of - /// 1,024. - HumanReadableDecimal, - - /// Use the largest divisor corresponding to a unit, like B, K, M, G, etc. - /// - /// This variant represents powers of 1,024. Contrast with - /// [`BlockSize::HumanReadableDecimal`], which represents powers - /// of 1,000. - HumanReadableBinary, } impl Default for BlockSize { @@ -109,11 +173,7 @@ impl Default for BlockSize { } pub(crate) fn block_size_from_matches(matches: &ArgMatches) -> Result { - if matches.is_present(OPT_HUMAN_READABLE_BINARY) { - Ok(BlockSize::HumanReadableBinary) - } else if matches.is_present(OPT_HUMAN_READABLE_DECIMAL) { - Ok(BlockSize::HumanReadableDecimal) - } else if matches.is_present(OPT_BLOCKSIZE) { + if matches.is_present(OPT_BLOCKSIZE) { let s = matches.value_of(OPT_BLOCKSIZE).unwrap(); Ok(BlockSize::Bytes(parse_size(s)?)) } else { @@ -124,10 +184,8 @@ pub(crate) fn block_size_from_matches(matches: &ArgMatches) -> Result fmt::Result { match self { - Self::HumanReadableBinary => write!(f, "Size"), - Self::HumanReadableDecimal => write!(f, "Size"), Self::Bytes(n) => match to_magnitude_and_suffix(*n as u128) { - Ok(s) => write!(f, "{}-blocks", s), + Ok(s) => write!(f, "{}", s), Err(_) => Err(fmt::Error), }, } @@ -153,46 +211,45 @@ mod tests { ); } - // TODO We have not yet implemented this behavior, but when we do, - // uncomment this test. + #[test] + fn test_to_magnitude_and_suffix_not_powers_of_1024() { + assert_eq!(to_magnitude_and_suffix(1).unwrap(), "1B"); + assert_eq!(to_magnitude_and_suffix(999).unwrap(), "999B"); - // #[test] - // fn test_to_magnitude_and_suffix_not_powers_of_1024() { - // assert_eq!(to_magnitude_and_suffix(1).unwrap(), "1B"); - // assert_eq!(to_magnitude_and_suffix(999).unwrap(), "999B"); + assert_eq!(to_magnitude_and_suffix(1000).unwrap(), "1kB"); + assert_eq!(to_magnitude_and_suffix(1001).unwrap(), "1.1kB"); + assert_eq!(to_magnitude_and_suffix(1023).unwrap(), "1.1kB"); + assert_eq!(to_magnitude_and_suffix(1025).unwrap(), "1.1kB"); + assert_eq!(to_magnitude_and_suffix(999_000).unwrap(), "999kB"); - // assert_eq!(to_magnitude_and_suffix(1000).unwrap(), "1kB"); - // assert_eq!(to_magnitude_and_suffix(1001).unwrap(), "1.1kB"); - // assert_eq!(to_magnitude_and_suffix(1023).unwrap(), "1.1kB"); - // assert_eq!(to_magnitude_and_suffix(1025).unwrap(), "1.1kB"); - // assert_eq!(to_magnitude_and_suffix(999_000).unwrap(), "999kB"); + assert_eq!(to_magnitude_and_suffix(999_001).unwrap(), "1MB"); + assert_eq!(to_magnitude_and_suffix(999_999).unwrap(), "1MB"); + assert_eq!(to_magnitude_and_suffix(1_000_000).unwrap(), "1MB"); + assert_eq!(to_magnitude_and_suffix(1_000_001).unwrap(), "1.1MB"); + assert_eq!(to_magnitude_and_suffix(1_100_000).unwrap(), "1.1MB"); + assert_eq!(to_magnitude_and_suffix(1_100_001).unwrap(), "1.2MB"); + assert_eq!(to_magnitude_and_suffix(1_900_000).unwrap(), "1.9MB"); + assert_eq!(to_magnitude_and_suffix(1_900_001).unwrap(), "2MB"); + assert_eq!(to_magnitude_and_suffix(9_900_000).unwrap(), "9.9MB"); + assert_eq!(to_magnitude_and_suffix(9_900_001).unwrap(), "10MB"); + assert_eq!(to_magnitude_and_suffix(999_000_000).unwrap(), "999MB"); - // assert_eq!(to_magnitude_and_suffix(999_001).unwrap(), "1MB"); - // assert_eq!(to_magnitude_and_suffix(999_999).unwrap(), "1MB"); - // assert_eq!(to_magnitude_and_suffix(1_000_000).unwrap(), "1MB"); - // assert_eq!(to_magnitude_and_suffix(1_000_001).unwrap(), "1.1MB"); - // assert_eq!(to_magnitude_and_suffix(1_100_000).unwrap(), "1.1MB"); - // assert_eq!(to_magnitude_and_suffix(1_100_001).unwrap(), "1.2MB"); - // assert_eq!(to_magnitude_and_suffix(1_900_000).unwrap(), "1.9MB"); - // assert_eq!(to_magnitude_and_suffix(1_900_001).unwrap(), "2MB"); - // assert_eq!(to_magnitude_and_suffix(9_900_000).unwrap(), "9.9MB"); - // assert_eq!(to_magnitude_and_suffix(9_900_001).unwrap(), "10MB"); - // assert_eq!(to_magnitude_and_suffix(999_000_000).unwrap(), "999MB"); + assert_eq!(to_magnitude_and_suffix(999_000_001).unwrap(), "1GB"); + assert_eq!(to_magnitude_and_suffix(1_000_000_000).unwrap(), "1GB"); + assert_eq!(to_magnitude_and_suffix(1_000_000_001).unwrap(), "1.1GB"); + } - // assert_eq!(to_magnitude_and_suffix(999_000_001).unwrap(), "1GB"); - // assert_eq!(to_magnitude_and_suffix(1_000_000_000).unwrap(), "1GB"); - // // etc. - // } + #[test] + fn test_to_magnitude_and_suffix_multiples_of_1000_and_1024() { + assert_eq!(to_magnitude_and_suffix(128_000).unwrap(), "128kB"); + assert_eq!(to_magnitude_and_suffix(1000 * 1024).unwrap(), "1.1MB"); + assert_eq!(to_magnitude_and_suffix(1_000_000_000_000).unwrap(), "1TB"); + } #[test] fn test_block_size_display() { - assert_eq!(format!("{}", BlockSize::HumanReadableBinary), "Size"); - assert_eq!(format!("{}", BlockSize::HumanReadableDecimal), "Size"); - assert_eq!(format!("{}", BlockSize::Bytes(1024)), "1K-blocks"); - assert_eq!(format!("{}", BlockSize::Bytes(2 * 1024)), "2K-blocks"); - assert_eq!( - format!("{}", BlockSize::Bytes(3 * 1024 * 1024)), - "3M-blocks" - ); + assert_eq!(format!("{}", BlockSize::Bytes(1024)), "1K"); + assert_eq!(format!("{}", BlockSize::Bytes(2 * 1024)), "2K"); + assert_eq!(format!("{}", BlockSize::Bytes(3 * 1024 * 1024)), "3M"); } } diff --git a/src/uu/df/src/columns.rs b/src/uu/df/src/columns.rs index bd8cab438..70b660a0b 100644 --- a/src/uu/df/src/columns.rs +++ b/src/uu/df/src/columns.rs @@ -197,6 +197,7 @@ impl Column { match column { // 14 = length of "Filesystem" plus 4 spaces Self::Source => 14, + Self::Used => 5, // the shortest headers have a length of 4 chars so we use that as the minimum width _ => 4, } diff --git a/src/uu/df/src/df.rs b/src/uu/df/src/df.rs index 2b9c929c7..d9fb1be7b 100644 --- a/src/uu/df/src/df.rs +++ b/src/uu/df/src/df.rs @@ -11,9 +11,11 @@ mod columns; mod filesystem; mod table; +use blocks::{HumanReadable, SizeFormat}; use uucore::display::Quotable; use uucore::error::{UError, UResult, USimpleError}; use uucore::fsext::{read_fs_list, MountInfo}; +use uucore::parse_size::ParseSizeError; use uucore::{format_usage, show}; use clap::{crate_version, Arg, ArgMatches, Command}; @@ -61,6 +63,7 @@ static OUTPUT_FIELD_LIST: [&str; 12] = [ struct Options { show_local_fs: bool, show_all_fs: bool, + size_format: SizeFormat, block_size: BlockSize, /// Optional list of filesystem types to include in the output table. @@ -88,6 +91,7 @@ impl Default for Options { show_local_fs: Default::default(), show_all_fs: Default::default(), block_size: Default::default(), + size_format: Default::default(), include: Default::default(), exclude: Default::default(), show_total: Default::default(), @@ -105,7 +109,8 @@ impl Default for Options { #[derive(Debug)] enum OptionsError { - InvalidBlockSize, + BlockSizeTooLarge(String), + InvalidBlockSize(String), /// An error getting the columns to display in the output table. ColumnError(ColumnError), @@ -116,11 +121,14 @@ enum OptionsError { impl fmt::Display for OptionsError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - // TODO This should include the raw string provided as the argument. - // // TODO This needs to vary based on whether `--block-size` // or `-B` were provided. - Self::InvalidBlockSize => write!(f, "invalid --block-size argument"), + Self::BlockSizeTooLarge(s) => { + write!(f, "--block-size argument {} too large", s.quote()) + } + // TODO This needs to vary based on whether `--block-size` + // or `-B` were provided. + Self::InvalidBlockSize(s) => write!(f, "invalid --block-size argument {}", s), Self::ColumnError(ColumnError::MultipleColumns(s)) => write!( f, "option --output: field {} used more than once", @@ -155,8 +163,21 @@ impl Options { Ok(Self { show_local_fs: matches.is_present(OPT_LOCAL), show_all_fs: matches.is_present(OPT_ALL), - block_size: block_size_from_matches(matches) - .map_err(|_| OptionsError::InvalidBlockSize)?, + block_size: block_size_from_matches(matches).map_err(|e| match e { + ParseSizeError::SizeTooBig(_) => OptionsError::BlockSizeTooLarge( + matches.value_of(OPT_BLOCKSIZE).unwrap().to_string(), + ), + ParseSizeError::ParseFailure(s) => OptionsError::InvalidBlockSize(s), + })?, + size_format: { + if matches.is_present(OPT_HUMAN_READABLE_BINARY) { + SizeFormat::HumanReadable(HumanReadable::Binary) + } else if matches.is_present(OPT_HUMAN_READABLE_DECIMAL) { + SizeFormat::HumanReadable(HumanReadable::Decimal) + } else { + SizeFormat::StaticBlockSize + } + }, include, exclude, show_total: matches.is_present(OPT_TOTAL), @@ -424,6 +445,7 @@ pub fn uu_app<'a>() -> Command<'a> { .short('B') .long("block-size") .takes_value(true) + .value_name("SIZE") .overrides_with_all(&[OPT_KILO, OPT_BLOCKSIZE]) .help( "scale sizes by SIZE before printing them; e.g.\ @@ -480,6 +502,7 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(OPT_OUTPUT) .long("output") .takes_value(true) + .value_name("FIELD_LIST") .min_values(0) .require_equals(true) .use_value_delimiter(true) @@ -489,7 +512,7 @@ pub fn uu_app<'a>() -> Command<'a> { .default_values(&["source", "size", "used", "avail", "pcent", "target"]) .conflicts_with_all(&[OPT_INODES, OPT_PORTABILITY, OPT_PRINT_TYPE]) .help( - "use the output format defined by FIELD_LIST,\ + "use the output format defined by FIELD_LIST, \ or print all fields if FIELD_LIST is omitted.", ), ) @@ -512,6 +535,7 @@ pub fn uu_app<'a>() -> Command<'a> { .long("type") .allow_invalid_utf8(true) .takes_value(true) + .value_name("TYPE") .multiple_occurrences(true) .help("limit listing to file systems of type TYPE"), ) @@ -528,6 +552,7 @@ pub fn uu_app<'a>() -> Command<'a> { .long("exclude-type") .allow_invalid_utf8(true) .takes_value(true) + .value_name("TYPE") .use_value_delimiter(true) .multiple_occurrences(true) .help("limit listing to file systems not of type TYPE"), diff --git a/src/uu/df/src/table.rs b/src/uu/df/src/table.rs index 6b64ce02c..5309da38f 100644 --- a/src/uu/df/src/table.rs +++ b/src/uu/df/src/table.rs @@ -10,6 +10,7 @@ use number_prefix::NumberPrefix; use unicode_width::UnicodeWidthStr; +use crate::blocks::{HumanReadable, SizeFormat}; use crate::columns::{Alignment, Column}; use crate::filesystem::Filesystem; use crate::{BlockSize, Options}; @@ -213,15 +214,10 @@ impl<'a> RowFormatter<'a> { } /// Get a human readable string giving the scaled version of the input number. - /// - /// The scaling factor is defined in the `options` field. - /// - /// This function is supposed to be used by `scaled_bytes()` and `scaled_inodes()` only. - fn scaled_human_readable(&self, size: u64) -> String { - let number_prefix = match self.options.block_size { - BlockSize::HumanReadableDecimal => NumberPrefix::decimal(size as f64), - BlockSize::HumanReadableBinary => NumberPrefix::binary(size as f64), - _ => unreachable!(), + fn scaled_human_readable(&self, size: u64, human_readable: HumanReadable) -> String { + let number_prefix = match human_readable { + HumanReadable::Decimal => NumberPrefix::decimal(size as f64), + HumanReadable::Binary => NumberPrefix::binary(size as f64), }; match number_prefix { NumberPrefix::Standalone(bytes) => bytes.to_string(), @@ -233,10 +229,12 @@ impl<'a> RowFormatter<'a> { /// /// The scaling factor is defined in the `options` field. fn scaled_bytes(&self, size: u64) -> String { - if let BlockSize::Bytes(d) = self.options.block_size { - (size / d).to_string() - } else { - self.scaled_human_readable(size) + match self.options.size_format { + SizeFormat::HumanReadable(h) => self.scaled_human_readable(size, h), + SizeFormat::StaticBlockSize => { + let BlockSize::Bytes(d) = self.options.block_size; + (size / d).to_string() + } } } @@ -244,10 +242,9 @@ impl<'a> RowFormatter<'a> { /// /// The scaling factor is defined in the `options` field. fn scaled_inodes(&self, size: u64) -> String { - if let BlockSize::Bytes(_) = self.options.block_size { - size.to_string() - } else { - self.scaled_human_readable(size) + match self.options.size_format { + SizeFormat::HumanReadable(h) => self.scaled_human_readable(size, h), + SizeFormat::StaticBlockSize => size.to_string(), } } @@ -305,7 +302,12 @@ impl Header { for column in &options.columns { let header = match column { Column::Source => String::from("Filesystem"), - Column::Size => options.block_size.to_string(), + Column::Size => match options.size_format { + SizeFormat::HumanReadable(_) => String::from("Size"), + SizeFormat::StaticBlockSize => { + format!("{}-blocks", options.block_size) + } + }, Column::Used => String::from("Used"), Column::Avail => String::from("Available"), Column::Pcent => String::from("Use%"), @@ -424,6 +426,7 @@ impl fmt::Display for Table { #[cfg(test)] mod tests { + use crate::blocks::{HumanReadable, SizeFormat}; use crate::columns::Column; use crate::table::{Header, Row, RowFormatter}; use crate::{BlockSize, Options}; @@ -446,6 +449,30 @@ mod tests { Column::Target, ]; + impl Default for Row { + fn default() -> Self { + Self { + file: Some("/path/to/file".to_string()), + fs_device: "my_device".to_string(), + fs_type: "my_type".to_string(), + fs_mount: "my_mount".to_string(), + + bytes: 100, + bytes_used: 25, + bytes_avail: 75, + bytes_usage: Some(0.25), + + #[cfg(target_os = "macos")] + bytes_capacity: Some(0.5), + + inodes: 10, + inodes_used: 2, + inodes_free: 8, + inodes_usage: Some(0.2), + } + } + } + #[test] fn test_default_header() { let options = Default::default(); @@ -523,7 +550,7 @@ mod tests { #[test] fn test_header_with_human_readable_binary() { let options = Options { - block_size: BlockSize::HumanReadableBinary, + size_format: SizeFormat::HumanReadable(HumanReadable::Binary), ..Default::default() }; assert_eq!( @@ -542,7 +569,7 @@ mod tests { #[test] fn test_header_with_human_readable_si() { let options = Options { - block_size: BlockSize::HumanReadableDecimal, + size_format: SizeFormat::HumanReadable(HumanReadable::Decimal), ..Default::default() }; assert_eq!( @@ -565,9 +592,7 @@ mod tests { ..Default::default() }; let row = Row { - file: Some("/path/to/file".to_string()), fs_device: "my_device".to_string(), - fs_type: "my_type".to_string(), fs_mount: "my_mount".to_string(), bytes: 100, @@ -575,13 +600,7 @@ mod tests { bytes_avail: 75, bytes_usage: Some(0.25), - #[cfg(target_os = "macos")] - bytes_capacity: Some(0.5), - - inodes: 10, - inodes_used: 2, - inodes_free: 8, - inodes_usage: Some(0.2), + ..Default::default() }; let fmt = RowFormatter::new(&row, &options); assert_eq!( @@ -598,7 +617,6 @@ mod tests { ..Default::default() }; let row = Row { - file: Some("/path/to/file".to_string()), fs_device: "my_device".to_string(), fs_type: "my_type".to_string(), fs_mount: "my_mount".to_string(), @@ -608,13 +626,7 @@ mod tests { bytes_avail: 75, bytes_usage: Some(0.25), - #[cfg(target_os = "macos")] - bytes_capacity: Some(0.5), - - inodes: 10, - inodes_used: 2, - inodes_free: 8, - inodes_usage: Some(0.2), + ..Default::default() }; let fmt = RowFormatter::new(&row, &options); assert_eq!( @@ -631,23 +643,15 @@ mod tests { ..Default::default() }; let row = Row { - file: Some("/path/to/file".to_string()), fs_device: "my_device".to_string(), - fs_type: "my_type".to_string(), fs_mount: "my_mount".to_string(), - bytes: 100, - bytes_used: 25, - bytes_avail: 75, - bytes_usage: Some(0.25), - - #[cfg(target_os = "macos")] - bytes_capacity: Some(0.5), - inodes: 10, inodes_used: 2, inodes_free: 8, inodes_usage: Some(0.2), + + ..Default::default() }; let fmt = RowFormatter::new(&row, &options); assert_eq!( @@ -664,23 +668,9 @@ mod tests { ..Default::default() }; let row = Row { - file: Some("/path/to/file".to_string()), - fs_device: "my_device".to_string(), - fs_type: "my_type".to_string(), - fs_mount: "my_mount".to_string(), - bytes: 100, - bytes_used: 25, - bytes_avail: 75, - bytes_usage: Some(0.25), - - #[cfg(target_os = "macos")] - bytes_capacity: Some(0.5), - inodes: 10, - inodes_used: 2, - inodes_free: 8, - inodes_usage: Some(0.2), + ..Default::default() }; let fmt = RowFormatter::new(&row, &options); assert_eq!(fmt.get_values(), vec!("1", "10")); @@ -689,12 +679,11 @@ mod tests { #[test] fn test_row_formatter_with_human_readable_si() { let options = Options { - block_size: BlockSize::HumanReadableDecimal, + size_format: SizeFormat::HumanReadable(HumanReadable::Decimal), columns: COLUMNS_WITH_FS_TYPE.to_vec(), ..Default::default() }; let row = Row { - file: Some("/path/to/file".to_string()), fs_device: "my_device".to_string(), fs_type: "my_type".to_string(), fs_mount: "my_mount".to_string(), @@ -704,13 +693,7 @@ mod tests { bytes_avail: 3000, bytes_usage: Some(0.25), - #[cfg(target_os = "macos")] - bytes_capacity: Some(0.5), - - inodes: 10, - inodes_used: 2, - inodes_free: 8, - inodes_usage: Some(0.2), + ..Default::default() }; let fmt = RowFormatter::new(&row, &options); assert_eq!( @@ -730,12 +713,11 @@ mod tests { #[test] fn test_row_formatter_with_human_readable_binary() { let options = Options { - block_size: BlockSize::HumanReadableBinary, + size_format: SizeFormat::HumanReadable(HumanReadable::Binary), columns: COLUMNS_WITH_FS_TYPE.to_vec(), ..Default::default() }; let row = Row { - file: Some("/path/to/file".to_string()), fs_device: "my_device".to_string(), fs_type: "my_type".to_string(), fs_mount: "my_mount".to_string(), @@ -745,13 +727,7 @@ mod tests { bytes_avail: 3072, bytes_usage: Some(0.25), - #[cfg(target_os = "macos")] - bytes_capacity: Some(0.5), - - inodes: 10, - inodes_used: 2, - inodes_free: 8, - inodes_usage: Some(0.2), + ..Default::default() }; let fmt = RowFormatter::new(&row, &options); assert_eq!( @@ -771,32 +747,14 @@ mod tests { #[test] fn test_row_formatter_with_round_up_usage() { let options = Options { - block_size: BlockSize::Bytes(1), + columns: vec![Column::Pcent], ..Default::default() }; let row = Row { - file: Some("/path/to/file".to_string()), - fs_device: "my_device".to_string(), - fs_type: "my_type".to_string(), - fs_mount: "my_mount".to_string(), - - bytes: 100, - bytes_used: 25, - bytes_avail: 75, bytes_usage: Some(0.251), - - #[cfg(target_os = "macos")] - bytes_capacity: Some(0.5), - - inodes: 10, - inodes_used: 2, - inodes_free: 8, - inodes_usage: Some(0.2), + ..Default::default() }; let fmt = RowFormatter::new(&row, &options); - assert_eq!( - fmt.get_values(), - vec!("my_device", "100", "25", "75", "26%", "my_mount") - ); + assert_eq!(fmt.get_values(), vec!("26%")); } } diff --git a/src/uu/du/src/du.rs b/src/uu/du/src/du.rs index ff7a5a5b7..b29a938a4 100644 --- a/src/uu/du/src/du.rs +++ b/src/uu/du/src/du.rs @@ -20,7 +20,7 @@ use std::fs::File; use std::fs::Metadata; use std::io::BufRead; use std::io::BufReader; -use std::io::{ErrorKind, Result}; +use std::io::Result; use std::iter; #[cfg(not(windows))] use std::os::unix::fs::MetadataExt; @@ -34,7 +34,8 @@ use std::str::FromStr; use std::time::{Duration, UNIX_EPOCH}; use std::{error::Error, fmt::Display}; use uucore::display::{print_verbatim, Quotable}; -use uucore::error::{set_exit_code, UError, UResult}; +use uucore::error::FromIo; +use uucore::error::{UError, UResult}; use uucore::format_usage; use uucore::parse_size::{parse_size, ParseSizeError}; use uucore::InvalidEncodingHandling; @@ -102,7 +103,6 @@ const UNITS: [(char, u32); 6] = [('E', 6), ('P', 5), ('T', 4), ('G', 3), ('M', 2 struct Options { all: bool, - util_name: String, max_depth: Option, total: bool, separate_dirs: bool, @@ -309,13 +309,9 @@ fn du( let read = match fs::read_dir(&my_stat.path) { Ok(read) => read, Err(e) => { - eprintln!( - "{}: cannot read directory {}: {}", - options.util_name, - my_stat.path.quote(), - e + show!( + e.map_err_context(|| format!("cannot read directory {}", my_stat.path.quote())) ); - set_exit_code(1); return Box::new(iter::once(my_stat)); } }; @@ -368,18 +364,9 @@ fn du( } } } - Err(error) => match error.kind() { - ErrorKind::PermissionDenied => { - let description = format!("cannot access {}", entry.path().quote()); - let error_message = "Permission denied"; - show_error_custom_description!(description, "{}", error_message); - set_exit_code(1); - } - _ => { - set_exit_code(1); - show_error!("cannot access {}: {}", entry.path().quote(), error); - } - }, + Err(e) => show!( + e.map_err_context(|| format!("cannot access {}", entry.path().quote())) + ), } } Err(error) => show_error!("{}", error), @@ -567,7 +554,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let options = Options { all: matches.is_present(options::ALL), - util_name: uucore::util_name().to_owned(), max_depth, total: matches.is_present(options::TOTAL), separate_dirs: matches.is_present(options::SEPARATE_DIRS), diff --git a/src/uu/expr/Cargo.toml b/src/uu/expr/Cargo.toml index c4a1bae4a..9ea8008af 100644 --- a/src/uu/expr/Cargo.toml +++ b/src/uu/expr/Cargo.toml @@ -17,7 +17,7 @@ path = "src/expr.rs" [dependencies] clap = { version = "3.1", features = ["wrap_help", "cargo"] } num-bigint = "0.4.0" -num-traits = "0.2.14" +num-traits = "0.2.15" onig = { version = "~6.3", default-features = false } uucore = { version=">=0.0.11", package="uucore", path="../../uucore" } diff --git a/src/uu/factor/Cargo.toml b/src/uu/factor/Cargo.toml index e1620ed90..20a21ac00 100644 --- a/src/uu/factor/Cargo.toml +++ b/src/uu/factor/Cargo.toml @@ -12,12 +12,12 @@ categories = ["command-line-utilities"] edition = "2021" [build-dependencies] -num-traits = "0.2.13" # used in src/numerics.rs, which is included by build.rs +num-traits = "0.2.15" # used in src/numerics.rs, which is included by build.rs [dependencies] clap = { version = "3.1", features = ["wrap_help", "cargo"] } coz = { version = "0.1.3", optional = true } -num-traits = "0.2.13" # Needs at least version 0.2.13 for "OverflowingAdd" +num-traits = "0.2.15" # Needs at least version 0.2.15 for "OverflowingAdd" rand = { version = "0.8", features = ["small_rng"] } smallvec = "1.7" # TODO(nicoo): Use `union` feature, requires Rust 1.49 or later. uucore = { version = ">=0.0.8", package = "uucore", path = "../../uucore" } diff --git a/src/uu/mktemp/src/mktemp.rs b/src/uu/mktemp/src/mktemp.rs index f999d6675..b14318679 100644 --- a/src/uu/mktemp/src/mktemp.rs +++ b/src/uu/mktemp/src/mktemp.rs @@ -205,20 +205,41 @@ pub fn uu_app<'a>() -> Command<'a> { ) } +/// Parse a template string into prefix, suffix, and random components. +/// +/// `temp` is the template string, with three or more consecutive `X`s +/// representing a placeholder for randomly generated characters (for +/// example, `"abc_XXX.txt"`). If `temp` ends in an `X`, then a suffix +/// can be specified by `suffix` instead. +/// +/// # Errors +/// +/// * If there are fewer than three consecutive `X`s in `temp`. +/// * If `suffix` is a [`Some`] object but `temp` does not end in `X`. +/// * If the suffix (specified either way) contains a path separator. +/// +/// # Examples +/// +/// ```rust,ignore +/// assert_eq!(parse_template("XXX", None).unwrap(), ("", 3, "")); +/// assert_eq!(parse_template("abcXXX", None).unwrap(), ("abc", 3, "")); +/// assert_eq!(parse_template("XXXdef", None).unwrap(), ("", 3, "def")); +/// assert_eq!(parse_template("abcXXXdef", None).unwrap(), ("abc", 3, "def")); +/// ``` fn parse_template<'a>( temp: &'a str, suffix: Option<&'a str>, -) -> UResult<(&'a str, usize, &'a str)> { +) -> Result<(&'a str, usize, &'a str), MkTempError> { let right = match temp.rfind('X') { Some(r) => r + 1, - None => return Err(MkTempError::TooFewXs(temp.into()).into()), + None => return Err(MkTempError::TooFewXs(temp.into())), }; let left = temp[..right].rfind(|c| c != 'X').map_or(0, |i| i + 1); let prefix = &temp[..left]; let rand = right - left; if rand < 3 { - return Err(MkTempError::TooFewXs(temp.into()).into()); + return Err(MkTempError::TooFewXs(temp.into())); } let mut suf = &temp[right..]; @@ -227,12 +248,12 @@ fn parse_template<'a>( if suf.is_empty() { suf = s; } else { - return Err(MkTempError::MustEndInX(temp.into()).into()); + return Err(MkTempError::MustEndInX(temp.into())); } }; if suf.chars().any(is_separator) { - return Err(MkTempError::ContainsDirSeparator(suf.into()).into()); + return Err(MkTempError::ContainsDirSeparator(suf.into())); } Ok((prefix, rand, suf)) @@ -304,3 +325,41 @@ fn exec(dir: &Path, prefix: &str, rand: usize, suffix: &str, make_dir: bool) -> println_verbatim(path).map_err_context(|| "failed to print directory name".to_owned()) } + +#[cfg(test)] +mod tests { + use crate::parse_template; + + #[test] + fn test_parse_template_no_suffix() { + assert_eq!(parse_template("XXX", None).unwrap(), ("", 3, "")); + assert_eq!(parse_template("abcXXX", None).unwrap(), ("abc", 3, "")); + assert_eq!(parse_template("XXXdef", None).unwrap(), ("", 3, "def")); + assert_eq!( + parse_template("abcXXXdef", None).unwrap(), + ("abc", 3, "def") + ); + } + + #[test] + fn test_parse_template_suffix() { + assert_eq!(parse_template("XXX", Some("def")).unwrap(), ("", 3, "def")); + assert_eq!( + parse_template("abcXXX", Some("def")).unwrap(), + ("abc", 3, "def") + ); + } + + #[test] + fn test_parse_template_errors() { + // TODO This should be an error as well, but we are not + // catching it just yet. A future commit will correct this. + // + // assert!(parse_template("a/bXXX", None).is_err()); + // + assert!(parse_template("XXXa/b", None).is_err()); + assert!(parse_template("XX", None).is_err()); + assert!(parse_template("XXXabc", Some("def")).is_err()); + assert!(parse_template("XXX", Some("a/b")).is_err()); + } +} diff --git a/src/uu/nice/src/nice.rs b/src/uu/nice/src/nice.rs index b75dd979e..e78de828b 100644 --- a/src/uu/nice/src/nice.rs +++ b/src/uu/nice/src/nice.rs @@ -17,7 +17,7 @@ use std::ptr; use clap::{crate_version, Arg, Command}; use uucore::{ - error::{set_exit_code, UResult, USimpleError, UUsageError}, + error::{set_exit_code, UClapError, UResult, USimpleError, UUsageError}, format_usage, }; @@ -35,7 +35,7 @@ const USAGE: &str = "{} [OPTIONS] [COMMAND [ARGS]]"; #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { - let matches = uu_app().get_matches_from(args); + let matches = uu_app().try_get_matches_from(args).with_exit_code(125)?; let mut niceness = unsafe { nix::errno::Errno::clear(); diff --git a/src/uu/seq/Cargo.toml b/src/uu/seq/Cargo.toml index ad8bba5b6..67226093d 100644 --- a/src/uu/seq/Cargo.toml +++ b/src/uu/seq/Cargo.toml @@ -19,7 +19,7 @@ path = "src/seq.rs" bigdecimal = "0.3" clap = { version = "3.1", features = ["wrap_help", "cargo"] } num-bigint = "0.4.0" -num-traits = "0.2.14" +num-traits = "0.2.15" uucore = { version=">=0.0.11", package="uucore", path="../../uucore", features=["memo"] } [[bin]] diff --git a/src/uucore/src/lib/mods/error.rs b/src/uucore/src/lib/mods/error.rs index dbe4d5bc1..1af6ef781 100644 --- a/src/uucore/src/lib/mods/error.rs +++ b/src/uucore/src/lib/mods/error.rs @@ -617,12 +617,75 @@ impl From for Box { } } -/// Implementations for clap::Error -impl UError for clap::Error { +/// A wrapper for `clap::Error` that implements [`UError`] +/// +/// Contains a custom error code. When `Display::fmt` is called on this struct +/// the [`clap::Error`] will be printed _directly to `stdout` or `stderr`_. +/// This is because `clap` only supports colored output when it prints directly. +/// +/// [`ClapErrorWrapper`] is generally created by calling the +/// [`UClapError::with_exit_code`] method on [`clap::Error`] or using the [`From`] +/// implementation from [`clap::Error`] to `Box`, which constructs +/// a [`ClapErrorWrapper`] with an exit code of `1`. +/// +/// ```rust +/// use uucore::error::{ClapErrorWrapper, UError, UClapError}; +/// let command = clap::Command::new("test"); +/// let result: Result<_, ClapErrorWrapper> = command.try_get_matches().with_exit_code(125); +/// +/// let command = clap::Command::new("test"); +/// let result: Result<_, Box> = command.try_get_matches().map_err(Into::into); +/// ``` +#[derive(Debug)] +pub struct ClapErrorWrapper { + code: i32, + error: clap::Error, +} + +/// Extension trait for `clap::Error` to adjust the exit code. +pub trait UClapError { + fn with_exit_code(self, code: i32) -> T; +} + +impl From for Box { + fn from(e: clap::Error) -> Self { + Box::new(ClapErrorWrapper { code: 1, error: e }) + } +} + +impl UClapError for clap::Error { + fn with_exit_code(self, code: i32) -> ClapErrorWrapper { + ClapErrorWrapper { code, error: self } + } +} + +impl UClapError> + for Result +{ + fn with_exit_code(self, code: i32) -> Result { + self.map_err(|e| e.with_exit_code(code)) + } +} + +impl UError for ClapErrorWrapper { fn code(&self) -> i32 { - match self.kind() { - clap::ErrorKind::DisplayHelp | clap::ErrorKind::DisplayVersion => 0, - _ => 1, + // If the error is a DisplayHelp or DisplayVersion variant, + // we don't want to apply the custom error code, but leave + // it 0. + if let clap::ErrorKind::DisplayHelp | clap::ErrorKind::DisplayVersion = self.error.kind() { + 0 + } else { + self.code } } } + +impl Error for ClapErrorWrapper {} + +// This is abuse of the Display trait +impl Display for ClapErrorWrapper { + fn fmt(&self, _f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + self.error.print().unwrap(); + Ok(()) + } +} diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index 90e85b76a..06134d2dd 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -1506,6 +1506,18 @@ fn test_copy_through_dangling_symlink() { .stderr_only("cp: not writing through dangling symlink 'target'"); } +#[test] +fn test_copy_through_dangling_symlink_no_dereference() { + let (at, mut ucmd) = at_and_ucmd!(); + at.symlink_file("no-such-file", "dangle"); + ucmd.arg("-P") + .arg("dangle") + .arg("d2") + .succeeds() + .no_stderr() + .no_stdout(); +} + #[test] #[cfg(unix)] fn test_cp_archive_on_nonexistent_file() { diff --git a/tests/by-util/test_df.rs b/tests/by-util/test_df.rs index 61ddcec5d..511956ec4 100644 --- a/tests/by-util/test_df.rs +++ b/tests/by-util/test_df.rs @@ -392,6 +392,11 @@ fn test_block_size_1024() { assert_eq!(get_header(2 * 1024 * 1024), "2M-blocks"); assert_eq!(get_header(1024 * 1024 * 1024), "1G-blocks"); assert_eq!(get_header(34 * 1024 * 1024 * 1024), "34G-blocks"); + + // multiples of both 1024 and 1000 + assert_eq!(get_header(128_000), "128kB-blocks"); + assert_eq!(get_header(1000 * 1024), "1.1MB-blocks"); + assert_eq!(get_header(1_000_000_000_000), "1TB-blocks"); } #[test] @@ -413,10 +418,33 @@ fn test_block_size_with_suffix() { assert_eq!(get_header("1KiB"), "1K-blocks"); assert_eq!(get_header("1MiB"), "1M-blocks"); assert_eq!(get_header("1GiB"), "1G-blocks"); - // TODO enable the following asserts when #3193 is resolved - //assert_eq!(get_header("1KB"), "1kB-blocks"); - //assert_eq!(get_header("1MB"), "1MB-blocks"); - //assert_eq!(get_header("1GB"), "1GB-blocks"); + assert_eq!(get_header("1KB"), "1kB-blocks"); + assert_eq!(get_header("1MB"), "1MB-blocks"); + assert_eq!(get_header("1GB"), "1GB-blocks"); +} + +#[test] +fn test_too_large_block_size() { + fn run_command(size: &str) { + new_ucmd!() + .arg(format!("--block-size={}", size)) + .fails() + .stderr_contains(format!("--block-size argument '{}' too large", size)); + } + + let too_large_sizes = vec!["1Y", "1Z"]; + + for size in too_large_sizes { + run_command(size); + } +} + +#[test] +fn test_invalid_block_size() { + new_ucmd!() + .arg("--block-size=x") + .fails() + .stderr_contains("invalid --block-size argument 'x'"); } #[test] diff --git a/tests/by-util/test_du.rs b/tests/by-util/test_du.rs index 254e75166..bf506c8b5 100644 --- a/tests/by-util/test_du.rs +++ b/tests/by-util/test_du.rs @@ -435,9 +435,7 @@ fn test_du_no_permission() { ts.ccmd("chmod").arg("-r").arg(SUB_DIR_LINKS).succeeds(); let result = ts.ucmd().arg(SUB_DIR_LINKS).fails(); - result.stderr_contains( - "du: cannot read directory 'subdir/links': Permission denied (os error 13)", - ); + result.stderr_contains("du: cannot read directory 'subdir/links': Permission denied"); #[cfg(any(target_os = "linux", target_os = "android"))] { diff --git a/tests/by-util/test_nice.rs b/tests/by-util/test_nice.rs index 2b53ed437..036723cde 100644 --- a/tests/by-util/test_nice.rs +++ b/tests/by-util/test_nice.rs @@ -58,3 +58,8 @@ fn test_command_where_command_takes_n_flag() { .run() .stdout_is("a"); } + +#[test] +fn test_invalid_argument() { + new_ucmd!().arg("--invalid").fails().code_is(125); +} diff --git a/tests/by-util/test_stat.rs b/tests/by-util/test_stat.rs index 0871a48fe..f1a687981 100644 --- a/tests/by-util/test_stat.rs +++ b/tests/by-util/test_stat.rs @@ -345,3 +345,58 @@ fn test_printf() { let expected_stdout = unwrap_or_return!(expected_result(&ts, &args)).stdout_move_str(); ts.ucmd().args(&args).succeeds().stdout_is(expected_stdout); } + +#[cfg(unix)] +#[test] +#[cfg(disable_until_fixed)] +fn test_stdin_pipe_fifo1() { + // $ echo | stat - + // File: - + // Size: 0 Blocks: 0 IO Block: 4096 fifo + // use std::process::{Command, Stdio}; + new_ucmd!() + .arg("-") + .set_stdin(std::process::Stdio::piped()) + .run() + .no_stderr() + .stdout_contains("fifo") + .stdout_contains("File: -") + .succeeded(); +} + +#[cfg(unix)] +#[test] +#[cfg(disable_until_fixed)] +fn test_stdin_pipe_fifo2() { + // $ stat - + // File: - + // Size: 0 Blocks: 0 IO Block: 1024 character special file + new_ucmd!() + .arg("-") + .run() + .no_stderr() + .stdout_contains("character special file") + .stdout_contains("File: -") + .succeeded(); +} + +#[cfg(unix)] +#[test] +#[cfg(disable_until_fixed)] +fn test_stdin_redirect() { + // $ touch f && stat - < f + // File: - + // Size: 0 Blocks: 0 IO Block: 4096 regular empty file + + let ts = TestScenario::new(util_name!()); + let at = &ts.fixtures; + at.touch("f"); + new_ucmd!() + .arg("-") + .set_stdin(std::fs::File::open("f").unwrap()) + .run() + .no_stderr() + .stdout_contains("regular empty file") + .stdout_contains("File: -") + .succeeded(); +} diff --git a/tests/common/util.rs b/tests/common/util.rs index 5a669fcd4..34bdc72f3 100644 --- a/tests/common/util.rs +++ b/tests/common/util.rs @@ -1362,6 +1362,60 @@ pub fn expected_result(ts: &TestScenario, args: &[&str]) -> std::result::Result< )) } +/// This is a convenience wrapper to run a ucmd with root permissions. +/// It can be used to test programs when being root is needed +/// This runs 'sudo -E --non-interactive target/debug/coreutils util_name args` +/// This is primarily designed to run in an environment where whoami is in $path +/// and where non-interactive sudo is possible. +/// To check if i) non-interactive sudo is possible and ii) if sudo works, this runs: +/// 'sudo -E --non-interactive whoami' first. +/// +/// Example: +/// +/// ```no_run +/// use crate::common::util::*; +/// #[test] +/// fn test_xyz() { +/// let ts = TestScenario::new("whoami"); +/// let expected = "root\n".to_string(); +/// if let Ok(result) = run_ucmd_as_root(&ts, &[]) { +/// result.stdout_is(expected); +/// } else { +/// print!("TEST SKIPPED"); +/// } +/// } +///``` +#[cfg(unix)] +pub fn run_ucmd_as_root( + ts: &TestScenario, + args: &[&str], +) -> std::result::Result { + // Apparently CICD environment has no `sudo`? + if ts + .cmd_keepenv("sudo") + .env("LC_ALL", "C") + .arg("-E") + .arg("--non-interactive") + .arg("whoami") + .run() + .stdout_str() + .trim() + != "root" + { + Err("\"sudo whoami\" didn't return \"root\"".to_string()) + } else { + Ok(ts + .cmd_keepenv("sudo") + .env("LC_ALL", "C") + .arg("-E") + .arg("--non-interactive") + .arg(&ts.bin_path) + .arg(&ts.util_name) + .args(args) + .run()) + } +} + /// Sanity checks for test utils #[cfg(test)] mod tests { @@ -1712,4 +1766,23 @@ mod tests { std::assert_eq!(host_name_for("gwho"), "gwho"); } } + + #[test] + #[cfg(unix)] + #[cfg(feature = "whoami")] + fn test_run_ucmd_as_root() { + // Skip test if we can't guarantee non-interactive `sudo`. + if let Ok(_status) = Command::new("sudo") + .args(&["-E", "-v", "--non-interactive"]) + .status() + { + let ts = TestScenario::new("whoami"); + std::assert_eq!( + run_ucmd_as_root(&ts, &[]).unwrap().stdout_str().trim(), + "root" + ); + } else { + print!("TEST SKIPPED"); + } + } }