Added ENotifyKeypresses and ECaptureCtrlC flags to CCommandBase.
authorTom Sutcliffe <thomas.sutcliffe@accenture.com>
Thu, 26 Aug 2010 00:49:35 +0100
changeset 37 534b01198c2d
parent 36 698ccde15713
child 40 d9b3ed11f017
Added ENotifyKeypresses and ECaptureCtrlC flags to CCommandBase. Commands can now get keypresses and handle ctrl-C via callbacks instead of having to implement custom active objects. As part of this extended the CCommandBase extension interface to MCommandExtensionsV2 for the new virtual functions KeyPressed(TUint aKeyCode, TUint aModifiers) and CtrlCPressed(). sudo now cleans up correctly by using ECaptureCtrlC.
build/symtb92/platform.mmh
commands/sudo/sudo.cpp
documentation/change_history.pod
libraries/iosrv/bwins/iocliu.def
libraries/iosrv/client/command_base.cpp
libraries/iosrv/client/command_base.h
libraries/iosrv/eabi/iocliu.def
libraries/iosrv/inc/ioutils.h
--- a/build/symtb92/platform.mmh	Wed Aug 25 22:17:52 2010 +0100
+++ b/build/symtb92/platform.mmh	Thu Aug 26 00:49:35 2010 +0100
@@ -24,5 +24,6 @@
 #define FSHELL_FLEXIBLEMM_AWARE
 #define FSHELL_DYNAMICSTARTUP_SUPPORT
 #define FSHELL_ARM11XX_SUPPORT
+#define FSHELL_TESTEXECUTE_SUPPORT
 
 #endif // FSHELL_PLATFORM_MMH
--- a/commands/sudo/sudo.cpp	Wed Aug 25 22:17:52 2010 +0100
+++ b/commands/sudo/sudo.cpp	Thu Aug 26 00:49:35 2010 +0100
@@ -18,7 +18,7 @@
 
 using namespace IoUtils;
 
-class CCmdSudo : public CMemoryAccessCommandBase
+class CCmdSudo : public CMemoryAccessCommandBase, public MCommandExtensionsV2
 	{
 public:
 	static CCommandBase* NewLC();
@@ -42,6 +42,10 @@
 	virtual void DoRunL();
 	virtual void ArgumentsL(RCommandArgumentList& aArguments);
 	virtual void OptionsL(RCommandOptionList& aOptions);
+	virtual void RunL();
+
+private: // From MCommandExtensionsV2
+	virtual void CtrlCPressed();
 
 private:
 	HBufC* iCmd;
@@ -68,6 +72,8 @@
 	// following 2 are temporaries needed during CopyExeLC
 	TFileName iTempSrc;
 	TFileName iTempDest;
+
+	RChildProcess iChildProcess;
 	};
 
 
@@ -97,10 +103,13 @@
 			}
 		}
 	delete iPathsToCleanup;
+	iChildProcess.Close();
 	}
 
 CCmdSudo::CCmdSudo()
+	: CMemoryAccessCommandBase(EManualComplete | ECaptureCtrlC)
 	{
+	SetExtension(this);
 	}
 
 TCapability CapabilityFromString(const TDesC& aName)
@@ -304,25 +313,22 @@
 		}
 #endif
 
-	RChildProcess childProcess;
-	TRAPL(childProcess.CreateL(iNewPath, iArgs ? *iArgs : KNullDesC(), IoSession(), Stdin(), Stdout(), Stderr(), Env()), _L("Failed to execute %S"), &iNewPath);
+	TRAPL(iChildProcess.CreateL(iNewPath, iArgs ? *iArgs : KNullDesC(), IoSession(), Stdin(), Stdout(), Stderr(), Env()), _L("Failed to execute %S"), &iNewPath);
 	if (iKeep)
 		{
 		Printf(_L("Executing %S...\r\n"), &iNewPath);
-		CleanupStack::Pop(); // Don't delete if user asked for --keep
 		}
-	else
-		{
-		CleanupStack::PopAndDestroy(); // DeleteModifiedBinary - remove the binary before we actually start running it, so it is guaranteed cleaned up even if the user kills us with ctrl-c
-		}
+	CleanupStack::Pop(); // DeleteModifiedBinary
+
+
 	if (!iChangeBinaryOnDisk)
 		{
 		// Time to get memaccess involved
-		TRAPD(err, FixupExeInMemoryL(childProcess.Process()));
+		TRAPD(err, FixupExeInMemoryL(iChildProcess.Process()));
 		if (err)
 			{
-			childProcess.Process().Kill(err);
-			childProcess.Close();
+			iChildProcess.Process().Kill(err);
+			iChildProcess.Close();
 			User::Leave(err);
 			}
 		}
@@ -330,15 +336,12 @@
 	if (iWait)
 		{
 		Printf(_L("Process is created but not yet resumed. Press a key to continue...\r\n"));
-		Stdin().ReadKey();
+		ReadKey();
+		Printf(_L("Resuming process...\r\n"));
 		}
 
-	TRequestStatus stat;
-	childProcess.Run(stat);
-	User::WaitForRequest(stat);
-	TInt err = stat.Int();
-	childProcess.Close();
-	User::LeaveIfError(err); // This gets translated to our exe's return code I hope
+	iChildProcess.Run(iStatus);
+	SetActive();
 	}
 
 const TDesC& CCmdSudo::Name() const
@@ -535,3 +538,19 @@
 	LeaveIfErr(KErrNotSupported, _L("Can't fixup an exe in Core image without memoryaccess"));
 #endif
 	}
+
+void CCmdSudo::CtrlCPressed()
+	{
+	Printf(_L("Ctrl-C pressed, killing %S and cleaning up.\r\n"), &iNewPath);
+	SetErrorReported(ETrue);
+	iChildProcess.Process().Kill(KErrAbort);
+	}
+
+void CCmdSudo::RunL()
+	{
+	if (!iKeep)
+		{
+		DeleteModifiedBinary();
+		}
+	Complete(iStatus.Int());
+	}
--- a/documentation/change_history.pod	Wed Aug 25 22:17:52 2010 +0100
+++ b/documentation/change_history.pod	Thu Aug 26 00:49:35 2010 +0100
@@ -20,6 +20,10 @@
 
 =item *
 
+First release of fshell through the Symbian Foundation.
+
+=item *
+
 Added L<terminal keyboard|terminalkeyboardcons> console, for platforms that support Terminal Keyboard and Trace Core.
 
 =item *
@@ -34,6 +38,14 @@
 
 Added C<--codesegs> option to L<ps|commands::ps>.
 
+=item *
+
+Added C<ENotifyKeypresses> and C<ECaptureCtrlC> flags to CCommandBase, so commands can get keypresses and handle ctrl-C via callbacks instead of having to implement custom active objects. As part of this extended the CCommandBase extension interface to MCommandExtensionsV2 for the new virtual functions KeyPressed(TUint aKeyCode, TUint aModifiers) and CtrlCPressed(). sudo now cleans up correctly by using ECaptureCtrlC.
+
+=head2 Release 000.2-000.5
+
+Test releases with build fixes.
+
 =head2 Release 000.1
 
 Prerelease to the SF staging server.
--- a/libraries/iosrv/bwins/iocliu.def	Wed Aug 25 22:17:52 2010 +0100
+++ b/libraries/iosrv/bwins/iocliu.def	Thu Aug 26 00:49:35 2010 +0100
@@ -545,4 +545,8 @@
 	?StringifyError@MCommandExtensionsV1@IoUtils@@UBEPBVTDesC16@@H@Z @ 544 NONAME ; class TDesC16 const * IoUtils::MCommandExtensionsV1::StringifyError(int) const
 	?ExtensionVersion@MCommandExtensionsV1@IoUtils@@UBE?AW4TCommandExtensionVersion@2@XZ @ 545 NONAME ; enum IoUtils::TCommandExtensionVersion IoUtils::MCommandExtensionsV1::ExtensionVersion(void) const
 	?SetExtension@CCommandBase@IoUtils@@IAEXPAVMCommandExtensionsV1@2@@Z @ 546 NONAME ; void IoUtils::CCommandBase::SetExtension(class IoUtils::MCommandExtensionsV1 *)
+	?CtrlCPressed@MCommandExtensionsV2@IoUtils@@UAEXXZ @ 547 NONAME ; void IoUtils::MCommandExtensionsV2::CtrlCPressed(void)
+	?ExtensionVersion@MCommandExtensionsV2@IoUtils@@UBE?AW4TCommandExtensionVersion@2@XZ @ 548 NONAME ; enum IoUtils::TCommandExtensionVersion IoUtils::MCommandExtensionsV2::ExtensionVersion(void) const
+	?KeyPressed@MCommandExtensionsV2@IoUtils@@UAEXII@Z @ 549 NONAME ; void IoUtils::MCommandExtensionsV2::KeyPressed(unsigned int, unsigned int)
+	?ReadKey@CCommandBase@IoUtils@@QAEIXZ @ 550 NONAME ; unsigned int IoUtils::CCommandBase::ReadKey(void)
 
--- a/libraries/iosrv/client/command_base.cpp	Wed Aug 25 22:17:52 2010 +0100
+++ b/libraries/iosrv/client/command_base.cpp	Thu Aug 26 00:49:35 2010 +0100
@@ -14,7 +14,7 @@
 #include "ioutils.h"
 #include "command_base.h"
 #include <fshell/ltkutils.h>
-
+#include <fshell/line_editor.h> // for definition of CTRL()
 using namespace IoUtils;
 
 #define RETURN_IF_ERROR(x) {TInt _err = (x); if (_err<0) return _err;}
@@ -1523,10 +1523,15 @@
 	TUint privateFlags = (iFlags & KPrivateFlagsMask);
 	iFlags = (aFlags & KPublicFlagsMask);
 	iFlags |= privateFlags;
+	// Make sure implied flags are set
 	if (iFlags & ECompleteOnRunL)
 		{
 		iFlags |= EManualComplete;
 		}
+	if (iFlags & ECaptureCtrlC)
+		{
+		iFlags |= ENotifyKeypresses;
+		}
 	}
 
 EXPORT_C void CCommandBase::SetCif(const CCommandInfoFile& aCif)
@@ -1588,6 +1593,17 @@
 			UpdateHandlesL();
 			}
 
+		if (iFlags & ENotifyKeypresses)
+			{
+			__ASSERT_ALWAYS(iExtension && iExtension->ExtensionVersion() >= ECommandExtensionV2, IoUtils::Panic(EENotifyKeypressesSpecifiedWithoutExtensionBeingSet));
+			iKeypressWatcher = new(ELeave) CKeypressWatcher(*static_cast<MCommandExtensionsV2*>(iExtension), Stdin());
+			if (iFlags & ECaptureCtrlC)
+				{
+				User::LeaveIfError(Stdin().CaptureKey(CTRL('c'), 0, 0));
+				}
+			iKeypressWatcher->Notify();
+			}
+
 		TBool deleted(EFalse);
 		iDeleted = &deleted;	
 		// Note, commands that manually complete may call CCommandBase::Complete from their DoRunL, which in the case 
@@ -1626,7 +1642,7 @@
 	}
 
 EXPORT_C CCommandBase::CCommandBase(TUint aFlags)
-	: CActive(CActive::EPriorityStandard), iFlags(aFlags)
+	: CActive(CActive::EPriorityStandard)
 	{
 	CActiveScheduler::Add(this);
 	if (Dll::Tls() == NULL)
@@ -1635,14 +1651,15 @@
 		iFlags |= ETlsSet;
 		}
 
-	// Ensure EManualComplete is set if ECompleteOnRunL is.
-	SetFlags(Flags());
+	// Don't initialise iFlags directly - SetFlags checks that private flags aren't being set
+	SetFlags(aFlags);
 	}
 
 EXPORT_C CCommandBase::~CCommandBase()
 	{
 	delete iReadChangeNotifier;
 	delete iCompleter;
+	delete iKeypressWatcher;
 	iFs.Close();
 	if (iFlags & EOwnsHandles)
 		{
@@ -1781,6 +1798,15 @@
 	iStdout.Write(aData); // Ignore error.
 	}
 
+EXPORT_C TUint CCommandBase::ReadKey()
+	{
+	// Make sure keypresswatcher doesn't get in the way (some commands do some synchronous ReadKeys followed by use of async ENotifyKeypresses callbacks)
+	if (iKeypressWatcher) iKeypressWatcher->Cancel();
+	TUint result = iStdin.ReadKey();
+	if (iKeypressWatcher) iKeypressWatcher->Notify();
+	return result;
+	}
+
 EXPORT_C void CCommandBase::Printf(TRefByValue<const TDesC> aFmt, ...)
 	{
 	VA_LIST list;
@@ -3538,7 +3564,7 @@
 	// This makes them attached to the same I/O end-points (consoles, files, etc)
 	// as the passed in handles.
 	User::LeaveIfError(iStdin.Duplicate(aStdin));
-	User::LeaveIfError(iStdin.SetToForeground());
+	// iStdin.SetToForeground() moved to Run() so the child doesn't steal foreground until it actually runs
 	User::LeaveIfError(iStdout.Duplicate(aStdout));
 	User::LeaveIfError(iStderr.Duplicate(aStderr));
 
@@ -3579,6 +3605,7 @@
 
 EXPORT_C void RChildProcess::Run(TRequestStatus& aStatus)
 	{
+	iStdin.SetToForeground();
 	iProcess.Logon(aStatus);
 	if (aStatus != KRequestPending)
 		{
@@ -3808,8 +3835,46 @@
 	iReadHandle.CancelNotifyChange();
 	}
 
+//
+// CKeypressWatcher
 // 
 
+CKeypressWatcher::CKeypressWatcher(IoUtils::MCommandExtensionsV2& aCmd, RIoConsoleReadHandle& aReadHandle)
+	: CActive(CActive::EPriorityStandard), iCmd(aCmd), iReadHandle(aReadHandle)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+CKeypressWatcher::~CKeypressWatcher()
+	{
+	Cancel();
+	}
+
+void CKeypressWatcher::Notify()
+	{
+	iReadHandle.WaitForKey(iStatus);
+	SetActive();
+	}
+
+void CKeypressWatcher::RunL()
+	{
+	TUint code = iReadHandle.KeyCode();
+	TUint modifiers = iReadHandle.KeyModifiers();
+	Notify();
+	iCmd.KeyPressed(code, modifiers);
+	if (code == CTRL('c'))
+		{
+		iCmd.CtrlCPressed();
+		}
+	}
+
+void CKeypressWatcher::DoCancel()
+	{
+	iReadHandle.WaitForKeyCancel();
+	}
+
+//
+
 EXPORT_C TCommandExtensionVersion MCommandExtensionsV1::ExtensionVersion() const
 	{
 	return ECommandExtensionV1;
@@ -3824,3 +3889,18 @@
 	{
 	iExtension = aExtension;
 	}
+
+EXPORT_C TCommandExtensionVersion MCommandExtensionsV2::ExtensionVersion() const
+	{
+	return ECommandExtensionV2;
+	}
+
+EXPORT_C void MCommandExtensionsV2::KeyPressed(TUint /*aKeyCode*/, TUint /*aModifiers*/)
+	{
+	// Default is to do nothing
+	}
+
+EXPORT_C void MCommandExtensionsV2::CtrlCPressed()
+	{
+	// Default is to do nothing
+	}
--- a/libraries/iosrv/client/command_base.h	Wed Aug 25 22:17:52 2010 +0100
+++ b/libraries/iosrv/client/command_base.h	Thu Aug 26 00:49:35 2010 +0100
@@ -52,6 +52,20 @@
 		};
 
 	void Panic(TCmdBasePanic aReason);
+
+	NONSHARABLE_CLASS(CKeypressWatcher) : public CActive
+		{
+	public:
+		CKeypressWatcher(IoUtils::MCommandExtensionsV2& aCmd, RIoConsoleReadHandle& aReadHandle);
+		~CKeypressWatcher();
+		void Notify();
+	private:
+		virtual void RunL();
+		virtual void DoCancel();
+	private:
+		IoUtils::MCommandExtensionsV2& iCmd;
+		RIoConsoleReadHandle& iReadHandle;
+		};
 	}
 	
 #endif //__COMMAND_BASE_H__
--- a/libraries/iosrv/eabi/iocliu.def	Wed Aug 25 22:17:52 2010 +0100
+++ b/libraries/iosrv/eabi/iocliu.def	Thu Aug 26 00:49:35 2010 +0100
@@ -619,4 +619,10 @@
 	_ZNK7IoUtils20MCommandExtensionsV116ExtensionVersionEv @ 618 NONAME
 	_ZTIN7IoUtils20MCommandExtensionsV1E @ 619 NONAME
 	_ZTVN7IoUtils20MCommandExtensionsV1E @ 620 NONAME
+	_ZN7IoUtils12CCommandBase7ReadKeyEv @ 621 NONAME
+	_ZN7IoUtils20MCommandExtensionsV210KeyPressedEjj @ 622 NONAME
+	_ZN7IoUtils20MCommandExtensionsV212CtrlCPressedEv @ 623 NONAME
+	_ZNK7IoUtils20MCommandExtensionsV216ExtensionVersionEv @ 624 NONAME
+	_ZTIN7IoUtils20MCommandExtensionsV2E @ 625 NONAME
+	_ZTVN7IoUtils20MCommandExtensionsV2E @ 626 NONAME
 
--- a/libraries/iosrv/inc/ioutils.h	Wed Aug 25 22:17:52 2010 +0100
+++ b/libraries/iosrv/inc/ioutils.h	Thu Aug 26 00:49:35 2010 +0100
@@ -100,13 +100,14 @@
 	EEnumValueListAlreadySet = 50,
 	EEnumDescriptionListAlreadySet = 51,
 	EIncompleteArgumentOrOptionInitialization = 52,
+	EENotifyKeypressesSpecifiedWithoutExtensionBeingSet = 53,
 	};
 
 class CCommandBase;
 class CReaderChangeNotifier;
 class CCommandCompleter;
 class MLineReader;
-
+class CKeypressWatcher;
 
 class TOverflowTruncate : public TDes16Overflow
 	{
@@ -641,6 +642,7 @@
 enum TCommandExtensionVersion
 	{
 	ECommandExtensionV1 = 1,
+	ECommandExtensionV2 = 2,
 	};
 
 class MCommandExtensionsV1
@@ -652,6 +654,17 @@
 	IMPORT_C virtual const TDesC* StringifyError(TInt aError) const;
 	};
 
+class MCommandExtensionsV2 : public MCommandExtensionsV1
+	{
+public:
+	IMPORT_C virtual TCommandExtensionVersion ExtensionVersion() const; // Don't override this yourself!
+
+	IMPORT_C virtual void KeyPressed(TUint aKeyCode, TUint aModifiers);
+
+	// Override this and set the ECaptureCtrlC flag to get a callback for custom cleanup when ctrl-c is pressed
+	IMPORT_C virtual void CtrlCPressed();
+	};
+
 class CCommandBase : public CActive
 	{
 public:
@@ -661,8 +674,9 @@
 		ESharableIoSession		= 0x00000002,
 		EReportAllErrors		= 0x00000004,
 		ENotifyStdinChanges		= 0x00000008,
-		//EDeprecated			= 0x00000010,
-		ECompleteOnRunL			= 0x00000020,
+		ENotifyKeypresses		= 0x00000010,
+		ECompleteOnRunL			= 0x00000020, // Implies EManualComplete
+		ECaptureCtrlC			= 0x00000040, // Implies ENotifyKeypresses
 		};
 public:
 	IMPORT_C ~CCommandBase();
@@ -688,6 +702,7 @@
 	IMPORT_C CEnvironment& Env();
 	IMPORT_C const CEnvironment& Env() const;
 	IMPORT_C void ReadL(TDes& aData);
+	IMPORT_C TUint ReadKey();
 	IMPORT_C void Write(const TDesC& aData);
 	IMPORT_C void Printf(TRefByValue<const TDesC> aFmt, ...);
 	IMPORT_C void Printf(TRefByValue<const TDesC8> aFmt, ...);
@@ -808,10 +823,10 @@
 	TInt iCompletionReason;
 	CCommandInfoFile* iCif;
 	MCommandExtensionsV1* iExtension;
+	CKeypressWatcher* iKeypressWatcher;
 	void* iSpare1;
 	void* iSpare2;
 	void* iSpare3;
-	void* iSpare4;
 	};