pr: move from getopts to clap

fixes
This commit is contained in:
Terts Diepraam 2022-02-23 21:56:24 +01:00 committed by Sylvestre Ledru
parent 1c10ed7171
commit 75b3cbcfd9
3 changed files with 298 additions and 344 deletions

10
Cargo.lock generated
View file

@ -890,15 +890,6 @@ dependencies = [
"version_check",
]
[[package]]
name = "getopts"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
dependencies = [
"unicode-width",
]
[[package]]
name = "getrandom"
version = "0.2.4"
@ -2905,7 +2896,6 @@ version = "0.0.12"
dependencies = [
"chrono",
"clap 3.0.10",
"getopts",
"itertools",
"quick-error",
"regex",

View file

@ -17,7 +17,6 @@ path = "src/pr.rs"
[dependencies]
clap = { version = "3.0", features = ["wrap_help", "cargo"] }
uucore = { version=">=0.0.7", package="uucore", path="../../uucore", features=["entries"] }
getopts = "0.2.21"
chrono = "0.4.19"
quick-error = "2.0.1"
itertools = "0.10.0"

View file

@ -1,5 +1,3 @@
#![crate_name = "uu_pr"]
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE file
@ -13,9 +11,7 @@ extern crate quick_error;
use chrono::offset::Local;
use chrono::DateTime;
use clap::{App, AppSettings};
use getopts::Matches;
use getopts::{HasArg, Occur};
use clap::{App, AppSettings, Arg, ArgMatches};
use itertools::Itertools;
use quick_error::ResultExt;
use regex::Regex;
@ -26,14 +22,35 @@ use std::io::{stdin, stdout, BufRead, BufReader, Lines, Read, Write};
use std::os::unix::fs::FileTypeExt;
use uucore::display::Quotable;
use uucore::error::UResult;
use uucore::error::{set_exit_code, UResult};
type IOError = std::io::Error;
const NAME: &str = "pr";
const VERSION: &str = env!("CARGO_PKG_VERSION");
const ABOUT: &str =
"Write content of given file or standard input to standard output with pagination filter";
const AFTER_HELP: &str =
" +PAGE\n Begin output at page number page of the formatted input.
-COLUMN\n Produce multi-column output. See --column
The pr utility is a printing and pagination filter
for text files. When multiple input files are specified,
each is read, formatted, and written to standard
output. By default, the input is separated
into 66-line pages, each with
o A 5-line header with the page number, date,
time, and the pathname of the file.
o A 5-line trailer consisting of blank lines.
If standard output is associated with a terminal,
diagnostic messages are suppressed until the pr
utility has completed processing.
When multiple column output is specified, text columns
are of equal width. By default text columns
are separated by at least one <blank>. Input lines
that do not fit into a text column are truncated.
Lines are not truncated under single column output.";
const TAB: char = '\t';
const LINES_PER_PAGE: usize = 66;
const LINES_PER_PAGE_FOR_FORM_FEED: usize = 63;
@ -47,25 +64,27 @@ const DEFAULT_COLUMN_SEPARATOR: &char = &TAB;
const FF: u8 = 0x0C_u8;
mod options {
pub const STRING_HEADER_OPTION: &str = "h";
pub const DOUBLE_SPACE_OPTION: &str = "d";
pub const NUMBERING_MODE_OPTION: &str = "n";
pub const FIRST_LINE_NUMBER_OPTION: &str = "N";
pub const PAGE_RANGE_OPTION: &str = "pages";
pub const NO_HEADER_TRAILER_OPTION: &str = "t";
pub const PAGE_LENGTH_OPTION: &str = "l";
pub const SUPPRESS_PRINTING_ERROR: &str = "r";
pub const FORM_FEED_OPTION: &str = "F";
pub const FORM_FEED_OPTION_SMALL: &str = "f";
pub const COLUMN_WIDTH_OPTION: &str = "w";
pub const PAGE_WIDTH_OPTION: &str = "W";
pub const ACROSS_OPTION: &str = "a";
pub const COLUMN_OPTION: &str = "column";
pub const COLUMN_CHAR_SEPARATOR_OPTION: &str = "s";
pub const COLUMN_STRING_SEPARATOR_OPTION: &str = "S";
pub const MERGE_FILES_PRINT: &str = "m";
pub const OFFSET_SPACES_OPTION: &str = "o";
pub const JOIN_LINES_OPTION: &str = "J";
pub const HEADER: &str = "header";
pub const DOUBLE_SPACE: &str = "double-space";
pub const NUMBER_LINES: &str = "number-lines";
pub const FIRST_LINE_NUMBER: &str = "first-line-number";
pub const PAGES: &str = "pages";
pub const OMIT_HEADER: &str = "omit-header";
pub const PAGE_LENGTH: &str = "length";
pub const NO_FILE_WARNINGS: &str = "no-file-warnings";
pub const FORM_FEED: &str = "form-feed";
pub const COLUMN_WIDTH: &str = "width";
pub const PAGE_WIDTH: &str = "page-width";
pub const ACROSS: &str = "across";
pub const COLUMN: &str = "column";
pub const COLUMN_CHAR_SEPARATOR: &str = "separator";
pub const COLUMN_STRING_SEPARATOR: &str = "sep-string";
pub const MERGE: &str = "merge";
pub const INDENT: &str = "indent";
pub const JOIN_LINES: &str = "join-lines";
pub const HELP: &str = "help";
pub const VERSION: &str = "version";
pub const FILES: &str = "files";
}
struct OutputOptions {
@ -95,7 +114,7 @@ struct FileLine {
line_number: usize,
page_number: usize,
group_key: usize,
line_content: Result<String, IOError>,
line_content: Result<String, std::io::Error>,
form_feeds_after: usize,
}
@ -136,8 +155,8 @@ impl Default for FileLine {
}
}
impl From<IOError> for PrError {
fn from(err: IOError) -> Self {
impl From<std::io::Error> for PrError {
fn from(err: std::io::Error) -> Self {
Self::EncounteredErrors(err.to_string())
}
}
@ -145,8 +164,8 @@ impl From<IOError> for PrError {
quick_error! {
#[derive(Debug)]
enum PrError {
Input(err: IOError, path: String) {
context(path: &'a str, err: IOError) -> (err, path.to_owned())
Input(err: std::io::Error, path: String) {
context(path: &'a str, err: std::io::Error) -> (err, path.to_owned())
display("pr: Reading from input {0} gave error", path)
source(err)
}
@ -177,7 +196,182 @@ pub fn uu_app<'a>() -> App<'a> {
App::new(uucore::util_name())
.version(VERSION)
.about(ABOUT)
.after_help(AFTER_HELP)
.setting(AppSettings::InferLongArgs)
.setting(AppSettings::AllArgsOverrideSelf)
.setting(AppSettings::NoAutoHelp)
.setting(AppSettings::NoAutoVersion)
.arg(
Arg::new(options::PAGES)
.long(options::PAGES)
.help("Begin and stop printing with page FIRST_PAGE[:LAST_PAGE]")
.takes_value(true)
.value_name("FIRST_PAGE[:LAST_PAGE]"),
)
.arg(
Arg::new(options::HEADER)
.short('h')
.long(options::HEADER)
.help(
"Use the string header to replace the file name \
in the header line.",
)
.takes_value(true)
.value_name("STRING"),
)
.arg(
Arg::new(options::DOUBLE_SPACE)
.short('d')
.long(options::DOUBLE_SPACE)
.help("Produce output that is double spaced. An extra <newline> character is output following every <newline>
found in the input.")
)
.arg(
Arg::new(options::NUMBER_LINES)
.short('n')
.long(options::NUMBER_LINES)
.help("Provide width digit line numbering. The default for width, if not specified, is 5. The number occupies
the first width column positions of each text column or each line of -m output. If char (any non-digit
character) is given, it is appended to the line number to separate it from whatever follows. The default
for char is a <tab>. Line numbers longer than width columns are truncated.")
.takes_value(true)
.value_name("[char][width]")
)
.arg(
Arg::new(options::FIRST_LINE_NUMBER)
.short('N')
.long(options::FIRST_LINE_NUMBER)
.help("start counting with NUMBER at 1st line of first page printed")
.takes_value(true)
.value_name("NUMBER")
)
.arg(
Arg::new(options::OMIT_HEADER)
.short('t')
.long(options::OMIT_HEADER)
.help("Write neither the five-line identifying header nor the five-line trailer usually supplied for each page. Quit
writing after the last line of each file without spacing to the end of the page.")
)
.arg(
Arg::new(options::PAGE_LENGTH)
.short('l')
.long(options::PAGE_LENGTH)
.help("Override the 66-line default (default number of lines of text 56, and with -F 63) and reset the page length to lines. If lines is not greater than the sum of both
the header and trailer depths (in lines), the pr utility shall suppress both the header and trailer, as if the
-t option were in effect. ")
.takes_value(true)
.value_name("PAGE_LENGTH")
)
.arg(
Arg::new(options::NO_FILE_WARNINGS)
.short('r')
.long(options::NO_FILE_WARNINGS)
.help("omit warning when a file cannot be opened")
)
.arg(
Arg::new(options::FORM_FEED)
.short('F')
.short_alias('f')
.long(options::FORM_FEED)
.help("Use a <form-feed> for new pages, instead of the default behavior that uses a sequence of <newline>s.")
)
.arg(
Arg::new(options::COLUMN_WIDTH)
.short('w')
.long(options::COLUMN_WIDTH)
.help("Set the width of the line to width column positions for multiple text-column output only. If the -w option is
not specified and the -s option is not specified, the default width shall be 72. If the -w option is not specified
and the -s option is specified, the default width shall be 512.")
.takes_value(true)
.value_name("width")
)
.arg(
Arg::new(options::PAGE_WIDTH)
.short('W')
.long(options::PAGE_WIDTH)
.help("set page width to PAGE_WIDTH (72) characters always,
truncate lines, except -J option is set, no interference
with -S or -s")
.takes_value(true)
.value_name("width")
)
.arg(
Arg::new(options::ACROSS)
.short('a')
.long(options::ACROSS)
.help("Modify the effect of the - column option so that the columns are filled across the page in a round-robin order
(for example, when column is 2, the first input line heads column 1, the second heads column 2, the third is the
second line in column 1, and so on).")
)
.arg(
Arg::new(options::COLUMN)
.long(options::COLUMN)
.help("Produce multi-column output that is arranged in column columns (the default shall be 1) and is written down each
column in the order in which the text is received from the input file. This option should not be used with -m.
The options -e and -i shall be assumed for multiple text-column output. Whether or not text columns are produced
with identical vertical lengths is unspecified, but a text column shall never exceed the length of the
page (see the -l option). When used with -t, use the minimum number of lines to write the output.")
.takes_value(true)
.value_name("column")
)
.arg(
Arg::new(options::COLUMN_CHAR_SEPARATOR)
.short('s')
.long(options::COLUMN_CHAR_SEPARATOR)
.help("Separate text columns by the single character char instead of by the appropriate number of <space>s
(default for char is the <tab> character).")
.takes_value(true)
.value_name("char")
)
.arg(
Arg::new(options::COLUMN_STRING_SEPARATOR)
.short('S')
.long(options::COLUMN_STRING_SEPARATOR)
.help("separate columns by STRING,
without -S: Default separator <TAB> with -J and <space>
otherwise (same as -S\" \"), no effect on column options")
.takes_value(true)
.value_name("string")
)
.arg(
Arg::new(options::MERGE)
.short('m')
.long(options::MERGE)
.help("Merge files. Standard output shall be formatted so the pr utility writes one line from each file specified by a
file operand, side by side into text columns of equal fixed widths, in terms of the number of column positions.
Implementations shall support merging of at least nine file operands.")
)
.arg(
Arg::new(options::INDENT)
.short('o')
.long(options::INDENT)
.help("Each line of output shall be preceded by offset <space>s. If the -o option is not specified, the default offset
shall be zero. The space taken is in addition to the output line width (see the -w option below).")
.takes_value(true)
.value_name("margin")
)
.arg(
Arg::new(options::JOIN_LINES)
.short('J')
.help("merge full lines, turns off -W line truncation, no column
alignment, --sep-string[=STRING] sets separators")
)
.arg(
Arg::new(options::HELP)
.long(options::HELP)
.help("Show this help message")
)
.arg(
Arg::new(options::VERSION)
.short('V')
.long(options::VERSION)
.help("Show version information")
)
.arg(
Arg::new(options::FILES)
.multiple_occurrences(true)
.multiple_values(true)
)
}
#[uucore::main]
@ -185,229 +379,39 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = args
.collect_str(uucore::InvalidEncodingHandling::Ignore)
.accept_any();
let mut opts = getopts::Options::new();
opts.opt(
"",
options::PAGE_RANGE_OPTION,
"Begin and stop printing with page FIRST_PAGE[:LAST_PAGE]",
"FIRST_PAGE[:LAST_PAGE]",
HasArg::Yes,
Occur::Optional,
);
opts.opt(
options::STRING_HEADER_OPTION,
"header",
"Use the string header to replace the file name \
in the header line.",
"STRING",
HasArg::Yes,
Occur::Optional,
);
opts.opt(
options::DOUBLE_SPACE_OPTION,
"double-space",
"Produce output that is double spaced. An extra <newline> character is output following every <newline>
found in the input.",
"",
HasArg::No,
Occur::Optional,
);
opts.opt(
options::NUMBERING_MODE_OPTION,
"number-lines",
"Provide width digit line numbering. The default for width, if not specified, is 5. The number occupies
the first width column positions of each text column or each line of -m output. If char (any non-digit
character) is given, it is appended to the line number to separate it from whatever follows. The default
for char is a <tab>. Line numbers longer than width columns are truncated.",
"[char][width]",
HasArg::Maybe,
Occur::Optional,
);
opts.opt(
options::FIRST_LINE_NUMBER_OPTION,
"first-line-number",
"start counting with NUMBER at 1st line of first page printed",
"NUMBER",
HasArg::Yes,
Occur::Optional,
);
opts.opt(
options::NO_HEADER_TRAILER_OPTION,
"omit-header",
"Write neither the five-line identifying header nor the five-line trailer usually supplied for each page. Quit
writing after the last line of each file without spacing to the end of the page.",
"",
HasArg::No,
Occur::Optional,
);
opts.opt(
options::PAGE_LENGTH_OPTION,
"length",
"Override the 66-line default (default number of lines of text 56, and with -F 63) and reset the page length to lines. If lines is not greater than the sum of both
the header and trailer depths (in lines), the pr utility shall suppress both the header and trailer, as if the
-t option were in effect. ",
"lines",
HasArg::Yes,
Occur::Optional,
);
opts.opt(
options::SUPPRESS_PRINTING_ERROR,
"no-file-warnings",
"omit warning when a file cannot be opened",
"",
HasArg::No,
Occur::Optional,
);
opts.opt(
options::FORM_FEED_OPTION,
"form-feed",
"Use a <form-feed> for new pages, instead of the default behavior that uses a sequence of <newline>s.",
"",
HasArg::No,
Occur::Optional,
);
opts.opt(
options::FORM_FEED_OPTION_SMALL,
"form-feed",
"Same as -F but pause before beginning the first page if standard output is a
terminal.",
"",
HasArg::No,
Occur::Optional,
);
opts.opt(
"",
options::COLUMN_OPTION,
"Produce multi-column output that is arranged in column columns (the default shall be 1) and is written down each
column in the order in which the text is received from the input file. This option should not be used with -m.
The options -e and -i shall be assumed for multiple text-column output. Whether or not text columns are produced
with identical vertical lengths is unspecified, but a text column shall never exceed the length of the
page (see the -l option). When used with -t, use the minimum number of lines to write the output.",
"[column]",
HasArg::Yes,
Occur::Optional,
);
opts.opt(
options::COLUMN_WIDTH_OPTION,
"width",
"Set the width of the line to width column positions for multiple text-column output only. If the -w option is
not specified and the -s option is not specified, the default width shall be 72. If the -w option is not specified
and the -s option is specified, the default width shall be 512.",
"[width]",
HasArg::Yes,
Occur::Optional,
);
opts.opt(
options::PAGE_WIDTH_OPTION,
"page-width",
"set page width to PAGE_WIDTH (72) characters always,
truncate lines, except -J option is set, no interference
with -S or -s",
"[width]",
HasArg::Yes,
Occur::Optional,
);
opts.opt(
options::ACROSS_OPTION,
"across",
"Modify the effect of the - column option so that the columns are filled across the page in a round-robin order
(for example, when column is 2, the first input line heads column 1, the second heads column 2, the third is the
second line in column 1, and so on).",
"",
HasArg::No,
Occur::Optional,
);
opts.opt(
options::COLUMN_CHAR_SEPARATOR_OPTION,
"separator",
"Separate text columns by the single character char instead of by the appropriate number of <space>s
(default for char is the <tab> character).",
"char",
HasArg::Yes,
Occur::Optional,
);
opts.opt(
options::COLUMN_STRING_SEPARATOR_OPTION,
"sep-string",
"separate columns by STRING,
without -S: Default separator <TAB> with -J and <space>
otherwise (same as -S\" \"), no effect on column options",
"string",
HasArg::Yes,
Occur::Optional,
);
opts.opt(
options::MERGE_FILES_PRINT,
"merge",
"Merge files. Standard output shall be formatted so the pr utility writes one line from each file specified by a
file operand, side by side into text columns of equal fixed widths, in terms of the number of column positions.
Implementations shall support merging of at least nine file operands.",
"",
HasArg::No,
Occur::Optional,
);
opts.opt(
options::OFFSET_SPACES_OPTION,
"indent",
"Each line of output shall be preceded by offset <space>s. If the -o option is not specified, the default offset
shall be zero. The space taken is in addition to the output line width (see the -w option below).",
"offset",
HasArg::Yes,
Occur::Optional,
);
opts.opt(
options::JOIN_LINES_OPTION,
"join-lines",
"merge full lines, turns off -W line truncation, no column
alignment, --sep-string[=STRING] sets separators",
"offset",
HasArg::No,
Occur::Optional,
);
opts.optflag("", "help", "display this help and exit");
opts.optflag("V", "version", "output version information and exit");
let opt_args = recreate_arguments(&args);
let matches = match opts.parse(&opt_args[1..]) {
let mut app = uu_app();
let matches = match app.try_get_matches_from_mut(opt_args) {
Ok(m) => m,
Err(e) => panic!("Invalid options\n{}", e),
Err(e) => {
e.print()?;
set_exit_code(1);
return Ok(());
}
};
if matches.opt_present("version") {
println!("{} {}", NAME, VERSION);
if matches.is_present(options::VERSION) {
println!("{}", app.render_long_version());
return Ok(());
}
let mut files = matches.free.clone();
if matches.is_present(options::HELP) {
app.print_long_help()?;
return Ok(());
}
let mut files = matches
.values_of(options::FILES)
.map(|v| v.collect::<Vec<_>>())
.unwrap_or_default()
.clone();
if files.is_empty() {
files.insert(0, FILE_STDIN.to_owned());
files.insert(0, FILE_STDIN);
}
if matches.opt_present("help") {
return print_usage(&mut opts, &matches);
}
let file_groups: Vec<_> = if matches.opt_present(options::MERGE_FILES_PRINT) {
let file_groups: Vec<_> = if matches.is_present(options::MERGE) {
vec![files]
} else {
files.into_iter().map(|i| vec![i]).collect()
@ -470,56 +474,13 @@ fn recreate_arguments(args: &[String]) -> Vec<String> {
.collect()
}
fn print_error(matches: &Matches, err: &PrError) {
if !matches.opt_present(options::SUPPRESS_PRINTING_ERROR) {
fn print_error(matches: &ArgMatches, err: &PrError) {
if !matches.is_present(options::NO_FILE_WARNINGS) {
eprintln!("{}", err);
}
}
fn print_usage(opts: &mut getopts::Options, matches: &Matches) -> UResult<()> {
println!("{} {} -- print files", NAME, VERSION);
println!();
println!(
"Usage: {} [+page] [-column] [-adFfmprt] [[-e] [char] [gap]]
[-L locale] [-h header] [[-i] [char] [gap]]
[-l lines] [-o offset] [[-s] [char]] [[-n] [char]
[width]] [-w width] [-] [file ...].",
NAME
);
println!();
let usage: &str = "The pr utility is a printing and pagination filter
for text files. When multiple input files are specified,
each is read, formatted, and written to standard
output. By default, the input is separated
into 66-line pages, each with
o A 5-line header with the page number, date,
time, and the pathname of the file.
o A 5-line trailer consisting of blank lines.
If standard output is associated with a terminal,
diagnostic messages are suppressed until the pr
utility has completed processing.
When multiple column output is specified, text columns
are of equal width. By default text columns
are separated by at least one <blank>. Input lines
that do not fit into a text column are truncated.
Lines are not truncated under single column output.";
println!("{}", opts.usage(usage));
println!(" +page \t\tBegin output at page number page of the formatted input.");
println!(
" -column \t\tProduce multi-column output. Refer --{}",
options::COLUMN_OPTION
);
if matches.free.is_empty() {
return Err(1.into());
}
Ok(())
}
fn parse_usize(matches: &Matches, opt: &str) -> Option<Result<usize, PrError>> {
fn parse_usize(matches: &ArgMatches, opt: &str) -> Option<Result<usize, PrError>> {
let from_parse_error_to_pr_error = |value_to_parse: (String, String)| {
let i = value_to_parse.0;
let option = value_to_parse.1;
@ -528,51 +489,51 @@ fn parse_usize(matches: &Matches, opt: &str) -> Option<Result<usize, PrError>> {
})
};
matches
.opt_str(opt)
.map(|i| (i, format!("-{}", opt)))
.value_of(opt)
.map(|i| (i.to_string(), format!("-{}", opt)))
.map(from_parse_error_to_pr_error)
}
fn build_options(
matches: &Matches,
paths: &[String],
matches: &ArgMatches,
paths: &[&str],
free_args: &str,
) -> Result<OutputOptions, PrError> {
let form_feed_used = matches.opt_present(options::FORM_FEED_OPTION)
|| matches.opt_present(options::FORM_FEED_OPTION_SMALL);
let form_feed_used = matches.is_present(options::FORM_FEED);
let is_merge_mode = matches.opt_present(options::MERGE_FILES_PRINT);
let is_merge_mode = matches.is_present(options::MERGE);
if is_merge_mode && matches.opt_present(options::COLUMN_OPTION) {
if is_merge_mode && matches.is_present(options::COLUMN) {
let err_msg = String::from("cannot specify number of columns when printing in parallel");
return Err(PrError::EncounteredErrors(err_msg));
}
if is_merge_mode && matches.opt_present(options::ACROSS_OPTION) {
if is_merge_mode && matches.is_present(options::ACROSS) {
let err_msg = String::from("cannot specify both printing across and printing in parallel");
return Err(PrError::EncounteredErrors(err_msg));
}
let merge_files_print = if matches.opt_present(options::MERGE_FILES_PRINT) {
let merge_files_print = if matches.is_present(options::MERGE) {
Some(paths.len())
} else {
None
};
let header = matches.opt_str(options::STRING_HEADER_OPTION).unwrap_or(
if is_merge_mode || paths[0] == FILE_STDIN {
String::new()
let header = matches
.value_of(options::HEADER)
.unwrap_or(if is_merge_mode || paths[0] == FILE_STDIN {
""
} else {
paths[0].to_string()
},
);
paths[0]
})
.to_string();
let default_first_number = NumberingMode::default().first_number;
let first_number = parse_usize(matches, options::FIRST_LINE_NUMBER_OPTION)
.unwrap_or(Ok(default_first_number))?;
let first_number =
parse_usize(matches, options::FIRST_LINE_NUMBER).unwrap_or(Ok(default_first_number))?;
let number = matches
.opt_str(options::NUMBERING_MODE_OPTION)
.value_of(options::NUMBER_LINES)
.map(|i| {
let parse_result = i.parse::<usize>();
@ -596,14 +557,14 @@ fn build_options(
}
})
.or_else(|| {
if matches.opt_present(options::NUMBERING_MODE_OPTION) {
if matches.is_present(options::NUMBER_LINES) {
Some(NumberingMode::default())
} else {
None
}
});
let double_space = matches.opt_present(options::DOUBLE_SPACE_OPTION);
let double_space = matches.is_present(options::DOUBLE_SPACE);
let content_line_separator = if double_space {
"\n".repeat(2)
@ -652,7 +613,7 @@ fn build_options(
};
let invalid_pages_map = |i: String| {
let unparsed_value = matches.opt_str(options::PAGE_RANGE_OPTION).unwrap();
let unparsed_value = matches.value_of(options::PAGES).unwrap();
i.parse::<usize>().map_err(|_e| {
PrError::EncounteredErrors(format!(
"invalid --pages argument {}",
@ -662,7 +623,7 @@ fn build_options(
};
let start_page = match matches
.opt_str(options::PAGE_RANGE_OPTION)
.value_of(options::PAGES)
.map(|i| {
let x: Vec<_> = i.split(':').collect();
x[0].to_string()
@ -674,7 +635,7 @@ fn build_options(
};
let end_page = match matches
.opt_str(options::PAGE_RANGE_OPTION)
.value_of(options::PAGES)
.filter(|i| i.contains(':'))
.map(|i| {
let x: Vec<_> = i.split(':').collect();
@ -702,12 +663,12 @@ fn build_options(
};
let page_length =
parse_usize(matches, options::PAGE_LENGTH_OPTION).unwrap_or(Ok(default_lines_per_page))?;
parse_usize(matches, options::PAGE_LENGTH).unwrap_or(Ok(default_lines_per_page))?;
let page_length_le_ht = page_length < (HEADER_LINES_PER_PAGE + TRAILER_LINES_PER_PAGE);
let display_header_and_trailer =
!(page_length_le_ht) && !matches.opt_present(options::NO_HEADER_TRAILER_OPTION);
!(page_length_le_ht) && !matches.is_present(options::OMIT_HEADER);
let content_lines_per_page = if page_length_le_ht {
page_length
@ -715,23 +676,24 @@ fn build_options(
page_length - (HEADER_LINES_PER_PAGE + TRAILER_LINES_PER_PAGE)
};
let page_separator_char = if matches.opt_present(options::FORM_FEED_OPTION) {
let page_separator_char = if matches.is_present(options::FORM_FEED) {
let bytes = vec![FF];
String::from_utf8(bytes).unwrap()
} else {
"\n".to_string()
};
let across_mode = matches.opt_present(options::ACROSS_OPTION);
let across_mode = matches.is_present(options::ACROSS);
let column_separator = match matches.opt_str(options::COLUMN_STRING_SEPARATOR_OPTION) {
let column_separator = match matches.value_of(options::COLUMN_STRING_SEPARATOR) {
Some(x) => Some(x),
None => matches.opt_str(options::COLUMN_CHAR_SEPARATOR_OPTION),
None => matches.value_of(options::COLUMN_CHAR_SEPARATOR),
}
.map(ToString::to_string)
.unwrap_or_else(|| DEFAULT_COLUMN_SEPARATOR.to_string());
let default_column_width = if matches.opt_present(options::COLUMN_WIDTH_OPTION)
&& matches.opt_present(options::COLUMN_CHAR_SEPARATOR_OPTION)
let default_column_width = if matches.is_present(options::COLUMN_WIDTH)
&& matches.is_present(options::COLUMN_CHAR_SEPARATOR)
{
DEFAULT_COLUMN_WIDTH_WITH_S_OPTION
} else {
@ -739,12 +701,12 @@ fn build_options(
};
let column_width =
parse_usize(matches, options::COLUMN_WIDTH_OPTION).unwrap_or(Ok(default_column_width))?;
parse_usize(matches, options::COLUMN_WIDTH).unwrap_or(Ok(default_column_width))?;
let page_width = if matches.opt_present(options::JOIN_LINES_OPTION) {
let page_width = if matches.is_present(options::JOIN_LINES) {
None
} else {
match parse_usize(matches, options::PAGE_WIDTH_OPTION) {
match parse_usize(matches, options::PAGE_WIDTH) {
Some(res) => Some(res?),
None => None,
}
@ -764,7 +726,7 @@ fn build_options(
// --column has more priority than -column
let column_option_value = match parse_usize(matches, options::COLUMN_OPTION) {
let column_option_value = match parse_usize(matches, options::COLUMN) {
Some(res) => Some(res?),
None => start_column_option,
};
@ -776,9 +738,8 @@ fn build_options(
across_mode,
});
let offset_spaces =
" ".repeat(parse_usize(matches, options::OFFSET_SPACES_OPTION).unwrap_or(Ok(0))?);
let join_lines = matches.opt_present(options::JOIN_LINES_OPTION);
let offset_spaces = " ".repeat(parse_usize(matches, options::INDENT).unwrap_or(Ok(0))?);
let join_lines = matches.is_present(options::JOIN_LINES);
let col_sep_for_printing = column_mode_options
.as_ref()
@ -855,7 +816,7 @@ fn open(path: &str) -> Result<Box<dyn Read>, PrError> {
.unwrap_or_else(|_| Err(PrError::NotExists(path.to_string())))
}
fn split_lines_if_form_feed(file_content: Result<String, IOError>) -> Vec<FileLine> {
fn split_lines_if_form_feed(file_content: Result<String, std::io::Error>) -> Vec<FileLine> {
file_content
.map(|content| {
let mut lines = Vec::new();
@ -972,7 +933,7 @@ fn read_stream_and_create_pages(
)
}
fn mpr(paths: &[String], options: &OutputOptions) -> Result<i32, PrError> {
fn mpr(paths: &[&str], options: &OutputOptions) -> Result<i32, PrError> {
let n_files = paths.len();
// Check if files exists
@ -1032,7 +993,11 @@ fn mpr(paths: &[String], options: &OutputOptions) -> Result<i32, PrError> {
Ok(0)
}
fn print_page(lines: &[FileLine], options: &OutputOptions, page: usize) -> Result<usize, IOError> {
fn print_page(
lines: &[FileLine],
options: &OutputOptions,
page: usize,
) -> Result<usize, std::io::Error> {
let line_separator = options.line_separator.as_bytes();
let page_separator = options.page_separator_char.as_bytes();
@ -1064,7 +1029,7 @@ fn write_columns(
lines: &[FileLine],
options: &OutputOptions,
out: &mut impl Write,
) -> Result<usize, IOError> {
) -> Result<usize, std::io::Error> {
let line_separator = options.content_line_separator.as_bytes();
let content_lines_per_page = if options.double_space {