Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h)
Have multiple extension sections in the bld.inf, one for each version
of the compiler. The RVCT version building the tools will build the
runtime libraries for its version, but make sure we extract all the other
versions from zip archives. Also add the archive for RVCT4.
// Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "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:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// f32\sfat\sl_vfat.cpp
//
//
#include "sl_std.h"
#include "sl_cache.h"
#include <e32svr.h>
#include <e32math.h>
IMPORT_C const TFatUtilityFunctions* GetFatUtilityFunctions();
const TInt KMaxLengthWithoutTilde = 8;
const TUint8 KLeadingE5Replacement = 0x05;
// use second half of ISO Latin 1 character set for extended chars
const TUint KExtendedCharStart=0x80;
const TUint KExtendedCharEnd=0xff;
LOCAL_C TBool IsLegalChar(TChar aCharacter,TBool aAllowWildChars,TBool aUseExtendedChars=EFalse,TBool aInScanDrive=EFalse)
//
// Returns ETrue if aCharacter is legal inside a dos filename
//
{
if ((aCharacter==KMatchOne) || (aCharacter==KMatchAny))
return(aAllowWildChars);
if ((TUint)aCharacter < 0x20)
return EFalse;
// Don't check illegal ascii char because some non-English char value may
// fall in this area
if (aInScanDrive)
return ETrue;
return LocaleUtils::IsLegalShortNameCharacter(aCharacter,aUseExtendedChars);
}
LOCAL_C void ReplaceFirstCharacterIfClashesWithE5L(TDes8& aShortName)
{
if (0 < aShortName.Length() && aShortName[0] == KEntryErasedMarker)
{
aShortName[0] = KLeadingE5Replacement;
}
}
LOCAL_C void ReplaceIllegalCharactersL(TDes& aLongName, TUint aCharacterToReplaceWith)
{
TBool alreadyFoundExtensionDelimiter=EFalse;
TInt LongNameLen = aLongName.Length();
TInt extDelimiterIndex = aLongName.LocateReverse(KExtDelimiter);
for (TInt i=LongNameLen-1; i>=0; --i) // iterate backwards as aLongName may change length during the loop, and also because we want to leave the *right-most* occurrence of KExtDelimiter unchanged
{
TUint character=aLongName[i];
if (character==(TUint)KExtDelimiter)
{
if (alreadyFoundExtensionDelimiter)
{
aLongName[i]=(TText)aCharacterToReplaceWith; // A.B.C becomes A_B.C
}
alreadyFoundExtensionDelimiter=ETrue;
}
else
{
// the code below doesn't need any #if defined(_UNICODE) stuff as a narrow-build aLongName would not contain values above 0xff (which is well below the surrogates area in Unicode 0xd800-0xdfff)
TBool isSurrogatePair=EFalse;
// LAST character in file name or file ext CAN NOT be HIGH surrogate
if (i==LongNameLen-1 || i==extDelimiterIndex-1)
{
if (IsHighSurrogate((TText16)character))
{
// Corrupt surrogate
User::Leave(KErrBadName);
}
}
// FIRST character in file name or file ext CAN NOT be LOW surrogate
if (i==0 || i==extDelimiterIndex+1)
{
if (IsLowSurrogate((TText16)character))
{
// Corrupt surrogate
User::Leave(KErrBadName);
}
}
// if LOW Surrogate
if (IsLowSurrogate((TText16)character))
{
// check for HIGH surrogate
if (!IsHighSurrogate(aLongName[--i]))
{
// Corrupt surrogate
User::Leave(KErrBadName);
}
// surrogate pair found
character&=~0xdc00;
character|=((aLongName[i]&~0xd800)<<10);
character+=0x00010000; // this must be added - it cannot be bitwise-"or"-ed
isSurrogatePair=ETrue;
}
// if High Surrogate
if (!isSurrogatePair && IsHighSurrogate((TText16)character))
{
// Corrupt surrogate
User::Leave(KErrBadName);
}
if (!IsLegalChar(character, EFalse))
{
if (isSurrogatePair)
{
aLongName.Delete(i+1, 1);
}
aLongName[i]=(TText)aCharacterToReplaceWith;
}
}
}
}
TShortName DoGenerateShortNameL(const TDesC& aLongName,TInt& aNum,TBool aUseTildeSelectively)
//
// Create a legal shortname from aLongName
//
{
TFileName longName(aLongName);
longName.UpperCase();
ReplaceIllegalCharactersL(longName, '_');
TPtrC longNameWithoutExtension(longName);
TPtrC longNameExtension(KNullDesC);
const TInt positionOfExtension=longName.LocateReverse(KExtDelimiter);
if (positionOfExtension==0)
{
// No filename specified, so use the extension as the basis of the shortname.
// Make sure we always append a tilde+number in this case to avoid generating the same
// short filename as one of the protected folders ("\SYS", "\RESOURCE","\PRIVATE")
longNameWithoutExtension.Set(longName.Mid(positionOfExtension+1));
aUseTildeSelectively = EFalse;
if (aNum < 0)
aNum = 1;
}
else if (positionOfExtension!=KErrNotFound)
{
longNameWithoutExtension.Set(longName.Left(positionOfExtension));
longNameExtension.Set(longName.Mid(positionOfExtension+1));
}
// Converts the original file name main part into 8-bit character string
TShortName tempShortName(0);
LocaleUtils::ConvertFromUnicodeL(tempShortName, longNameWithoutExtension);
const TInt originalNameLength = tempShortName.Length();
// Converts the original file name extension part into 8-bit character string
TShortName tempShortNameExt(0);
LocaleUtils::ConvertFromUnicodeL(tempShortNameExt, longNameExtension);
const TInt extensionNameLength = tempShortNameExt.Length();
// // const TInt extensionNameLength = tempShortNameExt.Length();
// Checks the length of both original file name main part and original file name extension part
if(aUseTildeSelectively)
{
// don't append ~<aNum>
if(originalNameLength<=KMaxLengthWithoutTilde && extensionNameLength<=KMaxFatFileNameExt)
aNum=-1;
}
// Applies tilde and number if necessary
TBuf8<5> tildeAndNumber;
if (aNum>=0)
{
tildeAndNumber.Append('~');
tildeAndNumber.AppendNumUC(aNum,EHex);
}
const TInt lengthOfTildeAndNumber=tildeAndNumber.Length();
// Creates actual shortname from longname of the original file
TShortName shortName(11);
#if defined(_DEBUG)
shortName.Fill(0x01); // fill shortName with garbage to ensure that every byte is written to by this function
#endif
// Fills the main part of the shortname of the original file
const TInt numberOfBytesFreeBeforeTilde=KMaxFatFileNameWithoutExt-lengthOfTildeAndNumber;
TPtr8 portionOfShortNameBeforeTilde((TUint8*)shortName.Ptr(), 0, numberOfBytesFreeBeforeTilde);
TInt lengthOfPortionOfShortNameBeforeTilde =
(originalNameLength < numberOfBytesFreeBeforeTilde) ? originalNameLength : numberOfBytesFreeBeforeTilde;
portionOfShortNameBeforeTilde.Copy((TUint8*)tempShortName.Ptr(), lengthOfPortionOfShortNameBeforeTilde);
if( lengthOfPortionOfShortNameBeforeTilde != originalNameLength)
{
for( int i = 0; i<lengthOfPortionOfShortNameBeforeTilde; i++)
{
if(portionOfShortNameBeforeTilde[i] >= 0x80) //leading byte found
{
if( i == lengthOfPortionOfShortNameBeforeTilde - 1) //leading byte found on the edge
{
lengthOfPortionOfShortNameBeforeTilde -= 1;
break;
}
else
{
i++;
}
}
}
}
Mem::Copy(((TUint8*)shortName.Ptr())+lengthOfPortionOfShortNameBeforeTilde, tildeAndNumber.Ptr(), lengthOfTildeAndNumber);
TInt i;
for (i=lengthOfPortionOfShortNameBeforeTilde+lengthOfTildeAndNumber; i<KMaxFatFileNameWithoutExt; ++i)
{
shortName[i]=' ';
}
// Fills the extension part of the shortname of the original file
TInt lengthOfExt =
(extensionNameLength < KMaxFatFileNameExt) ? extensionNameLength : KMaxFatFileNameExt;
if( lengthOfExt != extensionNameLength)
{
for( int i = 0; i<lengthOfExt; i++)
{
if(tempShortNameExt[i] >= 0x80)
{
if( i == lengthOfExt - 1)
{
lengthOfExt -= 1;
break;
}
else
{
i++;
}
}
}
}
Mem::Copy(((TUint8*)shortName.Ptr()) + KMaxFatFileNameWithoutExt, tempShortNameExt.Ptr(), lengthOfExt);
for (i = KMaxFatFileNameWithoutExt + lengthOfExt; i<KMaxFatFileNameWithoutExt+KMaxFatFileNameExt; ++i)
{
shortName[i]=' ';
}
ReplaceFirstCharacterIfClashesWithE5L(shortName);
return shortName;
}
/**
Check whether a Dos name is legal or not.
@param aName The entry name to be analysed (may be represented as TDes16& or TDes8&)
@param anAllowWildCards Flag to indicate whether to allow wildcards in name or not
@param aUseExtendedChars Flag to indicate if extended characters are allowed
@param aInScanDrive Flag to indicate whether called when scanning drive
@param aAllowLowerCase ETrue to allow lower case in the analysed DOS name
@return ETrue if the name is a legal DOS one.
*/
static TBool DoCheckLegalDosName(const TDesC& aName, TBool anAllowWildCards, TBool aUseExtendedChars, TBool aInScanDrive, TBool aAllowLowerCase, TBool aIsForFileCreation)
{
const TInt count=aName.Length();
if (count==0)
return EFalse;
TInt valid=0;
TInt i=0;
//-- check the entry name
while (i<count)
{
TChar c=aName[i++];
if (c==KExtDelimiter)
{
// DOS entry names must contain at least one valid character before the extension
if (i == 1)
return EFalse;
break;
}
if(!aAllowLowerCase && c.IsLower())
return EFalse; //-- low case is not allowed
if (!IsLegalChar(c,anAllowWildCards,aUseExtendedChars,aInScanDrive))
{
return EFalse;
}
if (aIsForFileCreation)
{
if ((aUseExtendedChars && (TUint) c > KExtendedCharEnd) ||
(!aUseExtendedChars && (TUint) c > KExtendedCharStart))
{
return EFalse;
}
}
if (c!=KMatchAny)
if (++valid>KMaxFatFileNameWithoutExt)
return EFalse;
}
//-- check entry extension
valid=0;
while (i<count)
{
TChar c=aName[i++];
if (c==KExtDelimiter)
return EFalse;
if(!aAllowLowerCase && c.IsLower())
return EFalse; //-- low case is not allowed
if (!IsLegalChar(c,anAllowWildCards,aUseExtendedChars,aInScanDrive))
return EFalse;
if (aIsForFileCreation)
{
if ((aUseExtendedChars && (TUint) c > KExtendedCharEnd) ||
(!aUseExtendedChars && (TUint) c > KExtendedCharStart))
{
return EFalse;
}
}
if (c!=KMatchAny)
if (++valid>KMaxFatFileNameExt)
return EFalse;
}
// Unicode file name checking for file opening.
if (!aIsForFileCreation && GetFatUtilityFunctions())
{
TBuf8<KMaxFileName*2> convertedName8;
TRAPD(err, LocaleUtils::ConvertFromUnicodeL(convertedName8, aName, TFatUtilityFunctions::EOverflowActionLeave));
if (err != KErrNone)
return EFalse;
const TInt len8 = convertedName8.Length();
TInt j = 0;
TInt nonWildChar = 0;
while (j < len8)
{
const TUint8 c8 = convertedName8[j++];
if (c8 == KExtDelimiter)
break;
if (c8 == '*' && !anAllowWildCards)
return EFalse;
if (c8 == '*' && anAllowWildCards)
continue;
if (++nonWildChar > KMaxFatFileNameWithoutExt)
return EFalse;
}
// check extension part
nonWildChar = 0;
while (j < len8)
{
const TUint8 c8 = convertedName8[j++];
if (c8 == KExtDelimiter)
return EFalse;
if (c8 == '*' && !anAllowWildCards)
return EFalse;
if (c8 == '*' && anAllowWildCards)
continue;
if (++nonWildChar > KMaxFatFileNameExt)
return EFalse;
}
}
return ETrue;
}
/**
Check whether a Dos name is legal or not. Unicode version
parameters and return value absolutely the same as in DoCheckLegalDosName()
*/
TBool IsLegalDosName(const TDesC16& aName, TBool anAllowWildCards, TBool aUseExtendedChars, TBool aInScanDrive, TBool aAllowLowerCase, TBool aIsForFileCreation)
{
__PRINT(_L("IsLegalDosName 16"));
return DoCheckLegalDosName(aName, anAllowWildCards, aUseExtendedChars, aInScanDrive, aAllowLowerCase, aIsForFileCreation);
}
TBool CFatMountCB::FindShortNameL(const TShortName& aName,TEntryPos& anEntryPos)
//
// Returns ETrue and the entryPos of aName if found or EFalse
//
{
__PRINT(_L("VFAT::CFatMountCB::FindShortNameL"));
TFatDirEntry fatEntry;
TInt count=0;
FOREVER
{
count++;
ReadDirEntryL(anEntryPos,fatEntry);
MoveToDosEntryL(anEntryPos,fatEntry);
if (fatEntry.IsEndOfDirectory())
break;
if (IsRootDir(anEntryPos)&&(anEntryPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
if (fatEntry.IsErased())
break;//Allows maximum number of entries in root directory
if (fatEntry.Name()==aName)
return ETrue;
MoveToNextEntryL(anEntryPos);
if (IsRootDir(anEntryPos)&&(StartOfRootDirInBytes()+anEntryPos.iPos==RootDirEnd()))
break;//Allows maximum number of entries in root directory
}
return EFalse;
}
TBool CFatMountCB::IsUniqueNameL(const TShortName& aName,TInt aDirCluster)
//
// Returns ETrue if aName is unique, EFalse if a matching name is found.
//
{
__PRINT(_L("VFAT::CFatMountCB::IsUniqueNameL"));
TEntryPos entryPos(aDirCluster,0);
if (FindShortNameL(aName,entryPos))
return(EFalse);
return(ETrue);
}
void CFatMountCB::ReplaceClashingNameL(const TShortName& aNewName,const TEntryPos& anEntryPos)
//
// A legal dos name has been typed that clashes with a computer generated shortname
// Change the shortname to something else.
//
{
__PRINT(_L("VFAT::CFatMountCB::ReplaceClashingNameL"));
TFatDirEntry entry;
ReadDirEntryL(anEntryPos,entry);
__ASSERT_ALWAYS(entry.IsEndOfDirectory()==EFalse,User::Leave(KErrCorrupt));
entry.SetName(aNewName);
WriteDirEntryL(anEntryPos,entry);
// We now need to fix up VFAT entries with a new checksum reflecting new shortname
// Calculate new checksum
TUint8 checksum=CalculateShortNameCheckSum(aNewName);
// Now go back and adjust all VFAT entries corresponding to this shortname
TEntryPos entryPos=anEntryPos;
FOREVER
{
entryPos.iPos-=KSizeOfFatDirEntry;
ReadDirEntryL(entryPos,entry);
entry.iData[0x0D]=checksum;
if (entry.iData[0]&0x40)
break;
}
}
TBool CFatMountCB::GenerateShortNameL(TInt aDirCluster,const TDesC& aName,TShortName& aGeneratedName, TBool aForceRandomize)
//
// Generate a legal dos filename as an alias for aName.
// Returns ETrue if aName is a legal dos name.
//
{
__PRINT(_L("VFAT::CFatMountCB::GenerateShortNameL"));
// Given the long file-name "ABCDEFGHI.TXT", EPOC used to generate short
// file-names in the following pecking order:
// "ABCDEFGH.TXT",
// "ABCDEF~0.TXT",
// "ABCDEF~1.TXT",
// "ABCDEF~2.TXT",
// etc.
// Now, however, EPOC behaves in a more Windows-like manner and
// generates short file-names in this pecking order:
// "ABCDEF~1.TXT",
// "ABCDEF~2.TXT",
// "ABCDEF~3.TXT",
// "ABCDEF~4.TXT",
// After failing to find an unused short name 4 times in a row,
// a random number is used to speed up the process. So subsequent
// short-file names become
// "ABC~nnnn.TXT" where nnnn is a random number
//
TBool useTildeSelectively = ETrue;
TInt endNum = KMaxDuplicateShortName; // 0xFFFF
const TInt KMaxNonRandomShortFileNames = 4;
TInt i = 1;
TBool randomize = aForceRandomize;
if (randomize)
{
i = (TInt) (Math::Random() & KMaxDuplicateShortName);
endNum = (i - 1) & KMaxDuplicateShortName;
}
while(i != endNum)
{
aGeneratedName=DoGenerateShortNameL(aName,i,useTildeSelectively);
if (IsUniqueNameL(aGeneratedName,aDirCluster))
break;
if (i == KMaxNonRandomShortFileNames && !randomize)
{
randomize = ETrue;
i = (TInt) (Math::Random() & KMaxDuplicateShortName);
endNum = (i - 1) & KMaxDuplicateShortName;
}
else if (i == -1)
{
useTildeSelectively=EFalse;
i = 1;
}
else
i = (i + 1) & KMaxDuplicateShortName;
}
if (i == endNum)
User::Leave(KErrAlreadyExists);
if((i == -1) && IsLegalDosName(aName,EFalse,EFalse,EFalse,EFalse,ETrue))
{
// Original file name is a legal 8.3 name
return(ETrue);
}
else
{
return(EFalse);
}
}
void TFatDirEntry::InitializeAsVFat(TUint8 aCheckSum)
//
// Initialize a FAT entry as a VFAT filename
//
{
Mem::Fill(this,sizeof(SFatDirEntry),0xFF);
iData[0x0B]=0x0F;
iData[0x0C]=0x00; iData[0x0D]=aCheckSum;
iData[0x1A]=0x00; iData[0x1B]=0x00;
}
void TFatDirEntry::SetVFatEntry(const TDesC& aName,TInt aLen)
//
// Write up to KMaxVFatEntryName unicode chars from aName to the entry
//
{
TInt rem=aName.Length()-aLen;
TPtrC section(aName.Ptr()+aLen,Min(rem,KMaxVFatEntryName));
TBuf16<KMaxVFatEntryName> buf16;
buf16.Copy(section);
if (rem<KMaxVFatEntryName)
{
rem++;
buf16.ZeroTerminate();
buf16.SetLength(rem); // Zero termination doesn't increase the buf length
}
TUint8 orderNo=(TUint8)(aLen/KMaxVFatEntryName+1);
TInt s=Min(rem,5);
Mem::Copy(&iData[0x01],buf16.Ptr(),s*2);//Copy up to 10 bytes of buf16 into iData
TInt offset=s;
rem-=s;
s=Min(rem,6);
Mem::Copy(&iData[0x0E],buf16.Ptr()+offset,s*2);
offset+=s;
rem-=s;
s=Min(rem,2);
Mem::Copy(&iData[0x1C],buf16.Ptr()+offset,s*2);
rem-=s;
if (rem==0)
orderNo|=0x40;
iData[0]=orderNo;
}
void TFatDirEntry::ReadVFatEntry(TDes16& aBuf) const
//
// Read KMaxVFatEntryName unicode chars from the entry
//
{
aBuf.SetLength(KMaxVFatEntryName);
Mem::Copy(&aBuf[0],&iData[0x01],5*2);
Mem::Copy(&aBuf[5],&iData[0x0E],6*2);
Mem::Copy(&aBuf[11],&iData[0x1C],2*2);
}
void CFatMountCB::WriteDirEntryL(TEntryPos& aPos,const TFatDirEntry& aFatDirEntry,const TDesC& aLongName)
//
// Write a VFAT directory entry to disk at position aPos - leave aPos refering to the dos entry
// Assumes sufficient space has been created for it by AddDirEntry.
//
{
__PRINT(_L("VFAT::CFatMountCB::WriteDirEntryL"));
__ASSERT_DEBUG(aLongName.Length(),Fault(EVFatNoLongName));
TEntryPos startPos(aPos.iCluster,aPos.iPos);
TUint8 localBuf[KDefaultSectorSize];
TUint8 cksum=CalculateShortNameCheckSum(aFatDirEntry.Name());
TInt numEntries=NumberOfVFatEntries(aLongName.Length())-1; // Excluding dos entry
// see if all entries written to one sector
// single sector writes not supported if sector size>default size
TInt dosOffset=numEntries<<KSizeOfFatDirEntryLog2;
TInt absolutePos=(aPos.iCluster<<ClusterSizeLog2())+ClusterRelativePos(aPos.iPos);
TBool isSameSector=(((absolutePos^(absolutePos+dosOffset))>>SectorSizeLog2())==0 && ((TUint)(1<<SectorSizeLog2())<=KDefaultSectorSize));
TFatDirEntry vFatEntry;
vFatEntry.InitializeAsVFat(cksum);
TInt offset=0;
while (numEntries--)
{
vFatEntry.SetVFatEntry(aLongName,KMaxVFatEntryName*numEntries);// KMaxVFatEntryName=13
if(isSameSector)
{
Mem::Copy(&localBuf[offset],&vFatEntry,KSizeOfFatDirEntry);
offset+=KSizeOfFatDirEntry;
MoveToNextEntryL(aPos);
}
else
{
WriteDirEntryL(aPos,vFatEntry);
MoveToNextEntryL(aPos);
}
}
if(isSameSector)
{
Mem::Copy(&localBuf[offset],&aFatDirEntry,KSizeOfFatDirEntry);
//-- use special interface to access FAT directory file
DirWriteL(startPos,TPtrC8(&localBuf[0],dosOffset+KSizeOfFatDirEntry));
}
else
WriteDirEntryL(aPos,aFatDirEntry);
}
void CFatMountCB::EraseDirEntryL(TEntryPos aPos,const TFatDirEntry& aFirstEntry)
//
// Mark all entries in a VFat directory entry as erased
//
{
__PRINT(_L("VFAT::CFatMountCB::EraseDirEntryL"));
TInt numEntries=0;
if (aFirstEntry.IsVFatEntry())
numEntries=aFirstEntry.NumFollowing();
if(IsRuggedFSys()&&numEntries)
{
TInt count=numEntries;
TEntryPos pos=aPos;
while(count--)
MoveToNextEntryL(pos);
EraseDirEntryL(pos);
numEntries--;
}
FOREVER
{
EraseDirEntryL(aPos);
if (!numEntries--)
break;
MoveToNextEntryL(aPos);
}
}
void LocaleUtils::ConvertFromUnicodeL(TDes8& aForeign, const TDesC16& aUnicode, TFatUtilityFunctions::TOverflowAction aOverflowAction)
//
// Convert the volume label using the algorithm specified in the current locale-DLL.
//
{
if(aOverflowAction == TFatUtilityFunctions::EOverflowActionLeave)
{
GetCodePage().ConvertFromUnicodeL(aForeign, aUnicode, TCodePageUtils::EOverflowActionLeave);
}
else
{
GetCodePage().ConvertFromUnicodeL(aForeign, aUnicode, TCodePageUtils::EOverflowActionTruncate);
}
}
void LocaleUtils::ConvertToUnicodeL(TDes16& aUnicode, const TDesC8& aForeign, TFatUtilityFunctions::TOverflowAction aOverflowAction)
//
// Convert the volume label using the algorithm specified in the current locale-DLL.
//
{
if(aOverflowAction == TFatUtilityFunctions::EOverflowActionLeave)
{
GetCodePage().ConvertToUnicodeL(aUnicode, aForeign, TCodePageUtils::EOverflowActionLeave);
}
else
{
GetCodePage().ConvertToUnicodeL(aUnicode, aForeign, TCodePageUtils::EOverflowActionTruncate);
}
}
TBool LocaleUtils::IsLegalShortNameCharacter(TUint aCharacter,TBool aUseExtendedChars)
//
// Convert the volume label using the algorithm specified in the current locale-DLL.
//
{
return GetCodePage().IsLegalShortNameCharacter(aCharacter, aUseExtendedChars);
}