|
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 |