diff --git a/app/src/main/java/io/xpipe/app/prefs/ExternalApplicationType.java b/app/src/main/java/io/xpipe/app/prefs/ExternalApplicationType.java index f43d83c72..13d84cbe8 100644 --- a/app/src/main/java/io/xpipe/app/prefs/ExternalApplicationType.java +++ b/app/src/main/java/io/xpipe/app/prefs/ExternalApplicationType.java @@ -2,6 +2,7 @@ import io.xpipe.app.ext.PrefsChoiceValue; import io.xpipe.app.issue.ErrorEvent; +import io.xpipe.core.impl.LocalStore; import io.xpipe.core.process.OsType; import io.xpipe.core.process.ShellProcessControl; import io.xpipe.core.store.ShellStore; @@ -76,7 +77,7 @@ public PathApplication(String id, String executable) { } public boolean isAvailable() { - try (ShellProcessControl pc = ShellStore.local().create().start()) { + try (ShellProcessControl pc = LocalStore.getShell()) { return pc.executeBooleanSimpleCommand(pc.getShellDialect().getWhichCommand(executable)); } catch (Exception e) { ErrorEvent.fromThrowable(e).omit().handle(); diff --git a/app/src/main/java/io/xpipe/app/prefs/ExternalTerminalType.java b/app/src/main/java/io/xpipe/app/prefs/ExternalTerminalType.java index 371f92cd8..34ced2e22 100644 --- a/app/src/main/java/io/xpipe/app/prefs/ExternalTerminalType.java +++ b/app/src/main/java/io/xpipe/app/prefs/ExternalTerminalType.java @@ -4,9 +4,9 @@ import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.util.ApplicationHelper; import io.xpipe.app.util.MacOsPermissions; +import io.xpipe.core.impl.LocalStore; import io.xpipe.core.process.OsType; import io.xpipe.core.process.ShellProcessControl; -import io.xpipe.core.store.ShellStore; import java.util.List; @@ -133,7 +133,7 @@ public MacOsTerminalType() { @Override public void launch(String name, String command) throws Exception { - try (ShellProcessControl pc = ShellStore.local().create().start()) { + try (ShellProcessControl pc = LocalStore.getShell()) { var suffix = command.equals(pc.getShellDialect().getNormalOpenCommand()) ? "\"\"" : "\"" + command.replaceAll("\"", "\\\\\"") + "\""; @@ -157,7 +157,7 @@ public void launch(String name, String command) throws Exception { } var format = custom.contains("$cmd") ? custom : custom + " $cmd"; - try (var pc = ShellStore.local().create().start()) { + try (var pc = LocalStore.getShell()) { var toExecute = format.replace("$cmd", command); if (pc.getOsType().equals(OsType.WINDOWS)) { toExecute = "start \"" + name + "\" " + toExecute; @@ -187,7 +187,7 @@ public ITerm2Type() { @Override public void launch(String name, String command) throws Exception { - try (ShellProcessControl pc = ShellStore.local().create().start()) { + try (ShellProcessControl pc = LocalStore.getShell()) { var cmd = String.format( """ osascript - "$@" < s) throws Exception { TrackEvent.withDebug("proc", "Executing local application") .elements(args) .handle(); - try (var c = ShellStore.local().create().command(s).start()) { + try (var c = LocalStore.getShell().command(s).start()) { c.discardOrThrow(); } } 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 727cced9e..f97f396de 100644 --- a/app/src/main/java/io/xpipe/app/util/DesktopShortcuts.java +++ b/app/src/main/java/io/xpipe/app/util/DesktopShortcuts.java @@ -1,5 +1,6 @@ package io.xpipe.app.util; +import io.xpipe.core.impl.LocalStore; import io.xpipe.core.process.OsType; import io.xpipe.core.store.ShellStore; import io.xpipe.core.util.XPipeInstallation; @@ -53,7 +54,7 @@ private static void createMacOSShortcut(String target, String name) throws Excep """, target); - try (var pc = ShellStore.local().create().start()) { + try (var pc = LocalStore.getShell()) { pc.executeSimpleCommand( pc.getShellDialect().flatten(pc.getShellDialect().getMkdirsCommand(base + "/Contents/MacOS"))); pc.executeSimpleCommand( diff --git a/core/src/main/java/io/xpipe/core/impl/LocalStore.java b/core/src/main/java/io/xpipe/core/impl/LocalStore.java index 8e537e1da..c2675287e 100644 --- a/core/src/main/java/io/xpipe/core/impl/LocalStore.java +++ b/core/src/main/java/io/xpipe/core/impl/LocalStore.java @@ -2,27 +2,29 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import io.xpipe.core.process.ProcessControlProvider; +import io.xpipe.core.process.ShellDialects; import io.xpipe.core.process.ShellProcessControl; -import io.xpipe.core.store.FileSystem; -import io.xpipe.core.store.FileSystemStore; -import io.xpipe.core.store.MachineStore; +import io.xpipe.core.store.*; import io.xpipe.core.util.JacksonizedValue; -import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.UncheckedIOException; -import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.util.*; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; @JsonTypeName("local") public class LocalStore extends JacksonizedValue implements FileSystemStore, MachineStore { + private static ShellProcessControl local; + + public static ShellProcessControl getShell() throws Exception { + if (local == null) { + local = new LocalStore().create().start(); + } + + return local; + } + @Override public boolean isLocal() { return true; @@ -30,100 +32,100 @@ public boolean isLocal() { @Override public FileSystem createFileSystem() { - return new FileSystem() { - @Override - public Optional getShell() { - return Optional.empty(); - } - - @Override - public FileSystem open() throws Exception { - return this; - } - - @Override - public boolean exists(String file) { - return Files.exists(Path.of(file)); - } - - @Override - public void delete(String file) throws Exception { - Files.delete(Path.of(file)); - } - - @Override - public void copy(String file, String newFile) throws Exception { - Files.copy(Path.of(file), Path.of(newFile), StandardCopyOption.REPLACE_EXISTING); - } - - @Override - public void move(String file, String newFile) throws Exception { - Files.move(Path.of(file), Path.of(newFile), StandardCopyOption.REPLACE_EXISTING); - } - - @Override - public boolean mkdirs(String file) throws Exception { - try { - Files.createDirectories(Path.of(file)); - return true; - } catch (Exception ex) { - return false; - } - } - - @Override - public void touch(String file) throws Exception { - if (exists(file)) { - return; - } - - Files.createFile(Path.of(file)); - } - - @Override - public boolean isDirectory(String file) throws Exception { - return Files.isDirectory(Path.of(file)); - } - - @Override - public Stream listFiles(String file) throws Exception { - return Files.list(Path.of(file)).map(path -> { - try { - var date = Files.getLastModifiedTime(path); - var size = Files.isDirectory(path) ? 0 : Files.size(path); - return new FileEntry( - this, - path.toString(), - date.toInstant(), - Files.isDirectory(path), - Files.isHidden(path), - Files.isExecutable(path), - size); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - }); - } - - @Override - public List listRoots() throws Exception { - return StreamSupport.stream(FileSystems.getDefault().getRootDirectories().spliterator(), false).map(path -> path.toString()).toList(); - } + return new ConnectionFileSystem(ShellStore.local().create()) { @Override public InputStream openInput(String file) throws Exception { - var p = Path.of(file); + var p = wrap(file); return Files.newInputStream(p); } @Override public OutputStream openOutput(String file) throws Exception { - var p = Path.of(file); + var p = wrap(file); return Files.newOutputStream(p); } - @Override - public void close() throws IOException {} + private Path wrap(String file) { + for (var e : System.getenv().entrySet()) { + file = file.replace( + ShellDialects.getPlatformDefault().environmentVariable(e.getKey()), + e.getValue()); + } + return Path.of(file); + } + +// @Override +// public boolean exists(String file) { +// return Files.exists(wrap(file)); +// } +// +// @Override +// public void delete(String file) throws Exception { +// Files.delete(wrap(file)); +// } +// +// @Override +// public void copy(String file, String newFile) throws Exception { +// Files.copy(wrap(file), wrap(newFile), StandardCopyOption.REPLACE_EXISTING); +// } +// +// @Override +// public void move(String file, String newFile) throws Exception { +// Files.move(wrap(file), wrap(newFile), StandardCopyOption.REPLACE_EXISTING); +// } +// +// @Override +// public boolean mkdirs(String file) throws Exception { +// try { +// Files.createDirectories(wrap(file)); +// return true; +// } catch (Exception ex) { +// return false; +// } +// } +// +// @Override +// public void touch(String file) throws Exception { +// if (exists(file)) { +// return; +// } +// +// Files.createFile(wrap(file)); +// } +// +// @Override +// public boolean isDirectory(String file) throws Exception { +// return Files.isDirectory(wrap(file)); +// } +// +// @Override +// public Stream listFiles(String file) throws Exception { +// return Files.list(wrap(file)).map(path -> { +// try { +// var date = Files.getLastModifiedTime(path); +// var size = Files.isDirectory(path) ? 0 : Files.size(path); +// return new FileEntry( +// this, +// path.toString(), +// date.toInstant(), +// Files.isDirectory(path), +// Files.isHidden(path), +// Files.isExecutable(path), +// size); +// } catch (IOException e) { +// throw new UncheckedIOException(e); +// } +// }); +// } +// +// @Override +// public List listRoots() throws Exception { +// return StreamSupport.stream( +// FileSystems.getDefault().getRootDirectories().spliterator(), false) +// .map(path -> path.toString()) +// .toList(); +// } }; } 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 5331377f0..7f81d1c3a 100644 --- a/core/src/main/java/io/xpipe/core/process/ShellDialect.java +++ b/core/src/main/java/io/xpipe/core/process/ShellDialect.java @@ -47,6 +47,8 @@ default String getExitCommand() { String getExitCodeVariable(); + String environmentVariable(String name); + default String getConcatenationOperator() { return ";"; } diff --git a/core/src/main/java/io/xpipe/core/store/ShellStore.java b/core/src/main/java/io/xpipe/core/store/ShellStore.java index 8381b78bb..078f512d5 100644 --- a/core/src/main/java/io/xpipe/core/store/ShellStore.java +++ b/core/src/main/java/io/xpipe/core/store/ShellStore.java @@ -10,7 +10,7 @@ public interface ShellStore extends DataStore, StatefulDataStore, LaunchableStore, FileSystemStore { - public static MachineStore local() { + public static ShellStore local() { return new LocalStore(); }