Fix a CD bug spotted by ravelo_. This is simlair to bug 690. cd foo\"bar", cd ...
[reactos.git] / reactos / subsys / system / cmd / internal.c
index 9d3bafe..32e7777 100644 (file)
  *        Improved chdir/cd command.
  *
  *    02-Apr-2004 (Magnus Olsen <magnus@greatlord.com>)
- *        Remove all hard code string so they can be 
+ *        Remove all hard code string so they can be
  *               translate to other langues.
+ *
+ *    19-jul-2005 (Brandon Turner <turnerb7@msu.edu)
+ *        Rewrite the CD, it working as Windows 2000 CMD
+ *             
+ *    19-jul-2005 (Magnus Olsen <magnus@greatlord.com)
+ *        Add SetRootPath and GetRootPath         
  */
 
-#include "precomp.h"
+#include <precomp.h>
 #include "resource.h"
 
 #ifdef INCLUDE_CMD_CHDIR
@@ -148,122 +154,258 @@ VOID FreeLastPath (VOID)
                free (lpLastPath);
 }
 
+/* help functions for getting current path from drive 
+   without changing drive. Return code 0 = ok, 1 = fail.
+   INT GetRootPath("C:",outbuffer,chater size of outbuffer);
+   the first param can have any size, if the the two frist
+   letter are not a drive with : it will get Currentpath on
+   current drive exacly as GetCurrentDirectory does.
+   */
+
+INT GetRootPath(TCHAR *InPath,TCHAR *OutPath,INT size)
+{
+  INT retcode = 1;
+  
+  if (_tcslen(InPath)>1)
+  {
+    if (InPath[1]==_T(':'))    
+    {   
+      INT t=0;
+     
+      if ((InPath[0] >= _T('0')) && (InPath[0] <= _T('9')))
+      {
+          t = (InPath[0] - _T('0')) +28;
+      }
+      
+      if ((InPath[0] >= _T('a')) && (InPath[0] <= _T('z')))
+      {
+          t = (InPath[0] - _T('a')) +1;
+          InPath[0] = t + _T('A') - 1;
+      }
+
+       if ((InPath[0] >= _T('A')) && (InPath[0] <= _T('Z')))
+      {
+          t = (InPath[0] - _T('A')) +1;
+      }
+                
+      if (_tgetdcwd(t,OutPath,size) != NULL) 
+      {                 
+        return 0;          
+      }    
+     } 
+    }
+   
+  /* fail */
+  if (_tcslen(InPath)>1)
+  {
+    if (InPath[1]==_T(':'))
+       return 1;  
+  }
+
+  /* Get current directory */
+  retcode = GetCurrentDirectory(size,OutPath);     
+  if (retcode==0) 
+      return 1;
+
+  return 0;
+}
+  
+
+BOOL SetRootPath(TCHAR *InPath)
+{
+  TCHAR oldpath[MAX_PATH];
+  TCHAR OutPath[MAX_PATH];
+  TCHAR OutPathUpper[MAX_PATH];
+  BOOL fail;
+  
+  
+  /* Get The current directory path and save it */
+  fail = GetCurrentDirectory(MAX_PATH,oldpath);
+  if (!fail)   
+      return 1;
+  
+  /* Get current drive directory path if C: was only pass down*/
+  
+  if (_tcsncicmp(&InPath[1],_T(":\\"),2)!=0)
+  {
+      if (!GetRootPath(InPath,OutPathUpper,MAX_PATH))
+         _tcscpy(OutPathUpper,InPath);
+  }
+  else 
+  {
+    _tcscpy(OutPathUpper,InPath);
+  }
+  
+   _tcsupr(OutPathUpper); 
+  GetLongPathName(OutPathUpper, OutPath, MAX_PATH);  
+
+  fail = SetCurrentDirectory(OutPath);
+  if (!fail) 
+      return 1;
+
+  
+    
+  SetCurrentDirectory(OutPath);
+  GetCurrentDirectory(MAX_PATH,OutPath);
+  _tchdir(OutPath);
+
+  if (_tcsncicmp(OutPath,oldpath,2)!=0)      
+      SetCurrentDirectory(oldpath);   
+
+ return 0;
+} 
+
+
 /*
  * CD / CHDIR
  *
  */
 INT cmd_chdir (LPTSTR cmd, LPTSTR param)
 {
-       LPTSTR dir;             /* pointer to the directory to change to */
-       LPTSTR lpOldPath;
-       LPTSTR endofstring; /* pointer to the null character in the directory to change to */
-       LPTSTR lastquote; /* pointer to the last quotation mark in the directory to change to */
-       WCHAR szMsg[RC_STRING_MAX_SIZE];
-
-       /*Should we better declare a variable containing _tsclen(dir) ? It's used a few times,
-         but on the other hand paths are generally not very long*/
-
-       if (!_tcsncmp (param, _T("/?"), 2))
-       {               
-               LoadString( GetModuleHandle(NULL), STRING_CD_HELP, (LPTSTR) szMsg,sizeof(szMsg));
-        ConOutPuts (_T((LPTSTR)szMsg));
+       WIN32_FIND_DATA f; 
+       HANDLE hFile;
+       BOOL bChangeDrive = FALSE;
+       TCHAR szPath[MAX_PATH];
+       TCHAR szFinalPath[MAX_PATH];
+       TCHAR * tmpPath;
+       TCHAR szCurrent[MAX_PATH];
+       TCHAR szMsg[RC_STRING_MAX_SIZE];
+       INT i;
+       /* Filter out special cases first */
+       /* Print Help */
+       if (!_tcsncmp(param, _T("/?"), 2))
+       {
+               ConOutResPaging(TRUE,STRING_CD_HELP);
                return 0;
        }
+  /* Set Error Level to Success */
+       nErrorLevel = 0;
 
-       /* The whole param string is our parameter these days. The only thing we do is eliminating every quotation mark */
-       /* Is it safe to change the characters param is pointing to? I presume it is, as there doesn't seem to be any
-       post-processing of it after the function call (what would that accomplish?) */
-
-       dir=param;
-       endofstring=dir+_tcslen(dir);
-
-       while ((lastquote = _tcsrchr(dir, _T('\"'))))
+       /* Input String Contains /D Switch */
+       if (!_tcsncicmp(param, _T("/D"), 2))
        {
-               endofstring--;
-               memmove(lastquote,lastquote+1,endofstring-lastquote);
-               *endofstring=_T('\0');
+               bChangeDrive = TRUE;
+               tmpPath = _tcsstr(param,_T(" "));
+               tmpPath++;
+               _tcscpy(szPath,tmpPath);
        }
-
-       /* if doing a CD and no parameters given, print out current directory */
-       if (!dir || !dir[0])
+       else
        {
-               TCHAR szPath[MAX_PATH];
-
-               GetCurrentDirectory (MAX_PATH, szPath);
-
-               ConOutPuts (szPath);
-
-
+               _tcscpy(szPath,param);
+       }
+       /* Print Current Directory on a disk */
+       if (_tcslen(szPath) == 2 && szPath[1] == _T(':'))
+       {               
+               if(GetRootPath(szPath,szCurrent,MAX_PATH))
+               {
+                       nErrorLevel = 1;
+                       return 1;
+               }
+               ConOutPuts(szCurrent);
                return 0;
        }
-
-       if (dir && _tcslen (dir) == 1 && *dir == _T('-'))
+       /* Get Current Directory */
+       GetRootPath(_T("."),szCurrent,MAX_PATH);
+   /* Remove " */
+       i = 0;
+       while(i < _tcslen(szPath))
        {
-               if (lpLastPath)
-                       dir = lpLastPath;
+               if(szPath[i] == _T('\"'))
+                       memmove(&szPath[i],&szPath[i + 1], _tcslen(&szPath[i]) * sizeof(TCHAR));
                else
-                       return 0;
+                       i++;
        }
-       else if (dir && _tcslen (dir)==2 && dir[1] == _T(':'))
+       tmpPath = szPath;
+       while (_istspace (*tmpPath))
+                       tmpPath++;
+       _tcscpy(szPath,tmpPath);
+       if (szPath[0] == _T('\0')) 
        {
-               TCHAR szRoot[3] = _T("A:");
-               TCHAR szPath[MAX_PATH];
-
-               szRoot[0] = _totupper (dir[0]);
-               GetFullPathName (szRoot, MAX_PATH, szPath, NULL);
-
-               /* PathRemoveBackslash */
-               if (_tcslen (szPath) > 3)
-               {
-                       LPTSTR p = _tcsrchr (szPath, _T('\\'));
-                       *p = _T('\0');
-               }
-
-               ConOutPuts (szPath);
-
-
+               ConOutPuts(szCurrent);
                return 0;
        }
-
-       /* remove trailing \ if any, but ONLY if dir is not the root dir */
-       if (_tcslen (dir) > 3 && dir[_tcslen (dir) - 1] == _T('\\'))
-               dir[_tcslen(dir) - 1] = _T('\0');
-
-
-       /* store current directory */
-       lpOldPath = (LPTSTR)malloc (MAX_PATH * sizeof(TCHAR));
-       GetCurrentDirectory (MAX_PATH, lpOldPath);
-
-       if (!SetCurrentDirectory (dir))
-       {
-               //ErrorMessage (GetLastError(), _T("CD"));
-               ConOutFormatMessage(GetLastError());
-               
-               /* throw away current directory */
-               free (lpOldPath);
-               lpOldPath = NULL;
-
+        
+
+       /* change to full path if relative path was given */
+       GetFullPathName(szPath,MAX_PATH,szFinalPath,NULL);
+       if(szFinalPath[_tcslen(szFinalPath) - 1] == _T('\\') && _tcslen(szFinalPath) > 3)
+               szFinalPath[_tcslen(szFinalPath) - 1] = _T('\0');
+       /* Handle Root Directory Alone*/
+       if (_tcslen(szFinalPath) == 3 && szFinalPath[1] == _T(':'))
+       {               
+               if(!SetRootPath(szFinalPath))
+               {
+                       /* Change prompt if it is one the same drive or /D */
+                       if(bChangeDrive || !_tcsncicmp(szFinalPath,szCurrent,1))
+                               SetCurrentDirectory(szFinalPath);
+                       return 0;
+               }
+               /* Didnt find an directories */
+               LoadString(CMD_ModuleHandle, STRING_ERROR_PATH_NOT_FOUND, szMsg, RC_STRING_MAX_SIZE);
+               ConErrPrintf(szMsg);
+               nErrorLevel = 1;
                return 1;
        }
-       else
+       /* Get a list of all the files */
+       hFile = FindFirstFile (szFinalPath, &f);
+       do
        {
-               GetCurrentDirectory(MAX_PATH, dir);
-               if (dir[0]!=lpOldPath[0])
-               {
-                       SetCurrentDirectory(lpOldPath);
-                       free(lpOldPath);
+               if(hFile == INVALID_HANDLE_VALUE)
+               {               
+                       ConErrFormatMessage (GetLastError(), szFinalPath);                      
+                       nErrorLevel = 1;
+                       return 1;
                }
-               else
-               {
-                       if (lpLastPath)
-                               free (lpLastPath);
-                       lpLastPath = lpOldPath;
+               /* Strip the paths back to the folder they are in */
+               for(i = (_tcslen(szFinalPath) -  1); i > -1; i--)
+                       if(szFinalPath[i] != _T('\\'))
+                               szFinalPath[i] = _T('\0');
+                       else
+                               break;
+               _tcscat(szFinalPath,f.cFileName);      
+               
+               if ((f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ==  FILE_ATTRIBUTE_DIRECTORY)
+    {                         
+                       if(!SetRootPath(szFinalPath))
+                       {
+                               /* Change for /D */
+                               if(bChangeDrive)
+        {   
+           _tcsupr(szFinalPath); 
+           GetLongPathName(szFinalPath, szPath, MAX_PATH);  
+                                        SetCurrentDirectory(szPath);
+        }
+                               return 0;
+                       }
+      
                }
-       }
-
-
-       return 0;
+       }while(FindNextFile (hFile, &f));
+       /* Didnt find an directories */
+       LoadString(CMD_ModuleHandle, STRING_ERROR_PATH_NOT_FOUND, szMsg, RC_STRING_MAX_SIZE);
+       ConErrPrintf(szMsg);
+       nErrorLevel = 1;
+       return 1;
 }
+
 #endif
 
 
@@ -279,13 +421,10 @@ INT cmd_mkdir (LPTSTR cmd, LPTSTR param)
        LPTSTR place;   /* used to search for the \ when no space is used */
        LPTSTR *p = NULL;
        INT argc;
-    WCHAR szMsg[RC_STRING_MAX_SIZE];
 
        if (!_tcsncmp (param, _T("/?"), 2))
        {
-               LoadString( GetModuleHandle(NULL), STRING_MKDIR_HELP, (LPTSTR) szMsg,sizeof(szMsg));
-        ConOutPuts (_T((LPTSTR)szMsg));
-
+               ConOutResPaging(TRUE,STRING_MKDIR_HELP);
                return 0;
        }
 
@@ -320,8 +459,7 @@ INT cmd_mkdir (LPTSTR cmd, LPTSTR param)
 
        if (!dir)
        {
-               LoadString( GetModuleHandle(NULL), STRING_PARAM_ERROR, (LPTSTR) szMsg,sizeof(szMsg));
-               ConErrPrintf (_T((LPTSTR)szMsg));
+               ConErrResPuts (STRING_ERROR_REQ_PARAM_MISSING);
                return 1;
        }
 
@@ -357,15 +495,9 @@ INT cmd_rmdir (LPTSTR cmd, LPTSTR param)
        LPTSTR *p = NULL;
        INT argc;
 
-       
-    WCHAR szMsg[RC_STRING_MAX_SIZE];
-
        if (!_tcsncmp (param, _T("/?"), 2))
        {
-               LoadString( GetModuleHandle(NULL), STRING_RMDIR_HELP, (LPTSTR) szMsg,sizeof(szMsg));
-
-               ConOutPuts (_T((LPTSTR)szMsg));
-
+               ConOutResPaging(TRUE,STRING_RMDIR_HELP);
                return 0;
        }
 
@@ -399,9 +531,7 @@ INT cmd_rmdir (LPTSTR cmd, LPTSTR param)
 
        if (!dir)
        {
-               LoadString( GetModuleHandle(NULL), STRING_PARAM_ERROR, (LPTSTR) szMsg,sizeof(szMsg));
-               ConErrPrintf (_T((LPTSTR)szMsg));
-
+               ConErrResPuts(STRING_ERROR_REQ_PARAM_MISSING);
                return 1;
        }
 
@@ -430,19 +560,26 @@ INT cmd_rmdir (LPTSTR cmd, LPTSTR param)
  */
 INT CommandExit (LPTSTR cmd, LPTSTR param)
 {
-       WCHAR szMsg[RC_STRING_MAX_SIZE];
-
        if (!_tcsncmp (param, _T("/?"), 2))
+               ConOutResPaging(TRUE,STRING_EXIT_HELP);
+
+       if (bc != NULL && _tcsnicmp(param,_T("/b"),2) == 0)
        {
-               LoadString( GetModuleHandle(NULL), STRING_EXIT_HELP, (LPTSTR) szMsg,sizeof(szMsg));
-        ConOutPuts (_T((LPTSTR)szMsg));
-               return 0;
+               param += 2;
+               while (_istspace (*param))
+                       param++;
+               if (_istdigit(*param))
+                       nErrorLevel = _ttoi(param);
+               ExitBatch (NULL);
        }
+               
+       else
+               bExit = TRUE;
 
-       bExit = TRUE;
+       
        return 0;
-}
 
+}
 
 #ifdef INCLUDE_CMD_REM
 /*
@@ -451,12 +588,9 @@ INT CommandExit (LPTSTR cmd, LPTSTR param)
  */
 INT CommandRem (LPTSTR cmd, LPTSTR param)
 {
-       WCHAR szMsg[RC_STRING_MAX_SIZE];
-
        if (!_tcsncmp (param, _T("/?"), 2))
        {
-         LoadString( GetModuleHandle(NULL), STRING_REM_HELP, (LPTSTR) szMsg,sizeof(szMsg));
-      ConOutPuts (_T((LPTSTR)szMsg));
+               ConOutResPaging(TRUE,STRING_REM_HELP);
        }
 
        return 0;
@@ -470,4 +604,10 @@ INT CommandShowCommands (LPTSTR cmd, LPTSTR param)
        return 0;
 }
 
+INT CommandShowCommandsDetail (LPTSTR cmd, LPTSTR param)
+{
+       PrintCommandListDetail ();
+       return 0;
+}
+
 /* EOF */