applesingle/tools/asu/src/main/java/io/github/applecommander/applesingle/tools/asu/ExtractCommand.java

99 lines
3.3 KiB
Java

package io.github.applecommander.applesingle.tools.asu;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.Callable;
import io.github.applecommander.applesingle.AppleSingle;
import picocli.CommandLine.Command;
import picocli.CommandLine.Help.Visibility;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
/**
* Supports extracting components of an AppleSingle archive.
*/
@Command(name = "extract", description = { "Extract contents of an AppleSingle file" },
parameterListHeading = "%nParameters:%n",
descriptionHeading = "%n",
optionListHeading = "%nOptions:%n")
public class ExtractCommand implements Callable<Void> {
@Option(names = { "-h", "--help" }, description = "Show help for subcommand", usageHelp = true)
private boolean helpFlag;
@Option(names = "--stdout", description = "Write selected fork to stdout")
private boolean stdoutFlag;
@Option(names = "--stdin", description = "Read AppleSingle from stdin")
private boolean stdinFlag;
@Option(names = "--fix-text", description = "Clear the high bit and fix line endings")
private boolean fixTextFlag;
@Option(names = "--fork", description = "Extract which fork type (specify data, resource, or both",
showDefaultValue = Visibility.ALWAYS)
private ForkType forkType = ForkType.data;
@Option(names = { "-o", "--output" }, description = "Write fork(s) to base filename")
private String baseFilename;
@Parameters(arity = "0..1", description = "File to process")
private Path file;
@Override
public Void call() throws IOException {
validateArguments();
AppleSingle applesingle = stdinFlag ? AppleSingle.read(System.in) : AppleSingle.read(file);
if (!stdoutFlag && baseFilename == null && applesingle.getRealName() == null) {
throw new IOException("Please include an output base filename; this AppleSingle file does not contain a name");
}
if (baseFilename == null) {
baseFilename = applesingle.getRealName();
}
writeFork(ForkType.data, applesingle.getDataFork());
writeFork(ForkType.resource, applesingle.getResourceFork());
return null;
}
public void validateArguments() throws IOException {
if (stdoutFlag && baseFilename != null) {
throw new IOException("Please choose one of stdout or output file");
}
if (stdoutFlag && forkType == ForkType.both) {
throw new IOException("Stdout only supports one type of fork for output");
}
if ((stdinFlag && file != null) || (!stdinFlag && file == null)) {
throw new IOException("Please select ONE of stdin or file");
}
}
public void writeFork(ForkType forkType, byte[] data) throws IOException {
if (this.forkType != forkType && this.forkType != ForkType.both) return;
if (data == null || data.length == 0) {
throw new IOException(String.format("There is no data in the %s fork, aborting", forkType));
}
if (fixTextFlag) {
for (int i=0; i<data.length; i++) {
data[i] = (byte)(data[i] & 0x7f);
if (data[i] == 0x0d) data[i] = '\n';
}
}
if (baseFilename != null) {
String targetFilename = String.format("%s.%s", baseFilename, forkType.name());
System.out.printf("Writing %s fork to file '%s'...\n", forkType.name(), targetFilename);
Path path = Paths.get(targetFilename);
Files.write(path, data);
}
if (stdoutFlag) {
System.out.write(data);
}
}
}