/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.command.archive;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.IOException;
import org.apache.tools.zip.ZipFile;
import org.jnode.shell.AbstractCommand;
import org.jnode.shell.syntax.Argument;
import org.jnode.shell.syntax.FlagArgument;
/**
* This is a base class that holds some convenience methods for implementing archive commands.
*
* @author chris boertien
*/
public class ArchiveCommand extends AbstractCommand {
private static final String help_verbose = "show the compression ratio for each file compressed";
private static final String help_debug = "internal debug output";
private static final String help_quiet = "supress non-essential warning messages";
private static final String help_stdout = "pipe data to stdout";
private static final String help_force = "force overwrite of output files";
protected static final String help_decompress = "force decompression";
protected static final String prompt_overwrite = " already exists. Do you wish to overwrite? [Y/n]: ";
protected static final String err_exception_uncaught = "Unhandled Exception thrown";
protected static final String err_file_create = "Could not create file: ";
protected static final String err_file_not_exist = "Could not find file: ";
protected static final String err_stream_create = "Could not create stream: ";
protected static final String fmt_size_diff = "%s:\t%f.2%% -- replaced with %s";
protected final FlagArgument Quiet = new FlagArgument("quiet", Argument.OPTIONAL, help_quiet);
protected final FlagArgument Verbose = new FlagArgument("verbose", Argument.OPTIONAL, help_verbose);
protected final FlagArgument Debug = new FlagArgument("debug", Argument.OPTIONAL, help_debug);
protected final FlagArgument Stdout = new FlagArgument("stdout", Argument.OPTIONAL, help_stdout);
protected final FlagArgument Force = new FlagArgument("force", Argument.OPTIONAL, help_force);
protected static final int OUT_FATAL = 0x01;
protected static final int OUT_ERROR = 0x02;
protected static final int OUT_WARN = 0x04;
protected static final int OUT_NOTICE = 0x08;
protected static final int OUT_DEBUG = 0x80;
protected static final int BUFFER_SIZE = 4096;
protected int outMode = OUT_ERROR | OUT_WARN;
protected PrintWriter stdoutWriter;
protected PrintWriter stderrWriter;
protected Reader stdinReader;
protected InputStream stdin;
protected OutputStream stdout;
protected String commandName;
protected int rc = 1;
protected boolean use_stdout;
protected boolean force;
protected boolean compress;
private byte[] buffer;
protected ArchiveCommand(String s) {
super(s);
}
public void execute(String command) {
stdoutWriter = getOutput().getPrintWriter();
stderrWriter = getError().getPrintWriter();
stdinReader = getInput().getReader();
stdin = getInput().getInputStream();
stdout = getOutput().getOutputStream();
// FIXME get rid of this {
if (command.equals("zcat") || command.equals("bzcat")) return;
if (!command.equals("tar")) {
if (Quiet.isSet()) outMode = 0;
force = Force.isSet();
}
if (!use_stdout) use_stdout = Stdout.isSet();
if (outMode != 0) {
if (Verbose.isSet()) outMode |= OUT_NOTICE;
if (Debug.isSet()) outMode |= OUT_DEBUG;
}
// }
}
protected void createStreamBuffer(int size) {
buffer = new byte[size];
}
/**
* Pipes the contents of the InputStream into the OutputStream.
*
* This is most usefull for applying a stream filter that reads data from a source
* and pipes the contents to an output stream.
*
* @param in stream to read from
* @param out stream to write to
*/
protected void processStream(InputStream in, OutputStream out) throws IOException {
int len;
if (buffer == null) buffer = new byte[4096];
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
}
/**
* Opens a FileOutputStream for a file.
*
* If there is a problem opening the stream, the exception is caught and an error message
* is displayed.
*
* @param file the file to open the stream on
* @param delete if the file exists, delete it first
* @param forced if true, this forces the deletion without prompting the user
* @return an OutputStream on the file, or null if there was a problem. null could also be
* returned if the delete option was chosen and the user said no to overwriting.
*/
protected OutputStream openFileWrite(File file, boolean delete, boolean forced) {
try {
boolean createNew = true;
if (file == null) {
error(err_file_create + "null");
return null;
}
if (file.exists()) {
if (delete) {
if (forced) {
file.delete();
} else {
if (askUser(file + prompt_overwrite, true)) {
file.delete();
} else {
notice("Skipping " + file);
return null;
}
}
} else {
return new FileOutputStream(file);
}
}
if (createNew && !file.createNewFile()) {
error(err_file_create + file);
return null;
}
return new FileOutputStream(file);
} catch (IOException ioe) {
error(err_stream_create + file);
return null;
}
}
/**
* Opens a FileInputStream on a file.
*
* If there is a problem opening the stream, the IOException is caught, and an
* error message displayed to the console.
*
* @param file the file to open the stream on
* @return the InputStream or null if there was a problem.
*/
protected InputStream openFileRead(File file) {
try {
if (file == null || !file.exists()) {
error(err_file_not_exist + file);
return null;
}
return new FileInputStream(file);
} catch (IOException ioe) {
error(err_stream_create + file);
return null;
}
}
/**
* Convenience method for closing streams and writers.
*/
protected void close(Closeable obj) {
if (obj != null) {
try {
obj.close();
} catch (IOException ex) {
//ignore
}
}
}
/**
* Convenience method for closing org.apache.tools.zip.ZipFile
*/
protected void close(ZipFile zfile) {
if (zfile != null) {
try {
zfile.close();
} catch (IOException ex) {
// ignore
}
}
}
/**
* Prompt the user with a question asking for a yes or no answer.
*
* FIXME This is unsafe as it will trigger an endless loop if stdin
* is not the terminal.
*
* @param s the question to ask the user
* @param defaultY if {#code true}, the default answer is yes, otherwise no.
* @return true if the user said yes, false if the user said no
*/
protected boolean askUser(String s, boolean defaultY) {
int choice;
// put a cap on the looping to prevent non-terminal stdin
// from an infinite loop
for (int i = 0; i < 10; i++) {
stdoutWriter.print(s);
try {
choice = stdinReader.read();
} catch (IOException ex) {
throw new RuntimeException("Problem with stdin");
}
stdoutWriter.println();
if (choice == 'y') return true;
if (choice == 'n') return false;
if (choice == '\n') return defaultY;
}
return false;
}
protected void out(String s) {
stdoutWriter.println(s);
}
protected void err(String s) {
stderrWriter.println(s);
}
protected void debug(String s) {
if ((outMode & OUT_DEBUG) == OUT_DEBUG) {
stderrWriter.print("debug: ");
stderrWriter.println(s);
}
}
protected void notice(String s) {
if ((outMode & OUT_NOTICE) == OUT_NOTICE) stdoutWriter.println(s);
}
protected void warn(String s) {
if ((outMode & OUT_WARN) == OUT_WARN) stdoutWriter.println(s);
}
protected void error(String s) {
if ((outMode & OUT_ERROR) == OUT_ERROR) stderrWriter.println(s);
}
protected void fatal(String s, int exitCode) {
stderrWriter.println("Fatal error: " + s);
exit(exitCode);
}
}