]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/SetupBrowserDxe/ProcessOptions.c
Update FormBrowser to conform to UEFI spec Table 197. Callback Behavior for Cross...
[mirror_edk2.git] / MdeModulePkg / Universal / SetupBrowserDxe / ProcessOptions.c
1 /** @file
2 Implementation for handling the User Interface option processing.
3
4
5 Copyright (c) 2004 - 2009, Intel Corporation
6 All rights reserved. This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 #include "Ui.h"
17 #include "Setup.h"
18
19
20 /**
21 Process Question Config.
22
23 @param Selection The UI menu selection.
24 @param Question The Question to be peocessed.
25
26 @retval EFI_SUCCESS Question Config process success.
27 @retval Other Question Config process fail.
28
29 **/
30 EFI_STATUS
31 ProcessQuestionConfig (
32 IN UI_MENU_SELECTION *Selection,
33 IN FORM_BROWSER_STATEMENT *Question
34 )
35 {
36 EFI_STATUS Status;
37 CHAR16 *ConfigResp;
38 CHAR16 *Progress;
39 EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess;
40
41 if (Question->QuestionConfig == 0) {
42 return EFI_SUCCESS;
43 }
44
45 //
46 // Get <ConfigResp>
47 //
48 ConfigResp = GetToken (Question->QuestionConfig, Selection->FormSet->HiiHandle);
49 if (ConfigResp == NULL) {
50 return EFI_NOT_FOUND;
51 }
52
53 //
54 // Send config to Configuration Driver
55 //
56 ConfigAccess = Selection->FormSet->ConfigAccess;
57 if (ConfigAccess == NULL) {
58 return EFI_UNSUPPORTED;
59 }
60 Status = ConfigAccess->RouteConfig (
61 ConfigAccess,
62 ConfigResp,
63 &Progress
64 );
65
66 return Status;
67 }
68
69
70 /**
71 Search an Option of a Question by its value.
72
73 @param Question The Question
74 @param OptionValue Value for Option to be searched.
75
76 @retval Pointer Pointer to the found Option.
77 @retval NULL Option not found.
78
79 **/
80 QUESTION_OPTION *
81 ValueToOption (
82 IN FORM_BROWSER_STATEMENT *Question,
83 IN EFI_HII_VALUE *OptionValue
84 )
85 {
86 LIST_ENTRY *Link;
87 QUESTION_OPTION *Option;
88
89 Link = GetFirstNode (&Question->OptionListHead);
90 while (!IsNull (&Question->OptionListHead, Link)) {
91 Option = QUESTION_OPTION_FROM_LINK (Link);
92
93 if (CompareHiiValue (&Option->Value, OptionValue, NULL) == 0) {
94 return Option;
95 }
96
97 Link = GetNextNode (&Question->OptionListHead, Link);
98 }
99
100 return NULL;
101 }
102
103
104 /**
105 Return data element in an Array by its Index.
106
107 @param Array The data array.
108 @param Type Type of the data in this array.
109 @param Index Zero based index for data in this array.
110
111 @retval Value The data to be returned
112
113 **/
114 UINT64
115 GetArrayData (
116 IN VOID *Array,
117 IN UINT8 Type,
118 IN UINTN Index
119 )
120 {
121 UINT64 Data;
122
123 ASSERT (Array != NULL);
124
125 Data = 0;
126 switch (Type) {
127 case EFI_IFR_TYPE_NUM_SIZE_8:
128 Data = (UINT64) *(((UINT8 *) Array) + Index);
129 break;
130
131 case EFI_IFR_TYPE_NUM_SIZE_16:
132 Data = (UINT64) *(((UINT16 *) Array) + Index);
133 break;
134
135 case EFI_IFR_TYPE_NUM_SIZE_32:
136 Data = (UINT64) *(((UINT32 *) Array) + Index);
137 break;
138
139 case EFI_IFR_TYPE_NUM_SIZE_64:
140 Data = (UINT64) *(((UINT64 *) Array) + Index);
141 break;
142
143 default:
144 break;
145 }
146
147 return Data;
148 }
149
150
151 /**
152 Set value of a data element in an Array by its Index.
153
154 @param Array The data array.
155 @param Type Type of the data in this array.
156 @param Index Zero based index for data in this array.
157 @param Value The value to be set.
158
159 **/
160 VOID
161 SetArrayData (
162 IN VOID *Array,
163 IN UINT8 Type,
164 IN UINTN Index,
165 IN UINT64 Value
166 )
167 {
168
169 ASSERT (Array != NULL);
170
171 switch (Type) {
172 case EFI_IFR_TYPE_NUM_SIZE_8:
173 *(((UINT8 *) Array) + Index) = (UINT8) Value;
174 break;
175
176 case EFI_IFR_TYPE_NUM_SIZE_16:
177 *(((UINT16 *) Array) + Index) = (UINT16) Value;
178 break;
179
180 case EFI_IFR_TYPE_NUM_SIZE_32:
181 *(((UINT32 *) Array) + Index) = (UINT32) Value;
182 break;
183
184 case EFI_IFR_TYPE_NUM_SIZE_64:
185 *(((UINT64 *) Array) + Index) = (UINT64) Value;
186 break;
187
188 default:
189 break;
190 }
191 }
192
193
194 /**
195 Print Question Value according to it's storage width and display attributes.
196
197 @param Question The Question to be printed.
198 @param FormattedNumber Buffer for output string.
199 @param BufferSize The FormattedNumber buffer size in bytes.
200
201 @retval EFI_SUCCESS Print success.
202 @retval EFI_BUFFER_TOO_SMALL Buffer size is not enough for formatted number.
203
204 **/
205 EFI_STATUS
206 PrintFormattedNumber (
207 IN FORM_BROWSER_STATEMENT *Question,
208 IN OUT CHAR16 *FormattedNumber,
209 IN UINTN BufferSize
210 )
211 {
212 INT64 Value;
213 CHAR16 *Format;
214 EFI_HII_VALUE *QuestionValue;
215
216 if (BufferSize < (21 * sizeof (CHAR16))) {
217 return EFI_BUFFER_TOO_SMALL;
218 }
219
220 QuestionValue = &Question->HiiValue;
221
222 Value = (INT64) QuestionValue->Value.u64;
223 switch (Question->Flags & EFI_IFR_DISPLAY) {
224 case EFI_IFR_DISPLAY_INT_DEC:
225 switch (QuestionValue->Type) {
226 case EFI_IFR_NUMERIC_SIZE_1:
227 Value = (INT64) ((INT8) QuestionValue->Value.u8);
228 break;
229
230 case EFI_IFR_NUMERIC_SIZE_2:
231 Value = (INT64) ((INT16) QuestionValue->Value.u16);
232 break;
233
234 case EFI_IFR_NUMERIC_SIZE_4:
235 Value = (INT64) ((INT32) QuestionValue->Value.u32);
236 break;
237
238 case EFI_IFR_NUMERIC_SIZE_8:
239 default:
240 break;
241 }
242
243 if (Value < 0) {
244 Value = -Value;
245 Format = L"-%ld";
246 } else {
247 Format = L"%ld";
248 }
249 break;
250
251 case EFI_IFR_DISPLAY_UINT_DEC:
252 Format = L"%ld";
253 break;
254
255 case EFI_IFR_DISPLAY_UINT_HEX:
256 Format = L"%lx";
257 break;
258
259 default:
260 return EFI_UNSUPPORTED;
261 break;
262 }
263
264 UnicodeSPrint (FormattedNumber, BufferSize, Format, Value);
265
266 return EFI_SUCCESS;
267 }
268
269
270 /**
271 Password may be stored as encrypted by Configuration Driver. When change a
272 password, user will be challenged with old password. To validate user input old
273 password, we will send the clear text to Configuration Driver via Callback().
274 Configuration driver is responsible to check the passed in password and return
275 the validation result. If validation pass, state machine in password Callback()
276 will transit from BROWSER_STATE_VALIDATE_PASSWORD to BROWSER_STATE_SET_PASSWORD.
277 After user type in new password twice, Callback() will be invoked to send the
278 new password to Configuration Driver.
279
280 @param Selection Pointer to UI_MENU_SELECTION.
281 @param MenuOption The MenuOption for this password Question.
282 @param String The clear text of password.
283
284 @retval EFI_NOT_AVAILABLE_YET Callback() request to terminate password input.
285 @return In state of BROWSER_STATE_VALIDATE_PASSWORD:
286 @retval EFI_SUCCESS Password correct, Browser will prompt for new
287 password.
288 @retval EFI_NOT_READY Password incorrect, Browser will show error
289 message.
290 @retval Other Browser will do nothing.
291 @return In state of BROWSER_STATE_SET_PASSWORD:
292 @retval EFI_SUCCESS Set password success.
293 @retval Other Set password failed.
294
295 **/
296 EFI_STATUS
297 PasswordCallback (
298 IN UI_MENU_SELECTION *Selection,
299 IN UI_MENU_OPTION *MenuOption,
300 IN CHAR16 *String
301 )
302 {
303 EFI_STATUS Status;
304 EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess;
305 EFI_BROWSER_ACTION_REQUEST ActionRequest;
306 EFI_HII_VALUE *QuestionValue;
307
308 QuestionValue = &MenuOption->ThisTag->HiiValue;
309 ConfigAccess = Selection->FormSet->ConfigAccess;
310 if (ConfigAccess == NULL) {
311 return EFI_UNSUPPORTED;
312 }
313
314 //
315 // Prepare password string in HII database
316 //
317 if (String != NULL) {
318 QuestionValue->Value.string = NewString (String, Selection->FormSet->HiiHandle);
319 } else {
320 QuestionValue->Value.string = 0;
321 }
322
323 //
324 // Send password to Configuration Driver for validation
325 //
326 Status = ConfigAccess->Callback (
327 ConfigAccess,
328 EFI_BROWSER_ACTION_CHANGING,
329 MenuOption->ThisTag->QuestionId,
330 QuestionValue->Type,
331 &QuestionValue->Value,
332 &ActionRequest
333 );
334
335 //
336 // Remove password string from HII database
337 //
338 if (String != NULL) {
339 DeleteString (QuestionValue->Value.string, Selection->FormSet->HiiHandle);
340 }
341
342 return Status;
343 }
344
345
346 /**
347 Display error message for invalid password.
348
349 **/
350 VOID
351 PasswordInvalid (
352 VOID
353 )
354 {
355 EFI_INPUT_KEY Key;
356
357 //
358 // Invalid password, prompt error message
359 //
360 do {
361 CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gPassowordInvalid, gPressEnter, gEmptyString);
362 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
363 }
364
365
366 /**
367 Process a Question's Option (whether selected or un-selected).
368
369 @param Selection Pointer to UI_MENU_SELECTION.
370 @param MenuOption The MenuOption for this Question.
371 @param Selected TRUE: if Question is selected.
372 @param OptionString Pointer of the Option String to be displayed.
373
374 @retval EFI_SUCCESS Question Option process success.
375 @retval Other Question Option process fail.
376
377 **/
378 EFI_STATUS
379 ProcessOptions (
380 IN UI_MENU_SELECTION *Selection,
381 IN UI_MENU_OPTION *MenuOption,
382 IN BOOLEAN Selected,
383 OUT CHAR16 **OptionString
384 )
385 {
386 EFI_STATUS Status;
387 CHAR16 *StringPtr;
388 CHAR16 *TempString;
389 UINTN Index;
390 FORM_BROWSER_STATEMENT *Question;
391 CHAR16 FormattedNumber[21];
392 UINT16 Number;
393 CHAR16 Character[2];
394 EFI_INPUT_KEY Key;
395 UINTN BufferSize;
396 QUESTION_OPTION *OneOfOption;
397 LIST_ENTRY *Link;
398 EFI_HII_VALUE HiiValue;
399 EFI_HII_VALUE *QuestionValue;
400 BOOLEAN Suppress;
401 UINT16 Maximum;
402 QUESTION_OPTION *Option;
403 UINTN Index2;
404 UINT8 *ValueArray;
405 UINT8 ValueType;
406
407 Status = EFI_SUCCESS;
408
409 StringPtr = NULL;
410 Character[1] = L'\0';
411 *OptionString = NULL;
412
413 ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
414 BufferSize = (gOptionBlockWidth + 1) * 2 * gScreenDimensions.BottomRow;
415
416 Question = MenuOption->ThisTag;
417 QuestionValue = &Question->HiiValue;
418 Maximum = (UINT16) Question->Maximum;
419
420 ValueArray = Question->BufferValue;
421 ValueType = Question->ValueType;
422
423 switch (Question->Operand) {
424 case EFI_IFR_ORDERED_LIST_OP:
425 //
426 // Initialize Option value array
427 //
428 if (GetArrayData (ValueArray, ValueType, 0) == 0) {
429 GetQuestionDefault (Selection->FormSet, Selection->Form, Question, 0);
430 }
431
432 if (Selected) {
433 //
434 // Go ask for input
435 //
436 Status = GetSelectionInputPopUp (Selection, MenuOption);
437 } else {
438 //
439 // We now know how many strings we will have, so we can allocate the
440 // space required for the array or strings.
441 //
442 *OptionString = AllocateZeroPool (Question->MaxContainers * BufferSize);
443 ASSERT (*OptionString);
444
445 HiiValue.Type = ValueType;
446 HiiValue.Value.u64 = 0;
447 for (Index = 0; Index < Question->MaxContainers; Index++) {
448 HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
449 if (HiiValue.Value.u64 == 0) {
450 //
451 // Values for the options in ordered lists should never be a 0
452 //
453 break;
454 }
455
456 OneOfOption = ValueToOption (Question, &HiiValue);
457 if (OneOfOption == NULL) {
458 //
459 // Show error message
460 //
461 do {
462 CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString);
463 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
464
465 //
466 // The initial value of the orderedlist is invalid, force to be valid value
467 //
468 Link = GetFirstNode (&Question->OptionListHead);
469 Index2 = 0;
470 while (!IsNull (&Question->OptionListHead, Link) && Index2 < Question->MaxContainers) {
471 Option = QUESTION_OPTION_FROM_LINK (Link);
472 SetArrayData (ValueArray, ValueType, Index2, Option->Value.Value.u64);
473 Index2++;
474 Link = GetNextNode (&Question->OptionListHead, Link);
475 }
476 SetArrayData (ValueArray, ValueType, Index2, 0);
477
478 Status = SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
479 UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
480
481 FreePool (*OptionString);
482 *OptionString = NULL;
483 return EFI_NOT_FOUND;
484 }
485
486 Suppress = FALSE;
487 if ((OneOfOption->SuppressExpression != NULL) &&
488 (OneOfOption->SuppressExpression->Result.Value.b)) {
489 //
490 // This option is suppressed
491 //
492 Suppress = TRUE;
493 }
494
495 if (!Suppress) {
496 Character[0] = LEFT_ONEOF_DELIMITER;
497 NewStrCat (OptionString[0], Character);
498 StringPtr = GetToken (OneOfOption->Text, Selection->Handle);
499 NewStrCat (OptionString[0], StringPtr);
500 Character[0] = RIGHT_ONEOF_DELIMITER;
501 NewStrCat (OptionString[0], Character);
502 Character[0] = CHAR_CARRIAGE_RETURN;
503 NewStrCat (OptionString[0], Character);
504
505 FreePool (StringPtr);
506 }
507 }
508 }
509 break;
510
511 case EFI_IFR_ONE_OF_OP:
512 if (Selected) {
513 //
514 // Go ask for input
515 //
516 Status = GetSelectionInputPopUp (Selection, MenuOption);
517 } else {
518 *OptionString = AllocateZeroPool (BufferSize);
519 ASSERT (*OptionString);
520
521 OneOfOption = ValueToOption (Question, QuestionValue);
522 if (OneOfOption == NULL) {
523 //
524 // Show error message
525 //
526 do {
527 CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString);
528 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
529
530 //
531 // Force the Question value to be valid
532 //
533 Link = GetFirstNode (&Question->OptionListHead);
534 while (!IsNull (&Question->OptionListHead, Link)) {
535 Option = QUESTION_OPTION_FROM_LINK (Link);
536
537 if ((Option->SuppressExpression == NULL) ||
538 !Option->SuppressExpression->Result.Value.b) {
539 CopyMem (QuestionValue, &Option->Value, sizeof (EFI_HII_VALUE));
540 SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
541 UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
542 break;
543 }
544
545 Link = GetNextNode (&Question->OptionListHead, Link);
546 }
547
548 FreePool (*OptionString);
549 *OptionString = NULL;
550 return EFI_NOT_FOUND;
551 }
552
553 if ((OneOfOption->SuppressExpression != NULL) &&
554 (OneOfOption->SuppressExpression->Result.Value.b)) {
555 //
556 // This option is suppressed
557 //
558 Suppress = TRUE;
559 } else {
560 Suppress = FALSE;
561 }
562
563 if (Suppress) {
564 //
565 // Current selected option happen to be suppressed,
566 // enforce to select on a non-suppressed option
567 //
568 Link = GetFirstNode (&Question->OptionListHead);
569 while (!IsNull (&Question->OptionListHead, Link)) {
570 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
571
572 if ((OneOfOption->SuppressExpression == NULL) ||
573 !OneOfOption->SuppressExpression->Result.Value.b) {
574 Suppress = FALSE;
575 CopyMem (QuestionValue, &OneOfOption->Value, sizeof (EFI_HII_VALUE));
576 SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
577 UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
578 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
579 break;
580 }
581
582 Link = GetNextNode (&Question->OptionListHead, Link);
583 }
584 }
585
586 if (!Suppress) {
587 Character[0] = LEFT_ONEOF_DELIMITER;
588 NewStrCat (OptionString[0], Character);
589 StringPtr = GetToken (OneOfOption->Text, Selection->Handle);
590 NewStrCat (OptionString[0], StringPtr);
591 Character[0] = RIGHT_ONEOF_DELIMITER;
592 NewStrCat (OptionString[0], Character);
593
594 FreePool (StringPtr);
595 }
596 }
597 break;
598
599 case EFI_IFR_CHECKBOX_OP:
600 *OptionString = AllocateZeroPool (BufferSize);
601 ASSERT (*OptionString);
602
603 *OptionString[0] = LEFT_CHECKBOX_DELIMITER;
604
605 if (Selected) {
606 //
607 // Since this is a BOOLEAN operation, flip it upon selection
608 //
609 QuestionValue->Value.b = (BOOLEAN) (QuestionValue->Value.b ? FALSE : TRUE);
610
611 //
612 // Perform inconsistent check
613 //
614 Status = ValidateQuestion (Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);
615 if (EFI_ERROR (Status)) {
616 //
617 // Inconsistent check fail, restore Question Value
618 //
619 QuestionValue->Value.b = (BOOLEAN) (QuestionValue->Value.b ? FALSE : TRUE);
620 FreePool (*OptionString);
621 *OptionString = NULL;
622 return Status;
623 }
624
625 //
626 // Save Question value
627 //
628 Status = SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
629 UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
630 }
631
632 if (QuestionValue->Value.b) {
633 *(OptionString[0] + 1) = CHECK_ON;
634 } else {
635 *(OptionString[0] + 1) = CHECK_OFF;
636 }
637 *(OptionString[0] + 2) = RIGHT_CHECKBOX_DELIMITER;
638 break;
639
640 case EFI_IFR_NUMERIC_OP:
641 if (Selected) {
642 //
643 // Go ask for input
644 //
645 Status = GetNumericInput (Selection, MenuOption);
646 } else {
647 *OptionString = AllocateZeroPool (BufferSize);
648 ASSERT (*OptionString);
649
650 *OptionString[0] = LEFT_NUMERIC_DELIMITER;
651
652 //
653 // Formatted print
654 //
655 PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
656 Number = (UINT16) GetStringWidth (FormattedNumber);
657 CopyMem (OptionString[0] + 1, FormattedNumber, Number);
658
659 *(OptionString[0] + Number / 2) = RIGHT_NUMERIC_DELIMITER;
660 }
661 break;
662
663 case EFI_IFR_DATE_OP:
664 if (Selected) {
665 //
666 // This is similar to numerics
667 //
668 Status = GetNumericInput (Selection, MenuOption);
669 } else {
670 *OptionString = AllocateZeroPool (BufferSize);
671 ASSERT (*OptionString);
672
673 switch (MenuOption->Sequence) {
674 case 0:
675 *OptionString[0] = LEFT_NUMERIC_DELIMITER;
676 UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Month);
677 *(OptionString[0] + 3) = DATE_SEPARATOR;
678 break;
679
680 case 1:
681 SetUnicodeMem (OptionString[0], 4, L' ');
682 UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Day);
683 *(OptionString[0] + 6) = DATE_SEPARATOR;
684 break;
685
686 case 2:
687 SetUnicodeMem (OptionString[0], 7, L' ');
688 UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%4d", QuestionValue->Value.date.Year);
689 *(OptionString[0] + 11) = RIGHT_NUMERIC_DELIMITER;
690 break;
691 }
692 }
693 break;
694
695 case EFI_IFR_TIME_OP:
696 if (Selected) {
697 //
698 // This is similar to numerics
699 //
700 Status = GetNumericInput (Selection, MenuOption);
701 } else {
702 *OptionString = AllocateZeroPool (BufferSize);
703 ASSERT (*OptionString);
704
705 switch (MenuOption->Sequence) {
706 case 0:
707 *OptionString[0] = LEFT_NUMERIC_DELIMITER;
708 UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Hour);
709 *(OptionString[0] + 3) = TIME_SEPARATOR;
710 break;
711
712 case 1:
713 SetUnicodeMem (OptionString[0], 4, L' ');
714 UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Minute);
715 *(OptionString[0] + 6) = TIME_SEPARATOR;
716 break;
717
718 case 2:
719 SetUnicodeMem (OptionString[0], 7, L' ');
720 UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Second);
721 *(OptionString[0] + 9) = RIGHT_NUMERIC_DELIMITER;
722 break;
723 }
724 }
725 break;
726
727 case EFI_IFR_STRING_OP:
728 if (Selected) {
729 StringPtr = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16));
730 ASSERT (StringPtr);
731
732 Status = ReadString (MenuOption, gPromptForData, StringPtr);
733 if (!EFI_ERROR (Status)) {
734 CopyMem (Question->BufferValue, StringPtr, Maximum * sizeof (CHAR16));
735 SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
736
737 UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
738 }
739
740 FreePool (StringPtr);
741 } else {
742 *OptionString = AllocateZeroPool (BufferSize);
743 ASSERT (*OptionString);
744
745 if (((CHAR16 *) Question->BufferValue)[0] == 0x0000) {
746 *(OptionString[0]) = '_';
747 } else {
748 if ((Maximum * sizeof (CHAR16)) < BufferSize) {
749 BufferSize = Maximum * sizeof (CHAR16);
750 }
751 CopyMem (OptionString[0], (CHAR16 *) Question->BufferValue, BufferSize);
752 }
753 }
754 break;
755
756 case EFI_IFR_PASSWORD_OP:
757 if (Selected) {
758 StringPtr = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16));
759 ASSERT (StringPtr);
760
761 //
762 // For interactive passwords, old password is validated by callback
763 //
764 if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
765 //
766 // Use a NULL password to test whether old password is required
767 //
768 *StringPtr = 0;
769 Status = PasswordCallback (Selection, MenuOption, StringPtr);
770 if (Status == EFI_NOT_AVAILABLE_YET) {
771 //
772 // Callback request to terminate password input
773 //
774 FreePool (StringPtr);
775 return EFI_SUCCESS;
776 }
777
778 if (EFI_ERROR (Status)) {
779 //
780 // Old password exist, ask user for the old password
781 //
782 Status = ReadString (MenuOption, gPromptForPassword, StringPtr);
783 if (EFI_ERROR (Status)) {
784 FreePool (StringPtr);
785 return Status;
786 }
787
788 //
789 // Check user input old password
790 //
791 Status = PasswordCallback (Selection, MenuOption, StringPtr);
792 if (EFI_ERROR (Status)) {
793 if (Status == EFI_NOT_READY) {
794 //
795 // Typed in old password incorrect
796 //
797 PasswordInvalid ();
798 } else {
799 Status = EFI_SUCCESS;
800 }
801
802 FreePool (StringPtr);
803 return Status;
804 }
805 }
806 } else {
807 //
808 // For non-interactive password, validate old password in local
809 //
810 if (*((CHAR16 *) Question->BufferValue) != 0) {
811 //
812 // There is something there! Prompt for password
813 //
814 Status = ReadString (MenuOption, gPromptForPassword, StringPtr);
815 if (EFI_ERROR (Status)) {
816 FreePool (StringPtr);
817 return Status;
818 }
819
820 TempString = AllocateCopyPool ((Maximum + 1) * sizeof (CHAR16), Question->BufferValue);
821 ASSERT (TempString != NULL);
822
823 TempString[Maximum] = L'\0';
824
825 if (StrCmp (StringPtr, TempString) != 0) {
826 //
827 // Typed in old password incorrect
828 //
829 PasswordInvalid ();
830
831 FreePool (StringPtr);
832 FreePool (TempString);
833 return Status;
834 }
835
836 FreePool (TempString);
837 }
838 }
839
840 //
841 // Ask for new password
842 //
843 ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
844 Status = ReadString (MenuOption, gPromptForNewPassword, StringPtr);
845 if (EFI_ERROR (Status)) {
846 //
847 // Reset state machine for interactive password
848 //
849 if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
850 PasswordCallback (Selection, MenuOption, NULL);
851 }
852
853 FreePool (StringPtr);
854 return Status;
855 }
856
857 //
858 // Confirm new password
859 //
860 TempString = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16));
861 ASSERT (TempString);
862 Status = ReadString (MenuOption, gConfirmPassword, TempString);
863 if (EFI_ERROR (Status)) {
864 //
865 // Reset state machine for interactive password
866 //
867 if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
868 PasswordCallback (Selection, MenuOption, NULL);
869 }
870
871 FreePool (StringPtr);
872 FreePool (TempString);
873 return Status;
874 }
875
876 //
877 // Compare two typed-in new passwords
878 //
879 if (StrCmp (StringPtr, TempString) == 0) {
880 //
881 // Two password match, send it to Configuration Driver
882 //
883 if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
884 PasswordCallback (Selection, MenuOption, StringPtr);
885 } else {
886 CopyMem (Question->BufferValue, StringPtr, Maximum * sizeof (CHAR16));
887 SetQuestionValue (Selection->FormSet, Selection->Form, Question, FALSE);
888 }
889 } else {
890 //
891 // Reset state machine for interactive password
892 //
893 if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
894 PasswordCallback (Selection, MenuOption, NULL);
895 }
896
897 //
898 // Two password mismatch, prompt error message
899 //
900 do {
901 CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gConfirmError, gPressEnter, gEmptyString);
902 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
903 }
904
905 FreePool (TempString);
906 FreePool (StringPtr);
907 }
908 break;
909
910 default:
911 break;
912 }
913
914 return Status;
915 }
916
917
918 /**
919 Process the help string: Split StringPtr to several lines of strings stored in
920 FormattedString and the glyph width of each line cannot exceed gHelpBlockWidth.
921
922 @param StringPtr The entire help string.
923 @param FormattedString The oupput formatted string.
924 @param RowCount TRUE: if Question is selected.
925
926 **/
927 VOID
928 ProcessHelpString (
929 IN CHAR16 *StringPtr,
930 OUT CHAR16 **FormattedString,
931 IN UINTN RowCount
932 )
933 {
934 UINTN BlockWidth;
935 UINTN AllocateSize;
936 //
937 // [PrevCurrIndex, CurrIndex) forms a range of a screen-line
938 //
939 UINTN CurrIndex;
940 UINTN PrevCurrIndex;
941 UINTN LineCount;
942 UINTN VirtualLineCount;
943 //
944 // GlyphOffset stores glyph width of current screen-line
945 //
946 UINTN GlyphOffset;
947 //
948 // GlyphWidth equals to 2 if we meet width directive
949 //
950 UINTN GlyphWidth;
951 //
952 // during scanning, we remember the position of last space character
953 // in case that if next word cannot put in current line, we could restore back to the position
954 // of last space character
955 // while we should also remmeber the glyph width of the last space character for restoring
956 //
957 UINTN LastSpaceIndex;
958 UINTN LastSpaceGlyphWidth;
959 //
960 // every time we begin to form a new screen-line, we should remember glyph width of single character
961 // of last line
962 //
963 UINTN LineStartGlyphWidth;
964 UINTN *IndexArray;
965 UINTN *OldIndexArray;
966
967 BlockWidth = (UINTN) gHelpBlockWidth - 1;
968
969 //
970 // every three elements of IndexArray form a screen-line of string:[ IndexArray[i*3], IndexArray[i*3+1] )
971 // IndexArray[i*3+2] stores the initial glyph width of single character. to save this is because we want
972 // to bring the width directive of the last line to current screen-line.
973 // e.g.: "\wideabcde ... fghi", if "fghi" also has width directive but is splitted to the next screen-line
974 // different from that of "\wideabcde", we should remember the width directive.
975 //
976 AllocateSize = 0x20;
977 IndexArray = AllocatePool (AllocateSize * sizeof (UINTN) * 3);
978 ASSERT (IndexArray != NULL);
979
980 if (*FormattedString != NULL) {
981 FreePool (*FormattedString);
982 *FormattedString = NULL;
983 }
984
985 for (PrevCurrIndex = 0, CurrIndex = 0, LineCount = 0, LastSpaceIndex = 0,
986 IndexArray[0] = 0, GlyphWidth = 1, GlyphOffset = 0, LastSpaceGlyphWidth = 1, LineStartGlyphWidth = 1;
987 (StringPtr[CurrIndex] != CHAR_NULL);
988 CurrIndex ++) {
989
990 if (LineCount == AllocateSize) {
991 AllocateSize += 0x10;
992 OldIndexArray = IndexArray;
993 IndexArray = AllocatePool (AllocateSize * sizeof (UINTN) * 3);
994 ASSERT (IndexArray != NULL);
995
996 CopyMem (IndexArray, OldIndexArray, LineCount * sizeof (UINTN) * 3);
997 FreePool (OldIndexArray);
998 }
999 switch (StringPtr[CurrIndex]) {
1000
1001 case NARROW_CHAR:
1002 case WIDE_CHAR:
1003 GlyphWidth = ((StringPtr[CurrIndex] == WIDE_CHAR) ? 2 : 1);
1004 if (CurrIndex == 0) {
1005 LineStartGlyphWidth = GlyphWidth;
1006 }
1007 break;
1008
1009 //
1010 // char is '\n'
1011 // "\r\n" isn't handled here, handled by case CHAR_CARRIAGE_RETURN
1012 //
1013 case CHAR_LINEFEED:
1014 //
1015 // Store a range of string as a line
1016 //
1017 IndexArray[LineCount*3] = PrevCurrIndex;
1018 IndexArray[LineCount*3+1] = CurrIndex;
1019 IndexArray[LineCount*3+2] = LineStartGlyphWidth;
1020 LineCount ++;
1021 //
1022 // Reset offset and save begin position of line
1023 //
1024 GlyphOffset = 0;
1025 LineStartGlyphWidth = GlyphWidth;
1026 PrevCurrIndex = CurrIndex + 1;
1027 break;
1028
1029 //
1030 // char is '\r'
1031 // "\r\n" and "\r" both are handled here
1032 //
1033 case CHAR_CARRIAGE_RETURN:
1034 if (StringPtr[CurrIndex + 1] == CHAR_LINEFEED) {
1035 //
1036 // next char is '\n'
1037 //
1038 IndexArray[LineCount*3] = PrevCurrIndex;
1039 IndexArray[LineCount*3+1] = CurrIndex;
1040 IndexArray[LineCount*3+2] = LineStartGlyphWidth;
1041 LineCount ++;
1042 CurrIndex ++;
1043 }
1044 GlyphOffset = 0;
1045 LineStartGlyphWidth = GlyphWidth;
1046 PrevCurrIndex = CurrIndex + 1;
1047 break;
1048
1049 //
1050 // char is space or other char
1051 //
1052 default:
1053 GlyphOffset += GlyphWidth;
1054 if (GlyphOffset >= BlockWidth) {
1055 if (LastSpaceIndex > PrevCurrIndex) {
1056 //
1057 // LastSpaceIndex points to space inside current screen-line,
1058 // restore to LastSpaceIndex
1059 // (Otherwise the word is too long to fit one screen-line, just cut it)
1060 //
1061 CurrIndex = LastSpaceIndex;
1062 GlyphWidth = LastSpaceGlyphWidth;
1063 } else if (GlyphOffset > BlockWidth) {
1064 //
1065 // the word is too long to fit one screen-line and we don't get the chance
1066 // of GlyphOffset == BlockWidth because GlyphWidth = 2
1067 //
1068 CurrIndex --;
1069 }
1070
1071 IndexArray[LineCount*3] = PrevCurrIndex;
1072 IndexArray[LineCount*3+1] = CurrIndex + 1;
1073 IndexArray[LineCount*3+2] = LineStartGlyphWidth;
1074 LineStartGlyphWidth = GlyphWidth;
1075 LineCount ++;
1076 //
1077 // Reset offset and save begin position of line
1078 //
1079 GlyphOffset = 0;
1080 PrevCurrIndex = CurrIndex + 1;
1081 }
1082
1083 //
1084 // LastSpaceIndex: remember position of last space
1085 //
1086 if (StringPtr[CurrIndex] == CHAR_SPACE) {
1087 LastSpaceIndex = CurrIndex;
1088 LastSpaceGlyphWidth = GlyphWidth;
1089 }
1090 break;
1091 }
1092 }
1093
1094 if (GlyphOffset > 0) {
1095 IndexArray[LineCount*3] = PrevCurrIndex;
1096 IndexArray[LineCount*3+1] = CurrIndex;
1097 IndexArray[LineCount*3+2] = GlyphWidth;
1098 LineCount ++;
1099 }
1100
1101 if (LineCount == 0) {
1102 //
1103 // in case we meet null string
1104 //
1105 IndexArray[0] = 0;
1106 IndexArray[1] = 1;
1107 //
1108 // we assume null string's glyph width is 1
1109 //
1110 IndexArray[1] = 1;
1111 LineCount ++;
1112 }
1113
1114 VirtualLineCount = RowCount * (LineCount / RowCount + (LineCount % RowCount > 0));
1115 *FormattedString = AllocateZeroPool (VirtualLineCount * (BlockWidth + 1) * sizeof (CHAR16) * 2);
1116 ASSERT (*FormattedString != NULL);
1117
1118 for (CurrIndex = 0; CurrIndex < LineCount; CurrIndex ++) {
1119 *(*FormattedString + CurrIndex * 2 * (BlockWidth + 1)) = (CHAR16) ((IndexArray[CurrIndex*3+2] == 2) ? WIDE_CHAR : NARROW_CHAR);
1120 StrnCpy (
1121 *FormattedString + CurrIndex * 2 * (BlockWidth + 1) + 1,
1122 StringPtr + IndexArray[CurrIndex*3],
1123 IndexArray[CurrIndex*3+1]-IndexArray[CurrIndex*3]
1124 );
1125 }
1126
1127 FreePool (IndexArray);
1128 }