]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ShellPkg/Application/Shell/Shell.c
ShellPkg: refactor out leading and trailing space trimming
[mirror_edk2.git] / ShellPkg / Application / Shell / Shell.c
index 182f896dbb3421aee46e93f7c1a7f219163bdeea..7af8153b4d560bcc4a211b599ff4ffbb73179560 100644 (file)
@@ -1,7 +1,8 @@
 /** @file\r
   This is THE shell (application)\r
 \r
-  Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2013, Hewlett-Packard Development Company, L.P.\r
   This program and the accompanying materials\r
   are licensed and made available under the terms and conditions of the BSD License\r
   which accompanies this distribution.  The full text of the license may be found at\r
@@ -23,22 +24,24 @@ SHELL_INFO ShellInfoObject = {
   FALSE,\r
   FALSE,\r
   {\r
-    0,\r
-    0,\r
-    0,\r
-    0,\r
-    0,\r
-    0,\r
-    0,\r
-    0,\r
-    0,\r
+    {{\r
+      0,\r
+      0,\r
+      0,\r
+      0,\r
+      0,\r
+      0,\r
+      0,\r
+      0,\r
+      0\r
+    }},\r
     0,\r
     NULL,\r
     NULL\r
   },\r
-  {0,0},\r
+  {{NULL, NULL}, NULL},\r
   {\r
-    {0,0},\r
+    {{NULL, NULL}, NULL},\r
     0,\r
     0,\r
     TRUE\r
@@ -50,8 +53,12 @@ SHELL_INFO ShellInfoObject = {
   NULL,\r
   NULL,\r
   NULL,\r
-  {0,0,NULL,NULL},\r
-  {0,0},\r
+  {{NULL, NULL}, NULL, NULL},\r
+  {{NULL, NULL}, NULL, NULL},\r
+  NULL,\r
+  NULL,\r
+  NULL,\r
+  NULL,\r
   NULL,\r
   NULL,\r
   NULL,\r
@@ -63,6 +70,83 @@ STATIC CONST CHAR16 mScriptExtension[]      = L".NSH";
 STATIC CONST CHAR16 mExecutableExtensions[] = L".NSH;.EFI";\r
 STATIC CONST CHAR16 mStartupScript[]        = L"startup.nsh";\r
 \r
+/**\r
+  Cleans off leading and trailing spaces and tabs\r
+\r
+  @param[in] String pointer to the string to trim them off\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+TrimSpaces(\r
+  IN CHAR16 **String\r
+  )\r
+{\r
+  ASSERT(String != NULL);\r
+  ASSERT(*String!= NULL);\r
+  //\r
+  // Remove any spaces and tabs at the beginning of the (*String).\r
+  //\r
+  while (((*String)[0] == L' ') || ((*String)[0] == L'\t')) {\r
+    CopyMem((*String), (*String)+1, StrSize((*String)) - sizeof((*String)[0]));\r
+  }\r
+\r
+  //\r
+  // Remove any spaces at the end of the (*String).\r
+  //\r
+  while ((*String)[StrLen((*String))-1] == L' ') {\r
+    (*String)[StrLen((*String))-1] = CHAR_NULL;\r
+  }\r
+\r
+  return (EFI_SUCCESS);\r
+}\r
+\r
+/**\r
+  Find a command line contains a split operation\r
+\r
+  @param[in] CmdLine      The command line to parse.\r
+\r
+  @retval                 A pointer to the | character in CmdLine or NULL if not present.\r
+**/\r
+CONST\r
+CHAR16*\r
+EFIAPI\r
+FindSplit(\r
+  IN CONST CHAR16 *CmdLine\r
+  )\r
+{\r
+  CONST CHAR16 *TempSpot;\r
+  TempSpot = NULL;\r
+  if (StrStr(CmdLine, L"|") != NULL) {\r
+    for (TempSpot = CmdLine ; TempSpot != NULL && *TempSpot != CHAR_NULL ; TempSpot++) {\r
+      if (*TempSpot == L'^' && *(TempSpot+1) == L'|') {\r
+        TempSpot++;\r
+      } else if (*TempSpot == L'|') {\r
+        break;\r
+      }\r
+    }\r
+  }\r
+  return (TempSpot);\r
+}\r
+\r
+/**\r
+  Determine if a command line contains a split operation\r
+\r
+  @param[in] CmdLine      The command line to parse.\r
+\r
+  @retval TRUE            CmdLine has a valid split.\r
+  @retval FALSE           CmdLine does not have a valid split.\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+ContainsSplit(\r
+  IN CONST CHAR16 *CmdLine\r
+  )\r
+{\r
+  CONST CHAR16 *TempSpot;\r
+  TempSpot = FindSplit(CmdLine);\r
+  return (TempSpot != NULL && *TempSpot != CHAR_NULL);\r
+}\r
+\r
 /**\r
   Function to start monitoring for CTRL-S using SimpleTextInputEx.  This \r
   feature's enabled state was not known when the shell initially launched.\r
@@ -124,7 +208,7 @@ InternalEfiShellStartCtrlSMonitor(
       SimpleEx,\r
       &KeyData,\r
       NotificationFunction,\r
-      &ShellInfoObject.CtrlSNotifyHandle2);\r
+      &ShellInfoObject.CtrlSNotifyHandle3);\r
   }  \r
   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;\r
   if (!EFI_ERROR(Status)) {\r
@@ -132,7 +216,7 @@ InternalEfiShellStartCtrlSMonitor(
       SimpleEx,\r
       &KeyData,\r
       NotificationFunction,\r
-      &ShellInfoObject.CtrlSNotifyHandle2);\r
+      &ShellInfoObject.CtrlSNotifyHandle4);\r
   }\r
   return (Status);\r
 }\r
@@ -290,11 +374,28 @@ UefiMain (
         0,\r
         gST->ConOut->Mode->CursorRow,\r
         NULL,\r
-        STRING_TOKEN (STR_VER_OUTPUT_MAIN),\r
+        STRING_TOKEN (STR_VER_OUTPUT_MAIN_SHELL),\r
         ShellInfoObject.HiiHandle,\r
         SupportLevel[PcdGet8(PcdShellSupportLevel)],\r
         gEfiShellProtocol->MajorVersion,\r
-        gEfiShellProtocol->MinorVersion,\r
+        gEfiShellProtocol->MinorVersion\r
+       );\r
+\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_VER_OUTPUT_MAIN_SUPPLIER),\r
+        ShellInfoObject.HiiHandle,\r
+        (CHAR16 *) PcdGetPtr (PcdShellSupplier)\r
+       );\r
+\r
+      ShellPrintHiiEx (\r
+        -1,\r
+        -1,\r
+        NULL,\r
+        STRING_TOKEN (STR_VER_OUTPUT_MAIN_UEFI),\r
+        ShellInfoObject.HiiHandle,\r
         (gST->Hdr.Revision&0xffff0000)>>16,\r
         (gST->Hdr.Revision&0x0000ffff),\r
         gST->FirmwareVendor,\r
@@ -375,7 +476,7 @@ UefiMain (
         Status = DoStartupScript(ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);\r
       }\r
 \r
-      if (!ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel) >= 3 || PcdGetBool(PcdShellForceConsole)) && !EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
+      if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit && !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel) >= 3 || PcdGetBool(PcdShellForceConsole)) && !EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
         //\r
         // begin the UI waiting loop\r
         //\r
@@ -435,7 +536,7 @@ UefiMain (
   }\r
   if (ShellInfoObject.NewEfiShellProtocol != NULL){\r
     if (ShellInfoObject.NewEfiShellProtocol->IsRootShell()){\r
-      ShellInfoObject.NewEfiShellProtocol->SetEnv(L"cwd", L"", TRUE);\r
+      InternalEfiShellSetEnv(L"cwd", NULL, TRUE);\r
     }\r
     CleanUpShellProtocol(ShellInfoObject.NewEfiShellProtocol);\r
     DEBUG_CODE(ShellInfoObject.NewEfiShellProtocol = NULL;);\r
@@ -636,6 +737,7 @@ STATIC CONST SHELL_PARAM_ITEM mShellParamList[] = {
   {L"-noversion",     TypeFlag},\r
   {L"-startup",       TypeFlag},\r
   {L"-delay",         TypeValue},\r
+  {L"-_exit",         TypeFlag},\r
   {NULL, TypeMax}\r
   };\r
 \r
@@ -743,6 +845,7 @@ ProcessCommandLine(
   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap        = ShellCommandLineGetFlag(Package, L"-nomap");\r
   ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoVersion    = ShellCommandLineGetFlag(Package, L"-noversion");\r
   ShellInfoObject.ShellInitSettings.BitUnion.Bits.Delay        = ShellCommandLineGetFlag(Package, L"-delay");\r
+  ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit         = ShellCommandLineGetFlag(Package, L"-_exit");\r
 \r
   ShellInfoObject.ShellInitSettings.Delay = 5;\r
 \r
@@ -831,12 +934,12 @@ DoStartupScript(
   //\r
   // print out our warning and see if they press a key\r
   //\r
-  for ( Status = EFI_UNSUPPORTED, Delay = ShellInfoObject.ShellInitSettings.Delay * 10\r
+  for ( Status = EFI_UNSUPPORTED, Delay = ShellInfoObject.ShellInitSettings.Delay\r
       ; Delay != 0 && EFI_ERROR(Status)\r
       ; Delay--\r
      ){\r
-    ShellPrintHiiEx(0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay/10);\r
-    gBS->Stall (100000);\r
+    ShellPrintHiiEx(0, gST->ConOut->Mode->CursorRow, NULL, STRING_TOKEN (STR_SHELL_STARTUP_QUESTION), ShellInfoObject.HiiHandle, Delay);\r
+    gBS->Stall (1000000);\r
     if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) {\r
       Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);\r
     }\r
@@ -1077,6 +1180,7 @@ ShellConvertVariables (
   CHAR16              *NewCommandLine1;\r
   CHAR16              *NewCommandLine2;\r
   CHAR16              *Temp;\r
+  CHAR16              *Temp2;\r
   UINTN               ItemSize;\r
   CHAR16              *ItemTemp;\r
   SCRIPT_FILE         *CurrentScriptFile;\r
@@ -1134,15 +1238,6 @@ ShellConvertVariables (
     }\r
   }\r
 \r
-  //\r
-  // Quick out if none were found...\r
-  //\r
-  if (NewSize == StrSize(OriginalCommandLine)) {\r
-    ASSERT(Temp == NULL);\r
-    Temp = StrnCatGrow(&Temp, NULL, OriginalCommandLine, 0);\r
-    return (Temp);\r
-  }\r
-\r
   //\r
   // now do the replacements...\r
   //\r
@@ -1174,8 +1269,51 @@ ShellConvertVariables (
     ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, AliasListNode->Alias, AliasListNode->CommandString, TRUE, FALSE);\r
     StrCpy(NewCommandLine1, NewCommandLine2);\r
     }\r
+\r
+    //\r
+    // Remove non-existant environment variables in scripts only\r
+    //\r
+    for (Temp = NewCommandLine1 ; Temp != NULL ; ) {\r
+      Temp = StrStr(Temp, L"%");\r
+      if (Temp == NULL) {\r
+        break;\r
+      }\r
+      while (*(Temp - 1) == L'^') {\r
+        Temp = StrStr(Temp + 1, L"%");\r
+        if (Temp == NULL) {\r
+          break;\r
+       }\r
+      }\r
+      if (Temp == NULL) {\r
+        break;\r
+      }\r
+      \r
+      Temp2 = StrStr(Temp + 1, L"%");\r
+      if (Temp2 == NULL) {\r
+        break;\r
+      }\r
+      while (*(Temp2 - 1) == L'^') {\r
+        Temp2 = StrStr(Temp2 + 1, L"%");\r
+        if (Temp2 == NULL) {\r
+          break;\r
+        }\r
+      }\r
+      if (Temp2 == NULL) {\r
+        break;\r
+      }\r
+      \r
+      Temp2++;\r
+      CopyMem(Temp, Temp2, StrSize(Temp2));\r
+    }\r
+\r
   }\r
 \r
+  //\r
+  // Now cleanup any straggler intentionally ignored "%" characters\r
+  //\r
+  ShellCopySearchAndReplace(NewCommandLine1, NewCommandLine2, NewSize, L"^%", L"%", TRUE, FALSE);\r
+  StrCpy(NewCommandLine1, NewCommandLine2);\r
+  \r
   FreePool(NewCommandLine2);\r
   FreePool(ItemTemp);\r
 \r
@@ -1316,7 +1454,7 @@ RunCommand(
   UINTN                     Argc;\r
   CHAR16                    **Argv;\r
   BOOLEAN                   LastError;\r
-  CHAR16                    LeString[11];\r
+  CHAR16                    LeString[19];\r
   CHAR16                    *PostAliasCmdLine;\r
   UINTN                     PostAliasSize;\r
   CHAR16                    *PostVariableCmdLine;\r
@@ -1328,7 +1466,6 @@ RunCommand(
   SHELL_FILE_HANDLE         OriginalStdOut;\r
   SHELL_FILE_HANDLE         OriginalStdErr;\r
   SYSTEM_TABLE_INFO         OriginalSystemTableInfo;\r
-  CHAR16                    *TempLocation3;\r
   UINTN                     Count;\r
   UINTN                     Count2;\r
   CHAR16                    *CleanOriginal;\r
@@ -1352,11 +1489,18 @@ RunCommand(
   if (CleanOriginal == NULL) {\r
     return (EFI_OUT_OF_RESOURCES);\r
   }\r
-  while (CleanOriginal[StrLen(CleanOriginal)-1] == L' ') {\r
-    CleanOriginal[StrLen(CleanOriginal)-1] = CHAR_NULL;\r
-  }\r
-  while (CleanOriginal[0] == L' ') {\r
-    CopyMem(CleanOriginal, CleanOriginal+1, StrSize(CleanOriginal) - sizeof(CleanOriginal[0]));\r
+\r
+  TrimSpaces(&CleanOriginal);\r
+\r
+  //\r
+  // Handle case that passed in command line is just 1 or more " " characters.\r
+  //\r
+  if (StrLen (CleanOriginal) == 0) {\r
+    if (CleanOriginal != NULL) {\r
+      FreePool(CleanOriginal);\r
+      CleanOriginal = NULL;\r
+    }\r
+    return (EFI_SUCCESS);\r
   }\r
 \r
   CommandName = NULL;\r
@@ -1404,27 +1548,12 @@ RunCommand(
     return (EFI_OUT_OF_RESOURCES);\r
   }\r
 \r
-  while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {\r
-    PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;\r
-  }\r
-  while (PostVariableCmdLine[0] == L' ') {\r
-    CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));\r
-  }\r
+  TrimSpaces(&PostVariableCmdLine);\r
 \r
   //\r
   // We dont do normal processing with a split command line (output from one command input to another)\r
   //\r
-  TempLocation3 = NULL;\r
-  if (StrStr(PostVariableCmdLine, L"|") != NULL) {\r
-    for (TempLocation3 = PostVariableCmdLine ; TempLocation3 != NULL && *TempLocation3 != CHAR_NULL ; TempLocation3++) {\r
-      if (*TempLocation3 == L'^' && *(TempLocation3+1) == L'|') {\r
-        TempLocation3++;\r
-      } else if (*TempLocation3 == L'|') {\r
-        break;\r
-      }\r
-    }\r
-  }\r
-  if (TempLocation3 != NULL && *TempLocation3 != CHAR_NULL) {\r
+  if (ContainsSplit(PostVariableCmdLine)) {\r
     //\r
     // are we in an existing split???\r
     //\r
@@ -1467,13 +1596,8 @@ RunCommand(
         ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_REDIR), ShellInfoObject.HiiHandle);\r
       }\r
     } else {\r
-      while (PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] == L' ') {\r
-        PostVariableCmdLine[StrLen(PostVariableCmdLine)-1] = CHAR_NULL;\r
-      }\r
-      while (PostVariableCmdLine[0] == L' ') {\r
-        CopyMem(PostVariableCmdLine, PostVariableCmdLine+1, StrSize(PostVariableCmdLine) - sizeof(PostVariableCmdLine[0]));\r
-      }\r
-\r
+      TrimSpaces(&PostVariableCmdLine);\r
+    \r
       //\r
       // get the argc and argv updated for internal commands\r
       //\r
@@ -1513,10 +1637,15 @@ RunCommand(
         if (!EFI_ERROR(Status))  {\r
           Status = ShellCommandRunCommandHandler(ShellInfoObject.NewShellParametersProtocol->Argv[0], &ShellStatus, &LastError);\r
           ASSERT_EFI_ERROR(Status);\r
-          UnicodeSPrint(LeString, sizeof(LeString)*sizeof(LeString[0]), L"0x%08x", ShellStatus);\r
-          DEBUG_CODE(InternalEfiShellSetEnv(L"DebugLasterror", LeString, TRUE););\r
+\r
+          if (sizeof(EFI_STATUS) == sizeof(UINT64)) {\r
+            UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ShellStatus);\r
+          } else {\r
+            UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", ShellStatus);\r
+          }\r
+          DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
           if (LastError) {\r
-            InternalEfiShellSetEnv(L"Lasterror", LeString, TRUE);\r
+            InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
           }\r
           //\r
           // Pass thru the exitcode from the app.\r
@@ -1542,6 +1671,14 @@ RunCommand(
         }\r
         if (CommandWithPath == NULL || ShellIsDirectory(CommandWithPath) == EFI_SUCCESS) {\r
           ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_NOT_FOUND), ShellInfoObject.HiiHandle, ShellInfoObject.NewShellParametersProtocol->Argv[0]);\r
+\r
+          if (sizeof(EFI_STATUS) == sizeof(UINT64)) {\r
+            UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", EFI_NOT_FOUND);\r
+          } else {\r
+            UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", EFI_NOT_FOUND);\r
+          }\r
+          DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
+          InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
         } else {\r
           //\r
           // Check if it's a NSH (script) file.\r
@@ -1562,11 +1699,15 @@ RunCommand(
              );\r
 \r
             //\r
-            // Updatet last error status.\r
+            // Update last error status.\r
             //\r
-            UnicodeSPrint(LeString, sizeof(LeString)*sizeof(LeString[0]), L"0x%08x", StatusCode);\r
-            DEBUG_CODE(InternalEfiShellSetEnv(L"DebugLasterror", LeString, TRUE););\r
-            InternalEfiShellSetEnv(L"Lasterror", LeString, TRUE);\r
+            if (sizeof(EFI_STATUS) == sizeof(UINT64)) {\r
+              UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", StatusCode);\r
+            } else {\r
+              UnicodeSPrint(LeString, sizeof(LeString), L"0x%x", StatusCode);\r
+            }\r
+            DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
+            InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
           }\r
         }\r
       }\r
@@ -1848,9 +1989,15 @@ RunScriptFileHandle (
             Status = RunCommand(CommandLine3+1);\r
 \r
             //\r
-            // Now restore the pre-'@' echo state.\r
+            // If command was "@echo -off" or "@echo -on" then don't restore echo state\r
             //\r
-            ShellCommandSetEchoState(PreCommandEchoState);\r
+            if (StrCmp (L"@echo -off", CommandLine3) != 0 &&\r
+                StrCmp (L"@echo -on", CommandLine3) != 0) {\r
+              //\r
+              // Now restore the pre-'@' echo state.\r
+              //\r
+              ShellCommandSetEchoState(PreCommandEchoState);\r
+            }\r
           } else {\r
             if (ShellCommandGetEchoState()) {\r
               CurDir = ShellInfoObject.NewEfiShellProtocol->GetEnv(L"cwd");\r
@@ -1866,9 +2013,12 @@ RunScriptFileHandle (
         }\r
 \r
         if (ShellCommandGetScriptExit()) {\r
-          UnicodeSPrint(LeString, sizeof(LeString)*sizeof(LeString[0]), L"0x%Lx", ShellCommandGetExitCode());\r
-          DEBUG_CODE(InternalEfiShellSetEnv(L"DebugLasterror", LeString, TRUE););\r
-          InternalEfiShellSetEnv(L"Lasterror", LeString, TRUE);\r
+          //\r
+          // ShellCommandGetExitCode() always returns a UINT64\r
+          //\r
+          UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ShellCommandGetExitCode());\r
+          DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE););\r
+          InternalEfiShellSetEnv(L"lasterror", LeString, TRUE);\r
 \r
           ShellCommandRegisterExit(FALSE, 0);\r
           Status = EFI_SUCCESS;\r