Sync with trunk head (r49139)
[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 hardcode string to 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 INT i = 0;
52 #ifdef UNICODE
53 DWORD dwCreationFlags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT;
54 #else
55 DWORD dwCreationFlags = CREATE_NEW_CONSOLE;
56 #endif
57 DWORD dwAffinityMask = 0;
58 LPTSTR lpTitle = NULL;
59 LPTSTR lpDirectory = NULL;
60 LPTSTR lpEnvironment = NULL;
61 WORD wShowWindow = SW_SHOWNORMAL;
62
63 while (1)
64 {
65 if (_istspace(*Rest))
66 {
67 Rest++;
68 }
69 else if (*Rest == _T('"') && !lpTitle)
70 {
71 lpTitle = GetParameter(&Rest);
72 StripQuotes(lpTitle);
73 }
74 else if (*Rest == L'/')
75 {
76 LPTSTR option;
77 Rest++;
78 option = GetParameter(&Rest);
79 if (*option == _T('?'))
80 {
81 ConOutResPaging(TRUE,STRING_START_HELP1);
82 return 0;
83 }
84 else if (_totupper(*option) == _T('D'))
85 {
86 lpDirectory = option + 1;
87 if (!*lpDirectory)
88 {
89 while (_istspace(*Rest))
90 Rest++;
91 lpDirectory = GetParameter(&Rest);
92 }
93 StripQuotes(lpDirectory);
94 }
95 else if (_totupper(*option) == _T('I'))
96 {
97 /* rest of the option is apparently ignored */
98 lpEnvironment = lpOriginalEnvironment;
99 }
100 else if (!_tcsicmp(option, _T("MIN")))
101 {
102 wShowWindow = SW_MINIMIZE;
103 }
104 else if (!_tcsicmp(option, _T("MAX")))
105 {
106 wShowWindow = SW_MAXIMIZE;
107 }
108 else if (!_tcsicmp(option, _T("AFFINITY")))
109 {
110 TCHAR *end;
111 while (_istspace(*Rest))
112 Rest++;
113 option = GetParameter(&Rest);
114 /* Affinity mask is given in hexadecimal */
115 dwAffinityMask = _tcstoul(option, &end, 16);
116 if (*end != _T('\0') || dwAffinityMask == 0 ||
117 dwAffinityMask == (DWORD)-1)
118 {
119 ConErrResPrintf(STRING_ERROR_INVALID_PARAM_FORMAT, option);
120 return 1;
121 }
122 dwCreationFlags |= CREATE_SUSPENDED;
123 }
124 else if (!_tcsicmp(option, _T("B")))
125 {
126 dwCreationFlags &= ~CREATE_NEW_CONSOLE;
127 dwCreationFlags |= CREATE_NEW_PROCESS_GROUP;
128 }
129 else if (!_tcsicmp(option, _T("LOW")))
130 {
131 dwCreationFlags |= IDLE_PRIORITY_CLASS;
132 }
133 else if (!_tcsicmp(option, _T("NORMAL")))
134 {
135 dwCreationFlags |= NORMAL_PRIORITY_CLASS;
136 }
137 else if (!_tcsicmp(option, _T("HIGH")))
138 {
139 dwCreationFlags |= HIGH_PRIORITY_CLASS;
140 }
141 else if (!_tcsicmp(option, _T("REALTIME")))
142 {
143 dwCreationFlags |= REALTIME_PRIORITY_CLASS;
144 }
145 else if (!_tcsicmp(option, _T("ABOVENORMAL")))
146 {
147 dwCreationFlags |= ABOVE_NORMAL_PRIORITY_CLASS;
148 }
149 else if (!_tcsicmp(option, _T("BELOWNORMAL")))
150 {
151 dwCreationFlags |= BELOW_NORMAL_PRIORITY_CLASS;
152 }
153 else if (!_tcsicmp(option, _T("SEPARATE")))
154 {
155 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
156 }
157 else if (!_tcsicmp(option, _T("SHARED")))
158 {
159 dwCreationFlags |= CREATE_SHARED_WOW_VDM;
160 }
161 else if (!_tcsicmp(option, _T("W")) ||
162 !_tcsicmp(option, _T("WAIT")))
163 {
164 bWait = TRUE;
165 }
166 else
167 {
168 ConErrResPrintf(STRING_TYPE_ERROR1, option);
169 return 0;
170 }
171 }
172 else
173 {
174 /* It's not an option - must be the beginning of
175 * the actual command. Leave the loop. */
176 break;
177 }
178 }
179
180 /* get comspec */
181 comspec = cmd_alloc ( MAX_PATH * sizeof(TCHAR));
182 if (comspec == NULL)
183 {
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 comspec = cmd_realloc(comspec,size * sizeof(TCHAR) );
198 if (comspec==NULL)
199 {
200 return 1;
201 }
202 size = GetEnvironmentVariable (_T("COMSPEC"), comspec, size);
203 }
204 }
205
206 nErrorLevel = 0;
207
208 if (!*Rest)
209 {
210 Rest = _T("cmd.exe");
211 }
212 else
213 /* Parsing the command that gets called by start, and it's parameters */
214 {
215 BOOL bInside = FALSE;
216
217 /* find the end of the command and put the arguments in param */
218 for (i = 0; Rest[i]; i++)
219 {
220 if (Rest[i] == _T('\"'))
221 bInside = !bInside;
222 if (_istspace(Rest[i]) && !bInside)
223 {
224 param = &Rest[i+1];
225 Rest[i] = _T('\0');
226 break;
227 }
228 }
229 }
230
231 _tcscpy(szUnquotedName, Rest);
232 StripQuotes(szUnquotedName);
233
234 /* get the PATH environment variable and parse it */
235 /* search the PATH environment variable for the binary */
236 if (SearchForExecutable(szUnquotedName, szFullName))
237 {
238 /* check if this is a .BAT or .CMD file */
239 dot = _tcsrchr(szFullName, _T('.'));
240 if (dot && (!_tcsicmp(dot, _T(".bat")) || !_tcsicmp(dot, _T(".cmd"))))
241 {
242 bBat = TRUE;
243 _stprintf(szFullCmdLine, _T("\"%s\" /K %s"), comspec, Rest);
244 TRACE ("[BATCH: %s %s]\n", debugstr_aw(szFullName), debugstr_aw(Rest));
245 }
246 else
247 {
248 TRACE ("[EXEC: %s %s]\n", debugstr_aw(szFullName), debugstr_aw(Rest));
249 _tcscpy(szFullCmdLine, szFullName);
250 }
251
252 /* build command line for CreateProcess() */
253 if (param != NULL)
254 {
255 _tcsncat(szFullCmdLine, _T(" "), CMDLINE_LENGTH - _tcslen(szFullCmdLine));
256 _tcsncat(szFullCmdLine, param, CMDLINE_LENGTH - _tcslen(szFullCmdLine));
257 }
258
259 /* fill startup info */
260 memset (&stui, 0, sizeof (STARTUPINFO));
261 stui.cb = sizeof (STARTUPINFO);
262 stui.dwFlags = STARTF_USESHOWWINDOW;
263 stui.lpTitle = lpTitle;
264 stui.wShowWindow = wShowWindow;
265
266 bCreate = CreateProcess(bBat ? comspec : szFullName,
267 szFullCmdLine, NULL, NULL, TRUE, dwCreationFlags,
268 lpEnvironment, lpDirectory, &stui, &prci);
269 if (bCreate)
270 {
271 if (dwAffinityMask)
272 {
273 SetProcessAffinityMask(prci.hProcess, dwAffinityMask);
274 ResumeThread(prci.hThread);
275 }
276 CloseHandle(prci.hThread);
277 }
278 }
279 else
280 {
281 /* The file name did not seem to be valid, but maybe it's actually a
282 * directory or URL, so we still want to pass it to ShellExecute. */
283 _tcscpy(szFullName, szUnquotedName);
284 }
285
286 if (!bCreate)
287 {
288 /* CreateProcess didn't work; try ShellExecute */
289 DWORD flags = SEE_MASK_NOCLOSEPROCESS;
290 if (!(dwCreationFlags & CREATE_NEW_CONSOLE))
291 flags |= SEE_MASK_NO_CONSOLE;
292 prci.hProcess = RunFile(flags, szFullName, param, lpDirectory, wShowWindow);
293 }
294
295 if (prci.hProcess != NULL)
296 {
297 if (bWait)
298 {
299 DWORD dwExitCode;
300 WaitForSingleObject (prci.hProcess, INFINITE);
301 GetExitCodeProcess (prci.hProcess, &dwExitCode);
302 nErrorLevel = (INT)dwExitCode;
303 }
304 CloseHandle (prci.hProcess);
305 /* Get New code page if it has change */
306 InputCodePage= GetConsoleCP();
307 OutputCodePage = GetConsoleOutputCP();
308 }
309 else
310 {
311 ErrorMessage(GetLastError (),
312 _T("Error executing CreateProcess()!!\n"));
313 }
314
315
316 cmd_free(comspec);
317 return 0;
318 }
319
320 #endif
321
322 /* EOF */