VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/DisplayEngineDxe/ProcessOptions.c@ 75265

Last change on this file since 75265 was 58466, checked in by vboxsync, 9 years ago

EFI/Firmware: Merged in the svn:eol-style, svn:mime-type and trailing whitespace cleanup that was done after the initial UDK2014.SP1 import: svn merge /vendor/edk2/UDK2014.SP1 /vendor/edk2/current .

  • Property svn:eol-style set to native
File size: 38.6 KB
Line 
1/** @file
2Implementation for handling the User Interface option processing.
3
4
5Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.<BR>
6This program and the accompanying materials
7are licensed and made available under the terms and conditions of the BSD License
8which accompanies this distribution. The full text of the license may be found at
9http://opensource.org/licenses/bsd-license.php
10
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12WITHOUT 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**/
28VOID
29NewStrCat (
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**/
59UINT64
60HiiValueToUINT64 (
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**/
113BOOLEAN
114IsTypeInBuffer (
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**/
139BOOLEAN
140IsTypeInUINT64 (
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**/
168VOID
169GetBufAndLenForValue (
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**/
217EFI_STATUS
218CompareHiiValue (
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**/
316DISPLAY_QUESTION_OPTION *
317ValueToOption (
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**/
356UINT64
357GetArrayData (
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**/
402VOID
403SetArrayData (
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**/
447BOOLEAN
448FindArrayData (
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**/
511EFI_STATUS
512PrintFormattedNumber (
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**/
587VOID
588CreateSharedPopUp (
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**/
692VOID
693EFIAPI
694CreateMultiStringPopUp (
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**/
716VOID
717EFIAPI
718EmptyEventProcess (
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**/
732VOID
733EFIAPI
734RefreshTimeOutProcess (
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**/
762VOID
763PasswordInvalid (
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**/
786EFI_STATUS
787PasswordProcess (
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**/
933EFI_STATUS
934ProcessOptions (
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 gMisMatch = TRUE;
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 gMisMatch = TRUE;
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 gMisMatch = TRUE;
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**/
1391UINTN
1392ProcessHelpString (
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}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette