xref: /reactos/sdk/lib/ucrt/lowio/mktemp.cpp (revision 53d808d2)
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