Don't print a CR, if the command line is from a batch file and starts with a '@'.
[reactos.git] / reactos / subsys / system / cmd / cmd.c
index 450e38d..bc7c05e 100644 (file)
  *
  *    06-jul-2005 (Magnus Olsen <magnus@greatlord.com>)
  *        translate '%errorlevel%' to the internal value.
- *        Add proper memmory alloc ProcessInput, the error 
+ *        Add proper memmory alloc ProcessInput, the error
  *        handling for memmory handling need to be improve
  */
 
 #include <precomp.h>
+#include <malloc.h>
 #include "resource.h"
 
 #ifndef NT_SUCCESS
 #define NT_SUCCESS(StatCode)  ((NTSTATUS)(StatCode) >= 0)
 #endif
 
-typedef NTSTATUS (STDCALL *NtQueryInformationProcessProc)(HANDLE, PROCESSINFOCLASS,
+typedef NTSTATUS (WINAPI *NtQueryInformationProcessProc)(HANDLE, PROCESSINFOCLASS,
                                                           PVOID, ULONG, PULONG);
-typedef NTSTATUS (STDCALL *NtReadVirtualMemoryProc)(HANDLE, PVOID, PVOID, ULONG, PULONG);
+typedef NTSTATUS (WINAPI *NtReadVirtualMemoryProc)(HANDLE, PVOID, PVOID, ULONG, PULONG);
 
 BOOL bExit = FALSE;       /* indicates EXIT was typed */
 BOOL bCanExit = TRUE;     /* indicates if this shell is exitable */
@@ -164,10 +165,10 @@ HANDLE hIn;
 HANDLE hOut;
 HANDLE hConsole;
 HANDLE CMD_ModuleHandle;
+HMODULE NtDllModule;
 
-static NtQueryInformationProcessProc NtQueryInformationProcessPtr;
-static NtReadVirtualMemoryProc       NtReadVirtualMemoryPtr;
-static BOOL NtDllChecked = FALSE;
+static NtQueryInformationProcessProc NtQueryInformationProcessPtr = NULL;
+static NtReadVirtualMemoryProc       NtReadVirtualMemoryPtr = NULL;
 
 #ifdef INCLUDE_CMD_COLOR
 WORD wColor;              /* current color */
@@ -193,36 +194,15 @@ static BOOL IsConsoleProcess(HANDLE Process)
        PROCESS_BASIC_INFORMATION Info;
        PEB ProcessPeb;
        ULONG BytesRead;
-       HMODULE NtDllModule;
-
-       /* Some people like to run ReactOS cmd.exe on Win98, it helps in the
-           build process. So don't link implicitly against ntdll.dll, load it
-           dynamically instead */
-       if (! NtDllChecked)
-       {
-               NtDllChecked = TRUE;
-               NtDllModule = LoadLibrary(_T("ntdll.dll"));
-               if (NULL == NtDllModule)
-               {
-                       /* Probably non-WinNT system. Just wait for the commands
-                           to finish. */
-                       NtQueryInformationProcessPtr = NULL;
-                       NtReadVirtualMemoryPtr = NULL;
-                       return TRUE;
-               }
-               NtQueryInformationProcessPtr = (NtQueryInformationProcessProc)
-                                               GetProcAddress(NtDllModule, "NtQueryInformationProcess");
-               NtReadVirtualMemoryPtr = (NtReadVirtualMemoryProc)
-                                        GetProcAddress(NtDllModule, "NtReadVirtualMemory");
-       }
 
        if (NULL == NtQueryInformationProcessPtr || NULL == NtReadVirtualMemoryPtr)
        {
                return TRUE;
        }
 
-       Status = NtQueryInformationProcessPtr(Process, ProcessBasicInformation,
-                                              &Info, sizeof(PROCESS_BASIC_INFORMATION), NULL);
+       Status = NtQueryInformationProcessPtr (
+               Process, ProcessBasicInformation,
+               &Info, sizeof(PROCESS_BASIC_INFORMATION), NULL);
        if (! NT_SUCCESS(Status))
        {
 #ifdef _DEBUG
@@ -230,8 +210,9 @@ static BOOL IsConsoleProcess(HANDLE Process)
 #endif
                return TRUE;
        }
-       Status = NtReadVirtualMemoryPtr(Process, Info.PebBaseAddress, &ProcessPeb,
-                                        sizeof(PEB), &BytesRead);
+       Status = NtReadVirtualMemoryPtr (
+               Process, Info.PebBaseAddress, &ProcessPeb,
+               sizeof(PEB), &BytesRead);
        if (! NT_SUCCESS(Status) || sizeof(PEB) != BytesRead)
        {
 #ifdef _DEBUG
@@ -252,24 +233,24 @@ static BOOL IsConsoleProcess(HANDLE Process)
 #endif
 
 typedef HINSTANCE (WINAPI *MYEX)(
-    HWND hwnd,
-    LPCTSTR lpOperation,
-    LPCTSTR lpFile,
-    LPCTSTR lpParameters,
-    LPCTSTR lpDirectory,
-    INT nShowCmd
+       HWND hwnd,
+       LPCTSTR lpOperation,
+       LPCTSTR lpFile,
+       LPCTSTR lpParameters,
+       LPCTSTR lpDirectory,
+       INT nShowCmd
 );
 
 
 
 static BOOL RunFile(LPTSTR filename)
 {
-       HMODULE         hShell32;
-       MYEX            hShExt;
-       HINSTANCE       ret;
+       HMODULE     hShell32;
+       MYEX        hShExt;
+       HINSTANCE   ret;
 
 #ifdef _DEBUG
-               DebugPrintf (_T("RunFile(%s)\n"), filename);
+       DebugPrintf (_T("RunFile(%s)\n"), filename);
 #endif
        hShell32 = LoadLibrary(_T("SHELL32.DLL"));
        if (!hShell32)
@@ -309,23 +290,110 @@ static BOOL RunFile(LPTSTR filename)
 /*
  * This command (in first) was not found in the command table
  *
- * first - first word on command line
- * rest  - rest of command line
+ * Full  - whole command line
+ * First - first word on command line
+ * Rest  - rest of command line
  */
 
 static VOID
-Execute (LPTSTR full, LPTSTR first, LPTSTR rest)
+Execute (LPTSTR Full, LPTSTR First, LPTSTR Rest)
 {
-       TCHAR szFullName[MAX_PATH];
-#ifndef __REACTOS__
+       TCHAR *szFullName=NULL;
+       TCHAR *first = NULL;
+       TCHAR *rest = NULL;
+       TCHAR *full = NULL;
+       TCHAR *dot = NULL;
        TCHAR szWindowTitle[MAX_PATH];
-#endif
        DWORD dwExitCode = 0;
 
 #ifdef _DEBUG
        DebugPrintf (_T("Execute: \'%s\' \'%s\'\n"), first, rest);
 #endif
 
+       /* we need biger buffer that First, Rest, Full are already
+          need rewrite some code to use realloc when it need instead
+          of add 512bytes extra */
+
+       first = malloc ( (_tcslen(First) + 512) * sizeof(TCHAR));
+       if (first == NULL)
+       {
+               error_out_of_memory();
+               return ;
+       }
+
+       rest = malloc ( (_tcslen(Rest) + 512) * sizeof(TCHAR));
+       if (rest == NULL)
+       {
+               free (first);
+               error_out_of_memory();
+               return ;
+       }
+
+       full = malloc ( (_tcslen(Full) + 512) * sizeof(TCHAR));
+       if (full == NULL)
+       {
+               free (first);
+               free (rest);
+               error_out_of_memory();
+               return ;
+       }
+
+       szFullName = malloc ( (_tcslen(Full) + 512) * sizeof(TCHAR));
+       if (full == NULL)
+       {
+               free (first);
+               free (rest);
+               free (full);
+               error_out_of_memory();
+               return ;
+       }
+
+
+       /* Though it was already parsed once, we have a different set of rules
+          for parsing before we pass to CreateProccess */
+       if(!_tcschr(Full,_T('\"')))
+       {
+               _tcscpy(first,First);
+               _tcscpy(rest,Rest);
+               _tcscpy(full,Full);
+       }
+       else
+       {
+               UINT i = 0;
+               BOOL bInside = FALSE;
+               rest[0] = _T('\0');
+               full[0] = _T('\0');
+               first[0] = _T('\0');
+               _tcscpy(first,Full);
+               /* find the end of the command and start of the args */
+               for(i = 0; i < _tcslen(first); i++)
+               {
+                       if(!_tcsncmp(&first[i], _T("\""), 1))
+                               bInside = !bInside;
+                       if(!_tcsncmp(&first[i], _T(" "), 1) && !bInside)
+                       {
+                               _tcscpy(rest,&first[i]);
+                               first[i] = _T('\0');
+                               break;
+                       }
+
+               }
+               i = 0;
+               /* remove any slashes */
+               while(i < _tcslen(first))
+               {
+                       if(first[i] == _T('\"'))
+                               memmove(&first[i],&first[i + 1], _tcslen(&first[i]) * sizeof(TCHAR));
+                       else
+                               i++;
+               }
+               /* Drop quotes around it just in case there is a space */
+               _tcscpy(full,_T("\""));
+               _tcscat(full,first);
+               _tcscat(full,_T("\" "));
+               _tcscat(full,rest);
+       }
+
        /* check for a drive change */
        if ((_istalpha (first[0])) && (!_tcscmp (first + 1, _T(":"))))
        {
@@ -343,6 +411,11 @@ Execute (LPTSTR full, LPTSTR first, LPTSTR rest)
 
                if (!working) ConErrResPuts (STRING_FREE_ERROR1);
 
+               free (first);
+               free (rest);
+               free (full);
+               free (szFullName);
+
                return;
        }
 
@@ -350,17 +423,20 @@ Execute (LPTSTR full, LPTSTR first, LPTSTR rest)
        /* search the PATH environment variable for the binary */
        if (!SearchForExecutable (first, szFullName))
        {
-               error_bad_command ();
-               return;
+                       error_bad_command ();
+                       free (first);
+                       free (rest);
+                       free (full);
+                       free (szFullName);
+                       return;
+
        }
 
-#ifndef __REACTOS__
        GetConsoleTitle (szWindowTitle, MAX_PATH);
-#endif
 
        /* check if this is a .BAT or .CMD file */
-       if (!_tcsicmp (_tcsrchr (szFullName, _T('.')), _T(".bat")) ||
-               !_tcsicmp (_tcsrchr (szFullName, _T('.')), _T(".cmd")))
+       dot = _tcsrchr (szFullName, _T('.'));
+       if (dot && (!_tcsicmp (dot, _T(".bat")) || !_tcsicmp (dot, _T(".cmd"))))
        {
 #ifdef _DEBUG
                DebugPrintf (_T("[BATCH: %s %s]\n"), szFullName, rest);
@@ -431,16 +507,20 @@ Execute (LPTSTR full, LPTSTR first, LPTSTR rest)
                        }
                }
                // restore console mode
-               SetConsoleMode( GetStdHandle( STD_INPUT_HANDLE ),
-                               ENABLE_PROCESSED_INPUT );
+               SetConsoleMode (
+                       GetStdHandle( STD_INPUT_HANDLE ),
+                       ENABLE_PROCESSED_INPUT );
        }
 
        /* Get code page if it has been change */
        InputCodePage= GetConsoleCP();
-    OutputCodePage = GetConsoleOutputCP();
-#ifndef __REACTOS__
+       OutputCodePage = GetConsoleOutputCP();
        SetConsoleTitle (szWindowTitle);
-#endif
+
+       free(first);
+       free(rest);
+       free(full);
+       free (szFullName);
 }
 
 
@@ -456,8 +536,8 @@ Execute (LPTSTR full, LPTSTR first, LPTSTR rest)
 static VOID
 DoCommand (LPTSTR line)
 {
-       TCHAR com[CMDLINE_LENGTH];  /* the first word in the command */
-       LPTSTR cp = com;
+       TCHAR *com = NULL;  /* the first word in the command */
+       TCHAR *cp = NULL;
        LPTSTR cstart;
        LPTSTR rest;   /* pointer to the rest of the command line */
        INT cl;
@@ -467,6 +547,14 @@ DoCommand (LPTSTR line)
        DebugPrintf (_T("DoCommand: (\'%s\')\n"), line);
 #endif /* DEBUG */
 
+       com = malloc( (_tcslen(line) +512)*sizeof(TCHAR) );
+       if (com == NULL)
+       {
+               error_out_of_memory();
+               return;
+       }
+
+       cp = com;
        /* Skip over initial white space */
        while (_istspace (*line))
                line++;
@@ -498,12 +586,15 @@ DoCommand (LPTSTR line)
                /* Terminate first word */
                *cp = _T('\0');
 
-               /* commands are limited to MAX_PATH */
+               /* Do not limit commands to MAX_PATH */
+               /*
                if(_tcslen(com) > MAX_PATH)
                {
-                 error_bad_command();
-                 return;
+                       error_bad_command();
+                       free(com);
+                       return;
                }
+               */
 
                /* Skip over whitespace to rest of line */
                while (_istspace (*rest))
@@ -551,6 +642,7 @@ DoCommand (LPTSTR line)
                        }
                }
        }
+       free(com);
 }
 
 
@@ -576,7 +668,7 @@ VOID ParseCommandLine (LPTSTR cmd)
        INT  nRedirFlags = 0;
        INT  Length;
        UINT Attributes;
-
+       BOOL bNewBatch = TRUE;
        HANDLE hOldConIn;
        HANDLE hOldConOut;
        HANDLE hOldConErr;
@@ -629,6 +721,20 @@ VOID ParseCommandLine (LPTSTR cmd)
                ;
        _tcscpy (err, t);
 
+       if(bc && !_tcslen (in) && _tcslen (bc->In))
+               _tcscpy(in, bc->In);
+       if(bc && !out[0] && _tcslen(bc->Out))
+       {
+               nRedirFlags |= OUTPUT_APPEND;
+               _tcscpy(out, bc->Out);
+       }
+       if(bc && !_tcslen (err) && _tcslen (bc->Err))
+       {
+               nRedirFlags |= ERROR_APPEND;
+               _tcscpy(err, bc->Err);
+       }
+
+
        /* Set up the initial conditions ... */
        /* preserve STDIN, STDOUT and STDERR handles */
        hOldConIn  = GetStdHandle (STD_INPUT_HANDLE);
@@ -641,6 +747,10 @@ VOID ParseCommandLine (LPTSTR cmd)
                HANDLE hFile;
                SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
 
+       /* we need make sure the LastError msg is zero before calling CreateFile */
+               SetLastError(0);
+
+       /* Set up pipe for the standard input handler */
                hFile = CreateFile (in, GENERIC_READ, FILE_SHARE_READ, &sa, OPEN_EXISTING,
                                    FILE_ATTRIBUTE_NORMAL, NULL);
                if (hFile == INVALID_HANDLE_VALUE)
@@ -672,9 +782,13 @@ VOID ParseCommandLine (LPTSTR cmd)
                /* Create unique temporary file name */
                GetTempFileName (szTempPath, _T("CMD"), 0, szFileName[1]);
 
+               /* we need make sure the LastError msg is zero before calling CreateFile */
+               SetLastError(0);
+
                /* Set current stdout to temporary file */
                hFile[1] = CreateFile (szFileName[1], GENERIC_WRITE, 0, &sa,
                                       TRUNCATE_EXISTING, FILE_ATTRIBUTE_TEMPORARY, NULL);
+
                if (hFile[1] == INVALID_HANDLE_VALUE)
                {
                        LoadString(CMD_ModuleHandle, STRING_CMD_ERROR2, szMsg, RC_STRING_MAX_SIZE);
@@ -709,6 +823,9 @@ VOID ParseCommandLine (LPTSTR cmd)
                _tcscpy (szFileName[0], szFileName[1]);
                *szFileName[1] = _T('\0');
 
+               /* we need make sure the LastError msg is zero before calling CreateFile */
+               SetLastError(0);
+
                /* open new stdin file */
                hFile[0] = CreateFile (szFileName[0], GENERIC_READ, 0, &sa,
                                       OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY, NULL);
@@ -725,14 +842,36 @@ VOID ParseCommandLine (LPTSTR cmd)
                HANDLE hFile;
                SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
 
-               hFile = CreateFile (out, GENERIC_WRITE, FILE_SHARE_READ, &sa,
+               /* we need make sure the LastError msg is zero before calling CreateFile */
+               SetLastError(0);
+
+               hFile = CreateFile (out, GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, &sa,
                                    (nRedirFlags & OUTPUT_APPEND) ? OPEN_ALWAYS : CREATE_ALWAYS,
-                                   FILE_ATTRIBUTE_NORMAL, NULL);
+                                   FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL);
+
                if (hFile == INVALID_HANDLE_VALUE)
                {
-                       LoadString(CMD_ModuleHandle, STRING_CMD_ERROR3, szMsg, RC_STRING_MAX_SIZE);
-                       ConErrPrintf(szMsg, out);
-                       return;
+                       INT size = _tcslen(out)-1;
+
+                       if (out[size] != _T(':'))
+                       {
+                               LoadString(CMD_ModuleHandle, STRING_CMD_ERROR3, szMsg, RC_STRING_MAX_SIZE);
+                               ConErrPrintf(szMsg, out);
+                               return;
+                       }
+
+                       out[size]=_T('\0');
+                       hFile = CreateFile (out, GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, &sa,
+                                           (nRedirFlags & OUTPUT_APPEND) ? OPEN_ALWAYS : CREATE_ALWAYS,
+                                           FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL);
+
+                       if (hFile == INVALID_HANDLE_VALUE)
+                       {
+                               LoadString(CMD_ModuleHandle, STRING_CMD_ERROR3, szMsg, RC_STRING_MAX_SIZE);
+                               ConErrPrintf(szMsg, out);
+                               return;
+                       }
+
                }
 
                if (!SetStdHandle (STD_OUTPUT_HANDLE, hFile))
@@ -784,10 +923,10 @@ VOID ParseCommandLine (LPTSTR cmd)
                {
                        hFile = CreateFile (err,
                                            GENERIC_WRITE,
-                                           0,
+                                           FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
                                            &sa,
                                            (nRedirFlags & ERROR_APPEND) ? OPEN_ALWAYS : CREATE_ALWAYS,
-                                           FILE_ATTRIBUTE_NORMAL,
+                                           FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
                                            NULL);
                        if (hFile == INVALID_HANDLE_VALUE)
                        {
@@ -824,12 +963,17 @@ VOID ParseCommandLine (LPTSTR cmd)
                        CloseHandle (hErr);
                hOldConErr = INVALID_HANDLE_VALUE;
        }
+
+       if(bc)
+               bNewBatch = FALSE;
 #endif
 
        /* process final command */
        DoCommand (s);
 
 #ifdef FEATURE_REDIRECTION
+       if(bNewBatch && bc)
+               AddBatchRedirection(in, out, err);
        /* close old stdin file */
 #if 0  /* buggy implementation */
        SetStdHandle (STD_INPUT_HANDLE, hOldConIn);
@@ -915,6 +1059,213 @@ VOID ParseCommandLine (LPTSTR cmd)
 #endif /* FEATURE_REDIRECTION */
 }
 
+BOOL
+GrowIfNecessary ( UINT needed, LPTSTR* ret, UINT* retlen )
+{
+       if ( *ret && needed < *retlen )
+               return TRUE;
+       *retlen = needed;
+       if ( *ret )
+               free ( *ret );
+       *ret = (LPTSTR)malloc ( *retlen * sizeof(TCHAR) );
+       if ( !*ret )
+               SetLastError ( ERROR_OUTOFMEMORY );
+       return *ret != NULL;
+}
+
+LPCTSTR
+GetEnvVarOrSpecial ( LPCTSTR varName )
+{
+       static LPTSTR ret = NULL;
+       static UINT retlen = 0;
+       UINT size;
+
+       size = GetEnvironmentVariable ( varName, ret, retlen );
+       if ( size > retlen )
+       {
+               if ( !GrowIfNecessary ( size, &ret, &retlen ) )
+                       return NULL;
+               size = GetEnvironmentVariable ( varName, ret, retlen );
+       }
+       if ( size )
+               return ret;
+
+       /* env var doesn't exist, look for a "special" one */
+       /* %CD% */
+       if (_tcsicmp(varName,_T("cd")) ==0)
+       {
+               size = GetCurrentDirectory ( retlen, ret );
+               if ( size > retlen )
+               {
+                       if ( !GrowIfNecessary ( size, &ret, &retlen ) )
+                               return NULL;
+                       size = GetCurrentDirectory ( retlen, ret );
+               }
+               if ( !size )
+                       return NULL;
+               return ret;
+       }
+       /* %TIME% */
+       else if (_tcsicmp(varName,_T("time")) ==0)
+       {
+               SYSTEMTIME t;
+               if ( !GrowIfNecessary ( MAX_PATH, &ret, &retlen ) )
+                       return NULL;
+               GetSystemTime(&t);
+               _sntprintf ( ret, retlen, _T("%02d%c%02d%c%02d%c%02d"),
+                       t.wHour, cTimeSeparator, t.wMinute, cTimeSeparator,
+                       t.wSecond, cDecimalSeparator, t.wMilliseconds );
+               return ret;
+       }
+       /* %DATE% */
+       else if (_tcsicmp(varName,_T("date")) ==0)
+       {
+               LPTSTR tmp;
+
+               if ( !GrowIfNecessary ( MAX_PATH, &ret, &retlen ) )
+                       return NULL;
+               size = GetDateFormat(LOCALE_USER_DEFAULT, 0, NULL, _T("ddd"), ret, retlen );
+               /* TODO FIXME - test whether GetDateFormat() can return a value indicating the buffer wasn't big enough */
+               if ( !size )
+                       return NULL;
+               tmp = ret + _tcslen(ret);
+               *tmp++ = _T(' ');
+               size = GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, NULL, NULL, tmp, retlen-(tmp-ret));
+               /* TODO FIXME - test whether GetDateFormat() can return a value indicating the buffer wasn't big enough */
+               if ( !size )
+                       return NULL;
+               return ret;
+       }
+
+       /* %RANDOM% */
+       else if (_tcsicmp(varName,_T("random")) ==0)
+       {
+               if ( !GrowIfNecessary ( MAX_PATH, &ret, &retlen ) )
+                       return NULL;
+               /* Get random number */
+               _itot(rand(),ret,10);
+               return ret;
+       }
+
+       /* %CMDCMDLINE% */
+       else if (_tcsicmp(varName,_T("cmdcmdline")) ==0)
+       {
+               return GetCommandLine();
+       }
+
+       /* %CMDEXTVERSION% */
+       else if (_tcsicmp(varName,_T("cmdextversion")) ==0)
+       {
+               if ( !GrowIfNecessary ( MAX_PATH, &ret, &retlen ) )
+                       return NULL;
+               /* Set version number to 2 */
+               _itot(2,ret,10);
+               return ret;
+       }
+
+       /* %ERRORLEVEL% */
+       else if (_tcsicmp(varName,_T("errorlevel")) ==0)
+       {
+               if ( !GrowIfNecessary ( MAX_PATH, &ret, &retlen ) )
+                       return NULL;
+               _itot(nErrorLevel,ret,10);
+               return ret;
+       }
+
+       return _T(""); /* not found - return empty string */
+}
+
+LPCTSTR
+GetParsedEnvVar ( LPCTSTR varName, UINT* varNameLen, BOOL ModeSetA )
+{
+       static LPTSTR ret = NULL;
+       static UINT retlen = 0;
+       LPTSTR p, tmp;
+       UINT size;
+
+       if ( varNameLen )
+               *varNameLen = 0;
+       SetLastError(0);
+       if ( *varName++ != '%' )
+               return NULL;
+       switch ( *varName )
+       {
+       case _T('0'):
+       case _T('1'):
+       case _T('2'):
+       case _T('3'):
+       case _T('4'):
+       case _T('5'):
+       case _T('6'):
+       case _T('7'):
+       case _T('8'):
+       case _T('9'):
+               if ((tmp = FindArg (*varName - _T('0'))))
+               {
+                       if ( varNameLen )
+                               *varNameLen = 2;
+                       if ( !*tmp )
+                               return _T("");
+                       if ( !GrowIfNecessary ( _tcslen(tmp)+1, &ret, &retlen ) )
+                               return NULL;
+                       _tcscpy ( ret, tmp );
+                       return ret;
+               }
+               if ( !GrowIfNecessary ( 3, &ret, &retlen ) )
+                       return NULL;
+               ret[0] = _T('%');
+               ret[1] = *varName;
+               ret[2] = 0;
+               if ( varNameLen )
+                       *varNameLen = 2;
+               return ret;
+
+       case _T('%'):
+               if ( !GrowIfNecessary ( 2, &ret, &retlen ) )
+                       return NULL;
+               ret[0] = _T('%');
+               ret[1] = 0;
+               if ( varNameLen )
+                       *varNameLen = 2;
+               return ret;
+
+       case _T('?'):
+               /* TODO FIXME 10 is only max size for 32-bit */
+               if ( !GrowIfNecessary ( 11, &ret, &retlen ) )
+                       return NULL;
+               _sntprintf ( ret, retlen, _T("%u"), nErrorLevel);
+               ret[retlen-1] = 0;
+               if ( varNameLen )
+                       *varNameLen = 2;
+               return ret;
+       }
+       if ( ModeSetA )
+       {
+               /* HACK for set/a */
+               if ( !GrowIfNecessary ( 2, &ret, &retlen ) )
+                       return NULL;
+               ret[0] = _T('%');
+               ret[1] = 0;
+               if ( varNameLen )
+                       *varNameLen = 1;
+               return ret;
+       }
+       p = _tcschr ( varName, _T('%') );
+       if ( !p )
+       {
+               SetLastError ( ERROR_INVALID_PARAMETER );
+               return NULL;
+       }
+       size = p-varName;
+       if ( varNameLen )
+               *varNameLen = size + 2;
+       p = alloca ( (size+1) * sizeof(TCHAR) );
+       memmove ( p, varName, size * sizeof(TCHAR) );
+       p[size] = 0;
+       varName = p;
+       return GetEnvVarOrSpecial ( varName );
+}
+
 
 /*
  * do the prompt/input/process loop
@@ -926,11 +1277,12 @@ ProcessInput (BOOL bFlag)
 {
        TCHAR commandline[CMDLINE_LENGTH];
        TCHAR readline[CMDLINE_LENGTH];
-       LPTSTR tp = NULL;
        LPTSTR ip;
        LPTSTR cp;
+       LPCTSTR tmp;
        BOOL bEchoThisLine;
-
+       BOOL bModeSetA;
+        BOOL bIsBatch;
 
        do
        {
@@ -943,157 +1295,109 @@ ProcessInput (BOOL bFlag)
                        ReadCommand (readline, CMDLINE_LENGTH);
                        ip = readline;
                        bEchoThisLine = FALSE;
+                        bIsBatch = FALSE;
                }
+                else
+                {
+                        bIsBatch = TRUE;
+                }
+
+               /* skip leading blanks */
+               while ( _istspace(*ip) )
+                       ++ip;
 
                cp = commandline;
+               bModeSetA = FALSE;
                while (*ip)
                {
-                       if (*ip == _T('%'))
+                       if ( *ip == _T('%') )
+                       {
+                               UINT envNameLen;
+                               LPCTSTR envVal = GetParsedEnvVar ( ip, &envNameLen, bModeSetA );
+                               if ( !envVal )
+                                       return 1;
+                               ip += envNameLen;
+                               cp = _stpcpy ( cp, envVal );
+                               continue;
+                       }
+
+                       if (_istcntrl (*ip))
+                               *ip = _T(' ');
+                       *cp++ = *ip++;
+
+                       /* HACK HACK HACK check whether bModeSetA needs to be toggled */
+                       *cp = 0;
+                       tmp = commandline;
+                       tmp += _tcsspn(tmp,_T(" \t"));
+                       /* first we find and skip and pre-redirections... */
+                       while ( _tcschr(_T("<>"),*tmp)
+                               || !_tcsncmp(tmp,_T("1>"),2)
+                               || !_tcsncmp(tmp,_T("2>"),2) )
                        {
-                               switch (*++ip)
+                               if ( _istdigit(*tmp) )
+                                       tmp += 2;
+                               else
+                                       tmp++;
+                               tmp += _tcsspn(tmp,_T(" \t"));
+                               if ( *tmp == _T('\"') )
                                {
-                                       case _T('%'):
-                                               *cp++ = *ip++;
-                                               break;
-
-                                       case _T('0'):
-                                       case _T('1'):
-                                       case _T('2'):
-                                       case _T('3'):
-                                       case _T('4'):
-                                       case _T('5'):
-                                       case _T('6'):
-                                       case _T('7'):
-                                       case _T('8'):
-                                       case _T('9'):
-                                               if ((tp = FindArg (*ip - _T('0'))))
+                                       tmp = _tcschr(tmp+1,_T('\"'));
+                                       if ( tmp )
+                                               ++tmp;
+                               }
+                               else
+                                       tmp = _tcspbrk(tmp,_T(" \t"));
+                               tmp += _tcsspn(tmp,_T(" \t"));
+                       }
+                       /* we should now be pointing to the actual command
+                        * (if there is one yet)*/
+                       if ( tmp )
+                       {
+                               /* if we're currently substituting ( which is default )
+                                * check to see if we've parsed out a set/a. if so, we
+                                * need to disable substitution until we come across a
+                                * redirection */
+                               if ( !bModeSetA )
+                               {
+                                       /* look for set /a */
+                                       if ( !_tcsnicmp(tmp,_T("set"),3) )
+                                       {
+                                               tmp += 3;
+                                               tmp += _tcsspn(tmp,_T(" \t"));
+                                               if ( !_tcsnicmp(tmp,_T("/a"),2) )
+                                                       bModeSetA = TRUE;
+                                       }
+                               }
+                               /* if we're not currently substituting, it means we're
+                                * already inside a set /a. now we need to look for
+                                * a redirection in order to turn redirection back on */
+                               else
+                               {
+                                       /* look for redirector of some kind after the command */
+                                       while ( (tmp = _tcspbrk ( tmp, _T("^<>|") )) )
+                                       {
+                                               if ( *tmp == _T('^') )
                                                {
-                                                       cp = _stpcpy (cp, tp);
-                                                       ip++;
+                                                       if ( _tcschr(_T("<>|&"), *++tmp ) && *tmp )
+                                                               ++tmp;
                                                }
                                                else
-                                                       *cp++ = _T('%');
-                                               break;
-
-                                       case _T('?'):
-                                               cp += _stprintf (cp, _T("%u"), nErrorLevel);
-                                               ip++;
-                                               break;
-
-                                       default:
-                                               tp = _tcschr(ip, _T('%'));
-                                               if ((tp != NULL) &&
-                                                   (tp <= _tcschr(ip, _T(' ')) - 1))
                                                {
-              INT size = 512;
-                                                       TCHAR *evar;
-                                                       *tp = _T('\0');
-
-              /* FIXME: Correct error handling when it can not alloc memmory */
-                               
-              /* %CD% */
-              if (_tcsicmp(ip,_T("cd")) ==0)
-              {
-                TCHAR szPath[MAX_PATH];
-                           GetCurrentDirectory (MAX_PATH, szPath);
-                cp = _stpcpy (cp, szPath);                 
-              }
-
-              /* %TIME% */
-              else if (_tcsicmp(ip,_T("time")) ==0)
-              {
-                TCHAR szTime[40];                                                               
-                GetTimeFormat(LOCALE_USER_DEFAULT, 0, NULL, NULL, szTime, sizeof(szTime));              
-                cp = _stpcpy (cp, szTime);                                   
-              }
-              
-              /* %DATE% */
-              else if (_tcsicmp(ip,_T("date")) ==0)
-              {
-              TCHAR szDate[40];
-
-                   GetDateFormat(LOCALE_USER_DEFAULT, 0, NULL, _T("ddd"), szDate, sizeof (szDate));
-              cp = _stpcpy (cp, szDate);        
-              cp = _stpcpy (cp, _T(" "));        
-              GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, NULL, NULL, szDate, sizeof (szDate));
-              cp = _stpcpy (cp, szDate);        
-              }
-
-              /* %RANDOM% */
-              else if (_tcsicmp(ip,_T("random")) ==0)
-              {
-               TCHAR szRand[40];               
-               /* Get random number */
-               _itot(rand(),szRand,10);
-               cp = _stpcpy (cp, szRand); 
-              }
-               
-         
-              /* %ERRORLEVEL% */
-              else if (_tcsicmp(ip,_T("errorlevel")) ==0)
-              {
-                evar = malloc ( size * sizeof(TCHAR));
-                if (evar==NULL) 
-                    return 1; 
-
-                memset(evar,0,512 * sizeof(TCHAR));
-                _itot(nErrorLevel,evar,10);        
-                cp = _stpcpy (cp, evar);
-
-                free(evar);
-              }               
-                                                       else 
-              {
-                evar = malloc ( size * sizeof(TCHAR));
-                if (evar==NULL) 
-                    return 1; 
-
-                size = GetEnvironmentVariable (ip, evar, size);
-                if (size!=0)
-                {
-                    evar = realloc(evar,size * sizeof(TCHAR) );
-                    if (evar!=NULL)
-                    {
-                      size = GetEnvironmentVariable (ip, evar, size);
-                    }
-                }
-
-                if (size)
-                {
-                                                                cp = _stpcpy (cp, evar);
-                }
-
-                free(evar);
-              }
-               
-                                                       ip = tp + 1;
-
+                                                       bModeSetA = FALSE;
+                                                       break;
                                                }
-                                               else
-                                               {
-                                                       *cp++ = _T('%');
-                                               }              
-           
-                                               break;
+                                       }
                                }
-                               continue;
                        }
-
-
-     
-
-                       if (_istcntrl (*ip))
-                               *ip = _T(' ');
-                       *cp++ = *ip++;
                }
 
                *cp = _T('\0');
 
                /* strip trailing spaces */
-               while ((--cp >= commandline) && _istspace (*cp));     
+               while ((--cp >= commandline) && _istspace (*cp));
 
                *(cp + 1) = _T('\0');
-   
+
                /* JPP 19980807 */
                /* Echo batch file line */
                if (bEchoThisLine)
@@ -1105,7 +1409,7 @@ ProcessInput (BOOL bFlag)
                if (*commandline)
                {
                        ParseCommandLine (commandline);
-                       if (bEcho && !bIgnoreEcho)
+                       if (bEcho && !bIgnoreEcho && (!bIsBatch || bEchoThisLine))
                                ConOutChar ('\n');
                        bIgnoreEcho = FALSE;
                }
@@ -1201,32 +1505,59 @@ Initialize (int argc, TCHAR* argv[])
        TCHAR commandline[CMDLINE_LENGTH];
        TCHAR ModuleName[_MAX_PATH + 1];
        INT i;
+       TCHAR lpBuffer[2];
 
        //INT len;
        //TCHAR *ptr, *cmdLine;
 
+       /* get version information */
+       osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+       GetVersionEx (&osvi);
+
+       /* Some people like to run ReactOS cmd.exe on Win98, it helps in the
+        * build process. So don't link implicitly against ntdll.dll, load it
+        * dynamically instead */
 
-#ifdef _DEBUG
-       INT x;
+       if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
+       {
+               /* ntdll is always present on NT */
+               NtDllModule = GetModuleHandle(TEXT("ntdll.dll"));
+       }
+       else
+       {
+               /* not all 9x versions have a ntdll.dll, try to load it */
+               NtDllModule = LoadLibrary(TEXT("ntdll.dll"));
+       }
 
+       if (NtDllModule != NULL)
+       {
+               NtQueryInformationProcessPtr = (NtQueryInformationProcessProc)GetProcAddress(NtDllModule, "NtQueryInformationProcess");
+               NtReadVirtualMemoryPtr = (NtReadVirtualMemoryProc)GetProcAddress(NtDllModule, "NtReadVirtualMemory");
+       }
+
+
+#ifdef _DEBUG
        DebugPrintf (_T("[command args:\n"));
-       for (x = 0; x < argc; x++)
+       for (i = 0; i < argc; i++)
        {
-               DebugPrintf (_T("%d. %s\n"), x, argv[x]);
+               DebugPrintf (_T("%d. %s\n"), i, argv[i]);
        }
        DebugPrintf (_T("]\n"));
 #endif
 
-       /* get version information */
-       osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
-       GetVersionEx (&osvi);
-
        InitLocale ();
 
        /* get default input and output console handles */
        hOut = GetStdHandle (STD_OUTPUT_HANDLE);
        hIn  = GetStdHandle (STD_INPUT_HANDLE);
 
+       /* Set EnvironmentVariable PROMPT if it does not exists any env value.
+          for you can change the EnvirommentVariable for prompt before cmd start
+          this patch are not 100% right, if it does not exists a PROMPT value cmd should use
+          $P$G as defualt not set EnvirommentVariable PROMPT to $P$G if it does not exists */
+       if (GetEnvironmentVariable(_T("PROMPT"),lpBuffer, sizeof(lpBuffer) / sizeof(lpBuffer[0])) == 0)
+           SetEnvironmentVariable (_T("PROMPT"), _T("$P$G"));
+
 
        if (argc >= 2 && !_tcsncmp (argv[1], _T("/?"), 2))
        {
@@ -1291,13 +1622,14 @@ Initialize (int argc, TCHAR* argv[])
                                ++i;
                                if (i < argc)
                                {
-                                       _tcscpy (commandline, argv[i]);
+                                       _tcscpy (commandline, _T("\""));
+                                       _tcscat (commandline, argv[i]);
+                                       _tcscat (commandline, _T("\""));
                                        while (++i < argc)
                                        {
                                                _tcscat (commandline, _T(" "));
                                                _tcscat (commandline, argv[i]);
                                        }
-
                                        ParseCommandLine(commandline);
                                }
                        }
@@ -1322,24 +1654,6 @@ Initialize (int argc, TCHAR* argv[])
        {
                ParseCommandLine (_T("\\cmdstart.bat"));
        }
-#ifndef __REACTOS__
-       else
-       {
-               /* try to run cmdstart.bat from install dir */
-               LPTSTR p;
-
-               _tcscpy (commandline, argv[0]);
-               p = _tcsrchr (commandline, _T('\\')) + 1;
-               _tcscpy (p, _T("cmdstart.bat"));
-
-               if (IsExistingFile (_T("commandline")))
-               {
-                       LoadString(CMD_ModuleHandle, STRING_CMD_ERROR4, szMsg, RC_STRING_MAX_SIZE);
-                       ConErrPrintf(szMsg, commandline);
-                       ParseCommandLine (commandline);
-               }
-       }
-#endif
 
 #ifdef FEATURE_DIR_STACK
        /* initialize directory stack */
@@ -1366,10 +1680,6 @@ Initialize (int argc, TCHAR* argv[])
 
 static VOID Cleanup (int argc, TCHAR *argv[])
 {
-#ifndef __REACTOS__
-       TCHAR szMsg[RC_STRING_MAX_SIZE];
-#endif
-
        /* run cmdexit.bat */
        if (IsExistingFile (_T("cmdexit.bat")))
        {
@@ -1382,25 +1692,6 @@ static VOID Cleanup (int argc, TCHAR *argv[])
                ConErrResPuts (STRING_CMD_ERROR5);
                ParseCommandLine (_T("\\cmdexit.bat"));
        }
-#ifndef __REACTOS__
-       else
-       {
-               /* try to run cmdexit.bat from install dir */
-               TCHAR commandline[CMDLINE_LENGTH];
-               LPTSTR p;
-
-               _tcscpy (commandline, argv[0]);
-               p = _tcsrchr (commandline, _T('\\')) + 1;
-               _tcscpy (p, _T("cmdexit.bat"));
-
-               if (IsExistingFile (_T("commandline")))
-               {
-                       LoadString(CMD_ModuleHandle, STRING_CMD_ERROR4, szMsg, RC_STRING_MAX_SIZE);
-                       ConErrPrintf(szMsg, commandline);
-                       ParseCommandLine (commandline);
-               }
-       }
-#endif
 
 #ifdef FEATURE_ALIASES
        DestroyAlias ();
@@ -1424,105 +1715,63 @@ static VOID Cleanup (int argc, TCHAR *argv[])
        RemoveBreakHandler ();
        SetConsoleMode( GetStdHandle( STD_INPUT_HANDLE ),
                        ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT );
-}
 
-#ifdef __REACTOS__
-#ifdef _UNICODE
-PWCHAR * _CommandLineToArgvW(PWCHAR lpCmdLine, int *pNumArgs)
-{
-   PWCHAR * argvw = NULL;
-   PWCHAR ptr = lpCmdLine;
-   PWCHAR str;
-   int len;
-   int NumArgs;
-
-   NumArgs = 0;
-
-   while(lpCmdLine && *lpCmdLine)
-   {
-       while (iswspace(*lpCmdLine)) lpCmdLine++;
-       if (*lpCmdLine)
-       {
-          if ((NumArgs % 10)==0)
-          {
-               PWCHAR * old_argvw = argvw;
-              argvw = malloc((NumArgs + 10) * sizeof(PWCHAR));
-              memcpy(argvw, old_argvw, NumArgs * sizeof(PWCHAR));
-              free(old_argvw);
-          }
-          ptr = wcschr(lpCmdLine, L' ');
-          if (ptr)
-          {
-              len = ptr - lpCmdLine;
-          }
-          else
-          {
-              len = wcslen(lpCmdLine);
-          }
-          str = malloc((len + 1) * sizeof(WCHAR));
-          memcpy(str, lpCmdLine, len * sizeof(WCHAR));
-          str[len] = 0;
-          argvw[NumArgs]=str;
-          NumArgs++;
-          lpCmdLine = ptr;
-       }
-   }
-   *pNumArgs = NumArgs;
-   return argvw;
+       if (NtDllModule != NULL)
+       {
+               FreeLibrary(NtDllModule);
+       }
 }
-#endif
-#endif
 
 /*
  * main function
  */
 #ifdef _UNICODE
-int main(void)
+int _main(void)
 #else
-int main (int argc, char *argv[])
+int _main (int argc, char *argv[])
 #endif
 {
-  CONSOLE_SCREEN_BUFFER_INFO Info;
-  INT nExitCode;
+       TCHAR startPath[MAX_PATH];
+       CONSOLE_SCREEN_BUFFER_INFO Info;
+       INT nExitCode;
 #ifdef _UNICODE
-  PWCHAR * argv;
-  int argc=0;
-#ifdef __REACTOS__
-  argv = _CommandLineToArgvW(GetCommandLineW(), &argc);
-#else
-  argv = CommandLineToArgvW(GetCommandLineW(), &argc);
-#endif
+       PWCHAR * argv;
+       int argc=0;
+       argv = CommandLineToArgvW(GetCommandLineW(), &argc);
 #endif
 
-  SetFileApisToOEM();
-  InputCodePage= 0;
-  OutputCodePage = 0;
+       GetCurrentDirectory(MAX_PATH,startPath);
+       _tchdir(startPath);
+
+       SetFileApisToOEM();
+       InputCodePage= 0;
+       OutputCodePage = 0;
 
-  hConsole = CreateFile(_T("CONOUT$"), GENERIC_READ|GENERIC_WRITE,
-                        FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
-                       OPEN_EXISTING, 0, NULL);
-  if (GetConsoleScreenBufferInfo(hConsole, &Info) == FALSE)
-    {
-      ConOutFormatMessage(GetLastError());
-      return(1);
-    }
-  wColor = Info.wAttributes;
-  wDefColor = wColor;
+       hConsole = CreateFile(_T("CONOUT$"), GENERIC_READ|GENERIC_WRITE,
+               FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
+               OPEN_EXISTING, 0, NULL);
+       if (GetConsoleScreenBufferInfo(hConsole, &Info) == FALSE)
+       {
+               ConErrFormatMessage(GetLastError());
+               return(1);
+       }
+       wColor = Info.wAttributes;
+       wDefColor = wColor;
 
-  InputCodePage= GetConsoleCP();
-  OutputCodePage = GetConsoleOutputCP();
-  CMD_ModuleHandle = GetModuleHandle(NULL);
+       InputCodePage= GetConsoleCP();
+       OutputCodePage = GetConsoleOutputCP();
+       CMD_ModuleHandle = GetModuleHandle(NULL);
 
-  /* check switches on command-line */
-  Initialize(argc, argv);
+       /* check switches on command-line */
+       Initialize(argc, argv);
 
-  /* call prompt routine */
-  nExitCode = ProcessInput(FALSE);
+       /* call prompt routine */
+       nExitCode = ProcessInput(FALSE);
 
-  /* do the cleanup */
-  Cleanup(argc, argv);
+       /* do the cleanup */
+       Cleanup(argc, argv);
 
-  return(nExitCode);
+       return(nExitCode);
 }
 
 /* EOF */