]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/DisplayEngineDxe/ProcessOptions.c
Clean the useless code.
[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 - 2014, 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_STRING_ID StringId;
959 EFI_IFR_ORDERED_LIST *OrderList;
960 BOOLEAN ValueInvalid;
961
962 Status = EFI_SUCCESS;
963
964 StringPtr = NULL;
965 Character[1] = L'\0';
966 *OptionString = NULL;
967 StringId = 0;
968 ValueInvalid = FALSE;
969
970 ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
971 BufferSize = (gOptionBlockWidth + 1) * 2 * gStatementDimensions.BottomRow;
972
973 Question = MenuOption->ThisTag;
974 QuestionValue = &Question->CurrentValue;
975
976 switch (Question->OpCode->OpCode) {
977 case EFI_IFR_ORDERED_LIST_OP:
978
979 //
980 // Check whether there are Options of this OrderedList
981 //
982 if (IsListEmpty (&Question->OptionListHead)) {
983 break;
984 }
985
986 OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode;
987
988 Link = GetFirstNode (&Question->OptionListHead);
989 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
990
991 ValueType = OneOfOption->OptionOpCode->Type;
992 ValueArray = Question->CurrentValue.Buffer;
993
994 if (Selected) {
995 //
996 // Go ask for input
997 //
998 Status = GetSelectionInputPopUp (MenuOption);
999 } else {
1000 //
1001 // We now know how many strings we will have, so we can allocate the
1002 // space required for the array or strings.
1003 //
1004 *OptionString = AllocateZeroPool (OrderList->MaxContainers * BufferSize);
1005 ASSERT (*OptionString);
1006
1007 HiiValue.Type = ValueType;
1008 HiiValue.Value.u64 = 0;
1009 for (Index = 0; Index < OrderList->MaxContainers; Index++) {
1010 HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
1011 if (HiiValue.Value.u64 == 0) {
1012 //
1013 // Values for the options in ordered lists should never be a 0
1014 //
1015 break;
1016 }
1017
1018 OneOfOption = ValueToOption (Question, &HiiValue);
1019 if (OneOfOption == NULL) {
1020 if (SkipErrorValue) {
1021 //
1022 // Just try to get the option string, skip the value which not has option.
1023 //
1024 continue;
1025 }
1026
1027 //
1028 // Show error message
1029 //
1030 do {
1031 CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL);
1032 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
1033
1034 //
1035 // The initial value of the orderedlist is invalid, force to be valid value
1036 // Exit current DisplayForm with new value.
1037 //
1038 gUserInput->SelectedStatement = Question;
1039
1040 ValueArray = AllocateZeroPool (Question->CurrentValue.BufferLen);
1041 ASSERT (ValueArray != NULL);
1042 gUserInput->InputValue.Buffer = ValueArray;
1043 gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
1044 gUserInput->InputValue.Type = Question->CurrentValue.Type;
1045
1046 Link = GetFirstNode (&Question->OptionListHead);
1047 Index2 = 0;
1048 while (!IsNull (&Question->OptionListHead, Link) && Index2 < OrderList->MaxContainers) {
1049 Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1050 Link = GetNextNode (&Question->OptionListHead, Link);
1051 SetArrayData (ValueArray, ValueType, Index2, Option->OptionOpCode->Value.u64);
1052 Index2++;
1053 }
1054 SetArrayData (ValueArray, ValueType, Index2, 0);
1055
1056 FreePool (*OptionString);
1057 *OptionString = NULL;
1058 return EFI_NOT_FOUND;
1059 }
1060
1061 Character[0] = LEFT_ONEOF_DELIMITER;
1062 NewStrCat (OptionString[0], Character);
1063 StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
1064 ASSERT (StringPtr != NULL);
1065 NewStrCat (OptionString[0], StringPtr);
1066 Character[0] = RIGHT_ONEOF_DELIMITER;
1067 NewStrCat (OptionString[0], Character);
1068 Character[0] = CHAR_CARRIAGE_RETURN;
1069 NewStrCat (OptionString[0], Character);
1070 FreePool (StringPtr);
1071 }
1072
1073 //
1074 // If valid option more than the max container, skip these options.
1075 //
1076 if (Index >= OrderList->MaxContainers) {
1077 break;
1078 }
1079
1080 //
1081 // Search the other options, try to find the one not in the container.
1082 //
1083 Link = GetFirstNode (&Question->OptionListHead);
1084 while (!IsNull (&Question->OptionListHead, Link)) {
1085 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1086 Link = GetNextNode (&Question->OptionListHead, Link);
1087
1088 if (FindArrayData (ValueArray, ValueType, OneOfOption->OptionOpCode->Value.u64, NULL)) {
1089 continue;
1090 }
1091
1092 if (SkipErrorValue) {
1093 //
1094 // Not report error, just get the correct option string info.
1095 //
1096 Character[0] = LEFT_ONEOF_DELIMITER;
1097 NewStrCat (OptionString[0], Character);
1098 StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
1099 ASSERT (StringPtr != NULL);
1100 NewStrCat (OptionString[0], StringPtr);
1101 Character[0] = RIGHT_ONEOF_DELIMITER;
1102 NewStrCat (OptionString[0], Character);
1103 Character[0] = CHAR_CARRIAGE_RETURN;
1104 NewStrCat (OptionString[0], Character);
1105 FreePool (StringPtr);
1106
1107 continue;
1108 }
1109
1110 if (!ValueInvalid) {
1111 ValueInvalid = TRUE;
1112 //
1113 // Show error message
1114 //
1115 do {
1116 CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL);
1117 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
1118
1119 //
1120 // The initial value of the orderedlist is invalid, force to be valid value
1121 // Exit current DisplayForm with new value.
1122 //
1123 gUserInput->SelectedStatement = Question;
1124
1125 ValueArray = AllocateCopyPool (Question->CurrentValue.BufferLen, Question->CurrentValue.Buffer);
1126 ASSERT (ValueArray != NULL);
1127 gUserInput->InputValue.Buffer = ValueArray;
1128 gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
1129 gUserInput->InputValue.Type = Question->CurrentValue.Type;
1130 }
1131
1132 SetArrayData (ValueArray, ValueType, Index++, OneOfOption->OptionOpCode->Value.u64);
1133 }
1134
1135 if (ValueInvalid) {
1136 FreePool (*OptionString);
1137 *OptionString = NULL;
1138 return EFI_NOT_FOUND;
1139 }
1140 }
1141 break;
1142
1143 case EFI_IFR_ONE_OF_OP:
1144 //
1145 // Check whether there are Options of this OneOf
1146 //
1147 if (IsListEmpty (&Question->OptionListHead)) {
1148 break;
1149 }
1150 if (Selected) {
1151 //
1152 // Go ask for input
1153 //
1154 Status = GetSelectionInputPopUp (MenuOption);
1155 } else {
1156 *OptionString = AllocateZeroPool (BufferSize);
1157 ASSERT (*OptionString);
1158
1159 OneOfOption = ValueToOption (Question, QuestionValue);
1160 if (OneOfOption == NULL) {
1161 if (SkipErrorValue) {
1162 //
1163 // Not report error, just get the correct option string info.
1164 //
1165 Link = GetFirstNode (&Question->OptionListHead);
1166 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1167 } else {
1168 //
1169 // Show error message
1170 //
1171 do {
1172 CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL);
1173 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
1174
1175 //
1176 // Force the Question value to be valid
1177 // Exit current DisplayForm with new value.
1178 //
1179 Link = GetFirstNode (&Question->OptionListHead);
1180 Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1181
1182 gUserInput->InputValue.Type = Option->OptionOpCode->Type;
1183 switch (gUserInput->InputValue.Type) {
1184 case EFI_IFR_TYPE_NUM_SIZE_8:
1185 gUserInput->InputValue.Value.u8 = Option->OptionOpCode->Value.u8;
1186 break;
1187 case EFI_IFR_TYPE_NUM_SIZE_16:
1188 CopyMem (&gUserInput->InputValue.Value.u16, &Option->OptionOpCode->Value.u16, sizeof (UINT16));
1189 break;
1190 case EFI_IFR_TYPE_NUM_SIZE_32:
1191 CopyMem (&gUserInput->InputValue.Value.u32, &Option->OptionOpCode->Value.u32, sizeof (UINT32));
1192 break;
1193 case EFI_IFR_TYPE_NUM_SIZE_64:
1194 CopyMem (&gUserInput->InputValue.Value.u64, &Option->OptionOpCode->Value.u64, sizeof (UINT64));
1195 break;
1196 default:
1197 ASSERT (FALSE);
1198 break;
1199 }
1200 gUserInput->SelectedStatement = Question;
1201
1202 FreePool (*OptionString);
1203 *OptionString = NULL;
1204 return EFI_NOT_FOUND;
1205 }
1206 }
1207
1208 Character[0] = LEFT_ONEOF_DELIMITER;
1209 NewStrCat (OptionString[0], Character);
1210 StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
1211 ASSERT (StringPtr != NULL);
1212 NewStrCat (OptionString[0], StringPtr);
1213 Character[0] = RIGHT_ONEOF_DELIMITER;
1214 NewStrCat (OptionString[0], Character);
1215
1216 FreePool (StringPtr);
1217 }
1218 break;
1219
1220 case EFI_IFR_CHECKBOX_OP:
1221 if (Selected) {
1222 //
1223 // Since this is a BOOLEAN operation, flip it upon selection
1224 //
1225 gUserInput->InputValue.Type = QuestionValue->Type;
1226 gUserInput->InputValue.Value.b = (BOOLEAN) (QuestionValue->Value.b ? FALSE : TRUE);
1227
1228 //
1229 // Perform inconsistent check
1230 //
1231 return EFI_SUCCESS;
1232 } else {
1233 *OptionString = AllocateZeroPool (BufferSize);
1234 ASSERT (*OptionString);
1235
1236 *OptionString[0] = LEFT_CHECKBOX_DELIMITER;
1237
1238 if (QuestionValue->Value.b) {
1239 *(OptionString[0] + 1) = CHECK_ON;
1240 } else {
1241 *(OptionString[0] + 1) = CHECK_OFF;
1242 }
1243 *(OptionString[0] + 2) = RIGHT_CHECKBOX_DELIMITER;
1244 }
1245 break;
1246
1247 case EFI_IFR_NUMERIC_OP:
1248 if (Selected) {
1249 //
1250 // Go ask for input
1251 //
1252 Status = GetNumericInput (MenuOption);
1253 } else {
1254 *OptionString = AllocateZeroPool (BufferSize);
1255 ASSERT (*OptionString);
1256
1257 *OptionString[0] = LEFT_NUMERIC_DELIMITER;
1258
1259 //
1260 // Formatted print
1261 //
1262 PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
1263 Number = (UINT16) GetStringWidth (FormattedNumber);
1264 CopyMem (OptionString[0] + 1, FormattedNumber, Number);
1265
1266 *(OptionString[0] + Number / 2) = RIGHT_NUMERIC_DELIMITER;
1267 }
1268 break;
1269
1270 case EFI_IFR_DATE_OP:
1271 if (Selected) {
1272 //
1273 // This is similar to numerics
1274 //
1275 Status = GetNumericInput (MenuOption);
1276 } else {
1277 *OptionString = AllocateZeroPool (BufferSize);
1278 ASSERT (*OptionString);
1279
1280 switch (MenuOption->Sequence) {
1281 case 0:
1282 *OptionString[0] = LEFT_NUMERIC_DELIMITER;
1283 UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Month);
1284 *(OptionString[0] + 3) = DATE_SEPARATOR;
1285 break;
1286
1287 case 1:
1288 SetUnicodeMem (OptionString[0], 4, L' ');
1289 UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Day);
1290 *(OptionString[0] + 6) = DATE_SEPARATOR;
1291 break;
1292
1293 case 2:
1294 SetUnicodeMem (OptionString[0], 7, L' ');
1295 UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%04d", QuestionValue->Value.date.Year);
1296 *(OptionString[0] + 11) = RIGHT_NUMERIC_DELIMITER;
1297 break;
1298 }
1299 }
1300 break;
1301
1302 case EFI_IFR_TIME_OP:
1303 if (Selected) {
1304 //
1305 // This is similar to numerics
1306 //
1307 Status = GetNumericInput (MenuOption);
1308 } else {
1309 *OptionString = AllocateZeroPool (BufferSize);
1310 ASSERT (*OptionString);
1311
1312 switch (MenuOption->Sequence) {
1313 case 0:
1314 *OptionString[0] = LEFT_NUMERIC_DELIMITER;
1315 UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Hour);
1316 *(OptionString[0] + 3) = TIME_SEPARATOR;
1317 break;
1318
1319 case 1:
1320 SetUnicodeMem (OptionString[0], 4, L' ');
1321 UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Minute);
1322 *(OptionString[0] + 6) = TIME_SEPARATOR;
1323 break;
1324
1325 case 2:
1326 SetUnicodeMem (OptionString[0], 7, L' ');
1327 UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Second);
1328 *(OptionString[0] + 9) = RIGHT_NUMERIC_DELIMITER;
1329 break;
1330 }
1331 }
1332 break;
1333
1334 case EFI_IFR_STRING_OP:
1335 if (Selected) {
1336 StringPtr = AllocateZeroPool (Question->CurrentValue.BufferLen + sizeof (CHAR16));
1337 ASSERT (StringPtr);
1338 CopyMem(StringPtr, Question->CurrentValue.Buffer, Question->CurrentValue.BufferLen);
1339
1340 Status = ReadString (MenuOption, gPromptForData, StringPtr);
1341 if (EFI_ERROR (Status)) {
1342 FreePool (StringPtr);
1343 return Status;
1344 }
1345
1346 gUserInput->InputValue.Buffer = AllocateCopyPool (Question->CurrentValue.BufferLen, StringPtr);
1347 gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
1348 gUserInput->InputValue.Type = Question->CurrentValue.Type;
1349 gUserInput->InputValue.Value.string = HiiSetString(gFormData->HiiHandle, gUserInput->InputValue.Value.string, StringPtr, NULL);
1350 FreePool (StringPtr);
1351 return EFI_SUCCESS;
1352 } else {
1353 *OptionString = AllocateZeroPool (BufferSize);
1354 ASSERT (*OptionString);
1355
1356 if (((CHAR16 *) Question->CurrentValue.Buffer)[0] == 0x0000) {
1357 *(OptionString[0]) = '_';
1358 } else {
1359 if (Question->CurrentValue.BufferLen < BufferSize) {
1360 BufferSize = Question->CurrentValue.BufferLen;
1361 }
1362 CopyMem (OptionString[0], (CHAR16 *) Question->CurrentValue.Buffer, BufferSize);
1363 }
1364 }
1365 break;
1366
1367 case EFI_IFR_PASSWORD_OP:
1368 if (Selected) {
1369 Status = PasswordProcess (MenuOption);
1370 }
1371 break;
1372
1373 default:
1374 break;
1375 }
1376
1377 return Status;
1378 }
1379
1380
1381 /**
1382 Process the help string: Split StringPtr to several lines of strings stored in
1383 FormattedString and the glyph width of each line cannot exceed gHelpBlockWidth.
1384
1385 @param StringPtr The entire help string.
1386 @param FormattedString The oupput formatted string.
1387 @param EachLineWidth The max string length of each line in the formatted string.
1388 @param RowCount TRUE: if Question is selected.
1389
1390 **/
1391 UINTN
1392 ProcessHelpString (
1393 IN CHAR16 *StringPtr,
1394 OUT CHAR16 **FormattedString,
1395 OUT UINT16 *EachLineWidth,
1396 IN UINTN RowCount
1397 )
1398 {
1399 UINTN Index;
1400 CHAR16 *OutputString;
1401 UINTN TotalRowNum;
1402 UINTN CheckedNum;
1403 UINT16 GlyphWidth;
1404 UINT16 LineWidth;
1405 UINT16 MaxStringLen;
1406 UINT16 StringLen;
1407
1408 TotalRowNum = 0;
1409 CheckedNum = 0;
1410 GlyphWidth = 1;
1411 Index = 0;
1412 MaxStringLen = 0;
1413 StringLen = 0;
1414
1415 //
1416 // Set default help string width.
1417 //
1418 LineWidth = (UINT16) (gHelpBlockWidth - 1);
1419
1420 //
1421 // Get row number of the String.
1422 //
1423 while ((StringLen = GetLineByWidth (StringPtr, LineWidth, &GlyphWidth, &Index, &OutputString)) != 0) {
1424 if (StringLen > MaxStringLen) {
1425 MaxStringLen = StringLen;
1426 }
1427
1428 TotalRowNum ++;
1429 FreePool (OutputString);
1430 }
1431 *EachLineWidth = MaxStringLen;
1432
1433 *FormattedString = AllocateZeroPool (TotalRowNum * MaxStringLen * sizeof (CHAR16));
1434 ASSERT (*FormattedString != NULL);
1435
1436 //
1437 // Generate formatted help string array.
1438 //
1439 GlyphWidth = 1;
1440 Index = 0;
1441 while((StringLen = GetLineByWidth (StringPtr, LineWidth, &GlyphWidth, &Index, &OutputString)) != 0) {
1442 CopyMem (*FormattedString + CheckedNum * MaxStringLen, OutputString, StringLen * sizeof (CHAR16));
1443 CheckedNum ++;
1444 FreePool (OutputString);
1445 }
1446
1447 return TotalRowNum;
1448 }