1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-29 12:07:46 +00:00

Merge pull request #2823 from jfinkels/split-uresult

split: return UResult from uumain() function
This commit is contained in:
Sylvestre Ledru 2022-01-07 21:49:11 +01:00 committed by GitHub
commit 3f27597fca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 43 additions and 47 deletions

View file

@ -2,6 +2,8 @@ use std::env;
use std::io::Write; use std::io::Write;
use std::io::{BufWriter, Result}; use std::io::{BufWriter, Result};
use std::process::{Child, Command, Stdio}; use std::process::{Child, Command, Stdio};
use uucore::crash;
/// A writer that writes to a shell_process' stdin /// A writer that writes to a shell_process' stdin
/// ///
/// We use a shell process (not directly calling a sub-process) so we can forward the name of the /// We use a shell process (not directly calling a sub-process) so we can forward the name of the

View file

@ -7,9 +7,6 @@
// spell-checker:ignore (ToDO) PREFIXaa // spell-checker:ignore (ToDO) PREFIXaa
#[macro_use]
extern crate uucore;
mod platform; mod platform;
use clap::{crate_version, App, Arg, ArgMatches}; use clap::{crate_version, App, Arg, ArgMatches};
@ -20,6 +17,7 @@ use std::io::{stdin, BufRead, BufReader, BufWriter, Read, Write};
use std::path::Path; use std::path::Path;
use std::{char, fs::remove_file}; use std::{char, fs::remove_file};
use uucore::display::Quotable; use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
use uucore::parse_size::parse_size; use uucore::parse_size::parse_size;
static OPT_BYTES: &str = "bytes"; static OPT_BYTES: &str = "bytes";
@ -53,7 +51,8 @@ size is 1000, and default PREFIX is 'x'. With no INPUT, or when INPUT is
) )
} }
pub fn uumain(args: impl uucore::Args) -> i32 { #[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = usage(); let usage = usage();
let long_usage = get_long_usage(); let long_usage = get_long_usage();
@ -83,15 +82,17 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
settings.additional_suffix = matches.value_of(OPT_ADDITIONAL_SUFFIX).unwrap().to_owned(); settings.additional_suffix = matches.value_of(OPT_ADDITIONAL_SUFFIX).unwrap().to_owned();
settings.verbose = matches.occurrences_of("verbose") > 0; settings.verbose = matches.occurrences_of("verbose") > 0;
settings.strategy = Strategy::from(&matches); settings.strategy = Strategy::from(&matches)?;
settings.input = matches.value_of(ARG_INPUT).unwrap().to_owned(); settings.input = matches.value_of(ARG_INPUT).unwrap().to_owned();
settings.prefix = matches.value_of(ARG_PREFIX).unwrap().to_owned(); settings.prefix = matches.value_of(ARG_PREFIX).unwrap().to_owned();
if matches.occurrences_of(OPT_FILTER) > 0 { if matches.occurrences_of(OPT_FILTER) > 0 {
if cfg!(windows) { if cfg!(windows) {
// see https://github.com/rust-lang/rust/issues/29494 // see https://github.com/rust-lang/rust/issues/29494
show_error!("{} is currently not supported in this platform", OPT_FILTER); return Err(USimpleError::new(
std::process::exit(-1); -1,
format!("{} is currently not supported in this platform", OPT_FILTER),
));
} else { } else {
settings.filter = Some(matches.value_of(OPT_FILTER).unwrap().to_owned()); settings.filter = Some(matches.value_of(OPT_FILTER).unwrap().to_owned());
} }
@ -193,7 +194,7 @@ enum Strategy {
impl Strategy { impl Strategy {
/// Parse a strategy from the command-line arguments. /// Parse a strategy from the command-line arguments.
fn from(matches: &ArgMatches) -> Self { fn from(matches: &ArgMatches) -> UResult<Self> {
// Check that the user is not specifying more than one strategy. // Check that the user is not specifying more than one strategy.
// //
// Note: right now, this exact behavior cannot be handled by // Note: right now, this exact behavior cannot be handled by
@ -204,26 +205,26 @@ impl Strategy {
matches.occurrences_of(OPT_BYTES), matches.occurrences_of(OPT_BYTES),
matches.occurrences_of(OPT_LINE_BYTES), matches.occurrences_of(OPT_LINE_BYTES),
) { ) {
(0, 0, 0) => Strategy::Lines(1000), (0, 0, 0) => Ok(Strategy::Lines(1000)),
(1, 0, 0) => { (1, 0, 0) => {
let s = matches.value_of(OPT_LINES).unwrap(); let s = matches.value_of(OPT_LINES).unwrap();
let n = let n = parse_size(s)
parse_size(s).unwrap_or_else(|e| crash!(1, "invalid number of lines: {}", e)); .map_err(|e| USimpleError::new(1, format!("invalid number of lines: {}", e)))?;
Strategy::Lines(n) Ok(Strategy::Lines(n))
} }
(0, 1, 0) => { (0, 1, 0) => {
let s = matches.value_of(OPT_BYTES).unwrap(); let s = matches.value_of(OPT_BYTES).unwrap();
let n = let n = parse_size(s)
parse_size(s).unwrap_or_else(|e| crash!(1, "invalid number of bytes: {}", e)); .map_err(|e| USimpleError::new(1, format!("invalid number of bytes: {}", e)))?;
Strategy::Bytes(n) Ok(Strategy::Bytes(n))
} }
(0, 0, 1) => { (0, 0, 1) => {
let s = matches.value_of(OPT_LINE_BYTES).unwrap(); let s = matches.value_of(OPT_LINE_BYTES).unwrap();
let n = let n = parse_size(s)
parse_size(s).unwrap_or_else(|e| crash!(1, "invalid number of bytes: {}", e)); .map_err(|e| USimpleError::new(1, format!("invalid number of bytes: {}", e)))?;
Strategy::LineBytes(n) Ok(Strategy::LineBytes(n))
} }
_ => crash!(1, "cannot split in more than one way"), _ => Err(UUsageError::new(1, "cannot split in more than one way")),
} }
} }
} }
@ -249,7 +250,7 @@ trait Splitter {
&mut self, &mut self,
reader: &mut BufReader<Box<dyn Read>>, reader: &mut BufReader<Box<dyn Read>>,
writer: &mut BufWriter<Box<dyn Write>>, writer: &mut BufWriter<Box<dyn Write>>,
) -> u128; ) -> std::io::Result<u128>;
} }
struct LineSplitter { struct LineSplitter {
@ -269,21 +270,17 @@ impl Splitter for LineSplitter {
&mut self, &mut self,
reader: &mut BufReader<Box<dyn Read>>, reader: &mut BufReader<Box<dyn Read>>,
writer: &mut BufWriter<Box<dyn Write>>, writer: &mut BufWriter<Box<dyn Write>>,
) -> u128 { ) -> std::io::Result<u128> {
let mut bytes_consumed = 0u128; let mut bytes_consumed = 0u128;
let mut buffer = String::with_capacity(1024); let mut buffer = String::with_capacity(1024);
for _ in 0..self.lines_per_split { for _ in 0..self.lines_per_split {
let bytes_read = reader let bytes_read = reader.read_line(&mut buffer)?;
.read_line(&mut buffer)
.unwrap_or_else(|_| crash!(1, "error reading bytes from input file"));
// If we ever read 0 bytes then we know we've hit EOF. // If we ever read 0 bytes then we know we've hit EOF.
if bytes_read == 0 { if bytes_read == 0 {
return bytes_consumed; return Ok(bytes_consumed);
} }
writer writer.write_all(buffer.as_bytes())?;
.write_all(buffer.as_bytes())
.unwrap_or_else(|_| crash!(1, "error writing bytes to output file"));
// Empty out the String buffer since `read_line` appends instead of // Empty out the String buffer since `read_line` appends instead of
// replaces. // replaces.
buffer.clear(); buffer.clear();
@ -291,7 +288,7 @@ impl Splitter for LineSplitter {
bytes_consumed += bytes_read as u128; bytes_consumed += bytes_read as u128;
} }
bytes_consumed Ok(bytes_consumed)
} }
} }
@ -312,7 +309,7 @@ impl Splitter for ByteSplitter {
&mut self, &mut self,
reader: &mut BufReader<Box<dyn Read>>, reader: &mut BufReader<Box<dyn Read>>,
writer: &mut BufWriter<Box<dyn Write>>, writer: &mut BufWriter<Box<dyn Write>>,
) -> u128 { ) -> std::io::Result<u128> {
// We buffer reads and writes. We proceed until `bytes_consumed` is // We buffer reads and writes. We proceed until `bytes_consumed` is
// equal to `self.bytes_per_split` or we reach EOF. // equal to `self.bytes_per_split` or we reach EOF.
let mut bytes_consumed = 0u128; let mut bytes_consumed = 0u128;
@ -329,22 +326,18 @@ impl Splitter for ByteSplitter {
// than BUFFER_SIZE in this branch. // than BUFFER_SIZE in this branch.
(self.bytes_per_split - bytes_consumed) as usize (self.bytes_per_split - bytes_consumed) as usize
}; };
let bytes_read = reader let bytes_read = reader.read(&mut buffer[0..bytes_desired])?;
.read(&mut buffer[0..bytes_desired])
.unwrap_or_else(|_| crash!(1, "error reading bytes from input file"));
// If we ever read 0 bytes then we know we've hit EOF. // If we ever read 0 bytes then we know we've hit EOF.
if bytes_read == 0 { if bytes_read == 0 {
return bytes_consumed; return Ok(bytes_consumed);
} }
writer writer.write_all(&buffer[0..bytes_read])?;
.write_all(&buffer[0..bytes_read])
.unwrap_or_else(|_| crash!(1, "error writing bytes to output file"));
bytes_consumed += bytes_read as u128; bytes_consumed += bytes_read as u128;
} }
bytes_consumed Ok(bytes_consumed)
} }
} }
@ -380,17 +373,16 @@ fn num_prefix(i: usize, width: usize) -> String {
c c
} }
fn split(settings: &Settings) -> i32 { fn split(settings: &Settings) -> UResult<()> {
let mut reader = BufReader::new(if settings.input == "-" { let mut reader = BufReader::new(if settings.input == "-" {
Box::new(stdin()) as Box<dyn Read> Box::new(stdin()) as Box<dyn Read>
} else { } else {
let r = File::open(Path::new(&settings.input)).unwrap_or_else(|_| { let r = File::open(Path::new(&settings.input)).map_err_context(|| {
crash!( format!(
1,
"cannot open {} for reading: No such file or directory", "cannot open {} for reading: No such file or directory",
settings.input.quote() settings.input.quote()
) )
}); })?;
Box::new(r) as Box<dyn Read> Box::new(r) as Box<dyn Read>
}); });
@ -416,10 +408,12 @@ fn split(settings: &Settings) -> i32 {
filename.push_str(settings.additional_suffix.as_ref()); filename.push_str(settings.additional_suffix.as_ref());
let mut writer = platform::instantiate_current_writer(&settings.filter, filename.as_str()); let mut writer = platform::instantiate_current_writer(&settings.filter, filename.as_str());
let bytes_consumed = splitter.consume(&mut reader, &mut writer); let bytes_consumed = splitter
.consume(&mut reader, &mut writer)
.map_err_context(|| "input/output error".to_string())?;
writer writer
.flush() .flush()
.unwrap_or_else(|e| crash!(1, "error flushing to output file: {}", e)); .map_err_context(|| "error flushing to output file".to_string())?;
// If we didn't write anything we should clean up the empty file, and // If we didn't write anything we should clean up the empty file, and
// break from the loop. // break from the loop.
@ -428,12 +422,12 @@ fn split(settings: &Settings) -> i32 {
// Complicated, I know... // Complicated, I know...
if settings.filter.is_none() { if settings.filter.is_none() {
remove_file(filename) remove_file(filename)
.unwrap_or_else(|e| crash!(1, "error removing empty file: {}", e)); .map_err_context(|| "error removing empty file".to_string())?;
} }
break; break;
} }
fileno += 1; fileno += 1;
} }
0 Ok(())
} }