[CSCRIPT][WSCRIPT] Sync with Wine Staging 3.17. CORE-15127
[reactos.git] / base / shell / cmd / start.c
1 /*
2 * START.C - start internal command.
3 *
4 *
5 * History:
6 *
7 * 24-Jul-1999 (Eric Kohl)
8 * Started.
9 *
10 * 30-Apr-2005 (Magnus Olsen <magnus@greatlord.com>)
11 * Remove all hardcoded strings in En.rc
12 */
13
14 #include "precomp.h"
15
16 #ifdef INCLUDE_CMD_START
17
18 /* Find the end of an option, and turn it into a nul-terminated string
19 * in place. (It's moved back one character, to make room for the nul) */
20 static TCHAR *GetParameter(TCHAR **pPointer)
21 {
22 BOOL bInQuote = FALSE;
23 TCHAR *start = *pPointer;
24 TCHAR *p;
25 for (p = start; *p; p++)
26 {
27 if (!bInQuote && (*p == _T('/') || _istspace(*p)))
28 break;
29 bInQuote ^= (*p == _T('"'));
30 p[-1] = *p;
31 }
32 p[-1] = _T('\0');
33 *pPointer = p;
34 return start - 1;
35 }
36
37 INT cmd_start (LPTSTR Rest)
38 {
39 TCHAR szFullName[CMDLINE_LENGTH];
40 TCHAR szUnquotedName[CMDLINE_LENGTH];
41 TCHAR *param = NULL;
42 TCHAR *dot;
43 INT size;
44 LPTSTR comspec;
45 BOOL bWait = FALSE;
46 BOOL bBat = FALSE;
47 BOOL bCreate = FALSE;
48 TCHAR szFullCmdLine[CMDLINE_LENGTH];
49 PROCESS_INFORMATION prci;
50 STARTUPINFO stui;
51 #ifdef UNICODE
52 DWORD dwCreationFlags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT;
53 #else
54 DWORD dwCreationFlags = CREATE_NEW_CONSOLE;
55 #endif
56 DWORD dwAffinityMask = 0;
57 LPTSTR lpTitle = NULL;
58 LPTSTR lpDirectory = NULL;
59 LPTSTR lpEnvironment = NULL;
60 WORD wShowWindow = SW_SHOWNORMAL;
61
62 while (1)
63 {
64 if (_istspace(*Rest))
65 {
66 Rest++;
67 }
68 else if (*Rest == _T('"') && !lpTitle)
69 {
70 lpTitle = GetParameter(&Rest);
71 StripQuotes(lpTitle);
72 }
73 else if (*Rest == _T('/'))
74 {
75 LPTSTR option;
76 Rest++;
77 option = GetParameter(&Rest);
78 if (*option == _T('?'))
79 {
80 ConOutResPaging(TRUE,STRING_START_HELP1);
81 return 0;
82 }
83 else if (_totupper(*option) == _T('D'))
84 {
85 lpDirectory = option + 1;
86 if (!*lpDirectory)
87 {
88 while (_istspace(*Rest))
89 Rest++;
90 lpDirectory = GetParameter(&Rest);
91 }
92 StripQuotes(lpDirectory);
93 }
94 else if (_totupper(*option) == _T('I'))
95 {
96 /* rest of the option is apparently ignored */
97 lpEnvironment = lpOriginalEnvironment;
98 }
99 else if (!_tcsicmp(option, _T("MIN")))
100 {
101 wShowWindow = SW_MINIMIZE;
102 }
103 else if (!_tcsicmp(option, _T("MAX")))
104 {
105 wShowWindow = SW_MAXIMIZE;
106 }
107 else if (!_tcsicmp(option, _T("AFFINITY")))
108 {
109 TCHAR *end;
110 while (_istspace(*Rest))
111 Rest++;
112 option = GetParameter(&Rest);
113 /* Affinity mask is given in hexadecimal */
114 dwAffinityMask = _tcstoul(option, &end, 16);
115 if (*end != _T('\0') || dwAffinityMask == 0 ||
116 dwAffinityMask == (DWORD)-1)
117 {
118 ConErrResPrintf(STRING_ERROR_INVALID_PARAM_FORMAT, option);
119 return 1;
120 }
121 dwCreationFlags |= CREATE_SUSPENDED;
122 }
123 else if (!_tcsicmp(option, _T("B")))
124 {
125 dwCreationFlags &= ~CREATE_NEW_CONSOLE;
126 dwCreationFlags |= CREATE_NEW_PROCESS_GROUP;
127 }
128 else if (!_tcsicmp(option, _T("LOW")))
129 {
130 dwCreationFlags |= IDLE_PRIORITY_CLASS;
131 }
132 else if (!_tcsicmp(option, _T("NORMAL")))
133 {
134 dwCreationFlags |= NORMAL_PRIORITY_CLASS;
135 }
136 else if (!_tcsicmp(option, _T("HIGH")))
137 {
138 dwCreationFlags |= HIGH_PRIORITY_CLASS;
139 }
140 else if (!_tcsicmp(option, _T("REALTIME")))
141 {
142 dwCreationFlags |= REALTIME_PRIORITY_CLASS;
143 }
144 else if (!_tcsicmp(option, _T("ABOVENORMAL")))
145 {
146 dwCreationFlags |= ABOVE_NORMAL_PRIORITY_CLASS;
147 }
148 else if (!_tcsicmp(option, _T("BELOWNORMAL")))
149 {
150 dwCreationFlags |= BELOW_NORMAL_PRIORITY_CLASS;
151 }
152 else if (!_tcsicmp(option, _T("SEPARATE")))
153 {
154 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
155 }
156 else if (!_tcsicmp(option, _T("SHARED")))
157 {
158 dwCreationFlags |= CREATE_SHARED_WOW_VDM;
159 }
160 else if (!_tcsicmp(option, _T("W")) ||
161 !_tcsicmp(option, _T("WAIT")))
162 {
163 bWait = TRUE;
164 }
165 else
166 {
167 ConErrResPrintf(STRING_TYPE_ERROR1, option);
168 return 0;
169 }
170 }
171 else
172 {
173 /* It's not an option - must be the beginning of
174 * the actual command. Leave the loop. */
175 break;
176 }
177 }
178
179 /* get comspec */
180 comspec = cmd_alloc ( MAX_PATH * sizeof(TCHAR));
181 if (!comspec)
182 {
183 WARN("Cannot allocate memory for start comspec!\n");
184 error_out_of_memory();
185 return 1;
186 }
187 SetLastError(0);
188 size = GetEnvironmentVariable (_T("COMSPEC"), comspec, MAX_PATH);
189 if (GetLastError() == ERROR_ENVVAR_NOT_FOUND)
190 {
191 _tcscpy(comspec, _T("cmd"));
192 }
193 else
194 {
195 if (size > MAX_PATH)
196 {
197 LPTSTR Oldcomspec = comspec;
198 comspec = cmd_realloc(comspec,size * sizeof(TCHAR) );
199 if (comspec==NULL)
200 {
201 cmd_free(Oldcomspec);
202 return 1;
203 }
204 size = GetEnvironmentVariable (_T("COMSPEC"), comspec, size);
205 }
206 }
207
208 nErrorLevel = 0;
209
210 if (!*Rest)
211 {
212 Rest = _T("cmd.exe");
213 }
214 else
215 /* Parsing the command that gets called by start, and it's parameters */
216 {
217 BOOL bInside = FALSE;
218 INT i;
219 /* find the end of the command and put the arguments in param */
220 for (i = 0; Rest[i]; i++)
221 {
222 if (Rest[i] == _T('\"'))
223 bInside = !bInside;
224 if (_istspace(Rest[i]) && !bInside)
225 {
226 param = &Rest[i+1];
227 Rest[i] = _T('\0');
228 break;
229 }
230 }
231 }
232
233 _tcscpy(szUnquotedName, Rest);
234 StripQuotes(szUnquotedName);
235
236 /* get the PATH environment variable and parse it */
237 /* search the PATH environment variable for the binary */
238 if (SearchForExecutable(szUnquotedName, szFullName))
239 {
240 /* check if this is a .BAT or .CMD file */
241 dot = _tcsrchr(szFullName, _T('.'));
242 if (dot && (!_tcsicmp(dot, _T(".bat")) || !_tcsicmp(dot, _T(".cmd"))))
243 {
244 bBat = TRUE;
245 _stprintf(szFullCmdLine, _T("\"%s\" /K %s"), comspec, Rest);
246 TRACE ("[BATCH: %s %s]\n", debugstr_aw(szFullName), debugstr_aw(Rest));
247 }
248 else
249 {
250 TRACE ("[EXEC: %s %s]\n", debugstr_aw(szFullName), debugstr_aw(Rest));
251 _tcscpy(szFullCmdLine, szFullName);
252 }
253
254 /* build command line for CreateProcess() */
255 if (param != NULL)
256 {
257 _tcsncat(szFullCmdLine, _T(" "), CMDLINE_LENGTH - _tcslen(szFullCmdLine));
258 _tcsncat(szFullCmdLine, param, CMDLINE_LENGTH - _tcslen(szFullCmdLine));
259 }
260
261 /* fill startup info */
262 memset (&stui, 0, sizeof (STARTUPINFO));
263 stui.cb = sizeof (STARTUPINFO);
264 stui.dwFlags = STARTF_USESHOWWINDOW;
265 stui.lpTitle = lpTitle;
266 stui.wShowWindow = wShowWindow;
267
268 bCreate = CreateProcess(bBat ? comspec : szFullName,
269 szFullCmdLine, NULL, NULL, TRUE, dwCreationFlags,
270 lpEnvironment, lpDirectory, &stui, &prci);
271 if (bCreate)
272 {
273 if (dwAffinityMask)
274 {
275 SetProcessAffinityMask(prci.hProcess, dwAffinityMask);
276 ResumeThread(prci.hThread);
277 }
278 CloseHandle(prci.hThread);
279 }
280 }
281 else
282 {
283 /* The file name did not seem to be valid, but maybe it's actually a
284 * directory or URL, so we still want to pass it to ShellExecute. */
285 _tcscpy(szFullName, szUnquotedName);
286 }
287
288 if (!bCreate)
289 {
290 /* CreateProcess didn't work; try ShellExecute */
291 DWORD flags = SEE_MASK_NOCLOSEPROCESS;
292 if (!(dwCreationFlags & CREATE_NEW_CONSOLE))
293 flags |= SEE_MASK_NO_CONSOLE;
294 prci.hProcess = RunFile(flags, szFullName, param, lpDirectory, wShowWindow);
295 }
296
297 if (prci.hProcess != NULL)
298 {
299 if (bWait)
300 {
301 DWORD dwExitCode;
302 WaitForSingleObject (prci.hProcess, INFINITE);
303 GetExitCodeProcess (prci.hProcess, &dwExitCode);
304 nErrorLevel = (INT)dwExitCode;
305 }
306 CloseHandle (prci.hProcess);
307
308 /* Update our local codepage cache */
309 {
310 UINT uNewInputCodePage = GetConsoleCP();
311 UINT uNewOutputCodePage = GetConsoleOutputCP();
312
313 if ((InputCodePage != uNewInputCodePage) ||
314 (OutputCodePage != uNewOutputCodePage))
315 {
316 /* Update the locale as well */
317 InitLocale();
318 }
319
320 InputCodePage = uNewInputCodePage;
321 OutputCodePage = uNewOutputCodePage;
322
323 /* Update the streams codepage cache as well */
324 ConStreamSetCacheCodePage(StdIn , InputCodePage );
325 ConStreamSetCacheCodePage(StdOut, OutputCodePage);
326 ConStreamSetCacheCodePage(StdErr, OutputCodePage);
327 }
328 }
329 else
330 {
331 ErrorMessage(GetLastError (),
332 _T("Error executing CreateProcess()!!\n"));
333 }
334
335 cmd_free(comspec);
336 return 0;
337 }
338
339 #endif
340
341 /* EOF */