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