xref: /reactos/base/shell/cmd/start.c (revision db219e45)
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) */
GetParameter(TCHAR ** pPointer)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 
cmd_start(LPTSTR Rest)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_ERROR, 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 the local code page cache */
309         {
310             UINT uNewInputCodePage  = GetConsoleCP();
311             UINT uNewOutputCodePage = GetConsoleOutputCP();
312 
313             if ((InputCodePage  != uNewInputCodePage) ||
314                 (OutputCodePage != uNewOutputCodePage))
315             {
316                 InputCodePage  = uNewInputCodePage;
317                 OutputCodePage = uNewOutputCodePage;
318 
319                 /* Reset the current thread UI language */
320                 if (IsConsoleHandle(ConStreamGetOSHandle(StdOut)) ||
321                     IsConsoleHandle(ConStreamGetOSHandle(StdErr)))
322                 {
323                     ConSetThreadUILanguage(0);
324                 }
325                 /* Update the streams cached code page */
326                 ConStdStreamsSetCacheCodePage(InputCodePage, OutputCodePage);
327 
328                 /* Update the locale as well */
329                 InitLocale();
330             }
331         }
332     }
333     else
334     {
335         ErrorMessage(GetLastError (),
336                       _T("Error executing CreateProcess()!!\n"));
337     }
338 
339     cmd_free(comspec);
340     return 0;
341 }
342 
343 #endif
344 
345 /* EOF */
346