\r
**/\r
UINTN\r
-EFIAPI\r
GetIndexFromSupportedLangCodes(\r
IN CHAR8 *SupportedLang,\r
IN CHAR8 *Lang,\r
) \r
{\r
UINTN Index;\r
- UINT32 CompareLength;\r
- CHAR8 *Supported;\r
+ UINTN CompareLength;\r
+ UINTN LanguageLength;\r
\r
- Index = 0;\r
- Supported = SupportedLang;\r
if (Iso639Language) {\r
- CompareLength = 3;\r
+ CompareLength = ISO_639_2_ENTRY_SIZE;\r
for (Index = 0; Index < AsciiStrLen (SupportedLang); Index += CompareLength) {\r
if (AsciiStrnCmp (Lang, SupportedLang + Index, CompareLength) == 0) {\r
//\r
//\r
// Compare RFC4646 language code\r
//\r
- while (*Supported != '\0') {\r
+ Index = 0;\r
+ for (LanguageLength = 0; Lang[LanguageLength] != '\0'; LanguageLength++);\r
+\r
+ for (Index = 0; *SupportedLang != '\0'; Index++, SupportedLang += CompareLength) {\r
//\r
- // take semicolon as delimitation, sequentially traverse supported language codes.\r
+ // Skip ';' characters in SupportedLang\r
//\r
- for (CompareLength = 0; *Supported != ';' && *Supported != '\0'; CompareLength++) {\r
- Supported++;\r
- }\r
- if (AsciiStrnCmp (Lang, Supported - CompareLength, CompareLength) == 0) {\r
+ for (; *SupportedLang != '\0' && *SupportedLang == ';'; SupportedLang++);\r
+ //\r
+ // Determine the length of the next language code in SupportedLang\r
+ //\r
+ for (CompareLength = 0; SupportedLang[CompareLength] != '\0' && SupportedLang[CompareLength] != ';'; CompareLength++);\r
+ \r
+ if ((CompareLength == LanguageLength) && \r
+ (AsciiStrnCmp (Lang, SupportedLang, CompareLength) == 0)) {\r
//\r
// Successfully find the index of Lang string in SupportedLang string.\r
//\r
return Index;\r
}\r
- Index++;\r
}\r
ASSERT (FALSE);\r
return 0;\r
\r
**/\r
CHAR8 *\r
-EFIAPI\r
GetLangFromSupportedLangCodes (\r
IN CHAR8 *SupportedLang,\r
IN UINTN Index,\r
)\r
{\r
UINTN SubIndex;\r
- UINT32 CompareLength;\r
+ UINTN CompareLength;\r
CHAR8 *Supported;\r
\r
SubIndex = 0;\r
// As this code will be invoked in RUNTIME, therefore there is not memory allocate/free operation.\r
// In driver entry, it pre-allocates a runtime attribute memory to accommodate this string.\r
//\r
- CompareLength = 3;\r
- SetMem (mGlobal->Lang, sizeof(mGlobal->Lang), 0);\r
+ CompareLength = ISO_639_2_ENTRY_SIZE;\r
+ mGlobal->Lang[CompareLength] = '\0';\r
return CopyMem (mGlobal->Lang, SupportedLang + Index * CompareLength, CompareLength);\r
- \r
+\r
} else {\r
while (TRUE) {\r
//\r
// As this code will be invoked in RUNTIME, therefore there is not memory allocate/free operation.\r
// In driver entry, it pre-allocates a runtime attribute memory to accommodate this string.\r
//\r
- SetMem (mGlobal->PlatformLang, sizeof (mGlobal->PlatformLang), 0);\r
+ mGlobal->PlatformLang[CompareLength] = '\0';\r
return CopyMem (mGlobal->PlatformLang, Supported - CompareLength, CompareLength);\r
}\r
SubIndex++;\r
}\r
}\r
\r
+/**\r
+ Returns a pointer to an allocated buffer that contains the best matching language \r
+ from a set of supported languages. \r
+ \r
+ This function supports both ISO 639-2 and RFC 4646 language codes, but language \r
+ code types may not be mixed in a single call to this function. This function\r
+ supports a variable argument list that allows the caller to pass in a prioritized\r
+ list of language codes to test against all the language codes in SupportedLanguages.\r
+\r
+ If SupportedLanguages is NULL, then ASSERT().\r
+\r
+ @param[in] SupportedLanguages A pointer to a Null-terminated ASCII string that\r
+ contains a set of language codes in the format \r
+ specified by Iso639Language.\r
+ @param[in] Iso639Language If TRUE, then all language codes are assumed to be\r
+ in ISO 639-2 format. If FALSE, then all language\r
+ codes are assumed to be in RFC 4646 language format\r
+ @param[in] ... A variable argument list that contains pointers to \r
+ Null-terminated ASCII strings that contain one or more\r
+ language codes in the format specified by Iso639Language.\r
+ The first language code from each of these language\r
+ code lists is used to determine if it is an exact or\r
+ close match to any of the language codes in \r
+ SupportedLanguages. Close matches only apply to RFC 4646\r
+ language codes, and the matching algorithm from RFC 4647\r
+ is used to determine if a close match is present. If \r
+ an exact or close match is found, then the matching\r
+ language code from SupportedLanguages is returned. If\r
+ no matches are found, then the next variable argument\r
+ parameter is evaluated. The variable argument list \r
+ is terminated by a NULL.\r
+\r
+ @retval NULL The best matching language could not be found in SupportedLanguages.\r
+ @retval NULL There are not enough resources available to return the best matching \r
+ language.\r
+ @retval Other A pointer to a Null-terminated ASCII string that is the best matching \r
+ language in SupportedLanguages.\r
+\r
+**/\r
+CHAR8 *\r
+VariableGetBestLanguage (\r
+ IN CONST CHAR8 *SupportedLanguages, \r
+ IN BOOLEAN Iso639Language,\r
+ ...\r
+ )\r
+{\r
+ VA_LIST Args;\r
+ CHAR8 *Language;\r
+ UINTN CompareLength;\r
+ UINTN LanguageLength;\r
+ CONST CHAR8 *Supported;\r
+ CHAR8 *Buffer;\r
+\r
+ ASSERT (SupportedLanguages != NULL);\r
+\r
+ VA_START (Args, Iso639Language);\r
+ while ((Language = VA_ARG (Args, CHAR8 *)) != NULL) {\r
+ //\r
+ // Default to ISO 639-2 mode\r
+ //\r
+ CompareLength = 3;\r
+ LanguageLength = MIN (3, AsciiStrLen (Language));\r
+\r
+ //\r
+ // If in RFC 4646 mode, then determine the length of the first RFC 4646 language code in Language\r
+ //\r
+ if (!Iso639Language) {\r
+ for (LanguageLength = 0; Language[LanguageLength] != 0 && Language[LanguageLength] != ';'; LanguageLength++);\r
+ }\r
+\r
+ //\r
+ // Trim back the length of Language used until it is empty\r
+ //\r
+ while (LanguageLength > 0) {\r
+ //\r
+ // Loop through all language codes in SupportedLanguages\r
+ //\r
+ for (Supported = SupportedLanguages; *Supported != '\0'; Supported += CompareLength) {\r
+ //\r
+ // In RFC 4646 mode, then Loop through all language codes in SupportedLanguages\r
+ //\r
+ if (!Iso639Language) {\r
+ //\r
+ // Skip ';' characters in Supported\r
+ //\r
+ for (; *Supported != '\0' && *Supported == ';'; Supported++);\r
+ //\r
+ // Determine the length of the next language code in Supported\r
+ //\r
+ for (CompareLength = 0; Supported[CompareLength] != 0 && Supported[CompareLength] != ';'; CompareLength++);\r
+ //\r
+ // If Language is longer than the Supported, then skip to the next language\r
+ //\r
+ if (LanguageLength > CompareLength) {\r
+ continue;\r
+ }\r
+ }\r
+ //\r
+ // See if the first LanguageLength characters in Supported match Language\r
+ //\r
+ if (AsciiStrnCmp (Supported, Language, LanguageLength) == 0) {\r
+ VA_END (Args);\r
+\r
+ Buffer = Iso639Language ? mGlobal->Lang : mGlobal->PlatformLang;\r
+ Buffer[CompareLength] = '\0';\r
+ return CopyMem (Buffer, Supported, CompareLength);\r
+ }\r
+ }\r
+\r
+ if (Iso639Language) {\r
+ //\r
+ // If ISO 639 mode, then each language can only be tested once\r
+ //\r
+ LanguageLength = 0;\r
+ } else {\r
+ //\r
+ // If RFC 4646 mode, then trim Language from the right to the next '-' character \r
+ //\r
+ for (LanguageLength--; LanguageLength > 0 && Language[LanguageLength] != '-'; LanguageLength--);\r
+ }\r
+ }\r
+ }\r
+ VA_END (Args);\r
+\r
+ //\r
+ // No matches were found \r
+ //\r
+ return NULL;\r
+}\r
+\r
/**\r
Hook the operations in PlatformLangCodes, LangCodes, PlatformLang and Lang.\r
\r
\r
@param[in] DataSize Size of data. 0 means delete\r
\r
- @retval EFI_SUCCESS auto update operation is successful.\r
-\r
**/\r
-EFI_STATUS\r
-EFIAPI\r
+VOID\r
AutoUpdateLangVariable(\r
IN CHAR16 *VariableName,\r
IN VOID *Data,\r
IN UINTN DataSize\r
)\r
{\r
- EFI_STATUS Status;\r
- CHAR8 *BestPlatformLang;\r
- CHAR8 *BestLang;\r
- UINTN Index;\r
- UINT32 Attributes;\r
+ EFI_STATUS Status;\r
+ CHAR8 *BestPlatformLang;\r
+ CHAR8 *BestLang;\r
+ UINTN Index;\r
+ UINT32 Attributes;\r
VARIABLE_POINTER_TRACK Variable;\r
+ BOOLEAN SetLanguageCodes;\r
\r
//\r
- // According to UEFI spec, "Lang" and "PlatformLang" is NV|BS|RT attributions.\r
+ // Don't do updates for delete operation\r
//\r
- Attributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;\r
+ if (DataSize == 0) {\r
+ return;\r
+ }\r
+\r
+ SetLanguageCodes = FALSE;\r
\r
if (StrCmp (VariableName, L"PlatformLangCodes") == 0) {\r
+ //\r
+ // PlatformLangCodes is a volatile variable, so it can not be updated at runtime.\r
+ //\r
+ if (EfiAtRuntime ()) {\r
+ return;\r
+ }\r
+\r
+ SetLanguageCodes = TRUE;\r
+\r
//\r
// According to UEFI spec, PlatformLangCodes is only set once in firmware initialization, and is read-only\r
// Therefore, in variable driver, only store the original value for other use.\r
//\r
- AsciiStrnCpy (mGlobal->PlatformLangCodes, Data, DataSize);\r
- } else if (StrCmp (VariableName, L"LangCodes") == 0) {\r
+ if (mGlobal->PlatformLangCodes != NULL) {\r
+ FreePool (mGlobal->PlatformLangCodes);\r
+ }\r
+ mGlobal->PlatformLangCodes = AllocateRuntimeCopyPool (DataSize, Data);\r
+ ASSERT (mGlobal->PlatformLangCodes != NULL);\r
+\r
//\r
- // According to UEFI spec, LangCodes is only set once in firmware initialization, and is read-only\r
- // Therefore, in variable driver, only store the original value for other use.\r
+ // PlatformLang holds a single language from PlatformLangCodes, \r
+ // so the size of PlatformLangCodes is enough for the PlatformLang.\r
//\r
- AsciiStrnCpy (mGlobal->LangCodes, Data, DataSize);\r
- } else if ((StrCmp (VariableName, L"PlatformLang") == 0) && (DataSize != 0)) {\r
- ASSERT (AsciiStrLen (mGlobal->PlatformLangCodes) != 0);\r
+ if (mGlobal->PlatformLang != NULL) {\r
+ FreePool (mGlobal->PlatformLang);\r
+ }\r
+ mGlobal->PlatformLang = AllocateRuntimePool (DataSize);\r
+ ASSERT (mGlobal->PlatformLang != NULL);\r
\r
+ } else if (StrCmp (VariableName, L"LangCodes") == 0) {\r
//\r
- // When setting PlatformLang, firstly get most matched language string from supported language codes.\r
+ // LangCodes is a volatile variable, so it can not be updated at runtime.\r
//\r
- BestPlatformLang = GetBestLanguage(mGlobal->PlatformLangCodes, FALSE, Data, NULL);\r
+ if (EfiAtRuntime ()) {\r
+ return;\r
+ }\r
+\r
+ SetLanguageCodes = TRUE;\r
\r
//\r
- // Get the corresponding index in language codes.\r
+ // According to UEFI spec, LangCodes is only set once in firmware initialization, and is read-only\r
+ // Therefore, in variable driver, only store the original value for other use.\r
//\r
- Index = GetIndexFromSupportedLangCodes(mGlobal->PlatformLangCodes, BestPlatformLang, FALSE);\r
+ if (mGlobal->LangCodes != NULL) {\r
+ FreePool (mGlobal->LangCodes);\r
+ }\r
+ mGlobal->LangCodes = AllocateRuntimeCopyPool (DataSize, Data);\r
+ ASSERT (mGlobal->LangCodes != NULL);\r
+ }\r
\r
+ if (SetLanguageCodes \r
+ && (mGlobal->PlatformLangCodes != NULL)\r
+ && (mGlobal->LangCodes != NULL)) {\r
//\r
- // Get the corresponding ISO639 language tag according to RFC4646 language tag.\r
+ // Update Lang if PlatformLang is already set\r
+ // Update PlatformLang if Lang is already set\r
//\r
- BestLang = GetLangFromSupportedLangCodes(mGlobal->LangCodes, Index, TRUE);\r
+ Status = FindVariable (L"PlatformLang", &gEfiGlobalVariableGuid, &Variable);\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Update Lang\r
+ //\r
+ VariableName = L"PlatformLang";\r
+ Data = GetVariableDataPtr (Variable.CurrPtr);\r
+ DataSize = Variable.CurrPtr->DataSize;\r
+ } else {\r
+ Status = FindVariable (L"Lang", &gEfiGlobalVariableGuid, &Variable);\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Update PlatformLang\r
+ //\r
+ VariableName = L"Lang";\r
+ Data = GetVariableDataPtr (Variable.CurrPtr);\r
+ DataSize = Variable.CurrPtr->DataSize;\r
+ } else {\r
+ //\r
+ // Neither PlatformLang nor Lang is set, directly return\r
+ //\r
+ return;\r
+ }\r
+ }\r
+ }\r
+ \r
+ //\r
+ // According to UEFI spec, "Lang" and "PlatformLang" is NV|BS|RT attributions.\r
+ //\r
+ Attributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;\r
\r
+ if (StrCmp (VariableName, L"PlatformLang") == 0) {\r
//\r
- // Successfully convert PlatformLang to Lang, and set the BestLang value into Lang variable simultaneously.\r
+ // Update Lang when PlatformLangCodes/LangCodes were set.\r
//\r
- FindVariable(L"Lang", &gEfiGlobalVariableGuid, &Variable);\r
+ if ((mGlobal->PlatformLangCodes != NULL) && (mGlobal->LangCodes != NULL)) {\r
+ //\r
+ // When setting PlatformLang, firstly get most matched language string from supported language codes.\r
+ //\r
+ BestPlatformLang = VariableGetBestLanguage (mGlobal->PlatformLangCodes, FALSE, Data, NULL);\r
+ if (BestPlatformLang != NULL) {\r
+ //\r
+ // Get the corresponding index in language codes.\r
+ //\r
+ Index = GetIndexFromSupportedLangCodes (mGlobal->PlatformLangCodes, BestPlatformLang, FALSE);\r
\r
- Status = UpdateVariable(L"Lang", &gEfiGlobalVariableGuid, \r
- BestLang, ISO_639_2_ENTRY_SIZE + 1, Attributes, &Variable);\r
+ //\r
+ // Get the corresponding ISO639 language tag according to RFC4646 language tag.\r
+ //\r
+ BestLang = GetLangFromSupportedLangCodes (mGlobal->LangCodes, Index, TRUE);\r
\r
- DEBUG((EFI_D_INFO, "Variable Driver Auto Update PlatformLang, PlatformLang:%a, Lang:%a\n", BestPlatformLang, BestLang));\r
+ //\r
+ // Successfully convert PlatformLang to Lang, and set the BestLang value into Lang variable simultaneously.\r
+ //\r
+ FindVariable(L"Lang", &gEfiGlobalVariableGuid, &Variable);\r
\r
- ASSERT_EFI_ERROR(Status);\r
- \r
- } else if ((StrCmp (VariableName, L"Lang") == 0) && (DataSize != 0)) {\r
- ASSERT (AsciiStrLen (mGlobal->LangCodes) != 0);\r
+ Status = UpdateVariable (L"Lang", &gEfiGlobalVariableGuid, BestLang, ISO_639_2_ENTRY_SIZE + 1, Attributes, &Variable);\r
\r
- //\r
- // When setting Lang, firstly get most matched language string from supported language codes.\r
- //\r
- BestLang = GetBestLanguage(mGlobal->LangCodes, TRUE, Data, NULL);\r
+ DEBUG ((EFI_D_INFO, "Variable Driver Auto Update PlatformLang, PlatformLang:%a, Lang:%a\n", BestPlatformLang, BestLang));\r
\r
- //\r
- // Get the corresponding index in language codes.\r
- //\r
- Index = GetIndexFromSupportedLangCodes(mGlobal->LangCodes, BestLang, TRUE);\r
+ ASSERT_EFI_ERROR(Status);\r
+ }\r
+ }\r
\r
+ } else if (StrCmp (VariableName, L"Lang") == 0) {\r
//\r
- // Get the corresponding RFC4646 language tag according to ISO639 language tag.\r
+ // Update PlatformLang when PlatformLangCodes/LangCodes were set.\r
//\r
- BestPlatformLang = GetLangFromSupportedLangCodes(mGlobal->PlatformLangCodes, Index, FALSE);\r
+ if ((mGlobal->PlatformLangCodes != NULL) && (mGlobal->LangCodes != NULL)) {\r
+ //\r
+ // When setting Lang, firstly get most matched language string from supported language codes.\r
+ //\r
+ BestLang = VariableGetBestLanguage (mGlobal->LangCodes, TRUE, Data, NULL);\r
+ if (BestLang != NULL) {\r
+ //\r
+ // Get the corresponding index in language codes.\r
+ //\r
+ Index = GetIndexFromSupportedLangCodes (mGlobal->LangCodes, BestLang, TRUE);\r
\r
- //\r
- // Successfully convert Lang to PlatformLang, and set the BestPlatformLang value into PlatformLang variable simultaneously.\r
- //\r
- FindVariable(L"PlatformLang", &gEfiGlobalVariableGuid, &Variable);\r
+ //\r
+ // Get the corresponding RFC4646 language tag according to ISO639 language tag.\r
+ //\r
+ BestPlatformLang = GetLangFromSupportedLangCodes (mGlobal->PlatformLangCodes, Index, FALSE);\r
+\r
+ //\r
+ // Successfully convert Lang to PlatformLang, and set the BestPlatformLang value into PlatformLang variable simultaneously.\r
+ //\r
+ FindVariable(L"PlatformLang", &gEfiGlobalVariableGuid, &Variable);\r
\r
- Status = UpdateVariable(L"PlatformLang", &gEfiGlobalVariableGuid, \r
- BestPlatformLang, AsciiStrSize (BestPlatformLang), Attributes, &Variable);\r
+ Status = UpdateVariable (L"PlatformLang", &gEfiGlobalVariableGuid, BestPlatformLang, \r
+ AsciiStrSize (BestPlatformLang), Attributes, &Variable);\r
\r
- DEBUG((EFI_D_INFO, "Variable Driver Auto Update Lang, Lang:%a, PlatformLang:%a\n", BestLang, BestPlatformLang));\r
- ASSERT_EFI_ERROR(Status);\r
+ DEBUG ((EFI_D_INFO, "Variable Driver Auto Update Lang, Lang:%a, PlatformLang:%a\n", BestLang, BestPlatformLang));\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+ }\r
}\r
- return EFI_SUCCESS;\r
}\r
\r
/**\r
EfiConvertPointer (0, (VOID**) &mGlobal->VariableStore[Index]);\r
EfiConvertPointer (0, &mGlobal->VariableBase[Index]);\r
}\r
+ EfiConvertPointer (0, (VOID **) &mGlobal->PlatformLangCodes);\r
+ EfiConvertPointer (0, (VOID **) &mGlobal->LangCodes);\r
+ EfiConvertPointer (0, (VOID **) &mGlobal->PlatformLang);\r
EfiConvertPointer (0, &mGlobal->Scratch);\r
EfiConvertPointer (0, (VOID**) &mGlobal);\r
}\r