--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/parser.cpp Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,904 @@
+// parser.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 <fshell/ioutils.h>
+#include <fshell/ltkutils.h>
+#include "parser.h"
+#include "fshell.h"
+#include "lexer.h"
+#include "command_factory.h"
+
+//
+// Globals.
+//
+
+_LIT(KChildError, "?");
+_LIT(KPipe, "|");
+_LIT(KDoublePipe, "||");
+_LIT(KRedirectStdinFromFile, "<");
+_LIT(KRedirectStdoutToFile1, ">");
+_LIT(KRedirectStdoutToFile2, "1>");
+_LIT(KRedirectStdoutToFileAppend1, ">>");
+_LIT(KRedirectStdoutToFileAppend2, "1>>");
+_LIT(KRedirectStdoutToStderr, "1>&2");
+_LIT(KRedirectStderrToFile, "2>");
+_LIT(KRedirectStderrToFileAppend, "2>>");
+_LIT(KRedirectStderrToStdout, "2>&1");
+_LIT(KAmpersand, "&");
+_LIT(KDoubleAmpersand, "&&");
+_LIT(KAmpersandPipe, "&|");
+_LIT(KNewLine1, "\r");
+_LIT(KNewLine2, "\n");
+_LIT(KNewLine3, "\r\n");
+_LIT(KNewLine4, "\n\r");
+_LIT(KSemicolon, ";");
+_LIT(KDollar, "$");
+
+
+void MParserObserver::AboutToExecuteLine(const TDesC&, const TDesC&)
+ {
+ }
+
+void MParserObserver::LineReturned(TInt)
+ {
+ }
+
+CParser* CParser::NewL(TUint aMode, const TDesC& aDes, RIoSession& aIoSession, RIoReadHandle& aStdin, RIoWriteHandle& aStdout, RIoWriteHandle& aStderr, IoUtils::CEnvironment& aEnv, CCommandFactory& aFactory, MParserObserver* aObserver)
+ {
+ CParser* self = new(ELeave) CParser(aMode, aDes, aIoSession, aStdin, aStdout, aStderr, aEnv, aFactory, aObserver);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop();
+ return self;
+ }
+
+CParser::~CParser()
+ {
+ delete iForegroundPipeLine;
+ iBackgroundPipeLines.ResetAndDestroy();
+ delete iLexer1;
+ delete iLexer2;
+ delete iCompletionCallBack;
+ delete iNextPipeLineCallBack;
+ delete iExitCallBack;
+ if (iOwnsIoHandles)
+ {
+ iStdin.Close();
+ iStdout.Close();
+ iStderr.Close();
+ }
+ }
+
+CParser::CParser(TUint aMode, const TDesC& aDes, RIoSession& aIoSession, RIoReadHandle& aStdin, RIoWriteHandle& aStdout, RIoWriteHandle& aStderr, IoUtils::CEnvironment& aEnv, CCommandFactory& aFactory, MParserObserver* aObserver)
+ : iMode(aMode), iData(aDes), iIoSession(aIoSession), iStdin(aStdin), iStdout(aStdout), iStderr(aStderr), iEnv(aEnv), iFactory(aFactory), iObserver(aObserver), iCompletionError(aStderr, aEnv), iNextLineNumber(1)
+ {
+ }
+
+void CParser::ConstructL()
+ {
+ if (iObserver)
+ {
+ iCompletionCallBack = new(ELeave) CAsyncCallBack(TCallBack(CompletionCallBack, this), CActive::EPriorityStandard);
+ }
+ iNextPipeLineCallBack = new(ELeave) CAsyncCallBack(TCallBack(NextCallBack, this), CActive::EPriorityStandard);
+
+ iLexer1 = CLexer::NewL(CLexer::EHandleSingleQuotes | CLexer::EHandleDoubleQuotes | CLexer::EHandleComments);
+ iLexer1->DefineTokenTypeL(TToken::EDoublePipe, KDoublePipe);
+ iLexer1->DefineTokenTypeL(TToken::EDoubleAmpersand, KDoubleAmpersand);
+ iLexer1->DefineTokenTypeL(TToken::EAmpersandPipe, KAmpersandPipe);
+ iLexer1->DefineTokenTypeL(TToken::ENewLine, KNewLine1);
+ iLexer1->DefineTokenTypeL(TToken::ENewLine, KNewLine2);
+ iLexer1->DefineTokenTypeL(TToken::ENewLine, KNewLine3);
+ iLexer1->DefineTokenTypeL(TToken::ENewLine, KNewLine4);
+ iLexer1->DefineTokenTypeL(TToken::ESemicolon, KSemicolon);
+ iLexer1->Set(iData, iEnv.EscapeChar());
+
+ iLexer2 = CLexer::NewL(CLexer::EHandleSingleQuotes | CLexer::EHandleDoubleQuotes);
+ iLexer2->DefineTokenTypeL(TToken::EPipe, KPipe);
+ iLexer2->DefineTokenTypeL(TToken::ERedirectStdinFromFile, KRedirectStdinFromFile);
+ iLexer2->DefineTokenTypeL(TToken::ERedirectStdoutToFile, KRedirectStdoutToFile1);
+ iLexer2->DefineTokenTypeL(TToken::ERedirectStdoutToFile, KRedirectStdoutToFile2);
+ iLexer2->DefineTokenTypeL(TToken::ERedirectStdoutToFileAppend, KRedirectStdoutToFileAppend1);
+ iLexer2->DefineTokenTypeL(TToken::ERedirectStdoutToFileAppend, KRedirectStdoutToFileAppend2);
+ iLexer2->DefineTokenTypeL(TToken::ERedirectStdoutToStderr, KRedirectStdoutToStderr);
+ iLexer2->DefineTokenTypeL(TToken::ERedirectStderrToFile, KRedirectStderrToFile);
+ iLexer2->DefineTokenTypeL(TToken::ERedirectStderrToFileAppend, KRedirectStderrToFileAppend);
+ iLexer2->DefineTokenTypeL(TToken::ERedirectStderrToStdout, KRedirectStderrToStdout);
+ iLexer2->DefineTokenTypeL(TToken::EAmpersand, KAmpersand);
+
+ if (iMode & EExportLineNumbers)
+ {
+ iEnv.SetL(KScriptLine, iNextLineNumber++);
+ }
+ if (iMode & EKeepGoing)
+ {
+ iEnv.SetL(KKeepGoing, 1);
+ }
+ }
+
+void CParser::Start()
+ {
+ CreateNextPipeLine(NULL);
+ }
+
+void CParser::Start(TBool& aIsForeground)
+ {
+ CreateNextPipeLine(&aIsForeground);
+ }
+
+void CParser::Kill()
+ {
+ iAbort = ETrue;
+ if (iForegroundPipeLine)
+ {
+ iForegroundPipeLine->Kill();
+ }
+ const TInt numBackground = iBackgroundPipeLines.Count();
+ for (TInt i = 0; i < numBackground; ++i)
+ {
+ iBackgroundPipeLines[i]->Kill();
+ }
+ }
+
+TInt CParser::Suspend()
+ {
+ TInt ret = KErrNone;
+ if (iForegroundPipeLine)
+ {
+ TInt err = iForegroundPipeLine->Suspend();
+ if (err)
+ {
+ ret = err;
+ }
+ }
+ const TInt numBackground = iBackgroundPipeLines.Count();
+ for (TInt i = 0; i < numBackground; ++i)
+ {
+ TInt err = iBackgroundPipeLines[i]->Suspend();
+ if (err && (ret == KErrNone))
+ {
+ ret = err;
+ }
+ }
+ return ret;
+ }
+
+TInt CParser::Resume()
+ {
+ TInt ret = KErrNone;
+ if (iForegroundPipeLine)
+ {
+ TInt err = iForegroundPipeLine->Resume();
+ if (err)
+ {
+ ret = err;
+ }
+ }
+ const TInt numBackground = iBackgroundPipeLines.Count();
+ for (TInt i = 0; i < numBackground; ++i)
+ {
+ TInt err = iBackgroundPipeLines[i]->Resume();
+ if (err && (ret == KErrNone))
+ {
+ ret = err;
+ }
+ }
+ return ret;
+ }
+
+TInt CParser::BringToForeground()
+ {
+ if (iForegroundPipeLine)
+ {
+ return iForegroundPipeLine->BringToForeground();
+ }
+ else if (iBackgroundPipeLines.Count() == 1)
+ {
+ return iBackgroundPipeLines[0]->BringToForeground();
+ }
+ return KErrGeneral;
+ }
+
+void CParser::SendToBackground()
+ {
+ if (iForegroundPipeLine)
+ {
+ iForegroundPipeLine->SendToBackground();
+ }
+ }
+
+TInt CParser::Reattach(RIoEndPoint& aStdinEndPoint, RIoEndPoint& aStdoutEndPoint, RIoEndPoint& aStderrEndPoint)
+ {
+ RIoReadHandle stdin;
+ RIoWriteHandle stdout;
+ RIoWriteHandle stderr;
+ TInt err = stdin.Create(iIoSession);
+ if (err == KErrNone)
+ {
+ err = stdout.Create(iIoSession);
+ if (err == KErrNone)
+ {
+ err = stderr.Create(iIoSession);
+ if (err == KErrNone)
+ {
+ err = aStdinEndPoint.Attach(stdin, RIoEndPoint::EBackground);
+ if (err == KErrNone)
+ {
+ err = aStdoutEndPoint.Attach(stdout);
+ if (err == KErrNone)
+ {
+ err = aStderrEndPoint.Attach(stderr);
+ if (err == KErrNone)
+ {
+ if (iForegroundPipeLine)
+ {
+ err = iForegroundPipeLine->Reattach(aStdinEndPoint, aStdoutEndPoint, aStderrEndPoint);
+ }
+ if (err == KErrNone)
+ {
+ const TInt numBackground = iBackgroundPipeLines.Count();
+ for (TInt i = 0; i < numBackground; ++i)
+ {
+ err = iBackgroundPipeLines[i]->Reattach(aStdinEndPoint, aStdoutEndPoint, aStderrEndPoint);
+ if (err)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (err == KErrNone)
+ {
+ iStdin = stdin;
+ iStdout = stdout;
+ iStderr = stderr;
+ iOwnsIoHandles = ETrue;
+ }
+ else
+ {
+ stdin.Close();
+ stdout.Close();
+ stderr.Close();
+ }
+ return err;
+ }
+
+TBool CParser::IsDisownable() const
+ {
+ if (iForegroundPipeLine && !iForegroundPipeLine->IsDisownable())
+ {
+ return EFalse;
+ }
+ const TInt numBackground = iBackgroundPipeLines.Count();
+ for (TInt i = 0; i < numBackground; ++i)
+ {
+ if (!(iBackgroundPipeLines[i]->IsDisownable()))
+ {
+ return EFalse;
+ }
+ }
+ return ETrue;
+ }
+
+void CParser::Disown()
+ {
+ if (iForegroundPipeLine)
+ {
+ iForegroundPipeLine->Disown();
+ }
+ const TInt numBackground = iBackgroundPipeLines.Count();
+ for (TInt i = 0; i < numBackground; ++i)
+ {
+ iBackgroundPipeLines[i]->Disown();
+ }
+ }
+
+TBool HandleRedirectionL(TToken::TType aTokenType, const TDesC& aCwd, CLexer& aLexer, RPipeSection& aPipeSection)
+ {
+ RPipeSection::TRedirection* redirection;
+ switch (aTokenType)
+ {
+ case TToken::ERedirectStdinFromFile:
+ {
+ redirection = &aPipeSection.iStdinRedirection;
+ break;
+ }
+ case TToken::ERedirectStdoutToFile:
+ case TToken::ERedirectStdoutToFileAppend:
+ case TToken::ERedirectStdoutToStderr:
+ {
+ redirection = &aPipeSection.iStdoutRedirection;
+ break;
+ }
+ case TToken::ERedirectStderrToFile:
+ case TToken::ERedirectStderrToFileAppend:
+ case TToken::ERedirectStderrToStdout:
+ {
+ redirection = &aPipeSection.iStderrRedirection;
+ break;
+ }
+ default:
+ {
+ redirection = NULL;
+ ASSERT(EFalse);
+ }
+ }
+
+ if (aTokenType == TToken::ERedirectStderrToStdout)
+ {
+ redirection->iType = RPipeSection::TRedirection::EHandle;
+ redirection->iHandle = RPipeSection::TRedirection::EStdout;
+ }
+ else if (aTokenType == TToken::ERedirectStdoutToStderr)
+ {
+ redirection->iType = RPipeSection::TRedirection::EHandle;
+ redirection->iHandle = RPipeSection::TRedirection::EStderr;
+ }
+ else
+ {
+ if (aLexer.More())
+ {
+ redirection->iType = ((aTokenType == TToken::ERedirectStdoutToFileAppend) || (aTokenType == TToken::ERedirectStderrToFileAppend)) ? RPipeSection::TRedirection::EFileAppend : RPipeSection::TRedirection::EFile;
+ TToken fileName(aLexer.NextToken());
+ redirection->SetFileNameL(aCwd, fileName.String());
+ }
+ else
+ {
+ return EFalse;
+ }
+ }
+
+ return ETrue;
+ }
+
+void CleanupPipeSectionsArray(TAny* aArray)
+ {
+ RArray<RPipeSection>* array = static_cast<RArray<RPipeSection>*>(aArray);
+ const TInt numSections = array->Count();
+ for (TInt i = 0; i < numSections; ++i)
+ {
+ (*array)[i].Close();
+ }
+ array->Close();
+ }
+
+void CParser::CreateNextPipeLine(TBool* aIsForeground)
+ {
+ TRAPD(err, CreateNextPipeLineL(aIsForeground));
+ if (err && iObserver)
+ {
+ iCompletionError.Set(err, TError::EFailedToCreatePipeLine);
+ iCompletionCallBack->CallBack();
+ }
+ }
+
+void CParser::CreateNextPipeLineL(TBool* aIsForeground)
+ {
+ // Parsing is now carried out in three main steps:
+ //
+ // 1) Use iLexer1 to find the next "pipe-line's worth" of data (carried out by FindNextPipeLine).
+ // 2) Expand environment variables in this data into a new HBufC ('expandedPipeLine' returned by ExpandVariablesLC).
+ // 3) Use iLexer2 to parse the data in this expanded descriptor.
+
+ if (aIsForeground)
+ {
+ *aIsForeground = ETrue;
+ }
+
+ TPtrC pipeLineData;
+ TBool reachedLineEnd(EFalse);
+ FindNextPipeLineL(pipeLineData, iCondition, reachedLineEnd);
+ HBufC* expandedPipeLine = ExpandVariablesLC(pipeLineData);
+ iLexer2->Set(*expandedPipeLine, iEnv.EscapeChar());
+
+ const TDesC& cwd = iEnv.Pwd();
+ RArray<RPipeSection> pipeSections;
+ CleanupStack::PushL(TCleanupItem(CleanupPipeSectionsArray, &pipeSections));
+ RPipeSection pipeSection;
+ CleanupClosePushL(pipeSection);
+ TInt offset = iLexer2->CurrentOffset();
+ TBool background(EFalse);
+ while (iLexer2->More())
+ {
+ TToken token(iLexer2->NextToken());
+ switch (token.Type())
+ {
+ case TToken::EPipe:
+ {
+ pipeSection.iFullName.Set(iData.Ptr() + offset, iLexer2->CurrentOffset() - offset - token.String().Length());
+ offset = iLexer2->CurrentOffset();
+ User::LeaveIfError(pipeSections.Append(pipeSection));
+ new(&pipeSection) RPipeSection;
+ break;
+ }
+ case TToken::EAmpersand:
+ {
+ background = ETrue;
+ break;
+ }
+ case TToken::ERedirectStdinFromFile:
+ case TToken::ERedirectStdoutToFile:
+ case TToken::ERedirectStdoutToFileAppend:
+ case TToken::ERedirectStdoutToStderr:
+ case TToken::ERedirectStderrToFile:
+ case TToken::ERedirectStderrToFileAppend:
+ case TToken::ERedirectStderrToStdout:
+ {
+ if (HandleRedirectionL(token.Type(), cwd, *iLexer2, pipeSection))
+ {
+ break;
+ }
+ // Deliberate fall through - if it wasn't possible to handle the redirection, treat token as a string.
+ }
+ case TToken::EString:
+ default:
+ {
+ if (token.String().Length() > 0)
+ {
+ if (pipeSection.iCommandName.Length() == 0)
+ {
+ pipeSection.iCommandName.Set(token.String());
+ }
+ else
+ {
+ User::LeaveIfError(pipeSection.iCommandArguments.Append(TPtrC(token.String())));
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ if (pipeSection.iCommandName.Length() > 0)
+ {
+ _LIT(KExit, "exit");
+ if ((pipeSections.Count() == 0) && (pipeSection.iCommandName.CompareF(KExit) == 0) && (pipeSection.iCommandArguments.Count() == 0))
+ {
+ // Special case the handling of 'exit'. This allows the concept of 'local commands' (i.e. commands that run in either fshell's main
+ // thread or in the thread belonging to a 'source' or 'debug' command) to be dropped. That's a good thing, because local commands
+ // can't synchronously interact with iosrv without risk of deadlock when two or more thread commands are run in a pipe-line.
+ CleanupStack::PopAndDestroy(2, &pipeSections);
+ if (CActiveScheduler::Current()->StackDepth() > 0)
+ {
+ CActiveScheduler::Stop();
+ }
+ else
+ {
+ // The active scheduler hasn't been started yet. Probably because someone is doing something crazy like 'fshell -e exit'.
+ iExitCallBack = new(ELeave) CAsyncCallBack(TCallBack(ExitCallBack, this), CActive::EPriorityStandard);
+ iExitCallBack->Call();
+ }
+ }
+ else
+ {
+ pipeSection.iFullName.Set(iData.Ptr() + offset, iLexer2->CurrentOffset() - offset);
+ User::LeaveIfError(pipeSections.Append(pipeSection));
+ CleanupStack::Pop(&pipeSection);
+ if ((iMode & EDebug) && iObserver)
+ {
+ iObserver->AboutToExecuteLine(pipeLineData, *expandedPipeLine);
+ }
+ if (background)
+ {
+ CPipeLine* pipeLine = CPipeLine::NewLC(iIoSession, iStdin, iStdout, iStderr, iEnv, iFactory, pipeSections, background, this, iCompletionError);
+ User::LeaveIfError(iBackgroundPipeLines.Append(pipeLine));
+ CleanupStack::Pop(pipeLine);
+ }
+ else
+ {
+ ASSERT(iForegroundPipeLine == NULL);
+ iForegroundPipeLine = CPipeLine::NewL(iIoSession, iStdin, iStdout, iStderr, iEnv, iFactory, pipeSections, background, this, iCompletionError);
+ }
+ CleanupStack::PopAndDestroy(&pipeSections);
+ if (aIsForeground && !iLexer1->More())
+ {
+ *aIsForeground = !background;
+ }
+ if (background && iLexer1->More())
+ {
+ iNextPipeLineCallBack->Call();
+ }
+ }
+ }
+ else
+ {
+ if (iObserver && (iForegroundPipeLine == NULL) && (iBackgroundPipeLines.Count() == 0))
+ {
+ iCompletionCallBack->CallBack();
+ }
+ CleanupStack::PopAndDestroy(2, &pipeSections);
+ }
+
+ CleanupStack::PopAndDestroy(expandedPipeLine);
+
+ if (reachedLineEnd && (iMode & EExportLineNumbers))
+ {
+ iEnv.SetL(KScriptLine, iNextLineNumber++);
+ }
+ }
+
+void CParser::FindNextPipeLineL(TPtrC& aData, TCondition& aCondition, TBool& aReachedLineEnd)
+ {
+ aReachedLineEnd = EFalse;
+ aCondition = ENone;
+ TInt startOffset = iLexer1->CurrentOffset();
+ TInt endOffset = -1;
+
+ TBool foundSomething(EFalse);
+ while (iLexer1->More())
+ {
+ TBool finished(EFalse);
+ TToken token(iLexer1->NextToken());
+
+ switch (token.Type())
+ {
+ case TToken::EString:
+ {
+ foundSomething = ETrue;
+ endOffset = iLexer1->CurrentOffset();
+ break;
+ }
+ case TToken::EDoublePipe:
+ {
+ finished = ETrue;
+ aCondition = EOr;
+ break;
+ }
+ case TToken::EDoubleAmpersand:
+ {
+ finished = ETrue;
+ aCondition = EAnd;
+ break;
+ }
+ case TToken::EAmpersandPipe:
+ {
+ finished = ETrue;
+ aCondition = EAndOr;
+ break;
+ }
+ case TToken::ENull:
+ case TToken::ENewLine:
+ {
+ if (foundSomething)
+ {
+ // Leave it to CreateNextPipeLineL to increment SCRIPT_LINE when it's finished building the pipe-line.
+ aReachedLineEnd = ETrue;
+ }
+ else
+ {
+ // Reached the end of a comment line - increment SCRIPT_LINE.
+ if (iMode & EExportLineNumbers)
+ {
+ iEnv.SetL(KScriptLine, iNextLineNumber++);
+ }
+ }
+ // Deliberate fall through.
+ }
+ case TToken::ESemicolon:
+ {
+ if (foundSomething)
+ {
+ finished = ETrue;
+ }
+ else
+ {
+ // Nothing on this line - adjust 'startOffset' to pretend it doesn't exist.
+ startOffset = iLexer1->CurrentOffset();
+ }
+ break;
+ }
+ default:
+ {
+ ASSERT(EFalse);
+ break;
+ }
+ }
+
+ if (finished)
+ {
+ break;
+ }
+ }
+
+ if (foundSomething)
+ {
+ aData.Set(iData.Ptr() + startOffset, endOffset - startOffset);
+ }
+ else
+ {
+ aData.Set(KNullDesC);
+ }
+ }
+
+HBufC* ExpandVariablesLC(const TDesC& aData, CLexer& aLexer, IoUtils::CEnvironment& aEnv, TBool aEscape)
+ {
+ TChar escapeChar = aEnv.EscapeChar();
+ RArray<TInt> charsToEscape;
+ CleanupClosePushL(charsToEscape);
+ HBufC* buf = aData.AllocLC();
+
+ // Repeatedly check the data for environment variable tokens. This is done in a loop
+ // because there could be variables within variables and we want to expand them all.
+ aLexer.Set(*buf, escapeChar);
+ FOREVER
+ {
+ TToken token(aLexer.NextToken());
+ if (token.Type() == TToken::ENull)
+ {
+ break;
+ }
+ else if (token.Type() == TToken::EVariable)
+ {
+ const TDesC& val = aEnv.GetAsDesL(token.String().Mid(1));
+ TPtr bufPtr(buf->Des());
+ const TInt freeSpace = bufPtr.MaxLength() - bufPtr.Length();
+ TInt requiredSpace = val.Length() - token.String().Length();
+ if (aEscape)
+ {
+ charsToEscape.Reset();
+ TLex lex(val);
+ while (!lex.Eos())
+ {
+ TChar c = lex.Get();
+ if ((c == aEnv.EscapeChar()) || (c == '\"'))
+ {
+ charsToEscape.AppendL(token.Position() + lex.Offset() - 1);
+ ++requiredSpace;
+ }
+ }
+ }
+ if (requiredSpace > freeSpace)
+ {
+ HBufC* oldBuf = buf;
+ buf = buf->ReAllocL(bufPtr.MaxLength() + requiredSpace - freeSpace);
+ CleanupStack::Pop(oldBuf);
+ CleanupStack::PushL(buf);
+ bufPtr.Set(buf->Des());
+ }
+
+ bufPtr.Replace(token.Position(), token.String().Length(), val);
+
+ if (aEscape)
+ {
+ TPtrC escape((TUint16*)&escapeChar, 1);
+ while (charsToEscape.Count() > 0)
+ {
+ bufPtr.Insert(charsToEscape[charsToEscape.Count() - 1], escape);
+ charsToEscape.Remove(charsToEscape.Count() - 1);
+ }
+ }
+
+ aLexer.Set(*buf, escapeChar);
+ }
+ }
+
+ CleanupStack::Pop(buf);
+ CleanupStack::PopAndDestroy(&charsToEscape);
+ CleanupStack::PushL(buf);
+
+ return buf;
+ }
+
+HBufC* CParser::ExpandVariablesLC(const TDesC& aData)
+ {
+ CLexer* lexer1 = CLexer::NewLC(CLexer::EHandleSingleQuotes | CLexer::EHandleDoubleQuotes | CLexer::EHandleComments);
+ CLexer* lexer2 = CLexer::NewLC(0);
+
+ // Populate 'lexer2' with a token definition for each environment variable (preceded with '$').
+ RPointerArray<HBufC> keys;
+ LtkUtils::CleanupResetAndDestroyPushL(keys);
+ iEnv.GetKeysL(keys);
+
+ const TInt numVars = keys.Count();
+ for (TInt i = 0; i < numVars; ++i)
+ {
+ keys[i] = keys[i]->ReAllocL(keys[i]->Length() + KDollar().Length());
+ keys[i]->Des().Insert(0, KDollar);
+ lexer2->DefineTokenTypeL(TToken::EVariable, *keys[i]);
+ }
+
+ HBufC* buf = aData.AllocLC();
+ lexer1->Set(*buf, iEnv.EscapeChar());
+ FOREVER
+ {
+ TToken token(lexer1->NextToken());
+ if (token.Type() == TToken::ENull)
+ {
+ break;
+ }
+ else if (token.Type() == TToken::EString)
+ {
+ if (token.String()[0] != '\'')
+ {
+ HBufC* expandedString = ::ExpandVariablesLC(token.String(), *lexer2, iEnv, token.String()[0] == '\"');
+ if (*expandedString != token.String())
+ {
+ TPtr bufPtr(buf->Des());
+ const TInt freeSpace = bufPtr.MaxLength() - bufPtr.Length();
+ const TInt requiredSpace = expandedString->Length() - token.String().Length();
+
+ if (requiredSpace > freeSpace)
+ {
+ HBufC* oldBuf = buf;
+ buf = buf->ReAllocL(bufPtr.MaxLength() + requiredSpace - freeSpace);
+ CleanupStack::Pop(expandedString);
+ CleanupStack::Pop(oldBuf);
+ CleanupStack::PushL(buf);
+ CleanupStack::PushL(expandedString);
+ bufPtr.Set(buf->Des());
+ }
+
+ bufPtr.Replace(token.Position(), token.String().Length(), *expandedString);
+ lexer1->Set(*buf, iEnv.EscapeChar());
+ }
+ CleanupStack::PopAndDestroy(expandedString);
+ }
+ }
+ }
+
+ CleanupStack::Pop(buf);
+ CleanupStack::PopAndDestroy(3, lexer1);
+ CleanupStack::PushL(buf);
+ return buf;
+ }
+
+TInt CParser::SkipLineRemainder()
+ {
+ while (iLexer1->More())
+ {
+ TToken token(iLexer1->NextToken());
+ if (token.Type() == TToken::ENewLine)
+ {
+ if (iMode & EExportLineNumbers)
+ {
+ // can we do something better with errors here?
+ TRAPD(err, iEnv.SetL(KScriptLine, iNextLineNumber++));
+ if (err!=KErrNone)
+ {
+ iCompletionError.Set(err, TError::EFailedToSetScriptLineVar);
+ return err;
+ }
+ }
+ break;
+ }
+ }
+ return KErrNone;
+ }
+
+void CParser::SkipToEnd()
+ {
+ while (iLexer1->More())
+ {
+ iLexer1->NextToken();
+ }
+ }
+
+TInt CParser::CompletionCallBack(TAny* aSelf)
+ {
+ CParser* self = static_cast<CParser*>(aSelf);
+ self->iObserver->HandleParserComplete(*self, self->iCompletionError);
+ return KErrNone;
+ }
+
+TInt CParser::NextCallBack(TAny* aSelf)
+ {
+ CParser* self = static_cast<CParser*>(aSelf);
+ self->CreateNextPipeLine(NULL);
+ return KErrNone;
+ }
+
+TInt CParser::ExitCallBack(TAny*)
+ {
+ CActiveScheduler::Stop();
+ return KErrNone;
+ }
+
+void CParser::HandlePipeLineComplete(CPipeLine& aPipeLine, const TError& aError)
+ {
+ TRAPD(err, iEnv.SetL(KChildError, aError.Error()));
+ if (err)
+ {
+ iCompletionError.Set(err, TError::EFailedToSetChildErrorVar);
+ iCompletionCallBack->CallBack();
+ return;
+ }
+
+ if ((iMode & EDebug) && iObserver)
+ {
+ iObserver->LineReturned(aError.Error());
+ }
+
+ if (iForegroundPipeLine == &aPipeLine)
+ {
+ switch (iCondition)
+ {
+ case ENone:
+ {
+ if ((aError.Error() != KErrNone) && !(iMode & EKeepGoing))
+ {
+ // Bail out of script if an error is found that isn't "handled" by an "&&" or and "||".
+ SkipToEnd();
+ iCompletionError.Set(aError);
+ }
+ break;
+ }
+ case EAnd:
+ {
+ if (iAbort)
+ {
+ SkipToEnd();
+ }
+ else if (aError.Error() != KErrNone)
+ {
+ TInt err = SkipLineRemainder();
+ if (err!=KErrNone) SkipToEnd();
+ }
+ break;
+ }
+ case EOr:
+ {
+ if (iAbort)
+ {
+ SkipToEnd();
+ }
+ else if (aError.Error() == KErrNone)
+ {
+ TInt err = SkipLineRemainder();
+ if (err!=KErrNone) SkipToEnd();
+ }
+ break;
+ }
+ case EAndOr:
+ {
+ if (iAbort)
+ {
+ SkipToEnd();
+ }
+ break;
+ }
+ default:
+ {
+ ASSERT(EFalse);
+ }
+ }
+
+ delete iForegroundPipeLine;
+ iForegroundPipeLine = NULL;
+
+ if (iLexer1->More())
+ {
+ iNextPipeLineCallBack->Call();
+ }
+ }
+ else
+ {
+ TInt pos = iBackgroundPipeLines.Find(&aPipeLine);
+ ASSERT(pos >= 0);
+ iBackgroundPipeLines.Remove(pos);
+ if (aError.Error() != KErrNone)
+ {
+ iCompletionError.Set(aError);
+ }
+ delete &aPipeLine;
+ }
+
+ if (iObserver && !iLexer1->More() && (iForegroundPipeLine == NULL) && (iBackgroundPipeLines.Count() == 0))
+ {
+ iCompletionCallBack->CallBack();
+ }
+ }
+