text: /** @file
    Driver to implement English version of Unicode Collation Protocol.
  
  Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent
  
  **/
  
  
  #include "OcUnicodeCollationEngInternal.h"
  
  CHAR8 mEngUpperMap[MAP_TABLE_SIZE];
  CHAR8 mEngLowerMap[MAP_TABLE_SIZE];
  CHAR8 mEngInfoMap[MAP_TABLE_SIZE];
  
  CHAR8 mOtherChars[] = {
    '0',
    '1',
    '2',
    '3',
    '4',
    '5',
    '6',
    '7',
    '8',
    '9',
    '\\',
    '.',
    '_',
    '^',
    '$',
    '~',
    '!',
    '#',
    '%',
    '&',
    '-',
    '{',
    '}',
    '(',
    ')',
    '@',
    '`',
    '\'',
    '\0'
  };
  
  //
  // Supported language list, meant to contain one extra language if necessary.
  //
  STATIC CHAR8  UnicodeLanguages[6] = "en";
  
  //
  // EFI Unicode Collation2 Protocol supporting RFC 4646 language code
  //
  GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_COLLATION_PROTOCOL  gInternalUnicode2Eng = {
    EngStriColl,
    EngMetaiMatch,
    EngStrLwr,
    EngStrUpr,
    EngFatToStr,
    EngStrToFat,
    UnicodeLanguages
  };
  
  VOID
  OcUnicodeCollationUpdatePlatformLanguage (
    VOID
    )
  {
    EFI_STATUS                      Status;
    CHAR8                           *PlatformLang;
    UINTN                           Size;
  
    //
    // On several platforms EFI_PLATFORM_LANG_VARIABLE_NAME is not available.
    // Fallback to "en-US" which is supported by this library and the wide majority of others.
    //
    Size = 0;
    PlatformLang = NULL;
    Status = gRT->GetVariable (
      EFI_PLATFORM_LANG_VARIABLE_NAME,
      &gEfiGlobalVariableGuid,
      NULL,
      &Size,
      PlatformLang
      );
    if (Status == EFI_BUFFER_TOO_SMALL) {
      Status = gBS->AllocatePool (
        EfiBootServicesData,
        Size,
        (VOID **) &PlatformLang
        );
      if (!EFI_ERROR (Status)) {
        Status = gRT->GetVariable (
          EFI_PLATFORM_LANG_VARIABLE_NAME,
          &gEfiGlobalVariableGuid,
          NULL,
          &Size,
          PlatformLang
          );
        if (EFI_ERROR (Status)) {
          gBS->FreePool (PlatformLang);
        }
      }
    }
  
    if (!EFI_ERROR (Status)) {
      if (AsciiStrnLenS (PlatformLang, Size) < 2) {
        gBS->FreePool (PlatformLang);
        PlatformLang = NULL;
      }
    } else {
      PlatformLang = NULL;
    }
  
    if (PlatformLang == NULL) {
      //
      // No value or something broken, discard and fallback.
      //
      gRT->SetVariable (
        EFI_PLATFORM_LANG_VARIABLE_NAME,
        &gEfiGlobalVariableGuid,
        EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
        sizeof ("en-US"),
        "en-US"
        );
    } else if (AsciiStrnCmp (PlatformLang, "en", 2) != 0) {
      //
      // On some platforms with missing gEfiUnicodeCollation2ProtocolGuid EFI_PLATFORM_LANG_VARIABLE_NAME is set
      // to the value different from "en" or "en-...". This is not going to work with our driver and UEFI Shell
      // will fail to load. Since we did not overwrite EFI_PLATFORM_LANG_VARIABLE_NAME, and it uses some other language,
      // workaround by appending the other language to the list of supported ones.
      //
      STATIC_ASSERT (sizeof (UnicodeLanguages) == 6 && sizeof (UnicodeLanguages[0]) == 1,
        "UnicodeLanguages must contain one extra language");
      UnicodeLanguages[2] = ';';
      UnicodeLanguages[3] = PlatformLang[0];
      UnicodeLanguages[4] = PlatformLang[1];
      UnicodeLanguages[5] = '\0';
    }
  
    //
    // Free allocated memory for platform languages
    //
    if (PlatformLang != NULL) {
      gBS->FreePool (PlatformLang);
    }
  }
  
  VOID
  OcUnicodeCollationInitializeMappingTables (
    VOID
    )
  {
    UINTN  Index;
    UINTN  Index2;
  
    //
    // Initialize mapping tables for the supported languages
    //
    for (Index = 0; Index < MAP_TABLE_SIZE; Index++) {
      mEngUpperMap[Index] = (CHAR8) Index;
      mEngLowerMap[Index] = (CHAR8) Index;
      mEngInfoMap[Index]  = 0;
  
      if ((Index >= 'a' && Index <= 'z') || (Index >= 0xe0 && Index <= 0xf6) || (Index >= 0xf8 && Index <= 0xfe)) {
  
        Index2                = Index - 0x20;
        mEngUpperMap[Index]   = (CHAR8) Index2;
        mEngLowerMap[Index2]  = (CHAR8) Index;
  
        mEngInfoMap[Index] |= CHAR_FAT_VALID;
        mEngInfoMap[Index2] |= CHAR_FAT_VALID;
      }
    }
  
    for (Index = 0; mOtherChars[Index] != 0; Index++) {
      Index2 = mOtherChars[Index];
      mEngInfoMap[Index2] |= CHAR_FAT_VALID;
    }
  }
  
  /**
    Performs a case-insensitive comparison of two Null-terminated strings.
  
    @param  This Protocol instance pointer.
    @param  Str1 A pointer to a Null-terminated string.
    @param  Str2 A pointer to a Null-terminated string.
  
    @retval 0   Str1 is equivalent to Str2
    @retval > 0 Str1 is lexically greater than Str2
    @retval < 0 Str1 is lexically less than Str2
  
  **/
  INTN
  EFIAPI
  EngStriColl (
    IN EFI_UNICODE_COLLATION_PROTOCOL   *This,
    IN CHAR16                           *Str1,
    IN CHAR16                           *Str2
    )
  {
    while (*Str1 != 0) {
      if (TO_UPPER (*Str1) != TO_UPPER (*Str2)) {
        break;
      }
  
      Str1 += 1;
      Str2 += 1;
    }
  
    return TO_UPPER (*Str1) - TO_UPPER (*Str2);
  }
  
  
  /**
    Converts all the characters in a Null-terminated string to
    lower case characters.
  
    @param  This   Protocol instance pointer.
    @param  Str    A pointer to a Null-terminated string.
  
  **/
  VOID
  EFIAPI
  EngStrLwr (
    IN EFI_UNICODE_COLLATION_PROTOCOL   *This,
    IN OUT CHAR16                       *Str
    )
  {
    while (*Str != 0) {
      *Str = TO_LOWER (*Str);
      Str += 1;
    }
  }
  
  
  /**
    Converts all the characters in a Null-terminated string to upper
    case characters.
  
    @param  This   Protocol instance pointer.
    @param  Str    A pointer to a Null-terminated string.
  
  **/
  VOID
  EFIAPI
  EngStrUpr (
    IN EFI_UNICODE_COLLATION_PROTOCOL   *This,
    IN OUT CHAR16                       *Str
    )
  {
    while (*Str != 0) {
      *Str = TO_UPPER (*Str);
      Str += 1;
    }
  }
  
  /**
    Performs a case-insensitive comparison of a Null-terminated
    pattern string and a Null-terminated string.
  
    @param  This    Protocol instance pointer.
    @param  String  A pointer to a Null-terminated string.
    @param  Pattern A pointer to a Null-terminated pattern string.
  
    @retval TRUE    Pattern was found in String.
    @retval FALSE   Pattern was not found in String.
  
  **/
  BOOLEAN
  EFIAPI
  EngMetaiMatch (
    IN EFI_UNICODE_COLLATION_PROTOCOL   *This,
    IN CHAR16                           *String,
    IN CHAR16                           *Pattern
    )
  {
    CHAR16  CharC;
    CHAR16  CharP;
    CHAR16  Index3;
  
    for (;;) {
      CharP = *Pattern;
      Pattern += 1;
  
      switch (CharP) {
      case 0:
        //
        // End of pattern.  If end of string, TRUE match
        //
        if (*String != 0) {
          return FALSE;
        } else {
          return TRUE;
        }
  
      case '*':
        //
        // Match zero or more chars
        //
        while (*String != 0) {
          if (EngMetaiMatch (This, String, Pattern)) {
            return TRUE;
          }
  
          String += 1;
        }
  
        return EngMetaiMatch (This, String, Pattern);
  
      case '?':
        //
        // Match any one char
        //
        if (*String == 0) {
          return FALSE;
        }
  
        String += 1;
        break;
  
      case '[':
        //
        // Match char set
        //
        CharC = *String;
        if (CharC == 0) {
          //
          // syntax problem
          //
          return FALSE;
        }
  
        Index3  = 0;
        CharP   = *Pattern++;
        while (CharP != 0) {
          if (CharP == ']') {
            return FALSE;
          }
  
          if (CharP == '-') {
            //
            // if range of chars, get high range
            //
            CharP = *Pattern;
            if (CharP == 0 || CharP == ']') {
              //
              // syntax problem
              //
              return FALSE;
            }
  
            if (TO_UPPER (CharC) >= TO_UPPER (Index3) && TO_UPPER (CharC) <= TO_UPPER (CharP)) {
              //
              // if in range, it's a match
              //
              break;
            }
          }
  
          Index3 = CharP;
          if (TO_UPPER (CharC) == TO_UPPER (CharP)) {
            //
            // if char matches
            //
            break;
          }
  
          CharP = *Pattern++;
        }
        //
        // skip to end of match char set
        //
        while ((CharP != 0) && (CharP != ']')) {
          CharP = *Pattern;
          Pattern += 1;
        }
  
        String += 1;
        break;
  
      default:
        CharC = *String;
        if (TO_UPPER (CharC) != TO_UPPER (CharP)) {
          return FALSE;
        }
  
        String += 1;
        break;
      }
    }
  }
  
  
  /**
    Converts an 8.3 FAT file name in an OEM character set to a Null-terminated string.
  
    @param  This    Protocol instance pointer.
    @param  FatSize The size of the string Fat in bytes.
    @param  Fat     A pointer to a Null-terminated string that contains an 8.3 file
                    name using an 8-bit OEM character set.
    @param  String  A pointer to a Null-terminated string. The string must
                    be preallocated to hold FatSize characters.
  
  **/
  VOID
  EFIAPI
  EngFatToStr (
    IN EFI_UNICODE_COLLATION_PROTOCOL   *This,
    IN UINTN                            FatSize,
    IN CHAR8                            *Fat,
    OUT CHAR16                          *String
    )
  {
    //
    // No DBCS issues, just expand and add null terminate to end of string
    //
    while ((*Fat != 0) && (FatSize != 0)) {
      *String = *Fat;
      String += 1;
      Fat += 1;
      FatSize -= 1;
    }
  
    *String = 0;
  }
  
  
  /**
    Converts a Null-terminated string to legal characters in a FAT
    filename using an OEM character set.
  
    @param  This    Protocol instance pointer.
    @param  String  A pointer to a Null-terminated string. The string must
                    be preallocated to hold FatSize characters.
    @param  FatSize The size of the string Fat in bytes.
    @param  Fat     A pointer to a Null-terminated string that contains an 8.3 file
                    name using an OEM character set.
  
    @retval TRUE    Fat is a Long File Name
    @retval FALSE   Fat is an 8.3 file name
  
  **/
  BOOLEAN
  EFIAPI
  EngStrToFat (
    IN EFI_UNICODE_COLLATION_PROTOCOL   *This,
    IN CHAR16                           *String,
    IN UINTN                            FatSize,
    OUT CHAR8                           *Fat
    )
  {
    BOOLEAN SpecialCharExist;
  
    SpecialCharExist = FALSE;
    while ((*String != 0) && (FatSize != 0)) {
      //
      // Skip '.' or ' ' when making a fat name
      //
      if (*String != '.' && *String != ' ') {
        //
        // If this is a valid fat char, move it.
        // Otherwise, move a '_' and flag the fact that the name needs a long file name.
        //
        if (*String < MAP_TABLE_SIZE && ((mEngInfoMap[*String] & CHAR_FAT_VALID) != 0)) {
          *Fat = mEngUpperMap[*String];
        } else {
          *Fat              = '_';
          SpecialCharExist  = TRUE;
        }
  
        Fat += 1;
        FatSize -= 1;
      }
  
      String += 1;
    }
    //
    // Do not terminate that fat string
    //
    return SpecialCharExist;
  }
  