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