VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.c@ 108794

Last change on this file since 108794 was 108794, checked in by vboxsync, 2 weeks ago

Devices/EFI/FirmwareNew: Merge edk2-stable202502 from the vendor branch and make it build for the important platforms, bugref:4643

  • Property svn:eol-style set to native
File size: 63.6 KB
Line 
1/** @file
2 Provides interface to shell internal functions for shell commands.
3
4 Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
5 (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.<BR>
6 (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
7
8 SPDX-License-Identifier: BSD-2-Clause-Patent
9
10**/
11
12#include "UefiShellCommandLib.h"
13
14// STATIC local variables
15STATIC SHELL_COMMAND_INTERNAL_LIST_ENTRY mCommandList;
16STATIC SCRIPT_FILE_LIST mScriptList;
17STATIC ALIAS_LIST mAliasList;
18STATIC BOOLEAN mEchoState;
19STATIC BOOLEAN mExitRequested;
20STATIC UINT64 mExitCode;
21STATIC BOOLEAN mExitScript;
22STATIC CHAR16 *mProfileList;
23STATIC UINTN mProfileListSize;
24STATIC UINTN mFsMaxCount = 0;
25STATIC UINTN mBlkMaxCount = 0;
26STATIC BUFFER_LIST mFileHandleList;
27
28STATIC CONST CHAR8 Hex[] = {
29 '0',
30 '1',
31 '2',
32 '3',
33 '4',
34 '5',
35 '6',
36 '7',
37 '8',
38 '9',
39 'A',
40 'B',
41 'C',
42 'D',
43 'E',
44 'F'
45};
46
47// global variables required by library class.
48EFI_UNICODE_COLLATION_PROTOCOL *gUnicodeCollation = NULL;
49SHELL_MAP_LIST gShellMapList;
50SHELL_MAP_LIST *gShellCurMapping = NULL;
51
52CONST CHAR16 *SupportLevel[] = {
53 L"Minimal",
54 L"Scripting",
55 L"Basic",
56 L"Interactive"
57};
58
59/**
60 Function to make sure that the global protocol pointers are valid.
61 must be called after constructor before accessing the pointers.
62**/
63EFI_STATUS
64EFIAPI
65CommandInit (
66 VOID
67 )
68{
69 UINTN NumHandles;
70 EFI_HANDLE *Handles;
71 EFI_UNICODE_COLLATION_PROTOCOL *Uc;
72 CHAR8 *BestLanguage;
73 UINTN Index;
74 EFI_STATUS Status;
75 CHAR8 *PlatformLang;
76
77 if (gUnicodeCollation == NULL) {
78 GetEfiGlobalVariable2 (EFI_PLATFORM_LANG_VARIABLE_NAME, (VOID **)&PlatformLang, NULL);
79
80 Status = gBS->LocateHandleBuffer (
81 ByProtocol,
82 &gEfiUnicodeCollation2ProtocolGuid,
83 NULL,
84 &NumHandles,
85 &Handles
86 );
87 if (EFI_ERROR (Status)) {
88 NumHandles = 0;
89 Handles = NULL;
90 }
91
92 for (Index = 0; Index < NumHandles; Index++) {
93 //
94 // Open Unicode Collation Protocol
95 //
96 Status = gBS->OpenProtocol (
97 Handles[Index],
98 &gEfiUnicodeCollation2ProtocolGuid,
99 (VOID **)&Uc,
100 gImageHandle,
101 NULL,
102 EFI_OPEN_PROTOCOL_GET_PROTOCOL
103 );
104 if (EFI_ERROR (Status)) {
105 continue;
106 }
107
108 //
109 // Without clue provided use the first Unicode Collation2 protocol.
110 // This may happen when PlatformLang is NULL or when no installed Unicode
111 // Collation2 protocol instance supports PlatformLang.
112 //
113 if (gUnicodeCollation == NULL) {
114 gUnicodeCollation = Uc;
115 }
116
117 if (PlatformLang == NULL) {
118 break;
119 }
120
121 //
122 // Find the best matching matching language from the supported languages
123 // of Unicode Collation2 protocol.
124 //
125 BestLanguage = GetBestLanguage (
126 Uc->SupportedLanguages,
127 FALSE,
128 PlatformLang,
129 NULL
130 );
131 if (BestLanguage != NULL) {
132 FreePool (BestLanguage);
133 gUnicodeCollation = Uc;
134 break;
135 }
136 }
137
138 if (Handles != NULL) {
139 FreePool (Handles);
140 }
141
142 if (PlatformLang != NULL) {
143 FreePool (PlatformLang);
144 }
145 }
146
147 return (gUnicodeCollation == NULL) ? EFI_UNSUPPORTED : EFI_SUCCESS;
148}
149
150/**
151 Constructor for the Shell Command library.
152
153 Initialize the library and determine if the underlying is a UEFI Shell 2.0 or an EFI shell.
154
155 @param ImageHandle the image handle of the process
156 @param SystemTable the EFI System Table pointer
157
158 @retval EFI_SUCCESS the initialization was complete successfully
159**/
160RETURN_STATUS
161EFIAPI
162ShellCommandLibConstructor (
163 IN EFI_HANDLE ImageHandle,
164 IN EFI_SYSTEM_TABLE *SystemTable
165 )
166{
167 EFI_STATUS Status;
168
169 InitializeListHead (&gShellMapList.Link);
170 InitializeListHead (&mCommandList.Link);
171 InitializeListHead (&mAliasList.Link);
172 InitializeListHead (&mScriptList.Link);
173 InitializeListHead (&mFileHandleList.Link);
174 mEchoState = TRUE;
175
176 mExitRequested = FALSE;
177 mExitScript = FALSE;
178 mProfileListSize = 0;
179 mProfileList = NULL;
180
181 Status = CommandInit ();
182 if (EFI_ERROR (Status)) {
183 return EFI_DEVICE_ERROR;
184 }
185
186 return (RETURN_SUCCESS);
187}
188
189/**
190 Frees list of file handles.
191
192 @param[in] List The list to free.
193**/
194VOID
195FreeFileHandleList (
196 IN BUFFER_LIST *List
197 )
198{
199 BUFFER_LIST *BufferListEntry;
200
201 if (List == NULL) {
202 return;
203 }
204
205 //
206 // enumerate through the buffer list and free all memory
207 //
208 for ( BufferListEntry = (BUFFER_LIST *)GetFirstNode (&List->Link)
209 ; !IsListEmpty (&List->Link)
210 ; BufferListEntry = (BUFFER_LIST *)GetFirstNode (&List->Link)
211 )
212 {
213 RemoveEntryList (&BufferListEntry->Link);
214 ASSERT (BufferListEntry->Buffer != NULL);
215 SHELL_FREE_NON_NULL (((SHELL_COMMAND_FILE_HANDLE *)(BufferListEntry->Buffer))->Path);
216 SHELL_FREE_NON_NULL (BufferListEntry->Buffer);
217 SHELL_FREE_NON_NULL (BufferListEntry);
218 }
219}
220
221/**
222 Destructor for the library. free any resources.
223
224 @param ImageHandle the image handle of the process
225 @param SystemTable the EFI System Table pointer
226
227 @retval RETURN_SUCCESS this function always returns success
228**/
229RETURN_STATUS
230EFIAPI
231ShellCommandLibDestructor (
232 IN EFI_HANDLE ImageHandle,
233 IN EFI_SYSTEM_TABLE *SystemTable
234 )
235{
236 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
237 ALIAS_LIST *Node2;
238 SCRIPT_FILE_LIST *Node3;
239 SHELL_MAP_LIST *MapNode;
240
241 //
242 // enumerate throught the list and free all the memory
243 //
244 while (!IsListEmpty (&mCommandList.Link)) {
245 Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link);
246 RemoveEntryList (&Node->Link);
247 SHELL_FREE_NON_NULL (Node->CommandString);
248 FreePool (Node);
249 DEBUG_CODE (
250 Node = NULL;
251 );
252 }
253
254 //
255 // enumerate through the alias list and free all memory
256 //
257 while (!IsListEmpty (&mAliasList.Link)) {
258 Node2 = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link);
259 RemoveEntryList (&Node2->Link);
260 SHELL_FREE_NON_NULL (Node2->CommandString);
261 SHELL_FREE_NON_NULL (Node2->Alias);
262 SHELL_FREE_NON_NULL (Node2);
263 DEBUG_CODE (
264 Node2 = NULL;
265 );
266 }
267
268 //
269 // enumerate throught the list and free all the memory
270 //
271 while (!IsListEmpty (&mScriptList.Link)) {
272 Node3 = (SCRIPT_FILE_LIST *)GetFirstNode (&mScriptList.Link);
273 RemoveEntryList (&Node3->Link);
274 DeleteScriptFileStruct (Node3->Data);
275 FreePool (Node3);
276 }
277
278 //
279 // enumerate throught the mappings list and free all the memory
280 //
281 if (!IsListEmpty (&gShellMapList.Link)) {
282 for (MapNode = (SHELL_MAP_LIST *)GetFirstNode (&gShellMapList.Link)
283 ; !IsListEmpty (&gShellMapList.Link)
284 ; MapNode = (SHELL_MAP_LIST *)GetFirstNode (&gShellMapList.Link)
285 )
286 {
287 ASSERT (MapNode != NULL);
288 RemoveEntryList (&MapNode->Link);
289 SHELL_FREE_NON_NULL (MapNode->DevicePath);
290 SHELL_FREE_NON_NULL (MapNode->MapName);
291 SHELL_FREE_NON_NULL (MapNode->CurrentDirectoryPath);
292 FreePool (MapNode);
293 }
294 }
295
296 if (!IsListEmpty (&mFileHandleList.Link)) {
297 FreeFileHandleList (&mFileHandleList);
298 }
299
300 if (mProfileList != NULL) {
301 FreePool (mProfileList);
302 }
303
304 gUnicodeCollation = NULL;
305 gShellCurMapping = NULL;
306
307 return (RETURN_SUCCESS);
308}
309
310/**
311 Find a dynamic command protocol instance given a command name string.
312
313 @param CommandString the command name string
314
315 @return instance the command protocol instance, if dynamic command instance found
316 @retval NULL no dynamic command protocol instance found for name
317**/
318CONST EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *
319ShellCommandFindDynamicCommand (
320 IN CONST CHAR16 *CommandString
321 )
322{
323 EFI_STATUS Status;
324 EFI_HANDLE *CommandHandleList;
325 EFI_HANDLE *NextCommand;
326 EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *DynamicCommand;
327
328 CommandHandleList = GetHandleListByProtocol (&gEfiShellDynamicCommandProtocolGuid);
329 if (CommandHandleList == NULL) {
330 //
331 // not found or out of resources
332 //
333 return NULL;
334 }
335
336 for (NextCommand = CommandHandleList; *NextCommand != NULL; NextCommand++) {
337 Status = gBS->HandleProtocol (
338 *NextCommand,
339 &gEfiShellDynamicCommandProtocolGuid,
340 (VOID **)&DynamicCommand
341 );
342
343 if (EFI_ERROR (Status)) {
344 continue;
345 }
346
347 if (gUnicodeCollation->StriColl (
348 gUnicodeCollation,
349 (CHAR16 *)CommandString,
350 (CHAR16 *)DynamicCommand->CommandName
351 ) == 0
352 )
353 {
354 FreePool (CommandHandleList);
355 return (DynamicCommand);
356 }
357 }
358
359 FreePool (CommandHandleList);
360 return (NULL);
361}
362
363/**
364 Checks if a command exists as a dynamic command protocol instance
365
366 @param[in] CommandString The command string to check for on the list.
367**/
368BOOLEAN
369ShellCommandDynamicCommandExists (
370 IN CONST CHAR16 *CommandString
371 )
372{
373 return (BOOLEAN)((ShellCommandFindDynamicCommand (CommandString) != NULL));
374}
375
376/**
377 Checks if a command is already on the internal command list.
378
379 @param[in] CommandString The command string to check for on the list.
380**/
381BOOLEAN
382ShellCommandIsCommandOnInternalList (
383 IN CONST CHAR16 *CommandString
384 )
385{
386 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
387
388 //
389 // assert for NULL parameter
390 //
391 ASSERT (CommandString != NULL);
392
393 //
394 // check for the command
395 //
396 for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link)
397 ; !IsNull (&mCommandList.Link, &Node->Link)
398 ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Node->Link)
399 )
400 {
401 ASSERT (Node->CommandString != NULL);
402 if (gUnicodeCollation->StriColl (
403 gUnicodeCollation,
404 (CHAR16 *)CommandString,
405 Node->CommandString
406 ) == 0
407 )
408 {
409 return (TRUE);
410 }
411 }
412
413 return (FALSE);
414}
415
416/**
417 Checks if a command exists, either internally or through the dynamic command protocol.
418
419 @param[in] CommandString The command string to check for on the list.
420**/
421BOOLEAN
422EFIAPI
423ShellCommandIsCommandOnList (
424 IN CONST CHAR16 *CommandString
425 )
426{
427 if (ShellCommandIsCommandOnInternalList (CommandString)) {
428 return TRUE;
429 }
430
431 return ShellCommandDynamicCommandExists (CommandString);
432}
433
434/**
435 Get the help text for a dynamic command.
436
437 @param[in] CommandString The command name.
438
439 @retval NULL No help text was found.
440 @return String of help text. Caller required to free.
441**/
442CHAR16 *
443ShellCommandGetDynamicCommandHelp (
444 IN CONST CHAR16 *CommandString
445 )
446{
447 EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *DynamicCommand;
448
449 DynamicCommand = (EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *)ShellCommandFindDynamicCommand (CommandString);
450 if (DynamicCommand == NULL) {
451 return (NULL);
452 }
453
454 //
455 // TODO: how to get proper language?
456 //
457 return DynamicCommand->GetHelp (DynamicCommand, "en");
458}
459
460/**
461 Get the help text for an internal command.
462
463 @param[in] CommandString The command name.
464
465 @retval NULL No help text was found.
466 @return String of help text. Caller reuiqred to free.
467**/
468CHAR16 *
469ShellCommandGetInternalCommandHelp (
470 IN CONST CHAR16 *CommandString
471 )
472{
473 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
474
475 //
476 // assert for NULL parameter
477 //
478 ASSERT (CommandString != NULL);
479
480 //
481 // check for the command
482 //
483 for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link)
484 ; !IsNull (&mCommandList.Link, &Node->Link)
485 ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Node->Link)
486 )
487 {
488 ASSERT (Node->CommandString != NULL);
489 if (gUnicodeCollation->StriColl (
490 gUnicodeCollation,
491 (CHAR16 *)CommandString,
492 Node->CommandString
493 ) == 0
494 )
495 {
496 return (HiiGetString (Node->HiiHandle, Node->ManFormatHelp, NULL));
497 }
498 }
499
500 return (NULL);
501}
502
503/**
504 Get the help text for a command.
505
506 @param[in] CommandString The command name.
507
508 @retval NULL No help text was found.
509 @return String of help text.Caller reuiqred to free.
510**/
511CHAR16 *
512EFIAPI
513ShellCommandGetCommandHelp (
514 IN CONST CHAR16 *CommandString
515 )
516{
517 CHAR16 *HelpStr;
518
519 HelpStr = ShellCommandGetInternalCommandHelp (CommandString);
520
521 if (HelpStr == NULL) {
522 HelpStr = ShellCommandGetDynamicCommandHelp (CommandString);
523 }
524
525 return HelpStr;
526}
527
528/**
529 Registers handlers of type SHELL_RUN_COMMAND and
530 SHELL_GET_MAN_FILENAME for each shell command.
531
532 If the ShellSupportLevel is greater than the value of the
533 PcdShellSupportLevel then return RETURN_UNSUPPORTED.
534
535 Registers the handlers specified by GetHelpInfoHandler and CommandHandler
536 with the command specified by CommandString. If the command named by
537 CommandString has already been registered, then return
538 RETURN_ALREADY_STARTED.
539
540 If there are not enough resources available to register the handlers then
541 RETURN_OUT_OF_RESOURCES is returned.
542
543 If CommandString is NULL, then ASSERT().
544 If GetHelpInfoHandler is NULL, then ASSERT().
545 If CommandHandler is NULL, then ASSERT().
546 If ProfileName is NULL, then ASSERT().
547
548 @param[in] CommandString Pointer to the command name. This is the
549 name to look for on the command line in
550 the shell.
551 @param[in] CommandHandler Pointer to a function that runs the
552 specified command.
553 @param[in] GetManFileName Pointer to a function that provides man
554 filename.
555 @param[in] ShellMinSupportLevel minimum Shell Support Level which has this
556 function.
557 @param[in] ProfileName profile name to require for support of this
558 function.
559 @param[in] CanAffectLE indicates whether this command's return value
560 can change the LASTERROR environment variable.
561 @param[in] HiiHandle Handle of this command's HII entry.
562 @param[in] ManFormatHelp HII locator for the help text.
563
564 @retval RETURN_SUCCESS The handlers were registered.
565 @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to
566 register the shell command.
567 @retval RETURN_UNSUPPORTED the ShellMinSupportLevel was higher than the
568 currently allowed support level.
569 @retval RETURN_ALREADY_STARTED The CommandString represents a command that
570 is already registered. Only 1 handler set for
571 a given command is allowed.
572 @sa SHELL_GET_MAN_FILENAME
573 @sa SHELL_RUN_COMMAND
574**/
575RETURN_STATUS
576EFIAPI
577ShellCommandRegisterCommandName (
578 IN CONST CHAR16 *CommandString,
579 IN SHELL_RUN_COMMAND CommandHandler,
580 IN SHELL_GET_MAN_FILENAME GetManFileName,
581 IN UINT32 ShellMinSupportLevel,
582 IN CONST CHAR16 *ProfileName,
583 IN CONST BOOLEAN CanAffectLE,
584 IN CONST EFI_HII_HANDLE HiiHandle,
585 IN CONST EFI_STRING_ID ManFormatHelp
586 )
587{
588 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
589 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Command;
590 SHELL_COMMAND_INTERNAL_LIST_ENTRY *PrevCommand;
591 INTN LexicalMatchValue;
592
593 //
594 // Initialize local variables.
595 //
596 Command = NULL;
597 PrevCommand = NULL;
598 LexicalMatchValue = 0;
599
600 //
601 // ASSERTs for NULL parameters
602 //
603 ASSERT (CommandString != NULL);
604 ASSERT (GetManFileName != NULL);
605 ASSERT (CommandHandler != NULL);
606 ASSERT (ProfileName != NULL);
607
608 //
609 // check for shell support level
610 //
611 if (PcdGet8 (PcdShellSupportLevel) < ShellMinSupportLevel) {
612 return (RETURN_UNSUPPORTED);
613 }
614
615 //
616 // check for already on the list
617 //
618 if (ShellCommandIsCommandOnList (CommandString)) {
619 return (RETURN_ALREADY_STARTED);
620 }
621
622 //
623 // allocate memory for new struct
624 //
625 Node = AllocateZeroPool (sizeof (SHELL_COMMAND_INTERNAL_LIST_ENTRY));
626 if (Node == NULL) {
627 return RETURN_OUT_OF_RESOURCES;
628 }
629
630 Node->CommandString = AllocateCopyPool (StrSize (CommandString), CommandString);
631 if (Node->CommandString == NULL) {
632 FreePool (Node);
633 return RETURN_OUT_OF_RESOURCES;
634 }
635
636 Node->GetManFileName = GetManFileName;
637 Node->CommandHandler = CommandHandler;
638 Node->LastError = CanAffectLE;
639 Node->HiiHandle = HiiHandle;
640 Node->ManFormatHelp = ManFormatHelp;
641
642 if ( (StrLen (ProfileName) > 0)
643 && (( (mProfileList != NULL)
644 && (StrStr (mProfileList, ProfileName) == NULL)) || (mProfileList == NULL))
645 )
646 {
647 ASSERT ((mProfileList == NULL && mProfileListSize == 0) || (mProfileList != NULL));
648 if (mProfileList == NULL) {
649 //
650 // If this is the first make a leading ';'
651 //
652 StrnCatGrow (&mProfileList, &mProfileListSize, L";", 0);
653 }
654
655 StrnCatGrow (&mProfileList, &mProfileListSize, ProfileName, 0);
656 StrnCatGrow (&mProfileList, &mProfileListSize, L";", 0);
657 }
658
659 //
660 // Insert a new entry on top of the list
661 //
662 InsertHeadList (&mCommandList.Link, &Node->Link);
663
664 //
665 // Move a new registered command to its sorted ordered location in the list
666 //
667 for (Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link),
668 PrevCommand = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link)
669 ; !IsNull (&mCommandList.Link, &Command->Link)
670 ; Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Command->Link))
671 {
672 //
673 // Get Lexical Comparison Value between PrevCommand and Command list entry
674 //
675 LexicalMatchValue = gUnicodeCollation->StriColl (
676 gUnicodeCollation,
677 PrevCommand->CommandString,
678 Command->CommandString
679 );
680
681 //
682 // Swap PrevCommand and Command list entry if PrevCommand list entry
683 // is alphabetically greater than Command list entry
684 //
685 if (LexicalMatchValue > 0) {
686 Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)SwapListEntries (&PrevCommand->Link, &Command->Link);
687 } else if (LexicalMatchValue < 0) {
688 //
689 // PrevCommand entry is lexically lower than Command entry
690 //
691 break;
692 }
693 }
694
695 return (RETURN_SUCCESS);
696}
697
698/**
699 Function to get the current Profile string.
700
701 @retval NULL There are no installed profiles.
702 @return A semi-colon delimited list of profiles.
703**/
704CONST CHAR16 *
705EFIAPI
706ShellCommandGetProfileList (
707 VOID
708 )
709{
710 return (mProfileList);
711}
712
713/**
714 Checks if a command string has been registered for CommandString and if so it runs
715 the previously registered handler for that command with the command line.
716
717 If CommandString is NULL, then ASSERT().
718
719 If Sections is specified, then each section name listed will be compared in a casesensitive
720 manner, to the section names described in Appendix B UEFI Shell 2.0 spec. If the section exists,
721 it will be appended to the returned help text. If the section does not exist, no
722 information will be returned. If Sections is NULL, then all help text information
723 available will be returned.
724
725 @param[in] CommandString Pointer to the command name. This is the name
726 found on the command line in the shell.
727 @param[in, out] RetVal Pointer to the return vaule from the command handler.
728
729 @param[in, out] CanAffectLE indicates whether this command's return value
730 needs to be placed into LASTERROR environment variable.
731
732 @retval RETURN_SUCCESS The handler was run.
733 @retval RETURN_NOT_FOUND The CommandString did not match a registered
734 command name.
735 @sa SHELL_RUN_COMMAND
736**/
737RETURN_STATUS
738EFIAPI
739ShellCommandRunCommandHandler (
740 IN CONST CHAR16 *CommandString,
741 IN OUT SHELL_STATUS *RetVal,
742 IN OUT BOOLEAN *CanAffectLE OPTIONAL
743 )
744{
745 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
746 EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *DynamicCommand;
747
748 //
749 // assert for NULL parameters
750 //
751 ASSERT (CommandString != NULL);
752
753 //
754 // check for the command
755 //
756 for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link)
757 ; !IsNull (&mCommandList.Link, &Node->Link)
758 ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Node->Link)
759 )
760 {
761 ASSERT (Node->CommandString != NULL);
762 if (gUnicodeCollation->StriColl (
763 gUnicodeCollation,
764 (CHAR16 *)CommandString,
765 Node->CommandString
766 ) == 0
767 )
768 {
769 if (CanAffectLE != NULL) {
770 *CanAffectLE = Node->LastError;
771 }
772
773 if (RetVal != NULL) {
774 *RetVal = Node->CommandHandler (NULL, gST);
775 } else {
776 Node->CommandHandler (NULL, gST);
777 }
778
779 return (RETURN_SUCCESS);
780 }
781 }
782
783 //
784 // An internal command was not found, try to find a dynamic command
785 //
786 DynamicCommand = (EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *)ShellCommandFindDynamicCommand (CommandString);
787 if (DynamicCommand != NULL) {
788 if (RetVal != NULL) {
789 *RetVal = DynamicCommand->Handler (DynamicCommand, gST, gEfiShellParametersProtocol, gEfiShellProtocol);
790 } else {
791 DynamicCommand->Handler (DynamicCommand, gST, gEfiShellParametersProtocol, gEfiShellProtocol);
792 }
793
794 return (RETURN_SUCCESS);
795 }
796
797 return (RETURN_NOT_FOUND);
798}
799
800/**
801 Checks if a command string has been registered for CommandString and if so it
802 returns the MAN filename specified for that command.
803
804 If CommandString is NULL, then ASSERT().
805
806 @param[in] CommandString Pointer to the command name. This is the name
807 found on the command line in the shell.\
808
809 @retval NULL the commandString was not a registered command.
810 @return other the name of the MAN file.
811 @sa SHELL_GET_MAN_FILENAME
812**/
813CONST CHAR16 *
814EFIAPI
815ShellCommandGetManFileNameHandler (
816 IN CONST CHAR16 *CommandString
817 )
818{
819 SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
820
821 //
822 // assert for NULL parameters
823 //
824 ASSERT (CommandString != NULL);
825
826 //
827 // check for the command
828 //
829 for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link)
830 ; !IsNull (&mCommandList.Link, &Node->Link)
831 ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Node->Link)
832 )
833 {
834 ASSERT (Node->CommandString != NULL);
835 if (gUnicodeCollation->StriColl (
836 gUnicodeCollation,
837 (CHAR16 *)CommandString,
838 Node->CommandString
839 ) == 0
840 )
841 {
842 return (Node->GetManFileName ());
843 }
844 }
845
846 return (NULL);
847}
848
849/**
850 Get the list of all available shell internal commands. This is a linked list
851 (via LIST_ENTRY structure). enumerate through it using the BaseLib linked
852 list functions. do not modify the values.
853
854 @param[in] Sort TRUE to alphabetically sort the values first. FALSE otherwise.
855
856 @return a Linked list of all available shell commands.
857**/
858CONST COMMAND_LIST *
859EFIAPI
860ShellCommandGetCommandList (
861 IN CONST BOOLEAN Sort
862 )
863{
864 // if (!Sort) {
865 // return ((COMMAND_LIST*)(&mCommandList));
866 // }
867 return ((COMMAND_LIST *)(&mCommandList));
868}
869
870/**
871 Registers aliases to be set as part of the initialization of the shell application.
872
873 If Command is NULL, then ASSERT().
874 If Alias is NULL, then ASSERT().
875
876 @param[in] Command Pointer to the Command
877 @param[in] Alias Pointer to Alias
878
879 @retval RETURN_SUCCESS The handlers were registered.
880 @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to
881 register the shell command.
882**/
883RETURN_STATUS
884EFIAPI
885ShellCommandRegisterAlias (
886 IN CONST CHAR16 *Command,
887 IN CONST CHAR16 *Alias
888 )
889{
890 ALIAS_LIST *Node;
891 ALIAS_LIST *CommandAlias;
892 ALIAS_LIST *PrevCommandAlias;
893 INTN LexicalMatchValue;
894
895 //
896 // Asserts for NULL
897 //
898 ASSERT (Command != NULL);
899 ASSERT (Alias != NULL);
900
901 //
902 // allocate memory for new struct
903 //
904 Node = AllocateZeroPool (sizeof (ALIAS_LIST));
905 if (Node == NULL) {
906 return RETURN_OUT_OF_RESOURCES;
907 }
908
909 Node->CommandString = AllocateCopyPool (StrSize (Command), Command);
910 if (Node->CommandString == NULL) {
911 FreePool (Node);
912 return RETURN_OUT_OF_RESOURCES;
913 }
914
915 Node->Alias = AllocateCopyPool (StrSize (Alias), Alias);
916 if (Node->Alias == NULL) {
917 FreePool (Node->CommandString);
918 FreePool (Node);
919 return RETURN_OUT_OF_RESOURCES;
920 }
921
922 InsertHeadList (&mAliasList.Link, &Node->Link);
923
924 //
925 // Move a new pre-defined registered alias to its sorted ordered location in the list
926 //
927 for ( CommandAlias = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link),
928 PrevCommandAlias = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link)
929 ; !IsNull (&mAliasList.Link, &CommandAlias->Link)
930 ; CommandAlias = (ALIAS_LIST *)GetNextNode (&mAliasList.Link, &CommandAlias->Link))
931 {
932 //
933 // Get Lexical comparison value between PrevCommandAlias and CommandAlias List Entry
934 //
935 LexicalMatchValue = gUnicodeCollation->StriColl (
936 gUnicodeCollation,
937 PrevCommandAlias->Alias,
938 CommandAlias->Alias
939 );
940
941 //
942 // Swap PrevCommandAlias and CommandAlias list entry if PrevCommandAlias list entry
943 // is alphabetically greater than CommandAlias list entry
944 //
945 if (LexicalMatchValue > 0) {
946 CommandAlias = (ALIAS_LIST *)SwapListEntries (&PrevCommandAlias->Link, &CommandAlias->Link);
947 } else if (LexicalMatchValue < 0) {
948 //
949 // PrevCommandAlias entry is lexically lower than CommandAlias entry
950 //
951 break;
952 }
953 }
954
955 return (RETURN_SUCCESS);
956}
957
958/**
959 Get the list of all shell alias commands. This is a linked list
960 (via LIST_ENTRY structure). enumerate through it using the BaseLib linked
961 list functions. do not modify the values.
962
963 @return a Linked list of all requested shell alias'.
964**/
965CONST ALIAS_LIST *
966EFIAPI
967ShellCommandGetInitAliasList (
968 VOID
969 )
970{
971 return (&mAliasList);
972}
973
974/**
975 Determine if a given alias is on the list of built in alias'.
976
977 @param[in] Alias The alias to test for
978
979 @retval TRUE The alias is a built in alias
980 @retval FALSE The alias is not a built in alias
981**/
982BOOLEAN
983EFIAPI
984ShellCommandIsOnAliasList (
985 IN CONST CHAR16 *Alias
986 )
987{
988 ALIAS_LIST *Node;
989
990 //
991 // assert for NULL parameter
992 //
993 ASSERT (Alias != NULL);
994
995 //
996 // check for the Alias
997 //
998 for ( Node = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link)
999 ; !IsNull (&mAliasList.Link, &Node->Link)
1000 ; Node = (ALIAS_LIST *)GetNextNode (&mAliasList.Link, &Node->Link)
1001 )
1002 {
1003 ASSERT (Node->CommandString != NULL);
1004 ASSERT (Node->Alias != NULL);
1005 if (gUnicodeCollation->StriColl (
1006 gUnicodeCollation,
1007 (CHAR16 *)Alias,
1008 Node->CommandString
1009 ) == 0
1010 )
1011 {
1012 return (TRUE);
1013 }
1014
1015 if (gUnicodeCollation->StriColl (
1016 gUnicodeCollation,
1017 (CHAR16 *)Alias,
1018 Node->Alias
1019 ) == 0
1020 )
1021 {
1022 return (TRUE);
1023 }
1024 }
1025
1026 return (FALSE);
1027}
1028
1029/**
1030 Function to determine current state of ECHO. Echo determines if lines from scripts
1031 and ECHO commands are enabled.
1032
1033 @retval TRUE Echo is currently enabled
1034 @retval FALSE Echo is currently disabled
1035**/
1036BOOLEAN
1037EFIAPI
1038ShellCommandGetEchoState (
1039 VOID
1040 )
1041{
1042 return (mEchoState);
1043}
1044
1045/**
1046 Function to set current state of ECHO. Echo determines if lines from scripts
1047 and ECHO commands are enabled.
1048
1049 If State is TRUE, Echo will be enabled.
1050 If State is FALSE, Echo will be disabled.
1051
1052 @param[in] State How to set echo.
1053**/
1054VOID
1055EFIAPI
1056ShellCommandSetEchoState (
1057 IN BOOLEAN State
1058 )
1059{
1060 mEchoState = State;
1061}
1062
1063/**
1064 Indicate that the current shell or script should exit.
1065
1066 @param[in] ScriptOnly TRUE if exiting a script; FALSE otherwise.
1067 @param[in] ErrorCode The 64 bit error code to return.
1068**/
1069VOID
1070EFIAPI
1071ShellCommandRegisterExit (
1072 IN BOOLEAN ScriptOnly,
1073 IN CONST UINT64 ErrorCode
1074 )
1075{
1076 mExitRequested = (BOOLEAN)(!mExitRequested);
1077 if (mExitRequested) {
1078 mExitScript = ScriptOnly;
1079 } else {
1080 mExitScript = FALSE;
1081 }
1082
1083 mExitCode = ErrorCode;
1084}
1085
1086/**
1087 Retrieve the Exit indicator.
1088
1089 @retval TRUE Exit was indicated.
1090 @retval FALSE Exis was not indicated.
1091**/
1092BOOLEAN
1093EFIAPI
1094ShellCommandGetExit (
1095 VOID
1096 )
1097{
1098 return (mExitRequested);
1099}
1100
1101/**
1102 Retrieve the Exit code.
1103
1104 If ShellCommandGetExit returns FALSE than the return from this is undefined.
1105
1106 @return the value passed into RegisterExit.
1107**/
1108UINT64
1109EFIAPI
1110ShellCommandGetExitCode (
1111 VOID
1112 )
1113{
1114 return (mExitCode);
1115}
1116
1117/**
1118 Retrieve the Exit script indicator.
1119
1120 If ShellCommandGetExit returns FALSE than the return from this is undefined.
1121
1122 @retval TRUE ScriptOnly was indicated.
1123 @retval FALSE ScriptOnly was not indicated.
1124**/
1125BOOLEAN
1126EFIAPI
1127ShellCommandGetScriptExit (
1128 VOID
1129 )
1130{
1131 return (mExitScript);
1132}
1133
1134/**
1135 Function to cleanup all memory from a SCRIPT_FILE structure.
1136
1137 @param[in] Script The pointer to the structure to cleanup.
1138**/
1139VOID
1140EFIAPI
1141DeleteScriptFileStruct (
1142 IN SCRIPT_FILE *Script
1143 )
1144{
1145 UINTN LoopVar;
1146
1147 if (Script == NULL) {
1148 return;
1149 }
1150
1151 for (LoopVar = 0; LoopVar < Script->Argc; LoopVar++) {
1152 SHELL_FREE_NON_NULL (Script->Argv[LoopVar]);
1153 }
1154
1155 if (Script->Argv != NULL) {
1156 SHELL_FREE_NON_NULL (Script->Argv);
1157 }
1158
1159 Script->CurrentCommand = NULL;
1160 while (!IsListEmpty (&Script->CommandList)) {
1161 Script->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode (&Script->CommandList);
1162 if (Script->CurrentCommand != NULL) {
1163 RemoveEntryList (&Script->CurrentCommand->Link);
1164 if (Script->CurrentCommand->Cl != NULL) {
1165 SHELL_FREE_NON_NULL (Script->CurrentCommand->Cl);
1166 }
1167
1168 if (Script->CurrentCommand->Data != NULL) {
1169 SHELL_FREE_NON_NULL (Script->CurrentCommand->Data);
1170 }
1171
1172 SHELL_FREE_NON_NULL (Script->CurrentCommand);
1173 }
1174 }
1175
1176 SHELL_FREE_NON_NULL (Script->ScriptName);
1177 SHELL_FREE_NON_NULL (Script);
1178}
1179
1180/**
1181 Function to return a pointer to the currently running script file object.
1182
1183 @retval NULL A script file is not currently running.
1184 @return A pointer to the current script file object.
1185**/
1186SCRIPT_FILE *
1187EFIAPI
1188ShellCommandGetCurrentScriptFile (
1189 VOID
1190 )
1191{
1192 SCRIPT_FILE_LIST *List;
1193
1194 if (IsListEmpty (&mScriptList.Link)) {
1195 return (NULL);
1196 }
1197
1198 List = ((SCRIPT_FILE_LIST *)GetFirstNode (&mScriptList.Link));
1199 return (List->Data);
1200}
1201
1202/**
1203 Function to set a new script as the currently running one.
1204
1205 This function will correctly stack and unstack nested scripts.
1206
1207 @param[in] Script Pointer to new script information structure. if NULL
1208 will remove and de-allocate the top-most Script structure.
1209
1210 @return A pointer to the current running script file after this
1211 change. NULL if removing the final script.
1212**/
1213SCRIPT_FILE *
1214EFIAPI
1215ShellCommandSetNewScript (
1216 IN SCRIPT_FILE *Script OPTIONAL
1217 )
1218{
1219 SCRIPT_FILE_LIST *Node;
1220
1221 if (Script == NULL) {
1222 if (IsListEmpty (&mScriptList.Link)) {
1223 return (NULL);
1224 }
1225
1226 Node = (SCRIPT_FILE_LIST *)GetFirstNode (&mScriptList.Link);
1227 RemoveEntryList (&Node->Link);
1228 DeleteScriptFileStruct (Node->Data);
1229 FreePool (Node);
1230 } else {
1231 Node = AllocateZeroPool (sizeof (SCRIPT_FILE_LIST));
1232 if (Node == NULL) {
1233 return (NULL);
1234 }
1235
1236 Node->Data = Script;
1237 InsertHeadList (&mScriptList.Link, &Node->Link);
1238 }
1239
1240 return (ShellCommandGetCurrentScriptFile ());
1241}
1242
1243/**
1244 Function to generate the next default mapping name.
1245
1246 If the return value is not NULL then it must be callee freed.
1247
1248 @param Type What kind of mapping name to make.
1249
1250 @retval NULL a memory allocation failed.
1251 @return a new map name string
1252**/
1253CHAR16 *
1254EFIAPI
1255ShellCommandCreateNewMappingName (
1256 IN CONST SHELL_MAPPING_TYPE Type
1257 )
1258{
1259 CHAR16 *String;
1260
1261 ASSERT (Type < MappingTypeMax);
1262
1263 String = NULL;
1264
1265 String = AllocateZeroPool (PcdGet8 (PcdShellMapNameLength) * sizeof (String[0]));
1266 if (String == NULL) {
1267 return (NULL);
1268 }
1269
1270 UnicodeSPrint (
1271 String,
1272 PcdGet8 (PcdShellMapNameLength) * sizeof (String[0]),
1273 Type == MappingTypeFileSystem ? L"FS%d:" : L"BLK%d:",
1274 Type == MappingTypeFileSystem ? mFsMaxCount++ : mBlkMaxCount++
1275 );
1276
1277 return (String);
1278}
1279
1280/**
1281 Function to add a map node to the list of map items and update the "path" environment variable (optionally).
1282
1283 If Path is TRUE (during initialization only), the path environment variable will also be updated to include
1284 default paths on the new map name...
1285
1286 Path should be FALSE when this function is called from the protocol SetMap function.
1287
1288 @param[in] Name The human readable mapped name.
1289 @param[in] DevicePath The Device Path for this map.
1290 @param[in] Flags The Flags attribute for this map item.
1291 @param[in] Path TRUE to update path, FALSE to skip this step (should only be TRUE during initialization).
1292
1293 @retval EFI_SUCCESS The addition was successful.
1294 @retval EFI_OUT_OF_RESOURCES A memory allocation failed.
1295 @retval EFI_INVALID_PARAMETER A parameter was invalid.
1296**/
1297EFI_STATUS
1298EFIAPI
1299ShellCommandAddMapItemAndUpdatePath (
1300 IN CONST CHAR16 *Name,
1301 IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
1302 IN CONST UINT64 Flags,
1303 IN CONST BOOLEAN Path
1304 )
1305{
1306 EFI_STATUS Status;
1307 SHELL_MAP_LIST *MapListNode;
1308 CONST CHAR16 *OriginalPath;
1309 CHAR16 *NewPath;
1310 UINTN NewPathSize;
1311
1312 NewPathSize = 0;
1313 NewPath = NULL;
1314 OriginalPath = NULL;
1315 Status = EFI_SUCCESS;
1316
1317 MapListNode = AllocateZeroPool (sizeof (SHELL_MAP_LIST));
1318 if (MapListNode == NULL) {
1319 Status = EFI_OUT_OF_RESOURCES;
1320 } else {
1321 MapListNode->Flags = Flags;
1322 MapListNode->MapName = AllocateCopyPool (StrSize (Name), Name);
1323 MapListNode->DevicePath = DuplicateDevicePath (DevicePath);
1324 if ((MapListNode->MapName == NULL) || (MapListNode->DevicePath == NULL)) {
1325 Status = EFI_OUT_OF_RESOURCES;
1326 } else {
1327 InsertTailList (&gShellMapList.Link, &MapListNode->Link);
1328 }
1329 }
1330
1331 if (EFI_ERROR (Status)) {
1332 if (MapListNode != NULL) {
1333 if (MapListNode->DevicePath != NULL) {
1334 FreePool (MapListNode->DevicePath);
1335 }
1336
1337 if (MapListNode->MapName != NULL) {
1338 FreePool (MapListNode->MapName);
1339 }
1340
1341 FreePool (MapListNode);
1342 }
1343 } else if (Path) {
1344 //
1345 // Since there was no error and Path was TRUE
1346 // Now add the correct path for that mapping
1347 //
1348 OriginalPath = gEfiShellProtocol->GetEnv (L"path");
1349 ASSERT ((NewPath == NULL && NewPathSize == 0) || (NewPath != NULL));
1350 if (OriginalPath != NULL) {
1351 StrnCatGrow (&NewPath, &NewPathSize, OriginalPath, 0);
1352 StrnCatGrow (&NewPath, &NewPathSize, L";", 0);
1353 }
1354
1355 StrnCatGrow (&NewPath, &NewPathSize, Name, 0);
1356 StrnCatGrow (&NewPath, &NewPathSize, L"\\efi\\tools\\;", 0);
1357 StrnCatGrow (&NewPath, &NewPathSize, Name, 0);
1358 StrnCatGrow (&NewPath, &NewPathSize, L"\\efi\\boot\\;", 0);
1359 StrnCatGrow (&NewPath, &NewPathSize, Name, 0);
1360 StrnCatGrow (&NewPath, &NewPathSize, L"\\", 0);
1361
1362 Status = gEfiShellProtocol->SetEnv (L"path", NewPath, TRUE);
1363 ASSERT_EFI_ERROR (Status);
1364 FreePool (NewPath);
1365 }
1366
1367 return (Status);
1368}
1369
1370/**
1371 Creates the default map names for each device path in the system with
1372 a protocol depending on the Type.
1373
1374 Creates the consistent map names for each device path in the system with
1375 a protocol depending on the Type.
1376
1377 Note: This will reset all mappings in the system("map -r").
1378
1379 Also sets up the default path environment variable if Type is FileSystem.
1380
1381 @retval EFI_SUCCESS All map names were created successfully.
1382 @retval EFI_NOT_FOUND No protocols were found in the system.
1383 @return Error returned from gBS->LocateHandle().
1384
1385 @sa LocateHandle
1386**/
1387EFI_STATUS
1388EFIAPI
1389ShellCommandCreateInitialMappingsAndPaths (
1390 VOID
1391 )
1392{
1393 EFI_STATUS Status;
1394 EFI_HANDLE *HandleList;
1395 UINTN Count;
1396 EFI_DEVICE_PATH_PROTOCOL **DevicePathList;
1397 CHAR16 *NewDefaultName;
1398 CHAR16 *NewConsistName;
1399 EFI_DEVICE_PATH_PROTOCOL **ConsistMappingTable;
1400 SHELL_MAP_LIST *MapListNode;
1401 CONST CHAR16 *CurDir;
1402 CHAR16 *SplitCurDir;
1403 CHAR16 *MapName;
1404 SHELL_MAP_LIST *MapListItem;
1405
1406 ConsistMappingTable = NULL;
1407 SplitCurDir = NULL;
1408 MapName = NULL;
1409 MapListItem = NULL;
1410 HandleList = NULL;
1411
1412 //
1413 // Reset the static members back to zero
1414 //
1415 mFsMaxCount = 0;
1416 mBlkMaxCount = 0;
1417
1418 gEfiShellProtocol->SetEnv (L"path", L"", TRUE);
1419
1420 //
1421 // First empty out the existing list.
1422 //
1423 if (!IsListEmpty (&gShellMapList.Link)) {
1424 for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode (&gShellMapList.Link)
1425 ; !IsListEmpty (&gShellMapList.Link)
1426 ; MapListNode = (SHELL_MAP_LIST *)GetFirstNode (&gShellMapList.Link)
1427 )
1428 {
1429 RemoveEntryList (&MapListNode->Link);
1430 SHELL_FREE_NON_NULL (MapListNode->DevicePath);
1431 SHELL_FREE_NON_NULL (MapListNode->MapName);
1432 SHELL_FREE_NON_NULL (MapListNode->CurrentDirectoryPath);
1433 FreePool (MapListNode);
1434 } // for loop
1435 }
1436
1437 //
1438 // Find each handle with Simple File System
1439 //
1440 HandleList = GetHandleListByProtocol (&gEfiSimpleFileSystemProtocolGuid);
1441 if (HandleList != NULL) {
1442 //
1443 // Do a count of the handles
1444 //
1445 for (Count = 0; HandleList[Count] != NULL; Count++) {
1446 }
1447
1448 //
1449 // Get all Device Paths
1450 //
1451 DevicePathList = AllocateZeroPool (sizeof (EFI_DEVICE_PATH_PROTOCOL *) * Count);
1452 if (DevicePathList == NULL) {
1453 SHELL_FREE_NON_NULL (HandleList);
1454 return EFI_OUT_OF_RESOURCES;
1455 }
1456
1457 for (Count = 0; HandleList[Count] != NULL; Count++) {
1458 DevicePathList[Count] = DevicePathFromHandle (HandleList[Count]);
1459 }
1460
1461 //
1462 // Sort all DevicePaths
1463 //
1464 PerformQuickSort (DevicePathList, Count, sizeof (EFI_DEVICE_PATH_PROTOCOL *), DevicePathCompare);
1465
1466 Status = ShellCommandConsistMappingInitialize (&ConsistMappingTable);
1467 if (EFI_ERROR (Status)) {
1468 SHELL_FREE_NON_NULL (HandleList);
1469 SHELL_FREE_NON_NULL (DevicePathList);
1470 return Status;
1471 }
1472
1473 //
1474 // Assign new Mappings to all...
1475 //
1476 for (Count = 0; HandleList[Count] != NULL; Count++) {
1477 //
1478 // Get default name first
1479 //
1480 NewDefaultName = ShellCommandCreateNewMappingName (MappingTypeFileSystem);
1481 if (NewDefaultName == NULL) {
1482 ASSERT (NewDefaultName != NULL);
1483 Status = EFI_OUT_OF_RESOURCES;
1484 break;
1485 }
1486
1487 Status = ShellCommandAddMapItemAndUpdatePath (NewDefaultName, DevicePathList[Count], 0, TRUE);
1488 ASSERT_EFI_ERROR (Status);
1489 FreePool (NewDefaultName);
1490
1491 //
1492 // Now do consistent name
1493 //
1494 NewConsistName = ShellCommandConsistMappingGenMappingName (DevicePathList[Count], ConsistMappingTable);
1495 if (NewConsistName != NULL) {
1496 Status = ShellCommandAddMapItemAndUpdatePath (NewConsistName, DevicePathList[Count], 0, FALSE);
1497 ASSERT_EFI_ERROR (Status);
1498 FreePool (NewConsistName);
1499 }
1500 }
1501
1502 if (ConsistMappingTable != NULL) {
1503 ShellCommandConsistMappingUnInitialize (ConsistMappingTable);
1504 }
1505
1506 SHELL_FREE_NON_NULL (HandleList);
1507 SHELL_FREE_NON_NULL (DevicePathList);
1508
1509 HandleList = NULL;
1510
1511 //
1512 // gShellCurMapping point to node of current file system in the gShellMapList. When reset all mappings,
1513 // all nodes in the gShellMapList will be free. Then gShellCurMapping will be a dangling pointer, So,
1514 // after created new mappings, we should reset the gShellCurMapping pointer back to node of current file system.
1515 //
1516 if (gShellCurMapping != NULL) {
1517 gShellCurMapping = NULL;
1518 CurDir = gEfiShellProtocol->GetEnv (L"cwd");
1519 if (CurDir != NULL) {
1520 MapName = AllocateCopyPool (StrSize (CurDir), CurDir);
1521 if (MapName == NULL) {
1522 return EFI_OUT_OF_RESOURCES;
1523 }
1524
1525 SplitCurDir = StrStr (MapName, L":");
1526 if (SplitCurDir == NULL) {
1527 SHELL_FREE_NON_NULL (MapName);
1528 return EFI_UNSUPPORTED;
1529 }
1530
1531 *(SplitCurDir + 1) = CHAR_NULL;
1532 MapListItem = ShellCommandFindMapItem (MapName);
1533 if (MapListItem != NULL) {
1534 gShellCurMapping = MapListItem;
1535 }
1536
1537 SHELL_FREE_NON_NULL (MapName);
1538 }
1539 }
1540 } else {
1541 Count = (UINTN)-1;
1542 }
1543
1544 //
1545 // Find each handle with Block Io
1546 //
1547 HandleList = GetHandleListByProtocol (&gEfiBlockIoProtocolGuid);
1548 if (HandleList != NULL) {
1549 for (Count = 0; HandleList[Count] != NULL; Count++) {
1550 }
1551
1552 //
1553 // Get all Device Paths
1554 //
1555 DevicePathList = AllocateZeroPool (sizeof (EFI_DEVICE_PATH_PROTOCOL *) * Count);
1556 if (DevicePathList == NULL) {
1557 SHELL_FREE_NON_NULL (HandleList);
1558 return EFI_OUT_OF_RESOURCES;
1559 }
1560
1561 for (Count = 0; HandleList[Count] != NULL; Count++) {
1562 DevicePathList[Count] = DevicePathFromHandle (HandleList[Count]);
1563 }
1564
1565 //
1566 // Sort all DevicePaths
1567 //
1568 PerformQuickSort (DevicePathList, Count, sizeof (EFI_DEVICE_PATH_PROTOCOL *), DevicePathCompare);
1569
1570 //
1571 // Assign new Mappings to all...
1572 //
1573 for (Count = 0; HandleList[Count] != NULL; Count++) {
1574 //
1575 // Get default name first
1576 //
1577 NewDefaultName = ShellCommandCreateNewMappingName (MappingTypeBlockIo);
1578 if (NewDefaultName == NULL) {
1579 ASSERT (NewDefaultName != NULL);
1580 SHELL_FREE_NON_NULL (HandleList);
1581 SHELL_FREE_NON_NULL (DevicePathList);
1582 return EFI_OUT_OF_RESOURCES;
1583 }
1584
1585 Status = ShellCommandAddMapItemAndUpdatePath (NewDefaultName, DevicePathList[Count], 0, FALSE);
1586 ASSERT_EFI_ERROR (Status);
1587 FreePool (NewDefaultName);
1588 }
1589
1590 SHELL_FREE_NON_NULL (HandleList);
1591 SHELL_FREE_NON_NULL (DevicePathList);
1592 } else if (Count == (UINTN)-1) {
1593 return (EFI_NOT_FOUND);
1594 }
1595
1596 return (EFI_SUCCESS);
1597}
1598
1599/**
1600 Add mappings for any devices without one. Do not change any existing maps.
1601
1602 @retval EFI_SUCCESS The operation was successful.
1603**/
1604EFI_STATUS
1605EFIAPI
1606ShellCommandUpdateMapping (
1607 VOID
1608 )
1609{
1610 EFI_STATUS Status;
1611 EFI_HANDLE *HandleList;
1612 UINTN Count;
1613 EFI_DEVICE_PATH_PROTOCOL **DevicePathList;
1614 CHAR16 *NewDefaultName;
1615 CHAR16 *NewConsistName;
1616 EFI_DEVICE_PATH_PROTOCOL **ConsistMappingTable;
1617
1618 HandleList = NULL;
1619 Status = EFI_SUCCESS;
1620
1621 //
1622 // remove mappings that represent removed devices.
1623 //
1624
1625 //
1626 // Find each handle with Simple File System
1627 //
1628 HandleList = GetHandleListByProtocol (&gEfiSimpleFileSystemProtocolGuid);
1629 if (HandleList != NULL) {
1630 //
1631 // Do a count of the handles
1632 //
1633 for (Count = 0; HandleList[Count] != NULL; Count++) {
1634 }
1635
1636 //
1637 // Get all Device Paths
1638 //
1639 DevicePathList = AllocateZeroPool (sizeof (EFI_DEVICE_PATH_PROTOCOL *) * Count);
1640 if (DevicePathList == NULL) {
1641 return (EFI_OUT_OF_RESOURCES);
1642 }
1643
1644 for (Count = 0; HandleList[Count] != NULL; Count++) {
1645 DevicePathList[Count] = DevicePathFromHandle (HandleList[Count]);
1646 }
1647
1648 //
1649 // Sort all DevicePaths
1650 //
1651 PerformQuickSort (DevicePathList, Count, sizeof (EFI_DEVICE_PATH_PROTOCOL *), DevicePathCompare);
1652
1653 Status = ShellCommandConsistMappingInitialize (&ConsistMappingTable);
1654 if (EFI_ERROR (Status)) {
1655 SHELL_FREE_NON_NULL (HandleList);
1656 SHELL_FREE_NON_NULL (DevicePathList);
1657 return Status;
1658 }
1659
1660 //
1661 // Assign new Mappings to remainders
1662 //
1663 for (Count = 0; !EFI_ERROR (Status) && HandleList[Count] != NULL; Count++) {
1664 //
1665 // Skip ones that already have
1666 //
1667 if (gEfiShellProtocol->GetMapFromDevicePath (&DevicePathList[Count]) != NULL) {
1668 continue;
1669 }
1670
1671 //
1672 // Get default name
1673 //
1674 NewDefaultName = ShellCommandCreateNewMappingName (MappingTypeFileSystem);
1675 if (NewDefaultName == NULL) {
1676 Status = EFI_OUT_OF_RESOURCES;
1677 break;
1678 }
1679
1680 //
1681 // Call shell protocol SetMap function now...
1682 //
1683 Status = gEfiShellProtocol->SetMap (DevicePathList[Count], NewDefaultName);
1684
1685 if (!EFI_ERROR (Status)) {
1686 //
1687 // Now do consistent name
1688 //
1689 NewConsistName = ShellCommandConsistMappingGenMappingName (DevicePathList[Count], ConsistMappingTable);
1690 if (NewConsistName != NULL) {
1691 Status = gEfiShellProtocol->SetMap (DevicePathList[Count], NewConsistName);
1692 FreePool (NewConsistName);
1693 }
1694 }
1695
1696 FreePool (NewDefaultName);
1697 }
1698
1699 ShellCommandConsistMappingUnInitialize (ConsistMappingTable);
1700 SHELL_FREE_NON_NULL (HandleList);
1701 SHELL_FREE_NON_NULL (DevicePathList);
1702
1703 HandleList = NULL;
1704 } else {
1705 Count = (UINTN)-1;
1706 }
1707
1708 //
1709 // Do it all over again for gEfiBlockIoProtocolGuid
1710 //
1711
1712 return (Status);
1713}
1714
1715/**
1716 Converts a SHELL_FILE_HANDLE to an EFI_FILE_PROTOCOL*.
1717
1718 @param[in] Handle The SHELL_FILE_HANDLE to convert.
1719
1720 @return a EFI_FILE_PROTOCOL* representing the same file.
1721**/
1722EFI_FILE_PROTOCOL *
1723EFIAPI
1724ConvertShellHandleToEfiFileProtocol (
1725 IN CONST SHELL_FILE_HANDLE Handle
1726 )
1727{
1728 return ((EFI_FILE_PROTOCOL *)(Handle));
1729}
1730
1731/**
1732 Converts a EFI_FILE_PROTOCOL* to an SHELL_FILE_HANDLE.
1733
1734 @param[in] Handle The pointer to EFI_FILE_PROTOCOL to convert.
1735 @param[in] Path The path to the file for verification.
1736
1737 @return A SHELL_FILE_HANDLE representing the same file.
1738 @retval NULL There was not enough memory.
1739**/
1740SHELL_FILE_HANDLE
1741EFIAPI
1742ConvertEfiFileProtocolToShellHandle (
1743 IN CONST EFI_FILE_PROTOCOL *Handle,
1744 IN CONST CHAR16 *Path
1745 )
1746{
1747 SHELL_COMMAND_FILE_HANDLE *Buffer;
1748 BUFFER_LIST *NewNode;
1749
1750 if (Path != NULL) {
1751 Buffer = AllocateZeroPool (sizeof (SHELL_COMMAND_FILE_HANDLE));
1752 if (Buffer == NULL) {
1753 return (NULL);
1754 }
1755
1756 NewNode = AllocateZeroPool (sizeof (BUFFER_LIST));
1757 if (NewNode == NULL) {
1758 SHELL_FREE_NON_NULL (Buffer);
1759 return (NULL);
1760 }
1761
1762 Buffer->FileHandle = (EFI_FILE_PROTOCOL *)Handle;
1763 Buffer->Path = StrnCatGrow (&Buffer->Path, NULL, Path, 0);
1764 if (Buffer->Path == NULL) {
1765 SHELL_FREE_NON_NULL (NewNode);
1766 SHELL_FREE_NON_NULL (Buffer);
1767 return (NULL);
1768 }
1769
1770 NewNode->Buffer = Buffer;
1771
1772 InsertHeadList (&mFileHandleList.Link, &NewNode->Link);
1773 }
1774
1775 return ((SHELL_FILE_HANDLE)(Handle));
1776}
1777
1778/**
1779 Find the path that was logged with the specified SHELL_FILE_HANDLE.
1780
1781 @param[in] Handle The SHELL_FILE_HANDLE to query on.
1782
1783 @return A pointer to the path for the file.
1784**/
1785CONST CHAR16 *
1786EFIAPI
1787ShellFileHandleGetPath (
1788 IN CONST SHELL_FILE_HANDLE Handle
1789 )
1790{
1791 BUFFER_LIST *Node;
1792
1793 for (Node = (BUFFER_LIST *)GetFirstNode (&mFileHandleList.Link)
1794 ; !IsNull (&mFileHandleList.Link, &Node->Link)
1795 ; Node = (BUFFER_LIST *)GetNextNode (&mFileHandleList.Link, &Node->Link)
1796 )
1797 {
1798 if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)) {
1799 return (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path);
1800 }
1801 }
1802
1803 return (NULL);
1804}
1805
1806/**
1807 Remove a SHELL_FILE_HANDLE from the list of SHELL_FILE_HANDLES.
1808
1809 @param[in] Handle The SHELL_FILE_HANDLE to remove.
1810
1811 @retval TRUE The item was removed.
1812 @retval FALSE The item was not found.
1813**/
1814BOOLEAN
1815EFIAPI
1816ShellFileHandleRemove (
1817 IN CONST SHELL_FILE_HANDLE Handle
1818 )
1819{
1820 BUFFER_LIST *Node;
1821
1822 for (Node = (BUFFER_LIST *)GetFirstNode (&mFileHandleList.Link)
1823 ; !IsNull (&mFileHandleList.Link, &Node->Link)
1824 ; Node = (BUFFER_LIST *)GetNextNode (&mFileHandleList.Link, &Node->Link)
1825 )
1826 {
1827 if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)) {
1828 RemoveEntryList (&Node->Link);
1829 SHELL_FREE_NON_NULL (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path);
1830 SHELL_FREE_NON_NULL (Node->Buffer);
1831 SHELL_FREE_NON_NULL (Node);
1832 return (TRUE);
1833 }
1834 }
1835
1836 return (FALSE);
1837}
1838
1839/**
1840 Function to determine if a SHELL_FILE_HANDLE is at the end of the file.
1841
1842 This will NOT work on directories.
1843
1844 If Handle is NULL, then ASSERT.
1845
1846 @param[in] Handle the file handle
1847
1848 @retval TRUE the position is at the end of the file
1849 @retval FALSE the position is not at the end of the file
1850**/
1851BOOLEAN
1852EFIAPI
1853ShellFileHandleEof (
1854 IN SHELL_FILE_HANDLE Handle
1855 )
1856{
1857 EFI_FILE_INFO *Info;
1858 UINT64 Pos;
1859 BOOLEAN RetVal;
1860
1861 //
1862 // ASSERT if Handle is NULL
1863 //
1864 ASSERT (Handle != NULL);
1865
1866 gEfiShellProtocol->GetFilePosition (Handle, &Pos);
1867 Info = gEfiShellProtocol->GetFileInfo (Handle);
1868 gEfiShellProtocol->SetFilePosition (Handle, Pos);
1869
1870 if (Info == NULL) {
1871 return (FALSE);
1872 }
1873
1874 if (Pos == Info->FileSize) {
1875 RetVal = TRUE;
1876 } else {
1877 RetVal = FALSE;
1878 }
1879
1880 FreePool (Info);
1881
1882 return (RetVal);
1883}
1884
1885/**
1886 Frees any BUFFER_LIST defined type.
1887
1888 @param[in] List The BUFFER_LIST object to free.
1889**/
1890VOID
1891EFIAPI
1892FreeBufferList (
1893 IN BUFFER_LIST *List
1894 )
1895{
1896 BUFFER_LIST *BufferListEntry;
1897
1898 if (List == NULL) {
1899 return;
1900 }
1901
1902 //
1903 // enumerate through the buffer list and free all memory
1904 //
1905 for ( BufferListEntry = (BUFFER_LIST *)GetFirstNode (&List->Link)
1906 ; !IsListEmpty (&List->Link)
1907 ; BufferListEntry = (BUFFER_LIST *)GetFirstNode (&List->Link)
1908 )
1909 {
1910 RemoveEntryList (&BufferListEntry->Link);
1911 if (BufferListEntry->Buffer != NULL) {
1912 FreePool (BufferListEntry->Buffer);
1913 }
1914
1915 FreePool (BufferListEntry);
1916 }
1917}
1918
1919/**
1920 Dump some hexadecimal data to the screen.
1921
1922 @param[in] Indent How many spaces to indent the output.
1923 @param[in] Offset The offset of the printing.
1924 @param[in] DataSize The size in bytes of UserData.
1925 @param[in] UserData The data to print out.
1926**/
1927VOID
1928EFIAPI
1929DumpHex (
1930 IN UINTN Indent,
1931 IN UINTN Offset,
1932 IN UINTN DataSize,
1933 IN VOID *UserData
1934 )
1935{
1936 UINT8 *Data;
1937
1938 CHAR8 Val[50];
1939
1940 CHAR8 Str[20];
1941
1942 UINT8 TempByte;
1943 UINTN Size;
1944 UINTN Index;
1945
1946 Data = UserData;
1947 while (DataSize != 0) {
1948 Size = 16;
1949 if (Size > DataSize) {
1950 Size = DataSize;
1951 }
1952
1953 for (Index = 0; Index < Size; Index += 1) {
1954 TempByte = Data[Index];
1955 Val[Index * 3 + 0] = Hex[TempByte >> 4];
1956 Val[Index * 3 + 1] = Hex[TempByte & 0xF];
1957 Val[Index * 3 + 2] = (CHAR8)((Index == 7) ? '-' : ' ');
1958 Str[Index] = (CHAR8)((TempByte < ' ' || TempByte > '~') ? '.' : TempByte);
1959 }
1960
1961 Val[Index * 3] = 0;
1962 Str[Index] = 0;
1963 ShellPrintEx (-1, -1, L"%*a%08X: %-48a *%a*\r\n", Indent, "", Offset, Val, Str);
1964
1965 Data += Size;
1966 Offset += Size;
1967 DataSize -= Size;
1968 }
1969}
1970
1971/**
1972 Dump HEX data into buffer.
1973
1974 @param[in] Buffer HEX data to be dumped in Buffer.
1975 @param[in] Indent How many spaces to indent the output.
1976 @param[in] Offset The offset of the printing.
1977 @param[in] DataSize The size in bytes of UserData.
1978 @param[in] UserData The data to print out.
1979**/
1980CHAR16 *
1981EFIAPI
1982CatSDumpHex (
1983 IN CHAR16 *Buffer,
1984 IN UINTN Indent,
1985 IN UINTN Offset,
1986 IN UINTN DataSize,
1987 IN VOID *UserData
1988 )
1989{
1990 UINT8 *Data;
1991 UINT8 TempByte;
1992 UINTN Size;
1993 UINTN Index;
1994 CHAR8 Val[50];
1995 CHAR8 Str[20];
1996 CHAR16 *RetVal;
1997 CHAR16 *TempRetVal;
1998
1999 Data = UserData;
2000 RetVal = Buffer;
2001 while (DataSize != 0) {
2002 Size = 16;
2003 if (Size > DataSize) {
2004 Size = DataSize;
2005 }
2006
2007 for (Index = 0; Index < Size; Index += 1) {
2008 TempByte = Data[Index];
2009 Val[Index * 3 + 0] = Hex[TempByte >> 4];
2010 Val[Index * 3 + 1] = Hex[TempByte & 0xF];
2011 Val[Index * 3 + 2] = (CHAR8)((Index == 7) ? '-' : ' ');
2012 Str[Index] = (CHAR8)((TempByte < ' ' || TempByte > 'z') ? '.' : TempByte);
2013 }
2014
2015 Val[Index * 3] = 0;
2016 Str[Index] = 0;
2017 TempRetVal = CatSPrint (RetVal, L"%*a%08X: %-48a *%a*\r\n", Indent, "", Offset, Val, Str);
2018 SHELL_FREE_NON_NULL (RetVal);
2019 RetVal = TempRetVal;
2020
2021 Data += Size;
2022 Offset += Size;
2023 DataSize -= Size;
2024 }
2025
2026 return RetVal;
2027}
2028
2029/**
2030 ORDERED_COLLECTION_USER_COMPARE function for SHELL_SORT_UNIQUE_NAME objects.
2031
2032 @param[in] Unique1AsVoid The first SHELL_SORT_UNIQUE_NAME object (Unique1),
2033 passed in as a pointer-to-VOID.
2034
2035 @param[in] Unique2AsVoid The second SHELL_SORT_UNIQUE_NAME object (Unique2),
2036 passed in as a pointer-to-VOID.
2037
2038 @retval <0 If Unique1 compares less than Unique2.
2039
2040 @retval 0 If Unique1 compares equal to Unique2.
2041
2042 @retval >0 If Unique1 compares greater than Unique2.
2043**/
2044STATIC
2045INTN
2046EFIAPI
2047UniqueNameCompare (
2048 IN CONST VOID *Unique1AsVoid,
2049 IN CONST VOID *Unique2AsVoid
2050 )
2051{
2052 CONST SHELL_SORT_UNIQUE_NAME *Unique1;
2053 CONST SHELL_SORT_UNIQUE_NAME *Unique2;
2054
2055 Unique1 = Unique1AsVoid;
2056 Unique2 = Unique2AsVoid;
2057
2058 //
2059 // We need to cast away CONST for EFI_UNICODE_COLLATION_STRICOLL.
2060 //
2061 return gUnicodeCollation->StriColl (
2062 gUnicodeCollation,
2063 (CHAR16 *)Unique1->Alias,
2064 (CHAR16 *)Unique2->Alias
2065 );
2066}
2067
2068/**
2069 ORDERED_COLLECTION_KEY_COMPARE function for SHELL_SORT_UNIQUE_NAME objects.
2070
2071 @param[in] UniqueAliasAsVoid The CHAR16 string UniqueAlias, passed in as a
2072 pointer-to-VOID.
2073
2074 @param[in] UniqueAsVoid The SHELL_SORT_UNIQUE_NAME object (Unique),
2075 passed in as a pointer-to-VOID.
2076
2077 @retval <0 If UniqueAlias compares less than Unique->Alias.
2078
2079 @retval 0 If UniqueAlias compares equal to Unique->Alias.
2080
2081 @retval >0 If UniqueAlias compares greater than Unique->Alias.
2082**/
2083STATIC
2084INTN
2085EFIAPI
2086UniqueNameAliasCompare (
2087 IN CONST VOID *UniqueAliasAsVoid,
2088 IN CONST VOID *UniqueAsVoid
2089 )
2090{
2091 CONST CHAR16 *UniqueAlias;
2092 CONST SHELL_SORT_UNIQUE_NAME *Unique;
2093
2094 UniqueAlias = UniqueAliasAsVoid;
2095 Unique = UniqueAsVoid;
2096
2097 //
2098 // We need to cast away CONST for EFI_UNICODE_COLLATION_STRICOLL.
2099 //
2100 return gUnicodeCollation->StriColl (
2101 gUnicodeCollation,
2102 (CHAR16 *)UniqueAlias,
2103 (CHAR16 *)Unique->Alias
2104 );
2105}
2106
2107/**
2108 Sort an EFI_SHELL_FILE_INFO list, optionally moving duplicates to a separate
2109 list.
2110
2111 @param[in,out] FileList The list of EFI_SHELL_FILE_INFO objects to sort.
2112
2113 If FileList is NULL on input, then FileList is
2114 considered an empty, hence already sorted, list.
2115
2116 Otherwise, if (*FileList) is NULL on input, then
2117 EFI_INVALID_PARAMETER is returned.
2118
2119 Otherwise, the caller is responsible for having
2120 initialized (*FileList)->Link with
2121 InitializeListHead(). No other fields in the
2122 (**FileList) head element are accessed by this
2123 function.
2124
2125 On output, (*FileList) is sorted according to Order.
2126 If Duplicates is NULL on input, then duplicate
2127 elements are preserved, sorted stably, on
2128 (*FileList). If Duplicates is not NULL on input,
2129 then duplicates are moved (stably sorted) to the
2130 new, dynamically allocated (*Duplicates) list.
2131
2132 @param[out] Duplicates If Duplicates is NULL on input, (*FileList) will be
2133 a monotonically ordered list on output, with
2134 duplicates stably sorted.
2135
2136 If Duplicates is not NULL on input, (*FileList) will
2137 be a strictly monotonically oredered list on output,
2138 with duplicates separated (stably sorted) to
2139 (*Duplicates). All fields except Link will be
2140 zero-initialized in the (**Duplicates) head element.
2141 If no duplicates exist, then (*Duplicates) is set to
2142 NULL on output.
2143
2144 @param[in] Order Determines the comparison operation between
2145 EFI_SHELL_FILE_INFO objects.
2146
2147 @retval EFI_INVALID_PARAMETER (UINTN)Order is greater than or equal to
2148 (UINTN)ShellSortFileListMax. Neither the
2149 (*FileList) nor the (*Duplicates) list has
2150 been modified.
2151
2152 @retval EFI_INVALID_PARAMETER (*FileList) was NULL on input. Neither the
2153 (*FileList) nor the (*Duplicates) list has
2154 been modified.
2155
2156 @retval EFI_OUT_OF_RESOURCES Memory allocation failed. Neither the
2157 (*FileList) nor the (*Duplicates) list has
2158 been modified.
2159
2160 @retval EFI_SUCCESS Sorting successful, including the case when
2161 FileList is NULL on input.
2162**/
2163EFI_STATUS
2164EFIAPI
2165ShellSortFileList (
2166 IN OUT EFI_SHELL_FILE_INFO **FileList,
2167 OUT EFI_SHELL_FILE_INFO **Duplicates OPTIONAL,
2168 IN SHELL_SORT_FILE_LIST Order
2169 )
2170{
2171 LIST_ENTRY *FilesHead;
2172 ORDERED_COLLECTION *Sort;
2173 LIST_ENTRY *FileEntry;
2174 EFI_SHELL_FILE_INFO *FileInfo;
2175 SHELL_SORT_UNIQUE_NAME *Unique;
2176 EFI_STATUS Status;
2177 EFI_SHELL_FILE_INFO *Dupes;
2178 LIST_ENTRY *NextFileEntry;
2179 CONST CHAR16 *Alias;
2180 ORDERED_COLLECTION_ENTRY *SortEntry;
2181 LIST_ENTRY *TargetFileList;
2182 ORDERED_COLLECTION_ENTRY *NextSortEntry;
2183 VOID *UniqueAsVoid;
2184
2185 if ((UINTN)Order >= (UINTN)ShellSortFileListMax) {
2186 return EFI_INVALID_PARAMETER;
2187 }
2188
2189 if (FileList == NULL) {
2190 //
2191 // FileList is considered empty, hence already sorted, with no duplicates.
2192 //
2193 if (Duplicates != NULL) {
2194 *Duplicates = NULL;
2195 }
2196
2197 return EFI_SUCCESS;
2198 }
2199
2200 if (*FileList == NULL) {
2201 return EFI_INVALID_PARAMETER;
2202 }
2203
2204 FilesHead = &(*FileList)->Link;
2205
2206 //
2207 // Collect all the unique names.
2208 //
2209 Sort = OrderedCollectionInit (UniqueNameCompare, UniqueNameAliasCompare);
2210 if (Sort == NULL) {
2211 return EFI_OUT_OF_RESOURCES;
2212 }
2213
2214 BASE_LIST_FOR_EACH (FileEntry, FilesHead) {
2215 FileInfo = (EFI_SHELL_FILE_INFO *)FileEntry;
2216
2217 //
2218 // Try to record the name of this file as a unique name.
2219 //
2220 Unique = AllocatePool (sizeof (*Unique));
2221 if (Unique == NULL) {
2222 Status = EFI_OUT_OF_RESOURCES;
2223 goto UninitSort;
2224 }
2225
2226 Unique->Alias = ((Order == ShellSortFileListByFileName) ?
2227 FileInfo->FileName :
2228 FileInfo->FullName);
2229 InitializeListHead (&Unique->SameNameList);
2230
2231 Status = OrderedCollectionInsert (Sort, NULL, Unique);
2232 if (EFI_ERROR (Status)) {
2233 //
2234 // Only two errors are possible: memory allocation failed, or this name
2235 // has been encountered before. In either case, the
2236 // SHELL_SORT_UNIQUE_NAME object being constructed has to be released.
2237 //
2238 FreePool (Unique);
2239 //
2240 // Memory allocation failure is fatal, while having seen the same name
2241 // before is normal.
2242 //
2243 if (Status == EFI_OUT_OF_RESOURCES) {
2244 goto UninitSort;
2245 }
2246
2247 ASSERT (Status == EFI_ALREADY_STARTED);
2248 }
2249 }
2250
2251 //
2252 // Set Dupes to suppress incorrect compiler/analyzer warnings.
2253 //
2254 Dupes = NULL;
2255
2256 //
2257 // If separation of duplicates has been requested, allocate the list for
2258 // them.
2259 //
2260 if (Duplicates != NULL) {
2261 Dupes = AllocateZeroPool (sizeof (*Dupes));
2262 if (Dupes == NULL) {
2263 Status = EFI_OUT_OF_RESOURCES;
2264 goto UninitSort;
2265 }
2266
2267 InitializeListHead (&Dupes->Link);
2268 }
2269
2270 //
2271 // No memory allocation beyond this point; thus, no chance to fail. We can
2272 // now migrate the EFI_SHELL_FILE_INFO objects from (*FileList) to Sort.
2273 //
2274 BASE_LIST_FOR_EACH_SAFE (FileEntry, NextFileEntry, FilesHead) {
2275 FileInfo = (EFI_SHELL_FILE_INFO *)FileEntry;
2276 //
2277 // Look up the SHELL_SORT_UNIQUE_NAME that matches FileInfo's name.
2278 //
2279 Alias = ((Order == ShellSortFileListByFileName) ?
2280 FileInfo->FileName :
2281 FileInfo->FullName);
2282 SortEntry = OrderedCollectionFind (Sort, Alias);
2283 ASSERT (SortEntry != NULL);
2284 Unique = OrderedCollectionUserStruct (SortEntry);
2285 //
2286 // Move FileInfo from (*FileList) to the end of the list of files whose
2287 // names all compare identical to FileInfo's name.
2288 //
2289 RemoveEntryList (&FileInfo->Link);
2290 InsertTailList (&Unique->SameNameList, &FileInfo->Link);
2291 }
2292
2293 //
2294 // All EFI_SHELL_FILE_INFO objects originally in (*FileList) have been
2295 // distributed to Sort. Now migrate them back to (*FileList), advancing in
2296 // unique name order.
2297 //
2298 for (SortEntry = OrderedCollectionMin (Sort);
2299 SortEntry != NULL;
2300 SortEntry = OrderedCollectionNext (SortEntry))
2301 {
2302 Unique = OrderedCollectionUserStruct (SortEntry);
2303 //
2304 // The first FileInfo encountered for each unique name goes back on
2305 // (*FileList) unconditionally. Further FileInfo instances for the same
2306 // unique name -- that is, duplicates -- are either returned to (*FileList)
2307 // or separated, dependent on the caller's request.
2308 //
2309 TargetFileList = FilesHead;
2310 BASE_LIST_FOR_EACH_SAFE (FileEntry, NextFileEntry, &Unique->SameNameList) {
2311 RemoveEntryList (FileEntry);
2312 InsertTailList (TargetFileList, FileEntry);
2313 if (Duplicates != NULL) {
2314 TargetFileList = &Dupes->Link;
2315 }
2316 }
2317 }
2318
2319 //
2320 // We're done. If separation of duplicates has been requested, output the
2321 // list of duplicates -- and free that list at once, if it's empty (i.e., if
2322 // no duplicates have been found).
2323 //
2324 if (Duplicates != NULL) {
2325 if (IsListEmpty (&Dupes->Link)) {
2326 FreePool (Dupes);
2327 *Duplicates = NULL;
2328 } else {
2329 *Duplicates = Dupes;
2330 }
2331 }
2332
2333 Status = EFI_SUCCESS;
2334
2335 //
2336 // Fall through.
2337 //
2338UninitSort:
2339 for (SortEntry = OrderedCollectionMin (Sort);
2340 SortEntry != NULL;
2341 SortEntry = NextSortEntry)
2342 {
2343 NextSortEntry = OrderedCollectionNext (SortEntry);
2344 OrderedCollectionDelete (Sort, SortEntry, &UniqueAsVoid);
2345 Unique = UniqueAsVoid;
2346 ASSERT (IsListEmpty (&Unique->SameNameList));
2347 FreePool (Unique);
2348 }
2349
2350 OrderedCollectionUninit (Sort);
2351
2352 return Status;
2353}
Note: See TracBrowser for help on using the repository browser.

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