commands/fzip/fzip.cpp
author Tom Sutcliffe <thomas.sutcliffe@accenture.com>
Wed, 23 Jun 2010 15:52:26 +0100
changeset 0 7f656887cf89
child 31 d0e1c40de386
permissions -rw-r--r--
First submission to Symbian Foundation staging server.

// fzip.cpp
// 
// Copyright (c) 2008 - 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 <zipfile.h>
#include <ezgzip.h>
#include "fzip.h"

_LIT(KGzExtension, ".gz");

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

CCmdZip::~CCmdZip()
	{
	if (iFileToZip.Count() > 0)
		iFileToZip.Close();
	}
	
CCmdZip::CCmdZip() : CCommandBase(CCommandBase::EManualComplete)
	{
	}

const TDesC& CCmdZip::Name() const
	{	
	_LIT(KName, "fzip");
	return KName;
	}

void CCmdZip::DoRunL()
	{
	FsL();
	if (iUnzip)
		{
		if (!iOptions.IsPresent(&iUnzipPath))
			{
			iUnzipPath = Env().Pwd();
			}

		if (iVerbose)
			{
			// command-line sanity checks
			if (iFileToZip.Count() > 0)
				{
				PrintWarning(_L("Ignoring \'-f\' file option."));
				}
			if (iRecurse)
				{
				PrintWarning(_L("Ignoring \'-r\' recurse option."));
				}
			}
		TRAPL(ExpandArchiveL(), _L("Couldn't expand archive"));
		}
	else
		{
		if (iVerbose)
			{
			// command-line sanity checks
			if (iUnzipPath.Length() > 0)
				{
				PrintWarning(_L("Ignoring '-d' directory option."));	
				}
			}
		if (iFileToZip.Count() == 0)
			{
			PrintError(KErrArgument, _L("Use '-f' to specify source files."));
			User::Leave(KErrArgument);
			}
		TRAPD(err, CreateArchiveL());
		if (err != KErrNone)
			{
			PrintError(err, _L("Couldn't create archive"));
			Fs().Delete(iArchive); // ignore error
			User::Leave(err);
			}
		}
	if (iVerbose)
		{
		Printf(_L("Done\r\n"));	
		}
	Complete(KErrNone);
	}
	
void CCmdZip::ArgumentsL(RCommandArgumentList& aArguments)
	{
	_LIT(KArg1, "archive");
	aArguments.AppendFileNameL(iArchive, KArg1);
	}

void CCmdZip::OptionsL(RCommandOptionList& aOptions)
	{
	_LIT(KOptVerbose, "verbose");
	aOptions.AppendBoolL(iVerbose, KOptVerbose);

	_LIT(KOptUnzip, "unzip");
	aOptions.AppendBoolL(iUnzip, KOptUnzip);

	_LIT(KOptDirectory, "directory");
	aOptions.AppendFileNameL(iUnzipPath, KOptDirectory);

	_LIT(KOptRecurse, "recurse");
	aOptions.AppendBoolL(iRecurse, KOptRecurse);

	_LIT(KOptSource, "file");
	aOptions.AppendFileNameL(iFileToZip, KOptSource);

	_LIT(KOptCompressionType, "compression-type");
	aOptions.AppendEnumL((TInt&)iCompressionType, KOptCompressionType);
	}


//
// COMPRESSION FUNCTIONS
//

//
// CCmdZip::CreateArchiveL
// determine which zip format to use and go ahead & create the archive
//
void CCmdZip::CreateArchiveL()
	{
	if (iCompressionType == EGZip)
		{
		CreateGzArchiveL();
		}
	else
		{
		CreateZipArchiveL();
		}
	}

//
// CCmdZip::CreateGzArchiveL
// create an archive, zipping up all the specified files
//
void CCmdZip::CreateGzArchiveL()
	{
	if (iRecurse)
		{
		LeaveIfErr(KErrArgument, _L("GNU Zip format does not support recursion"));
		}

	if (iFileToZip.Count() > 1)
		{
		LeaveIfErr(KErrArgument, _L("GNU Zip format can only handle a single file"));
		}

	if (iArchive.Length() == 0)
		{
		iArchive = iFileToZip[0];
		iArchive.Append(KGzExtension);
		}

	RFile input;
	if (iVerbose)
		{
		Printf(_L("Creating '%S'\r\n"), &iArchive);
		}

	// open the input file
	User::LeaveIfError(input.Open(Fs(), iFileToZip[0], EFileStream | EFileRead | EFileShareAny));
	CleanupClosePushL(input);

	CEZFileToGZip* zip = CEZFileToGZip::NewLC(Fs(), iArchive, input);
	while (zip->DeflateL())
		{
		// do nothing
		}
	if (iVerbose)
		{
		Printf(_L("Deflating '%S'\r\n"), &iFileToZip[0]);
		}

	CleanupStack::PopAndDestroy(2); // zip, input
	}

//
// CCmdZip::CreateZipArchiveL
// zip format archive creation
//
void CCmdZip::CreateZipArchiveL()
	{
	CZipItUp* zipArchive = CZipItUp::NewLC(Fs(), iArchive);
	for (TInt ii = 0 ; ii < iFileToZip.Count() ; ii++)
		{
		TFileName2& fileName = iFileToZip[ii];
		fileName.SetTypeL(Fs());
		AddFileL(*zipArchive, fileName);
		}
	zipArchive->CreateZipL();
	if (iVerbose)
		{
		Printf(_L("Created '%S'\r\n"), &iArchive);
		}
	CleanupStack::PopAndDestroy(); // zipArchive 
	}

//
// CCmdZip::AddFileL
// examines a file for wildcards, directories/recursion etc
// if it finds an actual file, it'll add it to the zip archive
// recursive function
//
void CCmdZip::AddFileL(CZipItUp& aZipArchive, const TFileName2& aFile)
	{
	if (aFile.IsDir())
		{
		CDir* dir;
		LeaveIfErr(Fs().GetDir(aFile, KEntryAttMatchMask, EDirsLast, dir), _L("Unable to read directory '%S'"), &aFile);
		CleanupStack::PushL(dir);
		for (TInt ii = 0 ; ii < dir->Count() ; ii++)
			{
			const TEntry& entry = (*dir)[ii];
			TFileName2* newFile = new(ELeave) TFileName2(entry.iName);
			CleanupStack::PushL(newFile);
			newFile->MakeAbsoluteL(aFile.DriveAndPath());
			newFile->SetTypeL(Fs());
			if (newFile->IsDir())
				{
				if (iRecurse)
					{
					AddFileL(aZipArchive, *newFile);
					}
				}
			else
				{
				AddFileL(aZipArchive, *newFile);
				}
			CleanupStack::PopAndDestroy(newFile);
			}
		CleanupStack::PopAndDestroy(dir);
		}
	else
		{
		if (iVerbose)
			{
			Printf(_L("Adding '%S'\r\n"), &aFile);
			}
		aZipArchive.AddFileL(aFile);
		}
	}


//
// DECOMPRESSION FUNCTIONS
//

//
// CCmdZip::ExpandArchiveL
// determine which zip format to use and go ahead & expand the archive
//
void CCmdZip::ExpandArchiveL()
	{
	if (iCompressionType == EGZip)
		{
		ExpandGzArchiveL();
		}
	else
		{
		ExpandZipArchiveL();
		}
	}

//
// CCmdZip::ExpandGzArchiveL
// unzip an existing gzip compressed file
//
void CCmdZip::ExpandGzArchiveL()
	{
	// open the destination file, determine where it goes
	RFile newFile;
	TFileName2 dest(iUnzipPath);
	if (iVerbose)
		{
		Printf(_L("Opening\t\t\'%S\'\r\n"), &iArchive);
		}
	if (iArchive.Ext().CompareF(KGzExtension) != 0)
		{
		LeaveIfErr(KErrArgument, _L("Compressed file must have '.gz' extension."));
		}
	dest.AppendComponentL(iArchive.Name());
	TInt err = Fs().MkDirAll(dest);
	if ((err != KErrNone) && (err != KErrAlreadyExists))
		{
		LeaveIfErr(err, _L("Couldn't create path '%S'"), &dest);
		}
	User::LeaveIfError(newFile.Replace(Fs(), dest, EFileStream | EFileRead | EFileShareAny));
	CleanupClosePushL(newFile);

	// inflate the compressed file
	CEZGZipToFile* zip = CEZGZipToFile::NewLC(Fs(), iArchive, newFile);
	while (zip->InflateL())
		{
		// do nothing
		}
	if (iVerbose)
		{
		Printf(_L("Inflating '%S'\r\n"), &dest);
		}
	CleanupStack::PopAndDestroy(2); // zip, newFile
	}

//
// CCmdZip::ExpandZipArchiveL
// Unzip an existing archive iterating through each member file contained within the archive
//
void CCmdZip::ExpandZipArchiveL()
	{
	if (iVerbose)
		{
		Printf(_L("Opening\t\t\'%S\'\r\n"), &iArchive);
		}
	CZipFile* zip = CZipFile::NewL(Fs(), iArchive);
	CleanupStack::PushL(zip);
	CZipFileMemberIterator* zipIterator = zip->GetMembersL();
	CleanupStack::PushL(zipIterator);
	CZipFileMember* zipMember = zipIterator->NextL();
	while (zipMember)
		{
		CleanupStack::PushL(zipMember);
		ExtractZipFileL(*zip, *zipMember);
		CleanupStack::PopAndDestroy(zipMember);
		zipMember = zipIterator->NextL();
		}
	CleanupStack::PopAndDestroy(2); // zipIterator, zip
	}
	
//
// CCmdZip::ExtractZipFileL
// extracts a single file from within the zip archive
//
void CCmdZip::ExtractZipFileL(CZipFile& aZip, const CZipFileMember& aMember)
	{
	// prep. the stream
	RZipFileMemberReaderStream* readStream;
	aZip.GetInputStreamL(&aMember, readStream);
	CleanupStack::PushL(readStream);
	// prep. the destination file. 
	// note if iUnzipPath is not specified, it'll stuff the extracted file in the current directory from which fzip.exe runs
	RFile newFile;
	TFileName2 dest(iUnzipPath);
	dest.AppendComponentL(*aMember.Name());
	TInt err = Fs().MkDirAll(dest);
	if ((err != KErrNone) && (err != KErrAlreadyExists))
		{
		User::Leave(err);
		}
	User::LeaveIfError(newFile.Replace(Fs(), dest, EFileShareExclusive));
	CleanupClosePushL(newFile);
	if (iVerbose)
		{
		Printf(_L("Inflating '%S'\r\n\tcrc: 0x%x\r\n\tcompressed size: %d\r\n\tuncompressed size: %d\r\n"), &dest, aMember.CRC32(), aMember.CompressedSize(), aMember.UncompressedSize());
		}
		
	// stream from the zip archive member into the destination file
	TInt bytesRead = 0;
	TInt length = KDefaultZipBufferLength; // 32Kb
	if (aMember.UncompressedSize() < KDefaultZipBufferLength)
		{
		length = aMember.UncompressedSize();
		}
	HBufC8* data = HBufC8::NewLC(length);
	TPtr8 ptr = data->Des();
	do
		{
		User::LeaveIfError(readStream->Read(ptr, length));
		User::LeaveIfError(newFile.Write(ptr));
		bytesRead += length;
		if ((aMember.UncompressedSize() - bytesRead) < KDefaultZipBufferLength)
			{
			length = aMember.UncompressedSize() - bytesRead;
			}
		} while (length > 0);
		
	// cleanup
	CleanupStack::PopAndDestroy(3); // data, newfile, readstream
	}

	
EXE_BOILER_PLATE(CCmdZip)