1 //
2 // getenv.cpp
3 //
4 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //
6 // Defines the getenv family of functions, which search the environment for a
7 // variable and return its value.
8 //
9 #include <corecrt_internal_traits.h>
10 #include <stdlib.h>
11 #include <string.h>
12
13
14
15 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
16 //
17 // getenv() and _wgetenv()
18 //
19 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
20 // These functions search the environment for a variable with the given name.
21 // If such a variable is found, a pointer to its value is returned. Otherwise,
22 // nullptr is returned. Note that if the environment is access and manipulated
23 // from multiple threads, this function cannot be safely used: the returned
24 // pointer may not be valid when the function returns.
25 template <typename Character>
common_getenv_nolock(Character const * const name)26 static Character* __cdecl common_getenv_nolock(Character const* const name) throw()
27 {
28 typedef __crt_char_traits<Character> traits;
29
30 Character** const environment = traits::get_or_create_environment_nolock();
31 if (environment == nullptr || name == nullptr)
32 return nullptr;
33
34 size_t const name_length = traits::tcslen(name);
35
36 for (Character** current = environment; *current; ++current)
37 {
38 if (traits::tcslen(*current) <= name_length)
39 continue;
40
41 if (*(*current + name_length) != '=')
42 continue;
43
44 if (traits::tcsnicoll(*current, name, name_length) != 0)
45 continue;
46
47 // Internal consistency check: The environment string should never use
48 // a bigger buffer than _MAX_ENV. See also the SetEnvironmentVariable
49 // SDK function.
50 _ASSERTE(traits::tcsnlen(*current + name_length + 1, _MAX_ENV) < _MAX_ENV);
51
52 return *current + name_length + 1;
53 }
54
55 return nullptr;
56 }
57
58
59
60 template <typename Character>
common_getenv(Character const * const name)61 static Character* __cdecl common_getenv(Character const* const name) throw()
62 {
63 typedef __crt_char_traits<Character> traits;
64
65 _VALIDATE_RETURN(name != nullptr, EINVAL, nullptr);
66 _VALIDATE_RETURN(traits::tcsnlen(name, _MAX_ENV) < _MAX_ENV, EINVAL, nullptr);
67
68 Character* result = 0;
69
70 __acrt_lock(__acrt_environment_lock);
71 __try
72 {
73 result = common_getenv_nolock(name);
74 }
75 __finally
76 {
77 __acrt_unlock(__acrt_environment_lock);
78 }
79 __endtry
80
81 return result;
82 }
83
getenv(char const * const name)84 extern "C" char* __cdecl getenv(char const* const name)
85 {
86 return common_getenv(name);
87 }
88
_wgetenv(wchar_t const * const name)89 extern "C" wchar_t* __cdecl _wgetenv(wchar_t const* const name)
90 {
91 return common_getenv(name);
92 }
93
94
95
96 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
97 //
98 // getenv_s() and _wgetenv_s()
99 //
100 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
101 // These functions search the environment for a variable with the given name.
102 // If such a variable is found, its value is copied into the provided buffer.
103 // The number of characters in the value (including the null terminator) is
104 // stored in '*required_count'. Returns 0 on success; returns ERANGE if the
105 // provided buffer is too small; otherwise returns an error code on failure.
106 template <typename Character>
107 _Success_(return == 0)
common_getenv_s_nolock(size_t * const required_count,_Out_writes_z_ (buffer_count)Character * const buffer,size_t const buffer_count,Character const * const name)108 static errno_t __cdecl common_getenv_s_nolock(
109 size_t* const required_count,
110 _Out_writes_z_(buffer_count) Character* const buffer,
111 size_t const buffer_count,
112 Character const* const name
113 ) throw()
114 {
115 typedef __crt_char_traits<Character> traits;
116
117 _VALIDATE_RETURN_ERRCODE(required_count != nullptr, EINVAL);
118 *required_count = 0;
119
120 _VALIDATE_RETURN_ERRCODE(
121 (buffer != nullptr && buffer_count > 0) ||
122 (buffer == nullptr && buffer_count == 0), EINVAL);
123
124 if (buffer)
125 buffer[0] = '\0';
126
127 Character const* const value = common_getenv_nolock(name);
128 if (!value)
129 return 0;
130
131 *required_count = traits::tcslen(value) + 1;
132 if (buffer_count == 0)
133 return 0;
134
135 // The buffer is too small; we return an error code and the caller can have
136 // the opportunity to try again with a larger buffer:
137 if (*required_count > buffer_count)
138 return ERANGE;
139
140 _ERRCHECK(traits::tcscpy_s(buffer, buffer_count, value));
141 return 0;
142 }
143
144 template <typename Character>
145 _Success_(return == 0)
common_getenv_s(size_t * const required_count,_Out_writes_z_ (buffer_count)Character * const buffer,size_t const buffer_count,Character const * const name)146 static errno_t __cdecl common_getenv_s(
147 size_t* const required_count,
148 _Out_writes_z_(buffer_count) Character* const buffer,
149 size_t const buffer_count,
150 Character const* const name
151 ) throw()
152 {
153 errno_t status = 0;
154
155 __acrt_lock(__acrt_environment_lock);
156 __try
157 {
158 status = common_getenv_s_nolock(required_count, buffer, buffer_count, name);
159 }
160 __finally
161 {
162 __acrt_unlock(__acrt_environment_lock);
163 }
164 __endtry
165
166 return status;
167 }
168
getenv_s(size_t * const required_count,char * const buffer,size_t const buffer_count,char const * const name)169 extern "C" errno_t __cdecl getenv_s(
170 size_t* const required_count,
171 char* const buffer,
172 size_t const buffer_count,
173 char const* const name
174 )
175 {
176 return common_getenv_s(required_count, buffer, buffer_count, name);
177 }
178
_wgetenv_s(size_t * const required_count,wchar_t * const buffer,size_t const buffer_count,wchar_t const * const name)179 extern "C" errno_t __cdecl _wgetenv_s(
180 size_t* const required_count,
181 wchar_t* const buffer,
182 size_t const buffer_count,
183 wchar_t const* const name
184 )
185 {
186 return common_getenv_s(required_count, buffer, buffer_count, name);
187 }
188
189
190
191 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
192 //
193 // _dupenv_s() and _wdupenv_s()
194 //
195 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
196 // These functions search the environment for a variable with the given name.
197 // If a variable is found, a buffer is allocated using the public malloc to hold
198 // the value of the variable. The value is copied into the buffer and the buffer
199 // is returned to the caller via 'buffer_pointer' and 'buffer_count'. The caller
200 // is responsible for freeing the buffer.
201 //
202 // Returns zero on success; returns an error code on failure. Note that if a
203 // variable with the specified name is not found, that is still considered a
204 // success; in this case, the 'value' is an empty string ('*buffer_count' will
205 // be zero and '*buffer_pointer' will be nullptr).
206 template <typename Character>
common_dupenv_s_nolock(_Outptr_result_buffer_maybenull_ (* buffer_count)_Outptr_result_maybenull_z_ Character ** const buffer_pointer,_Out_opt_ size_t * const buffer_count,Character const * const name,int const block_use,char const * const file_name,int const line_number)207 static errno_t __cdecl common_dupenv_s_nolock(
208 _Outptr_result_buffer_maybenull_(*buffer_count) _Outptr_result_maybenull_z_ Character** const buffer_pointer,
209 _Out_opt_ size_t* const buffer_count,
210 Character const* const name,
211 int const block_use,
212 char const* const file_name,
213 int const line_number
214 ) throw()
215 {
216 // These are referenced only in the Debug CRT build
217 UNREFERENCED_PARAMETER(block_use);
218 UNREFERENCED_PARAMETER(file_name);
219 UNREFERENCED_PARAMETER(line_number);
220
221 typedef __crt_char_traits<Character> traits;
222
223 _VALIDATE_RETURN_ERRCODE(buffer_pointer != nullptr, EINVAL);
224 *buffer_pointer = nullptr;
225
226 if (buffer_count != nullptr)
227 *buffer_count = 0;
228
229 _VALIDATE_RETURN_ERRCODE(name != nullptr, EINVAL);
230
231 Character const* const value = common_getenv_nolock(name);
232 if (value == nullptr)
233 return 0;
234
235 size_t const value_count = traits::tcslen(value) + 1;
236
237 *buffer_pointer = static_cast<Character*>(_calloc_dbg(
238 value_count,
239 sizeof(Character),
240 block_use,
241 file_name,
242 line_number));
243 _VALIDATE_RETURN_ERRCODE_NOEXC(*buffer_pointer != nullptr, ENOMEM);
244
245 _ERRCHECK(traits::tcscpy_s(*buffer_pointer, value_count, value));
246 if (buffer_count != nullptr)
247 *buffer_count = value_count;
248
249 return 0;
250 }
251
252 template <typename Character>
253 _Success_(return != 0)
common_dupenv_s(_Outptr_result_buffer_maybenull_ (* buffer_count)_Outptr_result_maybenull_z_ Character ** const buffer_pointer,_Out_opt_ size_t * const buffer_count,Character const * const name,int const block_use,char const * const file_name,int const line_number)254 static errno_t __cdecl common_dupenv_s(
255 _Outptr_result_buffer_maybenull_(*buffer_count) _Outptr_result_maybenull_z_ Character** const buffer_pointer,
256 _Out_opt_ size_t* const buffer_count,
257 Character const* const name,
258 int const block_use,
259 char const* const file_name,
260 int const line_number
261 ) throw()
262 {
263 errno_t status = 0;
264
265 __acrt_lock(__acrt_environment_lock);
266 __try
267 {
268 status = common_dupenv_s_nolock(
269 buffer_pointer,
270 buffer_count,
271 name,
272 block_use,
273 file_name,
274 line_number);
275 }
276 __finally
277 {
278 __acrt_unlock(__acrt_environment_lock);
279 }
280 __endtry
281
282 return status;
283 }
284
_dupenv_s(char ** const buffer_pointer,size_t * const buffer_count,char const * const name)285 extern "C" errno_t __cdecl _dupenv_s(
286 char** const buffer_pointer,
287 size_t* const buffer_count,
288 char const* const name
289 )
290 {
291 return common_dupenv_s(buffer_pointer, buffer_count, name, _NORMAL_BLOCK, nullptr, 0);
292 }
293
_wdupenv_s(wchar_t ** const buffer_pointer,size_t * const buffer_count,wchar_t const * const name)294 extern "C" errno_t __cdecl _wdupenv_s(
295 wchar_t** const buffer_pointer,
296 size_t* const buffer_count,
297 wchar_t const* const name
298 )
299 {
300 return common_dupenv_s(buffer_pointer, buffer_count, name, _NORMAL_BLOCK, nullptr, 0);
301 }
302
303 #ifdef _DEBUG
304
305 #undef _dupenv_s_dbg
306 #undef _wdupenv_s_dbg
307
_dupenv_s_dbg(char ** const buffer_pointer,size_t * const buffer_count,char const * const name,int const block_use,char const * const file_name,int const line_number)308 extern "C" errno_t __cdecl _dupenv_s_dbg(
309 char** const buffer_pointer,
310 size_t* const buffer_count,
311 char const* const name,
312 int const block_use,
313 char const* const file_name,
314 int const line_number
315 )
316 {
317 return common_dupenv_s(buffer_pointer, buffer_count, name, block_use, file_name, line_number);
318 }
319
_wdupenv_s_dbg(wchar_t ** const buffer_pointer,size_t * const buffer_count,wchar_t const * const name,int const block_use,char const * const file_name,int const line_number)320 extern "C" errno_t __cdecl _wdupenv_s_dbg(
321 wchar_t** const buffer_pointer,
322 size_t* const buffer_count,
323 wchar_t const* const name,
324 int const block_use,
325 char const* const file_name,
326 int const line_number
327 )
328 {
329 return common_dupenv_s(buffer_pointer, buffer_count, name, block_use, file_name, line_number);
330 }
331
332 #endif // _DEBUG
333