Adding 'shell' subcommand for 'acx' to enable multiple commands. Needs to run from a pipe as well, limiting shell capabilities a bit (?).

This commit is contained in:
Rob Greene 2023-12-26 17:07:31 -06:00
parent 47206f0fd8
commit 25ddc7f66a
2 changed files with 140 additions and 25 deletions

View File

@ -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
})

View File

@ -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<Integer> {
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<String> args = getArgs(line);
if (defaultDiskName != null) {
System.setProperty("ACX_DISK_NAME", defaultDiskName);
}
cmd.execute(args.toArray(new String[0]));
}
}
return 0;
}
private static List<String> 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<String> 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;
}
}