From 8324b37978334654e196b9bc8d18ce6153c08080 Mon Sep 17 00:00:00 2001 From: Siddharth Srinivasan Date: Wed, 30 Oct 2024 21:41:33 +0530 Subject: [PATCH] Fix NetBeans launch failure on unix shell for arguments with quotes et al Backporting Netbeans PR #7908 (still under review) in order for the LSP server launch to work for launch arguments including the project path containing special characters like quotes, spaces, backticks and $. This is an interim patch to mitigate the effect on user experience, while the upstream PR is under review. It will be updated to the approved patch, as and when it is made available. This is partially related to #301, which only fixes for spaces in arguments. Additionally, the same diff is applied to script/bin/nbcode which is a copy of the netbeans java.lsp.server module's copy. Signed-off-by: Siddharth Srinivasan --- build.xml | 1 + patches/7908-draft.diff | 233 ++++++++++++++++++++++++++++++++++++++++ script/bin/nbcode | 23 +++- 3 files changed, 253 insertions(+), 4 deletions(-) create mode 100644 patches/7908-draft.diff diff --git a/build.xml b/build.xml index faf2fa3..ec15014 100644 --- a/build.xml +++ b/build.xml @@ -56,6 +56,7 @@ patches/7724.diff patches/7733.diff patches/7750.diff + patches/7908-draft.diff patches/7910.diff patches/7921.diff patches/7923_draft.diff diff --git a/patches/7908-draft.diff b/patches/7908-draft.diff new file mode 100644 index 0000000..1c0af8e --- /dev/null +++ b/patches/7908-draft.diff @@ -0,0 +1,233 @@ +diff --git a/java/java.lsp.server/script/bin/nbcode b/java/java.lsp.server/script/bin/nbcode +index 326fe2d08a89..cea526130b6f 100755 +--- a/java/java.lsp.server/script/bin/nbcode ++++ b/java/java.lsp.server/script/bin/nbcode +@@ -52,7 +52,20 @@ if [ -f "$progdir/../etc/$APPNAME".conf ] ; then + . "$progdir/../etc/$APPNAME".conf + fi + +-# XXX does not correctly deal with spaces in non-userdir params ++doubleQuoteArg() { ++ # wrap all arguments as "" strings, escape any internal back-slash, double-quote, $, or back-tick characters ++ # use printf to avoid echo interpretation behaviors such as escapes and line continuation ++ # Mac bsd_sed does not support group-0, so pattern uses group-1 ++ printf '"%s"' "`printf '%s' "$@" | sed -e 's@\([$\"\`\\]\)@\\\\\\1@g' `" ++} ++ ++singleQuoteArg() { ++ # wrap all arguments as '' strings, escaping any internal single-quote characters ++ # use printf to avoid echo interpretation behaviors such as escapes and line continuation ++ # Mac bsd_sed does not support group-0, so pattern uses group-1 ++ printf "'%s'" "`printf '%s' "$@" | sed -e 's@'\''@'\''\\\\'\'''\''@g' `" ++} ++ + args="" + + case "`uname`" in +@@ -69,9 +82,11 @@ case "`uname`" in + esac + while [ $# -gt 0 ] ; do + case "$1" in ++ --jdkhome) shift; if [ $# -gt 0 ] ; then jdkhome="$1"; fi ++ ;; + --userdir) shift; if [ $# -gt 0 ] ; then userdir="$1"; fi + ;; +- *) args="$args \"$1\"" ++ *) args="$args `doubleQuoteArg \"$1\" `" + ;; + esac + shift +@@ -121,8 +136,8 @@ case "`uname`" in + Darwin*) + eval exec sh '"$nbexec"' \ + --jdkhome '"$jdkhome"' \ +- -J-Xdock:name='"$APPNAME"' \ +- '"-J-Xdock:icon=$progdir/../../$APPNAME.icns"' \ ++ '-J-Xdock:name="$APPNAME"' \ ++ '-J-Xdock:icon="$progdir/../../$APPNAME.icns"' \ + --clusters '"$clusters"' \ + --userdir '"${userdir}"' \ + ${default_options} \ +diff --git a/platform/o.n.bootstrap/launcher/unix/nbexec b/platform/o.n.bootstrap/launcher/unix/nbexec +index 1d6ad6e53019..abac07411d43 100755 +--- a/platform/o.n.bootstrap/launcher/unix/nbexec ++++ b/platform/o.n.bootstrap/launcher/unix/nbexec +@@ -56,6 +56,20 @@ postfixcp="" + + updater_class=org.netbeans.updater.UpdaterFrame + ++doubleQuoteArg() { ++ # wrap all arguments as "" strings, escape any internal back-slash, double-quote, $, or back-tick characters ++ # use printf to avoid echo interpretation behaviors such as escapes and line continuation ++ # Mac bsd_sed does not support group-0, so pattern uses group-1 ++ printf '"%s"' "`printf '%s' "$@" | sed -e 's@\([$\"\`\\]\)@\\\\\\1@g' `" ++} ++ ++singleQuoteArg() { ++ # wrap all arguments as '' strings, escaping any internal single-quote characters ++ # use printf to avoid echo interpretation behaviors such as escapes and line continuation ++ # Mac bsd_sed does not support group-0, so pattern uses group-1 ++ printf "'%s'" "`printf '%s' "$@" | sed -e 's@'\''@'\''\\\\'\'''\''@g' `" ++} ++ + # + # parse arguments + # +@@ -85,7 +99,7 @@ EOF + nogui="nogui"; + args="$args --nogui" + ;; +- --jdkhome) shift; if [ $# -gt 0 ] ; then jdkhome=$1; fi ++ --jdkhome) shift; if [ $# -gt 0 ] ; then jdkhome="$1"; fi + ;; + # this has to be here for purposes of updater.jar, but it should be + # better to handle this argument inside the java launcher part +@@ -117,8 +131,8 @@ EOF + -psn*) + shift; + ;; +- -J*) jopt=`expr "X-$1" : 'X--J\(.*\)'`; jargs="$jargs '$jopt'";; +- *) args="$args \"$1\"" ;; ++ -J*) jopt=`expr "X-$1" : 'X--J\(.*\)'`; jargs="$jargs `singleQuoteArg \"$jopt\" `";; ++ *) args="$args `doubleQuoteArg \"$1\" `";; + esac + shift + done +@@ -187,27 +201,27 @@ fi + + jargs="$jargs -XX:+HeapDumpOnOutOfMemoryError" + if [ -z "`echo $jargs | grep -- "-XX:HeapDumpPath="`" ] ; then +- jargs="$jargs -XX:HeapDumpPath=\"${userdir}/var/log/heapdump.hprof\"" ++ jargs="$jargs -XX:HeapDumpPath=`doubleQuoteArg \"${userdir}/var/log/heapdump.hprof\" `" + fi + # rename old heap dump to .old + mv "${userdir}/var/log/heapdump.hprof" "${userdir}/var/log/heapdump.hprof.old" > /dev/null 2>&1 + + jargs_without_clusters="$jargs -Djava.security.manager=allow" +-jargs="-Dnetbeans.dirs=\"${clusters}\" $jargs_without_clusters" ++jargs="-Dnetbeans.dirs=`doubleQuoteArg \"${clusters}\" ` $jargs_without_clusters" + + if [ -z "$cachedirspecified" ]; then + cachedir="${userdir}/var/cache" + fi + + if [ `uname` != Darwin -a -z "$nosplash" -a -f "${cachedir}/splash.png" -a ! -f "${userdir}/lock" ]; then +- jargs="$jargs -splash:\"${cachedir}/splash.png\"" ++ jargs="$jargs -splash:`doubleQuoteArg \"${cachedir}/splash.png\" `" + fi + + jdkhome=`absolutize_path "$jdkhome"` + +-args="--userdir \"${userdir}\" $args" ++args="--userdir `doubleQuoteArg \"${userdir}\"` $args" + +-args="--cachedir \"${cachedir}\" $args" ++args="--cachedir `doubleQuoteArg \"${cachedir}\"` $args" + + append_jars_to_cp() { + dir="$1" +@@ -279,7 +293,7 @@ build_cp() { + } + + do_run_updater() { +- eval "\"$jdkhome/bin/java\"" -classpath "\"${updatercp}\"" "$jargs" "-Dnetbeans.user=\"$userdir\"" $updater_class "$args" ++ eval '"$jdkhome/bin/java"' -classpath '"${updatercp}"' "$jargs" '-Dnetbeans.user="$userdir"' $updater_class "$args" + construct_cp + } + +@@ -360,7 +374,7 @@ else + fi + + if [ ! -z "${DEFAULT_USERDIR_ROOT}" ] ; then +- jargs="-Dnetbeans.default_userdir_root=\"${DEFAULT_USERDIR_ROOT}\" $jargs" ++ jargs="-Dnetbeans.default_userdir_root=`doubleQuoteArg \"${DEFAULT_USERDIR_ROOT}\"` $jargs" + unset DEFAULT_USERDIR_ROOT + fi + +@@ -427,7 +441,7 @@ while [ "$restart" ] ; do + # + delete_new_clusters_file + rm -f "${restart_file}" +- eval ${_NB_PROFILE_CMD} "\"${jdkhome}/bin/java\"" -Djdk.home="\"${jdkhome}\"" -classpath "\"$cp\"" \ ++ eval ${_NB_PROFILE_CMD} '"${jdkhome}/bin/java"' '-Djdk.home="${jdkhome}"' -classpath '"$cp"' \ + "$jargs" org.netbeans.Main "$args" '<&0' '&' + PID=$! + trap "kill $PID" EXIT +diff --git a/platform/o.n.bootstrap/test/unit/src/org/netbeans/nbexec/NbExecPassesCorrectlyQuotedArgsTest.java b/platform/o.n.bootstrap/test/unit/src/org/netbeans/nbexec/NbExecPassesCorrectlyQuotedArgsTest.java +index 3ccd38505fed..91923d6e4118 100644 +--- a/platform/o.n.bootstrap/test/unit/src/org/netbeans/nbexec/NbExecPassesCorrectlyQuotedArgsTest.java ++++ b/platform/o.n.bootstrap/test/unit/src/org/netbeans/nbexec/NbExecPassesCorrectlyQuotedArgsTest.java +@@ -22,9 +22,14 @@ + import java.io.IOException; + import java.io.InputStream; + import java.net.URL; ++import java.nio.file.Files; ++import java.nio.file.Path; + import java.util.Arrays; + import java.util.LinkedList; + import java.util.List; ++import java.util.Objects; ++import java.util.stream.Collectors; ++import java.util.stream.Stream; + import org.netbeans.junit.NbTestCase; + import org.openide.util.Lookup; + import org.openide.util.Utilities; +@@ -77,7 +82,57 @@ public void testStartsArePassedInUnparsed() throws Exception { + fail(str + " should be there: " + a); + } + } +- ++ ++ public void testJdkHomePassed() throws Exception { ++ File wd = new File(getWorkDir(), "jdk dir"); ++ wd.mkdirs(); ++ String origJdkHome = System.getProperty("java.home"); ++ Path origJdk = new File(origJdkHome).toPath(); ++ String[] linkNames = { ++ "openjdk's jdkhome", ++ "jdk \"latest\"", ++ "current$JAVA_HOME", ++ "link\"'d jdk" ++ }; ++ Path[] links = new Path[linkNames.length]; ++ for (int i = 0; i < linkNames.length; i++) { ++ links[i] = new File(wd, linkNames[i]).toPath(); ++ } ++ ++ String[] args = { ++ "1 * * * *", ++ "a b", ++ "c d", ++ "$1", ++ "$2", ++ "$3\"`$2`'$1" ++ }; ++ ++ for (Path link : links) { ++ try { ++ Path l = Files.createSymbolicLink(link, origJdk); ++ if (link.compareTo(l) != 0) { ++ fail("link creation mismatch: expected<" + link + "> != actual<" + l + ">"); ++ } ++ System.setProperty("java.home", link.toString()); ++ String[] nbArgs = { ++ "--jdkhome", ++ link.toString() ++ }; ++ run(wd, Stream.concat(Arrays.stream(nbArgs), Arrays.stream(args)).collect(Collectors.toList()).toArray(new String[]{})); ++ ++ String[] gotArgs = MainCallback.getArgs(getWorkDir()); ++ assertNotNull("args passed in", gotArgs); ++ for (int in = args.length, jn = gotArgs.length, i = 0, j = Math.max(0, jn - in); i < in ; i++, j++) { ++ if (j >= jn || !Objects.equals(args[i], gotArgs[j])) ++ fail("args do not match: expected<" + Arrays.toString(args) + "> != actual<" + Arrays.toString(gotArgs) + ">"); ++ } ++ } finally { ++ System.setProperty("java.home", origJdkHome); ++ Files.delete(link); ++ } ++ } ++ } + + private void run(File workDir, String... args) throws Exception { + URL u = Lookup.class.getProtectionDomain().getCodeSource().getLocation(); diff --git a/script/bin/nbcode b/script/bin/nbcode index 326fe2d..cea5261 100755 --- a/script/bin/nbcode +++ b/script/bin/nbcode @@ -52,7 +52,20 @@ if [ -f "$progdir/../etc/$APPNAME".conf ] ; then . "$progdir/../etc/$APPNAME".conf fi -# XXX does not correctly deal with spaces in non-userdir params +doubleQuoteArg() { + # wrap all arguments as "" strings, escape any internal back-slash, double-quote, $, or back-tick characters + # use printf to avoid echo interpretation behaviors such as escapes and line continuation + # Mac bsd_sed does not support group-0, so pattern uses group-1 + printf '"%s"' "`printf '%s' "$@" | sed -e 's@\([$\"\`\\]\)@\\\\\\1@g' `" +} + +singleQuoteArg() { + # wrap all arguments as '' strings, escaping any internal single-quote characters + # use printf to avoid echo interpretation behaviors such as escapes and line continuation + # Mac bsd_sed does not support group-0, so pattern uses group-1 + printf "'%s'" "`printf '%s' "$@" | sed -e 's@'\''@'\''\\\\'\'''\''@g' `" +} + args="" case "`uname`" in @@ -69,9 +82,11 @@ case "`uname`" in esac while [ $# -gt 0 ] ; do case "$1" in + --jdkhome) shift; if [ $# -gt 0 ] ; then jdkhome="$1"; fi + ;; --userdir) shift; if [ $# -gt 0 ] ; then userdir="$1"; fi ;; - *) args="$args \"$1\"" + *) args="$args `doubleQuoteArg \"$1\" `" ;; esac shift @@ -121,8 +136,8 @@ case "`uname`" in Darwin*) eval exec sh '"$nbexec"' \ --jdkhome '"$jdkhome"' \ - -J-Xdock:name='"$APPNAME"' \ - '"-J-Xdock:icon=$progdir/../../$APPNAME.icns"' \ + '-J-Xdock:name="$APPNAME"' \ + '-J-Xdock:icon="$progdir/../../$APPNAME.icns"' \ --clusters '"$clusters"' \ --userdir '"${userdir}"' \ ${default_options} \