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