userlibandfileserver/fileserver/etshell/ts_edshl.cpp
changeset 9 96e5fb8b040d
child 6 0173bcd7697c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/userlibandfileserver/fileserver/etshell/ts_edshl.cpp	Thu Dec 17 09:24:54 2009 +0200
@@ -0,0 +1,1315 @@
+// 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<0x140> 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<0x140> 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;			
+	}