1/** @file
2  Provides interface to shell internal functions for shell commands.
3
4  (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.<BR>
5  Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
6  This program and the accompanying materials
7  are licensed and made available under the terms and conditions of the BSD License
8  which accompanies this distribution.  The full text of the license may be found at
9  http://opensource.org/licenses/bsd-license.php
10
11  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14**/
15
16#include "UefiShellCommandLib.h"
17
18// STATIC local variables
19STATIC SHELL_COMMAND_INTERNAL_LIST_ENTRY  mCommandList;
20STATIC SCRIPT_FILE_LIST                   mScriptList;
21STATIC ALIAS_LIST                         mAliasList;
22STATIC BOOLEAN                            mEchoState;
23STATIC BOOLEAN                            mExitRequested;
24STATIC UINT64                             mExitCode;
25STATIC BOOLEAN                            mExitScript;
26STATIC CHAR16                             *mProfileList;
27STATIC UINTN                              mProfileListSize;
28STATIC UINTN                              mFsMaxCount = 0;
29STATIC UINTN                              mBlkMaxCount = 0;
30STATIC BUFFER_LIST                        mFileHandleList;
31
32// global variables required by library class.
33EFI_UNICODE_COLLATION_PROTOCOL    *gUnicodeCollation            = NULL;
34SHELL_MAP_LIST                    gShellMapList;
35SHELL_MAP_LIST                    *gShellCurDir                 = NULL;
36
37CONST CHAR16* SupportLevel[] = {
38  L"Minimal",
39  L"Scripting",
40  L"Basic",
41  L"Interactive"
42};
43
44/**
45  Function to make sure that the global protocol pointers are valid.
46  must be called after constructor before accessing the pointers.
47**/
48EFI_STATUS
49EFIAPI
50CommandInit(
51  VOID
52  )
53{
54  EFI_STATUS Status;
55  if (gUnicodeCollation == NULL) {
56    Status = gBS->LocateProtocol(&gEfiUnicodeCollation2ProtocolGuid, NULL, (VOID**)&gUnicodeCollation);
57    if (EFI_ERROR(Status)) {
58      return (EFI_DEVICE_ERROR);
59    }
60  }
61  return (EFI_SUCCESS);
62}
63
64/**
65  Constructor for the Shell Command library.
66
67  Initialize the library and determine if the underlying is a UEFI Shell 2.0 or an EFI shell.
68
69  @param ImageHandle    the image handle of the process
70  @param SystemTable    the EFI System Table pointer
71
72  @retval EFI_SUCCESS   the initialization was complete sucessfully
73**/
74RETURN_STATUS
75EFIAPI
76ShellCommandLibConstructor (
77  IN EFI_HANDLE        ImageHandle,
78  IN EFI_SYSTEM_TABLE  *SystemTable
79  )
80{
81  EFI_STATUS        Status;
82  InitializeListHead(&gShellMapList.Link);
83  InitializeListHead(&mCommandList.Link);
84  InitializeListHead(&mAliasList.Link);
85  InitializeListHead(&mScriptList.Link);
86  InitializeListHead(&mFileHandleList.Link);
87  mEchoState = TRUE;
88
89  mExitRequested    = FALSE;
90  mExitScript       = FALSE;
91  mProfileListSize  = 0;
92  mProfileList      = NULL;
93
94  if (gUnicodeCollation == NULL) {
95    Status = gBS->LocateProtocol(&gEfiUnicodeCollation2ProtocolGuid, NULL, (VOID**)&gUnicodeCollation);
96    if (EFI_ERROR(Status)) {
97      return (EFI_DEVICE_ERROR);
98    }
99  }
100
101  return (RETURN_SUCCESS);
102}
103
104/**
105  Frees list of file handles.
106
107  @param[in] List     The list to free.
108**/
109VOID
110EFIAPI
111FreeFileHandleList (
112  IN BUFFER_LIST *List
113  )
114{
115  BUFFER_LIST               *BufferListEntry;
116
117  if (List == NULL){
118    return;
119  }
120  //
121  // enumerate through the buffer list and free all memory
122  //
123  for ( BufferListEntry = ( BUFFER_LIST *)GetFirstNode(&List->Link)
124      ; !IsListEmpty (&List->Link)
125      ; BufferListEntry = (BUFFER_LIST *)GetFirstNode(&List->Link)
126     ){
127    RemoveEntryList(&BufferListEntry->Link);
128    ASSERT(BufferListEntry->Buffer != NULL);
129    SHELL_FREE_NON_NULL(((SHELL_COMMAND_FILE_HANDLE*)(BufferListEntry->Buffer))->Path);
130    SHELL_FREE_NON_NULL(BufferListEntry->Buffer);
131    SHELL_FREE_NON_NULL(BufferListEntry);
132  }
133}
134
135/**
136  Destructor for the library.  free any resources.
137
138  @param ImageHandle    the image handle of the process
139  @param SystemTable    the EFI System Table pointer
140
141  @retval RETURN_SUCCESS this function always returns success
142**/
143RETURN_STATUS
144EFIAPI
145ShellCommandLibDestructor (
146  IN EFI_HANDLE        ImageHandle,
147  IN EFI_SYSTEM_TABLE  *SystemTable
148  )
149{
150  SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
151  ALIAS_LIST                        *Node2;
152  SCRIPT_FILE_LIST                  *Node3;
153  SHELL_MAP_LIST                    *MapNode;
154  //
155  // enumerate throught the list and free all the memory
156  //
157  while (!IsListEmpty (&mCommandList.Link)) {
158    Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link);
159    RemoveEntryList(&Node->Link);
160    SHELL_FREE_NON_NULL(Node->CommandString);
161    FreePool(Node);
162    DEBUG_CODE(Node = NULL;);
163  }
164
165  //
166  // enumerate through the alias list and free all memory
167  //
168  while (!IsListEmpty (&mAliasList.Link)) {
169    Node2 = (ALIAS_LIST *)GetFirstNode(&mAliasList.Link);
170    RemoveEntryList(&Node2->Link);
171    SHELL_FREE_NON_NULL(Node2->CommandString);
172    SHELL_FREE_NON_NULL(Node2->Alias);
173    SHELL_FREE_NON_NULL(Node2);
174    DEBUG_CODE(Node2 = NULL;);
175  }
176
177  //
178  // enumerate throught the list and free all the memory
179  //
180  while (!IsListEmpty (&mScriptList.Link)) {
181    Node3 = (SCRIPT_FILE_LIST *)GetFirstNode(&mScriptList.Link);
182    RemoveEntryList(&Node3->Link);
183    DeleteScriptFileStruct(Node3->Data);
184    FreePool(Node3);
185  }
186
187  //
188  // enumerate throught the mappings list and free all the memory
189  //
190  if (!IsListEmpty(&gShellMapList.Link)) {
191    for (MapNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
192         ; !IsListEmpty (&gShellMapList.Link)
193         ; MapNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
194        ){
195      ASSERT(MapNode != NULL);
196      RemoveEntryList(&MapNode->Link);
197      SHELL_FREE_NON_NULL(MapNode->DevicePath);
198      SHELL_FREE_NON_NULL(MapNode->MapName);
199      SHELL_FREE_NON_NULL(MapNode->CurrentDirectoryPath);
200      FreePool(MapNode);
201    }
202  }
203  if (!IsListEmpty(&mFileHandleList.Link)){
204    FreeFileHandleList(&mFileHandleList);
205  }
206
207  if (mProfileList != NULL) {
208    FreePool(mProfileList);
209  }
210
211  gUnicodeCollation            = NULL;
212  gShellCurDir                 = NULL;
213
214  return (RETURN_SUCCESS);
215}
216
217/**
218  Find a dynamic command protocol instance given a command name string.
219
220  @param CommandString  the command name string
221
222  @return instance      the command protocol instance, if dynamic command instance found
223  @retval NULL          no dynamic command protocol instance found for name
224**/
225CONST EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *
226EFIAPI
227ShellCommandFindDynamicCommand (
228  IN CONST CHAR16 *CommandString
229  )
230{
231  EFI_STATUS                          Status;
232  EFI_HANDLE                          *CommandHandleList;
233  EFI_HANDLE                          *NextCommand;
234  EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL  *DynamicCommand;
235
236  CommandHandleList = GetHandleListByProtocol(&gEfiShellDynamicCommandProtocolGuid);
237  if (CommandHandleList == NULL) {
238    //
239    // not found or out of resources
240    //
241    return NULL;
242  }
243
244  for (NextCommand = CommandHandleList; *NextCommand != NULL; NextCommand++) {
245    Status = gBS->HandleProtocol(
246      *NextCommand,
247      &gEfiShellDynamicCommandProtocolGuid,
248      (VOID **)&DynamicCommand
249      );
250
251    if (EFI_ERROR(Status)) {
252      continue;
253    }
254
255    if (gUnicodeCollation->StriColl(
256          gUnicodeCollation,
257          (CHAR16*)CommandString,
258          (CHAR16*)DynamicCommand->CommandName) == 0
259          ){
260        FreePool(CommandHandleList);
261        return (DynamicCommand);
262    }
263  }
264
265  FreePool(CommandHandleList);
266  return (NULL);
267}
268
269/**
270  Checks if a command exists as a dynamic command protocol instance
271
272  @param[in] CommandString        The command string to check for on the list.
273**/
274BOOLEAN
275EFIAPI
276ShellCommandDynamicCommandExists (
277  IN CONST CHAR16 *CommandString
278  )
279{
280  return (BOOLEAN) ((ShellCommandFindDynamicCommand(CommandString) != NULL));
281}
282
283/**
284  Checks if a command is already on the internal command list.
285
286  @param[in] CommandString        The command string to check for on the list.
287**/
288BOOLEAN
289EFIAPI
290ShellCommandIsCommandOnInternalList(
291  IN CONST  CHAR16 *CommandString
292  )
293{
294  SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
295
296  //
297  // assert for NULL parameter
298  //
299  ASSERT(CommandString != NULL);
300
301  //
302  // check for the command
303  //
304  for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
305      ; !IsNull(&mCommandList.Link, &Node->Link)
306      ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
307     ){
308    ASSERT(Node->CommandString != NULL);
309    if (gUnicodeCollation->StriColl(
310          gUnicodeCollation,
311          (CHAR16*)CommandString,
312          Node->CommandString) == 0
313       ){
314      return (TRUE);
315    }
316  }
317  return (FALSE);
318}
319
320/**
321  Checks if a command exists, either internally or through the dynamic command protocol.
322
323  @param[in] CommandString        The command string to check for on the list.
324**/
325BOOLEAN
326EFIAPI
327ShellCommandIsCommandOnList(
328  IN CONST  CHAR16                      *CommandString
329  )
330{
331  if (ShellCommandIsCommandOnInternalList(CommandString)) {
332    return TRUE;
333  }
334
335  return ShellCommandDynamicCommandExists(CommandString);
336}
337
338/**
339 Get the help text for a dynamic command.
340
341  @param[in] CommandString        The command name.
342
343  @retval NULL  No help text was found.
344  @return       String of help text. Caller required to free.
345**/
346CHAR16*
347EFIAPI
348ShellCommandGetDynamicCommandHelp(
349  IN CONST  CHAR16                      *CommandString
350  )
351{
352  EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL  *DynamicCommand;
353
354  DynamicCommand = (EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL  *)ShellCommandFindDynamicCommand(CommandString);
355  if (DynamicCommand == NULL) {
356    return (NULL);
357  }
358
359  //
360  // TODO: how to get proper language?
361  //
362  return DynamicCommand->GetHelp(DynamicCommand, "en");
363}
364
365/**
366  Get the help text for an internal command.
367
368  @param[in] CommandString        The command name.
369
370  @retval NULL  No help text was found.
371  @return       String of help text. Caller reuiqred to free.
372**/
373CHAR16*
374EFIAPI
375ShellCommandGetInternalCommandHelp(
376  IN CONST  CHAR16                      *CommandString
377  )
378{
379  SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
380
381  //
382  // assert for NULL parameter
383  //
384  ASSERT(CommandString != NULL);
385
386  //
387  // check for the command
388  //
389  for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
390      ; !IsNull(&mCommandList.Link, &Node->Link)
391      ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
392     ){
393    ASSERT(Node->CommandString != NULL);
394    if (gUnicodeCollation->StriColl(
395          gUnicodeCollation,
396          (CHAR16*)CommandString,
397          Node->CommandString) == 0
398       ){
399      return (HiiGetString(Node->HiiHandle, Node->ManFormatHelp, NULL));
400    }
401  }
402  return (NULL);
403}
404
405/**
406  Get the help text for a command.
407
408  @param[in] CommandString        The command name.
409
410  @retval NULL  No help text was found.
411  @return       String of help text.Caller reuiqred to free.
412**/
413CHAR16*
414EFIAPI
415ShellCommandGetCommandHelp (
416  IN CONST  CHAR16                      *CommandString
417  )
418{
419  CHAR16      *HelpStr;
420  HelpStr = ShellCommandGetInternalCommandHelp(CommandString);
421
422  if (HelpStr == NULL) {
423    HelpStr = ShellCommandGetDynamicCommandHelp(CommandString);
424  }
425
426  return HelpStr;
427}
428
429
430/**
431  Registers handlers of type SHELL_RUN_COMMAND and
432  SHELL_GET_MAN_FILENAME for each shell command.
433
434  If the ShellSupportLevel is greater than the value of the
435  PcdShellSupportLevel then return RETURN_UNSUPPORTED.
436
437  Registers the handlers specified by GetHelpInfoHandler and CommandHandler
438  with the command specified by CommandString. If the command named by
439  CommandString has already been registered, then return
440  RETURN_ALREADY_STARTED.
441
442  If there are not enough resources available to register the handlers then
443  RETURN_OUT_OF_RESOURCES is returned.
444
445  If CommandString is NULL, then ASSERT().
446  If GetHelpInfoHandler is NULL, then ASSERT().
447  If CommandHandler is NULL, then ASSERT().
448  If ProfileName is NULL, then ASSERT().
449
450  @param[in]  CommandString         Pointer to the command name.  This is the
451                                    name to look for on the command line in
452                                    the shell.
453  @param[in]  CommandHandler        Pointer to a function that runs the
454                                    specified command.
455  @param[in]  GetManFileName        Pointer to a function that provides man
456                                    filename.
457  @param[in]  ShellMinSupportLevel  minimum Shell Support Level which has this
458                                    function.
459  @param[in]  ProfileName           profile name to require for support of this
460                                    function.
461  @param[in]  CanAffectLE           indicates whether this command's return value
462                                    can change the LASTERROR environment variable.
463  @param[in]  HiiHandle             Handle of this command's HII entry.
464  @param[in]  ManFormatHelp         HII locator for the help text.
465
466  @retval  RETURN_SUCCESS           The handlers were registered.
467  @retval  RETURN_OUT_OF_RESOURCES  There are not enough resources available to
468                                    register the shell command.
469  @retval RETURN_UNSUPPORTED        the ShellMinSupportLevel was higher than the
470                                    currently allowed support level.
471  @retval RETURN_ALREADY_STARTED    The CommandString represents a command that
472                                    is already registered.  Only 1 handler set for
473                                    a given command is allowed.
474  @sa SHELL_GET_MAN_FILENAME
475  @sa SHELL_RUN_COMMAND
476**/
477RETURN_STATUS
478EFIAPI
479ShellCommandRegisterCommandName (
480  IN CONST  CHAR16                      *CommandString,
481  IN        SHELL_RUN_COMMAND           CommandHandler,
482  IN        SHELL_GET_MAN_FILENAME      GetManFileName,
483  IN        UINT32                      ShellMinSupportLevel,
484  IN CONST  CHAR16                      *ProfileName,
485  IN CONST  BOOLEAN                     CanAffectLE,
486  IN CONST  EFI_HANDLE                  HiiHandle,
487  IN CONST  EFI_STRING_ID               ManFormatHelp
488  )
489{
490  SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
491  SHELL_COMMAND_INTERNAL_LIST_ENTRY *Command;
492  SHELL_COMMAND_INTERNAL_LIST_ENTRY *PrevCommand;
493  INTN LexicalMatchValue;
494
495  //
496  // Initialize local variables.
497  //
498  Command = NULL;
499  PrevCommand = NULL;
500  LexicalMatchValue = 0;
501
502  //
503  // ASSERTs for NULL parameters
504  //
505  ASSERT(CommandString  != NULL);
506  ASSERT(GetManFileName != NULL);
507  ASSERT(CommandHandler != NULL);
508  ASSERT(ProfileName    != NULL);
509
510  //
511  // check for shell support level
512  //
513  if (PcdGet8(PcdShellSupportLevel) < ShellMinSupportLevel) {
514    return (RETURN_UNSUPPORTED);
515  }
516
517  //
518  // check for already on the list
519  //
520  if (ShellCommandIsCommandOnList(CommandString)) {
521    return (RETURN_ALREADY_STARTED);
522  }
523
524  //
525  // allocate memory for new struct
526  //
527  Node = AllocateZeroPool(sizeof(SHELL_COMMAND_INTERNAL_LIST_ENTRY));
528  ASSERT(Node != NULL);
529  Node->CommandString = AllocateCopyPool(StrSize(CommandString), CommandString);
530  ASSERT(Node->CommandString != NULL);
531
532  Node->GetManFileName  = GetManFileName;
533  Node->CommandHandler  = CommandHandler;
534  Node->LastError       = CanAffectLE;
535  Node->HiiHandle       = HiiHandle;
536  Node->ManFormatHelp   = ManFormatHelp;
537
538  if ( StrLen(ProfileName)>0
539    && ((mProfileList != NULL
540    && StrStr(mProfileList, ProfileName) == NULL) || mProfileList == NULL)
541   ){
542    ASSERT((mProfileList == NULL && mProfileListSize == 0) || (mProfileList != NULL));
543    if (mProfileList == NULL) {
544      //
545      // If this is the first make a leading ';'
546      //
547      StrnCatGrow(&mProfileList, &mProfileListSize, L";", 0);
548    }
549    StrnCatGrow(&mProfileList, &mProfileListSize, ProfileName, 0);
550    StrnCatGrow(&mProfileList, &mProfileListSize, L";", 0);
551  }
552
553  //
554  // Insert a new entry on top of the list
555  //
556  InsertHeadList (&mCommandList.Link, &Node->Link);
557
558  //
559  // Move a new registered command to its sorted ordered location in the list
560  //
561  for (Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link),
562        PrevCommand = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode (&mCommandList.Link)
563        ; !IsNull (&mCommandList.Link, &Command->Link)
564        ; Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode (&mCommandList.Link, &Command->Link)) {
565
566    //
567    // Get Lexical Comparison Value between PrevCommand and Command list entry
568    //
569    LexicalMatchValue = gUnicodeCollation->StriColl (
570                                             gUnicodeCollation,
571                                             PrevCommand->CommandString,
572                                             Command->CommandString
573                                             );
574
575    //
576    // Swap PrevCommand and Command list entry if PrevCommand list entry
577    // is alphabetically greater than Command list entry
578    //
579    if (LexicalMatchValue > 0){
580      Command = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *) SwapListEntries (&PrevCommand->Link, &Command->Link);
581    } else if (LexicalMatchValue < 0) {
582      //
583      // PrevCommand entry is lexically lower than Command entry
584      //
585      break;
586    }
587  }
588
589  return (RETURN_SUCCESS);
590}
591
592/**
593  Function to get the current Profile string.
594
595  @retval NULL  There are no installed profiles.
596  @return       A semi-colon delimited list of profiles.
597**/
598CONST CHAR16 *
599EFIAPI
600ShellCommandGetProfileList (
601  VOID
602  )
603{
604  return (mProfileList);
605}
606
607/**
608  Checks if a command string has been registered for CommandString and if so it runs
609  the previously registered handler for that command with the command line.
610
611  If CommandString is NULL, then ASSERT().
612
613  If Sections is specified, then each section name listed will be compared in a casesensitive
614  manner, to the section names described in Appendix B UEFI Shell 2.0 spec. If the section exists,
615  it will be appended to the returned help text. If the section does not exist, no
616  information will be returned. If Sections is NULL, then all help text information
617  available will be returned.
618
619  @param[in]  CommandString          Pointer to the command name.  This is the name
620                                     found on the command line in the shell.
621  @param[in, out] RetVal             Pointer to the return vaule from the command handler.
622
623  @param[in, out]  CanAffectLE       indicates whether this command's return value
624                                     needs to be placed into LASTERROR environment variable.
625
626  @retval RETURN_SUCCESS            The handler was run.
627  @retval RETURN_NOT_FOUND          The CommandString did not match a registered
628                                    command name.
629  @sa SHELL_RUN_COMMAND
630**/
631RETURN_STATUS
632EFIAPI
633ShellCommandRunCommandHandler (
634  IN CONST CHAR16               *CommandString,
635  IN OUT SHELL_STATUS           *RetVal,
636  IN OUT BOOLEAN                *CanAffectLE OPTIONAL
637  )
638{
639  SHELL_COMMAND_INTERNAL_LIST_ENTRY   *Node;
640  EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL  *DynamicCommand;
641
642  //
643  // assert for NULL parameters
644  //
645  ASSERT(CommandString != NULL);
646
647  //
648  // check for the command
649  //
650  for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
651      ; !IsNull(&mCommandList.Link, &Node->Link)
652      ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
653     ){
654    ASSERT(Node->CommandString != NULL);
655    if (gUnicodeCollation->StriColl(
656          gUnicodeCollation,
657          (CHAR16*)CommandString,
658          Node->CommandString) == 0
659      ){
660      if (CanAffectLE != NULL) {
661        *CanAffectLE = Node->LastError;
662      }
663      if (RetVal != NULL) {
664        *RetVal = Node->CommandHandler(NULL, gST);
665      } else {
666        Node->CommandHandler(NULL, gST);
667      }
668      return (RETURN_SUCCESS);
669    }
670  }
671
672  //
673  // An internal command was not found, try to find a dynamic command
674  //
675  DynamicCommand = (EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL  *)ShellCommandFindDynamicCommand(CommandString);
676  if (DynamicCommand != NULL) {
677    if (RetVal != NULL) {
678      *RetVal = DynamicCommand->Handler(DynamicCommand, gST, gEfiShellParametersProtocol, gEfiShellProtocol);
679    } else {
680      DynamicCommand->Handler(DynamicCommand, gST, gEfiShellParametersProtocol, gEfiShellProtocol);
681    }
682    return (RETURN_SUCCESS);
683  }
684
685  return (RETURN_NOT_FOUND);
686}
687
688/**
689  Checks if a command string has been registered for CommandString and if so it
690  returns the MAN filename specified for that command.
691
692  If CommandString is NULL, then ASSERT().
693
694  @param[in]  CommandString         Pointer to the command name.  This is the name
695                                    found on the command line in the shell.\
696
697  @retval NULL                      the commandString was not a registered command.
698  @return other                     the name of the MAN file.
699  @sa SHELL_GET_MAN_FILENAME
700**/
701CONST CHAR16*
702EFIAPI
703ShellCommandGetManFileNameHandler (
704  IN CONST CHAR16               *CommandString
705  )
706{
707  SHELL_COMMAND_INTERNAL_LIST_ENTRY *Node;
708
709  //
710  // assert for NULL parameters
711  //
712  ASSERT(CommandString != NULL);
713
714  //
715  // check for the command
716  //
717  for ( Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetFirstNode(&mCommandList.Link)
718      ; !IsNull(&mCommandList.Link, &Node->Link)
719      ; Node = (SHELL_COMMAND_INTERNAL_LIST_ENTRY *)GetNextNode(&mCommandList.Link, &Node->Link)
720     ){
721    ASSERT(Node->CommandString != NULL);
722    if (gUnicodeCollation->StriColl(
723          gUnicodeCollation,
724          (CHAR16*)CommandString,
725          Node->CommandString) == 0
726       ){
727      return (Node->GetManFileName());
728    }
729  }
730  return (NULL);
731}
732
733/**
734  Get the list of all available shell internal commands.  This is a linked list
735  (via LIST_ENTRY structure).  enumerate through it using the BaseLib linked
736  list functions.  do not modify the values.
737
738  @param[in] Sort       TRUE to alphabetically sort the values first.  FALSE otherwise.
739
740  @return a Linked list of all available shell commands.
741**/
742CONST COMMAND_LIST*
743EFIAPI
744ShellCommandGetCommandList (
745  IN CONST BOOLEAN Sort
746  )
747{
748//  if (!Sort) {
749//    return ((COMMAND_LIST*)(&mCommandList));
750//  }
751  return ((COMMAND_LIST*)(&mCommandList));
752}
753
754/**
755  Registers aliases to be set as part of the initialization of the shell application.
756
757  If Command is NULL, then ASSERT().
758  If Alias is NULL, then ASSERT().
759
760  @param[in]  Command               Pointer to the Command
761  @param[in]  Alias                 Pointer to Alias
762
763  @retval  RETURN_SUCCESS           The handlers were registered.
764  @retval  RETURN_OUT_OF_RESOURCES  There are not enough resources available to
765                                    register the shell command.
766**/
767RETURN_STATUS
768EFIAPI
769ShellCommandRegisterAlias (
770  IN CONST CHAR16                       *Command,
771  IN CONST CHAR16                       *Alias
772  )
773{
774  ALIAS_LIST *Node;
775  ALIAS_LIST *CommandAlias;
776  ALIAS_LIST *PrevCommandAlias;
777  INTN       LexicalMatchValue;
778
779  //
780  // Asserts for NULL
781  //
782  ASSERT(Command != NULL);
783  ASSERT(Alias   != NULL);
784
785  //
786  // allocate memory for new struct
787  //
788  Node = AllocateZeroPool(sizeof(ALIAS_LIST));
789  ASSERT(Node != NULL);
790  Node->CommandString = AllocateCopyPool(StrSize(Command), Command);
791  Node->Alias = AllocateCopyPool(StrSize(Alias), Alias);
792  ASSERT(Node->CommandString != NULL);
793  ASSERT(Node->Alias != NULL);
794
795  InsertHeadList (&mAliasList.Link, &Node->Link);
796
797  //
798  // Move a new pre-defined registered alias to its sorted ordered location in the list
799  //
800  for ( CommandAlias = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link),
801         PrevCommandAlias = (ALIAS_LIST *)GetFirstNode (&mAliasList.Link)
802       ; !IsNull (&mAliasList.Link, &CommandAlias->Link)
803       ; CommandAlias = (ALIAS_LIST *) GetNextNode (&mAliasList.Link, &CommandAlias->Link) ) {
804    //
805    // Get Lexical comparison value between PrevCommandAlias and CommandAlias List Entry
806    //
807    LexicalMatchValue = gUnicodeCollation->StriColl (
808                                             gUnicodeCollation,
809                                             PrevCommandAlias->Alias,
810                                             CommandAlias->Alias
811                                             );
812
813    //
814    // Swap PrevCommandAlias and CommandAlias list entry if PrevCommandAlias list entry
815    // is alphabetically greater than CommandAlias list entry
816    //
817    if (LexicalMatchValue > 0) {
818      CommandAlias = (ALIAS_LIST *) SwapListEntries (&PrevCommandAlias->Link, &CommandAlias->Link);
819    } else if (LexicalMatchValue < 0) {
820      //
821      // PrevCommandAlias entry is lexically lower than CommandAlias entry
822      //
823      break;
824    }
825  }
826
827  return (RETURN_SUCCESS);
828}
829
830/**
831  Get the list of all shell alias commands.  This is a linked list
832  (via LIST_ENTRY structure).  enumerate through it using the BaseLib linked
833  list functions.  do not modify the values.
834
835  @return a Linked list of all requested shell alias'.
836**/
837CONST ALIAS_LIST*
838EFIAPI
839ShellCommandGetInitAliasList (
840  VOID
841  )
842{
843    return (&mAliasList);
844}
845
846/**
847  Determine if a given alias is on the list of built in alias'.
848
849  @param[in] Alias              The alias to test for
850
851  @retval TRUE                  The alias is a built in alias
852  @retval FALSE                 The alias is not a built in alias
853**/
854BOOLEAN
855EFIAPI
856ShellCommandIsOnAliasList(
857  IN CONST CHAR16 *Alias
858  )
859{
860  ALIAS_LIST *Node;
861
862  //
863  // assert for NULL parameter
864  //
865  ASSERT(Alias != NULL);
866
867  //
868  // check for the Alias
869  //
870  for ( Node = (ALIAS_LIST *)GetFirstNode(&mAliasList.Link)
871      ; !IsNull(&mAliasList.Link, &Node->Link)
872      ; Node = (ALIAS_LIST *)GetNextNode(&mAliasList.Link, &Node->Link)
873     ){
874    ASSERT(Node->CommandString != NULL);
875    ASSERT(Node->Alias != NULL);
876    if (gUnicodeCollation->StriColl(
877          gUnicodeCollation,
878          (CHAR16*)Alias,
879          Node->CommandString) == 0
880       ){
881      return (TRUE);
882    }
883    if (gUnicodeCollation->StriColl(
884          gUnicodeCollation,
885          (CHAR16*)Alias,
886          Node->Alias) == 0
887       ){
888      return (TRUE);
889    }
890  }
891  return (FALSE);
892}
893
894/**
895  Function to determine current state of ECHO.  Echo determines if lines from scripts
896  and ECHO commands are enabled.
897
898  @retval TRUE    Echo is currently enabled
899  @retval FALSE   Echo is currently disabled
900**/
901BOOLEAN
902EFIAPI
903ShellCommandGetEchoState(
904  VOID
905  )
906{
907  return (mEchoState);
908}
909
910/**
911  Function to set current state of ECHO.  Echo determines if lines from scripts
912  and ECHO commands are enabled.
913
914  If State is TRUE, Echo will be enabled.
915  If State is FALSE, Echo will be disabled.
916
917  @param[in] State      How to set echo.
918**/
919VOID
920EFIAPI
921ShellCommandSetEchoState(
922  IN BOOLEAN State
923  )
924{
925  mEchoState = State;
926}
927
928/**
929  Indicate that the current shell or script should exit.
930
931  @param[in] ScriptOnly   TRUE if exiting a script; FALSE otherwise.
932  @param[in] ErrorCode    The 64 bit error code to return.
933**/
934VOID
935EFIAPI
936ShellCommandRegisterExit (
937  IN BOOLEAN      ScriptOnly,
938  IN CONST UINT64 ErrorCode
939  )
940{
941  mExitRequested = (BOOLEAN)(!mExitRequested);
942  if (mExitRequested) {
943    mExitScript    = ScriptOnly;
944  } else {
945    mExitScript    = FALSE;
946  }
947  mExitCode = ErrorCode;
948}
949
950/**
951  Retrieve the Exit indicator.
952
953  @retval TRUE      Exit was indicated.
954  @retval FALSE     Exis was not indicated.
955**/
956BOOLEAN
957EFIAPI
958ShellCommandGetExit (
959  VOID
960  )
961{
962  return (mExitRequested);
963}
964
965/**
966  Retrieve the Exit code.
967
968  If ShellCommandGetExit returns FALSE than the return from this is undefined.
969
970  @return the value passed into RegisterExit.
971**/
972UINT64
973EFIAPI
974ShellCommandGetExitCode (
975  VOID
976  )
977{
978  return (mExitCode);
979}
980/**
981  Retrieve the Exit script indicator.
982
983  If ShellCommandGetExit returns FALSE than the return from this is undefined.
984
985  @retval TRUE      ScriptOnly was indicated.
986  @retval FALSE     ScriptOnly was not indicated.
987**/
988BOOLEAN
989EFIAPI
990ShellCommandGetScriptExit (
991  VOID
992  )
993{
994  return (mExitScript);
995}
996
997/**
998  Function to cleanup all memory from a SCRIPT_FILE structure.
999
1000  @param[in] Script     The pointer to the structure to cleanup.
1001**/
1002VOID
1003EFIAPI
1004DeleteScriptFileStruct (
1005  IN SCRIPT_FILE *Script
1006  )
1007{
1008  UINT8       LoopVar;
1009
1010  if (Script == NULL) {
1011    return;
1012  }
1013
1014  for (LoopVar = 0 ; LoopVar < Script->Argc ; LoopVar++) {
1015    SHELL_FREE_NON_NULL(Script->Argv[LoopVar]);
1016  }
1017  if (Script->Argv != NULL) {
1018    SHELL_FREE_NON_NULL(Script->Argv);
1019  }
1020  Script->CurrentCommand = NULL;
1021  while (!IsListEmpty (&Script->CommandList)) {
1022    Script->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&Script->CommandList);
1023    if (Script->CurrentCommand != NULL) {
1024      RemoveEntryList(&Script->CurrentCommand->Link);
1025      if (Script->CurrentCommand->Cl != NULL) {
1026        SHELL_FREE_NON_NULL(Script->CurrentCommand->Cl);
1027      }
1028      if (Script->CurrentCommand->Data != NULL) {
1029        SHELL_FREE_NON_NULL(Script->CurrentCommand->Data);
1030      }
1031      SHELL_FREE_NON_NULL(Script->CurrentCommand);
1032    }
1033  }
1034  SHELL_FREE_NON_NULL(Script->ScriptName);
1035  SHELL_FREE_NON_NULL(Script);
1036}
1037
1038/**
1039  Function to return a pointer to the currently running script file object.
1040
1041  @retval NULL        A script file is not currently running.
1042  @return             A pointer to the current script file object.
1043**/
1044SCRIPT_FILE*
1045EFIAPI
1046ShellCommandGetCurrentScriptFile (
1047  VOID
1048  )
1049{
1050  SCRIPT_FILE_LIST *List;
1051  if (IsListEmpty (&mScriptList.Link)) {
1052    return (NULL);
1053  }
1054  List = ((SCRIPT_FILE_LIST*)GetFirstNode(&mScriptList.Link));
1055  return (List->Data);
1056}
1057
1058/**
1059  Function to set a new script as the currently running one.
1060
1061  This function will correctly stack and unstack nested scripts.
1062
1063  @param[in] Script   Pointer to new script information structure.  if NULL
1064                      will remove and de-allocate the top-most Script structure.
1065
1066  @return             A pointer to the current running script file after this
1067                      change.  NULL if removing the final script.
1068**/
1069SCRIPT_FILE*
1070EFIAPI
1071ShellCommandSetNewScript (
1072  IN SCRIPT_FILE *Script OPTIONAL
1073  )
1074{
1075  SCRIPT_FILE_LIST *Node;
1076  if (Script == NULL) {
1077    if (IsListEmpty (&mScriptList.Link)) {
1078      return (NULL);
1079    }
1080    Node = (SCRIPT_FILE_LIST *)GetFirstNode(&mScriptList.Link);
1081    RemoveEntryList(&Node->Link);
1082    DeleteScriptFileStruct(Node->Data);
1083    FreePool(Node);
1084  } else {
1085    Node = AllocateZeroPool(sizeof(SCRIPT_FILE_LIST));
1086    if (Node == NULL) {
1087      return (NULL);
1088    }
1089    Node->Data = Script;
1090    InsertHeadList(&mScriptList.Link, &Node->Link);
1091  }
1092  return (ShellCommandGetCurrentScriptFile());
1093}
1094
1095/**
1096  Function to generate the next default mapping name.
1097
1098  If the return value is not NULL then it must be callee freed.
1099
1100  @param Type                   What kind of mapping name to make.
1101
1102  @retval NULL                  a memory allocation failed.
1103  @return a new map name string
1104**/
1105CHAR16*
1106EFIAPI
1107ShellCommandCreateNewMappingName(
1108  IN CONST SHELL_MAPPING_TYPE Type
1109  )
1110{
1111  CHAR16  *String;
1112  ASSERT(Type < MappingTypeMax);
1113
1114  String = NULL;
1115
1116  String = AllocateZeroPool(PcdGet8(PcdShellMapNameLength) * sizeof(String[0]));
1117  UnicodeSPrint(
1118    String,
1119    PcdGet8(PcdShellMapNameLength) * sizeof(String[0]),
1120    Type == MappingTypeFileSystem?L"FS%d:":L"BLK%d:",
1121    Type == MappingTypeFileSystem?mFsMaxCount++:mBlkMaxCount++);
1122
1123  return (String);
1124}
1125
1126/**
1127  Function to add a map node to the list of map items and update the "path" environment variable (optionally).
1128
1129  If Path is TRUE (during initialization only), the path environment variable will also be updated to include
1130  default paths on the new map name...
1131
1132  Path should be FALSE when this function is called from the protocol SetMap function.
1133
1134  @param[in] Name               The human readable mapped name.
1135  @param[in] DevicePath         The Device Path for this map.
1136  @param[in] Flags              The Flags attribute for this map item.
1137  @param[in] Path               TRUE to update path, FALSE to skip this step (should only be TRUE during initialization).
1138
1139  @retval EFI_SUCCESS           The addition was sucessful.
1140  @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.
1141  @retval EFI_INVALID_PARAMETER A parameter was invalid.
1142**/
1143EFI_STATUS
1144EFIAPI
1145ShellCommandAddMapItemAndUpdatePath(
1146  IN CONST CHAR16                   *Name,
1147  IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
1148  IN CONST UINT64                   Flags,
1149  IN CONST BOOLEAN                  Path
1150  )
1151{
1152  EFI_STATUS      Status;
1153  SHELL_MAP_LIST  *MapListNode;
1154  CONST CHAR16    *OriginalPath;
1155  CHAR16          *NewPath;
1156  UINTN           NewPathSize;
1157
1158  NewPathSize = 0;
1159  NewPath = NULL;
1160  OriginalPath = NULL;
1161  Status = EFI_SUCCESS;
1162
1163  MapListNode = AllocateZeroPool(sizeof(SHELL_MAP_LIST));
1164  if (MapListNode == NULL) {
1165    Status = EFI_OUT_OF_RESOURCES;
1166  } else {
1167    MapListNode->Flags = Flags;
1168    MapListNode->MapName = AllocateCopyPool(StrSize(Name), Name);
1169    MapListNode->DevicePath = DuplicateDevicePath(DevicePath);
1170    if ((MapListNode->MapName == NULL) || (MapListNode->DevicePath == NULL)){
1171      Status = EFI_OUT_OF_RESOURCES;
1172    } else {
1173      InsertTailList(&gShellMapList.Link, &MapListNode->Link);
1174    }
1175  }
1176  if (EFI_ERROR(Status)) {
1177    if (MapListNode != NULL) {
1178      if (MapListNode->DevicePath != NULL) {
1179        FreePool(MapListNode->DevicePath);
1180      }
1181      if (MapListNode->MapName != NULL) {
1182        FreePool(MapListNode->MapName);
1183      }
1184      FreePool(MapListNode);
1185    }
1186  } else if (Path) {
1187    //
1188    // Since there was no error and Path was TRUE
1189    // Now add the correct path for that mapping
1190    //
1191    OriginalPath = gEfiShellProtocol->GetEnv(L"path");
1192    ASSERT((NewPath == NULL && NewPathSize == 0) || (NewPath != NULL));
1193    if (OriginalPath != NULL) {
1194      StrnCatGrow(&NewPath, &NewPathSize, OriginalPath, 0);
1195    } else {
1196      StrnCatGrow(&NewPath, &NewPathSize, L".\\", 0);
1197    }
1198    StrnCatGrow(&NewPath, &NewPathSize, L";", 0);
1199    StrnCatGrow(&NewPath, &NewPathSize, Name, 0);
1200    StrnCatGrow(&NewPath, &NewPathSize, L"\\efi\\tools\\;", 0);
1201    StrnCatGrow(&NewPath, &NewPathSize, Name, 0);
1202    StrnCatGrow(&NewPath, &NewPathSize, L"\\efi\\boot\\;", 0);
1203    StrnCatGrow(&NewPath, &NewPathSize, Name, 0);
1204    StrnCatGrow(&NewPath, &NewPathSize, L"\\", 0);
1205
1206    Status = gEfiShellProtocol->SetEnv(L"path", NewPath, TRUE);
1207    ASSERT_EFI_ERROR(Status);
1208    FreePool(NewPath);
1209  }
1210  return (Status);
1211}
1212
1213/**
1214  Creates the default map names for each device path in the system with
1215  a protocol depending on the Type.
1216
1217  Creates the consistent map names for each device path in the system with
1218  a protocol depending on the Type.
1219
1220  Note: This will reset all mappings in the system("map -r").
1221
1222  Also sets up the default path environment variable if Type is FileSystem.
1223
1224  @retval EFI_SUCCESS           All map names were created sucessfully.
1225  @retval EFI_NOT_FOUND         No protocols were found in the system.
1226  @return                       Error returned from gBS->LocateHandle().
1227
1228  @sa LocateHandle
1229**/
1230EFI_STATUS
1231EFIAPI
1232ShellCommandCreateInitialMappingsAndPaths(
1233  VOID
1234  )
1235{
1236  EFI_STATUS                Status;
1237  EFI_HANDLE                *HandleList;
1238  UINTN                     Count;
1239  EFI_DEVICE_PATH_PROTOCOL  **DevicePathList;
1240  CHAR16                    *NewDefaultName;
1241  CHAR16                    *NewConsistName;
1242  EFI_DEVICE_PATH_PROTOCOL  **ConsistMappingTable;
1243  SHELL_MAP_LIST            *MapListNode;
1244
1245  HandleList  = NULL;
1246
1247  //
1248  // Reset the static members back to zero
1249  //
1250  mFsMaxCount = 0;
1251  mBlkMaxCount = 0;
1252
1253  gEfiShellProtocol->SetEnv(L"path", L"", TRUE);
1254
1255  //
1256  // First empty out the existing list.
1257  //
1258  if (!IsListEmpty(&gShellMapList.Link)) {
1259    for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
1260        ; !IsListEmpty(&gShellMapList.Link)
1261        ; MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
1262       ){
1263          RemoveEntryList(&MapListNode->Link);
1264          SHELL_FREE_NON_NULL(MapListNode->DevicePath);
1265          SHELL_FREE_NON_NULL(MapListNode->MapName);
1266          SHELL_FREE_NON_NULL(MapListNode->CurrentDirectoryPath);
1267          FreePool(MapListNode);
1268    } // for loop
1269  }
1270
1271  //
1272  // Find each handle with Simple File System
1273  //
1274  HandleList = GetHandleListByProtocol(&gEfiSimpleFileSystemProtocolGuid);
1275  if (HandleList != NULL) {
1276    //
1277    // Do a count of the handles
1278    //
1279    for (Count = 0 ; HandleList[Count] != NULL ; Count++);
1280
1281    //
1282    // Get all Device Paths
1283    //
1284    DevicePathList = AllocateZeroPool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count);
1285    ASSERT(DevicePathList != NULL);
1286
1287    for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
1288      DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]);
1289    }
1290
1291    //
1292    // Sort all DevicePaths
1293    //
1294    PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare);
1295
1296    ShellCommandConsistMappingInitialize(&ConsistMappingTable);
1297    //
1298    // Assign new Mappings to all...
1299    //
1300    for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
1301      //
1302      // Get default name first
1303      //
1304      NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeFileSystem);
1305      ASSERT(NewDefaultName != NULL);
1306      Status = ShellCommandAddMapItemAndUpdatePath(NewDefaultName, DevicePathList[Count], 0, TRUE);
1307      ASSERT_EFI_ERROR(Status);
1308      FreePool(NewDefaultName);
1309
1310      //
1311      // Now do consistent name
1312      //
1313      NewConsistName = ShellCommandConsistMappingGenMappingName(DevicePathList[Count], ConsistMappingTable);
1314      if (NewConsistName != NULL) {
1315        Status = ShellCommandAddMapItemAndUpdatePath(NewConsistName, DevicePathList[Count], 0, FALSE);
1316        ASSERT_EFI_ERROR(Status);
1317        FreePool(NewConsistName);
1318      }
1319    }
1320
1321    ShellCommandConsistMappingUnInitialize(ConsistMappingTable);
1322
1323    SHELL_FREE_NON_NULL(HandleList);
1324    SHELL_FREE_NON_NULL(DevicePathList);
1325
1326    HandleList = NULL;
1327  } else {
1328    Count = (UINTN)-1;
1329  }
1330
1331  //
1332  // Find each handle with Block Io
1333  //
1334  HandleList = GetHandleListByProtocol(&gEfiBlockIoProtocolGuid);
1335  if (HandleList != NULL) {
1336    for (Count = 0 ; HandleList[Count] != NULL ; Count++);
1337
1338    //
1339    // Get all Device Paths
1340    //
1341    DevicePathList = AllocateZeroPool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count);
1342    ASSERT(DevicePathList != NULL);
1343
1344    for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
1345      DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]);
1346    }
1347
1348    //
1349    // Sort all DevicePaths
1350    //
1351    PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare);
1352
1353    //
1354    // Assign new Mappings to all...
1355    //
1356    for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
1357      //
1358      // Get default name first
1359      //
1360      NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeBlockIo);
1361      ASSERT(NewDefaultName != NULL);
1362      Status = ShellCommandAddMapItemAndUpdatePath(NewDefaultName, DevicePathList[Count], 0, FALSE);
1363      ASSERT_EFI_ERROR(Status);
1364      FreePool(NewDefaultName);
1365    }
1366
1367    SHELL_FREE_NON_NULL(HandleList);
1368    SHELL_FREE_NON_NULL(DevicePathList);
1369  } else if (Count == (UINTN)-1) {
1370    return (EFI_NOT_FOUND);
1371  }
1372
1373  return (EFI_SUCCESS);
1374}
1375
1376/**
1377  Add mappings for any devices without one.  Do not change any existing maps.
1378
1379  @retval EFI_SUCCESS   The operation was successful.
1380**/
1381EFI_STATUS
1382EFIAPI
1383ShellCommandUpdateMapping (
1384  VOID
1385  )
1386{
1387  EFI_STATUS                Status;
1388  EFI_HANDLE                *HandleList;
1389  UINTN                     Count;
1390  EFI_DEVICE_PATH_PROTOCOL  **DevicePathList;
1391  CHAR16                    *NewDefaultName;
1392  CHAR16                    *NewConsistName;
1393  EFI_DEVICE_PATH_PROTOCOL  **ConsistMappingTable;
1394
1395  HandleList  = NULL;
1396  Status      = EFI_SUCCESS;
1397
1398  //
1399  // remove mappings that represent removed devices.
1400  //
1401
1402  //
1403  // Find each handle with Simple File System
1404  //
1405  HandleList = GetHandleListByProtocol(&gEfiSimpleFileSystemProtocolGuid);
1406  if (HandleList != NULL) {
1407    //
1408    // Do a count of the handles
1409    //
1410    for (Count = 0 ; HandleList[Count] != NULL ; Count++);
1411
1412    //
1413    // Get all Device Paths
1414    //
1415    DevicePathList = AllocateZeroPool(sizeof(EFI_DEVICE_PATH_PROTOCOL*) * Count);
1416    if (DevicePathList == NULL) {
1417      return (EFI_OUT_OF_RESOURCES);
1418    }
1419
1420    for (Count = 0 ; HandleList[Count] != NULL ; Count++) {
1421      DevicePathList[Count] = DevicePathFromHandle(HandleList[Count]);
1422    }
1423
1424    //
1425    // Sort all DevicePaths
1426    //
1427    PerformQuickSort(DevicePathList, Count, sizeof(EFI_DEVICE_PATH_PROTOCOL*), DevicePathCompare);
1428
1429    ShellCommandConsistMappingInitialize(&ConsistMappingTable);
1430
1431    //
1432    // Assign new Mappings to remainders
1433    //
1434    for (Count = 0 ; !EFI_ERROR(Status) && HandleList[Count] != NULL && !EFI_ERROR(Status); Count++) {
1435      //
1436      // Skip ones that already have
1437      //
1438      if (gEfiShellProtocol->GetMapFromDevicePath(&DevicePathList[Count]) != NULL) {
1439        continue;
1440      }
1441      //
1442      // Get default name
1443      //
1444      NewDefaultName = ShellCommandCreateNewMappingName(MappingTypeFileSystem);
1445      if (NewDefaultName == NULL) {
1446        Status = EFI_OUT_OF_RESOURCES;
1447        break;
1448      }
1449
1450      //
1451      // Call shell protocol SetMap function now...
1452      //
1453      Status = gEfiShellProtocol->SetMap(DevicePathList[Count], NewDefaultName);
1454
1455      if (!EFI_ERROR(Status)) {
1456        //
1457        // Now do consistent name
1458        //
1459        NewConsistName = ShellCommandConsistMappingGenMappingName(DevicePathList[Count], ConsistMappingTable);
1460        if (NewConsistName != NULL) {
1461          Status = gEfiShellProtocol->SetMap(DevicePathList[Count], NewConsistName);
1462          FreePool(NewConsistName);
1463        }
1464      }
1465
1466      FreePool(NewDefaultName);
1467    }
1468    ShellCommandConsistMappingUnInitialize(ConsistMappingTable);
1469    SHELL_FREE_NON_NULL(HandleList);
1470    SHELL_FREE_NON_NULL(DevicePathList);
1471
1472    HandleList = NULL;
1473  } else {
1474    Count = (UINTN)-1;
1475  }
1476  //
1477  // Do it all over again for gEfiBlockIoProtocolGuid
1478  //
1479
1480  return (Status);
1481}
1482
1483/**
1484  Converts a SHELL_FILE_HANDLE to an EFI_FILE_PROTOCOL*.
1485
1486  @param[in] Handle     The SHELL_FILE_HANDLE to convert.
1487
1488  @return a EFI_FILE_PROTOCOL* representing the same file.
1489**/
1490EFI_FILE_PROTOCOL*
1491EFIAPI
1492ConvertShellHandleToEfiFileProtocol(
1493  IN CONST SHELL_FILE_HANDLE Handle
1494  )
1495{
1496  return ((EFI_FILE_PROTOCOL*)(Handle));
1497}
1498
1499/**
1500  Converts a EFI_FILE_PROTOCOL* to an SHELL_FILE_HANDLE.
1501
1502  @param[in] Handle     The pointer to EFI_FILE_PROTOCOL to convert.
1503  @param[in] Path       The path to the file for verification.
1504
1505  @return               A SHELL_FILE_HANDLE representing the same file.
1506  @retval NULL          There was not enough memory.
1507**/
1508SHELL_FILE_HANDLE
1509EFIAPI
1510ConvertEfiFileProtocolToShellHandle(
1511  IN CONST EFI_FILE_PROTOCOL *Handle,
1512  IN CONST CHAR16            *Path
1513  )
1514{
1515  SHELL_COMMAND_FILE_HANDLE *Buffer;
1516  BUFFER_LIST               *NewNode;
1517
1518  if (Path != NULL) {
1519    Buffer              = AllocateZeroPool(sizeof(SHELL_COMMAND_FILE_HANDLE));
1520    if (Buffer == NULL) {
1521      return (NULL);
1522    }
1523    NewNode             = AllocateZeroPool(sizeof(BUFFER_LIST));
1524    if (NewNode == NULL) {
1525      SHELL_FREE_NON_NULL(Buffer);
1526      return (NULL);
1527    }
1528    Buffer->FileHandle  = (EFI_FILE_PROTOCOL*)Handle;
1529    Buffer->Path        = StrnCatGrow(&Buffer->Path, NULL, Path, 0);
1530    if (Buffer->Path == NULL) {
1531      SHELL_FREE_NON_NULL(NewNode);
1532      SHELL_FREE_NON_NULL(Buffer);
1533      return (NULL);
1534    }
1535    NewNode->Buffer     = Buffer;
1536
1537    InsertHeadList(&mFileHandleList.Link, &NewNode->Link);
1538  }
1539  return ((SHELL_FILE_HANDLE)(Handle));
1540}
1541
1542/**
1543  Find the path that was logged with the specified SHELL_FILE_HANDLE.
1544
1545  @param[in] Handle     The SHELL_FILE_HANDLE to query on.
1546
1547  @return A pointer to the path for the file.
1548**/
1549CONST CHAR16*
1550EFIAPI
1551ShellFileHandleGetPath(
1552  IN CONST SHELL_FILE_HANDLE Handle
1553  )
1554{
1555  BUFFER_LIST               *Node;
1556
1557  for (Node = (BUFFER_LIST*)GetFirstNode(&mFileHandleList.Link)
1558    ;  !IsNull(&mFileHandleList.Link, &Node->Link)
1559    ;  Node = (BUFFER_LIST*)GetNextNode(&mFileHandleList.Link, &Node->Link)
1560   ){
1561    if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)){
1562      return (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path);
1563    }
1564  }
1565  return (NULL);
1566}
1567
1568/**
1569  Remove a SHELL_FILE_HANDLE from the list of SHELL_FILE_HANDLES.
1570
1571  @param[in] Handle     The SHELL_FILE_HANDLE to remove.
1572
1573  @retval TRUE          The item was removed.
1574  @retval FALSE         The item was not found.
1575**/
1576BOOLEAN
1577EFIAPI
1578ShellFileHandleRemove(
1579  IN CONST SHELL_FILE_HANDLE Handle
1580  )
1581{
1582  BUFFER_LIST               *Node;
1583
1584  for (Node = (BUFFER_LIST*)GetFirstNode(&mFileHandleList.Link)
1585    ;  !IsNull(&mFileHandleList.Link, &Node->Link)
1586    ;  Node = (BUFFER_LIST*)GetNextNode(&mFileHandleList.Link, &Node->Link)
1587   ){
1588    if ((Node->Buffer) && (((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->FileHandle == Handle)){
1589      RemoveEntryList(&Node->Link);
1590      SHELL_FREE_NON_NULL(((SHELL_COMMAND_FILE_HANDLE *)Node->Buffer)->Path);
1591      SHELL_FREE_NON_NULL(Node->Buffer);
1592      SHELL_FREE_NON_NULL(Node);
1593      return (TRUE);
1594    }
1595  }
1596  return (FALSE);
1597}
1598
1599/**
1600  Function to determine if a SHELL_FILE_HANDLE is at the end of the file.
1601
1602  This will NOT work on directories.
1603
1604  If Handle is NULL, then ASSERT.
1605
1606  @param[in] Handle     the file handle
1607
1608  @retval TRUE          the position is at the end of the file
1609  @retval FALSE         the position is not at the end of the file
1610**/
1611BOOLEAN
1612EFIAPI
1613ShellFileHandleEof(
1614  IN SHELL_FILE_HANDLE Handle
1615  )
1616{
1617  EFI_FILE_INFO *Info;
1618  UINT64        Pos;
1619  BOOLEAN       RetVal;
1620
1621  //
1622  // ASSERT if Handle is NULL
1623  //
1624  ASSERT(Handle != NULL);
1625
1626  gEfiShellProtocol->GetFilePosition(Handle, &Pos);
1627  Info = gEfiShellProtocol->GetFileInfo (Handle);
1628  gEfiShellProtocol->SetFilePosition(Handle, Pos);
1629
1630  if (Info == NULL) {
1631    return (FALSE);
1632  }
1633
1634  if (Pos == Info->FileSize) {
1635    RetVal = TRUE;
1636  } else {
1637    RetVal = FALSE;
1638  }
1639
1640  FreePool (Info);
1641
1642  return (RetVal);
1643}
1644
1645/**
1646  Frees any BUFFER_LIST defined type.
1647
1648  @param[in] List     The BUFFER_LIST object to free.
1649**/
1650VOID
1651EFIAPI
1652FreeBufferList (
1653  IN BUFFER_LIST *List
1654  )
1655{
1656  BUFFER_LIST               *BufferListEntry;
1657
1658  if (List == NULL){
1659    return;
1660  }
1661  //
1662  // enumerate through the buffer list and free all memory
1663  //
1664  for ( BufferListEntry = ( BUFFER_LIST *)GetFirstNode(&List->Link)
1665      ; !IsListEmpty (&List->Link)
1666      ; BufferListEntry = (BUFFER_LIST *)GetFirstNode(&List->Link)
1667     ){
1668    RemoveEntryList(&BufferListEntry->Link);
1669    if (BufferListEntry->Buffer != NULL) {
1670      FreePool(BufferListEntry->Buffer);
1671    }
1672    FreePool(BufferListEntry);
1673  }
1674}
1675
1676