From 61345cbdc97d4820dd564bcb136f1526c4b54e16 Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Sat, 21 May 2022 23:31:12 -0400 Subject: [PATCH] mktemp: respect TMPDIR environment variable Change `mktemp` so that it respects the value of the `TMPDIR` environment variable if no directory is otherwise specified in its arguments. For example, before this commit $ TMPDIR=. mktemp /tmp/tmp.WDJ66MaS1T After this commit, $ TMPDIR=. mktemp ./tmp.h96VZBhv8P This matches the behavior of GNU `mktemp`. --- src/uu/mktemp/src/mktemp.rs | 21 ++++++++--- tests/by-util/test_mktemp.rs | 72 ++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/src/uu/mktemp/src/mktemp.rs b/src/uu/mktemp/src/mktemp.rs index 1131e0f01c0..dfa7fd10303 100644 --- a/src/uu/mktemp/src/mktemp.rs +++ b/src/uu/mktemp/src/mktemp.rs @@ -170,12 +170,21 @@ impl Options { let template = matches.value_of(OPT_TMPDIR).unwrap().to_string(); (tmpdir, template) } else { - let tmpdir = matches.value_of(OPT_TMPDIR).map(String::from); - let template = matches - .value_of(ARG_TEMPLATE) - .unwrap_or(DEFAULT_TEMPLATE) - .to_string(); - (tmpdir, template) + // If no template argument is given, `--tmpdir` is implied. + match matches.value_of(ARG_TEMPLATE) { + None => { + let tmpdir = match matches.value_of(OPT_TMPDIR) { + None => Some(env::temp_dir().display().to_string()), + Some(tmpdir) => Some(tmpdir.to_string()), + }; + let template = DEFAULT_TEMPLATE; + (tmpdir, template.to_string()) + } + Some(template) => { + let tmpdir = matches.value_of(OPT_TMPDIR).map(String::from); + (tmpdir, template.to_string()) + } + } }; Self { directory: matches.contains_id(OPT_DIRECTORY), diff --git a/tests/by-util/test_mktemp.rs b/tests/by-util/test_mktemp.rs index 9e4a2742cd8..8b58672a2d6 100644 --- a/tests/by-util/test_mktemp.rs +++ b/tests/by-util/test_mktemp.rs @@ -5,6 +5,8 @@ use crate::common::util::*; use uucore::display::Quotable; use std::path::PathBuf; +#[cfg(not(windows))] +use std::path::MAIN_SEPARATOR; use tempfile::tempdir; #[cfg(unix)] @@ -39,6 +41,22 @@ macro_rules! assert_matches_template { }}; } +/// Like [`assert_matches_template`] but for the suffix of a string. +#[cfg(windows)] +macro_rules! assert_suffix_matches_template { + ($template:expr, $s:expr) => {{ + let n = ($s).len(); + let m = ($template).len(); + let suffix = &$s[n - m..n]; + assert!( + matches_template($template, suffix), + "\"{}\" does not end with \"{}\"", + $template, + suffix + ); + }}; +} + #[test] fn test_mktemp_mktemp() { let scene = TestScenario::new(util_name!()); @@ -663,3 +681,57 @@ fn test_mktemp_with_posixly_correct() { .args(&["--suffix=b", "aXXXX"]) .succeeds(); } + +/// Test that files are created relative to `TMPDIR` environment variable. +#[test] +fn test_tmpdir_env_var() { + // `TMPDIR=. mktemp` + let (at, mut ucmd) = at_and_ucmd!(); + let result = ucmd.env(TMPDIR, ".").succeeds(); + let filename = result.no_stderr().stdout_str().trim_end(); + #[cfg(not(windows))] + { + let template = format!(".{}tmp.XXXXXXXXXX", MAIN_SEPARATOR); + assert_matches_template!(&template, filename); + } + // On Windows, `env::temp_dir()` seems to give an absolute path + // regardless of the value of `TMPDIR`; see + // * https://github.com/uutils/coreutils/pull/3552#issuecomment-1211804981 + // * https://doc.rust-lang.org/std/env/fn.temp_dir.html + // * https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppath2w + #[cfg(windows)] + assert_suffix_matches_template!("tmp.XXXXXXXXXX", filename); + assert!(at.file_exists(filename)); + + // FIXME This is not working because --tmpdir is configured to + // require a value. + // + // // `TMPDIR=. mktemp --tmpdir` + // let (at, mut ucmd) = at_and_ucmd!(); + // let result = ucmd.env(TMPDIR, ".").arg("--tmpdir").succeeds(); + // let filename = result.no_stderr().stdout_str().trim_end(); + // let template = format!(".{}tmp.XXXXXXXXXX", MAIN_SEPARATOR); + // assert_matches_template!(&template, filename); + // assert!(at.file_exists(filename)); + + // `TMPDIR=. mktemp --tmpdir XXX` + let (at, mut ucmd) = at_and_ucmd!(); + let result = ucmd.env(TMPDIR, ".").args(&["--tmpdir", "XXX"]).succeeds(); + let filename = result.no_stderr().stdout_str().trim_end(); + #[cfg(not(windows))] + { + let template = format!(".{}XXX", MAIN_SEPARATOR); + assert_matches_template!(&template, filename); + } + #[cfg(windows)] + assert_suffix_matches_template!("XXX", filename); + assert!(at.file_exists(filename)); + + // `TMPDIR=. mktemp XXX` - in this case `TMPDIR` is ignored. + let (at, mut ucmd) = at_and_ucmd!(); + let result = ucmd.env(TMPDIR, ".").arg("XXX").succeeds(); + let filename = result.no_stderr().stdout_str().trim_end(); + let template = "XXX"; + assert_matches_template!(template, filename); + assert!(at.file_exists(filename)); +}