1 //
2 // corecrt_internal_win32_buffer.h
3 //
4 //      Copyright (c) Microsoft Corporation.  All rights reserved.
5 //
6 // This internal header defines template-based utilities for handling call-twice
7 // Win32 APIs, where you first call the Win32 API with null or a fixed sized buffer,
8 // and if there is not enough space, allocate a dynamically sized buffer.
9 #pragma once
10 #include <corecrt_internal.h>
11 
12 #pragma pack(push, _CRT_PACKING)
13 
14 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
15 //
16 // __crt_win32_buffer_debug_info
17 //
18 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
19 // This is a class that can be used to describe the block_use, file_name, line_number
20 // debug data that is sometimes shuffled around between function calls.
21 
22 class __crt_win32_buffer_debug_info
23 {
24 #ifndef _DEBUG
25 public:
__crt_win32_buffer_debug_info(int,char const *,int)26     __crt_win32_buffer_debug_info(int, char const *, int)
27     {
28     }
29 #else /* ^^^^ Release ^^^^ / vvvv Debug vvvv */
30 public:
31     __crt_win32_buffer_debug_info(
32         int const          initial_block_use,
33         char const * const initial_file_name,
34         int const          initial_line_number
35         )
36         : _block_use(initial_block_use),
37           _file_name(initial_file_name),
38           _line_number(initial_line_number)
39     {
40     }
41 
42     int block_use() const
43     {
44         return _block_use;
45     }
46 
47     char const * file_name() const
48     {
49         return _file_name;
50     }
51 
52     int line_number() const
53     {
54         return _line_number;
55     }
56 
57 private:
58     int          _block_use;
59     char const * _file_name;
60     int          _line_number;
61 #endif /* _DEBUG */
62 };
63 
64 class __crt_win32_buffer_empty_debug_info
65 {
66 };
67 
68 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
69 //
70 // __crt_win32_buffer resize policies
71 //
72 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
73 // These classes are used to describe the different resize policies that a
74 // __crt_win32_buffer can have.
75 
76 struct __crt_win32_buffer_internal_dynamic_resizing
77 {
78     using debug_info_type = __crt_win32_buffer_empty_debug_info;
79 
80     _Check_return_
allocate__crt_win32_buffer_internal_dynamic_resizing81     static errno_t allocate(void ** const address, size_t const size, debug_info_type const&)
82     {
83         void * const ret = _malloc_crt(size);
84         *address = ret;
85         if (ret == nullptr) {
86             return ENOMEM;
87         }
88         return 0;
89     }
90 
deallocate__crt_win32_buffer_internal_dynamic_resizing91     static void deallocate(void * const ptr, debug_info_type const&)
92     {
93         _free_crt(ptr);
94     }
95 };
96 
97 struct __crt_win32_buffer_public_dynamic_resizing
98 {
99     using debug_info_type = __crt_win32_buffer_debug_info;
100 
101     _Check_return_
allocate__crt_win32_buffer_public_dynamic_resizing102     static errno_t allocate(void ** const address, size_t const size, debug_info_type const& debug_info)
103     {
104         UNREFERENCED_PARAMETER(debug_info); // only used in debug mode
105         void * const ret = _malloc_dbg(
106             size,
107             debug_info.block_use(),
108             debug_info.file_name(),
109             debug_info.line_number()
110             );
111         *address = ret;
112         if (ret == nullptr) {
113             return ENOMEM;
114         }
115         return 0;
116     }
117 
deallocate__crt_win32_buffer_public_dynamic_resizing118     static void deallocate(void * const ptr, debug_info_type const& debug_info)
119     {
120         UNREFERENCED_PARAMETER(debug_info); // only used in debug mode
121         _free_dbg(ptr, debug_info.block_use());
122     }
123 };
124 
125 struct __crt_win32_buffer_no_resizing
126 {
127     using debug_info_type = __crt_win32_buffer_empty_debug_info;
128 
129     _Check_return_
allocate__crt_win32_buffer_no_resizing130     static errno_t allocate(void ** const, size_t const, debug_info_type const&)
131     {
132         errno = ERANGE; // buffer not large enough
133         return ERANGE;
134     }
135 
deallocate__crt_win32_buffer_no_resizing136     static void deallocate(void * const, debug_info_type const&)
137     {
138     }
139 };
140 
141 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
142 //
143 // __crt_win32_buffer, __crt_internal_win32_buffer
144 // __crt_public_win32_buffer, __crt_no_alloc_win32_buffer
145 //
146 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
147 // Class and typedefs for buffers that support calling a win32 function and automatically
148 // resizing if needed.
149 
150 template <typename Character, typename ResizePolicy>
151 class __crt_win32_buffer : private ResizePolicy::debug_info_type
152 {   // Buffer type to enable Win32 call-twice-if-not-enough-space APIs.
153     // Using this type allows us to use a local buffer if possible and allocate if needed.
154 public:
155     using char_type = Character;
156     using debug_info_type = typename ResizePolicy::debug_info_type;
157 
__crt_win32_buffer()158     __crt_win32_buffer()
159         : debug_info_type(),
160           _initial_string(nullptr),
161           _initial_capacity(0),
162           _string(nullptr),
163           _capacity(0),
164           _size(0),
165           _is_dynamic(false)
166 
167     {
168     }
169 
__crt_win32_buffer(debug_info_type const & debug_info)170     explicit __crt_win32_buffer(debug_info_type const& debug_info)
171         : debug_info_type(debug_info),
172           _initial_string(nullptr),
173           _initial_capacity(0),
174           _string(nullptr),
175           _capacity(0),
176           _size(0),
177           _is_dynamic(false)
178 
179     {
180     }
181 
182     template <size_t N>
__crt_win32_buffer(Character (& buffer)[N])183     __crt_win32_buffer(Character (&buffer)[N])
184         : debug_info_type(),
185           _initial_string(buffer),
186           _initial_capacity(N),
187           _string(buffer),
188           _capacity(N),
189           _size(0),
190           _is_dynamic(false)
191     {
192     }
193 
194     template <size_t N>
__crt_win32_buffer(Character (& buffer)[N],debug_info_type const & debug_info)195     __crt_win32_buffer(Character (&buffer)[N], debug_info_type const& debug_info)
196         : debug_info_type(debug_info),
197           _initial_string(buffer),
198           _initial_capacity(N),
199           _string(buffer),
200           _capacity(N),
201           _size(0),
202           _is_dynamic(false)
203     {
204     }
205 
__crt_win32_buffer(Character * const buffer,size_t const buffer_capacity)206     __crt_win32_buffer(
207         Character * const buffer,
208         size_t const buffer_capacity
209         )
210         : debug_info_type(),
211           _initial_string(buffer),
212           _initial_capacity(buffer_capacity),
213           _string(buffer),
214           _capacity(buffer_capacity),
215           _size(0),
216           _is_dynamic(false)
217     {
218     }
219 
__crt_win32_buffer(Character * const buffer,size_t const buffer_capacity,debug_info_type const & debug_info)220     __crt_win32_buffer(
221         Character * const buffer,
222         size_t const buffer_capacity,
223         debug_info_type const& debug_info
224         )
225         : debug_info_type(debug_info),
226           _initial_string(buffer),
227           _initial_capacity(buffer_capacity),
228           _string(buffer),
229           _capacity(buffer_capacity),
230           _size(0),
231           _is_dynamic(false)
232     {
233     }
234 
~__crt_win32_buffer()235     ~__crt_win32_buffer()
236     {
237         _deallocate();
238     }
239 
240     __crt_win32_buffer(__crt_win32_buffer const&) = delete;
241     __crt_win32_buffer& operator=(__crt_win32_buffer const&) = delete;
242 
243     __crt_win32_buffer(__crt_win32_buffer&&) = delete;
244     __crt_win32_buffer& operator=(__crt_win32_buffer&&) = delete;
245 
data()246     char_type * data()
247     {
248         return _string;
249     }
250 
data()251     char_type const * data() const
252     {
253         return _string;
254     }
255 
size()256     size_t size() const
257     {
258         return _size;
259     }
260 
size(size_t new_size)261     void size(size_t new_size)
262     {
263         _size = new_size;
264     }
265 
capacity()266     size_t capacity() const
267     {
268         return _capacity;
269     }
270 
reset()271     void reset()
272     {
273         _deallocate();
274         _reset_no_dealloc();
275     }
276 
detach()277     char_type * detach()
278     {
279         if (_string == nullptr || _size == 0) {
280             return nullptr;
281         }
282 
283         char_type * return_val{};
284 
285         if (!_is_dynamic && _size > 0) {
286             // This pointer needs to live longer than the stack buffer
287             // Allocate + Copy
288             ResizePolicy::allocate(
289                 reinterpret_cast<void **>(&return_val),
290                 _size * sizeof(Character),
291                 debug_info()
292                 );
293             memcpy_s(return_val, _size, _string, _capacity);
294         } else {
295             return_val = _string;
296         }
297 
298         _reset_no_dealloc();
299         return return_val;
300     }
301 
302     template <typename Win32Function>
call_win32_function(Win32Function const & win32_function)303     errno_t call_win32_function(Win32Function const& win32_function)
304     {   // Suitable for more Win32 calls, where a size is returned
305         // if there is not enough space.
306 
307         size_t const required_size = win32_function(_string, static_cast<DWORD>(_capacity));
308         if (required_size == 0) {
309             __acrt_errno_map_os_error(GetLastError());
310             return errno;
311         }
312 
313         if (required_size <= _capacity) {
314             // Had enough space, data was written, save size and return
315             _size = required_size;
316             return 0;
317         }
318 
319         size_t const required_size_plus_null_terminator = required_size + 1;
320 
321         errno_t const alloc_err = allocate(required_size_plus_null_terminator);
322         if (alloc_err)
323         {
324             return alloc_err;
325         }
326 
327         // Upon success, return value is number of characters written, minus the null terminator.
328         size_t const required_size2 = win32_function(_string, static_cast<DWORD>(_capacity));
329         if (required_size2 == 0) {
330             __acrt_errno_map_os_error(GetLastError());
331             return errno;
332         }
333 
334         // Capacity should be large enough at this point.
335         _size = required_size2;
336         return 0;
337     }
338 
debug_info()339     debug_info_type const& debug_info() const
340     {
341         return static_cast<debug_info_type const&>(*this);
342     }
343 
allocate(size_t requested_size)344     errno_t allocate(size_t requested_size)
345     {
346         _deallocate();
347 
348         errno_t err = ResizePolicy::allocate(
349             reinterpret_cast<void **>(&_string),
350             requested_size * sizeof(Character),
351             debug_info()
352             );
353 
354         if (err) {
355             _is_dynamic = false;
356             _capacity = 0;
357             return err;
358         }
359 
360         _is_dynamic = true;
361         _capacity = requested_size;
362         return 0;
363     }
364 
set_to_nullptr()365     void set_to_nullptr()
366     {
367         _deallocate();
368 
369         _string = nullptr;
370         _capacity = 0;
371         _size = 0;
372     }
373 
374 private:
_reset_no_dealloc()375     void _reset_no_dealloc()
376     {
377         _string = _initial_string;
378         _capacity = _initial_capacity;
379         _size = 0;
380     }
381 
_deallocate()382     void _deallocate()
383     {
384         if (_is_dynamic) {
385             ResizePolicy::deallocate(_string, debug_info());
386             _is_dynamic = false;
387         }
388     }
389 
390     char_type * const _initial_string;
391     size_t            _initial_capacity;
392     char_type *       _string;
393     size_t            _capacity;
394     size_t            _size;
395     bool              _is_dynamic;
396 };
397 
398 template <typename Character>
399 using __crt_internal_win32_buffer = __crt_win32_buffer<Character, __crt_win32_buffer_internal_dynamic_resizing>;
400 
401 template <typename Character>
402 using __crt_public_win32_buffer = __crt_win32_buffer<Character, __crt_win32_buffer_public_dynamic_resizing>;
403 
404 template <typename Character>
405 using __crt_no_alloc_win32_buffer = __crt_win32_buffer<Character, __crt_win32_buffer_no_resizing>;
406 
407 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
408 //
409 // UTF-8 or ACP Helper
410 //
411 // Some POSIX functions have historically used the ACP for doing narrow->wide
412 // conversions. In order to support UTF-8 with these functions, they've been
413 // modified so that they use CP_UTF8 when current locale is UTF-8, but the ACP
414 // otherwise in order to preserve backwards compatibility.
415 //
416 // These POSIX functions can call __acrt_get_utf8_acp_compatibility_codepage to grab
417 // the code page they should use for their conversions.
418 //
419 // The Win32 ANSI "*A" APIs also use this to preserve their behavior as using the ACP, unless
420 // the current locale is set to UTF-8.
421 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
__acrt_get_utf8_acp_compatibility_codepage()422 inline unsigned int __acrt_get_utf8_acp_compatibility_codepage()
423 {
424     _LocaleUpdate locale_update(nullptr);
425     unsigned int const current_code_page = locale_update.GetLocaleT()->locinfo->_public._locale_lc_codepage;
426 
427     if (current_code_page == CP_UTF8) {
428         return CP_UTF8;
429     }
430 
431     bool const use_oem_code_page = !__acrt_AreFileApisANSI();
432 
433     if (use_oem_code_page) {
434         return CP_OEMCP;
435     }
436 
437     return CP_ACP;
438 }
439 
440 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
441 //
442 // Win32 APIs using __crt_win32_buffer
443 //
444 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
445 // See complete list of internal conversion functions in corecrt_internal_traits.h
446 
447 template <typename FromChar, typename ToChar, typename CvtFunction, typename ResizePolicy>
__acrt_convert_wcs_mbs(FromChar const * const null_terminated_input_string,__crt_win32_buffer<ToChar,ResizePolicy> & win32_buffer,CvtFunction const & cvt_func,_locale_t locale)448 errno_t __acrt_convert_wcs_mbs(
449     FromChar const * const                    null_terminated_input_string,
450     __crt_win32_buffer<ToChar, ResizePolicy>& win32_buffer,
451     CvtFunction const&                        cvt_func,
452     _locale_t                                 locale
453     )
454 {
455     // Common code path for conversions using mbstowcs and wcstombs.
456     if (null_terminated_input_string == nullptr) {
457         win32_buffer.set_to_nullptr();
458         return 0;
459     }
460 
461     // No empty string special case - mbstowcs/wcstombs handles them.
462     size_t const required_size = cvt_func(nullptr, null_terminated_input_string, 0, locale);
463 
464     if (required_size == static_cast<size_t>(-1)) {
465         return errno;
466     }
467 
468     size_t const required_size_plus_null_terminator = required_size + 1;
469 
470     if (required_size_plus_null_terminator > win32_buffer.capacity()) {
471         errno_t const alloc_err = win32_buffer.allocate(required_size_plus_null_terminator);
472         if (alloc_err != 0) {
473             return alloc_err;
474         }
475     }
476 
477     size_t const chars_converted = cvt_func(win32_buffer.data(), null_terminated_input_string, win32_buffer.capacity(), locale);
478     if (chars_converted == static_cast<size_t>(-1) || chars_converted == win32_buffer.capacity()) {
479         // check for error or if output is not null terminated
480         return errno;
481     }
482 
483     win32_buffer.size(chars_converted);
484     return 0;
485 }
486 
487 template <typename FromChar, typename ToChar, typename CvtFunction, typename ResizePolicy>
__acrt_convert_wcs_mbs_cp(FromChar const * const null_terminated_input_string,__crt_win32_buffer<ToChar,ResizePolicy> & win32_buffer,CvtFunction const & cvt_func,unsigned int const code_page)488 errno_t __acrt_convert_wcs_mbs_cp(
489     FromChar const * const                    null_terminated_input_string,
490     __crt_win32_buffer<ToChar, ResizePolicy>& win32_buffer,
491     CvtFunction const&                        cvt_func,
492     unsigned int const                        code_page
493     )
494 {
495     // Common code path for conversions using MultiByteToWideChar and WideCharToMultiByte with null terminated inputs.
496     if (null_terminated_input_string == nullptr) {
497         win32_buffer.set_to_nullptr();
498         return 0;
499     }
500 
501     // Special Case: Empty strings are not valid input to MultiByteToWideChar/WideCharToMultiByte
502     if (null_terminated_input_string[0] == '\0') {
503         if (win32_buffer.capacity() == 0) {
504             errno_t alloc_err = win32_buffer.allocate(1);
505             if (alloc_err != 0) {
506                 return alloc_err;
507             }
508         }
509 
510         win32_buffer.data()[0] = '\0';
511         win32_buffer.size(0);
512         return 0;
513     }
514 
515     size_t const required_size_plus_null_terminator = cvt_func(
516         code_page,
517         null_terminated_input_string,
518         nullptr,
519         0
520         );
521 
522     if (required_size_plus_null_terminator == 0) {
523         __acrt_errno_map_os_error(::GetLastError());
524         return errno;
525     }
526 
527     if (required_size_plus_null_terminator > win32_buffer.capacity()) {
528         errno_t alloc_err = win32_buffer.allocate(required_size_plus_null_terminator);
529         if (alloc_err != 0) {
530             return alloc_err;
531         }
532     }
533 
534     size_t const chars_converted_plus_null_terminator = cvt_func(
535         code_page,
536         null_terminated_input_string,
537         win32_buffer.data(),
538         win32_buffer.capacity()
539         );
540 
541     if (chars_converted_plus_null_terminator == 0) {
542         __acrt_errno_map_os_error(::GetLastError());
543         return errno;
544     }
545 
546     win32_buffer.size(chars_converted_plus_null_terminator - 1); // size does not include the null terminator
547     return 0;
548 }
549 
550 template <typename ResizePolicy>
551 errno_t __acrt_wcs_to_mbs(
552     wchar_t const * const                   null_terminated_input_string,
553     __crt_win32_buffer<char, ResizePolicy>& win32_buffer,
554     _locale_t                               locale = nullptr
555     )
556 {
557     _BEGIN_SECURE_CRT_DEPRECATION_DISABLE
558         return __acrt_convert_wcs_mbs(
559             null_terminated_input_string,
560             win32_buffer,
561             _wcstombs_l,
562             locale
563             );
564     _END_SECURE_CRT_DEPRECATION_DISABLE
565 }
566 
567 template <typename ResizePolicy>
__acrt_wcs_to_mbs_cp(wchar_t const * const null_terminated_input_string,__crt_win32_buffer<char,ResizePolicy> & win32_buffer,unsigned int const code_page)568 errno_t __acrt_wcs_to_mbs_cp(
569     wchar_t const * const                   null_terminated_input_string,
570     __crt_win32_buffer<char, ResizePolicy>& win32_buffer,
571     unsigned int const                      code_page
572     )
573 {
574     auto const wcs_to_mbs = [](
575         unsigned int const    code_page,
576         wchar_t const * const null_terminated_input_string,
577         char * const          buffer,
578         size_t const          buffer_size)
579     {
580         // Return value includes null terminator.
581         return __acrt_WideCharToMultiByte(
582             code_page,
583             0,
584             null_terminated_input_string,
585             -1,
586             buffer,
587             static_cast<int>(buffer_size),
588             nullptr,
589             nullptr
590             );
591     };
592 
593     return __acrt_convert_wcs_mbs_cp(
594         null_terminated_input_string,
595         win32_buffer,
596         wcs_to_mbs,
597         code_page
598         );
599 }
600 
601 template <typename ResizePolicy>
602 errno_t __acrt_mbs_to_wcs(
603     char const * const                         null_terminated_input_string,
604     __crt_win32_buffer<wchar_t, ResizePolicy>& win32_buffer,
605     _locale_t                                  locale = nullptr
606     )
607 {
608     _BEGIN_SECURE_CRT_DEPRECATION_DISABLE
609         return __acrt_convert_wcs_mbs(
610             null_terminated_input_string,
611             win32_buffer,
612             _mbstowcs_l,
613             locale
614             );
615     _END_SECURE_CRT_DEPRECATION_DISABLE
616 }
617 
618 template <typename ResizePolicy>
__acrt_mbs_to_wcs_cp(char const * const null_terminated_input_string,__crt_win32_buffer<wchar_t,ResizePolicy> & win32_buffer,unsigned int const code_page)619 errno_t __acrt_mbs_to_wcs_cp(
620     char const * const                         null_terminated_input_string,
621     __crt_win32_buffer<wchar_t, ResizePolicy>& win32_buffer,
622     unsigned int const                         code_page
623     )
624 {
625     auto const mbs_to_wcs = [](
626         unsigned int const code_page,
627         char const * const null_terminated_input_string,
628         wchar_t * const    buffer,
629         size_t const       buffer_size)
630     {
631         // Return value includes null terminator.
632         return __acrt_MultiByteToWideChar(
633             code_page,
634             MB_PRECOMPOSED | MB_ERR_INVALID_CHARS,
635             null_terminated_input_string,
636             -1,
637             buffer,
638             static_cast<int>(buffer_size)
639             );
640     };
641 
642     return __acrt_convert_wcs_mbs_cp(
643         null_terminated_input_string,
644         win32_buffer,
645         mbs_to_wcs,
646         code_page
647         );
648 }
649 
650 // Array overloads are useful for __try contexts where objects with unwind semantics cannot be used.
651 template <size_t N>
652 size_t __acrt_wcs_to_mbs_array(
653     wchar_t const * const null_terminated_input_string,
654     char                  (&buffer)[N],
655     _locale_t             locale = nullptr
656     )
657 {
658     __crt_no_alloc_win32_buffer<char> win32_buffer(buffer);
659     if (__acrt_wcs_to_mbs(null_terminated_input_string, win32_buffer, locale) != 0) {
660         return 0;
661     }
662 
663     return win32_buffer.size();
664 }
665 
666 template <size_t N>
__acrt_wcs_to_mbs_cp_array(wchar_t const * const null_terminated_input_string,char (& buffer)[N],unsigned int const code_page)667 size_t __acrt_wcs_to_mbs_cp_array(
668     wchar_t const * const null_terminated_input_string,
669     char                  (&buffer)[N],
670     unsigned int const    code_page
671     )
672 {
673     __crt_no_alloc_win32_buffer<char> win32_buffer(buffer);
674     if (__acrt_wcs_to_mbs_cp(null_terminated_input_string, win32_buffer, code_page) != 0) {
675         return 0;
676     }
677 
678     return win32_buffer.size();
679 }
680 
681 template <size_t N>
682 size_t __acrt_mbs_to_wcs_array(
683     char const * const null_terminated_input_string,
684     wchar_t            (&buffer)[N],
685     _locale_t          locale = nullptr
686     )
687 {
688     __crt_no_alloc_win32_buffer<wchar_t> win32_buffer(buffer);
689     if (__acrt_mbs_to_wcs(null_terminated_input_string, win32_buffer, locale) != 0) {
690         return 0;
691     }
692 
693     return win32_buffer.size();
694 }
695 
696 template <size_t N>
__acrt_mbs_to_wcs_cp_array(char const * const null_terminated_input_string,wchar_t (& buffer)[N],unsigned int const code_page)697 size_t __acrt_mbs_to_wcs_cp_array(
698     char const * const null_terminated_input_string,
699     wchar_t            (&buffer)[N],
700     unsigned int const code_page
701     )
702 {
703     __crt_no_alloc_win32_buffer<wchar_t> win32_buffer(buffer);
704     if (__acrt_mbs_to_wcs_cp(null_terminated_input_string, win32_buffer, code_page) != 0) {
705         return 0;
706     }
707 
708     return win32_buffer.size();
709 }
710 
711 template <typename ResizePolicy>
__acrt_get_current_directory_wide(__crt_win32_buffer<wchar_t,ResizePolicy> & win32_buffer)712 errno_t __acrt_get_current_directory_wide(
713      __crt_win32_buffer<wchar_t, ResizePolicy>& win32_buffer
714     )
715 {
716     return win32_buffer.call_win32_function([](wchar_t * buffer, DWORD buffer_length)
717     {
718         return ::GetCurrentDirectoryW(buffer_length, buffer);
719     });
720 }
721 
722 template <typename ResizePolicy>
__acrt_get_current_directory_narrow_acp_or_utf8(__crt_win32_buffer<char,ResizePolicy> & win32_buffer)723 errno_t __acrt_get_current_directory_narrow_acp_or_utf8(
724     __crt_win32_buffer<char, ResizePolicy>& win32_buffer
725     )
726 {
727     wchar_t default_buffer_space[_MAX_PATH];
728     __crt_internal_win32_buffer<wchar_t> wide_buffer(default_buffer_space);
729 
730     errno_t const err = __acrt_get_current_directory_wide(wide_buffer);
731 
732     if (err != 0) {
733         return err;
734     }
735 
736     return __acrt_wcs_to_mbs_cp(
737         wide_buffer.data(),
738         win32_buffer,
739         __acrt_get_utf8_acp_compatibility_codepage()
740         );
741 }
742 
743 template <typename ResizePolicy>
__acrt_get_full_path_name_wide(wchar_t const * const lpFileName,__crt_win32_buffer<wchar_t,ResizePolicy> & win32_buffer)744 errno_t __acrt_get_full_path_name_wide(
745     wchar_t const * const                      lpFileName,
746     __crt_win32_buffer<wchar_t, ResizePolicy>& win32_buffer
747     )
748 {
749     return win32_buffer.call_win32_function([lpFileName](wchar_t * buffer, DWORD buffer_length)
750     {
751         return ::GetFullPathNameW(
752             lpFileName,
753             buffer_length,
754             buffer,
755             nullptr
756         );
757     });
758 }
759 
760 template <typename ResizePolicy>
__acrt_get_full_path_name_narrow_acp_or_utf8(char const * const lpFileName,__crt_win32_buffer<char,ResizePolicy> & win32_buffer)761 errno_t __acrt_get_full_path_name_narrow_acp_or_utf8(
762     char const * const                      lpFileName,
763     __crt_win32_buffer<char, ResizePolicy>& win32_buffer
764     )
765 {
766     wchar_t default_buffer_space[_MAX_PATH];
767     __crt_internal_win32_buffer<wchar_t> wide_buffer(default_buffer_space);
768 
769     wchar_t default_file_name_space[_MAX_PATH];
770     __crt_internal_win32_buffer<wchar_t> wide_file_name(default_file_name_space);
771 
772     unsigned int const code_page = __acrt_get_utf8_acp_compatibility_codepage();
773 
774     errno_t const cvt_err = __acrt_mbs_to_wcs_cp(
775         lpFileName,
776         wide_file_name,
777         code_page
778         );
779 
780     if (cvt_err != 0)
781     {
782         return cvt_err;
783     }
784 
785     errno_t const err = __acrt_get_full_path_name_wide(wide_file_name.data(), wide_buffer);
786 
787     if (err != 0)
788     {
789         return err;
790     }
791 
792     return __acrt_wcs_to_mbs_cp(
793         wide_buffer.data(),
794         win32_buffer,
795         code_page
796         );
797 }
798 
799 #pragma pack(pop)
800