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
CommandInit(VOID)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
ShellCommandLibConstructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)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
FreeFileHandleList(IN BUFFER_LIST * List)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
ShellCommandLibDestructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)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 *
ShellCommandFindDynamicCommand(IN CONST CHAR16 * CommandString)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
ShellCommandDynamicCommandExists(IN CONST CHAR16 * CommandString)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
ShellCommandIsCommandOnInternalList(IN CONST CHAR16 * CommandString)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
ShellCommandIsCommandOnList(IN CONST CHAR16 * CommandString)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*
ShellCommandGetDynamicCommandHelp(IN CONST CHAR16 * CommandString)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*
ShellCommandGetInternalCommandHelp(IN CONST CHAR16 * CommandString)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
ShellCommandGetCommandHelp(IN CONST CHAR16 * CommandString)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
ShellCommandRegisterCommandName(IN CONST CHAR16 * CommandString,IN SHELL_RUN_COMMAND CommandHandler,IN SHELL_GET_MAN_FILENAME GetManFileName,IN UINT32 ShellMinSupportLevel,IN CONST CHAR16 * ProfileName,IN CONST BOOLEAN CanAffectLE,IN CONST EFI_HII_HANDLE HiiHandle,IN CONST EFI_STRING_ID ManFormatHelp)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
ShellCommandGetProfileList(VOID)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
ShellCommandRunCommandHandler(IN CONST CHAR16 * CommandString,IN OUT SHELL_STATUS * RetVal,IN OUT BOOLEAN * CanAffectLE OPTIONAL)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
ShellCommandGetManFileNameHandler(IN CONST CHAR16 * CommandString)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
ShellCommandGetCommandList(IN CONST BOOLEAN Sort)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
ShellCommandRegisterAlias(IN CONST CHAR16 * Command,IN CONST CHAR16 * Alias)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
ShellCommandGetInitAliasList(VOID)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
ShellCommandIsOnAliasList(IN CONST CHAR16 * Alias)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
ShellCommandGetEchoState(VOID)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
ShellCommandSetEchoState(IN BOOLEAN State)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
ShellCommandRegisterExit(IN BOOLEAN ScriptOnly,IN CONST UINT64 ErrorCode)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
ShellCommandGetExit(VOID)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
ShellCommandGetExitCode(VOID)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
ShellCommandGetScriptExit(VOID)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
DeleteScriptFileStruct(IN SCRIPT_FILE * Script)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
ShellCommandGetCurrentScriptFile(VOID)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
ShellCommandSetNewScript(IN SCRIPT_FILE * Script OPTIONAL)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
ShellCommandCreateNewMappingName(IN CONST SHELL_MAPPING_TYPE Type)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
ShellCommandAddMapItemAndUpdatePath(IN CONST CHAR16 * Name,IN CONST EFI_DEVICE_PATH_PROTOCOL * DevicePath,IN CONST UINT64 Flags,IN CONST BOOLEAN Path)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
ShellCommandCreateInitialMappingsAndPaths(VOID)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
ShellCommandUpdateMapping(VOID)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
ConvertShellHandleToEfiFileProtocol(IN CONST SHELL_FILE_HANDLE Handle)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
ConvertEfiFileProtocolToShellHandle(IN CONST EFI_FILE_PROTOCOL * Handle,IN CONST CHAR16 * Path)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
ShellFileHandleGetPath(IN CONST SHELL_FILE_HANDLE Handle)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
ShellFileHandleRemove(IN CONST SHELL_FILE_HANDLE Handle)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
ShellFileHandleEof(IN SHELL_FILE_HANDLE Handle)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
FreeBufferList(IN BUFFER_LIST * List)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
DumpHex(IN UINTN Indent,IN UINTN Offset,IN UINTN DataSize,IN VOID * UserData)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
CatSDumpHex(IN CHAR16 * Buffer,IN UINTN Indent,IN UINTN Offset,IN UINTN DataSize,IN VOID * UserData)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
UniqueNameCompare(IN CONST VOID * Unique1AsVoid,IN CONST VOID * Unique2AsVoid)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
UniqueNameAliasCompare(IN CONST VOID * UniqueAliasAsVoid,IN CONST VOID * UniqueAsVoid)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
ShellSortFileList(IN OUT EFI_SHELL_FILE_INFO ** FileList,OUT EFI_SHELL_FILE_INFO ** Duplicates OPTIONAL,IN SHELL_SORT_FILE_LIST Order)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 }
2224