/*++

Copyright (c) 2006, Intel Corporation                                                         
All rights reserved. This program and the accompanying materials                          
are licensed and made available under the terms and conditions of the BSD License         
which accompanies this distribution.  The full text of the license may be found at        
http://opensource.org/licenses/bsd-license.php                                            
                                                                                          
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             

Module Name:

  ProcessOptions.c

Abstract:
  
  Implementation for handling the User Interface option processing.

Revision History

--*/

#include "Setup.h"
#include "Ui.h"

EFI_STATUS
ExtractRequestedNvMap (
  IN  EFI_FILE_FORM_TAGS          *FileFormTags,
  IN  UINT16                      VariableId,
  OUT EFI_VARIABLE_DEFINITION     **VariableDefinition
  )
{
  *VariableDefinition = FileFormTags->VariableDefinitions;

  //
  // Extract the data from the NV variable - consumer will free the buffer.
  //
  for (; *VariableDefinition != NULL; *VariableDefinition = (*VariableDefinition)->Next) {
    //
    // If there is a variable with this ID return with EFI_SUCCESS
    //
    if (!CompareMem (&(*VariableDefinition)->VariableId, &VariableId, sizeof (UINT16))) {
      return EFI_SUCCESS;
    }
  }

  return EFI_NOT_FOUND;
}

EFI_STATUS
ExtractNvValue (
  IN  EFI_FILE_FORM_TAGS          *FileFormTags,
  IN  UINT16                      VariableId,
  IN  UINT16                      VariableSize,
  IN  UINT16                      OffsetValue,
  OUT VOID                        **Buffer
  )
{
  EFI_STATUS              Status;
  EFI_VARIABLE_DEFINITION *VariableDefinition;

  Status = ExtractRequestedNvMap (FileFormTags, VariableId, &VariableDefinition);

  if (!EFI_ERROR (Status)) {
    //
    // Allocate sufficient space for the data and copy it into the outgoing buffer
    //
    if (VariableSize != 0) {
      *Buffer = AllocateZeroPool (VariableSize);
      ASSERT (*Buffer != NULL);
      CopyMem (*Buffer, &VariableDefinition->NvRamMap[OffsetValue], VariableSize);
    }
    return EFI_SUCCESS;
  }

  return Status;
}

STATIC
VOID
AdjustNvMap (
  IN  EFI_FILE_FORM_TAGS          *FileFormTags,
  IN  UI_MENU_OPTION              *MenuOption
  )
{
  CHAR8                   *NvRamMap;
  UINTN                   SizeRequired;
  UINTN                   Index;
  UINTN                   CachedStart;
  EFI_VARIABLE_DEFINITION *VariableDefinition;

  CachedStart   = 0;

  SizeRequired  = MenuOption->ThisTag->StorageStart + MenuOption->ThisTag->StorageWidth;

  ExtractRequestedNvMap (FileFormTags, MenuOption->Tags->VariableNumber, &VariableDefinition);

  //
  // We arrived here because the current NvRamMap is too small for the new op-code to store things and
  // we need to adjust the buffer to support this.
  //
  NvRamMap = AllocateZeroPool (SizeRequired + 1);
  ASSERT (NvRamMap != NULL);

  //
  // Copy current NvRamMap to the new NvRamMap
  //
  CopyMem (NvRamMap, VariableDefinition->NvRamMap, VariableDefinition->VariableFakeSize);

  //
  // Remember, the only time we come here is because we are in the NVPlus section of the NvRamMap
  //
  for (Index = MenuOption->TagIndex;
       (MenuOption->Tags[Index].Operand != EFI_IFR_END_FORM_OP) && (MenuOption->Tags[Index].Operand != EFI_IFR_END_ONE_OF_OP);
       Index++
      ) {

    switch (MenuOption->Tags[Index].Operand) {
    case EFI_IFR_ORDERED_LIST_OP:
    case EFI_IFR_ONE_OF_OP:
      CachedStart = MenuOption->Tags[Index].StorageStart;
      break;

    case EFI_IFR_ONE_OF_OPTION_OP:
      if (MenuOption->Tags[Index].Flags & EFI_IFR_FLAG_DEFAULT) {
        CopyMem (&NvRamMap[CachedStart], &MenuOption->Tags[Index].Value, 2);
      }
      break;

    case EFI_IFR_CHECKBOX_OP:
      CopyMem (&NvRamMap[MenuOption->Tags[Index].StorageStart], &MenuOption->Tags[Index].Flags, 1);
      break;

    case EFI_IFR_NUMERIC_OP:
    case EFI_IFR_DATE_OP:
    case EFI_IFR_TIME_OP:
    case EFI_IFR_STRING_OP:
    case EFI_IFR_PASSWORD_OP:
      CopyMem (
        &NvRamMap[MenuOption->Tags[Index].StorageStart],
        &MenuOption->Tags[Index].Value,
        MenuOption->Tags[Index].StorageWidth
        );
      break;

    }
  }

  gBS->FreePool (VariableDefinition->NvRamMap);
  VariableDefinition->NvRamMap          = NvRamMap;
  VariableDefinition->VariableFakeSize  = (UINT16) SizeRequired;
}

EFI_STATUS
ProcessOptions (
  IN  UI_MENU_OPTION              *MenuOption,
  IN  BOOLEAN                     Selected,
  IN  EFI_FILE_FORM_TAGS          *FileFormTagsHead,
  IN  EFI_IFR_DATA_ARRAY          *PageData,
  OUT CHAR16                      **OptionString
  )
{
  EFI_STATUS                  Status;
  CHAR16                      *StringPtr;
  UINTN                       Index;
  UINTN                       CachedIndex;
  EFI_FILE_FORM_TAGS          *FileFormTags;
  EFI_TAG                     *Tag;
  CHAR16                      FormattedNumber[6];
  UINT16                      Number;
  UINT16                      Value;
  UINT16                      *ValueArray;
  UINT16                      *NvRamMap;
  CHAR8                       *TmpNvRamMap;
  UINTN                       Default;
  UINTN                       StringCount;
  CHAR16                      Character[2];
  UINTN                       Count;
  EFI_TIME                    Time;
  EFI_FORM_CALLBACK_PROTOCOL  *FormCallback;
  STRING_REF                  PopUp;
  CHAR16                      NullCharacter;
  EFI_INPUT_KEY               Key;
  EFI_VARIABLE_DEFINITION     *VariableDefinition;
  BOOLEAN                     OrderedList;
  BOOLEAN                     Initialized;
  UINT16                      KeyValue;
  BOOLEAN                     Skip;

  FileFormTags = FileFormTagsHead;

  for (Index = 0; Index < MenuOption->IfrNumber; Index++) {
    FileFormTags = FileFormTags->NextFile;
  }

  OrderedList         = FALSE;
  Initialized         = FALSE;
  ValueArray          = NULL;
  VariableDefinition  = NULL;
  Skip                = FALSE;

  ZeroMem (&Time, sizeof (EFI_TIME));

  StringPtr = (CHAR16 *) L"\0";
  Tag       = MenuOption->ThisTag;
  ExtractRequestedNvMap (FileFormTags, Tag->VariableNumber, &VariableDefinition);

  if (Tag->StorageStart > VariableDefinition->VariableSize) {
    NvRamMap = (UINT16 *) &VariableDefinition->FakeNvRamMap[Tag->StorageStart];
  } else {
    NvRamMap = (UINT16 *) &VariableDefinition->NvRamMap[Tag->StorageStart];
  }

  StringCount   = 0;
  Character[1]  = 0;
  Count         = 0;
  Default       = 0;
  NullCharacter = CHAR_NULL;
  FormCallback  = NULL;

  if (MenuOption->ThisTag->Operand == EFI_IFR_ORDERED_LIST_OP) {
    OrderedList = TRUE;
    if (((UINT8 *) NvRamMap)[0] != 0x00) {
      Initialized = TRUE;
    }
  }

  ZeroMem (FormattedNumber, 12);

  Status = gBS->HandleProtocol (
                  (VOID *) (UINTN) FileFormTags->FormTags.Tags[0].CallbackHandle,
                  &gEfiFormCallbackProtocolGuid,
                  (VOID **) &FormCallback
                  );

  if (*OptionString != NULL) {
    gBS->FreePool (*OptionString);
    *OptionString = NULL;
  }

  switch (Tag->Operand) {

  case EFI_IFR_ORDERED_LIST_OP:
  case EFI_IFR_ONE_OF_OP:
    //
    // If the op-code we are looking at is larger than the latest created NvMap - we likely encountered a dynamically
    // created entry which has an expanded NvMap requirement.  We won't save this information - but we need to adjust
    // the NvMap so that we can properly display the information
    //
    if ((UINTN) (Tag->StorageStart + Tag->StorageWidth) > VariableDefinition->VariableFakeSize) {
      AdjustNvMap (FileFormTags, MenuOption);
      NvRamMap = (UINT16 *) &VariableDefinition->NvRamMap[Tag->StorageStart];
    }

    CachedIndex = MenuOption->TagIndex;

    //
    // search for EFI_IFR_ONE_OF_OPTION_OP until you hit the EFI_IFR_END_ONE_OF_OP,
    // each of the .Text in the options are going to be what gets displayed.  Break each into 26 char chunks
    // when hit right/left arrow allows for selection - then repopulate Tag[TagIndex] with the choice
    //
    for (Index = MenuOption->TagIndex; MenuOption->Tags[Index].Operand != EFI_IFR_END_ONE_OF_OP; Index++) {
      //
      // We found an option - which assumedly has a string.  We will eventually have to support
      // wrapping of strings.  For now, let's pretend they don't wrap and code that up.
      //
      // Count how many strings there are
      //
      if (MenuOption->Tags[Index].Operand == EFI_IFR_ONE_OF_OPTION_OP) {
        //
        // If one of the options for the one-of has an interactive flag, back-define the oneof to have one too
        //
        if (MenuOption->Tags[Index].Flags & EFI_IFR_FLAG_INTERACTIVE) {
          MenuOption->Tags[CachedIndex].Flags = (UINT8) (MenuOption->Tags[CachedIndex].Flags | EFI_IFR_FLAG_INTERACTIVE);
        }

        StringCount++;
      }
    }
    //
    // We now know how many strings we will have, so we can allocate the
    // space required for the array or strings.
    //
    *OptionString = AllocateZeroPool (StringCount * (gOptionBlockWidth + 1) * 2 * gScreenDimensions.BottomRow);
    ASSERT (*OptionString);

    //
    // Add left delimeter to string
    //
    *OptionString[0] = LEFT_ONEOF_DELIMITER;

    //
    // Retrieve the current OneOf value
    //
    if (Selected) {
      //
      // Auto selection from list
      //
      Value = 0;
      //
      // Copy current setting to the seed Value
      //
      if (Tag->Operand == EFI_IFR_ORDERED_LIST_OP) {
        ValueArray = AllocateZeroPool (MenuOption->ThisTag->StorageWidth);
        ASSERT (ValueArray != NULL);
        CopyMem (ValueArray, NvRamMap, MenuOption->ThisTag->StorageWidth);
      } else {
        CopyMem (&Value, NvRamMap, MenuOption->ThisTag->StorageWidth);
        CopyMem (gPreviousValue, NvRamMap, MenuOption->ThisTag->StorageWidth);
      }

      Number = Value;
      if (Tag->Operand == EFI_IFR_ORDERED_LIST_OP) {
        Status = GetSelectionInputPopUp (MenuOption, Tag, MenuOption->ThisTag->StorageWidth, ValueArray, &KeyValue);
      } else {
        Status = GetSelectionInputPopUp (MenuOption, Tag, 1, &Value, &KeyValue);
      }

      if (!EFI_ERROR (Status)) {
        if (Tag->Operand == EFI_IFR_ORDERED_LIST_OP) {
          CopyMem (NvRamMap, ValueArray, MenuOption->ThisTag->StorageWidth);
          gBS->FreePool (ValueArray);
        } else {
          //
          // Since the value can be one byte long or two bytes long, do a CopyMem based on StorageWidth
          //
          CopyMem (NvRamMap, &Value, Tag->StorageWidth);
          MenuOption->ThisTag->Key = KeyValue;
        }
        //
        // If a late check is required save off the information.  This is used when consistency checks
        // are required, but certain values might be bound by an impossible consistency check such as
        // if two questions are bound by consistency checks and each only has two possible choices, there
        // would be no way for a user to switch the values.  Thus we require late checking.
        //
        if (Tag->Flags & EFI_IFR_FLAG_LATE_CHECK) {
          CopyMem (&Tag->OldValue, &Value, Tag->StorageWidth);
        } else {
          //
          // In theory, passing the value and the Id are sufficient to determine what needs
          // to be done.  The Id is the key to look for the entry needed in the Inconsistency
          // database.  That will yields operand and ID data - and since the ID's correspond
          // to the NV storage, we can determine the values for other IDs there.
          //
          if (ValueIsNotValid (TRUE, 0, Tag, FileFormTags, &PopUp)) {
            if (PopUp == 0x0000) {
              //
              // Restore Old Value
              //
              if (!Tag->Suppress && !Tag->GrayOut) {
                CopyMem (NvRamMap, &Number, MenuOption->ThisTag->StorageWidth);
              }
              break;
            }

            StringPtr = GetToken (PopUp, MenuOption->Handle);

            CreatePopUp (GetStringWidth (StringPtr) / 2, 3, &NullCharacter, StringPtr, &NullCharacter);

            do {
              Status = WaitForKeyStroke (&Key);

              switch (Key.UnicodeChar) {

              case CHAR_CARRIAGE_RETURN:
                //
                // Since the value can be one byte long or two bytes long, do a CopyMem based on StorageWidth
                //
                CopyMem (NvRamMap, &Number, MenuOption->ThisTag->StorageWidth);
                gBS->FreePool (StringPtr);
                break;

              default:
                break;
              }
            } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
          }
        }

        UpdateStatusBar (NV_UPDATE_REQUIRED, Tag->Flags, TRUE);
      } else {
        if (Tag->Operand == EFI_IFR_ORDERED_LIST_OP) {
          gBS->FreePool (ValueArray);
        }

        return EFI_SUCCESS;
      }
    } else {
      for (Index = MenuOption->TagIndex; MenuOption->Tags[Index].Operand != EFI_IFR_END_ONE_OF_OP; Index++) {
        //
        // We found an option - which assumedly has a string.  We will eventually have to support
        // wrapping of strings.  For now, let's pretend they don't wrap and code that up.
        //
        if (MenuOption->Tags[Index].Operand == EFI_IFR_ONE_OF_OPTION_OP) {
          if (OrderedList) {
            if (!Initialized) {
              //
              // If the first entry is invalid, then the "default" settings are based on what is reflected
              // in the order of the op-codes
              //
              ((UINT8 *) NvRamMap)[Index - MenuOption->TagIndex - 1] = (UINT8) MenuOption->Tags[Index].Value;
            }
            //
            // Only display 3 lines of stuff at most
            //
            if ((Index - MenuOption->TagIndex) > ORDERED_LIST_SIZE) {
              break;
            }

            if (((Index - MenuOption->TagIndex) != 1) && !Skip) {
              Character[0] = LEFT_ONEOF_DELIMITER;
              NewStrCat (OptionString[0], Character);
            }

            MenuOption->ThisTag->NumberOfLines = (UINT16) (Index - MenuOption->TagIndex);
            if (!Initialized) {
              StringPtr = GetToken (MenuOption->Tags[Index].Text, MenuOption->Handle);
            } else {
              for (Value = (UINT16) (MenuOption->TagIndex + 1);
                   MenuOption->Tags[Value].Operand != EFI_IFR_END_ONE_OF_OP;
                   Value++
                  ) {
                if (MenuOption->Tags[Value].Value == ((UINT8 *) NvRamMap)[Index - MenuOption->TagIndex - 1]) {
                  StringPtr = GetToken (MenuOption->Tags[Value].Text, MenuOption->Handle);
                  break;
                }
              }

              if (MenuOption->Tags[Value].Operand == EFI_IFR_END_ONE_OF_OP) {
                Skip = TRUE;
                continue;
              }
            }

            Skip = FALSE;
            NewStrCat (OptionString[0], StringPtr);
            Character[0] = RIGHT_ONEOF_DELIMITER;
            NewStrCat (OptionString[0], Character);
            Character[0] = CHAR_CARRIAGE_RETURN;
            NewStrCat (OptionString[0], Character);

            //
            // Remove Buffer allocated for StringPtr after it has been used.
            //
            gBS->FreePool (StringPtr);
          } else {
            //
            // The option value is the same as what is stored in NV store.  Print this.
            //
            if (!CompareMem (&(MenuOption->Tags[Index].Value), NvRamMap, MenuOption->ThisTag->StorageWidth)) {
              StringPtr = GetToken (MenuOption->Tags[Index].Text, MenuOption->Handle);
              NewStrCat (OptionString[0], StringPtr);
              Character[0] = RIGHT_ONEOF_DELIMITER;
              NewStrCat (OptionString[0], Character);
              //
              // Remove Buffer allocated for StringPtr after it has been used.
              //
              gBS->FreePool (StringPtr);
              Default = 0;
              break;
            }

            if ((MenuOption->Tags[Index].Flags & EFI_IFR_FLAG_DEFAULT) == 1) {
              Default = MenuOption->Tags[Index].Text;
              Value   = MenuOption->Tags[Index].Value;
            };
          }
        }
      }
      //
      // We didn't find a value that matched a setting in the NVRAM Map - display default - set default
      //
      if (Default != 0) {
        //
        // Since the value can be one byte long or two bytes long, do a CopyMem based on StorageWidth
        //
        CopyMem (NvRamMap, &Value, MenuOption->ThisTag->StorageWidth);

        StringPtr = GetToken ((UINT16) Default, MenuOption->Handle);
        NewStrCat (OptionString[0], StringPtr);
        Character[0] = RIGHT_ONEOF_DELIMITER;
        NewStrCat (OptionString[0], Character);
        //
        // Remove Buffer allocated for StringPtr after it has been used.
        //
        gBS->FreePool (StringPtr);
      }
    }
    break;

  case EFI_IFR_CHECKBOX_OP:
    //
    // If the op-code we are looking at is larger than the latest created NvMap - we likely encountered a dynamically
    // created entry which has an expanded NvMap requirement.  We won't save this information - but we need to adjust
    // the NvMap so that we can properly display the information
    //
    if ((UINTN) (Tag->StorageStart + Tag->StorageWidth) > VariableDefinition->VariableFakeSize) {
      AdjustNvMap (FileFormTags, MenuOption);
      NvRamMap = (UINT16 *) &VariableDefinition->NvRamMap[Tag->StorageStart];
    }

    Default = Tag->Flags & 1;
    //
    // If hit spacebar, set or unset Tag[TagIndex].Flags based on it's previous value - BOOLEAN
    //
    *OptionString = AllocateZeroPool ((gOptionBlockWidth + 1) * 2 * gScreenDimensions.BottomRow);
    ASSERT (*OptionString);

    //
    // Since Checkboxes are BOOLEAN values, bit 0 of the Flags bit defines the default option, therefore, if
    // the default option (only one option for checkboxes) is on, then the default value is on.  Tag.Default is not
    // an active field for Checkboxes.
    //
    StrnCpy (OptionString[0], (CHAR16 *) LEFT_CHECKBOX_DELIMITER, 1);

    //
    // Since this is a BOOLEAN operation, flip bit 0 upon selection
    //
    if (Selected) {
      Tag->Value          = (UINT16) (Tag->Value ^ 1);
      *(UINT8 *) NvRamMap = (UINT8) (Tag->Value & 1);
      UpdateStatusBar (NV_UPDATE_REQUIRED, Tag->Flags, TRUE);
    }

    if ((*(UINT8 *) NvRamMap & 1) == 0x01) {
      NewStrCat (OptionString[0], (CHAR16 *) CHECK_ON);
      //
      // If someone reset default variables - we may need to reload from our NvMapping....
      //
      Tag->Value = *(UINT8 *) NvRamMap;
    } else {
      //
      // If someone reset default variables - we may need to reload from our NvMapping....
      //
      NewStrCat (OptionString[0], (CHAR16 *) CHECK_OFF);
      Tag->Value = *(UINT8 *) NvRamMap;
    }

    NewStrCat (OptionString[0], (CHAR16 *) RIGHT_CHECKBOX_DELIMITER);
    NewStrCat (OptionString[0], StringPtr);
    break;

  case EFI_IFR_NUMERIC_OP:
    //
    // If the op-code we are looking at is larger than the latest created NvMap - we likely encountered a dynamically
    // created entry which has an expanded NvMap requirement.  We won't save this information - but we need to adjust
    // the NvMap so that we can properly display the information
    //
    if ((UINTN) (Tag->StorageStart + Tag->StorageWidth) > VariableDefinition->VariableFakeSize) {
      AdjustNvMap (FileFormTags, MenuOption);
      NvRamMap = (UINT16 *) &VariableDefinition->NvRamMap[Tag->StorageStart];
    }

    *OptionString = AllocateZeroPool ((gOptionBlockWidth + 1) * 2 * gScreenDimensions.BottomRow);
    ASSERT (*OptionString);

    //
    // Add left delimeter to string
    //
    *OptionString[0] = LEFT_NUMERIC_DELIMITER;

    //
    // Retrieve the current numeric value
    //
    if (Selected) {
      //
      // Go ask for input
      //
      if (Tag->Step == 0) {
        //
        // Manual Input
        //
        Status = GetNumericInput (MenuOption, FileFormTagsHead, TRUE, Tag, REGULAR_NUMERIC, &Number);
        if (!EFI_ERROR (Status)) {
          CopyMem (gPreviousValue, NvRamMap, MenuOption->ThisTag->StorageWidth);
          UpdateStatusBar (NV_UPDATE_REQUIRED, Tag->Flags, TRUE);

          //
          // Since the value can be one byte long or two bytes long, do a CopyMem based on StorageWidth
          //
          CopyMem (NvRamMap, &Number, MenuOption->ThisTag->StorageWidth);
        } else {
          return EFI_SUCCESS;
        }
      } else {
        //
        // Auto selection from list
        //
        if ((((Tag->StorageWidth == 1) && (UINT8) (*NvRamMap) > Tag->Maximum) || ((UINT8) (*NvRamMap) < Tag->Minimum)) ||
            (((Tag->StorageWidth == 2) && *NvRamMap > Tag->Maximum) || (*NvRamMap < Tag->Minimum))
            ) {
          //
          // Seed Number with valid value if currently invalid
          //
          Number = Tag->Default;
        } else {
          if (Tag->StorageWidth == 1) {
            Number = (UINT8) (*NvRamMap);
          } else {
            Number = *NvRamMap;
          }
        }

        Status = GetNumericInput (MenuOption, FileFormTagsHead, FALSE, Tag, REGULAR_NUMERIC, &Number);
        if (!EFI_ERROR (Status)) {
          CopyMem (gPreviousValue, NvRamMap, MenuOption->ThisTag->StorageWidth);
          UpdateStatusBar (NV_UPDATE_REQUIRED, Tag->Flags, TRUE);

          //
          // Since the value can be one byte long or two bytes long, do a CopyMem based on StorageWidth
          //
          CopyMem (NvRamMap, &Number, MenuOption->ThisTag->StorageWidth);
        } else {
          return EFI_SUCCESS;
        }
      }
    } else {
      if (((Tag->StorageWidth == 1) && (UINT8) (*NvRamMap) <= Tag->Maximum && (UINT8) (*NvRamMap) >= Tag->Minimum) ||
          ((Tag->StorageWidth == 2) && *NvRamMap <= Tag->Maximum && *NvRamMap >= Tag->Minimum)
          ) {
        if (Tag->StorageWidth == 1) {
          Number = (UINT8) (*NvRamMap);
        } else {
          Number = *NvRamMap;
        }
        UnicodeValueToString (
          FormattedNumber, 
          FALSE, 
          (UINTN) Number, 
          (sizeof (FormattedNumber) / sizeof (FormattedNumber[0]))
          );
        Number = (UINT16) GetStringWidth (FormattedNumber);
        StrnCpy (OptionString[0] + 1, FormattedNumber, Number);
      } else {
        //
        // If *NvRamMap isn't within parameters, set it to within parameters
        //
        //
        // Since the value can be one byte long or two bytes long, do a CopyMem based on StorageWidth
        //
        CopyMem (NvRamMap, &Tag->Default, MenuOption->ThisTag->StorageWidth);
        Number = Tag->Default;

        UnicodeValueToString (
          FormattedNumber, 
          FALSE, 
          (UINTN) Number, 
          (sizeof (FormattedNumber) / sizeof (FormattedNumber[0]))
          );
        Number = (UINT16) GetStringWidth (FormattedNumber);
        StrnCpy (OptionString[0] + 1, FormattedNumber, Number);
      }

      *(OptionString[0] + Number / 2) = RIGHT_NUMERIC_DELIMITER;
      NewStrCat (OptionString[0] + (Number / 2) + 1, StringPtr);
    }
    break;

  case EFI_IFR_DATE_OP:
    //
    // If the op-code we are looking at is larger than the latest created NvMap - we likely encountered a dynamically
    // created entry which has an expanded NvMap requirement.  We won't save this information - but we need to adjust
    // the NvMap so that we can properly display the information
    //
    if ((UINTN) (Tag->StorageStart + Tag->StorageWidth) > VariableDefinition->VariableFakeSize) {
      AdjustNvMap (FileFormTags, MenuOption);
      NvRamMap = (UINT16 *) &VariableDefinition->NvRamMap[Tag->StorageStart];
    }

    Status = gRT->GetTime (&Time, NULL);
    if (EFI_ERROR (Status)) {
      return EFI_SUCCESS;
    }
    //
    // This for loop advances Index till it points immediately after a date entry.  We can then
    // subtract MenuOption->TagIndex from Index and find out relative to the start of the Date
    // structure which field we were in.  For instance, if TagIndex was 52, and we advanced Index
    // to 53 and found it to no longer point to a date operand, we were pointing to the last of 3
    // date operands.
    //
    //
    // This has BUGBUG potential....fix this - if someone wants to ask two DATE questions in a row.....code
    // against such silliness.
    //
    // Also, we want to internationalize the order of the date information.  We need to code for it as well.
    //
    for (Index = MenuOption->TagIndex; MenuOption->Tags[Index].Operand == EFI_IFR_DATE_OP; Index++)
      ;

    //
    // Count 0 = We entered on the first Date operand
    // Count 1 = We entered on the second Date operand
    // Count 2 = We entered on the third Date operand
    //
    Count = 3 - (Index - MenuOption->TagIndex);
    if (Count > 2) {
      return EFI_SUCCESS;
    }
    //
    // This is similar to numerics, except for the following:
    // We will under normal circumstances get 3 consecutive calls
    // to process this opcodes data.
    //
    *OptionString = AllocateZeroPool ((gOptionBlockWidth + 1) * 2 * gScreenDimensions.BottomRow);
    ASSERT (*OptionString);

    switch (Count) {
    case 0:
      if (Selected) {
        Number = (UINT16) Time.Month;

        if (Tag->Step == 0) {
          MenuOption->OptCol++;
          Status = GetNumericInput (MenuOption, FileFormTagsHead, TRUE, Tag, DATE_NUMERIC, &Number);
        } else {
          //
          // Seed value with current setting
          //
          Tag->Value  = (UINT16) Time.Month;
          Status      = GetNumericInput (MenuOption, FileFormTagsHead, FALSE, Tag, DATE_NUMERIC, &Number);
        }

        if (!EFI_ERROR (Status)) {
          Time.Month = (UINT8) Number;
          gRT->SetTime (&Time);
        }
      }

      VariableDefinition->FakeNvRamMap[Tag->Id] = Time.Month;
      *OptionString[0]                          = LEFT_NUMERIC_DELIMITER;

      UnicodeValueToString (
        FormattedNumber, 
        FALSE, 
        (UINTN) Time.Month, 
        (sizeof (FormattedNumber) / sizeof (FormattedNumber[0]))
        );
      Number = (UINT16) GetStringWidth (FormattedNumber);

      if (Number == 4) {
        FormattedNumber[2]  = FormattedNumber[1];
        FormattedNumber[1]  = FormattedNumber[0];
        FormattedNumber[0]  = L'0';
        Number              = 6;
      }

      StrnCpy (OptionString[0] + 1, FormattedNumber, Number);
      *(OptionString[0] + Number / 2) = DATE_SEPARATOR;
      StrCat (OptionString[0] + (Number / 2) + 1, StringPtr);
      break;

    case 1:
      if (Selected) {
        Number = (UINT16) Time.Day;

        if (Tag->Step == 0) {
          Status = GetNumericInput (MenuOption, FileFormTagsHead, TRUE, Tag, DATE_NUMERIC, &Number);
        } else {
          //
          // Seed value with current setting
          //
          Tag->Value  = (UINT16) Time.Day;
          Status      = GetNumericInput (MenuOption, FileFormTagsHead, FALSE, Tag, DATE_NUMERIC, &Number);
        }

        if (!EFI_ERROR (Status)) {
          Time.Day = (UINT8) Number;
          gRT->SetTime (&Time);
        }
      }

      VariableDefinition->FakeNvRamMap[Tag->Id] = Time.Day;
      SetUnicodeMem (OptionString[0], 4, L' ');

      UnicodeValueToString (
        FormattedNumber, 
        FALSE, 
        (UINTN) Time.Day, 
        (sizeof (FormattedNumber) / sizeof (FormattedNumber[0]))
        );
      Number = (UINT16) GetStringWidth (FormattedNumber);
      if (Number == 4) {
        FormattedNumber[2]  = FormattedNumber[1];
        FormattedNumber[1]  = FormattedNumber[0];
        FormattedNumber[0]  = L'0';
        Number              = 6;
      }

      StrnCpy (OptionString[0] + 4, FormattedNumber, Number);
      *(OptionString[0] + Number / 2 + 3) = DATE_SEPARATOR;
      StrCat (OptionString[0] + (Number / 2) + 4, StringPtr);
      break;

    case 2:
      if (Selected) {
        Number = (UINT16) Time.Year;

        if (Tag->Step == 0) {
          Status = GetNumericInput (MenuOption, FileFormTagsHead, TRUE, Tag, DATE_NUMERIC, &Number);
        } else {
          //
          // Seed value with current setting
          //
          Status = GetNumericInput (MenuOption, FileFormTagsHead, FALSE, Tag, DATE_NUMERIC, &Number);
        }

        if (!EFI_ERROR (Status)) {
          Time.Year = (UINT16) Number;
          gRT->SetTime (&Time);
        }
      }

      Tag->Value  = (UINT16) Time.Year;
      VariableDefinition->FakeNvRamMap[Tag->Id]     = (UINT8) Tag->Value;
      VariableDefinition->FakeNvRamMap[Tag->Id + 1] = (UINT8) (Tag->Value >> 8);
      SetUnicodeMem (OptionString[0], 7, L' ');
      UnicodeValueToString (
        FormattedNumber, 
        FALSE, 
        (UINTN) Time.Year, 
        (sizeof (FormattedNumber) / sizeof (FormattedNumber[0]))
        );
      Number = (UINT16) GetStringWidth (FormattedNumber);
      StrnCpy (OptionString[0] + 7, FormattedNumber, Number);
      *(OptionString[0] + Number / 2 + 6) = RIGHT_NUMERIC_DELIMITER;
      StrCat (OptionString[0] + (Number / 2) + 7, StringPtr);
      break;
    }

    break;

  //
  // BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG
  // We need to add code to support the NVRam storage version of Date - this is the 1% case where someone
  // might want to set an alarm and actually preserve the data in NVRam so a driver can pick up the instruction
  // BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG
  //
  case EFI_IFR_TIME_OP:
    //
    // If the op-code we are looking at is larger than the latest created NvMap - we likely encountered a dynamically
    // created entry which has an expanded NvMap requirement.  We won't save this information - but we need to adjust
    // the NvMap so that we can properly display the information
    //
    if ((UINTN) (Tag->StorageStart + Tag->StorageWidth) > VariableDefinition->VariableFakeSize) {
      AdjustNvMap (FileFormTags, MenuOption);
      NvRamMap = (UINT16 *) &VariableDefinition->NvRamMap[Tag->StorageStart];
    }

    Status = gRT->GetTime (&Time, NULL);
    if (EFI_ERROR (Status)) {
      return EFI_SUCCESS;
    }
    //
    // This is similar to numerics, except for the following:
    // We will under normal circumstances get 3 consecutive calls
    // to process this opcodes data.
    //
    *OptionString = AllocateZeroPool ((gOptionBlockWidth + 1) * 2 * gScreenDimensions.BottomRow);
    ASSERT (*OptionString);

    //
    // This for loop advances Index till it points immediately after a date entry.  We can then
    // subtract MenuOption->TagIndex from Index and find out relative to the start of the Date
    // structure which field we were in.  For instance, if TagIndex was 52, and we advanced Index
    // to 53 and found it to no longer point to a date operand, we were pointing to the last of 3
    // date operands.
    //
    for (Index = MenuOption->TagIndex; MenuOption->Tags[Index].Operand == EFI_IFR_TIME_OP; Index++)
      ;
    //
    // Count 0 = We entered on the first Date operand
    // Count 1 = We entered on the second Date operand
    // Count 2 = We entered on the third Date operand
    //
    Count = 3 - (Index - MenuOption->TagIndex);
    if (Count > 2) {
      return EFI_SUCCESS;
    }

    switch (Count) {
    case 0:
      Number = Time.Hour;
      break;

    case 1:
      Number = Time.Minute;
      break;

    case 2:
      Number = Time.Second;
    }
    //
    // Retrieve the current numeric value
    //
    if (Selected) {
      //
      // Go ask for input
      //
      if (Tag->Step == 0) {
        //
        // Manual Input
        //
        Status = GetNumericInput (MenuOption, FileFormTagsHead, TRUE, Tag, TIME_NUMERIC, &Number);
        if (!EFI_ERROR (Status)) {
          *NvRamMap       = Number;
          Time.Nanosecond = 0;
          gRT->SetTime (&Time);
        } else {
          return EFI_SUCCESS;
        }
      } else {
        //
        // Auto selection from list
        //
        Status = GetNumericInput (MenuOption, FileFormTagsHead, FALSE, Tag, TIME_NUMERIC, &Number);
        if (!EFI_ERROR (Status)) {
          *NvRamMap = Number;
        } else {
          return EFI_SUCCESS;
        }
      }

      switch (Count) {
      case 0:
        Time.Hour = (UINT8) Number;
        break;

      case 1:
        Time.Minute = (UINT8) Number;
        break;

      case 2:
        Time.Second = (UINT8) Number;
      }

      Time.Nanosecond = 0;
      gRT->SetTime (&Time);
    } else {
      switch (Count) {
      case 0:
        *OptionString[0] = LEFT_NUMERIC_DELIMITER;
        UnicodeValueToString (
          FormattedNumber, 
          FALSE, 
          (UINTN) Time.Hour, 
          (sizeof (FormattedNumber) / sizeof (FormattedNumber[0]))
          );
        Number = (UINT16) GetStringWidth (FormattedNumber);
        if (Number == 4) {
          FormattedNumber[2]  = FormattedNumber[1];
          FormattedNumber[1]  = FormattedNumber[0];
          FormattedNumber[0]  = L'0';
          Number              = 6;
        }

        StrnCpy (OptionString[0] + 1, FormattedNumber, Number);
        *(OptionString[0] + Number / 2) = TIME_SEPARATOR;
        StrCat (OptionString[0] + (Number / 2) + 1, StringPtr);
        break;

      case 1:
        SetUnicodeMem (OptionString[0], 4, L' ');
        UnicodeValueToString (
          FormattedNumber, 
          FALSE, 
          (UINTN) Time.Minute, 
          (sizeof (FormattedNumber) / sizeof (FormattedNumber[0]))
          );
        Number = (UINT16) GetStringWidth (FormattedNumber);
        if (Number == 4) {
          FormattedNumber[2]  = FormattedNumber[1];
          FormattedNumber[1]  = FormattedNumber[0];
          FormattedNumber[0]  = L'0';
          Number              = 6;
        }

        StrnCpy (OptionString[0] + 4, FormattedNumber, Number);
        *(OptionString[0] + Number / 2 + 3) = TIME_SEPARATOR;
        StrCat (OptionString[0] + (Number / 2) + 4, StringPtr);
        break;

      case 2:
        SetUnicodeMem (OptionString[0], 7, L' ');
        UnicodeValueToString (
          FormattedNumber, 
          FALSE, 
          (UINTN) Time.Second, 
          (sizeof (FormattedNumber) / sizeof (FormattedNumber[0]))
          );
        Number = (UINT16) GetStringWidth (FormattedNumber);
        if (Number == 4) {
          FormattedNumber[2]  = FormattedNumber[1];
          FormattedNumber[1]  = FormattedNumber[0];
          FormattedNumber[0]  = L'0';
          Number              = 6;
        }

        StrnCpy (OptionString[0] + 7, FormattedNumber, Number);
        *(OptionString[0] + Number / 2 + 6) = RIGHT_NUMERIC_DELIMITER;
        StrCat (OptionString[0] + (Number / 2) + 7, StringPtr);
        break;
      }
      //
      // BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG
      // We need to add code to support the NVRam storage version of Date - this is the 1% case where someone
      // might want to set an alarm and actually preserve the data in NVRam so a driver can pick up the instruction
      // BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG BUGBUG
      //
    }
    break;

  case EFI_IFR_STRING_OP:
    //
    // If the op-code we are looking at is larger than the latest created NvMap - we likely encountered a dynamically
    // created entry which has an expanded NvMap requirement.  We won't save this information - but we need to adjust
    // the NvMap so that we can properly display the information
    //
    if ((UINTN) (Tag->StorageStart + Tag->StorageWidth) > VariableDefinition->VariableFakeSize) {
      AdjustNvMap (FileFormTags, MenuOption);
      NvRamMap = (UINT16 *) &VariableDefinition->NvRamMap[Tag->StorageStart];
    }

    *OptionString = AllocateZeroPool ((gOptionBlockWidth + 1) * 2 * gScreenDimensions.BottomRow);
    ASSERT (*OptionString);

    if (Selected) {
      StringPtr = AllocateZeroPool (Tag->Maximum);
      ASSERT (StringPtr);

      Status = ReadString (MenuOption, StringPtr);

      if (!EFI_ERROR (Status)) {
        CopyMem (gPreviousValue, NvRamMap, MenuOption->ThisTag->StorageWidth);
        CopyMem (&VariableDefinition->NvRamMap[Tag->StorageStart], StringPtr, Tag->StorageWidth);

        UpdateStatusBar (NV_UPDATE_REQUIRED, Tag->Flags, TRUE);
      }

      gBS->FreePool (StringPtr);
      return Status;
    } else {
      for (Index = 0; Index < gOptionBlockWidth; Index++) {
        if (VariableDefinition->NvRamMap[Tag->StorageStart + (Index * 2)] != 0x0000) {
          CopyMem (OptionString[0] + Index, &VariableDefinition->NvRamMap[Tag->StorageStart + (Index * 2)], 2);
        } else {
          if (Index == 0) {
            *(OptionString[0] + Index)      = '_';
            *(OptionString[0] + 1 + Index)  = 0;
          }
          break;
        }
      }

      return Status;
    }

  case EFI_IFR_PASSWORD_OP:
    //
    // If the op-code we are looking at is larger than the latest created NvMap - we likely encountered a dynamically
    // created entry which has an expanded NvMap requirement.  We won't save this information - but we need to adjust
    // the NvMap so that we can properly display the information
    //
    if ((UINTN) (Tag->StorageStart + Tag->StorageWidth) > VariableDefinition->VariableFakeSize) {
      AdjustNvMap (FileFormTags, MenuOption);
      NvRamMap = (UINT16 *) &VariableDefinition->NvRamMap[Tag->StorageStart];
    }

    if (Selected) {
      StringPtr = AllocateZeroPool (Tag->Maximum);
      ASSERT (StringPtr);

      //
      // If interactive, read the password and do the appropriate callbacks in that routine.
      // Since interactive passwords assume to handle the password data in a separate variable
      // storage, we don't need to do more than what is below for password callbacks
      //
      if (Tag->Flags & EFI_IFR_FLAG_INTERACTIVE) {
        MenuOption->Tags[0].CallbackHandle  = FileFormTags->FormTags.Tags[0].CallbackHandle;
        Status = ReadPassword (MenuOption, TRUE, Tag, PageData, FALSE, FileFormTags, StringPtr);
        ZeroMem (StringPtr, Tag->Maximum);

        if (EFI_ERROR (Status)) {
          if (Status == EFI_NOT_READY) {
            gBS->FreePool (StringPtr);
            return EFI_SUCCESS;
          }
        }

        Status = ReadPassword (MenuOption, TRUE, Tag, PageData, TRUE, FileFormTags, StringPtr);
        gBS->FreePool (StringPtr);
        return EFI_SUCCESS;
      }

      for (Index = 0; Index < Tag->Maximum; Index++) {
        if (VariableDefinition->NvRamMap[Tag->StorageStart + Index] != 0x00) {
          //
          // There is something there!  Prompt for password
          //
          Status = ReadPassword (MenuOption, TRUE, Tag, PageData, FALSE, FileFormTags, StringPtr);
          if (EFI_ERROR (Status)) {
            gBS->FreePool (StringPtr);
            return EFI_SUCCESS;
          }

          if (Tag->Encoding == 1) {
            EncodePassword (StringPtr, (UINT8) Tag->Maximum);
            Status = CompareMem (StringPtr, &VariableDefinition->NvRamMap[Tag->StorageStart], Tag->Maximum);
          } else {
            Status = CompareMem (StringPtr, &VariableDefinition->NvRamMap[Tag->StorageStart], Tag->Maximum);
          }

          if (Status != 0) {
            gBS->FreePool (StringPtr);
            return EFI_SUCCESS;
          } else {
            break;
          }
        }
      }
      //
      // Clean the string
      //
      ZeroMem (StringPtr, Tag->Maximum);

      //
      // No password set!  Go ahead and prompt the user for a password.
      //
      Status = ReadPassword (MenuOption, FALSE, Tag, PageData, FALSE, FileFormTags, StringPtr);

      if (EFI_ERROR (Status)) {
        //
        // User couldn't figure out how to type two identical passwords
        //
        gBS->FreePool (StringPtr);
        return EFI_SUCCESS;
      }
      //
      // Very simple example of how one MIGHT do password encoding
      //
      if (Tag->Encoding == 1) {
        EncodePassword (StringPtr, (UINT8) Tag->Maximum);
      }

      TmpNvRamMap = AllocatePool (VariableDefinition->VariableSize);
      ASSERT (TmpNvRamMap != NULL);

      Count = VariableDefinition->VariableSize;

      if ((FormCallback != NULL) && (FormCallback->NvRead != NULL)) {
        Status = FormCallback->NvRead (
                                FormCallback,
                                VariableDefinition->VariableName,
                                &VariableDefinition->Guid,
                                NULL,
                                &Count,
                                (VOID *) TmpNvRamMap
                                );
      } else {
        Status = gRT->GetVariable (
                        VariableDefinition->VariableName,
                        &VariableDefinition->Guid,
                        NULL,
                        &Count,
                        (VOID *) TmpNvRamMap
                        );
      }

      CopyMem (&VariableDefinition->NvRamMap[Tag->StorageStart], StringPtr, Tag->StorageWidth);
      CopyMem (&TmpNvRamMap[Tag->StorageStart], StringPtr, Tag->StorageWidth);

      if ((FormCallback != NULL) && (FormCallback->NvWrite != NULL)) {
        Status = FormCallback->NvWrite (
                                FormCallback,
                                VariableDefinition->VariableName,
                                &VariableDefinition->Guid,
                                EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
                                VariableDefinition->VariableSize,
                                (VOID *) TmpNvRamMap,
                                &gResetRequired
                                );
      } else {
        Status = gRT->SetVariable (
                        VariableDefinition->VariableName,
                        &VariableDefinition->Guid,
                        EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
                        VariableDefinition->VariableSize,
                        (VOID *) TmpNvRamMap
                        );
      }

      gBS->FreePool (TmpNvRamMap);
      gBS->FreePool (StringPtr);
      break;
    }

  default:
    break;
  }

  return EFI_SUCCESS;
}

VOID
ProcessHelpString (
  IN  CHAR16                      *StringPtr,
  OUT CHAR16                      **FormattedString,
  IN  UINTN                       RowCount
  )
{
  UINTN CurrIndex;
  UINTN PrevIndex;
  UINTN SearchIndex;
  UINTN PrevSearchIndex;
  UINTN StringCount;
  UINTN PageCount;

  StringCount = 0;
  PrevIndex   = 0;
  CurrIndex   = gHelpBlockWidth - 1;

  if (*FormattedString != NULL) {
    gBS->FreePool (*FormattedString);
    *FormattedString = NULL;
  }

  for (; CurrIndex > PrevIndex; CurrIndex--) {
    //
    // In the case where the string ended and a new one is immediately after it
    // we need to check for the null-terminator and reset the CurrIndex
    //
    SearchIndex     = CurrIndex;
    PrevSearchIndex = PrevIndex;

    for (; SearchIndex > PrevSearchIndex; PrevSearchIndex++) {
      if ((StringPtr[PrevSearchIndex] == CHAR_NULL) || (StringPtr[PrevSearchIndex] == CHAR_LINEFEED)) {
        CurrIndex = PrevSearchIndex;
        break;
      }

      if (StringPtr[PrevSearchIndex] == CHAR_CARRIAGE_RETURN) {
        if (StringPtr[PrevSearchIndex + 1] == CHAR_LINEFEED) {
          //
          // Found a "\n",advance to the next new line.
          //
          CurrIndex = PrevSearchIndex + 1;
          break;
        } else {
          //
          // Found a "\r",return to the start of the current line.
          //
          PrevIndex = PrevSearchIndex + 1;
          CurrIndex = PrevSearchIndex + gHelpBlockWidth;
          continue;
        }
      }
    }

    //
    // End of the string, thus stop counting.
    //
    if (StringPtr[CurrIndex] == CHAR_NULL) {
      StringCount++;
      break;
    }
    //
    // The premise is that for every HELP_BLOCK_WIDTH we rewind
    // until we find the first space.  That is the delimiter for
    // the string, and we will then advance our CurrIndex another
    // HELP_BLOCK_WIDTH and continue the process breaking the larger
    // string into chunks that fit within the HELP_BLOCK_WIDTH requirements.
    //
    if (StringPtr[CurrIndex] == CHAR_SPACE) {
      //
      // How many strings have been found?
      //
      StringCount++;
      PrevIndex = CurrIndex + 1;
      CurrIndex = CurrIndex + gHelpBlockWidth;
    }
    //
    // Found a Linefeed, advance to the next line.
    //
    if (StringPtr[CurrIndex] == CHAR_LINEFEED) {
      StringCount++;
      PrevIndex = CurrIndex + 1;
      CurrIndex = CurrIndex + gHelpBlockWidth;
    }
  }
  //
  // endfor
  //
  // Round the value up one (doesn't hurt)
  //
  StringCount++;

  //
  // Determine the number of pages this help string occupies
  //
  PageCount = StringCount / RowCount;
  if (StringCount % RowCount > 0) {
    PageCount++;
  }
  //
  // Convert the PageCount into lines so we can allocate the correct buffer size
  //
  StringCount = PageCount * RowCount;

  //
  // We now know how many strings we will have, so we can allocate the
  // space required for the array or strings.
  //
  *FormattedString = AllocateZeroPool ((StringCount) * (gHelpBlockWidth + 1) * 2);
  ASSERT (*FormattedString);

  StringCount = 0;
  PrevIndex   = 0;
  CurrIndex   = gHelpBlockWidth - 1;

  for (; CurrIndex > PrevIndex; CurrIndex--) {
    //
    // In the case where the string ended and a new one is immediately after it
    // we need to check for the null-terminator and reset the CurrIndex
    //
    SearchIndex     = CurrIndex;
    PrevSearchIndex = PrevIndex;

    for (; SearchIndex > PrevSearchIndex; PrevSearchIndex++) {
      if ((StringPtr[PrevSearchIndex] == CHAR_NULL) || (StringPtr[PrevSearchIndex] == CHAR_LINEFEED)) {
        CurrIndex = PrevSearchIndex;
        break;
      }

      if (StringPtr[PrevSearchIndex] == CHAR_CARRIAGE_RETURN) {
        if (StringPtr[PrevSearchIndex + 1] == CHAR_LINEFEED) {
          //
          // Found a "\n",advance to the next new line.
          //
          CurrIndex = PrevSearchIndex + 1;
          break;
        } else {
          //
          // Found a "\r",return to the start of the current line.
          //
          PrevIndex = PrevSearchIndex + 1;
          CurrIndex = PrevSearchIndex + gHelpBlockWidth;
          continue;
        }
      }
    }

    //
    // End of the string, thus stop counting.
    //
    if (StringPtr[CurrIndex] == CHAR_NULL) {
      //
      // Copy the fragment to the FormattedString buffer
      //
      StrnCpy ((FormattedString[0] + StringCount * gHelpBlockWidth), &StringPtr[PrevIndex], CurrIndex - PrevIndex);
      StringCount++;
      break;
    }
    //
    // The premise is that for every HELP_BLOCK_WIDTH we rewind
    // until we find the first space.  That is the delimiter for
    // the string, and we will then advance our CurrIndex another
    // HELP_BLOCK_WIDTH and continue the process breaking the larger
    // string into chunks that fit within the HELP_BLOCK_WIDTH requirements.
    //
    if (StringPtr[CurrIndex] == CHAR_SPACE) {
      //
      // Copy the fragment to the FormattedString buffer
      //
      StrnCpy ((FormattedString[0] + StringCount * gHelpBlockWidth), &StringPtr[PrevIndex], CurrIndex - PrevIndex);
      StringCount++;
      PrevIndex = CurrIndex + 1;
      CurrIndex = CurrIndex + gHelpBlockWidth;
    }
    //
    // Found a LineFeed, advance to the next line.
    //
    if (StringPtr[CurrIndex] == CHAR_LINEFEED) {
      StringPtr[CurrIndex] = CHAR_SPACE;
      //
      // "\n" is represented as CHAR_CARRIAGE_RETURN + CHAR_LINEFEED,check this.
      //
      if (StringPtr[CurrIndex - 1] == CHAR_CARRIAGE_RETURN) {
        StringPtr[CurrIndex - 1] = CHAR_SPACE;
      }

      StrnCpy ((FormattedString[0] + StringCount * gHelpBlockWidth), &StringPtr[PrevIndex], CurrIndex - PrevIndex);
      StringCount++;
      PrevIndex = CurrIndex + 1;
      CurrIndex = CurrIndex + gHelpBlockWidth;
    }
  }
  //
  // endfor
  //
  return ;
}

VOID
IfrToFormTag (
  IN  UINT8               OpCode,
  IN  EFI_TAG             *TargetTag,
  IN  VOID                *FormData,
  EFI_VARIABLE_DEFINITION *VariableDefinitionsHead
  )
{
  UINT16                  TempValue;
  CHAR16                  *VariableName;
  CHAR8                   *AsciiString;
  EFI_VARIABLE_DEFINITION *VariableDefinitions;
  EFI_VARIABLE_DEFINITION *PreviousVariableDefinitions;
  STATIC UINT16           VariableSize;
  EFI_GUID                Guid;
  STATIC UINT16           CurrentVariable;
  STATIC UINT16           CurrentVariable2;
  UINTN                   Index;

  switch (OpCode) {
  case EFI_IFR_FORM_OP:
    CopyMem (&TargetTag->Id, &((EFI_IFR_FORM *) FormData)->FormId, sizeof (UINT16));
    CopyMem (&TargetTag->Text, &((EFI_IFR_FORM *) FormData)->FormTitle, sizeof (UINT16));
    TargetTag->VariableNumber = CurrentVariable;
    if (VariableDefinitionsHead != NULL) {
      VariableName = AllocateZeroPool (12);
      ASSERT (VariableName != NULL);
      CopyMem (VariableName, L"Setup", 12);
      VariableDefinitionsHead->VariableName = VariableName;
      VariableDefinitionsHead->VariableSize = VariableSize;
      CopyMem (&VariableDefinitionsHead->Guid, &Guid, sizeof (EFI_GUID));
    }
    break;

  case EFI_IFR_SUBTITLE_OP:
    TargetTag->NumberOfLines = 1;
    CopyMem (&TargetTag->Text, &((EFI_IFR_SUBTITLE *) FormData)->SubTitle, sizeof (UINT16));
    TargetTag->VariableNumber = CurrentVariable;
    break;

  case EFI_IFR_TEXT_OP:
    TargetTag->NumberOfLines = 1;
    CopyMem (&TargetTag->Text, &((EFI_IFR_TEXT *) FormData)->Text, sizeof (UINT16));
    CopyMem (&TargetTag->Help, &((EFI_IFR_TEXT *) FormData)->Help, sizeof (UINT16));
    TargetTag->VariableNumber = CurrentVariable;

    //
    // To optimize the encoding size, certain opcodes have optional fields such as those
    // inside the if() statement.  If the encoded length is the complete size, then we
    // know we have valid data encoded that we want to integrate
    //
    if (((EFI_IFR_TEXT *) FormData)->Header.Length == sizeof (EFI_IFR_TEXT)) {
      //
      // Text has no help associated with it, but in case there is a second entry due to
      // dynamic/interactive flags being active, bring this data over.
      //
      CopyMem (&TargetTag->TextTwo, &((EFI_IFR_TEXT *) FormData)->TextTwo, sizeof (UINT16));
      TargetTag->Flags = ((EFI_IFR_TEXT *) FormData)->Flags;
      CopyMem (&TargetTag->Key, &((EFI_IFR_TEXT *) FormData)->Key, sizeof (UINT16));
    }
    break;

  case EFI_IFR_ONE_OF_OPTION_OP:
    CopyMem (&TargetTag->Text, &((EFI_IFR_ONE_OF_OPTION *) FormData)->Option, sizeof (UINT16));
    CopyMem (&TargetTag->Value, &((EFI_IFR_ONE_OF_OPTION *) FormData)->Value, sizeof (UINT16));
    TargetTag->Flags = ((EFI_IFR_ONE_OF_OPTION *) FormData)->Flags;
    CopyMem (&TargetTag->Key, &((EFI_IFR_ONE_OF_OPTION *) FormData)->Key, sizeof (UINT16));
    TargetTag->VariableNumber = CurrentVariable;
    break;

  case EFI_IFR_CHECKBOX_OP:
    TargetTag->Flags          = ((EFI_IFR_CHECKBOX *) FormData)->Flags;
    TargetTag->ResetRequired  = (BOOLEAN) (TargetTag->Flags & EFI_IFR_FLAG_RESET_REQUIRED);
    CopyMem (&TargetTag->Key, &((EFI_IFR_CHECKBOX *) FormData)->Key, sizeof (UINT16));
    TargetTag->VariableNumber = CurrentVariable;
    break;

  case EFI_IFR_NUMERIC_OP:
    TargetTag->Flags = ((EFI_IFR_NUMERIC *) FormData)->Flags;
    CopyMem (&TargetTag->Key, &((EFI_IFR_NUMERIC *) FormData)->Key, sizeof (UINT16));
    TargetTag->VariableNumber = CurrentVariable;
    break;

  case EFI_IFR_STRING_OP:
    TempValue = 0;
    CopyMem (&TempValue, &((EFI_IFR_STRING *) FormData)->MinSize, sizeof (UINT8));
    TempValue = (UINT16) (TempValue * 2);
    CopyMem (&TargetTag->Minimum, &TempValue, sizeof (UINT16));

    CopyMem (&TempValue, &((EFI_IFR_STRING *) FormData)->MaxSize, sizeof (UINT8));
    TempValue = (UINT16) (TempValue * 2);
    CopyMem (&TargetTag->Maximum, &TempValue, sizeof (UINT16));
    CopyMem (&TargetTag->StorageWidth, &TempValue, sizeof (UINT16));
    TargetTag->Flags          = (UINT8) (((EFI_IFR_STRING *) FormData)->Flags);
    TargetTag->ResetRequired  = (BOOLEAN) (TargetTag->Flags & EFI_IFR_FLAG_RESET_REQUIRED);
    CopyMem (&TargetTag->Key, &((EFI_IFR_STRING *) FormData)->Key, sizeof (UINT16));
    TargetTag->VariableNumber = CurrentVariable;
    break;

  case EFI_IFR_PASSWORD_OP:
    TempValue = 0;
    CopyMem (&TempValue, &((EFI_IFR_PASSWORD *) FormData)->MinSize, sizeof (UINT8));
    TempValue = (UINT16) (TempValue * 2);
    CopyMem (&TargetTag->Minimum, &TempValue, sizeof (UINT16));

    CopyMem (&TempValue, &((EFI_IFR_PASSWORD *) FormData)->MaxSize, sizeof (UINT8));
    TempValue = (UINT16) (TempValue * 2);
    CopyMem (&TargetTag->Maximum, &TempValue, sizeof (UINT16));
    CopyMem (&TargetTag->StorageWidth, &TempValue, sizeof (UINT16));
    TargetTag->Flags          = ((EFI_IFR_PASSWORD *) FormData)->Flags;
    TargetTag->ResetRequired  = (BOOLEAN) (TargetTag->Flags & EFI_IFR_FLAG_RESET_REQUIRED);
    CopyMem (&TargetTag->Key, &((EFI_IFR_PASSWORD *) FormData)->Key, sizeof (UINT16));
    CopyMem (&TargetTag->Encoding, &((EFI_IFR_PASSWORD *) FormData)->Encoding, sizeof (UINT16));
    TargetTag->VariableNumber = CurrentVariable;
    break;

  case EFI_IFR_VARSTORE_OP:
    //
    // It should NEVER be NULL
    //
    if (VariableDefinitionsHead == NULL) {
      break;
    }

    VariableDefinitions = VariableDefinitionsHead;

    //
    // Advance VariableDefinitions to the last entry
    //
    for (; VariableDefinitions != NULL; VariableDefinitions = VariableDefinitions->Next) {
      PreviousVariableDefinitions = VariableDefinitions;
      //
      // If there is a variable with this GUID and ID already, we need to bail out
      //
      if (!CompareMem (&VariableDefinitions->Guid, &((EFI_IFR_VARSTORE *) FormData)->Guid, sizeof (EFI_GUID)) &&
          !CompareMem (&VariableDefinitions->VariableId, &((EFI_IFR_VARSTORE *) FormData)->VarId, sizeof (UINT16))
            ) {
        return ;
      }

      if (VariableDefinitions->Next == NULL) {
        break;
      }
    }
    //
    // If the last entry has a variable in it already, allocate a new entry and use it
    //
    if (VariableDefinitions->VariableName != NULL) {
      VariableDefinitions->Next = AllocateZeroPool (sizeof (EFI_VARIABLE_DEFINITION));
      ASSERT (VariableDefinitions->Next != NULL);
      PreviousVariableDefinitions   = VariableDefinitions;
      VariableDefinitions           = VariableDefinitions->Next;
      VariableDefinitions->Previous = PreviousVariableDefinitions;
    }
    //
    // Copy the Variable data to our linked list
    //
    CopyMem (&VariableDefinitions->VariableId, &((EFI_IFR_VARSTORE *) FormData)->VarId, sizeof (UINT16));
    CopyMem (&VariableDefinitions->VariableSize, &((EFI_IFR_VARSTORE *) FormData)->Size, sizeof (UINT16));
    CopyMem (&VariableDefinitions->Guid, &((EFI_IFR_VARSTORE *) FormData)->Guid, sizeof (EFI_GUID));

    //
    // The ASCII String which is immediately past the EFI_IFR_VARSTORE is inferred by the structure definition
    // due to it being variable sized.  There are rules preventing it from being > 40 characters long and should
    // be enforced by the compiler.
    //
    AsciiString                       = (CHAR8 *) (&((EFI_IFR_VARSTORE *) FormData)->Size);
    AsciiString                       = AsciiString + 2;
    VariableDefinitions->VariableName = AllocateZeroPool ((AsciiStrLen (AsciiString) + 1) * 2);
    ASSERT (VariableDefinitions->VariableName != NULL);
    for (Index = 0; AsciiString[Index] != 0; Index++) {
      VariableDefinitions->VariableName[Index] = (CHAR16) AsciiString[Index];
    }

    VariableDefinitions->VariableName[Index] = 0;

    //
    // Propogate the tag information for this op-code
    //
    CopyMem (&TargetTag->VariableNumber, &((EFI_IFR_VARSTORE *) FormData)->VarId, sizeof (UINT16));
    CopyMem (&TargetTag->GuidValue, &((EFI_IFR_VARSTORE *) FormData)->Guid, sizeof (EFI_GUID));
    CopyMem (&TargetTag->StorageWidth, &((EFI_IFR_VARSTORE *) FormData)->Size, sizeof (UINT16));
    CopyMem (&TargetTag->Maximum, &((EFI_IFR_VARSTORE *) FormData)->Size, sizeof (UINT16));
    break;

  case EFI_IFR_VARSTORE_SELECT_OP:
    CopyMem (&TargetTag->VariableNumber, &((EFI_IFR_VARSTORE_SELECT *) FormData)->VarId, sizeof (UINT16));
    CopyMem (&CurrentVariable, &((EFI_IFR_VARSTORE_SELECT *) FormData)->VarId, sizeof (UINT16));
    CurrentVariable2 = CurrentVariable;
    break;

  case EFI_IFR_VARSTORE_SELECT_PAIR_OP:
    CopyMem (&TargetTag->VariableNumber, &((EFI_IFR_VARSTORE_SELECT_PAIR *) FormData)->VarId, sizeof (UINT16));
    CopyMem (
      &TargetTag->VariableNumber2,
      &((EFI_IFR_VARSTORE_SELECT_PAIR *) FormData)->SecondaryVarId,
      sizeof (UINT16)
      );
    CopyMem (&CurrentVariable, &((EFI_IFR_VARSTORE_SELECT_PAIR *) FormData)->VarId, sizeof (UINT16));
    CopyMem (&CurrentVariable2, &((EFI_IFR_VARSTORE_SELECT_PAIR *) FormData)->SecondaryVarId, sizeof (UINT16));
    break;

  case EFI_IFR_REF_OP:
    TargetTag->NumberOfLines = 1;
    CopyMem (&TargetTag->Id, &((EFI_IFR_REF *) FormData)->FormId, sizeof (UINT16));
    CopyMem (&TargetTag->Key, &((EFI_IFR_REF *) FormData)->Key, sizeof (UINT16));
    CopyMem (&TargetTag->Text, &((EFI_IFR_REF *) FormData)->Prompt, sizeof (UINT16));
    CopyMem (&TargetTag->Help, &((EFI_IFR_REF *) FormData)->Help, sizeof (UINT16));
    TargetTag->Flags          = ((EFI_IFR_REF *) FormData)->Flags;
    TargetTag->VariableNumber = CurrentVariable;
    break;

  case EFI_IFR_EQ_ID_VAL_OP:
    CopyMem (&TargetTag->Value, &((EFI_IFR_EQ_ID_VAL *) FormData)->Value, sizeof (UINT16));
    CopyMem (&TargetTag->Id, &((EFI_IFR_EQ_ID_VAL *) FormData)->QuestionId, sizeof (UINT16));
    TargetTag->StorageWidth   = ((EFI_IFR_EQ_ID_VAL *) FormData)->Width;
    TargetTag->VariableNumber = CurrentVariable;
    break;

  case EFI_IFR_EQ_VAR_VAL_OP:
    CopyMem (&TargetTag->Value, &((EFI_IFR_EQ_VAR_VAL *) FormData)->Value, sizeof (UINT16));
    CopyMem (&TargetTag->Id, &((EFI_IFR_EQ_VAR_VAL *) FormData)->VariableId, sizeof (UINT16));
    TargetTag->VariableNumber = CurrentVariable;
    break;

  case EFI_IFR_EQ_ID_ID_OP:
    CopyMem (&TargetTag->Id, &((EFI_IFR_EQ_ID_ID *) FormData)->QuestionId1, sizeof (UINT16));
    CopyMem (&TargetTag->Id2, &((EFI_IFR_EQ_ID_ID *) FormData)->QuestionId2, sizeof (UINT16));
    TargetTag->StorageWidth   = ((EFI_IFR_EQ_ID_ID *) FormData)->Width;
    TargetTag->VariableNumber = CurrentVariable;
    TargetTag->VariableNumber = CurrentVariable2;
    break;

  case EFI_IFR_EQ_ID_LIST_OP:
    CopyMem (&TargetTag->Id, &((EFI_IFR_EQ_ID_LIST *) FormData)->QuestionId, sizeof (UINT16));
    CopyMem (&TargetTag->Id2, &((EFI_IFR_EQ_ID_LIST *) FormData)->ListLength, sizeof (UINT16));
    TargetTag->StorageWidth = ((EFI_IFR_EQ_ID_LIST *) FormData)->Width;

    TargetTag->IntList      = AllocateZeroPool (TargetTag->Id2 * sizeof (UINT16));
    ASSERT (TargetTag->IntList);

    for (TempValue = 0; TempValue < TargetTag->Id2; TempValue++) {
      CopyMem (
        &TargetTag->IntList[TempValue],
        &((EFI_IFR_EQ_ID_LIST *) FormData)->ValueList[TempValue],
        sizeof (UINT16)
        );
    }

    TargetTag->VariableNumber = CurrentVariable;
    break;

  case EFI_IFR_FORM_SET_OP:
    CopyMem (&VariableSize, &((EFI_IFR_FORM_SET *) FormData)->NvDataSize, sizeof (UINT16));
    CopyMem (&Guid, &((EFI_IFR_FORM_SET *) FormData)->Guid, sizeof (EFI_GUID));
    //
    // If there is a size specified in the formste, we will establish a "default" variable
    //
    if (VariableDefinitionsHead != NULL) {
      VariableName = AllocateZeroPool (12);
      ASSERT (VariableName != NULL);
      CopyMem (VariableName, L"Setup", 12);
      VariableDefinitionsHead->VariableName = VariableName;
      VariableDefinitionsHead->VariableSize = VariableSize;
      CopyMem (&VariableDefinitionsHead->Guid, &Guid, sizeof (EFI_GUID));
    }
    break;

  case EFI_IFR_END_FORM_SET_OP:
    CurrentVariable   = 0;
    CurrentVariable2  = 0;
    break;
  }

  return ;
}
