// EIKFUTIL.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//
 
#include <e32hal.h>
#include <f32file.h>
#include <barsread.h>
#include <apparc.h>
#include <eikenv.h> 
#include <eikfutil.h>
#include <eikon.rsg>


const TInt KMaxFileNameLen=KMaxFileName;


class CEikDir : public CDir
	{
public:
	void RemoveSystem();
	TInt SortByTable(CEikFileSortTable* aTable);
	};


LOCAL_D void ReportErrorAndLeaveL(TInt aErrorCode)
	{
	// reports the error to the user (in some way) and always leaves
	if (aErrorCode == KErrNone)
		return;
	if ((aErrorCode == KErrNotReady) || (aErrorCode == KErrCorrupt))
		{
		CEikonEnv* env = CEikonEnv::Static();
		if (aErrorCode == KErrNotReady)
			env->LeaveWithInfoMsg(R_EIK_TBUF_DISK_NOT_PRESENT);
		else 
			env->LeaveWithInfoMsg(R_EIK_TBUF_CANNOT_BE_READ);
		}
	else
		User::Leave(aErrorCode);
	}


EXPORT_C TBool EikFileUtils::PathExists(const TDesC& aPath) 
	{ 
	// returns ETrue if the specified absolute path is valid
/*
	A valid path must:
		- fully specify an existing directory
		- end in a backslash
		- not specify a file
		- specify a drive
*/
	RFs& fs = CEikonEnv::Static()->FsSession();
	TParse parse;
	TInt retcode;
	retcode = parse.Set(aPath, NULL, NULL);
	if (retcode != KErrNone)
		return EFalse;
	if ((! parse.DrivePresent()) || (parse.NameOrExtPresent()))
		return EFalse;
	if (parse.Path().Length() == 0)
		return EFalse;
	TFileName dirName = parse.DriveAndPath();
	if ((dirName.Length() + 1) <= KMaxFileNameLen)
		dirName.Append(_L("*"));
	else
		return(EFalse);
    RDir dir;
    retcode = dir.Open(fs,dirName,0);
	if (retcode == KErrNone)
		dir.Close();
	return (retcode == KErrNone);
	}

EXPORT_C TInt EikFileUtils::IsFolder(const TDesC& aFullName, TBool& aIsFolder)
	{
	TParsePtrC parse(aFullName);
	if ((parse.DriveAndPath().Length() == 3) && (aFullName.Length() == 3))	
		{
		aIsFolder = ETrue;
		return(KErrNone);
		}
	TEntry entry;
	TInt retcode = CEikonEnv::Static()->FsSession().Entry(aFullName, entry);
	if (retcode == KErrNone)
		aIsFolder = (entry.iAtt & KEntryAttDir);
	return(retcode);
	}

EXPORT_C TBool EikFileUtils::FolderExists(const TDesC& aFolderName)
	{
    RDir dir;
    TInt retcode = dir.Open(CEikonEnv::Static()->FsSession(), aFolderName, 0);
	if (retcode == KErrNone)
		dir.Close();
	return ((retcode != KErrBadName) && (retcode != KErrPathNotFound) && (retcode != KErrNotFound));
	}

#pragma warning( disable : 4706 )

EXPORT_C TFileName EikFileUtils::FolderNameFromFullName(const TDesC& aFullName) 
	{
	TParsePtrC parse(aFullName);
	TFileName folderName = parse.Path();
	if (folderName != _L("\\"))
		{
		TInt len = folderName.Length();
		TInt pos = --len;
		while (--pos)
			if (folderName.Mid(pos, 1)==_L("\\"))
				break;
		folderName.Delete(len, 1);
		folderName.Delete(0, pos+1);
		}
	return(folderName);
	}

EXPORT_C TFileName EikFileUtils::DriveAndPathFromFullName(const TDesC& aFullName) 
	{
	TParsePtrC parse(aFullName);
	return (parse.DriveAndPath());
	}

EXPORT_C TFileName EikFileUtils::RootFolderPath(const TBuf<1> aDriveLetter)
	{
	TFileName rootFolderPath = aDriveLetter;
	rootFolderPath.Append(_L(":\\"));
	return rootFolderPath;
	}

EXPORT_C void EikFileUtils::AbbreviateFileName(const TFileName& aOriginalFileName, TDes& aAbbreviatedFileName)
	{
	TInt maxWidthInChars = aAbbreviatedFileName.MaxLength();
	if (aOriginalFileName.Length() <= maxWidthInChars)
		{
		aAbbreviatedFileName = aOriginalFileName;
		return;
		}
	TChar ellipsis(133);
	--maxWidthInChars;  // since the ellipsis will be the first char in aAbbreviatedFileName
	aAbbreviatedFileName.Zero();
	aAbbreviatedFileName.Append(ellipsis);
	aAbbreviatedFileName.Append(aOriginalFileName.Mid((aOriginalFileName.Length() - 1) - maxWidthInChars + 1, maxWidthInChars));
	}

EXPORT_C TFileName EikFileUtils::AbbreviatePath(TDesC& aPathName, const CFont& aFont, TInt aMaxWidthInPixels)
	{
	TInt widthOfOriginalText = aFont.TextWidthInPixels(aPathName);
	if (widthOfOriginalText <= aMaxWidthInPixels)
		return(TFileName(aPathName));
	TInt availableSpaceInPixels = aMaxWidthInPixels;
	TChar ellipsis(133);
	TBuf<1> temp;
	temp.Append(ellipsis);
	TInt widthOfEllipsis = aFont.TextWidthInPixels(temp);
	availableSpaceInPixels -= widthOfEllipsis;   // since the ellipsis will be the first char in the abbreviated pathname
	TFileName abbreviatedPath;
	abbreviatedPath.Append(ellipsis);
	TInt i = aPathName.Length() - 1;
	TInt widthOfCurrentChar = 0;
	while ((availableSpaceInPixels > 0) && (i >= 0))
		{
		temp[0] = aPathName[i];
		widthOfCurrentChar = aFont.TextWidthInPixels(temp);
		if (widthOfCurrentChar <= availableSpaceInPixels)
			{
			abbreviatedPath.Insert(1, temp);
			availableSpaceInPixels -= widthOfCurrentChar;
			--i;
			}
		else
			break;
		}
	return(abbreviatedPath);
	}

EXPORT_C TBool EikFileUtils::UidTypeMatches(const TUidType& aFileUid, const TUidType& aMatchUid)
	{
	for (TInt i=0; i<KMaxCheckedUid; i++)
		{
		if (aMatchUid[i] == KNullUid)
			continue;
		if (aMatchUid[i] != aFileUid[i])
			return(EFalse);
		}
	return(ETrue);
	}

EXPORT_C TInt EikFileUtils::Parse(const TDesC& aName)
	{ // keeps a TParse on the stack for the minimum time possible
	TParse parse;
	return parse.Set(aName,NULL,NULL);
	}

EXPORT_C TFileName EikFileUtils::ValidateFolderNameTypedByUserL(const TDesC& aFolderNameTypedByUser, const TDesC& aCurrentPath)
	{
	CEikonEnv* env = CEikonEnv::Static();

	if (aFolderNameTypedByUser.Length() == 0)
		env->LeaveWithInfoMsg(R_EIK_TBUF_NO_FOLDERNAME_SPECIFIED);

	TParse* targetParse = new(ELeave) TParse;
	CleanupStack::PushL(targetParse);

	TInt retcode = targetParse->Set(aFolderNameTypedByUser, NULL, NULL);
	User::LeaveIfError(retcode);
	if (targetParse->DrivePresent() || targetParse->PathPresent())
		env->LeaveWithInfoMsg(R_EIK_TBUF_INVALID_FOLDER_NAME);

	if (!(env->FsSession().IsValidName(aFolderNameTypedByUser)))
		env->LeaveWithInfoMsg(R_EIK_TBUF_INVALID_FOLDER_NAME);

	if ((aCurrentPath.Length() + aFolderNameTypedByUser.Length() + 1) > KMaxFileNameLen)
		env->LeaveWithInfoMsg(R_EIK_TBUF_FOLDERNAME_TOO_LONG);  

	TFileName newFolderFullName = aCurrentPath;

	if ((newFolderFullName.Length() + aFolderNameTypedByUser.Length() + 1) <= KMaxFileNameLen)
		{
		newFolderFullName.Append(aFolderNameTypedByUser);
		newFolderFullName.Append(_L("\\"));
		}
	else
		ReportErrorAndLeaveL(KErrOverflow);

	retcode = targetParse->Set(newFolderFullName, NULL, NULL);
	if (retcode != KErrNone)
		env->LeaveWithInfoMsg(R_EIK_TBUF_INVALID_FOLDER_NAME);
	
	CleanupStack::PopAndDestroy();

	return(newFolderFullName);
	}

EXPORT_C TInt EikFileUtils::CopyFile(const TDesC& aSourceFullName, const TDesC& aTargetFullName, TUint aSwitch)
	{
	CFileMan* fileMan = NULL;
	TInt retcode = KErrNone;
	TRAP(retcode, (fileMan = CFileMan::NewL(CEikonEnv::Static()->FsSession())));
	if (retcode != KErrNone)
		return(retcode);
	CleanupStack::PushL(fileMan);
	retcode = fileMan->Copy(aSourceFullName, aTargetFullName, aSwitch);
	CleanupStack::PopAndDestroy();
	return(retcode);
	}

EXPORT_C TInt EikFileUtils::DeleteFile(const TDesC& aSourceFullName, TUint aSwitch)
	{
	CFileMan* fileMan = NULL;
	TInt retcode = KErrNone;
	TRAP(retcode, (fileMan = CFileMan::NewL(CEikonEnv::Static()->FsSession())));
	if (retcode != KErrNone)
		return(retcode);
	CleanupStack::PushL(fileMan);
	retcode = fileMan->Delete(aSourceFullName, aSwitch);
	CleanupStack::PopAndDestroy();
	return(retcode);
	}

EXPORT_C TInt EikFileUtils::RenameFile(const TDesC& aOldFullName, const TDesC& aNewFullName, TUint aSwitch)
	{
	CFileMan* fileMan = NULL;
	TInt retcode = KErrNone;
	TRAP(retcode, (fileMan = CFileMan::NewL(CEikonEnv::Static()->FsSession())));
	if (retcode != KErrNone)
		return(retcode);
	CleanupStack::PushL(fileMan);
	retcode = fileMan->Rename(aOldFullName, aNewFullName, aSwitch);
	CleanupStack::PopAndDestroy();
	return(retcode);
	}

EXPORT_C TInt EikFileUtils::CheckWhetherFullNameRefersToFolder(const TDesC& aFullName, TBool& aIsFolder)
	{
	aIsFolder = EFalse;
	TInt retcode = EikFileUtils::Parse(aFullName);
	if (retcode != KErrNone)
		return(retcode);
	TParsePtrC parse(aFullName);
	if (! parse.NameOrExtPresent())
		aIsFolder = ETrue;
	return(KErrNone);
	}

EXPORT_C TInt EikFileUtils::MostSignificantPartOfFullName(const TDesC& aFullName, TFileName& aMostSignificantPart)
	{
	TBool entryIsAFolder;
	TInt retcode = CheckWhetherFullNameRefersToFolder(aFullName, entryIsAFolder);
	if (retcode != KErrNone)
		return(retcode);
	if (entryIsAFolder)
		{
		aMostSignificantPart = EikFileUtils::FolderNameFromFullName(aFullName);
		return (KErrNone);
		}
	// assume aFullName refers to a file
	TParsePtrC parse(aFullName);
	aMostSignificantPart = parse.NameAndExt();
	return(KErrNone);
	}

EXPORT_C TInt EikFileUtils::CheckFolder(const TDesC& aFolderName)
	{
    RDir dir;
    TInt retcode = dir.Open(CEikonEnv::Static()->FsSession(), aFolderName, 0);
	if (retcode == KErrNone)
		dir.Close();
	return (retcode);
	}

EXPORT_C TInt EikFileUtils::DiskIsReadOnly(const TDesC& aFullName, TBool& aIsReadOnly)
	{
	User::LeaveIfError(EikFileUtils::Parse(aFullName));
	TParsePtrC parse(aFullName);
	TBuf<1> drive=parse.Drive().Left(1);
	TChar driveLetter=drive[0];
	TInt driveId=0;
	TInt retcode=RFs::CharToDrive(driveLetter,driveId);
	if (retcode!=KErrNone)
		return retcode;
	TVolumeInfo volInfo;
	retcode=CEikonEnv::Static()->FsSession().Volume(volInfo,driveId);
	if (retcode!=KErrNone)
		return retcode;
	aIsReadOnly=(volInfo.iDrive.iMediaAtt&KMediaAttWriteProtected || volInfo.iDrive.iType==EMediaRom);
	return KErrNone;
	}

EXPORT_C void EikFileUtils::UpdateDiskListL(const RFs& aFs,CDesCArray& aArray,TBool aIncludeRom,TDriveNumber aDriveNumber)
// inserts a list of all present drives in aArray
	{ // static
	aArray.Reset();
	TDriveList driveList;
	User::LeaveIfError(aFs.DriveList(driveList));
	for (TInt ii=0;ii<KMaxDrives;ii++)
		{
		if (driveList[ii] || ii==aDriveNumber)
			{
			TVolumeInfo vInfo;
			const TInt err=aFs.Volume(vInfo,TDriveUnit(ii));
			if (err==KErrNone || ii==aDriveNumber || EikFileUtils::IsFirstDriveForSocket(TDriveUnit(ii)))
				{
				if (ii==EDriveZ && vInfo.iDrive.iDriveAtt&KDriveAttRom && !aIncludeRom)
					continue;
				TChar drive;
				User::LeaveIfError(aFs.DriveToChar(ii,drive));
				drive.UpperCase();
				TBuf<1> buf;
				buf.Append(drive);
				aArray.AppendL(buf);
				}
			}
		}
	}

EXPORT_C TBool EikFileUtils::IsFirstDriveForSocket(TDriveUnit aDriveUnit)
	{ // static
	TDriveInfoV1Buf buf;
	UserHal::DriveInfo(buf);
	return ((aDriveUnit-EDriveC)<=buf().iTotalSockets);
	}

EXPORT_C void EikFileUtils::RemoveSystemDirectory(CDir& aDir)
	{ // static
	STATIC_CAST(CEikDir&,aDir).RemoveSystem();
	}

EXPORT_C TInt EikFileUtils::SortByTable(CDir& aDir,CEikFileSortTable* aTable)
	{ // static
	return STATIC_CAST(CEikDir&,aDir).SortByTable(aTable);
	}

//
// class CEikFileSortTable
// 

const TInt KEikFileSortUidBase=0xff00000;
const TInt KEikFileSortUidLimit=0xfff0000;

EXPORT_C CEikFileSortTable::CEikFileSortTable()
	: CArrayFixFlat<TUid>(EArrayGranularity)
	{}

EXPORT_C void CEikFileSortTable::ConstructFromResourceL(TResourceReader& aReader)
	{
	const TInt count=aReader.ReadInt16();
	for (TInt i=0;i<count;i++)
		AppendL(TUid::Uid(aReader.ReadInt32()));
	}

//
// class CEikDir
//

#define KSystemDirName _L("System") // Name for System directory

void CEikDir::RemoveSystem()
// Remove "System" if in list and it's a directory.
	{
	TInt index;
	TEntry entry;
	entry.iName=KSystemDirName;
	TKeyArrayPak key(_FOFF(TEntry,iName),ECmpFolded);
	if (iArray->Find(entry,key,index)==KErrNone)
		{
		entry=(*iArray)[index];
		if (entry.IsDir())
			iArray->Delete(index);
		}
	};

TInt CEikDir::SortByTable(CEikFileSortTable* aTable)
// Sort into order from given table.
// Any file with uid[2] matching an entry in the table will be sorted relative to others
// in the table and before any files with no matching uid.
	{
	const TInt count=iArray->Count();
	for (TInt i=0;i<count;i++)
		{
		TEntry* pEntry=&(*iArray)[i];
		TInt index;
		TKeyArrayFix key(0,ECmpTInt);
		if (aTable->Find(pEntry->iType[2],key,index)==KErrNone)
			{
			CONST_CAST(TUid&,pEntry->iType[2])=TUid::Uid(index+KEikFileSortUidBase);
			}
		}
	TInt r=this->Sort(EAscending|ESortByUid|EDirsFirst);
	const TInt tableCount=aTable->Count();
	for (TInt j=0;j<count;j++)
		{
		TEntry* pEntry=&(*iArray)[j];
		TInt index=pEntry->iType[2].iUid;
		if (index>=KEikFileSortUidBase && index<KEikFileSortUidLimit)
			{
			const TInt tableIndex=index-KEikFileSortUidBase;
			if (tableIndex<tableCount)
				CONST_CAST(TUid&,pEntry->iType[2])=(*aTable)[tableIndex];
			}
		}
	return r;
	}
