1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

mktemp: only replace last contiguous block of Xs

Fix a bug in which `mktemp` would replace everything in the template
argument from the first 'X' to the last 'X' with random bytes, instead
of just replacing the last contiguous block of 'X's.

Before this commit,

    $ mktemp XXX_XXX
    2meCpfM

After this commit,

    $ mktemp XXX_XXX
    XXX_Rp5

This fixes test cases `suffix2f` and `suffix2d` in
`tests/misc/mktemp.pl` in the GNU coreutils test suite.
This commit is contained in:
Jeffrey Finkelstein 2022-06-01 09:35:31 -04:00
parent 08fed8fb7b
commit cff7833bf6
2 changed files with 59 additions and 3 deletions

View file

@ -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<Self, MkTempError> {
// 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);
}
}

View file

@ -572,3 +572,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));
}