Update contrib.
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".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
16 #include <bafl/langutil.h>
19 #include "LangUtilImpl.h"
23 Mimimum length of a filename and mimimum length of a suffix.
24 Note these two values are tied together.
26 const TInt KInvNameAndMinSuffixLength = 2;
28 #define ISDIGIT(c) (c-'0' >= 0 && c-'0' <= 9)
30 _LIT(KAllDrives, "YXWVUTSRQPONMLKJIHGFEDCBAZ");
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};
86 LOCAL_C TBool IsLanguageExtended(const TLanguage aLanguage)
88 // For compatibility reasons, ELangNone is 0xFFFF. However, it's not an extended language.
89 if ((aLanguage==ELangNone) || ((static_cast<TUint>(aLanguage))<=KDialectMask))
96 LOCAL_C TLanguage BaseLanguage(const TLanguage aLanguage)
98 if (IsLanguageExtended(aLanguage))
99 return static_cast<TLanguage>(aLanguage & KDialectMask);
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.
111 case ELangAustralian:
112 case ELangNewZealand:
113 case ELangSouthAfricanEnglish:
114 case ELangInternationalEnglish:
116 case ELangEnglish_Apac:
117 case ELangEnglish_Taiwan:
118 case ELangEnglish_HongKong:
119 case ELangEnglish_Prc:
120 case ELangEnglish_Japan:
121 case ELangEnglish_Thailand:
123 case ELangCanadianEnglish:
124 return ELangAmerican; // 2-stage downgrade
125 case ELangSwissFrench:
126 case ELangBelgianFrench:
127 case ELangInternationalFrench:
128 case ELangCanadianFrench:
130 case ELangSwissGerman:
133 case ELangInternationalSpanish:
134 case ELangLatinAmericanSpanish:
136 case ELangSwissItalian:
138 case ELangFinlandSwedish:
140 case ELangCyprusTurkish:
142 case ELangBelgianFlemish:
144 case ELangHongKongChinese:
145 return ELangTaiwanChinese;
146 case ELangCyprusGreek:
148 case ELangMalay_Apac:
150 case ELangBrazilianPortuguese:
151 return ELangPortuguese;
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
163 TLanguage *p = aPath;
164 const TLanguage *end = &(aPath[KMaxDowngradeLanguages]);
167 if (*p == aNewLanguage)
168 // language already in list
172 // found the end of the list
182 void MakeLanguageDowngradePath(TLanguagePath& aPath,
183 TLanguage aCurrent, TLanguage aIdeal, const TLocale& aLocale)
186 if( aIdeal != ELangNone)
190 aPath[j++] = aCurrent;
191 aPath[j++] = ELangNone;
193 if (aCurrent & ~KDialectMask)
194 AddLanguage(aPath, static_cast<TLanguage>(aCurrent & KDialectMask));
196 for (TInt i=0;i<=2;i++)
198 AddLanguage(aPath, aLocale.LanguageDowngrade(i));
199 AddLanguage(aPath, BaseLanguage(aLocale.LanguageDowngrade(i)));
202 while (ELangNone != (aCurrent = NextLanguage(BaseLanguage(aCurrent))))
203 AddLanguage(aPath, aCurrent);
209 void LangUtil::GetDowngradePathL(const RFs& aFs, const TLanguage aCurrentLanguage, RArray<TLanguage>& aLanguageArray){
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)
219 User::LeaveIfError(aLanguageArray.Append(*p));
227 void LangUtil::GetEquivalentLanguageList(TLanguage aLang, TLanguagePath& aEquivalents){
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)
234 const TLanguage *ptr = KEquivalentLists[i];
238 while (ELangNone != *ptr)
240 aEquivalents[index++] = (TLanguage)*(++ptr);
242 aEquivalents[index] = ELangNone;
252 TLanguage LangUtil::IdealLanguage(){
254 TLanguage* langPtr=(TLanguage*)Dll::Tls();
267 void LangUtil::NearestLanguageFile(const RFs& aFs, TFileName& aName){
271 NearestLanguageFile( aFs, aName, language);
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);
284 TNearestLanguageFileFinder finder(aFs);
285 TBool goodSuffix=finder.SetFileName(aName);
287 // Only continue if the suffix is good.
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();
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();
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);
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);
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);
324 void LangUtil::NearestLanguageFileV2(const RFs& aFs, TFileName& aName, TLanguage& aLanguage){
326 TNearestLanguageFileFinder finder(aFs);
327 TBool goodSuffix=finder.SetFileName(aName);
329 // Continue only if the suffix is good.
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();
338 GetEquivalentLanguageList(User::Language(), finder.iPath);
339 if (!finder.FindLanguageAndDrive()
340 && KErrNone != finder.FindFirstLanguageFileAndDrive())
341 finder.RepairFileName();
342 aLanguage = finder.Language();
346 aLanguage = ELangNone;
353 void LangUtil::ReleaseIdealLanguage(){
355 TLanguage* aLanguage=(TLanguage*)Dll::Tls();
366 TInt LangUtil::SetIdealLanguage(TLanguage aLanguage){
367 TLanguage* langPtr=(TLanguage*)Dll::Tls();
370 langPtr=(TLanguage*)User::Alloc(sizeof(TLanguage));
373 return(KErrNoMemory);
375 TInt ret=Dll::SetTls(langPtr);
385 TInt RRealDirectoryScanner::Open(RFs& aFs, const TDesC& aMatchPattern)
387 return iDir.Open(aFs, aMatchPattern,
388 KEntryAttReadOnly | KEntryAttHidden | KEntryAttSystem | KEntryAttArchive);
391 TInt RRealDirectoryScanner::Next(TEntry& aOut)
393 return iDir.Read(aOut);
396 void RRealDirectoryScanner::Close()
402 Simply counts the number of numerical characters at the end of the name passed.
405 @param aFilename The filename to parse
407 @return Count of the numeric digits at the end of the name passed,
410 TInt TNearestLanguageFileFinder::CountDigitsFromEnd(const TDesC& aFilename)
414 for (TInt idx = aFilename.Length()-1; idx>=0 && ISDIGIT (aFilename [idx]); --idx)
424 Counts the number of digits at the end of a filename.
427 @param aFilename The filename to parse
429 @return Count of the numeric digits at the end of the suffix,
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
436 TInt TNearestLanguageFileFinder::CountDigitsFromEndInSuffix (const TDesC& aFilename)
440 TInt len = aFilename.Length ();
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.
445 // don't work on the path
446 for (slashIdx=len-1; slashIdx >= 0 && aFilename[slashIdx] != '\\'; --slashIdx)
450 if (slashIdx>=0) {len = len-slashIdx-1;}
452 // Initial test to see if filename legal size.
453 if (len > KInvNameAndMinSuffixLength)
455 digitCount = CountDigitsFromEnd(aFilename);
457 // Can't store something bigger or we'll panic!
458 if (digitCount > KMaxSuffixLength)
460 digitCount = KErrBadName;
463 // numeric filename, e.g. "1234".
464 // No preceeding alpha character
465 if (!(len-digitCount))
467 digitCount = KErrBadName;
472 digitCount = KErrNotSupported;
478 RDirectoryScanner& TNearestLanguageFileFinder::DirectoryScanner()
483 TBool TNearestLanguageFileFinder::FileExists(const TDesC& aFileName) const
485 //return BaflUtils::FileExists(iFs, aFileName);
487 return(iFs.Entry(aFileName,entry)==KErrNone);
491 TBool TNearestLanguageFileFinder::FindDrive()
495 TInt driveLength=iDrives.Length();
496 for (TInt drive = 0; drive!=driveLength; ++drive)
498 (*iFileName)[0] = iDrives[drive];
499 if (FileExists(*iFileName))
508 TBool TNearestLanguageFileFinder::AppendLanguageCode(TLanguage aLanguage)
510 TInt rest = static_cast<TInt>(aLanguage);
512 _LIT(KErrorMessage, "Bafl");
514 __ASSERT_DEBUG(0 <= rest, User::Panic(KErrorMessage,KErrArgument));
515 iFileName->SetLength(iBaseLength);
516 const TInt remaining = iFileName->MaxLength() - iBaseLength;
520 TBool appendLangSuccess = ETrue;
525 if (remaining == soFar)
527 // no more room in descriptor- return rather than panic,
528 // file cannot exist.
529 iFileName->SetLength(iBaseLength);
530 appendLangSuccess= EFalse;
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)
542 num[0] = static_cast<TText16>(digit + '0');
543 iFileName->Insert(iBaseLength, num);
545 // Minimum suffix length is KInvNameAndMinSuffixLength
546 // so we have to insert zeros to make this up.
547 while (!rest && digitCount < KInvNameAndMinSuffixLength)
549 num[0] = static_cast<TText16>('0');
550 iFileName->Insert(iBaseLength, num);
557 return appendLangSuccess;
561 TBool TNearestLanguageFileFinder::FindLanguageAndDrive()
562 /** Search for files across all drives in all languages in the path plus the
563 language-neutral file. */
566 // No point appending if the suffix is bad
567 for (const TLanguage* currentLang = iPath; *currentLang != ELangNone; ++currentLang)
569 if (AppendLanguageCode(*currentLang) && FindDrive())
571 iLanguage = *currentLang;
575 // search for language-neutral file
576 iFileName->SetLength(iBaseLength);
577 iFileName->Append(iSuffix);
581 TInt TNearestLanguageFileFinder::LanguageNumberFromFile(const TDesC& aFileName, const TDesC& aStem)
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')
593 if (*currentChar == '0')
597 leadingZeroCount = 0;
598 lang += multiplier * (*currentChar - '0');
603 TInt along=lastChar - currentChar;
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)
611 leadingZeroCount = maxTrim;
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))
622 return languageNumber;
625 TInt TNearestLanguageFileFinder::FindFirstLanguageFile(RFs& aFs)
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);
641 while (KErrNone == scanner.Next(entry))
643 TInt lang = LanguageNumberFromFile(entry.iName, nameStem);
644 if (0 < lang && lang < bestLanguageMatch)
646 bestLanguageMatch = lang;
650 if (bestLanguageMatch != KMaxTInt)
652 iLanguage = static_cast<TLanguage>(bestLanguageMatch);
653 AppendLanguageCode(static_cast<TLanguage>(bestLanguageMatch));
659 // Try each drive for any language files
660 // iFileName must have a directory specifier
661 TInt TNearestLanguageFileFinder::FindFirstLanguageFileAndDrive()
664 TInt findFirstResult=KErrNotFound;
665 TInt driveLength=iDrives.Length();
666 for (TInt drive = 0; drive != driveLength; ++drive)
668 (*iFileName)[0] = iDrives[drive];
669 TInt err = FindFirstLanguageFile(CONST_CAST(RFs&,iFs));
670 if (err == KErrNone || err == KErrNoMemory)
676 return findFirstResult;
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'
683 TBool TNearestLanguageFileFinder::SetFileName(TFileName& aFileName)
686 iFileName = &aFileName;
687 iOriginalBaseLength = iFileName->Length();
689 TInt suffixLength = CountDigitsFromEndInSuffix (aFileName);
691 // No point trying for filenames thats are badly formed
692 // or that are too large.
693 if (suffixLength >= 0 &&
694 KInvNameAndMinSuffixLength < iOriginalBaseLength)
696 if (suffixLength > 0)
698 // all of suffix to be replaced
699 iSuffix = iFileName->Right(suffixLength);
700 iOriginalBaseLength -= suffixLength;
701 iFileName->SetLength(iOriginalBaseLength);
705 // No numerical part to suffix
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);
715 if ((*iFileName) [i] == '.')
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.
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)
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);
745 // Make the suffix start from KInvNameAndMinSuffixLength
747 TInt right = KInvNameAndMinSuffixLength;
748 iSuffix = iFileName->Right(right);
749 iOriginalBaseLength -= right;
750 iFileName->SetLength(iOriginalBaseLength);
756 // bad or no suffix - treat the same
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] != ':')
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("_:"));
776 // Use the drive supplied inthe aName to NearestLanguageFile()
777 iInitialDriveLetter = (*iFileName)[0];
778 iDrives.Append(iInitialDriveLetter);
781 iBaseLength = iFileName->Length();
787 TLanguage TNearestLanguageFileFinder::Language()
792 TNearestLanguageFileFinder::TNearestLanguageFileFinder(
794 : iFs(aFs), iFileName(0), iLanguage(ELangNone)
798 void TNearestLanguageFileFinder::RepairFileName()
801 iFileName->SetLength(iBaseLength);
802 if (iInitialDriveLetter == -1)
803 iFileName->Delete(0, 2);
805 (*iFileName)[0] = static_cast<TText>(iInitialDriveLetter);
806 iFileName->SetLength(iOriginalBaseLength);
807 iFileName->Append(iSuffix);
812 Add the custom resource drive to the start of the iDrives string.
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.
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).
822 TInt TNearestLanguageFileFinder::AddCustomResourceDrive()
824 TInt drive = GetCustomResourceDriveNumber();
828 // if drive not already in drive list
829 if (iDrives.LocateF('A' + drive) < 0)
832 _LIT(KDrivePlaceholder, "_");
833 iDrives.Insert(0, KDrivePlaceholder);
834 iDrives[0] = 'A' + drive;
838 return KErrAlreadyExists;
842 void TNearestLanguageFileFinder::AddAllDrives()
844 ASSERT(iDrives.Length() < 2);
845 if (iDrives.Length() == 0)
847 iDrives = KAllDrives;
850 TInt pos = KAllDrives().LocateF(iDrives[0]);
853 iDrives = KAllDrives;
856 iDrives.Append(KAllDrives().Left(pos));
857 iDrives.Append(KAllDrives().Mid(pos + 1));
862 Get the value of the custom resource drive.
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
870 Otherwise if neither a valid ECustomResourceDrive or ESystemDrive exists then KErrNotFound
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.
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
880 @see HAL::ECustomResourceDrive
881 @see HAL::ESystemDrive
883 TInt TNearestLanguageFileFinder::GetCustomResourceDriveNumber() const
885 TInt drive = KErrNotFound;
887 // access custom resource drive attribute
888 if (HAL::Get(HAL::ECustomResourceDrive, drive) == KErrNone)
890 // check that drive is valid
891 if (drive>=EDriveA && drive<=EDriveZ)
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)
900 // check that drive is valid
901 if (drive>=EDriveA && drive<=EDriveZ)