mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 03:27:44 +00:00
Merge pull request #2798 from jfinkels/fmt-uresult
fmt: return UResult from uumain() function
This commit is contained in:
commit
a8457bfad6
2 changed files with 81 additions and 59 deletions
|
@ -16,19 +16,11 @@ use std::fs::File;
|
||||||
use std::io::{stdin, stdout, Write};
|
use std::io::{stdin, stdout, Write};
|
||||||
use std::io::{BufReader, BufWriter, Read};
|
use std::io::{BufReader, BufWriter, Read};
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
|
use uucore::error::{FromIo, UResult, USimpleError};
|
||||||
|
|
||||||
use self::linebreak::break_lines;
|
use self::linebreak::break_lines;
|
||||||
use self::parasplit::ParagraphStream;
|
use self::parasplit::ParagraphStream;
|
||||||
|
|
||||||
macro_rules! silent_unwrap(
|
|
||||||
($exp:expr) => (
|
|
||||||
match $exp {
|
|
||||||
Ok(_) => (),
|
|
||||||
Err(_) => ::std::process::exit(1),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
mod linebreak;
|
mod linebreak;
|
||||||
mod parasplit;
|
mod parasplit;
|
||||||
|
|
||||||
|
@ -74,8 +66,9 @@ pub struct FmtOptions {
|
||||||
tabwidth: usize,
|
tabwidth: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[uucore_procs::gen_uumain]
|
||||||
#[allow(clippy::cognitive_complexity)]
|
#[allow(clippy::cognitive_complexity)]
|
||||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
let usage = usage();
|
let usage = usage();
|
||||||
|
|
||||||
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
|
||||||
|
@ -133,15 +126,20 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
fmt_opts.width = match s.parse::<usize>() {
|
fmt_opts.width = match s.parse::<usize>() {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
crash!(1, "Invalid WIDTH specification: {}: {}", s.quote(), e);
|
return Err(USimpleError::new(
|
||||||
|
1,
|
||||||
|
format!("Invalid WIDTH specification: {}: {}", s.quote(), e),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if fmt_opts.width > MAX_WIDTH {
|
if fmt_opts.width > MAX_WIDTH {
|
||||||
crash!(
|
return Err(USimpleError::new(
|
||||||
1,
|
1,
|
||||||
"invalid width: '{}': Numerical result out of range",
|
format!(
|
||||||
fmt_opts.width
|
"invalid width: '{}': Numerical result out of range",
|
||||||
);
|
fmt_opts.width,
|
||||||
|
),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
fmt_opts.goal = cmp::min(fmt_opts.width * 94 / 100, fmt_opts.width - 3);
|
fmt_opts.goal = cmp::min(fmt_opts.width * 94 / 100, fmt_opts.width - 3);
|
||||||
};
|
};
|
||||||
|
@ -150,13 +148,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
fmt_opts.goal = match s.parse::<usize>() {
|
fmt_opts.goal = match s.parse::<usize>() {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
crash!(1, "Invalid GOAL specification: {}: {}", s.quote(), e);
|
return Err(USimpleError::new(
|
||||||
|
1,
|
||||||
|
format!("Invalid GOAL specification: {}: {}", s.quote(), e),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if !matches.is_present(OPT_WIDTH) {
|
if !matches.is_present(OPT_WIDTH) {
|
||||||
fmt_opts.width = cmp::max(fmt_opts.goal * 100 / 94, fmt_opts.goal + 3);
|
fmt_opts.width = cmp::max(fmt_opts.goal * 100 / 94, fmt_opts.goal + 3);
|
||||||
} else if fmt_opts.goal > fmt_opts.width {
|
} else if fmt_opts.goal > fmt_opts.width {
|
||||||
crash!(1, "GOAL cannot be greater than WIDTH.");
|
return Err(USimpleError::new(1, "GOAL cannot be greater than WIDTH."));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -164,7 +165,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
fmt_opts.tabwidth = match s.parse::<usize>() {
|
fmt_opts.tabwidth = match s.parse::<usize>() {
|
||||||
Ok(t) => t,
|
Ok(t) => t,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
crash!(1, "Invalid TABWIDTH specification: {}: {}", s.quote(), e);
|
return Err(USimpleError::new(
|
||||||
|
1,
|
||||||
|
format!("Invalid TABWIDTH specification: {}: {}", s.quote(), e),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -197,18 +201,25 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||||
for para_result in p_stream {
|
for para_result in p_stream {
|
||||||
match para_result {
|
match para_result {
|
||||||
Err(s) => {
|
Err(s) => {
|
||||||
silent_unwrap!(ostream.write_all(s.as_bytes()));
|
ostream
|
||||||
silent_unwrap!(ostream.write_all(b"\n"));
|
.write_all(s.as_bytes())
|
||||||
|
.map_err_context(|| "failed to write output".to_string())?;
|
||||||
|
ostream
|
||||||
|
.write_all(b"\n")
|
||||||
|
.map_err_context(|| "failed to write output".to_string())?;
|
||||||
}
|
}
|
||||||
Ok(para) => break_lines(¶, &fmt_opts, &mut ostream),
|
Ok(para) => break_lines(¶, &fmt_opts, &mut ostream)
|
||||||
|
.map_err_context(|| "failed to write output".to_string())?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// flush the output after each file
|
// flush the output after each file
|
||||||
silent_unwrap!(ostream.flush());
|
ostream
|
||||||
|
.flush()
|
||||||
|
.map_err_context(|| "failed to write output".to_string())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
0
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uu_app() -> App<'static, 'static> {
|
pub fn uu_app() -> App<'static, 'static> {
|
||||||
|
|
|
@ -40,7 +40,11 @@ impl<'a> BreakArgs<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn break_lines(para: &Paragraph, opts: &FmtOptions, ostream: &mut BufWriter<Stdout>) {
|
pub fn break_lines(
|
||||||
|
para: &Paragraph,
|
||||||
|
opts: &FmtOptions,
|
||||||
|
ostream: &mut BufWriter<Stdout>,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
// indent
|
// indent
|
||||||
let p_indent = ¶.indent_str[..];
|
let p_indent = ¶.indent_str[..];
|
||||||
let p_indent_len = para.indent_len;
|
let p_indent_len = para.indent_len;
|
||||||
|
@ -54,26 +58,25 @@ pub fn break_lines(para: &Paragraph, opts: &FmtOptions, ostream: &mut BufWriter<
|
||||||
let (w, w_len) = match p_words_words.next() {
|
let (w, w_len) = match p_words_words.next() {
|
||||||
Some(winfo) => (winfo.word, winfo.word_nchars),
|
Some(winfo) => (winfo.word, winfo.word_nchars),
|
||||||
None => {
|
None => {
|
||||||
silent_unwrap!(ostream.write_all(b"\n"));
|
return ostream.write_all(b"\n");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// print the init, if it exists, and get its length
|
// print the init, if it exists, and get its length
|
||||||
let p_init_len = w_len
|
let p_init_len = w_len
|
||||||
+ if opts.crown || opts.tagged {
|
+ if opts.crown || opts.tagged {
|
||||||
// handle "init" portion
|
// handle "init" portion
|
||||||
silent_unwrap!(ostream.write_all(para.init_str.as_bytes()));
|
ostream.write_all(para.init_str.as_bytes())?;
|
||||||
para.init_len
|
para.init_len
|
||||||
} else if !para.mail_header {
|
} else if !para.mail_header {
|
||||||
// for non-(crown, tagged) that's the same as a normal indent
|
// for non-(crown, tagged) that's the same as a normal indent
|
||||||
silent_unwrap!(ostream.write_all(p_indent.as_bytes()));
|
ostream.write_all(p_indent.as_bytes())?;
|
||||||
p_indent_len
|
p_indent_len
|
||||||
} else {
|
} else {
|
||||||
// except that mail headers get no indent at all
|
// except that mail headers get no indent at all
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
// write first word after writing init
|
// write first word after writing init
|
||||||
silent_unwrap!(ostream.write_all(w.as_bytes()));
|
ostream.write_all(w.as_bytes())?;
|
||||||
|
|
||||||
// does this paragraph require uniform spacing?
|
// does this paragraph require uniform spacing?
|
||||||
let uniform = para.mail_header || opts.uniform;
|
let uniform = para.mail_header || opts.uniform;
|
||||||
|
@ -88,26 +91,29 @@ pub fn break_lines(para: &Paragraph, opts: &FmtOptions, ostream: &mut BufWriter<
|
||||||
};
|
};
|
||||||
|
|
||||||
if opts.quick || para.mail_header {
|
if opts.quick || para.mail_header {
|
||||||
break_simple(p_words_words, &mut break_args);
|
break_simple(p_words_words, &mut break_args)
|
||||||
} else {
|
} else {
|
||||||
break_knuth_plass(p_words_words, &mut break_args);
|
break_knuth_plass(p_words_words, &mut break_args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// break_simple implements a "greedy" breaking algorithm: print words until
|
// break_simple implements a "greedy" breaking algorithm: print words until
|
||||||
// maxlength would be exceeded, then print a linebreak and indent and continue.
|
// maxlength would be exceeded, then print a linebreak and indent and continue.
|
||||||
fn break_simple<'a, T: Iterator<Item = &'a WordInfo<'a>>>(iter: T, args: &mut BreakArgs<'a>) {
|
fn break_simple<'a, T: Iterator<Item = &'a WordInfo<'a>>>(
|
||||||
iter.fold((args.init_len, false), |l, winfo| {
|
mut iter: T,
|
||||||
|
args: &mut BreakArgs<'a>,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
|
iter.try_fold((args.init_len, false), |l, winfo| {
|
||||||
accum_words_simple(args, l, winfo)
|
accum_words_simple(args, l, winfo)
|
||||||
});
|
})?;
|
||||||
silent_unwrap!(args.ostream.write_all(b"\n"));
|
args.ostream.write_all(b"\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accum_words_simple<'a>(
|
fn accum_words_simple<'a>(
|
||||||
args: &mut BreakArgs<'a>,
|
args: &mut BreakArgs<'a>,
|
||||||
(l, prev_punct): (usize, bool),
|
(l, prev_punct): (usize, bool),
|
||||||
winfo: &'a WordInfo<'a>,
|
winfo: &'a WordInfo<'a>,
|
||||||
) -> (usize, bool) {
|
) -> std::io::Result<(usize, bool)> {
|
||||||
// compute the length of this word, considering how tabs will expand at this position on the line
|
// compute the length of this word, considering how tabs will expand at this position on the line
|
||||||
let wlen = winfo.word_nchars + args.compute_width(winfo, l, false);
|
let wlen = winfo.word_nchars + args.compute_width(winfo, l, false);
|
||||||
|
|
||||||
|
@ -119,12 +125,12 @@ fn accum_words_simple<'a>(
|
||||||
);
|
);
|
||||||
|
|
||||||
if l + wlen + slen > args.opts.width {
|
if l + wlen + slen > args.opts.width {
|
||||||
write_newline(args.indent_str, args.ostream);
|
write_newline(args.indent_str, args.ostream)?;
|
||||||
write_with_spaces(&winfo.word[winfo.word_start..], 0, args.ostream);
|
write_with_spaces(&winfo.word[winfo.word_start..], 0, args.ostream)?;
|
||||||
(args.indent_len + winfo.word_nchars, winfo.ends_punct)
|
Ok((args.indent_len + winfo.word_nchars, winfo.ends_punct))
|
||||||
} else {
|
} else {
|
||||||
write_with_spaces(winfo.word, slen, args.ostream);
|
write_with_spaces(winfo.word, slen, args.ostream)?;
|
||||||
(l + wlen + slen, winfo.ends_punct)
|
Ok((l + wlen + slen, winfo.ends_punct))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,16 +141,16 @@ fn accum_words_simple<'a>(
|
||||||
fn break_knuth_plass<'a, T: Clone + Iterator<Item = &'a WordInfo<'a>>>(
|
fn break_knuth_plass<'a, T: Clone + Iterator<Item = &'a WordInfo<'a>>>(
|
||||||
mut iter: T,
|
mut iter: T,
|
||||||
args: &mut BreakArgs<'a>,
|
args: &mut BreakArgs<'a>,
|
||||||
) {
|
) -> std::io::Result<()> {
|
||||||
// run the algorithm to get the breakpoints
|
// run the algorithm to get the breakpoints
|
||||||
let breakpoints = find_kp_breakpoints(iter.clone(), args);
|
let breakpoints = find_kp_breakpoints(iter.clone(), args);
|
||||||
|
|
||||||
// iterate through the breakpoints (note that breakpoints is in reverse break order, so we .rev() it
|
// iterate through the breakpoints (note that breakpoints is in reverse break order, so we .rev() it
|
||||||
let (mut prev_punct, mut fresh) = breakpoints.iter().rev().fold(
|
let result: std::io::Result<(bool, bool)> = breakpoints.iter().rev().try_fold(
|
||||||
(false, false),
|
(false, false),
|
||||||
|(mut prev_punct, mut fresh), &(next_break, break_before)| {
|
|(mut prev_punct, mut fresh), &(next_break, break_before)| {
|
||||||
if fresh {
|
if fresh {
|
||||||
write_newline(args.indent_str, args.ostream);
|
write_newline(args.indent_str, args.ostream)?;
|
||||||
}
|
}
|
||||||
// at each breakpoint, keep emitting words until we find the word matching this breakpoint
|
// at each breakpoint, keep emitting words until we find the word matching this breakpoint
|
||||||
for winfo in &mut iter {
|
for winfo in &mut iter {
|
||||||
|
@ -167,26 +173,27 @@ fn break_knuth_plass<'a, T: Clone + Iterator<Item = &'a WordInfo<'a>>>(
|
||||||
if winfo_ptr == next_break_ptr {
|
if winfo_ptr == next_break_ptr {
|
||||||
// OK, we found the matching word
|
// OK, we found the matching word
|
||||||
if break_before {
|
if break_before {
|
||||||
write_newline(args.indent_str, args.ostream);
|
write_newline(args.indent_str, args.ostream)?;
|
||||||
write_with_spaces(&winfo.word[winfo.word_start..], 0, args.ostream);
|
write_with_spaces(&winfo.word[winfo.word_start..], 0, args.ostream)?;
|
||||||
} else {
|
} else {
|
||||||
// breaking after this word, so that means "fresh" is true for the next iteration
|
// breaking after this word, so that means "fresh" is true for the next iteration
|
||||||
write_with_spaces(word, slen, args.ostream);
|
write_with_spaces(word, slen, args.ostream)?;
|
||||||
fresh = true;
|
fresh = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
write_with_spaces(word, slen, args.ostream);
|
write_with_spaces(word, slen, args.ostream)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(prev_punct, fresh)
|
Ok((prev_punct, fresh))
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
let (mut prev_punct, mut fresh) = result?;
|
||||||
|
|
||||||
// after the last linebreak, write out the rest of the final line.
|
// after the last linebreak, write out the rest of the final line.
|
||||||
for winfo in iter {
|
for winfo in iter {
|
||||||
if fresh {
|
if fresh {
|
||||||
write_newline(args.indent_str, args.ostream);
|
write_newline(args.indent_str, args.ostream)?;
|
||||||
}
|
}
|
||||||
let (slen, word) = slice_if_fresh(
|
let (slen, word) = slice_if_fresh(
|
||||||
fresh,
|
fresh,
|
||||||
|
@ -199,9 +206,9 @@ fn break_knuth_plass<'a, T: Clone + Iterator<Item = &'a WordInfo<'a>>>(
|
||||||
);
|
);
|
||||||
prev_punct = winfo.ends_punct;
|
prev_punct = winfo.ends_punct;
|
||||||
fresh = false;
|
fresh = false;
|
||||||
write_with_spaces(word, slen, args.ostream);
|
write_with_spaces(word, slen, args.ostream)?;
|
||||||
}
|
}
|
||||||
silent_unwrap!(args.ostream.write_all(b"\n"));
|
args.ostream.write_all(b"\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LineBreak<'a> {
|
struct LineBreak<'a> {
|
||||||
|
@ -494,17 +501,21 @@ fn slice_if_fresh(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write a newline and add the indent.
|
// Write a newline and add the indent.
|
||||||
fn write_newline(indent: &str, ostream: &mut BufWriter<Stdout>) {
|
fn write_newline(indent: &str, ostream: &mut BufWriter<Stdout>) -> std::io::Result<()> {
|
||||||
silent_unwrap!(ostream.write_all(b"\n"));
|
ostream.write_all(b"\n")?;
|
||||||
silent_unwrap!(ostream.write_all(indent.as_bytes()));
|
ostream.write_all(indent.as_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the word, along with slen spaces.
|
// Write the word, along with slen spaces.
|
||||||
fn write_with_spaces(word: &str, slen: usize, ostream: &mut BufWriter<Stdout>) {
|
fn write_with_spaces(
|
||||||
|
word: &str,
|
||||||
|
slen: usize,
|
||||||
|
ostream: &mut BufWriter<Stdout>,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
if slen == 2 {
|
if slen == 2 {
|
||||||
silent_unwrap!(ostream.write_all(b" "));
|
ostream.write_all(b" ")?;
|
||||||
} else if slen == 1 {
|
} else if slen == 1 {
|
||||||
silent_unwrap!(ostream.write_all(b" "));
|
ostream.write_all(b" ")?;
|
||||||
}
|
}
|
||||||
silent_unwrap!(ostream.write_all(word.as_bytes()));
|
ostream.write_all(word.as_bytes())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue