libraries/iosrv/client/server_command.cpp
changeset 0 7f656887cf89
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libraries/iosrv/client/server_command.cpp	Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,970 @@
+// server_command.cpp
+// 
+// Copyright (c) 2009 - 2010 Accenture. All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the "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:
+// Accenture - Initial contribution
+//
+
+#include <fshell/ioutils.h>
+#include <fshell/line_editor.h>
+#include "command_base.h"
+
+using namespace IoUtils;
+
+
+//
+// Constants.
+//
+
+_LIT(KTab, "\t");
+_LIT(KSpace, " ");
+_LIT(KNewLine, "\r\n");
+_LIT(KHelpCommandName, "help");
+
+
+// used for sorting RArray<TPtrC>'s
+TInt PtrOrder(const TPtrC& aDennis, const TPtrC& aGnasher)
+	{
+	return aDennis.Compare(aGnasher);
+	}
+
+
+//
+// TServerCommandId.
+//
+
+EXPORT_C TServerCommandId::TServerCommandId()
+	: iId(0)
+	{
+	}
+
+EXPORT_C TServerCommandId::TServerCommandId(TUint aValue)
+	: iId(aValue)
+	{
+	}
+
+EXPORT_C TUint TServerCommandId::Value() const
+	{
+	return iId;
+	}
+
+EXPORT_C void TServerCommandId::Set(const TServerCommandId& aId)
+	{
+	iId = aId.iId;
+	}
+
+EXPORT_C TBool TServerCommandId::operator==(const TServerCommandId& aId) const
+	{
+	return (iId == aId.iId);
+	}
+
+
+//
+// CServerCommandBase.
+//
+
+EXPORT_C CServerCommandBase::~CServerCommandBase()
+	{
+	}
+
+EXPORT_C const TServerCommandId& CServerCommandBase::Id() const
+	{
+	return iId;
+	}
+
+EXPORT_C void CServerCommandBase::SetId(const TServerCommandId& aId)
+	{
+	iId.Set(aId);
+	}
+
+EXPORT_C void CServerCommandBase::SetFactory(CServerCommandFactory& aFactory)
+	{
+	iFactory = &aFactory;
+	}
+
+EXPORT_C CServerCommandBase::CServerCommandBase()
+	{
+	}
+
+EXPORT_C CServerCommandBase::CServerCommandBase(TUint aFlags)
+	: CCommandBase(aFlags)
+	{
+	}
+
+EXPORT_C CServerCommandFactory& CServerCommandBase::Factory()
+	{
+	__ASSERT_ALWAYS(iFactory, Panic(ENoFactory));
+	return *iFactory;
+	}
+
+EXPORT_C void CServerCommandBase::PrintList(TRefByValue<const TDesC> aFmt, VA_LIST& aList)
+	{
+	if (iReporter)
+		{
+		iReporter->ReportList(iId, aFmt, aList);
+		}
+	else
+		{
+		CCommandBase::PrintList(aFmt, aList);
+		}
+	}
+
+EXPORT_C void CServerCommandBase::PrintList(TRefByValue<const TDesC8> aFmt, VA_LIST& aList)
+	{
+	if (iReporter)
+		{
+		iReporter->ReportList(iId, aFmt, aList);
+		}
+	else
+		{
+		CCommandBase::PrintList(aFmt, aList);
+		}
+	}
+
+EXPORT_C void CServerCommandBase::PrintErrorList(TInt aError, TRefByValue<const TDesC> aFmt, VA_LIST& aList)
+	{
+	if (iReporter)
+		{
+		iReporter->ReportErrorList(iId, aError, aFmt, aList);
+		}
+	else
+		{
+		CCommandBase::PrintErrorList(aError, aFmt, aList);
+		}
+	}
+
+EXPORT_C void CServerCommandBase::PrintWarningList(TRefByValue<const TDesC> aFmt, VA_LIST& aList)
+	{
+	if (iReporter)
+		{
+		iReporter->ReportWarningList(iId, aFmt, aList);
+		}
+	else
+		{
+		CCommandBase::PrintWarningList(aFmt, aList);
+		}
+	}
+
+void CServerCommandBase::SetReporter(MServerCommandReporter* aReporter)
+	{
+	iReporter = aReporter;
+	}
+
+
+//
+// CHelpCommand.
+//
+
+NONSHARABLE_CLASS(CHelpCommand) : public CServerCommandBase
+	{
+public:
+	static CServerCommandBase* NewLC();
+	~CHelpCommand();
+	static const TDesC& NameS();
+private:
+	CHelpCommand();
+	void ConstructL();
+	void GenerateTextL(const CServerCommandFactory& aFactory, TInt aIndent = 0);
+	HBufC* FullCommandNameLC() const;
+	void Write(TInt aIndent, const TDesC& aCommandName);
+private: // From CCommandBase.
+	virtual const TDesC& Name() const;
+	virtual const TDesC& Description() const;
+	virtual void DoRunL();
+private:
+	CTextBuffer* iBuffer;
+	CTextFormatter* iFormatter;
+	RArray<TPtrC> iCommandNameStack;
+	RFile iPodIndex;
+	};
+
+CServerCommandBase* CHelpCommand::NewLC()
+	{
+	CHelpCommand* self = new(ELeave) CHelpCommand();
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	return self;
+	}
+
+CHelpCommand::~CHelpCommand()
+	{
+	delete iBuffer;
+	delete iFormatter;
+	iPodIndex.Close();
+	iCommandNameStack.Close();
+	}
+
+const TDesC& CHelpCommand::NameS()
+	{
+	return KHelpCommandName;
+	}
+
+CHelpCommand::CHelpCommand()
+	{
+	}
+
+void CHelpCommand::ConstructL()
+	{
+	BaseConstructL();
+	}
+
+const TDesC& CHelpCommand::Name() const
+	{
+	return KHelpCommandName;
+	}
+
+const TDesC& CHelpCommand::Description() const
+	{
+	_LIT(KDescription, "List the commands the amsrv supports.");
+	return KDescription;
+	}
+	
+void CHelpCommand::GenerateTextL(const CServerCommandFactory& aFactory, TInt aIndent)
+	{
+	RArray<TPtrC> commands;
+	CleanupClosePushL(commands);
+	aFactory.ListCommandsL(commands);
+	commands.Sort(TLinearOrder<TPtrC>(PtrOrder));
+	
+	const TInt numCommands = commands.Count();
+	for (TInt i = 0; i < numCommands; ++i)
+		{
+		const TDesC& commandName = commands[i];
+		Write(aIndent, commandName);
+
+		const CServerCommandConstructor& cmnd = aFactory.GetSubCommandL(commandName);
+		if (cmnd.IsFactory())
+			{
+			GenerateTextL((const CServerCommandFactory&)cmnd, aIndent+1);
+			}
+		}
+	CleanupStack::PopAndDestroy();
+	}
+
+HBufC* CHelpCommand::FullCommandNameLC() const
+	{
+	const TInt numCommands = iCommandNameStack.Count();
+	TInt length = 0;
+	for (TInt i = 0; i < numCommands; ++i)
+		{
+		length += iCommandNameStack[i].Length();
+		if (i > 0)
+			{
+			++length;
+			}
+		}
+	HBufC* name = HBufC::NewLC(length);
+	TPtr namePtr(name->Des());
+	for (TInt i = 0; i < numCommands; ++i)
+		{
+		if (i > 0)
+			{
+			namePtr.Append('_');
+			}
+		namePtr.Append(iCommandNameStack[i]);
+		}
+	return name;
+	}
+
+void CHelpCommand::Write(TInt aIndent, const TDesC& aCommandName)
+	{
+	_LIT(KIndent, "  ");
+
+	if (iBuffer)
+		{
+		for (TInt j=0; j<aIndent; ++j)
+			{
+			iBuffer->AppendL(KIndent);
+			}
+		iBuffer->AppendL(aCommandName);
+		iBuffer->AppendL(KTab);
+		}
+	else
+		{
+		for (TInt j=0; j<aIndent; ++j)
+			{
+			CCommandBase::Write(KIndent);
+			}
+		CCommandBase::Write(aCommandName);
+		CCommandBase::Write(KNewLine);
+		}
+	}
+
+void CHelpCommand::DoRunL()
+	{
+	if (Stdout().AttachedToConsole())
+		{
+		iBuffer = CTextBuffer::NewL(0x100);
+		iFormatter = CTextFormatter::NewL(Stdout());
+		}
+	GenerateTextL(Factory());
+	if (iBuffer)
+		{
+		iFormatter->ColumnizeL(0, 2, iBuffer->Descriptor());
+		CCommandBase::Write(iFormatter->Descriptor());
+		}
+	CCommandBase::Write(_L("\r\n"));
+	}
+
+
+//
+// CRootCommand.
+//
+
+NONSHARABLE_CLASS(CRootCommand) : public CBranchCommandBase
+	{
+public:
+	static CRootCommand* NewL(CServerCommandFactory& aFactory);
+	~CRootCommand();
+private:
+	CRootCommand();
+	void ConstructL(CServerCommandFactory& aFactory);
+private: // From CCommandBase.
+	virtual const TDesC& Name() const;
+	virtual const TDesC& Description() const;
+private: // From CAmCmndParent.
+	virtual const TDesC& TypeDescription();
+	};
+
+CRootCommand* CRootCommand::NewL(CServerCommandFactory& aFactory)
+	{
+	CRootCommand* root = new(ELeave)CRootCommand();
+	CleanupStack::PushL(root);
+	root->ConstructL(aFactory);
+	CleanupStack::Pop(root);
+	return root;
+	}
+
+CRootCommand::~CRootCommand()
+	{
+	}
+
+CRootCommand::CRootCommand()
+	{
+	}
+
+void CRootCommand::ConstructL(CServerCommandFactory& aFactory)
+	{
+	SetFactory(aFactory);
+	}
+
+const TDesC& CRootCommand::Name() const
+	{
+	_LIT(KCommandName, "root");
+	return KCommandName;
+	}
+
+const TDesC& CRootCommand::Description() const
+	{
+	_LIT(KDescription, "A pseudo-command that is the ancestor of all other commands available in this server. Not intended to be run directly.");
+	return KDescription;
+	}
+
+const TDesC& CRootCommand::TypeDescription()
+	{
+	return KNullDesC;
+	}
+
+
+//
+// CServerBase.
+//
+
+EXPORT_C CServerBase::~CServerBase()
+	{
+	delete iLineEditor;
+	delete iFactory;
+	}
+
+EXPORT_C CServerBase::CServerBase(TUint aFlags, const TDesC& aPrompt, const TDesC& aCommandHistoryFileName)
+	: CCommandBase(aFlags), iPrompt(aPrompt), iCommandHistoryFileName(aCommandHistoryFileName), iWriterAdaptor(Stdout())
+	{
+	}
+
+EXPORT_C CServerCommandFactory& CServerBase::Factory()
+	{
+	return *iFactory;
+	}
+
+EXPORT_C void CServerBase::Exit(TInt aError)
+	{
+	iExitReason = aError;
+	iExit = ETrue;
+	}
+
+EXPORT_C void CServerBase::BaseConstructL()
+	{
+	CCommandBase::BaseConstructL();
+	}
+
+EXPORT_C void CServerBase::DoRunL()
+	{
+	iFactory = CServerCommandFactory::NewL(this, Cif());
+	iLineEditor = CLineEditor::NewL(FsL(), iWriterAdaptor, *this, *iFactory, iCommandHistoryFileName);
+	InitializeL();
+	RProcess::Rendezvous(KErrNone);
+
+	do
+		{
+		CheckNewConsoleLine();
+		Write(iPrompt);
+		ParseLineL(NextLineL());
+		}
+		while (!iExit);
+
+	Complete(iExitReason);
+	}
+
+EXPORT_C void CServerBase::PrintList(TRefByValue<const TDesC> aFmt, VA_LIST& aList)
+	{
+	TOverflowTruncate overflow;
+	TBuf<0x100> buf;
+	buf.AppendFormatList(aFmt, aList, &overflow);
+	Report(TServerCommandId(), buf);
+	}
+
+EXPORT_C void CServerBase::PrintList(TRefByValue<const TDesC8> aFmt, VA_LIST& aList)
+	{
+	TOverflowTruncate8 overflow;
+	TBuf8<0x100> buf;
+	buf.AppendFormatList(aFmt, aList, &overflow);
+	Report(TServerCommandId(), buf);
+	}
+
+EXPORT_C void CServerBase::PrintErrorList(TInt aError, TRefByValue<const TDesC> aFmt, VA_LIST& aList)
+	{
+	if (!iErrorReported)
+		{
+		TOverflowTruncate overflow;
+		TBuf<0x100> buf;
+		buf.AppendFormatList(aFmt, aList, &overflow);
+		ReportError(TServerCommandId(), aError, buf);
+		iErrorReported = ETrue;
+		}
+	}
+
+EXPORT_C void CServerBase::PrintWarningList(TRefByValue<const TDesC> aFmt, VA_LIST& aList)
+	{
+	TOverflowTruncate overflow;
+	TBuf<0x100> buf;
+	buf.AppendFormatList(aFmt, aList, &overflow);
+	ReportWarning(TServerCommandId(), buf);
+	}
+
+EXPORT_C void CServerBase::Report(const TServerCommandId& aId, TRefByValue<const TDesC> aFmt, ...)
+	{
+	VA_LIST list;
+	VA_START(list, aFmt);
+	ReportList(aId, aFmt, list);
+	VA_END(list);
+	}
+
+EXPORT_C void CServerBase::ReportList(const TServerCommandId& aId, TRefByValue<const TDesC> aFmt, VA_LIST& aList)
+	{
+	TOverflowTruncate overflow;
+	TBuf<0x100> buf;
+	buf.AppendFormatList(aFmt, aList, &overflow);
+	Report(aId, buf);
+	}
+
+EXPORT_C void CServerBase::Report(const TServerCommandId& aId, TRefByValue<const TDesC8> aFmt, ...)
+	{
+	VA_LIST list;
+	VA_START(list, aFmt);
+	ReportList(aId, aFmt, list);
+	VA_END(list);
+	}
+
+EXPORT_C void CServerBase::ReportList(const TServerCommandId& aId, TRefByValue<const TDesC8> aFmt, VA_LIST& aList)
+	{
+	TOverflowTruncate8 overflow;
+	TBuf8<0x100> buf;
+	buf.AppendFormatList(aFmt, aList, &overflow);
+	Report(aId, buf);
+	}
+
+EXPORT_C void CServerBase::ReportWarning(const TServerCommandId& aId, TRefByValue<const TDesC> aFmt, ...)
+	{
+	VA_LIST list;
+	VA_START(list, aFmt);
+	ReportWarningList(aId, aFmt, list);
+	VA_END(list);
+	}
+
+EXPORT_C void CServerBase::ReportWarningList(const TServerCommandId& aId, TRefByValue<const TDesC> aFmt, VA_LIST& aList)
+	{
+	TOverflowTruncate overflow;
+	TBuf<0x100> buf;
+	buf.AppendFormatList(aFmt, aList, &overflow);
+	ReportWarning(aId, buf);
+	}
+
+EXPORT_C void CServerBase::ReportError(const TServerCommandId& aId, TInt aError, TRefByValue<const TDesC> aFmt, ...)
+	{
+	VA_LIST list;
+	VA_START(list, aFmt);
+	ReportErrorList(aId, aError, aFmt, list);
+	VA_END(list);
+	}
+
+EXPORT_C void CServerBase::ReportErrorList(const TServerCommandId& aId, TInt aError, TRefByValue<const TDesC> aFmt, VA_LIST& aList)
+	{
+	if (!iErrorReported)
+		{
+		TOverflowTruncate overflow;
+		TBuf<0x100> buf;
+		buf.AppendFormatList(aFmt, aList, &overflow);
+		ReportError(aId, aError, buf);
+		iErrorReported = ETrue;
+		}
+	}
+
+EXPORT_C void CServerBase::LeoHandleLine(const TDesC& aLine)
+	{
+	iLine = aLine;
+	iLineRead = ETrue;
+	}
+
+void CServerBase::ParseLineL(const TDesC& aLine)
+	{
+	if (aLine.Length() > 0)
+		{
+		iErrorReported = EFalse;
+		CRootCommand* root = CRootCommand::NewL(Factory());
+		root->SetReporter(this);
+		root->RunCommand(IoSession(), Stdin(), Stdout(), Stderr(), &aLine, &Env(), NULL);
+		__ASSERT_ALWAYS(root->IsComplete(), Panic(EUnexpectedAsyncCommand));
+		delete root;
+		}
+	}
+
+const TDesC& CServerBase::NextLineL()
+	{
+retry:
+	iLine.Zero();
+
+	if (Stdin().AttachedToConsole())
+		{
+		iLineEditor->Start(KNullDesC);
+		iLineEditor->ReinstatePromptAndUserInput();
+		iLineRead = EFalse;
+		do
+			{
+			TRequestStatus status;
+			Stdin().WaitForKey(status);
+			User::WaitForRequest(status);
+			if (status.Int() == KErrNotSupported)
+				{
+				// Likely that we're no longer attached to a console and are instead attached to a pipe.
+				// Note, using "pcons" directly gives us a console, but using "am.exe" gives us a pipe.
+				iLineEditor->Abort();
+				goto retry;
+				}
+			User::LeaveIfError(status.Int());
+			iLineEditor->HandleKey(Stdin().KeyCode(), Stdin().KeyModifiers());
+			}
+			while (!iLineRead);
+		}
+	else
+		{
+		LeaveIfErr(Stdin().SetReadMode(RIoReadHandle::ELine), _L("Couldn't set stdin to line mode"));
+		Stdin().SetLineSeparatorL(KNewLine());
+		User::LeaveIfError(Stdin().Read(iLine));
+		}
+
+	iLine.TrimRight();
+	return iLine;
+	}
+
+EXPORT_C void CServerBase::CheckNewConsoleLine()
+	{
+	if (Stdout().AttachedToConsole())
+		{
+		TPoint cursorPos(0, 0);
+		Stdout().GetCursorPos(cursorPos);
+		if (cursorPos.iX != 0)
+			{
+			Stdout().Write(KNewLine);
+			}
+		}
+	}
+
+
+//
+// CServerCommandConstructor.
+//
+
+EXPORT_C CServerCommandConstructor* CServerCommandConstructor::NewLC(const TDesC& aName, TServerCommandConstructor aConstructor, CServerCommandFactory& aFactory, MServerCommandReporter* aReporter, const CCommandInfoFile* aCif)
+	{
+	CServerCommandConstructor* self = new(ELeave)CServerCommandConstructor(aName, aConstructor, aFactory, aReporter, aCif);
+	CleanupStack::PushL(self);
+	return self;
+	}
+
+EXPORT_C CServerCommandConstructor::~CServerCommandConstructor()
+	{
+	}
+
+EXPORT_C const TPtrC& CServerCommandConstructor::Name() const
+	{
+	return iName;
+	}
+
+EXPORT_C CServerCommandBase* CServerCommandConstructor::CreateImplementationLC() const
+	{
+	CServerCommandBase* commandBase = (*iConstructorLC)();
+	commandBase->SetReporter(iReporter);
+	commandBase->SetFactory(iFactory);
+	if (iCif)
+		{
+		commandBase->SetCif(*iCif);
+		}
+	return commandBase;	
+	}
+
+EXPORT_C TBool CServerCommandConstructor::IsFactory() const
+	{
+	return EFalse;
+	}
+
+CServerCommandConstructor::CServerCommandConstructor(const TDesC& aName, TServerCommandConstructor aConstructor, CServerCommandFactory& aFactory, MServerCommandReporter* aReporter, const CCommandInfoFile* aCif)
+	: iName(aName), iConstructorLC(aConstructor), iFactory(aFactory), iReporter(aReporter), iCif(aCif)
+	{
+	}
+
+TUint32 DefaultTPtrCHash(const TPtrC& aPtr)
+	{
+	return DefaultHash::Des16(aPtr);
+	}
+	
+TBool DefaultIdTPtrCRelation(const TPtrC& aPtr1, const TPtrC& aPtr2)
+	{
+	return DefaultIdentity::Des16(aPtr1, aPtr2);
+	}
+
+	
+//
+// CServerCommandFactory.
+//
+
+EXPORT_C CServerCommandFactory* CServerCommandFactory::NewL(MServerCommandReporter* aReporter, const CCommandInfoFile* aCif)
+	{
+	CServerCommandFactory* self = new(ELeave)CServerCommandFactory(aReporter, aCif);
+	CleanupStack::PushL(self);
+	self->ConstructL(ERoot);
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+EXPORT_C CServerCommandFactory* CServerCommandFactory::NewLC(const TDesC& aName, TServerCommandConstructor aConstructor, MServerCommandReporter* aReporter, const CCommandInfoFile* aCif)
+	{
+	CServerCommandFactory* self = new(ELeave)CServerCommandFactory(aName, aConstructor, aReporter, aCif);
+	CleanupStack::PushL(self);
+	self->ConstructL(EBranch);
+	return self;
+	}
+	
+EXPORT_C CServerCommandFactory::~CServerCommandFactory()
+	{
+	THashMapIter<const TPtrC, const CServerCommandConstructor*> iter(iSubCommands);
+	while (iter.NextKey())
+		{
+		delete *iter.CurrentValue();
+		}
+	iSubCommands.Close();
+	}
+
+EXPORT_C const CServerCommandConstructor& CServerCommandFactory::GetSubCommandL(const TDesC& aName) const
+	{
+	const CServerCommandConstructor* cmnd = GetSubCommand(aName);
+	if (!cmnd)
+		{
+		if (iReporter)
+			{
+			iReporter->ReportError(TServerCommandId(), KErrNotFound, _L("Couldn't create command \"%S\""), &aName);
+			}
+		User::Leave(KErrNotFound);
+		}
+	return *cmnd;
+	}
+
+const CServerCommandConstructor* CServerCommandFactory::GetSubCommand(const TDesC& aName) const
+	{
+	TPtrC name(aName);
+	const CServerCommandConstructor* const * cmnd = iSubCommands.Find(name);
+	if (!cmnd)
+		{
+		return NULL;
+		}
+	return *cmnd;
+	}
+
+EXPORT_C TBool CServerCommandFactory::IsFactory() const
+	{
+	return ETrue;
+	}
+
+EXPORT_C void CServerCommandFactory::AddSubCommandL(const CServerCommandConstructor* aCommand)
+	{
+	iSubCommands.InsertL(aCommand->Name(), aCommand);
+	}
+
+CServerCommandFactory::CServerCommandFactory(MServerCommandReporter* aReporter, const CCommandInfoFile* aCif)
+	: CServerCommandConstructor(KNullDesC, NULL, *this, aReporter, aCif), iSubCommands(THashFunction32<const TPtrC>(&DefaultTPtrCHash), TIdentityRelation<const TPtrC>(&DefaultIdTPtrCRelation))
+	{
+	}
+
+CServerCommandFactory::CServerCommandFactory(const TDesC& aName, TServerCommandConstructor aConstructor, MServerCommandReporter* aReporter, const CCommandInfoFile* aCif)
+	: CServerCommandConstructor(aName, aConstructor, *this, aReporter, aCif), iSubCommands(THashFunction32<const TPtrC>(&DefaultTPtrCHash), TIdentityRelation<const TPtrC>(&DefaultIdTPtrCRelation))
+	{
+	}
+
+void CServerCommandFactory::ConstructL(TFactoryType aType)
+	{
+	if (aType == ERoot)
+		{
+		AddLeafCommandL<CHelpCommand>();
+		}
+	}
+
+void CServerCommandFactory::LcCompleteLineL(TConsoleLine& aLine, const TChar&)
+	{
+	TLex lex(aLine.ContentsToCursor());
+	TInt pos = 0;
+	
+	const CServerCommandFactory* factory = this;
+	
+	do
+		{
+		lex.SkipSpaceAndMark();
+		pos = lex.Offset();
+		lex.SkipCharacters();
+		
+		if (!lex.Eos())
+			{
+			const CServerCommandConstructor* command = GetSubCommand(lex.MarkedToken());
+			if (!command || !command->IsFactory())
+				{
+				User::Leave(KErrNotFound);
+				}
+			factory = static_cast<const CServerCommandFactory*>(command);
+			}
+		
+		} while (!lex.Eos());
+	
+	
+	
+	RArray<TPtrC> commands;
+	CleanupClosePushL(commands);
+	TPtrC partialCommandName(lex.MarkedToken());
+
+	factory->ListCommandsL(commands);
+
+	// Remove commands that don't match.
+	TInt i;
+	for (i = (commands.Count() - 1); i >= 0; --i)
+		{
+		if (commands[i].Find(partialCommandName) != 0)
+			{
+			commands.Remove(i);
+			}
+		}
+
+	if (commands.Count() > 0)
+		{
+		CompleteLineL(aLine, pos, commands);
+		}
+
+	CleanupStack::PopAndDestroy(&commands);
+	}
+
+void CServerCommandFactory::CompleteLineL(TConsoleLine& aLine, TInt aPos, const RArray<TPtrC> aPossibilities) const
+	{
+	const TInt numPossibilities = aPossibilities.Count();
+
+	if (numPossibilities > 1)
+		{
+		// Fill out possibilities buffer.
+		IoUtils::CTextBuffer* possibilities = IoUtils::CTextBuffer::NewLC(0x100);
+		for (TInt i = 0; i < numPossibilities; ++i)
+			{
+			possibilities->AppendL(aPossibilities[i]);
+			if (i != (numPossibilities - 1))
+				{
+				possibilities->AppendL(KTab);
+				}
+			}
+
+		aLine.PrintCompletionPossibilitiesL(possibilities->Descriptor());
+		CleanupStack::PopAndDestroy(possibilities);
+		}
+
+	if (numPossibilities > 0)
+		{
+		IoUtils::CTextBuffer* completion = IoUtils::CTextBuffer::NewLC(0x100);
+		TPtrC commonChars(NULL, 0);
+		if (numPossibilities > 1)
+			{
+			// Find common leading characters of the possibilities.
+			TInt commonCharPos = -1;
+			TBool finished(EFalse);
+			do
+				{
+				++commonCharPos;
+				TChar character(0);
+				for (TInt i = 0; i < numPossibilities; ++i)
+					{
+					if (commonCharPos >= aPossibilities[i].Length())
+						{
+						finished = ETrue;
+						break;
+						}
+					else if (i == 0)
+						{
+						character = aPossibilities[0][commonCharPos];
+						character.Fold();
+						}
+					else
+						{
+						TChar c(aPossibilities[i][commonCharPos]);
+						c.Fold();
+						if (c != character)
+							{
+							finished = ETrue;
+							break;
+							}
+						}
+					}
+				}
+				while (!finished);
+
+			commonChars.Set(aPossibilities[0].Mid(0, commonCharPos));
+			}
+		else
+			{
+			commonChars.Set(aPossibilities[0]);
+			}
+
+		completion->AppendL(commonChars);
+		if (numPossibilities == 1)
+			{
+			completion->AppendL(KSpace);
+			}
+		if (completion->Descriptor().Length() > 0)
+			{
+			aLine.Replace(aPos, completion->Descriptor());
+			}
+		CleanupStack::PopAndDestroy(completion);
+		}
+	}
+
+EXPORT_C void CServerCommandFactory::ListCommandsL(RArray<TPtrC>& aList) const
+	{
+	THashMapIter<const TPtrC, const CServerCommandConstructor*> iter(iSubCommands);
+	while (iter.NextKey())
+		{
+		aList.AppendL(*iter.CurrentKey());
+		}
+	aList.Sort(TLinearOrder<TPtrC>(PtrOrder));
+	}
+
+
+
+//
+// CBranchCommandBase.
+//
+
+EXPORT_C CBranchCommandBase::~CBranchCommandBase()
+	{
+	delete iSubCommandEnum;
+	delete iSubCommandArgs;
+	}
+
+EXPORT_C CBranchCommandBase::CBranchCommandBase()
+	{
+	}
+
+EXPORT_C void CBranchCommandBase::DoRunL()
+	{
+	TPtrC subCommandName(iArguments.AsString(&iType));
+
+	CServerCommandBase* subCommand = Factory().GetSubCommandL(subCommandName).CreateImplementationLC();
+	ConfigureSubCommand(*subCommand);
+	subCommand->RunCommand(IoSession(), Stdin(), Stdout(), Stderr(), iSubCommandArgs ? iSubCommandArgs : &KNullDesC, &Env(), NULL);
+	if (subCommand->IsComplete())
+		{
+		HandleSubCommandComplete(*subCommand);
+		CleanupStack::PopAndDestroy(subCommand);
+		}
+	else
+		{
+		HandleBackgroundSubCommand(*subCommand);
+		CleanupStack::Pop(subCommand);
+		}
+	}
+
+void CBranchCommandBase::BuildEnumL()
+	{
+	_LIT(KComma, ",");
+	RArray<TPtrC> subCommands;
+	CleanupClosePushL(subCommands);
+	Factory().ListCommandsL(subCommands);
+	subCommands.Sort(TLinearOrder<TPtrC>(PtrOrder));
+	iSubCommandEnum = IoUtils::CTextBuffer::NewL(0x10);
+	for (TInt i=0; i<subCommands.Count(); ++i)
+		{
+		iSubCommandEnum->AppendL(subCommands[i]);
+		iSubCommandEnum->AppendL(KComma);
+		}
+	CleanupStack::PopAndDestroy(&subCommands);
+	}
+	
+EXPORT_C void CBranchCommandBase::ArgumentsL(RCommandArgumentList& aArguments)
+	{
+	_LIT(KArgType, "type");
+	_LIT(KArgArgs, "arguments");
+
+	if (!iSubCommandEnum)
+		{
+		BuildEnumL();
+		}
+
+	if (UsingCif())
+		{
+		aArguments.AppendEnumL((TInt&)iType, KArgType, iSubCommandEnum->Descriptor());
+		aArguments.AppendStringL(iSubCommandArgs, KArgArgs);
+		}
+	else
+		{
+		aArguments.AppendEnumL((TInt&)iType, KArgType, TypeDescription(), iSubCommandEnum->Descriptor());
+
+		_LIT(KArgArgsDescription, "A set of type-specific arguments and options.");
+		aArguments.AppendStringL(iSubCommandArgs, KArgArgs, KArgArgsDescription, KValueTypeFlagOptional | KValueTypeFlagLast);
+		}
+	}
+
+EXPORT_C const TDesC& CBranchCommandBase::TypeDescription()
+	{
+	return KNullDesC;
+	}
+
+EXPORT_C void CBranchCommandBase::ConfigureSubCommand(CServerCommandBase&)
+	{
+	}
+
+EXPORT_C void CBranchCommandBase::HandleSubCommandComplete(CServerCommandBase& aCommand)
+	{
+	if (aCommand.CompletionReason() < 0)
+		{
+		PrintError(aCommand.CompletionReason(), _L("Couldn't run sub-command \"%S\" of command \"%S\""), &aCommand.Name(), &Name());
+		}
+	}
+
+EXPORT_C void CBranchCommandBase::HandleBackgroundSubCommand(CServerCommandBase&)
+	{
+	}