]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ShellPkg/Application/Shell/Shell.c
Refine code to make it more safely.
[mirror_edk2.git] / ShellPkg / Application / Shell / Shell.c
index df101a32af0157e16eeac13a085e67e62397796b..2f1e3eb65c89ecdce0a16f4cddd9f17109f8ea78 100644 (file)
@@ -2,7 +2,7 @@
   This is THE shell (application)\r
 \r
   Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>\r
-  Copyright (c) 2013, Hewlett-Packard Development Company, L.P.\r
+  (C) Copyright 2013-2014, 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
@@ -359,7 +359,10 @@ UefiMain (
     //\r
     // Check the command line\r
     //\r
-    Status = ProcessCommandLine();\r
+    Status = ProcessCommandLine ();\r
+    if (EFI_ERROR (Status)) {\r
+      goto FreeResources;\r
+    }\r
 \r
     //\r
     // If shell support level is >= 1 create the mappings and paths\r
@@ -506,6 +509,7 @@ UefiMain (
           // Reset page break back to default.\r
           //\r
           ShellInfoObject.PageBreakEnabled        = PcdGetBool(PcdShellPageBreakDefault);\r
+          ASSERT (ShellInfoObject.ConsoleInfo != NULL);\r
           ShellInfoObject.ConsoleInfo->Enabled    = TRUE;\r
           ShellInfoObject.ConsoleInfo->RowCounter = 0;\r
 \r
@@ -832,9 +836,13 @@ ProcessCommandLine(
   ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit         = FALSE;\r
   ShellInfoObject.ShellInitSettings.Delay = 5;\r
 \r
-  // Start LoopVar at 1 to ignore Argv[0] which is the name of this binary\r
-  // (probably "Shell.efi")\r
-  for (LoopVar = 1 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
+  //\r
+  // Start LoopVar at 0 to parse only optional arguments at Argv[0]\r
+  // and parse other parameters from Argv[1].  This is for use case that\r
+  // UEFI Shell boot option is created, and OptionalData is provided\r
+  // that starts with shell command-line options.\r
+  //\r
+  for (LoopVar = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
     CurrentArg = gEfiShellParametersProtocol->Argv[LoopVar];\r
     if (UnicodeCollation->StriColl (\r
                             UnicodeCollation,\r
@@ -922,6 +930,13 @@ ProcessCommandLine(
         );\r
       return EFI_INVALID_PARAMETER;\r
     } else {\r
+      //\r
+      // First argument should be Shell.efi image name\r
+      //\r
+      if (LoopVar == 0) {\r
+        continue;\r
+      }\r
+\r
       ShellInfoObject.ShellInitSettings.FileName = AllocateZeroPool(StrSize(CurrentArg));\r
       if (ShellInfoObject.ShellInitSettings.FileName == NULL) {\r
         return (EFI_OUT_OF_RESOURCES);\r
@@ -935,7 +950,7 @@ ProcessCommandLine(
       LoopVar++;\r
 \r
       // Add `file-name-options`\r
-      for ( ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
+      for (Size = 0 ; LoopVar < gEfiShellParametersProtocol->Argc ; LoopVar++) {\r
         ASSERT((ShellInfoObject.ShellInitSettings.FileOptions == NULL && Size == 0) || (ShellInfoObject.ShellInitSettings.FileOptions != NULL));\r
         StrnCatGrow(&ShellInfoObject.ShellInitSettings.FileOptions,\r
                     &Size,\r
@@ -1273,6 +1288,113 @@ ShellConvertAlias(
   return (EFI_SUCCESS);\r
 }\r
 \r
+/**\r
+  Parse for the next instance of one string within another string. Can optionally make sure that \r
+  the string was not escaped (^ character) per the shell specification.\r
+\r
+  @param[in] SourceString             The string to search within\r
+  @param[in] FindString               The string to look for\r
+  @param[in] CheckForEscapeCharacter  TRUE to skip escaped instances of FinfString, otherwise will return even escaped instances\r
+**/\r
+CHAR16*\r
+EFIAPI\r
+FindNextInstance(\r
+  IN CONST CHAR16   *SourceString,\r
+  IN CONST CHAR16   *FindString,\r
+  IN CONST BOOLEAN  CheckForEscapeCharacter\r
+  )\r
+{\r
+  CHAR16 *Temp;\r
+  if (SourceString == NULL) {\r
+    return (NULL);\r
+  }\r
+  Temp = StrStr(SourceString, FindString);\r
+\r
+  //\r
+  // If nothing found, or we dont care about escape characters\r
+  //\r
+  if (Temp == NULL || !CheckForEscapeCharacter) {\r
+    return (Temp);\r
+  }\r
+\r
+  //\r
+  // If we found an escaped character, try again on the remainder of the string\r
+  //\r
+  if ((Temp > (SourceString)) && *(Temp-1) == L'^') {\r
+    return FindNextInstance(Temp+1, FindString, CheckForEscapeCharacter);\r
+  }\r
+\r
+  //\r
+  // we found the right character\r
+  //\r
+  return (Temp);\r
+}\r
+\r
+/**\r
+  This function will eliminate unreplaced (and therefore non-found) environment variables.\r
+\r
+  @param[in,out] CmdLine   The command line to update.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+StripUnreplacedEnvironmentVariables(\r
+  IN OUT CHAR16 *CmdLine\r
+  )\r
+{\r
+  CHAR16 *FirstPercent;\r
+  CHAR16 *FirstQuote;\r
+  CHAR16 *SecondPercent;\r
+  CHAR16 *SecondQuote;\r
+  CHAR16 *CurrentLocator;\r
+\r
+  for (CurrentLocator = CmdLine ; CurrentLocator != NULL ; ) {\r
+    FirstQuote = FindNextInstance(CurrentLocator, L"\"", TRUE);\r
+    FirstPercent = FindNextInstance(CurrentLocator, L"%", TRUE);\r
+    SecondPercent = FirstPercent!=NULL?FindNextInstance(FirstPercent+1, L"%", TRUE):NULL;\r
+    if (FirstPercent == NULL || SecondPercent == NULL) {\r
+      //\r
+      // If we ever dont have 2 % we are done.\r
+      //\r
+      break;\r
+    }\r
+\r
+    if (FirstQuote < FirstPercent) {\r
+      SecondQuote = FirstQuote!= NULL?FindNextInstance(FirstQuote+1, L"\"", TRUE):NULL;\r
+      //\r
+      // Quote is first found\r
+      //\r
+\r
+      if (SecondQuote < FirstPercent) {\r
+        //\r
+        // restart after the pair of "\r
+        //\r
+        CurrentLocator = SecondQuote + 1;\r
+      } else /* FirstPercent < SecondQuote */{\r
+        //\r
+        // Restart on the first percent\r
+        //\r
+        CurrentLocator = FirstPercent;\r
+      }\r
+      continue;\r
+    }\r
+    ASSERT(FirstPercent < FirstQuote);\r
+    if (SecondPercent < FirstQuote) {\r
+      FirstPercent[0] = L'\"';\r
+      SecondPercent[0] = L'\"';\r
+\r
+      //\r
+      // We need to remove from FirstPercent to SecondPercent\r
+      //\r
+      CopyMem(FirstPercent + 1, SecondPercent, StrSize(SecondPercent));\r
+      CurrentLocator = FirstPercent + 2;\r
+      continue;\r
+    }\r
+    ASSERT(FirstQuote < SecondPercent);\r
+    CurrentLocator = FirstQuote;\r
+  }\r
+  return (EFI_SUCCESS);\r
+}\r
+\r
 /**\r
   Function allocates a new command line and replaces all instances of environment\r
   variable names that are correctly preset to their values.\r
@@ -1295,7 +1417,6 @@ ShellConvertVariables (
   CHAR16              *NewCommandLine1;\r
   CHAR16              *NewCommandLine2;\r
   CHAR16              *Temp;\r
-  CHAR16              *Temp2;\r
   UINTN               ItemSize;\r
   CHAR16              *ItemTemp;\r
   SCRIPT_FILE         *CurrentScriptFile;\r
@@ -1388,39 +1509,7 @@ ShellConvertVariables (
     //\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
+    StripUnreplacedEnvironmentVariables(NewCommandLine1);\r
   }\r
 \r
   //\r
@@ -1674,10 +1763,12 @@ GetOperationType(
   }\r
 \r
   //\r
-  // Test for file system change request.  anything ending with : and cant have spaces.\r
+  // Test for file system change request.  anything ending with first : and cant have spaces.\r
   //\r
   if (CmdName[(StrLen(CmdName)-1)] == L':') {\r
-    if (StrStr(CmdName, L" ") != NULL) {\r
+    if ( StrStr(CmdName, L" ") != NULL \r
+      || StrLen(StrStr(CmdName, L":")) > 1\r
+      ) {\r
       return (Unknown_Invalid);\r
     }\r
     return (File_Sys_Change);\r
@@ -2001,6 +2092,7 @@ ProcessCommandLineToFinal(
   if (EFI_ERROR(Status)) {\r
     return (Status);\r
   }\r
+  ASSERT (*CmdLine != NULL);\r
 \r
   TrimSpaces(CmdLine);\r
 \r
@@ -2141,6 +2233,7 @@ RunCommandOrFile(
 )\r
 {\r
   EFI_STATUS                Status;\r
+  EFI_STATUS                StartStatus;\r
   CHAR16                    *CommandWithPath;\r
   EFI_DEVICE_PATH_PROTOCOL  *DevPath;\r
   SHELL_STATUS              CalleeExitStatus;\r
@@ -2218,6 +2311,7 @@ RunCommandOrFile(
             DevPath,\r
             CmdLine,\r
             NULL,\r
+            &StartStatus,\r
             NULL,\r
             NULL\r
            );\r
@@ -2227,7 +2321,7 @@ RunCommandOrFile(
           if(EFI_ERROR (Status)) {\r
             CalleeExitStatus = (SHELL_STATUS) (Status & (~MAX_BIT));\r
           } else {\r
-            CalleeExitStatus = SHELL_SUCCESS;\r
+            CalleeExitStatus = (SHELL_STATUS) StartStatus;\r
           }\r
 \r
           //\r
@@ -2402,7 +2496,7 @@ RunCommand(
   //\r
   switch (Type = GetOperationType(FirstParameter)) {\r
     case   File_Sys_Change:\r
-      Status = ChangeMappedDrive(CleanOriginal);\r
+      Status = ChangeMappedDrive (FirstParameter);\r
       break;\r
     case   Internal_Command:\r
     case   Script_File_Name:\r