From e7992907fc3b061e54be91bcbef3afc7dc070563 Mon Sep 17 00:00:00 2001 From: crschnick Date: Mon, 16 Jun 2025 16:36:10 +0000 Subject: [PATCH] Rework compress actions --- .../app/browser/action/BrowserAction.java | 11 +- .../file/BrowserFileSystemTabModel.java | 35 ---- .../impl/compress/BaseUntarMenuProvider.java | 30 +-- .../compress/BaseUnzipUnixMenuProvider.java | 30 +-- .../BaseUnzipWindowsActionProvider.java | 47 +---- .../impl/compress/CompressMenuProvider.java | 190 ++---------------- .../menu/impl/compress/TarActionProvider.java | 67 ++++++ .../impl/compress/UntarActionProvider.java | 61 ++++++ .../impl/compress/UnzipActionProvider.java | 82 ++++++++ .../menu/impl/compress/ZipActionProvider.java | 89 ++++++++ app/src/main/java/module-info.java | 4 + 11 files changed, 352 insertions(+), 294 deletions(-) create mode 100644 app/src/main/java/io/xpipe/app/browser/menu/impl/compress/TarActionProvider.java create mode 100644 app/src/main/java/io/xpipe/app/browser/menu/impl/compress/UntarActionProvider.java create mode 100644 app/src/main/java/io/xpipe/app/browser/menu/impl/compress/UnzipActionProvider.java create mode 100644 app/src/main/java/io/xpipe/app/browser/menu/impl/compress/ZipActionProvider.java diff --git a/app/src/main/java/io/xpipe/app/browser/action/BrowserAction.java b/app/src/main/java/io/xpipe/app/browser/action/BrowserAction.java index 566e6d54a..1643083c1 100644 --- a/app/src/main/java/io/xpipe/app/browser/action/BrowserAction.java +++ b/app/src/main/java/io/xpipe/app/browser/action/BrowserAction.java @@ -22,6 +22,9 @@ public abstract class BrowserAction extends StoreAction { @JsonIgnore protected BrowserFileSystemTabModel model; + @JsonIgnore + private List entries; + @Override protected boolean beforeExecute() throws Exception { AppLayoutModel.get().selectBrowser(); @@ -67,7 +70,11 @@ public abstract class BrowserAction extends StoreAction { } protected List getEntries() { - return files.stream() + if (entries != null) { + return entries; + } + + entries = files.stream() .map(filePath -> { var be = model.getFileList().getAll().getValue().stream() .filter(browserEntry -> @@ -81,6 +88,7 @@ public abstract class BrowserAction extends StoreAction { }) .filter(browserEntry -> browserEntry != null) .toList(); + return entries; } public abstract static class BrowserActionBuilder> @@ -92,6 +100,7 @@ public abstract class BrowserAction extends StoreAction { files(entries.stream() .map(browserEntry -> browserEntry.getRawFileEntry().getPath()) .toList()); + entries(entries); } public void initFiles(BrowserFileSystemTabModel model, List entries) { diff --git a/app/src/main/java/io/xpipe/app/browser/file/BrowserFileSystemTabModel.java b/app/src/main/java/io/xpipe/app/browser/file/BrowserFileSystemTabModel.java index 85a6085b8..874e01433 100644 --- a/app/src/main/java/io/xpipe/app/browser/file/BrowserFileSystemTabModel.java +++ b/app/src/main/java/io/xpipe/app/browser/file/BrowserFileSystemTabModel.java @@ -451,41 +451,6 @@ public final class BrowserFileSystemTabModel extends BrowserStoreSessionTab { - BooleanScope.executeExclusive(busy, () -> { - if (getCurrentDirectory() == null) { - return; - } - - fileSystem - .getShell() - .orElseThrow() - .command(command) - .withWorkingDirectory(getCurrentDirectory().getPath()) - .execute(); - if (refresh) { - refreshSync(); - } - }); - }); - } - - public void runAsync(FailableRunnable r, boolean refresh) { - ThreadHelper.runFailableAsync(() -> { - BooleanScope.executeExclusive(busy, () -> { - if (getCurrentDirectory() == null) { - return; - } - - r.run(); - if (refresh) { - refreshSync(); - } - }); - }); - } - public boolean isClosed() { return false; } diff --git a/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/BaseUntarMenuProvider.java b/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/BaseUntarMenuProvider.java index d886c4683..e254343a5 100644 --- a/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/BaseUntarMenuProvider.java +++ b/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/BaseUntarMenuProvider.java @@ -1,5 +1,7 @@ package io.xpipe.app.browser.menu.impl.compress; +import io.xpipe.app.action.AbstractAction; +import io.xpipe.app.browser.action.BrowserActionProvider; import io.xpipe.app.browser.file.BrowserEntry; import io.xpipe.app.browser.file.BrowserFileSystemTabModel; import io.xpipe.app.browser.icon.BrowserIconFileType; @@ -38,28 +40,12 @@ public class BaseUntarMenuProvider implements BrowserApplicationPathMenuProvider } @Override - public void execute(BrowserFileSystemTabModel model, List entries) { - model.runAsync( - () -> { - ShellControl sc = model.getFileSystem().getShell().orElseThrow(); - for (BrowserEntry entry : entries) { - var target = getTarget(entry.getRawFileEntry().getPath()); - var c = CommandBuilder.of().add("tar"); - if (toDirectory) { - c.add("-C").addFile(target); - } - c.add("-x").addIf(gz, "-z").add("-f"); - c.addFile(entry.getRawFileEntry().getPath()); - if (toDirectory) { - model.getFileSystem().mkdirs(target); - } - sc.command(c) - .withWorkingDirectory( - model.getCurrentDirectory().getPath()) - .execute(); - } - }, - true); + public AbstractAction createAction(BrowserFileSystemTabModel model, List entries) { + var builder = UntarActionProvider.Action.builder(); + builder.initEntries(model, entries); + builder.gz(gz); + builder.toDirectory(toDirectory); + return builder.build(); } @Override diff --git a/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/BaseUnzipUnixMenuProvider.java b/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/BaseUnzipUnixMenuProvider.java index 979df85bb..67246c86f 100644 --- a/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/BaseUnzipUnixMenuProvider.java +++ b/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/BaseUnzipUnixMenuProvider.java @@ -1,5 +1,6 @@ package io.xpipe.app.browser.menu.impl.compress; +import io.xpipe.app.action.AbstractAction; import io.xpipe.app.browser.file.BrowserEntry; import io.xpipe.app.browser.file.BrowserFileSystemTabModel; import io.xpipe.app.browser.icon.BrowserIconFileType; @@ -36,23 +37,12 @@ public abstract class BaseUnzipUnixMenuProvider implements BrowserMenuLeafProvid return "unzip"; } - public void execute(BrowserFileSystemTabModel model, List entries) throws Exception { - ShellControl sc = model.getFileSystem().getShell().orElseThrow(); - for (BrowserEntry entry : entries) { - var command = CommandBuilder.of() - .add("unzip", "-o") - .addFile(entry.getRawFileEntry().getPath()); - if (toDirectory) { - command.add("-d").addFile(getTarget(entry.getRawFileEntry().getPath())); - } - try (var cc = sc.command(command) - .withWorkingDirectory(model.getCurrentDirectory().getPath()) - .start()) { - cc.discardOrThrow(); - } - } - - model.refreshSync(); + @Override + public AbstractAction createAction(BrowserFileSystemTabModel model, List entries) { + var builder = UnzipActionProvider.Action.builder(); + builder.initEntries(model, entries); + builder.toDirectory(toDirectory); + return builder.build(); } @Override @@ -65,14 +55,10 @@ public abstract class BaseUnzipUnixMenuProvider implements BrowserMenuLeafProvid var sep = model.getFileSystem().getShell().orElseThrow().getOsType().getFileSystemSeparator(); var dir = entries.size() > 1 ? "[...]" - : getTarget(entries.getFirst().getRawFileEntry().getPath()).getFileName() + sep; + : UnzipActionProvider.getTarget(entries.getFirst().getRawFileEntry().getPath()).getFileName() + sep; return toDirectory ? AppI18n.observable("unzipDirectory", dir) : AppI18n.observable("unzipHere"); } - private FilePath getTarget(FilePath name) { - return FilePath.of(name.toString().replaceAll("\\.zip$", "")); - } - @Override public boolean isApplicable(BrowserFileSystemTabModel model, List entries) { return entries.stream() diff --git a/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/BaseUnzipWindowsActionProvider.java b/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/BaseUnzipWindowsActionProvider.java index c9702a876..ac3924121 100644 --- a/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/BaseUnzipWindowsActionProvider.java +++ b/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/BaseUnzipWindowsActionProvider.java @@ -1,5 +1,6 @@ package io.xpipe.app.browser.menu.impl.compress; +import io.xpipe.app.action.AbstractAction; import io.xpipe.app.browser.file.BrowserEntry; import io.xpipe.app.browser.file.BrowserFileSystemTabModel; import io.xpipe.app.browser.icon.BrowserIconFileType; @@ -8,11 +9,7 @@ import io.xpipe.app.browser.menu.BrowserMenuCategory; import io.xpipe.app.browser.menu.BrowserMenuLeafProvider; import io.xpipe.app.core.AppI18n; import io.xpipe.app.util.LabelGraphic; -import io.xpipe.core.process.CommandBuilder; import io.xpipe.core.process.OsType; -import io.xpipe.core.process.ShellControl; -import io.xpipe.core.process.ShellDialects; -import io.xpipe.core.store.FilePath; import javafx.beans.value.ObservableValue; @@ -31,38 +28,6 @@ public abstract class BaseUnzipWindowsActionProvider implements BrowserMenuLeafP return new LabelGraphic.CompGraphic(BrowserIcons.createContextMenuIcon(BrowserIconFileType.byId("zip"))); } - @Override - public void execute(BrowserFileSystemTabModel model, List entries) { - model.runAsync( - () -> { - var sc = model.getFileSystem().getShell().orElseThrow(); - if (ShellDialects.isPowershell(sc)) { - for (BrowserEntry entry : entries) { - runCommand(sc, model, entry); - } - } else { - try (var sub = sc.subShell(ShellDialects.POWERSHELL)) { - for (BrowserEntry entry : entries) { - runCommand(sub, model, entry); - } - } - } - }, - true); - } - - private void runCommand(ShellControl sc, BrowserFileSystemTabModel model, BrowserEntry entry) throws Exception { - var command = CommandBuilder.of().add("Expand-Archive", "-Force"); - if (toDirectory) { - var target = getTarget(entry.getRawFileEntry().getPath()); - command.add("-DestinationPath").addFile(target); - } - command.add("-Path").addFile(entry.getRawFileEntry().getPath()); - sc.command(command) - .withWorkingDirectory(model.getCurrentDirectory().getPath()) - .execute(); - } - @Override public BrowserMenuCategory getCategory() { return BrowserMenuCategory.CUSTOM; @@ -73,12 +38,16 @@ public abstract class BaseUnzipWindowsActionProvider implements BrowserMenuLeafP var sep = model.getFileSystem().getShell().orElseThrow().getOsType().getFileSystemSeparator(); var dir = entries.size() > 1 ? "[...]" - : getTarget(entries.getFirst().getRawFileEntry().getPath()).getFileName() + sep; + : UnzipActionProvider.getTarget(entries.getFirst().getRawFileEntry().getPath()).getFileName() + sep; return toDirectory ? AppI18n.observable("unzipDirectory", dir) : AppI18n.observable("unzipHere"); } - private FilePath getTarget(FilePath name) { - return FilePath.of(name.toString().replaceAll("\\.zip$", "")); + @Override + public AbstractAction createAction(BrowserFileSystemTabModel model, List entries) { + var builder = UnzipActionProvider.Action.builder(); + builder.initEntries(model, entries); + builder.toDirectory(toDirectory); + return builder.build(); } @Override diff --git a/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/CompressMenuProvider.java b/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/CompressMenuProvider.java index 11502f7c2..996b94337 100644 --- a/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/CompressMenuProvider.java +++ b/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/CompressMenuProvider.java @@ -10,18 +10,13 @@ import io.xpipe.app.comp.base.ModalOverlay; import io.xpipe.app.core.AppI18n; import io.xpipe.app.util.CommandSupport; import io.xpipe.app.util.LabelGraphic; -import io.xpipe.core.process.CommandBuilder; import io.xpipe.core.process.OsType; -import io.xpipe.core.process.ShellDialects; import io.xpipe.core.store.FileKind; -import io.xpipe.core.store.FilePath; import javafx.beans.property.SimpleStringProperty; import javafx.beans.value.ObservableValue; import javafx.scene.control.TextField; -import org.kordamp.ikonli.javafx.FontIcon; - import java.util.List; public abstract class CompressMenuProvider implements BrowserMenuBranchProvider { @@ -39,20 +34,7 @@ public abstract class CompressMenuProvider implements BrowserMenuBranchProvider var foundTar = CommandSupport.findProgram(sc, "tar"); model.getCache().getInstalledApplications().put("tar", foundTar.isPresent()); - if (sc.getOsType() == OsType.WINDOWS) { - var found = CommandSupport.findProgram(sc, "7z"); - if (found.isPresent()) { - model.getCache().getMultiPurposeCache().put("7zExecutable", "7z"); - return; - } - - var pf = sc.command(sc.getShellDialect().getPrintEnvironmentVariableCommand("ProgramFiles")) - .readStdoutOrThrow(); - var loc = FilePath.of(pf).join("7-Zip", "7z.exe").toWindows(); - if (model.getFileSystem().fileExists(loc)) { - model.getCache().getMultiPurposeCache().put("7zExecutable", loc); - } - } else { + if (sc.getOsType() != OsType.WINDOWS) { var found = CommandSupport.findProgram(sc, "zip"); model.getCache().getInstalledApplications().put("zip", found.isPresent()); } @@ -75,7 +57,7 @@ public abstract class CompressMenuProvider implements BrowserMenuBranchProvider @Override public boolean isApplicable(BrowserFileSystemTabModel model, List entries) { - var ext = List.of("zip", "tar", "tar.gz", "tgz", "7z", "rar", "xar"); + var ext = List.of("zip", "tar", "tar.gz", "tgz", "rar", "xar"); if (entries.stream().anyMatch(browserEntry -> ext.stream().anyMatch(s -> browserEntry .getRawFileEntry() .getPath() @@ -94,9 +76,7 @@ public abstract class CompressMenuProvider implements BrowserMenuBranchProvider public List getBranchingActions( BrowserFileSystemTabModel model, List entries) { return List.of( - new Windows7ZActionProvider(), - new WindowsZipActionProvider(), - new UnixZipActionProvider(), + new ZipActionProvider(), new TarBasedActionProvider(false) { @Override protected String getExtension() { @@ -150,141 +130,21 @@ public abstract class CompressMenuProvider implements BrowserMenuBranchProvider protected abstract String getExtension(); } - private class WindowsZipActionProvider extends LeafProvider { + private class ZipActionProvider extends LeafProvider { @Override protected void create(String fileName, BrowserFileSystemTabModel model, List entries) { - var base = model.getCurrentDirectory().getPath(); - var target = base.join(fileName); - var command = CommandBuilder.of() - .add("Compress-Archive", "-Force", "-DestinationPath") - .addFile(target) - .add("-Path"); - for (int i = 0; i < entries.size(); i++) { - var rel = entries.get(i).getRawFileEntry().getPath().relativize(base); - if (directory) { - command.addQuoted(rel.toDirectory().toWindows() + "*"); - } else { - command.addFile(rel.toWindows()); - } - if (i != entries.size() - 1) { - command.add(","); - } - } - - model.runAsync( - () -> { - var sc = model.getFileSystem().getShell().orElseThrow(); - if (ShellDialects.isPowershell(sc)) { - sc.command(command).withWorkingDirectory(base).execute(); - } else { - try (var sub = sc.subShell(ShellDialects.POWERSHELL)) { - sub.command(command).withWorkingDirectory(base).execute(); - } - } - }, - true); + var builder = io.xpipe.app.browser.menu.impl.compress.ZipActionProvider.Action.builder(); + builder.initEntries(model, entries); + builder.target(model.getCurrentDirectory().getPath().join(fileName)); + builder.directoryContentOnly(directory); + builder.build().executeAsync(); } @Override protected String getExtension() { return "zip"; } - - @Override - public boolean isApplicable(BrowserFileSystemTabModel model, List entries) { - return model.getFileSystem().getShell().orElseThrow().getOsType() == OsType.WINDOWS; - } - } - - private class UnixZipActionProvider extends LeafProvider { - - @Override - protected void create(String fileName, BrowserFileSystemTabModel model, List entries) { - var base = model.getCurrentDirectory().getPath(); - var target = base.join(fileName); - var command = CommandBuilder.of().add("zip", "-r", "-"); - for (BrowserEntry entry : entries) { - var rel = entry.getRawFileEntry().getPath().relativize(base).toUnix(); - if (directory) { - command.add("."); - } else { - command.addFile(rel); - } - } - command.add(">").addFile(target); - - if (directory) { - model.runAsync( - () -> { - var sc = model.getFileSystem().getShell().orElseThrow(); - sc.command(command) - .withWorkingDirectory( - entries.getFirst().getRawFileEntry().getPath()) - .execute(); - }, - true); - } else { - model.runCommandAsync(command, true); - } - } - - @Override - protected String getExtension() { - return "zip"; - } - - @Override - public boolean isActive(BrowserFileSystemTabModel model, List entries) { - return model.getCache().getInstalledApplications().get("zip"); - } - - @Override - public boolean isApplicable(BrowserFileSystemTabModel model, List entries) { - return model.getFileSystem().getShell().orElseThrow().getOsType() != OsType.WINDOWS; - } - } - - private class Windows7ZActionProvider extends LeafProvider { - - @Override - protected void create(String fileName, BrowserFileSystemTabModel model, List entries) { - var base = model.getCurrentDirectory().getPath(); - var target = base.join(fileName); - var command = CommandBuilder.of() - .addFile(model.getCache() - .getMultiPurposeCache() - .get("7zExecutable") - .toString()) - .add("a") - .add("-r") - .addFile(target); - for (BrowserEntry entry : entries) { - var rel = entry.getRawFileEntry().getPath().relativize(base); - if (directory) { - command.addQuoted(".\\" + rel.toDirectory().toWindows() + "*"); - } else { - command.addFile(rel.toWindows()); - } - } - - model.runCommandAsync(command, true); - } - - @Override - protected String getExtension() { - return "7z"; - } - - @Override - public boolean isActive(BrowserFileSystemTabModel model, List entries) { - return model.getCache().getMultiPurposeCache().containsKey("7zExecutable"); - } - - @Override - public boolean isApplicable(BrowserFileSystemTabModel model, List entries) { - return model.getFileSystem().getShell().orElseThrow().getOsType() == OsType.WINDOWS; - } } private abstract class TarBasedActionProvider extends LeafProvider { @@ -297,32 +157,12 @@ public abstract class CompressMenuProvider implements BrowserMenuBranchProvider @Override protected void create(String fileName, BrowserFileSystemTabModel model, List entries) { - var tar = CommandBuilder.of() - .add("tar", "-c") - .addIf(gz, "-z") - .add("-f") - .addFile(fileName); - var base = model.getCurrentDirectory().getPath(); - - if (directory) { - var dir = entries.getFirst().getRawFileEntry().getPath(); - // Fix for bsd find, remove / - var command = CommandBuilder.of() - .add("find") - .addFile(dir.removeTrailingSlash().toUnix()) - .add("|", "sed") - .addLiteral("s,^" + dir.toDirectory().toUnix() + "*,,") - .add("|"); - command.add(tar).add("-C").addFile(dir.toDirectory().toUnix()).add("-T", "-"); - model.runCommandAsync(command, true); - } else { - var command = CommandBuilder.of().add(tar); - for (BrowserEntry entry : entries) { - var rel = entry.getRawFileEntry().getPath().relativize(base); - command.addFile(rel); - } - model.runCommandAsync(command, true); - } + var builder = TarActionProvider.Action.builder(); + builder.initEntries(model, entries); + builder.target(model.getCurrentDirectory().getPath().join(fileName)); + builder.directoryContentOnly(directory); + builder.gz(gz); + builder.build().executeAsync(); } @Override diff --git a/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/TarActionProvider.java b/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/TarActionProvider.java new file mode 100644 index 000000000..77034af25 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/TarActionProvider.java @@ -0,0 +1,67 @@ +package io.xpipe.app.browser.menu.impl.compress; + +import io.xpipe.app.browser.action.BrowserAction; +import io.xpipe.app.browser.action.BrowserActionProvider; +import io.xpipe.app.browser.file.BrowserEntry; +import io.xpipe.core.process.CommandBuilder; +import io.xpipe.core.store.FilePath; +import lombok.NonNull; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; + +public class TarActionProvider implements BrowserActionProvider { + + @Jacksonized + @SuperBuilder + public static class Action extends BrowserAction { + + @NonNull + private final FilePath target; + + private final boolean directoryContentOnly; + + private final boolean gz; + + @Override + public boolean isMutation() { + return true; + } + + @Override + public void executeImpl() throws Exception { + var sc = model.getFileSystem().getShell().orElseThrow(); + var tar = CommandBuilder.of() + .add("tar", "-c") + .addIf(gz, "-z") + .add("-f") + .addFile(target); + var base = model.getCurrentDirectory().getPath(); + + if (directoryContentOnly) { + var dir = getEntries().getFirst().getRawFileEntry().getPath(); + // Fix for bsd find, remove / + var command = CommandBuilder.of() + .add("find") + .addFile(dir.removeTrailingSlash().toUnix()) + .add("|", "sed") + .addLiteral("s,^" + dir.toDirectory().toUnix() + "*,,") + .add("|"); + command.add(tar).add("-C").addFile(dir.toDirectory().toUnix()).add("-T", "-"); + sc.command(command).execute(); + } else { + var command = CommandBuilder.of().add(tar); + for (BrowserEntry entry : getEntries()) { + var rel = entry.getRawFileEntry().getPath().relativize(base); + command.addFile(rel); + } + sc.command(command).execute(); + } + model.refreshSync(); + } + } + + @Override + public String getId() { + return "tar"; + } +} diff --git a/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/UntarActionProvider.java b/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/UntarActionProvider.java new file mode 100644 index 000000000..0ae0137f0 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/UntarActionProvider.java @@ -0,0 +1,61 @@ +package io.xpipe.app.browser.menu.impl.compress; + +import io.xpipe.app.browser.action.BrowserAction; +import io.xpipe.app.browser.action.BrowserActionProvider; +import io.xpipe.app.browser.file.BrowserEntry; +import io.xpipe.core.process.CommandBuilder; +import io.xpipe.core.process.ShellControl; +import io.xpipe.core.store.FileKind; +import io.xpipe.core.store.FilePath; +import lombok.NonNull; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; + +public class UntarActionProvider implements BrowserActionProvider { + + @Jacksonized + @SuperBuilder + public static class Action extends BrowserAction { + + private final boolean gz; + private final boolean toDirectory; + + @Override + public boolean isMutation() { + return true; + } + + @Override + public void executeImpl() throws Exception { + ShellControl sc = model.getFileSystem().getShell().orElseThrow(); + for (BrowserEntry entry : getEntries()) { + var target = getTarget(entry.getRawFileEntry().getPath()); + var c = CommandBuilder.of().add("tar"); + if (toDirectory) { + c.add("-C").addFile(target); + } + c.add("-x").addIf(gz, "-z").add("-f"); + c.addFile(entry.getRawFileEntry().getPath()); + if (toDirectory) { + model.getFileSystem().mkdirs(target); + } + sc.command(c) + .withWorkingDirectory( + model.getCurrentDirectory().getPath()) + .execute(); + } + } + + private FilePath getTarget(FilePath name) { + return FilePath.of(name.toString() + .replaceAll("\\.tar$", "") + .replaceAll("\\.tar.gz$", "") + .replaceAll("\\.tgz$", "")); + } + } + + @Override + public String getId() { + return "untar"; + } +} diff --git a/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/UnzipActionProvider.java b/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/UnzipActionProvider.java new file mode 100644 index 000000000..8b10da408 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/UnzipActionProvider.java @@ -0,0 +1,82 @@ +package io.xpipe.app.browser.menu.impl.compress; + +import io.xpipe.app.browser.action.BrowserAction; +import io.xpipe.app.browser.action.BrowserActionProvider; +import io.xpipe.app.browser.file.BrowserEntry; +import io.xpipe.app.browser.file.BrowserFileSystemTabModel; +import io.xpipe.core.process.CommandBuilder; +import io.xpipe.core.process.OsType; +import io.xpipe.core.process.ShellControl; +import io.xpipe.core.process.ShellDialects; +import io.xpipe.core.store.FilePath; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; + +public class UnzipActionProvider implements BrowserActionProvider { + + public static FilePath getTarget(FilePath name) { + return FilePath.of(name.toString().replaceAll("\\.zip$", "")); + } + + @Jacksonized + @SuperBuilder + public static class Action extends BrowserAction { + + private final boolean toDirectory; + + @Override + public boolean isMutation() { + return true; + } + + @Override + public void executeImpl() throws Exception { + var sc = model.getFileSystem().getShell().orElseThrow(); + if (sc.getOsType() == OsType.WINDOWS) { + if (ShellDialects.isPowershell(sc)) { + for (BrowserEntry entry : getEntries()) { + runPowershellCommand(sc, model, entry); + } + } else { + try (var sub = sc.subShell(ShellDialects.POWERSHELL)) { + for (BrowserEntry entry : getEntries()) { + runPowershellCommand(sub, model, entry); + } + } + } + } else { + for (BrowserEntry entry : getEntries()) { + var command = CommandBuilder.of() + .add("unzip", "-o") + .addFile(entry.getRawFileEntry().getPath()); + if (toDirectory) { + command.add("-d").addFile(getTarget(entry.getRawFileEntry().getPath())); + } + try (var cc = sc.command(command) + .withWorkingDirectory(model.getCurrentDirectory().getPath()) + .start()) { + cc.discardOrThrow(); + } + } + } + model.refreshSync(); + } + + private void runPowershellCommand(ShellControl sc, BrowserFileSystemTabModel model, BrowserEntry entry) throws Exception { + var command = CommandBuilder.of().add("Expand-Archive", "-Force"); + if (toDirectory) { + var target = getTarget(entry.getRawFileEntry().getPath()); + command.add("-DestinationPath").addFile(target); + } + command.add("-Path").addFile(entry.getRawFileEntry().getPath()); + sc.command(command) + .withWorkingDirectory(model.getCurrentDirectory().getPath()) + .execute(); + } + } + + @Override + public String getId() { + return "unzip"; + } +} diff --git a/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/ZipActionProvider.java b/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/ZipActionProvider.java new file mode 100644 index 000000000..1edff8e76 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/browser/menu/impl/compress/ZipActionProvider.java @@ -0,0 +1,89 @@ +package io.xpipe.app.browser.menu.impl.compress; + +import io.xpipe.app.browser.action.BrowserAction; +import io.xpipe.app.browser.action.BrowserActionProvider; +import io.xpipe.app.browser.file.BrowserEntry; +import io.xpipe.core.process.CommandBuilder; +import io.xpipe.core.process.OsType; +import io.xpipe.core.process.ShellDialects; +import io.xpipe.core.store.FileKind; +import io.xpipe.core.store.FilePath; +import lombok.NonNull; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; + +public class ZipActionProvider implements BrowserActionProvider { + + @Jacksonized + @SuperBuilder + public static class Action extends BrowserAction { + + @NonNull + private final FilePath target; + + private final boolean directoryContentOnly; + + @Override + public boolean isMutation() { + return true; + } + + @Override + public void executeImpl() throws Exception { + var sc = model.getFileSystem().getShell().orElseThrow(); + if (sc.getOsType() == OsType.WINDOWS) { + var base = model.getCurrentDirectory().getPath(); + var command = CommandBuilder.of() + .add("Compress-Archive", "-Force", "-DestinationPath") + .addFile(target) + .add("-Path"); + for (int i = 0; i < getEntries().size(); i++) { + var rel = getEntries().get(i).getRawFileEntry().getPath().relativize(base); + if (getEntries().get(i).getRawFileEntry().getKind() == FileKind.DIRECTORY && directoryContentOnly) { + command.addQuoted(rel.toDirectory().toWindows() + "*"); + } else { + command.addFile(rel.toWindows()); + } + if (i != getEntries().size() - 1) { + command.add(","); + } + } + + if (ShellDialects.isPowershell(sc)) { + sc.command(command).withWorkingDirectory(base).execute(); + } else { + try (var sub = sc.subShell(ShellDialects.POWERSHELL)) { + sub.command(command).withWorkingDirectory(base).execute(); + } + } + } else { + var command = CommandBuilder.of().add("zip", "-r", "-"); + for (BrowserEntry entry : getEntries()) { + var base = target.getParent(); + var rel = entry.getRawFileEntry().getPath().relativize(base).toUnix(); + if (entry.getRawFileEntry().getKind() == FileKind.DIRECTORY && directoryContentOnly) { + command.add("."); + } else { + command.addFile(rel); + } + } + command.add(">").addFile(target); + + if (directoryContentOnly) { + sc.command(command) + .withWorkingDirectory( + getEntries().getFirst().getRawFileEntry().getPath()) + .execute(); + } else { + sc.command(command).execute(); + } + } + model.refreshSync(); + } + } + + @Override + public String getId() { + return "zip"; + } +} diff --git a/app/src/main/java/module-info.java b/app/src/main/java/module-info.java index a10ca0587..8ab90cbb5 100644 --- a/app/src/main/java/module-info.java +++ b/app/src/main/java/module-info.java @@ -157,6 +157,10 @@ open module io.xpipe.app { DeleteMenuProvider, ChownActionProvider, ChmodActionProvider, + TarActionProvider, + UntarActionProvider, + ZipActionProvider, + UnzipActionProvider, UnzipHereUnixMenuProvider, UnzipDirectoryUnixMenuProvider, UnzipHereWindowsActionProvider,