libraries/iosrv/client/command_info_file.cpp
author Tom Sutcliffe <thomas.sutcliffe@accenture.com>
Thu, 26 Aug 2010 00:49:35 +0100
changeset 45 534b01198c2d
parent 0 7f656887cf89
child 70 b06038904ef8
permissions -rw-r--r--
Added ENotifyKeypresses and ECaptureCtrlC flags to CCommandBase. Commands can now get keypresses and handle ctrl-C via callbacks instead of having to implement custom active objects. As part of this extended the CCommandBase extension interface to MCommandExtensionsV2 for the new virtual functions KeyPressed(TUint aKeyCode, TUint aModifiers) and CtrlCPressed(). sudo now cleans up correctly by using ECaptureCtrlC.

// command_info_file.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 <f32file.h>
#include "ioutils.h"
#include "command_base.h"

_LIT(KCifExt, ".cif");
_LIT(KCifPathVar, "CIF_PATH");
_LIT(KCifPath, "\\resource\\cif\\fshell");
_LIT(KEnumSeparator, ",");
_LIT(KEnumQuote, "\"");
_LIT(KNewLine, "\r\n");
_LIT(KPartialCommandStart, "==");
_LIT(KCommandStart, "\r\n==");
_LIT(KCmndName, "name");
_LIT(KCmndShortDescription, "short-description");
_LIT(KCmndLongDescription, "long-description");
_LIT(KCmndSeeAlso, "see-also");
_LIT(KCmndCopyright, "copyright");
_LIT(KCmndArgument, "argument");
_LIT(KCmndOption, "option");
_LIT(KCmndInclude, "include");
_LIT(KCmndSubCommand, "sub-command");
_LIT(KCmndEnumValue, "enum-value");
_LIT(KCmndFlagOptional, "optional");
_LIT(KCmndFlagMultiple, "multiple");
_LIT(KCmndFlagLast, "last");
_LIT(KTypeBool, "bool");
_LIT(KTypeInt, "int");
_LIT(KTypeUint, "uint");
_LIT(KTypeString, "string");
_LIT(KTypeFileName, "filename");
_LIT(KTypeEnum, "enum");
_LIT(KTypeReal, "real");
_LIT(KTypeUint64, "uint64");
_LIT(KTypeInt64, "int64");

using namespace IoUtils;

TInt SkipTo(TLex& aLex, const TDesC& aDes)
	{
	TInt ret = aLex.Remainder().Find(aDes);
	if (ret >= 0)
		{
		aLex.Inc(ret);
		return KErrNone;
		}
	return KErrNotFound;
	}

TInt SkipToEnd(TLex& aLex, const TDesC& aDes)
	{
	TInt ret = aLex.Remainder().Find(aDes);
	if (ret >= 0)
		{
		aLex.Inc(ret + aDes.Length());
		return KErrNone;
		}
	return KErrNotFound;
	}

TInt SkipToNextCommand(TLex& aLex)
	{
	if ((aLex.Offset() == 0) && (aLex.Remainder().Find(KPartialCommandStart) == 0))
		{
		return KErrNone;
		}

	return SkipTo(aLex, KCommandStart);
	}

TPtrC NextCommand(TLex& aLex)
	{
	TPtrC command;
	if (SkipToNextCommand(aLex) == KErrNone)
		{
		SkipToEnd(aLex, KPartialCommandStart);
		aLex.Mark();
		aLex.SkipCharacters();
		command.Set(aLex.MarkedToken());
		}
	else
		{
		aLex.Inc(aLex.Remainder().Length());
		}
	return command;
	}

TPtrC LineRemainder(TLex& aLex)
	{
	aLex.Mark();
	if (SkipTo(aLex, KNewLine) == KErrNone)
		{
		return aLex.MarkedToken();
		}
	else
		{
		return aLex.Remainder();
		}
	}

TPtrC NextWord(TLex& aLex)
	{
	aLex.SkipSpaceAndMark();
	aLex.SkipCharacters();
	TPtrC word(aLex.MarkedToken());
	return word;
	}

TPtrC TextToNextCommand(TLex& aLex)
	{
	aLex.Mark();

	if (SkipToNextCommand(aLex) != KErrNone)
		{
		aLex.Inc(aLex.Remainder().Length());
		}

	TLex lex(aLex.MarkedToken());
	
	// Strip leading white space.
	while (!lex.Eos())
		{
		if (lex.Peek().IsSpace())
			{
			lex.Get();
			}
		else
			{
			break;
			}
		}
	lex.Mark();
	TInt startOffset = lex.Offset();

	// Remove trailing white space.
	lex.Inc(lex.Remainder().Length());
	while (lex.Offset() > startOffset)
		{
		lex.UnGet();
		if (!lex.Peek().IsSpace())
			{
			lex.Get();
			break;
			}
		}

	return lex.MarkedToken();
	}

void AppendL(RBuf& aBuf, const TDesC& aDes, TBool aQuote)
	{
	const TInt requiredSpace = aDes.Length() + ((aBuf.Length() == 0) ? 0 : KEnumSeparator().Length()) + (aQuote ? 2 : 0);
	if ((aBuf.Length() + requiredSpace) > aBuf.MaxLength())
		{
		aBuf.ReAllocL(Max(aBuf.Length() * 2, aBuf.Length() + requiredSpace));
		}
	if (aBuf.Length() > 0)
		{
		aBuf.Append(KEnumSeparator);
		}
	if (aQuote)
		{
		aBuf.Append(KEnumQuote);
		}
	aBuf.Append(aDes);
	if (aQuote)
		{
		aBuf.Append(KEnumQuote);
		}
	}

void GetEnumValuesL(TLex& aLex, RBuf& aValues, RBuf& aDescriptions)
	{
	aValues.CreateL(0x100);
	aDescriptions.CreateL(0x100);

	while (!aLex.Eos())
		{
		TLexMark mark;
		aLex.Mark(mark);
		TPtrC command(NextCommand(aLex));
		if (command == KCmndEnumValue)
			{
			TPtrC value(NextWord(aLex));
			TPtrC description(TextToNextCommand(aLex));
			if (value.Length() == 0)
				{
				User::Leave(KErrArgument);
				}
			AppendL(aValues, value, EFalse);
			if (description.Length() > 0)
				{
				AppendL(aDescriptions, description, ETrue);
				}
			}
		else
			{
			aLex.UnGetToMark(mark);
			break;
			}
		}
	}


EXPORT_C CCommandInfoFile* CCommandInfoFile::NewL(RFs& aFs, const TDesC& aFileName)
	{
	CCommandInfoFile* self = new(ELeave) CCommandInfoFile(aFileName);
	CleanupStack::PushL(self);
	self->ConstructL(aFs);
	CleanupStack::Pop(self);
	return self;
	}

EXPORT_C CCommandInfoFile* CCommandInfoFile::NewL(RFs& aFs, const CEnvironment& aEnvironment, const TDesC& aCommandName)
	{
	TFileName2 fileName;
	TRAPD(err, fileName.Copy(aEnvironment.GetAsDesL(KCifPathVar)));
	if (err) fileName.Copy(KCifPath);

	// Search the drive the command is running from first (rather than what the session path happens to be, which was the previous behaviour)
	if (!fileName.HasDriveLetter()) fileName.Insert(0, _L("?:"));
	fileName[0] = RProcess().FileName()[0];

	fileName.AppendComponentL(aCommandName);
	fileName.SetExtL(KCifExt);
	StaticLeaveIfErr(fileName.FindFile(aFs), _L("Couldn't find \"%S\""), &fileName);

	CCommandInfoFile* cif = CCommandInfoFile::NewL(aFs, fileName);
	if (cif->Name() != aCommandName)
		{
		CleanupStack::PushL(cif);
		StaticLeaveIfErr(KErrArgument, _L("Command named \"%S\" in implementation but \"%S\" in \"%S\""), &aCommandName, &cif->Name(), &fileName);
		}
	return cif;
	}

void CCommandInfoFile::ConstructL(RFs& aFs)
	{
	ReadFileL(aFs, iFileName);
	}

void CCommandInfoFile::ReadFileL(RFs& aFs, const TDesC& aFileName)
	{
	// Read the file into a buffer.
	RFile file;
	StaticLeaveIfErr(file.Open(aFs, aFileName, EFileRead | EFileShareReadersOnly), _L("Couldn't open '%S' for reading"), &aFileName);
	CleanupClosePushL(file);
	TInt fileSize;
	User::LeaveIfError(file.Size(fileSize));
	RBuf8 buf8;
	buf8.CreateL(fileSize);
	buf8.CleanupClosePushL();
	User::LeaveIfError(file.Read(buf8));
	RBuf& buf = NewBuffer();
	buf.CreateL(fileSize);
	buf.Copy(buf8);
	CleanupStack::PopAndDestroy(2, &file);
	TLex lex(buf);
	while (!lex.Eos())
		{
		if (iProcessInclude || !iCurrentChild)
			{
			ReadDetailsL(lex, aFs, aFileName);
			}
		else
			{
			iCurrentChild->ReadDetailsL(lex, aFs, aFileName);
			}
		}
	}

void CCommandInfoFile::ReadDetailsL(TLex& aLex, RFs& aFs, const TDesC& aFileName)
	{
	SkipToNextCommand(aLex); // Ignore everything before the first '==' command.

	while (!aLex.Eos())
		{
		TLexMark mark;
		aLex.Mark(mark);
		TPtrC command(NextCommand(aLex));
		if (command == KNullDesC)
			{
			// Do nothing - we're at the end of the string.
			}
		else if (command == KCmndName)
			{
			aLex.SkipSpaceAndMark();
			aLex.SkipCharacters();
			iName.Set(aLex.MarkedToken());
			}
		else if (command == KCmndShortDescription)
			{
			iShortDescription.Set(TextToNextCommand(aLex));
			}
		else if (command == KCmndLongDescription)
			{
			iLongDescription.Set(TextToNextCommand(aLex));
			}
		else if (command == KCmndSeeAlso)
			{
			iSeeAlso.Set(TextToNextCommand(aLex));
			}
		else if (command == KCmndCopyright)
			{
			iCopyright.Set(TextToNextCommand(aLex));
			}
		else if (command == KCmndArgument)
			{
			ReadArgumentL(aLex, aFileName);
			}
		else if (command == KCmndOption)
			{
			ReadOptionL(aLex, aFileName);
			}
		else if (command == KCmndInclude)
			{
			if (iParent == NULL)
				{
				iProcessInclude = EFalse;
				TLex lineLex(LineRemainder(aLex));
				TPtrC fileName(NextWord(lineLex));
				TFileName2* fullFileName = new(ELeave) TFileName2(aFileName);
				CleanupStack::PushL(fullFileName);
				fullFileName->SetNameAndExtL(fileName);
				ReadFileL(aFs, *fullFileName);
				CleanupStack::PopAndDestroy(fullFileName);
				break;
				}
			else
				{
				// We're a sub-command. Let control return to the root to handle the include.
				aLex.UnGetToMark(mark);
				iParent->ProcessInclude(*this);
				break;
				}
			}
		else if (command == KCmndSubCommand)
			{
			if (iParent == NULL)
				{
				TLex lineLex(LineRemainder(aLex));
				AddSubCommandL(lineLex, aLex, aFs, aFileName);
				}
			else
				{
				// We're a sub-command. Let control return to the root to handle the next sub-command.
				aLex.UnGetToMark(mark);
				iParent->ProcessNewChild();
				break;
				}
			}
		else
			{
			StaticLeaveIfErr(KErrArgument, _L("Unknown command \"%S\" in \"%S\""), &command, &aFileName);
			}
		}
	}

void CCommandInfoFile::ReadArgumentL(TLex& aLex, const TDesC& aFileName)
	{
	TLex lineLex(LineRemainder(aLex));
	TUint flags = 0;
	TBool multiple(EFalse);
	TPtrC argType(NextWord(lineLex));
	TPtrC argName(NextWord(lineLex));
	TPtrC envVar;
	while (!lineLex.Eos())
		{
		TPtrC word(NextWord(lineLex));
		if (word == KCmndFlagOptional)
			{
			flags |= KValueTypeFlagOptional;
			}
		else if (word == KCmndFlagMultiple)
			{
			multiple = ETrue;
			}
		else if (word == KCmndFlagLast)
			{
			flags |= KValueTypeFlagLast;
			}
		else
			{
			envVar.Set(word);
			}
		}

	if (argType.Length() == 0)
		{
		StaticLeaveIfErr(KErrArgument, _L("Argument missing type"));
		}
	else if (argName.Length() == 0)
		{
		StaticLeaveIfErr(KErrArgument, _L("Argument missing name"));
		}
	else
		{
		TPtrC argDescription = TextToNextCommand(aLex);
		if (argType == KTypeEnum)
			{
			RBuf& enumValues = NewBuffer();
			RBuf& enumDescriptions = NewBuffer();
			GetEnumValuesL(aLex, enumValues, enumDescriptions);
			if (multiple)
				{
				RArray<TInt> dummyArray;
				if (enumDescriptions.Length() > 0)
					{
					iArguments.AppendEnumL(dummyArray, argName, argDescription, enumValues, enumDescriptions, flags, envVar);
					}
				else
					{
					iArguments.AppendEnumL(dummyArray, argName, argDescription, enumValues, flags, envVar);
					}
				}
			else
				{
				TInt dummyInt;
				if (enumDescriptions.Length() > 0)
					{
					iArguments.AppendEnumL(dummyInt, argName, argDescription, enumValues, enumDescriptions, flags, envVar);
					}
				else
					{
					iArguments.AppendEnumL(dummyInt, argName, argDescription, enumValues, flags, envVar);
					}
				}
			}
		else if (argType == KTypeInt)
			{
			if (multiple)
				{
				RArray<TInt> dummyArray;
				iArguments.AppendIntL(dummyArray, argName, argDescription, flags, envVar);
				}
			else
				{
				TInt dummyInt;
				iArguments.AppendIntL(dummyInt, argName, argDescription, flags, envVar);
				}
			}
		else if (argType == KTypeUint)
			{
			if (multiple)
				{
				RArray<TUint> dummyArray;
				iArguments.AppendUintL(dummyArray, argName, argDescription, flags, envVar);
				}
			else
				{
				TUint dummyUint;
				iArguments.AppendUintL(dummyUint, argName, argDescription, flags, envVar);
				}
			}
		else if (argType == KTypeString)
			{
			if (multiple)
				{
				RPointerArray<HBufC> dummyArray;
				iArguments.AppendStringL(dummyArray, argName, argDescription, flags, envVar);
				}
			else
				{
				HBufC* dummyHBufC;
				iArguments.AppendStringL(dummyHBufC, argName, argDescription, flags, envVar);
				}
			}
		else if (argType == KTypeFileName)
			{
			if (multiple)
				{
				RArray<TFileName2> dummyArray;
				iArguments.AppendFileNameL(dummyArray, argName, argDescription, flags, envVar);
				}
			else
				{
				TFileName2 dummyFileName;
				iArguments.AppendFileNameL(dummyFileName, argName, argDescription, flags, envVar);
				}
			}
		else if (argType == KTypeReal)
			{
			if (multiple)
				{
				RArray<TReal64> dummyArray;
				iArguments.AppendRealL(dummyArray, argName, argDescription, flags, envVar);
				}
			else
				{
				TReal64 dummyReal;
				iArguments.AppendRealL(dummyReal, argName, argDescription, flags, envVar);
				}
			}
		else if (argType == KTypeUint64)
			{
			if (multiple)
				{
				RArray<TUint64> dummyArray;
				iArguments.AppendUintL(dummyArray, argName, argDescription, flags, envVar);
				}
			else
				{
				TUint64 dummyUint64;
				iArguments.AppendUintL(dummyUint64, argName, argDescription, flags, envVar);
				}
			}
		else if (argType == KTypeInt64)
			{
			if (multiple)
				{
				RArray<TInt64> dummyArray;
				iArguments.AppendIntL(dummyArray, argName, argDescription, flags, envVar);
				}
			else
				{
				TInt64 dummyInt64;
				iArguments.AppendIntL(dummyInt64, argName, argDescription, flags, envVar);
				}
			}
		else
			{
			StaticLeaveIfErr(KErrArgument, _L("Unknown argument type \"%S\" in \"%S\""), &argType, &aFileName);
			}
		}
	}

void CCommandInfoFile::ReadOptionL(TLex& aLex, const TDesC& aFileName)
	{
	TLex lineLex(LineRemainder(aLex));
	TBool multiple(EFalse);
	TPtrC optType(NextWord(lineLex));
	TPtrC optShortName(NextWord(lineLex));
	TPtrC optLongName(NextWord(lineLex));
	TPtrC envVar;
	while (!lineLex.Eos())
		{
		TPtrC word(NextWord(lineLex));
		if (word == KCmndFlagMultiple)
			{
			multiple = ETrue;
			}
		else
			{
			envVar.Set(word);
			}
		}

	if (optType.Length() == 0)
		{
		StaticLeaveIfErr(KErrArgument, _L("Option missing type"));
		}
	else if (optShortName.Length() == 0)
		{
		StaticLeaveIfErr(KErrArgument, _L("Option missing short name"));
		}
	else if (optShortName.Length() != 1)
		{
		StaticLeaveIfErr(KErrArgument, _L("Option short name not a single character"));
		}
	else if (optLongName.Length() == 0)
		{
		StaticLeaveIfErr(KErrArgument, _L("Option missing long name"));
		}
	else
		{
		TPtrC argDescription = TextToNextCommand(aLex);
		if (optType == KTypeEnum)
			{
			RBuf& enumValues = NewBuffer();
			RBuf& enumDescriptions = NewBuffer();
			GetEnumValuesL(aLex, enumValues, enumDescriptions);
			if (multiple)
				{
				RArray<TInt> dummyArray;
				if (enumDescriptions.Length() > 0)
					{
					iOptions.AppendEnumL(dummyArray, optShortName[0], optLongName, argDescription, enumValues, enumDescriptions, 0, envVar);
					}
				else
					{
					iOptions.AppendEnumL(dummyArray, optShortName[0], optLongName, argDescription, enumValues, 0, envVar);
					}
				}
			else
				{
				TInt dummyInt;
				if (enumDescriptions.Length() > 0)
					{
					iOptions.AppendEnumL(dummyInt, optShortName[0], optLongName, argDescription, enumValues, enumDescriptions, 0, envVar);
					}
				else
					{
					iOptions.AppendEnumL(dummyInt, optShortName[0], optLongName, argDescription, enumValues, 0, envVar);
					}
				}
			}
		else if (optType == KTypeBool)
			{
			if (multiple)
				{
				RArray<TBool> dummyArray;
				iOptions.AppendBoolL(dummyArray, optShortName[0], optLongName, argDescription, 0, envVar);
				}
			else
				{
				TBool dummyBool;
				iOptions.AppendBoolL(dummyBool, optShortName[0], optLongName, argDescription, 0, envVar);
				}
			}
		else if (optType == KTypeInt)
			{
			if (multiple)
				{
				RArray<TInt> dummyArray;
				iOptions.AppendIntL(dummyArray, optShortName[0], optLongName, argDescription, 0, envVar);
				}
			else
				{
				TInt dummyInt;
				iOptions.AppendIntL(dummyInt, optShortName[0], optLongName, argDescription, 0, envVar);
				}
			}
		else if (optType == KTypeUint)
			{
			if (multiple)
				{
				RArray<TUint> dummyArray;
				iOptions.AppendUintL(dummyArray, optShortName[0], optLongName, argDescription, 0, envVar);
				}
			else
				{
				TUint dummyUint;
				iOptions.AppendUintL(dummyUint, optShortName[0], optLongName, argDescription, 0, envVar);
				}
			}
		else if (optType == KTypeString)
			{
			if (multiple)
				{
				RPointerArray<HBufC> dummyArray;
				iOptions.AppendStringL(dummyArray, optShortName[0], optLongName, argDescription, 0, envVar);
				}
			else
				{
				HBufC* dummyHBufC;
				iOptions.AppendStringL(dummyHBufC, optShortName[0], optLongName, argDescription, 0, envVar);
				}
			}
		else if (optType == KTypeFileName)
			{
			if (multiple)
				{
				RArray<TFileName2> dummyArray;
				iOptions.AppendFileNameL(dummyArray, optShortName[0], optLongName, argDescription, 0, envVar);
				}
			else
				{
				TFileName2 dummyFileName;
				iOptions.AppendFileNameL(dummyFileName, optShortName[0], optLongName, argDescription, 0, envVar);
				}
			}
		else if (optType == KTypeReal)
			{
			if (multiple)
				{
				RArray<TReal64> dummyArray;
				iOptions.AppendRealL(dummyArray, optShortName[0], optLongName, argDescription, 0, envVar);
				}
			else
				{
				TReal64 dummyReal;
				iOptions.AppendRealL(dummyReal, optShortName[0], optLongName, argDescription, 0, envVar);
				}
			}
		else if (optType == KTypeUint64)
			{
			if (multiple)
				{
				RArray<TUint64> dummyArray;
				iOptions.AppendUintL(dummyArray, optShortName[0], optLongName, argDescription, 0, envVar);
				}
			else
				{
				TUint64 dummyUint64;
				iOptions.AppendUintL(dummyUint64, optShortName[0], optLongName, argDescription, 0, envVar);
				}
			}
		else if (optType == KTypeInt64)
			{
			if (multiple)
				{
				RArray<TInt64> dummyArray;
				iOptions.AppendIntL(dummyArray, optShortName[0], optLongName, argDescription, 0, envVar);
				}
			else
				{
				TInt64 dummyInt64;
				iOptions.AppendIntL(dummyInt64, optShortName[0], optLongName, argDescription, 0, envVar);
				}
			}
		else
			{
			StaticLeaveIfErr(KErrArgument, _L("Unknown option type \"%S\" in \"%S\""), &optType, &aFileName);
			}
		}
	}

void CCommandInfoFile::ProcessNewChild()
	{
	if (iParent)
		{
		iParent->ProcessNewChild();
		}
	else
		{
		ASSERT(!iProcessInclude);
		iCurrentChild = NULL;
		}
	}

void CCommandInfoFile::ProcessInclude(CCommandInfoFile& aChild)
	{
	if (iParent)
		{
		iParent->ProcessInclude(aChild);
		}
	else
		{
		iProcessInclude = ETrue;
		iCurrentChild = &aChild;
		}
	}

void CCommandInfoFile::AddSubCommandL(TLex& aNameLex, TLex& aDataLex, RFs& aFs, const TDesC& aFileName)
	{
	TPtrC subCommandName(NextWord(aNameLex));
	TBool found(EFalse);
	for (TInt i = (iChildren.Count() - 1); i >= 0; --i)
		{
		if (iChildren[i]->Name() == subCommandName)
			{
			iChildren[i]->AddSubCommandL(aNameLex, aDataLex, aFs, aFileName);
			found = ETrue;
			break;
			}
		}
	__ASSERT_ALWAYS(found || (aNameLex.Remainder().Length() == 0), IoUtils::Panic(ECifSubCommandParentNotFound));
	if (!found)
		{
		CCommandInfoFile* subCommand = new(ELeave) CCommandInfoFile(*this);
		CleanupStack::PushL(subCommand);
		subCommand->iName.Set(subCommandName);
		subCommand->ReadDetailsL(aDataLex, aFs, aFileName);
		iChildren.AppendL(subCommand);
		CleanupStack::Pop(subCommand);
		}
	}

RBuf& CCommandInfoFile::NewBuffer()
	{
	RBuf buf;
	iBufs.AppendL(buf);
	return iBufs[iBufs.Count() - 1];
	}

EXPORT_C CCommandInfoFile::~CCommandInfoFile()
	{
	iArguments.Close();
	iOptions.Close();

	const TInt numBufs = iBufs.Count();
	for (TInt i = 0; i < numBufs; ++i)
		{
		iBufs[i].Close();
		}
	iBufs.Close();
	iChildren.ResetAndDestroy();
	}

EXPORT_C const TDesC& CCommandInfoFile::Name() const
	{
	return iName;
	}

EXPORT_C const TDesC& CCommandInfoFile::ShortDescription() const
	{
	return iShortDescription;
	}

EXPORT_C const TDesC& CCommandInfoFile::LongDescription() const
	{
	return iLongDescription;
	}

EXPORT_C const TDesC& CCommandInfoFile::SeeAlso() const
	{
	return iSeeAlso;
	}

EXPORT_C const TDesC& CCommandInfoFile::Copyright() const
	{
	return iCopyright;
	}

EXPORT_C const RCommandArgumentList& CCommandInfoFile::Arguments()
	{
	return iArguments;
	}

EXPORT_C const RCommandOptionList& CCommandInfoFile::Options() const
	{
	return iOptions;
	}

template <class T> void CombineL(const T& aSourceList, T& aDestinationList, const TDesC& aListType, const TDesC& aCifName)
	{
	const TInt sourceCount = aSourceList.Count();
	const TInt destinationCount = aDestinationList.Count();

	if (sourceCount < destinationCount) // Note, allow the source to have more than the destination because some things are compile time configurable but CIF files don't support this.
		{
		StaticLeaveIfErr(KErrArgument, _L("%S count mismatch between \"%S\" (%d) and implementation (%d)"), &aListType, &aCifName, sourceCount, destinationCount);
		}

	for (TInt i = 0; i < destinationCount; ++i)
		{
		const TDesC& destinationName = aDestinationList[i].Name();
		TInt sourcePos = aSourceList.Find(destinationName);
		if (sourcePos < 0)
			{
			StaticLeaveIfErr(KErrArgument, _L("Couldn't find %S \"%S\" in \"%S\""), &aListType, &destinationName, &aCifName);
			}

		TUint sourceType = aSourceList[sourcePos].ValueType();
		TUint sourceTypeForComparison = sourceType & ~(KValueTypeFlagOptional|KValueTypeFlagLast); // destType can't know about these flags, so clear them from sourceType so we don't have problems when destType doesn't set them
		TUint destType = aDestinationList[i].ValueType();
		if (sourceTypeForComparison != destType)
			{
			StaticLeaveIfErr(KErrArgument, _L("Type mismatch for %S '%S' between \"%S\" (0x%x) and implementation (0x%x)"), &aListType, &destinationName, &aCifName, sourceType, destType);
			}
		aDestinationList[i].Combine(aSourceList[sourcePos]);
		}
	}

EXPORT_C void CCommandInfoFile::AssignL(RCommandArgumentList& aArguments, RCommandOptionList& aOptions) const
	{
	CombineL(iArguments, aArguments, _L("Argument"), iFileName);
	CombineL(iOptions, aOptions, _L("Option"), iFileName);
	}

EXPORT_C TInt CCommandInfoFile::NumSubCommands() const
	{
	return iChildren.Count();
	}

EXPORT_C const CCommandInfoFile& CCommandInfoFile::SubCommand(TInt aIndex) const
	{
	return *(iChildren[aIndex]);
	}

EXPORT_C const CCommandInfoFile* CCommandInfoFile::SubCommand(const TDesC& aSubCommandName) const
	{
	for (TInt i = (NumSubCommands() - 1); i >= 0; --i)
		{
		const CCommandInfoFile& thisSubCommand = SubCommand(i);
		if (thisSubCommand.Name() == aSubCommandName)
			{
			return &thisSubCommand;
			}
		}
	return NULL;
	}

CCommandInfoFile::CCommandInfoFile(const TDesC& aFileName)
	: iFileName(aFileName)
	{
	}

CCommandInfoFile::CCommandInfoFile(CCommandInfoFile& aParent)
	: iFileName(aParent.iFileName), iParent(&aParent)
	{
	}