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:
commit
3f27597fca
2 changed files with 43 additions and 47 deletions
|
@ -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
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue