Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion src/uu/pr/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ clap = { workspace = true }
uucore = { workspace = true, features = ["entries", "time"] }
itertools = { workspace = true }
memchr = { workspace = true }
regex = { workspace = true }
thiserror = { workspace = true }
fluent = { workspace = true }

Expand Down
144 changes: 91 additions & 53 deletions src/uu/pr/src/pr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

use clap::{Arg, ArgAction, ArgMatches, Command};
use itertools::Itertools;
use regex::Regex;
use std::ffi::OsStr;
use std::fs::metadata;
use std::io::{Read, Write, stderr, stdin, stdout};
Expand Down Expand Up @@ -426,20 +425,64 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
Ok(())
}

/// Extract the `digits[:digits]` part from a `+digits[:digits]` token found anywhere in the string.
fn extract_plus_page(s: &str) -> Option<String> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can return an Option<&str>.

for token in s.split_whitespace() {
if let Some(rest) = token.strip_prefix('+') {
if rest.starts_with(|c: char| c.is_ascii_digit()) {
return Some(rest.to_string());
}
}
}
None
}

/// Extract the digits part from a `-digits` token found anywhere in the string.
/// Equivalent to the regex `\s*-(\d+)\s*` capture group 1.
fn extract_minus_col(s: &str) -> Option<String> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

for token in s.split_whitespace() {
if let Some(rest) = token.strip_prefix('-') {
if !rest.is_empty() && rest.chars().all(|c| c.is_ascii_digit()) {
return Some(rest.to_string());
}
}
}
None
}

/// Check if string matches `^[-+]\d+.*` (starts with `-` or `+` followed by a digit).
fn is_column_page_option(s: &str) -> bool {
let mut chars = s.chars();
matches!(chars.next(), Some('-' | '+')) && matches!(chars.next(), Some('0'..='9'))
}

/// Check if string matches `^[^-]\d*$` (doesn't start with `-`, rest are all digits).
fn is_num_value(s: &str) -> bool {
let mut chars = s.chars();
match chars.next() {
Some('-') | None => false,
Some(_) => chars.all(|c| c.is_ascii_digit()),
}
Comment on lines +461 to +465
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would use an early exit instead of a match:

Suggested change
let mut chars = s.chars();
match chars.next() {
Some('-') | None => false,
Some(_) => chars.all(|c| c.is_ascii_digit()),
}
if s.is_empty() || s.starts_with('-') {
return false;
}
s.chars().skip(1).all(|c| c.is_ascii_digit())

}

/// Check if string matches `^-n\s*$` (is `-n` optionally followed by whitespace).
fn is_n_flag(s: &str) -> bool {
s.trim().trim_end() == "-n"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The trim() is not correct if you want to ensure the string starts with -n.

Suggested change
s.trim().trim_end() == "-n"
s.trim_end() == "-n"

}

/// Check if string matches `^-e` (starts with `-e`).
fn is_e_flag(s: &str) -> bool {
s.trim().starts_with("-e")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

Suggested change
s.trim().starts_with("-e")
s.starts_with("-e")

}

/// Returns re-written arguments which are passed to the program.
/// Removes -column and +page option as getopts cannot parse things like -3 etc
/// # Arguments
/// * `args` - Command line arguments
/// Removes -column and +page option as getopts cannot parse things like -3 etc.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it should be clap as we don't use getopts.

Suggested change
/// Removes -column and +page option as getopts cannot parse things like -3 etc.
/// Removes -column and +page option as clap cannot parse things like -3 etc.

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()));
let num_option = args.iter().find_position(|x| is_n_flag(x.trim()));
if let Some((pos, _value)) = num_option {
if let Some(num_val_opt) = args.get(pos + 1) {
if !num_regex.is_match(num_val_opt) {
if !is_num_value(num_val_opt) {
let could_be_file = arguments.remove(pos + 1);
arguments.insert(pos + 1, format!("{}", NumberingMode::default().width));
arguments.insert(pos + 2, could_be_file);
Expand All @@ -449,9 +492,7 @@ 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()));
let expand_tabs_option = arguments.iter().find_position(|x| is_e_flag(x.trim()));
if let Some((pos, value)) = expand_tabs_option {
if value.trim().len() <= 2 {
arguments[pos] = "-e\t8".to_string();
Expand All @@ -460,7 +501,7 @@ fn recreate_arguments(args: &[String]) -> Vec<String> {

arguments
.into_iter()
.filter(|i| !column_page_option.is_match(i))
.filter(|i| !is_column_page_option(i))
.collect()
}

Expand Down Expand Up @@ -632,35 +673,36 @@ fn build_options(
};

// +page option is less priority than --pages
let page_plus_re = Regex::new(r"\s*\+(\d+:*\d*)\s*").unwrap();
let res = page_plus_re.captures(free_args).map(|i| {
let unparsed_num = i.get(1).unwrap().as_str().trim();
let x: Vec<_> = unparsed_num.split(':').collect();
x[0].to_string()
.parse::<usize>()
.map_err(|_e| PrError::EncounteredErrors {
msg: format!("invalid {} argument {}", "+", unparsed_num.quote()),
})
});
let start_page_in_plus_option = match res {
Some(res) => res?,
None => 1,
};
// Extract the page spec from a "+digits[:digits]" token in free_args.
let page_plus_match = extract_plus_page(free_args);

let res = page_plus_re
.captures(free_args)
.map(|i| i.get(1).unwrap().as_str().trim())
.filter(|i| i.contains(':'))
.map(|unparsed_num| {
let start_page_in_plus_option = match page_plus_match {
Some(ref s) => {
let unparsed_num = s.as_str();
let x: Vec<_> = unparsed_num.split(':').collect();
x[1].to_string()
.parse::<usize>()
x[0].parse::<usize>()
.map_err(|_e| PrError::EncounteredErrors {
msg: format!("invalid {} argument {}", "+", unparsed_num.quote()),
})
});
let end_page_in_plus_option = match res {
Some(res) => Some(res?),
})?
}
None => 1,
};

let end_page_in_plus_option = match page_plus_match {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the logic is similar to start_page_in_plus_option it might be possible to combine them.

Some(ref s) => {
let unparsed_num = s.as_str();
if unparsed_num.contains(':') {
let x: Vec<_> = unparsed_num.split(':').collect();
Some(
x[1].parse::<usize>()
.map_err(|_e| PrError::EncounteredErrors {
msg: format!("invalid {} argument {}", "+", unparsed_num.quote()),
})?,
)
} else {
None
}
}
None => None,
};

Expand Down Expand Up @@ -760,20 +802,16 @@ fn build_options(
}
};

let re_col = Regex::new(r"\s*-(\d+)\s*").unwrap();

let res = re_col.captures(free_args).map(|i| {
let unparsed_num = i.get(1).unwrap().as_str().trim();
unparsed_num
.parse::<usize>()
.map_err(|_e| PrError::EncounteredErrors {
msg: format!("invalid {} argument {}", "-", unparsed_num.quote()),
})
});
let start_column_option = match res {
Some(res) => Some(res?),
None => None,
};
// Extract column count from a "-digits" token in free_args.
let start_column_option = extract_minus_col(free_args)
.map(|unparsed_num| {
unparsed_num
.parse::<usize>()
.map_err(|_e| PrError::EncounteredErrors {
msg: format!("invalid {} argument {}", "-", unparsed_num.quote()),
})
})
.transpose()?;

// --column has more priority than -column

Expand Down
Loading