git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@4599 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			987 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			987 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| 
 | |
| Copyright (c) 2004 - 2007, 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 "Ui.h"
 | |
| #include "Setup.h"
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Process Question Config.
 | |
| 
 | |
|   @param  Selection              The UI menu selection.
 | |
|   @param  Question               The Question to be peocessed.
 | |
| 
 | |
|   @retval EFI_SUCCESS            Question Config process success.
 | |
|   @retval Other                  Question Config process fail.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ProcessQuestionConfig (
 | |
|   IN  UI_MENU_SELECTION       *Selection,
 | |
|   IN  FORM_BROWSER_STATEMENT  *Question
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                      Status;
 | |
|   CHAR16                          *ConfigResp;
 | |
|   CHAR16                          *Progress;
 | |
|   EFI_HII_CONFIG_ACCESS_PROTOCOL  *ConfigAccess;
 | |
| 
 | |
|   if (Question->QuestionConfig == 0) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Get <ConfigResp>
 | |
|   //
 | |
|   ConfigResp = GetToken (Question->QuestionConfig, Selection->FormSet->HiiHandle);
 | |
|   if (ConfigResp == NULL) {
 | |
|     return EFI_NOT_FOUND;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Send config to Configuration Driver
 | |
|   //
 | |
|   ConfigAccess = Selection->FormSet->ConfigAccess;
 | |
|   if (ConfigAccess == NULL) {
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
|   Status = ConfigAccess->RouteConfig (
 | |
|                            ConfigAccess,
 | |
|                            ConfigResp,
 | |
|                            &Progress
 | |
|                            );
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   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.
 | |
| 
 | |
| **/
 | |
| QUESTION_OPTION *
 | |
| ValueToOption (
 | |
|   IN FORM_BROWSER_STATEMENT   *Question,
 | |
|   IN EFI_HII_VALUE            *OptionValue
 | |
|   )
 | |
| {
 | |
|   LIST_ENTRY       *Link;
 | |
|   QUESTION_OPTION  *Option;
 | |
| 
 | |
|   Link = GetFirstNode (&Question->OptionListHead);
 | |
|   while (!IsNull (&Question->OptionListHead, Link)) {
 | |
|     Option = QUESTION_OPTION_FROM_LINK (Link);
 | |
| 
 | |
|     if (CompareHiiValue (&Option->Value, OptionValue, NULL) == 0) {
 | |
|       return Option;
 | |
|     }
 | |
| 
 | |
|     Link = GetNextNode (&Question->OptionListHead, Link);
 | |
|   }
 | |
| 
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Print Question Value according to it's storage width and display attributes.
 | |
| 
 | |
|   @param  Event                  The event to wait for
 | |
|   @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_BROWSER_STATEMENT   *Question,
 | |
|   IN OUT CHAR16               *FormattedNumber,
 | |
|   IN UINTN                    BufferSize
 | |
|   )
 | |
| {
 | |
|   INT64          Value;
 | |
|   CHAR16         *Format;
 | |
|   EFI_HII_VALUE  *QuestionValue;
 | |
| 
 | |
|   if (BufferSize < (21 * sizeof (CHAR16))) {
 | |
|     return EFI_BUFFER_TOO_SMALL;
 | |
|   }
 | |
| 
 | |
|   QuestionValue = &Question->HiiValue;
 | |
| 
 | |
|   Value = (INT64) QuestionValue->Value.u64;
 | |
|   switch (Question->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;
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   UnicodeSPrint (FormattedNumber, BufferSize, Format, Value);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Password may be stored as encrypted by Configuration Driver. When change a
 | |
|   password, user will be challenged with old password. To validate user input old
 | |
|   password, we will send the clear text to Configuration Driver via Callback().
 | |
|   Configuration driver is responsible to check the passed in password and return
 | |
|   the validation result. If validation pass, state machine in password Callback()
 | |
|   will transit from BROWSER_STATE_VALIDATE_PASSWORD to BROWSER_STATE_SET_PASSWORD.
 | |
|   After user type in new password twice, Callback() will be invoked to send the
 | |
|   new password to Configuration Driver.
 | |
| 
 | |
|   @param  Selection              Pointer to UI_MENU_SELECTION.
 | |
|   @param  MenuOption             The MenuOption for this password Question.
 | |
|   @param  String                 The clear text of password.
 | |
| 
 | |
|   @retval EFI_NOT_AVAILABLE_YET  Callback() request to terminate password input.
 | |
|   @return In state of BROWSER_STATE_VALIDATE_PASSWORD:
 | |
|   @retval EFI_SUCCESS            Password correct, Browser will prompt for new
 | |
|                                  password.
 | |
|   @retval EFI_NOT_READY          Password incorrect, Browser will show error
 | |
|                                  message.
 | |
|   @retval Other                  Browser will do nothing.
 | |
|   @return In state of BROWSER_STATE_SET_PASSWORD:
 | |
|   @retval EFI_SUCCESS            Set password success.
 | |
|   @retval Other                  Set password failed.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| PasswordCallback (
 | |
|   IN  UI_MENU_SELECTION           *Selection,
 | |
|   IN  UI_MENU_OPTION              *MenuOption,
 | |
|   IN  CHAR16                      *String
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                      Status;
 | |
|   EFI_HII_CONFIG_ACCESS_PROTOCOL  *ConfigAccess;
 | |
|   EFI_BROWSER_ACTION_REQUEST      ActionRequest;
 | |
|   EFI_HII_VALUE                   *QuestionValue;
 | |
| 
 | |
|   QuestionValue = &MenuOption->ThisTag->HiiValue;
 | |
|   ConfigAccess = Selection->FormSet->ConfigAccess;
 | |
|   if (ConfigAccess == NULL) {
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Prepare password string in HII database
 | |
|   //
 | |
|   if (String != NULL) {
 | |
|     QuestionValue->Value.string = NewString (String, Selection->FormSet->HiiHandle);
 | |
|   } else {
 | |
|     QuestionValue->Value.string = 0;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Send password to Configuration Driver for validation
 | |
|   //
 | |
|   Status = ConfigAccess->Callback (
 | |
|                            ConfigAccess,
 | |
|                            EFI_BROWSER_ACTION_CHANGING,
 | |
|                            MenuOption->ThisTag->QuestionId,
 | |
|                            QuestionValue->Type,
 | |
|                            &QuestionValue->Value,
 | |
|                            &ActionRequest
 | |
|                            );
 | |
| 
 | |
|   //
 | |
|   // Remove password string from HII database
 | |
|   //
 | |
|   if (String != NULL) {
 | |
|     DeleteString (QuestionValue->Value.string, Selection->FormSet->HiiHandle);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Display error message for invalid password.
 | |
| 
 | |
|   None.
 | |
| 
 | |
|   @return None.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| PasswordInvalid (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   EFI_INPUT_KEY  Key;
 | |
| 
 | |
|   //
 | |
|   // Invalid password, prompt error message
 | |
|   //
 | |
|   do {
 | |
|     CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gPassowordInvalid, gPressEnter, gEmptyString);
 | |
|   } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Process a Question's Option (whether selected or un-selected).
 | |
| 
 | |
|   @param  Selection              Pointer to UI_MENU_SELECTION.
 | |
|   @param  MenuOption             The MenuOption for this Question.
 | |
|   @param  Selected               TRUE: if Question is selected.
 | |
|   @param  OptionString           Pointer of the Option String to be displayed.
 | |
| 
 | |
|   @retval EFI_SUCCESS            Question Option process success.
 | |
|   @retval Other                  Question Option process fail.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ProcessOptions (
 | |
|   IN  UI_MENU_SELECTION           *Selection,
 | |
|   IN  UI_MENU_OPTION              *MenuOption,
 | |
|   IN  BOOLEAN                     Selected,
 | |
|   OUT CHAR16                      **OptionString
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                      Status;
 | |
|   CHAR16                          *StringPtr;
 | |
|   CHAR16                          *TempString;
 | |
|   UINTN                           Index;
 | |
|   FORM_BROWSER_STATEMENT          *Question;
 | |
|   CHAR16                          FormattedNumber[21];
 | |
|   UINT16                          Number;
 | |
|   CHAR16                          Character[2];
 | |
|   EFI_INPUT_KEY                   Key;
 | |
|   UINTN                           BufferSize;
 | |
|   QUESTION_OPTION                 *OneOfOption;
 | |
|   LIST_ENTRY                      *Link;
 | |
|   EFI_HII_VALUE                   HiiValue;
 | |
|   EFI_HII_VALUE                   *QuestionValue;
 | |
|   BOOLEAN                         Suppress;
 | |
|   UINT16                          Maximum;
 | |
| 
 | |
|   Status        = EFI_SUCCESS;
 | |
| 
 | |
|   StringPtr     = NULL;
 | |
|   Character[1]  = L'\0';
 | |
|   *OptionString = NULL;
 | |
| 
 | |
|   ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
 | |
|   BufferSize = (gOptionBlockWidth + 1) * 2 * gScreenDimensions.BottomRow;
 | |
| 
 | |
|   Question = MenuOption->ThisTag;
 | |
|   QuestionValue = &Question->HiiValue;
 | |
|   Maximum = (UINT16) Question->Maximum;
 | |
| 
 | |
|   switch (Question->Operand) {
 | |
|   case EFI_IFR_ORDERED_LIST_OP:
 | |
|     //
 | |
|     // Initialize Option value array
 | |
|     //
 | |
|     if (Question->BufferValue[0] == 0) {
 | |
|       GetQuestionDefault (Selection->FormSet, Selection->Form, Question, 0);
 | |
|     }
 | |
| 
 | |
|     if (Selected) {
 | |
|       //
 | |
|       // Go ask for input
 | |
|       //
 | |
|       Status = GetSelectionInputPopUp (Selection, MenuOption);
 | |
|     } else {
 | |
|       //
 | |
|       // We now know how many strings we will have, so we can allocate the
 | |
|       // space required for the array or strings.
 | |
|       //
 | |
|       *OptionString = AllocateZeroPool (Question->MaxContainers * BufferSize);
 | |
|       ASSERT (*OptionString);
 | |
| 
 | |
|       HiiValue.Type = EFI_IFR_TYPE_NUM_SIZE_8;
 | |
|       HiiValue.Value.u64 = 0;
 | |
|       for (Index = 0; Index < Question->MaxContainers; Index++) {
 | |
|         HiiValue.Value.u8 = Question->BufferValue[Index];
 | |
|         if (HiiValue.Value.u8 == 0) {
 | |
|           //
 | |
|           // Values for the options in ordered lists should never be a 0
 | |
|           //
 | |
|           break;
 | |
|         }
 | |
| 
 | |
|         OneOfOption = ValueToOption (Question, &HiiValue);
 | |
|         if (OneOfOption == NULL) {
 | |
|           gBS->FreePool (*OptionString);
 | |
|           return EFI_NOT_FOUND;
 | |
|         }
 | |
| 
 | |
|         Suppress = FALSE;
 | |
|         if ((OneOfOption->SuppressExpression != NULL) &&
 | |
|             (OneOfOption->SuppressExpression->Result.Value.b)) {
 | |
|           //
 | |
|           // This option is suppressed
 | |
|           //
 | |
|           Suppress = TRUE;
 | |
|         }
 | |
| 
 | |
|         if (!Suppress) {
 | |
|           Character[0] = LEFT_ONEOF_DELIMITER;
 | |
|           NewStrCat (OptionString[0], Character);
 | |
|           StringPtr = GetToken (OneOfOption->Text, Selection->Handle);
 | |
|           NewStrCat (OptionString[0], StringPtr);
 | |
|           Character[0] = RIGHT_ONEOF_DELIMITER;
 | |
|           NewStrCat (OptionString[0], Character);
 | |
|           Character[0] = CHAR_CARRIAGE_RETURN;
 | |
|           NewStrCat (OptionString[0], Character);
 | |
| 
 | |
|           gBS->FreePool (StringPtr);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|   case EFI_IFR_ONE_OF_OP:
 | |
|     if (Selected) {
 | |
|       //
 | |
|       // Go ask for input
 | |
|       //
 | |
|       Status = GetSelectionInputPopUp (Selection, MenuOption);
 | |
|     } else {
 | |
|       *OptionString = AllocateZeroPool (BufferSize);
 | |
|       ASSERT (*OptionString);
 | |
| 
 | |
|       OneOfOption = ValueToOption (Question, QuestionValue);
 | |
|       if (OneOfOption == NULL) {
 | |
|         gBS->FreePool (*OptionString);
 | |
|         return EFI_NOT_FOUND;
 | |
|       }
 | |
| 
 | |
|       if ((OneOfOption->SuppressExpression != NULL) &&
 | |
|           (OneOfOption->SuppressExpression->Result.Value.b)) {
 | |
|         //
 | |
|         // This option is suppressed
 | |
|         //
 | |
|         Suppress = TRUE;
 | |
|       } else {
 | |
|         Suppress = FALSE;
 | |
|       }
 | |
| 
 | |
|       if (Suppress) {
 | |
|         //
 | |
|         // Current selected option happen to be suppressed,
 | |
|         // enforce to select on a non-suppressed option
 | |
|         //
 | |
|         Link = GetFirstNode (&Question->OptionListHead);
 | |
|         while (!IsNull (&Question->OptionListHead, Link)) {
 | |
|           OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
 | |
| 
 | |
|           if ((OneOfOption->SuppressExpression == NULL) ||
 | |
|               !OneOfOption->SuppressExpression->Result.Value.b) {
 | |
|             Suppress = FALSE;
 | |
|             CopyMem (QuestionValue, &OneOfOption->Value, sizeof (EFI_HII_VALUE));
 | |
|             SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
 | |
|             break;
 | |
|           }
 | |
| 
 | |
|           Link = GetNextNode (&Question->OptionListHead, Link);
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (!Suppress) {
 | |
|         Character[0] = LEFT_ONEOF_DELIMITER;
 | |
|         NewStrCat (OptionString[0], Character);
 | |
|         StringPtr = GetToken (OneOfOption->Text, Selection->Handle);
 | |
|         NewStrCat (OptionString[0], StringPtr);
 | |
|         Character[0] = RIGHT_ONEOF_DELIMITER;
 | |
|         NewStrCat (OptionString[0], Character);
 | |
| 
 | |
|         gBS->FreePool (StringPtr);
 | |
|       }
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|   case EFI_IFR_CHECKBOX_OP:
 | |
|     *OptionString = AllocateZeroPool (BufferSize);
 | |
|     ASSERT (*OptionString);
 | |
| 
 | |
|     *OptionString[0] = LEFT_CHECKBOX_DELIMITER;
 | |
| 
 | |
|     if (Selected) {
 | |
|       //
 | |
|       // Since this is a BOOLEAN operation, flip it upon selection
 | |
|       //
 | |
|       QuestionValue->Value.b = (BOOLEAN) (QuestionValue->Value.b ? FALSE : TRUE);
 | |
| 
 | |
|       //
 | |
|       // Perform inconsistent check
 | |
|       //
 | |
|       Status = ValidateQuestion (Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         //
 | |
|         // Inconsistent check fail, restore Question Value
 | |
|         //
 | |
|         QuestionValue->Value.b = (BOOLEAN) (QuestionValue->Value.b ? FALSE : TRUE);
 | |
|         gBS->FreePool (*OptionString);
 | |
|         return Status;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Save Question value
 | |
|       //
 | |
|       Status = SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
 | |
|       UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
 | |
|     }
 | |
| 
 | |
|     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 (Selection, 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 (Selection, MenuOption);
 | |
|     } else {
 | |
|       *OptionString = AllocateZeroPool (BufferSize);
 | |
|       ASSERT (*OptionString);
 | |
| 
 | |
|       switch (MenuOption->Sequence) {
 | |
|       case 0:
 | |
|         *OptionString[0] = LEFT_NUMERIC_DELIMITER;
 | |
|         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' ');
 | |
|         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' ');
 | |
|         UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%4d", 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 (Selection, MenuOption);
 | |
|     } else {
 | |
|       *OptionString = AllocateZeroPool (BufferSize);
 | |
|       ASSERT (*OptionString);
 | |
| 
 | |
|       switch (MenuOption->Sequence) {
 | |
|       case 0:
 | |
|         *OptionString[0] = LEFT_NUMERIC_DELIMITER;
 | |
|         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' ');
 | |
|         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' ');
 | |
|         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 ((Maximum + 1) * sizeof (CHAR16));
 | |
|       ASSERT (StringPtr);
 | |
| 
 | |
|       Status = ReadString (MenuOption, gPromptForData, StringPtr);
 | |
|       if (!EFI_ERROR (Status)) {
 | |
|         CopyMem (Question->BufferValue, StringPtr, Maximum * sizeof (CHAR16));
 | |
|         SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
 | |
| 
 | |
|         UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
 | |
|       }
 | |
| 
 | |
|       gBS->FreePool (StringPtr);
 | |
|     } else {
 | |
|       *OptionString = AllocateZeroPool (BufferSize);
 | |
|       ASSERT (*OptionString);
 | |
| 
 | |
|       if (((CHAR16 *) Question->BufferValue)[0] == 0x0000) {
 | |
|         *(OptionString[0]) = '_';
 | |
|       } else {
 | |
|         if ((Maximum * sizeof (CHAR16)) < BufferSize) {
 | |
|           BufferSize = Maximum * sizeof (CHAR16);
 | |
|         }
 | |
|         CopyMem (OptionString[0], (CHAR16 *) Question->BufferValue, BufferSize);
 | |
|       }
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|   case EFI_IFR_PASSWORD_OP:
 | |
|     if (Selected) {
 | |
|       StringPtr = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16));
 | |
|       ASSERT (StringPtr);
 | |
| 
 | |
|       //
 | |
|       // For interactive passwords, old password is validated by callback
 | |
|       //
 | |
|       if (Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) {
 | |
|         //
 | |
|         // Use a NULL password to test whether old password is required
 | |
|         //
 | |
|         *StringPtr = 0;
 | |
|         Status = PasswordCallback (Selection, MenuOption, StringPtr);
 | |
|         if (Status == EFI_NOT_AVAILABLE_YET) {
 | |
|           //
 | |
|           // Callback request to terminate password input
 | |
|           //
 | |
|           gBS->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)) {
 | |
|             gBS->FreePool (StringPtr);
 | |
|             return Status;
 | |
|           }
 | |
| 
 | |
|           //
 | |
|           // Check user input old password
 | |
|           //
 | |
|           Status = PasswordCallback (Selection, MenuOption, StringPtr);
 | |
|           if (EFI_ERROR (Status)) {
 | |
|             if (Status == EFI_NOT_READY) {
 | |
|               //
 | |
|               // Typed in old password incorrect
 | |
|               //
 | |
|               PasswordInvalid ();
 | |
|             } else {
 | |
|               Status = EFI_SUCCESS;
 | |
|             }
 | |
| 
 | |
|             gBS->FreePool (StringPtr);
 | |
|             return Status;
 | |
|           }
 | |
|         }
 | |
|       } else {
 | |
|         //
 | |
|         // For non-interactive password, validate old password in local
 | |
|         //
 | |
|         if (*((CHAR16 *) Question->BufferValue) != 0) {
 | |
|           //
 | |
|           // There is something there!  Prompt for password
 | |
|           //
 | |
|           Status = ReadString (MenuOption, gPromptForPassword, StringPtr);
 | |
|           if (EFI_ERROR (Status)) {
 | |
|             gBS->FreePool (StringPtr);
 | |
|             return Status;
 | |
|           }
 | |
| 
 | |
|           TempString = AllocateCopyPool ((Maximum + 1) * sizeof (CHAR16), Question->BufferValue);
 | |
|           TempString[Maximum] = L'\0';
 | |
| 
 | |
|           if (StrCmp (StringPtr, TempString) != 0) {
 | |
|             //
 | |
|             // Typed in old password incorrect
 | |
|             //
 | |
|             PasswordInvalid ();
 | |
| 
 | |
|             gBS->FreePool (StringPtr);
 | |
|             gBS->FreePool (TempString);
 | |
|             return Status;
 | |
|           }
 | |
| 
 | |
|           gBS->FreePool (TempString);
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Ask for new password
 | |
|       //
 | |
|       ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
 | |
|       Status = ReadString (MenuOption, gPromptForNewPassword, StringPtr);
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         //
 | |
|         // Reset state machine for interactive password
 | |
|         //
 | |
|         if (Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) {
 | |
|           PasswordCallback (Selection, MenuOption, NULL);
 | |
|         }
 | |
| 
 | |
|         gBS->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 interactive password
 | |
|         //
 | |
|         if (Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) {
 | |
|           PasswordCallback (Selection, MenuOption, NULL);
 | |
|         }
 | |
| 
 | |
|         gBS->FreePool (StringPtr);
 | |
|         gBS->FreePool (TempString);
 | |
|         return Status;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Compare two typed-in new passwords
 | |
|       //
 | |
|       if (StrCmp (StringPtr, TempString) == 0) {
 | |
|         //
 | |
|         // Two password match, send it to Configuration Driver
 | |
|         //
 | |
|         if (Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) {
 | |
|           PasswordCallback (Selection, MenuOption, StringPtr);
 | |
|         } else {
 | |
|           CopyMem (Question->BufferValue, StringPtr, Maximum * sizeof (CHAR16));
 | |
|           SetQuestionValue (Selection->FormSet, Selection->Form, Question, FALSE);
 | |
|         }
 | |
|       } else {
 | |
|         //
 | |
|         // Reset state machine for interactive password
 | |
|         //
 | |
|         if (Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) {
 | |
|           PasswordCallback (Selection, MenuOption, NULL);
 | |
|         }
 | |
| 
 | |
|         //
 | |
|         // Two password mismatch, prompt error message
 | |
|         //
 | |
|         do {
 | |
|           CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gConfirmError, gPressEnter, gEmptyString);
 | |
|         } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
 | |
|       }
 | |
| 
 | |
|       gBS->FreePool (TempString);
 | |
|       gBS->FreePool (StringPtr);
 | |
|     }
 | |
|     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  MenuOption             The MenuOption for this Question.
 | |
|   @param  RowCount               TRUE: if Question is selected.
 | |
|   @param  OptionString           Pointer of the Option String to be displayed.
 | |
| 
 | |
|   @return None.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| ProcessHelpString (
 | |
|   IN  CHAR16  *StringPtr,
 | |
|   OUT CHAR16  **FormattedString,
 | |
|   IN  UINTN   RowCount
 | |
|   )
 | |
| {
 | |
|   CONST UINTN BlockWidth = (UINTN) gHelpBlockWidth - 1;
 | |
|   UINTN AllocateSize;
 | |
|   //
 | |
|   // [PrevCurrIndex, CurrIndex) forms a range of a screen-line
 | |
|   //
 | |
|   UINTN CurrIndex;
 | |
|   UINTN PrevCurrIndex;
 | |
|   UINTN LineCount;
 | |
|   UINTN VirtualLineCount;
 | |
|   //
 | |
|   // GlyphOffset stores glyph width of current screen-line
 | |
|   //
 | |
|   UINTN GlyphOffset;
 | |
|   //
 | |
|   // GlyphWidth equals to 2 if we meet width directive
 | |
|   //
 | |
|   UINTN GlyphWidth;
 | |
|   //
 | |
|   // during scanning, we remember the position of last space character
 | |
|   // in case that if next word cannot put in current line, we could restore back to the position
 | |
|   // of last space character
 | |
|   // while we should also remmeber the glyph width of the last space character for restoring
 | |
|   //
 | |
|   UINTN LastSpaceIndex;
 | |
|   UINTN LastSpaceGlyphWidth;
 | |
|   //
 | |
|   // every time we begin to form a new screen-line, we should remember glyph width of single character
 | |
|   // of last line
 | |
|   //
 | |
|   UINTN LineStartGlyphWidth;
 | |
|   UINTN *IndexArray;
 | |
|   UINTN *OldIndexArray;
 | |
| 
 | |
|   //
 | |
|   // every three elements of IndexArray form a screen-line of string:[ IndexArray[i*3], IndexArray[i*3+1] )
 | |
|   // IndexArray[i*3+2] stores the initial glyph width of single character. to save this is because we want
 | |
|   // to bring the width directive of the last line to current screen-line.
 | |
|   // e.g.: "\wideabcde ... fghi", if "fghi" also has width directive but is splitted to the next screen-line
 | |
|   // different from that of "\wideabcde", we should remember the width directive.
 | |
|   //
 | |
|   AllocateSize  = 0x20;
 | |
|   IndexArray    = AllocatePool (AllocateSize * sizeof (UINTN) * 3);
 | |
| 
 | |
|   if (*FormattedString != NULL) {
 | |
|     gBS->FreePool (*FormattedString);
 | |
|     *FormattedString = NULL;
 | |
|   }
 | |
| 
 | |
|   for (PrevCurrIndex = 0, CurrIndex  = 0, LineCount   = 0, LastSpaceIndex = 0,
 | |
|        IndexArray[0] = 0, GlyphWidth = 1, GlyphOffset = 0, LastSpaceGlyphWidth = 1, LineStartGlyphWidth = 1;
 | |
|        (StringPtr[CurrIndex] != CHAR_NULL);
 | |
|        CurrIndex ++) {
 | |
| 
 | |
|     if (LineCount == AllocateSize) {
 | |
|       AllocateSize += 0x10;
 | |
|       OldIndexArray  = IndexArray;
 | |
|       IndexArray = AllocatePool (AllocateSize * sizeof (UINTN) * 3);
 | |
|       CopyMem (IndexArray, OldIndexArray, LineCount * sizeof (UINTN) * 3);
 | |
|       gBS->FreePool (OldIndexArray);
 | |
|     }
 | |
|     switch (StringPtr[CurrIndex]) {
 | |
| 
 | |
|       case NARROW_CHAR:
 | |
|       case WIDE_CHAR:
 | |
|         GlyphWidth = ((StringPtr[CurrIndex] == WIDE_CHAR) ? 2 : 1);
 | |
|         if (CurrIndex == 0) {
 | |
|           LineStartGlyphWidth = GlyphWidth;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|       //
 | |
|       // char is '\n'
 | |
|       // "\r\n" isn't handled here, handled by case CHAR_CARRIAGE_RETURN
 | |
|       //
 | |
|       case CHAR_LINEFEED:
 | |
|         //
 | |
|         // Store a range of string as a line
 | |
|         //
 | |
|         IndexArray[LineCount*3]   = PrevCurrIndex;
 | |
|         IndexArray[LineCount*3+1] = CurrIndex;
 | |
|         IndexArray[LineCount*3+2] = LineStartGlyphWidth;
 | |
|         LineCount ++;
 | |
|         //
 | |
|         // Reset offset and save begin position of line
 | |
|         //
 | |
|         GlyphOffset = 0;
 | |
|         LineStartGlyphWidth = GlyphWidth;
 | |
|         PrevCurrIndex = CurrIndex + 1;
 | |
|         break;
 | |
| 
 | |
|       //
 | |
|       // char is '\r'
 | |
|       // "\r\n" and "\r" both are handled here
 | |
|       //
 | |
|       case CHAR_CARRIAGE_RETURN:
 | |
|         if (StringPtr[CurrIndex + 1] == CHAR_LINEFEED) {
 | |
|           //
 | |
|           // next char is '\n'
 | |
|           //
 | |
|           IndexArray[LineCount*3]   = PrevCurrIndex;
 | |
|           IndexArray[LineCount*3+1] = CurrIndex;
 | |
|           IndexArray[LineCount*3+2] = LineStartGlyphWidth;
 | |
|           LineCount ++;
 | |
|           CurrIndex ++;
 | |
|         }
 | |
|         GlyphOffset = 0;
 | |
|         LineStartGlyphWidth = GlyphWidth;
 | |
|         PrevCurrIndex = CurrIndex + 1;
 | |
|         break;
 | |
| 
 | |
|       //
 | |
|       // char is space or other char
 | |
|       //
 | |
|       default:
 | |
|         GlyphOffset     += GlyphWidth;
 | |
|         if (GlyphOffset >= BlockWidth) {
 | |
|           if (LastSpaceIndex > PrevCurrIndex) {
 | |
|             //
 | |
|             // LastSpaceIndex points to space inside current screen-line,
 | |
|             // restore to LastSpaceIndex
 | |
|             // (Otherwise the word is too long to fit one screen-line, just cut it)
 | |
|             //
 | |
|             CurrIndex  = LastSpaceIndex;
 | |
|             GlyphWidth = LastSpaceGlyphWidth;
 | |
|           } else if (GlyphOffset > BlockWidth) {
 | |
|             //
 | |
|             // the word is too long to fit one screen-line and we don't get the chance
 | |
|             // of GlyphOffset == BlockWidth because GlyphWidth = 2
 | |
|             //
 | |
|             CurrIndex --;
 | |
|           }
 | |
| 
 | |
|           IndexArray[LineCount*3]   = PrevCurrIndex;
 | |
|           IndexArray[LineCount*3+1] = CurrIndex + 1;
 | |
|           IndexArray[LineCount*3+2] = LineStartGlyphWidth;
 | |
|           LineStartGlyphWidth = GlyphWidth;
 | |
|           LineCount ++;
 | |
|           //
 | |
|           // Reset offset and save begin position of line
 | |
|           //
 | |
|           GlyphOffset                 = 0;
 | |
|           PrevCurrIndex               = CurrIndex + 1;
 | |
|         }
 | |
| 
 | |
|         //
 | |
|         // LastSpaceIndex: remember position of last space
 | |
|         //
 | |
|         if (StringPtr[CurrIndex] == CHAR_SPACE) {
 | |
|           LastSpaceIndex      = CurrIndex;
 | |
|           LastSpaceGlyphWidth = GlyphWidth;
 | |
|         }
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (GlyphOffset > 0) {
 | |
|     IndexArray[LineCount*3]   = PrevCurrIndex;
 | |
|     IndexArray[LineCount*3+1] = CurrIndex;
 | |
|     IndexArray[LineCount*3+2] = GlyphWidth;
 | |
|     LineCount ++;
 | |
|   }
 | |
| 
 | |
|   if (LineCount == 0) {
 | |
|     //
 | |
|     // in case we meet null string
 | |
|     //
 | |
|     IndexArray[0] = 0;
 | |
|     IndexArray[1] = 1;
 | |
|     //
 | |
|     // we assume null string's glyph width is 1
 | |
|     //
 | |
|     IndexArray[1] = 1;
 | |
|     LineCount ++;
 | |
|   }
 | |
| 
 | |
|   VirtualLineCount = RowCount * (LineCount / RowCount + (LineCount % RowCount > 0));
 | |
|   *FormattedString = AllocateZeroPool (VirtualLineCount * (BlockWidth + 1) * sizeof (CHAR16) * 2);
 | |
| 
 | |
|   for (CurrIndex = 0; CurrIndex < LineCount; CurrIndex ++) {
 | |
|     *(*FormattedString + CurrIndex * 2 * (BlockWidth + 1)) = (CHAR16) ((IndexArray[CurrIndex*3+2] == 2) ? WIDE_CHAR : NARROW_CHAR);
 | |
|     StrnCpy (
 | |
|       *FormattedString + CurrIndex * 2 * (BlockWidth + 1) + 1,
 | |
|       StringPtr + IndexArray[CurrIndex*3],
 | |
|       IndexArray[CurrIndex*3+1]-IndexArray[CurrIndex*3]
 | |
|       );
 | |
|   }
 | |
| 
 | |
|   gBS->FreePool (IndexArray);
 | |
| }
 |