core/src/fshell.cpp
changeset 0 7f656887cf89
child 42 e81b4e28b3e2
equal deleted inserted replaced
-1:000000000000 0:7f656887cf89
       
     1 // fshell.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 <e32cons.h>
       
    14 #include <f32file.h>
       
    15 #include <fshell/common.mmh>
       
    16 #include <fshell/ltkutils.h>
       
    17 #include "string_utils.h"
       
    18 #include "line_completer.h"
       
    19 #include "command_factory.h"
       
    20 #include "fshell.h"
       
    21 #include "license.h"
       
    22 #include "script_command.h"
       
    23 
       
    24 #ifdef FSHELL_MEMORY_ACCESS_SUPPORT
       
    25 #include <fshell/memoryaccess.h>
       
    26 #endif
       
    27 
       
    28 //
       
    29 // Constants.
       
    30 //
       
    31 
       
    32 const TInt KNoForegroundJob = -1;
       
    33 
       
    34 
       
    35 //
       
    36 // Globals.
       
    37 //
       
    38 
       
    39 CShell* gShell;
       
    40 TInt gExitValue(KErrNone);
       
    41 
       
    42 
       
    43 //
       
    44 // TError.
       
    45 //
       
    46 
       
    47 TError::TError(RIoWriteHandle& aStderr, IoUtils::CEnvironment& aEnv)
       
    48 	: iStderr(&aStderr), iEnv(&aEnv), iScriptLineNumber(-1)
       
    49 	{
       
    50 	if (iEnv->IsDefined(KScriptLine))
       
    51 		{
       
    52 		iScriptFileName = iEnv->GetAsDes(KScriptPath);
       
    53 		iScriptFileName.Append(iEnv->GetAsDes(KScriptName));
       
    54 		iScriptLineNumber = iEnv->GetAsInt(KScriptLine);
       
    55 		}
       
    56 	}
       
    57 
       
    58 void TError::Set(const TError& aError)
       
    59 	{
       
    60 	if (!iSet)
       
    61 		{
       
    62 		iError = aError.iError;
       
    63 		iReason = aError.iReason;
       
    64 		iContext = aError.iContext;
       
    65 		iScriptFileName = aError.iScriptFileName;
       
    66 		iScriptLineNumber = aError.iScriptLineNumber;
       
    67 		iSet = ETrue;
       
    68 		}
       
    69 	}
       
    70 
       
    71 void TError::Set(TInt aError, TReason aReason)
       
    72 	{
       
    73 	if (!iSet)
       
    74 		{
       
    75 		iError = aError;
       
    76 		iReason = aReason;
       
    77 		iContext.Zero();
       
    78 		iSet = ETrue;
       
    79 		}
       
    80 	LogIfRequired(aError, aReason, KNullDesC);
       
    81 	}
       
    82 
       
    83 void TError::Set(TInt aError, TReason aReason, TRefByValue<const TDesC> aFmt, ...)
       
    84 	{
       
    85 	VA_LIST list;
       
    86 	VA_START(list, aFmt);
       
    87 	if (!iSet)
       
    88 		{
       
    89 		iError = aError;
       
    90 		iReason = aReason;
       
    91 		Format(aFmt, list);
       
    92 		iSet = ETrue;
       
    93 		}
       
    94 	LogListIfRequired(aError, aReason, aFmt, list);
       
    95 	VA_END(list);
       
    96 	}
       
    97 
       
    98 void TError::Report() const
       
    99 	{
       
   100 	ASSERT(iError < 0);
       
   101 
       
   102 	switch (iReason)
       
   103 		{
       
   104 		case ECommandError:
       
   105 			{
       
   106 			// Do nothing - assume an error message has been printed by CCommandBase.
       
   107 			break;
       
   108 			}
       
   109 		case EFailedToConstructCommand:
       
   110 			{
       
   111 			FormatError(_L("Unable to create command \"%S\""), &iContext);
       
   112 			break;
       
   113 			}
       
   114 		case EFailedToRunCommand:
       
   115 			{
       
   116 			FormatError(_L("Unable to run command \"%S\""), &iContext);
       
   117 			break;
       
   118 			}
       
   119 		case EFailedToCreatePipeLine:
       
   120 		case EFailedToSetChildErrorVar:
       
   121 		case EFailedToSetScriptLineVar:
       
   122 		case EUnknown:
       
   123 		default:
       
   124 			{
       
   125 			PrintError();
       
   126 			break;
       
   127 			}
       
   128 		}
       
   129 	}
       
   130 
       
   131 void TError::Format(TRefByValue<const TDesC> aFmt, ...)
       
   132 	{
       
   133 	VA_LIST list;
       
   134 	VA_START(list, aFmt);
       
   135 	FormatList(aFmt, list);
       
   136 	VA_END(list);
       
   137 	}
       
   138 
       
   139 void TError::FormatList(TRefByValue<const TDesC> aFmt, VA_LIST& aList)
       
   140 	{
       
   141 	iContext.Zero();
       
   142 	TOverflowTruncate overflow;
       
   143 	iContext.AppendFormatList(aFmt, aList, &overflow);
       
   144 	}
       
   145 
       
   146 TInt TError::Error() const
       
   147 	{
       
   148 	return iError;
       
   149 	}
       
   150 
       
   151 TError::TReason TError::Reason() const
       
   152 	{
       
   153 	return iReason;
       
   154 	}
       
   155 
       
   156 const TDesC* TError::Context() const
       
   157 	{
       
   158 	return &iContext;
       
   159 	}
       
   160 
       
   161 const TDesC& TError::ScriptFileName() const
       
   162 	{
       
   163 	return iScriptFileName;
       
   164 	}
       
   165 
       
   166 TInt TError::ScriptLineNumber() const
       
   167 	{
       
   168 	return iScriptLineNumber;
       
   169 	}
       
   170 
       
   171 void TError::LogIfRequired(TInt aError, TReason aReason, TRefByValue<const TDesC> aFmt, ...)
       
   172 	{
       
   173 	VA_LIST list;
       
   174 	VA_START(list, aFmt);
       
   175 	LogListIfRequired(aError, aReason, aFmt, list);
       
   176 	VA_END(list);
       
   177 	}
       
   178 
       
   179 void TError::LogListIfRequired(TInt aError, TReason aReason, TRefByValue<const TDesC> aFmt, VA_LIST& aList)
       
   180 	{
       
   181 	if (Verbose())
       
   182 		{
       
   183 		IoUtils::TOverflowTruncate overflow;
       
   184 		iScratch.Zero();
       
   185 		iScratch.AppendFormat(_L("Error: %S (%d), Reason: %S (%d), Context: \""), &overflow, Stringify::Error(aError), aError, StringifyReason(aReason), aReason);
       
   186 		iScratch.AppendFormatList(aFmt, aList, &overflow);
       
   187 		iScratch.AppendFormat(_L("\"\r\n"), &overflow);
       
   188 		iStderr->Write(iScratch);
       
   189 		}
       
   190 	}
       
   191 
       
   192 #define CASE_RETURN_LIT(XXX) case XXX: { _LIT(_KLit, #XXX); return &_KLit; }
       
   193 #define DEFAULT_RETURN_LIT(XXX) default: { _LIT(_KLit, XXX); return &_KLit; }
       
   194 
       
   195 const TDesC* TError::StringifyReason(TReason aReason) const
       
   196 	{
       
   197 	switch (aReason)
       
   198 		{
       
   199 		CASE_RETURN_LIT(EUnknown);
       
   200 		CASE_RETURN_LIT(EFailedToCreatePipeLine);
       
   201 		CASE_RETURN_LIT(EFailedToConstructCommand);
       
   202 		CASE_RETURN_LIT(EFailedToRunCommand);
       
   203 		CASE_RETURN_LIT(EFailedToSetChildErrorVar);
       
   204 		CASE_RETURN_LIT(EFailedToSetScriptLineVar);
       
   205 		CASE_RETURN_LIT(ECommandError);
       
   206 		DEFAULT_RETURN_LIT("*** REASON UNKNOWN ***");
       
   207 		}
       
   208 	}
       
   209 
       
   210 TBool TError::Verbose() const
       
   211 	{
       
   212 	_LIT(KVerbose, "FSHELL_VERBOSE");
       
   213 	return (iEnv->IsDefined(KVerbose));
       
   214 	}
       
   215 
       
   216 void TError::NewLine() const
       
   217 	{
       
   218 	if (iStderr->AttachedToConsole())
       
   219 		{
       
   220 		RIoConsoleWriteHandle consoleWriteHandle;
       
   221 		consoleWriteHandle = *iStderr;
       
   222 		TPoint cursorPos;
       
   223 		consoleWriteHandle.GetCursorPos(cursorPos);
       
   224 		if (cursorPos.iX != 0)
       
   225 			{
       
   226 			iStderr->Write(_L("\r\n"));
       
   227 			}
       
   228 		}
       
   229 	}
       
   230 
       
   231 void TError::PrintError() const
       
   232 	{
       
   233 	NewLine();
       
   234 	IoUtils::TOverflowTruncate overflow;
       
   235 	iScratch.Zero();
       
   236 	iScratch.AppendFormat(_L("Error: %S (%d)\r\n"), &overflow, Stringify::Error(iError), iError);
       
   237 	iStderr->Write(iScratch);
       
   238 	}
       
   239 
       
   240 void TError::FormatError(TRefByValue<const TDesC> aFmt, ...) const
       
   241 	{
       
   242 	NewLine();
       
   243 	VA_LIST list;
       
   244 	VA_START(list, aFmt);
       
   245 	IoUtils::TOverflowTruncate overflow;
       
   246 	iScratch = _L("Error: ");
       
   247 	iScratch.AppendFormatList(aFmt, list, &overflow);
       
   248 	VA_END(list);
       
   249 	iScratch.AppendFormat(_L(" : %S (%d)\r\n"), Stringify::Error(iError), iError);
       
   250 	iStderr->Write(iScratch);
       
   251 	}
       
   252 
       
   253 //
       
   254 // CShell.
       
   255 //
       
   256 
       
   257 
       
   258 void Log(TRefByValue<const TDesC8> aFmt, ...)
       
   259 	{
       
   260 	RFs fs;
       
   261 	TInt err = fs.Connect();
       
   262 	if (err == KErrNone)
       
   263 		{
       
   264 		RFile file;
       
   265 		err = file.Open(fs, _L("c:\\fshell.txt"), EFileWrite);
       
   266 		if (err == KErrNotFound)
       
   267 			{
       
   268 			err = file.Replace(fs, _L("c:\\fshell.txt"), EFileWrite);
       
   269 			}
       
   270 		if (err == KErrNone)
       
   271 			{
       
   272 			TOverflowTruncate8 overflow;
       
   273 			VA_LIST list;
       
   274 			VA_START(list, aFmt);
       
   275 			TBuf8<0x100> buf;
       
   276 			buf.AppendFormatList(aFmt, list, &overflow);
       
   277 			VA_END(list);
       
   278 			buf.Append(_L("\r\n"));
       
   279 			TInt pos(0);
       
   280 			file.Seek(ESeekEnd, pos);
       
   281 			file.Write(buf);
       
   282 			file.Close();
       
   283 			}
       
   284 		fs.Close();
       
   285 		}
       
   286 	}
       
   287 
       
   288 
       
   289 CShell* CShell::NewLC()
       
   290 	{
       
   291 	CShell* self = new(ELeave) CShell();
       
   292 	CleanupStack::PushL(self);
       
   293 	self->ConstructL();
       
   294 	return self;
       
   295 	}
       
   296 
       
   297 CShell::~CShell()
       
   298 	{
       
   299 	iJobsLock.Close();
       
   300 	iJobs.ResetAndDestroy();
       
   301 	delete iScriptArgs;
       
   302 	delete iLineEditor;
       
   303 	delete iLineCompleter;
       
   304 	delete iConsole;
       
   305 	delete iCommandFactory;
       
   306 	delete iScriptData;
       
   307 	delete iOneLiner;
       
   308 	delete iParser;
       
   309 	}
       
   310 
       
   311 CCommandFactory& CShell::CommandFactory()
       
   312 	{
       
   313 	return *iCommandFactory;
       
   314 	}
       
   315 
       
   316 void CShell::ClaimJobsLockLC()
       
   317 	{
       
   318 	iJobsLock.Wait();
       
   319 	CleanupStack::PushL(TCleanupItem(ReleaseJobsLock, const_cast<CShell*>(this)));
       
   320 	}
       
   321 
       
   322 void CShell::ReleaseJobsLock(TAny* aSelf)
       
   323 	{
       
   324 	static_cast<CShell*>(aSelf)->iJobsLock.Signal();
       
   325 	}
       
   326 
       
   327 TInt CShell::BringJobToForeground(TInt aJobId)
       
   328 	{
       
   329 	TInt ret = KErrNotFound;
       
   330 	CJob* job = Job(aJobId);
       
   331 	if (job != NULL)
       
   332 		{
       
   333 		Printf(_L("[%d] %S\r\n"), aJobId, job->Name());
       
   334 		job->Resume();
       
   335 		ret = job->BringToForeground();
       
   336 		if (ret == KErrNone)
       
   337 			{
       
   338 			if (iForegroundJobId != KNoForegroundJob)
       
   339 				{
       
   340 				ForegroundJob().SendToBackground();
       
   341 				}
       
   342 			iForegroundJobId = aJobId;
       
   343 			}
       
   344 		}
       
   345 	if (ret)
       
   346 		{
       
   347 		PrintError(ret, _L("Unable to bring job [%d] to foreground"), aJobId);
       
   348 		}
       
   349 	return ret;
       
   350 	}
       
   351 
       
   352 void CShell::SendJobToBackground(TInt aJobId)
       
   353 	{
       
   354 	CJob* job = Job(aJobId);
       
   355 	if (job != NULL)
       
   356 		{
       
   357 		job->SendToBackground();
       
   358 		job->Resume();
       
   359 		Printf(_L("[%d] %S\r\n"), aJobId, job->Name());
       
   360 		if (aJobId == iForegroundJobId)
       
   361 			{
       
   362 			iForegroundJobId = KNoForegroundJob;
       
   363 			}
       
   364 		}
       
   365 	else
       
   366 		{
       
   367 		PrintError(KErrNotFound, _L("Unable to send job [%d] to background"), aJobId);
       
   368 		}
       
   369 	}
       
   370 
       
   371 const RPointerArray<CJob>& CShell::Jobs() const
       
   372 	{
       
   373 	ASSERT(const_cast<RMutex&>(iJobsLock).IsHeld()); // IsHeld should be const but isn't
       
   374 	return iJobs;
       
   375 	}
       
   376 
       
   377 CShell::CShell()
       
   378 	: CCommandBase(EManualComplete | ENotifyStdinChanges | ESharableIoSession), iForegroundJobId(KNoForegroundJob), iWriterAdaptor(Stdout())
       
   379 	{
       
   380 	gShell = this;
       
   381 	}
       
   382 
       
   383 void CShell::ConstructL()
       
   384 	{
       
   385 	BaseConstructL();
       
   386 	User::LeaveIfError(iJobsLock.CreateLocal());
       
   387 #ifdef FSHELL_MEMORY_ACCESS_SUPPORT
       
   388 	// We call LoadDriver here so that memoryaccess gets a chance to start the thread creator tracker as early as possible
       
   389 	// Possibly this isn't the best place to put it...
       
   390 	RMemoryAccess::LoadDriver();
       
   391 #endif
       
   392 	}
       
   393 
       
   394 void CShell::PrintPrompt()
       
   395 	{
       
   396 	const TDesC& pwd = Env().Pwd();
       
   397 	// Truncate the prompt if it will take up more than half the screen, or half of KMaxLineLength
       
   398 	TInt maxLen = KMaxLineLength / 2;
       
   399 	TSize screenSize;
       
   400 	TInt err = Stdout().GetScreenSize(screenSize);
       
   401 	if (err == KErrNone && screenSize.iWidth > 3) maxLen = Min(maxLen, screenSize.iWidth / 2); // Nullcons has a width of 1, apparently
       
   402 	TBuf<KMaxLineLength> prompt;
       
   403 	if (pwd.Length() > maxLen)
       
   404 		{
       
   405 		TPtrC left(pwd.Left(3)); // For the driveletter:\ bit
       
   406 		TPtrC right = pwd.Right(maxLen - 3);
       
   407 		prompt.Format(_L("%S...%S>"), &left, &right);
       
   408 		}
       
   409 	else
       
   410 		{
       
   411 		prompt.Format(_L("%S>"), &pwd);
       
   412 		}
       
   413 	iLineEditor->Start(prompt);
       
   414 	iLineEditor->ReinstatePromptAndUserInput();
       
   415 	}
       
   416 
       
   417 void CShell::PrintErr(TInt aError)
       
   418 	{
       
   419 	TPoint cursorPos;
       
   420 	Stdout().GetCursorPos(cursorPos);
       
   421 	if (cursorPos.iX != 0)
       
   422 		{
       
   423 		Stderr().Write(_L("\r\n"));
       
   424 		}
       
   425 	TBuf<128> buf;
       
   426 	IoUtils::TOverflowTruncate overflow;
       
   427 	buf.AppendFormat(_L("Error: %S (%d)\r\n"), &overflow, Stringify::Error(aError), aError);
       
   428 	Stderr().Write(buf);
       
   429 	}
       
   430 
       
   431 void CShell::ProcessLineL(const TDesC& aLine)
       
   432 	{
       
   433 	TInt jobId = NextJobId();
       
   434 	CJob* job = CJob::NewL(jobId, aLine, IoSession(), Stdin(), Stdout(), Stderr(), Env(), *iCommandFactory, this);
       
   435 	CleanupStack::PushL(job);
       
   436 	ClaimJobsLockLC();
       
   437 	User::LeaveIfError(iJobs.Append(job));
       
   438 	CleanupStack::PopAndDestroy(); // Jobs lock
       
   439 	CleanupStack::Pop(job);
       
   440 	iNextJobId = jobId;
       
   441 	TBool isForeground(EFalse);
       
   442 	job->Start(isForeground);
       
   443 	if (isForeground)
       
   444 		{
       
   445 		iForegroundJobId = jobId;
       
   446 		}
       
   447 	else
       
   448 		{
       
   449 		PrintPrompt();
       
   450 		}
       
   451 	}
       
   452 
       
   453 const TDesC& CShell::Name() const
       
   454 	{
       
   455 	_LIT(KName, "fshell");
       
   456 	return KName;
       
   457 	}
       
   458 
       
   459 void CShell::DoRunL()
       
   460 	{
       
   461 #if defined (__WINS__) && !defined (EKA2)
       
   462 	RThread().SetPriority(::EPriorityAbsoluteHigh);
       
   463 #else
       
   464 	RProcess().SetPriority(::EPriorityHigh);
       
   465 #endif
       
   466 	User::LeaveIfError(iFs.Connect());
       
   467 	User::LeaveIfError(iFs.ShareAuto()); // Necessary because this handle is used by CCommandFactory and that can be used from the context of other threads (e.g. the "debug" command).
       
   468 	iCommandFactory = CCommandFactory::NewL(iFs);
       
   469 
       
   470 	if (iOneLiner)
       
   471 		{
       
   472 		// One line script mode.
       
   473 		iParser = CParser::NewL(CParser::ENormal, *iOneLiner, IoSession(), Stdin(), Stdout(), Stderr(), Env(), *iCommandFactory, this);
       
   474 		RProcess::Rendezvous(KErrNone);
       
   475 		iParser->Start();
       
   476 		}
       
   477 	else if (iScriptName.Length() > 0)
       
   478 		{
       
   479 		TIoHandleSet ioHandles(IoSession(), Stdin(), Stdout(), Stderr());
       
   480 		TBool helpPrinted;
       
   481 		iScriptData = ReadScriptL(iScriptName, iScriptArgs, Env(), FsL(), ioHandles, helpPrinted);
       
   482 		if (helpPrinted)
       
   483 			{
       
   484 			Complete();
       
   485 			}
       
   486 		else
       
   487 			{
       
   488 			TUint mode = CParser::EExportLineNumbers;
       
   489 			if (iKeepGoing)
       
   490 				{
       
   491 				mode |= CParser::EKeepGoing;
       
   492 				}
       
   493 			iParser = CParser::NewL(mode, *iScriptData, IoSession(), Stdin(), Stdout(), Stderr(), Env(), *iCommandFactory, this);
       
   494 			RProcess::Rendezvous(KErrNone);
       
   495 			iParser->Start();
       
   496 			}
       
   497 		}
       
   498 	else
       
   499 		{
       
   500 		// Interactive mode.
       
   501 
       
   502 #ifdef FSHELL_CORE_SUPPORT_LICENSE
       
   503 		if (TLicense::CheckL(Stdin(), Stdout()) != TLicense::EValid)
       
   504 			{
       
   505 			Complete(KErrPermissionDenied);
       
   506 			return;
       
   507 			}
       
   508 #endif
       
   509 		
       
   510 		User::LeaveIfError(Stdin().CaptureKey(CTRL('c'), 0, 0));
       
   511 		User::LeaveIfError(Stdin().CaptureKey(CTRL('z'), 0, 0));
       
   512 		iConsole = CConsoleReader::NewL(Stdin(), *this);
       
   513 		iLineCompleter = CLineCompleter::NewL(iFs, *iCommandFactory, Env());
       
   514 		_LIT(KFshellHistory, "c:\\system\\console\\shell\\history");
       
   515 		iLineEditor = CLineEditor::NewL(iFs, iWriterAdaptor, *this, *iLineCompleter, KFshellHistory);
       
   516 		RProcess::Rendezvous(KErrNone);
       
   517 		PrintPrompt();
       
   518 		}
       
   519 	}
       
   520 
       
   521 void CShell::OptionsL(RCommandOptionList& aOptions)
       
   522 	{
       
   523 	_LIT(KOptExec, "exec");
       
   524 	aOptions.AppendStringL(iOneLiner, KOptExec);
       
   525 	_LIT(KOptKeepGoing, "keep-going");
       
   526 	aOptions.AppendBoolL(iKeepGoing, KOptKeepGoing);
       
   527 	}
       
   528 
       
   529 void CShell::ArgumentsL(RCommandArgumentList& aArguments)
       
   530 	{
       
   531 	_LIT(KArgScriptName, "script_name");
       
   532 	aArguments.AppendFileNameL(iScriptName, KArgScriptName);
       
   533 
       
   534 	_LIT(KArgArgs, "script_args");
       
   535 	aArguments.AppendStringL(iScriptArgs, KArgArgs);
       
   536 	}
       
   537 	
       
   538 void CShell::StdinChange(TUint)
       
   539 	{
       
   540 	if (iLineEditor && !iIgnoreNextStdinChange)
       
   541 		{
       
   542 		iLineEditor->Redraw();
       
   543 		}
       
   544 	iIgnoreNextStdinChange = EFalse;
       
   545 	}
       
   546 
       
   547 TInt CShell::NextJobId()
       
   548 	{
       
   549 	TInt next = iNextJobId;
       
   550 	++next;
       
   551 	if (next == KNoForegroundJob)
       
   552 		{
       
   553 		next = 0;
       
   554 		}
       
   555 	return next;
       
   556 	}
       
   557 
       
   558 CJob& CShell::ForegroundJob()
       
   559 	{
       
   560 	ASSERT(iForegroundJobId != KNoForegroundJob);
       
   561 	CJob* job = Job(iForegroundJobId);
       
   562 	ASSERT(job != NULL);
       
   563 	return *job;
       
   564 	}
       
   565 
       
   566 CJob* CShell::Job(TInt aId)
       
   567 	{
       
   568 	ASSERT(iJobsLock.IsHeld());
       
   569 	const TInt numJobs = iJobs.Count();
       
   570 	CJob* job = NULL;
       
   571 	for (TInt i = 0; i < numJobs; ++i)
       
   572 		{
       
   573 		if (iJobs[i]->Id() == aId)
       
   574 			{
       
   575 			job = iJobs[i];
       
   576 			break;
       
   577 			}
       
   578 		}
       
   579 	return job;
       
   580 	}
       
   581 
       
   582 TInt CShell::DisownJob(TInt aId)
       
   583 	{
       
   584 	ASSERT(iJobsLock.IsHeld());
       
   585 	const TInt numJobs = iJobs.Count();
       
   586 	for (TInt i = 0; i < numJobs; ++i)
       
   587 		{
       
   588 		CJob* job = iJobs[i];
       
   589 		if (job->Id() == aId)
       
   590 			{
       
   591 			if (job->IsDisownable())
       
   592 				{
       
   593 				job->Disown();
       
   594 				delete job;
       
   595 				iJobs.Remove(i);
       
   596 				return KErrNone;
       
   597 				}
       
   598 			else
       
   599 				{
       
   600 				return KErrAccessDenied;
       
   601 				}
       
   602 			}
       
   603 		}
       
   604 	return KErrNotFound;
       
   605 	}
       
   606 
       
   607 HBufC* CShell::ReadScriptL(const TDesC& aScriptName, const TDesC* aArguments, IoUtils::CEnvironment& aEnv, RFs& aFs, TIoHandleSet& aIoHandles, TBool& aHelpPrinted, RPointerArray<HBufC>* aAdditionalPrefixArguments)
       
   608 	{
       
   609 	TFileName2 scriptName(aScriptName);
       
   610 
       
   611 	// Check the scripts dirs in case it wasn't given as an absolute path (although iocli will have made it absolute relative to the pwd)
       
   612 	_LIT(KScriptDir, "y:\\system\\console\\scripts\\");
       
   613 	_LIT(KScriptSuffix, ".script");
       
   614 	TInt scriptNameLen = scriptName.Length();
       
   615 	if (!scriptName.Exists(aFs) && (scriptName.DriveAndPath().CompareF(aEnv.Pwd()) == 0))
       
   616 		{
       
   617 		TFindFile finder(aFs);
       
   618 		TInt found = finder.FindByDir(scriptName.NameAndExt(), KScriptDir);
       
   619 		if (found == KErrNotFound && scriptName.Right(KScriptSuffix().Length()).CompareF(KScriptSuffix) != 0)
       
   620 			{
       
   621 			// If it doesn't already end in .script, try that
       
   622 			scriptName.Append(KScriptSuffix);
       
   623 			found = finder.FindByDir(scriptName.NameAndExt(), KScriptDir);
       
   624 			}
       
   625 		if (found == KErrNone)
       
   626 			{
       
   627 			scriptName.Copy(finder.File());
       
   628 			}
       
   629 		else
       
   630 			{
       
   631 			// Put scriptname back the way it was
       
   632 			scriptName.SetLength(scriptNameLen);
       
   633 			} 
       
   634 		}
       
   635 
       
   636 	RFile scriptFile;
       
   637 	TInt err;
       
   638 	TInt retries = 5;
       
   639 	do
       
   640 		{
       
   641 		err = scriptFile.Open(aFs, scriptName, EFileRead | EFileShareReadersOnly);
       
   642 		if ((err == KErrNone) || (err != KErrInUse))
       
   643 			{
       
   644 			break;
       
   645 			}
       
   646 		User::After(500000);
       
   647 		--retries;
       
   648 		}
       
   649 		while (retries >= 0);
       
   650 	StaticLeaveIfErr(err, _L("Couldn't open script file %S"), &scriptName);
       
   651 	CleanupClosePushL(scriptFile);
       
   652 	TInt scriptFileSize;
       
   653 	User::LeaveIfError(scriptFile.Size(scriptFileSize));
       
   654 	HBufC8* scriptData = HBufC8::NewLC(scriptFileSize);
       
   655 	TPtr8 scriptDataPtr(scriptData->Des());
       
   656 	User::LeaveIfError(scriptFile.Read(scriptDataPtr));
       
   657 	HBufC* decodedScriptData = LtkUtils::DecodeUtf8L(*scriptData);
       
   658 	CleanupStack::PopAndDestroy(2, &scriptFile);
       
   659 	CleanupStack::PushL(decodedScriptData);
       
   660 	aEnv.SetLocalL(KScriptName);
       
   661 	aEnv.SetLocalL(KScriptPath);
       
   662 	aEnv.SetLocalL(KScriptLine);
       
   663 	aEnv.SetLocalL(_L("0"));
       
   664 	aEnv.SetL(KScriptName, scriptName.NameAndExt());
       
   665 	aEnv.SetL(KScriptPath, scriptName.DriveAndPath());
       
   666 	aEnv.SetL(_L("0"), scriptName);
       
   667 	CScriptCommand* scriptCommand = CScriptCommand::NewLC(scriptName, aIoHandles);
       
   668 	TRAP(err, scriptCommand->ParseCommandLineArgsL(aArguments ? *aArguments : KNullDesC(), aEnv, aAdditionalPrefixArguments));
       
   669 	if (err == KErrArgument || scriptCommand->ShouldDisplayHelp())
       
   670 		{
       
   671 		// Need to display help
       
   672 		if (scriptCommand->ShouldDisplayHelp())
       
   673 			{
       
   674 			err = KErrNone;
       
   675 			}
       
   676 		else if (err == KErrArgument)
       
   677 			{
       
   678 			scriptCommand->PrintError(KErrArgument, _L("Couldn't parse command line"));
       
   679 			}
       
   680 		scriptCommand->DisplayScriptHelpL();
       
   681 		aHelpPrinted = ETrue;
       
   682 		}
       
   683 	else
       
   684 		{
       
   685 		aHelpPrinted = EFalse;
       
   686 		}
       
   687 	User::LeaveIfError(err); // Propagate error
       
   688 	CleanupStack::PopAndDestroy(scriptCommand);
       
   689 	CleanupStack::Pop(decodedScriptData);
       
   690 	return decodedScriptData;
       
   691 	}
       
   692 
       
   693 void CShell::SetToForeground()
       
   694 	{
       
   695 	Stdin().SetToForeground();
       
   696 	iIgnoreNextStdinChange = ETrue;
       
   697 	}
       
   698 
       
   699 void CShell::CoHandleKey(TUint aKeyCode, TUint aModifiers)
       
   700 	{
       
   701 	switch (aKeyCode)
       
   702 		{
       
   703 		case CTRL('c'):
       
   704 			{
       
   705 			if (iForegroundJobId == KNoForegroundJob)
       
   706 				{
       
   707 				PrintPrompt();
       
   708 				}
       
   709 			else
       
   710 				{
       
   711 				ClaimJobsLockLC();
       
   712 				ForegroundJob().Kill();
       
   713 				CleanupStack::PopAndDestroy(); // Jobs lock.
       
   714 				}
       
   715 			SetToForeground();
       
   716 			break;
       
   717 			}
       
   718 		case CTRL('z'):
       
   719 			{
       
   720 			if (iForegroundJobId != KNoForegroundJob)
       
   721 				{
       
   722 				ClaimJobsLockLC();
       
   723 				CJob& job = ForegroundJob();
       
   724 				job.Suspend();
       
   725 				CleanupStack::PopAndDestroy(); // Jobs lock.
       
   726 				SetToForeground();
       
   727 				Printf(_L("[%d] %S\t\t%S\r\n"), iForegroundJobId, ShStringify::JobStatus(job.Status()), job.Name());
       
   728 				PrintPrompt();
       
   729 				iForegroundJobId = KNoForegroundJob;
       
   730 				}
       
   731 			break;
       
   732 			}
       
   733 		default:
       
   734 			{
       
   735 			// Only handle the key if there's no foreground job. Note, it's possible for the foreground job
       
   736 			// to still be pending, but have implicilty given keyboard focus back to fshell. For example:
       
   737 			//
       
   738 			// sleep& && echo hello
       
   739 			//
       
   740 			// The above job will not complete until the background sleep command is killed. During this period
       
   741 			// fshell has implicitly taken keyboard focus because the hello command has released it. However
       
   742 			// fshell's line editor hasn't been started and so isn't ready to handle input. Perhaps the line
       
   743 			// editor should implicitly start in this situation? For the time being playing safe by ignoring
       
   744 			// the keyboard input if there's a foreground job in progress.
       
   745 			if (iForegroundJobId == KNoForegroundJob)
       
   746 				{
       
   747 				iLineEditor->HandleKey(aKeyCode, aModifiers);
       
   748 				}
       
   749 			}
       
   750 		}
       
   751 	}
       
   752 
       
   753 void CShell::CoHandleError(TInt aError)
       
   754 	{
       
   755 	// Treat console errors as fatal (they're likely to be due to a remote console being closed).
       
   756 	if (!IsComplete()) Complete(aError);
       
   757 	}
       
   758 
       
   759 void CShell::LeoHandleLine(const TDesC& aLine)
       
   760 	{
       
   761 	if (aLine.Length() > 0)
       
   762 		{
       
   763 		TRAPD(err, ProcessLineL(aLine));
       
   764 		if (err)
       
   765 			{
       
   766 			PrintErr(err);
       
   767 			PrintPrompt();
       
   768 			}
       
   769 		}
       
   770 	else
       
   771 		{
       
   772 		PrintPrompt();
       
   773 		}
       
   774 	}
       
   775 
       
   776 void CShell::HandleJobComplete(CJob& aJob, const TError& aError)
       
   777 	{
       
   778 	if (aError.Error() < 0)
       
   779 		{
       
   780 		aError.Report();
       
   781 		}
       
   782 
       
   783 	if (aJob.Id() == iForegroundJobId)
       
   784 		{
       
   785 		iForegroundJobId = KNoForegroundJob;
       
   786 		PrintPrompt();
       
   787 		}
       
   788 
       
   789 #ifdef _DEBUG
       
   790 	TInt pos = iJobs.Find(&aJob);
       
   791 	ASSERT(pos >= 0);
       
   792 #else
       
   793 	TInt pos = iJobs.Find(&aJob);
       
   794 #endif
       
   795 
       
   796 	delete &aJob;
       
   797 	iJobs.Remove(pos);
       
   798 	}
       
   799 
       
   800 void CShell::HandleParserComplete(CParser&, const TError& aError)
       
   801 	{
       
   802 	if ((aError.Error() < 0))
       
   803 		{
       
   804 		aError.Report();
       
   805 		if (!iOneLiner)
       
   806 			{
       
   807 			PrintError(aError.Error(), _L("Aborted \"%S\" at line %d"), &aError.ScriptFileName(), aError.ScriptLineNumber());
       
   808 			}
       
   809 		SetErrorReported(ETrue);
       
   810 		}
       
   811 
       
   812 	gExitValue = aError.Error();
       
   813 	Complete(aError.Error());
       
   814 	}
       
   815 
       
   816 
       
   817 //
       
   818 // Main.
       
   819 //
       
   820 
       
   821 void MainL()
       
   822 	{
       
   823 	_LIT(KShellName, "fshell");
       
   824 #ifdef EKA2
       
   825 	User::RenameThread(KShellName);
       
   826 #else
       
   827 	RThread().Rename(KShellName);
       
   828 #endif
       
   829 	CActiveScheduler* scheduler = new(ELeave) CActiveScheduler;
       
   830 	CleanupStack::PushL(scheduler);
       
   831 	CActiveScheduler::Install(scheduler);
       
   832 	IoUtils::CCommandBase* command = CShell::NewLC();
       
   833 	command->RunCommandL();
       
   834 	CleanupStack::PopAndDestroy(2, scheduler);
       
   835 	if (gExitValue)
       
   836 		{
       
   837 		User::Leave(gExitValue);
       
   838 		}
       
   839 	}
       
   840 
       
   841 GLDEF_C TInt E32Main()
       
   842 	{
       
   843 	__UHEAP_MARK;
       
   844 	TInt err = KErrNoMemory;
       
   845 	CTrapCleanup* cleanup = CTrapCleanup::New();
       
   846 	if (cleanup)
       
   847 		{
       
   848 		TRAP(err, MainL());
       
   849 		delete cleanup;
       
   850 		}
       
   851 	__UHEAP_MARKEND;
       
   852 	return err;
       
   853 	}