diff --git a/src/uu/mktemp/src/mktemp.rs b/src/uu/mktemp/src/mktemp.rs index dd1f57b8b44..78e36874ea6 100644 --- a/src/uu/mktemp/src/mktemp.rs +++ b/src/uu/mktemp/src/mktemp.rs @@ -6,7 +6,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (paths) GPGHome +// spell-checker:ignore (paths) GPGHome findxs use clap::{crate_version, Arg, ArgMatches, Command}; use uucore::display::{println_verbatim, Quotable}; @@ -205,12 +205,29 @@ struct Params { suffix: String, } +/// Find the start and end indices of the last contiguous block of Xs. +/// +/// If no contiguous block of at least three Xs could be found, this +/// function returns `None`. +/// +/// # Examples +/// +/// ```rust,ignore +/// assert_eq!(find_last_contiguous_block_of_xs("XXX_XXX"), Some((4, 7))); +/// assert_eq!(find_last_contiguous_block_of_xs("aXbXcX"), None); +/// ``` +fn find_last_contiguous_block_of_xs(s: &str) -> Option<(usize, usize)> { + let j = s.rfind("XXX")? + 3; + let i = s[..j].rfind(|c| c != 'X').map_or(0, |i| i + 1); + Some((i, j)) +} + impl Params { fn from(options: Options) -> Result { // Get the start and end indices of the randomized part of the template. // // For example, if the template is "abcXXXXyz", then `i` is 3 and `j` is 7. - let i = match options.template.find("XXX") { + let (i, j) = match find_last_contiguous_block_of_xs(&options.template) { None => { let s = match options.suffix { None => options.template, @@ -218,9 +235,8 @@ impl Params { }; return Err(MkTempError::TooFewXs(s)); } - Some(i) => i, + Some(indices) => indices, }; - let j = options.template.rfind("XXX").unwrap() + 3; // Combine the directory given as an option and the prefix of the template. // @@ -450,3 +466,21 @@ fn exec(dir: &str, prefix: &str, rand: usize, suffix: &str, make_dir: bool) -> U println_verbatim(path).map_err_context(|| "failed to print directory name".to_owned()) } + +#[cfg(test)] +mod tests { + use crate::find_last_contiguous_block_of_xs as findxs; + + #[test] + fn test_find_last_contiguous_block_of_xs() { + assert_eq!(findxs("XXX"), Some((0, 3))); + assert_eq!(findxs("XXX_XXX"), Some((4, 7))); + assert_eq!(findxs("XXX_XXX_XXX"), Some((8, 11))); + assert_eq!(findxs("aaXXXbb"), Some((2, 5))); + assert_eq!(findxs(""), None); + assert_eq!(findxs("X"), None); + assert_eq!(findxs("XX"), None); + assert_eq!(findxs("aXbXcX"), None); + assert_eq!(findxs("aXXbXXcXX"), None); + } +} diff --git a/tests/by-util/test_mktemp.rs b/tests/by-util/test_mktemp.rs index 2da42c3e9a5..df630e715a4 100644 --- a/tests/by-util/test_mktemp.rs +++ b/tests/by-util/test_mktemp.rs @@ -597,3 +597,25 @@ fn test_too_few_xs_suffix_directory() { fn test_too_many_arguments() { new_ucmd!().args(&["-q", "a", "b"]).fails().code_is(1); } + +#[test] +fn test_two_contiguous_wildcard_blocks() { + let (at, mut ucmd) = at_and_ucmd!(); + let template = "XXX_XXX"; + let result = ucmd.arg(template).succeeds(); + let filename = result.no_stderr().stdout_str().trim_end(); + assert_eq!(&filename[..4], "XXX_"); + assert_matches_template!(template, filename); + assert!(at.file_exists(filename)); +} + +#[test] +fn test_three_contiguous_wildcard_blocks() { + let (at, mut ucmd) = at_and_ucmd!(); + let template = "XXX_XXX_XXX"; + let result = ucmd.arg(template).succeeds(); + let filename = result.no_stderr().stdout_str().trim_end(); + assert_eq!(&filename[..8], "XXX_XXX_"); + assert_matches_template!(template, filename); + assert!(at.file_exists(filename)); +}