textandlocutils/nearestlangutils/src/LangUtil.cpp
changeset 63 a145c1c7a5f4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/textandlocutils/nearestlangutils/src/LangUtil.cpp	Fri Sep 17 08:40:09 2010 +0300
@@ -0,0 +1,908 @@
+// Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "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:
+//
+
+#include <bafl/langutil.h>
+#include <hal.h>
+#include <hal_data.h>
+#include "LangUtilImpl.h"
+
+
+/**
+Mimimum length of a filename and mimimum length of a suffix.
+Note these two values are tied together.
+*/
+const TInt KInvNameAndMinSuffixLength = 2;
+
+#define ISDIGIT(c) (c-'0' >= 0 && c-'0' <= 9)
+
+_LIT(KAllDrives, "YXWVUTSRQPONMLKJIHGFEDCBAZ");
+
+LOCAL_C const TLanguage dp0[] = { ELangCanadianEnglish, ELangAmerican,ELangEnglish, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangAustralian,ELangNewZealand,ELangInternationalEnglish,ELangSouthAfricanEnglish,ELangNone };
+LOCAL_C const TLanguage dp1[] = { ELangAmerican, ELangEnglish,ELangCanadianEnglish, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangAustralian,ELangNewZealand,ELangInternationalEnglish,ELangSouthAfricanEnglish, ELangNone };
+LOCAL_C const TLanguage dp2[] = { ELangAustralian, ELangEnglish, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangInternationalEnglish,ELangSouthAfricanEnglish, ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp3[] = { ELangSouthAfricanEnglish, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangInternationalEnglish,ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp4[] = { ELangInternationalEnglish, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp5[] = { ELangEnglish_Apac, ELangEnglish, ELangAustralian, ELangAmerican,ELangInternationalEnglish,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp6[] = { ELangEnglish_Taiwan, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangInternationalEnglish,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp7[] = { ELangEnglish_HongKong, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangInternationalEnglish,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp8[] = { ELangEnglish_Prc, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangInternationalEnglish,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp9[] = { ELangEnglish_Japan, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangInternationalEnglish,ELangEnglish_Thailand,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp10[] = { ELangEnglish_Thailand, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangInternationalEnglish,ELangEnglish_India,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp11[] = { ELangEnglish_India, ELangEnglish, ELangAustralian, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangInternationalEnglish,ELangNewZealand,ELangSouthAfricanEnglish,ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp12[] = { ELangNewZealand, ELangEnglish, ELangAmerican, ELangEnglish_Apac,ELangEnglish_Taiwan,ELangEnglish_HongKong,ELangEnglish_Prc,ELangEnglish_Japan,ELangEnglish_Thailand,ELangEnglish_India,ELangAustralian,ELangInternationalEnglish,ELangSouthAfricanEnglish, ELangCanadianEnglish,ELangNone };
+LOCAL_C const TLanguage dp13[] = { ELangInternationalFrench,ELangFrench,ELangSwissFrench,ELangBelgianFrench,ELangCanadianFrench,ELangNone };
+LOCAL_C const TLanguage dp14[] = { ELangBelgianFrench, ELangFrench,ELangInternationalFrench,ELangSwissFrench,ELangCanadianFrench,ELangNone };
+LOCAL_C const TLanguage dp15[] = { ELangCanadianFrench, ELangFrench,ELangInternationalFrench,ELangSwissFrench,ELangBelgianFrench,ELangNone };
+LOCAL_C const TLanguage dp16[] = { ELangFrench,ELangInternationalFrench,ELangSwissFrench,ELangBelgianFrench,ELangCanadianFrench,ELangNone };
+LOCAL_C const TLanguage dp17[] = { ELangSwissFrench,ELangFrench,ELangInternationalFrench,ELangBelgianFrench,ELangCanadianFrench,ELangNone };
+LOCAL_C const TLanguage dp18[] = { ELangSwissGerman,ELangGerman,ELangAustrian,ELangNone };
+LOCAL_C const TLanguage dp19[] = { ELangAustrian,ELangGerman,ELangSwissGerman,ELangNone };
+LOCAL_C const TLanguage dp20[] = { ELangGerman,ELangSwissGerman,ELangAustrian,ELangNone };
+LOCAL_C const TLanguage dp21[] = { ELangSerbian,ELangCroatian,ELangNone };
+LOCAL_C const TLanguage dp22[] = { ELangCroatian,ELangSerbian,ELangNone };
+LOCAL_C const TLanguage dp23[] = { ELangRomanian,ELangMoldavian,ELangNone };
+LOCAL_C const TLanguage dp24[] = { ELangMoldavian,ELangRomanian,ELangNone };
+LOCAL_C const TLanguage dp25[] = { ELangBelgianFlemish,ELangDutch,ELangNone };
+LOCAL_C const TLanguage dp26[] = { ELangDutch,ELangBelgianFlemish,ELangNone };
+LOCAL_C const TLanguage dp27[] = { ELangAfrikaans,ELangDutch,ELangNone };
+LOCAL_C const TLanguage dp28[] = { ELangMalay_Apac,ELangMalay,ELangNone };
+LOCAL_C const TLanguage dp29[] = { ELangIndonesian_Apac,ELangIndonesian,ELangNone };
+LOCAL_C const TLanguage dp30[] = { ELangSpanish,ELangInternationalSpanish,ELangLatinAmericanSpanish,ELangNone };
+LOCAL_C const TLanguage dp31[] = { ELangLatinAmericanSpanish,ELangSpanish,ELangInternationalSpanish,ELangNone };
+LOCAL_C const TLanguage dp32[] = { ELangInternationalSpanish,ELangSpanish,ELangLatinAmericanSpanish,ELangNone };
+LOCAL_C const TLanguage dp33[] = { ELangCyprusGreek,ELangGreek,ELangNone };
+LOCAL_C const TLanguage dp34[] = { ELangGreek,ELangCyprusGreek,ELangNone };
+LOCAL_C const TLanguage dp35[] = { ELangSwissItalian,ELangItalian,ELangNone };
+LOCAL_C const TLanguage dp36[] = { ELangItalian,ELangSwissItalian,ELangNone };
+LOCAL_C const TLanguage dp37[] = { ELangBrazilianPortuguese,ELangPortuguese,ELangNone };
+LOCAL_C const TLanguage dp38[] = { ELangPortuguese,ELangBrazilianPortuguese,ELangNone };
+LOCAL_C const TLanguage dp39[] = { ELangFinlandSwedish,ELangSwedish,ELangNone };
+LOCAL_C const TLanguage dp40[] = { ELangSwedish,ELangFinlandSwedish,ELangNone };
+LOCAL_C const TLanguage dp41[] = { ELangCyprusTurkish,ELangTurkish,ELangNone };
+LOCAL_C const TLanguage dp42[] = { ELangTurkish,ELangCyprusTurkish,ELangNone };
+LOCAL_C const TLanguage dp43[] = { ELangHongKongChinese, ELangTaiwanChinese, ELangPrcChinese,ELangNone };
+LOCAL_C const TLanguage dp44[] = { ELangTaiwanChinese, ELangHongKongChinese,ELangPrcChinese,ELangNone };
+LOCAL_C const TLanguage dp45[] = { ELangPrcChinese, ELangHongKongChinese, ELangTaiwanChinese,ELangNone };
+LOCAL_C const TLanguage * const KEquivalentLists[] = { dp0,  dp1,  dp2,  dp3,  dp4,  dp5,  dp6,  
+		dp7,  dp8,  dp9,  dp10,  dp11,  dp12,  dp13,  dp14,  dp15,  dp16,  dp17,
+		dp18,  dp19,  dp20,  dp21,  dp22,  dp23,  dp24,  dp25,  dp26,  dp27,  
+		dp28,  dp29,  dp30,  dp31,  dp32,  dp33,  dp34,  dp35,  dp36,  dp37,  
+		dp38,  dp39,  dp40,  dp41,  dp42,  dp43,  dp44,  dp45};
+
+
+
+LOCAL_C TBool IsLanguageExtended(const TLanguage aLanguage)
+	{
+	// For compatibility reasons, ELangNone is 0xFFFF. However, it's not an extended language.
+	if ((aLanguage==ELangNone) || ((static_cast<TUint>(aLanguage))<=KDialectMask))
+		return EFalse;
+	else
+		return ETrue;
+	}
+
+
+LOCAL_C TLanguage BaseLanguage(const TLanguage aLanguage)
+	{
+	if (IsLanguageExtended(aLanguage))
+		return static_cast<TLanguage>(aLanguage & KDialectMask);
+	else
+		return aLanguage;
+	}
+
+LOCAL_C TLanguage NextLanguage(TLanguage aLanguage)
+/** Returns the next best language to use after aLanguage,
+based on Symbian's base table of language near-equivalence.
+@internalAll */
+	{
+	switch (aLanguage)
+		{
+		case ELangAustralian:
+		case ELangNewZealand:
+		case ELangSouthAfricanEnglish:
+		case ELangInternationalEnglish:
+		case ELangAmerican:
+		case ELangEnglish_Apac:
+		case ELangEnglish_Taiwan:
+		case ELangEnglish_HongKong:
+		case ELangEnglish_Prc:
+		case ELangEnglish_Japan:
+		case ELangEnglish_Thailand:
+			return ELangEnglish;
+		case ELangCanadianEnglish:
+			return ELangAmerican;	// 2-stage downgrade
+		case ELangSwissFrench:
+		case ELangBelgianFrench:
+		case ELangInternationalFrench:
+		case ELangCanadianFrench:
+			return ELangFrench;
+		case ELangSwissGerman:
+		case ELangAustrian:
+			return ELangGerman;
+		case ELangInternationalSpanish:
+		case ELangLatinAmericanSpanish:
+			return ELangSpanish;
+		case ELangSwissItalian:
+			return ELangItalian;
+		case ELangFinlandSwedish:
+			return ELangSwedish;
+		case ELangCyprusTurkish:
+			return ELangTurkish;
+		case ELangBelgianFlemish:
+			return ELangDutch;
+		case ELangHongKongChinese:
+			return ELangTaiwanChinese;
+		case ELangCyprusGreek:
+			return ELangGreek;
+		case ELangMalay_Apac:
+			return ELangMalay;
+		case ELangBrazilianPortuguese:
+			return ELangPortuguese;
+		default:
+			return ELangNone;	
+		}
+	}
+
+
+void AddLanguage(TLanguagePath& aPath, TLanguage aNewLanguage)
+/** Add language to the language path if there is space.
+The first empty slot must have "ELangNone" in it. This will also be true
+on exit. */ 
+    {
+    TLanguage *p = aPath;
+    const TLanguage *end = &(aPath[KMaxDowngradeLanguages]);
+    while (p != end)
+        {
+        if (*p == aNewLanguage)
+            // language already in list
+            break;
+        if (*p == ELangNone)
+            {
+            // found the end of the list
+            p[0] = aNewLanguage;
+            p[1] = ELangNone;
+            break;
+            }
+        ++p;
+        }
+    return;
+    }
+
+void MakeLanguageDowngradePath(TLanguagePath& aPath,
+    TLanguage aCurrent, TLanguage aIdeal, const TLocale& aLocale)
+    {
+    TInt j = 0;
+    if( aIdeal != ELangNone)
+        {
+        aPath[j++]=aIdeal;  
+        }
+    aPath[j++] = aCurrent;
+    aPath[j++] = ELangNone;
+
+    if (aCurrent & ~KDialectMask)
+        AddLanguage(aPath, static_cast<TLanguage>(aCurrent & KDialectMask));
+
+    for (TInt i=0;i<=2;i++)
+        {
+        AddLanguage(aPath, aLocale.LanguageDowngrade(i));
+        AddLanguage(aPath, BaseLanguage(aLocale.LanguageDowngrade(i)));
+        }
+
+    while (ELangNone != (aCurrent = NextLanguage(BaseLanguage(aCurrent))))  
+        AddLanguage(aPath, aCurrent);
+    }
+
+
+
+//EXPORT_C 
+void LangUtil::GetDowngradePathL(const RFs& aFs, const TLanguage aCurrentLanguage, RArray<TLanguage>& aLanguageArray){
+
+ 	TLocale currentLocale; 
+  	TNearestLanguageFileFinder languageDowngradePath(aFs);
+  	TLanguage idealLanguage=IdealLanguage();
+  	MakeLanguageDowngradePath(languageDowngradePath.iPath,aCurrentLanguage,idealLanguage, currentLocale);
+ 	aLanguageArray.Reset();
+  	const TLanguage* p=languageDowngradePath.iPath;
+  	while (*p != ELangNone)
+  		{
+		User::LeaveIfError(aLanguageArray.Append(*p));
+  		++p;
+  		}
+
+}
+
+
+//EXPORT_C 
+void LangUtil::GetEquivalentLanguageList(TLanguage aLang, TLanguagePath& aEquivalents){
+
+    aEquivalents[0] = aLang;
+	aEquivalents[1] = ELangNone;
+	const TInt len = sizeof(KEquivalentLists) / sizeof(KEquivalentLists[0]);
+	for (TInt i = 0; i < len; ++i)
+		{
+		const TLanguage *ptr = KEquivalentLists[i];
+		if (ptr[0] == aLang)
+			{
+			TInt index = 1;
+			while (ELangNone != *ptr)
+				{
+				aEquivalents[index++] = (TLanguage)*(++ptr);
+				}
+			aEquivalents[index] = ELangNone;
+			break;
+			} // end if ptr[0]
+		} // end for i
+
+}
+
+
+
+//EXPORT_C 
+TLanguage LangUtil::IdealLanguage(){
+
+		TLanguage* langPtr=(TLanguage*)Dll::Tls();
+	
+	if( langPtr==NULL)
+		{
+		return(ELangNone);
+		}
+
+	return(*langPtr);
+
+}
+
+
+//EXPORT_C 
+void LangUtil::NearestLanguageFile(const RFs& aFs, TFileName& aName){
+	
+	TLanguage language;
+	
+	NearestLanguageFile( aFs, aName, language);
+	
+	(void)language;
+
+}
+
+
+//EXPORT_C 
+void LangUtil::NearestLanguageFile(const RFs& aFs, TFileName& aName, TLanguage& aLanguage){
+#if defined(DO_PROFILING)
+	RDebug::ProfileReset(FIRST_PROFILE_INDEX, PROFILE_COUNT);
+	RDebug::ProfileStart(PROFILE_INDEX_1);
+#endif
+	TNearestLanguageFileFinder finder(aFs);
+	TBool goodSuffix=finder.SetFileName(aName);
+	
+	// Only continue if the suffix is good.
+	if(goodSuffix)
+		{
+		// add preset customised resource drive to drive list  
+		// Note that errors returned from AddCustomResourceDrive are ignored. This is because if 
+		// a custom resource drive has not been found we still want to continue on with searching 
+		// other drives according to our algorithm
+		finder.AddCustomResourceDrive();
+		
+		TLocale locale;
+		TLanguage idealLanguage;
+		idealLanguage = IdealLanguage();
+		MakeLanguageDowngradePath(finder.iPath, User::Language(), idealLanguage, locale);
+		if (!finder.FindLanguageAndDrive()
+			&& KErrNone != finder.FindFirstLanguageFileAndDrive())
+			finder.RepairFileName();
+		aLanguage = finder.Language();
+		}
+		
+#if defined(DO_PROFILING)
+	RDebug::ProfileEnd(PROFILE_INDEX_1);
+	TProfile profile[PROFILE_COUNT];
+	RDebug::ProfileResult(&profile[0], FIRST_PROFILE_INDEX, PROFILE_COUNT);
+	if(goodSuffix)
+		{
+		RDebug::Print(_L("BaflUtils::NearestLanguageFile profile info: %d.%03ds"), profile[PROFILE_INDEX_1-FIRST_PROFILE_INDEX].iTime/1000000, profile[PROFILE_INDEX_1-FIRST_PROFILE_INDEX].iTime%1000000);
+		}
+	else
+		{
+		RDebug::Print(_L("BaflUtils::NearestLanguageFile (bad suffix ) profile info: %d.%03ds"), profile[PROFILE_INDEX_1-FIRST_PROFILE_INDEX].iTime/1000000, profile[PROFILE_INDEX_1-FIRST_PROFILE_INDEX].iTime%1000000);
+		}
+#endif
+
+}
+
+
+//EXPORT_C 
+void LangUtil::NearestLanguageFileV2(const RFs& aFs, TFileName& aName, TLanguage& aLanguage){
+	
+	TNearestLanguageFileFinder finder(aFs);
+	TBool goodSuffix=finder.SetFileName(aName);
+	
+	// Continue only if the suffix is good.
+	if(goodSuffix)
+		{
+		// add preset customised resource drive to drive list  
+		// Note that errors returned from AddCustomResourceDrive are ignored. This is because if 
+		// a custom resource drive has not been found we still want to continue on with searching 
+		// other drives according to our algorithm
+		finder.AddCustomResourceDrive();
+		
+		GetEquivalentLanguageList(User::Language(), finder.iPath);
+		if (!finder.FindLanguageAndDrive()
+			&& KErrNone != finder.FindFirstLanguageFileAndDrive())
+			finder.RepairFileName();
+		aLanguage = finder.Language();
+		}
+	else
+		{
+		aLanguage = ELangNone;
+		}
+
+}
+
+
+//EXPORT_C 
+void LangUtil::ReleaseIdealLanguage(){
+
+	TLanguage* aLanguage=(TLanguage*)Dll::Tls();
+	if( aLanguage==NULL)
+		return;
+	
+	delete aLanguage;
+	Dll::FreeTls();
+
+}
+
+
+//EXPORT_C
+TInt LangUtil::SetIdealLanguage(TLanguage aLanguage){
+TLanguage* langPtr=(TLanguage*)Dll::Tls();
+	if( langPtr==NULL)
+		{
+		langPtr=(TLanguage*)User::Alloc(sizeof(TLanguage));
+		
+		if(!langPtr) 
+			return(KErrNoMemory);
+		
+		TInt ret=Dll::SetTls(langPtr);
+		
+		if(ret!=KErrNone)
+			return(ret);
+		}
+	*langPtr=aLanguage;
+	return(KErrNone);
+	
+}
+
+TInt RRealDirectoryScanner::Open(RFs& aFs, const TDesC& aMatchPattern)
+    {
+    return iDir.Open(aFs, aMatchPattern,
+        KEntryAttReadOnly | KEntryAttHidden | KEntryAttSystem | KEntryAttArchive);
+    }
+
+TInt RRealDirectoryScanner::Next(TEntry& aOut)
+    {
+    return iDir.Read(aOut);
+    }
+
+void RRealDirectoryScanner::Close()
+    {
+    iDir.Close();
+    }
+
+/**
+Simply counts the number of numerical characters at the end of the name passed.
+
+@internalComponent
+@param          aFilename The filename to parse
+                
+@return         Count of the numeric digits at the end of the name passed, 
+                e.g. x.r491 gives 3.
+*/
+TInt TNearestLanguageFileFinder::CountDigitsFromEnd(const TDesC& aFilename)
+    {
+    TInt digitCount = 0;
+    
+    for (TInt idx = aFilename.Length()-1; idx>=0 && ISDIGIT (aFilename [idx]); --idx)
+        {
+        ++digitCount;
+        }
+        
+    return digitCount;
+    }
+
+
+/**
+Counts the number of digits at the end of a filename.
+
+@internalComponent
+@param          aFilename The filename to parse
+                
+@return         Count of the numeric digits at the end of the suffix, 
+                e.g. x.r491 gives 3.
+                0 if no numeric end of suffix,
+                KErrBadName for an invalid filename,
+                KErrNotSupported if the filename (minus path) is less
+                than or equal to KInvNameAndMinSuffixLength in length
+*/
+TInt TNearestLanguageFileFinder::CountDigitsFromEndInSuffix (const TDesC& aFilename)
+    {
+    TInt digitCount = 0;
+    TInt slashIdx = 0;
+    TInt len = aFilename.Length ();
+    
+    // NOTE: We didn't use TChar here as they are too slow.
+    // We also didn't use TParse as they are too large.
+    
+    // don't work on the path
+    for (slashIdx=len-1; slashIdx >= 0 && aFilename[slashIdx] != '\\'; --slashIdx)
+    {/*do nothing*/};
+    
+    // Get new length
+    if (slashIdx>=0) {len = len-slashIdx-1;}
+
+    // Initial test to see if filename legal size.
+    if (len > KInvNameAndMinSuffixLength)
+        {
+        digitCount = CountDigitsFromEnd(aFilename);
+
+        // Can't store something bigger or we'll panic!
+        if (digitCount > KMaxSuffixLength)
+            {
+            digitCount = KErrBadName;
+            }
+        else
+        // numeric filename, e.g. "1234". 
+        // No preceeding alpha character
+        if (!(len-digitCount))
+            {
+            digitCount = KErrBadName;
+            }
+        }
+    else
+        {
+        digitCount = KErrNotSupported;
+        }
+        
+    return digitCount;
+    }
+
+RDirectoryScanner& TNearestLanguageFileFinder::DirectoryScanner()
+    {
+    return iDirScanner;
+    }
+
+TBool TNearestLanguageFileFinder::FileExists(const TDesC& aFileName) const
+    {
+    //return BaflUtils::FileExists(iFs, aFileName);
+    TEntry entry;
+    return(iFs.Entry(aFileName,entry)==KErrNone);
+    
+    }
+
+TBool TNearestLanguageFileFinder::FindDrive()
+    {
+    ASSERT(iFileName);
+    TBool found=EFalse;
+    TInt driveLength=iDrives.Length();
+    for (TInt drive = 0; drive!=driveLength; ++drive)
+        {
+        (*iFileName)[0] = iDrives[drive];
+        if (FileExists(*iFileName))
+            {
+            found=ETrue;
+            break;
+            }
+        }
+    return found;
+    }
+
+TBool TNearestLanguageFileFinder::AppendLanguageCode(TLanguage aLanguage)
+    {
+    TInt rest = static_cast<TInt>(aLanguage);
+#ifdef _DEBUG
+    _LIT(KErrorMessage, "Bafl");
+#endif
+    __ASSERT_DEBUG(0 <= rest, User::Panic(KErrorMessage,KErrArgument));
+    iFileName->SetLength(iBaseLength);
+    const TInt remaining = iFileName->MaxLength() - iBaseLength;
+    TInt soFar = 0;
+    TBuf<1> num;
+    num.Append('0');
+    TBool appendLangSuccess = ETrue;
+    TInt digitCount = 0;
+    TInt digit = 0;
+    while (rest)
+        {
+        if (remaining == soFar)
+            {
+            // no more room in descriptor- return rather than panic,
+            // file cannot exist.
+            iFileName->SetLength(iBaseLength);
+            appendLangSuccess= EFalse;
+            break;
+            }
+        // Convert the number to ASCII by consistantly getting the base 10 remainder to convert.
+        // The number is updated minus the remainder for the next iteration.
+        // eg (rest = 123) -> (12, r3) -> (1, r2) -> (0, r1)
+        // Then insert the ASCII representation of the remainder into the filename end
+        // so it appears the correct way round.
+        // eg (filename.r) -> (filename.r3) -> (filename.r23) -> (filename.r123)
+        digit = rest % 10;
+        digitCount++;
+        rest /= 10;
+        num[0] = static_cast<TText16>(digit + '0');
+        iFileName->Insert(iBaseLength, num);
+
+        // Minimum suffix length is KInvNameAndMinSuffixLength
+        // so we have to insert zeros to make this up.
+        while (!rest && digitCount < KInvNameAndMinSuffixLength)
+            {
+            num[0] = static_cast<TText16>('0');
+            iFileName->Insert(iBaseLength, num);
+            ++digitCount;
+            }
+            
+        ++soFar;
+        }
+        
+    return appendLangSuccess;
+    }
+
+
+TBool TNearestLanguageFileFinder::FindLanguageAndDrive()
+/** Search for files across all drives in all languages in the path plus the
+language-neutral file. */
+    {
+    ASSERT(iFileName);
+    // No point appending if the suffix is bad
+    for (const TLanguage* currentLang = iPath; *currentLang != ELangNone; ++currentLang)
+        {
+        if (AppendLanguageCode(*currentLang) && FindDrive())
+            {
+            iLanguage = *currentLang;
+            return ETrue;
+            }
+        }
+    // search for language-neutral file
+    iFileName->SetLength(iBaseLength);
+    iFileName->Append(iSuffix);
+    return FindDrive();
+    }
+
+TInt TNearestLanguageFileFinder::LanguageNumberFromFile(const TDesC& aFileName, const TDesC& aStem)
+    {
+    TInt lang = 0;
+    TInt multiplier = 1;
+    TInt leadingZeroCount = 0;
+    TInt languageNumber = KErrNotFound;
+    const TText* firstChar = aFileName.Ptr();
+    const TText* lastChar = firstChar + aFileName.Length() - 1;
+    const TText* currentChar = lastChar;
+    // string cannot contain only numbers, because it must have a ':' in it
+    while ('0' <= *currentChar && *currentChar <= '9')
+        {
+        if (*currentChar == '0')
+            leadingZeroCount++;
+        else
+            {
+            leadingZeroCount = 0;
+            lang += multiplier * (*currentChar - '0');
+            }
+        multiplier *= 10;
+        --currentChar;
+        }
+    TInt along=lastChar - currentChar;
+    if (2 <= along)
+        {
+        // We have at least 2 digits at the end.
+        // trim of bad leading zeros
+        TInt maxTrim = along - 2;
+        if (maxTrim < leadingZeroCount)
+            {
+            leadingZeroCount = maxTrim;
+            }
+        currentChar += leadingZeroCount;
+        // we have at least 2 digits at the end but does the rest of it match the stem?
+        TPtrC foundStem(firstChar, currentChar - firstChar + 1);
+        //foundStem.CompareF(aStem.Right(foundStem.Length()))
+        if (0 == foundStem.CompareF(aStem))
+            {
+            languageNumber=lang;
+            }
+        }
+    return languageNumber;
+    }
+
+TInt TNearestLanguageFileFinder::FindFirstLanguageFile(RFs& aFs)
+    {
+    ASSERT(iFileName);
+    iFileName->SetLength(iBaseLength);
+    TPtrC name(*iFileName);
+    TParsePtrC nameToParse(name);
+    TPtrC nameStem(nameToParse.NameAndExt());
+    iFileName->Append('*');
+    TInt bestLanguageMatch = KMaxTInt;
+    RDirectoryScanner& scanner = DirectoryScanner();
+    TInt err = scanner.Open(aFs, *iFileName);
+    if (err != KErrNone)
+        {
+        return err;
+        }
+    TEntry entry;
+    while (KErrNone == scanner.Next(entry))
+        {
+        TInt lang = LanguageNumberFromFile(entry.iName, nameStem);
+        if (0 < lang && lang < bestLanguageMatch)
+            {
+            bestLanguageMatch = lang;
+            }
+        }
+    scanner.Close();
+    if (bestLanguageMatch != KMaxTInt)
+        {
+        iLanguage = static_cast<TLanguage>(bestLanguageMatch);
+        AppendLanguageCode(static_cast<TLanguage>(bestLanguageMatch));
+        return KErrNone;
+        }
+    return KErrNotFound;
+    }
+
+// Try each drive for any language files
+// iFileName must have a directory specifier
+TInt TNearestLanguageFileFinder::FindFirstLanguageFileAndDrive()
+    {
+    ASSERT(iFileName);
+    TInt findFirstResult=KErrNotFound;
+    TInt driveLength=iDrives.Length();
+    for (TInt drive = 0; drive != driveLength; ++drive)
+        {
+        (*iFileName)[0] = iDrives[drive];
+        TInt err = FindFirstLanguageFile(CONST_CAST(RFs&,iFs));
+        if (err == KErrNone || err == KErrNoMemory)
+            {
+            findFirstResult=err;
+            break;
+            }
+        }
+    return findFirstResult;
+    }
+
+/**
+Invalid filenames are any filename whose length (minus path) must be greater
+than KInvNameAndMinSuffixLength, and whose form is purely numerical, i.e. '1234' 
+*/
+TBool TNearestLanguageFileFinder::SetFileName(TFileName& aFileName)
+    {
+    iDrives.Zero();
+    iFileName = &aFileName;
+    iOriginalBaseLength = iFileName->Length();
+    
+    TInt suffixLength = CountDigitsFromEndInSuffix (aFileName);
+    
+    // No point trying for filenames thats are badly formed
+    // or that are too large.
+    if (suffixLength >= 0 && 
+        KInvNameAndMinSuffixLength < iOriginalBaseLength)
+        {
+        if (suffixLength > 0)
+            {
+            // all of suffix to be replaced 
+            iSuffix = iFileName->Right(suffixLength);
+            iOriginalBaseLength -= suffixLength;
+            iFileName->SetLength(iOriginalBaseLength);
+            }
+        else
+            { 
+            // No numerical part to suffix
+            TInt periodIdx = 0;
+            
+            // Search for the period within range KInvNameAndMinSuffixLength 
+            // from the end. As this must work for all values of
+            // KInvNameAndMinSuffixLength
+            for (TInt i = iOriginalBaseLength-1; 
+                 !periodIdx && i >= (iOriginalBaseLength-KInvNameAndMinSuffixLength-1);
+                 --i)
+                {
+                if ((*iFileName) [i] == '.')
+                    {
+                    periodIdx = i;
+                    }
+                }
+            
+            // Don't handle files ending in a period.
+            // This is because the behaviour is different between Windows
+            // and Symbian Fs. In Windows it strips the period off.
+            //
+            // However, and this shouldn't happen as it is not shown
+            // (in the documentation) to be valid.
+            // Just try our best.
+            if (periodIdx == iOriginalBaseLength-1)
+                {
+                iSuffix.Zero();
+                return EFalse;
+                }
+            else
+            if (periodIdx)
+                {
+                // If there are KInvNameAndMinSuffixLength chars after the period
+                // simply replace them.
+                TInt right = iOriginalBaseLength-periodIdx-1;
+                iSuffix = iFileName->Right(right);
+                iOriginalBaseLength -= right;
+                iFileName->SetLength(iOriginalBaseLength);                  
+                }
+            else
+                {
+                // Make the suffix start from KInvNameAndMinSuffixLength 
+                // from the right
+                TInt right = KInvNameAndMinSuffixLength;
+                iSuffix = iFileName->Right(right);
+                iOriginalBaseLength -= right;
+                iFileName->SetLength(iOriginalBaseLength);                  
+                }
+            }
+        }
+    else
+        {
+        // bad or no suffix - treat the same
+        iSuffix.Zero();
+        return EFalse;
+        }
+
+    // For filenames with no drive letter prefix and also for filenames
+    // shorter than the drive letter length, i.e. with no drive
+    // information, insert it.
+    // Handles if the user simply enters the drive, e.g. "c:".
+    if (iOriginalBaseLength < KMaxDriveName || (*iFileName)[1] != ':')
+        {
+        // Set up the default if none supplied and make room in the filename 
+        // array to contain a drive specification. Set initial drive letter to -1
+        // so the iFileName is repaired before exited 
+        iInitialDriveLetter = -1;
+        iFileName->Insert(0, _L("_:")); 
+        iDrives.Append('Z');
+        }
+    else
+        {
+       // Use the drive supplied inthe aName to NearestLanguageFile()
+        iInitialDriveLetter = (*iFileName)[0];
+        iDrives.Append(iInitialDriveLetter);
+        }
+    
+    iBaseLength = iFileName->Length();
+    
+    return ETrue;
+    }
+
+
+TLanguage TNearestLanguageFileFinder::Language()
+    {
+    return iLanguage;
+    }
+
+TNearestLanguageFileFinder::TNearestLanguageFileFinder(
+    const RFs& aFs)
+    : iFs(aFs), iFileName(0), iLanguage(ELangNone)
+    {
+    }
+
+void TNearestLanguageFileFinder::RepairFileName()
+    {
+    ASSERT(iFileName);
+    iFileName->SetLength(iBaseLength);
+    if (iInitialDriveLetter == -1)
+        iFileName->Delete(0, 2);
+    else
+        (*iFileName)[0] = static_cast<TText>(iInitialDriveLetter);
+    iFileName->SetLength(iOriginalBaseLength);
+    iFileName->Append(iSuffix);
+    }
+
+
+/**
+Add the custom resource drive to the start of the iDrives string.
+
+The custom resource drive is a preset writeable drive on which customised 
+resource files may be present. This drive takes priority over the other 
+drives when searching for language files.
+
+@return KErrNone if iDrives string was successfully modified; KErrAlreadyExists 
+if the drive is already present in the string; otherwise one of 
+the other system-wide error codes (iDrives will be unmodified). 
+*/
+TInt TNearestLanguageFileFinder::AddCustomResourceDrive()
+    {
+    TInt drive = GetCustomResourceDriveNumber();
+    if (drive<0)
+        return drive;
+    
+    // if drive not already in drive list
+    if (iDrives.LocateF('A' + drive) < 0)
+        {
+        // add it
+        _LIT(KDrivePlaceholder, "_");
+        iDrives.Insert(0, KDrivePlaceholder);
+        iDrives[0] = 'A' + drive;
+        return KErrNone;
+        }
+    else
+        return KErrAlreadyExists;
+    }
+
+
+void TNearestLanguageFileFinder::AddAllDrives()
+    {
+    ASSERT(iDrives.Length() < 2);
+    if (iDrives.Length() == 0)
+        {
+        iDrives = KAllDrives;
+        return;
+        }
+    TInt pos = KAllDrives().LocateF(iDrives[0]);
+    if (pos < 0)
+        {
+        iDrives = KAllDrives;
+        return;
+        }
+    iDrives.Append(KAllDrives().Left(pos));
+    iDrives.Append(KAllDrives().Mid(pos + 1));
+    }
+
+
+/**
+Get the value of the custom resource drive.
+
+The custom resource drive is a preset writeable drive on which customised language resource 
+files can reside. The drive number is accessed via the HAL attribute ECustomResourceDrive. 
+It is then returned if it has been defined as a valid drive no.
+Otherwise for backward compatibility reasons an attempt is then made to access the system 
+drive HAL attribute instead. This drive number is returned if it has been defined as a valid 
+drive number.  
+Otherwise if neither a valid ECustomResourceDrive or ESystemDrive exists then KErrNotFound 
+is returned.
+ 
+Note that the ESystemDrive HAL attribute has been deprecated. It is accessed here to cater 
+for existing implementations which still expect it to be used.
+ 
+@return The drive number (corresponding to a TDriveNumber value) if successful; 
+KErrNotFound if neither a valid ECustomResourceDrive or a valid ESystemDrive HAL attribute 
+is defined;  
+ 
+@see HAL::ECustomResourceDrive
+@see HAL::ESystemDrive
+*/
+TInt TNearestLanguageFileFinder::GetCustomResourceDriveNumber() const
+    {
+    TInt drive = KErrNotFound;
+    
+    // access custom resource drive attribute  
+    if (HAL::Get(HAL::ECustomResourceDrive, drive) == KErrNone)
+        {
+        // check that drive is valid
+        if (drive>=EDriveA && drive<=EDriveZ)
+            return drive;    
+        }
+                        
+    // access system drive attribute  
+    // (Note that ESystemDrive is deprecated. It is checked here 
+    // solely for backward compatibility reasons.)      
+    if (HAL::Get(HAL::ESystemDrive, drive) == KErrNone)
+        {
+        // check that drive is valid
+        if (drive>=EDriveA && drive<=EDriveZ)
+                return drive;
+        }       
+ 
+    return KErrNotFound;
+    }
+
+