mirror of
https://github.com/uutils/coreutils
synced 2024-11-16 01:38:04 +00:00
Merge branch 'main' into mktemp-set-dir-mode
This commit is contained in:
commit
2086d04996
88 changed files with 725 additions and 221 deletions
44
.github/workflows/CICD.yml
vendored
44
.github/workflows/CICD.yml
vendored
|
@ -24,7 +24,7 @@ jobs:
|
|||
name: Style/cargo-deny
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||
|
||||
style_deps:
|
||||
|
@ -43,7 +43,7 @@ jobs:
|
|||
- { os: macos-latest , features: "feat_Tier1,feat_require_unix,feat_require_unix_utmpx" }
|
||||
- { os: windows-latest , features: feat_os_windows }
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Initialize workflow variables
|
||||
id: vars
|
||||
|
@ -101,7 +101,7 @@ jobs:
|
|||
job:
|
||||
- { os: ubuntu-latest , features: feat_os_unix }
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Initialize workflow variables
|
||||
id: vars
|
||||
|
@ -165,7 +165,7 @@ jobs:
|
|||
- { os: macos-latest , features: feat_os_macos }
|
||||
- { os: windows-latest , features: feat_os_windows }
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Initialize workflow variables
|
||||
id: vars
|
||||
|
@ -223,7 +223,7 @@ jobs:
|
|||
job:
|
||||
- { os: ubuntu-latest , features: feat_os_unix }
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Initialize workflow variables
|
||||
id: vars
|
||||
|
@ -275,7 +275,7 @@ jobs:
|
|||
# - { os: macos-latest , features: feat_os_macos }
|
||||
# - { os: windows-latest , features: feat_os_windows }
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Initialize workflow variables
|
||||
id: vars
|
||||
|
@ -320,7 +320,7 @@ jobs:
|
|||
job:
|
||||
- { os: ubuntu-latest , features: feat_os_unix }
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Initialize workflow variables
|
||||
id: vars
|
||||
|
@ -397,7 +397,7 @@ jobs:
|
|||
job:
|
||||
- { os: ubuntu-latest , features: feat_os_unix }
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install `rust` toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
|
@ -422,7 +422,7 @@ jobs:
|
|||
job:
|
||||
- { os: ubuntu-latest , features: feat_os_unix }
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install `rust` toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
|
@ -452,7 +452,7 @@ jobs:
|
|||
- { os: macos-latest , features: feat_os_macos }
|
||||
- { os: windows-latest , features: feat_os_windows }
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install `rust` toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
|
@ -478,7 +478,7 @@ jobs:
|
|||
- { os: macos-latest , features: feat_os_macos }
|
||||
- { os: windows-latest , features: feat_os_windows }
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install `rust` toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
|
@ -502,7 +502,7 @@ jobs:
|
|||
job:
|
||||
- { os: ubuntu-latest , features: feat_os_unix }
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
|
@ -534,7 +534,7 @@ jobs:
|
|||
--arg size "$SIZE" \
|
||||
--arg multisize "$SIZEMULTI" \
|
||||
'{($date): { sha: $sha, size: $size, multisize: $multisize, }}' > size-result.json
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: size-result
|
||||
path: size-result.json
|
||||
|
@ -568,7 +568,7 @@ jobs:
|
|||
- { os: windows-latest , target: x86_64-pc-windows-gnu , features: feat_os_windows } ## note: requires rust >= 1.43.0 to link correctly
|
||||
- { os: windows-latest , target: x86_64-pc-windows-msvc , features: feat_os_windows }
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Initialize workflow variables
|
||||
id: vars
|
||||
|
@ -762,7 +762,7 @@ jobs:
|
|||
args: --target=${{ matrix.job.target }} ${{ steps.vars.outputs.CARGO_TEST_OPTIONS}} ${{ matrix.job.cargo-options }} ${{ steps.dep_vars.outputs.CARGO_UTILITY_LIST_OPTIONS }}
|
||||
toolchain: ${{ env.RUST_MIN_SRV }}
|
||||
- name: Archive executable artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.PROJECT_NAME }}-${{ matrix.job.target }}
|
||||
path: target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}
|
||||
|
@ -820,7 +820,7 @@ jobs:
|
|||
job:
|
||||
- { os: ubuntu-latest }
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install/setup prerequisites
|
||||
shell: bash
|
||||
|
@ -857,9 +857,9 @@ jobs:
|
|||
env:
|
||||
TERMUX: v0.118.0
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: AVD cache
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
id: avd-cache
|
||||
with:
|
||||
path: |
|
||||
|
@ -911,11 +911,11 @@ jobs:
|
|||
env:
|
||||
mem: 2048
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Prepare, build and test
|
||||
## spell-checker:ignore (ToDO) sshfs usesh vmactions
|
||||
uses: vmactions/freebsd-vm@v0.1.5
|
||||
uses: vmactions/freebsd-vm@v0.1.6
|
||||
with:
|
||||
usesh: true
|
||||
# sync: sshfs
|
||||
|
@ -979,7 +979,7 @@ jobs:
|
|||
- { os: macos-latest , features: macos }
|
||||
- { os: windows-latest , features: windows }
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
# - name: Reattach HEAD ## may be needed for accurate code coverage info
|
||||
# run: git checkout ${{ github.head_ref }}
|
||||
|
@ -1100,7 +1100,7 @@ jobs:
|
|||
grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()"
|
||||
echo ::set-output name=report::${COVERAGE_REPORT_FILE}
|
||||
- name: Upload coverage results (to Codecov.io)
|
||||
uses: codecov/codecov-action@v1
|
||||
uses: codecov/codecov-action@v3
|
||||
# if: steps.vars.outputs.HAS_CODECOV_TOKEN
|
||||
with:
|
||||
# token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
|
8
.github/workflows/FixPR.yml
vendored
8
.github/workflows/FixPR.yml
vendored
|
@ -28,7 +28,7 @@ jobs:
|
|||
job:
|
||||
- { os: ubuntu-latest , features: feat_os_unix }
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Initialize job variables
|
||||
id: vars
|
||||
|
@ -80,7 +80,7 @@ jobs:
|
|||
## * using the 'stable' toolchain is necessary to avoid "unexpected '--filter-platform'" errors
|
||||
RUSTUP_TOOLCHAIN=stable cargo-tree tree --locked --all --no-dev-dependencies --no-indent --features ${{ matrix.job.features }} | grep -vE "$PWD" | sort --unique
|
||||
- name: Commit any changes (to '${{ env.BRANCH_TARGET }}')
|
||||
uses: EndBug/add-and-commit@v7
|
||||
uses: EndBug/add-and-commit@v9
|
||||
with:
|
||||
branch: ${{ env.BRANCH_TARGET }}
|
||||
default_author: github_actions
|
||||
|
@ -100,7 +100,7 @@ jobs:
|
|||
job:
|
||||
- { os: ubuntu-latest , features: feat_os_unix }
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Initialize job variables
|
||||
id: vars
|
||||
|
@ -130,7 +130,7 @@ jobs:
|
|||
# `cargo fmt` of tests
|
||||
find tests -name "*.rs" -print0 | xargs -0 cargo fmt --
|
||||
- name: Commit any changes (to '${{ env.BRANCH_TARGET }}')
|
||||
uses: EndBug/add-and-commit@v7
|
||||
uses: EndBug/add-and-commit@v9
|
||||
with:
|
||||
branch: ${{ env.BRANCH_TARGET }}
|
||||
default_author: github_actions
|
||||
|
|
18
.github/workflows/GnuTests.yml
vendored
18
.github/workflows/GnuTests.yml
vendored
|
@ -41,11 +41,11 @@ jobs:
|
|||
TEST_FULL_SUMMARY_FILE='gnu-full-result.json'
|
||||
outputs SUITE_LOG_FILE TEST_FILESET_PREFIX TEST_FILESET_SUFFIX TEST_LOGS_GLOB TEST_SUMMARY_FILE TEST_FULL_SUMMARY_FILE
|
||||
- name: Checkout code (uutil)
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: '${{ steps.vars.outputs.path_UUTILS }}'
|
||||
- name: Checkout code (GNU coreutils)
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: 'coreutils/coreutils'
|
||||
path: '${{ steps.vars.outputs.path_GNU }}'
|
||||
|
@ -146,22 +146,22 @@ jobs:
|
|||
# Compress logs before upload (fails otherwise)
|
||||
gzip ${{ steps.vars.outputs.TEST_LOGS_GLOB }}
|
||||
- name: Reserve SHA1/ID of 'test-summary'
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: "${{ steps.summary.outputs.HASH }}"
|
||||
path: "${{ steps.vars.outputs.TEST_SUMMARY_FILE }}"
|
||||
- name: Reserve test results summary
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: test-summary
|
||||
path: "${{ steps.vars.outputs.TEST_SUMMARY_FILE }}"
|
||||
- name: Reserve test logs
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: test-logs
|
||||
path: "${{ steps.vars.outputs.TEST_LOGS_GLOB }}"
|
||||
- name: Upload full json results
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: gnu-full-result.json
|
||||
path: ${{ steps.vars.outputs.TEST_FULL_SUMMARY_FILE }}
|
||||
|
@ -229,11 +229,11 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code uutil
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: 'uutils'
|
||||
- name: Checkout GNU coreutils
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: 'coreutils/coreutils'
|
||||
path: 'gnu'
|
||||
|
@ -293,7 +293,7 @@ jobs:
|
|||
grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()"
|
||||
echo ::set-output name=report::${COVERAGE_REPORT_FILE}
|
||||
- name: Upload coverage results (to Codecov.io)
|
||||
uses: codecov/codecov-action@v2
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
file: ${{ steps.coverage.outputs.report }}
|
||||
flags: gnutests
|
||||
|
|
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -273,9 +273,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "3.1.3"
|
||||
version = "3.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d7ca9141e27e6ebc52e3c378b0c07f3cea52db46ed1cc5861735fb697b56356"
|
||||
checksum = "da92e6facd8d73c22745a5d3cbb59bdf8e46e3235c923e516527d8e81eec14a4"
|
||||
dependencies = [
|
||||
"clap 3.1.15",
|
||||
]
|
||||
|
@ -2027,9 +2027,9 @@ checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
|||
|
||||
[[package]]
|
||||
name = "unindent"
|
||||
version = "0.1.8"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "514672a55d7380da379785a4d70ca8386c8883ff7eaae877be4d2081cebe73d8"
|
||||
checksum = "52fee519a3e570f7df377a06a1a7775cdbfb7aa460be7e08de2b1f0e69973a44"
|
||||
|
||||
[[package]]
|
||||
name = "unix_socket"
|
||||
|
|
|
@ -123,7 +123,12 @@ pub fn base_app<'a>(about: &'a str, usage: &'a str) -> Command<'a> {
|
|||
)
|
||||
// "multiple" arguments are used to check whether there is more than one
|
||||
// file passed in.
|
||||
.arg(Arg::new(options::FILE).index(1).multiple_occurrences(true))
|
||||
.arg(
|
||||
Arg::new(options::FILE)
|
||||
.index(1)
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_input<'a>(config: &Config, stdin_ref: &'a Stdin) -> UResult<Box<dyn Read + 'a>> {
|
||||
|
|
|
@ -102,6 +102,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(options::NAME)
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::AnyPath)
|
||||
.hide(true),
|
||||
)
|
||||
.arg(
|
||||
|
|
|
@ -249,7 +249,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(options::FILE)
|
||||
.hide(true)
|
||||
.multiple_occurrences(true),
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::SHOW_ALL)
|
||||
|
|
|
@ -197,6 +197,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.long(options::REFERENCE)
|
||||
.takes_value(true)
|
||||
.value_name("RFILE")
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.conflicts_with_all(&[options::USER, options::ROLE, options::TYPE, options::RANGE])
|
||||
.help(
|
||||
"Use security context of RFILE, rather than specifying \
|
||||
|
@ -210,6 +211,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.long(options::USER)
|
||||
.takes_value(true)
|
||||
.value_name("USER")
|
||||
.value_hint(clap::ValueHint::Username)
|
||||
.help("Set user USER in the target security context.")
|
||||
.allow_invalid_utf8(true),
|
||||
)
|
||||
|
@ -294,6 +296,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new("FILE")
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.min_values(1)
|
||||
.allow_invalid_utf8(true),
|
||||
)
|
||||
|
|
|
@ -113,6 +113,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
Arg::new(options::REFERENCE)
|
||||
.long(options::REFERENCE)
|
||||
.value_name("RFILE")
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.help("use RFILE's group rather than specifying GROUP values")
|
||||
.takes_value(true)
|
||||
.multiple_occurrences(false),
|
||||
|
|
|
@ -157,6 +157,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
Arg::new(options::REFERENCE)
|
||||
.long("reference")
|
||||
.takes_value(true)
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.help("use RFILE's mode instead of MODE values"),
|
||||
)
|
||||
.arg(
|
||||
|
@ -170,7 +171,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(options::FILE)
|
||||
.required_unless_present(options::MODE)
|
||||
.multiple_occurrences(true),
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::AnyPath),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -134,6 +134,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.long(options::REFERENCE)
|
||||
.help("use RFILE's owner and group rather than specifying OWNER:GROUP values")
|
||||
.value_name("RFILE")
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.min_values(1),
|
||||
)
|
||||
.arg(
|
||||
|
@ -167,17 +168,18 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
)
|
||||
}
|
||||
|
||||
/// Parse the username and groupname
|
||||
/// Parse the owner/group specifier string into a user ID and a group ID.
|
||||
///
|
||||
/// In theory, it should be username:groupname
|
||||
/// but ...
|
||||
/// it can user.name:groupname
|
||||
/// or username.groupname
|
||||
/// The `spec` can be of the form:
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `"owner:group"`,
|
||||
/// * `"owner"`,
|
||||
/// * `":group"`,
|
||||
///
|
||||
/// * `spec` - The input from the user
|
||||
/// * `sep` - Should be ':' or '.'
|
||||
/// and the owner or group can be specified either as an ID or a
|
||||
/// name. The `sep` argument specifies which character to use as a
|
||||
/// separator between the owner and group; calling code should set
|
||||
/// this to `':'`.
|
||||
fn parse_spec(spec: &str, sep: char) -> UResult<(Option<u32>, Option<u32>)> {
|
||||
assert!(['.', ':'].contains(&sep));
|
||||
let mut args = spec.splitn(2, sep);
|
||||
|
@ -197,10 +199,17 @@ fn parse_spec(spec: &str, sep: char) -> UResult<(Option<u32>, Option<u32>)> {
|
|||
// So, try to parse it this way
|
||||
return parse_spec(spec, '.');
|
||||
} else {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("invalid user: {}", spec.quote()),
|
||||
));
|
||||
// It's possible that the `user` string contains a
|
||||
// numeric user ID, in which case, we respect that.
|
||||
match user.parse() {
|
||||
Ok(uid) => uid,
|
||||
Err(_) => {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("invalid user: {}", spec.quote()),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -208,11 +217,18 @@ fn parse_spec(spec: &str, sep: char) -> UResult<(Option<u32>, Option<u32>)> {
|
|||
None
|
||||
};
|
||||
let gid = if !group.is_empty() {
|
||||
Some(
|
||||
Group::locate(group)
|
||||
.map_err(|_| USimpleError::new(1, format!("invalid group: {}", spec.quote())))?
|
||||
.gid,
|
||||
)
|
||||
Some(match Group::locate(group) {
|
||||
Ok(g) => g.gid,
|
||||
Err(_) => match group.parse() {
|
||||
Ok(gid) => gid,
|
||||
Err(_) => {
|
||||
return Err(USimpleError::new(
|
||||
1,
|
||||
format!("invalid group: {}", spec.quote()),
|
||||
));
|
||||
}
|
||||
},
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -231,4 +247,17 @@ mod test {
|
|||
assert!(format!("{}", parse_spec("::", ':').err().unwrap()).starts_with("invalid group: "));
|
||||
assert!(format!("{}", parse_spec("..", ':').err().unwrap()).starts_with("invalid group: "));
|
||||
}
|
||||
|
||||
/// Test for parsing IDs that don't correspond to a named user or group.
|
||||
#[test]
|
||||
fn test_parse_spec_nameless_ids() {
|
||||
// This assumes that there is no named user with ID 12345.
|
||||
assert!(matches!(parse_spec("12345", ':'), Ok((Some(12345), None))));
|
||||
// This assumes that there is no named group with ID 54321.
|
||||
assert!(matches!(parse_spec(":54321", ':'), Ok((None, Some(54321)))));
|
||||
assert!(matches!(
|
||||
parse_spec("12345:54321", ':'),
|
||||
Ok((Some(12345), Some(54321)))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,6 +102,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.infer_long_args(true)
|
||||
.arg(
|
||||
Arg::new(options::NEWROOT)
|
||||
.value_hint(clap::ValueHint::DirPath)
|
||||
.hide(true)
|
||||
.required(true)
|
||||
.index(1),
|
||||
|
@ -139,6 +140,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
)
|
||||
.arg(
|
||||
Arg::new(options::COMMAND)
|
||||
.value_hint(clap::ValueHint::CommandName)
|
||||
.hide(true)
|
||||
.multiple_occurrences(true)
|
||||
.index(2),
|
||||
|
|
|
@ -150,6 +150,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(options::FILE)
|
||||
.hide(true)
|
||||
.multiple_occurrences(true),
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -173,6 +173,14 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.default_value(options::DELIMITER_DEFAULT)
|
||||
.hide_default_value(true),
|
||||
)
|
||||
.arg(Arg::new(options::FILE_1).required(true))
|
||||
.arg(Arg::new(options::FILE_2).required(true))
|
||||
.arg(
|
||||
Arg::new(options::FILE_1)
|
||||
.required(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::FILE_2)
|
||||
.required(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -314,6 +314,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.conflicts_with(options::NO_TARGET_DIRECTORY)
|
||||
.long(options::TARGET_DIRECTORY)
|
||||
.value_name(options::TARGET_DIRECTORY)
|
||||
.value_hint(clap::ValueHint::DirPath)
|
||||
.takes_value(true)
|
||||
.validator(|s| {
|
||||
if Path::new(s).is_dir() {
|
||||
|
@ -464,7 +465,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
// END TODO
|
||||
|
||||
.arg(Arg::new(options::PATHS)
|
||||
.multiple_occurrences(true))
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::AnyPath))
|
||||
}
|
||||
|
||||
#[uucore::main]
|
||||
|
|
|
@ -797,7 +797,12 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.long(options::ELIDE_EMPTY_FILES)
|
||||
.help("remove empty output files"),
|
||||
)
|
||||
.arg(Arg::new(options::FILE).hide(true).required(true))
|
||||
.arg(
|
||||
Arg::new(options::FILE)
|
||||
.hide(true)
|
||||
.required(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::PATTERN)
|
||||
.hide(true)
|
||||
|
|
|
@ -614,6 +614,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(options::FILE)
|
||||
.hide(true)
|
||||
.multiple_occurrences(true)
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -272,6 +272,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.short('f')
|
||||
.long(OPT_FILE)
|
||||
.takes_value(true)
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.help("like --date; once for each line of DATEFILE"),
|
||||
)
|
||||
.arg(
|
||||
|
@ -303,6 +304,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.short('r')
|
||||
.long(OPT_REFERENCE)
|
||||
.takes_value(true)
|
||||
.value_hint(clap::ValueHint::AnyPath)
|
||||
.help("display the last modification time of FILE"),
|
||||
)
|
||||
.arg(
|
||||
|
|
|
@ -742,6 +742,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.long(options::INFILE)
|
||||
.overrides_with(options::INFILE)
|
||||
.takes_value(true)
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.require_equals(true)
|
||||
.value_name("FILE")
|
||||
.help("(alternatively if=FILE) specifies the file used for input. When not specified, stdin is used instead")
|
||||
|
@ -751,6 +752,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.long(options::OUTFILE)
|
||||
.overrides_with(options::OUTFILE)
|
||||
.takes_value(true)
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.require_equals(true)
|
||||
.value_name("FILE")
|
||||
.help("(alternatively of=FILE) specifies the file used for output. When not specified, stdout is used instead")
|
||||
|
|
|
@ -7,7 +7,10 @@ use crate::OPT_BLOCKSIZE;
|
|||
use clap::ArgMatches;
|
||||
use std::{env, fmt};
|
||||
|
||||
use uucore::parse_size::{parse_size, ParseSizeError};
|
||||
use uucore::{
|
||||
display::Quotable,
|
||||
parse_size::{parse_size, ParseSizeError},
|
||||
};
|
||||
|
||||
/// The first ten powers of 1024.
|
||||
const IEC_BASES: [u128; 10] = [
|
||||
|
@ -167,6 +170,15 @@ pub(crate) enum BlockSize {
|
|||
Bytes(u64),
|
||||
}
|
||||
|
||||
impl BlockSize {
|
||||
/// Returns the associated value
|
||||
pub(crate) fn as_u64(&self) -> u64 {
|
||||
match *self {
|
||||
Self::Bytes(n) => n,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for BlockSize {
|
||||
fn default() -> Self {
|
||||
if env::var("POSIXLY_CORRECT").is_ok() {
|
||||
|
@ -180,7 +192,13 @@ impl Default for BlockSize {
|
|||
pub(crate) fn block_size_from_matches(matches: &ArgMatches) -> Result<BlockSize, ParseSizeError> {
|
||||
if matches.is_present(OPT_BLOCKSIZE) {
|
||||
let s = matches.value_of(OPT_BLOCKSIZE).unwrap();
|
||||
Ok(BlockSize::Bytes(parse_size(s)?))
|
||||
let bytes = parse_size(s)?;
|
||||
|
||||
if bytes > 0 {
|
||||
Ok(BlockSize::Bytes(bytes))
|
||||
} else {
|
||||
Err(ParseSizeError::ParseFailure(format!("{}", s.quote())))
|
||||
}
|
||||
} else {
|
||||
Ok(Default::default())
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ mod filesystem;
|
|||
mod table;
|
||||
|
||||
use blocks::{HumanReadable, SizeFormat};
|
||||
use table::HeaderMode;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::{UError, UResult, USimpleError};
|
||||
use uucore::fsext::{read_fs_list, MountInfo};
|
||||
|
@ -72,6 +73,7 @@ struct Options {
|
|||
show_all_fs: bool,
|
||||
size_format: SizeFormat,
|
||||
block_size: BlockSize,
|
||||
header_mode: HeaderMode,
|
||||
|
||||
/// Optional list of filesystem types to include in the output table.
|
||||
///
|
||||
|
@ -99,6 +101,7 @@ impl Default for Options {
|
|||
show_all_fs: Default::default(),
|
||||
block_size: Default::default(),
|
||||
size_format: Default::default(),
|
||||
header_mode: Default::default(),
|
||||
include: Default::default(),
|
||||
exclude: Default::default(),
|
||||
show_total: Default::default(),
|
||||
|
@ -176,6 +179,21 @@ impl Options {
|
|||
),
|
||||
ParseSizeError::ParseFailure(s) => OptionsError::InvalidBlockSize(s),
|
||||
})?,
|
||||
header_mode: {
|
||||
if matches.is_present(OPT_HUMAN_READABLE_BINARY)
|
||||
|| matches.is_present(OPT_HUMAN_READABLE_DECIMAL)
|
||||
{
|
||||
HeaderMode::HumanReadable
|
||||
} else if matches.is_present(OPT_PORTABILITY) {
|
||||
HeaderMode::PosixPortability
|
||||
// is_present() doesn't work here, it always returns true because OPT_OUTPUT has
|
||||
// default values and hence is always present
|
||||
} else if matches.occurrences_of(OPT_OUTPUT) > 0 {
|
||||
HeaderMode::Output
|
||||
} else {
|
||||
HeaderMode::Default
|
||||
}
|
||||
},
|
||||
size_format: {
|
||||
if matches.is_present(OPT_HUMAN_READABLE_BINARY) {
|
||||
SizeFormat::HumanReadable(HumanReadable::Binary)
|
||||
|
@ -565,7 +583,11 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.multiple_occurrences(true)
|
||||
.help("limit listing to file systems not of type TYPE"),
|
||||
)
|
||||
.arg(Arg::new(OPT_PATHS).multiple_occurrences(true))
|
||||
.arg(
|
||||
Arg::new(OPT_PATHS)
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::AnyPath),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -233,7 +233,7 @@ impl<'a> RowFormatter<'a> {
|
|||
SizeFormat::HumanReadable(h) => self.scaled_human_readable(size, h),
|
||||
SizeFormat::StaticBlockSize => {
|
||||
let BlockSize::Bytes(d) = self.options.block_size;
|
||||
(size / d).to_string()
|
||||
(size as f64 / d as f64).ceil().to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -289,6 +289,23 @@ impl<'a> RowFormatter<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A HeaderMode defines what header labels should be shown.
|
||||
pub(crate) enum HeaderMode {
|
||||
Default,
|
||||
// the user used -h or -H
|
||||
HumanReadable,
|
||||
// the user used -P
|
||||
PosixPortability,
|
||||
// the user used --output
|
||||
Output,
|
||||
}
|
||||
|
||||
impl Default for HeaderMode {
|
||||
fn default() -> Self {
|
||||
Self::Default
|
||||
}
|
||||
}
|
||||
|
||||
/// The data of the header row.
|
||||
struct Header {}
|
||||
|
||||
|
@ -302,15 +319,22 @@ impl Header {
|
|||
for column in &options.columns {
|
||||
let header = match column {
|
||||
Column::Source => String::from("Filesystem"),
|
||||
Column::Size => match options.size_format {
|
||||
SizeFormat::HumanReadable(_) => String::from("Size"),
|
||||
SizeFormat::StaticBlockSize => {
|
||||
format!("{}-blocks", options.block_size)
|
||||
Column::Size => match options.header_mode {
|
||||
HeaderMode::HumanReadable => String::from("Size"),
|
||||
HeaderMode::PosixPortability => {
|
||||
format!("{}-blocks", options.block_size.as_u64())
|
||||
}
|
||||
_ => format!("{}-blocks", options.block_size),
|
||||
},
|
||||
Column::Used => String::from("Used"),
|
||||
Column::Avail => String::from("Available"),
|
||||
Column::Pcent => String::from("Use%"),
|
||||
Column::Avail => match options.header_mode {
|
||||
HeaderMode::HumanReadable | HeaderMode::Output => String::from("Avail"),
|
||||
_ => String::from("Available"),
|
||||
},
|
||||
Column::Pcent => match options.header_mode {
|
||||
HeaderMode::PosixPortability => String::from("Capacity"),
|
||||
_ => String::from("Use%"),
|
||||
},
|
||||
Column::Target => String::from("Mounted on"),
|
||||
Column::Itotal => String::from("Inodes"),
|
||||
Column::Iused => String::from("IUsed"),
|
||||
|
@ -428,7 +452,7 @@ mod tests {
|
|||
|
||||
use crate::blocks::{HumanReadable, SizeFormat};
|
||||
use crate::columns::Column;
|
||||
use crate::table::{Header, Row, RowFormatter};
|
||||
use crate::table::{Header, HeaderMode, Row, RowFormatter};
|
||||
use crate::{BlockSize, Options};
|
||||
|
||||
const COLUMNS_WITH_FS_TYPE: [Column; 7] = [
|
||||
|
@ -548,37 +572,49 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_header_with_human_readable_binary() {
|
||||
fn test_human_readable_header() {
|
||||
let options = Options {
|
||||
size_format: SizeFormat::HumanReadable(HumanReadable::Binary),
|
||||
header_mode: HeaderMode::HumanReadable,
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(
|
||||
Header::get_headers(&options),
|
||||
vec!("Filesystem", "Size", "Used", "Avail", "Use%", "Mounted on")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_posix_portability_header() {
|
||||
let options = Options {
|
||||
header_mode: HeaderMode::PosixPortability,
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(
|
||||
Header::get_headers(&options),
|
||||
vec!(
|
||||
"Filesystem",
|
||||
"Size",
|
||||
"1024-blocks",
|
||||
"Used",
|
||||
"Available",
|
||||
"Use%",
|
||||
"Capacity",
|
||||
"Mounted on"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_header_with_human_readable_si() {
|
||||
fn test_output_header() {
|
||||
let options = Options {
|
||||
size_format: SizeFormat::HumanReadable(HumanReadable::Decimal),
|
||||
header_mode: HeaderMode::Output,
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(
|
||||
Header::get_headers(&options),
|
||||
vec!(
|
||||
"Filesystem",
|
||||
"Size",
|
||||
"1K-blocks",
|
||||
"Used",
|
||||
"Available",
|
||||
"Avail",
|
||||
"Use%",
|
||||
"Mounted on"
|
||||
)
|
||||
|
@ -757,4 +793,28 @@ mod tests {
|
|||
let fmt = RowFormatter::new(&row, &options);
|
||||
assert_eq!(fmt.get_values(), vec!("26%"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_row_formatter_with_round_up_byte_values() {
|
||||
fn get_formatted_values(bytes: u64, bytes_used: u64, bytes_avail: u64) -> Vec<String> {
|
||||
let options = Options {
|
||||
block_size: BlockSize::Bytes(1000),
|
||||
columns: vec![Column::Size, Column::Used, Column::Avail],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let row = Row {
|
||||
bytes,
|
||||
bytes_used,
|
||||
bytes_avail,
|
||||
..Default::default()
|
||||
};
|
||||
RowFormatter::new(&row, &options).get_values()
|
||||
}
|
||||
|
||||
assert_eq!(get_formatted_values(100, 100, 0), vec!("1", "1", "0"));
|
||||
assert_eq!(get_formatted_values(100, 99, 1), vec!("1", "1", "1"));
|
||||
assert_eq!(get_formatted_values(1000, 1000, 0), vec!("1", "1", "0"));
|
||||
assert_eq!(get_formatted_values(1001, 1000, 1), vec!("2", "1", "1"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -187,6 +187,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(options::FILE)
|
||||
.hide(true)
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.multiple_occurrences(true),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -88,5 +88,10 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.short('z')
|
||||
.help("separate output with NUL rather than newline"),
|
||||
)
|
||||
.arg(Arg::new(options::DIR).hide(true).multiple_occurrences(true))
|
||||
.arg(
|
||||
Arg::new(options::DIR)
|
||||
.hide(true)
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::AnyPath),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -884,6 +884,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.short('X')
|
||||
.long("exclude-from")
|
||||
.value_name("FILE")
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.help("exclude files that match any pattern in FILE")
|
||||
.multiple_occurrences(true)
|
||||
|
||||
|
@ -913,6 +914,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(options::FILE)
|
||||
.hide(true)
|
||||
.value_hint(clap::ValueHint::AnyPath)
|
||||
.multiple_occurrences(true)
|
||||
)
|
||||
}
|
||||
|
|
2
src/uu/env/src/env.rs
vendored
2
src/uu/env/src/env.rs
vendored
|
@ -144,6 +144,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.takes_value(true)
|
||||
.number_of_values(1)
|
||||
.value_name("DIR")
|
||||
.value_hint(clap::ValueHint::DirPath)
|
||||
.help("change working directory to DIR"))
|
||||
.arg(Arg::new("null")
|
||||
.short('0')
|
||||
|
@ -156,6 +157,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.takes_value(true)
|
||||
.number_of_values(1)
|
||||
.value_name("PATH")
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.multiple_occurrences(true)
|
||||
.help("read and set variables from a \".env\"-style configuration file (prior to any \
|
||||
unset and/or set)"))
|
||||
|
|
|
@ -207,6 +207,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.multiple_occurrences(true)
|
||||
.hide(true)
|
||||
.takes_value(true)
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -344,6 +344,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(ARG_FILES)
|
||||
.multiple_occurrences(true)
|
||||
.takes_value(true),
|
||||
.takes_value(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -99,7 +99,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(options::FILE)
|
||||
.hide(true)
|
||||
.multiple_occurrences(true),
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -112,6 +112,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
Arg::new(options::USERS)
|
||||
.multiple_occurrences(true)
|
||||
.takes_value(true)
|
||||
.value_name(options::USERS),
|
||||
.value_name(options::USERS)
|
||||
.value_hint(clap::ValueHint::Username),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -423,6 +423,7 @@ pub fn uu_app_common<'a>() -> Command<'a> {
|
|||
.index(1)
|
||||
.multiple_occurrences(true)
|
||||
.value_name("FILE")
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.allow_invalid_utf8(true),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -107,7 +107,11 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.help("line delimiter is NUL, not newline")
|
||||
.overrides_with(options::ZERO_NAME),
|
||||
)
|
||||
.arg(Arg::new(options::FILES_NAME).multiple_occurrences(true))
|
||||
.arg(
|
||||
Arg::new(options::FILES_NAME)
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
|
|
@ -105,7 +105,11 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.overrides_with_all(&[OPT_DOMAIN, OPT_IP_ADDRESS, OPT_FQDN, OPT_SHORT])
|
||||
.help("Display the short hostname (the portion before the first dot) if possible"),
|
||||
)
|
||||
.arg(Arg::new(OPT_HOST).allow_invalid_utf8(true))
|
||||
.arg(
|
||||
Arg::new(OPT_HOST)
|
||||
.allow_invalid_utf8(true)
|
||||
.value_hint(clap::ValueHint::Hostname),
|
||||
)
|
||||
}
|
||||
|
||||
fn display_hostname(matches: &ArgMatches) -> UResult<()> {
|
||||
|
|
|
@ -443,7 +443,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
Arg::new(options::ARG_USERS)
|
||||
.multiple_occurrences(true)
|
||||
.takes_value(true)
|
||||
.value_name(options::ARG_USERS),
|
||||
.value_name(options::ARG_USERS)
|
||||
.value_hint(clap::ValueHint::Username),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -248,6 +248,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.help("set ownership (super-user only)")
|
||||
.value_name("OWNER")
|
||||
.takes_value(true)
|
||||
.value_hint(clap::ValueHint::Username)
|
||||
)
|
||||
.arg(
|
||||
Arg::new(OPT_PRESERVE_TIMESTAMPS)
|
||||
|
@ -266,6 +267,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.long(OPT_STRIP_PROGRAM)
|
||||
.help("program used to strip binaries (no action Windows)")
|
||||
.value_name("PROGRAM")
|
||||
.value_hint(clap::ValueHint::CommandName)
|
||||
)
|
||||
.arg(
|
||||
backup_control::arguments::suffix()
|
||||
|
@ -277,6 +279,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.long(OPT_TARGET_DIRECTORY)
|
||||
.help("move all SOURCE arguments into DIRECTORY")
|
||||
.value_name("DIRECTORY")
|
||||
.value_hint(clap::ValueHint::DirPath)
|
||||
)
|
||||
.arg(
|
||||
// TODO implement flag
|
||||
|
@ -307,7 +310,13 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.help("(unimplemented) set security context of files and directories")
|
||||
.value_name("CONTEXT")
|
||||
)
|
||||
.arg(Arg::new(ARG_FILES).multiple_occurrences(true).takes_value(true).min_values(1))
|
||||
.arg(
|
||||
Arg::new(ARG_FILES)
|
||||
.multiple_occurrences(true)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.value_hint(clap::ValueHint::AnyPath)
|
||||
)
|
||||
}
|
||||
|
||||
/// Check for unimplemented command line arguments.
|
||||
|
|
|
@ -801,12 +801,14 @@ FILENUM is 1 or 2, corresponding to FILE1 or FILE2",
|
|||
Arg::new("file1")
|
||||
.required(true)
|
||||
.value_name("FILE1")
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.hide(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("file2")
|
||||
.required(true)
|
||||
.value_name("FILE2")
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.hide(true),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.min_values(2)
|
||||
.max_values(2)
|
||||
.takes_value(true)
|
||||
.value_hint(clap::ValueHint::AnyPath)
|
||||
.allow_invalid_utf8(true),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -232,6 +232,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.long(options::TARGET_DIRECTORY)
|
||||
.help("specify the DIRECTORY in which to create the links")
|
||||
.value_name("DIRECTORY")
|
||||
.value_hint(clap::ValueHint::DirPath)
|
||||
.conflicts_with(options::NO_TARGET_DIRECTORY),
|
||||
)
|
||||
.arg(
|
||||
|
@ -257,6 +258,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
Arg::new(ARG_FILES)
|
||||
.multiple_occurrences(true)
|
||||
.takes_value(true)
|
||||
.value_hint(clap::ValueHint::AnyPath)
|
||||
.required(true)
|
||||
.min_values(1),
|
||||
)
|
||||
|
|
|
@ -1402,7 +1402,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
Arg::new(options::PATHS)
|
||||
.multiple_occurrences(true)
|
||||
.takes_value(true)
|
||||
.allow_invalid_utf8(true),
|
||||
.value_hint(clap::ValueHint::AnyPath)
|
||||
.allow_invalid_utf8(true)
|
||||
)
|
||||
.after_help(
|
||||
"The TIME_STYLE argument can be full-iso, long-iso, iso. \
|
||||
|
|
|
@ -137,7 +137,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.multiple_occurrences(true)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.allow_invalid_utf8(true),
|
||||
.allow_invalid_utf8(true)
|
||||
.value_hint(clap::ValueHint::DirPath),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -102,6 +102,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(options::FIFO)
|
||||
.hide(true)
|
||||
.multiple_occurrences(true),
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::AnyPath),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -162,7 +162,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.value_name("NAME")
|
||||
.help("name of the new file")
|
||||
.required(true)
|
||||
.index(1),
|
||||
.index(1)
|
||||
.value_hint(clap::ValueHint::AnyPath),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("type")
|
||||
|
|
|
@ -13,12 +13,16 @@ use uucore::display::{println_verbatim, Quotable};
|
|||
use uucore::error::{FromIo, UError, UResult};
|
||||
use uucore::format_usage;
|
||||
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::fmt::Display;
|
||||
use std::iter;
|
||||
use std::os::unix::prelude::PermissionsExt;
|
||||
use std::path::{is_separator, Path, PathBuf};
|
||||
use std::{env, fs};
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::fs;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::prelude::PermissionsExt;
|
||||
|
||||
use rand::Rng;
|
||||
use tempfile::Builder;
|
||||
|
@ -42,7 +46,12 @@ enum MkTempError {
|
|||
PersistError(PathBuf),
|
||||
MustEndInX(String),
|
||||
TooFewXs(String),
|
||||
ContainsDirSeparator(String),
|
||||
|
||||
/// The template prefix contains a path separator (e.g. `"a/bXXX"`).
|
||||
PrefixContainsDirSeparator(String),
|
||||
|
||||
/// The template suffix contains a path separator (e.g. `"XXXa/b"`).
|
||||
SuffixContainsDirSeparator(String),
|
||||
InvalidTemplate(String),
|
||||
}
|
||||
|
||||
|
@ -57,7 +66,14 @@ impl Display for MkTempError {
|
|||
PersistError(p) => write!(f, "could not persist file {}", p.quote()),
|
||||
MustEndInX(s) => write!(f, "with --suffix, template {} must end in X", s.quote()),
|
||||
TooFewXs(s) => write!(f, "too few X's in template {}", s.quote()),
|
||||
ContainsDirSeparator(s) => {
|
||||
PrefixContainsDirSeparator(s) => {
|
||||
write!(
|
||||
f,
|
||||
"invalid template, {}, contains directory separator",
|
||||
s.quote()
|
||||
)
|
||||
}
|
||||
SuffixContainsDirSeparator(s) => {
|
||||
write!(
|
||||
f,
|
||||
"invalid suffix {}, contains directory separator",
|
||||
|
@ -191,7 +207,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
be an absolute name; unlike with -t, TEMPLATE may contain \
|
||||
slashes, but mktemp creates only the final component",
|
||||
)
|
||||
.value_name("DIR"),
|
||||
.value_name("DIR")
|
||||
.value_hint(clap::ValueHint::DirPath),
|
||||
)
|
||||
.arg(Arg::new(OPT_T).short('t').help(
|
||||
"Generate a template (using the supplied prefix and TMPDIR (TMP on windows) if set) \
|
||||
|
@ -253,8 +270,12 @@ fn parse_template<'a>(
|
|||
}
|
||||
};
|
||||
|
||||
if prefix.chars().any(is_separator) {
|
||||
return Err(MkTempError::PrefixContainsDirSeparator(temp.into()));
|
||||
}
|
||||
|
||||
if suf.chars().any(is_separator) {
|
||||
return Err(MkTempError::ContainsDirSeparator(suf.into()));
|
||||
return Err(MkTempError::SuffixContainsDirSeparator(suf.into()));
|
||||
}
|
||||
|
||||
Ok((prefix, rand, suf))
|
||||
|
@ -358,11 +379,7 @@ mod tests {
|
|||
|
||||
#[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("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());
|
||||
|
|
|
@ -183,7 +183,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
Arg::new(options::FILES)
|
||||
.required(false)
|
||||
.multiple_occurrences(true)
|
||||
.help("Path to the files to be read"),
|
||||
.help("Path to the files to be read")
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -156,14 +156,15 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.help("move all SOURCE arguments into DIRECTORY")
|
||||
.takes_value(true)
|
||||
.value_name("DIRECTORY")
|
||||
.value_hint(clap::ValueHint::DirPath)
|
||||
.conflicts_with(OPT_NO_TARGET_DIRECTORY)
|
||||
.allow_invalid_utf8(true)
|
||||
)
|
||||
.arg(
|
||||
Arg::new(OPT_NO_TARGET_DIRECTORY)
|
||||
.short('T')
|
||||
.long(OPT_NO_TARGET_DIRECTORY).
|
||||
help("treat DEST as a normal file")
|
||||
.long(OPT_NO_TARGET_DIRECTORY)
|
||||
.help("treat DEST as a normal file")
|
||||
)
|
||||
.arg(
|
||||
Arg::new(OPT_UPDATE)
|
||||
|
@ -183,6 +184,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.min_values(2)
|
||||
.required(true)
|
||||
.allow_invalid_utf8(true)
|
||||
.value_hint(clap::ValueHint::AnyPath)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -117,5 +117,9 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.takes_value(true)
|
||||
.allow_hyphen_values(true),
|
||||
)
|
||||
.arg(Arg::new(options::COMMAND).multiple_occurrences(true))
|
||||
.arg(
|
||||
Arg::new(options::COMMAND)
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::CommandName),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -154,7 +154,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(options::FILE)
|
||||
.hide(true)
|
||||
.multiple_occurrences(true),
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::BODY_NUMBERING)
|
||||
|
|
|
@ -126,7 +126,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
Arg::new(options::CMD)
|
||||
.hide(true)
|
||||
.required(true)
|
||||
.multiple_occurrences(true),
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::CommandName),
|
||||
)
|
||||
.trailing_var_arg(true)
|
||||
.infer_long_args(true)
|
||||
|
|
|
@ -512,7 +512,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(options::FILENAME)
|
||||
.hide(true)
|
||||
.multiple_occurrences(true),
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -71,7 +71,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
Arg::new(options::FILE)
|
||||
.value_name("FILE")
|
||||
.multiple_occurrences(true)
|
||||
.default_value("-"),
|
||||
.default_value("-")
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -108,7 +108,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(options::PATH)
|
||||
.hide(true)
|
||||
.multiple_occurrences(true),
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::AnyPath),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -179,7 +179,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(options::USER)
|
||||
.takes_value(true)
|
||||
.multiple_occurrences(true),
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::Username),
|
||||
)
|
||||
.arg(
|
||||
// Redefine the help argument to not include the short flag
|
||||
|
|
|
@ -372,6 +372,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
Arg::new(options::FILES)
|
||||
.multiple_occurrences(true)
|
||||
.multiple_values(true)
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -754,7 +754,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(options::FILE)
|
||||
.hide(true)
|
||||
.multiple_occurrences(true),
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::AUTO_REFERENCE)
|
||||
|
@ -826,7 +827,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.long(options::BREAK_FILE)
|
||||
.help("word break characters in this FILE")
|
||||
.value_name("FILE")
|
||||
.takes_value(true),
|
||||
.takes_value(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::IGNORE_CASE)
|
||||
|
@ -849,7 +851,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.long(options::IGNORE_FILE)
|
||||
.help("read ignore word list from FILE")
|
||||
.value_name("FILE")
|
||||
.takes_value(true),
|
||||
.takes_value(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::ONLY_FILE)
|
||||
|
@ -857,7 +860,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.long(options::ONLY_FILE)
|
||||
.help("read only word list from this FILE")
|
||||
.value_name("FILE")
|
||||
.takes_value(true),
|
||||
.takes_value(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::REFERENCES)
|
||||
|
|
|
@ -161,7 +161,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(ARG_FILES)
|
||||
.multiple_occurrences(true)
|
||||
.takes_value(true),
|
||||
.takes_value(true)
|
||||
.value_hint(clap::ValueHint::AnyPath),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -130,7 +130,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.multiple_occurrences(true)
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.min_values(1),
|
||||
.min_values(1)
|
||||
.value_hint(clap::ValueHint::AnyPath),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -87,6 +87,15 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(Arg::new(options::DIR).short('d').takes_value(true).help(
|
||||
"If any of FROM and TO is not subpath of DIR, output absolute path instead of relative",
|
||||
))
|
||||
.arg(Arg::new(options::TO).required(true).takes_value(true))
|
||||
.arg(Arg::new(options::FROM).takes_value(true))
|
||||
.arg(
|
||||
Arg::new(options::TO)
|
||||
.required(true)
|
||||
.takes_value(true)
|
||||
.value_hint(clap::ValueHint::AnyPath),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::FROM)
|
||||
.takes_value(true)
|
||||
.value_hint(clap::ValueHint::AnyPath),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -227,6 +227,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.multiple_occurrences(true)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.value_hint(clap::ValueHint::AnyPath)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -197,6 +197,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.required(true)
|
||||
.allow_invalid_utf8(true),
|
||||
.allow_invalid_utf8(true)
|
||||
.value_hint(clap::ValueHint::DirPath),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -156,7 +156,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new("ARG")
|
||||
.multiple_occurrences(true)
|
||||
.allow_invalid_utf8(true),
|
||||
.allow_invalid_utf8(true)
|
||||
.value_hint(clap::ValueHint::CommandName),
|
||||
)
|
||||
// Once "ARG" is parsed, everything after that belongs to it.
|
||||
//
|
||||
|
|
|
@ -374,7 +374,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(options::FILE)
|
||||
.hide(true)
|
||||
.multiple_occurrences(true),
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -158,14 +158,16 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.long(options::OUTPUT)
|
||||
.takes_value(true)
|
||||
.value_name("FILE")
|
||||
.help("write result to FILE instead of standard output"),
|
||||
.help("write result to FILE instead of standard output")
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::RANDOM_SOURCE)
|
||||
.long(options::RANDOM_SOURCE)
|
||||
.takes_value(true)
|
||||
.value_name("FILE")
|
||||
.help("get random bytes from FILE"),
|
||||
.help("get random bytes from FILE")
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::REPEAT)
|
||||
|
@ -179,7 +181,11 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.long(options::ZERO_TERMINATED)
|
||||
.help("line delimiter is NUL, not newline"),
|
||||
)
|
||||
.arg(Arg::new(options::FILE).takes_value(true))
|
||||
.arg(
|
||||
Arg::new(options::FILE)
|
||||
.takes_value(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
||||
fn read_input_file(filename: &str) -> UResult<Vec<u8>> {
|
||||
|
|
|
@ -1397,7 +1397,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.long(options::OUTPUT)
|
||||
.help("write output to FILENAME instead of stdout")
|
||||
.takes_value(true)
|
||||
.value_name("FILENAME"),
|
||||
.value_name("FILENAME")
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::REVERSE)
|
||||
|
@ -1461,13 +1462,15 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.long(options::TMP_DIR)
|
||||
.help("use DIR for temporaries, not $TMPDIR or /tmp")
|
||||
.takes_value(true)
|
||||
.value_name("DIR"),
|
||||
.value_name("DIR")
|
||||
.value_hint(clap::ValueHint::DirPath),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::COMPRESS_PROG)
|
||||
.long(options::COMPRESS_PROG)
|
||||
.help("compress temporary files with PROG, decompress with PROG -d; PROG has to take input from stdin and output to stdout")
|
||||
.value_name("PROG"),
|
||||
.value_name("PROG")
|
||||
.value_hint(clap::ValueHint::CommandName),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::BATCH_SIZE)
|
||||
|
@ -1482,7 +1485,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.takes_value(true)
|
||||
.value_name("NUL_FILES")
|
||||
.multiple_occurrences(true)
|
||||
.allow_invalid_utf8(true),
|
||||
.allow_invalid_utf8(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::DEBUG)
|
||||
|
@ -1493,7 +1497,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
Arg::new(options::FILES)
|
||||
.multiple_occurrences(true)
|
||||
.takes_value(true)
|
||||
.allow_invalid_utf8(true),
|
||||
.allow_invalid_utf8(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -111,9 +111,11 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
Arg::new(OPT_FILTER)
|
||||
.long(OPT_FILTER)
|
||||
.takes_value(true)
|
||||
.value_name("COMMAND")
|
||||
.value_hint(clap::ValueHint::CommandName)
|
||||
.help(
|
||||
"write to shell COMMAND file name is $FILE (Currently not implemented for Windows)",
|
||||
),
|
||||
"write to shell COMMAND; file name is $FILE (Currently not implemented for Windows)",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(OPT_ELIDE_EMPTY_FILES)
|
||||
|
@ -162,7 +164,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
Arg::new(ARG_INPUT)
|
||||
.takes_value(true)
|
||||
.default_value("-")
|
||||
.index(1),
|
||||
.index(1)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(ARG_PREFIX)
|
||||
|
|
|
@ -19,7 +19,9 @@ use uucore::{entries, format_usage};
|
|||
use clap::{crate_version, Arg, ArgMatches, Command};
|
||||
use std::borrow::Cow;
|
||||
use std::convert::AsRef;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::os::unix::fs::{FileTypeExt, MetadataExt};
|
||||
use std::os::unix::prelude::OsStrExt;
|
||||
use std::path::Path;
|
||||
use std::{cmp, fs, iter};
|
||||
|
||||
|
@ -221,7 +223,7 @@ pub struct Stater {
|
|||
follow: bool,
|
||||
show_fs: bool,
|
||||
from_user: bool,
|
||||
files: Vec<String>,
|
||||
files: Vec<OsString>,
|
||||
mount_list: Option<Vec<String>>,
|
||||
default_tokens: Vec<Token>,
|
||||
default_dev_tokens: Vec<Token>,
|
||||
|
@ -471,24 +473,10 @@ impl Stater {
|
|||
}
|
||||
|
||||
fn new(matches: &ArgMatches) -> UResult<Self> {
|
||||
let mut files: Vec<String> = matches
|
||||
.values_of(ARG_FILES)
|
||||
.map(|v| v.map(ToString::to_string).collect())
|
||||
let files = matches
|
||||
.values_of_os(ARG_FILES)
|
||||
.map(|v| v.map(OsString::from).collect())
|
||||
.unwrap_or_default();
|
||||
#[cfg(unix)]
|
||||
if files.contains(&String::from("-")) {
|
||||
let redirected_path = Path::new("/dev/stdin")
|
||||
.canonicalize()
|
||||
.expect("unable to canonicalize /dev/stdin")
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.unwrap();
|
||||
for file in &mut files {
|
||||
if file == "-" {
|
||||
*file = redirected_path.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
let format_str = if matches.is_present(options::PRINTF) {
|
||||
matches
|
||||
.value_of(options::PRINTF)
|
||||
|
@ -550,19 +538,37 @@ impl Stater {
|
|||
}
|
||||
|
||||
fn exec(&self) -> i32 {
|
||||
let mut stdin_is_fifo = false;
|
||||
if cfg!(unix) {
|
||||
if let Ok(md) = fs::metadata("/dev/stdin") {
|
||||
stdin_is_fifo = md.file_type().is_fifo();
|
||||
}
|
||||
}
|
||||
|
||||
let mut ret = 0;
|
||||
for f in &self.files {
|
||||
ret |= self.do_stat(f.as_str());
|
||||
ret |= self.do_stat(f, stdin_is_fifo);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn do_stat(&self, file: &str) -> i32 {
|
||||
if !self.show_fs {
|
||||
let result = if self.follow {
|
||||
fs::metadata(file)
|
||||
fn do_stat(&self, file: &OsStr, stdin_is_fifo: bool) -> i32 {
|
||||
let display_name = file.to_string_lossy();
|
||||
let file: OsString = if cfg!(unix) && display_name.eq("-") {
|
||||
if let Ok(p) = Path::new("/dev/stdin").canonicalize() {
|
||||
p.into_os_string()
|
||||
} else {
|
||||
fs::symlink_metadata(file)
|
||||
OsString::from("/dev/stdin")
|
||||
}
|
||||
} else {
|
||||
OsString::from(file)
|
||||
};
|
||||
|
||||
if !self.show_fs {
|
||||
let result = if self.follow || stdin_is_fifo && display_name.eq("-") {
|
||||
fs::metadata(&file)
|
||||
} else {
|
||||
fs::symlink_metadata(&file)
|
||||
};
|
||||
match result {
|
||||
Ok(meta) => {
|
||||
|
@ -658,28 +664,32 @@ impl Stater {
|
|||
|
||||
// mount point
|
||||
'm' => {
|
||||
arg = self.find_mount_point(file).unwrap();
|
||||
arg = self.find_mount_point(&file).unwrap();
|
||||
output_type = OutputType::Str;
|
||||
}
|
||||
|
||||
// file name
|
||||
'n' => {
|
||||
arg = file.to_owned();
|
||||
arg = display_name.to_string();
|
||||
output_type = OutputType::Str;
|
||||
}
|
||||
// quoted file name with dereference if symbolic link
|
||||
'N' => {
|
||||
if file_type.is_symlink() {
|
||||
let dst = match fs::read_link(file) {
|
||||
let dst = match fs::read_link(&file) {
|
||||
Ok(path) => path,
|
||||
Err(e) => {
|
||||
println!("{}", e);
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
arg = format!("{} -> {}", file.quote(), dst.quote());
|
||||
arg = format!(
|
||||
"{} -> {}",
|
||||
display_name.quote(),
|
||||
dst.quote()
|
||||
);
|
||||
} else {
|
||||
arg = file.to_string();
|
||||
arg = display_name.to_string();
|
||||
}
|
||||
output_type = OutputType::Str;
|
||||
}
|
||||
|
@ -771,12 +781,16 @@ impl Stater {
|
|||
}
|
||||
}
|
||||
Err(e) => {
|
||||
show_error!("cannot stat {}: {}", file.quote(), e);
|
||||
show_error!("cannot stat {}: {}", display_name.quote(), e);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match statfs(file) {
|
||||
#[cfg(unix)]
|
||||
let p = file.as_bytes();
|
||||
#[cfg(not(unix))]
|
||||
let p = file.into_string().unwrap();
|
||||
match statfs(p) {
|
||||
Ok(meta) => {
|
||||
let tokens = &self.default_tokens;
|
||||
|
||||
|
@ -829,7 +843,7 @@ impl Stater {
|
|||
}
|
||||
// file name
|
||||
'n' => {
|
||||
arg = file.to_owned();
|
||||
arg = display_name.to_string();
|
||||
output_type = OutputType::Str;
|
||||
}
|
||||
// block size (for faster transfers)
|
||||
|
@ -866,7 +880,7 @@ impl Stater {
|
|||
Err(e) => {
|
||||
show_error!(
|
||||
"cannot read file system information for {}: {}",
|
||||
file.quote(),
|
||||
display_name.quote(),
|
||||
e
|
||||
);
|
||||
return 1;
|
||||
|
@ -1028,6 +1042,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
Arg::new(ARG_FILES)
|
||||
.multiple_occurrences(true)
|
||||
.takes_value(true)
|
||||
.min_values(1),
|
||||
.allow_invalid_utf8(true)
|
||||
.min_values(1)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -232,6 +232,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.multiple_occurrences(true)
|
||||
.takes_value(true)
|
||||
.hide(true)
|
||||
.required(true),
|
||||
.required(true)
|
||||
.value_hint(clap::ValueHint::CommandName),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -150,7 +150,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(options::FILE)
|
||||
.multiple_occurrences(true)
|
||||
.hide(true),
|
||||
.hide(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::BSD_COMPATIBLE)
|
||||
|
|
|
@ -217,7 +217,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(ARG_FILES)
|
||||
.multiple_occurrences(true)
|
||||
.takes_value(true),
|
||||
.takes_value(true)
|
||||
.value_hint(clap::ValueHint::AnyPath),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -91,7 +91,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.arg(
|
||||
Arg::new(options::FILE)
|
||||
.hide(true)
|
||||
.multiple_occurrences(true),
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -351,7 +351,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
Arg::new(options::ARG_FILES)
|
||||
.multiple_occurrences(true)
|
||||
.takes_value(true)
|
||||
.min_values(1),
|
||||
.min_values(1)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,11 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.short('i')
|
||||
.help("ignore interrupt signals (ignored on non-Unix platforms)"),
|
||||
)
|
||||
.arg(Arg::new(options::FILE).multiple_occurrences(true))
|
||||
.arg(
|
||||
Arg::new(options::FILE)
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
|
|
|
@ -170,6 +170,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.index(2)
|
||||
.required(true)
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::CommandName)
|
||||
)
|
||||
.trailing_var_arg(true)
|
||||
.infer_long_args(true)
|
||||
|
|
|
@ -219,7 +219,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.long(options::sources::REFERENCE)
|
||||
.help("use this file's times instead of the current time")
|
||||
.value_name("FILE")
|
||||
.allow_invalid_utf8(true),
|
||||
.allow_invalid_utf8(true)
|
||||
.value_hint(clap::ValueHint::AnyPath),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::TIME)
|
||||
|
@ -238,7 +239,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.multiple_occurrences(true)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.allow_invalid_utf8(true),
|
||||
.allow_invalid_utf8(true)
|
||||
.value_hint(clap::ValueHint::AnyPath),
|
||||
)
|
||||
.group(ArgGroup::new(options::SOURCES).args(&[
|
||||
options::sources::CURRENT,
|
||||
|
|
|
@ -143,7 +143,6 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.about(ABOUT)
|
||||
.override_usage(format_usage(USAGE))
|
||||
.infer_long_args(true)
|
||||
.infer_long_args(true)
|
||||
.arg(
|
||||
Arg::new(options::COMPLEMENT)
|
||||
.visible_short_alias('C')
|
||||
|
|
|
@ -162,6 +162,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.required_unless_present(options::SIZE)
|
||||
.help("base the size of each file on the size of RFILE")
|
||||
.value_name("RFILE")
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::SIZE)
|
||||
|
@ -176,7 +177,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.multiple_occurrences(true)
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.min_values(1))
|
||||
.min_values(1)
|
||||
.value_hint(clap::ValueHint::FilePath))
|
||||
}
|
||||
|
||||
/// Truncate the named file to the specified size.
|
||||
|
|
|
@ -99,7 +99,12 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.override_usage(format_usage(USAGE))
|
||||
.about(SUMMARY)
|
||||
.infer_long_args(true)
|
||||
.arg(Arg::new(options::FILE).default_value("-").hide(true))
|
||||
.arg(
|
||||
Arg::new(options::FILE)
|
||||
.default_value("-")
|
||||
.hide(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
||||
// We use String as a representation of node here
|
||||
|
|
|
@ -109,7 +109,12 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.override_usage(format_usage(USAGE))
|
||||
.about(SUMMARY)
|
||||
.infer_long_args(true)
|
||||
.arg(Arg::new(options::FILE).hide(true).multiple_occurrences(true))
|
||||
.arg(
|
||||
Arg::new(options::FILE)
|
||||
.hide(true)
|
||||
.multiple_occurrences(true)
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::ALL)
|
||||
.short('a')
|
||||
|
|
|
@ -392,7 +392,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
Arg::new(ARG_FILES)
|
||||
.multiple_occurrences(true)
|
||||
.takes_value(true)
|
||||
.max_values(2),
|
||||
.max_values(2)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
Arg::new(OPT_PATH)
|
||||
.required(true)
|
||||
.hide(true)
|
||||
.allow_invalid_utf8(true),
|
||||
.allow_invalid_utf8(true)
|
||||
.value_hint(clap::ValueHint::AnyPath),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -64,5 +64,10 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
.about(ABOUT)
|
||||
.override_usage(format_usage(USAGE))
|
||||
.infer_long_args(true)
|
||||
.arg(Arg::new(ARG_FILES).takes_value(true).max_values(1))
|
||||
.arg(
|
||||
Arg::new(ARG_FILES)
|
||||
.takes_value(true)
|
||||
.max_values(1)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -210,7 +210,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
"read input from the files specified by
|
||||
NUL-terminated names in file F;
|
||||
If F is - then read names from standard input",
|
||||
),
|
||||
)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::LINES)
|
||||
|
@ -234,7 +235,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
Arg::new(ARG_FILES)
|
||||
.multiple_occurrences(true)
|
||||
.takes_value(true)
|
||||
.allow_invalid_utf8(true),
|
||||
.allow_invalid_utf8(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -250,7 +250,8 @@ pub fn uu_app<'a>() -> Command<'a> {
|
|||
Arg::new(options::FILE)
|
||||
.takes_value(true)
|
||||
.min_values(1)
|
||||
.max_values(2),
|
||||
.max_values(2)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ use std::ffi::CString;
|
|||
use std::io::Error as IOError;
|
||||
#[cfg(unix)]
|
||||
use std::mem;
|
||||
#[cfg(not(unix))]
|
||||
use std::path::Path;
|
||||
use std::time::UNIX_EPOCH;
|
||||
|
||||
|
@ -709,9 +710,9 @@ impl FsMeta for StatFs {
|
|||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn statfs<P: AsRef<Path>>(path: P) -> Result<StatFs, String>
|
||||
pub fn statfs<P>(path: P) -> Result<StatFs, String>
|
||||
where
|
||||
Vec<u8>: From<P>,
|
||||
P: Into<Vec<u8>>,
|
||||
{
|
||||
match CString::new(path) {
|
||||
Ok(p) => {
|
||||
|
|
|
@ -92,22 +92,25 @@ pub fn wrap_chown<P: AsRef<Path>>(
|
|||
);
|
||||
if level == VerbosityLevel::Verbose {
|
||||
out = if verbosity.groups_only {
|
||||
let gid = meta.gid();
|
||||
format!(
|
||||
"{}\nfailed to change group of {} from {} to {}",
|
||||
out,
|
||||
path.quote(),
|
||||
entries::gid2grp(meta.gid()).unwrap(),
|
||||
entries::gid2grp(dest_gid).unwrap()
|
||||
entries::gid2grp(gid).unwrap_or_else(|_| gid.to_string()),
|
||||
entries::gid2grp(dest_gid).unwrap_or_else(|_| dest_gid.to_string())
|
||||
)
|
||||
} else {
|
||||
let uid = meta.uid();
|
||||
let gid = meta.gid();
|
||||
format!(
|
||||
"{}\nfailed to change ownership of {} from {}:{} to {}:{}",
|
||||
out,
|
||||
path.quote(),
|
||||
entries::uid2usr(meta.uid()).unwrap(),
|
||||
entries::gid2grp(meta.gid()).unwrap(),
|
||||
entries::uid2usr(dest_uid).unwrap(),
|
||||
entries::gid2grp(dest_gid).unwrap()
|
||||
entries::uid2usr(uid).unwrap_or_else(|_| uid.to_string()),
|
||||
entries::gid2grp(gid).unwrap_or_else(|_| gid.to_string()),
|
||||
entries::uid2usr(dest_uid).unwrap_or_else(|_| dest_uid.to_string()),
|
||||
entries::gid2grp(dest_gid).unwrap_or_else(|_| dest_gid.to_string())
|
||||
)
|
||||
};
|
||||
};
|
||||
|
@ -119,21 +122,24 @@ pub fn wrap_chown<P: AsRef<Path>>(
|
|||
if changed {
|
||||
match verbosity.level {
|
||||
VerbosityLevel::Changes | VerbosityLevel::Verbose => {
|
||||
let gid = meta.gid();
|
||||
out = if verbosity.groups_only {
|
||||
format!(
|
||||
"changed group of {} from {} to {}",
|
||||
path.quote(),
|
||||
entries::gid2grp(meta.gid()).unwrap(),
|
||||
entries::gid2grp(dest_gid).unwrap()
|
||||
entries::gid2grp(gid).unwrap_or_else(|_| gid.to_string()),
|
||||
entries::gid2grp(dest_gid).unwrap_or_else(|_| dest_gid.to_string())
|
||||
)
|
||||
} else {
|
||||
let gid = meta.gid();
|
||||
let uid = meta.uid();
|
||||
format!(
|
||||
"changed ownership of {} from {}:{} to {}:{}",
|
||||
path.quote(),
|
||||
entries::uid2usr(meta.uid()).unwrap(),
|
||||
entries::gid2grp(meta.gid()).unwrap(),
|
||||
entries::uid2usr(dest_uid).unwrap(),
|
||||
entries::gid2grp(dest_gid).unwrap()
|
||||
entries::uid2usr(uid).unwrap_or_else(|_| uid.to_string()),
|
||||
entries::gid2grp(gid).unwrap_or_else(|_| gid.to_string()),
|
||||
entries::uid2usr(dest_uid).unwrap_or_else(|_| dest_uid.to_string()),
|
||||
entries::gid2grp(dest_gid).unwrap_or_else(|_| dest_gid.to_string())
|
||||
)
|
||||
};
|
||||
}
|
||||
|
@ -150,8 +156,8 @@ pub fn wrap_chown<P: AsRef<Path>>(
|
|||
format!(
|
||||
"ownership of {} retained as {}:{}",
|
||||
path.quote(),
|
||||
entries::uid2usr(dest_uid).unwrap(),
|
||||
entries::gid2grp(dest_gid).unwrap()
|
||||
entries::uid2usr(dest_uid).unwrap_or_else(|_| dest_uid.to_string()),
|
||||
entries::gid2grp(dest_gid).unwrap_or_else(|_| dest_gid.to_string())
|
||||
)
|
||||
};
|
||||
}
|
||||
|
@ -456,6 +462,7 @@ pub fn chown_base<'a>(
|
|||
command = command.arg(
|
||||
Arg::new(options::ARG_FILES)
|
||||
.value_name(options::ARG_FILES)
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.multiple_occurrences(true)
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
|
|
|
@ -418,6 +418,29 @@ fn test_chown_only_user_id() {
|
|||
.stderr_contains(&"failed to change");
|
||||
}
|
||||
|
||||
/// Test for setting the owner to a user ID for a user that does not exist.
|
||||
///
|
||||
/// For example:
|
||||
///
|
||||
/// $ touch f && chown 12345 f
|
||||
///
|
||||
/// succeeds with exit status 0 and outputs nothing. The owner of the
|
||||
/// file is set to 12345, even though no user with that ID exists.
|
||||
///
|
||||
/// This test must be run as root, because only the root user can
|
||||
/// transfer ownership of a file.
|
||||
#[test]
|
||||
fn test_chown_only_user_id_nonexistent_user() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
let at = ts.fixtures.clone();
|
||||
at.touch("f");
|
||||
if let Ok(result) = run_ucmd_as_root(&ts, &["12345", "f"]) {
|
||||
result.success().no_stdout().no_stderr();
|
||||
} else {
|
||||
print!("Test skipped; requires root user");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
// FixME: stderr = chown: ownership of 'test_chown_file1' retained as cuuser:wheel
|
||||
#[cfg(not(target_os = "freebsd"))]
|
||||
|
@ -461,6 +484,29 @@ fn test_chown_only_group_id() {
|
|||
.stderr_contains(&"failed to change");
|
||||
}
|
||||
|
||||
/// Test for setting the group to a group ID for a group that does not exist.
|
||||
///
|
||||
/// For example:
|
||||
///
|
||||
/// $ touch f && chown :12345 f
|
||||
///
|
||||
/// succeeds with exit status 0 and outputs nothing. The group of the
|
||||
/// file is set to 12345, even though no group with that ID exists.
|
||||
///
|
||||
/// This test must be run as root, because only the root user can
|
||||
/// transfer ownership of a file.
|
||||
#[test]
|
||||
fn test_chown_only_group_id_nonexistent_group() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
let at = ts.fixtures.clone();
|
||||
at.touch("f");
|
||||
if let Ok(result) = run_ucmd_as_root(&ts, &[":12345", "f"]) {
|
||||
result.success().no_stdout().no_stderr();
|
||||
} else {
|
||||
print!("Test skipped; requires root user");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chown_owner_group_id() {
|
||||
// test chown 1111:1111 file.txt
|
||||
|
|
|
@ -73,7 +73,7 @@ fn test_df_output() {
|
|||
"Filesystem",
|
||||
"Size",
|
||||
"Used",
|
||||
"Available",
|
||||
"Avail",
|
||||
"Capacity",
|
||||
"Use%",
|
||||
"Mounted",
|
||||
|
@ -84,7 +84,7 @@ fn test_df_output() {
|
|||
"Filesystem",
|
||||
"Size",
|
||||
"Used",
|
||||
"Available",
|
||||
"Avail",
|
||||
"Use%",
|
||||
"Mounted",
|
||||
"on",
|
||||
|
@ -107,7 +107,7 @@ fn test_df_output_overridden() {
|
|||
"Filesystem",
|
||||
"Size",
|
||||
"Used",
|
||||
"Available",
|
||||
"Avail",
|
||||
"Capacity",
|
||||
"Use%",
|
||||
"Mounted",
|
||||
|
@ -118,7 +118,7 @@ fn test_df_output_overridden() {
|
|||
"Filesystem",
|
||||
"Size",
|
||||
"Used",
|
||||
"Available",
|
||||
"Avail",
|
||||
"Use%",
|
||||
"Mounted",
|
||||
"on",
|
||||
|
@ -134,6 +134,46 @@ fn test_df_output_overridden() {
|
|||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_headers() {
|
||||
let expected = if cfg!(target_os = "macos") {
|
||||
vec![
|
||||
"Filesystem",
|
||||
"1K-blocks",
|
||||
"Used",
|
||||
"Available",
|
||||
"Capacity",
|
||||
"Use%",
|
||||
"Mounted",
|
||||
"on",
|
||||
]
|
||||
} else {
|
||||
vec![
|
||||
"Filesystem",
|
||||
"1K-blocks",
|
||||
"Used",
|
||||
"Available",
|
||||
"Use%",
|
||||
"Mounted",
|
||||
"on",
|
||||
]
|
||||
};
|
||||
let output = new_ucmd!().succeeds().stdout_move_str();
|
||||
let actual = output.lines().take(1).collect::<Vec<&str>>()[0];
|
||||
let actual = actual.split_whitespace().collect::<Vec<_>>();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_precedence_of_human_readable_header_over_output_header() {
|
||||
let output = new_ucmd!()
|
||||
.args(&["-H", "--output=size"])
|
||||
.succeeds()
|
||||
.stdout_move_str();
|
||||
let header = output.lines().next().unwrap().to_string();
|
||||
assert_eq!(header.trim(), "Size");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_total_option_with_single_dash() {
|
||||
// These should fail because `-total` should have two dashes,
|
||||
|
@ -395,6 +435,30 @@ fn test_default_block_size() {
|
|||
assert_eq!(header, "512B-blocks");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_block_size_in_posix_portability_mode() {
|
||||
fn get_header(s: &str) -> String {
|
||||
s.lines()
|
||||
.next()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.split_whitespace()
|
||||
.nth(1)
|
||||
.unwrap()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
let output = new_ucmd!().arg("-P").succeeds().stdout_move_str();
|
||||
assert_eq!(get_header(&output), "1024-blocks");
|
||||
|
||||
let output = new_ucmd!()
|
||||
.arg("-P")
|
||||
.env("POSIXLY_CORRECT", "1")
|
||||
.succeeds()
|
||||
.stdout_move_str();
|
||||
assert_eq!(get_header(&output), "512-blocks");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_block_size_1024() {
|
||||
fn get_header(block_size: u64) -> String {
|
||||
|
@ -443,6 +507,31 @@ fn test_block_size_with_suffix() {
|
|||
assert_eq!(get_header("1GB"), "1GB-blocks");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_block_size_in_posix_portability_mode() {
|
||||
fn get_header(block_size: &str) -> String {
|
||||
let output = new_ucmd!()
|
||||
.args(&["-P", "-B", block_size])
|
||||
.succeeds()
|
||||
.stdout_move_str();
|
||||
output
|
||||
.lines()
|
||||
.next()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.split_whitespace()
|
||||
.nth(1)
|
||||
.unwrap()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
assert_eq!(get_header("1024"), "1024-blocks");
|
||||
assert_eq!(get_header("1K"), "1024-blocks");
|
||||
assert_eq!(get_header("1KB"), "1000-blocks");
|
||||
assert_eq!(get_header("1M"), "1048576-blocks");
|
||||
assert_eq!(get_header("1MB"), "1000000-blocks");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_too_large_block_size() {
|
||||
fn run_command(size: &str) {
|
||||
|
@ -465,6 +554,16 @@ fn test_invalid_block_size() {
|
|||
.arg("--block-size=x")
|
||||
.fails()
|
||||
.stderr_contains("invalid --block-size argument 'x'");
|
||||
|
||||
new_ucmd!()
|
||||
.arg("--block-size=0")
|
||||
.fails()
|
||||
.stderr_contains("invalid --block-size argument '0'");
|
||||
|
||||
new_ucmd!()
|
||||
.arg("--block-size=0K")
|
||||
.fails()
|
||||
.stderr_contains("invalid --block-size argument '0K'");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
use crate::common::util::*;
|
||||
|
||||
use uucore::display::Quotable;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use tempfile::tempdir;
|
||||
|
||||
|
@ -497,3 +499,15 @@ fn test_directory_permissions() {
|
|||
assert!(metadata.is_dir());
|
||||
assert_eq!(metadata.permissions().mode(), 0o40700);
|
||||
}
|
||||
|
||||
/// Test that a template with a path separator is invalid.
|
||||
#[test]
|
||||
fn test_template_path_separator() {
|
||||
new_ucmd!()
|
||||
.args(&["-t", "a/bXXX"])
|
||||
.fails()
|
||||
.stderr_only(format!(
|
||||
"mktemp: invalid template, {}, contains directory separator\n",
|
||||
"a/bXXX".quote()
|
||||
));
|
||||
}
|
||||
|
|
|
@ -346,14 +346,25 @@ fn test_printf() {
|
|||
ts.ucmd().args(&args).succeeds().stdout_is(expected_stdout);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
#[cfg(disable_until_fixed)]
|
||||
#[cfg(unix)]
|
||||
fn test_pipe_fifo() {
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
at.mkfifo("FIFO");
|
||||
ucmd.arg("FIFO")
|
||||
.run()
|
||||
.no_stderr()
|
||||
.stdout_contains("fifo")
|
||||
.stdout_contains("File: FIFO")
|
||||
.succeeded();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(unix, not(target_os = "android")))]
|
||||
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())
|
||||
|
@ -362,17 +373,25 @@ fn test_stdin_pipe_fifo1() {
|
|||
.stdout_contains("fifo")
|
||||
.stdout_contains("File: -")
|
||||
.succeeded();
|
||||
new_ucmd!()
|
||||
.args(&["-L", "-"])
|
||||
.set_stdin(std::process::Stdio::piped())
|
||||
.run()
|
||||
.no_stderr()
|
||||
.stdout_contains("fifo")
|
||||
.stdout_contains("File: -")
|
||||
.succeeded();
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
#[cfg(disable_until_fixed)]
|
||||
#[cfg(all(unix, not(target_os = "android")))]
|
||||
fn test_stdin_pipe_fifo2() {
|
||||
// $ stat -
|
||||
// File: -
|
||||
// Size: 0 Blocks: 0 IO Block: 1024 character special file
|
||||
new_ucmd!()
|
||||
.arg("-")
|
||||
.set_stdin(std::process::Stdio::null())
|
||||
.run()
|
||||
.no_stderr()
|
||||
.stdout_contains("character special file")
|
||||
|
@ -380,20 +399,18 @@ fn test_stdin_pipe_fifo2() {
|
|||
.succeeded();
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
#[cfg(disable_until_fixed)]
|
||||
#[cfg(all(unix, not(any(target_os = "android", target_os = "macos"))))]
|
||||
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!()
|
||||
ts.ucmd()
|
||||
.arg("-")
|
||||
.set_stdin(std::fs::File::open("f").unwrap())
|
||||
.set_stdin(std::fs::File::open(at.plus("f")).unwrap())
|
||||
.run()
|
||||
.no_stderr()
|
||||
.stdout_contains("regular empty file")
|
||||
|
|
Loading…
Reference in a new issue