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