]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/DisplayEngineDxe/ProcessOptions.c
Update question validation logic, move the check pointer from after user input to...
[mirror_edk2.git] / MdeModulePkg / Universal / DisplayEngineDxe / ProcessOptions.c
1 /** @file
2 Implementation for handling the User Interface option processing.
3
4
5 Copyright (c) 2004 - 2012, Intel Corporation. All rights reserved.<BR>
6 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 "FormDisplay.h"
17
18 #define MAX_TIME_OUT_LEN 0x10
19
20 /**
21 Concatenate a narrow string to another string.
22
23 @param Destination The destination string.
24 @param Source The source string. The string to be concatenated.
25 to the end of Destination.
26
27 **/
28 VOID
29 NewStrCat (
30 IN OUT CHAR16 *Destination,
31 IN CHAR16 *Source
32 )
33 {
34 UINTN Length;
35
36 for (Length = 0; Destination[Length] != 0; Length++)
37 ;
38
39 //
40 // We now have the length of the original string
41 // We can safely assume for now that we are concatenating a narrow value to this string.
42 // For instance, the string is "XYZ" and cat'ing ">"
43 // If this assumption changes, we need to make this routine a bit more complex
44 //
45 Destination[Length] = NARROW_CHAR;
46 Length++;
47
48 StrCpy (Destination + Length, Source);
49 }
50
51 /**
52 Get UINT64 type value.
53
54 @param Value Input Hii value.
55
56 @retval UINT64 Return the UINT64 type value.
57
58 **/
59 UINT64
60 HiiValueToUINT64 (
61 IN EFI_HII_VALUE *Value
62 )
63 {
64 UINT64 RetVal;
65
66 RetVal = 0;
67
68 switch (Value->Type) {
69 case EFI_IFR_TYPE_NUM_SIZE_8:
70 RetVal = Value->Value.u8;
71 break;
72
73 case EFI_IFR_TYPE_NUM_SIZE_16:
74 RetVal = Value->Value.u16;
75 break;
76
77 case EFI_IFR_TYPE_NUM_SIZE_32:
78 RetVal = Value->Value.u32;
79 break;
80
81 case EFI_IFR_TYPE_BOOLEAN:
82 RetVal = Value->Value.b;
83 break;
84
85 case EFI_IFR_TYPE_DATE:
86 RetVal = *(UINT64*) &Value->Value.date;
87 break;
88
89 case EFI_IFR_TYPE_TIME:
90 RetVal = (*(UINT64*) &Value->Value.time) & 0xffffff;
91 break;
92
93 default:
94 RetVal = Value->Value.u64;
95 break;
96 }
97
98 return RetVal;
99 }
100
101 /**
102 Compare two Hii value.
103
104 @param Value1 Expression value to compare on left-hand.
105 @param Value2 Expression value to compare on right-hand.
106 @param Result Return value after compare.
107 retval 0 Two operators equal.
108 return Positive value if Value1 is greater than Value2.
109 retval Negative value if Value1 is less than Value2.
110 @param HiiHandle Only required for string compare.
111
112 @retval other Could not perform compare on two values.
113 @retval EFI_SUCCESS Compare the value success.
114
115 **/
116 EFI_STATUS
117 CompareHiiValue (
118 IN EFI_HII_VALUE *Value1,
119 IN EFI_HII_VALUE *Value2,
120 OUT INTN *Result,
121 IN EFI_HII_HANDLE HiiHandle OPTIONAL
122 )
123 {
124 INT64 Temp64;
125 CHAR16 *Str1;
126 CHAR16 *Str2;
127 UINTN Len;
128
129 if (Value1->Type >= EFI_IFR_TYPE_OTHER || Value2->Type >= EFI_IFR_TYPE_OTHER ) {
130 if (Value1->Type != EFI_IFR_TYPE_BUFFER && Value2->Type != EFI_IFR_TYPE_BUFFER) {
131 return EFI_UNSUPPORTED;
132 }
133 }
134
135 if (Value1->Type == EFI_IFR_TYPE_STRING || Value2->Type == EFI_IFR_TYPE_STRING ) {
136 if (Value1->Type != Value2->Type) {
137 //
138 // Both Operator should be type of String
139 //
140 return EFI_UNSUPPORTED;
141 }
142
143 if (Value1->Value.string == 0 || Value2->Value.string == 0) {
144 //
145 // StringId 0 is reserved
146 //
147 return EFI_INVALID_PARAMETER;
148 }
149
150 if (Value1->Value.string == Value2->Value.string) {
151 *Result = 0;
152 return EFI_SUCCESS;
153 }
154
155 Str1 = GetToken (Value1->Value.string, HiiHandle);
156 if (Str1 == NULL) {
157 //
158 // String not found
159 //
160 return EFI_NOT_FOUND;
161 }
162
163 Str2 = GetToken (Value2->Value.string, HiiHandle);
164 if (Str2 == NULL) {
165 FreePool (Str1);
166 return EFI_NOT_FOUND;
167 }
168
169 *Result = StrCmp (Str1, Str2);
170
171 FreePool (Str1);
172 FreePool (Str2);
173
174 return EFI_SUCCESS;
175 }
176
177 if (Value1->Type == EFI_IFR_TYPE_BUFFER || Value2->Type == EFI_IFR_TYPE_BUFFER ) {
178 if (Value1->Type != Value2->Type) {
179 //
180 // Both Operator should be type of Buffer.
181 //
182 return EFI_UNSUPPORTED;
183 }
184 Len = Value1->BufferLen > Value2->BufferLen ? Value2->BufferLen : Value1->BufferLen;
185 *Result = CompareMem (Value1->Buffer, Value2->Buffer, Len);
186 if ((*Result == 0) && (Value1->BufferLen != Value2->BufferLen))
187 {
188 //
189 // In this case, means base on samll number buffer, the data is same
190 // So which value has more data, which value is bigger.
191 //
192 *Result = Value1->BufferLen > Value2->BufferLen ? 1 : -1;
193 }
194 return EFI_SUCCESS;
195 }
196
197 //
198 // Take remain types(integer, boolean, date/time) as integer
199 //
200 Temp64 = HiiValueToUINT64(Value1) - HiiValueToUINT64(Value2);
201 if (Temp64 > 0) {
202 *Result = 1;
203 } else if (Temp64 < 0) {
204 *Result = -1;
205 } else {
206 *Result = 0;
207 }
208
209 return EFI_SUCCESS;
210 }
211
212 /**
213 Search an Option of a Question by its value.
214
215 @param Question The Question
216 @param OptionValue Value for Option to be searched.
217
218 @retval Pointer Pointer to the found Option.
219 @retval NULL Option not found.
220
221 **/
222 DISPLAY_QUESTION_OPTION *
223 ValueToOption (
224 IN FORM_DISPLAY_ENGINE_STATEMENT *Question,
225 IN EFI_HII_VALUE *OptionValue
226 )
227 {
228 LIST_ENTRY *Link;
229 DISPLAY_QUESTION_OPTION *Option;
230 INTN Result;
231 EFI_HII_VALUE Value;
232
233 Link = GetFirstNode (&Question->OptionListHead);
234 while (!IsNull (&Question->OptionListHead, Link)) {
235 Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
236
237 ZeroMem (&Value, sizeof (EFI_HII_VALUE));
238 Value.Type = Option->OptionOpCode->Type;
239 CopyMem (&Value.Value, &Option->OptionOpCode->Value, Option->OptionOpCode->Header.Length - OFFSET_OF (EFI_IFR_ONE_OF_OPTION, Value));
240
241 if ((CompareHiiValue (&Value, OptionValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) {
242 return Option;
243 }
244
245 Link = GetNextNode (&Question->OptionListHead, Link);
246 }
247
248 return NULL;
249 }
250
251
252 /**
253 Return data element in an Array by its Index.
254
255 @param Array The data array.
256 @param Type Type of the data in this array.
257 @param Index Zero based index for data in this array.
258
259 @retval Value The data to be returned
260
261 **/
262 UINT64
263 GetArrayData (
264 IN VOID *Array,
265 IN UINT8 Type,
266 IN UINTN Index
267 )
268 {
269 UINT64 Data;
270
271 ASSERT (Array != NULL);
272
273 Data = 0;
274 switch (Type) {
275 case EFI_IFR_TYPE_NUM_SIZE_8:
276 Data = (UINT64) *(((UINT8 *) Array) + Index);
277 break;
278
279 case EFI_IFR_TYPE_NUM_SIZE_16:
280 Data = (UINT64) *(((UINT16 *) Array) + Index);
281 break;
282
283 case EFI_IFR_TYPE_NUM_SIZE_32:
284 Data = (UINT64) *(((UINT32 *) Array) + Index);
285 break;
286
287 case EFI_IFR_TYPE_NUM_SIZE_64:
288 Data = (UINT64) *(((UINT64 *) Array) + Index);
289 break;
290
291 default:
292 break;
293 }
294
295 return Data;
296 }
297
298
299 /**
300 Set value of a data element in an Array by its Index.
301
302 @param Array The data array.
303 @param Type Type of the data in this array.
304 @param Index Zero based index for data in this array.
305 @param Value The value to be set.
306
307 **/
308 VOID
309 SetArrayData (
310 IN VOID *Array,
311 IN UINT8 Type,
312 IN UINTN Index,
313 IN UINT64 Value
314 )
315 {
316
317 ASSERT (Array != NULL);
318
319 switch (Type) {
320 case EFI_IFR_TYPE_NUM_SIZE_8:
321 *(((UINT8 *) Array) + Index) = (UINT8) Value;
322 break;
323
324 case EFI_IFR_TYPE_NUM_SIZE_16:
325 *(((UINT16 *) Array) + Index) = (UINT16) Value;
326 break;
327
328 case EFI_IFR_TYPE_NUM_SIZE_32:
329 *(((UINT32 *) Array) + Index) = (UINT32) Value;
330 break;
331
332 case EFI_IFR_TYPE_NUM_SIZE_64:
333 *(((UINT64 *) Array) + Index) = (UINT64) Value;
334 break;
335
336 default:
337 break;
338 }
339 }
340
341 /**
342 Check whether this value already in the array, if yes, return the index.
343
344 @param Array The data array.
345 @param Type Type of the data in this array.
346 @param Value The value to be find.
347 @param Index The index in the array which has same value with Value.
348
349 @retval TRUE Found the value in the array.
350 @retval FALSE Not found the value.
351
352 **/
353 BOOLEAN
354 FindArrayData (
355 IN VOID *Array,
356 IN UINT8 Type,
357 IN UINT64 Value,
358 OUT UINTN *Index OPTIONAL
359 )
360 {
361 UINTN Count;
362 UINT64 TmpValue;
363 UINT64 ValueComp;
364
365 ASSERT (Array != NULL);
366
367 Count = 0;
368 TmpValue = 0;
369
370 switch (Type) {
371 case EFI_IFR_TYPE_NUM_SIZE_8:
372 ValueComp = (UINT8) Value;
373 break;
374
375 case EFI_IFR_TYPE_NUM_SIZE_16:
376 ValueComp = (UINT16) Value;
377 break;
378
379 case EFI_IFR_TYPE_NUM_SIZE_32:
380 ValueComp = (UINT32) Value;
381 break;
382
383 case EFI_IFR_TYPE_NUM_SIZE_64:
384 ValueComp = (UINT64) Value;
385 break;
386
387 default:
388 ValueComp = 0;
389 break;
390 }
391
392 while ((TmpValue = GetArrayData (Array, Type, Count)) != 0) {
393 if (ValueComp == TmpValue) {
394 if (Index != NULL) {
395 *Index = Count;
396 }
397 return TRUE;
398 }
399
400 Count ++;
401 }
402
403 return FALSE;
404 }
405
406 /**
407 Print Question Value according to it's storage width and display attributes.
408
409 @param Question The Question to be printed.
410 @param FormattedNumber Buffer for output string.
411 @param BufferSize The FormattedNumber buffer size in bytes.
412
413 @retval EFI_SUCCESS Print success.
414 @retval EFI_BUFFER_TOO_SMALL Buffer size is not enough for formatted number.
415
416 **/
417 EFI_STATUS
418 PrintFormattedNumber (
419 IN FORM_DISPLAY_ENGINE_STATEMENT *Question,
420 IN OUT CHAR16 *FormattedNumber,
421 IN UINTN BufferSize
422 )
423 {
424 INT64 Value;
425 CHAR16 *Format;
426 EFI_HII_VALUE *QuestionValue;
427 EFI_IFR_NUMERIC *NumericOp;
428
429 if (BufferSize < (21 * sizeof (CHAR16))) {
430 return EFI_BUFFER_TOO_SMALL;
431 }
432
433 QuestionValue = &Question->CurrentValue;
434 NumericOp = (EFI_IFR_NUMERIC *) Question->OpCode;
435
436 Value = (INT64) QuestionValue->Value.u64;
437 switch (NumericOp->Flags & EFI_IFR_DISPLAY) {
438 case EFI_IFR_DISPLAY_INT_DEC:
439 switch (QuestionValue->Type) {
440 case EFI_IFR_NUMERIC_SIZE_1:
441 Value = (INT64) ((INT8) QuestionValue->Value.u8);
442 break;
443
444 case EFI_IFR_NUMERIC_SIZE_2:
445 Value = (INT64) ((INT16) QuestionValue->Value.u16);
446 break;
447
448 case EFI_IFR_NUMERIC_SIZE_4:
449 Value = (INT64) ((INT32) QuestionValue->Value.u32);
450 break;
451
452 case EFI_IFR_NUMERIC_SIZE_8:
453 default:
454 break;
455 }
456
457 if (Value < 0) {
458 Value = -Value;
459 Format = L"-%ld";
460 } else {
461 Format = L"%ld";
462 }
463 break;
464
465 case EFI_IFR_DISPLAY_UINT_DEC:
466 Format = L"%ld";
467 break;
468
469 case EFI_IFR_DISPLAY_UINT_HEX:
470 Format = L"%lx";
471 break;
472
473 default:
474 return EFI_UNSUPPORTED;
475 break;
476 }
477
478 UnicodeSPrint (FormattedNumber, BufferSize, Format, Value);
479
480 return EFI_SUCCESS;
481 }
482
483
484 /**
485 Draw a pop up windows based on the dimension, number of lines and
486 strings specified.
487
488 @param RequestedWidth The width of the pop-up.
489 @param NumberOfLines The number of lines.
490 @param Marker The variable argument list for the list of string to be printed.
491
492 **/
493 VOID
494 CreateSharedPopUp (
495 IN UINTN RequestedWidth,
496 IN UINTN NumberOfLines,
497 IN VA_LIST Marker
498 )
499 {
500 UINTN Index;
501 UINTN Count;
502 CHAR16 Character;
503 UINTN Start;
504 UINTN End;
505 UINTN Top;
506 UINTN Bottom;
507 CHAR16 *String;
508 UINTN DimensionsWidth;
509 UINTN DimensionsHeight;
510
511 DimensionsWidth = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn;
512 DimensionsHeight = gStatementDimensions.BottomRow - gStatementDimensions.TopRow;
513
514 gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
515
516 if ((RequestedWidth + 2) > DimensionsWidth) {
517 RequestedWidth = DimensionsWidth - 2;
518 }
519
520 //
521 // Subtract the PopUp width from total Columns, allow for one space extra on
522 // each end plus a border.
523 //
524 Start = (DimensionsWidth - RequestedWidth - 2) / 2 + gStatementDimensions.LeftColumn + 1;
525 End = Start + RequestedWidth + 1;
526
527 Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gStatementDimensions.TopRow - 1;
528 Bottom = Top + NumberOfLines + 2;
529
530 Character = BOXDRAW_DOWN_RIGHT;
531 PrintCharAt (Start, Top, Character);
532 Character = BOXDRAW_HORIZONTAL;
533 for (Index = Start; Index + 2 < End; Index++) {
534 PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
535 }
536
537 Character = BOXDRAW_DOWN_LEFT;
538 PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
539 Character = BOXDRAW_VERTICAL;
540
541 Count = 0;
542 for (Index = Top; Index + 2 < Bottom; Index++, Count++) {
543 String = VA_ARG (Marker, CHAR16*);
544
545 //
546 // This will clear the background of the line - we never know who might have been
547 // here before us. This differs from the next clear in that it used the non-reverse
548 // video for normal printing.
549 //
550 if (GetStringWidth (String) / 2 > 1) {
551 ClearLines (Start, End, Index + 1, Index + 1, GetPopupColor ());
552 }
553
554 //
555 // Passing in a space results in the assumption that this is where typing will occur
556 //
557 if (String[0] == L' ') {
558 ClearLines (Start + 1, End - 1, Index + 1, Index + 1, GetPopupInverseColor ());
559 }
560
561 //
562 // Passing in a NULL results in a blank space
563 //
564 if (String[0] == CHAR_NULL) {
565 ClearLines (Start, End, Index + 1, Index + 1, GetPopupColor ());
566 }
567
568 PrintStringAt (
569 ((DimensionsWidth - GetStringWidth (String) / 2) / 2) + gStatementDimensions.LeftColumn + 1,
570 Index + 1,
571 String
572 );
573 gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
574 PrintCharAt (Start, Index + 1, Character);
575 PrintCharAt (End - 1, Index + 1, Character);
576 }
577
578 Character = BOXDRAW_UP_RIGHT;
579 PrintCharAt (Start, Bottom - 1, Character);
580 Character = BOXDRAW_HORIZONTAL;
581 for (Index = Start; Index + 2 < End; Index++) {
582 PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
583 }
584
585 Character = BOXDRAW_UP_LEFT;
586 PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
587 }
588
589 /**
590 Draw a pop up windows based on the dimension, number of lines and
591 strings specified.
592
593 @param RequestedWidth The width of the pop-up.
594 @param NumberOfLines The number of lines.
595 @param ... A series of text strings that displayed in the pop-up.
596
597 **/
598 VOID
599 EFIAPI
600 CreateMultiStringPopUp (
601 IN UINTN RequestedWidth,
602 IN UINTN NumberOfLines,
603 ...
604 )
605 {
606 VA_LIST Marker;
607
608 VA_START (Marker, NumberOfLines);
609
610 CreateSharedPopUp (RequestedWidth, NumberOfLines, Marker);
611
612 VA_END (Marker);
613 }
614
615 /**
616 Process nothing.
617
618 @param Event The Event need to be process
619 @param Context The context of the event.
620
621 **/
622 VOID
623 EFIAPI
624 EmptyEventProcess (
625 IN EFI_EVENT Event,
626 IN VOID *Context
627 )
628 {
629 }
630
631 /**
632 Process for the refresh interval statement.
633
634 @param Event The Event need to be process
635 @param Context The context of the event.
636
637 **/
638 VOID
639 EFIAPI
640 RefreshTimeOutProcess (
641 IN EFI_EVENT Event,
642 IN VOID *Context
643 )
644 {
645 WARNING_IF_CONTEXT *EventInfo;
646 CHAR16 TimeOutString[MAX_TIME_OUT_LEN];
647
648 EventInfo = (WARNING_IF_CONTEXT *) Context;
649
650 if (*(EventInfo->TimeOut) == 0) {
651 gBS->CloseEvent (Event);
652
653 gBS->SignalEvent (EventInfo->SyncEvent);
654 return;
655 }
656
657 UnicodeSPrint(TimeOutString, MAX_TIME_OUT_LEN, L"%d", *(EventInfo->TimeOut));
658
659 CreateDialog (NULL, gEmptyString, EventInfo->ErrorInfo, gPressEnter, gEmptyString, TimeOutString, NULL);
660
661 *(EventInfo->TimeOut) -= 1;
662 }
663
664 /**
665 Display error message for invalid password.
666
667 **/
668 VOID
669 PasswordInvalid (
670 VOID
671 )
672 {
673 EFI_INPUT_KEY Key;
674
675 //
676 // Invalid password, prompt error message
677 //
678 do {
679 CreateDialog (&Key, gEmptyString, gPassowordInvalid, gPressEnter, gEmptyString, NULL);
680 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
681 }
682
683 /**
684 Process password op code.
685
686 @param MenuOption The menu for current password op code.
687
688 @retval EFI_SUCCESS Question Option process success.
689 @retval Other Question Option process fail.
690
691 **/
692 EFI_STATUS
693 PasswordProcess (
694 IN UI_MENU_OPTION *MenuOption
695 )
696 {
697 CHAR16 *StringPtr;
698 CHAR16 *TempString;
699 UINTN Maximum;
700 EFI_STATUS Status;
701 EFI_IFR_PASSWORD *PasswordInfo;
702 FORM_DISPLAY_ENGINE_STATEMENT *Question;
703 EFI_INPUT_KEY Key;
704
705 Question = MenuOption->ThisTag;
706 PasswordInfo = (EFI_IFR_PASSWORD *) Question->OpCode;
707 Maximum = PasswordInfo->MaxSize;
708 Status = EFI_SUCCESS;
709
710 StringPtr = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16));
711 ASSERT (StringPtr);
712
713 //
714 // Use a NULL password to test whether old password is required
715 //
716 *StringPtr = 0;
717 Status = Question->PasswordCheck (gFormData, Question, StringPtr);
718 if (Status == EFI_NOT_AVAILABLE_YET || Status == EFI_UNSUPPORTED) {
719 //
720 // Password can't be set now.
721 //
722 FreePool (StringPtr);
723 return EFI_SUCCESS;
724 }
725
726 if (EFI_ERROR (Status)) {
727 //
728 // Old password exist, ask user for the old password
729 //
730 Status = ReadString (MenuOption, gPromptForPassword, StringPtr);
731 if (EFI_ERROR (Status)) {
732 FreePool (StringPtr);
733 return Status;
734 }
735
736 //
737 // Check user input old password
738 //
739 Status = Question->PasswordCheck (gFormData, Question, StringPtr);
740 if (EFI_ERROR (Status)) {
741 if (Status == EFI_NOT_READY) {
742 //
743 // Typed in old password incorrect
744 //
745 PasswordInvalid ();
746 } else {
747 Status = EFI_SUCCESS;
748 }
749
750 FreePool (StringPtr);
751 return Status;
752 }
753 }
754
755 //
756 // Ask for new password
757 //
758 ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
759 Status = ReadString (MenuOption, gPromptForNewPassword, StringPtr);
760 if (EFI_ERROR (Status)) {
761 //
762 // Reset state machine for password
763 //
764 Question->PasswordCheck (gFormData, Question, NULL);
765 FreePool (StringPtr);
766 return Status;
767 }
768
769 //
770 // Confirm new password
771 //
772 TempString = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16));
773 ASSERT (TempString);
774 Status = ReadString (MenuOption, gConfirmPassword, TempString);
775 if (EFI_ERROR (Status)) {
776 //
777 // Reset state machine for password
778 //
779 Question->PasswordCheck (gFormData, Question, NULL);
780 FreePool (StringPtr);
781 FreePool (TempString);
782 return Status;
783 }
784
785 //
786 // Compare two typed-in new passwords
787 //
788 if (StrCmp (StringPtr, TempString) == 0) {
789 gUserInput->InputValue.Buffer = AllocateCopyPool (Question->CurrentValue.BufferLen, StringPtr);
790 gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
791 gUserInput->InputValue.Type = Question->CurrentValue.Type;
792 gUserInput->InputValue.Value.string = HiiSetString(gFormData->HiiHandle, gUserInput->InputValue.Value.string, StringPtr, NULL);
793 FreePool (StringPtr);
794
795 Status = EFI_SUCCESS;
796
797 if (EFI_ERROR (Status)) {
798 //
799 // Reset state machine for password
800 //
801 Question->PasswordCheck (gFormData, Question, NULL);
802 }
803
804 return Status;
805 } else {
806 //
807 // Reset state machine for password
808 //
809 Question->PasswordCheck (gFormData, Question, NULL);
810
811 //
812 // Two password mismatch, prompt error message
813 //
814 do {
815 CreateDialog (&Key, gEmptyString, gConfirmError, gPressEnter, gEmptyString, NULL);
816 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
817
818 Status = EFI_INVALID_PARAMETER;
819 }
820
821 FreePool (TempString);
822 FreePool (StringPtr);
823
824 return Status;
825 }
826
827 /**
828 Process a Question's Option (whether selected or un-selected).
829
830 @param MenuOption The MenuOption for this Question.
831 @param Selected TRUE: if Question is selected.
832 @param OptionString Pointer of the Option String to be displayed.
833 @param SkipErrorValue Whether need to return when value without option for it.
834
835 @retval EFI_SUCCESS Question Option process success.
836 @retval Other Question Option process fail.
837
838 **/
839 EFI_STATUS
840 ProcessOptions (
841 IN UI_MENU_OPTION *MenuOption,
842 IN BOOLEAN Selected,
843 OUT CHAR16 **OptionString,
844 IN BOOLEAN SkipErrorValue
845 )
846 {
847 EFI_STATUS Status;
848 CHAR16 *StringPtr;
849 UINTN Index;
850 FORM_DISPLAY_ENGINE_STATEMENT *Question;
851 CHAR16 FormattedNumber[21];
852 UINT16 Number;
853 CHAR16 Character[2];
854 EFI_INPUT_KEY Key;
855 UINTN BufferSize;
856 DISPLAY_QUESTION_OPTION *OneOfOption;
857 LIST_ENTRY *Link;
858 EFI_HII_VALUE HiiValue;
859 EFI_HII_VALUE *QuestionValue;
860 DISPLAY_QUESTION_OPTION *Option;
861 UINTN Index2;
862 UINT8 *ValueArray;
863 UINT8 ValueType;
864 EFI_STRING_ID StringId;
865 EFI_IFR_ORDERED_LIST *OrderList;
866 BOOLEAN ValueInvalid;
867
868 Status = EFI_SUCCESS;
869
870 StringPtr = NULL;
871 Character[1] = L'\0';
872 *OptionString = NULL;
873 StringId = 0;
874 ValueInvalid = FALSE;
875
876 ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
877 BufferSize = (gOptionBlockWidth + 1) * 2 * gStatementDimensions.BottomRow;
878
879 Question = MenuOption->ThisTag;
880 QuestionValue = &Question->CurrentValue;
881
882 switch (Question->OpCode->OpCode) {
883 case EFI_IFR_ORDERED_LIST_OP:
884
885 //
886 // Check whether there are Options of this OrderedList
887 //
888 if (IsListEmpty (&Question->OptionListHead)) {
889 break;
890 }
891
892 OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode;
893
894 Link = GetFirstNode (&Question->OptionListHead);
895 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
896
897 ValueType = OneOfOption->OptionOpCode->Type;
898 ValueArray = Question->CurrentValue.Buffer;
899
900 if (Selected) {
901 //
902 // Go ask for input
903 //
904 Status = GetSelectionInputPopUp (MenuOption);
905 } else {
906 //
907 // We now know how many strings we will have, so we can allocate the
908 // space required for the array or strings.
909 //
910 *OptionString = AllocateZeroPool (OrderList->MaxContainers * BufferSize);
911 ASSERT (*OptionString);
912
913 HiiValue.Type = ValueType;
914 HiiValue.Value.u64 = 0;
915 for (Index = 0; Index < OrderList->MaxContainers; Index++) {
916 HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
917 if (HiiValue.Value.u64 == 0) {
918 //
919 // Values for the options in ordered lists should never be a 0
920 //
921 break;
922 }
923
924 OneOfOption = ValueToOption (Question, &HiiValue);
925 if (OneOfOption == NULL) {
926 if (SkipErrorValue) {
927 //
928 // Just try to get the option string, skip the value which not has option.
929 //
930 continue;
931 }
932
933 //
934 // Show error message
935 //
936 do {
937 CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL);
938 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
939
940 //
941 // The initial value of the orderedlist is invalid, force to be valid value
942 // Exit current DisplayForm with new value.
943 //
944 gUserInput->SelectedStatement = Question;
945
946 ValueArray = AllocateZeroPool (Question->CurrentValue.BufferLen);
947 ASSERT (ValueArray != NULL);
948 gUserInput->InputValue.Buffer = ValueArray;
949 gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
950 gUserInput->InputValue.Type = Question->CurrentValue.Type;
951
952 Link = GetFirstNode (&Question->OptionListHead);
953 Index2 = 0;
954 while (!IsNull (&Question->OptionListHead, Link) && Index2 < OrderList->MaxContainers) {
955 Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
956 Link = GetNextNode (&Question->OptionListHead, Link);
957 SetArrayData (ValueArray, ValueType, Index2, Option->OptionOpCode->Value.u64);
958 Index2++;
959 }
960 SetArrayData (ValueArray, ValueType, Index2, 0);
961
962 FreePool (*OptionString);
963 *OptionString = NULL;
964 return EFI_NOT_FOUND;
965 }
966
967 Character[0] = LEFT_ONEOF_DELIMITER;
968 NewStrCat (OptionString[0], Character);
969 StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
970 ASSERT (StringPtr != NULL);
971 NewStrCat (OptionString[0], StringPtr);
972 Character[0] = RIGHT_ONEOF_DELIMITER;
973 NewStrCat (OptionString[0], Character);
974 Character[0] = CHAR_CARRIAGE_RETURN;
975 NewStrCat (OptionString[0], Character);
976 FreePool (StringPtr);
977 }
978
979 //
980 // If valid option more than the max container, skip these options.
981 //
982 if (Index >= OrderList->MaxContainers) {
983 break;
984 }
985
986 //
987 // Search the other options, try to find the one not in the container.
988 //
989 Link = GetFirstNode (&Question->OptionListHead);
990 while (!IsNull (&Question->OptionListHead, Link)) {
991 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
992 Link = GetNextNode (&Question->OptionListHead, Link);
993
994 if (FindArrayData (ValueArray, ValueType, OneOfOption->OptionOpCode->Value.u64, NULL)) {
995 continue;
996 }
997
998 if (SkipErrorValue) {
999 //
1000 // Not report error, just get the correct option string info.
1001 //
1002 Character[0] = LEFT_ONEOF_DELIMITER;
1003 NewStrCat (OptionString[0], Character);
1004 StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
1005 ASSERT (StringPtr != NULL);
1006 NewStrCat (OptionString[0], StringPtr);
1007 Character[0] = RIGHT_ONEOF_DELIMITER;
1008 NewStrCat (OptionString[0], Character);
1009 Character[0] = CHAR_CARRIAGE_RETURN;
1010 NewStrCat (OptionString[0], Character);
1011 FreePool (StringPtr);
1012
1013 continue;
1014 }
1015
1016 if (!ValueInvalid) {
1017 ValueInvalid = TRUE;
1018 //
1019 // Show error message
1020 //
1021 do {
1022 CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL);
1023 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
1024
1025 //
1026 // The initial value of the orderedlist is invalid, force to be valid value
1027 // Exit current DisplayForm with new value.
1028 //
1029 gUserInput->SelectedStatement = Question;
1030
1031 ValueArray = AllocateCopyPool (Question->CurrentValue.BufferLen, Question->CurrentValue.Buffer);
1032 ASSERT (ValueArray != NULL);
1033 gUserInput->InputValue.Buffer = ValueArray;
1034 gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
1035 gUserInput->InputValue.Type = Question->CurrentValue.Type;
1036 }
1037
1038 SetArrayData (ValueArray, ValueType, Index++, OneOfOption->OptionOpCode->Value.u64);
1039 }
1040
1041 if (ValueInvalid) {
1042 FreePool (*OptionString);
1043 *OptionString = NULL;
1044 return EFI_NOT_FOUND;
1045 }
1046 }
1047 break;
1048
1049 case EFI_IFR_ONE_OF_OP:
1050 //
1051 // Check whether there are Options of this OneOf
1052 //
1053 if (IsListEmpty (&Question->OptionListHead)) {
1054 break;
1055 }
1056 if (Selected) {
1057 //
1058 // Go ask for input
1059 //
1060 Status = GetSelectionInputPopUp (MenuOption);
1061 } else {
1062 *OptionString = AllocateZeroPool (BufferSize);
1063 ASSERT (*OptionString);
1064
1065 OneOfOption = ValueToOption (Question, QuestionValue);
1066 if (OneOfOption == NULL) {
1067 if (SkipErrorValue) {
1068 //
1069 // Not report error, just get the correct option string info.
1070 //
1071 Link = GetFirstNode (&Question->OptionListHead);
1072 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1073 } else {
1074 //
1075 // Show error message
1076 //
1077 do {
1078 CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL);
1079 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
1080
1081 //
1082 // Force the Question value to be valid
1083 // Exit current DisplayForm with new value.
1084 //
1085 Link = GetFirstNode (&Question->OptionListHead);
1086 Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1087
1088 gUserInput->InputValue.Type = Option->OptionOpCode->Type;
1089 switch (gUserInput->InputValue.Type) {
1090 case EFI_IFR_TYPE_NUM_SIZE_8:
1091 gUserInput->InputValue.Value.u8 = Option->OptionOpCode->Value.u8;
1092 break;
1093 case EFI_IFR_TYPE_NUM_SIZE_16:
1094 CopyMem (&gUserInput->InputValue.Value.u16, &Option->OptionOpCode->Value.u16, sizeof (UINT16));
1095 break;
1096 case EFI_IFR_TYPE_NUM_SIZE_32:
1097 CopyMem (&gUserInput->InputValue.Value.u32, &Option->OptionOpCode->Value.u32, sizeof (UINT32));
1098 break;
1099 case EFI_IFR_TYPE_NUM_SIZE_64:
1100 CopyMem (&gUserInput->InputValue.Value.u64, &Option->OptionOpCode->Value.u64, sizeof (UINT64));
1101 break;
1102 default:
1103 ASSERT (FALSE);
1104 break;
1105 }
1106 gUserInput->SelectedStatement = Question;
1107
1108 FreePool (*OptionString);
1109 *OptionString = NULL;
1110 return EFI_NOT_FOUND;
1111 }
1112 }
1113
1114 Character[0] = LEFT_ONEOF_DELIMITER;
1115 NewStrCat (OptionString[0], Character);
1116 StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
1117 ASSERT (StringPtr != NULL);
1118 NewStrCat (OptionString[0], StringPtr);
1119 Character[0] = RIGHT_ONEOF_DELIMITER;
1120 NewStrCat (OptionString[0], Character);
1121
1122 FreePool (StringPtr);
1123 }
1124 break;
1125
1126 case EFI_IFR_CHECKBOX_OP:
1127 if (Selected) {
1128 //
1129 // Since this is a BOOLEAN operation, flip it upon selection
1130 //
1131 gUserInput->InputValue.Type = QuestionValue->Type;
1132 gUserInput->InputValue.Value.b = (BOOLEAN) (QuestionValue->Value.b ? FALSE : TRUE);
1133
1134 //
1135 // Perform inconsistent check
1136 //
1137 return EFI_SUCCESS;
1138 } else {
1139 *OptionString = AllocateZeroPool (BufferSize);
1140 ASSERT (*OptionString);
1141
1142 *OptionString[0] = LEFT_CHECKBOX_DELIMITER;
1143
1144 if (QuestionValue->Value.b) {
1145 *(OptionString[0] + 1) = CHECK_ON;
1146 } else {
1147 *(OptionString[0] + 1) = CHECK_OFF;
1148 }
1149 *(OptionString[0] + 2) = RIGHT_CHECKBOX_DELIMITER;
1150 }
1151 break;
1152
1153 case EFI_IFR_NUMERIC_OP:
1154 if (Selected) {
1155 //
1156 // Go ask for input
1157 //
1158 Status = GetNumericInput (MenuOption);
1159 } else {
1160 *OptionString = AllocateZeroPool (BufferSize);
1161 ASSERT (*OptionString);
1162
1163 *OptionString[0] = LEFT_NUMERIC_DELIMITER;
1164
1165 //
1166 // Formatted print
1167 //
1168 PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
1169 Number = (UINT16) GetStringWidth (FormattedNumber);
1170 CopyMem (OptionString[0] + 1, FormattedNumber, Number);
1171
1172 *(OptionString[0] + Number / 2) = RIGHT_NUMERIC_DELIMITER;
1173 }
1174 break;
1175
1176 case EFI_IFR_DATE_OP:
1177 if (Selected) {
1178 //
1179 // This is similar to numerics
1180 //
1181 Status = GetNumericInput (MenuOption);
1182 } else {
1183 *OptionString = AllocateZeroPool (BufferSize);
1184 ASSERT (*OptionString);
1185
1186 switch (MenuOption->Sequence) {
1187 case 0:
1188 *OptionString[0] = LEFT_NUMERIC_DELIMITER;
1189 UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Month);
1190 *(OptionString[0] + 3) = DATE_SEPARATOR;
1191 break;
1192
1193 case 1:
1194 SetUnicodeMem (OptionString[0], 4, L' ');
1195 UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Day);
1196 *(OptionString[0] + 6) = DATE_SEPARATOR;
1197 break;
1198
1199 case 2:
1200 SetUnicodeMem (OptionString[0], 7, L' ');
1201 UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%04d", QuestionValue->Value.date.Year);
1202 *(OptionString[0] + 11) = RIGHT_NUMERIC_DELIMITER;
1203 break;
1204 }
1205 }
1206 break;
1207
1208 case EFI_IFR_TIME_OP:
1209 if (Selected) {
1210 //
1211 // This is similar to numerics
1212 //
1213 Status = GetNumericInput (MenuOption);
1214 } else {
1215 *OptionString = AllocateZeroPool (BufferSize);
1216 ASSERT (*OptionString);
1217
1218 switch (MenuOption->Sequence) {
1219 case 0:
1220 *OptionString[0] = LEFT_NUMERIC_DELIMITER;
1221 UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Hour);
1222 *(OptionString[0] + 3) = TIME_SEPARATOR;
1223 break;
1224
1225 case 1:
1226 SetUnicodeMem (OptionString[0], 4, L' ');
1227 UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Minute);
1228 *(OptionString[0] + 6) = TIME_SEPARATOR;
1229 break;
1230
1231 case 2:
1232 SetUnicodeMem (OptionString[0], 7, L' ');
1233 UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Second);
1234 *(OptionString[0] + 9) = RIGHT_NUMERIC_DELIMITER;
1235 break;
1236 }
1237 }
1238 break;
1239
1240 case EFI_IFR_STRING_OP:
1241 if (Selected) {
1242 StringPtr = AllocateZeroPool (Question->CurrentValue.BufferLen + sizeof (CHAR16));
1243 ASSERT (StringPtr);
1244 CopyMem(StringPtr, Question->CurrentValue.Buffer, Question->CurrentValue.BufferLen);
1245
1246 Status = ReadString (MenuOption, gPromptForData, StringPtr);
1247 if (EFI_ERROR (Status)) {
1248 FreePool (StringPtr);
1249 return Status;
1250 }
1251
1252 gUserInput->InputValue.Buffer = AllocateCopyPool (Question->CurrentValue.BufferLen, StringPtr);
1253 gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
1254 gUserInput->InputValue.Type = Question->CurrentValue.Type;
1255 gUserInput->InputValue.Value.string = HiiSetString(gFormData->HiiHandle, gUserInput->InputValue.Value.string, StringPtr, NULL);
1256 FreePool (StringPtr);
1257 return EFI_SUCCESS;
1258 } else {
1259 *OptionString = AllocateZeroPool (BufferSize);
1260 ASSERT (*OptionString);
1261
1262 if (((CHAR16 *) Question->CurrentValue.Buffer)[0] == 0x0000) {
1263 *(OptionString[0]) = '_';
1264 } else {
1265 if (Question->CurrentValue.BufferLen < BufferSize) {
1266 BufferSize = Question->CurrentValue.BufferLen;
1267 }
1268 CopyMem (OptionString[0], (CHAR16 *) Question->CurrentValue.Buffer, BufferSize);
1269 }
1270 }
1271 break;
1272
1273 case EFI_IFR_PASSWORD_OP:
1274 if (Selected) {
1275 Status = PasswordProcess (MenuOption);
1276 }
1277 break;
1278
1279 default:
1280 break;
1281 }
1282
1283 return Status;
1284 }
1285
1286
1287 /**
1288 Process the help string: Split StringPtr to several lines of strings stored in
1289 FormattedString and the glyph width of each line cannot exceed gHelpBlockWidth.
1290
1291 @param StringPtr The entire help string.
1292 @param FormattedString The oupput formatted string.
1293 @param EachLineWidth The max string length of each line in the formatted string.
1294 @param RowCount TRUE: if Question is selected.
1295
1296 **/
1297 UINTN
1298 ProcessHelpString (
1299 IN CHAR16 *StringPtr,
1300 OUT CHAR16 **FormattedString,
1301 OUT UINT16 *EachLineWidth,
1302 IN UINTN RowCount
1303 )
1304 {
1305 UINTN Index;
1306 CHAR16 *OutputString;
1307 UINTN TotalRowNum;
1308 UINTN CheckedNum;
1309 UINT16 GlyphWidth;
1310 UINT16 LineWidth;
1311 UINT16 MaxStringLen;
1312 UINT16 StringLen;
1313
1314 TotalRowNum = 0;
1315 CheckedNum = 0;
1316 GlyphWidth = 1;
1317 Index = 0;
1318 MaxStringLen = 0;
1319 StringLen = 0;
1320
1321 //
1322 // Set default help string width.
1323 //
1324 LineWidth = (UINT16) (gHelpBlockWidth - 1);
1325
1326 //
1327 // Get row number of the String.
1328 //
1329 while ((StringLen = GetLineByWidth (StringPtr, LineWidth, &GlyphWidth, &Index, &OutputString)) != 0) {
1330 if (StringLen > MaxStringLen) {
1331 MaxStringLen = StringLen;
1332 }
1333
1334 TotalRowNum ++;
1335 FreePool (OutputString);
1336 }
1337 *EachLineWidth = MaxStringLen;
1338
1339 *FormattedString = AllocateZeroPool (TotalRowNum * MaxStringLen * sizeof (CHAR16));
1340 ASSERT (*FormattedString != NULL);
1341
1342 //
1343 // Generate formatted help string array.
1344 //
1345 GlyphWidth = 1;
1346 Index = 0;
1347 while((StringLen = GetLineByWidth (StringPtr, LineWidth, &GlyphWidth, &Index, &OutputString)) != 0) {
1348 CopyMem (*FormattedString + CheckedNum * MaxStringLen, OutputString, StringLen * sizeof (CHAR16));
1349 CheckedNum ++;
1350 FreePool (OutputString);
1351 }
1352
1353 return TotalRowNum;
1354 }