< prev index next >

src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CommandExecutor.java

Print this page
rev 1636 : 8151291: $EXEC yields "unknown command" on Cygwin
Reviewed-by: jlaskey, hannesw, sdama

@@ -62,10 +62,13 @@
     private static final boolean IS_WINDOWS =
         AccessController.doPrivileged((PrivilegedAction<Boolean>)() -> {
         return System.getProperty("os.name").contains("Windows");
     });
 
+    // Cygwin drive alias prefix.
+    private static final String CYGDRIVE = "/cygdrive/";
+
     // User's home directory
     private static final String HOME_DIRECTORY =
         AccessController.doPrivileged((PrivilegedAction<String>)() -> {
         return System.getProperty("user.home");
     });

@@ -386,11 +389,11 @@
      * @param cwd      current working directory
      * @param fileName name of file or directory
      * @return resolved Path to file
      */
     private static Path resolvePath(final String cwd, final String fileName) {
-        return Paths.get(cwd).resolve(fileName).normalize();
+        return Paths.get(sanitizePath(cwd)).resolve(fileName).normalize();
     }
 
     /**
      * builtIn - checks to see if the command is a builtin and performs
      * appropriate action.

@@ -400,11 +403,12 @@
      */
     private boolean builtIn(final List<String> cmd, final String cwd) {
         switch (cmd.get(0)) {
             // Set current working directory.
             case "cd":
-                // If zero args then use home dirrectory as cwd else use first arg.
+                final boolean cygpath = IS_WINDOWS && cwd.startsWith(CYGDRIVE);
+                // If zero args then use home directory as cwd else use first arg.
                 final String newCWD = cmd.size() < 2 ? HOME_DIRECTORY : cmd.get(1);
                 // Normalize the cwd
                 final Path cwdPath = resolvePath(cwd, newCWD);
 
                 // Check if is a directory.

@@ -416,11 +420,17 @@
                     reportError("not.directory", file.toString());
                     return true;
                 }
 
                 // Set PWD environment variable to be picked up as cwd.
-                environment.put("PWD", cwdPath.toString());
+                // Make sure Cygwin paths look like Unix paths.
+                String scwd = cwdPath.toString();
+                if (cygpath && scwd.length() >= 2 &&
+                        Character.isLetter(scwd.charAt(0)) && scwd.charAt(1) == ':') {
+                    scwd = CYGDRIVE + Character.toLowerCase(scwd.charAt(0)) + "/" + scwd.substring(2);
+                }
+                environment.put("PWD", scwd);
                 return true;
 
             // Set an environment variable.
             case "setenv":
                 if (3 <= cmd.size()) {

@@ -443,11 +453,12 @@
 
         return false;
     }
 
     /**
-     * preprocessCommand - scan the command for redirects
+     * preprocessCommand - scan the command for redirects, and sanitize the
+     * executable path
      * @param tokens       command tokens
      * @param cwd          current working directory
      * @param redirectInfo redirection information
      * @return tokens remaining for actual command
      */

@@ -469,25 +480,54 @@
 
             // Strip quotes and add to command.
             command.add(stripQuotes(token));
         }
 
+        if (command.size() > 0) {
+            command.set(0, sanitizePath(command.get(0)));
+        }
+
         return command;
     }
 
     /**
+     * Sanitize a path in case the underlying platform is Cygwin. In that case,
+     * convert from the {@code /cygdrive/x} drive specification to the usual
+     * Windows {@code X:} format.
+     *
+     * @param d a String representing a path
+     * @return a String representing the same path in a form that can be
+     *         processed by the underlying platform
+     */
+    private static String sanitizePath(final String d) {
+        if (!IS_WINDOWS || (IS_WINDOWS && !d.startsWith(CYGDRIVE))) {
+            return d;
+        }
+        final String pd = d.substring(CYGDRIVE.length());
+        if (pd.length() >= 2 && pd.charAt(1) == '/') {
+            // drive letter plus / -> convert /cygdrive/x/... to X:/...
+            return pd.charAt(0) + ":" + pd.substring(1);
+        } else if (pd.length() == 1) {
+            // just drive letter -> convert /cygdrive/x to X:
+            return pd.charAt(0) + ":";
+        }
+        // remaining case: /cygdrive/ -> can't convert
+        return d;
+    }
+
+    /**
      * createProcessBuilder - create a ProcessBuilder for the command.
      * @param command      command tokens
      * @param cwd          current working directory
      * @param redirectInfo redirect information
      */
     private void createProcessBuilder(final List<String> command,
             final String cwd, final RedirectInfo redirectInfo) {
         // Create new ProcessBuilder.
         final ProcessBuilder pb = new ProcessBuilder(command);
         // Set current working directory.
-        pb.directory(new File(cwd));
+        pb.directory(new File(sanitizePath(cwd)));
 
         // Map environment variables.
         final Map<String, String> processEnvironment = pb.environment();
         processEnvironment.clear();
         processEnvironment.putAll(environment);

@@ -521,11 +561,11 @@
         }
 
         // Create ProcessBuilder with cwd and redirects set.
         createProcessBuilder(command, cwd, redirectInfo);
 
-        // If piped the wait for the next command.
+        // If piped, wait for the next command.
         if (isPiped) {
             return;
         }
 
         // Fetch first and last ProcessBuilder.

@@ -763,11 +803,11 @@
         command(command, false);
     }
 
     /**
      * process - process a command array of strings
-     * @param script command script to be processed
+     * @param tokens command script to be processed
      */
     void process(final List<String> tokens) {
         // Prepare to accumulate command tokens.
         final List<String> command = new ArrayList<>();
 
< prev index next >