:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / subsys / system / shell / shell.c
1 /* $Id$
2  *
3  * PROJECT    : ReactOS Operating System
4  * DESCRIPTION: ReactOS' Native Shell
5  * LICENSE    : See top level directory
6  *
7  */
8 #define NTOS_MODE_USER
9 #include <ntos.h>
10 #include <windows.h>
11 #include <stdarg.h>
12 #include <string.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <ctype.h>
16
17
18 HANDLE InputHandle, OutputHandle;
19 BOOL   bCanExit = TRUE, bCanLinespace = TRUE;
20
21
22 void debug_printf(char* fmt, ...)
23 {
24    va_list args;
25    char buffer[512];
26    DWORD nbChar;
27
28    va_start(args,fmt);
29    _vsnprintf(buffer,512,fmt,args);
30    va_end(args);
31    WriteConsoleA(OutputHandle, buffer, strlen(buffer), &nbChar, NULL);
32 }
33
34 void ExecuteCls(void)
35 {
36    DWORD dwWritten;
37    CONSOLE_SCREEN_BUFFER_INFO csbi;
38    COORD coPos;
39
40    GetConsoleScreenBufferInfo (OutputHandle, &csbi);
41
42    coPos.X = 0;
43    coPos.Y = 0;
44    FillConsoleOutputAttribute (OutputHandle, csbi.wAttributes, (csbi.dwSize.X)*(csbi.dwSize.Y), coPos, &dwWritten);
45    FillConsoleOutputCharacter (OutputHandle, _T(' '), (csbi.dwSize.X)*(csbi.dwSize.Y), coPos, &dwWritten);
46    SetConsoleCursorPosition (OutputHandle, coPos);
47 }
48
49 void ExecuteVer(void)
50 {
51     debug_printf(
52         "Reactos Simple Shell\n(compiled on %s, at %s)\n",
53         __DATE__,
54         __TIME__
55         );
56 }
57
58 void ExecuteCd(char* cmdline)
59 {
60    if (!SetCurrentDirectoryA(cmdline))
61      {
62         debug_printf("Invalid directory\n");
63      }
64 }
65
66 void ExecuteMd (char *cmdline)
67 {
68    if (!CreateDirectoryA (cmdline, NULL))
69      {
70         debug_printf("Create Directory failed!\n");
71      }
72 }
73
74 void ExecuteDir(char* cmdline)
75 {
76    HANDLE shandle;
77    WIN32_FIND_DATA FindData;
78    int nFile=0, nRep=0;
79    FILETIME fTime;
80    SYSTEMTIME sTime;
81
82    shandle = FindFirstFile("*",&FindData);
83
84    if (shandle==INVALID_HANDLE_VALUE)
85      {
86         debug_printf("File not found\n");
87         return;
88      }
89    do
90      {
91         debug_printf("%-15.15s",FindData.cAlternateFileName);
92         if(FindData.dwFileAttributes &FILE_ATTRIBUTE_DIRECTORY)
93           debug_printf("<DIR>       "),nRep++;
94         else
95           debug_printf(" %10d ",FindData.nFileSizeLow),nFile++;
96
97         FileTimeToLocalFileTime(&FindData.ftLastWriteTime ,&fTime);
98         FileTimeToSystemTime(&fTime, &sTime);
99         debug_printf("%02d/%02d/%04d %02d:%02d:%02d "
100             ,sTime.wMonth,sTime.wDay,sTime.wYear
101             ,sTime.wHour,sTime.wMinute,sTime.wSecond);
102
103         debug_printf("%s\n",FindData.cFileName);
104      } while(FindNextFile(shandle,&FindData));
105    debug_printf("\n    %d files\n    %d directories\n",nFile,nRep);
106    FindClose(shandle);
107 }
108
109
110 void ExecuteReboot(char* cmdline)
111 {
112    NtShutdownSystem (ShutdownReboot);
113 }
114
115
116 void ExecuteShutdown(char* cmdline)
117 {
118    NtShutdownSystem (ShutdownNoReboot);
119 }
120
121
122 void ExecuteType(char* cmdline)
123 {
124    HANDLE FileHandle;
125    char c;
126    DWORD Result;
127    BOOL Success;
128
129    FileHandle = CreateFile(cmdline,
130                            FILE_GENERIC_READ,
131                            0,
132                            NULL,
133                            OPEN_EXISTING,
134                            0,
135                            NULL);
136    if (FileHandle == NULL)
137      {
138         debug_printf("Unknown file\n");
139         return;
140      }
141    do
142      {
143        Success = ReadFile(FileHandle,
144                           &c,
145                           1,
146                           &Result,
147                           NULL);
148        if (Success)
149          {
150            debug_printf("%c",c);
151            c = 0;
152          }
153      } while (Success && Result > 0);
154    CloseHandle(FileHandle);
155 }
156
157 int ExecuteProcess(char* name, char* cmdline, BOOL detached)
158 {
159    PROCESS_INFORMATION  ProcessInformation;
160    STARTUPINFO          StartupInfo;
161    BOOL                 ret;
162    CHAR                 fullname[260];
163    PCHAR                p;
164    
165    /* append '.exe' if needed */
166    strcpy (fullname, name);
167    p = strrchr (fullname, '.');
168    if ((p == NULL) ||
169        (_stricmp (p, ".exe") != 0))
170      {
171         strcat (fullname, ".exe");
172      }
173
174    memset(&StartupInfo, 0, sizeof(StartupInfo));
175    StartupInfo.cb = sizeof (STARTUPINFO);
176    StartupInfo.lpTitle = name;
177    if( cmdline && *cmdline  )
178      *(cmdline-1) = ' ';  // fix command line so it is the FULL command, including exe name
179    ret = CreateProcessA(fullname,
180                         name,
181                         NULL,
182                         NULL,
183                         FALSE,
184                         ((TRUE == detached)
185                          ? CREATE_NEW_CONSOLE
186                         : 0
187                         ),
188                         NULL,
189                         NULL,
190                         & StartupInfo,
191                         & ProcessInformation
192                         );
193    if (TRUE == detached)
194      {
195         if (ret)
196           {
197              debug_printf("\"%s\" detached:\n"
198                           "\thProcess = %08X\n"
199                           "\thThread  = %08X\n"
200                           "\tPID      = %d\n"
201                           "\tTID      = %d\n\n",
202                           fullname,
203                           ProcessInformation.hProcess,
204                           ProcessInformation.hThread,
205                           ProcessInformation.dwProcessId,
206                           ProcessInformation.dwThreadId);
207              CloseHandle(ProcessInformation.hProcess);
208              CloseHandle(ProcessInformation.hThread);
209           }
210         else
211           {
212              debug_printf("Could not detach %s\n", name);
213           }
214      }
215    else
216      {
217         if (ret)
218           {
219 //              debug_printf("ProcessInformation.hThread %x\n",
220 //                           ProcessInformation.hThread);
221              WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
222              CloseHandle(ProcessInformation.hProcess);
223 //           debug_printf("Thandle %x\n", ProcessInformation.hThread);
224              CloseHandle(ProcessInformation.hThread);
225           }
226      }
227    return(ret);
228 }
229
230 void ExecuteStart(char* CommandLine)
231 {
232            char *ImageName = CommandLine;
233
234            for (        ;
235                         (       (*CommandLine)
236                                 && (*CommandLine != ' ')
237                                 && (*CommandLine != '\t')
238                                 );
239                         CommandLine++
240                 );
241            *CommandLine++ = '\0';
242            while (      (*CommandLine)
243                         && (    (*CommandLine == ' ')
244                                 || (*CommandLine == '\t')
245                                 )
246                         );
247            ExecuteProcess(
248                 ImageName,
249                 CommandLine,
250                 TRUE
251                 );
252            return;
253 }
254
255 void
256 ExecuteKill(char * lpPid)
257 {
258         HANDLE  hProcess;
259         DWORD   dwProcessId;
260
261         dwProcessId = (DWORD) atol(lpPid);
262         debug_printf("Killing PID %d...\n",dwProcessId);
263         hProcess = OpenProcess(
264                         PROCESS_TERMINATE,
265                         FALSE,
266                         dwProcessId
267                         );
268         if (NULL == hProcess)
269         {
270                 debug_printf(
271                         "Could not open the process with PID = %d\n",
272                         dwProcessId
273                         );
274                 return;
275         }
276         if (FALSE == TerminateProcess(
277                         hProcess,
278                         0
279                         )
280         ) {
281                 debug_printf(
282                         "Could not terminate the process with PID = %d\n",
283                         dwProcessId
284                         );
285         }
286         CloseHandle(hProcess);
287         return;
288 }
289
290 void ExecuteHelp (void * dummy)
291 {
292         debug_printf (
293                 "A:\t\t\tCurrent drive is A:\n"\r
294                 "C:\t\t\tCurrent drive is C:\n"
295                 "cd [directory]\t\tChange current directory\n"
296                 "dir [directory]\t\tList directory\n"
297                 "exit\t\t\tTerminate the shell\n"
298                 "help\t\t\tPrint this help message\n"
299                 "kill [pid]\t\tKill process which PID is pid\n"
300                 "md [directory]\t\tCreate a new directory\n"
301                 "reboot\t\t\tRestart the system\n"
302                 "start [program.exe]\tDetach program.exe\n"
303                 "type [file]\t\tPrint the file on console\n"
304                 "validate\t\tValidate the process' heap\n"
305                 "ver\t\t\tPrint version information\n"
306                 "[program.exe]\t\tStart synchronously program.exe\n"
307                 );
308 }
309
310 void ExecuteCommand(char* line)
311 {
312    char* cmd;
313    char* tail;
314
315    if (isalpha(line[0]) && line[1] == ':' && line[2] == 0)
316      {
317         char szPath[MAX_PATH];
318         char szVar[5];
319
320         /* save curent directory in environment variable */
321         GetCurrentDirectory (MAX_PATH, szPath);
322
323         strcpy (szVar, "=A:");
324         szVar[1] = toupper (szPath[0]);
325         SetEnvironmentVariable (szVar, szPath);
326
327         /* check for current directory of new drive */
328         strcpy (szVar, "=A:");
329         szVar[1] = toupper (line[0]);
330         if (GetEnvironmentVariable (szVar, szPath, MAX_PATH) == 0)
331           {
332              /* no environment variable found */
333              strcpy (szPath, "A:\\");
334              szPath[0] = toupper (line[0]);
335           }
336
337         /* set new current directory */
338         SetCurrentDirectory (szPath);
339         GetCurrentDirectory (MAX_PATH, szPath);
340         if (szPath[0] != (char)toupper (line[0]))
341              debug_printf("Invalid drive\n");
342
343         return;
344      }
345
346    tail = line;
347    while ((*tail)!=' ' && (*tail)!=0)
348      {
349         tail++; 
350      }
351    if ((*tail)==' ')
352      {
353         *tail = 0;
354         tail++;
355      }
356    cmd = line;
357
358
359    if (cmd==NULL || *cmd == '\0' )
360      {
361         return;
362      }
363    if (strcmp(cmd,"cd")==0)
364      {
365         ExecuteCd(tail);
366         return;
367      }
368    if (strcmp(cmd,"dir")==0)
369      {
370         ExecuteDir(tail);
371         return;
372      }
373    if (strcmp(cmd,"kill")==0)
374      {
375         ExecuteKill(tail);
376         return;
377      }
378    if (strcmp(cmd,"md")==0)
379      {
380         ExecuteMd(tail);
381         return;
382      }
383    if (strcmp(cmd,"reboot")==0)
384      {
385         ExecuteReboot(tail);
386         return;
387      }
388    if (strcmp(cmd,"shutdown")==0)
389      {
390         ExecuteShutdown(tail);
391         return;
392      }
393    if (strcmp(cmd,"type")==0)
394      {
395         ExecuteType(tail);
396         return;
397      }
398    if (strcmp(cmd,"ver")==0)
399      {
400         ExecuteVer();
401         return;
402      }
403    if (strcmp(cmd,"validate")==0)
404      {
405         debug_printf("Validating heap...");
406         if (HeapValidate(GetProcessHeap(),0,NULL))
407           {
408              debug_printf("succeeded\n");
409           }
410         else
411           {
412              debug_printf("failed\n");
413           }
414         return;
415      }
416    if (strcmp(cmd,"start") == 0)
417      {
418         ExecuteStart(tail);
419         return;
420      }
421    if ((strcmp(cmd,"help") * strcmp(cmd,"?")) == 0)
422      {
423         ExecuteHelp(tail);
424         return;
425      }
426    if (strcmp(cmd,"exit")==0)
427      {
428         if (bCanExit == TRUE)
429           {
430              ExitProcess(0);
431           }
432         return;
433      }
434    if (strcmp(cmd,"cls") == 0)
435      {
436         ExecuteCls();
437         bCanLinespace = FALSE;
438         return;
439      }
440    if (ExecuteProcess(cmd,tail,FALSE))
441      {
442         return;
443      }
444    debug_printf("Unknown command '%s'\n", cmd);
445 }
446
447 void ReadLine(char* line)
448 {
449    DWORD Result;
450    UCHAR CurrentDir[255];
451
452    GetCurrentDirectoryA(255,CurrentDir);
453    debug_printf("%s>", CurrentDir);
454    if ( !ReadConsoleA(InputHandle, line, 252, &Result, NULL) )
455       {
456          debug_printf("Failed to read from console\n");
457          for(;;);
458       }
459    // If the \n is in there, so is the \r so null it, otherwise, null the last char
460    if( line[Result-1] == '\n' )
461       line[Result-2] = 0;
462    else {
463      line[Result] = 0;
464    }
465 }
466
467 void ParseCommandLine (void)
468 {
469    char *pszCommandLine;
470
471    pszCommandLine = GetCommandLineA ();
472    _strupr (pszCommandLine);
473    if (strstr (pszCommandLine, "/P") != NULL)
474      {
475         debug_printf("Permanent shell\n");
476         bCanExit = FALSE;
477      }
478 }
479
480 int main(void)
481 {
482    static char line[256];
483    
484    AllocConsole();
485    InputHandle = GetStdHandle(STD_INPUT_HANDLE);
486    OutputHandle = GetStdHandle(STD_OUTPUT_HANDLE);
487
488    ParseCommandLine ();
489
490    for(;;)
491      {
492         ReadLine(line);
493         ExecuteCommand(line);
494         if(bCanLinespace)
495         {
496            printf("\n");
497         } else
498            bCanLinespace = TRUE;
499      }
500
501    return 0;
502 }
503
504 /* EOF */