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