104e0dc4aSTimo Kreuzer //
204e0dc4aSTimo Kreuzer // tempnam.cpp
304e0dc4aSTimo Kreuzer //
404e0dc4aSTimo Kreuzer // Copyright (c) Microsoft Corporation. All rights reserved.
504e0dc4aSTimo Kreuzer //
604e0dc4aSTimo Kreuzer // Defines _tempnam(), which generates temporary file names.
704e0dc4aSTimo Kreuzer //
804e0dc4aSTimo Kreuzer #include <corecrt_internal_stdio.h>
904e0dc4aSTimo Kreuzer
1004e0dc4aSTimo Kreuzer
1104e0dc4aSTimo Kreuzer
1204e0dc4aSTimo Kreuzer // Strips quotes from a string. If the source string contains no quotes, it is
1304e0dc4aSTimo Kreuzer // returned. If the source string contains quotes, a new string is allocated
1404e0dc4aSTimo Kreuzer // and the string is copied into it, character by character, skipping "'s. If
1504e0dc4aSTimo Kreuzer // a non-null pointer is returned, the caller is responsible for freeing it.
1604e0dc4aSTimo Kreuzer template <typename Character>
strip_quotes(Character const * const source)1704e0dc4aSTimo Kreuzer static Character const* __cdecl strip_quotes(Character const* const source) throw()
1804e0dc4aSTimo Kreuzer {
1904e0dc4aSTimo Kreuzer // Count the number of quotation marks in the string, and compute the length
2004e0dc4aSTimo Kreuzer // of the string, in case we need to allocate a new string:
2104e0dc4aSTimo Kreuzer size_t quote_count = 0;
2204e0dc4aSTimo Kreuzer size_t source_length = 0;
2304e0dc4aSTimo Kreuzer for (Character const* it = source; *it; ++it)
2404e0dc4aSTimo Kreuzer {
2504e0dc4aSTimo Kreuzer if (*it == '\"')
2604e0dc4aSTimo Kreuzer ++quote_count;
2704e0dc4aSTimo Kreuzer
2804e0dc4aSTimo Kreuzer ++source_length;
2904e0dc4aSTimo Kreuzer }
3004e0dc4aSTimo Kreuzer
3104e0dc4aSTimo Kreuzer // No quotes? No problem!
3204e0dc4aSTimo Kreuzer if (quote_count == 0)
3304e0dc4aSTimo Kreuzer return nullptr;
3404e0dc4aSTimo Kreuzer
3504e0dc4aSTimo Kreuzer size_t const destination_length = source_length - quote_count + 1;
3604e0dc4aSTimo Kreuzer __crt_unique_heap_ptr<Character> destination(_calloc_crt_t(Character, destination_length));
3704e0dc4aSTimo Kreuzer if (destination.get() == nullptr)
3804e0dc4aSTimo Kreuzer return nullptr;
3904e0dc4aSTimo Kreuzer
4004e0dc4aSTimo Kreuzer // Copy the string, stripping quotation marks:
4104e0dc4aSTimo Kreuzer Character* destination_it = destination.get();
4204e0dc4aSTimo Kreuzer for (Character const* source_it = source; *source_it; ++source_it)
4304e0dc4aSTimo Kreuzer {
4404e0dc4aSTimo Kreuzer if (*source_it == '\"')
4504e0dc4aSTimo Kreuzer continue;
4604e0dc4aSTimo Kreuzer
4704e0dc4aSTimo Kreuzer *destination_it++ = *source_it;
4804e0dc4aSTimo Kreuzer }
4904e0dc4aSTimo Kreuzer
5004e0dc4aSTimo Kreuzer *destination_it = '\0';
5104e0dc4aSTimo Kreuzer return destination.detach();
5204e0dc4aSTimo Kreuzer }
5304e0dc4aSTimo Kreuzer
5404e0dc4aSTimo Kreuzer // Gets the path to the %TMP% directory
5504e0dc4aSTimo Kreuzer template <typename Character>
get_tmp_directory()5604e0dc4aSTimo Kreuzer static Character const* __cdecl get_tmp_directory() throw()
5704e0dc4aSTimo Kreuzer {
5804e0dc4aSTimo Kreuzer typedef __acrt_stdio_char_traits<Character> stdio_traits;
5904e0dc4aSTimo Kreuzer
6004e0dc4aSTimo Kreuzer static Character const tmp_name[] = { 'T', 'M', 'P', '\0' };
6104e0dc4aSTimo Kreuzer
6204e0dc4aSTimo Kreuzer Character* tmp_value = nullptr;
6304e0dc4aSTimo Kreuzer if (_ERRCHECK_EINVAL(stdio_traits::tdupenv_s_crt(&tmp_value, nullptr, tmp_name)) != 0)
6404e0dc4aSTimo Kreuzer return nullptr;
6504e0dc4aSTimo Kreuzer
6604e0dc4aSTimo Kreuzer return tmp_value;
6704e0dc4aSTimo Kreuzer }
6804e0dc4aSTimo Kreuzer
6904e0dc4aSTimo Kreuzer
7004e0dc4aSTimo Kreuzer
7104e0dc4aSTimo Kreuzer // Gets the directory relative to which the temporary name should be formed.
7204e0dc4aSTimo Kreuzer // The directory to be used is returned via the 'result' out parameter. The
7304e0dc4aSTimo Kreuzer // '*result' pointer is never null on return. If '*result' needs to be freed
7404e0dc4aSTimo Kreuzer // by the caller, the function also returns the pointer; if null is returned,
7504e0dc4aSTimo Kreuzer // no caller cleanup is required.
7604e0dc4aSTimo Kreuzer template <typename Character>
get_directory(Character const * const alternative,Character const ** const result)7704e0dc4aSTimo Kreuzer static Character const* __cdecl get_directory(
7804e0dc4aSTimo Kreuzer Character const* const alternative,
7904e0dc4aSTimo Kreuzer Character const** const result
8004e0dc4aSTimo Kreuzer ) throw()
8104e0dc4aSTimo Kreuzer {
8204e0dc4aSTimo Kreuzer typedef __acrt_stdio_char_traits<Character> stdio_traits;
8304e0dc4aSTimo Kreuzer
8404e0dc4aSTimo Kreuzer __crt_unique_heap_ptr<Character const> tmp(get_tmp_directory<Character>());
8504e0dc4aSTimo Kreuzer if (tmp.get() != nullptr)
8604e0dc4aSTimo Kreuzer {
8704e0dc4aSTimo Kreuzer if (stdio_traits::taccess_s(tmp.get(), 0) == 0)
8804e0dc4aSTimo Kreuzer return *result = tmp.detach();
8904e0dc4aSTimo Kreuzer
9004e0dc4aSTimo Kreuzer // Otherwise, try stripping quotes out of the TMP path and check again:
9104e0dc4aSTimo Kreuzer __crt_unique_heap_ptr<Character const> unquoted_tmp(strip_quotes(tmp.get()));
9204e0dc4aSTimo Kreuzer if (unquoted_tmp.get() != nullptr && stdio_traits::taccess_s(unquoted_tmp.get(), 0) == 0)
9304e0dc4aSTimo Kreuzer return *result = unquoted_tmp.detach();
9404e0dc4aSTimo Kreuzer }
9504e0dc4aSTimo Kreuzer
9604e0dc4aSTimo Kreuzer // Otherwise, the TMP path is not usable; use the alternative path if one
9704e0dc4aSTimo Kreuzer // was provided and is accessible:
9804e0dc4aSTimo Kreuzer if (alternative != nullptr && stdio_traits::taccess_s(alternative, 0) == 0)
9904e0dc4aSTimo Kreuzer return (*result = alternative), nullptr;
10004e0dc4aSTimo Kreuzer
10104e0dc4aSTimo Kreuzer // Otherwise, fall back to \ or .:
10204e0dc4aSTimo Kreuzer static Character const root_fallback[] = { '\\', '\0' };
10304e0dc4aSTimo Kreuzer static Character const cwd_fallback [] = { '.', '\0' };
10404e0dc4aSTimo Kreuzer
10504e0dc4aSTimo Kreuzer if (stdio_traits::taccess_s(root_fallback, 0) == 0)
10604e0dc4aSTimo Kreuzer return (*result = root_fallback), nullptr;
10704e0dc4aSTimo Kreuzer
10804e0dc4aSTimo Kreuzer return (*result = cwd_fallback), nullptr;
10904e0dc4aSTimo Kreuzer }
11004e0dc4aSTimo Kreuzer
11104e0dc4aSTimo Kreuzer
11204e0dc4aSTimo Kreuzer
11304e0dc4aSTimo Kreuzer // The path_buffer is a pointer to the beginning of the buffer into which we
11404e0dc4aSTimo Kreuzer // are formatting the temporary path. The suffix_pointer is a pointer into
11504e0dc4aSTimo Kreuzer // that same buffer, to the position where the unique identifier is to be
11604e0dc4aSTimo Kreuzer // written. The suffix_count is the number of characters that can be written
11704e0dc4aSTimo Kreuzer // to the suffix_pointer. The prefix_length is the length of the prefix that
11804e0dc4aSTimo Kreuzer // appears before the suffix in the path_buffer already.
11904e0dc4aSTimo Kreuzer template <typename Character>
compute_name(Character const * const path_buffer,Character * const suffix_pointer,size_t const suffix_count,size_t const prefix_length)12004e0dc4aSTimo Kreuzer static bool __cdecl compute_name(
12104e0dc4aSTimo Kreuzer Character const* const path_buffer,
12204e0dc4aSTimo Kreuzer Character* const suffix_pointer,
12304e0dc4aSTimo Kreuzer size_t const suffix_count,
12404e0dc4aSTimo Kreuzer size_t const prefix_length
12504e0dc4aSTimo Kreuzer ) throw()
12604e0dc4aSTimo Kreuzer {
12704e0dc4aSTimo Kreuzer typedef __acrt_stdio_char_traits<Character> stdio_traits;
12804e0dc4aSTimo Kreuzer
12904e0dc4aSTimo Kreuzer // Re-initialize _tempoff if necessary. If we don't re-init _tempoff, we
13004e0dc4aSTimo Kreuzer // can get into an infinate loop (e.g., (a) _tempoff is a big number on
13104e0dc4aSTimo Kreuzer // entry, (b) prefix is a long string (e.g., 8 chars) and all tempfiles
13204e0dc4aSTimo Kreuzer // with that prefix exist, (c) _tempoff will never equal first and we'll
13304e0dc4aSTimo Kreuzer // loop forever).
13404e0dc4aSTimo Kreuzer
13504e0dc4aSTimo Kreuzer // [NOTE: To avoid a conflict that causes the same bug as that discussed
13604e0dc4aSTimo Kreuzer // above, _tempnam() uses _tempoff; tmpnam() uses _tmpoff]
13704e0dc4aSTimo Kreuzer
13804e0dc4aSTimo Kreuzer bool return_value = false;
13904e0dc4aSTimo Kreuzer
14004e0dc4aSTimo Kreuzer __acrt_lock(__acrt_tempnam_lock);
14104e0dc4aSTimo Kreuzer __try
14204e0dc4aSTimo Kreuzer {
14304e0dc4aSTimo Kreuzer if (_old_pfxlen < prefix_length)
14404e0dc4aSTimo Kreuzer _tempoff = 1;
14504e0dc4aSTimo Kreuzer
14604e0dc4aSTimo Kreuzer _old_pfxlen = static_cast<unsigned int>(prefix_length);
14704e0dc4aSTimo Kreuzer
14804e0dc4aSTimo Kreuzer unsigned const first = _tempoff;
14904e0dc4aSTimo Kreuzer
15004e0dc4aSTimo Kreuzer errno_t const saved_errno = errno;
15104e0dc4aSTimo Kreuzer do
15204e0dc4aSTimo Kreuzer {
15304e0dc4aSTimo Kreuzer ++_tempoff;
15404e0dc4aSTimo Kreuzer if (_tempoff - first > _TMP_MAX_S)
15504e0dc4aSTimo Kreuzer {
15604e0dc4aSTimo Kreuzer errno = saved_errno;
15704e0dc4aSTimo Kreuzer __leave;
15804e0dc4aSTimo Kreuzer }
15904e0dc4aSTimo Kreuzer
16004e0dc4aSTimo Kreuzer // The maximum length string returned by the conversion is ten
16104e0dc4aSTimo Kreuzer // characters, assuming a 32-bit unsigned integer, so there is
16204e0dc4aSTimo Kreuzer // sufficient room in the result buffer for it.
16304e0dc4aSTimo Kreuzer _ERRCHECK(stdio_traits::ultot_s(_tempoff, suffix_pointer, suffix_count, 10));
16404e0dc4aSTimo Kreuzer errno = 0;
16504e0dc4aSTimo Kreuzer }
16604e0dc4aSTimo Kreuzer while (stdio_traits::taccess_s(path_buffer, 0) == 0 || errno == EACCES);
16704e0dc4aSTimo Kreuzer
16804e0dc4aSTimo Kreuzer errno = saved_errno;
16904e0dc4aSTimo Kreuzer return_value = true;
17004e0dc4aSTimo Kreuzer }
17104e0dc4aSTimo Kreuzer __finally
17204e0dc4aSTimo Kreuzer {
17304e0dc4aSTimo Kreuzer __acrt_unlock(__acrt_tempnam_lock);
17404e0dc4aSTimo Kreuzer }
175*e98e9000STimo Kreuzer __endtry
17604e0dc4aSTimo Kreuzer
17704e0dc4aSTimo Kreuzer return return_value;
17804e0dc4aSTimo Kreuzer }
17904e0dc4aSTimo Kreuzer
18004e0dc4aSTimo Kreuzer
18104e0dc4aSTimo Kreuzer
18204e0dc4aSTimo Kreuzer // Generates a unique file name within the temporary directory. If the TMP
18304e0dc4aSTimo Kreuzer // environment variable is defined and is accessible, that directory is used.
18404e0dc4aSTimo Kreuzer // Otherwise, the given 'alternative' directory is used, if that is non-null and
18504e0dc4aSTimo Kreuzer // the path is accessible. Otherwise, the root of the drive is used if it is
18604e0dc4aSTimo Kreuzer // accessible; otherwise, the local directory is used.
18704e0dc4aSTimo Kreuzer //
18804e0dc4aSTimo Kreuzer // Returns a pointer to the resulting file name on success; returns nullptr on
18904e0dc4aSTimo Kreuzer // failure. If a non-null pointer is returned, the caller is responsible for
19004e0dc4aSTimo Kreuzer // freeing it by calling 'free()'.
19104e0dc4aSTimo Kreuzer template <typename Character>
19204e0dc4aSTimo Kreuzer _Success_(return != 0)
common_tempnam(Character const * const alternative,Character const * const prefix,int const block_use,char const * const file_name,int const line_number)19304e0dc4aSTimo Kreuzer static Character* __cdecl common_tempnam(
19404e0dc4aSTimo Kreuzer Character const* const alternative,
19504e0dc4aSTimo Kreuzer Character const* const prefix,
19604e0dc4aSTimo Kreuzer int const block_use,
19704e0dc4aSTimo Kreuzer char const* const file_name,
19804e0dc4aSTimo Kreuzer int const line_number
19904e0dc4aSTimo Kreuzer ) throw()
20004e0dc4aSTimo Kreuzer {
20104e0dc4aSTimo Kreuzer // These are referenced only in the Debug CRT build
20204e0dc4aSTimo Kreuzer UNREFERENCED_PARAMETER(block_use);
20304e0dc4aSTimo Kreuzer UNREFERENCED_PARAMETER(file_name);
20404e0dc4aSTimo Kreuzer UNREFERENCED_PARAMETER(line_number);
20504e0dc4aSTimo Kreuzer
20604e0dc4aSTimo Kreuzer typedef __acrt_stdio_char_traits<Character> stdio_traits;
20704e0dc4aSTimo Kreuzer
20804e0dc4aSTimo Kreuzer Character const* directory = nullptr;
20904e0dc4aSTimo Kreuzer __crt_unique_heap_ptr<Character const> const directory_cleanup(get_directory(alternative, &directory));
21004e0dc4aSTimo Kreuzer
21104e0dc4aSTimo Kreuzer unsigned const prefix_length = prefix != nullptr
21204e0dc4aSTimo Kreuzer ? static_cast<unsigned>(stdio_traits::tcslen(prefix))
21304e0dc4aSTimo Kreuzer : 0;
21404e0dc4aSTimo Kreuzer
21504e0dc4aSTimo Kreuzer // The 12 allows for a backslash, a ten character temporary string, and a
21604e0dc4aSTimo Kreuzer // null terminator.
21704e0dc4aSTimo Kreuzer unsigned const buffer_size = static_cast<unsigned>(stdio_traits::tcslen(directory)) + prefix_length + 12;
21804e0dc4aSTimo Kreuzer
21904e0dc4aSTimo Kreuzer __crt_unique_heap_ptr<Character, __crt_public_free_policy> result(
22004e0dc4aSTimo Kreuzer static_cast<Character*>(_calloc_dbg(
22104e0dc4aSTimo Kreuzer buffer_size,
22204e0dc4aSTimo Kreuzer sizeof(Character),
22304e0dc4aSTimo Kreuzer block_use,
22404e0dc4aSTimo Kreuzer file_name,
22504e0dc4aSTimo Kreuzer line_number)));
22604e0dc4aSTimo Kreuzer
22704e0dc4aSTimo Kreuzer if (!result)
22804e0dc4aSTimo Kreuzer return nullptr;
22904e0dc4aSTimo Kreuzer
23004e0dc4aSTimo Kreuzer *result.get() = 0;
23104e0dc4aSTimo Kreuzer _ERRCHECK(stdio_traits::tcscat_s(result.get(), buffer_size, directory));
23204e0dc4aSTimo Kreuzer
23304e0dc4aSTimo Kreuzer if (__crt_stdio_path_requires_backslash(directory))
23404e0dc4aSTimo Kreuzer {
23504e0dc4aSTimo Kreuzer static Character const backslash[] = { '\\', '\0' };
23604e0dc4aSTimo Kreuzer _ERRCHECK(stdio_traits::tcscat_s(result.get(), buffer_size, backslash));
23704e0dc4aSTimo Kreuzer }
23804e0dc4aSTimo Kreuzer
23904e0dc4aSTimo Kreuzer if (prefix != nullptr)
24004e0dc4aSTimo Kreuzer {
24104e0dc4aSTimo Kreuzer _ERRCHECK(stdio_traits::tcscat_s(result.get(), buffer_size, prefix));
24204e0dc4aSTimo Kreuzer }
24304e0dc4aSTimo Kreuzer
24404e0dc4aSTimo Kreuzer Character* const ptr = result.get() + stdio_traits::tcslen(result.get());
24504e0dc4aSTimo Kreuzer size_t const ptr_size = buffer_size - (ptr - result.get());
24604e0dc4aSTimo Kreuzer
24704e0dc4aSTimo Kreuzer if (!compute_name(result.get(), ptr, ptr_size, prefix_length))
24804e0dc4aSTimo Kreuzer return nullptr;
24904e0dc4aSTimo Kreuzer
25004e0dc4aSTimo Kreuzer return result.detach();
25104e0dc4aSTimo Kreuzer }
25204e0dc4aSTimo Kreuzer
25304e0dc4aSTimo Kreuzer
25404e0dc4aSTimo Kreuzer
_tempnam(char const * const alternative,char const * const prefix)25504e0dc4aSTimo Kreuzer extern "C" char* __cdecl _tempnam(
25604e0dc4aSTimo Kreuzer char const* const alternative,
25704e0dc4aSTimo Kreuzer char const* const prefix
25804e0dc4aSTimo Kreuzer )
25904e0dc4aSTimo Kreuzer {
26004e0dc4aSTimo Kreuzer return common_tempnam(alternative, prefix, _NORMAL_BLOCK, nullptr, 0);
26104e0dc4aSTimo Kreuzer }
26204e0dc4aSTimo Kreuzer
_wtempnam(wchar_t const * const alternative,wchar_t const * const prefix)26304e0dc4aSTimo Kreuzer extern "C" wchar_t* __cdecl _wtempnam(
26404e0dc4aSTimo Kreuzer wchar_t const* const alternative,
26504e0dc4aSTimo Kreuzer wchar_t const* const prefix
26604e0dc4aSTimo Kreuzer )
26704e0dc4aSTimo Kreuzer {
26804e0dc4aSTimo Kreuzer return common_tempnam(alternative, prefix, _NORMAL_BLOCK, nullptr, 0);
26904e0dc4aSTimo Kreuzer }
27004e0dc4aSTimo Kreuzer
27104e0dc4aSTimo Kreuzer #ifdef _DEBUG
27204e0dc4aSTimo Kreuzer
_tempnam_dbg(char const * const alternative,char const * const prefix,int const block_use,char const * const file_name,int const line_number)27304e0dc4aSTimo Kreuzer extern "C" char* __cdecl _tempnam_dbg(
27404e0dc4aSTimo Kreuzer char const* const alternative,
27504e0dc4aSTimo Kreuzer char const* const prefix,
27604e0dc4aSTimo Kreuzer int const block_use,
27704e0dc4aSTimo Kreuzer char const* const file_name,
27804e0dc4aSTimo Kreuzer int const line_number
27904e0dc4aSTimo Kreuzer )
28004e0dc4aSTimo Kreuzer {
28104e0dc4aSTimo Kreuzer return common_tempnam(alternative, prefix, block_use, file_name, line_number);
28204e0dc4aSTimo Kreuzer }
28304e0dc4aSTimo Kreuzer
_wtempnam_dbg(wchar_t const * const alternative,wchar_t const * const prefix,int const block_use,char const * const file_name,int const line_number)28404e0dc4aSTimo Kreuzer extern "C" wchar_t* __cdecl _wtempnam_dbg(
28504e0dc4aSTimo Kreuzer wchar_t const* const alternative,
28604e0dc4aSTimo Kreuzer wchar_t const* const prefix,
28704e0dc4aSTimo Kreuzer int const block_use,
28804e0dc4aSTimo Kreuzer char const* const file_name,
28904e0dc4aSTimo Kreuzer int const line_number
29004e0dc4aSTimo Kreuzer )
29104e0dc4aSTimo Kreuzer {
29204e0dc4aSTimo Kreuzer return common_tempnam(alternative, prefix, block_use, file_name, line_number);
29304e0dc4aSTimo Kreuzer }
29404e0dc4aSTimo Kreuzer
29504e0dc4aSTimo Kreuzer #endif
296