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