mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
Merge branch 'main' into fix-cp-socket-file-unix
This commit is contained in:
@@ -62,6 +62,7 @@ jobs:
|
||||
- { name: fuzz_parse_size, should_pass: true }
|
||||
- { name: fuzz_parse_time, should_pass: true }
|
||||
- { name: fuzz_seq_parse_number, should_pass: true }
|
||||
- { name: fuzz_non_utf8_paths, should_pass: true }
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
@@ -129,6 +129,176 @@ jobs:
|
||||
|
||||
echo "::notice::All Fluent files passed Mozilla Fluent Linter validation"
|
||||
|
||||
l10n_clap_error_localization:
|
||||
name: L10n/Clap Error Localization Test
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SCCACHE_GHA_ENABLED: "true"
|
||||
RUSTC_WRAPPER: "sccache"
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.9
|
||||
- name: Install/setup prerequisites
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get -y update ; sudo apt-get -y install libselinux1-dev locales
|
||||
sudo locale-gen --keep-existing fr_FR.UTF-8
|
||||
locale -a | grep -i fr || exit 1
|
||||
- name: Build coreutils with clap localization support
|
||||
shell: bash
|
||||
run: |
|
||||
cargo build --features feat_os_unix --bin coreutils
|
||||
- name: Test English clap error localization
|
||||
shell: bash
|
||||
run: |
|
||||
export LANG=en_US.UTF-8
|
||||
export LC_ALL=en_US.UTF-8
|
||||
|
||||
# Test invalid argument error - should show colored error message
|
||||
echo "Testing invalid argument error..."
|
||||
error_output=$(cargo run --features feat_os_unix --bin coreutils -- cp --invalid-arg 2>&1 || echo "Expected error occurred")
|
||||
echo "Error output: $error_output"
|
||||
|
||||
# Check for expected English clap error patterns
|
||||
english_errors_found=0
|
||||
|
||||
if echo "$error_output" | grep -q "error.*unexpected argument"; then
|
||||
echo "✓ Found English clap error message pattern"
|
||||
english_errors_found=$((english_errors_found + 1))
|
||||
fi
|
||||
|
||||
if echo "$error_output" | grep -q "Usage:"; then
|
||||
echo "✓ Found English usage pattern"
|
||||
english_errors_found=$((english_errors_found + 1))
|
||||
fi
|
||||
|
||||
if echo "$error_output" | grep -q "For more information.*--help"; then
|
||||
echo "✓ Found English help suggestion"
|
||||
english_errors_found=$((english_errors_found + 1))
|
||||
fi
|
||||
|
||||
# Test typo suggestion
|
||||
echo "Testing typo suggestion..."
|
||||
typo_output=$(cargo run --features feat_os_unix --bin coreutils -- ls --verbos 2>&1 || echo "Expected error occurred")
|
||||
echo "Typo output: $typo_output"
|
||||
|
||||
if echo "$typo_output" | grep -q "similar.*verbose"; then
|
||||
echo "✓ Found English typo suggestion"
|
||||
english_errors_found=$((english_errors_found + 1))
|
||||
fi
|
||||
|
||||
echo "English clap errors found: $english_errors_found"
|
||||
if [ "$english_errors_found" -ge 2 ]; then
|
||||
echo "✓ SUCCESS: English clap error localization working"
|
||||
else
|
||||
echo "✗ ERROR: English clap error localization not working properly"
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
RUST_BACKTRACE: "1"
|
||||
|
||||
- name: Test French clap error localization
|
||||
shell: bash
|
||||
run: |
|
||||
export LANG=fr_FR.UTF-8
|
||||
export LC_ALL=fr_FR.UTF-8
|
||||
|
||||
# Test invalid argument error - should show French colored error message
|
||||
echo "Testing invalid argument error in French..."
|
||||
error_output=$(cargo run --features feat_os_unix --bin coreutils -- cp --invalid-arg 2>&1 || echo "Expected error occurred")
|
||||
echo "French error output: $error_output"
|
||||
|
||||
# Check for expected French clap error patterns
|
||||
french_errors_found=0
|
||||
|
||||
if echo "$error_output" | grep -q "erreur.*argument inattendu"; then
|
||||
echo "✓ Found French clap error message: 'erreur: argument inattendu'"
|
||||
french_errors_found=$((french_errors_found + 1))
|
||||
fi
|
||||
|
||||
if echo "$error_output" | grep -q "conseil.*pour passer.*comme valeur"; then
|
||||
echo "✓ Found French tip message: 'conseil: pour passer ... comme valeur'"
|
||||
french_errors_found=$((french_errors_found + 1))
|
||||
fi
|
||||
|
||||
if echo "$error_output" | grep -q "Utilisation:"; then
|
||||
echo "✓ Found French usage pattern: 'Utilisation:'"
|
||||
french_errors_found=$((french_errors_found + 1))
|
||||
fi
|
||||
|
||||
if echo "$error_output" | grep -q "Pour plus d'informations.*--help"; then
|
||||
echo "✓ Found French help suggestion: 'Pour plus d'informations'"
|
||||
french_errors_found=$((french_errors_found + 1))
|
||||
fi
|
||||
|
||||
# Test typo suggestion in French
|
||||
echo "Testing typo suggestion in French..."
|
||||
typo_output=$(cargo run --features feat_os_unix --bin coreutils -- ls --verbos 2>&1 || echo "Expected error occurred")
|
||||
echo "French typo output: $typo_output"
|
||||
|
||||
if echo "$typo_output" | grep -q "conseil.*similaire.*verbose"; then
|
||||
echo "✓ Found French typo suggestion with 'conseil'"
|
||||
french_errors_found=$((french_errors_found + 1))
|
||||
fi
|
||||
|
||||
echo "French clap errors found: $french_errors_found"
|
||||
if [ "$french_errors_found" -ge 2 ]; then
|
||||
echo "✓ SUCCESS: French clap error localization working - found $french_errors_found French patterns"
|
||||
else
|
||||
echo "✗ ERROR: French clap error localization not working properly"
|
||||
echo "Note: This might be expected if French common locale files are not available"
|
||||
# Don't fail the build - French clap localization might not be fully set up yet
|
||||
echo "::warning::French clap error localization not working, but continuing"
|
||||
fi
|
||||
|
||||
# Test that colors are working (ANSI escape codes)
|
||||
echo "Testing ANSI color codes in error output..."
|
||||
if echo "$error_output" | grep -q $'\x1b\[3[0-7]m'; then
|
||||
echo "✓ Found ANSI color codes in error output"
|
||||
else
|
||||
echo "✗ No ANSI color codes found - colors may not be working"
|
||||
echo "::warning::ANSI color codes not detected in clap error output"
|
||||
fi
|
||||
env:
|
||||
RUST_BACKTRACE: "1"
|
||||
|
||||
- name: Test clap localization with multiple utilities
|
||||
shell: bash
|
||||
run: |
|
||||
export LANG=en_US.UTF-8
|
||||
export LC_ALL=en_US.UTF-8
|
||||
|
||||
utilities_to_test=("ls" "cat" "touch" "cp" "mv")
|
||||
utilities_passed=0
|
||||
|
||||
for util in "${utilities_to_test[@]}"; do
|
||||
echo "Testing $util with invalid argument..."
|
||||
util_error=$(cargo run --features feat_os_unix --bin coreutils -- "$util" --nonexistent-flag 2>&1 || echo "Expected error occurred")
|
||||
|
||||
if echo "$util_error" | grep -q "error.*unexpected argument"; then
|
||||
echo "✓ $util: clap localization working"
|
||||
utilities_passed=$((utilities_passed + 1))
|
||||
else
|
||||
echo "✗ $util: clap localization not working"
|
||||
echo "Output: $util_error"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Utilities with working clap localization: $utilities_passed/${#utilities_to_test[@]}"
|
||||
if [ "$utilities_passed" -ge 3 ]; then
|
||||
echo "✓ SUCCESS: Clap localization working across multiple utilities"
|
||||
else
|
||||
echo "✗ ERROR: Clap localization not working for enough utilities"
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
RUST_BACKTRACE: "1"
|
||||
|
||||
l10n_french_integration:
|
||||
name: L10n/French Integration Test
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
Vendored
+2
-1
@@ -27,7 +27,8 @@
|
||||
"src/uu/dd/test-resources/**",
|
||||
"vendor/**",
|
||||
"**/*.svg",
|
||||
"src/uu/*/locales/*.ftl"
|
||||
"src/uu/*/locales/*.ftl",
|
||||
"src/uucore/locales/*.ftl"
|
||||
],
|
||||
|
||||
"enableGlobDot": true,
|
||||
|
||||
@@ -418,6 +418,14 @@ endif
|
||||
|
||||
ifeq ($(LOCALES),y)
|
||||
locales:
|
||||
@# Copy uucore common locales
|
||||
@if [ -d "$(BASEDIR)/src/uucore/locales" ]; then \
|
||||
mkdir -p "$(BUILDDIR)/locales/uucore"; \
|
||||
for locale_file in "$(BASEDIR)"/src/uucore/locales/*.ftl; do \
|
||||
$(INSTALL) -v "$$locale_file" "$(BUILDDIR)/locales/uucore/"; \
|
||||
done; \
|
||||
fi; \
|
||||
# Copy utility-specific locales
|
||||
@for prog in $(INSTALLEES); do \
|
||||
if [ -d "$(BASEDIR)/src/uu/$$prog/locales" ]; then \
|
||||
mkdir -p "$(BUILDDIR)/locales/$$prog"; \
|
||||
|
||||
@@ -138,3 +138,9 @@ name = "fuzz_cksum"
|
||||
path = "fuzz_targets/fuzz_cksum.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_non_utf8_paths"
|
||||
path = "fuzz_targets/fuzz_non_utf8_paths.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
@@ -0,0 +1,442 @@
|
||||
// This file is part of the uutils coreutils package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
// spell-checker:ignore osstring
|
||||
|
||||
#![no_main]
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use rand::Rng;
|
||||
use rand::prelude::IndexedRandom;
|
||||
use std::collections::HashSet;
|
||||
use std::env::temp_dir;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::fs;
|
||||
use std::os::unix::ffi::{OsStrExt, OsStringExt};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use uufuzz::{CommandResult, run_gnu_cmd};
|
||||
// Programs that typically take file/path arguments and should be tested
|
||||
static PATH_PROGRAMS: &[&str] = &[
|
||||
// Core file operations
|
||||
"cat",
|
||||
"cp",
|
||||
"mv",
|
||||
"rm",
|
||||
"ln",
|
||||
"link",
|
||||
"unlink",
|
||||
"touch",
|
||||
"truncate",
|
||||
// Path operations
|
||||
"ls",
|
||||
"mkdir",
|
||||
"rmdir",
|
||||
"du",
|
||||
"stat",
|
||||
"mktemp",
|
||||
"df",
|
||||
"basename",
|
||||
"dirname",
|
||||
"readlink",
|
||||
"realpath",
|
||||
"pathchk",
|
||||
"chroot",
|
||||
// File processing
|
||||
"head",
|
||||
"tail",
|
||||
"tee",
|
||||
"more",
|
||||
"od",
|
||||
"wc",
|
||||
"cksum",
|
||||
"sum",
|
||||
"nl",
|
||||
"tac",
|
||||
"sort",
|
||||
"uniq",
|
||||
"split",
|
||||
"csplit",
|
||||
"cut",
|
||||
"tr",
|
||||
"shred",
|
||||
"shuf",
|
||||
"ptx",
|
||||
"tsort",
|
||||
// Text processing with files
|
||||
"chmod",
|
||||
"chown",
|
||||
"chgrp",
|
||||
"install",
|
||||
"chcon",
|
||||
"runcon",
|
||||
"comm",
|
||||
"join",
|
||||
"paste",
|
||||
"pr",
|
||||
"fmt",
|
||||
"fold",
|
||||
"expand",
|
||||
"unexpand",
|
||||
"dir",
|
||||
"vdir",
|
||||
"mkfifo",
|
||||
"mknod",
|
||||
"hashsum",
|
||||
// File I/O utilities
|
||||
"dd",
|
||||
"sync",
|
||||
"stdbuf",
|
||||
"dircolors",
|
||||
// Encoding/decoding utilities
|
||||
"base32",
|
||||
"base64",
|
||||
"basenc",
|
||||
"stty",
|
||||
"tty",
|
||||
"env",
|
||||
"nohup",
|
||||
"nice",
|
||||
"timeout",
|
||||
];
|
||||
|
||||
fn generate_non_utf8_bytes() -> Vec<u8> {
|
||||
let mut rng = rand::rng();
|
||||
let mut bytes = Vec::new();
|
||||
|
||||
// Start with some valid UTF-8 to make it look like a reasonable path
|
||||
bytes.extend_from_slice(b"test_");
|
||||
|
||||
// Add some invalid UTF-8 sequences
|
||||
match rng.random_range(0..4) {
|
||||
0 => bytes.extend_from_slice(&[0xFF, 0xFE]), // Invalid UTF-8
|
||||
1 => bytes.extend_from_slice(&[0xC0, 0x80]), // Overlong encoding
|
||||
2 => bytes.extend_from_slice(&[0xED, 0xA0, 0x80]), // UTF-16 surrogate
|
||||
_ => bytes.extend_from_slice(&[0xF4, 0x90, 0x80, 0x80]), // Beyond Unicode range
|
||||
}
|
||||
|
||||
bytes
|
||||
}
|
||||
|
||||
fn generate_non_utf8_osstring() -> OsString {
|
||||
OsString::from_vec(generate_non_utf8_bytes())
|
||||
}
|
||||
|
||||
fn setup_test_files() -> Result<(PathBuf, Vec<PathBuf>), std::io::Error> {
|
||||
let mut rng = rand::rng();
|
||||
let temp_root = temp_dir().join(format!("utf8_test_{}", rng.random::<u64>()));
|
||||
fs::create_dir_all(&temp_root)?;
|
||||
|
||||
let mut test_files = Vec::new();
|
||||
|
||||
// Create some files with non-UTF-8 names
|
||||
for i in 0..3 {
|
||||
let mut path_bytes = temp_root.as_os_str().as_bytes().to_vec();
|
||||
path_bytes.push(b'/');
|
||||
|
||||
if i == 0 {
|
||||
// One normal UTF-8 file for comparison
|
||||
path_bytes.extend_from_slice(b"normal_file.txt");
|
||||
} else {
|
||||
// Files with invalid UTF-8 names
|
||||
path_bytes.extend_from_slice(&generate_non_utf8_bytes());
|
||||
}
|
||||
|
||||
let file_path = PathBuf::from(OsStr::from_bytes(&path_bytes));
|
||||
|
||||
// Try to create the file - this may fail on some filesystems
|
||||
if let Ok(mut file) = fs::File::create(&file_path) {
|
||||
use std::io::Write;
|
||||
let _ = write!(file, "test content for file {}\n", i);
|
||||
test_files.push(file_path);
|
||||
}
|
||||
}
|
||||
|
||||
Ok((temp_root, test_files))
|
||||
}
|
||||
|
||||
fn test_program_with_non_utf8_path(program: &str, path: &PathBuf) -> CommandResult {
|
||||
let path_os = path.as_os_str();
|
||||
|
||||
// Use the locally built uutils binary instead of system PATH
|
||||
let local_binary = std::env::var("CARGO_BIN_FILE_COREUTILS")
|
||||
.unwrap_or_else(|_| "target/release/coreutils".to_string());
|
||||
|
||||
// Build appropriate arguments for each program
|
||||
let local_args = match program {
|
||||
// Programs that need mode/permissions
|
||||
"chmod" => vec![
|
||||
OsString::from(program),
|
||||
OsString::from("644"),
|
||||
path_os.to_owned(),
|
||||
],
|
||||
"chown" => vec![
|
||||
OsString::from(program),
|
||||
OsString::from("root:root"),
|
||||
path_os.to_owned(),
|
||||
],
|
||||
"chgrp" => vec![
|
||||
OsString::from(program),
|
||||
OsString::from("root"),
|
||||
path_os.to_owned(),
|
||||
],
|
||||
"chcon" => vec![
|
||||
OsString::from(program),
|
||||
OsString::from("system_u:object_r:admin_home_t:s0"),
|
||||
path_os.to_owned(),
|
||||
],
|
||||
"runcon" => {
|
||||
let coreutils_binary = std::env::var("CARGO_BIN_FILE_COREUTILS")
|
||||
.unwrap_or_else(|_| "target/release/coreutils".to_string());
|
||||
vec![
|
||||
OsString::from(program),
|
||||
OsString::from("system_u:object_r:admin_home_t:s0"),
|
||||
OsString::from(coreutils_binary),
|
||||
OsString::from("cat"),
|
||||
path_os.to_owned(),
|
||||
]
|
||||
}
|
||||
// Programs that need source and destination
|
||||
"cp" | "mv" | "ln" | "link" => {
|
||||
let dest_path = path.with_extension("dest");
|
||||
vec![
|
||||
OsString::from(program),
|
||||
path_os.to_owned(),
|
||||
dest_path.as_os_str().to_owned(),
|
||||
]
|
||||
}
|
||||
"install" => {
|
||||
let dest_path = path.with_extension("dest");
|
||||
vec![
|
||||
OsString::from(program),
|
||||
path_os.to_owned(),
|
||||
dest_path.as_os_str().to_owned(),
|
||||
]
|
||||
}
|
||||
// Programs that need size/truncate operations
|
||||
"truncate" => vec![
|
||||
OsString::from(program),
|
||||
OsString::from("--size=0"),
|
||||
path_os.to_owned(),
|
||||
],
|
||||
"split" => vec![
|
||||
OsString::from(program),
|
||||
path_os.to_owned(),
|
||||
OsString::from("split_prefix_"),
|
||||
],
|
||||
"csplit" => vec![
|
||||
OsString::from(program),
|
||||
path_os.to_owned(),
|
||||
OsString::from("1"),
|
||||
],
|
||||
// File creation programs
|
||||
"mkfifo" | "mknod" => {
|
||||
let new_path = path.with_extension("new");
|
||||
if program == "mknod" {
|
||||
vec![
|
||||
OsString::from(program),
|
||||
new_path.as_os_str().to_owned(),
|
||||
OsString::from("c"),
|
||||
OsString::from("1"),
|
||||
OsString::from("3"),
|
||||
]
|
||||
} else {
|
||||
vec![OsString::from(program), new_path.as_os_str().to_owned()]
|
||||
}
|
||||
}
|
||||
"dd" => vec![
|
||||
OsString::from(program),
|
||||
OsString::from(format!("if={}", path_os.to_string_lossy())),
|
||||
OsString::from("of=/dev/null"),
|
||||
OsString::from("bs=1"),
|
||||
OsString::from("count=1"),
|
||||
],
|
||||
// Hashsum needs algorithm
|
||||
"hashsum" => vec![
|
||||
OsString::from(program),
|
||||
OsString::from("--md5"),
|
||||
path_os.to_owned(),
|
||||
],
|
||||
// Encoding/decoding programs
|
||||
"base32" | "base64" | "basenc" => vec![OsString::from(program), path_os.to_owned()],
|
||||
"df" => vec![OsString::from(program), path_os.to_owned()],
|
||||
"chroot" => {
|
||||
// chroot needs a directory and command
|
||||
vec![
|
||||
OsString::from(program),
|
||||
path_os.to_owned(),
|
||||
OsString::from("true"),
|
||||
]
|
||||
}
|
||||
"sync" => vec![OsString::from(program), path_os.to_owned()],
|
||||
"stty" => vec![
|
||||
OsString::from(program),
|
||||
OsString::from("-F"),
|
||||
path_os.to_owned(),
|
||||
],
|
||||
"tty" => vec![OsString::from(program)], // tty doesn't take file args, but test anyway
|
||||
"env" => {
|
||||
let coreutils_binary = std::env::var("CARGO_BIN_FILE_COREUTILS")
|
||||
.unwrap_or_else(|_| "target/release/coreutils".to_string());
|
||||
vec![
|
||||
OsString::from(program),
|
||||
OsString::from(coreutils_binary),
|
||||
OsString::from("cat"),
|
||||
path_os.to_owned(),
|
||||
]
|
||||
}
|
||||
"nohup" => {
|
||||
let coreutils_binary = std::env::var("CARGO_BIN_FILE_COREUTILS")
|
||||
.unwrap_or_else(|_| "target/release/coreutils".to_string());
|
||||
vec![
|
||||
OsString::from(program),
|
||||
OsString::from(coreutils_binary),
|
||||
OsString::from("cat"),
|
||||
path_os.to_owned(),
|
||||
]
|
||||
}
|
||||
"nice" => {
|
||||
let coreutils_binary = std::env::var("CARGO_BIN_FILE_COREUTILS")
|
||||
.unwrap_or_else(|_| "target/release/coreutils".to_string());
|
||||
vec![
|
||||
OsString::from(program),
|
||||
OsString::from(coreutils_binary),
|
||||
OsString::from("cat"),
|
||||
path_os.to_owned(),
|
||||
]
|
||||
}
|
||||
"timeout" => {
|
||||
let coreutils_binary = std::env::var("CARGO_BIN_FILE_COREUTILS")
|
||||
.unwrap_or_else(|_| "target/release/coreutils".to_string());
|
||||
vec![
|
||||
OsString::from(program),
|
||||
OsString::from("1"),
|
||||
OsString::from(coreutils_binary),
|
||||
OsString::from("cat"),
|
||||
path_os.to_owned(),
|
||||
]
|
||||
}
|
||||
"stdbuf" => {
|
||||
let coreutils_binary = std::env::var("CARGO_BIN_FILE_COREUTILS")
|
||||
.unwrap_or_else(|_| "target/release/coreutils".to_string());
|
||||
vec![
|
||||
OsString::from(program),
|
||||
OsString::from("-o0"),
|
||||
OsString::from(coreutils_binary),
|
||||
OsString::from("cat"),
|
||||
path_os.to_owned(),
|
||||
]
|
||||
}
|
||||
// Programs that work with multiple files (use just one for testing)
|
||||
"comm" | "join" => {
|
||||
// These need two files, use the same file twice for simplicity
|
||||
vec![
|
||||
OsString::from(program),
|
||||
path_os.to_owned(),
|
||||
path_os.to_owned(),
|
||||
]
|
||||
}
|
||||
// Programs that typically take file input
|
||||
_ => vec![OsString::from(program), path_os.to_owned()],
|
||||
};
|
||||
|
||||
// Try to run the local uutils version
|
||||
match run_gnu_cmd(&local_binary, &local_args, false, None) {
|
||||
Ok(result) => result,
|
||||
Err(error_result) => {
|
||||
// Local command failed, return the error
|
||||
error_result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cleanup_test_files(temp_root: &PathBuf) {
|
||||
let _ = fs::remove_dir_all(temp_root);
|
||||
}
|
||||
|
||||
fn check_for_utf8_error_and_panic(result: &CommandResult, program: &str, path: &PathBuf) {
|
||||
let stderr_lower = result.stderr.to_lowercase();
|
||||
let is_utf8_error = stderr_lower.contains("invalid utf-8")
|
||||
|| stderr_lower.contains("not valid unicode")
|
||||
|| stderr_lower.contains("invalid utf8")
|
||||
|| stderr_lower.contains("utf-8 error");
|
||||
|
||||
if is_utf8_error {
|
||||
println!(
|
||||
"UTF-8 conversion error detected in {}: {}",
|
||||
program, result.stderr
|
||||
);
|
||||
println!("Path: {:?}", path);
|
||||
println!("Exit code: {}", result.exit_code);
|
||||
panic!(
|
||||
"FUZZER FAILURE: {} failed with UTF-8 error on non-UTF-8 path: {:?}",
|
||||
program, path
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fuzz_target!(|_data: &[u8]| {
|
||||
let mut rng = rand::rng();
|
||||
|
||||
// Set up test environment
|
||||
let (temp_root, test_files) = match setup_test_files() {
|
||||
Ok(files) => files,
|
||||
Err(_) => return, // Skip if we can't set up test files
|
||||
};
|
||||
|
||||
// Pick multiple random programs to test in each iteration
|
||||
let num_programs_to_test = rng.random_range(1..=3); // Test 1-3 programs per iteration
|
||||
let mut tested_programs = HashSet::new();
|
||||
|
||||
let mut programs_tested = Vec::<String>::new();
|
||||
|
||||
for _ in 0..num_programs_to_test {
|
||||
// Pick a random program that we haven't tested yet in this iteration
|
||||
let available_programs: Vec<_> = PATH_PROGRAMS
|
||||
.iter()
|
||||
.filter(|p| !tested_programs.contains(*p))
|
||||
.collect();
|
||||
|
||||
if available_programs.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
let program = available_programs.choose(&mut rng).unwrap();
|
||||
tested_programs.insert(*program);
|
||||
programs_tested.push(program.to_string());
|
||||
|
||||
// Test with one random file that has non-UTF-8 names (not all files to speed up)
|
||||
if let Some(test_file) = test_files.choose(&mut rng) {
|
||||
let result = test_program_with_non_utf8_path(program, test_file);
|
||||
|
||||
// Check if the program handled the non-UTF-8 path gracefully
|
||||
check_for_utf8_error_and_panic(&result, program, test_file);
|
||||
}
|
||||
|
||||
// Special cases for programs that need additional testing
|
||||
if **program == "mkdir" || **program == "mktemp" {
|
||||
let non_utf8_dir_name = generate_non_utf8_osstring();
|
||||
let non_utf8_dir = temp_root.join(non_utf8_dir_name);
|
||||
|
||||
let local_binary = std::env::var("CARGO_BIN_FILE_COREUTILS")
|
||||
.unwrap_or_else(|_| "target/release/coreutils".to_string());
|
||||
let mkdir_args = vec![OsString::from("mkdir"), non_utf8_dir.as_os_str().to_owned()];
|
||||
|
||||
let mkdir_result = run_gnu_cmd(&local_binary, &mkdir_args, false, None);
|
||||
match mkdir_result {
|
||||
Ok(result) => {
|
||||
check_for_utf8_error_and_panic(&result, "mkdir", &non_utf8_dir);
|
||||
}
|
||||
Err(error) => {
|
||||
check_for_utf8_error_and_panic(&error, "mkdir", &non_utf8_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("Tested programs: {}", programs_tested.join(", "));
|
||||
|
||||
// Clean up
|
||||
cleanup_test_files(&temp_root);
|
||||
});
|
||||
@@ -6,12 +6,13 @@
|
||||
use platform_info::*;
|
||||
|
||||
use clap::Command;
|
||||
use uucore::LocalizedCommand;
|
||||
use uucore::error::{UResult, USimpleError};
|
||||
use uucore::translate;
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
uu_app().try_get_matches_from(args)?;
|
||||
uu_app().get_matches_from_localized(args);
|
||||
|
||||
let uts =
|
||||
PlatformInfo::new().map_err(|_e| USimpleError::new(1, translate!("cannot-get-system")))?;
|
||||
@@ -23,6 +24,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
pub fn uu_app() -> Command {
|
||||
Command::new(uucore::util_name())
|
||||
.version(uucore::crate_version!())
|
||||
.help_template(uucore::localized_help_template(uucore::util_name()))
|
||||
.about(translate!("arch-about"))
|
||||
.after_help(translate!("arch-after-help"))
|
||||
.infer_long_args(true)
|
||||
|
||||
@@ -6,15 +6,16 @@
|
||||
// spell-checker:ignore hexupper lsbf msbf unpadded nopad aGVsbG8sIHdvcmxkIQ
|
||||
|
||||
use clap::{Arg, ArgAction, Command};
|
||||
use std::ffi::OsString;
|
||||
use std::fs::File;
|
||||
use std::io::{self, ErrorKind, Read, Seek, SeekFrom};
|
||||
use std::path::{Path, PathBuf};
|
||||
use uucore::LocalizedCommand;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::encoding::{
|
||||
BASE2LSBF, BASE2MSBF, Format, Z85Wrapper,
|
||||
BASE2LSBF, BASE2MSBF, EncodingWrapper, Format, SupportsFastDecodeAndEncode, Z85Wrapper,
|
||||
for_base_common::{BASE32, BASE32HEX, BASE64, BASE64_NOPAD, BASE64URL, HEXUPPER_PERMISSIVE},
|
||||
};
|
||||
use uucore::encoding::{EncodingWrapper, SupportsFastDecodeAndEncode};
|
||||
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
|
||||
use uucore::format_usage;
|
||||
use uucore::translate;
|
||||
@@ -44,14 +45,14 @@ pub mod options {
|
||||
|
||||
impl Config {
|
||||
pub fn from(options: &clap::ArgMatches) -> UResult<Self> {
|
||||
let to_read = match options.get_many::<String>(options::FILE) {
|
||||
let to_read = match options.get_many::<OsString>(options::FILE) {
|
||||
Some(mut values) => {
|
||||
let name = values.next().unwrap();
|
||||
|
||||
if let Some(extra_op) = values.next() {
|
||||
return Err(UUsageError::new(
|
||||
BASE_CMD_PARSE_ERROR,
|
||||
translate!("base-common-extra-operand", "operand" => extra_op.quote()),
|
||||
translate!("base-common-extra-operand", "operand" => extra_op.to_string_lossy().quote()),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -100,12 +101,14 @@ pub fn parse_base_cmd_args(
|
||||
usage: &str,
|
||||
) -> UResult<Config> {
|
||||
let command = base_app(about, usage);
|
||||
Config::from(&command.try_get_matches_from(args)?)
|
||||
let matches = command.get_matches_from_localized(args);
|
||||
Config::from(&matches)
|
||||
}
|
||||
|
||||
pub fn base_app(about: &'static str, usage: &str) -> Command {
|
||||
Command::new(uucore::util_name())
|
||||
.version(uucore::crate_version!())
|
||||
.help_template(uucore::localized_help_template(uucore::util_name()))
|
||||
.about(about)
|
||||
.override_usage(format_usage(usage))
|
||||
.infer_long_args(true)
|
||||
@@ -141,6 +144,7 @@ pub fn base_app(about: &'static str, usage: &str) -> Command {
|
||||
Arg::new(options::FILE)
|
||||
.index(1)
|
||||
.action(ArgAction::Append)
|
||||
.value_parser(clap::value_parser!(OsString))
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ use uucore::error::{UResult, UUsageError};
|
||||
use uucore::format_usage;
|
||||
use uucore::line_ending::LineEnding;
|
||||
|
||||
use uucore::LocalizedCommand;
|
||||
use uucore::translate;
|
||||
|
||||
pub mod options {
|
||||
@@ -29,7 +30,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
//
|
||||
// Argument parsing
|
||||
//
|
||||
let matches = uu_app().try_get_matches_from(args)?;
|
||||
let matches = uu_app().get_matches_from_localized(args);
|
||||
|
||||
let line_ending = LineEnding::from_zero_flag(matches.get_flag(options::ZERO));
|
||||
|
||||
@@ -81,6 +82,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
pub fn uu_app() -> Command {
|
||||
Command::new(uucore::util_name())
|
||||
.version(uucore::crate_version!())
|
||||
.help_template(uucore::localized_help_template(uucore::util_name()))
|
||||
.about(translate!("basename-about"))
|
||||
.override_usage(format_usage(&translate!("basename-usage")))
|
||||
.infer_long_args(true)
|
||||
|
||||
+10
-6
@@ -10,6 +10,7 @@ mod platform;
|
||||
use crate::platform::is_unsafe_overwrite;
|
||||
use clap::{Arg, ArgAction, Command};
|
||||
use memchr::memchr2;
|
||||
use std::ffi::OsString;
|
||||
use std::fs::{File, metadata};
|
||||
use std::io::{self, BufWriter, ErrorKind, IsTerminal, Read, Write};
|
||||
/// Unix domain socket support
|
||||
@@ -22,6 +23,7 @@ use std::os::unix::fs::FileTypeExt;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::net::UnixStream;
|
||||
use thiserror::Error;
|
||||
use uucore::LocalizedCommand;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::UResult;
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
@@ -230,7 +232,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
libc::signal(libc::SIGPIPE, libc::SIG_DFL);
|
||||
}
|
||||
|
||||
let matches = uu_app().try_get_matches_from(args)?;
|
||||
let matches = uu_app().get_matches_from_localized(args);
|
||||
|
||||
let number_mode = if matches.get_flag(options::NUMBER_NONBLANK) {
|
||||
NumberingMode::NonEmpty
|
||||
@@ -266,9 +268,9 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
.any(|v| matches.get_flag(v));
|
||||
|
||||
let squeeze_blank = matches.get_flag(options::SQUEEZE_BLANK);
|
||||
let files: Vec<String> = match matches.get_many::<String>(options::FILE) {
|
||||
let files: Vec<OsString> = match matches.get_many::<OsString>(options::FILE) {
|
||||
Some(v) => v.cloned().collect(),
|
||||
None => vec!["-".to_owned()],
|
||||
None => vec![OsString::from("-")],
|
||||
};
|
||||
|
||||
let options = OutputOptions {
|
||||
@@ -286,12 +288,14 @@ pub fn uu_app() -> Command {
|
||||
.version(uucore::crate_version!())
|
||||
.override_usage(format_usage(&translate!("cat-usage")))
|
||||
.about(translate!("cat-about"))
|
||||
.help_template(uucore::localized_help_template(uucore::util_name()))
|
||||
.infer_long_args(true)
|
||||
.args_override_self(true)
|
||||
.arg(
|
||||
Arg::new(options::FILE)
|
||||
.hide(true)
|
||||
.action(ArgAction::Append)
|
||||
.value_parser(clap::value_parser!(OsString))
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
)
|
||||
.arg(
|
||||
@@ -377,7 +381,7 @@ fn cat_handle<R: FdReadable>(
|
||||
}
|
||||
}
|
||||
|
||||
fn cat_path(path: &str, options: &OutputOptions, state: &mut OutputState) -> CatResult<()> {
|
||||
fn cat_path(path: &OsString, options: &OutputOptions, state: &mut OutputState) -> CatResult<()> {
|
||||
match get_input_type(path)? {
|
||||
InputType::StdIn => {
|
||||
let stdin = io::stdin();
|
||||
@@ -415,7 +419,7 @@ fn cat_path(path: &str, options: &OutputOptions, state: &mut OutputState) -> Cat
|
||||
}
|
||||
}
|
||||
|
||||
fn cat_files(files: &[String], options: &OutputOptions) -> UResult<()> {
|
||||
fn cat_files(files: &[OsString], options: &OutputOptions) -> UResult<()> {
|
||||
let mut state = OutputState {
|
||||
line_number: LineNumber::new(),
|
||||
at_line_start: true,
|
||||
@@ -450,7 +454,7 @@ fn cat_files(files: &[String], options: &OutputOptions) -> UResult<()> {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `path` - Path on a file system to classify metadata
|
||||
fn get_input_type(path: &str) -> CatResult<InputType> {
|
||||
fn get_input_type(path: &OsString) -> CatResult<InputType> {
|
||||
if path == "-" {
|
||||
return Ok(InputType::StdIn);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#![allow(clippy::upper_case_acronyms)]
|
||||
|
||||
use clap::builder::ValueParser;
|
||||
use uucore::LocalizedCommand;
|
||||
use uucore::error::{UResult, USimpleError, UUsageError};
|
||||
use uucore::translate;
|
||||
use uucore::{display::Quotable, format_usage, show_error, show_warning};
|
||||
@@ -156,6 +157,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
pub fn uu_app() -> Command {
|
||||
Command::new(uucore::util_name())
|
||||
.version(uucore::crate_version!())
|
||||
.help_template(uucore::localized_help_template(uucore::util_name()))
|
||||
.about(translate!("chcon-about"))
|
||||
.override_usage(format_usage(&translate!("chcon-usage")))
|
||||
.infer_long_args(true)
|
||||
@@ -303,7 +305,7 @@ struct Options {
|
||||
}
|
||||
|
||||
fn parse_command_line(config: Command, args: impl uucore::Args) -> Result<Options> {
|
||||
let matches = config.try_get_matches_from(args)?;
|
||||
let matches = config.get_matches_from_localized(args);
|
||||
|
||||
let verbose = matches.get_flag(options::VERBOSE);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// spell-checker:ignore (ToDO) COMFOLLOW Chowner RFILE RFILE's derefer dgid nonblank nonprint nonprinting
|
||||
|
||||
use uucore::display::Quotable;
|
||||
pub use uucore::entries;
|
||||
use uucore::entries;
|
||||
use uucore::error::{FromIo, UResult, USimpleError};
|
||||
use uucore::format_usage;
|
||||
use uucore::perms::{GidUidOwnerFilter, IfFrom, chown_base, options};
|
||||
@@ -37,15 +37,16 @@ fn parse_gid_from_str(group: &str) -> Result<u32, String> {
|
||||
|
||||
fn get_dest_gid(matches: &ArgMatches) -> UResult<(Option<u32>, String)> {
|
||||
let mut raw_group = String::new();
|
||||
let dest_gid = if let Some(file) = matches.get_one::<String>(options::REFERENCE) {
|
||||
fs::metadata(file)
|
||||
let dest_gid = if let Some(file) = matches.get_one::<std::ffi::OsString>(options::REFERENCE) {
|
||||
let path = std::path::Path::new(file);
|
||||
fs::metadata(path)
|
||||
.map(|meta| {
|
||||
let gid = meta.gid();
|
||||
raw_group = entries::gid2grp(gid).unwrap_or_else(|_| gid.to_string());
|
||||
Some(gid)
|
||||
})
|
||||
.map_err_context(
|
||||
|| translate!("chgrp-error-failed-to-get-attributes", "file" => file.quote()),
|
||||
|| translate!("chgrp-error-failed-to-get-attributes", "file" => path.quote()),
|
||||
)?
|
||||
} else {
|
||||
let group = matches
|
||||
@@ -99,6 +100,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
pub fn uu_app() -> Command {
|
||||
Command::new(uucore::util_name())
|
||||
.version(uucore::crate_version!())
|
||||
.help_template(uucore::localized_help_template(uucore::util_name()))
|
||||
.about(translate!("chgrp-about"))
|
||||
.override_usage(format_usage(&translate!("chgrp-usage")))
|
||||
.infer_long_args(true)
|
||||
@@ -152,6 +154,7 @@ pub fn uu_app() -> Command {
|
||||
.long(options::REFERENCE)
|
||||
.value_name("RFILE")
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.value_parser(clap::value_parser!(std::ffi::OsString))
|
||||
.help(translate!("chgrp-help-reference")),
|
||||
)
|
||||
.arg(
|
||||
|
||||
+22
-16
@@ -11,6 +11,7 @@ use std::fs;
|
||||
use std::os::unix::fs::{MetadataExt, PermissionsExt};
|
||||
use std::path::Path;
|
||||
use thiserror::Error;
|
||||
use uucore::LocalizedCommand;
|
||||
use uucore::display::Quotable;
|
||||
use uucore::error::{ExitCode, UError, UResult, USimpleError, UUsageError, set_exit_code};
|
||||
use uucore::fs::display_permissions_unix;
|
||||
@@ -112,17 +113,17 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let (parsed_cmode, args) = extract_negative_modes(args.skip(1)); // skip binary name
|
||||
let matches = uu_app()
|
||||
.after_help(translate!("chmod-after-help"))
|
||||
.try_get_matches_from(args)?;
|
||||
.get_matches_from_localized(args);
|
||||
|
||||
let changes = matches.get_flag(options::CHANGES);
|
||||
let quiet = matches.get_flag(options::QUIET);
|
||||
let verbose = matches.get_flag(options::VERBOSE);
|
||||
let preserve_root = matches.get_flag(options::PRESERVE_ROOT);
|
||||
let fmode = match matches.get_one::<String>(options::REFERENCE) {
|
||||
let fmode = match matches.get_one::<OsString>(options::REFERENCE) {
|
||||
Some(fref) => match fs::metadata(fref) {
|
||||
Ok(meta) => Some(meta.mode() & 0o7777),
|
||||
Err(_) => {
|
||||
return Err(ChmodError::CannotStat(fref.to_string()).into());
|
||||
return Err(ChmodError::CannotStat(fref.to_string_lossy().to_string()).into());
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
@@ -134,16 +135,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
} else {
|
||||
modes.unwrap().to_string() // modes is required
|
||||
};
|
||||
// FIXME: enable non-utf8 paths
|
||||
let mut files: Vec<String> = matches
|
||||
.get_many::<String>(options::FILE)
|
||||
.map(|v| v.map(ToString::to_string).collect())
|
||||
let mut files: Vec<OsString> = matches
|
||||
.get_many::<OsString>(options::FILE)
|
||||
.map(|v| v.cloned().collect())
|
||||
.unwrap_or_default();
|
||||
let cmode = if fmode.is_some() {
|
||||
// "--reference" and MODE are mutually exclusive
|
||||
// if "--reference" was used MODE needs to be interpreted as another FILE
|
||||
// it wasn't possible to implement this behavior directly with clap
|
||||
files.push(cmode);
|
||||
files.push(OsString::from(cmode));
|
||||
None
|
||||
} else {
|
||||
Some(cmode)
|
||||
@@ -177,6 +177,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
pub fn uu_app() -> Command {
|
||||
Command::new(uucore::util_name())
|
||||
.version(uucore::crate_version!())
|
||||
.help_template(uucore::localized_help_template(uucore::util_name()))
|
||||
.about(translate!("chmod-about"))
|
||||
.override_usage(format_usage(&translate!("chmod-usage")))
|
||||
.args_override_self(true)
|
||||
@@ -234,6 +235,7 @@ pub fn uu_app() -> Command {
|
||||
Arg::new(options::REFERENCE)
|
||||
.long("reference")
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.value_parser(clap::value_parser!(OsString))
|
||||
.help(translate!("chmod-help-reference")),
|
||||
)
|
||||
.arg(
|
||||
@@ -246,7 +248,8 @@ pub fn uu_app() -> Command {
|
||||
Arg::new(options::FILE)
|
||||
.required_unless_present(options::MODE)
|
||||
.action(ArgAction::Append)
|
||||
.value_hint(clap::ValueHint::AnyPath),
|
||||
.value_hint(clap::ValueHint::AnyPath)
|
||||
.value_parser(clap::value_parser!(OsString)),
|
||||
)
|
||||
// Add common arguments with chgrp, chown & chmod
|
||||
.args(uucore::perms::common_args())
|
||||
@@ -265,11 +268,10 @@ struct Chmoder {
|
||||
}
|
||||
|
||||
impl Chmoder {
|
||||
fn chmod(&self, files: &[String]) -> UResult<()> {
|
||||
fn chmod(&self, files: &[OsString]) -> UResult<()> {
|
||||
let mut r = Ok(());
|
||||
|
||||
for filename in files {
|
||||
let filename = &filename[..];
|
||||
let file = Path::new(filename);
|
||||
if !file.exists() {
|
||||
if file.is_symlink() {
|
||||
@@ -283,18 +285,22 @@ impl Chmoder {
|
||||
}
|
||||
|
||||
if !self.quiet {
|
||||
show!(ChmodError::DanglingSymlink(filename.to_string()));
|
||||
show!(ChmodError::DanglingSymlink(
|
||||
filename.to_string_lossy().to_string()
|
||||
));
|
||||
set_exit_code(1);
|
||||
}
|
||||
|
||||
if self.verbose {
|
||||
println!(
|
||||
"{}",
|
||||
translate!("chmod-verbose-failed-dangling", "file" => filename.quote())
|
||||
translate!("chmod-verbose-failed-dangling", "file" => filename.to_string_lossy().quote())
|
||||
);
|
||||
}
|
||||
} else if !self.quiet {
|
||||
show!(ChmodError::NoSuchFile(filename.to_string()));
|
||||
show!(ChmodError::NoSuchFile(
|
||||
filename.to_string_lossy().to_string()
|
||||
));
|
||||
}
|
||||
// GNU exits with exit code 1 even if -q or --quiet are passed
|
||||
// So we set the exit code, because it hasn't been set yet if `self.quiet` is true.
|
||||
@@ -306,8 +312,8 @@ impl Chmoder {
|
||||
// should not change the permissions in this case
|
||||
continue;
|
||||
}
|
||||
if self.recursive && self.preserve_root && filename == "/" {
|
||||
return Err(ChmodError::PreserveRoot(filename.to_string()).into());
|
||||
if self.recursive && self.preserve_root && file == Path::new("/") {
|
||||
return Err(ChmodError::PreserveRoot("/".to_string()).into());
|
||||
}
|
||||
if self.recursive {
|
||||
r = self.walk_dir_with_context(file, true);
|
||||
|
||||
@@ -77,6 +77,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
pub fn uu_app() -> Command {
|
||||
Command::new(uucore::util_name())
|
||||
.version(uucore::crate_version!())
|
||||
.help_template(uucore::localized_help_template(uucore::util_name()))
|
||||
.about(translate!("chown-about"))
|
||||
.override_usage(format_usage(&translate!("chown-usage")))
|
||||
.infer_long_args(true)
|
||||
|
||||
@@ -236,6 +236,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
pub fn uu_app() -> Command {
|
||||
Command::new(uucore::util_name())
|
||||
.version(uucore::crate_version!())
|
||||
.help_template(uucore::localized_help_template(uucore::util_name()))
|
||||
.about(translate!("chroot-about"))
|
||||
.override_usage(format_usage(&translate!("chroot-usage")))
|
||||
.infer_long_args(true)
|
||||
|
||||
@@ -20,6 +20,7 @@ use uucore::checksum::{
|
||||
};
|
||||
use uucore::translate;
|
||||
|
||||
use uucore::LocalizedCommand;
|
||||
use uucore::{
|
||||
encoding,
|
||||
error::{FromIo, UResult, USimpleError},
|
||||
@@ -236,7 +237,7 @@ fn handle_tag_text_binary_flags<S: AsRef<OsStr>>(
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let matches = uu_app().try_get_matches_from(args)?;
|
||||
let matches = uu_app().get_matches_from_localized(args);
|
||||
|
||||
let check = matches.get_flag(options::CHECK);
|
||||
|
||||
@@ -343,6 +344,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
pub fn uu_app() -> Command {
|
||||
Command::new(uucore::util_name())
|
||||
.version(uucore::crate_version!())
|
||||
.help_template(uucore::localized_help_template(uucore::util_name()))
|
||||
.about(translate!("cksum-about"))
|
||||
.override_usage(format_usage(&translate!("cksum-usage")))
|
||||
.infer_long_args(true)
|
||||
|
||||
+21
-13
@@ -6,8 +6,11 @@
|
||||
// spell-checker:ignore (ToDO) delim mkdelim pairable
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::ffi::OsString;
|
||||
use std::fs::{File, metadata};
|
||||
use std::io::{self, BufRead, BufReader, Read, Stdin, stdin};
|
||||
use std::path::Path;
|
||||
use uucore::LocalizedCommand;
|
||||
use uucore::error::{FromIo, UResult, USimpleError};
|
||||
use uucore::format_usage;
|
||||
use uucore::fs::paths_refer_to_same_file;
|
||||
@@ -114,7 +117,7 @@ impl OrderChecker {
|
||||
}
|
||||
|
||||
// Check if two files are identical by comparing their contents
|
||||
pub fn are_files_identical(path1: &str, path2: &str) -> io::Result<bool> {
|
||||
pub fn are_files_identical(path1: &Path, path2: &Path) -> io::Result<bool> {
|
||||
// First compare file sizes
|
||||
let metadata1 = metadata(path1)?;
|
||||
let metadata2 = metadata(path2)?;
|
||||
@@ -173,11 +176,11 @@ fn comm(a: &mut LineReader, b: &mut LineReader, delim: &str, opts: &ArgMatches)
|
||||
let should_check_order = !no_check_order
|
||||
&& (check_order
|
||||
|| if let (Some(file1), Some(file2)) = (
|
||||
opts.get_one::<String>(options::FILE_1),
|
||||
opts.get_one::<String>(options::FILE_2),
|
||||
opts.get_one::<OsString>(options::FILE_1),
|
||||
opts.get_one::<OsString>(options::FILE_2),
|
||||
) {
|
||||
!(paths_refer_to_same_file(file1, file2, true)
|
||||
|| are_files_identical(file1, file2).unwrap_or(false))
|
||||
!(paths_refer_to_same_file(file1.as_os_str(), file2.as_os_str(), true)
|
||||
|| are_files_identical(Path::new(file1), Path::new(file2)).unwrap_or(false))
|
||||
} else {
|
||||
true
|
||||
});
|
||||
@@ -263,7 +266,7 @@ fn comm(a: &mut LineReader, b: &mut LineReader, delim: &str, opts: &ArgMatches)
|
||||
}
|
||||
}
|
||||
|
||||
fn open_file(name: &str, line_ending: LineEnding) -> io::Result<LineReader> {
|
||||
fn open_file(name: &OsString, line_ending: LineEnding) -> io::Result<LineReader> {
|
||||
if name == "-" {
|
||||
Ok(LineReader::new(Input::Stdin(stdin()), line_ending))
|
||||
} else {
|
||||
@@ -280,12 +283,14 @@ fn open_file(name: &str, line_ending: LineEnding) -> io::Result<LineReader> {
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let matches = uu_app().try_get_matches_from(args)?;
|
||||
let matches = uu_app().get_matches_from_localized(args);
|
||||
let line_ending = LineEnding::from_zero_flag(matches.get_flag(options::ZERO_TERMINATED));
|
||||
let filename1 = matches.get_one::<String>(options::FILE_1).unwrap();
|
||||
let filename2 = matches.get_one::<String>(options::FILE_2).unwrap();
|
||||
let mut f1 = open_file(filename1, line_ending).map_err_context(|| filename1.to_string())?;
|
||||
let mut f2 = open_file(filename2, line_ending).map_err_context(|| filename2.to_string())?;
|
||||
let filename1 = matches.get_one::<OsString>(options::FILE_1).unwrap();
|
||||
let filename2 = matches.get_one::<OsString>(options::FILE_2).unwrap();
|
||||
let mut f1 = open_file(filename1, line_ending)
|
||||
.map_err_context(|| filename1.to_string_lossy().to_string())?;
|
||||
let mut f2 = open_file(filename2, line_ending)
|
||||
.map_err_context(|| filename2.to_string_lossy().to_string())?;
|
||||
|
||||
// Due to default_value(), there must be at least one value here, thus unwrap() must not panic.
|
||||
let all_delimiters = matches
|
||||
@@ -315,6 +320,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
pub fn uu_app() -> Command {
|
||||
Command::new(uucore::util_name())
|
||||
.version(uucore::crate_version!())
|
||||
.help_template(uucore::localized_help_template(uucore::util_name()))
|
||||
.about(translate!("comm-about"))
|
||||
.override_usage(format_usage(&translate!("comm-usage")))
|
||||
.infer_long_args(true)
|
||||
@@ -358,12 +364,14 @@ pub fn uu_app() -> Command {
|
||||
.arg(
|
||||
Arg::new(options::FILE_1)
|
||||
.required(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.value_parser(clap::value_parser!(OsString)),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::FILE_2)
|
||||
.required(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.value_parser(clap::value_parser!(OsString)),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::TOTAL)
|
||||
|
||||
+3
-1
@@ -15,6 +15,7 @@ use std::os::unix::fs::{FileTypeExt, PermissionsExt};
|
||||
use std::os::unix::net::UnixListener;
|
||||
use std::path::{Path, PathBuf, StripPrefixError};
|
||||
use std::{fmt, io};
|
||||
use uucore::LocalizedCommand;
|
||||
#[cfg(all(unix, not(target_os = "android")))]
|
||||
use uucore::fsxattr::copy_xattrs;
|
||||
use uucore::translate;
|
||||
@@ -522,6 +523,7 @@ pub fn uu_app() -> Command {
|
||||
Command::new(uucore::util_name())
|
||||
.version(uucore::crate_version!())
|
||||
.about(translate!("cp-about"))
|
||||
.help_template(uucore::localized_help_template(uucore::util_name()))
|
||||
.override_usage(format_usage(&translate!("cp-usage")))
|
||||
.after_help(format!(
|
||||
"{}\n\n{}",
|
||||
@@ -780,7 +782,7 @@ pub fn uu_app() -> Command {
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let matches = uu_app().try_get_matches_from(args)?;
|
||||
let matches = uu_app().get_matches_from_localized(args);
|
||||
|
||||
let options = Options::from_matches(&matches)?;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#![allow(rustdoc::private_intra_doc_links)]
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::ffi::OsString;
|
||||
use std::io::{self, BufReader, ErrorKind};
|
||||
use std::{
|
||||
fs::{File, remove_file},
|
||||
@@ -25,6 +26,7 @@ mod split_name;
|
||||
use crate::csplit_error::CsplitError;
|
||||
use crate::split_name::SplitName;
|
||||
|
||||
use uucore::LocalizedCommand;
|
||||
use uucore::translate;
|
||||
|
||||
mod options {
|
||||
@@ -604,10 +606,10 @@ where
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let matches = uu_app().try_get_matches_from(args)?;
|
||||
let matches = uu_app().get_matches_from_localized(args);
|
||||
|
||||
// get the file to split
|
||||
let file_name = matches.get_one::<String>(options::FILE).unwrap();
|
||||
let file_name = matches.get_one::<OsString>(options::FILE).unwrap();
|
||||
|
||||
// get the patterns to split on
|
||||
let patterns: Vec<String> = matches
|
||||
@@ -629,6 +631,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
pub fn uu_app() -> Command {
|
||||
Command::new(uucore::util_name())
|
||||
.version(uucore::crate_version!())
|
||||
.help_template(uucore::localized_help_template(uucore::util_name()))
|
||||
.about(translate!("csplit-about"))
|
||||
.override_usage(format_usage(&translate!("csplit-usage")))
|
||||
.args_override_self(true)
|
||||
@@ -687,7 +690,8 @@ pub fn uu_app() -> Command {
|
||||
Arg::new(options::FILE)
|
||||
.hide(true)
|
||||
.required(true)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.value_parser(clap::value_parser!(OsString)),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::PATTERN)
|
||||
|
||||
+12
-9
@@ -18,6 +18,7 @@ use uucore::os_str_as_bytes;
|
||||
|
||||
use self::searcher::Searcher;
|
||||
use matcher::{ExactMatcher, Matcher, WhitespaceMatcher};
|
||||
use uucore::LocalizedCommand;
|
||||
use uucore::ranges::Range;
|
||||
use uucore::translate;
|
||||
use uucore::{format_usage, show_error, show_if_err};
|
||||
@@ -342,11 +343,11 @@ fn cut_fields<R: Read, W: Write>(
|
||||
}
|
||||
}
|
||||
|
||||
fn cut_files(mut filenames: Vec<String>, mode: &Mode) {
|
||||
fn cut_files(mut filenames: Vec<OsString>, mode: &Mode) {
|
||||
let mut stdin_read = false;
|
||||
|
||||
if filenames.is_empty() {
|
||||
filenames.push("-".to_owned());
|
||||
filenames.push(OsString::from("-"));
|
||||
}
|
||||
|
||||
let mut out: Box<dyn Write> = if stdout().is_terminal() {
|
||||
@@ -369,12 +370,12 @@ fn cut_files(mut filenames: Vec<String>, mode: &Mode) {
|
||||
|
||||
stdin_read = true;
|
||||
} else {
|
||||
let path = Path::new(&filename[..]);
|
||||
let path = Path::new(filename);
|
||||
|
||||
if path.is_dir() {
|
||||
show_error!(
|
||||
"{}: {}",
|
||||
filename.maybe_quote(),
|
||||
filename.to_string_lossy().maybe_quote(),
|
||||
translate!("cut-error-is-directory")
|
||||
);
|
||||
set_exit_code(1);
|
||||
@@ -383,7 +384,7 @@ fn cut_files(mut filenames: Vec<String>, mode: &Mode) {
|
||||
|
||||
show_if_err!(
|
||||
File::open(path)
|
||||
.map_err_context(|| filename.maybe_quote().to_string())
|
||||
.map_err_context(|| filename.to_string_lossy().to_string())
|
||||
.and_then(|file| {
|
||||
match &mode {
|
||||
Mode::Bytes(ranges, opts) | Mode::Characters(ranges, opts) => {
|
||||
@@ -482,7 +483,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let matches = uu_app().try_get_matches_from(args)?;
|
||||
let matches = uu_app().get_matches_from_localized(args);
|
||||
|
||||
let complement = matches.get_flag(options::COMPLEMENT);
|
||||
let only_delimited = matches.get_flag(options::ONLY_DELIMITED);
|
||||
@@ -576,8 +577,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
},
|
||||
};
|
||||
|
||||
let files: Vec<String> = matches
|
||||
.get_many::<String>(options::FILE)
|
||||
let files: Vec<OsString> = matches
|
||||
.get_many::<OsString>(options::FILE)
|
||||
.unwrap_or_default()
|
||||
.cloned()
|
||||
.collect();
|
||||
@@ -594,6 +595,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
pub fn uu_app() -> Command {
|
||||
Command::new(uucore::util_name())
|
||||
.version(uucore::crate_version!())
|
||||
.help_template(uucore::localized_help_template(uucore::util_name()))
|
||||
.override_usage(format_usage(&translate!("cut-usage")))
|
||||
.about(translate!("cut-about"))
|
||||
.after_help(translate!("cut-after-help"))
|
||||
@@ -679,6 +681,7 @@ pub fn uu_app() -> Command {
|
||||
Arg::new(options::FILE)
|
||||
.hide(true)
|
||||
.action(ArgAction::Append)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.value_parser(clap::value_parser!(OsString)),
|
||||
)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user