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