From ae2b7289cc8c2bafb5da2b04daa3ac998020b1ee Mon Sep 17 00:00:00 2001 From: crschnick Date: Fri, 17 Mar 2023 04:52:57 +0000 Subject: [PATCH] Shell handling improvements --- .../app/comp/about/BrowseDirectoryComp.java | 14 ++- .../xpipe/app/prefs/ExternalEditorType.java | 4 +- .../io/xpipe/app/util/ApplicationHelper.java | 2 - .../io/xpipe/app/util/DesktopShortcuts.java | 6 +- .../java/io/xpipe/app/util/ScriptHelper.java | 106 ++---------------- .../resources/lang/translations_en.properties | 1 + .../io/xpipe/core/process/ShellDialect.java | 22 ++-- .../io/xpipe/core/process/ShellDialects.java | 2 + .../core/store/ConnectionFileSystem.java | 3 +- .../xpipe/core/util/XPipeTempDirectory.java | 3 +- 10 files changed, 40 insertions(+), 123 deletions(-) diff --git a/app/src/main/java/io/xpipe/app/comp/about/BrowseDirectoryComp.java b/app/src/main/java/io/xpipe/app/comp/about/BrowseDirectoryComp.java index 1d0f74bb..2a0f7657 100644 --- a/app/src/main/java/io/xpipe/app/comp/about/BrowseDirectoryComp.java +++ b/app/src/main/java/io/xpipe/app/comp/about/BrowseDirectoryComp.java @@ -3,12 +3,16 @@ package io.xpipe.app.comp.about; import io.xpipe.app.comp.base.ButtonComp; import io.xpipe.app.core.AppI18n; import io.xpipe.app.core.AppLogs; +import io.xpipe.app.core.mode.OperationMode; import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.issue.UserReportComp; import io.xpipe.app.util.DesktopHelper; import io.xpipe.app.util.DynamicOptionsBuilder; import io.xpipe.app.util.FileOpener; +import io.xpipe.app.util.ScriptHelper; +import io.xpipe.core.impl.FileNames; +import io.xpipe.core.store.ShellStore; import io.xpipe.core.util.XPipeInstallation; import javafx.scene.layout.Region; @@ -37,8 +41,14 @@ public class BrowseDirectoryComp extends SimpleComp { }), null) .addComp( - "logFiles", - new ButtonComp(AppI18n.observable("openLogsDirectory"), () -> { + "launchDebugMode", + new ButtonComp(AppI18n.observable("launchDebugMode"), () -> { + OperationMode.executeAfterShutdown(() -> { + try (var sc = ShellStore.createLocal().create().start()) { + var script = FileNames.join(XPipeInstallation.getCurrentInstallationBasePath().toString(), XPipeInstallation.getDaemonDebugScriptPath(sc.getOsType())); + sc.executeSimpleCommand(ScriptHelper.createDetachCommand(sc, script)); + } + }); DesktopHelper.browsePath(AppLogs.get().getSessionLogsDirectory()); }), null) diff --git a/app/src/main/java/io/xpipe/app/prefs/ExternalEditorType.java b/app/src/main/java/io/xpipe/app/prefs/ExternalEditorType.java index ad20693e..9876321b 100644 --- a/app/src/main/java/io/xpipe/app/prefs/ExternalEditorType.java +++ b/app/src/main/java/io/xpipe/app/prefs/ExternalEditorType.java @@ -4,7 +4,6 @@ import io.xpipe.app.ext.PrefsChoiceValue; import io.xpipe.app.util.ApplicationHelper; import io.xpipe.app.util.WindowsRegistry; import io.xpipe.core.process.OsType; -import io.xpipe.core.process.ShellDialects; import java.io.IOException; import java.nio.file.Path; @@ -112,8 +111,7 @@ public interface ExternalEditorType extends PrefsChoiceValue { @Override public void launch(Path file) throws IOException { - var list = ShellDialects.getPlatformDefault().executeCommandListWithShell(executable + " \"" + file + "\""); - new ProcessBuilder(list).start(); + new ProcessBuilder(List.of(executable, file.toString())).start(); } @Override diff --git a/app/src/main/java/io/xpipe/app/util/ApplicationHelper.java b/app/src/main/java/io/xpipe/app/util/ApplicationHelper.java index 13c9a39a..89f205aa 100644 --- a/app/src/main/java/io/xpipe/app/util/ApplicationHelper.java +++ b/app/src/main/java/io/xpipe/app/util/ApplicationHelper.java @@ -2,7 +2,6 @@ package io.xpipe.app.util; import io.xpipe.app.issue.TrackEvent; import io.xpipe.core.impl.LocalStore; -import io.xpipe.core.process.ShellDialects; import io.xpipe.core.process.ShellControl; import java.io.IOException; @@ -11,7 +10,6 @@ import java.util.List; public class ApplicationHelper { public static void executeLocalApplication(String s) throws Exception { - var args = ShellDialects.getPlatformDefault().executeCommandListWithShell(s); TrackEvent.withDebug("proc", "Executing local application") .tag("command", s) .handle(); diff --git a/app/src/main/java/io/xpipe/app/util/DesktopShortcuts.java b/app/src/main/java/io/xpipe/app/util/DesktopShortcuts.java index e2ab15b7..905d635d 100644 --- a/app/src/main/java/io/xpipe/app/util/DesktopShortcuts.java +++ b/app/src/main/java/io/xpipe/app/util/DesktopShortcuts.java @@ -54,10 +54,8 @@ public class DesktopShortcuts { target); try (var pc = LocalStore.getShell()) { - pc.executeSimpleCommand( - pc.getShellDialect().flatten(pc.getShellDialect().getMkdirsCommand(base + "/Contents/MacOS"))); - pc.executeSimpleCommand( - pc.getShellDialect().flatten(pc.getShellDialect().getMkdirsCommand(base + "/Contents/Resources"))); + pc.executeSimpleCommand(pc.getShellDialect().getMkdirsCommand(base + "/Contents/MacOS")); + pc.executeSimpleCommand(pc.getShellDialect().getMkdirsCommand(base + "/Contents/Resources")); var executable = base + "/Contents/MacOS/" + name; pc.getShellDialect().createTextFileWriteCommand(pc, content, executable).execute(); diff --git a/app/src/main/java/io/xpipe/app/util/ScriptHelper.java b/app/src/main/java/io/xpipe/app/util/ScriptHelper.java index 6529186a..22b568f0 100644 --- a/app/src/main/java/io/xpipe/app/util/ScriptHelper.java +++ b/app/src/main/java/io/xpipe/app/util/ScriptHelper.java @@ -4,9 +4,9 @@ import io.xpipe.app.issue.TrackEvent; import io.xpipe.core.impl.FileNames; import io.xpipe.core.impl.LocalStore; import io.xpipe.core.process.OsType; +import io.xpipe.core.process.ShellControl; import io.xpipe.core.process.ShellDialect; import io.xpipe.core.process.ShellDialects; -import io.xpipe.core.process.ShellControl; import io.xpipe.core.util.SecretValue; import lombok.SneakyThrows; @@ -15,16 +15,6 @@ import java.util.Random; public class ScriptHelper { - public static String createDefaultOpenCommand(ShellControl pc, String file) { - if (pc.getOsType().equals(OsType.WINDOWS)) { - return "\"" + file + "\""; - } else if (pc.getOsType().equals(OsType.LINUX)){ - return "xdg-open \"" + file + "\""; - } else { - return "open \"" + file + "\""; - } - } - public static String createDetachCommand(ShellControl pc, String command) { if (pc.getOsType().equals(OsType.WINDOWS)) { return "start \"\" " + command; @@ -47,69 +37,6 @@ public class ScriptHelper { } } - private static final String ZSHI = - """ - #!/usr/bin/env zsh - - emulate -L zsh -o no_unset - - if (( ARGC == 0 )); then - print -ru2 -- 'Usage: zshi [zsh-flag]... - The same as plain `zsh [zsh-flag]...` except that an additional - gets executed after all standard Zsh startup files - have been sourced.' - return 1 - fi - - () { - local init=$1 - shift - local tmp - { - tmp=$(mktemp -d ${TMPDIR:-/tmp}/zsh.XXXXXXXXXX) || return - local rc - for rc in .zshenv .zprofile .zshrc .zlogin; do - >$tmp/$rc <<<'{ - if (( ${+_zshi_global_rcs} )); then - "builtin" "set" "-o" "global_rcs" - "builtin" "unset" "_zshi_global_rcs" - fi - ZDOTDIR="$_zshi_zdotdir" - # Not .zshenv because /etc/zshenv has already been read - if [[ -o global_rcs && "'$rc'" != ".zshenv" && -f "/etc/'${rc:1}'" && -r "/etc/'${rc:1}'" ]]; then - "builtin" "source" "--" "/etc/'${rc:1}'" - fi - if [[ -f "$ZDOTDIR/'$rc'" && -r "$ZDOTDIR/'$rc'" ]]; then - "builtin" "source" "--" "$ZDOTDIR/'$rc'" - fi - } always { - if [[ -o "no_rcs" || - -o "login" && "'$rc'" == ".zlogin" || - -o "no_login" && "'$rc'" == ".zshrc" || - -o "no_login" && -o "no_interactive" && "'$rc'" == ".zshenv" ]]; then - if (( ${+_zshi_global_rcs} )); then - set -o global_rcs - fi - "builtin" "unset" "_zshi_rcs" "_zshi_zdotdir" - "builtin" "command" "rm" "-rf" "--" '${(q)tmp}' - "builtin" "eval" '${(q)init}' - else - if [[ -o global_rcs ]]; then - _zshi_global_rcs= - fi - set -o no_global_rcs - _zshi_zdotdir=${ZDOTDIR:-~} - ZDOTDIR='${(q)tmp}' - fi - }' || return - done - _zshi_zdotdir=${ZDOTDIR:-~} ZDOTDIR=$tmp zsh "$@" - } always { - [[ -e $tmp ]] && rm -rf -- $tmp - } - } "$@" - """; - public static String unquote(String input) { if (input.startsWith("\"") && input.endsWith("\"")) { return input.substring(1, input.length() - 1); @@ -122,33 +49,26 @@ public class ScriptHelper { return input; } - public static String constructOpenWithInitScriptCommand( + public static String constructInitFile( ShellControl processControl, List init, String toExecuteInShell) { ShellDialect t = processControl.getShellDialect(); if (init.size() == 0 && toExecuteInShell == null) { - return t.getNormalOpenCommand(); + return null; } if (init.size() == 0) { - var cmd = unquote(toExecuteInShell); - // Check for special case of the command to be executed just being another shell script - if (cmd.endsWith(".sh") || cmd.endsWith(".bat")) { - return t.executeCommandWithShell(cmd); - } - - // Check for special case of the command being a shell command - if (ShellDialects.ALL.stream() - .anyMatch(shellType -> cmd.equals(shellType.getNormalOpenCommand()))) { - return cmd; + if (toExecuteInShell.endsWith(".sh") || toExecuteInShell.endsWith(".bat")) { + return toExecuteInShell; } } String nl = t.getNewLine().getNewLineString(); var content = String.join(nl, init) + nl; - if (t.equals(ShellDialects.BASH)) { - content = "if [ -f ~/.bashrc ]; then . ~/.bashrc; fi\n" + content; + var applyCommand = t.applyRcFileCommand(); + if (applyCommand != null) { + content = applyCommand + "\n" + content; } if (toExecuteInShell != null) { @@ -158,13 +78,7 @@ public class ScriptHelper { } var initFile = createExecScript(processControl, content); - - if (t.equals(ShellDialects.ZSH)) { - var zshiFile = createExecScript(processControl, ZSHI); - return t.getNormalOpenCommand() + " \"" + zshiFile + "\" \"" + initFile + "\""; - } - - return t.getInitFileOpenCommand(initFile); + return initFile; } @SneakyThrows @@ -218,7 +132,7 @@ public class ScriptHelper { } private static String createAskPassScript(SecretValue pass, ShellControl parent, ShellDialect type) throws Exception { - var content = type.getScriptEchoCommand(pass.getSecretValue()); + var content = type.getSelfdeleteScriptEchoCommand(pass.getSecretValue()); var temp = parent.getTemporaryDirectory(); var file = FileNames.join(temp, "askpass-" + getScriptId() + "." + type.getScriptFileEnding()); return createExecScript(parent, file, content); diff --git a/app/src/main/resources/io/xpipe/app/resources/lang/translations_en.properties b/app/src/main/resources/io/xpipe/app/resources/lang/translations_en.properties index 576aa8c9..b9a97f9d 100644 --- a/app/src/main/resources/io/xpipe/app/resources/lang/translations_en.properties +++ b/app/src/main/resources/io/xpipe/app/resources/lang/translations_en.properties @@ -174,6 +174,7 @@ openCurrentLogFile=Open current log file openLogsDirectory=Open logs directory installationFiles=Installation Files openInstallationDirectory=Open installation directory +launchDebugMode=Launch debug mode extensionInstallTitle=Download extensionInstallDescription=This action requires additional third party libraries that are not distributed by X-Pipe. You can automatically install them here. The components are then downloaded from the vendor website: extensionInstallLicenseNote=By performing the download and automatic installation you agree to the terms of the third party licenses: diff --git a/core/src/main/java/io/xpipe/core/process/ShellDialect.java b/core/src/main/java/io/xpipe/core/process/ShellDialect.java index 47539bbc..ffefb403 100644 --- a/core/src/main/java/io/xpipe/core/process/ShellDialect.java +++ b/core/src/main/java/io/xpipe/core/process/ShellDialect.java @@ -13,6 +13,12 @@ import java.util.stream.Stream; @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") public interface ShellDialect { + String argument(String s); + + default String applyRcFileCommand() { + return null; + } + CommandControl createStreamFileWriteCommand(ShellControl shellControl, String file); default String getCdCommand(String directory){ @@ -53,8 +59,6 @@ public interface ShellDialect { return "exit"; } - String getExitCodeVariable(); - String environmentVariable(String name); default String getConcatenationOperator() { @@ -67,7 +71,7 @@ public interface ShellDialect { String getMakeExecutableCommand(String file); - default String getScriptEchoCommand(String s) { + default String getSelfdeleteScriptEchoCommand(String s) { return getEchoCommand(s, false); } @@ -85,15 +89,11 @@ public interface ShellDialect { String getNormalOpenCommand(); - String getInitFileOpenCommand(String file); + String prepareInitFileOpenCommand(ShellControl parent, String file); String executeCommandWithShell(String cmd); - List executeCommandListWithShell(String cmd); - - List executeCommandListWithShell(List cmd); - - List getMkdirsCommand(String dirs); + String getMkdirsCommand(String dirs); String getFileReadCommand(String file); @@ -121,7 +121,5 @@ public interface ShellDialect { String getDisplayName(); - String getExecutable(); - - boolean doesRepeatInput(); + boolean doesEchoInput(); } diff --git a/core/src/main/java/io/xpipe/core/process/ShellDialects.java b/core/src/main/java/io/xpipe/core/process/ShellDialects.java index 62dcf8fe..b1208514 100644 --- a/core/src/main/java/io/xpipe/core/process/ShellDialects.java +++ b/core/src/main/java/io/xpipe/core/process/ShellDialects.java @@ -12,6 +12,7 @@ public class ShellDialects { public static ShellDialect POWERSHELL; public static ShellDialect CMD; public static ShellDialect SH; + public static ShellDialect DASH; public static ShellDialect BASH; public static ShellDialect ZSH; @@ -26,6 +27,7 @@ public class ShellDialects { CMD = byName("cmd"); POWERSHELL = byName("powershell"); SH = byName("sh"); + DASH = byName("dash"); BASH = byName("bash"); ZSH = byName("zsh"); } diff --git a/core/src/main/java/io/xpipe/core/store/ConnectionFileSystem.java b/core/src/main/java/io/xpipe/core/store/ConnectionFileSystem.java index d599e863..f15f44c4 100644 --- a/core/src/main/java/io/xpipe/core/store/ConnectionFileSystem.java +++ b/core/src/main/java/io/xpipe/core/store/ConnectionFileSystem.java @@ -107,8 +107,7 @@ public class ConnectionFileSystem implements FileSystem { @Override public boolean mkdirs(String file) throws Exception { try (var pc = shellControl.command(proc -> proc.getShellDialect() - .flatten(proc.getShellDialect() - .getMkdirsCommand(proc.getOsType().normalizeFileName(file)))) + .getMkdirsCommand(proc.getOsType().normalizeFileName(file))) .start()) { return pc.discardAndCheckExit(); } diff --git a/core/src/main/java/io/xpipe/core/util/XPipeTempDirectory.java b/core/src/main/java/io/xpipe/core/util/XPipeTempDirectory.java index 7286b1ca..d9429661 100644 --- a/core/src/main/java/io/xpipe/core/util/XPipeTempDirectory.java +++ b/core/src/main/java/io/xpipe/core/util/XPipeTempDirectory.java @@ -24,8 +24,7 @@ public class XPipeTempDirectory { var dir = FileNames.join(base, "xpipe"); if (!proc.executeBooleanSimpleCommand(proc.getShellDialect().getFileExistsCommand(dir))) { - proc.executeSimpleCommand( - proc.getShellDialect().flatten(proc.getShellDialect().getMkdirsCommand(dir)), + proc.executeSimpleCommand(proc.getShellDialect().getMkdirsCommand(dir), "Unable to access or create temporary directory " + dir); if (proc.getOsType().equals(OsType.LINUX) || proc.getOsType().equals(OsType.MACOS)) {