textandlocutils/nearestlangutils/src/LangUtil.cpp
changeset 59 7d891bb52a7d
equal deleted inserted replaced
53:11e2bb0d14ba 59:7d891bb52a7d
       
     1 // Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include <bafl/langutil.h>
       
    17 #include <hal.h>
       
    18 #include <hal_data.h>
       
    19 #include "LangUtilImpl.h"
       
    20 
       
    21 
       
    22 /**
       
    23 Mimimum length of a filename and mimimum length of a suffix.
       
    24 Note these two values are tied together.
       
    25 */
       
    26 const TInt KInvNameAndMinSuffixLength = 2;
       
    27 
       
    28 #define ISDIGIT(c) (c-'0' >= 0 && c-'0' <= 9)
       
    29 
       
    30 _LIT(KAllDrives, "YXWVUTSRQPONMLKJIHGFEDCBAZ");
       
    31 
       
    32 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 };
       
    33 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 };
       
    34 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 };
       
    35 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 };
       
    36 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 };
       
    37 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 };
       
    38 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 };
       
    39 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 };
       
    40 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 };
       
    41 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 };
       
    42 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 };
       
    43 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 };
       
    44 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 };
       
    45 LOCAL_C const TLanguage dp13[] = { ELangInternationalFrench,ELangFrench,ELangSwissFrench,ELangBelgianFrench,ELangCanadianFrench,ELangNone };
       
    46 LOCAL_C const TLanguage dp14[] = { ELangBelgianFrench, ELangFrench,ELangInternationalFrench,ELangSwissFrench,ELangCanadianFrench,ELangNone };
       
    47 LOCAL_C const TLanguage dp15[] = { ELangCanadianFrench, ELangFrench,ELangInternationalFrench,ELangSwissFrench,ELangBelgianFrench,ELangNone };
       
    48 LOCAL_C const TLanguage dp16[] = { ELangFrench,ELangInternationalFrench,ELangSwissFrench,ELangBelgianFrench,ELangCanadianFrench,ELangNone };
       
    49 LOCAL_C const TLanguage dp17[] = { ELangSwissFrench,ELangFrench,ELangInternationalFrench,ELangBelgianFrench,ELangCanadianFrench,ELangNone };
       
    50 LOCAL_C const TLanguage dp18[] = { ELangSwissGerman,ELangGerman,ELangAustrian,ELangNone };
       
    51 LOCAL_C const TLanguage dp19[] = { ELangAustrian,ELangGerman,ELangSwissGerman,ELangNone };
       
    52 LOCAL_C const TLanguage dp20[] = { ELangGerman,ELangSwissGerman,ELangAustrian,ELangNone };
       
    53 LOCAL_C const TLanguage dp21[] = { ELangSerbian,ELangCroatian,ELangNone };
       
    54 LOCAL_C const TLanguage dp22[] = { ELangCroatian,ELangSerbian,ELangNone };
       
    55 LOCAL_C const TLanguage dp23[] = { ELangRomanian,ELangMoldavian,ELangNone };
       
    56 LOCAL_C const TLanguage dp24[] = { ELangMoldavian,ELangRomanian,ELangNone };
       
    57 LOCAL_C const TLanguage dp25[] = { ELangBelgianFlemish,ELangDutch,ELangNone };
       
    58 LOCAL_C const TLanguage dp26[] = { ELangDutch,ELangBelgianFlemish,ELangNone };
       
    59 LOCAL_C const TLanguage dp27[] = { ELangAfrikaans,ELangDutch,ELangNone };
       
    60 LOCAL_C const TLanguage dp28[] = { ELangMalay_Apac,ELangMalay,ELangNone };
       
    61 LOCAL_C const TLanguage dp29[] = { ELangIndonesian_Apac,ELangIndonesian,ELangNone };
       
    62 LOCAL_C const TLanguage dp30[] = { ELangSpanish,ELangInternationalSpanish,ELangLatinAmericanSpanish,ELangNone };
       
    63 LOCAL_C const TLanguage dp31[] = { ELangLatinAmericanSpanish,ELangSpanish,ELangInternationalSpanish,ELangNone };
       
    64 LOCAL_C const TLanguage dp32[] = { ELangInternationalSpanish,ELangSpanish,ELangLatinAmericanSpanish,ELangNone };
       
    65 LOCAL_C const TLanguage dp33[] = { ELangCyprusGreek,ELangGreek,ELangNone };
       
    66 LOCAL_C const TLanguage dp34[] = { ELangGreek,ELangCyprusGreek,ELangNone };
       
    67 LOCAL_C const TLanguage dp35[] = { ELangSwissItalian,ELangItalian,ELangNone };
       
    68 LOCAL_C const TLanguage dp36[] = { ELangItalian,ELangSwissItalian,ELangNone };
       
    69 LOCAL_C const TLanguage dp37[] = { ELangBrazilianPortuguese,ELangPortuguese,ELangNone };
       
    70 LOCAL_C const TLanguage dp38[] = { ELangPortuguese,ELangBrazilianPortuguese,ELangNone };
       
    71 LOCAL_C const TLanguage dp39[] = { ELangFinlandSwedish,ELangSwedish,ELangNone };
       
    72 LOCAL_C const TLanguage dp40[] = { ELangSwedish,ELangFinlandSwedish,ELangNone };
       
    73 LOCAL_C const TLanguage dp41[] = { ELangCyprusTurkish,ELangTurkish,ELangNone };
       
    74 LOCAL_C const TLanguage dp42[] = { ELangTurkish,ELangCyprusTurkish,ELangNone };
       
    75 LOCAL_C const TLanguage dp43[] = { ELangHongKongChinese, ELangTaiwanChinese, ELangPrcChinese,ELangNone };
       
    76 LOCAL_C const TLanguage dp44[] = { ELangTaiwanChinese, ELangHongKongChinese,ELangPrcChinese,ELangNone };
       
    77 LOCAL_C const TLanguage dp45[] = { ELangPrcChinese, ELangHongKongChinese, ELangTaiwanChinese,ELangNone };
       
    78 LOCAL_C const TLanguage * const KEquivalentLists[] = { dp0,  dp1,  dp2,  dp3,  dp4,  dp5,  dp6,  
       
    79 		dp7,  dp8,  dp9,  dp10,  dp11,  dp12,  dp13,  dp14,  dp15,  dp16,  dp17,
       
    80 		dp18,  dp19,  dp20,  dp21,  dp22,  dp23,  dp24,  dp25,  dp26,  dp27,  
       
    81 		dp28,  dp29,  dp30,  dp31,  dp32,  dp33,  dp34,  dp35,  dp36,  dp37,  
       
    82 		dp38,  dp39,  dp40,  dp41,  dp42,  dp43,  dp44,  dp45};
       
    83 
       
    84 
       
    85 
       
    86 LOCAL_C TBool IsLanguageExtended(const TLanguage aLanguage)
       
    87 	{
       
    88 	// For compatibility reasons, ELangNone is 0xFFFF. However, it's not an extended language.
       
    89 	if ((aLanguage==ELangNone) || ((static_cast<TUint>(aLanguage))<=KDialectMask))
       
    90 		return EFalse;
       
    91 	else
       
    92 		return ETrue;
       
    93 	}
       
    94 
       
    95 
       
    96 LOCAL_C TLanguage BaseLanguage(const TLanguage aLanguage)
       
    97 	{
       
    98 	if (IsLanguageExtended(aLanguage))
       
    99 		return static_cast<TLanguage>(aLanguage & KDialectMask);
       
   100 	else
       
   101 		return aLanguage;
       
   102 	}
       
   103 
       
   104 LOCAL_C TLanguage NextLanguage(TLanguage aLanguage)
       
   105 /** Returns the next best language to use after aLanguage,
       
   106 based on Symbian's base table of language near-equivalence.
       
   107 @internalAll */
       
   108 	{
       
   109 	switch (aLanguage)
       
   110 		{
       
   111 		case ELangAustralian:
       
   112 		case ELangNewZealand:
       
   113 		case ELangSouthAfricanEnglish:
       
   114 		case ELangInternationalEnglish:
       
   115 		case ELangAmerican:
       
   116 		case ELangEnglish_Apac:
       
   117 		case ELangEnglish_Taiwan:
       
   118 		case ELangEnglish_HongKong:
       
   119 		case ELangEnglish_Prc:
       
   120 		case ELangEnglish_Japan:
       
   121 		case ELangEnglish_Thailand:
       
   122 			return ELangEnglish;
       
   123 		case ELangCanadianEnglish:
       
   124 			return ELangAmerican;	// 2-stage downgrade
       
   125 		case ELangSwissFrench:
       
   126 		case ELangBelgianFrench:
       
   127 		case ELangInternationalFrench:
       
   128 		case ELangCanadianFrench:
       
   129 			return ELangFrench;
       
   130 		case ELangSwissGerman:
       
   131 		case ELangAustrian:
       
   132 			return ELangGerman;
       
   133 		case ELangInternationalSpanish:
       
   134 		case ELangLatinAmericanSpanish:
       
   135 			return ELangSpanish;
       
   136 		case ELangSwissItalian:
       
   137 			return ELangItalian;
       
   138 		case ELangFinlandSwedish:
       
   139 			return ELangSwedish;
       
   140 		case ELangCyprusTurkish:
       
   141 			return ELangTurkish;
       
   142 		case ELangBelgianFlemish:
       
   143 			return ELangDutch;
       
   144 		case ELangHongKongChinese:
       
   145 			return ELangTaiwanChinese;
       
   146 		case ELangCyprusGreek:
       
   147 			return ELangGreek;
       
   148 		case ELangMalay_Apac:
       
   149 			return ELangMalay;
       
   150 		case ELangBrazilianPortuguese:
       
   151 			return ELangPortuguese;
       
   152 		default:
       
   153 			return ELangNone;	
       
   154 		}
       
   155 	}
       
   156 
       
   157 
       
   158 void AddLanguage(TLanguagePath& aPath, TLanguage aNewLanguage)
       
   159 /** Add language to the language path if there is space.
       
   160 The first empty slot must have "ELangNone" in it. This will also be true
       
   161 on exit. */ 
       
   162     {
       
   163     TLanguage *p = aPath;
       
   164     const TLanguage *end = &(aPath[KMaxDowngradeLanguages]);
       
   165     while (p != end)
       
   166         {
       
   167         if (*p == aNewLanguage)
       
   168             // language already in list
       
   169             break;
       
   170         if (*p == ELangNone)
       
   171             {
       
   172             // found the end of the list
       
   173             p[0] = aNewLanguage;
       
   174             p[1] = ELangNone;
       
   175             break;
       
   176             }
       
   177         ++p;
       
   178         }
       
   179     return;
       
   180     }
       
   181 
       
   182 void MakeLanguageDowngradePath(TLanguagePath& aPath,
       
   183     TLanguage aCurrent, TLanguage aIdeal, const TLocale& aLocale)
       
   184     {
       
   185     TInt j = 0;
       
   186     if( aIdeal != ELangNone)
       
   187         {
       
   188         aPath[j++]=aIdeal;  
       
   189         }
       
   190     aPath[j++] = aCurrent;
       
   191     aPath[j++] = ELangNone;
       
   192 
       
   193     if (aCurrent & ~KDialectMask)
       
   194         AddLanguage(aPath, static_cast<TLanguage>(aCurrent & KDialectMask));
       
   195 
       
   196     for (TInt i=0;i<=2;i++)
       
   197         {
       
   198         AddLanguage(aPath, aLocale.LanguageDowngrade(i));
       
   199         AddLanguage(aPath, BaseLanguage(aLocale.LanguageDowngrade(i)));
       
   200         }
       
   201 
       
   202     while (ELangNone != (aCurrent = NextLanguage(BaseLanguage(aCurrent))))  
       
   203         AddLanguage(aPath, aCurrent);
       
   204     }
       
   205 
       
   206 
       
   207 
       
   208 //EXPORT_C 
       
   209 void LangUtil::GetDowngradePathL(const RFs& aFs, const TLanguage aCurrentLanguage, RArray<TLanguage>& aLanguageArray){
       
   210 
       
   211  	TLocale currentLocale; 
       
   212   	TNearestLanguageFileFinder languageDowngradePath(aFs);
       
   213   	TLanguage idealLanguage=IdealLanguage();
       
   214   	MakeLanguageDowngradePath(languageDowngradePath.iPath,aCurrentLanguage,idealLanguage, currentLocale);
       
   215  	aLanguageArray.Reset();
       
   216   	const TLanguage* p=languageDowngradePath.iPath;
       
   217   	while (*p != ELangNone)
       
   218   		{
       
   219 		User::LeaveIfError(aLanguageArray.Append(*p));
       
   220   		++p;
       
   221   		}
       
   222 
       
   223 }
       
   224 
       
   225 
       
   226 //EXPORT_C 
       
   227 void LangUtil::GetEquivalentLanguageList(TLanguage aLang, TLanguagePath& aEquivalents){
       
   228 
       
   229     aEquivalents[0] = aLang;
       
   230 	aEquivalents[1] = ELangNone;
       
   231 	const TInt len = sizeof(KEquivalentLists) / sizeof(KEquivalentLists[0]);
       
   232 	for (TInt i = 0; i < len; ++i)
       
   233 		{
       
   234 		const TLanguage *ptr = KEquivalentLists[i];
       
   235 		if (ptr[0] == aLang)
       
   236 			{
       
   237 			TInt index = 1;
       
   238 			while (ELangNone != *ptr)
       
   239 				{
       
   240 				aEquivalents[index++] = (TLanguage)*(++ptr);
       
   241 				}
       
   242 			aEquivalents[index] = ELangNone;
       
   243 			break;
       
   244 			} // end if ptr[0]
       
   245 		} // end for i
       
   246 
       
   247 }
       
   248 
       
   249 
       
   250 
       
   251 //EXPORT_C 
       
   252 TLanguage LangUtil::IdealLanguage(){
       
   253 
       
   254 		TLanguage* langPtr=(TLanguage*)Dll::Tls();
       
   255 	
       
   256 	if( langPtr==NULL)
       
   257 		{
       
   258 		return(ELangNone);
       
   259 		}
       
   260 
       
   261 	return(*langPtr);
       
   262 
       
   263 }
       
   264 
       
   265 
       
   266 //EXPORT_C 
       
   267 void LangUtil::NearestLanguageFile(const RFs& aFs, TFileName& aName){
       
   268 	
       
   269 	TLanguage language;
       
   270 	
       
   271 	NearestLanguageFile( aFs, aName, language);
       
   272 	
       
   273 	(void)language;
       
   274 
       
   275 }
       
   276 
       
   277 
       
   278 //EXPORT_C 
       
   279 void LangUtil::NearestLanguageFile(const RFs& aFs, TFileName& aName, TLanguage& aLanguage){
       
   280 #if defined(DO_PROFILING)
       
   281 	RDebug::ProfileReset(FIRST_PROFILE_INDEX, PROFILE_COUNT);
       
   282 	RDebug::ProfileStart(PROFILE_INDEX_1);
       
   283 #endif
       
   284 	TNearestLanguageFileFinder finder(aFs);
       
   285 	TBool goodSuffix=finder.SetFileName(aName);
       
   286 	
       
   287 	// Only continue if the suffix is good.
       
   288 	if(goodSuffix)
       
   289 		{
       
   290 		// add preset customised resource drive to drive list  
       
   291 		// Note that errors returned from AddCustomResourceDrive are ignored. This is because if 
       
   292 		// a custom resource drive has not been found we still want to continue on with searching 
       
   293 		// other drives according to our algorithm
       
   294 		finder.AddCustomResourceDrive();
       
   295 		
       
   296 		TLocale locale;
       
   297 		TLanguage idealLanguage;
       
   298 		idealLanguage = IdealLanguage();
       
   299 		MakeLanguageDowngradePath(finder.iPath, User::Language(), idealLanguage, locale);
       
   300 		if (!finder.FindLanguageAndDrive()
       
   301 			&& KErrNone != finder.FindFirstLanguageFileAndDrive())
       
   302 			finder.RepairFileName();
       
   303 		aLanguage = finder.Language();
       
   304 		}
       
   305 		
       
   306 #if defined(DO_PROFILING)
       
   307 	RDebug::ProfileEnd(PROFILE_INDEX_1);
       
   308 	TProfile profile[PROFILE_COUNT];
       
   309 	RDebug::ProfileResult(&profile[0], FIRST_PROFILE_INDEX, PROFILE_COUNT);
       
   310 	if(goodSuffix)
       
   311 		{
       
   312 		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);
       
   313 		}
       
   314 	else
       
   315 		{
       
   316 		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);
       
   317 		}
       
   318 #endif
       
   319 
       
   320 }
       
   321 
       
   322 
       
   323 //EXPORT_C 
       
   324 void LangUtil::NearestLanguageFileV2(const RFs& aFs, TFileName& aName, TLanguage& aLanguage){
       
   325 	
       
   326 	TNearestLanguageFileFinder finder(aFs);
       
   327 	TBool goodSuffix=finder.SetFileName(aName);
       
   328 	
       
   329 	// Continue only if the suffix is good.
       
   330 	if(goodSuffix)
       
   331 		{
       
   332 		// add preset customised resource drive to drive list  
       
   333 		// Note that errors returned from AddCustomResourceDrive are ignored. This is because if 
       
   334 		// a custom resource drive has not been found we still want to continue on with searching 
       
   335 		// other drives according to our algorithm
       
   336 		finder.AddCustomResourceDrive();
       
   337 		
       
   338 		GetEquivalentLanguageList(User::Language(), finder.iPath);
       
   339 		if (!finder.FindLanguageAndDrive()
       
   340 			&& KErrNone != finder.FindFirstLanguageFileAndDrive())
       
   341 			finder.RepairFileName();
       
   342 		aLanguage = finder.Language();
       
   343 		}
       
   344 	else
       
   345 		{
       
   346 		aLanguage = ELangNone;
       
   347 		}
       
   348 
       
   349 }
       
   350 
       
   351 
       
   352 //EXPORT_C 
       
   353 void LangUtil::ReleaseIdealLanguage(){
       
   354 
       
   355 	TLanguage* aLanguage=(TLanguage*)Dll::Tls();
       
   356 	if( aLanguage==NULL)
       
   357 		return;
       
   358 	
       
   359 	delete aLanguage;
       
   360 	Dll::FreeTls();
       
   361 
       
   362 }
       
   363 
       
   364 
       
   365 //EXPORT_C
       
   366 TInt LangUtil::SetIdealLanguage(TLanguage aLanguage){
       
   367 TLanguage* langPtr=(TLanguage*)Dll::Tls();
       
   368 	if( langPtr==NULL)
       
   369 		{
       
   370 		langPtr=(TLanguage*)User::Alloc(sizeof(TLanguage));
       
   371 		
       
   372 		if(!langPtr) 
       
   373 			return(KErrNoMemory);
       
   374 		
       
   375 		TInt ret=Dll::SetTls(langPtr);
       
   376 		
       
   377 		if(ret!=KErrNone)
       
   378 			return(ret);
       
   379 		}
       
   380 	*langPtr=aLanguage;
       
   381 	return(KErrNone);
       
   382 	
       
   383 }
       
   384 
       
   385 TInt RRealDirectoryScanner::Open(RFs& aFs, const TDesC& aMatchPattern)
       
   386     {
       
   387     return iDir.Open(aFs, aMatchPattern,
       
   388         KEntryAttReadOnly | KEntryAttHidden | KEntryAttSystem | KEntryAttArchive);
       
   389     }
       
   390 
       
   391 TInt RRealDirectoryScanner::Next(TEntry& aOut)
       
   392     {
       
   393     return iDir.Read(aOut);
       
   394     }
       
   395 
       
   396 void RRealDirectoryScanner::Close()
       
   397     {
       
   398     iDir.Close();
       
   399     }
       
   400 
       
   401 /**
       
   402 Simply counts the number of numerical characters at the end of the name passed.
       
   403 
       
   404 @internalComponent
       
   405 @param          aFilename The filename to parse
       
   406                 
       
   407 @return         Count of the numeric digits at the end of the name passed, 
       
   408                 e.g. x.r491 gives 3.
       
   409 */
       
   410 TInt TNearestLanguageFileFinder::CountDigitsFromEnd(const TDesC& aFilename)
       
   411     {
       
   412     TInt digitCount = 0;
       
   413     
       
   414     for (TInt idx = aFilename.Length()-1; idx>=0 && ISDIGIT (aFilename [idx]); --idx)
       
   415         {
       
   416         ++digitCount;
       
   417         }
       
   418         
       
   419     return digitCount;
       
   420     }
       
   421 
       
   422 
       
   423 /**
       
   424 Counts the number of digits at the end of a filename.
       
   425 
       
   426 @internalComponent
       
   427 @param          aFilename The filename to parse
       
   428                 
       
   429 @return         Count of the numeric digits at the end of the suffix, 
       
   430                 e.g. x.r491 gives 3.
       
   431                 0 if no numeric end of suffix,
       
   432                 KErrBadName for an invalid filename,
       
   433                 KErrNotSupported if the filename (minus path) is less
       
   434                 than or equal to KInvNameAndMinSuffixLength in length
       
   435 */
       
   436 TInt TNearestLanguageFileFinder::CountDigitsFromEndInSuffix (const TDesC& aFilename)
       
   437     {
       
   438     TInt digitCount = 0;
       
   439     TInt slashIdx = 0;
       
   440     TInt len = aFilename.Length ();
       
   441     
       
   442     // NOTE: We didn't use TChar here as they are too slow.
       
   443     // We also didn't use TParse as they are too large.
       
   444     
       
   445     // don't work on the path
       
   446     for (slashIdx=len-1; slashIdx >= 0 && aFilename[slashIdx] != '\\'; --slashIdx)
       
   447     {/*do nothing*/};
       
   448     
       
   449     // Get new length
       
   450     if (slashIdx>=0) {len = len-slashIdx-1;}
       
   451 
       
   452     // Initial test to see if filename legal size.
       
   453     if (len > KInvNameAndMinSuffixLength)
       
   454         {
       
   455         digitCount = CountDigitsFromEnd(aFilename);
       
   456 
       
   457         // Can't store something bigger or we'll panic!
       
   458         if (digitCount > KMaxSuffixLength)
       
   459             {
       
   460             digitCount = KErrBadName;
       
   461             }
       
   462         else
       
   463         // numeric filename, e.g. "1234". 
       
   464         // No preceeding alpha character
       
   465         if (!(len-digitCount))
       
   466             {
       
   467             digitCount = KErrBadName;
       
   468             }
       
   469         }
       
   470     else
       
   471         {
       
   472         digitCount = KErrNotSupported;
       
   473         }
       
   474         
       
   475     return digitCount;
       
   476     }
       
   477 
       
   478 RDirectoryScanner& TNearestLanguageFileFinder::DirectoryScanner()
       
   479     {
       
   480     return iDirScanner;
       
   481     }
       
   482 
       
   483 TBool TNearestLanguageFileFinder::FileExists(const TDesC& aFileName) const
       
   484     {
       
   485     //return BaflUtils::FileExists(iFs, aFileName);
       
   486     TEntry entry;
       
   487     return(iFs.Entry(aFileName,entry)==KErrNone);
       
   488     
       
   489     }
       
   490 
       
   491 TBool TNearestLanguageFileFinder::FindDrive()
       
   492     {
       
   493     ASSERT(iFileName);
       
   494     TBool found=EFalse;
       
   495     TInt driveLength=iDrives.Length();
       
   496     for (TInt drive = 0; drive!=driveLength; ++drive)
       
   497         {
       
   498         (*iFileName)[0] = iDrives[drive];
       
   499         if (FileExists(*iFileName))
       
   500             {
       
   501             found=ETrue;
       
   502             break;
       
   503             }
       
   504         }
       
   505     return found;
       
   506     }
       
   507 
       
   508 TBool TNearestLanguageFileFinder::AppendLanguageCode(TLanguage aLanguage)
       
   509     {
       
   510     TInt rest = static_cast<TInt>(aLanguage);
       
   511 #ifdef _DEBUG
       
   512     _LIT(KErrorMessage, "Bafl");
       
   513 #endif
       
   514     __ASSERT_DEBUG(0 <= rest, User::Panic(KErrorMessage,KErrArgument));
       
   515     iFileName->SetLength(iBaseLength);
       
   516     const TInt remaining = iFileName->MaxLength() - iBaseLength;
       
   517     TInt soFar = 0;
       
   518     TBuf<1> num;
       
   519     num.Append('0');
       
   520     TBool appendLangSuccess = ETrue;
       
   521     TInt digitCount = 0;
       
   522     TInt digit = 0;
       
   523     while (rest)
       
   524         {
       
   525         if (remaining == soFar)
       
   526             {
       
   527             // no more room in descriptor- return rather than panic,
       
   528             // file cannot exist.
       
   529             iFileName->SetLength(iBaseLength);
       
   530             appendLangSuccess= EFalse;
       
   531             break;
       
   532             }
       
   533         // Convert the number to ASCII by consistantly getting the base 10 remainder to convert.
       
   534         // The number is updated minus the remainder for the next iteration.
       
   535         // eg (rest = 123) -> (12, r3) -> (1, r2) -> (0, r1)
       
   536         // Then insert the ASCII representation of the remainder into the filename end
       
   537         // so it appears the correct way round.
       
   538         // eg (filename.r) -> (filename.r3) -> (filename.r23) -> (filename.r123)
       
   539         digit = rest % 10;
       
   540         digitCount++;
       
   541         rest /= 10;
       
   542         num[0] = static_cast<TText16>(digit + '0');
       
   543         iFileName->Insert(iBaseLength, num);
       
   544 
       
   545         // Minimum suffix length is KInvNameAndMinSuffixLength
       
   546         // so we have to insert zeros to make this up.
       
   547         while (!rest && digitCount < KInvNameAndMinSuffixLength)
       
   548             {
       
   549             num[0] = static_cast<TText16>('0');
       
   550             iFileName->Insert(iBaseLength, num);
       
   551             ++digitCount;
       
   552             }
       
   553             
       
   554         ++soFar;
       
   555         }
       
   556         
       
   557     return appendLangSuccess;
       
   558     }
       
   559 
       
   560 
       
   561 TBool TNearestLanguageFileFinder::FindLanguageAndDrive()
       
   562 /** Search for files across all drives in all languages in the path plus the
       
   563 language-neutral file. */
       
   564     {
       
   565     ASSERT(iFileName);
       
   566     // No point appending if the suffix is bad
       
   567     for (const TLanguage* currentLang = iPath; *currentLang != ELangNone; ++currentLang)
       
   568         {
       
   569         if (AppendLanguageCode(*currentLang) && FindDrive())
       
   570             {
       
   571             iLanguage = *currentLang;
       
   572             return ETrue;
       
   573             }
       
   574         }
       
   575     // search for language-neutral file
       
   576     iFileName->SetLength(iBaseLength);
       
   577     iFileName->Append(iSuffix);
       
   578     return FindDrive();
       
   579     }
       
   580 
       
   581 TInt TNearestLanguageFileFinder::LanguageNumberFromFile(const TDesC& aFileName, const TDesC& aStem)
       
   582     {
       
   583     TInt lang = 0;
       
   584     TInt multiplier = 1;
       
   585     TInt leadingZeroCount = 0;
       
   586     TInt languageNumber = KErrNotFound;
       
   587     const TText* firstChar = aFileName.Ptr();
       
   588     const TText* lastChar = firstChar + aFileName.Length() - 1;
       
   589     const TText* currentChar = lastChar;
       
   590     // string cannot contain only numbers, because it must have a ':' in it
       
   591     while ('0' <= *currentChar && *currentChar <= '9')
       
   592         {
       
   593         if (*currentChar == '0')
       
   594             leadingZeroCount++;
       
   595         else
       
   596             {
       
   597             leadingZeroCount = 0;
       
   598             lang += multiplier * (*currentChar - '0');
       
   599             }
       
   600         multiplier *= 10;
       
   601         --currentChar;
       
   602         }
       
   603     TInt along=lastChar - currentChar;
       
   604     if (2 <= along)
       
   605         {
       
   606         // We have at least 2 digits at the end.
       
   607         // trim of bad leading zeros
       
   608         TInt maxTrim = along - 2;
       
   609         if (maxTrim < leadingZeroCount)
       
   610             {
       
   611             leadingZeroCount = maxTrim;
       
   612             }
       
   613         currentChar += leadingZeroCount;
       
   614         // we have at least 2 digits at the end but does the rest of it match the stem?
       
   615         TPtrC foundStem(firstChar, currentChar - firstChar + 1);
       
   616         //foundStem.CompareF(aStem.Right(foundStem.Length()))
       
   617         if (0 == foundStem.CompareF(aStem))
       
   618             {
       
   619             languageNumber=lang;
       
   620             }
       
   621         }
       
   622     return languageNumber;
       
   623     }
       
   624 
       
   625 TInt TNearestLanguageFileFinder::FindFirstLanguageFile(RFs& aFs)
       
   626     {
       
   627     ASSERT(iFileName);
       
   628     iFileName->SetLength(iBaseLength);
       
   629     TPtrC name(*iFileName);
       
   630     TParsePtrC nameToParse(name);
       
   631     TPtrC nameStem(nameToParse.NameAndExt());
       
   632     iFileName->Append('*');
       
   633     TInt bestLanguageMatch = KMaxTInt;
       
   634     RDirectoryScanner& scanner = DirectoryScanner();
       
   635     TInt err = scanner.Open(aFs, *iFileName);
       
   636     if (err != KErrNone)
       
   637         {
       
   638         return err;
       
   639         }
       
   640     TEntry entry;
       
   641     while (KErrNone == scanner.Next(entry))
       
   642         {
       
   643         TInt lang = LanguageNumberFromFile(entry.iName, nameStem);
       
   644         if (0 < lang && lang < bestLanguageMatch)
       
   645             {
       
   646             bestLanguageMatch = lang;
       
   647             }
       
   648         }
       
   649     scanner.Close();
       
   650     if (bestLanguageMatch != KMaxTInt)
       
   651         {
       
   652         iLanguage = static_cast<TLanguage>(bestLanguageMatch);
       
   653         AppendLanguageCode(static_cast<TLanguage>(bestLanguageMatch));
       
   654         return KErrNone;
       
   655         }
       
   656     return KErrNotFound;
       
   657     }
       
   658 
       
   659 // Try each drive for any language files
       
   660 // iFileName must have a directory specifier
       
   661 TInt TNearestLanguageFileFinder::FindFirstLanguageFileAndDrive()
       
   662     {
       
   663     ASSERT(iFileName);
       
   664     TInt findFirstResult=KErrNotFound;
       
   665     TInt driveLength=iDrives.Length();
       
   666     for (TInt drive = 0; drive != driveLength; ++drive)
       
   667         {
       
   668         (*iFileName)[0] = iDrives[drive];
       
   669         TInt err = FindFirstLanguageFile(CONST_CAST(RFs&,iFs));
       
   670         if (err == KErrNone || err == KErrNoMemory)
       
   671             {
       
   672             findFirstResult=err;
       
   673             break;
       
   674             }
       
   675         }
       
   676     return findFirstResult;
       
   677     }
       
   678 
       
   679 /**
       
   680 Invalid filenames are any filename whose length (minus path) must be greater
       
   681 than KInvNameAndMinSuffixLength, and whose form is purely numerical, i.e. '1234' 
       
   682 */
       
   683 TBool TNearestLanguageFileFinder::SetFileName(TFileName& aFileName)
       
   684     {
       
   685     iDrives.Zero();
       
   686     iFileName = &aFileName;
       
   687     iOriginalBaseLength = iFileName->Length();
       
   688     
       
   689     TInt suffixLength = CountDigitsFromEndInSuffix (aFileName);
       
   690     
       
   691     // No point trying for filenames thats are badly formed
       
   692     // or that are too large.
       
   693     if (suffixLength >= 0 && 
       
   694         KInvNameAndMinSuffixLength < iOriginalBaseLength)
       
   695         {
       
   696         if (suffixLength > 0)
       
   697             {
       
   698             // all of suffix to be replaced 
       
   699             iSuffix = iFileName->Right(suffixLength);
       
   700             iOriginalBaseLength -= suffixLength;
       
   701             iFileName->SetLength(iOriginalBaseLength);
       
   702             }
       
   703         else
       
   704             { 
       
   705             // No numerical part to suffix
       
   706             TInt periodIdx = 0;
       
   707             
       
   708             // Search for the period within range KInvNameAndMinSuffixLength 
       
   709             // from the end. As this must work for all values of
       
   710             // KInvNameAndMinSuffixLength
       
   711             for (TInt i = iOriginalBaseLength-1; 
       
   712                  !periodIdx && i >= (iOriginalBaseLength-KInvNameAndMinSuffixLength-1);
       
   713                  --i)
       
   714                 {
       
   715                 if ((*iFileName) [i] == '.')
       
   716                     {
       
   717                     periodIdx = i;
       
   718                     }
       
   719                 }
       
   720             
       
   721             // Don't handle files ending in a period.
       
   722             // This is because the behaviour is different between Windows
       
   723             // and Symbian Fs. In Windows it strips the period off.
       
   724             //
       
   725             // However, and this shouldn't happen as it is not shown
       
   726             // (in the documentation) to be valid.
       
   727             // Just try our best.
       
   728             if (periodIdx == iOriginalBaseLength-1)
       
   729                 {
       
   730                 iSuffix.Zero();
       
   731                 return EFalse;
       
   732                 }
       
   733             else
       
   734             if (periodIdx)
       
   735                 {
       
   736                 // If there are KInvNameAndMinSuffixLength chars after the period
       
   737                 // simply replace them.
       
   738                 TInt right = iOriginalBaseLength-periodIdx-1;
       
   739                 iSuffix = iFileName->Right(right);
       
   740                 iOriginalBaseLength -= right;
       
   741                 iFileName->SetLength(iOriginalBaseLength);                  
       
   742                 }
       
   743             else
       
   744                 {
       
   745                 // Make the suffix start from KInvNameAndMinSuffixLength 
       
   746                 // from the right
       
   747                 TInt right = KInvNameAndMinSuffixLength;
       
   748                 iSuffix = iFileName->Right(right);
       
   749                 iOriginalBaseLength -= right;
       
   750                 iFileName->SetLength(iOriginalBaseLength);                  
       
   751                 }
       
   752             }
       
   753         }
       
   754     else
       
   755         {
       
   756         // bad or no suffix - treat the same
       
   757         iSuffix.Zero();
       
   758         return EFalse;
       
   759         }
       
   760 
       
   761     // For filenames with no drive letter prefix and also for filenames
       
   762     // shorter than the drive letter length, i.e. with no drive
       
   763     // information, insert it.
       
   764     // Handles if the user simply enters the drive, e.g. "c:".
       
   765     if (iOriginalBaseLength < KMaxDriveName || (*iFileName)[1] != ':')
       
   766         {
       
   767         // Set up the default if none supplied and make room in the filename 
       
   768         // array to contain a drive specification. Set initial drive letter to -1
       
   769         // so the iFileName is repaired before exited 
       
   770         iInitialDriveLetter = -1;
       
   771         iFileName->Insert(0, _L("_:")); 
       
   772         iDrives.Append('Z');
       
   773         }
       
   774     else
       
   775         {
       
   776        // Use the drive supplied inthe aName to NearestLanguageFile()
       
   777         iInitialDriveLetter = (*iFileName)[0];
       
   778         iDrives.Append(iInitialDriveLetter);
       
   779         }
       
   780     
       
   781     iBaseLength = iFileName->Length();
       
   782     
       
   783     return ETrue;
       
   784     }
       
   785 
       
   786 
       
   787 TLanguage TNearestLanguageFileFinder::Language()
       
   788     {
       
   789     return iLanguage;
       
   790     }
       
   791 
       
   792 TNearestLanguageFileFinder::TNearestLanguageFileFinder(
       
   793     const RFs& aFs)
       
   794     : iFs(aFs), iFileName(0), iLanguage(ELangNone)
       
   795     {
       
   796     }
       
   797 
       
   798 void TNearestLanguageFileFinder::RepairFileName()
       
   799     {
       
   800     ASSERT(iFileName);
       
   801     iFileName->SetLength(iBaseLength);
       
   802     if (iInitialDriveLetter == -1)
       
   803         iFileName->Delete(0, 2);
       
   804     else
       
   805         (*iFileName)[0] = static_cast<TText>(iInitialDriveLetter);
       
   806     iFileName->SetLength(iOriginalBaseLength);
       
   807     iFileName->Append(iSuffix);
       
   808     }
       
   809 
       
   810 
       
   811 /**
       
   812 Add the custom resource drive to the start of the iDrives string.
       
   813 
       
   814 The custom resource drive is a preset writeable drive on which customised 
       
   815 resource files may be present. This drive takes priority over the other 
       
   816 drives when searching for language files.
       
   817 
       
   818 @return KErrNone if iDrives string was successfully modified; KErrAlreadyExists 
       
   819 if the drive is already present in the string; otherwise one of 
       
   820 the other system-wide error codes (iDrives will be unmodified). 
       
   821 */
       
   822 TInt TNearestLanguageFileFinder::AddCustomResourceDrive()
       
   823     {
       
   824     TInt drive = GetCustomResourceDriveNumber();
       
   825     if (drive<0)
       
   826         return drive;
       
   827     
       
   828     // if drive not already in drive list
       
   829     if (iDrives.LocateF('A' + drive) < 0)
       
   830         {
       
   831         // add it
       
   832         _LIT(KDrivePlaceholder, "_");
       
   833         iDrives.Insert(0, KDrivePlaceholder);
       
   834         iDrives[0] = 'A' + drive;
       
   835         return KErrNone;
       
   836         }
       
   837     else
       
   838         return KErrAlreadyExists;
       
   839     }
       
   840 
       
   841 
       
   842 void TNearestLanguageFileFinder::AddAllDrives()
       
   843     {
       
   844     ASSERT(iDrives.Length() < 2);
       
   845     if (iDrives.Length() == 0)
       
   846         {
       
   847         iDrives = KAllDrives;
       
   848         return;
       
   849         }
       
   850     TInt pos = KAllDrives().LocateF(iDrives[0]);
       
   851     if (pos < 0)
       
   852         {
       
   853         iDrives = KAllDrives;
       
   854         return;
       
   855         }
       
   856     iDrives.Append(KAllDrives().Left(pos));
       
   857     iDrives.Append(KAllDrives().Mid(pos + 1));
       
   858     }
       
   859 
       
   860 
       
   861 /**
       
   862 Get the value of the custom resource drive.
       
   863 
       
   864 The custom resource drive is a preset writeable drive on which customised language resource 
       
   865 files can reside. The drive number is accessed via the HAL attribute ECustomResourceDrive. 
       
   866 It is then returned if it has been defined as a valid drive no.
       
   867 Otherwise for backward compatibility reasons an attempt is then made to access the system 
       
   868 drive HAL attribute instead. This drive number is returned if it has been defined as a valid 
       
   869 drive number.  
       
   870 Otherwise if neither a valid ECustomResourceDrive or ESystemDrive exists then KErrNotFound 
       
   871 is returned.
       
   872  
       
   873 Note that the ESystemDrive HAL attribute has been deprecated. It is accessed here to cater 
       
   874 for existing implementations which still expect it to be used.
       
   875  
       
   876 @return The drive number (corresponding to a TDriveNumber value) if successful; 
       
   877 KErrNotFound if neither a valid ECustomResourceDrive or a valid ESystemDrive HAL attribute 
       
   878 is defined;  
       
   879  
       
   880 @see HAL::ECustomResourceDrive
       
   881 @see HAL::ESystemDrive
       
   882 */
       
   883 TInt TNearestLanguageFileFinder::GetCustomResourceDriveNumber() const
       
   884     {
       
   885     TInt drive = KErrNotFound;
       
   886     
       
   887     // access custom resource drive attribute  
       
   888     if (HAL::Get(HAL::ECustomResourceDrive, drive) == KErrNone)
       
   889         {
       
   890         // check that drive is valid
       
   891         if (drive>=EDriveA && drive<=EDriveZ)
       
   892             return drive;    
       
   893         }
       
   894                         
       
   895     // access system drive attribute  
       
   896     // (Note that ESystemDrive is deprecated. It is checked here 
       
   897     // solely for backward compatibility reasons.)      
       
   898     if (HAL::Get(HAL::ESystemDrive, drive) == KErrNone)
       
   899         {
       
   900         // check that drive is valid
       
   901         if (drive>=EDriveA && drive<=EDriveZ)
       
   902                 return drive;
       
   903         }       
       
   904  
       
   905     return KErrNotFound;
       
   906     }
       
   907 
       
   908