merge from MCL
authorTom Sutcliffe <thomas.sutcliffe@accenture.com>
Thu, 26 Aug 2010 11:47:04 +0100
changeset 40 d9b3ed11f017
parent 39 e7ccdc7f7dbe (current diff)
parent 37 534b01198c2d (diff)
child 41 4a2ffd3562a3
merge from MCL
--- a/build/symtb92/platform.mmh	Wed Aug 25 18:03:34 2010 +0100
+++ b/build/symtb92/platform.mmh	Thu Aug 26 11:47:04 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 18:03:34 2010 +0100
+++ b/commands/sudo/sudo.cpp	Thu Aug 26 11:47:04 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 18:03:34 2010 +0100
+++ b/documentation/change_history.pod	Thu Aug 26 11:47:04 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/documentation/writing_fshell_commands.pod	Wed Aug 25 18:03:34 2010 +0100
+++ b/documentation/writing_fshell_commands.pod	Thu Aug 26 11:47:04 2010 +0100
@@ -15,164 +15,4 @@
 
 =head1 Writing fshell commands
 
-A quick guide to adding a new fshell command, covering the fshell build and configuration system, the internals of commands themselves and some background on the relevant concepts.
-
-=head1 Before you start
-
-=head2 Step 1: RTM!
-
-The first step is to make sure there isn't already a command that does what you need. It may seem obvious but there are quite a lot of commands and some have a lot of options and different functionality. Skim through the L<command list|fshell::commands> to be certain. For example you might note there's no 'bluetooth' command in fshell - however a check of the L<command list|fshell::commands> will reveal the L<btservices|fshell::commands::btservices> command which supports a number of Bluetooth-related operations.
-
-=head2 Check the todo list
-
-Check the todo list (I<TODO: figure out where the todo list is going to live!>). Someone else may have similar ideas or already be working on similar functionality.
-
-=head2 Decide whether you want to add it to fshell
-
-You're free to write whatever tools you like that use fshell and build on top of it. We (Accenture) have a number of specialised tools that use fshell but aren't part of the fshell package itself. You may however wish to contribute back to the fshell package; if so there are some additional constraints regarding where to put the source and the fshell build and configuration system (in addition to the Foundation Contribution guidelines http://developer.symbian.org/wiki/index.php/Contribution_Process ). You can ignore a lot of the below if you don't plan to add your command to the fshell package.
-
-=head1 Getting stuck in
-
-Once you've decided there isn't a suitable existing command, and no-one else is developing one, it's time to get started. Let's assume your new fshell command will be called C<qotd> and displays an insightful Quote Of The Day each time you run it. We're also assuming that you plan to integrate the command into the fshell package, thus will create the source code in the fshell\commands\qotd directory. (Please note we have no plans to actually add a qotd command - attributing the quotes and copyright is far too much like hard work!)
-
-If you are not contributing the command to the fshell package please put the source outside of the fshell tree.
-
-=head2 Built-in vs external
-
-(aka 'thread' vs 'process')
-
-There are two types of fshell command: ones built into fshell.exe itself (such as cd, export, source, etc) and ones that are implemented as standalone exes (such as listapps.exe, drvinfo.exe and so forth). Built-in commands are restricted as to what APIs they can use (so as to not introduce problematic dependancies), so we generally recommend that new commands are external ones that run in a separate process. Contact the fshell maintainers if you think you've a candidate for a new built-in command.
-
-Each external command lives in a separate directory \fshell\commands\<commandname>. Built in commands live in the \fshell\core tree.
-
-=head2 Using C<createsrc> to generate the boilerplate
-
-The createsrc command (installed in \epoc32\tools) is a great way to automate creating the skeleton of a new command. Run it with no arguments to be prompted for every option, for brevity the example below shortcuts it slightly and assumes you've already created the directory F<fshell\commands\qotd>. It also overrides the default copyright message, since you presumably won't want to assign copyright to Accenture!
-
-    M:\fshell\commands\qotd>
-    M:\fshell\commands\qotd>createsrc fshellcmd qotd -DCOPYRIGHT="Copyright (c) 2010 N. Seagoon. All Rights Reserved."
-    Enter short description of the command (for the .cif file) for qotd: Displays a randomly selected quote of the day.
-    M:\fshell\commands\qotd>
-    M:\fshell\commands\qotd>dir /B
-    bld.inf
-    qotd.cif
-    qotd.cpp
-    qotd.mmp
-
-At this point you have an fshell command that will compile and run - although it won't do very much yet.
-
-The C<.cif> file contains meta-data about the command - its description and what arguments and options it accepts. See L<here|cif_syntax> for more information on the CIF file format. The C<bld.inf> and C<.mmp> are the standard Symbian OS build files. The C<.cpp> is a minimalist implementation of your command, based on the information you passed to createsrc.
-
-=head2 CCommandBase
-
-As you'll see, the C<.cpp> file declares a single class CCmdQotd, which derives from C<IoUtils::CCommandBase> and implements a few derived functions. CCommandBase is the base class for all fshell commands. When your command is executed, fshell converts the command line arguments into member variables of your CCommandBase subclass and calls DoRunL(). (It figures out how to do this from the CIF file and the result of calling the OptionsL() and ArgumentsL() functions). The implementation of your command goes in DoRunL(). For now we will implement a very simple DoRunL that just prints a single string and exits:
-
-    void CCmdQotd::DoRunL()
-        {
-        Printf(_L("Quote goes here!\r\n"));
-        }
-
-If you build your command now, you'll see it in action:
-
-    M:\fshell\commands\qotd>sbs -c winscw_udeb
-    ...
-    M:\fshell\commands\qotd>fshell
-    ...
-    c:\>qotd
-    Quote goes here!
-    c:\>exit
-    M:\fshell\commands\qotd>
-
-Note how we've used the fshell bat file to start the winscw emulator inside the DOS command prompt. There's a lot of stuff going on behind the scenes to make this work, but it's a convenient way of testing commands. Normally you'd build the fshell tree (or any part of it) from one of the \fshell\build directories, and you wouldn't use the bld.inf that createsrc generated. I've skipped that for the moment for simplicity's sake.
-
-=head2 Adding options and arguments
-
-Fshell takes care of parsing command lines in a consistant manner, but you have to let it know what your command supports. Let's say that you want to give qotd a C<--shouty> option which, if specified, prints the quote in upper case. Since all options in fshell commands have a long form and a single-letter abbreviation, we'll choose C<-s>. Firstly we need to add a C<==option> section to the CIF file:
-
-    ==option bool s shouty
-
-    Displays the quote in UPPER CASE.
-
-Now we need to update the C++ code. Firstly, to add a TBool to CCmdQotd for fshell to fill in:
-
-     class CCmdQotd : public CCommandBase
-        {
-    ...
-     private:
-    -//TODO: arguments/options
-    +   TBool iShouty;    
-        };
-
-Then we need to associate the --shouty option with the iShouty member variable:
-
-    void CCmdQotd::OptionsL(RCommandOptionList& aOptions)
-        {
-        aOptions.AppendBoolL(iShouty, _L("shouty"));
-        }
-
-If you rebuild and run the help now, you'll see that the option is all configured:
-
-    c:\>qotd --help
-    SYNTAX
-
-        qotd [options]
-
-    OPTIONS
-
-         -s (--shouty)  Displays the quote in UPPER CASE.
-         -h (--help)    Display help.
-
-    DESCRIPTION
-
-    Displays a randomly selected quote of the day.
-
-    COPYRIGHT
-
-    Copyright (c) 2010 N. Seagoon. All Rights Reserved.
-
-    c:\>
-
-All that remains is to actually do something when --shouty is specified:
-
-    void CCmdQotd::DoRunL()
-        {
-        if (iShouty)
-            {
-            Printf(_L("QUOTE GOES HERE!\r\n"));
-            }
-        else
-            {
-            Printf(_L("Quote goes here!\r\n"));
-            }
-        }
-
-After rebuilding, you should be able to do:
-
-    c:\>qotd
-    Quote goes here!
-    c:\>
-    c:\>qotd --shouty
-    QUOTE GOES HERE!
-    c:\>
-    c:\>qotd -s
-    QUOTE GOES HERE!
-    c:\>
-
-Adding other types of option/argument is done in a similar way, see L<here|cif_syntax/CIF types and CCommandBase types> for the list of CIF types and their equivalent C++ types. The one thing to be aware of is that you are responsible for deleting any string or array arguments in the command's destructor, even though the fshell framework created them. This is mainly for legacy compatability reasons, it should probably be done automatically in ~CCommandBase().
-
-=head2 Integrating with fshell's build system
-
-I<If you're not writing a command that's going to be integrated into the fshell source tree, you can mostly ignore this section.>
-
-To make sure your new command gets built, add the mmp and any exports (eg the CIF) to \fshell\commands\group\bld.inf:
-
-    PRJ_EXPORTS
-    ..\qotd\qotd.cif            z:\resource\cif\fshell\qotd.cif
-    PRJ_MMPFILES
-    ..\qotd\qotd.mmp
-
-The fshell build system makes heavy use of preprocessor conditionals to make sure that the codebase builds on a wide variety of platforms. In this example qotd doesn't have any depenancies so the files can be added unconditionally to the bld.inf. If however you take the USB command as an example, you'll see that in the bld.inf it is surrounded by C<#ifdef FSHELL_CORE_SUPPORT_USB>. This in turn is defined in L<common.mmh|common_mmh> only if C<FSHELL_COMMS_SUPPORT> is available. In other words the USB command relies on comms support being available in the platform that's being build. (You may thing that's overkill but early baseport bringup regularly needs to build minimalist textshell ROMs without any form of comms support other than maybe a uart - this level of configurability means it's possible to include fshell on there too.)
-
-If your command can take advantage of a feature, but doesn't absolutely require it, you may wish to guard only sections of code with an appropriate C<FSHELL_xyz> macro. common.mmh can be included anywhere the preprocessor is used (source files, bld.inf, MMPs, resource files) so you can be as fine-grained as you like. For example fshell itself uses many C<#ifdef FSHELL_MEMORYACCESS_SUPPORT> blocks so that ps can give more detailed process listings if MemoryAccess is available, but falls back gracefully to less detailed info if it isn't.
-
-B<I<To be completed...>>
\ No newline at end of file
+This guide has moved to http://developer.symbian.org/wiki/index.php/Fshell/Writing_fshell_commands
--- a/libraries/iosrv/bwins/iocliu.def	Wed Aug 25 18:03:34 2010 +0100
+++ b/libraries/iosrv/bwins/iocliu.def	Thu Aug 26 11:47:04 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 18:03:34 2010 +0100
+++ b/libraries/iosrv/client/command_base.cpp	Thu Aug 26 11:47:04 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 18:03:34 2010 +0100
+++ b/libraries/iosrv/client/command_base.h	Thu Aug 26 11:47:04 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 18:03:34 2010 +0100
+++ b/libraries/iosrv/eabi/iocliu.def	Thu Aug 26 11:47:04 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 18:03:34 2010 +0100
+++ b/libraries/iosrv/inc/ioutils.h	Thu Aug 26 11:47:04 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;
 	};