8151291: $EXEC yields "unknown command" on Cygwin
Reviewed-by: jlaskey, hannesw, sdama
--- a/nashorn/.hgignore Wed Jul 05 21:25:35 2017 +0200
+++ b/nashorn/.hgignore Wed Mar 09 13:24:01 2016 +0100
@@ -28,3 +28,4 @@
.project
.externalToolBuilders/*
.settings/*
+NashornProfile.txt
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java Wed Jul 05 21:25:35 2017 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java Wed Mar 09 13:24:01 2016 +0100
@@ -2724,12 +2724,9 @@
// Retrieve current state of ENV variables.
env.putAll(System.getenv(), scriptEnv._strict);
- // Some platforms, e.g., Windows, do not define the PWD environment
- // variable, so that the $ENV.PWD property needs to be explicitly
- // set.
- if (!env.containsKey(ScriptingFunctions.PWD_NAME)) {
- env.put(ScriptingFunctions.PWD_NAME, System.getProperty("user.dir"), scriptEnv._strict);
- }
+ // Set the PWD variable to a value that is guaranteed to be understood
+ // by the underlying platform.
+ env.put(ScriptingFunctions.PWD_NAME, System.getProperty("user.dir"), scriptEnv._strict);
}
addOwnProperty(ScriptingFunctions.ENV_NAME, Attribute.NOT_ENUMERABLE, env);
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CommandExecutor.java Wed Jul 05 21:25:35 2017 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CommandExecutor.java Wed Mar 09 13:24:01 2016 +0100
@@ -64,6 +64,9 @@
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>)() -> {
@@ -388,7 +391,7 @@
* @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();
}
/**
@@ -402,7 +405,8 @@
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);
@@ -418,7 +422,13 @@
}
// 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.
@@ -445,7 +455,8 @@
}
/**
- * 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
@@ -471,10 +482,39 @@
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
@@ -485,7 +525,7 @@
// 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();
@@ -523,7 +563,7 @@
// 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;
}
@@ -765,7 +805,7 @@
/**
* 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.
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptingFunctions.java Wed Jul 05 21:25:35 2017 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptingFunctions.java Wed Mar 09 13:24:01 2016 +0100
@@ -72,7 +72,7 @@
public static final String EXIT_NAME = "$EXIT";
/** Names of special properties used by $ENV API. */
- public static final String ENV_NAME = "$ENV";
+ public static final String ENV_NAME = "$ENV";
/** Name of the environment variable for the current working directory. */
public static final String PWD_NAME = "PWD";
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/nosecurity/JDK-8151291.js Wed Mar 09 13:24:01 2016 +0100
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Test $EXEC and $ENV.PWD handling across platforms.
+ * There must be a java executable in the PATH.
+ *
+ * @test
+ * @option -scripting
+ * @run
+ */
+
+$EXEC(["java", "-version"])
+if ($EXIT != 0) {
+ throw 'java executable problem: ' + $ERR
+}
+
+function eq(p, q, e) {
+ if (p != q) {
+ throw e
+ }
+}
+
+function neq(p, q, e) {
+ if (p == q) {
+ throw e
+ }
+}
+
+var File = Java.type("java.io.File"),
+ System = Java.type("java.lang.System"),
+ win = System.getProperty("os.name").startsWith("Windows"),
+ sep = File.separator,
+ startwd = $ENV.PWD,
+ upwd = startwd.substring(0, startwd.lastIndexOf(sep))
+
+$EXEC("ls")
+var ls_startwd = $OUT
+$EXEC("cd ..; ls")
+var ls_cdupwd = $OUT
+eq($ENV.PWD, startwd, 'PWD changed during $EXEC cd')
+neq(ls_startwd, ls_cdupwd, 'same ls result for startwd and upwd')
+
+$ENV.PWD = upwd
+eq($ENV.PWD, upwd, '$ENV.PWD change had no effect')
+$EXEC("ls")
+var ls_epupwd = $OUT
+eq(ls_cdupwd, ls_epupwd, 'different results for upwd')