1 // 2 // mktemp.cpp 3 // 4 // Copyright (c) Microsoft Corporation. All rights reserved. 5 // 6 // Defines _tmktemp() and _tmktemp_s(), which create unique file names. 7 // 8 #include <corecrt_internal_lowio.h> 9 #include <corecrt_internal_securecrt.h> 10 #include <mbctype.h> 11 #include <process.h> 12 #include <stddef.h> 13 #include <stdio.h> 14 15 16 17 // These functions test whether we should continue scanning for X's. 18 static bool common_mktemp_s_continue(char const* const string, char const* const current) throw() 19 { 20 return !_ismbstrail( 21 reinterpret_cast<unsigned char const*>(string), 22 reinterpret_cast<unsigned char const*>(current)); 23 } 24 25 static bool common_mktemp_s_continue(wchar_t const* const, wchar_t const* const) throw() 26 { 27 return true; 28 } 29 30 31 32 // Creates a unique file name given a template string of the form "fnamXXXXXX". 33 // The "XXXXXX" sequence is replaced by a letter and the five-digit thread 34 // identifier. 35 // 36 // The template string is modified in-place. Returns 0 on success; returns an 37 // errno error code on failure. 38 template <typename Character> 39 _Success_(return == 0) 40 static errno_t __cdecl common_mktemp_s( 41 _Inout_updates_z_(buffer_size_in_chars) Character* const template_string, 42 size_t const buffer_size_in_chars 43 ) throw() 44 { 45 typedef __crt_char_traits<Character> traits; 46 47 _VALIDATE_RETURN_ERRCODE(template_string != nullptr && buffer_size_in_chars > 0, EINVAL); 48 49 size_t const template_string_length = traits::tcsnlen(template_string, buffer_size_in_chars); 50 if (template_string_length >= buffer_size_in_chars) 51 { 52 _RESET_STRING(template_string, buffer_size_in_chars); 53 _RETURN_DEST_NOT_NULL_TERMINATED(template_string, buffer_size_in_chars); 54 } 55 _FILL_STRING(template_string, buffer_size_in_chars, template_string_length + 1); 56 57 if(template_string_length < 6 || buffer_size_in_chars <= template_string_length) 58 { 59 _RESET_STRING(template_string, buffer_size_in_chars); 60 _VALIDATE_RETURN_ERRCODE(("Incorrect Input for mktemp", 0), EINVAL); 61 } 62 63 // The Win32 thread identifier is unique across all threads in all processes. 64 // Note, however, that unlike *NIX process identifiers, which are not reused 65 // until all values up to 32K have been used, Win32 thread identifiers are 66 // frequenty reused and usually have small numbers. 67 unsigned number = GetCurrentThreadId(); 68 69 // 'string' points to the null-terminator: 70 Character* string = template_string + template_string_length; 71 72 size_t template_length = 0; 73 74 // Replace the last five 'X' characters with the number: 75 while (--string >= template_string 76 && common_mktemp_s_continue(template_string, string) 77 && *string == 'X' 78 && template_length < 5) 79 { 80 ++template_length; 81 *string = static_cast<Character>((number % 10) + '0'); 82 number /= 10; 83 } 84 85 // Too few X's? 86 if (*string != 'X' || template_length < 5) 87 { 88 _RESET_STRING(template_string, buffer_size_in_chars); 89 _VALIDATE_RETURN_ERRCODE(("Incorrect Input for mktemp", 0), EINVAL); 90 } 91 92 // Finally, add the letter: 93 Character letter = 'a'; 94 95 *string = letter++; 96 97 errno_t const saved_errno = errno; 98 errno = 0; 99 100 // Test each letter, from a-z, until we find a name that is not yet used: 101 while (traits::taccess_s(template_string, 0) == 0) 102 { 103 if (letter == 'z' + 1) 104 { 105 _RESET_STRING(template_string, buffer_size_in_chars); 106 errno = EEXIST; 107 return errno; 108 } 109 110 *string = letter++; 111 errno = 0; 112 } 113 114 // Restore the old value of errno and return success: 115 errno = saved_errno; 116 return 0; 117 } 118 119 extern "C" errno_t __cdecl _mktemp_s( 120 char* const template_string, 121 size_t const buffer_size_in_chars 122 ) 123 { 124 return common_mktemp_s(template_string, buffer_size_in_chars); 125 } 126 127 extern "C" errno_t __cdecl _wmktemp_s( 128 wchar_t* const template_string, 129 size_t const buffer_size_in_chars 130 ) 131 { 132 return common_mktemp_s(template_string, buffer_size_in_chars); 133 } 134 135 136 137 // Creates a unique file name given a template string of the form "fnamXXXXXX". 138 // The "XXXXXX" sequence is replaced by a letter and the five-digit thread 139 // identifier. The template string is modified in-place. 140 // 141 // On success, returns the pointer to the modified template string. On failure, 142 // nullptr is returned (e.g. if the template string is malformed or there are no 143 // more unique names). 144 template <typename Character> 145 static Character* __cdecl common_mktemp( 146 Character* const template_string 147 ) throw() 148 { 149 typedef __crt_char_traits<Character> traits; 150 151 _VALIDATE_RETURN(template_string != nullptr, EINVAL, nullptr); 152 153 errno_t const result = common_mktemp_s( 154 template_string, 155 static_cast<size_t>(traits::tcslen(template_string) + 1)); 156 157 return result == 0 ? template_string : nullptr; 158 } 159 160 extern "C" char* __cdecl _mktemp(char* const template_string) 161 { 162 return common_mktemp(template_string); 163 } 164 165 extern "C" wchar_t* __cdecl _wmktemp(wchar_t* const template_string) 166 { 167 return common_mktemp(template_string); 168 } 169