Home | History | Annotate | Download | only in Shell
      1 /** @file
      2   This is THE shell (application)
      3 
      4   Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
      5   (C) Copyright 2013-2014 Hewlett-Packard Development Company, L.P.<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 "Shell.h"
     17 
     18 //
     19 // Initialize the global structure
     20 //
     21 SHELL_INFO ShellInfoObject = {
     22   NULL,
     23   NULL,
     24   FALSE,
     25   FALSE,
     26   {
     27     {{
     28       0,
     29       0,
     30       0,
     31       0,
     32       0,
     33       0,
     34       0,
     35       0,
     36       0
     37     }},
     38     0,
     39     NULL,
     40     NULL
     41   },
     42   {{NULL, NULL}, NULL},
     43   {
     44     {{NULL, NULL}, NULL},
     45     0,
     46     0,
     47     TRUE
     48   },
     49   NULL,
     50   0,
     51   NULL,
     52   NULL,
     53   NULL,
     54   NULL,
     55   NULL,
     56   {{NULL, NULL}, NULL, NULL},
     57   {{NULL, NULL}, NULL, NULL},
     58   NULL,
     59   NULL,
     60   NULL,
     61   NULL,
     62   NULL,
     63   NULL,
     64   NULL,
     65   NULL,
     66   FALSE
     67 };
     68 
     69 STATIC CONST CHAR16 mScriptExtension[]      = L".NSH";
     70 STATIC CONST CHAR16 mExecutableExtensions[] = L".NSH;.EFI";
     71 STATIC CONST CHAR16 mStartupScript[]        = L"startup.nsh";
     72 
     73 /**
     74   Cleans off leading and trailing spaces and tabs.
     75 
     76   @param[in] String pointer to the string to trim them off.
     77 **/
     78 EFI_STATUS
     79 EFIAPI
     80 TrimSpaces(
     81   IN CHAR16 **String
     82   )
     83 {
     84   ASSERT(String != NULL);
     85   ASSERT(*String!= NULL);
     86   //
     87   // Remove any spaces and tabs at the beginning of the (*String).
     88   //
     89   while (((*String)[0] == L' ') || ((*String)[0] == L'\t')) {
     90     CopyMem((*String), (*String)+1, StrSize((*String)) - sizeof((*String)[0]));
     91   }
     92 
     93   //
     94   // Remove any spaces and tabs at the end of the (*String).
     95   //
     96   while ((StrLen (*String) > 0) && (((*String)[StrLen((*String))-1] == L' ') || ((*String)[StrLen((*String))-1] == L'\t'))) {
     97     (*String)[StrLen((*String))-1] = CHAR_NULL;
     98   }
     99 
    100   return (EFI_SUCCESS);
    101 }
    102 
    103 /**
    104   Parse for the next instance of one string within another string. Can optionally make sure that
    105   the string was not escaped (^ character) per the shell specification.
    106 
    107   @param[in] SourceString             The string to search within
    108   @param[in] FindString               The string to look for
    109   @param[in] CheckForEscapeCharacter  TRUE to skip escaped instances of FinfString, otherwise will return even escaped instances
    110 **/
    111 CHAR16*
    112 EFIAPI
    113 FindNextInstance(
    114   IN CONST CHAR16   *SourceString,
    115   IN CONST CHAR16   *FindString,
    116   IN CONST BOOLEAN  CheckForEscapeCharacter
    117   )
    118 {
    119   CHAR16 *Temp;
    120   if (SourceString == NULL) {
    121     return (NULL);
    122   }
    123   Temp = StrStr(SourceString, FindString);
    124 
    125   //
    126   // If nothing found, or we don't care about escape characters
    127   //
    128   if (Temp == NULL || !CheckForEscapeCharacter) {
    129     return (Temp);
    130   }
    131 
    132   //
    133   // If we found an escaped character, try again on the remainder of the string
    134   //
    135   if ((Temp > (SourceString)) && *(Temp-1) == L'^') {
    136     return FindNextInstance(Temp+1, FindString, CheckForEscapeCharacter);
    137   }
    138 
    139   //
    140   // we found the right character
    141   //
    142   return (Temp);
    143 }
    144 
    145 /**
    146   Check whether the string between a pair of % is a valid environment variable name.
    147 
    148   @param[in] BeginPercent       pointer to the first percent.
    149   @param[in] EndPercent          pointer to the last percent.
    150 
    151   @retval TRUE                          is a valid environment variable name.
    152   @retval FALSE                         is NOT a valid environment variable name.
    153 **/
    154 BOOLEAN
    155 IsValidEnvironmentVariableName(
    156   IN CONST CHAR16     *BeginPercent,
    157   IN CONST CHAR16     *EndPercent
    158   )
    159 {
    160   CONST CHAR16    *Walker;
    161 
    162   Walker = NULL;
    163 
    164   ASSERT (BeginPercent != NULL);
    165   ASSERT (EndPercent != NULL);
    166   ASSERT (BeginPercent < EndPercent);
    167 
    168   if ((BeginPercent + 1) == EndPercent) {
    169     return FALSE;
    170   }
    171 
    172   for (Walker = BeginPercent + 1; Walker < EndPercent; Walker++) {
    173     if (
    174         (*Walker >= L'0' && *Walker <= L'9') ||
    175         (*Walker >= L'A' && *Walker <= L'Z') ||
    176         (*Walker >= L'a' && *Walker <= L'z') ||
    177         (*Walker == L'_')
    178       ) {
    179       if (Walker == BeginPercent + 1 && (*Walker >= L'0' && *Walker <= L'9')) {
    180         return FALSE;
    181       } else {
    182         continue;
    183       }
    184     } else {
    185       return FALSE;
    186     }
    187   }
    188 
    189   return TRUE;
    190 }
    191 
    192 /**
    193   Determine if a command line contains a split operation
    194 
    195   @param[in] CmdLine      The command line to parse.
    196 
    197   @retval TRUE            CmdLine has a valid split.
    198   @retval FALSE           CmdLine does not have a valid split.
    199 **/
    200 BOOLEAN
    201 EFIAPI
    202 ContainsSplit(
    203   IN CONST CHAR16 *CmdLine
    204   )
    205 {
    206   CONST CHAR16 *TempSpot;
    207   CONST CHAR16 *FirstQuote;
    208   CONST CHAR16 *SecondQuote;
    209 
    210   FirstQuote    = FindNextInstance (CmdLine, L"\"", TRUE);
    211   SecondQuote   = NULL;
    212   TempSpot      = FindFirstCharacter(CmdLine, L"|", L'^');
    213 
    214   if (FirstQuote == NULL    ||
    215       TempSpot == NULL      ||
    216       TempSpot == CHAR_NULL ||
    217       FirstQuote > TempSpot
    218       ) {
    219     return (BOOLEAN) ((TempSpot != NULL) && (*TempSpot != CHAR_NULL));
    220   }
    221 
    222   while ((TempSpot != NULL) && (*TempSpot != CHAR_NULL)) {
    223     if (FirstQuote == NULL || FirstQuote > TempSpot) {
    224       break;
    225     }
    226     SecondQuote = FindNextInstance (FirstQuote + 1, L"\"", TRUE);
    227     if (SecondQuote == NULL) {
    228       break;
    229     }
    230     if (SecondQuote < TempSpot) {
    231       FirstQuote = FindNextInstance (SecondQuote + 1, L"\"", TRUE);
    232       continue;
    233     } else {
    234       FirstQuote = FindNextInstance (SecondQuote + 1, L"\"", TRUE);
    235       TempSpot = FindFirstCharacter(TempSpot + 1, L"|", L'^');
    236       continue;
    237     }
    238   }
    239 
    240   return (BOOLEAN) ((TempSpot != NULL) && (*TempSpot != CHAR_NULL));
    241 }
    242 
    243 /**
    244   Function to start monitoring for CTRL-S using SimpleTextInputEx.  This
    245   feature's enabled state was not known when the shell initially launched.
    246 
    247   @retval EFI_SUCCESS           The feature is enabled.
    248   @retval EFI_OUT_OF_RESOURCES  There is not enough memory available.
    249 **/
    250 EFI_STATUS
    251 EFIAPI
    252 InternalEfiShellStartCtrlSMonitor(
    253   VOID
    254   )
    255 {
    256   EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
    257   EFI_KEY_DATA                      KeyData;
    258   EFI_STATUS                        Status;
    259 
    260   Status = gBS->OpenProtocol(
    261     gST->ConsoleInHandle,
    262     &gEfiSimpleTextInputExProtocolGuid,
    263     (VOID**)&SimpleEx,
    264     gImageHandle,
    265     NULL,
    266     EFI_OPEN_PROTOCOL_GET_PROTOCOL);
    267   if (EFI_ERROR(Status)) {
    268     ShellPrintHiiEx(
    269       -1,
    270       -1,
    271       NULL,
    272       STRING_TOKEN (STR_SHELL_NO_IN_EX),
    273       ShellInfoObject.HiiHandle);
    274     return (EFI_SUCCESS);
    275   }
    276 
    277   KeyData.KeyState.KeyToggleState = 0;
    278   KeyData.Key.ScanCode            = 0;
    279   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
    280   KeyData.Key.UnicodeChar         = L's';
    281 
    282   Status = SimpleEx->RegisterKeyNotify(
    283     SimpleEx,
    284     &KeyData,
    285     NotificationFunction,
    286     &ShellInfoObject.CtrlSNotifyHandle1);
    287 
    288   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
    289   if (!EFI_ERROR(Status)) {
    290     Status = SimpleEx->RegisterKeyNotify(
    291       SimpleEx,
    292       &KeyData,
    293       NotificationFunction,
    294       &ShellInfoObject.CtrlSNotifyHandle2);
    295   }
    296   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
    297   KeyData.Key.UnicodeChar         = 19;
    298 
    299   if (!EFI_ERROR(Status)) {
    300     Status = SimpleEx->RegisterKeyNotify(
    301       SimpleEx,
    302       &KeyData,
    303       NotificationFunction,
    304       &ShellInfoObject.CtrlSNotifyHandle3);
    305   }
    306   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
    307   if (!EFI_ERROR(Status)) {
    308     Status = SimpleEx->RegisterKeyNotify(
    309       SimpleEx,
    310       &KeyData,
    311       NotificationFunction,
    312       &ShellInfoObject.CtrlSNotifyHandle4);
    313   }
    314   return (Status);
    315 }
    316 
    317 
    318 
    319 /**
    320   The entry point for the application.
    321 
    322   @param[in] ImageHandle    The firmware allocated handle for the EFI image.
    323   @param[in] SystemTable    A pointer to the EFI System Table.
    324 
    325   @retval EFI_SUCCESS       The entry point is executed successfully.
    326   @retval other             Some error occurs when executing this entry point.
    327 
    328 **/
    329 EFI_STATUS
    330 EFIAPI
    331 UefiMain (
    332   IN EFI_HANDLE        ImageHandle,
    333   IN EFI_SYSTEM_TABLE  *SystemTable
    334   )
    335 {
    336   EFI_STATUS                      Status;
    337   CHAR16                          *TempString;
    338   UINTN                           Size;
    339   EFI_HANDLE                      ConInHandle;
    340   EFI_SIMPLE_TEXT_INPUT_PROTOCOL  *OldConIn;
    341 
    342   if (PcdGet8(PcdShellSupportLevel) > 3) {
    343     return (EFI_UNSUPPORTED);
    344   }
    345 
    346   //
    347   // Clear the screen
    348   //
    349   Status = gST->ConOut->ClearScreen(gST->ConOut);
    350   if (EFI_ERROR(Status)) {
    351     return (Status);
    352   }
    353 
    354   //
    355   // Populate the global structure from PCDs
    356   //
    357   ShellInfoObject.ImageDevPath                = NULL;
    358   ShellInfoObject.FileDevPath                 = NULL;
    359   ShellInfoObject.PageBreakEnabled            = PcdGetBool(PcdShellPageBreakDefault);
    360   ShellInfoObject.ViewingSettings.InsertMode  = PcdGetBool(PcdShellInsertModeDefault);
    361   ShellInfoObject.LogScreenCount              = PcdGet8   (PcdShellScreenLogCount  );
    362 
    363   //
    364   // verify we dont allow for spec violation
    365   //
    366   ASSERT(ShellInfoObject.LogScreenCount >= 3);
    367 
    368   //
    369   // Initialize the LIST ENTRY objects...
    370   //
    371   InitializeListHead(&ShellInfoObject.BufferToFreeList.Link);
    372   InitializeListHead(&ShellInfoObject.ViewingSettings.CommandHistory.Link);
    373   InitializeListHead(&ShellInfoObject.SplitList.Link);
    374 
    375   //
    376   // Check PCDs for optional features that are not implemented yet.
    377   //
    378   if (   PcdGetBool(PcdShellSupportOldProtocols)
    379       || !FeaturePcdGet(PcdShellRequireHiiPlatform)
    380       || FeaturePcdGet(PcdShellSupportFrameworkHii)
    381    ) {
    382     return (EFI_UNSUPPORTED);
    383   }
    384 
    385   //
    386   // turn off the watchdog timer
    387   //
    388   gBS->SetWatchdogTimer (0, 0, 0, NULL);
    389 
    390   //
    391   // install our console logger.  This will keep a log of the output for back-browsing
    392   //
    393   Status = ConsoleLoggerInstall(ShellInfoObject.LogScreenCount, &ShellInfoObject.ConsoleInfo);
    394   if (!EFI_ERROR(Status)) {
    395     //
    396     // Enable the cursor to be visible
    397     //
    398     gST->ConOut->EnableCursor (gST->ConOut, TRUE);
    399 
    400     //
    401     // If supporting EFI 1.1 we need to install HII protocol
    402     // only do this if PcdShellRequireHiiPlatform == FALSE
    403     //
    404     // remove EFI_UNSUPPORTED check above when complete.
    405     ///@todo add support for Framework HII
    406 
    407     //
    408     // install our (solitary) HII package
    409     //
    410     ShellInfoObject.HiiHandle = HiiAddPackages (&gEfiCallerIdGuid, gImageHandle, ShellStrings, NULL);
    411     if (ShellInfoObject.HiiHandle == NULL) {
    412       if (PcdGetBool(PcdShellSupportFrameworkHii)) {
    413         ///@todo Add our package into Framework HII
    414       }
    415       if (ShellInfoObject.HiiHandle == NULL) {
    416         Status = EFI_NOT_STARTED;
    417         goto FreeResources;
    418       }
    419     }
    420 
    421     //
    422     // create and install the EfiShellParametersProtocol
    423     //
    424     Status = CreatePopulateInstallShellParametersProtocol(&ShellInfoObject.NewShellParametersProtocol, &ShellInfoObject.RootShellInstance);
    425     ASSERT_EFI_ERROR(Status);
    426     ASSERT(ShellInfoObject.NewShellParametersProtocol != NULL);
    427 
    428     //
    429     // create and install the EfiShellProtocol
    430     //
    431     Status = CreatePopulateInstallShellProtocol(&ShellInfoObject.NewEfiShellProtocol);
    432     ASSERT_EFI_ERROR(Status);
    433     ASSERT(ShellInfoObject.NewEfiShellProtocol != NULL);
    434 
    435     //
    436     // Now initialize the shell library (it requires Shell Parameters protocol)
    437     //
    438     Status = ShellInitialize();
    439     ASSERT_EFI_ERROR(Status);
    440 
    441     Status = CommandInit();
    442     ASSERT_EFI_ERROR(Status);
    443 
    444     //
    445     // Check the command line
    446     //
    447     Status = ProcessCommandLine ();
    448     if (EFI_ERROR (Status)) {
    449       goto FreeResources;
    450     }
    451 
    452     //
    453     // If shell support level is >= 1 create the mappings and paths
    454     //
    455     if (PcdGet8(PcdShellSupportLevel) >= 1) {
    456       Status = ShellCommandCreateInitialMappingsAndPaths();
    457     }
    458 
    459     //
    460     // save the device path for the loaded image and the device path for the filepath (under loaded image)
    461     // These are where to look for the startup.nsh file
    462     //
    463     Status = GetDevicePathsForImageAndFile(&ShellInfoObject.ImageDevPath, &ShellInfoObject.FileDevPath);
    464     ASSERT_EFI_ERROR(Status);
    465 
    466     //
    467     // Display the version
    468     //
    469     if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion) {
    470       ShellPrintHiiEx (
    471         0,
    472         gST->ConOut->Mode->CursorRow,
    473         NULL,
    474         STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL),
    475         ShellInfoObject.HiiHandle,
    476         SupportLevel[PcdGet8(PcdShellSupportLevel)],
    477         gEfiShellProtocol->MajorVersion,
    478         gEfiShellProtocol->MinorVersion
    479        );
    480 
    481       ShellPrintHiiEx (
    482         -1,
    483         -1,
    484         NULL,
    485         STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER),
    486         ShellInfoObject.HiiHandle,
    487         (CHAR16 *) PcdGetPtr (PcdShellSupplier)
    488        );
    489 
    490       ShellPrintHiiEx (
    491         -1,
    492         -1,
    493         NULL,
    494         STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI),
    495         ShellInfoObject.HiiHandle,
    496         (gST->Hdr.Revision&0xffff0000)>>16,
    497         (gST->Hdr.Revision&0x0000ffff),
    498         gST->FirmwareVendor,
    499         gST->FirmwareRevision
    500        );
    501     }
    502 
    503     //
    504     // Display the mapping
    505     //
    506     if (PcdGet8(PcdShellSupportLevel) >= 2 && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap) {
    507       Status = RunCommand(L"map");
    508       ASSERT_EFI_ERROR(Status);
    509     }
    510 
    511     //
    512     // init all the built in alias'
    513     //
    514     Status = SetBuiltInAlias();
    515     ASSERT_EFI_ERROR(Status);
    516 
    517     //
    518     // Initialize environment variables
    519     //
    520     if (ShellCommandGetProfileList() != NULL) {
    521       Status = InternalEfiShellSetEnv(L"profiles", ShellCommandGetProfileList(), TRUE);
    522       ASSERT_EFI_ERROR(Status);
    523     }
    524 
    525     Size        = 100;
    526     TempString  = AllocateZeroPool(Size);
    527 
    528     UnicodeSPrint(TempString, Size, L"%d", PcdGet8(PcdShellSupportLevel));
    529     Status = InternalEfiShellSetEnv(L"uefishellsupport", TempString, TRUE);
    530     ASSERT_EFI_ERROR(Status);
    531 
    532     UnicodeSPrint(TempString, Size, L"%d.%d", ShellInfoObject.NewEfiShellProtocol->MajorVersion, ShellInfoObject.NewEfiShellProtocol->MinorVersion);
    533     Status = InternalEfiShellSetEnv(L"uefishellversion", TempString, TRUE);
    534     ASSERT_EFI_ERROR(Status);
    535 
    536     UnicodeSPrint(TempString, Size, L"%d.%d", (gST->Hdr.Revision & 0xFFFF0000) >> 16, gST->Hdr.Revision & 0x0000FFFF);
    537     Status = InternalEfiShellSetEnv(L"uefiversion", TempString, TRUE);
    538     ASSERT_EFI_ERROR(Status);
    539 
    540     FreePool(TempString);
    541 
    542     if (!EFI_ERROR(Status)) {
    543       if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {
    544         //
    545         // Set up the event for CTRL-C monitoring...
    546         //
    547         Status = InernalEfiShellStartMonitor();
    548       }
    549 
    550       if (!EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
    551         //
    552         // Set up the event for CTRL-S monitoring...
    553         //
    554         Status = InternalEfiShellStartCtrlSMonitor();
    555       }
    556 
    557       if (!EFI_ERROR(Status) && ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
    558         //
    559         // close off the gST->ConIn
    560         //
    561         OldConIn      = gST->ConIn;
    562         ConInHandle   = gST->ConsoleInHandle;
    563         gST->ConIn = CreateSimpleTextInOnFile((SHELL_FILE_HANDLE)&FileInterfaceNulFile, &gST->ConsoleInHandle);
    564       } else {
    565         OldConIn      = NULL;
    566         ConInHandle   = NULL;
    567       }
    568 
    569       if (!EFI_ERROR(Status) && PcdGet8(PcdShellSupportLevel) >= 1) {
    570         //
    571         // process the startup script or launch the called app.
    572         //
    573         Status = DoStartupScript(ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);
    574       }
    575 
    576       if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit && !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel) >= 3 || PcdGetBool(PcdShellForceConsole)) && !EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
    577         //
    578         // begin the UI waiting loop
    579         //
    580         do {
    581           //
    582           // clean out all the memory allocated for CONST <something> * return values
    583           // between each shell prompt presentation
    584           //
    585           if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){
    586             FreeBufferList(&ShellInfoObject.BufferToFreeList);
    587           }
    588 
    589           //
    590           // Reset page break back to default.
    591           //
    592           ShellInfoObject.PageBreakEnabled        = PcdGetBool(PcdShellPageBreakDefault);
    593           ASSERT (ShellInfoObject.ConsoleInfo != NULL);
    594           ShellInfoObject.ConsoleInfo->Enabled    = TRUE;
    595           ShellInfoObject.ConsoleInfo->RowCounter = 0;
    596 
    597           //
    598           // Reset the CTRL-C event (yes we ignore the return values)
    599           //
    600           Status = gBS->CheckEvent (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak);
    601 
    602           //
    603           // Display Prompt
    604           //
    605           Status = DoShellPrompt();
    606         } while (!ShellCommandGetExit());
    607       }
    608       if (OldConIn != NULL && ConInHandle != NULL) {
    609         CloseSimpleTextInOnFile (gST->ConIn);
    610         gST->ConIn            = OldConIn;
    611         gST->ConsoleInHandle  = ConInHandle;
    612       }
    613     }
    614   }
    615 
    616 FreeResources:
    617   //
    618   // uninstall protocols / free memory / etc...
    619   //
    620   if (ShellInfoObject.UserBreakTimer != NULL) {
    621     gBS->CloseEvent(ShellInfoObject.UserBreakTimer);
    622     DEBUG_CODE(ShellInfoObject.UserBreakTimer = NULL;);
    623   }
    624   if (ShellInfoObject.ImageDevPath != NULL) {
    625     FreePool(ShellInfoObject.ImageDevPath);
    626     DEBUG_CODE(ShellInfoObject.ImageDevPath = NULL;);
    627   }
    628   if (ShellInfoObject.FileDevPath != NULL) {
    629     FreePool(ShellInfoObject.FileDevPath);
    630     DEBUG_CODE(ShellInfoObject.FileDevPath = NULL;);
    631   }
    632   if (ShellInfoObject.NewShellParametersProtocol != NULL) {
    633     CleanUpShellParametersProtocol(ShellInfoObject.NewShellParametersProtocol);
    634     DEBUG_CODE(ShellInfoObject.NewShellParametersProtocol = NULL;);
    635   }
    636   if (ShellInfoObject.NewEfiShellProtocol != NULL){
    637     if (ShellInfoObject.NewEfiShellProtocol->IsRootShell()){
    638       InternalEfiShellSetEnv(L"cwd", NULL, TRUE);
    639     }
    640     CleanUpShellProtocol(ShellInfoObject.NewEfiShellProtocol);
    641     DEBUG_CODE(ShellInfoObject.NewEfiShellProtocol = NULL;);
    642   }
    643 
    644   if (!IsListEmpty(&ShellInfoObject.BufferToFreeList.Link)){
    645     FreeBufferList(&ShellInfoObject.BufferToFreeList);
    646   }
    647 
    648   if (!IsListEmpty(&ShellInfoObject.SplitList.Link)){
    649     ASSERT(FALSE); ///@todo finish this de-allocation.
    650   }
    651 
    652   if (ShellInfoObject.ShellInitSettings.FileName != NULL) {
    653     FreePool(ShellInfoObject.ShellInitSettings.FileName);
    654     DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileName = NULL;);
    655   }
    656 
    657   if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
    658     FreePool(ShellInfoObject.ShellInitSettings.FileOptions);
    659     DEBUG_CODE(ShellInfoObject.ShellInitSettings.FileOptions = NULL;);
    660   }
    661 
    662   if (ShellInfoObject.HiiHandle != NULL) {
    663     HiiRemovePackages(ShellInfoObject.HiiHandle);
    664     DEBUG_CODE(ShellInfoObject.HiiHandle = NULL;);
    665   }
    666 
    667   if (!IsListEmpty(&ShellInfoObject.ViewingSettings.CommandHistory.Link)){
    668     FreeBufferList(&ShellInfoObject.ViewingSettings.CommandHistory);
    669   }
    670 
    671   ASSERT(ShellInfoObject.ConsoleInfo != NULL);
    672   if (ShellInfoObject.ConsoleInfo != NULL) {
    673     ConsoleLoggerUninstall(ShellInfoObject.ConsoleInfo);
    674     FreePool(ShellInfoObject.ConsoleInfo);
    675     DEBUG_CODE(ShellInfoObject.ConsoleInfo = NULL;);
    676   }
    677 
    678   if (ShellCommandGetExit()) {
    679     return ((EFI_STATUS)ShellCommandGetExitCode());
    680   }
    681   return (Status);
    682 }
    683 
    684 /**
    685   Sets all the alias' that were registered with the ShellCommandLib library.
    686 
    687   @retval EFI_SUCCESS           all init commands were run successfully.
    688 **/
    689 EFI_STATUS
    690 EFIAPI
    691 SetBuiltInAlias(
    692   )
    693 {
    694   EFI_STATUS          Status;
    695   CONST ALIAS_LIST    *List;
    696   ALIAS_LIST          *Node;
    697 
    698   //
    699   // Get all the commands we want to run
    700   //
    701   List = ShellCommandGetInitAliasList();
    702 
    703   //
    704   // for each command in the List
    705   //
    706   for ( Node = (ALIAS_LIST*)GetFirstNode(&List->Link)
    707       ; !IsNull (&List->Link, &Node->Link)
    708       ; Node = (ALIAS_LIST *)GetNextNode(&List->Link, &Node->Link)
    709    ){
    710     //
    711     // install the alias'
    712     //
    713     Status = InternalSetAlias(Node->CommandString, Node->Alias, TRUE);
    714     ASSERT_EFI_ERROR(Status);
    715   }
    716   return (EFI_SUCCESS);
    717 }
    718 
    719 /**
    720   Internal function to determine if 2 command names are really the same.
    721 
    722   @param[in] Command1       The pointer to the first command name.
    723   @param[in] Command2       The pointer to the second command name.
    724 
    725   @retval TRUE              The 2 command names are the same.
    726   @retval FALSE             The 2 command names are not the same.
    727 **/
    728 BOOLEAN
    729 EFIAPI
    730 IsCommand(
    731   IN CONST CHAR16 *Command1,
    732   IN CONST CHAR16 *Command2
    733   )
    734 {
    735   if (StringNoCaseCompare(&Command1, &Command2) == 0) {
    736     return (TRUE);
    737   }
    738   return (FALSE);
    739 }
    740 
    741 /**
    742   Internal function to determine if a command is a script only command.
    743 
    744   @param[in] CommandName    The pointer to the command name.
    745 
    746   @retval TRUE              The command is a script only command.
    747   @retval FALSE             The command is not a script only command.
    748 **/
    749 BOOLEAN
    750 EFIAPI
    751 IsScriptOnlyCommand(
    752   IN CONST CHAR16 *CommandName
    753   )
    754 {
    755   if (IsCommand(CommandName, L"for")
    756     ||IsCommand(CommandName, L"endfor")
    757     ||IsCommand(CommandName, L"if")
    758     ||IsCommand(CommandName, L"else")
    759     ||IsCommand(CommandName, L"endif")
    760     ||IsCommand(CommandName, L"goto")) {
    761     return (TRUE);
    762   }
    763   return (FALSE);
    764 }
    765 
    766 /**
    767   This function will populate the 2 device path protocol parameters based on the
    768   global gImageHandle.  The DevPath will point to the device path for the handle that has
    769   loaded image protocol installed on it.  The FilePath will point to the device path
    770   for the file that was loaded.
    771 
    772   @param[in, out] DevPath       On a successful return the device path to the loaded image.
    773   @param[in, out] FilePath      On a successful return the device path to the file.
    774 
    775   @retval EFI_SUCCESS           The 2 device paths were successfully returned.
    776   @retval other                 A error from gBS->HandleProtocol.
    777 
    778   @sa HandleProtocol
    779 **/
    780 EFI_STATUS
    781 EFIAPI
    782 GetDevicePathsForImageAndFile (
    783   IN OUT EFI_DEVICE_PATH_PROTOCOL **DevPath,
    784   IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath
    785   )
    786 {
    787   EFI_STATUS                Status;
    788   EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
    789   EFI_DEVICE_PATH_PROTOCOL  *ImageDevicePath;
    790 
    791   ASSERT(DevPath  != NULL);
    792   ASSERT(FilePath != NULL);
    793 
    794   Status = gBS->OpenProtocol (
    795                 gImageHandle,
    796                 &gEfiLoadedImageProtocolGuid,
    797                 (VOID**)&LoadedImage,
    798                 gImageHandle,
    799                 NULL,
    800                 EFI_OPEN_PROTOCOL_GET_PROTOCOL
    801                );
    802   if (!EFI_ERROR (Status)) {
    803     Status = gBS->OpenProtocol (
    804                   LoadedImage->DeviceHandle,
    805                   &gEfiDevicePathProtocolGuid,
    806                   (VOID**)&ImageDevicePath,
    807                   gImageHandle,
    808                   NULL,
    809                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
    810                  );
    811     if (!EFI_ERROR (Status)) {
    812       *DevPath  = DuplicateDevicePath (ImageDevicePath);
    813       *FilePath = DuplicateDevicePath (LoadedImage->FilePath);
    814       gBS->CloseProtocol(
    815                   LoadedImage->DeviceHandle,
    816                   &gEfiDevicePathProtocolGuid,
    817                   gImageHandle,
    818                   NULL);
    819     }
    820     gBS->CloseProtocol(
    821                 gImageHandle,
    822                 &gEfiLoadedImageProtocolGuid,
    823                 gImageHandle,
    824                 NULL);
    825   }
    826   return (Status);
    827 }
    828 
    829 /**
    830   Process all Uefi Shell 2.0 command line options.
    831 
    832   see Uefi Shell 2.0 section 3.2 for full details.
    833 
    834   the command line must resemble the following:
    835 
    836   shell.efi [ShellOpt-options] [options] [file-name [file-name-options]]
    837 
    838   ShellOpt-options  Options which control the initialization behavior of the shell.
    839                     These options are read from the EFI global variable "ShellOpt"
    840                     and are processed before options or file-name.
    841 
    842   options           Options which control the initialization behavior of the shell.
    843 
    844   file-name         The name of a UEFI shell application or script to be executed
    845                     after initialization is complete. By default, if file-name is
    846                     specified, then -nostartup is implied. Scripts are not supported
    847                     by level 0.
    848 
    849   file-name-options The command-line options that are passed to file-name when it
    850                     is invoked.
    851 
    852   This will initialize the ShellInfoObject.ShellInitSettings global variable.
    853 
    854   @retval EFI_SUCCESS           The variable is initialized.
    855 **/
    856 EFI_STATUS
    857 EFIAPI
    858 ProcessCommandLine(
    859   VOID
    860   )
    861 {
    862   UINTN                           Size;
    863   UINTN                           LoopVar;
    864   CHAR16                          *CurrentArg;
    865   CHAR16                          *DelayValueStr;
    866   UINT64                          DelayValue;
    867   EFI_STATUS                      Status;
    868   EFI_UNICODE_COLLATION_PROTOCOL  *UnicodeCollation;
    869 
    870   // `file-name-options` will contain arguments to `file-name` that we don't
    871   // know about. This would cause ShellCommandLineParse to error, so we parse
    872   // arguments manually, ignoring those after the first thing that doesn't look
    873   // like a shell option (which is assumed to be `file-name`).
    874 
    875   Status = gBS->LocateProtocol (
    876                   &gEfiUnicodeCollationProtocolGuid,
    877                   NULL,
    878                   (VOID **) &UnicodeCollation
    879                   );
    880   if (EFI_ERROR (Status)) {
    881     return Status;
    882   }
    883 
    884   // Set default options
    885   ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup      = FALSE;
    886   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup    = FALSE;
    887   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = FALSE;
    888   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn  = FALSE;
    889   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt  = FALSE;
    890   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap        = FALSE;
    891   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion    = FALSE;
    892   ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay        = FALSE;
    893   ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit         = FALSE;
    894   ShellInfoObject.ShellInitSettings.Delay = 5;
    895 
    896   //
    897   // Start LoopVar at 0 to parse only optional arguments at Argv[0]
    898   // and parse other parameters from Argv[1].  This is for use case that
    899   // UEFI Shell boot option is created, and OptionalData is provided
    900   // that starts with shell command-line options.
    901   //
    902   for (LoopVar = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
    903     CurrentArg = gEfiShellParametersProtocol->Argv[LoopVar];
    904     if (UnicodeCollation->StriColl (
    905                             UnicodeCollation,
    906                             L"-startup",
    907                             CurrentArg
    908                             ) == 0) {
    909       ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup      = TRUE;
    910     }
    911     else if (UnicodeCollation->StriColl (
    912                                  UnicodeCollation,
    913                                  L"-nostartup",
    914                                  CurrentArg
    915                                  ) == 0) {
    916       ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup    = TRUE;
    917     }
    918     else if (UnicodeCollation->StriColl (
    919                                  UnicodeCollation,
    920                                  L"-noconsoleout",
    921                                  CurrentArg
    922                                  ) == 0) {
    923       ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleOut = TRUE;
    924     }
    925     else if (UnicodeCollation->StriColl (
    926                                  UnicodeCollation,
    927                                  L"-noconsolein",
    928                                  CurrentArg
    929                                  ) == 0) {
    930       ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn  = TRUE;
    931     }
    932     else if (UnicodeCollation->StriColl (
    933                                  UnicodeCollation,
    934                                  L"-nointerrupt",
    935                                  CurrentArg
    936                                  ) == 0) {
    937       ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt  = TRUE;
    938     }
    939     else if (UnicodeCollation->StriColl (
    940                                  UnicodeCollation,
    941                                  L"-nomap",
    942                                  CurrentArg
    943                                  ) == 0) {
    944       ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap        = TRUE;
    945     }
    946     else if (UnicodeCollation->StriColl (
    947                                  UnicodeCollation,
    948                                  L"-noversion",
    949                                  CurrentArg
    950                                  ) == 0) {
    951       ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion    = TRUE;
    952     }
    953     else if (UnicodeCollation->StriColl (
    954                                  UnicodeCollation,
    955                                  L"-delay",
    956                                  CurrentArg
    957                                  ) == 0) {
    958       ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay        = TRUE;
    959       // Check for optional delay value following "-delay"
    960       DelayValueStr = gEfiShellParametersProtocol->Argv[LoopVar + 1];
    961       if (DelayValueStr != NULL){
    962         if (*DelayValueStr == L':') {
    963           DelayValueStr++;
    964         }
    965         if (!EFI_ERROR(ShellConvertStringToUint64 (
    966                         DelayValueStr,
    967                         &DelayValue,
    968                         FALSE,
    969                         FALSE
    970                         ))) {
    971           ShellInfoObject.ShellInitSettings.Delay = (UINTN)DelayValue;
    972           LoopVar++;
    973         }
    974       }
    975     } else if (UnicodeCollation->StriColl (
    976                                    UnicodeCollation,
    977                                    L"-_exit",
    978                                    CurrentArg
    979                                    ) == 0) {
    980       ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit         = TRUE;
    981     } else if (StrnCmp (L"-", CurrentArg, 1) == 0) {
    982       // Unrecognized option
    983       ShellPrintHiiEx(-1, -1, NULL,
    984         STRING_TOKEN (STR_GEN_PROBLEM),
    985         ShellInfoObject.HiiHandle,
    986         CurrentArg
    987         );
    988       return EFI_INVALID_PARAMETER;
    989     } else {
    990       //
    991       // First argument should be Shell.efi image name
    992       //
    993       if (LoopVar == 0) {
    994         continue;
    995       }
    996 
    997       ShellInfoObject.ShellInitSettings.FileName = AllocateCopyPool(StrSize(CurrentArg), CurrentArg);
    998       if (ShellInfoObject.ShellInitSettings.FileName == NULL) {
    999         return (EFI_OUT_OF_RESOURCES);
   1000       }
   1001       //
   1002       // We found `file-name`.
   1003       //
   1004       ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup = 1;
   1005       LoopVar++;
   1006 
   1007       // Add `file-name-options`
   1008       for (Size = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {
   1009         ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));
   1010         StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,
   1011                     &Size,
   1012                     L" ",
   1013                     0);
   1014         if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
   1015           SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);
   1016           return (EFI_OUT_OF_RESOURCES);
   1017         }
   1018         StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,
   1019                     &Size,
   1020                     gEfiShellParametersProtocol->Argv[LoopVar],
   1021                     0);
   1022         if (ShellInfoObject.ShellInitSettings.FileOptions == NULL) {
   1023           SHELL_FREE_NON_NULL(ShellInfoObject.ShellInitSettings.FileName);
   1024           return (EFI_OUT_OF_RESOURCES);
   1025         }
   1026       }
   1027     }
   1028   }
   1029 
   1030   // "-nointerrupt" overrides "-delay"
   1031   if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoInterrupt) {
   1032     ShellInfoObject.ShellInitSettings.Delay = 0;
   1033   }
   1034 
   1035   return EFI_SUCCESS;
   1036 }
   1037 
   1038 /**
   1039   Handles all interaction with the default startup script.
   1040 
   1041   this will check that the correct command line parameters were passed, handle the delay, and then start running the script.
   1042 
   1043   @param ImagePath              the path to the image for shell.  first place to look for the startup script
   1044   @param FilePath               the path to the file for shell.  second place to look for the startup script.
   1045 
   1046   @retval EFI_SUCCESS           the variable is initialized.
   1047 **/
   1048 EFI_STATUS
   1049 EFIAPI
   1050 DoStartupScript(
   1051   IN EFI_DEVICE_PATH_PROTOCOL *ImagePath,
   1052   IN EFI_DEVICE_PATH_PROTOCOL *FilePath
   1053   )
   1054 {
   1055   EFI_STATUS                    Status;
   1056   EFI_STATUS                    CalleeStatus;
   1057   UINTN                         Delay;
   1058   EFI_INPUT_KEY                 Key;
   1059   SHELL_FILE_HANDLE             FileHandle;
   1060   EFI_DEVICE_PATH_PROTOCOL      *NewPath;
   1061   EFI_DEVICE_PATH_PROTOCOL      *NamePath;
   1062   CHAR16                        *FileStringPath;
   1063   CHAR16                        *TempSpot;
   1064   UINTN                         NewSize;
   1065   CONST CHAR16                  *MapName;
   1066 
   1067   Key.UnicodeChar = CHAR_NULL;
   1068   Key.ScanCode    = 0;
   1069   FileHandle      = NULL;
   1070 
   1071   if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup && ShellInfoObject.ShellInitSettings.FileName != NULL) {
   1072     //
   1073     // launch something else instead
   1074     //
   1075     NewSize = StrSize(ShellInfoObject.ShellInitSettings.FileName);
   1076     if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
   1077       NewSize += StrSize(ShellInfoObject.ShellInitSettings.FileOptions) + sizeof(CHAR16);
   1078     }
   1079     FileStringPath = AllocateZeroPool(NewSize);
   1080     if (FileStringPath == NULL) {
   1081       return (EFI_OUT_OF_RESOURCES);
   1082     }
   1083     StrCpyS(FileStringPath, NewSize/sizeof(CHAR16), ShellInfoObject.ShellInitSettings.FileName);
   1084     if (ShellInfoObject.ShellInitSettings.FileOptions != NULL) {
   1085       StrnCatS(FileStringPath, NewSize/sizeof(CHAR16), L" ", NewSize/sizeof(CHAR16) - StrLen(FileStringPath) -1);
   1086       StrnCatS(FileStringPath, NewSize/sizeof(CHAR16), ShellInfoObject.ShellInitSettings.FileOptions, NewSize/sizeof(CHAR16) - StrLen(FileStringPath) -1);
   1087     }
   1088     Status = RunShellCommand(FileStringPath, &CalleeStatus);
   1089     if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit == TRUE) {
   1090       ShellCommandRegisterExit(gEfiShellProtocol->BatchIsActive(), (UINT64)CalleeStatus);
   1091     }
   1092     FreePool(FileStringPath);
   1093     return (Status);
   1094 
   1095   }
   1096 
   1097   //
   1098   // for shell level 0 we do no scripts
   1099   // Without the Startup bit overriding we allow for nostartup to prevent scripts
   1100   //
   1101   if ( (PcdGet8(PcdShellSupportLevel) < 1)
   1102     || (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoStartup && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.Startup)
   1103    ){
   1104     return (EFI_SUCCESS);
   1105   }
   1106 
   1107   gST->ConOut->EnableCursor(gST->ConOut, FALSE);
   1108   //
   1109   // print out our warning and see if they press a key
   1110   //
   1111   for ( Status = EFI_UNSUPPORTED, Delay = ShellInfoObject.ShellInitSettings.Delay
   1112       ; Delay != 0 && EFI_ERROR(Status)
   1113       ; Delay--
   1114      ){
   1115     ShellPrintHiiEx(0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay);
   1116     gBS->Stall (1000000);
   1117     if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {
   1118       Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
   1119     }
   1120   }
   1121   ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CRLF), ShellInfoObject.HiiHandle);
   1122   gST->ConOut->EnableCursor(gST->ConOut, TRUE);
   1123 
   1124   //
   1125   // ESC was pressed
   1126   //
   1127   if (Status == EFI_SUCCESS && Key.UnicodeChar == 0 && Key.ScanCode == SCAN_ESC) {
   1128     return (EFI_SUCCESS);
   1129   }
   1130 
   1131   //
   1132   // Try the first location (must be file system)
   1133   //
   1134   MapName = ShellInfoObject.NewEfiShellProtocol->GetMapFromDevicePath(&ImagePath);
   1135   if (MapName != NULL) {
   1136     FileStringPath = NULL;
   1137     NewSize = 0;
   1138     FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, MapName, 0);
   1139     if (FileStringPath == NULL) {
   1140       Status = EFI_OUT_OF_RESOURCES;
   1141     } else {
   1142       TempSpot = StrStr(FileStringPath, L";");
   1143       if (TempSpot != NULL) {
   1144         *TempSpot = CHAR_NULL;
   1145       }
   1146       FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, ((FILEPATH_DEVICE_PATH*)FilePath)->PathName, 0);
   1147       PathRemoveLastItem(FileStringPath);
   1148       FileStringPath = StrnCatGrow(&FileStringPath, &NewSize, mStartupScript, 0);
   1149       Status = ShellInfoObject.NewEfiShellProtocol->OpenFileByName(FileStringPath, &FileHandle, EFI_FILE_MODE_READ);
   1150       FreePool(FileStringPath);
   1151     }
   1152   }
   1153   if (EFI_ERROR(Status)) {
   1154     NamePath = FileDevicePath (NULL, mStartupScript);
   1155     NewPath = AppendDevicePathNode (ImagePath, NamePath);
   1156     FreePool(NamePath);
   1157 
   1158     //
   1159     // Try the location
   1160     //
   1161     Status = InternalOpenFileDevicePath(NewPath, &FileHandle, EFI_FILE_MODE_READ, 0);
   1162     FreePool(NewPath);
   1163   }
   1164   //
   1165   // If we got a file, run it
   1166   //
   1167   if (!EFI_ERROR(Status) && FileHandle != NULL) {
   1168     Status = RunScriptFile (mStartupScript, FileHandle, L"", ShellInfoObject.NewShellParametersProtocol);
   1169     ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle);
   1170   } else {
   1171     FileStringPath = ShellFindFilePath(mStartupScript);
   1172     if (FileStringPath == NULL) {
   1173       //
   1174       // we return success since we don't need to have a startup script
   1175       //
   1176       Status = EFI_SUCCESS;
   1177       ASSERT(FileHandle == NULL);
   1178     } else {
   1179       Status = RunScriptFile(FileStringPath, NULL, L"", ShellInfoObject.NewShellParametersProtocol);
   1180       FreePool(FileStringPath);
   1181     }
   1182   }
   1183 
   1184 
   1185   return (Status);
   1186 }
   1187 
   1188 /**
   1189   Function to perform the shell prompt looping.  It will do a single prompt,
   1190   dispatch the result, and then return.  It is expected that the caller will
   1191   call this function in a loop many times.
   1192 
   1193   @retval EFI_SUCCESS
   1194   @retval RETURN_ABORTED
   1195 **/
   1196 EFI_STATUS
   1197 EFIAPI
   1198 DoShellPrompt (
   1199   VOID
   1200   )
   1201 {
   1202   UINTN         Column;
   1203   UINTN         Row;
   1204   CHAR16        *CmdLine;
   1205   CONST CHAR16  *CurDir;
   1206   UINTN         BufferSize;
   1207   EFI_STATUS    Status;
   1208   LIST_ENTRY    OldBufferList;
   1209 
   1210   CurDir  = NULL;
   1211 
   1212   //
   1213   // Get screen setting to decide size of the command line buffer
   1214   //
   1215   gST->ConOut->QueryMode (gST->ConOut, gST->ConOut->Mode->Mode, &Column, &Row);
   1216   BufferSize  = Column * Row * sizeof (CHAR16);
   1217   CmdLine     = AllocateZeroPool (BufferSize);
   1218   if (CmdLine == NULL) {
   1219     return EFI_OUT_OF_RESOURCES;
   1220   }
   1221 
   1222   SaveBufferList(&OldBufferList);
   1223   CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");
   1224 
   1225   //
   1226   // Prompt for input
   1227   //
   1228   gST->ConOut->SetCursorPosition (gST->ConOut, 0, gST->ConOut->Mode->CursorRow);
   1229 
   1230   if (CurDir != NULL && StrLen(CurDir) > 1) {
   1231     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
   1232   } else {
   1233     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
   1234   }
   1235 
   1236   //
   1237   // Read a line from the console
   1238   //
   1239   Status = ShellInfoObject.NewEfiShellProtocol->ReadFile(ShellInfoObject.NewShellParametersProtocol->StdIn, &BufferSize, CmdLine);
   1240 
   1241   //
   1242   // Null terminate the string and parse it
   1243   //
   1244   if (!EFI_ERROR (Status)) {
   1245     CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL;
   1246     Status = RunCommand(CmdLine);
   1247     }
   1248 
   1249   //
   1250   // Done with this command
   1251   //
   1252   RestoreBufferList(&OldBufferList);
   1253   FreePool (CmdLine);
   1254   return Status;
   1255 }
   1256 
   1257 /**
   1258   Add a buffer to the Buffer To Free List for safely returning buffers to other
   1259   places without risking letting them modify internal shell information.
   1260 
   1261   @param Buffer   Something to pass to FreePool when the shell is exiting.
   1262 **/
   1263 VOID*
   1264 EFIAPI
   1265 AddBufferToFreeList(
   1266   VOID *Buffer
   1267   )
   1268 {
   1269   BUFFER_LIST   *BufferListEntry;
   1270 
   1271   if (Buffer == NULL) {
   1272     return (NULL);
   1273   }
   1274 
   1275   BufferListEntry = AllocateZeroPool(sizeof(BUFFER_LIST));
   1276   ASSERT(BufferListEntry != NULL);
   1277   BufferListEntry->Buffer = Buffer;
   1278   InsertTailList(&ShellInfoObject.BufferToFreeList.Link, &BufferListEntry->Link);
   1279   return (Buffer);
   1280 }
   1281 
   1282 
   1283 /**
   1284   Create a new buffer list and stores the old one to OldBufferList
   1285 
   1286   @param OldBufferList   The temporary list head used to store the nodes in BufferToFreeList.
   1287 **/
   1288 VOID
   1289 SaveBufferList (
   1290   OUT LIST_ENTRY     *OldBufferList
   1291   )
   1292 {
   1293   CopyMem (OldBufferList, &ShellInfoObject.BufferToFreeList.Link, sizeof (LIST_ENTRY));
   1294   InitializeListHead (&ShellInfoObject.BufferToFreeList.Link);
   1295 }
   1296 
   1297 /**
   1298   Restore previous nodes into BufferToFreeList .
   1299 
   1300   @param OldBufferList   The temporary list head used to store the nodes in BufferToFreeList.
   1301 **/
   1302 VOID
   1303 RestoreBufferList (
   1304   IN OUT LIST_ENTRY     *OldBufferList
   1305   )
   1306 {
   1307   FreeBufferList (&ShellInfoObject.BufferToFreeList);
   1308   CopyMem (&ShellInfoObject.BufferToFreeList.Link, OldBufferList, sizeof (LIST_ENTRY));
   1309 }
   1310 
   1311 
   1312 /**
   1313   Add a buffer to the Line History List
   1314 
   1315   @param Buffer     The line buffer to add.
   1316 **/
   1317 VOID
   1318 EFIAPI
   1319 AddLineToCommandHistory(
   1320   IN CONST CHAR16 *Buffer
   1321   )
   1322 {
   1323   BUFFER_LIST *Node;
   1324   BUFFER_LIST *Walker;
   1325   UINT16       MaxHistoryCmdCount;
   1326   UINT16       Count;
   1327 
   1328   Count = 0;
   1329   MaxHistoryCmdCount = PcdGet16(PcdShellMaxHistoryCommandCount);
   1330 
   1331   if (MaxHistoryCmdCount == 0) {
   1332     return ;
   1333   }
   1334 
   1335 
   1336   Node = AllocateZeroPool(sizeof(BUFFER_LIST));
   1337   ASSERT(Node != NULL);
   1338   Node->Buffer = AllocateCopyPool(StrSize(Buffer), Buffer);
   1339   ASSERT(Node->Buffer != NULL);
   1340 
   1341   for ( Walker = (BUFFER_LIST*)GetFirstNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link)
   1342       ; !IsNull(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link)
   1343       ; Walker = (BUFFER_LIST*)GetNextNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Walker->Link)
   1344    ){
   1345     Count++;
   1346   }
   1347   if (Count < MaxHistoryCmdCount){
   1348     InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);
   1349   } else {
   1350     Walker = (BUFFER_LIST*)GetFirstNode(&ShellInfoObject.ViewingSettings.CommandHistory.Link);
   1351     RemoveEntryList(&Walker->Link);
   1352     if (Walker->Buffer != NULL) {
   1353       FreePool(Walker->Buffer);
   1354     }
   1355     FreePool(Walker);
   1356     InsertTailList(&ShellInfoObject.ViewingSettings.CommandHistory.Link, &Node->Link);
   1357   }
   1358 }
   1359 
   1360 /**
   1361   Checks if a string is an alias for another command.  If yes, then it replaces the alias name
   1362   with the correct command name.
   1363 
   1364   @param[in, out] CommandString    Upon entry the potential alias.  Upon return the
   1365                                    command name if it was an alias.  If it was not
   1366                                    an alias it will be unchanged.  This function may
   1367                                    change the buffer to fit the command name.
   1368 
   1369   @retval EFI_SUCCESS             The name was changed.
   1370   @retval EFI_SUCCESS             The name was not an alias.
   1371   @retval EFI_OUT_OF_RESOURCES    A memory allocation failed.
   1372 **/
   1373 EFI_STATUS
   1374 EFIAPI
   1375 ShellConvertAlias(
   1376   IN OUT CHAR16 **CommandString
   1377   )
   1378 {
   1379   CONST CHAR16  *NewString;
   1380 
   1381   NewString = ShellInfoObject.NewEfiShellProtocol->GetAlias(*CommandString, NULL);
   1382   if (NewString == NULL) {
   1383     return (EFI_SUCCESS);
   1384   }
   1385   FreePool(*CommandString);
   1386   *CommandString = AllocateCopyPool(StrSize(NewString), NewString);
   1387   if (*CommandString == NULL) {
   1388     return (EFI_OUT_OF_RESOURCES);
   1389   }
   1390   return (EFI_SUCCESS);
   1391 }
   1392 
   1393 /**
   1394   This function will eliminate unreplaced (and therefore non-found) environment variables.
   1395 
   1396   @param[in,out] CmdLine   The command line to update.
   1397 **/
   1398 EFI_STATUS
   1399 EFIAPI
   1400 StripUnreplacedEnvironmentVariables(
   1401   IN OUT CHAR16 *CmdLine
   1402   )
   1403 {
   1404   CHAR16 *FirstPercent;
   1405   CHAR16 *FirstQuote;
   1406   CHAR16 *SecondPercent;
   1407   CHAR16 *SecondQuote;
   1408   CHAR16 *CurrentLocator;
   1409 
   1410   for (CurrentLocator = CmdLine ; CurrentLocator != NULL ; ) {
   1411     FirstQuote = FindNextInstance(CurrentLocator, L"\"", TRUE);
   1412     FirstPercent = FindNextInstance(CurrentLocator, L"%", TRUE);
   1413     SecondPercent = FirstPercent!=NULL?FindNextInstance(FirstPercent+1, L"%", TRUE):NULL;
   1414     if (FirstPercent == NULL || SecondPercent == NULL) {
   1415       //
   1416       // If we ever don't have 2 % we are done.
   1417       //
   1418       break;
   1419     }
   1420 
   1421     if (FirstQuote!= NULL && FirstQuote < FirstPercent) {
   1422       SecondQuote = FindNextInstance(FirstQuote+1, L"\"", TRUE);
   1423       //
   1424       // Quote is first found
   1425       //
   1426 
   1427       if (SecondQuote < FirstPercent) {
   1428         //
   1429         // restart after the pair of "
   1430         //
   1431         CurrentLocator = SecondQuote + 1;
   1432       } else /* FirstPercent < SecondQuote */{
   1433         //
   1434         // Restart on the first percent
   1435         //
   1436         CurrentLocator = FirstPercent;
   1437       }
   1438       continue;
   1439     }
   1440 
   1441     if (FirstQuote == NULL || SecondPercent < FirstQuote) {
   1442       if (IsValidEnvironmentVariableName(FirstPercent, SecondPercent)) {
   1443         //
   1444         // We need to remove from FirstPercent to SecondPercent
   1445         //
   1446         CopyMem(FirstPercent, SecondPercent + 1, StrSize(SecondPercent + 1));
   1447         //
   1448         // don't need to update the locator.  both % characters are gone.
   1449         //
   1450       } else {
   1451         CurrentLocator = SecondPercent + 1;
   1452       }
   1453       continue;
   1454     }
   1455     CurrentLocator = FirstQuote;
   1456   }
   1457   return (EFI_SUCCESS);
   1458 }
   1459 
   1460 /**
   1461   Function allocates a new command line and replaces all instances of environment
   1462   variable names that are correctly preset to their values.
   1463 
   1464   If the return value is not NULL the memory must be caller freed.
   1465 
   1466   @param[in] OriginalCommandLine    The original command line
   1467 
   1468   @retval NULL                      An error occurred.
   1469   @return                           The new command line with no environment variables present.
   1470 **/
   1471 CHAR16*
   1472 EFIAPI
   1473 ShellConvertVariables (
   1474   IN CONST CHAR16 *OriginalCommandLine
   1475   )
   1476 {
   1477   CONST CHAR16        *MasterEnvList;
   1478   UINTN               NewSize;
   1479   CHAR16              *NewCommandLine1;
   1480   CHAR16              *NewCommandLine2;
   1481   CHAR16              *Temp;
   1482   UINTN               ItemSize;
   1483   CHAR16              *ItemTemp;
   1484   SCRIPT_FILE         *CurrentScriptFile;
   1485   ALIAS_LIST          *AliasListNode;
   1486 
   1487   ASSERT(OriginalCommandLine != NULL);
   1488 
   1489   ItemSize          = 0;
   1490   NewSize           = StrSize(OriginalCommandLine);
   1491   CurrentScriptFile = ShellCommandGetCurrentScriptFile();
   1492   Temp              = NULL;
   1493 
   1494   ///@todo update this to handle the %0 - %9 for scripting only (borrow from line 1256 area) ? ? ?
   1495 
   1496   //
   1497   // calculate the size required for the post-conversion string...
   1498   //
   1499   if (CurrentScriptFile != NULL) {
   1500     for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)
   1501       ;  !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)
   1502       ;  AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)
   1503    ){
   1504       for (Temp = StrStr(OriginalCommandLine, AliasListNode->Alias)
   1505         ;  Temp != NULL
   1506         ;  Temp = StrStr(Temp+1, AliasListNode->Alias)
   1507        ){
   1508         //
   1509         // we need a preceding and if there is space no ^ preceding (if no space ignore)
   1510         //
   1511         if ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2)) {
   1512           NewSize += StrSize(AliasListNode->CommandString);
   1513         }
   1514       }
   1515     }
   1516   }
   1517 
   1518   for (MasterEnvList = EfiShellGetEnv(NULL)
   1519     ;  MasterEnvList != NULL && *MasterEnvList != CHAR_NULL //&& *(MasterEnvList+1) != CHAR_NULL
   1520     ;  MasterEnvList += StrLen(MasterEnvList) + 1
   1521    ){
   1522     if (StrSize(MasterEnvList) > ItemSize) {
   1523       ItemSize = StrSize(MasterEnvList);
   1524     }
   1525     for (Temp = StrStr(OriginalCommandLine, MasterEnvList)
   1526       ;  Temp != NULL
   1527       ;  Temp = StrStr(Temp+1, MasterEnvList)
   1528      ){
   1529       //
   1530       // we need a preceding and following % and if there is space no ^ preceding (if no space ignore)
   1531       //
   1532       if (*(Temp-1) == L'%' && *(Temp+StrLen(MasterEnvList)) == L'%' &&
   1533         ((((Temp-OriginalCommandLine)>2) && *(Temp-2) != L'^') || ((Temp-OriginalCommandLine)<=2))) {
   1534         NewSize+=StrSize(EfiShellGetEnv(MasterEnvList));
   1535       }
   1536     }
   1537   }
   1538 
   1539   //
   1540   // now do the replacements...
   1541   //
   1542   NewCommandLine1 = AllocateCopyPool(NewSize, OriginalCommandLine);
   1543   NewCommandLine2 = AllocateZeroPool(NewSize);
   1544   ItemTemp        = AllocateZeroPool(ItemSize+(2*sizeof(CHAR16)));
   1545   if (NewCommandLine1 == NULL || NewCommandLine2 == NULL || ItemTemp == NULL) {
   1546     SHELL_FREE_NON_NULL(NewCommandLine1);
   1547     SHELL_FREE_NON_NULL(NewCommandLine2);
   1548     SHELL_FREE_NON_NULL(ItemTemp);
   1549     return (NULL);
   1550   }
   1551   for (MasterEnvList = EfiShellGetEnv(NULL)
   1552     ;  MasterEnvList != NULL && *MasterEnvList != CHAR_NULL
   1553     ;  MasterEnvList += StrLen(MasterEnvList) + 1
   1554    ){
   1555     StrCpyS( ItemTemp,
   1556               ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16)),
   1557               L"%"
   1558               );
   1559     StrCatS( ItemTemp,
   1560               ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16)),
   1561               MasterEnvList
   1562               );
   1563     StrCatS( ItemTemp,
   1564               ((ItemSize+(2*sizeof(CHAR16)))/sizeof(CHAR16)),
   1565               L"%"
   1566               );
   1567     ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, ItemTemp, EfiShellGetEnv(MasterEnvList), TRUE, FALSE);
   1568     StrCpyS(NewCommandLine1, NewSize/sizeof(CHAR16), NewCommandLine2);
   1569   }
   1570   if (CurrentScriptFile != NULL) {
   1571     for (AliasListNode = (ALIAS_LIST*)GetFirstNode(&CurrentScriptFile->SubstList)
   1572       ;  !IsNull(&CurrentScriptFile->SubstList, &AliasListNode->Link)
   1573       ;  AliasListNode = (ALIAS_LIST*)GetNextNode(&CurrentScriptFile->SubstList, &AliasListNode->Link)
   1574    ){
   1575     ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);
   1576     StrCpyS(NewCommandLine1, NewSize/sizeof(CHAR16), NewCommandLine2);
   1577     }
   1578   }
   1579 
   1580   //
   1581   // Remove non-existent environment variables
   1582   //
   1583   StripUnreplacedEnvironmentVariables(NewCommandLine1);
   1584 
   1585   //
   1586   // Now cleanup any straggler intentionally ignored "%" characters
   1587   //
   1588   ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, L"^%", L"%", TRUE, FALSE);
   1589   StrCpyS(NewCommandLine1, NewSize/sizeof(CHAR16), NewCommandLine2);
   1590 
   1591   FreePool(NewCommandLine2);
   1592   FreePool(ItemTemp);
   1593 
   1594   return (NewCommandLine1);
   1595 }
   1596 
   1597 /**
   1598   Internal function to run a command line with pipe usage.
   1599 
   1600   @param[in] CmdLine        The pointer to the command line.
   1601   @param[in] StdIn          The pointer to the Standard input.
   1602   @param[in] StdOut         The pointer to the Standard output.
   1603 
   1604   @retval EFI_SUCCESS       The split command is executed successfully.
   1605   @retval other             Some error occurs when executing the split command.
   1606 **/
   1607 EFI_STATUS
   1608 EFIAPI
   1609 RunSplitCommand(
   1610   IN CONST CHAR16             *CmdLine,
   1611   IN       SHELL_FILE_HANDLE  *StdIn,
   1612   IN       SHELL_FILE_HANDLE  *StdOut
   1613   )
   1614 {
   1615   EFI_STATUS        Status;
   1616   CHAR16            *NextCommandLine;
   1617   CHAR16            *OurCommandLine;
   1618   UINTN             Size1;
   1619   UINTN             Size2;
   1620   SPLIT_LIST        *Split;
   1621   SHELL_FILE_HANDLE *TempFileHandle;
   1622   BOOLEAN           Unicode;
   1623 
   1624   ASSERT(StdOut == NULL);
   1625 
   1626   ASSERT(StrStr(CmdLine, L"|") != NULL);
   1627 
   1628   Status          = EFI_SUCCESS;
   1629   NextCommandLine = NULL;
   1630   OurCommandLine  = NULL;
   1631   Size1           = 0;
   1632   Size2           = 0;
   1633 
   1634   NextCommandLine = StrnCatGrow(&NextCommandLine, &Size1, StrStr(CmdLine, L"|")+1, 0);
   1635   OurCommandLine  = StrnCatGrow(&OurCommandLine , &Size2, CmdLine                , StrStr(CmdLine, L"|") - CmdLine);
   1636 
   1637   if (NextCommandLine == NULL || OurCommandLine == NULL) {
   1638     SHELL_FREE_NON_NULL(OurCommandLine);
   1639     SHELL_FREE_NON_NULL(NextCommandLine);
   1640     return (EFI_OUT_OF_RESOURCES);
   1641   } else if (StrStr(OurCommandLine, L"|") != NULL || Size1 == 0 || Size2 == 0) {
   1642     SHELL_FREE_NON_NULL(OurCommandLine);
   1643     SHELL_FREE_NON_NULL(NextCommandLine);
   1644     return (EFI_INVALID_PARAMETER);
   1645   } else if (NextCommandLine[0] == L'a' &&
   1646              (NextCommandLine[1] == L' ' || NextCommandLine[1] == CHAR_NULL)
   1647             ){
   1648     CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));
   1649     while (NextCommandLine[0] == L' ') {
   1650       CopyMem(NextCommandLine, NextCommandLine+1, StrSize(NextCommandLine) - sizeof(NextCommandLine[0]));
   1651     }
   1652     if (NextCommandLine[0] == CHAR_NULL) {
   1653       SHELL_FREE_NON_NULL(OurCommandLine);
   1654       SHELL_FREE_NON_NULL(NextCommandLine);
   1655       return (EFI_INVALID_PARAMETER);
   1656     }
   1657     Unicode = FALSE;
   1658   } else {
   1659     Unicode = TRUE;
   1660   }
   1661 
   1662 
   1663   //
   1664   // make a SPLIT_LIST item and add to list
   1665   //
   1666   Split = AllocateZeroPool(sizeof(SPLIT_LIST));
   1667   ASSERT(Split != NULL);
   1668   Split->SplitStdIn   = StdIn;
   1669   Split->SplitStdOut  = ConvertEfiFileProtocolToShellHandle(CreateFileInterfaceMem(Unicode), NULL);
   1670   ASSERT(Split->SplitStdOut != NULL);
   1671   InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link);
   1672 
   1673   Status = RunCommand(OurCommandLine);
   1674 
   1675   //
   1676   // move the output from the first to the in to the second.
   1677   //
   1678   TempFileHandle      = Split->SplitStdOut;
   1679   if (Split->SplitStdIn == StdIn) {
   1680     Split->SplitStdOut = NULL;
   1681   } else {
   1682     Split->SplitStdOut  = Split->SplitStdIn;
   1683   }
   1684   Split->SplitStdIn   = TempFileHandle;
   1685   ShellInfoObject.NewEfiShellProtocol->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn), 0);
   1686 
   1687   if (!EFI_ERROR(Status)) {
   1688     Status = RunCommand(NextCommandLine);
   1689   }
   1690 
   1691   //
   1692   // remove the top level from the ScriptList
   1693   //
   1694   ASSERT((SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link) == Split);
   1695   RemoveEntryList(&Split->Link);
   1696 
   1697   //
   1698   // Note that the original StdIn is now the StdOut...
   1699   //
   1700   if (Split->SplitStdOut != NULL && Split->SplitStdOut != StdIn) {
   1701     ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdOut));
   1702   }
   1703   if (Split->SplitStdIn != NULL) {
   1704     ShellInfoObject.NewEfiShellProtocol->CloseFile(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn));
   1705   }
   1706 
   1707   FreePool(Split);
   1708   FreePool(NextCommandLine);
   1709   FreePool(OurCommandLine);
   1710 
   1711   return (Status);
   1712 }
   1713 
   1714 /**
   1715   Take the original command line, substitute any variables, free
   1716   the original string, return the modified copy.
   1717 
   1718   @param[in] CmdLine  pointer to the command line to update.
   1719 
   1720   @retval EFI_SUCCESS           the function was successful.
   1721   @retval EFI_OUT_OF_RESOURCES  a memory allocation failed.
   1722 **/
   1723 EFI_STATUS
   1724 EFIAPI
   1725 ShellSubstituteVariables(
   1726   IN CHAR16 **CmdLine
   1727   )
   1728 {
   1729   CHAR16      *NewCmdLine;
   1730   NewCmdLine = ShellConvertVariables(*CmdLine);
   1731   SHELL_FREE_NON_NULL(*CmdLine);
   1732   if (NewCmdLine == NULL) {
   1733     return (EFI_OUT_OF_RESOURCES);
   1734   }
   1735   *CmdLine = NewCmdLine;
   1736   return (EFI_SUCCESS);
   1737 }
   1738 
   1739 /**
   1740   Take the original command line, substitute any alias in the first group of space delimited characters, free
   1741   the original string, return the modified copy.
   1742 
   1743   @param[in] CmdLine  pointer to the command line to update.
   1744 
   1745   @retval EFI_SUCCESS           the function was successful.
   1746   @retval EFI_OUT_OF_RESOURCES  a memory allocation failed.
   1747 **/
   1748 EFI_STATUS
   1749 EFIAPI
   1750 ShellSubstituteAliases(
   1751   IN CHAR16 **CmdLine
   1752   )
   1753 {
   1754   CHAR16      *NewCmdLine;
   1755   CHAR16      *CommandName;
   1756   EFI_STATUS  Status;
   1757   UINTN       PostAliasSize;
   1758   ASSERT(CmdLine != NULL);
   1759   ASSERT(*CmdLine!= NULL);
   1760 
   1761 
   1762   CommandName = NULL;
   1763   if (StrStr((*CmdLine), L" ") == NULL){
   1764     StrnCatGrow(&CommandName, NULL, (*CmdLine), 0);
   1765   } else {
   1766     StrnCatGrow(&CommandName, NULL, (*CmdLine), StrStr((*CmdLine), L" ") - (*CmdLine));
   1767   }
   1768 
   1769   //
   1770   // This cannot happen 'inline' since the CmdLine can need extra space.
   1771   //
   1772   NewCmdLine = NULL;
   1773   if (!ShellCommandIsCommandOnList(CommandName)) {
   1774     //
   1775     // Convert via alias
   1776     //
   1777     Status = ShellConvertAlias(&CommandName);
   1778     if (EFI_ERROR(Status)){
   1779       return (Status);
   1780     }
   1781     PostAliasSize = 0;
   1782     NewCmdLine = StrnCatGrow(&NewCmdLine, &PostAliasSize, CommandName, 0);
   1783     if (NewCmdLine == NULL) {
   1784       SHELL_FREE_NON_NULL(CommandName);
   1785       SHELL_FREE_NON_NULL(*CmdLine);
   1786       return (EFI_OUT_OF_RESOURCES);
   1787     }
   1788     NewCmdLine = StrnCatGrow(&NewCmdLine, &PostAliasSize, StrStr((*CmdLine), L" "), 0);
   1789     if (NewCmdLine == NULL) {
   1790       SHELL_FREE_NON_NULL(CommandName);
   1791       SHELL_FREE_NON_NULL(*CmdLine);
   1792       return (EFI_OUT_OF_RESOURCES);
   1793     }
   1794   } else {
   1795     NewCmdLine = StrnCatGrow(&NewCmdLine, NULL, (*CmdLine), 0);
   1796   }
   1797 
   1798   SHELL_FREE_NON_NULL(*CmdLine);
   1799   SHELL_FREE_NON_NULL(CommandName);
   1800 
   1801   //
   1802   // re-assign the passed in double pointer to point to our newly allocated buffer
   1803   //
   1804   *CmdLine = NewCmdLine;
   1805 
   1806   return (EFI_SUCCESS);
   1807 }
   1808 
   1809 /**
   1810   Takes the Argv[0] part of the command line and determine the meaning of it.
   1811 
   1812   @param[in] CmdName  pointer to the command line to update.
   1813 
   1814   @retval Internal_Command    The name is an internal command.
   1815   @retval File_Sys_Change     the name is a file system change.
   1816   @retval Script_File_Name    the name is a NSH script file.
   1817   @retval Unknown_Invalid     the name is unknown.
   1818   @retval Efi_Application     the name is an application (.EFI).
   1819 **/
   1820 SHELL_OPERATION_TYPES
   1821 EFIAPI
   1822 GetOperationType(
   1823   IN CONST CHAR16 *CmdName
   1824   )
   1825 {
   1826         CHAR16* FileWithPath;
   1827   CONST CHAR16* TempLocation;
   1828   CONST CHAR16* TempLocation2;
   1829 
   1830   FileWithPath = NULL;
   1831   //
   1832   // test for an internal command.
   1833   //
   1834   if (ShellCommandIsCommandOnList(CmdName)) {
   1835     return (Internal_Command);
   1836   }
   1837 
   1838   //
   1839   // Test for file system change request.  anything ending with first : and cant have spaces.
   1840   //
   1841   if (CmdName[(StrLen(CmdName)-1)] == L':') {
   1842     if ( StrStr(CmdName, L" ") != NULL
   1843       || StrLen(StrStr(CmdName, L":")) > 1
   1844       ) {
   1845       return (Unknown_Invalid);
   1846     }
   1847     return (File_Sys_Change);
   1848   }
   1849 
   1850   //
   1851   // Test for a file
   1852   //
   1853   if ((FileWithPath = ShellFindFilePathEx(CmdName, mExecutableExtensions)) != NULL) {
   1854     //
   1855     // See if that file has a script file extension
   1856     //
   1857     if (StrLen(FileWithPath) > 4) {
   1858       TempLocation = FileWithPath+StrLen(FileWithPath)-4;
   1859       TempLocation2 = mScriptExtension;
   1860       if (StringNoCaseCompare((VOID*)(&TempLocation), (VOID*)(&TempLocation2)) == 0) {
   1861         SHELL_FREE_NON_NULL(FileWithPath);
   1862         return (Script_File_Name);
   1863       }
   1864     }
   1865 
   1866     //
   1867     // Was a file, but not a script.  we treat this as an application.
   1868     //
   1869     SHELL_FREE_NON_NULL(FileWithPath);
   1870     return (Efi_Application);
   1871   }
   1872 
   1873   SHELL_FREE_NON_NULL(FileWithPath);
   1874   //
   1875   // No clue what this is... return invalid flag...
   1876   //
   1877   return (Unknown_Invalid);
   1878 }
   1879 
   1880 /**
   1881   Determine if the first item in a command line is valid.
   1882 
   1883   @param[in] CmdLine            The command line to parse.
   1884 
   1885   @retval EFI_SUCCESS           The item is valid.
   1886   @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.
   1887   @retval EFI_NOT_FOUND         The operation type is unknown or invalid.
   1888 **/
   1889 EFI_STATUS
   1890 EFIAPI
   1891 IsValidSplit(
   1892   IN CONST CHAR16 *CmdLine
   1893   )
   1894 {
   1895   CHAR16        *Temp;
   1896   CHAR16        *FirstParameter;
   1897   CHAR16        *TempWalker;
   1898   EFI_STATUS    Status;
   1899 
   1900   Temp           = NULL;
   1901 
   1902   Temp = StrnCatGrow(&Temp, NULL, CmdLine, 0);
   1903   if (Temp == NULL) {
   1904     return (EFI_OUT_OF_RESOURCES);
   1905   }
   1906 
   1907   FirstParameter = StrStr(Temp, L"|");
   1908   if (FirstParameter != NULL) {
   1909     *FirstParameter = CHAR_NULL;
   1910   }
   1911 
   1912   FirstParameter = NULL;
   1913 
   1914   //
   1915   // Process the command line
   1916   //
   1917   Status = ProcessCommandLineToFinal(&Temp);
   1918 
   1919   if (!EFI_ERROR(Status)) {
   1920     FirstParameter = AllocateZeroPool(StrSize(CmdLine));
   1921     if (FirstParameter == NULL) {
   1922       SHELL_FREE_NON_NULL(Temp);
   1923       return (EFI_OUT_OF_RESOURCES);
   1924     }
   1925     TempWalker = (CHAR16*)Temp;
   1926     if (!EFI_ERROR(GetNextParameter(&TempWalker, &FirstParameter, StrSize(CmdLine), TRUE))) {
   1927       if (GetOperationType(FirstParameter) == Unknown_Invalid) {
   1928         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
   1929         SetLastError(SHELL_NOT_FOUND);
   1930         Status = EFI_NOT_FOUND;
   1931       }
   1932     }
   1933   }
   1934 
   1935   SHELL_FREE_NON_NULL(Temp);
   1936   SHELL_FREE_NON_NULL(FirstParameter);
   1937   return Status;
   1938 }
   1939 
   1940 /**
   1941   Determine if a command line contains with a split contains only valid commands.
   1942 
   1943   @param[in] CmdLine      The command line to parse.
   1944 
   1945   @retval EFI_SUCCESS     CmdLine has only valid commands, application, or has no split.
   1946   @retval EFI_ABORTED     CmdLine has at least one invalid command or application.
   1947 **/
   1948 EFI_STATUS
   1949 EFIAPI
   1950 VerifySplit(
   1951   IN CONST CHAR16 *CmdLine
   1952   )
   1953 {
   1954   CONST CHAR16  *TempSpot;
   1955   EFI_STATUS    Status;
   1956 
   1957   //
   1958   // If this was the only item, then get out
   1959   //
   1960   if (!ContainsSplit(CmdLine)) {
   1961     return (EFI_SUCCESS);
   1962   }
   1963 
   1964   //
   1965   // Verify up to the pipe or end character
   1966   //
   1967   Status = IsValidSplit(CmdLine);
   1968   if (EFI_ERROR(Status)) {
   1969     return (Status);
   1970   }
   1971 
   1972   //
   1973   // recurse to verify the next item
   1974   //
   1975   TempSpot = FindFirstCharacter(CmdLine, L"|", L'^') + 1;
   1976   if (*TempSpot == L'a' &&
   1977       (*(TempSpot + 1) == L' ' || *(TempSpot + 1) == CHAR_NULL)
   1978      ) {
   1979     // If it's an ASCII pipe '|a'
   1980     TempSpot += 1;
   1981   }
   1982 
   1983   return (VerifySplit(TempSpot));
   1984 }
   1985 
   1986 /**
   1987   Process a split based operation.
   1988 
   1989   @param[in] CmdLine    pointer to the command line to process
   1990 
   1991   @retval EFI_SUCCESS   The operation was successful
   1992   @return               an error occurred.
   1993 **/
   1994 EFI_STATUS
   1995 EFIAPI
   1996 ProcessNewSplitCommandLine(
   1997   IN CONST CHAR16 *CmdLine
   1998   )
   1999 {
   2000   SPLIT_LIST                *Split;
   2001   EFI_STATUS                Status;
   2002 
   2003   Status = VerifySplit(CmdLine);
   2004   if (EFI_ERROR(Status)) {
   2005     return (Status);
   2006   }
   2007 
   2008   Split = NULL;
   2009 
   2010   //
   2011   // are we in an existing split???
   2012   //
   2013   if (!IsListEmpty(&ShellInfoObject.SplitList.Link)) {
   2014     Split = (SPLIT_LIST*)GetFirstNode(&ShellInfoObject.SplitList.Link);
   2015   }
   2016 
   2017   if (Split == NULL) {
   2018     Status = RunSplitCommand(CmdLine, NULL, NULL);
   2019   } else {
   2020     Status = RunSplitCommand(CmdLine, Split->SplitStdIn, Split->SplitStdOut);
   2021   }
   2022   if (EFI_ERROR(Status)) {
   2023     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CmdLine);
   2024   }
   2025   return (Status);
   2026 }
   2027 
   2028 /**
   2029   Handle a request to change the current file system.
   2030 
   2031   @param[in] CmdLine  The passed in command line.
   2032 
   2033   @retval EFI_SUCCESS The operation was successful.
   2034 **/
   2035 EFI_STATUS
   2036 EFIAPI
   2037 ChangeMappedDrive(
   2038   IN CONST CHAR16 *CmdLine
   2039   )
   2040 {
   2041   EFI_STATUS Status;
   2042   Status = EFI_SUCCESS;
   2043 
   2044   //
   2045   // make sure we are the right operation
   2046   //
   2047   ASSERT(CmdLine[(StrLen(CmdLine)-1)] == L':' && StrStr(CmdLine, L" ") == NULL);
   2048 
   2049   //
   2050   // Call the protocol API to do the work
   2051   //
   2052   Status = ShellInfoObject.NewEfiShellProtocol->SetCurDir(NULL, CmdLine);
   2053 
   2054   //
   2055   // Report any errors
   2056   //
   2057   if (EFI_ERROR(Status)) {
   2058     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_MAPPING), ShellInfoObject.HiiHandle, CmdLine);
   2059   }
   2060 
   2061   return (Status);
   2062 }
   2063 
   2064 /**
   2065   Reprocess the command line to direct all -? to the help command.
   2066 
   2067   if found, will add "help" as argv[0], and move the rest later.
   2068 
   2069   @param[in,out] CmdLine        pointer to the command line to update
   2070 **/
   2071 EFI_STATUS
   2072 EFIAPI
   2073 DoHelpUpdate(
   2074   IN OUT CHAR16 **CmdLine
   2075   )
   2076 {
   2077   CHAR16 *CurrentParameter;
   2078   CHAR16 *Walker;
   2079   CHAR16 *NewCommandLine;
   2080   EFI_STATUS Status;
   2081   UINTN  NewCmdLineSize;
   2082 
   2083   Status = EFI_SUCCESS;
   2084 
   2085   CurrentParameter = AllocateZeroPool(StrSize(*CmdLine));
   2086   if (CurrentParameter == NULL) {
   2087     return (EFI_OUT_OF_RESOURCES);
   2088   }
   2089 
   2090   Walker = *CmdLine;
   2091   while(Walker != NULL && *Walker != CHAR_NULL) {
   2092     if (!EFI_ERROR(GetNextParameter(&Walker, &CurrentParameter, StrSize(*CmdLine), TRUE))) {
   2093       if (StrStr(CurrentParameter, L"-?") == CurrentParameter) {
   2094         CurrentParameter[0] = L' ';
   2095         CurrentParameter[1] = L' ';
   2096         NewCmdLineSize = StrSize(L"help ") + StrSize(*CmdLine);
   2097         NewCommandLine = AllocateZeroPool(NewCmdLineSize);
   2098         if (NewCommandLine == NULL) {
   2099           Status = EFI_OUT_OF_RESOURCES;
   2100           break;
   2101         }
   2102 
   2103         //
   2104         // We know the space is sufficient since we just calculated it.
   2105         //
   2106         StrnCpyS(NewCommandLine, NewCmdLineSize/sizeof(CHAR16), L"help ", 5);
   2107         StrnCatS(NewCommandLine, NewCmdLineSize/sizeof(CHAR16), *CmdLine, StrLen(*CmdLine));
   2108         SHELL_FREE_NON_NULL(*CmdLine);
   2109         *CmdLine = NewCommandLine;
   2110         break;
   2111       }
   2112     }
   2113   }
   2114 
   2115   SHELL_FREE_NON_NULL(CurrentParameter);
   2116 
   2117   return (Status);
   2118 }
   2119 
   2120 /**
   2121   Function to update the shell variable "lasterror".
   2122 
   2123   @param[in] ErrorCode      the error code to put into lasterror.
   2124 **/
   2125 EFI_STATUS
   2126 EFIAPI
   2127 SetLastError(
   2128   IN CONST SHELL_STATUS   ErrorCode
   2129   )
   2130 {
   2131   CHAR16 LeString[19];
   2132   if (sizeof(EFI_STATUS) == sizeof(UINT64)) {
   2133     UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ErrorCode);
   2134   } else {
   2135     UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", ErrorCode);
   2136   }
   2137   DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););
   2138   InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);
   2139 
   2140   return (EFI_SUCCESS);
   2141 }
   2142 
   2143 /**
   2144   Converts the command line to it's post-processed form.  this replaces variables and alias' per UEFI Shell spec.
   2145 
   2146   @param[in,out] CmdLine        pointer to the command line to update
   2147 
   2148   @retval EFI_SUCCESS           The operation was successful
   2149   @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.
   2150   @return                       some other error occurred
   2151 **/
   2152 EFI_STATUS
   2153 EFIAPI
   2154 ProcessCommandLineToFinal(
   2155   IN OUT CHAR16 **CmdLine
   2156   )
   2157 {
   2158   EFI_STATUS                Status;
   2159   TrimSpaces(CmdLine);
   2160 
   2161   Status = ShellSubstituteAliases(CmdLine);
   2162   if (EFI_ERROR(Status)) {
   2163     return (Status);
   2164   }
   2165 
   2166   TrimSpaces(CmdLine);
   2167 
   2168   Status = ShellSubstituteVariables(CmdLine);
   2169   if (EFI_ERROR(Status)) {
   2170     return (Status);
   2171   }
   2172   ASSERT (*CmdLine != NULL);
   2173 
   2174   TrimSpaces(CmdLine);
   2175 
   2176   //
   2177   // update for help parsing
   2178   //
   2179   if (StrStr(*CmdLine, L"?") != NULL) {
   2180     //
   2181     // This may do nothing if the ? does not indicate help.
   2182     // Save all the details for in the API below.
   2183     //
   2184     Status = DoHelpUpdate(CmdLine);
   2185   }
   2186 
   2187   TrimSpaces(CmdLine);
   2188 
   2189   return (EFI_SUCCESS);
   2190 }
   2191 
   2192 /**
   2193   Run an internal shell command.
   2194 
   2195   This API will update the shell's environment since these commands are libraries.
   2196 
   2197   @param[in] CmdLine          the command line to run.
   2198   @param[in] FirstParameter   the first parameter on the command line
   2199   @param[in] ParamProtocol    the shell parameters protocol pointer
   2200   @param[out] CommandStatus   the status from the command line.
   2201 
   2202   @retval EFI_SUCCESS     The command was completed.
   2203   @retval EFI_ABORTED     The command's operation was aborted.
   2204 **/
   2205 EFI_STATUS
   2206 EFIAPI
   2207 RunInternalCommand(
   2208   IN CONST CHAR16                   *CmdLine,
   2209   IN       CHAR16                   *FirstParameter,
   2210   IN EFI_SHELL_PARAMETERS_PROTOCOL  *ParamProtocol,
   2211   OUT EFI_STATUS                    *CommandStatus
   2212 )
   2213 {
   2214   EFI_STATUS                Status;
   2215   UINTN                     Argc;
   2216   CHAR16                    **Argv;
   2217   SHELL_STATUS              CommandReturnedStatus;
   2218   BOOLEAN                   LastError;
   2219   CHAR16                    *Walker;
   2220   CHAR16                    *NewCmdLine;
   2221 
   2222   NewCmdLine = AllocateCopyPool (StrSize (CmdLine), CmdLine);
   2223   if (NewCmdLine == NULL) {
   2224     return EFI_OUT_OF_RESOURCES;
   2225   }
   2226 
   2227   for (Walker = NewCmdLine; Walker != NULL && *Walker != CHAR_NULL ; Walker++) {
   2228     if (*Walker == L'^' && *(Walker+1) == L'#') {
   2229       CopyMem(Walker, Walker+1, StrSize(Walker) - sizeof(Walker[0]));
   2230     }
   2231   }
   2232 
   2233   //
   2234   // get the argc and argv updated for internal commands
   2235   //
   2236   Status = UpdateArgcArgv(ParamProtocol, NewCmdLine, Internal_Command, &Argv, &Argc);
   2237   if (!EFI_ERROR(Status)) {
   2238     //
   2239     // Run the internal command.
   2240     //
   2241     Status = ShellCommandRunCommandHandler(FirstParameter, &CommandReturnedStatus, &LastError);
   2242 
   2243     if (!EFI_ERROR(Status)) {
   2244       if (CommandStatus != NULL) {
   2245         if (CommandReturnedStatus != SHELL_SUCCESS) {
   2246           *CommandStatus = (EFI_STATUS)(CommandReturnedStatus | MAX_BIT);
   2247         } else {
   2248           *CommandStatus = EFI_SUCCESS;
   2249         }
   2250       }
   2251 
   2252       //
   2253       // Update last error status.
   2254       // some commands do not update last error.
   2255       //
   2256       if (LastError) {
   2257         SetLastError(CommandReturnedStatus);
   2258       }
   2259 
   2260       //
   2261       // Pass thru the exitcode from the app.
   2262       //
   2263       if (ShellCommandGetExit()) {
   2264         //
   2265         // An Exit was requested ("exit" command), pass its value up.
   2266         //
   2267         Status = CommandReturnedStatus;
   2268       } else if (CommandReturnedStatus != SHELL_SUCCESS && IsScriptOnlyCommand(FirstParameter)) {
   2269         //
   2270         // Always abort when a script only command fails for any reason
   2271         //
   2272         Status = EFI_ABORTED;
   2273       } else if (ShellCommandGetCurrentScriptFile() != NULL && CommandReturnedStatus == SHELL_ABORTED) {
   2274         //
   2275         // Abort when in a script and a command aborted
   2276         //
   2277         Status = EFI_ABORTED;
   2278       }
   2279     }
   2280   }
   2281 
   2282   //
   2283   // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.
   2284   // This is safe even if the update API failed.  In this case, it may be a no-op.
   2285   //
   2286   RestoreArgcArgv(ParamProtocol, &Argv, &Argc);
   2287 
   2288   //
   2289   // If a script is running and the command is not a script only command, then
   2290   // change return value to success so the script won't halt (unless aborted).
   2291   //
   2292   // Script only commands have to be able halt the script since the script will
   2293   // not operate if they are failing.
   2294   //
   2295   if ( ShellCommandGetCurrentScriptFile() != NULL
   2296     && !IsScriptOnlyCommand(FirstParameter)
   2297     && Status != EFI_ABORTED
   2298     ) {
   2299     Status = EFI_SUCCESS;
   2300   }
   2301 
   2302   FreePool (NewCmdLine);
   2303   return (Status);
   2304 }
   2305 
   2306 /**
   2307   Function to run the command or file.
   2308 
   2309   @param[in] Type             the type of operation being run.
   2310   @param[in] CmdLine          the command line to run.
   2311   @param[in] FirstParameter   the first parameter on the command line
   2312   @param[in] ParamProtocol    the shell parameters protocol pointer
   2313   @param[out] CommandStatus   the status from the command line.
   2314 
   2315   @retval EFI_SUCCESS     The command was completed.
   2316   @retval EFI_ABORTED     The command's operation was aborted.
   2317 **/
   2318 EFI_STATUS
   2319 EFIAPI
   2320 RunCommandOrFile(
   2321   IN       SHELL_OPERATION_TYPES    Type,
   2322   IN CONST CHAR16                   *CmdLine,
   2323   IN       CHAR16                   *FirstParameter,
   2324   IN EFI_SHELL_PARAMETERS_PROTOCOL  *ParamProtocol,
   2325   OUT EFI_STATUS                    *CommandStatus
   2326 )
   2327 {
   2328   EFI_STATUS                Status;
   2329   EFI_STATUS                StartStatus;
   2330   CHAR16                    *CommandWithPath;
   2331   EFI_DEVICE_PATH_PROTOCOL  *DevPath;
   2332   SHELL_STATUS              CalleeExitStatus;
   2333 
   2334   Status            = EFI_SUCCESS;
   2335   CommandWithPath   = NULL;
   2336   DevPath           = NULL;
   2337   CalleeExitStatus  = SHELL_INVALID_PARAMETER;
   2338 
   2339   switch (Type) {
   2340     case   Internal_Command:
   2341       Status = RunInternalCommand(CmdLine, FirstParameter, ParamProtocol, CommandStatus);
   2342       break;
   2343     case   Script_File_Name:
   2344     case   Efi_Application:
   2345       //
   2346       // Process a fully qualified path
   2347       //
   2348       if (StrStr(FirstParameter, L":") != NULL) {
   2349         ASSERT (CommandWithPath == NULL);
   2350         if (ShellIsFile(FirstParameter) == EFI_SUCCESS) {
   2351           CommandWithPath = StrnCatGrow(&CommandWithPath, NULL, FirstParameter, 0);
   2352         }
   2353       }
   2354 
   2355       //
   2356       // Process a relative path and also check in the path environment variable
   2357       //
   2358       if (CommandWithPath == NULL) {
   2359         CommandWithPath = ShellFindFilePathEx(FirstParameter, mExecutableExtensions);
   2360       }
   2361 
   2362       //
   2363       // This should be impossible now.
   2364       //
   2365       ASSERT(CommandWithPath != NULL);
   2366 
   2367       //
   2368       // Make sure that path is not just a directory (or not found)
   2369       //
   2370       if (!EFI_ERROR(ShellIsDirectory(CommandWithPath))) {
   2371         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
   2372         SetLastError(SHELL_NOT_FOUND);
   2373       }
   2374       switch (Type) {
   2375         case   Script_File_Name:
   2376           Status = RunScriptFile (CommandWithPath, NULL, CmdLine, ParamProtocol);
   2377           break;
   2378         case   Efi_Application:
   2379           //
   2380           // Get the device path of the application image
   2381           //
   2382           DevPath = ShellInfoObject.NewEfiShellProtocol->GetDevicePathFromFilePath(CommandWithPath);
   2383           if (DevPath == NULL){
   2384             Status = EFI_OUT_OF_RESOURCES;
   2385             break;
   2386           }
   2387 
   2388           //
   2389           // Execute the device path
   2390           //
   2391           Status = InternalShellExecuteDevicePath(
   2392             &gImageHandle,
   2393             DevPath,
   2394             CmdLine,
   2395             NULL,
   2396             &StartStatus
   2397            );
   2398 
   2399           SHELL_FREE_NON_NULL(DevPath);
   2400 
   2401           if(EFI_ERROR (Status)) {
   2402             CalleeExitStatus = (SHELL_STATUS) (Status & (~MAX_BIT));
   2403           } else {
   2404             CalleeExitStatus = (SHELL_STATUS) StartStatus;
   2405           }
   2406 
   2407           if (CommandStatus != NULL) {
   2408             *CommandStatus = CalleeExitStatus;
   2409           }
   2410 
   2411           //
   2412           // Update last error status.
   2413           //
   2414           // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS
   2415           SetLastError(CalleeExitStatus);
   2416           break;
   2417         default:
   2418           //
   2419           // Do nothing.
   2420           //
   2421           break;
   2422       }
   2423       break;
   2424     default:
   2425       //
   2426       // Do nothing.
   2427       //
   2428       break;
   2429   }
   2430 
   2431   SHELL_FREE_NON_NULL(CommandWithPath);
   2432 
   2433   return (Status);
   2434 }
   2435 
   2436 /**
   2437   Function to setup StdIn, StdErr, StdOut, and then run the command or file.
   2438 
   2439   @param[in] Type             the type of operation being run.
   2440   @param[in] CmdLine          the command line to run.
   2441   @param[in] FirstParameter   the first parameter on the command line.
   2442   @param[in] ParamProtocol    the shell parameters protocol pointer
   2443   @param[out] CommandStatus   the status from the command line.
   2444 
   2445   @retval EFI_SUCCESS     The command was completed.
   2446   @retval EFI_ABORTED     The command's operation was aborted.
   2447 **/
   2448 EFI_STATUS
   2449 EFIAPI
   2450 SetupAndRunCommandOrFile(
   2451   IN   SHELL_OPERATION_TYPES          Type,
   2452   IN   CHAR16                         *CmdLine,
   2453   IN   CHAR16                         *FirstParameter,
   2454   IN   EFI_SHELL_PARAMETERS_PROTOCOL  *ParamProtocol,
   2455   OUT EFI_STATUS                    *CommandStatus
   2456 )
   2457 {
   2458   EFI_STATUS                Status;
   2459   SHELL_FILE_HANDLE         OriginalStdIn;
   2460   SHELL_FILE_HANDLE         OriginalStdOut;
   2461   SHELL_FILE_HANDLE         OriginalStdErr;
   2462   SYSTEM_TABLE_INFO         OriginalSystemTableInfo;
   2463 
   2464   //
   2465   // Update the StdIn, StdOut, and StdErr for redirection to environment variables, files, etc... unicode and ASCII
   2466   //
   2467   Status = UpdateStdInStdOutStdErr(ParamProtocol, CmdLine, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);
   2468 
   2469   //
   2470   // The StdIn, StdOut, and StdErr are set up.
   2471   // Now run the command, script, or application
   2472   //
   2473   if (!EFI_ERROR(Status)) {
   2474     TrimSpaces(&CmdLine);
   2475     Status = RunCommandOrFile(Type, CmdLine, FirstParameter, ParamProtocol, CommandStatus);
   2476   }
   2477 
   2478   //
   2479   // Now print errors
   2480   //
   2481   if (EFI_ERROR(Status)) {
   2482     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_ERROR), ShellInfoObject.HiiHandle, (VOID*)(Status));
   2483   }
   2484 
   2485   //
   2486   // put back the original StdIn, StdOut, and StdErr
   2487   //
   2488   RestoreStdInStdOutStdErr(ParamProtocol, &OriginalStdIn, &OriginalStdOut, &OriginalStdErr, &OriginalSystemTableInfo);
   2489 
   2490   return (Status);
   2491 }
   2492 
   2493 /**
   2494   Function will process and run a command line.
   2495 
   2496   This will determine if the command line represents an internal shell
   2497   command or dispatch an external application.
   2498 
   2499   @param[in] CmdLine      The command line to parse.
   2500   @param[out] CommandStatus   The status from the command line.
   2501 
   2502   @retval EFI_SUCCESS     The command was completed.
   2503   @retval EFI_ABORTED     The command's operation was aborted.
   2504 **/
   2505 EFI_STATUS
   2506 EFIAPI
   2507 RunShellCommand(
   2508   IN CONST CHAR16   *CmdLine,
   2509   OUT EFI_STATUS    *CommandStatus
   2510   )
   2511 {
   2512   EFI_STATUS                Status;
   2513   CHAR16                    *CleanOriginal;
   2514   CHAR16                    *FirstParameter;
   2515   CHAR16                    *TempWalker;
   2516   SHELL_OPERATION_TYPES     Type;
   2517 
   2518   ASSERT(CmdLine != NULL);
   2519   if (StrLen(CmdLine) == 0) {
   2520     return (EFI_SUCCESS);
   2521   }
   2522 
   2523   Status              = EFI_SUCCESS;
   2524   CleanOriginal       = NULL;
   2525 
   2526   CleanOriginal = StrnCatGrow(&CleanOriginal, NULL, CmdLine, 0);
   2527   if (CleanOriginal == NULL) {
   2528     return (EFI_OUT_OF_RESOURCES);
   2529   }
   2530 
   2531   TrimSpaces(&CleanOriginal);
   2532 
   2533   //
   2534   // NULL out comments (leveraged from RunScriptFileHandle() ).
   2535   // The # character on a line is used to denote that all characters on the same line
   2536   // and to the right of the # are to be ignored by the shell.
   2537   // Afterwards, again remove spaces, in case any were between the last command-parameter and '#'.
   2538   //
   2539   for (TempWalker = CleanOriginal; TempWalker != NULL && *TempWalker != CHAR_NULL; TempWalker++) {
   2540     if (*TempWalker == L'^') {
   2541       if (*(TempWalker + 1) == L'#') {
   2542         TempWalker++;
   2543       }
   2544     } else if (*TempWalker == L'#') {
   2545       *TempWalker = CHAR_NULL;
   2546     }
   2547   }
   2548 
   2549   TrimSpaces(&CleanOriginal);
   2550 
   2551   //
   2552   // Handle case that passed in command line is just 1 or more " " characters.
   2553   //
   2554   if (StrLen (CleanOriginal) == 0) {
   2555     SHELL_FREE_NON_NULL(CleanOriginal);
   2556     return (EFI_SUCCESS);
   2557   }
   2558 
   2559   Status = ProcessCommandLineToFinal(&CleanOriginal);
   2560   if (EFI_ERROR(Status)) {
   2561     SHELL_FREE_NON_NULL(CleanOriginal);
   2562     return (Status);
   2563   }
   2564 
   2565   //
   2566   // We don't do normal processing with a split command line (output from one command input to another)
   2567   //
   2568   if (ContainsSplit(CleanOriginal)) {
   2569     Status = ProcessNewSplitCommandLine(CleanOriginal);
   2570     SHELL_FREE_NON_NULL(CleanOriginal);
   2571     return (Status);
   2572   }
   2573 
   2574   //
   2575   // We need the first parameter information so we can determine the operation type
   2576   //
   2577   FirstParameter = AllocateZeroPool(StrSize(CleanOriginal));
   2578   if (FirstParameter == NULL) {
   2579     SHELL_FREE_NON_NULL(CleanOriginal);
   2580     return (EFI_OUT_OF_RESOURCES);
   2581   }
   2582   TempWalker = CleanOriginal;
   2583   if (!EFI_ERROR(GetNextParameter(&TempWalker, &FirstParameter, StrSize(CleanOriginal), TRUE))) {
   2584     //
   2585     // Depending on the first parameter we change the behavior
   2586     //
   2587     switch (Type = GetOperationType(FirstParameter)) {
   2588       case   File_Sys_Change:
   2589         Status = ChangeMappedDrive (FirstParameter);
   2590         break;
   2591       case   Internal_Command:
   2592       case   Script_File_Name:
   2593       case   Efi_Application:
   2594         Status = SetupAndRunCommandOrFile(Type, CleanOriginal, FirstParameter, ShellInfoObject.NewShellParametersProtocol, CommandStatus);
   2595         break;
   2596       default:
   2597         //
   2598         // Whatever was typed, it was invalid.
   2599         //
   2600         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
   2601         SetLastError(SHELL_NOT_FOUND);
   2602         break;
   2603     }
   2604   } else {
   2605     ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, FirstParameter);
   2606     SetLastError(SHELL_NOT_FOUND);
   2607   }
   2608 
   2609   SHELL_FREE_NON_NULL(CleanOriginal);
   2610   SHELL_FREE_NON_NULL(FirstParameter);
   2611 
   2612   return (Status);
   2613 }
   2614 
   2615 /**
   2616   Function will process and run a command line.
   2617 
   2618   This will determine if the command line represents an internal shell
   2619   command or dispatch an external application.
   2620 
   2621   @param[in] CmdLine      The command line to parse.
   2622 
   2623   @retval EFI_SUCCESS     The command was completed.
   2624   @retval EFI_ABORTED     The command's operation was aborted.
   2625 **/
   2626 EFI_STATUS
   2627 EFIAPI
   2628 RunCommand(
   2629   IN CONST CHAR16   *CmdLine
   2630   )
   2631 {
   2632   return (RunShellCommand(CmdLine, NULL));
   2633 }
   2634 
   2635 
   2636 STATIC CONST UINT16 InvalidChars[] = {L'*', L'?', L'<', L'>', L'\\', L'/', L'\"', 0x0001, 0x0002};
   2637 /**
   2638   Function determines if the CommandName COULD be a valid command.  It does not determine whether
   2639   this is a valid command.  It only checks for invalid characters.
   2640 
   2641   @param[in] CommandName    The name to check
   2642 
   2643   @retval TRUE              CommandName could be a command name
   2644   @retval FALSE             CommandName could not be a valid command name
   2645 **/
   2646 BOOLEAN
   2647 EFIAPI
   2648 IsValidCommandName(
   2649   IN CONST CHAR16     *CommandName
   2650   )
   2651 {
   2652   UINTN Count;
   2653   if (CommandName == NULL) {
   2654     ASSERT(FALSE);
   2655     return (FALSE);
   2656   }
   2657   for ( Count = 0
   2658       ; Count < sizeof(InvalidChars) / sizeof(InvalidChars[0])
   2659       ; Count++
   2660      ){
   2661     if (ScanMem16(CommandName, StrSize(CommandName), InvalidChars[Count]) != NULL) {
   2662       return (FALSE);
   2663     }
   2664   }
   2665   return (TRUE);
   2666 }
   2667 
   2668 /**
   2669   Function to process a NSH script file via SHELL_FILE_HANDLE.
   2670 
   2671   @param[in] Handle             The handle to the already opened file.
   2672   @param[in] Name               The name of the script file.
   2673 
   2674   @retval EFI_SUCCESS           the script completed successfully
   2675 **/
   2676 EFI_STATUS
   2677 EFIAPI
   2678 RunScriptFileHandle (
   2679   IN SHELL_FILE_HANDLE  Handle,
   2680   IN CONST CHAR16       *Name
   2681   )
   2682 {
   2683   EFI_STATUS          Status;
   2684   SCRIPT_FILE         *NewScriptFile;
   2685   UINTN               LoopVar;
   2686   UINTN               PrintBuffSize;
   2687   CHAR16              *CommandLine;
   2688   CHAR16              *CommandLine2;
   2689   CHAR16              *CommandLine3;
   2690   SCRIPT_COMMAND_LIST *LastCommand;
   2691   BOOLEAN             Ascii;
   2692   BOOLEAN             PreScriptEchoState;
   2693   BOOLEAN             PreCommandEchoState;
   2694   CONST CHAR16        *CurDir;
   2695   UINTN               LineCount;
   2696   CHAR16              LeString[50];
   2697   LIST_ENTRY          OldBufferList;
   2698 
   2699   ASSERT(!ShellCommandGetScriptExit());
   2700 
   2701   PreScriptEchoState = ShellCommandGetEchoState();
   2702   PrintBuffSize = PcdGet16(PcdShellPrintBufferSize);
   2703 
   2704   NewScriptFile = (SCRIPT_FILE*)AllocateZeroPool(sizeof(SCRIPT_FILE));
   2705   if (NewScriptFile == NULL) {
   2706     return (EFI_OUT_OF_RESOURCES);
   2707   }
   2708 
   2709   //
   2710   // Set up the name
   2711   //
   2712   ASSERT(NewScriptFile->ScriptName == NULL);
   2713   NewScriptFile->ScriptName = StrnCatGrow(&NewScriptFile->ScriptName, NULL, Name, 0);
   2714   if (NewScriptFile->ScriptName == NULL) {
   2715     DeleteScriptFileStruct(NewScriptFile);
   2716     return (EFI_OUT_OF_RESOURCES);
   2717   }
   2718 
   2719   //
   2720   // Save the parameters (used to replace %0 to %9 later on)
   2721   //
   2722   NewScriptFile->Argc = ShellInfoObject.NewShellParametersProtocol->Argc;
   2723   if (NewScriptFile->Argc != 0) {
   2724     NewScriptFile->Argv = (CHAR16**)AllocateZeroPool(NewScriptFile->Argc * sizeof(CHAR16*));
   2725     if (NewScriptFile->Argv == NULL) {
   2726       DeleteScriptFileStruct(NewScriptFile);
   2727       return (EFI_OUT_OF_RESOURCES);
   2728     }
   2729     for (LoopVar = 0 ; LoopVar < 10 && LoopVar < NewScriptFile->Argc; LoopVar++) {
   2730       ASSERT(NewScriptFile->Argv[LoopVar] == NULL);
   2731       NewScriptFile->Argv[LoopVar] = StrnCatGrow(&NewScriptFile->Argv[LoopVar], NULL, ShellInfoObject.NewShellParametersProtocol->Argv[LoopVar], 0);
   2732       if (NewScriptFile->Argv[LoopVar] == NULL) {
   2733         DeleteScriptFileStruct(NewScriptFile);
   2734         return (EFI_OUT_OF_RESOURCES);
   2735       }
   2736     }
   2737   } else {
   2738     NewScriptFile->Argv = NULL;
   2739   }
   2740 
   2741   InitializeListHead(&NewScriptFile->CommandList);
   2742   InitializeListHead(&NewScriptFile->SubstList);
   2743 
   2744   //
   2745   // Now build the list of all script commands.
   2746   //
   2747   LineCount = 0;
   2748   while(!ShellFileHandleEof(Handle)) {
   2749     CommandLine = ShellFileHandleReturnLine(Handle, &Ascii);
   2750     LineCount++;
   2751     if (CommandLine == NULL || StrLen(CommandLine) == 0 || CommandLine[0] == '#') {
   2752       SHELL_FREE_NON_NULL(CommandLine);
   2753       continue;
   2754     }
   2755     NewScriptFile->CurrentCommand = AllocateZeroPool(sizeof(SCRIPT_COMMAND_LIST));
   2756     if (NewScriptFile->CurrentCommand == NULL) {
   2757       SHELL_FREE_NON_NULL(CommandLine);
   2758       DeleteScriptFileStruct(NewScriptFile);
   2759       return (EFI_OUT_OF_RESOURCES);
   2760     }
   2761 
   2762     NewScriptFile->CurrentCommand->Cl   = CommandLine;
   2763     NewScriptFile->CurrentCommand->Data = NULL;
   2764     NewScriptFile->CurrentCommand->Line = LineCount;
   2765 
   2766     InsertTailList(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
   2767   }
   2768 
   2769   //
   2770   // Add this as the topmost script file
   2771   //
   2772   ShellCommandSetNewScript (NewScriptFile);
   2773 
   2774   //
   2775   // Now enumerate through the commands and run each one.
   2776   //
   2777   CommandLine = AllocateZeroPool(PrintBuffSize);
   2778   if (CommandLine == NULL) {
   2779     DeleteScriptFileStruct(NewScriptFile);
   2780     return (EFI_OUT_OF_RESOURCES);
   2781   }
   2782   CommandLine2 = AllocateZeroPool(PrintBuffSize);
   2783   if (CommandLine2 == NULL) {
   2784     FreePool(CommandLine);
   2785     DeleteScriptFileStruct(NewScriptFile);
   2786     return (EFI_OUT_OF_RESOURCES);
   2787   }
   2788 
   2789   for ( NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetFirstNode(&NewScriptFile->CommandList)
   2790       ; !IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)
   2791       ; // conditional increment in the body of the loop
   2792   ){
   2793     ASSERT(CommandLine2 != NULL);
   2794     StrnCpyS( CommandLine2,
   2795               PrintBuffSize/sizeof(CHAR16),
   2796               NewScriptFile->CurrentCommand->Cl,
   2797               PrintBuffSize/sizeof(CHAR16) - 1
   2798               );
   2799 
   2800     SaveBufferList(&OldBufferList);
   2801 
   2802     //
   2803     // NULL out comments
   2804     //
   2805     for (CommandLine3 = CommandLine2 ; CommandLine3 != NULL && *CommandLine3 != CHAR_NULL ; CommandLine3++) {
   2806       if (*CommandLine3 == L'^') {
   2807         if ( *(CommandLine3+1) == L':') {
   2808           CopyMem(CommandLine3, CommandLine3+1, StrSize(CommandLine3) - sizeof(CommandLine3[0]));
   2809         } else if (*(CommandLine3+1) == L'#') {
   2810           CommandLine3++;
   2811         }
   2812       } else if (*CommandLine3 == L'#') {
   2813         *CommandLine3 = CHAR_NULL;
   2814       }
   2815     }
   2816 
   2817     if (CommandLine2 != NULL && StrLen(CommandLine2) >= 1) {
   2818       //
   2819       // Due to variability in starting the find and replace action we need to have both buffers the same.
   2820       //
   2821       StrnCpyS( CommandLine,
   2822                 PrintBuffSize/sizeof(CHAR16),
   2823                 CommandLine2,
   2824                 PrintBuffSize/sizeof(CHAR16) - 1
   2825                 );
   2826 
   2827       //
   2828       // Remove the %0 to %9 from the command line (if we have some arguments)
   2829       //
   2830       if (NewScriptFile->Argv != NULL) {
   2831         switch (NewScriptFile->Argc) {
   2832           default:
   2833             Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%9", NewScriptFile->Argv[9], FALSE, FALSE);
   2834             ASSERT_EFI_ERROR(Status);
   2835           case 9:
   2836             Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%8", NewScriptFile->Argv[8], FALSE, FALSE);
   2837             ASSERT_EFI_ERROR(Status);
   2838           case 8:
   2839             Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%7", NewScriptFile->Argv[7], FALSE, FALSE);
   2840             ASSERT_EFI_ERROR(Status);
   2841           case 7:
   2842             Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%6", NewScriptFile->Argv[6], FALSE, FALSE);
   2843             ASSERT_EFI_ERROR(Status);
   2844           case 6:
   2845             Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%5", NewScriptFile->Argv[5], FALSE, FALSE);
   2846             ASSERT_EFI_ERROR(Status);
   2847           case 5:
   2848             Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%4", NewScriptFile->Argv[4], FALSE, FALSE);
   2849             ASSERT_EFI_ERROR(Status);
   2850           case 4:
   2851             Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%3", NewScriptFile->Argv[3], FALSE, FALSE);
   2852             ASSERT_EFI_ERROR(Status);
   2853           case 3:
   2854             Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%2", NewScriptFile->Argv[2], FALSE, FALSE);
   2855             ASSERT_EFI_ERROR(Status);
   2856           case 2:
   2857             Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%1", NewScriptFile->Argv[1], FALSE, FALSE);
   2858             ASSERT_EFI_ERROR(Status);
   2859           case 1:
   2860             Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%0", NewScriptFile->Argv[0], FALSE, FALSE);
   2861             ASSERT_EFI_ERROR(Status);
   2862             break;
   2863           case 0:
   2864             break;
   2865         }
   2866       }
   2867       Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%1", L"\"\"", FALSE, FALSE);
   2868       Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%2", L"\"\"", FALSE, FALSE);
   2869       Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%3", L"\"\"", FALSE, FALSE);
   2870       Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%4", L"\"\"", FALSE, FALSE);
   2871       Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%5", L"\"\"", FALSE, FALSE);
   2872       Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%6", L"\"\"", FALSE, FALSE);
   2873       Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%7", L"\"\"", FALSE, FALSE);
   2874       Status = ShellCopySearchAndReplace(CommandLine,  CommandLine2, PrintBuffSize, L"%8", L"\"\"", FALSE, FALSE);
   2875       Status = ShellCopySearchAndReplace(CommandLine2,  CommandLine, PrintBuffSize, L"%9", L"\"\"", FALSE, FALSE);
   2876 
   2877       StrnCpyS( CommandLine2,
   2878                 PrintBuffSize/sizeof(CHAR16),
   2879                 CommandLine,
   2880                 PrintBuffSize/sizeof(CHAR16) - 1
   2881                 );
   2882 
   2883       LastCommand = NewScriptFile->CurrentCommand;
   2884 
   2885       for (CommandLine3 = CommandLine2 ; CommandLine3[0] == L' ' ; CommandLine3++);
   2886 
   2887       if (CommandLine3 != NULL && CommandLine3[0] == L':' ) {
   2888         //
   2889         // This line is a goto target / label
   2890         //
   2891       } else {
   2892         if (CommandLine3 != NULL && StrLen(CommandLine3) > 0) {
   2893           if (CommandLine3[0] == L'@') {
   2894             //
   2895             // We need to save the current echo state
   2896             // and disable echo for just this command.
   2897             //
   2898             PreCommandEchoState = ShellCommandGetEchoState();
   2899             ShellCommandSetEchoState(FALSE);
   2900             Status = RunCommand(CommandLine3+1);
   2901 
   2902             //
   2903             // If command was "@echo -off" or "@echo -on" then don't restore echo state
   2904             //
   2905             if (StrCmp (L"@echo -off", CommandLine3) != 0 &&
   2906                 StrCmp (L"@echo -on", CommandLine3) != 0) {
   2907               //
   2908               // Now restore the pre-'@' echo state.
   2909               //
   2910               ShellCommandSetEchoState(PreCommandEchoState);
   2911             }
   2912           } else {
   2913             if (ShellCommandGetEchoState()) {
   2914               CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");
   2915               if (CurDir != NULL && StrLen(CurDir) > 1) {
   2916                 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_CURDIR), ShellInfoObject.HiiHandle, CurDir);
   2917               } else {
   2918                 ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_SHELL), ShellInfoObject.HiiHandle);
   2919               }
   2920               ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2);
   2921             }
   2922             Status = RunCommand(CommandLine3);
   2923           }
   2924         }
   2925 
   2926         if (ShellCommandGetScriptExit()) {
   2927           //
   2928           // ShellCommandGetExitCode() always returns a UINT64
   2929           //
   2930           UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ShellCommandGetExitCode());
   2931           DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););
   2932           InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);
   2933 
   2934           ShellCommandRegisterExit(FALSE, 0);
   2935           Status = EFI_SUCCESS;
   2936           RestoreBufferList(&OldBufferList);
   2937           break;
   2938         }
   2939         if (ShellGetExecutionBreakFlag()) {
   2940           RestoreBufferList(&OldBufferList);
   2941           break;
   2942         }
   2943         if (EFI_ERROR(Status)) {
   2944           RestoreBufferList(&OldBufferList);
   2945           break;
   2946         }
   2947         if (ShellCommandGetExit()) {
   2948           RestoreBufferList(&OldBufferList);
   2949           break;
   2950         }
   2951       }
   2952       //
   2953       // If that commend did not update the CurrentCommand then we need to advance it...
   2954       //
   2955       if (LastCommand == NewScriptFile->CurrentCommand) {
   2956         NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
   2957         if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {
   2958           NewScriptFile->CurrentCommand->Reset = TRUE;
   2959         }
   2960       }
   2961     } else {
   2962       NewScriptFile->CurrentCommand = (SCRIPT_COMMAND_LIST *)GetNextNode(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link);
   2963       if (!IsNull(&NewScriptFile->CommandList, &NewScriptFile->CurrentCommand->Link)) {
   2964         NewScriptFile->CurrentCommand->Reset = TRUE;
   2965       }
   2966     }
   2967     RestoreBufferList(&OldBufferList);
   2968   }
   2969 
   2970 
   2971   FreePool(CommandLine);
   2972   FreePool(CommandLine2);
   2973   ShellCommandSetNewScript (NULL);
   2974 
   2975   //
   2976   // Only if this was the last script reset the state.
   2977   //
   2978   if (ShellCommandGetCurrentScriptFile()==NULL) {
   2979     ShellCommandSetEchoState(PreScriptEchoState);
   2980   }
   2981   return (EFI_SUCCESS);
   2982 }
   2983 
   2984 /**
   2985   Function to process a NSH script file.
   2986 
   2987   @param[in] ScriptPath         Pointer to the script file name (including file system path).
   2988   @param[in] Handle             the handle of the script file already opened.
   2989   @param[in] CmdLine            the command line to run.
   2990   @param[in] ParamProtocol      the shell parameters protocol pointer
   2991 
   2992   @retval EFI_SUCCESS           the script completed successfully
   2993 **/
   2994 EFI_STATUS
   2995 EFIAPI
   2996 RunScriptFile (
   2997   IN CONST CHAR16                   *ScriptPath,
   2998   IN SHELL_FILE_HANDLE              Handle OPTIONAL,
   2999   IN CONST CHAR16                   *CmdLine,
   3000   IN EFI_SHELL_PARAMETERS_PROTOCOL  *ParamProtocol
   3001   )
   3002 {
   3003   EFI_STATUS          Status;
   3004   SHELL_FILE_HANDLE   FileHandle;
   3005   UINTN                     Argc;
   3006   CHAR16                    **Argv;
   3007 
   3008   if (ShellIsFile(ScriptPath) != EFI_SUCCESS) {
   3009     return (EFI_INVALID_PARAMETER);
   3010   }
   3011 
   3012   //
   3013   // get the argc and argv updated for scripts
   3014   //
   3015   Status = UpdateArgcArgv(ParamProtocol, CmdLine, Script_File_Name, &Argv, &Argc);
   3016   if (!EFI_ERROR(Status)) {
   3017 
   3018     if (Handle == NULL) {
   3019       //
   3020       // open the file
   3021       //
   3022       Status = ShellOpenFileByName(ScriptPath, &FileHandle, EFI_FILE_MODE_READ, 0);
   3023       if (!EFI_ERROR(Status)) {
   3024         //
   3025         // run it
   3026         //
   3027         Status = RunScriptFileHandle(FileHandle, ScriptPath);
   3028 
   3029         //
   3030         // now close the file
   3031         //
   3032         ShellCloseFile(&FileHandle);
   3033       }
   3034     } else {
   3035       Status = RunScriptFileHandle(Handle, ScriptPath);
   3036     }
   3037   }
   3038 
   3039   //
   3040   // This is guaranteed to be called after UpdateArgcArgv no matter what else happened.
   3041   // This is safe even if the update API failed.  In this case, it may be a no-op.
   3042   //
   3043   RestoreArgcArgv(ParamProtocol, &Argv, &Argc);
   3044 
   3045   return (Status);
   3046 }
   3047 
   3048 /**
   3049   Return the pointer to the first occurrence of any character from a list of characters.
   3050 
   3051   @param[in] String           the string to parse
   3052   @param[in] CharacterList    the list of character to look for
   3053   @param[in] EscapeCharacter  An escape character to skip
   3054 
   3055   @return the location of the first character in the string
   3056   @retval CHAR_NULL no instance of any character in CharacterList was found in String
   3057 **/
   3058 CONST CHAR16*
   3059 EFIAPI
   3060 FindFirstCharacter(
   3061   IN CONST CHAR16 *String,
   3062   IN CONST CHAR16 *CharacterList,
   3063   IN CONST CHAR16 EscapeCharacter
   3064   )
   3065 {
   3066   UINT32 WalkChar;
   3067   UINT32 WalkStr;
   3068 
   3069   for (WalkStr = 0; WalkStr < StrLen(String); WalkStr++) {
   3070     if (String[WalkStr] == EscapeCharacter) {
   3071       WalkStr++;
   3072       continue;
   3073     }
   3074     for (WalkChar = 0; WalkChar < StrLen(CharacterList); WalkChar++) {
   3075       if (String[WalkStr] == CharacterList[WalkChar]) {
   3076         return (&String[WalkStr]);
   3077       }
   3078     }
   3079   }
   3080   return (String + StrLen(String));
   3081 }
   3082