-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Pr e flag #10167
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Pr e flag #10167
Changes from all commits
fd869f2
5439c3b
e677925
e52e6ab
9643a05
ffd752c
7620c60
7fa6712
b15c6f4
8eb2f7e
ec75a47
512b39f
e6d9031
a7c6fd2
4d4c06f
02e5b7e
067bba8
48eb688
2b98632
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,6 +58,7 @@ mod options { | |
| pub const JOIN_LINES: &str = "join-lines"; | ||
| pub const HELP: &str = "help"; | ||
| pub const FILES: &str = "files"; | ||
| pub const EXPAND_TABS: &str = "expand-tabs"; | ||
| } | ||
|
|
||
| struct OutputOptions { | ||
|
|
@@ -80,6 +81,7 @@ struct OutputOptions { | |
| join_lines: bool, | ||
| col_sep_for_printing: String, | ||
| line_width: Option<usize>, | ||
| expand_tabs: Option<ExpandTabsOptions>, | ||
| } | ||
|
|
||
| struct FileLine { | ||
|
|
@@ -105,6 +107,21 @@ struct NumberingMode { | |
| first_number: usize, | ||
| } | ||
|
|
||
| #[derive(Debug)] | ||
| struct ExpandTabsOptions { | ||
| input_char: char, | ||
| width: i32, | ||
| } | ||
|
|
||
| impl Default for ExpandTabsOptions { | ||
| fn default() -> Self { | ||
| Self { | ||
| width: 8, | ||
| input_char: TAB, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl Default for NumberingMode { | ||
| fn default() -> Self { | ||
| Self { | ||
|
|
@@ -328,6 +345,14 @@ pub fn uu_app() -> Command { | |
| .action(ArgAction::Append) | ||
| .value_hint(clap::ValueHint::FilePath), | ||
| ) | ||
| .arg( | ||
| Arg::new(options::EXPAND_TABS) | ||
| .long(options::EXPAND_TABS) | ||
| .short('e') | ||
| .num_args(1) | ||
| .value_name("[CHAR][WIDTH]") | ||
| .help(translate!("pr-help-expand-tabs")), | ||
| ) | ||
| } | ||
|
|
||
| #[uucore::main] | ||
|
|
@@ -392,6 +417,7 @@ fn recreate_arguments(args: &[String]) -> Vec<String> { | |
| let column_page_option = Regex::new(r"^[-+]\d+.*").unwrap(); | ||
| let num_regex = Regex::new(r"^[^-]\d*$").unwrap(); | ||
| let n_regex = Regex::new(r"^-n\s*$").unwrap(); | ||
| let e_regex = Regex::new(r"^-e").unwrap(); | ||
| let mut arguments = args.to_owned(); | ||
| let num_option = args.iter().find_position(|x| n_regex.is_match(x.trim())); | ||
| if let Some((pos, _value)) = num_option { | ||
|
|
@@ -404,6 +430,17 @@ fn recreate_arguments(args: &[String]) -> Vec<String> { | |
| } | ||
| } | ||
|
|
||
| // To ensure not to accidentally delete the next argument after a short flag for -e we insert | ||
| // the default values for the -e flag is '-e' is present without direct arguments. | ||
| let expand_tabs_option = arguments | ||
| .iter() | ||
| .find_position(|x| e_regex.is_match(x.trim())); | ||
| if let Some((pos, value)) = expand_tabs_option { | ||
| if value.trim().len() <= 2 { | ||
| arguments[pos] = "-e\t8".to_string(); | ||
| } | ||
| } | ||
|
|
||
| arguments | ||
| .into_iter() | ||
| .filter(|i| !column_page_option.is_match(i)) | ||
|
|
@@ -525,6 +562,26 @@ fn build_options( | |
| } | ||
| }); | ||
|
|
||
| let expand_tabs = matches | ||
| .get_one::<String>(options::EXPAND_TABS) | ||
| .map(|s| { | ||
| s.chars().next().map_or(Ok(ExpandTabsOptions::default()), |c| { | ||
| if c.is_ascii_digit() { | ||
| s | ||
| .parse() | ||
| .map_err(|_e| PrError::EncounteredErrors { msg: format!("{}\n{}", translate!("pr-error-invalid-expand-tab-argument", "arg" => s), translate!("pr-try-help-message")) }) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment as here: https://github.com/uutils/coreutils/pull/10167/changes#r2688579349 |
||
| .map(|width| ExpandTabsOptions{input_char: TAB, width}) | ||
| } else if s.len() > 1 { | ||
| s[1..] | ||
| .parse() | ||
| .map_err(|_e| PrError::EncounteredErrors { msg: format!("{}\n{}", translate!("pr-error-invalid-expand-tab-argument", "arg" => &s[1..]), translate!("pr-try-help-message")) }) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment as here: https://github.com/uutils/coreutils/pull/10167/changes#r2688579349 |
||
| .map(|width| ExpandTabsOptions{input_char: c, width}) | ||
| } else { | ||
| Ok(ExpandTabsOptions{input_char: c, width: 8}) | ||
| } | ||
| }) | ||
| }).transpose()?; | ||
|
|
||
| let double_space = matches.get_flag(options::DOUBLE_SPACE); | ||
|
|
||
| let content_line_separator = if double_space { | ||
|
|
@@ -761,6 +818,7 @@ fn build_options( | |
| join_lines, | ||
| col_sep_for_printing, | ||
| line_width, | ||
| expand_tabs, | ||
| }) | ||
| } | ||
|
|
||
|
|
@@ -807,7 +865,10 @@ fn open(path: &str) -> Result<Box<dyn Read>, PrError> { | |
| ) | ||
| } | ||
|
|
||
| fn split_lines_if_form_feed(file_content: Result<String, std::io::Error>) -> Vec<FileLine> { | ||
| fn split_lines_if_form_feed( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a bit tricky to review from the context that when trying to see if the implementation of -e is correct I don't have the baseline that the other aspects of the pr utility is working correctly. I would personally just try to implement the handler first and then making sure that the base case for pr actually produces the same output as pr before implementing the logic for the many flags and options that can be provided. |
||
| file_content: Result<String, std::io::Error>, | ||
| expand_options: Option<&ExpandTabsOptions>, | ||
| ) -> Vec<FileLine> { | ||
| file_content.map_or_else( | ||
| |e| { | ||
| vec![FileLine { | ||
|
|
@@ -832,7 +893,14 @@ fn split_lines_if_form_feed(file_content: Result<String, std::io::Error>) -> Vec | |
| }); | ||
| chunk.clear(); | ||
| } | ||
| chunk.push(*byte); | ||
|
|
||
| // The -e flag determines that an encountered input char (TAB) shall be expanded. | ||
| // If the -e flag was not set we pass the byte through unchanged. | ||
| match expand_options { | ||
| Some(expand_options) => apply_expand_tab(&mut chunk, *byte, expand_options), | ||
| None => chunk.push(*byte), | ||
| } | ||
|
|
||
| f_occurred = 0; | ||
| } | ||
| } | ||
|
|
@@ -848,6 +916,27 @@ fn split_lines_if_form_feed(file_content: Result<String, std::io::Error>) -> Vec | |
| ) | ||
| } | ||
|
|
||
| fn apply_expand_tab(chunk: &mut Vec<u8>, byte: u8, expand_options: &ExpandTabsOptions) { | ||
| if byte == expand_options.input_char as u8 { | ||
| // If the byte encountered is the input char we use width to calculate | ||
| // the amount of spaces needed (if no input char given we stored '\t' | ||
| // in our struct) | ||
| let spaces_needed = | ||
| expand_options.width as usize - (chunk.len() % expand_options.width as usize); | ||
| chunk.resize(chunk.len() + spaces_needed, b' '); | ||
| } else if byte == TAB as u8 { | ||
| // If a byte got passed to the -e flag (eg -ea1) which is not '\t' GNU | ||
| // still expands it but does not use an optionally given width parameter | ||
| // but does the '\t' expansion with the default value (8) | ||
| let spaces_needed = 8 - (chunk.len() % 8); | ||
| chunk.resize(chunk.len() + spaces_needed, b' '); | ||
| } else { | ||
| // This arm means the byte is neither '\t' nor the bytes to be | ||
| // expanded | ||
| chunk.push(byte); | ||
| } | ||
| } | ||
|
|
||
| fn pr(path: &str, options: &OutputOptions) -> Result<i32, PrError> { | ||
| let lines = BufReader::with_capacity(READ_BUFFER_SIZE, open(path)?).lines(); | ||
|
|
||
|
|
@@ -866,15 +955,15 @@ fn read_stream_and_create_pages( | |
| options: &OutputOptions, | ||
| lines: Lines<BufReader<Box<dyn Read>>>, | ||
| file_id: usize, | ||
| ) -> Box<dyn Iterator<Item = (usize, Vec<FileLine>)>> { | ||
| ) -> Box<dyn Iterator<Item = (usize, Vec<FileLine>)> + '_> { | ||
| let start_page = options.start_page; | ||
| let start_line_number = get_start_line_number(options); | ||
| let last_page = options.end_page; | ||
| let lines_needed_per_page = lines_to_read_for_page(options); | ||
|
|
||
| Box::new( | ||
| lines | ||
| .flat_map(split_lines_if_form_feed) | ||
| .flat_map(|s| split_lines_if_form_feed(s, options.expand_tabs.as_ref())) | ||
| .enumerate() | ||
| .map(move |(i, line)| FileLine { | ||
| line_number: i + start_line_number, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This message in other utilities we get from using the UError from uucore, it just wraps the error message with this for each utility. For example this error message: https://github.com/uutils/coreutils/blob/main/src/uu/stty/src/stty.rs#L447 is wrapped with: "Try "stty --help" for more imformation."