Fixes CodeQL alerts for CWE-457: https://cwe.mitre.org/data/definitions/457.html Cc: Dandan Bi <dandan.bi@intel.com> Cc: Eric Dong <eric.dong@intel.com> Cc: Erich McMillan <emcmillan@microsoft.com> Cc: Guomin Jiang <guomin.jiang@intel.com> Cc: Jian J Wang <jian.j.wang@intel.com> Cc: Liming Gao <gaoliming@byosoft.com.cn> Cc: Michael Kubacki <mikuback@linux.microsoft.com> Cc: Ray Ni <ray.ni@intel.com> Cc: Zhichao Gao <zhichao.gao@intel.com> Co-authored-by: Erich McMillan <emcmillan@microsoft.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn> Reviewed-by: Oliver Smith-Denny <osd@smith-denny.com>
		
			
				
	
	
		
			1618 lines
		
	
	
		
			48 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1618 lines
		
	
	
		
			48 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| Implementation for handling the User Interface option processing.
 | |
| 
 | |
| 
 | |
| Copyright (c) 2004 - 2020, Intel Corporation. All rights reserved.<BR>
 | |
| SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "FormDisplay.h"
 | |
| 
 | |
| #define MAX_TIME_OUT_LEN  0x10
 | |
| 
 | |
| /**
 | |
|   Concatenate a narrow string to another string.
 | |
| 
 | |
|   @param Destination The destination string.
 | |
|   @param DestMax     The Max length of destination string.
 | |
|   @param Source      The source string. The string to be concatenated.
 | |
|                      to the end of Destination.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| NewStrCat (
 | |
|   IN OUT CHAR16  *Destination,
 | |
|   IN     UINTN   DestMax,
 | |
|   IN     CHAR16  *Source
 | |
|   )
 | |
| {
 | |
|   UINTN  Length;
 | |
| 
 | |
|   for (Length = 0; Destination[Length] != 0; Length++) {
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // We now have the length of the original string
 | |
|   // We can safely assume for now that we are concatenating a narrow value to this string.
 | |
|   // For instance, the string is "XYZ" and cat'ing ">"
 | |
|   // If this assumption changes, we need to make this routine a bit more complex
 | |
|   //
 | |
|   Destination[Length] = NARROW_CHAR;
 | |
|   Length++;
 | |
| 
 | |
|   StrCpyS (Destination + Length, DestMax - Length, Source);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Get UINT64 type value.
 | |
| 
 | |
|   @param  Value                  Input Hii value.
 | |
| 
 | |
|   @retval UINT64                 Return the UINT64 type value.
 | |
| 
 | |
| **/
 | |
| UINT64
 | |
| HiiValueToUINT64 (
 | |
|   IN EFI_HII_VALUE  *Value
 | |
|   )
 | |
| {
 | |
|   UINT64  RetVal;
 | |
| 
 | |
|   RetVal = 0;
 | |
| 
 | |
|   switch (Value->Type) {
 | |
|     case EFI_IFR_TYPE_NUM_SIZE_8:
 | |
|       RetVal = Value->Value.u8;
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_TYPE_NUM_SIZE_16:
 | |
|       RetVal = Value->Value.u16;
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_TYPE_NUM_SIZE_32:
 | |
|       RetVal = Value->Value.u32;
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_TYPE_BOOLEAN:
 | |
|       RetVal = Value->Value.b;
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_TYPE_DATE:
 | |
|       RetVal = *(UINT64 *)&Value->Value.date;
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_TYPE_TIME:
 | |
|       RetVal = (*(UINT64 *)&Value->Value.time) & 0xffffff;
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       RetVal = Value->Value.u64;
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   return RetVal;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check whether this value type can be transfer to EFI_IFR_TYPE_BUFFER type.
 | |
| 
 | |
|   EFI_IFR_TYPE_REF, EFI_IFR_TYPE_DATE and EFI_IFR_TYPE_TIME are converted to
 | |
|   EFI_IFR_TYPE_BUFFER when do the value compare.
 | |
| 
 | |
|   @param  Value                  Expression value to compare on.
 | |
| 
 | |
|   @retval TRUE                   This value type can be transter to EFI_IFR_TYPE_BUFFER type.
 | |
|   @retval FALSE                  This value type can't be transter to EFI_IFR_TYPE_BUFFER type.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| IsTypeInBuffer (
 | |
|   IN  EFI_HII_VALUE  *Value
 | |
|   )
 | |
| {
 | |
|   switch (Value->Type) {
 | |
|     case EFI_IFR_TYPE_BUFFER:
 | |
|     case EFI_IFR_TYPE_DATE:
 | |
|     case EFI_IFR_TYPE_TIME:
 | |
|     case EFI_IFR_TYPE_REF:
 | |
|       return TRUE;
 | |
| 
 | |
|     default:
 | |
|       return FALSE;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check whether this value type can be transfer to EFI_IFR_TYPE_UINT64
 | |
| 
 | |
|   @param  Value                  Expression value to compare on.
 | |
| 
 | |
|   @retval TRUE                   This value type can be transter to EFI_IFR_TYPE_BUFFER type.
 | |
|   @retval FALSE                  This value type can't be transter to EFI_IFR_TYPE_BUFFER type.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| IsTypeInUINT64 (
 | |
|   IN  EFI_HII_VALUE  *Value
 | |
|   )
 | |
| {
 | |
|   switch (Value->Type) {
 | |
|     case EFI_IFR_TYPE_NUM_SIZE_8:
 | |
|     case EFI_IFR_TYPE_NUM_SIZE_16:
 | |
|     case EFI_IFR_TYPE_NUM_SIZE_32:
 | |
|     case EFI_IFR_TYPE_NUM_SIZE_64:
 | |
|     case EFI_IFR_TYPE_BOOLEAN:
 | |
|       return TRUE;
 | |
| 
 | |
|     default:
 | |
|       return FALSE;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Return the buffer length and buffer pointer for this value.
 | |
| 
 | |
|   EFI_IFR_TYPE_REF, EFI_IFR_TYPE_DATE and EFI_IFR_TYPE_TIME are converted to
 | |
|   EFI_IFR_TYPE_BUFFER when do the value compare.
 | |
| 
 | |
|   @param  Value                  Expression value to compare on.
 | |
|   @param  Buf                    Return the buffer pointer.
 | |
|   @param  BufLen                 Return the buffer length.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| GetBufAndLenForValue (
 | |
|   IN  EFI_HII_VALUE  *Value,
 | |
|   OUT UINT8          **Buf,
 | |
|   OUT UINT16         *BufLen
 | |
|   )
 | |
| {
 | |
|   switch (Value->Type) {
 | |
|     case EFI_IFR_TYPE_BUFFER:
 | |
|       *Buf    = Value->Buffer;
 | |
|       *BufLen = Value->BufferLen;
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_TYPE_DATE:
 | |
|       *Buf    = (UINT8 *)(&Value->Value.date);
 | |
|       *BufLen = (UINT16)sizeof (EFI_HII_DATE);
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_TYPE_TIME:
 | |
|       *Buf    = (UINT8 *)(&Value->Value.time);
 | |
|       *BufLen = (UINT16)sizeof (EFI_HII_TIME);
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_TYPE_REF:
 | |
|       *Buf    = (UINT8 *)(&Value->Value.ref);
 | |
|       *BufLen = (UINT16)sizeof (EFI_HII_REF);
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       *Buf    = NULL;
 | |
|       *BufLen = 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Compare two Hii value.
 | |
| 
 | |
|   @param  Value1                 Expression value to compare on left-hand.
 | |
|   @param  Value2                 Expression value to compare on right-hand.
 | |
|   @param  Result                 Return value after compare.
 | |
|                                  retval 0                      Two operators equal.
 | |
|                                  return Positive value if Value1 is greater than Value2.
 | |
|                                  retval Negative value if Value1 is less than Value2.
 | |
|   @param  HiiHandle              Only required for string compare.
 | |
| 
 | |
|   @retval other                  Could not perform compare on two values.
 | |
|   @retval EFI_SUCCESS            Compare the value success.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| CompareHiiValue (
 | |
|   IN  EFI_HII_VALUE   *Value1,
 | |
|   IN  EFI_HII_VALUE   *Value2,
 | |
|   OUT INTN            *Result,
 | |
|   IN  EFI_HII_HANDLE  HiiHandle OPTIONAL
 | |
|   )
 | |
| {
 | |
|   INT64   Temp64;
 | |
|   CHAR16  *Str1;
 | |
|   CHAR16  *Str2;
 | |
|   UINTN   Len;
 | |
|   UINT8   *Buf1;
 | |
|   UINT16  Buf1Len;
 | |
|   UINT8   *Buf2;
 | |
|   UINT16  Buf2Len;
 | |
| 
 | |
|   if ((Value1->Type == EFI_IFR_TYPE_STRING) && (Value2->Type == EFI_IFR_TYPE_STRING)) {
 | |
|     if ((Value1->Value.string == 0) || (Value2->Value.string == 0)) {
 | |
|       //
 | |
|       // StringId 0 is reserved
 | |
|       //
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     if (Value1->Value.string == Value2->Value.string) {
 | |
|       *Result = 0;
 | |
|       return EFI_SUCCESS;
 | |
|     }
 | |
| 
 | |
|     Str1 = GetToken (Value1->Value.string, HiiHandle);
 | |
|     if (Str1 == NULL) {
 | |
|       //
 | |
|       // String not found
 | |
|       //
 | |
|       return EFI_NOT_FOUND;
 | |
|     }
 | |
| 
 | |
|     Str2 = GetToken (Value2->Value.string, HiiHandle);
 | |
|     if (Str2 == NULL) {
 | |
|       FreePool (Str1);
 | |
|       return EFI_NOT_FOUND;
 | |
|     }
 | |
| 
 | |
|     *Result = StrCmp (Str1, Str2);
 | |
| 
 | |
|     FreePool (Str1);
 | |
|     FreePool (Str2);
 | |
| 
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Take types(date, time, ref, buffer) as buffer
 | |
|   //
 | |
|   if (IsTypeInBuffer (Value1) && IsTypeInBuffer (Value2)) {
 | |
|     GetBufAndLenForValue (Value1, &Buf1, &Buf1Len);
 | |
|     GetBufAndLenForValue (Value2, &Buf2, &Buf2Len);
 | |
| 
 | |
|     Len     = Buf1Len > Buf2Len ? Buf2Len : Buf1Len;
 | |
|     *Result = CompareMem (Buf1, Buf2, Len);
 | |
|     if ((*Result == 0) && (Buf1Len != Buf2Len)) {
 | |
|       //
 | |
|       // In this case, means base on samll number buffer, the data is same
 | |
|       // So which value has more data, which value is bigger.
 | |
|       //
 | |
|       *Result = Buf1Len > Buf2Len ? 1 : -1;
 | |
|     }
 | |
| 
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Take remain types(integer, boolean, date/time) as integer
 | |
|   //
 | |
|   if (IsTypeInUINT64 (Value1) && IsTypeInUINT64 (Value2)) {
 | |
|     Temp64 = HiiValueToUINT64 (Value1) - HiiValueToUINT64 (Value2);
 | |
|     if (Temp64 > 0) {
 | |
|       *Result = 1;
 | |
|     } else if (Temp64 < 0) {
 | |
|       *Result = -1;
 | |
|     } else {
 | |
|       *Result = 0;
 | |
|     }
 | |
| 
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   return EFI_UNSUPPORTED;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Search an Option of a Question by its value.
 | |
| 
 | |
|   @param  Question               The Question
 | |
|   @param  OptionValue            Value for Option to be searched.
 | |
| 
 | |
|   @retval Pointer                Pointer to the found Option.
 | |
|   @retval NULL                   Option not found.
 | |
| 
 | |
| **/
 | |
| DISPLAY_QUESTION_OPTION *
 | |
| ValueToOption (
 | |
|   IN FORM_DISPLAY_ENGINE_STATEMENT  *Question,
 | |
|   IN EFI_HII_VALUE                  *OptionValue
 | |
|   )
 | |
| {
 | |
|   LIST_ENTRY               *Link;
 | |
|   DISPLAY_QUESTION_OPTION  *Option;
 | |
|   INTN                     Result;
 | |
|   EFI_HII_VALUE            Value;
 | |
| 
 | |
|   Link = GetFirstNode (&Question->OptionListHead);
 | |
|   while (!IsNull (&Question->OptionListHead, Link)) {
 | |
|     Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
 | |
| 
 | |
|     ZeroMem (&Value, sizeof (EFI_HII_VALUE));
 | |
|     Value.Type = Option->OptionOpCode->Type;
 | |
|     CopyMem (&Value.Value, &Option->OptionOpCode->Value, Option->OptionOpCode->Header.Length - OFFSET_OF (EFI_IFR_ONE_OF_OPTION, Value));
 | |
| 
 | |
|     if ((CompareHiiValue (&Value, OptionValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) {
 | |
|       return Option;
 | |
|     }
 | |
| 
 | |
|     Link = GetNextNode (&Question->OptionListHead, Link);
 | |
|   }
 | |
| 
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Return data element in an Array by its Index.
 | |
| 
 | |
|   @param  Array                  The data array.
 | |
|   @param  Type                   Type of the data in this array.
 | |
|   @param  Index                  Zero based index for data in this array.
 | |
| 
 | |
|   @retval Value                  The data to be returned
 | |
| 
 | |
| **/
 | |
| UINT64
 | |
| GetArrayData (
 | |
|   IN VOID   *Array,
 | |
|   IN UINT8  Type,
 | |
|   IN UINTN  Index
 | |
|   )
 | |
| {
 | |
|   UINT64  Data;
 | |
| 
 | |
|   ASSERT (Array != NULL);
 | |
| 
 | |
|   Data = 0;
 | |
|   switch (Type) {
 | |
|     case EFI_IFR_TYPE_NUM_SIZE_8:
 | |
|       Data = (UINT64)*(((UINT8 *)Array) + Index);
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_TYPE_NUM_SIZE_16:
 | |
|       Data = (UINT64)*(((UINT16 *)Array) + Index);
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_TYPE_NUM_SIZE_32:
 | |
|       Data = (UINT64)*(((UINT32 *)Array) + Index);
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_TYPE_NUM_SIZE_64:
 | |
|       Data = (UINT64)*(((UINT64 *)Array) + Index);
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   return Data;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Set value of a data element in an Array by its Index.
 | |
| 
 | |
|   @param  Array                  The data array.
 | |
|   @param  Type                   Type of the data in this array.
 | |
|   @param  Index                  Zero based index for data in this array.
 | |
|   @param  Value                  The value to be set.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| SetArrayData (
 | |
|   IN VOID    *Array,
 | |
|   IN UINT8   Type,
 | |
|   IN UINTN   Index,
 | |
|   IN UINT64  Value
 | |
|   )
 | |
| {
 | |
|   ASSERT (Array != NULL);
 | |
| 
 | |
|   switch (Type) {
 | |
|     case EFI_IFR_TYPE_NUM_SIZE_8:
 | |
|       *(((UINT8 *)Array) + Index) = (UINT8)Value;
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_TYPE_NUM_SIZE_16:
 | |
|       *(((UINT16 *)Array) + Index) = (UINT16)Value;
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_TYPE_NUM_SIZE_32:
 | |
|       *(((UINT32 *)Array) + Index) = (UINT32)Value;
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_TYPE_NUM_SIZE_64:
 | |
|       *(((UINT64 *)Array) + Index) = (UINT64)Value;
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check whether this value already in the array, if yes, return the index.
 | |
| 
 | |
|   @param  Array                  The data array.
 | |
|   @param  Type                   Type of the data in this array.
 | |
|   @param  Value                  The value to be find.
 | |
|   @param  Index                  The index in the array which has same value with Value.
 | |
| 
 | |
|   @retval   TRUE Found the value in the array.
 | |
|   @retval   FALSE Not found the value.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| FindArrayData (
 | |
|   IN VOID    *Array,
 | |
|   IN UINT8   Type,
 | |
|   IN UINT64  Value,
 | |
|   OUT UINTN  *Index OPTIONAL
 | |
|   )
 | |
| {
 | |
|   UINTN   Count;
 | |
|   UINT64  TmpValue;
 | |
|   UINT64  ValueComp;
 | |
| 
 | |
|   ASSERT (Array != NULL);
 | |
| 
 | |
|   Count    = 0;
 | |
|   TmpValue = 0;
 | |
| 
 | |
|   switch (Type) {
 | |
|     case EFI_IFR_TYPE_NUM_SIZE_8:
 | |
|       ValueComp = (UINT8)Value;
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_TYPE_NUM_SIZE_16:
 | |
|       ValueComp = (UINT16)Value;
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_TYPE_NUM_SIZE_32:
 | |
|       ValueComp = (UINT32)Value;
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_TYPE_NUM_SIZE_64:
 | |
|       ValueComp = (UINT64)Value;
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       ValueComp = 0;
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   while ((TmpValue = GetArrayData (Array, Type, Count)) != 0) {
 | |
|     if (ValueComp == TmpValue) {
 | |
|       if (Index != NULL) {
 | |
|         *Index = Count;
 | |
|       }
 | |
| 
 | |
|       return TRUE;
 | |
|     }
 | |
| 
 | |
|     Count++;
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Print Question Value according to it's storage width and display attributes.
 | |
| 
 | |
|   @param  Question               The Question to be printed.
 | |
|   @param  FormattedNumber        Buffer for output string.
 | |
|   @param  BufferSize             The FormattedNumber buffer size in bytes.
 | |
| 
 | |
|   @retval EFI_SUCCESS            Print success.
 | |
|   @retval EFI_BUFFER_TOO_SMALL   Buffer size is not enough for formatted number.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| PrintFormattedNumber (
 | |
|   IN FORM_DISPLAY_ENGINE_STATEMENT  *Question,
 | |
|   IN OUT CHAR16                     *FormattedNumber,
 | |
|   IN UINTN                          BufferSize
 | |
|   )
 | |
| {
 | |
|   INT64            Value;
 | |
|   CHAR16           *Format;
 | |
|   EFI_HII_VALUE    *QuestionValue;
 | |
|   EFI_IFR_NUMERIC  *NumericOp;
 | |
| 
 | |
|   if (BufferSize < (21 * sizeof (CHAR16))) {
 | |
|     return EFI_BUFFER_TOO_SMALL;
 | |
|   }
 | |
| 
 | |
|   QuestionValue = &Question->CurrentValue;
 | |
|   NumericOp     = (EFI_IFR_NUMERIC *)Question->OpCode;
 | |
| 
 | |
|   Value = (INT64)QuestionValue->Value.u64;
 | |
|   switch (NumericOp->Flags & EFI_IFR_DISPLAY) {
 | |
|     case EFI_IFR_DISPLAY_INT_DEC:
 | |
|       switch (QuestionValue->Type) {
 | |
|         case EFI_IFR_NUMERIC_SIZE_1:
 | |
|           Value = (INT64)((INT8)QuestionValue->Value.u8);
 | |
|           break;
 | |
| 
 | |
|         case EFI_IFR_NUMERIC_SIZE_2:
 | |
|           Value = (INT64)((INT16)QuestionValue->Value.u16);
 | |
|           break;
 | |
| 
 | |
|         case EFI_IFR_NUMERIC_SIZE_4:
 | |
|           Value = (INT64)((INT32)QuestionValue->Value.u32);
 | |
|           break;
 | |
| 
 | |
|         case EFI_IFR_NUMERIC_SIZE_8:
 | |
|         default:
 | |
|           break;
 | |
|       }
 | |
| 
 | |
|       if (Value < 0) {
 | |
|         Value  = -Value;
 | |
|         Format = L"-%ld";
 | |
|       } else {
 | |
|         Format = L"%ld";
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_DISPLAY_UINT_DEC:
 | |
|       Format = L"%ld";
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_DISPLAY_UINT_HEX:
 | |
|       Format = L"%lx";
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   UnicodeSPrint (FormattedNumber, BufferSize, Format, Value);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Draw a pop up windows based on the dimension, number of lines and
 | |
|   strings specified.
 | |
| 
 | |
|   @param RequestedWidth  The width of the pop-up.
 | |
|   @param NumberOfLines   The number of lines.
 | |
|   @param Marker          The variable argument list for the list of string to be printed.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| CreateSharedPopUp (
 | |
|   IN  UINTN    RequestedWidth,
 | |
|   IN  UINTN    NumberOfLines,
 | |
|   IN  VA_LIST  Marker
 | |
|   )
 | |
| {
 | |
|   UINTN   Index;
 | |
|   UINTN   Count;
 | |
|   CHAR16  Character;
 | |
|   UINTN   Start;
 | |
|   UINTN   End;
 | |
|   UINTN   Top;
 | |
|   UINTN   Bottom;
 | |
|   CHAR16  *String;
 | |
|   UINTN   DimensionsWidth;
 | |
|   UINTN   DimensionsHeight;
 | |
| 
 | |
|   DimensionsWidth  = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn;
 | |
|   DimensionsHeight = gStatementDimensions.BottomRow - gStatementDimensions.TopRow;
 | |
| 
 | |
|   gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
 | |
| 
 | |
|   if ((RequestedWidth + 2) > DimensionsWidth) {
 | |
|     RequestedWidth = DimensionsWidth - 2;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Subtract the PopUp width from total Columns, allow for one space extra on
 | |
|   // each end plus a border.
 | |
|   //
 | |
|   Start = (DimensionsWidth - RequestedWidth - 2) / 2 + gStatementDimensions.LeftColumn + 1;
 | |
|   End   = Start + RequestedWidth + 1;
 | |
| 
 | |
|   Top    = ((DimensionsHeight - NumberOfLines - 2) / 2) + gStatementDimensions.TopRow - 1;
 | |
|   Bottom = Top + NumberOfLines + 2;
 | |
| 
 | |
|   Character = BOXDRAW_DOWN_RIGHT;
 | |
|   PrintCharAt (Start, Top, Character);
 | |
|   Character = BOXDRAW_HORIZONTAL;
 | |
|   for (Index = Start; Index + 2 < End; Index++) {
 | |
|     PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
 | |
|   }
 | |
| 
 | |
|   Character = BOXDRAW_DOWN_LEFT;
 | |
|   PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
 | |
|   Character = BOXDRAW_VERTICAL;
 | |
| 
 | |
|   Count = 0;
 | |
|   for (Index = Top; Index + 2 < Bottom; Index++, Count++) {
 | |
|     String = VA_ARG (Marker, CHAR16 *);
 | |
| 
 | |
|     //
 | |
|     // This will clear the background of the line - we never know who might have been
 | |
|     // here before us.  This differs from the next clear in that it used the non-reverse
 | |
|     // video for normal printing.
 | |
|     //
 | |
|     if (GetStringWidth (String) / 2 > 1) {
 | |
|       ClearLines (Start, End, Index + 1, Index + 1, GetPopupColor ());
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Passing in a space results in the assumption that this is where typing will occur
 | |
|     //
 | |
|     if (String[0] == L' ') {
 | |
|       ClearLines (Start + 1, End - 1, Index + 1, Index + 1, GetPopupInverseColor ());
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Passing in a NULL results in a blank space
 | |
|     //
 | |
|     if (String[0] == CHAR_NULL) {
 | |
|       ClearLines (Start, End, Index + 1, Index + 1, GetPopupColor ());
 | |
|     }
 | |
| 
 | |
|     PrintStringAt (
 | |
|       ((DimensionsWidth - GetStringWidth (String) / 2) / 2) + gStatementDimensions.LeftColumn + 1,
 | |
|       Index + 1,
 | |
|       String
 | |
|       );
 | |
|     gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
 | |
|     PrintCharAt (Start, Index + 1, Character);
 | |
|     PrintCharAt (End - 1, Index + 1, Character);
 | |
|   }
 | |
| 
 | |
|   Character = BOXDRAW_UP_RIGHT;
 | |
|   PrintCharAt (Start, Bottom - 1, Character);
 | |
|   Character = BOXDRAW_HORIZONTAL;
 | |
|   for (Index = Start; Index + 2 < End; Index++) {
 | |
|     PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
 | |
|   }
 | |
| 
 | |
|   Character = BOXDRAW_UP_LEFT;
 | |
|   PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Draw a pop up windows based on the dimension, number of lines and
 | |
|   strings specified.
 | |
| 
 | |
|   @param RequestedWidth  The width of the pop-up.
 | |
|   @param NumberOfLines   The number of lines.
 | |
|   @param ...             A series of text strings that displayed in the pop-up.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| CreateMultiStringPopUp (
 | |
|   IN  UINTN  RequestedWidth,
 | |
|   IN  UINTN  NumberOfLines,
 | |
|   ...
 | |
|   )
 | |
| {
 | |
|   VA_LIST  Marker;
 | |
| 
 | |
|   VA_START (Marker, NumberOfLines);
 | |
| 
 | |
|   CreateSharedPopUp (RequestedWidth, NumberOfLines, Marker);
 | |
| 
 | |
|   VA_END (Marker);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Process nothing.
 | |
| 
 | |
|   @param Event    The Event need to be process
 | |
|   @param Context  The context of the event.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| EmptyEventProcess (
 | |
|   IN  EFI_EVENT  Event,
 | |
|   IN  VOID       *Context
 | |
|   )
 | |
| {
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Process for the refresh interval statement.
 | |
| 
 | |
|   @param Event    The Event need to be process
 | |
|   @param Context  The context of the event.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| RefreshTimeOutProcess (
 | |
|   IN  EFI_EVENT  Event,
 | |
|   IN  VOID       *Context
 | |
|   )
 | |
| {
 | |
|   WARNING_IF_CONTEXT  *EventInfo;
 | |
|   CHAR16              TimeOutString[MAX_TIME_OUT_LEN];
 | |
| 
 | |
|   EventInfo = (WARNING_IF_CONTEXT *)Context;
 | |
| 
 | |
|   if (*(EventInfo->TimeOut) == 0) {
 | |
|     gBS->CloseEvent (Event);
 | |
| 
 | |
|     gBS->SignalEvent (EventInfo->SyncEvent);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   UnicodeSPrint (TimeOutString, MAX_TIME_OUT_LEN, L"%d", *(EventInfo->TimeOut));
 | |
| 
 | |
|   CreateDialog (NULL, gEmptyString, EventInfo->ErrorInfo, gPressEnter, gEmptyString, TimeOutString, NULL);
 | |
| 
 | |
|   *(EventInfo->TimeOut) -= 1;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Display error message for invalid password.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| PasswordInvalid (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   EFI_INPUT_KEY  Key;
 | |
| 
 | |
|   //
 | |
|   // Invalid password, prompt error message
 | |
|   //
 | |
|   do {
 | |
|     CreateDialog (&Key, gEmptyString, gPassowordInvalid, gPressEnter, gEmptyString, NULL);
 | |
|   } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Process password op code.
 | |
| 
 | |
|   @param  MenuOption             The menu for current password op code.
 | |
| 
 | |
|   @retval EFI_SUCCESS            Question Option process success.
 | |
|   @retval Other                  Question Option process fail.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| PasswordProcess (
 | |
|   IN  UI_MENU_OPTION  *MenuOption
 | |
|   )
 | |
| {
 | |
|   CHAR16                         *StringPtr;
 | |
|   CHAR16                         *TempString;
 | |
|   UINTN                          Maximum;
 | |
|   EFI_STATUS                     Status;
 | |
|   EFI_IFR_PASSWORD               *PasswordInfo;
 | |
|   FORM_DISPLAY_ENGINE_STATEMENT  *Question;
 | |
|   EFI_INPUT_KEY                  Key;
 | |
| 
 | |
|   Question     = MenuOption->ThisTag;
 | |
|   PasswordInfo = (EFI_IFR_PASSWORD *)Question->OpCode;
 | |
|   Maximum      = PasswordInfo->MaxSize;
 | |
|   Status       = EFI_SUCCESS;
 | |
| 
 | |
|   StringPtr = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16));
 | |
|   ASSERT (StringPtr);
 | |
| 
 | |
|   //
 | |
|   // Use a NULL password to test whether old password is required
 | |
|   //
 | |
|   *StringPtr = 0;
 | |
|   Status     = Question->PasswordCheck (gFormData, Question, StringPtr);
 | |
|   if ((Status == EFI_NOT_AVAILABLE_YET) || (Status == EFI_UNSUPPORTED)) {
 | |
|     //
 | |
|     // Password can't be set now.
 | |
|     //
 | |
|     if (Status == EFI_UNSUPPORTED) {
 | |
|       do {
 | |
|         CreateDialog (&Key, gEmptyString, gPasswordUnsupported, gPressEnter, gEmptyString, NULL);
 | |
|       } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
 | |
|     }
 | |
| 
 | |
|     FreePool (StringPtr);
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     //
 | |
|     // Old password exist, ask user for the old password
 | |
|     //
 | |
|     Status = ReadString (MenuOption, gPromptForPassword, StringPtr);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
 | |
|       FreePool (StringPtr);
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Check user input old password
 | |
|     //
 | |
|     Status = Question->PasswordCheck (gFormData, Question, StringPtr);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       if (Status == EFI_NOT_READY) {
 | |
|         //
 | |
|         // Typed in old password incorrect
 | |
|         //
 | |
|         PasswordInvalid ();
 | |
|       } else {
 | |
|         Status = EFI_SUCCESS;
 | |
|       }
 | |
| 
 | |
|       ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
 | |
|       FreePool (StringPtr);
 | |
|       return Status;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Ask for new password
 | |
|   //
 | |
|   ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
 | |
|   Status = ReadString (MenuOption, gPromptForNewPassword, StringPtr);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     //
 | |
|     // Reset state machine for password
 | |
|     //
 | |
|     Question->PasswordCheck (gFormData, Question, NULL);
 | |
|     ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
 | |
|     FreePool (StringPtr);
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Confirm new password
 | |
|   //
 | |
|   TempString = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16));
 | |
|   ASSERT (TempString);
 | |
|   Status = ReadString (MenuOption, gConfirmPassword, TempString);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     //
 | |
|     // Reset state machine for password
 | |
|     //
 | |
|     Question->PasswordCheck (gFormData, Question, NULL);
 | |
|     ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
 | |
|     ZeroMem (TempString, (Maximum + 1) * sizeof (CHAR16));
 | |
|     FreePool (StringPtr);
 | |
|     FreePool (TempString);
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Compare two typed-in new passwords
 | |
|   //
 | |
|   if (StrCmp (StringPtr, TempString) == 0) {
 | |
|     gUserInput->InputValue.Buffer       = AllocateCopyPool (Question->CurrentValue.BufferLen, StringPtr);
 | |
|     gUserInput->InputValue.BufferLen    = Question->CurrentValue.BufferLen;
 | |
|     gUserInput->InputValue.Type         = Question->CurrentValue.Type;
 | |
|     gUserInput->InputValue.Value.string = HiiSetString (gFormData->HiiHandle, gUserInput->InputValue.Value.string, StringPtr, NULL);
 | |
| 
 | |
|     Status = EFI_SUCCESS;
 | |
|   } else {
 | |
|     //
 | |
|     // Reset state machine for password
 | |
|     //
 | |
|     Question->PasswordCheck (gFormData, Question, NULL);
 | |
| 
 | |
|     //
 | |
|     // Two password mismatch, prompt error message
 | |
|     //
 | |
|     do {
 | |
|       CreateDialog (&Key, gEmptyString, gConfirmError, gPressEnter, gEmptyString, NULL);
 | |
|     } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
 | |
| 
 | |
|     Status = EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   ZeroMem (TempString, (Maximum + 1) * sizeof (CHAR16));
 | |
|   ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
 | |
|   FreePool (TempString);
 | |
|   FreePool (StringPtr);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Print some debug message about mismatched menu info.
 | |
| 
 | |
|   @param  MenuOption             The MenuOption for this Question.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| PrintMismatchMenuInfo (
 | |
|   IN  UI_MENU_OPTION  *MenuOption
 | |
|   )
 | |
| {
 | |
|   CHAR16                         *FormTitleStr;
 | |
|   CHAR16                         *FormSetTitleStr;
 | |
|   CHAR16                         *OneOfOptionStr;
 | |
|   CHAR16                         *QuestionName;
 | |
|   LIST_ENTRY                     *Link;
 | |
|   FORM_DISPLAY_ENGINE_STATEMENT  *Question;
 | |
|   EFI_IFR_ORDERED_LIST           *OrderList;
 | |
|   UINT8                          Index;
 | |
|   EFI_HII_VALUE                  HiiValue;
 | |
|   EFI_HII_VALUE                  *QuestionValue;
 | |
|   DISPLAY_QUESTION_OPTION        *Option;
 | |
|   UINT8                          *ValueArray;
 | |
|   UINT8                          ValueType;
 | |
|   EFI_IFR_FORM_SET               *FormsetBuffer;
 | |
|   UINTN                          FormsetBufferSize;
 | |
| 
 | |
|   Question = MenuOption->ThisTag;
 | |
| 
 | |
|   if (!EFI_ERROR (HiiGetFormSetFromHiiHandle (gFormData->HiiHandle, &FormsetBuffer, &FormsetBufferSize))) {
 | |
|     FormSetTitleStr = GetToken (FormsetBuffer->FormSetTitle, gFormData->HiiHandle);
 | |
|     FormTitleStr    = GetToken (gFormData->FormTitle, gFormData->HiiHandle);
 | |
| 
 | |
|     DEBUG ((DEBUG_ERROR, "\n[%a]: Mismatch Formset    : Formset Guid = %g,  FormSet title = %s\n", gEfiCallerBaseName, &gFormData->FormSetGuid, FormSetTitleStr));
 | |
|     DEBUG ((DEBUG_ERROR, "[%a]: Mismatch Form       : FormId = %d,  Form title = %s.\n", gEfiCallerBaseName, gFormData->FormId, FormTitleStr));
 | |
|   }
 | |
| 
 | |
|   if (Question->OpCode->OpCode == EFI_IFR_ORDERED_LIST_OP) {
 | |
|     QuestionName = GetToken (((EFI_IFR_ORDERED_LIST *)MenuOption->ThisTag->OpCode)->Question.Header.Prompt, gFormData->HiiHandle);
 | |
|     Link         = GetFirstNode (&Question->OptionListHead);
 | |
|     Option       = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
 | |
|     ValueType    = Option->OptionOpCode->Type;
 | |
|     DEBUG ((DEBUG_ERROR, "[%a]: Mismatch Error      : OrderedList value in the array doesn't match with option value.\n", gEfiCallerBaseName));
 | |
|     DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OrderedList: Name = %s.\n", gEfiCallerBaseName, QuestionName));
 | |
|     DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OrderedList: OrderedList array value :\n", gEfiCallerBaseName));
 | |
| 
 | |
|     OrderList = (EFI_IFR_ORDERED_LIST *)Question->OpCode;
 | |
|     for (Index = 0; Index < OrderList->MaxContainers; Index++) {
 | |
|       ValueArray         = Question->CurrentValue.Buffer;
 | |
|       HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
 | |
|       DEBUG ((DEBUG_ERROR, "                                       Value[%d] =%ld.\n", Index, HiiValue.Value.u64));
 | |
|     }
 | |
|   } else if (Question->OpCode->OpCode == EFI_IFR_ONE_OF_OP) {
 | |
|     QuestionName  = GetToken (((EFI_IFR_ONE_OF *)MenuOption->ThisTag->OpCode)->Question.Header.Prompt, gFormData->HiiHandle);
 | |
|     QuestionValue = &Question->CurrentValue;
 | |
|     DEBUG ((DEBUG_ERROR, "[%a]: Mismatch Error      : OneOf value doesn't match with option value.\n", gEfiCallerBaseName));
 | |
|     DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OneOf      : Name = %s.\n", gEfiCallerBaseName, QuestionName));
 | |
|     switch (QuestionValue->Type) {
 | |
|       case EFI_IFR_TYPE_NUM_SIZE_64:
 | |
|         DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OneOf      : OneOf value = %ld.\n", gEfiCallerBaseName, QuestionValue->Value.u64));
 | |
|         break;
 | |
| 
 | |
|       case EFI_IFR_TYPE_NUM_SIZE_32:
 | |
|         DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OneOf      : OneOf value = %d.\n", gEfiCallerBaseName, QuestionValue->Value.u32));
 | |
|         break;
 | |
| 
 | |
|       case EFI_IFR_TYPE_NUM_SIZE_16:
 | |
|         DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OneOf      : OneOf value = %d.\n", gEfiCallerBaseName, QuestionValue->Value.u16));
 | |
|         break;
 | |
| 
 | |
|       case EFI_IFR_TYPE_NUM_SIZE_8:
 | |
|         DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OneOf      : OneOf value = %d.\n", gEfiCallerBaseName, QuestionValue->Value.u8));
 | |
|         break;
 | |
| 
 | |
|       default:
 | |
|         ASSERT (FALSE);
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Index = 0;
 | |
|   Link  = GetFirstNode (&Question->OptionListHead);
 | |
|   while (!IsNull (&Question->OptionListHead, Link)) {
 | |
|     Option         = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
 | |
|     OneOfOptionStr = GetToken (Option->OptionOpCode->Option, gFormData->HiiHandle);
 | |
|     switch (Option->OptionOpCode->Type) {
 | |
|       case EFI_IFR_TYPE_NUM_SIZE_64:
 | |
|         DEBUG ((DEBUG_ERROR, "[%a]: Option %d            : Option Value = %ld,  Option Name = %s.\n", gEfiCallerBaseName, Index, Option->OptionOpCode->Value.u64, OneOfOptionStr));
 | |
|         break;
 | |
| 
 | |
|       case EFI_IFR_TYPE_NUM_SIZE_32:
 | |
|         DEBUG ((DEBUG_ERROR, "[%a]: Option %d            : Option Value = %d,  Option Name = %s.\n", gEfiCallerBaseName, Index, Option->OptionOpCode->Value.u32, OneOfOptionStr));
 | |
|         break;
 | |
| 
 | |
|       case EFI_IFR_TYPE_NUM_SIZE_16:
 | |
|         DEBUG ((DEBUG_ERROR, "[%a]: Option %d            : Option Value = %d,  Option Name = %s.\n", gEfiCallerBaseName, Index, Option->OptionOpCode->Value.u16, OneOfOptionStr));
 | |
|         break;
 | |
| 
 | |
|       case EFI_IFR_TYPE_NUM_SIZE_8:
 | |
|         DEBUG ((DEBUG_ERROR, "[%a]: Option %d            : Option Value = %d,  Option Name = %s.\n", gEfiCallerBaseName, Index, Option->OptionOpCode->Value.u8, OneOfOptionStr));
 | |
|         break;
 | |
| 
 | |
|       default:
 | |
|         ASSERT (FALSE);
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     Link = GetNextNode (&Question->OptionListHead, Link);
 | |
|     Index++;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Process a Question's Option (whether selected or un-selected).
 | |
| 
 | |
|   @param  MenuOption             The MenuOption for this Question.
 | |
|   @param  Selected               TRUE: if Question is selected.
 | |
|   @param  OptionString           Pointer of the Option String to be displayed.
 | |
|   @param  SkipErrorValue         Whether need to return when value without option for it.
 | |
| 
 | |
|   @retval EFI_SUCCESS            Question Option process success.
 | |
|   @retval Other                  Question Option process fail.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ProcessOptions (
 | |
|   IN  UI_MENU_OPTION  *MenuOption,
 | |
|   IN  BOOLEAN         Selected,
 | |
|   OUT CHAR16          **OptionString,
 | |
|   IN  BOOLEAN         SkipErrorValue
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                     Status;
 | |
|   CHAR16                         *StringPtr;
 | |
|   UINTN                          Index;
 | |
|   FORM_DISPLAY_ENGINE_STATEMENT  *Question;
 | |
|   CHAR16                         FormattedNumber[21];
 | |
|   UINT16                         Number;
 | |
|   CHAR16                         Character[2];
 | |
|   EFI_INPUT_KEY                  Key;
 | |
|   UINTN                          BufferSize;
 | |
|   DISPLAY_QUESTION_OPTION        *OneOfOption;
 | |
|   LIST_ENTRY                     *Link;
 | |
|   EFI_HII_VALUE                  HiiValue;
 | |
|   EFI_HII_VALUE                  *QuestionValue;
 | |
|   DISPLAY_QUESTION_OPTION        *Option;
 | |
|   UINTN                          Index2;
 | |
|   UINT8                          *ValueArray;
 | |
|   UINT8                          ValueType;
 | |
|   EFI_IFR_ORDERED_LIST           *OrderList;
 | |
|   BOOLEAN                        ValueInvalid;
 | |
|   UINTN                          MaxLen;
 | |
| 
 | |
|   Status = EFI_SUCCESS;
 | |
| 
 | |
|   StringPtr     = NULL;
 | |
|   Character[1]  = L'\0';
 | |
|   *OptionString = NULL;
 | |
|   ValueInvalid  = FALSE;
 | |
| 
 | |
|   ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
 | |
|   BufferSize = (gOptionBlockWidth + 1) * 2 * gStatementDimensions.BottomRow;
 | |
| 
 | |
|   Question      = MenuOption->ThisTag;
 | |
|   QuestionValue = &Question->CurrentValue;
 | |
| 
 | |
|   switch (Question->OpCode->OpCode) {
 | |
|     case EFI_IFR_ORDERED_LIST_OP:
 | |
| 
 | |
|       //
 | |
|       // Check whether there are Options of this OrderedList
 | |
|       //
 | |
|       if (IsListEmpty (&Question->OptionListHead)) {
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       OrderList = (EFI_IFR_ORDERED_LIST *)Question->OpCode;
 | |
| 
 | |
|       Link        = GetFirstNode (&Question->OptionListHead);
 | |
|       OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
 | |
| 
 | |
|       ValueType  =  OneOfOption->OptionOpCode->Type;
 | |
|       ValueArray = Question->CurrentValue.Buffer;
 | |
| 
 | |
|       if (Selected) {
 | |
|         //
 | |
|         // Go ask for input
 | |
|         //
 | |
|         Status = GetSelectionInputPopUp (MenuOption);
 | |
|       } else {
 | |
|         //
 | |
|         // We now know how many strings we will have, so we can allocate the
 | |
|         // space required for the array or strings.
 | |
|         //
 | |
|         MaxLen        = OrderList->MaxContainers * BufferSize / sizeof (CHAR16);
 | |
|         *OptionString = AllocateZeroPool (MaxLen * sizeof (CHAR16));
 | |
|         ASSERT (*OptionString);
 | |
| 
 | |
|         HiiValue.Type      = ValueType;
 | |
|         HiiValue.Value.u64 = 0;
 | |
|         for (Index = 0; Index < OrderList->MaxContainers; Index++) {
 | |
|           HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
 | |
|           if (HiiValue.Value.u64 == 0) {
 | |
|             //
 | |
|             // Values for the options in ordered lists should never be a 0
 | |
|             //
 | |
|             break;
 | |
|           }
 | |
| 
 | |
|           OneOfOption = ValueToOption (Question, &HiiValue);
 | |
|           if (OneOfOption == NULL) {
 | |
|             //
 | |
|             // Print debug msg for the mistach menu.
 | |
|             //
 | |
|             PrintMismatchMenuInfo (MenuOption);
 | |
| 
 | |
|             if (SkipErrorValue) {
 | |
|               //
 | |
|               // Just try to get the option string, skip the value which not has option.
 | |
|               //
 | |
|               continue;
 | |
|             }
 | |
| 
 | |
|             //
 | |
|             // Show error message
 | |
|             //
 | |
|             do {
 | |
|               CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL);
 | |
|             } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
 | |
| 
 | |
|             //
 | |
|             // The initial value of the orderedlist is invalid, force to be valid value
 | |
|             // Exit current DisplayForm with new value.
 | |
|             //
 | |
|             gUserInput->SelectedStatement = Question;
 | |
|             gMisMatch                     = TRUE;
 | |
|             ValueArray                    = AllocateZeroPool (Question->CurrentValue.BufferLen);
 | |
|             ASSERT (ValueArray != NULL);
 | |
|             gUserInput->InputValue.Buffer    = ValueArray;
 | |
|             gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
 | |
|             gUserInput->InputValue.Type      = Question->CurrentValue.Type;
 | |
| 
 | |
|             Link   = GetFirstNode (&Question->OptionListHead);
 | |
|             Index2 = 0;
 | |
|             while (!IsNull (&Question->OptionListHead, Link) && Index2 < OrderList->MaxContainers) {
 | |
|               Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
 | |
|               Link   = GetNextNode (&Question->OptionListHead, Link);
 | |
|               SetArrayData (ValueArray, ValueType, Index2, Option->OptionOpCode->Value.u64);
 | |
|               Index2++;
 | |
|             }
 | |
| 
 | |
|             SetArrayData (ValueArray, ValueType, Index2, 0);
 | |
| 
 | |
|             FreePool (*OptionString);
 | |
|             *OptionString = NULL;
 | |
|             return EFI_NOT_FOUND;
 | |
|           }
 | |
| 
 | |
|           Character[0] = LEFT_ONEOF_DELIMITER;
 | |
|           NewStrCat (OptionString[0], MaxLen, Character);
 | |
|           StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
 | |
|           ASSERT (StringPtr != NULL);
 | |
|           NewStrCat (OptionString[0], MaxLen, StringPtr);
 | |
|           Character[0] = RIGHT_ONEOF_DELIMITER;
 | |
|           NewStrCat (OptionString[0], MaxLen, Character);
 | |
|           Character[0] = CHAR_CARRIAGE_RETURN;
 | |
|           NewStrCat (OptionString[0], MaxLen, Character);
 | |
|           FreePool (StringPtr);
 | |
|         }
 | |
| 
 | |
|         //
 | |
|         // If valid option more than the max container, skip these options.
 | |
|         //
 | |
|         if (Index >= OrderList->MaxContainers) {
 | |
|           break;
 | |
|         }
 | |
| 
 | |
|         //
 | |
|         // Search the other options, try to find the one not in the container.
 | |
|         //
 | |
|         Link = GetFirstNode (&Question->OptionListHead);
 | |
|         while (!IsNull (&Question->OptionListHead, Link)) {
 | |
|           OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
 | |
|           Link        = GetNextNode (&Question->OptionListHead, Link);
 | |
| 
 | |
|           if (FindArrayData (ValueArray, ValueType, OneOfOption->OptionOpCode->Value.u64, NULL)) {
 | |
|             continue;
 | |
|           }
 | |
| 
 | |
|           //
 | |
|           // Print debug msg for the mistach menu.
 | |
|           //
 | |
|           PrintMismatchMenuInfo (MenuOption);
 | |
| 
 | |
|           if (SkipErrorValue) {
 | |
|             //
 | |
|             // Not report error, just get the correct option string info.
 | |
|             //
 | |
|             Character[0] = LEFT_ONEOF_DELIMITER;
 | |
|             NewStrCat (OptionString[0], MaxLen, Character);
 | |
|             StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
 | |
|             ASSERT (StringPtr != NULL);
 | |
|             NewStrCat (OptionString[0], MaxLen, StringPtr);
 | |
|             Character[0] = RIGHT_ONEOF_DELIMITER;
 | |
|             NewStrCat (OptionString[0], MaxLen, Character);
 | |
|             Character[0] = CHAR_CARRIAGE_RETURN;
 | |
|             NewStrCat (OptionString[0], MaxLen, Character);
 | |
|             FreePool (StringPtr);
 | |
| 
 | |
|             continue;
 | |
|           }
 | |
| 
 | |
|           if (!ValueInvalid) {
 | |
|             ValueInvalid = TRUE;
 | |
|             //
 | |
|             // Show error message
 | |
|             //
 | |
|             do {
 | |
|               CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL);
 | |
|             } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
 | |
| 
 | |
|             //
 | |
|             // The initial value of the orderedlist is invalid, force to be valid value
 | |
|             // Exit current DisplayForm with new value.
 | |
|             //
 | |
|             gUserInput->SelectedStatement = Question;
 | |
|             gMisMatch                     = TRUE;
 | |
|             ValueArray                    = AllocateCopyPool (Question->CurrentValue.BufferLen, Question->CurrentValue.Buffer);
 | |
|             ASSERT (ValueArray != NULL);
 | |
|             gUserInput->InputValue.Buffer    = ValueArray;
 | |
|             gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
 | |
|             gUserInput->InputValue.Type      = Question->CurrentValue.Type;
 | |
|           }
 | |
| 
 | |
|           SetArrayData (ValueArray, ValueType, Index++, OneOfOption->OptionOpCode->Value.u64);
 | |
|         }
 | |
| 
 | |
|         if (ValueInvalid) {
 | |
|           FreePool (*OptionString);
 | |
|           *OptionString = NULL;
 | |
|           return EFI_NOT_FOUND;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_ONE_OF_OP:
 | |
|       //
 | |
|       // Check whether there are Options of this OneOf
 | |
|       //
 | |
|       if (IsListEmpty (&Question->OptionListHead)) {
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       if (Selected) {
 | |
|         //
 | |
|         // Go ask for input
 | |
|         //
 | |
|         Status = GetSelectionInputPopUp (MenuOption);
 | |
|       } else {
 | |
|         MaxLen        = BufferSize / sizeof (CHAR16);
 | |
|         *OptionString = AllocateZeroPool (BufferSize);
 | |
|         ASSERT (*OptionString);
 | |
| 
 | |
|         OneOfOption = ValueToOption (Question, QuestionValue);
 | |
|         if (OneOfOption == NULL) {
 | |
|           //
 | |
|           // Print debug msg for the mistach menu.
 | |
|           //
 | |
|           PrintMismatchMenuInfo (MenuOption);
 | |
| 
 | |
|           if (SkipErrorValue) {
 | |
|             //
 | |
|             // Not report error, just get the correct option string info.
 | |
|             //
 | |
|             Link        = GetFirstNode (&Question->OptionListHead);
 | |
|             OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
 | |
|           } else {
 | |
|             //
 | |
|             // Show error message
 | |
|             //
 | |
|             do {
 | |
|               CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL);
 | |
|             } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
 | |
| 
 | |
|             //
 | |
|             // Force the Question value to be valid
 | |
|             // Exit current DisplayForm with new value.
 | |
|             //
 | |
|             Link   = GetFirstNode (&Question->OptionListHead);
 | |
|             Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
 | |
| 
 | |
|             gUserInput->InputValue.Type = Option->OptionOpCode->Type;
 | |
|             switch (gUserInput->InputValue.Type) {
 | |
|               case EFI_IFR_TYPE_NUM_SIZE_8:
 | |
|                 gUserInput->InputValue.Value.u8 = Option->OptionOpCode->Value.u8;
 | |
|                 break;
 | |
|               case EFI_IFR_TYPE_NUM_SIZE_16:
 | |
|                 CopyMem (&gUserInput->InputValue.Value.u16, &Option->OptionOpCode->Value.u16, sizeof (UINT16));
 | |
|                 break;
 | |
|               case EFI_IFR_TYPE_NUM_SIZE_32:
 | |
|                 CopyMem (&gUserInput->InputValue.Value.u32, &Option->OptionOpCode->Value.u32, sizeof (UINT32));
 | |
|                 break;
 | |
|               case EFI_IFR_TYPE_NUM_SIZE_64:
 | |
|                 CopyMem (&gUserInput->InputValue.Value.u64, &Option->OptionOpCode->Value.u64, sizeof (UINT64));
 | |
|                 break;
 | |
|               default:
 | |
|                 ASSERT (FALSE);
 | |
|                 break;
 | |
|             }
 | |
| 
 | |
|             gUserInput->SelectedStatement = Question;
 | |
|             gMisMatch                     = TRUE;
 | |
|             FreePool (*OptionString);
 | |
|             *OptionString = NULL;
 | |
|             return EFI_NOT_FOUND;
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         Character[0] = LEFT_ONEOF_DELIMITER;
 | |
|         NewStrCat (OptionString[0], MaxLen, Character);
 | |
|         StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
 | |
|         ASSERT (StringPtr != NULL);
 | |
|         NewStrCat (OptionString[0], MaxLen, StringPtr);
 | |
|         Character[0] = RIGHT_ONEOF_DELIMITER;
 | |
|         NewStrCat (OptionString[0], MaxLen, Character);
 | |
| 
 | |
|         FreePool (StringPtr);
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_CHECKBOX_OP:
 | |
|       if (Selected) {
 | |
|         //
 | |
|         // Since this is a BOOLEAN operation, flip it upon selection
 | |
|         //
 | |
|         gUserInput->InputValue.Type    = QuestionValue->Type;
 | |
|         gUserInput->InputValue.Value.b = (BOOLEAN)(QuestionValue->Value.b ? FALSE : TRUE);
 | |
| 
 | |
|         //
 | |
|         // Perform inconsistent check
 | |
|         //
 | |
|         return EFI_SUCCESS;
 | |
|       } else {
 | |
|         *OptionString = AllocateZeroPool (BufferSize);
 | |
|         ASSERT (*OptionString);
 | |
| 
 | |
|         *OptionString[0] = LEFT_CHECKBOX_DELIMITER;
 | |
| 
 | |
|         if (QuestionValue->Value.b) {
 | |
|           *(OptionString[0] + 1) = CHECK_ON;
 | |
|         } else {
 | |
|           *(OptionString[0] + 1) = CHECK_OFF;
 | |
|         }
 | |
| 
 | |
|         *(OptionString[0] + 2) = RIGHT_CHECKBOX_DELIMITER;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_NUMERIC_OP:
 | |
|       if (Selected) {
 | |
|         //
 | |
|         // Go ask for input
 | |
|         //
 | |
|         Status = GetNumericInput (MenuOption);
 | |
|       } else {
 | |
|         *OptionString = AllocateZeroPool (BufferSize);
 | |
|         ASSERT (*OptionString);
 | |
| 
 | |
|         *OptionString[0] = LEFT_NUMERIC_DELIMITER;
 | |
| 
 | |
|         //
 | |
|         // Formatted print
 | |
|         //
 | |
|         PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
 | |
|         Number = (UINT16)GetStringWidth (FormattedNumber);
 | |
|         CopyMem (OptionString[0] + 1, FormattedNumber, Number);
 | |
| 
 | |
|         *(OptionString[0] + Number / 2) = RIGHT_NUMERIC_DELIMITER;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_DATE_OP:
 | |
|       if (Selected) {
 | |
|         //
 | |
|         // This is similar to numerics
 | |
|         //
 | |
|         Status = GetNumericInput (MenuOption);
 | |
|       } else {
 | |
|         *OptionString = AllocateZeroPool (BufferSize);
 | |
|         ASSERT (*OptionString);
 | |
| 
 | |
|         switch (MenuOption->Sequence) {
 | |
|           case 0:
 | |
|             *OptionString[0] = LEFT_NUMERIC_DELIMITER;
 | |
|             if (QuestionValue->Value.date.Month == 0xff) {
 | |
|               UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"??");
 | |
|             } else {
 | |
|               UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Month);
 | |
|             }
 | |
| 
 | |
|             *(OptionString[0] + 3) = DATE_SEPARATOR;
 | |
|             break;
 | |
| 
 | |
|           case 1:
 | |
|             SetUnicodeMem (OptionString[0], 4, L' ');
 | |
|             if (QuestionValue->Value.date.Day == 0xff) {
 | |
|               UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"??");
 | |
|             } else {
 | |
|               UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Day);
 | |
|             }
 | |
| 
 | |
|             *(OptionString[0] + 6) = DATE_SEPARATOR;
 | |
|             break;
 | |
| 
 | |
|           case 2:
 | |
|             SetUnicodeMem (OptionString[0], 7, L' ');
 | |
|             if (QuestionValue->Value.date.Year == 0xff) {
 | |
|               UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"????");
 | |
|             } else {
 | |
|               UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%04d", QuestionValue->Value.date.Year);
 | |
|             }
 | |
| 
 | |
|             *(OptionString[0] + 11) = RIGHT_NUMERIC_DELIMITER;
 | |
|             break;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_TIME_OP:
 | |
|       if (Selected) {
 | |
|         //
 | |
|         // This is similar to numerics
 | |
|         //
 | |
|         Status = GetNumericInput (MenuOption);
 | |
|       } else {
 | |
|         *OptionString = AllocateZeroPool (BufferSize);
 | |
|         ASSERT (*OptionString);
 | |
| 
 | |
|         switch (MenuOption->Sequence) {
 | |
|           case 0:
 | |
|             *OptionString[0] = LEFT_NUMERIC_DELIMITER;
 | |
|             if (QuestionValue->Value.time.Hour == 0xff) {
 | |
|               UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"??");
 | |
|             } else {
 | |
|               UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Hour);
 | |
|             }
 | |
| 
 | |
|             *(OptionString[0] + 3) = TIME_SEPARATOR;
 | |
|             break;
 | |
| 
 | |
|           case 1:
 | |
|             SetUnicodeMem (OptionString[0], 4, L' ');
 | |
|             if (QuestionValue->Value.time.Minute == 0xff) {
 | |
|               UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"??");
 | |
|             } else {
 | |
|               UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Minute);
 | |
|             }
 | |
| 
 | |
|             *(OptionString[0] + 6) = TIME_SEPARATOR;
 | |
|             break;
 | |
| 
 | |
|           case 2:
 | |
|             SetUnicodeMem (OptionString[0], 7, L' ');
 | |
|             if (QuestionValue->Value.time.Second == 0xff) {
 | |
|               UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"??");
 | |
|             } else {
 | |
|               UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Second);
 | |
|             }
 | |
| 
 | |
|             *(OptionString[0] + 9) = RIGHT_NUMERIC_DELIMITER;
 | |
|             break;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_STRING_OP:
 | |
|       if (Selected) {
 | |
|         StringPtr = AllocateZeroPool (Question->CurrentValue.BufferLen + sizeof (CHAR16));
 | |
|         ASSERT (StringPtr);
 | |
|         CopyMem (StringPtr, Question->CurrentValue.Buffer, Question->CurrentValue.BufferLen);
 | |
| 
 | |
|         Status = ReadString (MenuOption, gPromptForData, StringPtr);
 | |
|         if (EFI_ERROR (Status)) {
 | |
|           FreePool (StringPtr);
 | |
|           return Status;
 | |
|         }
 | |
| 
 | |
|         gUserInput->InputValue.Buffer       = AllocateCopyPool (Question->CurrentValue.BufferLen, StringPtr);
 | |
|         gUserInput->InputValue.BufferLen    = Question->CurrentValue.BufferLen;
 | |
|         gUserInput->InputValue.Type         = Question->CurrentValue.Type;
 | |
|         gUserInput->InputValue.Value.string = HiiSetString (gFormData->HiiHandle, gUserInput->InputValue.Value.string, StringPtr, NULL);
 | |
|         FreePool (StringPtr);
 | |
|         return EFI_SUCCESS;
 | |
|       } else {
 | |
|         *OptionString = AllocateZeroPool (BufferSize);
 | |
|         ASSERT (*OptionString);
 | |
| 
 | |
|         if (((CHAR16 *)Question->CurrentValue.Buffer)[0] == 0x0000) {
 | |
|           *(OptionString[0]) = '_';
 | |
|         } else {
 | |
|           if (Question->CurrentValue.BufferLen < BufferSize) {
 | |
|             BufferSize = Question->CurrentValue.BufferLen;
 | |
|           }
 | |
| 
 | |
|           CopyMem (OptionString[0], (CHAR16 *)Question->CurrentValue.Buffer, BufferSize);
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case EFI_IFR_PASSWORD_OP:
 | |
|       if (Selected) {
 | |
|         Status = PasswordProcess (MenuOption);
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Process the help string: Split StringPtr to several lines of strings stored in
 | |
|   FormattedString and the glyph width of each line cannot exceed gHelpBlockWidth.
 | |
| 
 | |
|   @param  StringPtr              The entire help string.
 | |
|   @param  FormattedString        The oupput formatted string.
 | |
|   @param  EachLineWidth          The max string length of each line in the formatted string.
 | |
|   @param  RowCount               TRUE: if Question is selected.
 | |
| 
 | |
| **/
 | |
| UINTN
 | |
| ProcessHelpString (
 | |
|   IN  CHAR16  *StringPtr,
 | |
|   OUT CHAR16  **FormattedString,
 | |
|   OUT UINT16  *EachLineWidth,
 | |
|   IN  UINTN   RowCount
 | |
|   )
 | |
| {
 | |
|   UINTN   Index;
 | |
|   CHAR16  *OutputString;
 | |
|   UINTN   TotalRowNum;
 | |
|   UINTN   CheckedNum;
 | |
|   UINT16  GlyphWidth;
 | |
|   UINT16  LineWidth;
 | |
|   UINT16  MaxStringLen;
 | |
|   UINT16  StringLen;
 | |
| 
 | |
|   TotalRowNum  = 0;
 | |
|   CheckedNum   = 0;
 | |
|   GlyphWidth   = 1;
 | |
|   Index        = 0;
 | |
|   MaxStringLen = 0;
 | |
|   StringLen    = 0;
 | |
| 
 | |
|   //
 | |
|   // Set default help string width.
 | |
|   //
 | |
|   LineWidth = (UINT16)(gHelpBlockWidth - 1);
 | |
| 
 | |
|   //
 | |
|   // Get row number of the String.
 | |
|   //
 | |
|   while ((StringLen = GetLineByWidth (StringPtr, LineWidth, &GlyphWidth, &Index, &OutputString)) != 0) {
 | |
|     if (StringLen > MaxStringLen) {
 | |
|       MaxStringLen = StringLen;
 | |
|     }
 | |
| 
 | |
|     TotalRowNum++;
 | |
|     FreePool (OutputString);
 | |
|   }
 | |
| 
 | |
|   *EachLineWidth = MaxStringLen;
 | |
| 
 | |
|   *FormattedString = AllocateZeroPool (TotalRowNum * MaxStringLen * sizeof (CHAR16));
 | |
|   ASSERT (*FormattedString != NULL);
 | |
| 
 | |
|   //
 | |
|   // Generate formatted help string array.
 | |
|   //
 | |
|   GlyphWidth = 1;
 | |
|   Index      = 0;
 | |
|   while ((StringLen = GetLineByWidth (StringPtr, LineWidth, &GlyphWidth, &Index, &OutputString)) != 0) {
 | |
|     CopyMem (*FormattedString + CheckedNum * MaxStringLen, OutputString, StringLen * sizeof (CHAR16));
 | |
|     CheckedNum++;
 | |
|     FreePool (OutputString);
 | |
|   }
 | |
| 
 | |
|   return TotalRowNum;
 | |
| }
 |