xref: /reactos/sdk/lib/ucrt/filesystem/makepath.cpp (revision 04e0dc4a)
1 //
2 // makepath.cpp
3 //
4 //      Copyright (c) Microsoft Corporation.  All rights reserved.
5 //
6 // Defines the makepath family of functions, which compose a path string.
7 //
8 #include <corecrt_internal_securecrt.h>
9 #include <mbstring.h>
10 #include <stdlib.h>
11 
12 
13 
previous_character(char const * const first,char const * const current)14 static char const* previous_character(char const* const first, char const* const current) throw()
15 {
16     return reinterpret_cast<char const*>(_mbsdec(
17         reinterpret_cast<unsigned char const*>(first),
18         reinterpret_cast<unsigned char const*>(current)));
19 }
20 
previous_character(wchar_t const *,wchar_t const * const current)21 static wchar_t const* previous_character(wchar_t const*, wchar_t const* const current) throw()
22 {
23     return current - 1;
24 }
25 
26 
27 
28 template <typename Character>
cleanup_after_error(_Out_writes_z_ (count)Character * const buffer,size_t const count)29 static errno_t __cdecl cleanup_after_error(
30     _Out_writes_z_(count) Character* const buffer,
31     size_t                           const count) throw()
32 {
33     // This is referenced only in the Debug CRT build
34     UNREFERENCED_PARAMETER(count);
35 
36     _RESET_STRING(buffer, count);
37     _RETURN_BUFFER_TOO_SMALL(buffer, count);
38     return EINVAL; // This is unreachable
39 }
40 
41 
42 
43 // These functions compose a path string from its component parts.  They
44 // concatenate the drive, directory, file_name, and extension into the provided
45 // result_buffer.  For the functions that do not have a buffer count parameter,
46 // the buffer is assumed to be large enough to hold however many characters are
47 // required.
48 //
49 // The drive may or may not contain a ':'.  The directory may or may not contain
50 // a leading or trailing '/' or '\'.  The extension may or may not contain a
51 // leading '.'.
52 template <typename Character>
common_makepath_s(_Out_writes_z_ (result_count)Character * const result_buffer,_In_ size_t const result_count,_In_opt_z_ Character const * const drive,_In_opt_z_ Character const * const directory,_In_opt_z_ Character const * const file_name,_In_opt_z_ Character const * const extension)53 static errno_t __cdecl common_makepath_s(
54     _Out_writes_z_(result_count) Character* const result_buffer,
55     _In_       size_t           const result_count,
56     _In_opt_z_ Character const* const drive,
57     _In_opt_z_ Character const* const directory,
58     _In_opt_z_ Character const* const file_name,
59     _In_opt_z_ Character const* const extension
60     ) throw()
61 {
62     _VALIDATE_STRING(result_buffer, result_count);
63 
64     Character*       result_it  = result_buffer;
65 
66     // For the non-secure makepath functions, result_count is _CRT_UNBOUNDED_BUFFER_SIZE.
67     // In this case, we do not want to perform arithmetic with the result_count.  Instead,
68     // we set the result_end to nullptr:  result_it will never be a null pointer,
69     // and when we need to perform arithmetic with result_end we can check it for
70     // null first.
71     Character* const result_end = result_count != _CRT_UNBOUNDED_BUFFER_SIZE
72         ? result_buffer + result_count
73         : nullptr;
74 
75     CRT_WARNING_DISABLE_PUSH(26015, "Silence prefast about overflow - covered by result_end for secure callers")
76 
77     // Copy the drive:
78     if (drive && drive[0] != '\0')
79     {
80         if (result_end != nullptr && result_end - result_it < 2)
81             return cleanup_after_error(result_buffer, result_count);
82 
83         *result_it++ = *drive;
84         *result_it++ = ':';
85     }
86 
87     // Copy the directory:
88     if (directory && directory[0] != '\0')
89     {
90         Character const* source_it = directory;
91         while (*source_it != '\0')
92         {
93             if ((result_end != nullptr) && (result_it >= result_end))
94                 return cleanup_after_error(result_buffer, result_count);
95 
96             *result_it++ = *source_it++;
97         }
98 
99         // Write a trailing backslash if there isn't one:
100         source_it = previous_character(directory, source_it);
101         if (*source_it != '/' && *source_it != '\\')
102         {
103             if ((result_end != nullptr) && (result_it >= result_end))
104                 return cleanup_after_error(result_buffer, result_count);
105 
106             *result_it++ = '\\';
107         }
108     }
109 
110     // Copy the file name:
111     if (file_name)
112     {
113         Character const* source_it = file_name;
114         while (*source_it != '\0')
115         {
116             if ((result_end != nullptr) && (result_it >= result_end))
117                 return cleanup_after_error(result_buffer, result_count);
118 
119             *result_it++ = *source_it++;
120         }
121     }
122 
123     // Copy the extension:
124     if (extension)
125     {
126         // Add a '.' if one is required:
127         if (extension[0] != '\0' && extension[0] != '.')
128         {
129             if ((result_end != nullptr) && (result_it >= result_end))
130                 return cleanup_after_error(result_buffer, result_count);
131 
132             *result_it++ = '.';
133         }
134 
135         Character const* source_it = extension;
136         while (*source_it != '\0')
137         {
138             if ((result_end != nullptr) && (result_it >= result_end))
139                 return cleanup_after_error(result_buffer, result_count);
140 
141             *result_it++ = *source_it++;
142         }
143     }
144 
145     // Copy the null terminator:
146     if ((result_end != nullptr) && (result_it >= result_end))
147         return cleanup_after_error(result_buffer, result_count);
148 
149     *result_it++ = '\0';
150 
151     CRT_WARNING_POP
152 
153     _FILL_STRING(result_buffer, result_count, result_it - result_buffer);
154     return 0;
155 }
156 
157 
158 
_makepath(char * const result_buffer,char const * const drive,char const * const directory,char const * const file_name,char const * const extension)159 extern "C" void __cdecl _makepath(
160     char*       const result_buffer,
161     char const* const drive,
162     char const* const directory,
163     char const* const file_name,
164     char const* const extension
165     )
166 {
167     _makepath_s(result_buffer, _CRT_UNBOUNDED_BUFFER_SIZE, drive, directory, file_name, extension);
168 }
169 
_wmakepath(wchar_t * const result_buffer,wchar_t const * const drive,wchar_t const * const directory,wchar_t const * const file_name,wchar_t const * const extension)170 extern "C" void __cdecl _wmakepath(
171     wchar_t*       const result_buffer,
172     wchar_t const* const drive,
173     wchar_t const* const directory,
174     wchar_t const* const file_name,
175     wchar_t const* const extension
176     )
177 {
178     _wmakepath_s(result_buffer, _CRT_UNBOUNDED_BUFFER_SIZE, drive, directory, file_name, extension);
179 }
180 
181 
182 
_makepath_s(char * const result_buffer,size_t const result_count,char const * const drive,char const * const directory,char const * const file_name,char const * const extension)183 extern "C" errno_t __cdecl _makepath_s(
184     char*       const result_buffer,
185     size_t      const result_count,
186     char const* const drive,
187     char const* const directory,
188     char const* const file_name,
189     char const* const extension
190     )
191 {
192     return common_makepath_s(result_buffer, result_count, drive, directory, file_name, extension);
193 }
194 
_wmakepath_s(wchar_t * const result_buffer,size_t const result_count,wchar_t const * const drive,wchar_t const * const directory,wchar_t const * const file_name,wchar_t const * const extension)195 extern "C" errno_t __cdecl _wmakepath_s(
196     wchar_t*       const result_buffer,
197     size_t         const result_count,
198     wchar_t const* const drive,
199     wchar_t const* const directory,
200     wchar_t const* const file_name,
201     wchar_t const* const extension
202     )
203 {
204     return common_makepath_s(result_buffer, result_count, drive, directory, file_name, extension);
205 }
206