1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/textandloc/textandlocutils/nearestlangutils/src/LangUtil.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,908 @@
1.4 +// Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
1.5 +// All rights reserved.
1.6 +// This component and the accompanying materials are made available
1.7 +// under the terms of "Eclipse Public License v1.0"
1.8 +// which accompanies this distribution, and is available
1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.10 +//
1.11 +// Initial Contributors:
1.12 +// Nokia Corporation - initial contribution.
1.13 +//
1.14 +// Contributors:
1.15 +//
1.16 +// Description:
1.17 +//
1.18 +
1.19 +#include <bafl/langutil.h>
1.20 +#include <hal.h>
1.21 +#include <hal_data.h>
1.22 +#include "LangUtilImpl.h"
1.23 +
1.24 +
1.25 +/**
1.26 +Mimimum length of a filename and mimimum length of a suffix.
1.27 +Note these two values are tied together.
1.28 +*/
1.29 +const TInt KInvNameAndMinSuffixLength = 2;
1.30 +
1.31 +#define ISDIGIT(c) (c-'0' >= 0 && c-'0' <= 9)
1.32 +
1.33 +_LIT(KAllDrives, "YXWVUTSRQPONMLKJIHGFEDCBAZ");
1.34 +
1.35 +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 };
1.36 +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 };
1.37 +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 };
1.38 +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 };
1.39 +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 };
1.40 +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 };
1.41 +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 };
1.42 +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 };
1.43 +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 };
1.44 +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 };
1.45 +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 };
1.46 +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 };
1.47 +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 };
1.48 +LOCAL_C const TLanguage dp13[] = { ELangInternationalFrench,ELangFrench,ELangSwissFrench,ELangBelgianFrench,ELangCanadianFrench,ELangNone };
1.49 +LOCAL_C const TLanguage dp14[] = { ELangBelgianFrench, ELangFrench,ELangInternationalFrench,ELangSwissFrench,ELangCanadianFrench,ELangNone };
1.50 +LOCAL_C const TLanguage dp15[] = { ELangCanadianFrench, ELangFrench,ELangInternationalFrench,ELangSwissFrench,ELangBelgianFrench,ELangNone };
1.51 +LOCAL_C const TLanguage dp16[] = { ELangFrench,ELangInternationalFrench,ELangSwissFrench,ELangBelgianFrench,ELangCanadianFrench,ELangNone };
1.52 +LOCAL_C const TLanguage dp17[] = { ELangSwissFrench,ELangFrench,ELangInternationalFrench,ELangBelgianFrench,ELangCanadianFrench,ELangNone };
1.53 +LOCAL_C const TLanguage dp18[] = { ELangSwissGerman,ELangGerman,ELangAustrian,ELangNone };
1.54 +LOCAL_C const TLanguage dp19[] = { ELangAustrian,ELangGerman,ELangSwissGerman,ELangNone };
1.55 +LOCAL_C const TLanguage dp20[] = { ELangGerman,ELangSwissGerman,ELangAustrian,ELangNone };
1.56 +LOCAL_C const TLanguage dp21[] = { ELangSerbian,ELangCroatian,ELangNone };
1.57 +LOCAL_C const TLanguage dp22[] = { ELangCroatian,ELangSerbian,ELangNone };
1.58 +LOCAL_C const TLanguage dp23[] = { ELangRomanian,ELangMoldavian,ELangNone };
1.59 +LOCAL_C const TLanguage dp24[] = { ELangMoldavian,ELangRomanian,ELangNone };
1.60 +LOCAL_C const TLanguage dp25[] = { ELangBelgianFlemish,ELangDutch,ELangNone };
1.61 +LOCAL_C const TLanguage dp26[] = { ELangDutch,ELangBelgianFlemish,ELangNone };
1.62 +LOCAL_C const TLanguage dp27[] = { ELangAfrikaans,ELangDutch,ELangNone };
1.63 +LOCAL_C const TLanguage dp28[] = { ELangMalay_Apac,ELangMalay,ELangNone };
1.64 +LOCAL_C const TLanguage dp29[] = { ELangIndonesian_Apac,ELangIndonesian,ELangNone };
1.65 +LOCAL_C const TLanguage dp30[] = { ELangSpanish,ELangInternationalSpanish,ELangLatinAmericanSpanish,ELangNone };
1.66 +LOCAL_C const TLanguage dp31[] = { ELangLatinAmericanSpanish,ELangSpanish,ELangInternationalSpanish,ELangNone };
1.67 +LOCAL_C const TLanguage dp32[] = { ELangInternationalSpanish,ELangSpanish,ELangLatinAmericanSpanish,ELangNone };
1.68 +LOCAL_C const TLanguage dp33[] = { ELangCyprusGreek,ELangGreek,ELangNone };
1.69 +LOCAL_C const TLanguage dp34[] = { ELangGreek,ELangCyprusGreek,ELangNone };
1.70 +LOCAL_C const TLanguage dp35[] = { ELangSwissItalian,ELangItalian,ELangNone };
1.71 +LOCAL_C const TLanguage dp36[] = { ELangItalian,ELangSwissItalian,ELangNone };
1.72 +LOCAL_C const TLanguage dp37[] = { ELangBrazilianPortuguese,ELangPortuguese,ELangNone };
1.73 +LOCAL_C const TLanguage dp38[] = { ELangPortuguese,ELangBrazilianPortuguese,ELangNone };
1.74 +LOCAL_C const TLanguage dp39[] = { ELangFinlandSwedish,ELangSwedish,ELangNone };
1.75 +LOCAL_C const TLanguage dp40[] = { ELangSwedish,ELangFinlandSwedish,ELangNone };
1.76 +LOCAL_C const TLanguage dp41[] = { ELangCyprusTurkish,ELangTurkish,ELangNone };
1.77 +LOCAL_C const TLanguage dp42[] = { ELangTurkish,ELangCyprusTurkish,ELangNone };
1.78 +LOCAL_C const TLanguage dp43[] = { ELangHongKongChinese, ELangTaiwanChinese, ELangPrcChinese,ELangNone };
1.79 +LOCAL_C const TLanguage dp44[] = { ELangTaiwanChinese, ELangHongKongChinese,ELangPrcChinese,ELangNone };
1.80 +LOCAL_C const TLanguage dp45[] = { ELangPrcChinese, ELangHongKongChinese, ELangTaiwanChinese,ELangNone };
1.81 +LOCAL_C const TLanguage * const KEquivalentLists[] = { dp0, dp1, dp2, dp3, dp4, dp5, dp6,
1.82 + dp7, dp8, dp9, dp10, dp11, dp12, dp13, dp14, dp15, dp16, dp17,
1.83 + dp18, dp19, dp20, dp21, dp22, dp23, dp24, dp25, dp26, dp27,
1.84 + dp28, dp29, dp30, dp31, dp32, dp33, dp34, dp35, dp36, dp37,
1.85 + dp38, dp39, dp40, dp41, dp42, dp43, dp44, dp45};
1.86 +
1.87 +
1.88 +
1.89 +LOCAL_C TBool IsLanguageExtended(const TLanguage aLanguage)
1.90 + {
1.91 + // For compatibility reasons, ELangNone is 0xFFFF. However, it's not an extended language.
1.92 + if ((aLanguage==ELangNone) || ((static_cast<TUint>(aLanguage))<=KDialectMask))
1.93 + return EFalse;
1.94 + else
1.95 + return ETrue;
1.96 + }
1.97 +
1.98 +
1.99 +LOCAL_C TLanguage BaseLanguage(const TLanguage aLanguage)
1.100 + {
1.101 + if (IsLanguageExtended(aLanguage))
1.102 + return static_cast<TLanguage>(aLanguage & KDialectMask);
1.103 + else
1.104 + return aLanguage;
1.105 + }
1.106 +
1.107 +LOCAL_C TLanguage NextLanguage(TLanguage aLanguage)
1.108 +/** Returns the next best language to use after aLanguage,
1.109 +based on Symbian's base table of language near-equivalence.
1.110 +@internalAll */
1.111 + {
1.112 + switch (aLanguage)
1.113 + {
1.114 + case ELangAustralian:
1.115 + case ELangNewZealand:
1.116 + case ELangSouthAfricanEnglish:
1.117 + case ELangInternationalEnglish:
1.118 + case ELangAmerican:
1.119 + case ELangEnglish_Apac:
1.120 + case ELangEnglish_Taiwan:
1.121 + case ELangEnglish_HongKong:
1.122 + case ELangEnglish_Prc:
1.123 + case ELangEnglish_Japan:
1.124 + case ELangEnglish_Thailand:
1.125 + return ELangEnglish;
1.126 + case ELangCanadianEnglish:
1.127 + return ELangAmerican; // 2-stage downgrade
1.128 + case ELangSwissFrench:
1.129 + case ELangBelgianFrench:
1.130 + case ELangInternationalFrench:
1.131 + case ELangCanadianFrench:
1.132 + return ELangFrench;
1.133 + case ELangSwissGerman:
1.134 + case ELangAustrian:
1.135 + return ELangGerman;
1.136 + case ELangInternationalSpanish:
1.137 + case ELangLatinAmericanSpanish:
1.138 + return ELangSpanish;
1.139 + case ELangSwissItalian:
1.140 + return ELangItalian;
1.141 + case ELangFinlandSwedish:
1.142 + return ELangSwedish;
1.143 + case ELangCyprusTurkish:
1.144 + return ELangTurkish;
1.145 + case ELangBelgianFlemish:
1.146 + return ELangDutch;
1.147 + case ELangHongKongChinese:
1.148 + return ELangTaiwanChinese;
1.149 + case ELangCyprusGreek:
1.150 + return ELangGreek;
1.151 + case ELangMalay_Apac:
1.152 + return ELangMalay;
1.153 + case ELangBrazilianPortuguese:
1.154 + return ELangPortuguese;
1.155 + default:
1.156 + return ELangNone;
1.157 + }
1.158 + }
1.159 +
1.160 +
1.161 +void AddLanguage(TLanguagePath& aPath, TLanguage aNewLanguage)
1.162 +/** Add language to the language path if there is space.
1.163 +The first empty slot must have "ELangNone" in it. This will also be true
1.164 +on exit. */
1.165 + {
1.166 + TLanguage *p = aPath;
1.167 + const TLanguage *end = &(aPath[KMaxDowngradeLanguages]);
1.168 + while (p != end)
1.169 + {
1.170 + if (*p == aNewLanguage)
1.171 + // language already in list
1.172 + break;
1.173 + if (*p == ELangNone)
1.174 + {
1.175 + // found the end of the list
1.176 + p[0] = aNewLanguage;
1.177 + p[1] = ELangNone;
1.178 + break;
1.179 + }
1.180 + ++p;
1.181 + }
1.182 + return;
1.183 + }
1.184 +
1.185 +void MakeLanguageDowngradePath(TLanguagePath& aPath,
1.186 + TLanguage aCurrent, TLanguage aIdeal, const TLocale& aLocale)
1.187 + {
1.188 + TInt j = 0;
1.189 + if( aIdeal != ELangNone)
1.190 + {
1.191 + aPath[j++]=aIdeal;
1.192 + }
1.193 + aPath[j++] = aCurrent;
1.194 + aPath[j++] = ELangNone;
1.195 +
1.196 + if (aCurrent & ~KDialectMask)
1.197 + AddLanguage(aPath, static_cast<TLanguage>(aCurrent & KDialectMask));
1.198 +
1.199 + for (TInt i=0;i<=2;i++)
1.200 + {
1.201 + AddLanguage(aPath, aLocale.LanguageDowngrade(i));
1.202 + AddLanguage(aPath, BaseLanguage(aLocale.LanguageDowngrade(i)));
1.203 + }
1.204 +
1.205 + while (ELangNone != (aCurrent = NextLanguage(BaseLanguage(aCurrent))))
1.206 + AddLanguage(aPath, aCurrent);
1.207 + }
1.208 +
1.209 +
1.210 +
1.211 +//EXPORT_C
1.212 +void LangUtil::GetDowngradePathL(const RFs& aFs, const TLanguage aCurrentLanguage, RArray<TLanguage>& aLanguageArray){
1.213 +
1.214 + TLocale currentLocale;
1.215 + TNearestLanguageFileFinder languageDowngradePath(aFs);
1.216 + TLanguage idealLanguage=IdealLanguage();
1.217 + MakeLanguageDowngradePath(languageDowngradePath.iPath,aCurrentLanguage,idealLanguage, currentLocale);
1.218 + aLanguageArray.Reset();
1.219 + const TLanguage* p=languageDowngradePath.iPath;
1.220 + while (*p != ELangNone)
1.221 + {
1.222 + User::LeaveIfError(aLanguageArray.Append(*p));
1.223 + ++p;
1.224 + }
1.225 +
1.226 +}
1.227 +
1.228 +
1.229 +//EXPORT_C
1.230 +void LangUtil::GetEquivalentLanguageList(TLanguage aLang, TLanguagePath& aEquivalents){
1.231 +
1.232 + aEquivalents[0] = aLang;
1.233 + aEquivalents[1] = ELangNone;
1.234 + const TInt len = sizeof(KEquivalentLists) / sizeof(KEquivalentLists[0]);
1.235 + for (TInt i = 0; i < len; ++i)
1.236 + {
1.237 + const TLanguage *ptr = KEquivalentLists[i];
1.238 + if (ptr[0] == aLang)
1.239 + {
1.240 + TInt index = 1;
1.241 + while (ELangNone != *ptr)
1.242 + {
1.243 + aEquivalents[index++] = (TLanguage)*(++ptr);
1.244 + }
1.245 + aEquivalents[index] = ELangNone;
1.246 + break;
1.247 + } // end if ptr[0]
1.248 + } // end for i
1.249 +
1.250 +}
1.251 +
1.252 +
1.253 +
1.254 +//EXPORT_C
1.255 +TLanguage LangUtil::IdealLanguage(){
1.256 +
1.257 + TLanguage* langPtr=(TLanguage*)Dll::Tls();
1.258 +
1.259 + if( langPtr==NULL)
1.260 + {
1.261 + return(ELangNone);
1.262 + }
1.263 +
1.264 + return(*langPtr);
1.265 +
1.266 +}
1.267 +
1.268 +
1.269 +//EXPORT_C
1.270 +void LangUtil::NearestLanguageFile(const RFs& aFs, TFileName& aName){
1.271 +
1.272 + TLanguage language;
1.273 +
1.274 + NearestLanguageFile( aFs, aName, language);
1.275 +
1.276 + (void)language;
1.277 +
1.278 +}
1.279 +
1.280 +
1.281 +//EXPORT_C
1.282 +void LangUtil::NearestLanguageFile(const RFs& aFs, TFileName& aName, TLanguage& aLanguage){
1.283 +#if defined(DO_PROFILING)
1.284 + RDebug::ProfileReset(FIRST_PROFILE_INDEX, PROFILE_COUNT);
1.285 + RDebug::ProfileStart(PROFILE_INDEX_1);
1.286 +#endif
1.287 + TNearestLanguageFileFinder finder(aFs);
1.288 + TBool goodSuffix=finder.SetFileName(aName);
1.289 +
1.290 + // Only continue if the suffix is good.
1.291 + if(goodSuffix)
1.292 + {
1.293 + // add preset customised resource drive to drive list
1.294 + // Note that errors returned from AddCustomResourceDrive are ignored. This is because if
1.295 + // a custom resource drive has not been found we still want to continue on with searching
1.296 + // other drives according to our algorithm
1.297 + finder.AddCustomResourceDrive();
1.298 +
1.299 + TLocale locale;
1.300 + TLanguage idealLanguage;
1.301 + idealLanguage = IdealLanguage();
1.302 + MakeLanguageDowngradePath(finder.iPath, User::Language(), idealLanguage, locale);
1.303 + if (!finder.FindLanguageAndDrive()
1.304 + && KErrNone != finder.FindFirstLanguageFileAndDrive())
1.305 + finder.RepairFileName();
1.306 + aLanguage = finder.Language();
1.307 + }
1.308 +
1.309 +#if defined(DO_PROFILING)
1.310 + RDebug::ProfileEnd(PROFILE_INDEX_1);
1.311 + TProfile profile[PROFILE_COUNT];
1.312 + RDebug::ProfileResult(&profile[0], FIRST_PROFILE_INDEX, PROFILE_COUNT);
1.313 + if(goodSuffix)
1.314 + {
1.315 + 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);
1.316 + }
1.317 + else
1.318 + {
1.319 + 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);
1.320 + }
1.321 +#endif
1.322 +
1.323 +}
1.324 +
1.325 +
1.326 +//EXPORT_C
1.327 +void LangUtil::NearestLanguageFileV2(const RFs& aFs, TFileName& aName, TLanguage& aLanguage){
1.328 +
1.329 + TNearestLanguageFileFinder finder(aFs);
1.330 + TBool goodSuffix=finder.SetFileName(aName);
1.331 +
1.332 + // Continue only if the suffix is good.
1.333 + if(goodSuffix)
1.334 + {
1.335 + // add preset customised resource drive to drive list
1.336 + // Note that errors returned from AddCustomResourceDrive are ignored. This is because if
1.337 + // a custom resource drive has not been found we still want to continue on with searching
1.338 + // other drives according to our algorithm
1.339 + finder.AddCustomResourceDrive();
1.340 +
1.341 + GetEquivalentLanguageList(User::Language(), finder.iPath);
1.342 + if (!finder.FindLanguageAndDrive()
1.343 + && KErrNone != finder.FindFirstLanguageFileAndDrive())
1.344 + finder.RepairFileName();
1.345 + aLanguage = finder.Language();
1.346 + }
1.347 + else
1.348 + {
1.349 + aLanguage = ELangNone;
1.350 + }
1.351 +
1.352 +}
1.353 +
1.354 +
1.355 +//EXPORT_C
1.356 +void LangUtil::ReleaseIdealLanguage(){
1.357 +
1.358 + TLanguage* aLanguage=(TLanguage*)Dll::Tls();
1.359 + if( aLanguage==NULL)
1.360 + return;
1.361 +
1.362 + delete aLanguage;
1.363 + Dll::FreeTls();
1.364 +
1.365 +}
1.366 +
1.367 +
1.368 +//EXPORT_C
1.369 +TInt LangUtil::SetIdealLanguage(TLanguage aLanguage){
1.370 +TLanguage* langPtr=(TLanguage*)Dll::Tls();
1.371 + if( langPtr==NULL)
1.372 + {
1.373 + langPtr=(TLanguage*)User::Alloc(sizeof(TLanguage));
1.374 +
1.375 + if(!langPtr)
1.376 + return(KErrNoMemory);
1.377 +
1.378 + TInt ret=Dll::SetTls(langPtr);
1.379 +
1.380 + if(ret!=KErrNone)
1.381 + return(ret);
1.382 + }
1.383 + *langPtr=aLanguage;
1.384 + return(KErrNone);
1.385 +
1.386 +}
1.387 +
1.388 +TInt RRealDirectoryScanner::Open(RFs& aFs, const TDesC& aMatchPattern)
1.389 + {
1.390 + return iDir.Open(aFs, aMatchPattern,
1.391 + KEntryAttReadOnly | KEntryAttHidden | KEntryAttSystem | KEntryAttArchive);
1.392 + }
1.393 +
1.394 +TInt RRealDirectoryScanner::Next(TEntry& aOut)
1.395 + {
1.396 + return iDir.Read(aOut);
1.397 + }
1.398 +
1.399 +void RRealDirectoryScanner::Close()
1.400 + {
1.401 + iDir.Close();
1.402 + }
1.403 +
1.404 +/**
1.405 +Simply counts the number of numerical characters at the end of the name passed.
1.406 +
1.407 +@internalComponent
1.408 +@param aFilename The filename to parse
1.409 +
1.410 +@return Count of the numeric digits at the end of the name passed,
1.411 + e.g. x.r491 gives 3.
1.412 +*/
1.413 +TInt TNearestLanguageFileFinder::CountDigitsFromEnd(const TDesC& aFilename)
1.414 + {
1.415 + TInt digitCount = 0;
1.416 +
1.417 + for (TInt idx = aFilename.Length()-1; idx>=0 && ISDIGIT (aFilename [idx]); --idx)
1.418 + {
1.419 + ++digitCount;
1.420 + }
1.421 +
1.422 + return digitCount;
1.423 + }
1.424 +
1.425 +
1.426 +/**
1.427 +Counts the number of digits at the end of a filename.
1.428 +
1.429 +@internalComponent
1.430 +@param aFilename The filename to parse
1.431 +
1.432 +@return Count of the numeric digits at the end of the suffix,
1.433 + e.g. x.r491 gives 3.
1.434 + 0 if no numeric end of suffix,
1.435 + KErrBadName for an invalid filename,
1.436 + KErrNotSupported if the filename (minus path) is less
1.437 + than or equal to KInvNameAndMinSuffixLength in length
1.438 +*/
1.439 +TInt TNearestLanguageFileFinder::CountDigitsFromEndInSuffix (const TDesC& aFilename)
1.440 + {
1.441 + TInt digitCount = 0;
1.442 + TInt slashIdx = 0;
1.443 + TInt len = aFilename.Length ();
1.444 +
1.445 + // NOTE: We didn't use TChar here as they are too slow.
1.446 + // We also didn't use TParse as they are too large.
1.447 +
1.448 + // don't work on the path
1.449 + for (slashIdx=len-1; slashIdx >= 0 && aFilename[slashIdx] != '\\'; --slashIdx)
1.450 + {/*do nothing*/};
1.451 +
1.452 + // Get new length
1.453 + if (slashIdx>=0) {len = len-slashIdx-1;}
1.454 +
1.455 + // Initial test to see if filename legal size.
1.456 + if (len > KInvNameAndMinSuffixLength)
1.457 + {
1.458 + digitCount = CountDigitsFromEnd(aFilename);
1.459 +
1.460 + // Can't store something bigger or we'll panic!
1.461 + if (digitCount > KMaxSuffixLength)
1.462 + {
1.463 + digitCount = KErrBadName;
1.464 + }
1.465 + else
1.466 + // numeric filename, e.g. "1234".
1.467 + // No preceeding alpha character
1.468 + if (!(len-digitCount))
1.469 + {
1.470 + digitCount = KErrBadName;
1.471 + }
1.472 + }
1.473 + else
1.474 + {
1.475 + digitCount = KErrNotSupported;
1.476 + }
1.477 +
1.478 + return digitCount;
1.479 + }
1.480 +
1.481 +RDirectoryScanner& TNearestLanguageFileFinder::DirectoryScanner()
1.482 + {
1.483 + return iDirScanner;
1.484 + }
1.485 +
1.486 +TBool TNearestLanguageFileFinder::FileExists(const TDesC& aFileName) const
1.487 + {
1.488 + //return BaflUtils::FileExists(iFs, aFileName);
1.489 + TEntry entry;
1.490 + return(iFs.Entry(aFileName,entry)==KErrNone);
1.491 +
1.492 + }
1.493 +
1.494 +TBool TNearestLanguageFileFinder::FindDrive()
1.495 + {
1.496 + ASSERT(iFileName);
1.497 + TBool found=EFalse;
1.498 + TInt driveLength=iDrives.Length();
1.499 + for (TInt drive = 0; drive!=driveLength; ++drive)
1.500 + {
1.501 + (*iFileName)[0] = iDrives[drive];
1.502 + if (FileExists(*iFileName))
1.503 + {
1.504 + found=ETrue;
1.505 + break;
1.506 + }
1.507 + }
1.508 + return found;
1.509 + }
1.510 +
1.511 +TBool TNearestLanguageFileFinder::AppendLanguageCode(TLanguage aLanguage)
1.512 + {
1.513 + TInt rest = static_cast<TInt>(aLanguage);
1.514 +#ifdef _DEBUG
1.515 + _LIT(KErrorMessage, "Bafl");
1.516 +#endif
1.517 + __ASSERT_DEBUG(0 <= rest, User::Panic(KErrorMessage,KErrArgument));
1.518 + iFileName->SetLength(iBaseLength);
1.519 + const TInt remaining = iFileName->MaxLength() - iBaseLength;
1.520 + TInt soFar = 0;
1.521 + TBuf<1> num;
1.522 + num.Append('0');
1.523 + TBool appendLangSuccess = ETrue;
1.524 + TInt digitCount = 0;
1.525 + TInt digit = 0;
1.526 + while (rest)
1.527 + {
1.528 + if (remaining == soFar)
1.529 + {
1.530 + // no more room in descriptor- return rather than panic,
1.531 + // file cannot exist.
1.532 + iFileName->SetLength(iBaseLength);
1.533 + appendLangSuccess= EFalse;
1.534 + break;
1.535 + }
1.536 + // Convert the number to ASCII by consistantly getting the base 10 remainder to convert.
1.537 + // The number is updated minus the remainder for the next iteration.
1.538 + // eg (rest = 123) -> (12, r3) -> (1, r2) -> (0, r1)
1.539 + // Then insert the ASCII representation of the remainder into the filename end
1.540 + // so it appears the correct way round.
1.541 + // eg (filename.r) -> (filename.r3) -> (filename.r23) -> (filename.r123)
1.542 + digit = rest % 10;
1.543 + digitCount++;
1.544 + rest /= 10;
1.545 + num[0] = static_cast<TText16>(digit + '0');
1.546 + iFileName->Insert(iBaseLength, num);
1.547 +
1.548 + // Minimum suffix length is KInvNameAndMinSuffixLength
1.549 + // so we have to insert zeros to make this up.
1.550 + while (!rest && digitCount < KInvNameAndMinSuffixLength)
1.551 + {
1.552 + num[0] = static_cast<TText16>('0');
1.553 + iFileName->Insert(iBaseLength, num);
1.554 + ++digitCount;
1.555 + }
1.556 +
1.557 + ++soFar;
1.558 + }
1.559 +
1.560 + return appendLangSuccess;
1.561 + }
1.562 +
1.563 +
1.564 +TBool TNearestLanguageFileFinder::FindLanguageAndDrive()
1.565 +/** Search for files across all drives in all languages in the path plus the
1.566 +language-neutral file. */
1.567 + {
1.568 + ASSERT(iFileName);
1.569 + // No point appending if the suffix is bad
1.570 + for (const TLanguage* currentLang = iPath; *currentLang != ELangNone; ++currentLang)
1.571 + {
1.572 + if (AppendLanguageCode(*currentLang) && FindDrive())
1.573 + {
1.574 + iLanguage = *currentLang;
1.575 + return ETrue;
1.576 + }
1.577 + }
1.578 + // search for language-neutral file
1.579 + iFileName->SetLength(iBaseLength);
1.580 + iFileName->Append(iSuffix);
1.581 + return FindDrive();
1.582 + }
1.583 +
1.584 +TInt TNearestLanguageFileFinder::LanguageNumberFromFile(const TDesC& aFileName, const TDesC& aStem)
1.585 + {
1.586 + TInt lang = 0;
1.587 + TInt multiplier = 1;
1.588 + TInt leadingZeroCount = 0;
1.589 + TInt languageNumber = KErrNotFound;
1.590 + const TText* firstChar = aFileName.Ptr();
1.591 + const TText* lastChar = firstChar + aFileName.Length() - 1;
1.592 + const TText* currentChar = lastChar;
1.593 + // string cannot contain only numbers, because it must have a ':' in it
1.594 + while ('0' <= *currentChar && *currentChar <= '9')
1.595 + {
1.596 + if (*currentChar == '0')
1.597 + leadingZeroCount++;
1.598 + else
1.599 + {
1.600 + leadingZeroCount = 0;
1.601 + lang += multiplier * (*currentChar - '0');
1.602 + }
1.603 + multiplier *= 10;
1.604 + --currentChar;
1.605 + }
1.606 + TInt along=lastChar - currentChar;
1.607 + if (2 <= along)
1.608 + {
1.609 + // We have at least 2 digits at the end.
1.610 + // trim of bad leading zeros
1.611 + TInt maxTrim = along - 2;
1.612 + if (maxTrim < leadingZeroCount)
1.613 + {
1.614 + leadingZeroCount = maxTrim;
1.615 + }
1.616 + currentChar += leadingZeroCount;
1.617 + // we have at least 2 digits at the end but does the rest of it match the stem?
1.618 + TPtrC foundStem(firstChar, currentChar - firstChar + 1);
1.619 + //foundStem.CompareF(aStem.Right(foundStem.Length()))
1.620 + if (0 == foundStem.CompareF(aStem))
1.621 + {
1.622 + languageNumber=lang;
1.623 + }
1.624 + }
1.625 + return languageNumber;
1.626 + }
1.627 +
1.628 +TInt TNearestLanguageFileFinder::FindFirstLanguageFile(RFs& aFs)
1.629 + {
1.630 + ASSERT(iFileName);
1.631 + iFileName->SetLength(iBaseLength);
1.632 + TPtrC name(*iFileName);
1.633 + TParsePtrC nameToParse(name);
1.634 + TPtrC nameStem(nameToParse.NameAndExt());
1.635 + iFileName->Append('*');
1.636 + TInt bestLanguageMatch = KMaxTInt;
1.637 + RDirectoryScanner& scanner = DirectoryScanner();
1.638 + TInt err = scanner.Open(aFs, *iFileName);
1.639 + if (err != KErrNone)
1.640 + {
1.641 + return err;
1.642 + }
1.643 + TEntry entry;
1.644 + while (KErrNone == scanner.Next(entry))
1.645 + {
1.646 + TInt lang = LanguageNumberFromFile(entry.iName, nameStem);
1.647 + if (0 < lang && lang < bestLanguageMatch)
1.648 + {
1.649 + bestLanguageMatch = lang;
1.650 + }
1.651 + }
1.652 + scanner.Close();
1.653 + if (bestLanguageMatch != KMaxTInt)
1.654 + {
1.655 + iLanguage = static_cast<TLanguage>(bestLanguageMatch);
1.656 + AppendLanguageCode(static_cast<TLanguage>(bestLanguageMatch));
1.657 + return KErrNone;
1.658 + }
1.659 + return KErrNotFound;
1.660 + }
1.661 +
1.662 +// Try each drive for any language files
1.663 +// iFileName must have a directory specifier
1.664 +TInt TNearestLanguageFileFinder::FindFirstLanguageFileAndDrive()
1.665 + {
1.666 + ASSERT(iFileName);
1.667 + TInt findFirstResult=KErrNotFound;
1.668 + TInt driveLength=iDrives.Length();
1.669 + for (TInt drive = 0; drive != driveLength; ++drive)
1.670 + {
1.671 + (*iFileName)[0] = iDrives[drive];
1.672 + TInt err = FindFirstLanguageFile(CONST_CAST(RFs&,iFs));
1.673 + if (err == KErrNone || err == KErrNoMemory)
1.674 + {
1.675 + findFirstResult=err;
1.676 + break;
1.677 + }
1.678 + }
1.679 + return findFirstResult;
1.680 + }
1.681 +
1.682 +/**
1.683 +Invalid filenames are any filename whose length (minus path) must be greater
1.684 +than KInvNameAndMinSuffixLength, and whose form is purely numerical, i.e. '1234'
1.685 +*/
1.686 +TBool TNearestLanguageFileFinder::SetFileName(TFileName& aFileName)
1.687 + {
1.688 + iDrives.Zero();
1.689 + iFileName = &aFileName;
1.690 + iOriginalBaseLength = iFileName->Length();
1.691 +
1.692 + TInt suffixLength = CountDigitsFromEndInSuffix (aFileName);
1.693 +
1.694 + // No point trying for filenames thats are badly formed
1.695 + // or that are too large.
1.696 + if (suffixLength >= 0 &&
1.697 + KInvNameAndMinSuffixLength < iOriginalBaseLength)
1.698 + {
1.699 + if (suffixLength > 0)
1.700 + {
1.701 + // all of suffix to be replaced
1.702 + iSuffix = iFileName->Right(suffixLength);
1.703 + iOriginalBaseLength -= suffixLength;
1.704 + iFileName->SetLength(iOriginalBaseLength);
1.705 + }
1.706 + else
1.707 + {
1.708 + // No numerical part to suffix
1.709 + TInt periodIdx = 0;
1.710 +
1.711 + // Search for the period within range KInvNameAndMinSuffixLength
1.712 + // from the end. As this must work for all values of
1.713 + // KInvNameAndMinSuffixLength
1.714 + for (TInt i = iOriginalBaseLength-1;
1.715 + !periodIdx && i >= (iOriginalBaseLength-KInvNameAndMinSuffixLength-1);
1.716 + --i)
1.717 + {
1.718 + if ((*iFileName) [i] == '.')
1.719 + {
1.720 + periodIdx = i;
1.721 + }
1.722 + }
1.723 +
1.724 + // Don't handle files ending in a period.
1.725 + // This is because the behaviour is different between Windows
1.726 + // and Symbian Fs. In Windows it strips the period off.
1.727 + //
1.728 + // However, and this shouldn't happen as it is not shown
1.729 + // (in the documentation) to be valid.
1.730 + // Just try our best.
1.731 + if (periodIdx == iOriginalBaseLength-1)
1.732 + {
1.733 + iSuffix.Zero();
1.734 + return EFalse;
1.735 + }
1.736 + else
1.737 + if (periodIdx)
1.738 + {
1.739 + // If there are KInvNameAndMinSuffixLength chars after the period
1.740 + // simply replace them.
1.741 + TInt right = iOriginalBaseLength-periodIdx-1;
1.742 + iSuffix = iFileName->Right(right);
1.743 + iOriginalBaseLength -= right;
1.744 + iFileName->SetLength(iOriginalBaseLength);
1.745 + }
1.746 + else
1.747 + {
1.748 + // Make the suffix start from KInvNameAndMinSuffixLength
1.749 + // from the right
1.750 + TInt right = KInvNameAndMinSuffixLength;
1.751 + iSuffix = iFileName->Right(right);
1.752 + iOriginalBaseLength -= right;
1.753 + iFileName->SetLength(iOriginalBaseLength);
1.754 + }
1.755 + }
1.756 + }
1.757 + else
1.758 + {
1.759 + // bad or no suffix - treat the same
1.760 + iSuffix.Zero();
1.761 + return EFalse;
1.762 + }
1.763 +
1.764 + // For filenames with no drive letter prefix and also for filenames
1.765 + // shorter than the drive letter length, i.e. with no drive
1.766 + // information, insert it.
1.767 + // Handles if the user simply enters the drive, e.g. "c:".
1.768 + if (iOriginalBaseLength < KMaxDriveName || (*iFileName)[1] != ':')
1.769 + {
1.770 + // Set up the default if none supplied and make room in the filename
1.771 + // array to contain a drive specification. Set initial drive letter to -1
1.772 + // so the iFileName is repaired before exited
1.773 + iInitialDriveLetter = -1;
1.774 + iFileName->Insert(0, _L("_:"));
1.775 + iDrives.Append('Z');
1.776 + }
1.777 + else
1.778 + {
1.779 + // Use the drive supplied inthe aName to NearestLanguageFile()
1.780 + iInitialDriveLetter = (*iFileName)[0];
1.781 + iDrives.Append(iInitialDriveLetter);
1.782 + }
1.783 +
1.784 + iBaseLength = iFileName->Length();
1.785 +
1.786 + return ETrue;
1.787 + }
1.788 +
1.789 +
1.790 +TLanguage TNearestLanguageFileFinder::Language()
1.791 + {
1.792 + return iLanguage;
1.793 + }
1.794 +
1.795 +TNearestLanguageFileFinder::TNearestLanguageFileFinder(
1.796 + const RFs& aFs)
1.797 + : iFs(aFs), iFileName(0), iLanguage(ELangNone)
1.798 + {
1.799 + }
1.800 +
1.801 +void TNearestLanguageFileFinder::RepairFileName()
1.802 + {
1.803 + ASSERT(iFileName);
1.804 + iFileName->SetLength(iBaseLength);
1.805 + if (iInitialDriveLetter == -1)
1.806 + iFileName->Delete(0, 2);
1.807 + else
1.808 + (*iFileName)[0] = static_cast<TText>(iInitialDriveLetter);
1.809 + iFileName->SetLength(iOriginalBaseLength);
1.810 + iFileName->Append(iSuffix);
1.811 + }
1.812 +
1.813 +
1.814 +/**
1.815 +Add the custom resource drive to the start of the iDrives string.
1.816 +
1.817 +The custom resource drive is a preset writeable drive on which customised
1.818 +resource files may be present. This drive takes priority over the other
1.819 +drives when searching for language files.
1.820 +
1.821 +@return KErrNone if iDrives string was successfully modified; KErrAlreadyExists
1.822 +if the drive is already present in the string; otherwise one of
1.823 +the other system-wide error codes (iDrives will be unmodified).
1.824 +*/
1.825 +TInt TNearestLanguageFileFinder::AddCustomResourceDrive()
1.826 + {
1.827 + TInt drive = GetCustomResourceDriveNumber();
1.828 + if (drive<0)
1.829 + return drive;
1.830 +
1.831 + // if drive not already in drive list
1.832 + if (iDrives.LocateF('A' + drive) < 0)
1.833 + {
1.834 + // add it
1.835 + _LIT(KDrivePlaceholder, "_");
1.836 + iDrives.Insert(0, KDrivePlaceholder);
1.837 + iDrives[0] = 'A' + drive;
1.838 + return KErrNone;
1.839 + }
1.840 + else
1.841 + return KErrAlreadyExists;
1.842 + }
1.843 +
1.844 +
1.845 +void TNearestLanguageFileFinder::AddAllDrives()
1.846 + {
1.847 + ASSERT(iDrives.Length() < 2);
1.848 + if (iDrives.Length() == 0)
1.849 + {
1.850 + iDrives = KAllDrives;
1.851 + return;
1.852 + }
1.853 + TInt pos = KAllDrives().LocateF(iDrives[0]);
1.854 + if (pos < 0)
1.855 + {
1.856 + iDrives = KAllDrives;
1.857 + return;
1.858 + }
1.859 + iDrives.Append(KAllDrives().Left(pos));
1.860 + iDrives.Append(KAllDrives().Mid(pos + 1));
1.861 + }
1.862 +
1.863 +
1.864 +/**
1.865 +Get the value of the custom resource drive.
1.866 +
1.867 +The custom resource drive is a preset writeable drive on which customised language resource
1.868 +files can reside. The drive number is accessed via the HAL attribute ECustomResourceDrive.
1.869 +It is then returned if it has been defined as a valid drive no.
1.870 +Otherwise for backward compatibility reasons an attempt is then made to access the system
1.871 +drive HAL attribute instead. This drive number is returned if it has been defined as a valid
1.872 +drive number.
1.873 +Otherwise if neither a valid ECustomResourceDrive or ESystemDrive exists then KErrNotFound
1.874 +is returned.
1.875 +
1.876 +Note that the ESystemDrive HAL attribute has been deprecated. It is accessed here to cater
1.877 +for existing implementations which still expect it to be used.
1.878 +
1.879 +@return The drive number (corresponding to a TDriveNumber value) if successful;
1.880 +KErrNotFound if neither a valid ECustomResourceDrive or a valid ESystemDrive HAL attribute
1.881 +is defined;
1.882 +
1.883 +@see HAL::ECustomResourceDrive
1.884 +@see HAL::ESystemDrive
1.885 +*/
1.886 +TInt TNearestLanguageFileFinder::GetCustomResourceDriveNumber() const
1.887 + {
1.888 + TInt drive = KErrNotFound;
1.889 +
1.890 + // access custom resource drive attribute
1.891 + if (HAL::Get(HAL::ECustomResourceDrive, drive) == KErrNone)
1.892 + {
1.893 + // check that drive is valid
1.894 + if (drive>=EDriveA && drive<=EDriveZ)
1.895 + return drive;
1.896 + }
1.897 +
1.898 + // access system drive attribute
1.899 + // (Note that ESystemDrive is deprecated. It is checked here
1.900 + // solely for backward compatibility reasons.)
1.901 + if (HAL::Get(HAL::ESystemDrive, drive) == KErrNone)
1.902 + {
1.903 + // check that drive is valid
1.904 + if (drive>=EDriveA && drive<=EDriveZ)
1.905 + return drive;
1.906 + }
1.907 +
1.908 + return KErrNotFound;
1.909 + }
1.910 +
1.911 +