1
0
Эх сурвалжийг харах

Merge topic 'get_filename_component-fix-program-split'

31f73eb1 get_filename_component: Revise PROGRAM/PROGRAM_ARGS split semantics

Acked-by: Kitware Robot <[email protected]>
Merge-request: !1251
Brad King 8 жил өмнө
parent
commit
01b9d039e7

+ 9 - 0
Help/release/dev/get_filename_component-fix-program-split.rst

@@ -0,0 +1,9 @@
+get_filename_component-fix-program-split
+----------------------------------------
+
+* The :command:`get_filename_component` ``PROGRAM`` mode semantics
+  have been revised to not tolerate unquoted spaces in the path
+  to the program while also accepting arguments.  While technically
+  incompatible with the old behavior, it is expected that behavior
+  under typical use cases with properly-quoted command-lines has
+  not changed.

+ 24 - 1
Source/cmGetFilenameComponentCommand.cxx

@@ -60,7 +60,30 @@ bool cmGetFilenameComponentCommand::InitialPass(
         }
       }
     }
-    cmSystemTools::SplitProgramFromArgs(filename, result, programArgs);
+
+    // First assume the path to the program was specified with no
+    // arguments and with no quoting or escaping for spaces.
+    // Only bother doing this if there is non-whitespace.
+    if (!cmSystemTools::TrimWhitespace(filename).empty()) {
+      result = cmSystemTools::FindProgram(filename);
+    }
+
+    // If that failed then assume a command-line string was given
+    // and split the program part from the rest of the arguments.
+    if (result.empty()) {
+      std::string program;
+      if (cmSystemTools::SplitProgramFromArgs(filename, program,
+                                              programArgs)) {
+        if (cmSystemTools::FileExists(program)) {
+          result = program;
+        } else {
+          result = cmSystemTools::FindProgram(program);
+        }
+      }
+      if (result.empty()) {
+        programArgs.clear();
+      }
+    }
   } else if (args[2] == "EXT") {
     result = cmSystemTools::GetFilenameExtension(filename);
   } else if (args[2] == "NAME_WE") {

+ 50 - 0
Source/cmSystemTools.cxx

@@ -603,6 +603,56 @@ std::vector<std::string> cmSystemTools::ParseArguments(const char* command)
   return args;
 }
 
+bool cmSystemTools::SplitProgramFromArgs(std::string const& command,
+                                         std::string& program,
+                                         std::string& args)
+{
+  const char* c = command.c_str();
+
+  // Skip leading whitespace.
+  while (isspace(static_cast<unsigned char>(*c))) {
+    ++c;
+  }
+
+  // Parse one command-line element up to an unquoted space.
+  bool in_escape = false;
+  bool in_double = false;
+  bool in_single = false;
+  for (; *c; ++c) {
+    if (in_single) {
+      if (*c == '\'') {
+        in_single = false;
+      } else {
+        program += *c;
+      }
+    } else if (in_escape) {
+      in_escape = false;
+      program += *c;
+    } else if (*c == '\\') {
+      in_escape = true;
+    } else if (in_double) {
+      if (*c == '"') {
+        in_double = false;
+      } else {
+        program += *c;
+      }
+    } else if (*c == '"') {
+      in_double = true;
+    } else if (*c == '\'') {
+      in_single = true;
+    } else if (isspace(static_cast<unsigned char>(*c))) {
+      break;
+    } else {
+      program += *c;
+    }
+  }
+
+  // The remainder of the command line holds unparsed arguments.
+  args = c;
+
+  return !in_single && !in_escape && !in_double;
+}
+
 size_t cmSystemTools::CalculateCommandLineLengthLimit()
 {
   size_t sz =

+ 5 - 0
Source/cmSystemTools.h

@@ -255,6 +255,11 @@ public:
   static void ParseUnixCommandLine(const char* command,
                                    std::vector<std::string>& args);
 
+  /** Split a command-line string into the parsed command and the unparsed
+      arguments.  Returns false on unfinished quoting or escaping.  */
+  static bool SplitProgramFromArgs(std::string const& command,
+                                   std::string& program, std::string& args);
+
   /**
    * Handle response file in an argument list and return a new argument list
    * **/

+ 11 - 0
Tests/RunCMake/get_filename_component/KnownComponents.cmake

@@ -80,6 +80,17 @@ get_filename_component(test_program_name "/ arg1 arg2" PROGRAM
 check("PROGRAM with args output: name" "${test_program_name}" "/")
 check("PROGRAM with args output: args" "${test_program_args}" " arg1 arg2")
 
+get_filename_component(test_program_name " " PROGRAM)
+check("PROGRAM with just a space" "${test_program_name}" "")
+
+get_filename_component(test_program_name "${CMAKE_CURRENT_LIST_FILE}" PROGRAM)
+check("PROGRAM specified explicitly without quoting" "${test_program_name}" "${CMAKE_CURRENT_LIST_FILE}")
+
+get_filename_component(test_program_name "\"${CMAKE_CURRENT_LIST_FILE}\" arg1 arg2" PROGRAM
+  PROGRAM_ARGS test_program_args)
+check("PROGRAM specified explicitly with arguments: name" "${test_program_name}" "${CMAKE_CURRENT_LIST_FILE}")
+check("PROGRAM specified explicitly with arguments: args" "${test_program_args}" " arg1 arg2")
+
 list(APPEND non_cache_vars test_program_name)
 list(APPEND non_cache_vars test_program_args)