Tidied iocli exports, build macro tweaks.
Removed 4 overloads of CCommandBase::RunCommand[L] that are no longer used at all, and changed one more to not be exported as it's only used internally to iocli.dll.
fixed builds on platforms that don't support btrace or any form of tracing.
// line_completer.cpp
//
// Copyright (c) 2006 - 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 <fshell/ltkutils.h>
#include "lexer.h"
#include "command_factory.h"
#include "line_completer.h"
_LIT(KTab, "\t");
_LIT(KWild, "*");
_LIT(KDoubleQuote, "\"");
_LIT(KSpace, " ");
_LIT(KFileNameSlash, "\\");
_LIT(KPipe, "|");
_LIT(KDoublePipe, "||");
_LIT(KAmpersand, "&");
_LIT(KDoubleAmpersand, "&&");
_LIT(KAmpersandPipe, "&|");
_LIT(KSemicolon, ";");
_LIT(KDollar, "$");
_LIT(KDashDash, "--");
_LIT(KDash, "-");
CLineCompleter* CLineCompleter::NewL(RFs& aFs, CCommandFactory& aCommandFactory, IoUtils::CEnvironment& aEnv)
{
CLineCompleter* self = new(ELeave) CLineCompleter(aFs, aCommandFactory, aEnv);
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop(self);
return self;
}
CLineCompleter::~CLineCompleter()
{
delete iLexer;
delete iLastUsedCif;
}
void CLineCompleter::LcCompleteLineL(TConsoleLine& aLine, const TChar& aEscapeChar)
{
iLexer->Set(aLine.ContentsToCursor(), aEscapeChar);
TToken token(iLexer->NextToken());
TToken firstToken = token;
TToken prevToken(TToken::EString, KNullDesC, aLine.ContentsToCursor().Length());
TInt lastTokenIdx = 0;
while (iLexer->More())
{
// More than one token - skip to last.
prevToken = token;
token = iLexer->NextToken();
lastTokenIdx++;
}
switch (prevToken.Type())
{
case TToken::EPipe:
case TToken::EAmpersand:
case TToken::ESemicolon:
case TToken::EDoublePipe:
case TToken::EDoubleAmpersand:
case TToken::EAmpersandPipe:
CompleteCommandNameL(aLine, token, aEscapeChar);
break;
default:
if (token.Position() + token.String().Length() < aLine.ContentsToCursor().Length())
{
// Looks like there's whitespace after this token, so consider it to be completing the empty token.
token = TToken(TToken::EString, KNullDesC, aLine.ContentsToCursor().Length());
}
else if (lastTokenIdx == 0)
{
CompleteCommandNameL(aLine, token, aEscapeChar);
break;
}
if (token.String().Left(1) == KDollar)
{
CompleteEnvVarL(aLine, token, aEscapeChar);
}
else if (token.String() == KDash || token.String().Left(2) == KDashDash)
{
// Check the CIF is loaded and it's the right one
if (iLastUsedCif && iLastUsedCif->Name().CompareF(firstToken.String()) != 0)
{
delete iLastUsedCif;
iLastUsedCif = NULL;
}
TInt err = KErrNone;
if (iLastUsedCif == NULL)
{
// We need to supress StaticLeaveIfErrs from CCommandInfoFile::NewL causing a PrintError if there's no CIF for this command,
// hence the use of TRAP_QUIETLY.
TRAP_QUIETLY(err, iLastUsedCif = IoUtils::CCommandInfoFile::NewL(iFs, iEnv, firstToken.String()));
}
if (err == KErrNone)
{
CompleteOptionL(aLine, token, aEscapeChar);
}
}
else
{
CompleteFileNameL(aLine, token, aEscapeChar);
}
break;
}
}
CLineCompleter::CLineCompleter(RFs& aFs, CCommandFactory& aCommandFactory, IoUtils::CEnvironment& aEnv)
: iFs(aFs), iCommandFactory(aCommandFactory), iEnv(aEnv)
{
}
void CLineCompleter::ConstructL()
{
iLexer = CLexer::NewL();
iLexer->DefineTokenTypeL(TToken::EPipe, KPipe);
iLexer->DefineTokenTypeL(TToken::EAmpersand, KAmpersand);
iLexer->DefineTokenTypeL(TToken::ESemicolon, KSemicolon);
iLexer->DefineTokenTypeL(TToken::EDoublePipe, KDoublePipe);
iLexer->DefineTokenTypeL(TToken::EDoubleAmpersand, KDoubleAmpersand);
iLexer->DefineTokenTypeL(TToken::EAmpersandPipe, KAmpersandPipe);
}
HBufC* SimplifyStringLC(const TDesC& aString, const TChar& aEscapeChar)
{
HBufC* buf = HBufC::NewLC(aString.Length());
TPtr bufPtr(buf->Des());
TLex lex(aString);
while (!lex.Eos())
{
TChar c = lex.Get();
if (c == aEscapeChar)
{
if (!lex.Eos())
{
bufPtr.Append(lex.Get());
}
}
else if (c == '\'')
{
while (!lex.Eos())
{
c = lex.Get();
if (c == '\'')
{
break;
}
else
{
bufPtr.Append(c);
}
}
}
else if (c == '"')
{
while (!lex.Eos())
{
c = lex.Get();
if (c == aEscapeChar)
{
bufPtr.Append(lex.Get());
}
else if (c == '"')
{
break;
}
else
{
bufPtr.Append(c);
}
}
}
else
{
bufPtr.Append(c);
}
}
return buf;
}
void CLineCompleter::CompleteCommandNameL(TConsoleLine& aLine, const TToken& aToken, const TChar& aEscapeChar) const
{
RArray<TPtrC> commands;
CleanupClosePushL(commands);
iCommandFactory.ListCommandsL(commands);
HBufC* commandBuf = SimplifyStringLC(aToken.String(), aEscapeChar);
// Remove commands that don't match aLine.
TInt i;
for (i = (commands.Count() - 1); i >= 0; --i)
{
if (commands[i].Find(*commandBuf) != 0)
{
commands.Remove(i);
}
}
if (commands.Count() > 0)
{
CompleteL(aLine, aToken, commands, NULL, NULL);
}
else
{
CompleteFileNameL(aLine, aToken, aEscapeChar);
}
CleanupStack::PopAndDestroy(2, &commands);
}
void RemovePartialElement(HBufC*& aBuf)
{
TPtr ptr(aBuf->Des());
while (ptr.Length() > 0)
{
if (ptr[ptr.Length() - 1] == KPathDelimiter)
{
break;
}
else
{
ptr.Delete(ptr.Length() - 1, 1);
}
}
}
void CLineCompleter::CompleteFileNameL(TConsoleLine& aLine, const TToken& aToken, const TChar& aEscapeChar) const
{
HBufC* fileNameBuf = SimplifyStringLC(aToken.String(), aEscapeChar);
IoUtils::TFileName2 absFileName(*fileNameBuf);
RemovePartialElement(fileNameBuf);
absFileName.Append(KWild);
absFileName.MakeAbsoluteL(iEnv.Pwd());
CDir* files;
User::LeaveIfError(iFs.GetDir(absFileName, KEntryAttNormal | KEntryAttDir, ESortByName, files));
CleanupStack::PushL(files);
RArray<TPtrC> fileNames;
CleanupClosePushL(fileNames);
const TInt numFiles = files->Count();
if (numFiles == 1)
{
absFileName = absFileName.DriveAndPath();
absFileName.AppendComponentL((*files)[0].iName);
TEntry entry;
User::LeaveIfError(iFs.Entry(absFileName, entry));
User::LeaveIfError(fileNames.Append(absFileName.NameAndExt()));
CompleteL(aLine, aToken, fileNames, fileNameBuf, entry.IsDir() ? &KFileNameSlash : NULL);
}
else if (numFiles > 1)
{
for (TInt i = 0; i < numFiles; ++i)
{
User::LeaveIfError(fileNames.Append(TPtrC((*files)[i].iName)));
}
CompleteL(aLine, aToken, fileNames, fileNameBuf, NULL);
}
CleanupStack::PopAndDestroy(3, fileNameBuf);
}
TBool NeedsQuoting(const TDesC& aDes)
{
return ((aDes.Locate(TChar(' ')) >= 0) || aDes.Locate(TChar('\'')) >= 0);
}
void CLineCompleter::CompleteL(TConsoleLine& aLine, const TToken& aToken, const RArray<TPtrC> aPossibilities, const TDesC* aPrefix, const TDesC* aSuffix, TBool aPrefixIsPartOfToken) 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)
{
if (aPrefixIsPartOfToken) possibilities->AppendL(*aPrefix);
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]);
}
TBool quote(EFalse);
if ((aPrefix && NeedsQuoting(*aPrefix)) || NeedsQuoting(commonChars) || (aSuffix && NeedsQuoting(*aSuffix)))
{
quote = ETrue;
}
if (quote)
{
completion->AppendL(KDoubleQuote);
}
if (aPrefix)
{
completion->AppendL(*aPrefix);
}
completion->AppendL(commonChars);
if ((numPossibilities == 1) && quote)
{
completion->AppendL(KDoubleQuote);
}
if (numPossibilities == 1)
{
if (aSuffix)
{
completion->AppendL(*aSuffix);
}
else
{
completion->AppendL(KSpace);
}
}
if (completion->Descriptor().Length() > 0)
{
aLine.Replace(aToken.Position(), completion->Descriptor());
}
CleanupStack::PopAndDestroy(completion);
}
}
void CLineCompleter::CompleteEnvVarL(TConsoleLine& aLine, const TToken& aToken, const TChar& /*aEscapeChar*/) const
{
RArray<TPtrC> matchingVars;
CleanupClosePushL(matchingVars);
TPtrC envVar = aToken.String().Mid(1); // Lose the $
RPointerArray<HBufC> keys;
LtkUtils::CleanupResetAndDestroyPushL(keys);
iEnv.GetKeysL(keys);
const TInt count = keys.Count();
for (TInt i = 0; i < count; i++)
{
const TDesC& key = *keys[i];
if (key.Left(envVar.Length()) == envVar)
{
matchingVars.AppendL(TPtrC(key));
}
}
CompleteL(aLine, aToken, matchingVars, &KDollar, NULL, ETrue);
CleanupStack::PopAndDestroy(2, &matchingVars); // keys, matchingVars
}
void CLineCompleter::CompleteOptionL(TConsoleLine& aLine, const TToken& aToken, const TChar& /*aEscapeChar*/) const
{
RArray<TPtrC> matchingOptions;
CleanupClosePushL(matchingOptions);
TPtrC optFrag = aToken.String();
if (optFrag.Length() >= 2) optFrag.Set(optFrag.Mid(2)); // Lose the --
else if (optFrag.Length() == 1) optFrag.Set(TPtrC()); // single dash gets dropped completely
const IoUtils::RCommandOptionList& options = iLastUsedCif->Options();
const TInt count = options.Count();
for (TInt i = 0; i < count; i++)
{
const TDesC& optName = options[i].Name();;
if (optName.Left(optFrag.Length()) == optFrag)
{
matchingOptions.AppendL(TPtrC(optName));
}
}
CompleteL(aLine, aToken, matchingOptions, &KDashDash, NULL, ETrue);
CleanupStack::PopAndDestroy(&matchingOptions);
}