1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

wc: Accept badly-encoded filenames

This commit is contained in:
Jan Verbeek 2021-08-25 14:09:24 +02:00 committed by Michael Debertol
parent 6f7d740592
commit 35793fc260
2 changed files with 19 additions and 19 deletions

View file

@ -23,7 +23,7 @@ use thiserror::Error;
use std::cmp::max; use std::cmp::max;
use std::fs::{self, File}; use std::fs::{self, File};
use std::io::{self, ErrorKind, Write}; use std::io::{self, ErrorKind, Write};
use std::path::Path; use std::path::{Path, PathBuf};
/// The minimum character width for formatting counts when reading from stdin. /// The minimum character width for formatting counts when reading from stdin.
const MINIMUM_WIDTH: usize = 7; const MINIMUM_WIDTH: usize = 7;
@ -33,7 +33,7 @@ pub enum WcError {
#[error("{0}")] #[error("{0}")]
Io(#[from] io::Error), Io(#[from] io::Error),
#[error("Expected a file, found directory {0}")] #[error("Expected a file, found directory {0}")]
IsDirectory(String), IsDirectory(PathBuf),
} }
type WcResult<T> = Result<T, WcError>; type WcResult<T> = Result<T, WcError>;
@ -117,7 +117,7 @@ enum StdinKind {
/// Supported inputs. /// Supported inputs.
enum Input { enum Input {
/// A regular file. /// A regular file.
Path(String), Path(PathBuf),
/// Standard input. /// Standard input.
Stdin(StdinKind), Stdin(StdinKind),
@ -125,10 +125,10 @@ enum Input {
impl Input { impl Input {
/// Converts input to title that appears in stats. /// Converts input to title that appears in stats.
fn to_title(&self) -> Option<&str> { fn to_title(&self) -> Option<&Path> {
match self { match self {
Input::Path(path) => Some(path), Input::Path(path) => Some(path),
Input::Stdin(StdinKind::Explicit) => Some("-"), Input::Stdin(StdinKind::Explicit) => Some("-".as_ref()),
Input::Stdin(StdinKind::Implicit) => None, Input::Stdin(StdinKind::Implicit) => None,
} }
} }
@ -140,13 +140,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let matches = uu_app().usage(&usage[..]).get_matches_from(args); let matches = uu_app().usage(&usage[..]).get_matches_from(args);
let mut inputs: Vec<Input> = matches let mut inputs: Vec<Input> = matches
.values_of(ARG_FILES) .values_of_os(ARG_FILES)
.map(|v| { .map(|v| {
v.map(|i| { v.map(|i| {
if i == "-" { if i == "-" {
Input::Stdin(StdinKind::Explicit) Input::Stdin(StdinKind::Explicit)
} else { } else {
Input::Path(ToString::to_string(i)) Input::Path(i.into())
} }
}) })
.collect() .collect()
@ -206,7 +206,7 @@ pub fn uu_app() -> App<'static, 'static> {
fn word_count_from_reader<T: WordCountable>( fn word_count_from_reader<T: WordCountable>(
mut reader: T, mut reader: T,
settings: &Settings, settings: &Settings,
path: &str, path: &Path,
) -> WcResult<WordCount> { ) -> WcResult<WordCount> {
let only_count_bytes = settings.show_bytes let only_count_bytes = settings.show_bytes
&& (!(settings.show_chars && (!(settings.show_chars
@ -273,7 +273,7 @@ fn word_count_from_reader<T: WordCountable>(
total.bytes += bytes.len(); total.bytes += bytes.len();
} }
Err(BufReadDecoderError::Io(e)) => { Err(BufReadDecoderError::Io(e)) => {
show_warning!("Error while reading {}: {}", path, e); show_warning!("Error while reading {}: {}", path.display(), e);
} }
} }
} }
@ -288,11 +288,10 @@ fn word_count_from_input(input: &Input, settings: &Settings) -> WcResult<WordCou
Input::Stdin(_) => { Input::Stdin(_) => {
let stdin = io::stdin(); let stdin = io::stdin();
let stdin_lock = stdin.lock(); let stdin_lock = stdin.lock();
word_count_from_reader(stdin_lock, settings, "-") word_count_from_reader(stdin_lock, settings, "-".as_ref())
} }
Input::Path(path) => { Input::Path(path) => {
let path_obj = Path::new(path); if path.is_dir() {
if path_obj.is_dir() {
Err(WcError::IsDirectory(path.to_owned())) Err(WcError::IsDirectory(path.to_owned()))
} else { } else {
let file = File::open(path)?; let file = File::open(path)?;
@ -314,10 +313,10 @@ fn word_count_from_input(input: &Input, settings: &Settings) -> WcResult<WordCou
fn show_error(input: &Input, err: WcError) { fn show_error(input: &Input, err: WcError) {
match (input, err) { match (input, err) {
(_, WcError::IsDirectory(path)) => { (_, WcError::IsDirectory(path)) => {
show_error_custom_description!(path, "Is a directory"); show_error_custom_description!(path.display(), "Is a directory");
} }
(Input::Path(path), WcError::Io(e)) if e.kind() == ErrorKind::NotFound => { (Input::Path(path), WcError::Io(e)) if e.kind() == ErrorKind::NotFound => {
show_error_custom_description!(path, "No such file or directory"); show_error_custom_description!(path.display(), "No such file or directory");
} }
(_, e) => { (_, e) => {
show_error!("{}", e); show_error!("{}", e);
@ -428,7 +427,7 @@ fn wc(inputs: Vec<Input>, settings: &Settings) -> Result<(), u32> {
if let Err(err) = print_stats(settings, &result, max_width) { if let Err(err) = print_stats(settings, &result, max_width) {
show_warning!( show_warning!(
"failed to print result for {}: {}", "failed to print result for {}: {}",
result.title.unwrap_or("<stdin>"), result.title.unwrap_or_else(|| "<stdin>".as_ref()).display(),
err err
); );
error_count += 1; error_count += 1;
@ -436,7 +435,7 @@ fn wc(inputs: Vec<Input>, settings: &Settings) -> Result<(), u32> {
} }
if num_inputs > 1 { if num_inputs > 1 {
let total_result = total_word_count.with_title(Some("total")); let total_result = total_word_count.with_title(Some("total".as_ref()));
if let Err(err) = print_stats(settings, &total_result, max_width) { if let Err(err) = print_stats(settings, &total_result, max_width) {
show_warning!("failed to print total: {}", err); show_warning!("failed to print total: {}", err);
error_count += 1; error_count += 1;
@ -506,7 +505,7 @@ fn print_stats(
} }
if let Some(title) = result.title { if let Some(title) = result.title {
writeln!(stdout_lock, " {}", title)?; writeln!(stdout_lock, " {}", title.display())?;
} else { } else {
writeln!(stdout_lock)?; writeln!(stdout_lock)?;
} }

View file

@ -1,5 +1,6 @@
use std::cmp::max; use std::cmp::max;
use std::ops::{Add, AddAssign}; use std::ops::{Add, AddAssign};
use std::path::Path;
#[derive(Debug, Default, Copy, Clone)] #[derive(Debug, Default, Copy, Clone)]
pub struct WordCount { pub struct WordCount {
@ -31,7 +32,7 @@ impl AddAssign for WordCount {
} }
impl WordCount { impl WordCount {
pub fn with_title(self, title: Option<&str>) -> TitledWordCount { pub fn with_title(self, title: Option<&Path>) -> TitledWordCount {
TitledWordCount { title, count: self } TitledWordCount { title, count: self }
} }
} }
@ -42,6 +43,6 @@ impl WordCount {
/// it would result in unnecessary copying of `String`. /// it would result in unnecessary copying of `String`.
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
pub struct TitledWordCount<'a> { pub struct TitledWordCount<'a> {
pub title: Option<&'a str>, pub title: Option<&'a Path>,
pub count: WordCount, pub count: WordCount,
} }