xref: /reactos/base/shell/cmd/start.c (revision 5100859e)
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 == L'/')
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 == NULL)
182     {
183         error_out_of_memory();
184         return 1;
185     }
186     SetLastError(0);
187     size = GetEnvironmentVariable (_T("COMSPEC"), comspec, MAX_PATH);
188     if (GetLastError() == ERROR_ENVVAR_NOT_FOUND)
189     {
190         _tcscpy(comspec, _T("cmd"));
191     }
192     else
193     {
194         if (size > MAX_PATH)
195         {
196             LPTSTR Oldcomspec = comspec;
197             comspec = cmd_realloc(comspec,size * sizeof(TCHAR) );
198             if (comspec==NULL)
199             {
200                 cmd_free(Oldcomspec);
201                 return 1;
202             }
203             size = GetEnvironmentVariable (_T("COMSPEC"), comspec, size);
204         }
205     }
206 
207     nErrorLevel = 0;
208 
209     if (!*Rest)
210     {
211         Rest = _T("cmd.exe");
212     }
213     else
214     /* Parsing the command that gets called by start, and it's parameters */
215     {
216         BOOL bInside = FALSE;
217         INT i;
218         /* find the end of the command and put the arguments in param */
219         for (i = 0; Rest[i]; i++)
220         {
221             if (Rest[i] == _T('\"'))
222                 bInside = !bInside;
223             if (_istspace(Rest[i]) && !bInside)
224             {
225                 param = &Rest[i+1];
226                 Rest[i] = _T('\0');
227                 break;
228             }
229         }
230     }
231 
232     _tcscpy(szUnquotedName, Rest);
233     StripQuotes(szUnquotedName);
234 
235     /* get the PATH environment variable and parse it */
236     /* search the PATH environment variable for the binary */
237     if (SearchForExecutable(szUnquotedName, szFullName))
238     {
239         /* check if this is a .BAT or .CMD file */
240         dot = _tcsrchr(szFullName, _T('.'));
241         if (dot && (!_tcsicmp(dot, _T(".bat")) || !_tcsicmp(dot, _T(".cmd"))))
242         {
243             bBat = TRUE;
244             _stprintf(szFullCmdLine, _T("\"%s\" /K %s"), comspec, Rest);
245             TRACE ("[BATCH: %s %s]\n", debugstr_aw(szFullName), debugstr_aw(Rest));
246         }
247         else
248         {
249             TRACE ("[EXEC: %s %s]\n", debugstr_aw(szFullName), debugstr_aw(Rest));
250             _tcscpy(szFullCmdLine, szFullName);
251         }
252 
253         /* build command line for CreateProcess() */
254         if (param != NULL)
255         {
256             _tcsncat(szFullCmdLine, _T(" "), CMDLINE_LENGTH - _tcslen(szFullCmdLine));
257             _tcsncat(szFullCmdLine, param, CMDLINE_LENGTH - _tcslen(szFullCmdLine));
258         }
259 
260         /* fill startup info */
261         memset (&stui, 0, sizeof (STARTUPINFO));
262         stui.cb = sizeof (STARTUPINFO);
263         stui.dwFlags = STARTF_USESHOWWINDOW;
264         stui.lpTitle = lpTitle;
265         stui.wShowWindow = wShowWindow;
266 
267         bCreate = CreateProcess(bBat ? comspec : szFullName,
268                                 szFullCmdLine, NULL, NULL, TRUE, dwCreationFlags,
269                                 lpEnvironment, lpDirectory, &stui, &prci);
270         if (bCreate)
271         {
272             if (dwAffinityMask)
273             {
274                 SetProcessAffinityMask(prci.hProcess, dwAffinityMask);
275                 ResumeThread(prci.hThread);
276             }
277             CloseHandle(prci.hThread);
278         }
279     }
280     else
281     {
282         /* The file name did not seem to be valid, but maybe it's actually a
283          * directory or URL, so we still want to pass it to ShellExecute. */
284         _tcscpy(szFullName, szUnquotedName);
285     }
286 
287     if (!bCreate)
288     {
289         /* CreateProcess didn't work; try ShellExecute */
290         DWORD flags = SEE_MASK_NOCLOSEPROCESS;
291         if (!(dwCreationFlags & CREATE_NEW_CONSOLE))
292             flags |= SEE_MASK_NO_CONSOLE;
293         prci.hProcess = RunFile(flags, szFullName, param, lpDirectory, wShowWindow);
294     }
295 
296     if (prci.hProcess != NULL)
297     {
298         if (bWait)
299         {
300             DWORD dwExitCode;
301             WaitForSingleObject (prci.hProcess, INFINITE);
302             GetExitCodeProcess (prci.hProcess, &dwExitCode);
303             nErrorLevel = (INT)dwExitCode;
304         }
305         CloseHandle (prci.hProcess);
306 
307         /* Update our local codepage cache */
308         {
309             UINT uNewInputCodePage  = GetConsoleCP();
310             UINT uNewOutputCodePage = GetConsoleOutputCP();
311 
312             if ((InputCodePage  != uNewInputCodePage) ||
313                 (OutputCodePage != uNewOutputCodePage))
314             {
315                 /* Update the locale as well */
316                 InitLocale();
317             }
318 
319             InputCodePage  = uNewInputCodePage;
320             OutputCodePage = uNewOutputCodePage;
321 
322             /* Update the streams codepage cache as well */
323             ConStreamSetCacheCodePage(StdIn , InputCodePage );
324             ConStreamSetCacheCodePage(StdOut, OutputCodePage);
325             ConStreamSetCacheCodePage(StdErr, OutputCodePage);
326         }
327     }
328     else
329     {
330         ErrorMessage(GetLastError (),
331                       _T("Error executing CreateProcess()!!\n"));
332     }
333 
334     cmd_free(comspec);
335     return 0;
336 }
337 
338 #endif
339 
340 /* EOF */
341