@@ -113,6 +113,115 @@ char * rcutils_get_executable_name(rcutils_allocator_t allocator)
113113 return executable_name ;
114114}
115115
116+ #if defined _WIN32 || defined __CYGWIN__
117+ static
118+ rcutils_ret_t
119+ append_backslashes (rcutils_char_array_t * char_array , size_t num_backslashes )
120+ {
121+ if (num_backslashes <= 0 ) {
122+ return RCUTILS_RET_OK ;
123+ }
124+
125+ size_t current_strlen ;
126+ if (char_array -> buffer_length == 0 ) {
127+ current_strlen = 0 ;
128+ } else {
129+ // The buffer length always contains the trailing \0, so the strlen is one less than that.
130+ current_strlen = char_array -> buffer_length - 1 ;
131+ }
132+
133+ size_t new_length = current_strlen + num_backslashes + 1 ;
134+ rcutils_ret_t ret = rcutils_char_array_expand_as_needed (char_array , new_length );
135+ if (RCUTILS_RET_OK != ret ) {
136+ return ret ;
137+ }
138+
139+ memset (char_array -> buffer + current_strlen , '\\' , num_backslashes );
140+ char_array -> buffer [new_length - 1 ] = '\0' ;
141+
142+ char_array -> buffer_length = new_length ;
143+
144+ return RCUTILS_RET_OK ;
145+ }
146+
147+ /**
148+ * This algorithm is based on the MSDN blog "everyone quotes command line arguments the wrong way".
149+ * See https://learn.microsoft.com/en-us/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way
150+ */
151+ static
152+ rcutils_ret_t
153+ build_command_line (
154+ const rcutils_string_array_t * string_array ,
155+ rcutils_char_array_t * char_array )
156+ {
157+ rcutils_ret_t ret ;
158+
159+ for (size_t i = 0 ; i < string_array -> size ; i ++ ) {
160+ // Append argument separator
161+ if (i != 0 ) {
162+ ret = rcutils_char_array_strncat (char_array , " " , 1 );
163+ if (RCUTILS_RET_OK != ret ) {
164+ return ret ;
165+ }
166+ }
167+
168+ // Append argument which doesn't need to be quoted
169+ size_t arg_length = strlen (string_array -> data [i ]);
170+ if (strcspn (string_array -> data [i ], " \t\n\v\"" ) >= arg_length ) {
171+ ret = rcutils_char_array_strncat (char_array , string_array -> data [i ], arg_length );
172+ if (RCUTILS_RET_OK != ret ) {
173+ return ret ;
174+ }
175+ continue ;
176+ }
177+
178+ // Append opening quote
179+ ret = rcutils_char_array_strncat (char_array , "\"" , 1 );
180+ if (RCUTILS_RET_OK != ret ) {
181+ return ret ;
182+ }
183+
184+ for (const char * c = string_array -> data [i ]; ; c ++ ) {
185+ size_t backslash_count = 0 ;
186+ while ('\\' == * c ) {
187+ backslash_count ++ ;
188+ c ++ ;
189+ }
190+
191+ if ('\0' == * c ) {
192+ ret = append_backslashes (char_array , backslash_count * 2 );
193+ if (RCUTILS_RET_OK != ret ) {
194+ return ret ;
195+ }
196+ break ;
197+ }
198+
199+ if ('"' == * c ) {
200+ backslash_count = backslash_count * 2 + 1 ;
201+ }
202+
203+ ret = append_backslashes (char_array , backslash_count );
204+ if (RCUTILS_RET_OK != ret ) {
205+ return ret ;
206+ }
207+
208+ ret = rcutils_char_array_strncat (char_array , c , 1 );
209+ if (RCUTILS_RET_OK != ret ) {
210+ return ret ;
211+ }
212+ }
213+
214+ // Append closing quote
215+ ret = rcutils_char_array_strncat (char_array , "\"" , 1 );
216+ if (RCUTILS_RET_OK != ret ) {
217+ return ret ;
218+ }
219+ }
220+
221+ return RCUTILS_RET_OK ;
222+ }
223+ #endif
224+
116225rcutils_process_t *
117226rcutils_start_process (
118227 const rcutils_string_array_t * args ,
@@ -135,25 +244,39 @@ rcutils_start_process(
135244 process -> allocator = * allocator ;
136245
137246#if defined _WIN32 || defined __CYGWIN__
138- LPSTR cmd = rcutils_join (args , " " , * allocator );
139- if (NULL == cmd ) {
247+ rcutils_char_array_t cmd = rcutils_get_zero_initialized_char_array ();
248+ rcutils_ret_t ret = rcutils_char_array_init (& cmd , 0 , allocator );
249+ if (RCUTILS_RET_OK != ret ) {
250+ rcutils_process_close (process );
251+ return NULL ;
252+ }
253+
254+ ret = build_command_line (args , & cmd );
255+ if (RCUTILS_RET_OK != ret ) {
256+ if (RCUTILS_RET_OK != rcutils_char_array_fini (& cmd )) {
257+ RCUTILS_SAFE_FWRITE_TO_STDERR ("Failed to fini array.\n" );
258+ }
140259 rcutils_process_close (process );
141260 return NULL ;
142261 }
143262
144263 STARTUPINFO start_info = {sizeof (start_info )};
145264 PROCESS_INFORMATION process_info = {0 };
146265 if (!CreateProcess (
147- NULL , cmd , NULL , NULL , TRUE, 0 ,
266+ NULL , cmd . buffer , NULL , NULL , TRUE, 0 ,
148267 NULL , NULL , & start_info , & process_info ))
149268 {
150269 RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING ("Failed to create process: %lu" , GetLastError ());
151- allocator -> deallocate (cmd , & allocator -> state );
270+ if (RCUTILS_RET_OK != rcutils_char_array_fini (& cmd )) {
271+ RCUTILS_SAFE_FWRITE_TO_STDERR ("Failed to fini array.\n" );
272+ }
152273 rcutils_process_close (process );
153274 return NULL ;
154275 }
155276
156- allocator -> deallocate (cmd , & allocator -> state );
277+ if (RCUTILS_RET_OK != rcutils_char_array_fini (& cmd )) {
278+ RCUTILS_SAFE_FWRITE_TO_STDERR ("Failed to fini array.\n" );
279+ }
157280
158281 CloseHandle (process_info .hThread );
159282
0 commit comments