userlibandfileserver/fileserver/etshell/ts_edshl.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:26:05 +0100
branchRCL_3
changeset 136 743008598095
parent 33 0173bcd7697c
child 249 a179b74831c9
child 254 1560c419b176
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 1995-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// f32\etshell\ts_edshl.cpp
// CLineEdit and CShell code
// 
//


#include <e32hal.h>
#include "ts_std.h"
#include "ts_clicomp.h"
#include "cleanuputils.h"

CLineEdit* CShell::TheEditor;
CFileMan* CShell::TheFileMan;
TBuf<KMaxFileName> CShell::currentPath;
RFs CShell::TheFs;
CConsoleBase* CShell::TheConsole;
CCliCompleter* CShell::TheCliCompleter;

class CFileManObserver : public MFileManObserver
	{
	TControl NotifyFileManEnded();
	};

MFileManObserver::TControl CFileManObserver::NotifyFileManEnded()
//
// Print out what CFileMan is doing
//
	{

	TInt err=CShell::TheFileMan->GetLastError();
	TFileName srcfile;
	CShell::TheFileMan->GetCurrentSource(srcfile);
	if (err!=KErrNone)
		{
		if(err == KErrNotReady)
			{
			FOREVER
				{
				CShell::TheConsole->Printf(_L("Not ready - Retry? [y/n]\n"));
				TChar key = CShell::TheConsole->Getch();
				key.UpperCase();
				if(key == 'Y')
					{
					return(MFileManObserver::ERetry);
					}
				if(key == 'N')
					{
					return(MFileManObserver::EAbort);
					}
				}
			}
		else
			{
			CShell::TheConsole->Printf(_L("Error %d\n"),err);
			}
		}
	else
		{			
		switch (CShell::TheFileMan->CurrentAction())
			{
			case CFileMan::ECopy:
				CShell::TheConsole->Printf(_L("Copied %S\n"),&srcfile);
				break;
			case CFileMan::EAttribs:
				CShell::TheConsole->Printf(_L("Setting Attributes for %S\n"),&srcfile);
				break;
			case CFileMan::EDelete:
				CShell::TheConsole->Printf(_L("Deleted %S\n"),&srcfile);
				break;
			case CFileMan::EMove:
				CShell::TheConsole->Printf(_L("Moved %S\n"),&srcfile);
				break;
			case CFileMan::ERename:
				CShell::TheConsole->Printf(_L("Renamed %S\n"),&srcfile);
				break;
			case CFileMan::ERmDir:
				CShell::TheConsole->Printf(_L("RmDir deleted %S\n"),&srcfile);
				break;
			default:
				CShell::TheConsole->Printf(_L("Unknown action %S\n"),&srcfile);
				break;
				}
		}
	return(MFileManObserver::EContinue);
	}

LOCAL_C TInt charToDrive(TChar aChar,TInt& aDrive)
//
// Convert the drive character to a drive index.
//
	{

	TInt r=RFs::CharToDrive(aChar,aDrive);
	return(r);
	}

CLineEdit::CLineEdit()
//
// Constructor
//
	{
	}


CLineEdit::~CLineEdit()
//
// Destroy the line editor
//
	{

    if (iHistory)
        {
        TInt count=iHistory->Count();
        while (count--)
            User::Free((*iHistory)[count]);
        delete iHistory;
        }
	}

CLineEdit* CLineEdit::NewL(CConsoleBase* aConsole,TInt aMaxHistory)
//
// Create a new line editor
//
	{

	CLineEdit* pE=new(ELeave) CLineEdit;
    CleanupStack::PushL(pE);
	pE->iHistory=new(ELeave) CArrayFixFlat<HBufC*>(aMaxHistory+2);
	pE->iConsole=aConsole;
	pE->iMaxHistory=aMaxHistory;
	pE->iWidth=CShell::TheConsole->ScreenSize().iWidth;
	pE->iHeight=CShell::TheConsole->ScreenSize().iHeight;
	
	// !E32WindowServer Text Shell Console has frame 
	// of 2 characters each vertically and horizontally.
	// !Windowserver Shell Console does not.
	// Assume no Frame is present !
	TFindServer findServer;
	TFullName wsName;
	TInt err = KErrNone;
	
	pE->iFrameSizeChar=TSize(0,0);
	
	// Find: !E32WindowServer is running?
	while (err=findServer.Next(wsName), err==KErrNone)
		{
		if(err=wsName.FindF(KE32WindowServer), err!=KErrNotFound)
			{
			// E32WindowServer is running. 
			// Frame is present ! Frame Size is (2,2) !
			pE->iFrameSizeChar=TSize(2,2);
			break;
			}
		}
	
    CleanupStack::Pop();
	return(pE);
	}

TInt CLineEdit::Lines()
//
// The number of lines being edited.
//
    {

    TInt nL=1;
    if (Buf().Length()>=iWidth-iFrameSizeChar.iWidth-iOrigin)
		nL+=(Buf().Length()+iOrigin)/(iWidth-iFrameSizeChar.iWidth);
    return(nL);
    }

TPoint CLineEdit::Where()
//
// Return the real cursor position.
//
    {

    if (iPos>=(iWidth-iFrameSizeChar.iWidth-iOrigin))
		return(TPoint((iPos+iOrigin)%(iWidth-iFrameSizeChar.iWidth),((iPos+iOrigin)/(iWidth-iFrameSizeChar.iWidth))+iLine));
	return(TPoint(iPos+iOrigin,iLine));
    }

void CLineEdit::ClearLine()
//
// Clears the line being edited.
//
    {

    if (Buf().Length())
		{
		TInt nL=Lines();
		while (nL--)
	    	{
	    	iConsole->SetPos(nL ? 0 : iOrigin,iLine+nL);
	    	iConsole->ClearToEndOfLine();
	    	}
		Buf().Zero();
		iPos=0;
		}
    }

void CLineEdit::ClearLast(TInt aCnt)
//
// Clears the last aCnt characters.
//
    {

    TInt aPos=iPos;
    iPos=((TInt)Buf().Length())-aCnt;
    while (iPos<((TInt)Buf().Length()))
		{
		TPoint p=Where();
		iConsole->SetCursorPosAbs(p);
		iConsole->ClearToEndOfLine();
		iPos+=(iWidth-p.iX);
		}
    iPos=aPos;
    }

void CLineEdit::Recall()
//
// Recall a line for editing.
//
    {

	if (iRecall!=(-1))
		{
		ClearLine();
		HBufC* pL=(*iHistory)[iRecall];
		Buf()=(*pL);
		iConsole->Write(Buf());
		iPos=Buf().Length();
		TInt nL=Lines();
		if ((iLine+nL)>iHeight)
			iLine -= iLine + nL - iHeight;	

		}
    }

TInt CLineEdit::WordLeft()
//
// Position the cursor to the next word left.
//
    {

    TInt x=iPos-1;
    while (x && TChar(Buf()[x]).IsSpace())
		x--;
    while (x && TChar(Buf()[x]).IsGraph())
		x--;
    if (TChar(Buf()[x]).IsSpace())
		x++;
    return(x);
    }

TInt CLineEdit::WordRight()
//
// Position the cursor to the next word right.
//
    {

    TInt x=iPos;
    while (x<(TInt)Buf().Length() && TChar(Buf()[x]).IsGraph())
		x++;
    while (x<(TInt)Buf().Length() && TChar(Buf()[x]).IsSpace())
		x++;
    return(x);
    }

void CLineEdit::Cursor()
//
// Position the cursor.
//
    {

    iConsole->SetCursorPosAbs(Where());
    }

void CLineEdit::Refresh()
//
// Refresh the line.
//
    {

	iConsole->SetCursorHeight(ECursorNone);
    iConsole->SetPos(iOrigin,iLine);
    iConsole->Write(Buf());
	Cursor();
	iConsole->SetCursorHeight(iMode==EEditOverWrite ? ECursorNormal : ECursorInsert);
    }
       
TLineEditAction CLineEdit::Edit(const TDesC& aPrompt, TDes* aBuf, TBool aNewLine)
//
// Start the editor or a single key fetch.
//
	{
	iBuf = aBuf;
	
	if(aNewLine)
		{
		iMode = EEditInsert;
		iConsole->Write(aPrompt);
		iConsole->SetCursorHeight(iMode == EEditOverWrite ? ECursorNormal : ECursorInsert);
		iOrigin = iConsole->WhereX();
		iLine = iConsole->WhereY();  
		}

	if(iBuf->Length() == 0)
		{			
		iPos = 0;
		}
	
	Refresh();
		
	iRecall = (-1);
	TInt hCount = iHistory->Count();
	
	if (hCount > iMaxHistory)
		{
		hCount = iMaxHistory;
		}
	
	FOREVER
		{
		TChar gChar = iConsole->Getch();
		
		switch (gChar)
	    	{
		case EKeyEscape:
	    	ClearLine();
			iRecall=(-1);
	    	break;
		case EKeyHome:
	    	iPos=0;
	    	Cursor();
	    	break;
		case EKeyLeftArrow:
	    	if (iPos)
                {
                if(iConsole->KeyModifiers()==EModifierCtrl)
                    iPos=WordLeft();
                else
    				iPos--;
                Cursor();
                }
	    	break;
		case EKeyRightArrow:
	    	if (iPos<((TInt)Buf().Length()))
                {
                if(iConsole->KeyModifiers()==EModifierCtrl)
                    iPos=WordRight();
                else
    				iPos++;
                Cursor();
                }
	    	break;
		case EKeyEnd:
	    	iPos=((TInt)Buf().Length());
	    	Cursor();
	    	break;
		case EKeyPageUp:
	    	if (hCount==0)
				break;
	    	iRecall=hCount-1;
	    	Recall();
	    	break;
		case EKeyUpArrow:
	    	if (iRecall==(-1))	//	Beginning of history
				{
				if (hCount==0)
		    		break;
				iRecall=0;
				}
	    	else if (iRecall>=(hCount-1))	//	End
				{
				ClearLine();
				iRecall=(-1);
				break;
				}
	    	else
				iRecall++;
	    	Recall();
	    	break;
		case EKeyDownArrow:
	    	if (iRecall==(-1))
				{
				if (hCount==0)
		    		break;
				iRecall=hCount-1;
				}
	    	else if (iRecall==0)
				{
				ClearLine();
				iRecall=(-1);
				break;
				}
	    	else
				iRecall--;
	    	Recall();
	    	break;
		case EKeyPageDown:
	    	if (hCount==0)
				break;
	    	iRecall=0;
	    	Recall();
	    	break;
	    	
		case EKeyEnter:
			NewLine();
			StoreBufferHistory();		
	    	return EShellCommand;
	    	
		case EKeyBackspace:
	    	if (iPos)
				{
				TInt iN=1;
				if (iConsole->KeyModifiers()==EModifierCtrl)
		    		iN=iPos-WordLeft();
				ClearLast(iN);
				iPos-=iN;
				Buf().Delete(iPos,iN);
				Refresh();
				}
	    	break;
		case EKeyDelete:
	    	if (iPos<((TInt)Buf().Length()))
				{
				TInt iN=1;
				if (iConsole->KeyModifiers()==EModifierCtrl)
		    		iN=WordRight()-iPos;
				ClearLast(iN);
				Buf().Delete(iPos,iN);
				Refresh();
				}
	    	break;
	    	
		case EKeyInsert:
	    	iMode=(iMode==EEditOverWrite ? EEditInsert : EEditOverWrite);
			iConsole->SetCursorHeight(iMode==EEditOverWrite ? ECursorNormal : ECursorInsert);
	    	break;

		case EKeyTab:
			return ECommandCompletion;

		default:
   	    	if (!gChar.IsPrint())
				break;
	    	if (iMode==EEditOverWrite && iPos<((TInt)Buf().Length()))
				Buf()[iPos++]=(TText)gChar;
	    	else if (Buf().Length()<KShellMaxCommandLine)
				{
				TInt oL=Lines();
				TBuf<0x02> b;
				b.Append(gChar);
				Buf().Insert(iPos++,b);
				TInt nL=Lines();
				if (nL!=oL)
		    		{
		    		iConsole->SetCursorHeight(ECursorNone);
		    		iConsole->SetPos(0,iLine+oL-1);
		    		iConsole->Write(_L("\n"));
		    		iConsole->SetPos(0,iLine);
		    		if (iLine+nL>iHeight-iFrameSizeChar.iHeight)
						iLine=iHeight-iFrameSizeChar.iHeight-nL;
		    		}
				}
			else
				{
				iConsole->Write(_L("\7"));
				iConsole->SetPos((iOrigin+iPos)%(iWidth-iFrameSizeChar.iWidth),iLine+Lines()-1);
				break;
				}
			Refresh();
			}
		}
	}
	
void CLineEdit::NewLine()
	{
 	iConsole->SetCursorHeight(ECursorNone);
 	iLine += (Lines() - 1);
 	iConsole->SetPos(0, iLine);
 	iConsole->Write(_L("\n")); // Just a line feed
 	iRecall = (-1);
	}

void CLineEdit::StoreBufferHistory()
	{
    Buf().TrimRight(); 

	if (Buf().Length()>=1)
		{
		
        //-- find out if we already have exactly the same command in the history.
        //-- if it is there, don't add a new item, just move it to the top
        for(TInt i=0; i<iHistory->Count(); ++i)
        {
            HBufC* pCmd = iHistory->At(i);    
            const TDesC& cmdLine = pCmd->Des();
            if(cmdLine == Buf())
            {
                iHistory->Delete(i);
                TRAP_IGNORE(iHistory->InsertL(0, pCmd));
                return;
            }
        }

        if (iHistory->Count()==iMaxHistory+1)
			{
			User::Free((*iHistory)[iMaxHistory]);
    		iHistory->Delete(iMaxHistory);
			}
		
        HBufC* pB=Buf().Alloc();
		if(pB != NULL)
			{
			TRAP_IGNORE(iHistory->InsertL(0, pB));
			}
		}
	}

//////////////////////////////////////
//CShell
//////////////////////////////////////
TShellCommand::TShellCommand(const TDesC& aName,const TDesC& aHelp,const TDesC& aHelpDetail,TUint aSwitches,TInt (*aFunction)(TDes&,TUint))
	:iName(aName),
	iHelp(aHelp),
	iHelpDetail(aHelpDetail),
	iSwitchesSupported(aSwitches),
	iFunction(aFunction)
	{
	}

CShell* CShell::NewL()
	{
	CShell *pS = new(ELeave) CShell;
	CleanupStack::PushL(pS);
	
	// No need to PushL these, if CShell::NewL leaves then eshell
	// fails in its entirety.
	TheConsole = Console::NewL(_L("ESHELL"), TSize(KConsFullScreen, KConsFullScreen));
	TheEditor = CLineEdit::NewL(TheConsole, KDefaultHistorySize);
	TheCliCompleter = CCliCompleter::NewL();
   
	CleanupStack::Pop();
	return(pS);
	}

CShell::~CShell()
	{
	ShellFunction::TheShell=NULL;
	TheFs.Close();
	delete TheEditor;
  	delete TheFileMan;
  	delete TheConsole;
  	delete TheCliCompleter;
 	}

void CShell::DoBanner()
	{
	TBuf<40> shlver=TheShellVersion.Name();
	TheConsole->Printf(_L("ESHELL %S   CFG="),&shlver);
#ifdef _UNICODE
	TheConsole->Printf(_L("U"));
#endif
#ifdef _DEBUG
	TheConsole->Printf(_L("DEB\r\n"));
#else
	TheConsole->Printf(_L("REL\r\n"));
#endif

#if !defined(__WINS__)
	TMachineStartupType reason;
	UserHal::StartupReason(reason);
	
	switch (reason)
		{
		case EStartupCold:		TheConsole->Printf(_L("Cold Start\n")); break;
		case EStartupColdReset: 	TheConsole->Printf(_L("Cold Reset\n")); break;
		case EStartupNewOs: 		TheConsole->Printf(_L("New OS\n")); break;
		case EStartupPowerFail:		TheConsole->Printf(_L("Power failed\n")); break;
		case EStartupWarmReset:		TheConsole->Printf(_L("Warm Reset\n")); break;
		case EStartupKernelFault:	
			
			TInt faultno;
			UserHal::FaultReason(faultno);
			if (faultno == 0x10000000)
				{
				TheConsole->Printf(_L("Kernel Exception\n"));
				}
			else 
				{
				TExcInfo exceptInfo;
				UserHal::ExceptionInfo(exceptInfo);
				TUint32 decode[3];
				decode[0]=TUint32(exceptInfo.iCodeAddress);
				decode[1]=TUint32(exceptInfo.iDataAddress);
				decode[2]=0;
			
			//	interpret decode as null-terminated string
				TPtrC category((TText*)&decode[0]);
			
				if (faultno >= 0x10000)
					TheConsole->Printf(_L("Kernel PANIC: %S %d\n"),&category, faultno-0x10000);
				else
					TheConsole->Printf(_L("Kernel FAULT: %S %d\n"),&category, faultno);
				}
			break;
		case EStartupSafeReset:		TheConsole->Printf(_L("Safe Reset\n")); break;
		default:
			TheConsole->Printf(_L("<?reason=%d>\n"), reason);
			break;
		}

	if (reason==EStartupWarmReset || reason==EStartupKernelFault)
		{
		TInt excId;
		TExcInfo excInfo;
		UserHal::ExceptionId(excId);
		UserHal::ExceptionInfo(excInfo);
		TheConsole->Printf(_L("(last exception %d: code %08x data %08x extra %08x) "),
			excId, excInfo.iCodeAddress, excInfo.iDataAddress, excInfo.iExtraData);
		}
#endif
	TheConsole->Printf(_L("\r\n\nCopyright (c) 1998 Symbian Ltd\r\n\n"));
	}

_LIT(KRootdir,"?:\\");
void CShell::RunL()
	{
	DoBanner();
	TBuf<sizeof(KRootdir)> rootdir(KRootdir);
	rootdir[0] = (TUint8) RFs::GetSystemDriveChar();
	__ASSERT_ALWAYS(TheFs.Connect()==KErrNone,User::Panic(_L("Connect"),0));
	__ASSERT_ALWAYS(TheFs.SetSessionPath(rootdir) == KErrNone, User::Panic(_L("Set Session path"),0)); 
	__ASSERT_ALWAYS(TheFs.SessionPath(currentPath)==KErrNone,User::Panic(_L("Session path"),0));
	TInt drive;
	__ASSERT_ALWAYS(charToDrive(currentPath[0],drive)==KErrNone,User::Panic(_L("Invalid Path"),0));
	drivePaths[drive]=currentPath;
//
//	going to creat the shell's private path here
//	TheFs.
//
	CFileManObserver* fileManObserver=new(ELeave) CFileManObserver;
	CleanupStack::PushL(fileManObserver);
	TheFileMan=CFileMan::NewL(TheFs,fileManObserver);
//
	TBuf<16> startupFile=_L("0:\\AUTOEXEC.BAT");
	TEntry startEntry;
	
//	Search all drives for autoexec.bat starting y,x,...,a then z
	
	const TInt KIndexDriveA=0;
	const TInt KIndexDriveY=24;
	const TInt KIndexDriveZ=25;
	TBuf<KMaxFileName>* searchDrive;

	for (searchDrive=&drivePaths[KIndexDriveY];searchDrive>=&drivePaths[KIndexDriveA];searchDrive--)
		{
		currentPath=*searchDrive;
		startupFile[0]=currentPath[0];
		if (TheFs.Entry(startupFile,startEntry)==KErrNone)
			{
#ifdef __X86__
			if (startEntry.iSize != 0)
#endif
				{
				RunBatch(startupFile);
				break;
				}
			}
		if (searchDrive==&drivePaths[KIndexDriveA])
			{
			currentPath=drivePaths[KIndexDriveZ];
			startupFile[0]=currentPath[0];
			if (TheFs.Entry(startupFile,startEntry)==KErrNone)
				{
				RunBatch(startupFile);
				break;
				}
			}
		}

	TLineEditAction result;
	TBuf<KShellMaxCommandLine> commandText;
	TBool exit = EFalse;
	
	TInt tabCount = 0;
	TBuf<KMaxPath + 1> prompt;
	TBool newLine = ETrue;

	FOREVER
		{
		__ASSERT_ALWAYS(TheFs.SessionPath(currentPath)==KErrNone,User::Panic(_L("Session path"),0));
      TInt drive;
       __ASSERT_ALWAYS(charToDrive(currentPath[0],drive)==KErrNone,User::Panic(_L("Invalid Path"),0));
		drivePaths[drive] = currentPath;

		if(currentPath[currentPath.Length() - 2] == KDriveDelimiter)
			{
			prompt = currentPath;		
			}
		else 
			{
			TInt i = (currentPath.LocateReverse(KPathDelimiter));
			prompt = currentPath.Left(i);
			}
		prompt.Append(_L(">"));

		result = TheEditor->Edit(prompt, &commandText, newLine);
		
		switch(result)
			{
			case EShellCommand:
				tabCount = 0;
				
#if !defined(_EPOC)
				if(commandText.CompareF(_L("EXIT")) == 0)
					{
					exit = ETrue;
					break;
					}
#endif
				commandText.Trim();
				DoCommand(commandText);
				commandText.Zero();
				
				newLine = ETrue;
				
				break;
			
			case ECommandCompletion:
				{
				tabCount++;
				
				TBuf<KMaxPath> knownPart;
				TheCliCompleter->EstablishCompletionContext(commandText, TheEditor->Pos(), knownPart);

				TInt preCompletedLength = knownPart.Length();
				
				newLine = EFalse;
				
				RPointerArray<HBufC> alternatives;
 				CleanupResetAndDestroyPushL(alternatives);
				
				if(TheCliCompleter->AttemptCompletionL(knownPart, alternatives))
					{ // We completed something successfully
					tabCount = 0;
					}
					
				if(knownPart.Length() > preCompletedLength) 
					{
					commandText.Delete(TheEditor->Pos() - preCompletedLength, preCompletedLength);

					// Don't allow the completion to cause the line buffer length to be exceeded
					TInt excess = ((TheEditor->Pos() - preCompletedLength) + knownPart.Length()) - KShellMaxCommandLine;
					if(excess > 0)
						{
						knownPart.Delete(knownPart.Length() - excess, excess);
						}						
					else
						{
						excess = (commandText.Length() + knownPart.Length()) - KShellMaxCommandLine;
						
						if(excess > 0)
							{
							commandText.Delete(commandText.Length() - excess, excess);
							}
						}

					commandText.Insert(TheEditor->Pos() - preCompletedLength, knownPart);
					TheEditor->SetPos(TheEditor->Pos() + (knownPart.Length() - preCompletedLength));
					}
				
				if(alternatives.Count() > 0)
					{
					if(tabCount == 2)
						{
						tabCount = 0;
						
						TheCliCompleter->DisplayAlternatives(alternatives);
						newLine = ETrue;
						}
					}
				
				CleanupStack::PopAndDestroy(&alternatives);
				
				break;
				}

			case ENoAction:
			default:
				tabCount = 0;
				break;
			}
		
		if(exit) 
			{
			break;
			}
		}
	
	CleanupStack::PopAndDestroy(fileManObserver);
	}

void CShell::DoCommand(TDes& aCommand)
//
// Evaluate the commandline and run the command or file
//
	{

	aCommand.TrimAll();
	
	const TShellCommand* commandPtr=&iCommand[0];
	for (;commandPtr<=&iCommand[ENoShellCommands-1];commandPtr++)
		{
		TInt length=commandPtr->iName.Length();
		if ((aCommand.Length()>length && aCommand.Left(length).CompareF(commandPtr->iName)==0 && !TChar(aCommand[length]).IsAlphaDigit())
			|| (aCommand.Length()==length && aCommand.CompareF(commandPtr->iName)==0))
				{
				aCommand.Delete(0,length);
				break;
				}
		}

	if (commandPtr<=&iCommand[ENoShellCommands-1])
		{
		if (aCommand.Find(_L("/?"))>=0)
			PrintHelp(commandPtr);
		else // No /? switch
			{
			TUint switchesSet=0;
			TInt r;
			while ((r=aCommand.Locate('/'))!=KErrNotFound)
				{
				TChar switchChar='\0';
                TInt switchInt=switchChar;
				if ((r+1)==aCommand.Length() || (switchChar=aCommand[r+1]).IsAlpha()==EFalse)
					{
					TheConsole->Printf(_L("Invalid switch - \"%c\".\n"),switchInt);
					return;
					}
				switchChar.UpperCase();
				switchesSet|=(1<<((TInt)switchChar-'A'));
				TChar extraChar;
				if ((r+2)<aCommand.Length() && (extraChar=aCommand[r+2])!=' ' && extraChar!='/')
					{
                    TInt switchInt=switchChar;
                    TInt extraInt=extraChar; // Gcc debugger warning if pass TChar to ...
                    TheConsole->Printf(_L("Parameter format not correct - \"%c%c\".\n"),switchInt,extraInt);
					return;
					}
				aCommand.Delete(r,2); 
				}
			if (switchesSet&~commandPtr->iSwitchesSupported)
				{
				TheConsole->Printf(_L("Switch not supported\n"));
				return;
				}
			aCommand.Trim();
		
		//	RUN SHELL FUNCTION	
			r=commandPtr->iFunction(aCommand,switchesSet);
			if (r!=KErrNone) 
				{
				PrintError(r);
				}
			}
		}
	else //Generic commands
		{
		TInt r;
		
		if (aCommand.CompareF(_L("HELP"))==0)
			PrintHelp();
		else if (aCommand.CompareF(_L("CLS"))==0)
			TheConsole->ClearScreen(); 
		else if (aCommand.Length()==2 && TChar(aCommand[0]).IsAlpha() && aCommand[1]==':')
			ChangeDrive(aCommand[0]);
		else if (aCommand.Length()!=0)
			{
			r=RunBatch(aCommand);
			if (r != KErrNone)		// Typically KErrNotFound, KErrBadName, KErrLocked...
				r=RunExecutable(aCommand,ETrue);
			if (r!=KErrNone)
				PrintError(r);
			}
		}
	}

void CShell::PrintHelp()
	{
			
	for(const TShellCommand* commandPtr=&iCommand[0];commandPtr<=&iCommand[ENoShellCommands-1];commandPtr++)
		{
		OutputStringToConsole(ETrue,_L("%- 10S%S\n"),&commandPtr->iName,&commandPtr->iHelp);			
		}
	
	}

void CShell::PrintHelp(const TShellCommand* aCommand)
	{
	OutputStringToConsole(ETrue,_L("%S\n\n  %S "),&aCommand->iHelp,&aCommand->iName);
	OutputStringToConsole(ETrue,_L("%S\n\n"),&aCommand->iHelpDetail);
	}

void CShell::PrintError(TInt aError)
	{
	switch (aError)
		{
	case KErrAlreadyExists:
		TheConsole->Printf(_L("Already exists\n"));
		break;
	case KErrCorrupt:
		TheConsole->Printf(_L("Corrupt or unformatted drive\n"));
		break;
	case KErrNotSupported:
		TheConsole->Printf(_L("Not supported\n"));
		break;
	case KErrGeneral:
		TheConsole->Printf(_L("General Error\n"));
		break;
	case KErrNotFound:
		TheConsole->Printf(_L("Not found\n"));
		break;
	case KErrPathNotFound:
		TheConsole->Printf(_L("Path not found\n"));
		break;
	case KErrBadName:
		TheConsole->Printf(_L("Bad name\n"));
		break;
	case KErrNotReady:
		TheConsole->Printf(_L("Drive not ready\n"));
		break;
	case KErrAccessDenied:
		TheConsole->Printf(_L("Access denied\n"));
		break;
	case KErrEof:
		TheConsole->Printf(_L("Unexpected end of file\n"));
		break;
	case KErrTooBig:
		TheConsole->Printf(_L("Too Big\n"));
		break;
	default:
		TheConsole->Printf(_L("Error %d\n"),aError);
		}
	}

void CShell::ChangeDrive(TChar aDrive)
	{

    TInt drive;
    __ASSERT_ALWAYS(charToDrive(aDrive,drive)==KErrNone,User::Panic(_L("Invalid drive letter"),0));
	TDriveList driveList;
	TheFs.DriveList(driveList);
	if (driveList[drive]) 
		{
		TInt r=TheFs.SetSessionPath(drivePaths[drive]);
		if (r!=KErrNone)
			PrintError(r);
		}
	else 
		PrintError(KErrNotFound);
	}  

TInt CShell::RunBatch(TDes& aCommand)
	{
	TBool appendedBat=EFalse;
	if (aCommand.FindF(_L(".BAT"))<0 && (aCommand.Length()+4)<=KShellMaxCommandLine)
		{
		aCommand.Append(_L(".BAT"));
		appendedBat=ETrue;
		}
	RFile file;
	TInt r=file.Open(TheFs,aCommand,EFileStreamText);
	if (r!=KErrNone)
		{
		if (appendedBat)
			aCommand.Delete(aCommand.Length()-4,4);		
		return r;
		}
	__ASSERT_ALWAYS(TheFs.SessionPath(currentPath)==KErrNone,User::Panic(_L("Session path"),0));
    TInt drive;
    __ASSERT_ALWAYS(charToDrive(currentPath[0],drive)==KErrNone,User::Panic(_L("Invalid drive letter"),0));
	drivePaths[drive]=currentPath;
	TInt filePos=0;
	
	TBuf<KShellMaxCommandLine> readBuf;
	
	FOREVER
		{
#ifdef _UNICODE
		TBuf8<KShellMaxCommandLine> buf8;
		r=file.Read(filePos,buf8);
		readBuf.Copy(buf8);		
#else
		r=file.Read(filePos,readBuf);
#endif
		if (r!=KErrNone)
			{
			PrintError(r);
			break;
			}

		r=readBuf.Locate('\n');
		if (r==KErrNotFound)
			{
			r=readBuf.Length();
			filePos+=r;
			}
		
		else if (r<=1)					//	Indicates /n before batch file instructions
			{
			TInt temp=readBuf.Length();	
			readBuf.TrimLeft();			//	Removes initial /n
			temp-=readBuf.Length();		
			r=readBuf.Locate('\n');
			if(r==KErrNotFound)
				{
				r=readBuf.Length();
				}
			filePos+=r+1+temp;			//	Offsets filePos correctly in the file
			}
		else filePos+=r+1;
		
		if (readBuf.Length()==0)
			break;
		readBuf.SetLength(r);
		
		readBuf.Trim();
		TheFs.SessionPath(currentPath);
		TheConsole->Printf(currentPath);		
		TheConsole->Printf(_L(">%S\n"),&readBuf);

	//	If command was a drive change, reset the current path here		
		if (readBuf.Length()==2 && TChar(readBuf[0]).IsAlpha() && readBuf[1]==':')
			{
			TInt drive;
			__ASSERT_ALWAYS(charToDrive(readBuf[0],drive)==KErrNone,User::Panic(_L("Invalid drive letter"),0));
			
			TDriveList driveList;
			TheFs.DriveList(driveList);
			if (driveList[drive]) 
				{
				TInt r=TheFs.SetSessionPath(drivePaths[drive]);
				if (r!=KErrNone)
					PrintError(r);
				currentPath=drivePaths[drive];
				}
			else 
			PrintError(KErrNotFound);
			}
			
		else if (readBuf.Length()<3 || readBuf.Left(3).CompareF(_L("REM"))!=0)
			DoCommand(readBuf);
		}
	file.Close();
	return KErrNone;
	}



TInt CShell::RunExecutable(TDes& aCommand,TBool aWaitForCompletion)
	{
	aCommand.Trim();
	TBuf<KShellMaxCommandLine> parameters(0);
   	TInt r=aCommand.Locate(' ');	// can't be the last character because of Trim()
   	if (r!=KErrNotFound)
		{
		parameters=aCommand.Mid(r+1);
   		aCommand.SetLength(r);
		}
	// aCommand is now just the executable
	if (aCommand.FindF(_L(".EXE"))==KErrNotFound && (aCommand.Length()+4)<=KShellMaxCommandLine)
		aCommand.Append(_L(".EXE"));
	TInt specificExe=1;
	if (aCommand.Length()>2 && aCommand[1]==':' && aCommand[2]!='\\')
		{
		if (TChar(aCommand[0]).IsAlpha())
			{
			TInt drive;
			__ASSERT_ALWAYS(charToDrive(aCommand[0],drive)==KErrNone,User::Panic(_L("Invalid drive letter"),0));
			currentPath=drivePaths[drive];
			aCommand.Delete(0,2);
            if (aCommand.Length()+currentPath.Length() <= KShellMaxCommandLine)
    			aCommand.Insert(0,currentPath);
			}
		else
			return KErrNotFound;
		}
	if (aCommand.Length()>2 && aCommand[1]!=':')
		{
		if(aCommand[0]!='\\')
			{
			if (aCommand.Locate('\\')==KErrNotFound)
				specificExe=0;	// no drive, no path - be ready to let the system find it...
            if (aCommand.Length()+currentPath.Length() <= KShellMaxCommandLine)
                aCommand.Insert(0,currentPath);
			}
		else
            if (aCommand.Length()+currentPath.Left(2).Length() <= KShellMaxCommandLine)
    			aCommand.Insert(0,currentPath.Left(2));
		}

	RFile file;
	r=file.Open(CShell::TheFs,aCommand,EFileStream);
	if (r!=KErrNone)
		{
		if (specificExe)
			return(r);
		r=aCommand.LocateReverse('\\');	// must exist because this is a full filename
		aCommand.Delete(0,r+1);
		}
	else
		{
		// If the file can be opened, it *must* exist, and we'll assume that the user
		// really intended this specific file.
		specificExe=1;
		file.Close();
		}

	RProcess newProcess;
	r=newProcess.Create(aCommand,parameters);
	
	if (r==KErrNone)	//	Executable can run OK
		{
		TRequestStatus status=KRequestPending;
		if(aWaitForCompletion)
    		newProcess.Logon(status);
		newProcess.Resume();
		if (aWaitForCompletion)
			User::WaitForRequest(status);
		if (aWaitForCompletion && (newProcess.ExitType()!=EExitKill || status!=KErrNone))
			{
			TBuf<KMaxExitCategoryName> exitCat=newProcess.ExitCategory();
			TheConsole->Printf(_L("\nExit type %d,%d,%S\n"),newProcess.ExitType(),newProcess.ExitReason(),&exitCat);
			}
			
		newProcess.Close(); // get rid of our handle
		return KErrNone;
		}

	//	Executable could not be run

#if 0	//defined(__EPOC32__)
	if (specificExe)		
		{	
	//	Use class CDllChecker to check the dependencies
	//	for MARM exes only
		CDllChecker check;
	
	//	Create an array to store each dependency's name, Uid and result
		TRAPD(leaveCode,(check.ConstructL()));	
	
		if (leaveCode!=KErrNone)	//	If function leaves
			{
			TheConsole->Printf(_L("Dependency checking failed due to error %d\n"), leaveCode);
			return(KErrNone);
			}

		TInt result=KErrNone;
		TRAP(result,(check.GetImportDataL(aCommand,NULL)));
		
		if (result==KErrNone)
			{
			check.ListArray();	//	Print out the results of DllCheck		
			return(KErrNone);
			}
		}
#endif
	return (r);
}

void CShell::SetCurrentPath(const TDesC& aDes)
//
// Set the current Directory
//
	{

	__ASSERT_DEBUG(currentPath.MaxLength()>=aDes.Length(),Panic(EShellFilePathTooBig));
	currentPath=aDes;
	}

TDes& CShell::CurrentPath()
//
// Accessor function
//
	{
	return currentPath;
	}



void CShell::SetDrivePath(const TDesC& aDrivePath)
//
// Set the drive path
//
	{

	__ASSERT_DEBUG(aDrivePath.Length()>=3 && aDrivePath[1]==KDriveDelimiter,Panic(EShellBadDrivePath));
	TChar drvLetter=aDrivePath[0];
	__ASSERT_DEBUG(drvLetter.IsAlpha(),Panic(EShellBadDrivePath));
	TInt drvNum;
    __ASSERT_ALWAYS(charToDrive(drvLetter,drvNum)==KErrNone,User::Panic(_L("Invalid drive letter"),0));
	drivePaths[drvNum]=aDrivePath;
	}


TKeyCode CShell::OutputStringToConsole(TBool aPageSwitch,TRefByValue<const TDesC> aFmt,... )
//function for output of a sring to console
//aPageSwitch flag indicates that output should be page-by-page 
	{
	//create an object to truncate argument list substitution 
	SimpleOverflowTruncate overflow;	
	VA_LIST list;
	VA_START(list,aFmt);
	
	TBuf<0x200> aBuf;
	//format output string using argument list
	
	//coverity[uninit_use_in_call]
	TRAP_IGNORE(aBuf.AppendFormatList(aFmt,list,&overflow)); // ignore leave in TTimeOverflowLeave::Overflow()
	
	_LIT(KPrompt , "Press any key to continue\n");		
	
	return OutputStringToConsole(KPrompt,aPageSwitch,_L("%S"),&aBuf);
	}

TKeyCode CShell::OutputStringToConsole(const TDesC& aNotification,TBool aPageSwitch,TRefByValue<const TDesC> aFmt,...)
	//function for output of a string to console aPageSwitch flag indicates that output should be page-by-page 
	//if aPageSwitch==ETrue user will be prompted with the message passed as aNotification
	//code of key pressed will be returned as a return value
	{
	//create variable to store code of the key pressed by the user
	TKeyCode key=EKeyNull;	
	//create an object to truncate argument list substitution 
	SimpleOverflowTruncate overflow;
	
	VA_LIST list;
	VA_START(list,aFmt);
	
	TBuf<0x200> aBuf;
	//format output string using argumen list
	
	//coverity[uninit_use_in_call]
	TRAP_IGNORE(aBuf.AppendFormatList(aFmt,list,&overflow)); // ignore leave in TTimeOverflowLeave::Overflow()
	//if we are requested to wait for the user input at the end of each page, we check whether output of next piece of text will fit into the screen
	if (aPageSwitch)
		{
		key=PageSwitchDisplay(aNotification);				
		}
	//output current string
	TheConsole->Write(aBuf);
	return key;			
	}

TKeyCode CShell::OutputStringToConsole(TBool aPageSwitch, const TDesC& aBuf)
	{
	_LIT(KPrompt , "Press any key to continue\n");
	
    TKeyCode key=EKeyNull;	

	//if we are requested to wait for the user input at the end of each page, we check whether output of next piece of text will fit into the screen
	if (aPageSwitch)
		{
		key = PageSwitchDisplay(KPrompt);				
		}
	//output current string
	TheConsole->Write(aBuf);
	
    return key;
	}

TKeyCode CShell::PageSwitchDisplay(const TDesC& aNotification)
	{
	//create variable to store code of the key pressed by the user
	TKeyCode key=EKeyNull;	
    //obtain a current cursor position
	TInt line_count=TheConsole->WhereY();
    //calculate how many lines is needed to output current string in the current screen rect
	TInt add=(TheConsole->WhereX()+aNotification.Length())/(TheConsole->ScreenSize().iWidth-2);				
	if ((TheConsole->WhereX()+aNotification.Length())%(TheConsole->ScreenSize().iWidth-2)) 
		{
		add+=1;				
		}	
	//if we will not fit into the screen after output of the current string, then we should prompt for the user input to start new page 
	TInt notification_height=aNotification.Length()/(TheConsole->ScreenSize().iWidth-2);//we provide 2 additional characters for the frame				
	if (aNotification.Length()%(TheConsole->ScreenSize().iWidth-2)) 
		{
		notification_height+=1;	
		}
	if (add<(TheConsole->ScreenSize().iHeight-2))
	if ((line_count+add+notification_height)>=(TheConsole->ScreenSize().iHeight-4))
		{
		TInt previous_cursor_pos_x=TheConsole->WhereX();
		TheConsole->Printf(_L("%S"),&aNotification);
		key=TheConsole->Getch();
		TheConsole->ClearScreen();
		TheConsole->SetCursorPosAbs(TPoint (previous_cursor_pos_x,0)) ;			
		}						
	return key;			
	}