Merge branch 'main' into fix-cp-socket-file-unix

This commit is contained in:
Alen Antony
2025-08-14 18:39:45 +05:30
committed by GitHub
164 changed files with 3657 additions and 1000 deletions
+1
View File
@@ -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
+170
View File
@@ -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
+2 -1
View File
@@ -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,
+8
View File
@@ -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"; \
+6
View File
@@ -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
+442
View File
@@ -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);
});
+3 -1
View File
@@ -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)
+9 -5
View File
@@ -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),
)
}
+3 -1
View File
@@ -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
View File
@@ -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);
}
+3 -1
View File
@@ -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);
+7 -4
View File
@@ -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
View File
@@ -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);
+1
View File
@@ -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)
+1
View File
@@ -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)
+3 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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)?;
+7 -3
View File
@@ -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
View File
@@ -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