core/src/parser.cpp
changeset 0 7f656887cf89
child 70 b06038904ef8
equal deleted inserted replaced
-1:000000000000 0:7f656887cf89
       
     1 // parser.cpp
       
     2 // 
       
     3 // Copyright (c) 2006 - 2010 Accenture. All rights reserved.
       
     4 // This component and the accompanying materials are made available
       
     5 // under the terms of the "Eclipse Public License v1.0"
       
     6 // which accompanies this distribution, and is available
       
     7 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 // 
       
     9 // Initial Contributors:
       
    10 // Accenture - Initial contribution
       
    11 //
       
    12 
       
    13 #include <fshell/ioutils.h>
       
    14 #include <fshell/ltkutils.h>
       
    15 #include "parser.h"
       
    16 #include "fshell.h"
       
    17 #include "lexer.h"
       
    18 #include "command_factory.h"
       
    19 
       
    20 //
       
    21 // Globals.
       
    22 //
       
    23 
       
    24 _LIT(KChildError, "?");
       
    25 _LIT(KPipe, "|");
       
    26 _LIT(KDoublePipe, "||");
       
    27 _LIT(KRedirectStdinFromFile, "<");
       
    28 _LIT(KRedirectStdoutToFile1, ">");
       
    29 _LIT(KRedirectStdoutToFile2, "1>");
       
    30 _LIT(KRedirectStdoutToFileAppend1, ">>");
       
    31 _LIT(KRedirectStdoutToFileAppend2, "1>>");
       
    32 _LIT(KRedirectStdoutToStderr, "1>&2");
       
    33 _LIT(KRedirectStderrToFile, "2>");
       
    34 _LIT(KRedirectStderrToFileAppend, "2>>");
       
    35 _LIT(KRedirectStderrToStdout, "2>&1");
       
    36 _LIT(KAmpersand, "&");
       
    37 _LIT(KDoubleAmpersand, "&&");
       
    38 _LIT(KAmpersandPipe, "&|");
       
    39 _LIT(KNewLine1, "\r");
       
    40 _LIT(KNewLine2, "\n");
       
    41 _LIT(KNewLine3, "\r\n");
       
    42 _LIT(KNewLine4, "\n\r");
       
    43 _LIT(KSemicolon, ";");
       
    44 _LIT(KDollar, "$");
       
    45 
       
    46 
       
    47 void MParserObserver::AboutToExecuteLine(const TDesC&, const TDesC&)
       
    48 	{
       
    49 	}
       
    50 
       
    51 void MParserObserver::LineReturned(TInt)
       
    52 	{
       
    53 	}
       
    54 
       
    55 CParser* CParser::NewL(TUint aMode, const TDesC& aDes, RIoSession& aIoSession, RIoReadHandle& aStdin, RIoWriteHandle& aStdout, RIoWriteHandle& aStderr, IoUtils::CEnvironment& aEnv, CCommandFactory& aFactory, MParserObserver* aObserver)
       
    56 	{
       
    57 	CParser* self = new(ELeave) CParser(aMode, aDes, aIoSession, aStdin, aStdout, aStderr, aEnv, aFactory, aObserver);
       
    58 	CleanupStack::PushL(self);
       
    59 	self->ConstructL();
       
    60 	CleanupStack::Pop();
       
    61 	return self;
       
    62 	}
       
    63 
       
    64 CParser::~CParser()
       
    65 	{
       
    66 	delete iForegroundPipeLine;
       
    67 	iBackgroundPipeLines.ResetAndDestroy();
       
    68 	delete iLexer1;
       
    69 	delete iLexer2;
       
    70 	delete iCompletionCallBack;
       
    71 	delete iNextPipeLineCallBack;
       
    72 	delete iExitCallBack;
       
    73 	if (iOwnsIoHandles)
       
    74 		{
       
    75 		iStdin.Close();
       
    76 		iStdout.Close();
       
    77 		iStderr.Close();
       
    78 		}
       
    79 	}
       
    80 
       
    81 CParser::CParser(TUint aMode, const TDesC& aDes, RIoSession& aIoSession, RIoReadHandle& aStdin, RIoWriteHandle& aStdout, RIoWriteHandle& aStderr, IoUtils::CEnvironment& aEnv, CCommandFactory& aFactory, MParserObserver* aObserver)
       
    82 	: iMode(aMode), iData(aDes), iIoSession(aIoSession), iStdin(aStdin), iStdout(aStdout), iStderr(aStderr), iEnv(aEnv), iFactory(aFactory), iObserver(aObserver), iCompletionError(aStderr, aEnv), iNextLineNumber(1)
       
    83 	{
       
    84 	}
       
    85 
       
    86 void CParser::ConstructL()
       
    87 	{
       
    88 	if (iObserver)
       
    89 		{
       
    90 		iCompletionCallBack = new(ELeave) CAsyncCallBack(TCallBack(CompletionCallBack, this), CActive::EPriorityStandard);
       
    91 		}
       
    92 	iNextPipeLineCallBack = new(ELeave) CAsyncCallBack(TCallBack(NextCallBack, this), CActive::EPriorityStandard);
       
    93 
       
    94 	iLexer1 = CLexer::NewL(CLexer::EHandleSingleQuotes | CLexer::EHandleDoubleQuotes | CLexer::EHandleComments);
       
    95 	iLexer1->DefineTokenTypeL(TToken::EDoublePipe, KDoublePipe);
       
    96 	iLexer1->DefineTokenTypeL(TToken::EDoubleAmpersand, KDoubleAmpersand);
       
    97 	iLexer1->DefineTokenTypeL(TToken::EAmpersandPipe, KAmpersandPipe);
       
    98 	iLexer1->DefineTokenTypeL(TToken::ENewLine, KNewLine1);
       
    99 	iLexer1->DefineTokenTypeL(TToken::ENewLine, KNewLine2);
       
   100 	iLexer1->DefineTokenTypeL(TToken::ENewLine, KNewLine3);
       
   101 	iLexer1->DefineTokenTypeL(TToken::ENewLine, KNewLine4);
       
   102 	iLexer1->DefineTokenTypeL(TToken::ESemicolon, KSemicolon);
       
   103 	iLexer1->Set(iData, iEnv.EscapeChar());
       
   104 
       
   105 	iLexer2 = CLexer::NewL(CLexer::EHandleSingleQuotes | CLexer::EHandleDoubleQuotes);
       
   106 	iLexer2->DefineTokenTypeL(TToken::EPipe, KPipe);
       
   107 	iLexer2->DefineTokenTypeL(TToken::ERedirectStdinFromFile, KRedirectStdinFromFile);
       
   108 	iLexer2->DefineTokenTypeL(TToken::ERedirectStdoutToFile, KRedirectStdoutToFile1);
       
   109 	iLexer2->DefineTokenTypeL(TToken::ERedirectStdoutToFile, KRedirectStdoutToFile2);
       
   110 	iLexer2->DefineTokenTypeL(TToken::ERedirectStdoutToFileAppend, KRedirectStdoutToFileAppend1);
       
   111 	iLexer2->DefineTokenTypeL(TToken::ERedirectStdoutToFileAppend, KRedirectStdoutToFileAppend2);
       
   112 	iLexer2->DefineTokenTypeL(TToken::ERedirectStdoutToStderr, KRedirectStdoutToStderr);
       
   113 	iLexer2->DefineTokenTypeL(TToken::ERedirectStderrToFile, KRedirectStderrToFile);
       
   114 	iLexer2->DefineTokenTypeL(TToken::ERedirectStderrToFileAppend, KRedirectStderrToFileAppend);
       
   115 	iLexer2->DefineTokenTypeL(TToken::ERedirectStderrToStdout, KRedirectStderrToStdout);
       
   116 	iLexer2->DefineTokenTypeL(TToken::EAmpersand, KAmpersand);
       
   117 
       
   118 	if (iMode & EExportLineNumbers)
       
   119 		{
       
   120 		iEnv.SetL(KScriptLine, iNextLineNumber++);
       
   121 		}
       
   122 	if (iMode & EKeepGoing)
       
   123 		{
       
   124 		iEnv.SetL(KKeepGoing, 1);
       
   125 		}
       
   126 	}
       
   127 
       
   128 void CParser::Start()
       
   129 	{
       
   130 	CreateNextPipeLine(NULL);
       
   131 	}
       
   132 
       
   133 void CParser::Start(TBool& aIsForeground)
       
   134 	{
       
   135 	CreateNextPipeLine(&aIsForeground);
       
   136 	}
       
   137 
       
   138 void CParser::Kill()
       
   139 	{
       
   140 	iAbort = ETrue;
       
   141 	if (iForegroundPipeLine)
       
   142 		{
       
   143 		iForegroundPipeLine->Kill();
       
   144 		}
       
   145 	const TInt numBackground = iBackgroundPipeLines.Count();
       
   146 	for (TInt i = 0; i < numBackground; ++i)
       
   147 		{
       
   148 		iBackgroundPipeLines[i]->Kill();
       
   149 		}
       
   150 	}
       
   151 
       
   152 TInt CParser::Suspend()
       
   153 	{
       
   154 	TInt ret = KErrNone;
       
   155 	if (iForegroundPipeLine)
       
   156 		{
       
   157 		TInt err = iForegroundPipeLine->Suspend();
       
   158 		if (err)
       
   159 			{
       
   160 			ret = err;
       
   161 			}
       
   162 		}
       
   163 	const TInt numBackground = iBackgroundPipeLines.Count();
       
   164 	for (TInt i = 0; i < numBackground; ++i)
       
   165 		{
       
   166 		TInt err = iBackgroundPipeLines[i]->Suspend();
       
   167 		if (err && (ret == KErrNone))
       
   168 			{
       
   169 			ret = err;
       
   170 			}
       
   171 		}
       
   172 	return ret;
       
   173 	}
       
   174 
       
   175 TInt CParser::Resume()
       
   176 	{
       
   177 	TInt ret = KErrNone;
       
   178 	if (iForegroundPipeLine)
       
   179 		{
       
   180 		TInt err = iForegroundPipeLine->Resume();
       
   181 		if (err)
       
   182 			{
       
   183 			ret = err;
       
   184 			}
       
   185 		}
       
   186 	const TInt numBackground = iBackgroundPipeLines.Count();
       
   187 	for (TInt i = 0; i < numBackground; ++i)
       
   188 		{
       
   189 		TInt err = iBackgroundPipeLines[i]->Resume();
       
   190 		if (err && (ret == KErrNone))
       
   191 			{
       
   192 			ret = err;
       
   193 			}
       
   194 		}
       
   195 	return ret;
       
   196 	}
       
   197 
       
   198 TInt CParser::BringToForeground()
       
   199 	{
       
   200 	if (iForegroundPipeLine)
       
   201 		{
       
   202 		return iForegroundPipeLine->BringToForeground();
       
   203 		}
       
   204 	else if (iBackgroundPipeLines.Count() == 1)
       
   205 		{
       
   206 		return iBackgroundPipeLines[0]->BringToForeground();
       
   207 		}
       
   208 	return KErrGeneral;
       
   209 	}
       
   210 
       
   211 void CParser::SendToBackground()
       
   212 	{
       
   213 	if (iForegroundPipeLine)
       
   214 		{
       
   215 		iForegroundPipeLine->SendToBackground();
       
   216 		}
       
   217 	}
       
   218 
       
   219 TInt CParser::Reattach(RIoEndPoint& aStdinEndPoint, RIoEndPoint& aStdoutEndPoint, RIoEndPoint& aStderrEndPoint)
       
   220 	{
       
   221 	RIoReadHandle stdin;
       
   222 	RIoWriteHandle stdout;
       
   223 	RIoWriteHandle stderr;
       
   224 	TInt err = stdin.Create(iIoSession);
       
   225 	if (err == KErrNone)
       
   226 		{
       
   227 		err = stdout.Create(iIoSession);
       
   228 		if (err == KErrNone)
       
   229 			{
       
   230 			err = stderr.Create(iIoSession);
       
   231 			if (err == KErrNone)
       
   232 				{
       
   233 				err = aStdinEndPoint.Attach(stdin, RIoEndPoint::EBackground);
       
   234 				if (err == KErrNone)
       
   235 					{
       
   236 					err = aStdoutEndPoint.Attach(stdout);
       
   237 					if (err == KErrNone)
       
   238 						{
       
   239 						err = aStderrEndPoint.Attach(stderr);
       
   240 						if (err == KErrNone)
       
   241 							{
       
   242 							if (iForegroundPipeLine)
       
   243 								{
       
   244 								err = iForegroundPipeLine->Reattach(aStdinEndPoint, aStdoutEndPoint, aStderrEndPoint);
       
   245 								}
       
   246 							if (err == KErrNone)
       
   247 								{
       
   248 								const TInt numBackground = iBackgroundPipeLines.Count();
       
   249 								for (TInt i = 0; i < numBackground; ++i)
       
   250 									{
       
   251 									err = iBackgroundPipeLines[i]->Reattach(aStdinEndPoint, aStdoutEndPoint, aStderrEndPoint);
       
   252 									if (err)
       
   253 										{
       
   254 										break;
       
   255 										}
       
   256 									}
       
   257 								}
       
   258 							}
       
   259 						}
       
   260 					}
       
   261 				}
       
   262 			}
       
   263 		}
       
   264 	if (err == KErrNone)
       
   265 		{
       
   266 		iStdin = stdin;
       
   267 		iStdout = stdout;
       
   268 		iStderr = stderr;
       
   269 		iOwnsIoHandles = ETrue;
       
   270 		}
       
   271 	else
       
   272 		{
       
   273 		stdin.Close();
       
   274 		stdout.Close();
       
   275 		stderr.Close();
       
   276 		}
       
   277 	return err;
       
   278 	}
       
   279 
       
   280 TBool CParser::IsDisownable() const
       
   281 	{
       
   282 	if (iForegroundPipeLine && !iForegroundPipeLine->IsDisownable())
       
   283 		{
       
   284 		return EFalse;
       
   285 		}
       
   286 	const TInt numBackground = iBackgroundPipeLines.Count();
       
   287 	for (TInt i = 0; i < numBackground; ++i)
       
   288 		{
       
   289 		if (!(iBackgroundPipeLines[i]->IsDisownable()))
       
   290 			{
       
   291 			return EFalse;
       
   292 			}
       
   293 		}
       
   294 	return ETrue;
       
   295 	}
       
   296 
       
   297 void CParser::Disown()
       
   298 	{
       
   299 	if (iForegroundPipeLine)
       
   300 		{
       
   301 		iForegroundPipeLine->Disown();
       
   302 		}
       
   303 	const TInt numBackground = iBackgroundPipeLines.Count();
       
   304 	for (TInt i = 0; i < numBackground; ++i)
       
   305 		{
       
   306 		iBackgroundPipeLines[i]->Disown();
       
   307 		}
       
   308 	}
       
   309 
       
   310 TBool HandleRedirectionL(TToken::TType aTokenType, const TDesC& aCwd, CLexer& aLexer, RPipeSection& aPipeSection)
       
   311 	{
       
   312 	RPipeSection::TRedirection* redirection;
       
   313 	switch (aTokenType)
       
   314 		{
       
   315 		case TToken::ERedirectStdinFromFile:
       
   316 			{
       
   317 			redirection = &aPipeSection.iStdinRedirection;
       
   318 			break;
       
   319 			}
       
   320 		case TToken::ERedirectStdoutToFile:
       
   321 		case TToken::ERedirectStdoutToFileAppend:
       
   322 		case TToken::ERedirectStdoutToStderr:
       
   323 			{
       
   324 			redirection = &aPipeSection.iStdoutRedirection;
       
   325 			break;
       
   326 			}
       
   327 		case TToken::ERedirectStderrToFile:
       
   328 		case TToken::ERedirectStderrToFileAppend:
       
   329 		case TToken::ERedirectStderrToStdout:
       
   330 			{
       
   331 			redirection = &aPipeSection.iStderrRedirection;
       
   332 			break;
       
   333 			}
       
   334 		default:
       
   335 			{
       
   336 			redirection = NULL;
       
   337 			ASSERT(EFalse);
       
   338 			}
       
   339 		}
       
   340 
       
   341 	if (aTokenType == TToken::ERedirectStderrToStdout)
       
   342 		{
       
   343 		redirection->iType = RPipeSection::TRedirection::EHandle;
       
   344 		redirection->iHandle = RPipeSection::TRedirection::EStdout;
       
   345 		}
       
   346 	else if (aTokenType == TToken::ERedirectStdoutToStderr)
       
   347 		{
       
   348 		redirection->iType = RPipeSection::TRedirection::EHandle;
       
   349 		redirection->iHandle = RPipeSection::TRedirection::EStderr;
       
   350 		}
       
   351 	else
       
   352 		{
       
   353 		if (aLexer.More())
       
   354 			{
       
   355 			redirection->iType = ((aTokenType == TToken::ERedirectStdoutToFileAppend) || (aTokenType == TToken::ERedirectStderrToFileAppend)) ? RPipeSection::TRedirection::EFileAppend : RPipeSection::TRedirection::EFile;
       
   356 			TToken fileName(aLexer.NextToken());
       
   357 			redirection->SetFileNameL(aCwd, fileName.String());
       
   358 			}
       
   359 		else
       
   360 			{
       
   361 			return EFalse;
       
   362 			}
       
   363 		}
       
   364 
       
   365 	return ETrue;
       
   366 	}
       
   367 
       
   368 void CleanupPipeSectionsArray(TAny* aArray)
       
   369 	{
       
   370 	RArray<RPipeSection>* array = static_cast<RArray<RPipeSection>*>(aArray);
       
   371 	const TInt numSections = array->Count();
       
   372 	for (TInt i = 0; i < numSections; ++i)
       
   373 		{
       
   374 		(*array)[i].Close();
       
   375 		}
       
   376 	array->Close();
       
   377 	}
       
   378 
       
   379 void CParser::CreateNextPipeLine(TBool* aIsForeground)
       
   380 	{
       
   381 	TRAPD(err, CreateNextPipeLineL(aIsForeground));
       
   382 	if (err && iObserver)
       
   383 		{
       
   384 		iCompletionError.Set(err, TError::EFailedToCreatePipeLine);
       
   385 		iCompletionCallBack->CallBack();
       
   386 		}
       
   387 	}
       
   388 
       
   389 void CParser::CreateNextPipeLineL(TBool* aIsForeground)
       
   390 	{
       
   391 	// Parsing is now carried out in three main steps:
       
   392 	//
       
   393 	// 1) Use iLexer1 to find the next "pipe-line's worth" of data (carried out by FindNextPipeLine).
       
   394 	// 2) Expand environment variables in this data into a new HBufC ('expandedPipeLine' returned by ExpandVariablesLC).
       
   395 	// 3) Use iLexer2 to parse the data in this expanded descriptor.
       
   396 
       
   397 	if (aIsForeground)
       
   398 		{
       
   399 		*aIsForeground = ETrue;
       
   400 		}
       
   401 
       
   402 	TPtrC pipeLineData;
       
   403 	TBool reachedLineEnd(EFalse);
       
   404 	FindNextPipeLineL(pipeLineData, iCondition, reachedLineEnd);
       
   405 	HBufC* expandedPipeLine = ExpandVariablesLC(pipeLineData);
       
   406 	iLexer2->Set(*expandedPipeLine, iEnv.EscapeChar());
       
   407 
       
   408 	const TDesC& cwd = iEnv.Pwd();
       
   409 	RArray<RPipeSection> pipeSections;
       
   410 	CleanupStack::PushL(TCleanupItem(CleanupPipeSectionsArray, &pipeSections));
       
   411 	RPipeSection pipeSection;
       
   412 	CleanupClosePushL(pipeSection);
       
   413 	TInt offset = iLexer2->CurrentOffset();
       
   414 	TBool background(EFalse);
       
   415 	while (iLexer2->More())
       
   416 		{
       
   417 		TToken token(iLexer2->NextToken());
       
   418 		switch (token.Type())
       
   419 			{
       
   420 			case TToken::EPipe:
       
   421 				{
       
   422 				pipeSection.iFullName.Set(iData.Ptr() + offset, iLexer2->CurrentOffset() - offset - token.String().Length());
       
   423 				offset = iLexer2->CurrentOffset();
       
   424 				User::LeaveIfError(pipeSections.Append(pipeSection));
       
   425 				new(&pipeSection) RPipeSection;
       
   426 				break;
       
   427 				}
       
   428 			case TToken::EAmpersand:
       
   429 				{
       
   430 				background = ETrue;
       
   431 				break;
       
   432 				}
       
   433 			case TToken::ERedirectStdinFromFile:
       
   434 			case TToken::ERedirectStdoutToFile:
       
   435 			case TToken::ERedirectStdoutToFileAppend:
       
   436 			case TToken::ERedirectStdoutToStderr:
       
   437 			case TToken::ERedirectStderrToFile:
       
   438 			case TToken::ERedirectStderrToFileAppend:
       
   439 			case TToken::ERedirectStderrToStdout:
       
   440 				{
       
   441 				if (HandleRedirectionL(token.Type(), cwd, *iLexer2, pipeSection))
       
   442 					{
       
   443 					break;
       
   444 					}
       
   445 				// Deliberate fall through - if it wasn't possible to handle the redirection, treat token as a string.
       
   446 				}
       
   447 			case TToken::EString:
       
   448 			default:
       
   449 				{
       
   450 				if (token.String().Length() > 0)
       
   451 					{
       
   452 					if (pipeSection.iCommandName.Length() == 0)
       
   453 						{
       
   454 						pipeSection.iCommandName.Set(token.String());
       
   455 						}
       
   456 					else
       
   457 						{
       
   458 						User::LeaveIfError(pipeSection.iCommandArguments.Append(TPtrC(token.String())));
       
   459 						}
       
   460 					}
       
   461 				break;
       
   462 				}
       
   463 			}
       
   464 		}
       
   465 
       
   466 	if (pipeSection.iCommandName.Length() > 0)
       
   467 		{
       
   468 		_LIT(KExit, "exit");
       
   469 		if ((pipeSections.Count() == 0) && (pipeSection.iCommandName.CompareF(KExit) == 0) && (pipeSection.iCommandArguments.Count() == 0))
       
   470 			{
       
   471 			// Special case the handling of 'exit'. This allows the concept of 'local commands' (i.e. commands that run in either fshell's main
       
   472 			// thread or in the thread belonging to a 'source' or 'debug' command) to be dropped. That's a good thing, because local commands
       
   473 			// can't synchronously interact with iosrv without risk of deadlock when two or more thread commands are run in a pipe-line. 
       
   474 			CleanupStack::PopAndDestroy(2, &pipeSections);
       
   475 			if (CActiveScheduler::Current()->StackDepth() > 0)
       
   476 				{
       
   477 				CActiveScheduler::Stop();
       
   478 				}
       
   479 			else
       
   480 				{
       
   481 				// The active scheduler hasn't been started yet. Probably because someone is doing something crazy like 'fshell -e exit'.
       
   482 				iExitCallBack = new(ELeave) CAsyncCallBack(TCallBack(ExitCallBack, this), CActive::EPriorityStandard);
       
   483 				iExitCallBack->Call();
       
   484 				}
       
   485 			}
       
   486 		else
       
   487 			{
       
   488 			pipeSection.iFullName.Set(iData.Ptr() + offset, iLexer2->CurrentOffset() - offset);
       
   489 			User::LeaveIfError(pipeSections.Append(pipeSection));
       
   490 			CleanupStack::Pop(&pipeSection);
       
   491 			if ((iMode & EDebug) && iObserver)
       
   492 				{
       
   493 				iObserver->AboutToExecuteLine(pipeLineData, *expandedPipeLine);
       
   494 				}
       
   495 			if (background)
       
   496 				{
       
   497 				CPipeLine* pipeLine = CPipeLine::NewLC(iIoSession, iStdin, iStdout, iStderr, iEnv, iFactory, pipeSections, background, this, iCompletionError);
       
   498 				User::LeaveIfError(iBackgroundPipeLines.Append(pipeLine));
       
   499 				CleanupStack::Pop(pipeLine);
       
   500 				}
       
   501 			else
       
   502 				{
       
   503 				ASSERT(iForegroundPipeLine == NULL);
       
   504 				iForegroundPipeLine = CPipeLine::NewL(iIoSession, iStdin, iStdout, iStderr, iEnv, iFactory, pipeSections, background, this, iCompletionError);
       
   505 				}
       
   506 			CleanupStack::PopAndDestroy(&pipeSections);
       
   507 			if (aIsForeground && !iLexer1->More())
       
   508 				{
       
   509 				*aIsForeground = !background;
       
   510 				}
       
   511 			if (background && iLexer1->More())
       
   512 				{
       
   513 				iNextPipeLineCallBack->Call();
       
   514 				}
       
   515 			}
       
   516 		}
       
   517 	else
       
   518 		{
       
   519 		if (iObserver && (iForegroundPipeLine == NULL) && (iBackgroundPipeLines.Count() == 0))
       
   520 			{
       
   521 			iCompletionCallBack->CallBack();
       
   522 			}
       
   523 		CleanupStack::PopAndDestroy(2, &pipeSections);
       
   524 		}
       
   525 
       
   526 	CleanupStack::PopAndDestroy(expandedPipeLine);
       
   527 
       
   528 	if (reachedLineEnd && (iMode & EExportLineNumbers))
       
   529 		{
       
   530 		iEnv.SetL(KScriptLine, iNextLineNumber++);
       
   531 		}
       
   532 	}
       
   533 
       
   534 void CParser::FindNextPipeLineL(TPtrC& aData, TCondition& aCondition, TBool& aReachedLineEnd)
       
   535 	{
       
   536 	aReachedLineEnd = EFalse;
       
   537 	aCondition = ENone;
       
   538 	TInt startOffset = iLexer1->CurrentOffset();
       
   539 	TInt endOffset = -1;
       
   540 
       
   541 	TBool foundSomething(EFalse);
       
   542 	while (iLexer1->More())
       
   543 		{
       
   544 		TBool finished(EFalse);
       
   545 		TToken token(iLexer1->NextToken());
       
   546 
       
   547 		switch (token.Type())
       
   548 			{
       
   549 			case TToken::EString:
       
   550 				{
       
   551 				foundSomething = ETrue;
       
   552 				endOffset = iLexer1->CurrentOffset();
       
   553 				break;
       
   554 				}
       
   555 			case TToken::EDoublePipe:
       
   556 				{
       
   557 				finished = ETrue;
       
   558 				aCondition = EOr;
       
   559 				break;
       
   560 				}
       
   561 			case TToken::EDoubleAmpersand:
       
   562 				{
       
   563 				finished = ETrue;
       
   564 				aCondition = EAnd;
       
   565 				break;
       
   566 				}
       
   567 			case TToken::EAmpersandPipe:
       
   568 				{
       
   569 				finished = ETrue;
       
   570 				aCondition = EAndOr;
       
   571 				break;
       
   572 				}
       
   573 			case TToken::ENull:
       
   574 			case TToken::ENewLine:
       
   575 				{
       
   576 				if (foundSomething)
       
   577 					{
       
   578 					// Leave it to CreateNextPipeLineL to increment SCRIPT_LINE when it's finished building the pipe-line.
       
   579 					aReachedLineEnd = ETrue;
       
   580 					}
       
   581 				else
       
   582 					{
       
   583 					// Reached the end of a comment line - increment SCRIPT_LINE.
       
   584 					if (iMode & EExportLineNumbers)
       
   585 						{
       
   586 						iEnv.SetL(KScriptLine, iNextLineNumber++);
       
   587 						}
       
   588 					}
       
   589 				// Deliberate fall through.
       
   590 				}
       
   591 			case TToken::ESemicolon:
       
   592 				{
       
   593 				if (foundSomething)
       
   594 					{
       
   595 					finished = ETrue;
       
   596 					}
       
   597 				else
       
   598 					{
       
   599 					// Nothing on this line - adjust 'startOffset' to pretend it doesn't exist.
       
   600 					startOffset = iLexer1->CurrentOffset();
       
   601 					}
       
   602 				break;
       
   603 				}
       
   604 			default:
       
   605 				{
       
   606 				ASSERT(EFalse);
       
   607 				break;
       
   608 				}
       
   609 			}
       
   610 
       
   611 		if (finished)
       
   612 			{
       
   613 			break;
       
   614 			}
       
   615 		}
       
   616 
       
   617 	if (foundSomething)
       
   618 		{
       
   619 		aData.Set(iData.Ptr() + startOffset, endOffset - startOffset);
       
   620 		}
       
   621 	else
       
   622 		{
       
   623 		aData.Set(KNullDesC);
       
   624 		}
       
   625 	}
       
   626 
       
   627 HBufC* ExpandVariablesLC(const TDesC& aData, CLexer& aLexer, IoUtils::CEnvironment& aEnv, TBool aEscape)
       
   628 	{
       
   629 	TChar escapeChar = aEnv.EscapeChar();
       
   630 	RArray<TInt> charsToEscape;
       
   631 	CleanupClosePushL(charsToEscape);
       
   632 	HBufC* buf = aData.AllocLC();
       
   633 
       
   634 	// Repeatedly check the data for environment variable tokens. This is done in a loop
       
   635 	// because there could be variables within variables and we want to expand them all.
       
   636 	aLexer.Set(*buf, escapeChar);
       
   637 	FOREVER
       
   638 		{
       
   639 		TToken token(aLexer.NextToken());
       
   640 		if (token.Type() == TToken::ENull)
       
   641 			{
       
   642 			break;
       
   643 			}
       
   644 		else if (token.Type() == TToken::EVariable)
       
   645 			{
       
   646 			const TDesC& val = aEnv.GetAsDesL(token.String().Mid(1));
       
   647 			TPtr bufPtr(buf->Des());
       
   648 			const TInt freeSpace = bufPtr.MaxLength() - bufPtr.Length();
       
   649 			TInt requiredSpace = val.Length() - token.String().Length();
       
   650 			if (aEscape)
       
   651 				{
       
   652 				charsToEscape.Reset();
       
   653 				TLex lex(val);
       
   654 				while (!lex.Eos())
       
   655 					{
       
   656 					TChar c = lex.Get();
       
   657 					if ((c == aEnv.EscapeChar()) || (c == '\"'))
       
   658 						{
       
   659 						charsToEscape.AppendL(token.Position() + lex.Offset() - 1);
       
   660 						++requiredSpace;
       
   661 						}
       
   662 					}
       
   663 				}
       
   664 			if (requiredSpace > freeSpace)
       
   665 				{
       
   666 				HBufC* oldBuf = buf;
       
   667 				buf = buf->ReAllocL(bufPtr.MaxLength() + requiredSpace - freeSpace);
       
   668 				CleanupStack::Pop(oldBuf);
       
   669 				CleanupStack::PushL(buf);
       
   670 				bufPtr.Set(buf->Des());
       
   671 				}
       
   672 
       
   673 			bufPtr.Replace(token.Position(), token.String().Length(), val);
       
   674 
       
   675 			if (aEscape)
       
   676 				{
       
   677 				TPtrC escape((TUint16*)&escapeChar, 1);
       
   678 				while (charsToEscape.Count() > 0)
       
   679 					{
       
   680 					bufPtr.Insert(charsToEscape[charsToEscape.Count() - 1], escape);
       
   681 					charsToEscape.Remove(charsToEscape.Count() - 1);
       
   682 					}
       
   683 				}
       
   684 
       
   685 			aLexer.Set(*buf, escapeChar);
       
   686 			}
       
   687 		}
       
   688 
       
   689 	CleanupStack::Pop(buf);
       
   690 	CleanupStack::PopAndDestroy(&charsToEscape);
       
   691 	CleanupStack::PushL(buf);
       
   692 
       
   693 	return buf;
       
   694 	}
       
   695 
       
   696 HBufC* CParser::ExpandVariablesLC(const TDesC& aData)
       
   697 	{
       
   698 	CLexer* lexer1 = CLexer::NewLC(CLexer::EHandleSingleQuotes | CLexer::EHandleDoubleQuotes  | CLexer::EHandleComments);
       
   699 	CLexer* lexer2 = CLexer::NewLC(0);
       
   700 
       
   701 	// Populate 'lexer2' with a token definition for each environment variable (preceded with '$').
       
   702 	RPointerArray<HBufC> keys;
       
   703 	LtkUtils::CleanupResetAndDestroyPushL(keys);
       
   704 	iEnv.GetKeysL(keys);
       
   705 
       
   706 	const TInt numVars = keys.Count();
       
   707 	for (TInt i = 0; i < numVars; ++i)
       
   708 		{
       
   709 		keys[i] = keys[i]->ReAllocL(keys[i]->Length() + KDollar().Length());
       
   710 		keys[i]->Des().Insert(0, KDollar);
       
   711 		lexer2->DefineTokenTypeL(TToken::EVariable, *keys[i]);
       
   712 		}
       
   713 
       
   714 	HBufC* buf = aData.AllocLC();
       
   715 	lexer1->Set(*buf, iEnv.EscapeChar());
       
   716 	FOREVER
       
   717 		{
       
   718 		TToken token(lexer1->NextToken());
       
   719 		if (token.Type() == TToken::ENull)
       
   720 			{
       
   721 			break;
       
   722 			}
       
   723 		else if (token.Type() == TToken::EString)
       
   724 			{
       
   725 			if (token.String()[0] != '\'')
       
   726 				{
       
   727 				HBufC* expandedString = ::ExpandVariablesLC(token.String(), *lexer2, iEnv, token.String()[0] == '\"');
       
   728 				if (*expandedString != token.String())
       
   729 					{
       
   730 					TPtr bufPtr(buf->Des());
       
   731 					const TInt freeSpace = bufPtr.MaxLength() - bufPtr.Length();
       
   732 					const TInt requiredSpace = expandedString->Length() - token.String().Length();
       
   733 
       
   734 					if (requiredSpace > freeSpace)
       
   735 						{
       
   736 						HBufC* oldBuf = buf;
       
   737 						buf = buf->ReAllocL(bufPtr.MaxLength() + requiredSpace - freeSpace);
       
   738 						CleanupStack::Pop(expandedString);
       
   739 						CleanupStack::Pop(oldBuf);
       
   740 						CleanupStack::PushL(buf);
       
   741 						CleanupStack::PushL(expandedString);
       
   742 						bufPtr.Set(buf->Des());
       
   743 						}
       
   744 
       
   745 					bufPtr.Replace(token.Position(), token.String().Length(), *expandedString);
       
   746 					lexer1->Set(*buf, iEnv.EscapeChar());
       
   747 					}
       
   748 				CleanupStack::PopAndDestroy(expandedString);
       
   749 				}
       
   750 			}
       
   751 		}
       
   752 
       
   753 	CleanupStack::Pop(buf);
       
   754 	CleanupStack::PopAndDestroy(3, lexer1);
       
   755 	CleanupStack::PushL(buf);
       
   756 	return buf;
       
   757 	}
       
   758 
       
   759 TInt CParser::SkipLineRemainder()
       
   760 	{
       
   761 	while (iLexer1->More())
       
   762 		{
       
   763 		TToken token(iLexer1->NextToken());
       
   764 		if (token.Type() == TToken::ENewLine)
       
   765 			{
       
   766 			if (iMode & EExportLineNumbers)
       
   767 				{
       
   768 				// can we do something better with errors here?
       
   769 				TRAPD(err, iEnv.SetL(KScriptLine, iNextLineNumber++));
       
   770 				if (err!=KErrNone)
       
   771 					{
       
   772 					iCompletionError.Set(err, TError::EFailedToSetScriptLineVar);
       
   773 					return err;
       
   774 					}
       
   775 				}
       
   776 			break;
       
   777 			}
       
   778 		}
       
   779 	return KErrNone;
       
   780 	}
       
   781 
       
   782 void CParser::SkipToEnd()
       
   783 	{
       
   784 	while (iLexer1->More())
       
   785 		{
       
   786 		iLexer1->NextToken();
       
   787 		}
       
   788 	}
       
   789 
       
   790 TInt CParser::CompletionCallBack(TAny* aSelf)
       
   791 	{
       
   792 	CParser* self = static_cast<CParser*>(aSelf);
       
   793 	self->iObserver->HandleParserComplete(*self, self->iCompletionError);
       
   794 	return KErrNone;
       
   795 	}
       
   796 
       
   797 TInt CParser::NextCallBack(TAny* aSelf)
       
   798 	{
       
   799 	CParser* self = static_cast<CParser*>(aSelf);
       
   800 	self->CreateNextPipeLine(NULL);
       
   801 	return KErrNone;
       
   802 	}
       
   803 
       
   804 TInt CParser::ExitCallBack(TAny*)
       
   805 	{
       
   806 	CActiveScheduler::Stop();
       
   807 	return KErrNone;
       
   808 	}
       
   809 
       
   810 void CParser::HandlePipeLineComplete(CPipeLine& aPipeLine, const TError& aError)
       
   811 	{
       
   812 	TRAPD(err, iEnv.SetL(KChildError, aError.Error()));
       
   813 	if (err)
       
   814 		{
       
   815 		iCompletionError.Set(err, TError::EFailedToSetChildErrorVar);
       
   816 		iCompletionCallBack->CallBack();
       
   817 		return;
       
   818 		}
       
   819 
       
   820 	if ((iMode & EDebug) && iObserver)
       
   821 		{
       
   822 		iObserver->LineReturned(aError.Error());
       
   823 		}
       
   824 
       
   825 	if (iForegroundPipeLine == &aPipeLine)
       
   826 		{
       
   827 		switch (iCondition)
       
   828 			{
       
   829 			case ENone:
       
   830 				{
       
   831 				if ((aError.Error() != KErrNone) && !(iMode & EKeepGoing))
       
   832 					{
       
   833 					// Bail out of script if an error is found that isn't "handled" by an "&&" or and "||".
       
   834 					SkipToEnd();
       
   835 					iCompletionError.Set(aError);
       
   836 					}
       
   837 				break;
       
   838 				}
       
   839 			case EAnd:
       
   840 				{
       
   841 				if (iAbort)
       
   842 					{
       
   843 					SkipToEnd();
       
   844 					}
       
   845 				else if (aError.Error() != KErrNone)
       
   846 					{
       
   847 					TInt err = SkipLineRemainder();
       
   848 					if (err!=KErrNone) SkipToEnd();
       
   849 					}
       
   850 				break;
       
   851 				}
       
   852 			case EOr:
       
   853 				{
       
   854 				if (iAbort)
       
   855 					{
       
   856 					SkipToEnd();
       
   857 					}
       
   858 				else if (aError.Error() == KErrNone)
       
   859 					{
       
   860 					TInt err = SkipLineRemainder();
       
   861 					if (err!=KErrNone) SkipToEnd();
       
   862 					}
       
   863 				break;
       
   864 				}
       
   865 			case EAndOr:
       
   866 				{
       
   867 				if (iAbort)
       
   868 					{
       
   869 					SkipToEnd();
       
   870 					}
       
   871 				break;
       
   872 				}
       
   873 			default:
       
   874 				{
       
   875 				ASSERT(EFalse);
       
   876 				}
       
   877 			}
       
   878 
       
   879 		delete iForegroundPipeLine;
       
   880 		iForegroundPipeLine = NULL;
       
   881 
       
   882 		if (iLexer1->More())
       
   883 			{
       
   884 			iNextPipeLineCallBack->Call();
       
   885 			}
       
   886 		}
       
   887 	else
       
   888 		{
       
   889 		TInt pos = iBackgroundPipeLines.Find(&aPipeLine);
       
   890 		ASSERT(pos >= 0);
       
   891 		iBackgroundPipeLines.Remove(pos);
       
   892 		if (aError.Error() != KErrNone)
       
   893 			{
       
   894 			iCompletionError.Set(aError);
       
   895 			}
       
   896 		delete &aPipeLine;
       
   897 		}
       
   898 
       
   899 	if (iObserver && !iLexer1->More() && (iForegroundPipeLine == NULL) && (iBackgroundPipeLines.Count() == 0))
       
   900 		{
       
   901 		iCompletionCallBack->CallBack();
       
   902 		}
       
   903 	}
       
   904