From 25ddc7f66a6d516d9c8cbbd6bafe2a2205e55ca1 Mon Sep 17 00:00:00 2001 From: Rob Greene Date: Tue, 26 Dec 2023 17:07:31 -0600 Subject: [PATCH] Adding 'shell' subcommand for 'acx' to enable multiple commands. Needs to run from a pipe as well, limiting shell capabilities a bit (?). --- .../io/github/applecommander/acx/Main.java | 32 +---- .../acx/command/ShellCommand.java | 133 ++++++++++++++++++ 2 files changed, 140 insertions(+), 25 deletions(-) create mode 100644 app/cli-acx/src/main/java/io/github/applecommander/acx/command/ShellCommand.java diff --git a/app/cli-acx/src/main/java/io/github/applecommander/acx/Main.java b/app/cli-acx/src/main/java/io/github/applecommander/acx/Main.java index 40ac3f4..2292959 100644 --- a/app/cli-acx/src/main/java/io/github/applecommander/acx/Main.java +++ b/app/cli-acx/src/main/java/io/github/applecommander/acx/Main.java @@ -19,36 +19,17 @@ */ package io.github.applecommander.acx; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.LogManager; -import java.util.logging.Logger; - -import io.github.applecommander.acx.command.CompareCommand; -import io.github.applecommander.acx.command.ConvertCommand; -import io.github.applecommander.acx.command.CopyFileCommand; -import io.github.applecommander.acx.command.CreateDiskCommand; -import io.github.applecommander.acx.command.DeleteCommand; -import io.github.applecommander.acx.command.DiskMapCommand; -import io.github.applecommander.acx.command.DumpCommand; -import io.github.applecommander.acx.command.ExportCommand; -import io.github.applecommander.acx.command.FindDuplicateFilesCommand; -import io.github.applecommander.acx.command.ImportCommand; -import io.github.applecommander.acx.command.InfoCommand; -import io.github.applecommander.acx.command.ListCommand; -import io.github.applecommander.acx.command.LockCommand; -import io.github.applecommander.acx.command.MkdirCommand; -import io.github.applecommander.acx.command.ReadCommand; -import io.github.applecommander.acx.command.RenameDiskCommand; -import io.github.applecommander.acx.command.RenameFileCommand; -import io.github.applecommander.acx.command.RmdirCommand; -import io.github.applecommander.acx.command.UnlockCommand; -import io.github.applecommander.acx.command.WriteCommand; +import io.github.applecommander.acx.command.*; import picocli.CommandLine; import picocli.CommandLine.Command; import picocli.CommandLine.HelpCommand; import picocli.CommandLine.Option; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.Logger; + /** * Primary entry point into the 'acx' utility. */ @@ -77,6 +58,7 @@ import picocli.CommandLine.Option; RenameFileCommand.class, RenameDiskCommand.class, RmdirCommand.class, + ShellCommand.class, UnlockCommand.class, WriteCommand.class }) diff --git a/app/cli-acx/src/main/java/io/github/applecommander/acx/command/ShellCommand.java b/app/cli-acx/src/main/java/io/github/applecommander/acx/command/ShellCommand.java new file mode 100644 index 0000000..fe5f02d --- /dev/null +++ b/app/cli-acx/src/main/java/io/github/applecommander/acx/command/ShellCommand.java @@ -0,0 +1,133 @@ +/* + * AppleCommander - An Apple ][ image utility. + * Copyright (C) 2019-2023 by Robert Greene and others + * robgreene at users.sourceforge.net + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program 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 + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package io.github.applecommander.acx.command; + +import io.github.applecommander.acx.PrintExceptionMessageHandler; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Parameters; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Command(name = "shell", + descriptionHeading = "%n", + commandListHeading = "%nCommands:%n", + optionListHeading = "%nOptions:%n", + description = "Enter 'acx' shell mode to support multiple commands.", + subcommands = { + CompareCommand.class, + ConvertCommand.class, + CopyFileCommand.class, + CreateDiskCommand.class, + DeleteCommand.class, + DiskMapCommand.class, + DumpCommand.class, + ExportCommand.class, + FindDuplicateFilesCommand.class, + CommandLine.HelpCommand.class, + ImportCommand.class, + InfoCommand.class, + ListCommand.class, + LockCommand.class, + MkdirCommand.class, + ReadCommand.class, + RenameFileCommand.class, + RenameDiskCommand.class, + RmdirCommand.class, + UnlockCommand.class, + WriteCommand.class + }) +public class ShellCommand implements Callable { + private static final String PROMPT = "ACX> "; + + private boolean exit = false; + @Command(name = "exit", aliases = { "quit" }, description = "Exit shell environment") + public void exitShell() { + exit = true; + } + + private String defaultDiskName = null; + @Command(name = "default", description = "Display or set the default disk") + public void defaultDisk(@Parameters(description = "Disk to be set as default", arity = "0..1", defaultValue = "") String diskName) { + if (diskName == null || diskName.isEmpty()) { + if (defaultDiskName == null) { + System.out.println("No default disk has been set."); + } + else { + System.out.printf("The default disk is '%s'.\n", defaultDiskName); + } + } + else { + // we don't know the commands being used, so can't really test for existence... + defaultDiskName = diskName; + } + } + + @Override + public Integer call() throws IOException { + final boolean isTTY = (System.console() != null); + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) { + while (!exit) { + System.out.print(PROMPT); + String line = reader.readLine(); + if (line == null) break; + if (!isTTY) { + System.out.println(line); + } + + CommandLine cmd = new CommandLine(new ShellCommand()); + cmd.setExecutionExceptionHandler(new PrintExceptionMessageHandler()); + cmd.setCaseInsensitiveEnumValuesAllowed(true); + + List args = getArgs(line); + if (defaultDiskName != null) { + System.setProperty("ACX_DISK_NAME", defaultDiskName); + } + cmd.execute(args.toArray(new String[0])); + } + } + + return 0; + } + + private static List getArgs(String line) { + // See https://stackoverflow.com/questions/7804335/split-string-on-spaces-in-java-except-if-between-quotes-i-e-treat-hello-wor + final Pattern splitter = Pattern.compile("([^\"]\\S*|\".+?\")\\s*"); + + Matcher m = splitter.matcher(line); + List args = new ArrayList<>(); + while (m.find()) { + String s = m.group(1); + if (s.startsWith("\"") && s.endsWith("\"")) { + s = s.substring(1, s.length()-1); + } + args.add(s); + } + return args; + } +}