commands/localdrive/localdrive.cpp
author Tom Sutcliffe <thomas.sutcliffe@accenture.com>
Thu, 26 Aug 2010 00:49:35 +0100
changeset 37 534b01198c2d
parent 26 5d370dafea69
child 66 2a78c4ff2eab
permissions -rw-r--r--
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.

// localdrive.cpp
// 
// Copyright (c) 2010 Accenture. All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
// 
// Initial Contributors:
// Accenture - Initial contribution
//

#include <fshell/ioutils.h>
#include <fshell/common.mmh>
#include <d32locd.h>

using namespace IoUtils;

class CCmdLocalDrive : public CCommandBase
	{
public:
	static CCommandBase* NewLC();
	~CCmdLocalDrive();
private:
	CCmdLocalDrive();
	TInt OpenL(TInt aDrive, TBool aLeaveOnConnectErr);
private: // From CCommandBase.
	virtual const TDesC& Name() const;
	virtual void DoRunL();
	virtual void ArgumentsL(RCommandArgumentList& aArguments);
	virtual void OptionsL(RCommandOptionList& aOptions);
private:
	enum TCmd
		{
		EList,
		ERead,
		EWrite,
		};
	TInt iDriveNum;
	TCmd iCmd;
	TBool iSizesInBytes;
	TFileName2 iFile;

	RLocalDrive iDrive;
	TLocalDriveCapsV2 iCaps;
	};

EXE_BOILER_PLATE(CCmdLocalDrive)

CCommandBase* CCmdLocalDrive::NewLC()
	{
	CCmdLocalDrive* self = new(ELeave) CCmdLocalDrive();
	CleanupStack::PushL(self);
	self->BaseConstructL();
	return self;
	}

CCmdLocalDrive::~CCmdLocalDrive()
	{
	iDrive.Close();
	}

CCmdLocalDrive::CCmdLocalDrive()
	{
	}

const TDesC& CCmdLocalDrive::Name() const
	{
	_LIT(KName, "localdrive");	
	return KName;
	}

void CCmdLocalDrive::ArgumentsL(RCommandArgumentList& aArguments)
	{
	aArguments.AppendEnumL((TInt&)iCmd, _L("operation"));
	aArguments.AppendIntL(iDriveNum, _L("drive"));
	}

void CCmdLocalDrive::OptionsL(RCommandOptionList& aOptions)
	{
	aOptions.AppendBoolL(iSizesInBytes, _L("bytes"));
	aOptions.AppendFileNameL(iFile, _L("file"));
	}

// More GCC issues (see qresources3\src\utils.cpp). Happily the compiler is happier with SLitC and DESC than it is with TLitC and _LIT
#include <fshell/descriptorutils.h>
#define CASE_LIT(x) case x: { static const LtkUtils::SLitC KName = DESC(#x); return &KName; }

const TDesC* MediaTypeDesc(TMediaType aType)
	{
	enum TEnumsNotIn91
		{
		EMediaRotatingMedia = EMediaNANDFlash + 1,
		};
	switch (aType)
		{
		CASE_LIT(EMediaNotPresent)
		CASE_LIT(EMediaUnknown)
		CASE_LIT(EMediaFloppy)
		CASE_LIT(EMediaHardDisk)
		CASE_LIT(EMediaCdRom)
		CASE_LIT(EMediaRam)
		CASE_LIT(EMediaFlash)
		CASE_LIT(EMediaRom)
		CASE_LIT(EMediaRemote)
		CASE_LIT(EMediaNANDFlash)
		CASE_LIT(EMediaRotatingMedia)
		default:
			{
			_LIT(KUnknown, "?");
			return &KUnknown;
			}
		}
	}

void CCmdLocalDrive::DoRunL()
	{
	if (iCmd == EList)
		{
		for (TInt i = 0; i < KMaxLocalDrives; i++)
			{
			if (iArguments.IsPresent(&iDriveNum) && i != iDriveNum) continue;
			TInt err = OpenL(i, EFalse);
			if (err == KErrNone)
				{
				CTextBuffer* buf = CTextBuffer::NewLC(256);
				buf->AppendFormatL(_L("Found drive %d partitionType=0x%x mediatype=%S(%d) size="), i, iCaps.iPartitionType, MediaTypeDesc(iCaps.iType), iCaps.iType);
				if (iSizesInBytes)
					{
					buf->AppendFormatL(_L("%Ld"), iCaps.iSize);
					}
				else
					{
					buf->AppendHumanReadableSizeL(iCaps.iSize, EUnaligned);
					}
				if (iCaps.iType == EMediaFlash)
					{
					// NOR flash
					buf->AppendFormatL(_L(" eraseblocksize=%d"), iCaps.iEraseBlockSize);
					}

				buf->AppendL(_L("\r\n"));
				buf->Write(Stdout());
				CleanupStack::PopAndDestroy(buf);
				}
			iDrive.Close();
			}
		}
	else if (iCmd == ERead)
		{
		if (!iArguments.IsPresent(&iDriveNum)) LeaveIfErr(KErrArgument, _L("Must supply <drive> argument for the read command"));
		OpenL(iDriveNum, ETrue);
		RFile file;
		CleanupClosePushL(file);
		const TInt KBufSize = 32*1024;
		RBuf8 buf;
		CleanupClosePushL(buf);
		if (iFile.Length())
			{
			LeaveIfErr(file.Create(FsL(), iFile, EFileWrite), _L("Couldn't create file %S"), &iFile);
			buf.CreateL(KBufSize);
			}
		else
			{
			LeaveIfErr(Stdout().SetMode(RIoReadWriteHandle::EBinary), _L("Unable to set stdout to binary mode"));
			buf.CreateL(KBufSize*2);
			}

		TInt64 bytesRead = 0;
		while (bytesRead < iCaps.iSize)
			{
			buf.Zero();
			LeaveIfErr(iDrive.Read(bytesRead, KBufSize, buf), _L("Failed to read %Ld+%d bytes from partition"), bytesRead, KBufSize);
			bytesRead += buf.Length();
			if (file.SubSessionHandle())
				{
				LeaveIfErr(file.Write(buf), _L("Couldn't write %d bytes to file"), buf.Length());
				}
			else
				{
				Stdout().Write(buf.Expand());
				}
			}
		CleanupStack::PopAndDestroy(2, &file); // buf, file
		}
	else if (iCmd == EWrite)
		{
		if (!iArguments.IsPresent(&iDriveNum)) LeaveIfErr(KErrArgument, _L("Must supply <drive> argument for the write command"));
		if (!iOptions.IsPresent(&iFile)) LeaveIfErr(KErrArgument, _L("Must supply file name argument for the write command, using --file"));

		OpenL(iDriveNum, ETrue);
		RFile file;
		CleanupClosePushL(file);
		const TInt KBufSize = 32*1024;
		RBuf8 buf;
		CleanupClosePushL(buf);
		LeaveIfErr(file.Open(FsL(), iFile, EFileRead), _L("Couldn't open file %S"), &iFile);
		TInt fileSize;
		LeaveIfErr(file.Size(fileSize), _L("Couldn't read file size"));
		if (fileSize != iCaps.iSize) LeaveIfErr(KErrNotReady, _L("File size (%d) does not match partition size (%d)"), fileSize, (TInt)iCaps.iSize);

		buf.CreateL(KBufSize);

		TInt64 bytesRead = 0;
		while (bytesRead < fileSize)
			{
			buf.Zero();
			LeaveIfErr(file.Read(buf), _L("Failed to read file"));
			LeaveIfErr(iDrive.Write(bytesRead, buf), _L("Couldn't write %d bytes to flash at partition offset %Ld"), buf.Length(), bytesRead);
			bytesRead += buf.Length();
			}
		CleanupStack::PopAndDestroy(2, &file); // buf, file
		}
	else
		{
		LeaveIfErr(KErrArgument, _L("Unrecognised operation %d"), iCmd);
		}
	}

TInt CCmdLocalDrive::OpenL(TInt aDrive, TBool aLeaveOnConnectErr)
	{
	if (aDrive >= KMaxLocalDrives) LeaveIfErr(KErrArgument, _L("drive number %d is outside of KMaxLocalDrives (%d)"), aDrive, KMaxLocalDrives);

	TBool changed; // What is this for?
	TInt err = iDrive.Connect(aDrive, changed);
	if (err == KErrNone)
		{
		TPckg<TLocalDriveCapsV2> capsBuf(iCaps);
		LeaveIfErr(iDrive.Caps(capsBuf), _L("Opened drive %d but couldn't read caps"), aDrive);
		}
	else if (aLeaveOnConnectErr)
		{
		LeaveIfErr(err, _L("Couldn't open drive %d"), aDrive);
		}
	return err;
	}