+/**\r
+ Get language string from supported language codes according to index.\r
+\r
+ This code is used to get corresponding language strings in supported language codes. It can handle\r
+ RFC4646 and ISO639 language tags.\r
+ In ISO639 language tags, take 3-characters as a delimitation. Find language string according to the index.\r
+ In RFC4646 language tags, take semicolon as a delimitation. Find language string according to the index.\r
+\r
+ For example:\r
+ SupportedLang = "engfraengfra"\r
+ Index = "1"\r
+ Iso639Language = TRUE\r
+ The return value is "fra".\r
+ Another example:\r
+ SupportedLang = "en;fr;en-US;fr-FR"\r
+ Index = "1"\r
+ Iso639Language = FALSE\r
+ The return value is "fr".\r
+\r
+ @param SupportedLang Platform supported language codes.\r
+ @param Index The index in supported language codes.\r
+ @param Iso639Language A bool value to signify if the handler is operated on ISO639 or RFC4646.\r
+\r
+ @retval The language string in the language codes.\r
+\r
+**/\r
+CHAR8 *\r
+GetLangFromSupportedLangCodes (\r
+ IN CHAR8 *SupportedLang,\r
+ IN UINTN Index,\r
+ IN BOOLEAN Iso639Language\r
+)\r
+{\r
+ UINTN SubIndex;\r
+ UINTN CompareLength;\r
+ CHAR8 *Supported;\r
+\r
+ SubIndex = 0;\r
+ Supported = SupportedLang;\r
+ if (Iso639Language) {\r
+ //\r
+ // According to the index of Lang string in SupportedLang string to get the language.\r
+ // This code will be invoked in RUNTIME, therefore there is not a memory allocate/free operation.\r
+ // In driver entry, it pre-allocates a runtime attribute memory to accommodate this string.\r
+ //\r
+ CompareLength = ISO_639_2_ENTRY_SIZE;\r
+ mVariableModuleGlobal->Lang[CompareLength] = '\0';\r
+ return CopyMem (mVariableModuleGlobal->Lang, SupportedLang + Index * CompareLength, CompareLength);\r
+ \r
+ } else {\r
+ while (TRUE) {\r
+ //\r
+ // Take semicolon as delimitation, sequentially traverse supported language codes.\r
+ //\r
+ for (CompareLength = 0; *Supported != ';' && *Supported != '\0'; CompareLength++) {\r
+ Supported++;\r
+ }\r
+ if ((*Supported == '\0') && (SubIndex != Index)) {\r
+ //\r
+ // Have completed the traverse, but not find corrsponding string.\r
+ // This case is not allowed to happen.\r
+ //\r
+ ASSERT(FALSE);\r
+ return NULL;\r
+ }\r
+ if (SubIndex == Index) {\r
+ //\r
+ // According to the index of Lang string in SupportedLang string to get the language.\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
+ mVariableModuleGlobal->PlatformLang[CompareLength] = '\0';\r
+ return CopyMem (mVariableModuleGlobal->PlatformLang, Supported - CompareLength, CompareLength);\r
+ }\r
+ SubIndex++;\r
+\r
+ //\r
+ // Skip ';' characters in Supported\r
+ //\r
+ for (; *Supported != '\0' && *Supported == ';'; Supported++);\r
+ }\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
+EFIAPI\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