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