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