|
From: <chr...@us...> - 2009-04-03 02:05:05
|
Revision: 5211
http://jnode.svn.sourceforge.net/jnode/?rev=5211&view=rev
Author: chrisboertien
Date: 2009-04-03 02:05:01 +0000 (Fri, 03 Apr 2009)
Log Message:
-----------
Implemented bzip/gzip for tar as well as other fixes
Signed-off-by: Chris Boertien <chr...@gm...>
Modified Paths:
--------------
trunk/fs/descriptors/org.jnode.fs.command.archive.xml
trunk/fs/src/fs/org/jnode/fs/command/archive/TarCommand.java
trunk/fs/src/fs/org/jnode/fs/command/archive/UnzipCommand.java
trunk/fs/src/fs/org/jnode/fs/command/archive/Zip.java
trunk/fs/src/fs/org/jnode/fs/command/archive/ZipCommand.java
Modified: trunk/fs/descriptors/org.jnode.fs.command.archive.xml
===================================================================
--- trunk/fs/descriptors/org.jnode.fs.command.archive.xml 2009-04-02 19:01:01 UTC (rev 5210)
+++ trunk/fs/descriptors/org.jnode.fs.command.archive.xml 2009-04-03 02:05:01 UTC (rev 5211)
@@ -31,7 +31,6 @@
</extension>
<extension point="org.jnode.shell.syntaxes">
-
<syntax alias="tar">
<sequence>
<alternatives description="tar operations">
@@ -54,13 +53,15 @@
<option argLabel="fileList" shortName="T" longName="files-from"/>
<option argLabel="gzip" shortName="z" longName="gzip" />
<option argLabel="interact" shortName="w" longName="interactive" />
- <option argLabel="keep" shortName="k" longName="keep-old-files" />
+ <option argLabel="keep_old" shortName="k" longName="keep-old-files" />
+ <option argLabel="keep_new" longName="keep-newer-files" />
<option argLabel="noRecurse" longName="no-recursion" />
<option argLabel="recurse" longName="recursion" />
<option argLabel="removeFiles" longName="remove-files" />
<option argLabel="showTotals" longName="totals" />
<option argLabel="suffix" longName="suffix" />
<option argLabel="stdout" shortName="O" longName="to-stdout" />
+ <option argLabel="unlink" longName="unlink" />
<option argLabel="verbose" shortName="v" longName="verbose" />
<option argLabel="verify" shortName="W" longName="verify" />
<option argLabel="xfile" shortName="X" longName="exclude-from"/>
@@ -198,10 +199,8 @@
<extension point="org.jnode.security.permissions">
<permission class="java.io.FilePermission" name="<<ALL FILES>>" actions="read,write,delete"/>
- <permission class="java.net.SocketPermission" name="*:0-" actions="connect,resolve"/>
<permission class="java.util.PropertyPermission" name="user.dir" actions="read,write"/>
<permission class="java.util.PropertyPermission" name="*" actions="read,write"/>
- <permission class="java.net.NetPermission" name="specifyStreamHandler"/>
<permission class="java.lang.RuntimePermission" name="modifyThreadGroup"/>
<permission class="java.lang.RuntimePermission" name="exitVM"/>
<permission class="org.jnode.security.JNodePermission" name="getVmClass"/>
Modified: trunk/fs/src/fs/org/jnode/fs/command/archive/TarCommand.java
===================================================================
--- trunk/fs/src/fs/org/jnode/fs/command/archive/TarCommand.java 2009-04-02 19:01:01 UTC (rev 5210)
+++ trunk/fs/src/fs/org/jnode/fs/command/archive/TarCommand.java 2009-04-03 02:05:01 UTC (rev 5211)
@@ -23,6 +23,8 @@
import org.apache.tools.tar.TarEntry;
import org.apache.tools.tar.TarInputStream;
import org.apache.tools.tar.TarOutputStream;
+import org.apache.tools.bzip2.CBZip2InputStream;
+import org.apache.tools.bzip2.CBZip2OutputStream;
import org.jnode.shell.syntax.Argument;
import org.jnode.shell.syntax.FlagArgument;
@@ -36,7 +38,164 @@
import java.util.ArrayList;
import java.util.Collections;
+import java.util.TreeMap;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ *
+ * This version of tar has the ability to magically work with compressed archives without being
+ * told with the -jz flags on the command line. The -jz flags are seen as an override to force tar
+ * to work with the selected compression method, and fail if it can't do that.
+ *
+ * Reading :
+ * 1) If the -j or -z flags are set, then tar will check for the magic bytes in the header for the
+ * given compression method. If they are not found, tar will fail.
+ * 2) If the -j or -z flags are not set, then tar will check for the magic bytes in the header for
+ * both compression methods. If either are found, tar will wrap the FileInputStream given to TarInputStream
+ * with a decompression wrapper stream. If neither are found, then tar assumes the archive is an
+ * uncompressed archive.
+ * If the archive is being extracted from stdin, and the archive is compressed, then the -j or -z flags
+ * must be given, or tar will assume it is already uncompressed.
+ *
+ * Creating :
+ * 1) If the -j or -z flags are set, then tar will output a new archive compressed with the given method.
+ * 2) If the -j or -z flags are not set, then tar will check the file suffix of the new archive. If the suffix
+ * is either .tbz, .tbz2, .tar.bz or .tar.bz2, then tar will compress with bzip2. If the suffix is .tar.gz
+ * then tar will compress with gzip. Otherwise an uncompressed archive is created.
+ *
+ * Writing :
+ * 1) If the -j or -z flags are set, then tar will output an archive compressed with the given method.
+ * 2) If the -j or -z flags are not set, and the original archive was compressed, then the same method will
+ * be used to recompress the archive. Otherwise the archive will be output uncompressed.
+ *
+ * TODO Currently, in order to set the output stream to the last entry of an archive, in order to append new entries,
+ * we're moving the archive, creating a new archive, and copying the data via streams. A more effecient method
+ * would be a reverse search of the file looking for the last block to be written too. Blocks are written in
+ * groups of 20, so it will be relatively fast to find. Once we read the header, we'll know how big the entry is
+ * and we can position the OutputStream to the first available block. We can't simply append to the end of the
+ * file because the last block is not necessarily the end of the file.
+ * This might not be quite so simple though, especially when dealing with compressed archives. Which can't be
+ * randomly accessed.
+ *
+ * TODO Implement a way to check if a file being handled is actually a tar file. The tar input stream will gladly
+ * read a file that is not a tar archive. This can cause any sort of irrational behavior, especially if the
+ * data is binary in nature.
+ * TODO Implement update/delete.
+ * TODO Implement interactive (global)
+ * TODO Implement display totals. (global)
+ * TODO Implement verification (verify)
+ * FIXME BUG - if extracting an archive given on stdin, and that archive overwrites files on the filesystem
+ * it will cause openFileWrite() to prompt the user and ask to overwrite, but reading from stdin
+ * wont come from the console since stdin is attached to a pipe. In this case we will either have
+ * to default to overwrite or skip.
+ * TODO When extracting entries, tar needs to strip any leading slashes by default, turning absolute pathnames
+ * into relative ones. When inserting entries, leading slashes should also be stripped by default. Also
+ * warn about inserting entries with a '..' prefix, and refuse to extract them.
+ * TODO Default behavior when extracting is to overwrite.
+ * TODO Implement exclusion by date, --newer=<date> ignores files with a mod time older than the given date.
+ *
+ * TODO Options to implement
+ * -P --absolute-names [Create, Append, Update] TODO
+ * Overrides the default behavior of stripping leading slashes while inserting or extracting entries. Will
+ * also force tar to extract entries prefixed with '..'
+ * -N --newer [All] TODO
+ * Limit operating on files to only those that are newer than the given date. If the value starts with a
+ * / or ., the value is a file, and its mod time should be used.
+ * --newer-mtime [All] TODO
+ * Similar to --newer, except it doesn't take into account status changes made to the file, only content
+ * changes (might not be a difference atm in jnode, so they'll both be the same).
+ * --atime-preserve [Create, Append, Update, Extract]
+ * Preserve the access time of the file. (Not important)
+ * -a --auto-compress [Create]
+ * When creating an archive, determine the compression type from the archive suffix. We already do this by
+ * default if no compress flags (j/z) were given.
+ * --no-auto-compress [Create] TODO
+ * Turn off the automatic checking of archive suffix to determine compression type.
+ * --backup [Extract] TODO
+ * Backup files instead of overwriting them. We currently implement simple backups using a given or default
+ * suffix. There is also the option of creating numbered backups. And a hybrid option that makes numbered
+ * backups if they exist already, simple backups if they do not. As well this option can also apply to the
+ * archive itself if it is being modified. This option gets its default from the VERSION_CONTROL env variable.
+ * If this is not set, the default is 'existing'.
+ * Options are 't' | 'numbered', 'nil' | 'existing' and 'never' | 'simple'.
+ * --suffix [Extract/backup]
+ * Need to read the SIMPLE_BACKUP_SUFFIX env variable for the default, otherwise use ~
+ * --checkpoint n
+ * Checkpoints are issued every nth recorded that is written or read to/from the archive.
+ * --checkpoing-action <action>
+ * Perform one of the following actions every checkpoint
+ * - bell Produces an audible bell on the system speaker
+ * - dot prints a '.' to stdout
+ * - echo Display a message to stderr
+ * - echo=string Display string to stderr, string is subject to meta-character expansion
+ * - exec=command Execute the given command
+ * - sleep=time Sleep for <time> seconds
+ * - ttyout=string Output string to /dev/tty (the current console, if stderr is redirected)
+ * This argument may be given multiple times to perform different actions. They will be executed in the
+ * order found on the command line.
+ * -l --check-links
+ * --delay-directory-restore
+ * --no-delay-directory-restore
+ * Restore directory timestamps and permissions after extraction has completed.
+ * -h --dereference
+ * Store the file a link points to instead of the link itself
+ * --anchored
+ * --no-anchored
+ * --ignore-case
+ * --no-ignore-case
+ * --wildcards
+ * --no-wildcards
+ * --wildcards-match-slash
+ * --no-wildcards-match-slash
+ * Pattern matching modifier
+ * --ignore-failed-read
+ * Do not exit just because a read of a file failed. (We do this by default atm)
+ * --index-file=<file>
+ * Send verbose output to <file>
+ * --overwrite
+ * --overwrite-dir
+ * Overwrite existing files when extracting (default)
+ *
+ * --lzma
+ * --lzop
+ * -Z --compress
+ * Other compression methods, not supported atm.
+ *
+ * --pax-option=<list>
+ * --owner=<name>
+ * -o --no-same-owner
+ * --no-same-permissions
+ * --no-unquote
+ * --numberic-owner
+ * --occurence=<number>
+ * --one-file-system
+ * --null
+ * --no-null
+ * --no-overwrite-dir
+ * --no-ignore-command-error
+ * -M --multi-volume
+ * --no-check-device
+ * -g --listed-incremental=<file>
+ * -V --label=<string>
+ * -F --info-script=<file>
+ * -G --incremental
+ * --group
+ * --owner
+ * --mode
+ * --mtime
+ * -H --format
+ * --interactive
+ * -R --block-number
+ * -b --blocking-factor
+ * -i --ignore-zeroes
+ * -B --read-full-records
+ * --check-device
+ * (ignore for now)
+ *
+ * @author chris boertien
+ */
public class TarCommand extends ArchiveCommand {
private static final String help_append = "append entries to an archive";
@@ -57,6 +216,7 @@
private static final String help_gzip = "compress the archive with gzip";
private static final String help_interact = "ask for confirmation for every action";
private static final String help_keep_old = "keep existing files; don't overwrite from archive";
+ private static final String help_keep_new = "keep existing files if they are newer than the archive entry";
private static final String help_norecurse = "do not recurse into directories";
private static final String help_paths = "files and directories to include in archive";
private static final String help_recurse = "recurse into directories";
@@ -64,22 +224,32 @@
private static final String help_stdout = "extract files to stdout";
private static final String help_suffix = "append <suffix> to backup files (default ~)";
private static final String help_totals = "display total bytes written after creating the archive";
+ private static final String help_unlink = "when extracting, delete files if they exist";
private static final String help_verbose = "list files processed";
private static final String help_verify = "verify the archive after writing it";
private static final String help_xfile = "exclude files matching patterns in <file>";
private static final String err_options = "required options -Acdtrux not found, or multiple options set";
- private static final int TAR_APPEND = 0x01;
- private static final int TAR_CREATE = 0x02;
- private static final int TAR_CONCAT = 0x04;
- private static final int TAR_DELETE = 0x08;
- private static final int TAR_UPDATE = 0x10;
- private static final int TAR_LIST = 0x20;
- private static final int TAR_DIFF = 0x40;
- private static final int TAR_EXTRACT = 0x80;
- private static final int TAR_INSERT = TAR_APPEND | TAR_CREATE;
+ private static final int TAR_APPEND = 0x01;
+ private static final int TAR_CREATE = 0x02;
+ private static final int TAR_CONCAT = 0x04;
+ private static final int TAR_DELETE = 0x08;
+ private static final int TAR_UPDATE = 0x10;
+ private static final int TAR_LIST = 0x20;
+ private static final int TAR_DIFF = 0x40;
+ private static final int TAR_EXTRACT = 0x80;
+ private static final int TAR_REQ_ARCH
+ = TAR_APPEND | TAR_CREATE | TAR_CONCAT | TAR_DELETE | TAR_UPDATE | TAR_LIST | TAR_DIFF;
+ private static final int TAR_INSERT = TAR_APPEND | TAR_CREATE;
+ private static final int TAR_VERIFY = TAR_APPEND | TAR_CONCAT | TAR_DELETE | TAR_UPDATE;
+ private static final int TAR_COMPRESS = TAR_APPEND | TAR_CREATE | TAR_CONCAT | TAR_DELETE | TAR_UPDATE;
+ private static final int TAR_DECOMPRESS =
+ TAR_APPEND | TAR_CONCAT | TAR_DELETE | TAR_DIFF | TAR_UPDATE | TAR_LIST | TAR_EXTRACT;
+ private static final int USE_BZIP = 1;
+ private static final int USE_GZIP = 2;
+
private final FlagArgument DoAppend = new FlagArgument("doAppend", Argument.OPTIONAL, help_append);
private final FlagArgument DoConcat = new FlagArgument("doConcat", Argument.OPTIONAL, help_concat);
private final FlagArgument DoCreate = new FlagArgument("doCreate", Argument.OPTIONAL, help_create);
@@ -97,12 +267,14 @@
private final FileArgument FileList = new FileArgument("fileList", Argument.OPTIONAL, help_file_list);
private final FlagArgument UseGzip = new FlagArgument("gzip", Argument.OPTIONAL, help_gzip);
private final FlagArgument Interact = new FlagArgument("interact", Argument.OPTIONAL, help_interact);
- private final FlagArgument Keep = new FlagArgument("keep", Argument.OPTIONAL, help_keep_old);
+ private final FlagArgument KeepOld = new FlagArgument("keep_old", Argument.OPTIONAL, help_keep_old);
+ private final FlagArgument KeepNew = new FlagArgument("keep_new", Argument.OPTIONAL, help_keep_new);
private final FlagArgument NoRecurse = new FlagArgument("noRecurse", Argument.OPTIONAL, help_norecurse);
private final FlagArgument Recurse = new FlagArgument("recurse", Argument.OPTIONAL, help_recurse);
private final FlagArgument RemoveFiles = new FlagArgument("removeFiles", Argument.OPTIONAL, help_remove);
private final FlagArgument ShowTotals = new FlagArgument("showTotals", Argument.OPTIONAL, help_totals);
private final StringArgument Suffix = new StringArgument("suffix", Argument.OPTIONAL, help_suffix);
+ private final FlagArgument Unlink = new FlagArgument("unlink", Argument.OPTIONAL, help_unlink);
private final FlagArgument Verify = new FlagArgument("verify", Argument.OPTIONAL, help_verify);
private final FileArgument ExcludeFile = new FileArgument("xfile", Argument.OPTIONAL, help_xfile);
@@ -114,6 +286,8 @@
private String suffix = "~";
private String exclude = "";
private int mode;
+ private int compress;
+ private int decompress;
private boolean recurse;
private boolean pipeInOut;
private boolean backup;
@@ -122,28 +296,23 @@
private boolean interact;
private boolean verify;
private boolean showTotals;
- private boolean keep;
+ private boolean keepOld;
+ private boolean keepNew;
+ private boolean unlink;
public TarCommand() {
super("Create/Modify/Extract tape archives");
registerArguments(DoAppend, DoConcat, DoCreate, DoDelete, DoDiff, DoExtract, DoList, DoUpdate,
- Backup, UseBzip, ChangeDir, Exclude, Archive, FileList, UseGzip, Interact,
- Keep, NoRecurse, Recurse, RemoveFiles, ShowTotals, Suffix, Verify, Paths, ExcludeFile);
+ Backup, UseBzip, ChangeDir, Exclude, Archive, FileList, UseGzip, Interact, KeepNew, Unlink,
+ KeepOld, NoRecurse, Recurse, RemoveFiles, ShowTotals, Suffix, Verify, Paths, ExcludeFile);
}
+ // TODO Allow working directory to be changed
public void execute() {
super.execute();
if (!checkMode()) {
fatal(err_options, 1);
}
- if (DoAppend.isSet()) mode = TAR_APPEND;
- else if (DoConcat.isSet()) mode = TAR_CONCAT;
- else if (DoCreate.isSet()) mode = TAR_CREATE;
- else if (DoDelete.isSet()) mode = TAR_DELETE;
- else if (DoDiff.isSet()) mode = TAR_DIFF;
- else if (DoExtract.isSet()) mode = TAR_EXTRACT;
- else if (DoList.isSet()) mode = TAR_LIST;
- else if (DoUpdate.isSet()) mode = TAR_UPDATE;
if (Suffix.isSet()) suffix = Suffix.getValue();
if (Exclude.isSet()) exclude = Exclude.getValue();
@@ -156,70 +325,158 @@
interact = Interact.isSet();
verify = Verify.isSet();
showTotals = ShowTotals.isSet();
- keep = Keep.isSet();
+ keepOld = KeepOld.isSet();
+ keepNew = KeepNew.isSet();
recurse = !NoRecurse.isSet();
+ unlink = Unlink.isSet();
if (Archive.isSet()) archive = Archive.getValue();
else error("No archive given");
//if (!(pipeInOut = !Archive.isSet())) archive = Archive.getValue();
- debug("Mode: " + mode);
- debug("Archive: " + archive);
- debug("Suffix: " + suffix);
- debug("Exclude: " + exclude);
- debug("Exclude File: " + excludeFile);
- debug("File List: " + fileList);
- debug("Backup: " + backup);
- debug("BZip: " + bzip);
- debug("GZip: " + gzip);
- debug("Interactive: " + interact);
- debug("Recurse: " + recurse);
- debug("Verify: " + verify);
- debug("Use StdOut: " + use_stdout);
- debug("Keep Old Files: " + keep);
- debug("Show Totals: " + showTotals);
- debug("pipeInOut: " + pipeInOut);
-
try {
+ if ((mode & TAR_REQ_ARCH) != 0 && archive == null) {
+ fatal("Archive required for -Acdtru", 1);
+ }
+
+ if ((mode & TAR_DECOMPRESS) != 0 && archive != null) {
+ if (checkCompressed(archive) == -1) {
+ // happens when -j or -z were specified, but the archive is not
+ // in the given format.
+ if (bzip) {
+ fatal("Archive is not compressed with bzip2.", 1);
+ }
+ if (gzip) {
+ fatal("Archive is not compressed with gzip.", 1);
+ }
+ fatal("Internal Error: checkCompressed() returned -1", -1);
+ }
+ }
+
+ if ((mode & TAR_COMPRESS) != 0) {
+ if (bzip) {
+ compress = USE_BZIP;
+ } else if (gzip) {
+ compress = USE_GZIP;
+ } else {
+ compress = decompress;
+ }
+ }
+
+ if ((mode & TAR_VERIFY) != 0 && verify) {
+ // backup original archive
+ }
+
+ if ((mode & TAR_CREATE) != 0 && compress == 0) {
+ compress = checkSuffix(archive);
+ }
+
if ((mode & TAR_INSERT) != 0) {
- File[] files;
- files = processFiles(Paths.getValues(), recurse);
- insert(files);
- return;
+ insert(processFiles(Paths.getValues(), recurse));
}
+
+ if ((mode & TAR_UPDATE) != 0) {
+ update(processFiles(Paths.getValues(), recurse));
+ }
+
+ if ((mode & TAR_CONCAT) != 0) {
+ concat(processArchives(Paths.getValues()));
+ }
+
+ if ((mode & TAR_DELETE) != 0) {
+ //delete();
+ }
+
if ((mode & TAR_EXTRACT) != 0) {
+ if (decompress == 0 && archive == null) {
+ if (bzip) {
+ decompress = USE_BZIP;
+ } else if (gzip) {
+ decompress = USE_GZIP;
+ }
+ }
extract();
}
+
if ((mode & TAR_LIST) != 0) {
list();
}
+
+ if ((mode & TAR_DIFF) != 0) {
+ diff();
+ }
} catch (Exception e) {
e.printStackTrace();
fatal(err_exception_uncaught, 1);
}
}
+ /**
+ * Concatenates a list of archives with this archive.
+ *
+ * TODO If the verify option is set, the original archive is backed up before operating
+ * on it, and verified before exiting. If the archive is bad, the original is restored.
+ */
+ private void concat(File[] archives) throws IOException {
+ InputStream in;
+ OutputStream out;
+ TarInputStream tin;
+ TarOutputStream tout;
+ File tmpArchive;
+
+ // Setup archive for appending
+ tout = appendTarOutputStream();
+
+ // Concatenate new archives
+ for(File arch : archives) {
+ if ((in = openFileRead(arch)) == null) {
+ continue;
+ }
+ bzip = gzip = false;
+ decompress = checkCompressed(arch);
+ if (decompress != 0) {
+ in = wrapInputStream(in);
+ }
+ tin = new TarInputStream(in);
+ copy(tin, tout);
+ }
+ tout.close();
+ }
+
+ /**
+ * Insert a list of files into an archive.
+ *
+ * This is used by Create and Append to insert new entries into the archive.
+ *
+ * TODO Allow files to be delete from the filesystem after the archive has been written.
+ * If the verify option is set, then the archive must pass verification before the
+ * files are deleted.
+ *
+ * TODO If the verify option is set, the original archive is backed up before operating
+ * on it, and verified before exiting. If the archive is bad, the original is restored.
+ */
private void insert(File[] files) throws IOException {
- debug("insert");
InputStream in;
OutputStream out;
- TarOutputStream tout;
+ TarOutputStream tout = null;
TarEntry entry;
+ File tmpArchive = null;
- if (!use_stdout) {
- if (mode == TAR_CREATE || (mode == TAR_APPEND && !archive.exists())) createArchive();
-
+ if (mode == TAR_APPEND && archive.exists()) {
+ tout = appendTarOutputStream();
+ } else {
+ createArchive();
if ((out = openFileWrite(archive, false, false)) == null) {
- fatal(err_stream_create + archive, 1);
+ fatal(" ", 1);
}
- } else {
- debug("out=stdout");
- out = getOutput().getOutputStream();
+ if (compress != 0) {
+ out = wrapOutputStream(out);
+ }
+ tout = new TarOutputStream(out);
}
- tout = new TarOutputStream(out);
- debug("begin");
+ // Insert new entries
for (File file : files) {
- notice(file.getName());
+ notice(file.getPath());
entry = new TarEntry(file);
tout.putNextEntry(entry);
@@ -231,9 +488,56 @@
tout.closeEntry();
}
tout.close();
- debug("end");
}
+ // TODO
+ private void update(File[] files) throws IOException {
+ InputStream in;
+ TarInputStream tin;
+ TarEntry entry;
+ TreeMap<String,Long> entries = new TreeMap();
+
+ if ((in = openFileRead(archive)) == null) {
+ fatal(" ", 1);
+ }
+ if (decompress != 0) {
+ in = wrapInputStream(in);
+ }
+
+ tin = new TarInputStream(in);
+
+ while ((entry = tin.getNextEntry()) != null) {
+ entries.put(entry.getName(), entry.getModTime().getTime());
+ }
+ tin.close();
+
+ long etime, ftime;
+ ArrayList<File> list = new ArrayList<File>();
+ for (File file : files) {
+ if (entries.containsKey(file.getPath())) {
+ etime = entries.get(file.getPath()).longValue();
+ ftime = file.lastModified();
+ if (etime >= ftime) {
+ continue;
+ }
+ }
+ list.add(file);
+ }
+
+ insert(list.toArray(files));
+ }
+
+ // TODO
+ private void delete(String[] names) throws IOException {
+ }
+
+ /**
+ * Extract entries from an archive.
+ *
+ * TODO Need to parse Path for choosing specific files/directories either by direct naming
+ * or by wildcard patterns.
+ * TODO Read list of entries to extract from FileList if its set.
+ */
private void extract() throws IOException {
TarEntry entry;
InputStream in = null;
@@ -241,24 +545,23 @@
TarInputStream tin;
File file;
- // add support for reading compress archives with gzip/bzip
-
if (archive != null) {
if ((in = openFileRead(archive)) == null) {
- exit(1);
+ fatal(" ", 1);
}
} else {
- in = getInput().getInputStream();
+ in = stdin;
}
+ if (decompress != 0) {
+ in = wrapInputStream(in);
+ }
tin = new TarInputStream(in);
if (use_stdout) {
- debug("out=stdout");
- out = getOutput().getOutputStream();
+ out = stdout;
}
- debug("begin");
while ((entry = tin.getNextEntry()) != null) {
notice(entry.getName());
file = new File(entry.getName());
@@ -267,26 +570,43 @@
file.mkdirs();
}
continue;
+ } else {
+ if (file.exists()) {
+ if (keepOld || (keepNew && (file.lastModified() >= entry.getModTime().getTime()))) {
+ continue;
+ }
+ if (backup) {
+ file.renameTo(new File(file.getPath() + suffix));
+ }
+ }
}
- if ((out = openFileWrite(file, true, false)) == null) {
+ if ((out = openFileWrite(file, true, true)) == null) {
continue;
}
tin.copyEntryContents(out);
out.close();
}
tin.close();
- debug("end");
}
+ /**
+ * List the contents of an archive.
+ *
+ * TODO Need to parse Path for choosing specific files/directories either by direct naming
+ * or by wildcard patterns.
+ */
private void list() throws IOException {
TarEntry entry;
InputStream in = null;
TarInputStream tin;
if ((in = openFileRead(archive)) == null) {
- exit(1);
+ fatal(" ", 1);
}
+ if (decompress != 0) {
+ in = wrapInputStream(in);
+ }
tin = new TarInputStream(in);
while ((entry = tin.getNextEntry()) != null) {
@@ -294,6 +614,9 @@
}
}
+ /**
+ * Outputs the differences found between the archive and the file system.
+ */
private void diff() throws IOException {
TarEntry entry;
InputStream in = null;
@@ -304,6 +627,9 @@
exit(1);
}
+ if (decompress != 0) {
+ in = wrapInputStream(in);
+ }
tin = new TarInputStream(in);
while ((entry = tin.getNextEntry()) != null) {
@@ -311,29 +637,98 @@
if (!file.exists()) {
out(file + ": Warning: No such file or directory");
+ continue;
}
if (file.lastModified() != entry.getModTime().getTime()) {
- out(file + ": Mod time differs");
+ out(file + ": Mod time is different");
}
if (file.length() != entry.getSize()) {
- out(file + ": Size differs");
+ out(file + ": Size is different");
}
+
+ // TODO check file mode
+ // TODO check file ownership
}
}
+ /**
+ * Copies an archive to another archive.
+ *
+ * This is used to set an output stream into position for appending new entries,
+ * and to copy entries from another archive into another archive.
+ *
+ * FIXME does not verify that tin is actually a tar archive (Concat)
+ */
+ private void copy(TarInputStream tin, TarOutputStream tout) throws IOException {
+ TarEntry entry;
+ while ((entry = tin.getNextEntry()) != null) {
+ tout.putNextEntry(entry);
+ tin.copyEntryContents(tout);
+ tout.closeEntry();
+ }
+ tin.close();
+ }
+
+ /**
+ * Sets up a TarOutputStream suitable for appending new entries.
+ */
+ private TarOutputStream appendTarOutputStream() throws IOException {
+ OutputStream out;
+ InputStream in;
+ TarOutputStream tout;
+ TarInputStream tin;
+ File tmpArchive;
+
+ tmpArchive = archive.getAbsoluteFile();
+ tmpArchive.renameTo(new File(archive.getName() + ".tmp"));
+
+ createArchive();
+
+ if ((out = openFileWrite(archive, false, false)) == null) {
+ fatal(" ", 1);
+ }
+ if (compress != 0) {
+ out = wrapOutputStream(out);
+ }
+ tout = new TarOutputStream(out);
+
+ if ((in = openFileRead(tmpArchive)) == null) {
+ fatal(" ", 1);
+ }
+ if (decompress != 0) {
+ in = wrapInputStream(in);
+ }
+ tin = new TarInputStream(in);
+ copy(tin, tout);
+ tmpArchive.delete();
+
+ return tout;
+ }
+
+ /**
+ * Creates a file for an archive, deleting it first if it already exists.
+ */
private void createArchive() {
try {
if (archive.exists()) {
archive.delete();
}
- archive.createNewFile();
+ if (!archive.createNewFile()) {
+ throw new IOException();
+ }
} catch (IOException e) {
fatal(err_file_create + archive, 1);
}
}
+ /**
+ * Processes a list of files and directories given on the command line
+ * in order to generate a list of files for Append, Create and Update.
+ *
+ * TODO Add parsing of FileList and exclusion filtering.
+ */
private File[] processFiles(File[] files , boolean recurse) {
// FIXME object pollution
ArrayList<File> _files = new ArrayList<File>();
@@ -359,16 +754,128 @@
return _files.toArray(files);
}
+ // TODO Need to check that the list of files are actually archives or compressed archives.
+ private File[] processArchives(File[] files) {
+ return files;
+ }
+
+ /**
+ * Wraps an InputStream with a decompression stream for reading compressed archives.
+ */
+ private InputStream wrapInputStream( InputStream in ) throws IOException {
+ if (decompress == USE_BZIP) {
+ return new CBZip2InputStream(in);
+ }
+ if (decompress == USE_GZIP) {
+ return new GZIPInputStream(in, BUFFER_SIZE);
+ }
+
+ fatal("Internal Error: Unknown compress type", -1);
+ return null;
+ }
+
+ /**
+ * Wraps an OutputStream with a compression stream for writing compressed archives.
+ */
+ private OutputStream wrapOutputStream( OutputStream out ) throws IOException {
+ if (compress == USE_BZIP) {
+ return new CBZip2OutputStream(out);
+ }
+ if (compress == USE_GZIP) {
+ return new GZIPOutputStream(out);
+ }
+
+ fatal("Internal Error: Unknown decompress type", -1);
+ return null;
+ }
+
+ /**
+ * Used by create to determine if the archive should be compressed even if the -j or -z flags
+ * were not given.
+ */
+ private int checkSuffix(File file) {
+ String name = file.getName();
+ if (name.endsWith(".tbz") || name.endsWith(".tbz2") || name.endsWith(".tar.bz") || name.endsWith(".tar.bz2")) {
+ return USE_BZIP;
+ }
+ if (name.endsWith(".tar.gz")) {
+ return USE_GZIP;
+ }
+ return 0;
+ }
+
+ /**
+ * Check via the file header the type of compression used to create it.
+ */
+ private int checkCompressed(File file) throws IOException {
+ if (bzip) {
+ return checkBZipMagic(file) ? USE_BZIP : -1;
+ }
+ if (gzip) {
+ return checkGZipMagic(file) ? USE_GZIP : -1;
+ }
+ /*
+ if (checkBZipMagic(file)) {
+ return USE_BZIP;
+ }
+ if (checkGZipMagic(file)) {
+ return USE_GZIP;
+ }
+ */
+ return 0;
+ }
+
+ // TODO
+ private boolean checkBZipMagic(File file) throws IOException {
+ return true;
+ }
+
+ // TODO
+ private boolean checkGZipMagic(File file) throws IOException {
+ return true;
+ }
+
+ /**
+ * Checks which operational mode was selected.
+ *
+ * If no mode was selected, or more than one mode was selected, the return
+ * value will be false, otherwise the return value is true, and this.mode will
+ * be set with the selected mode.
+ */
private boolean checkMode() {
int check = 0;
- if (DoAppend.isSet()) check++;
- if (DoCreate.isSet()) check++;
- if (DoConcat.isSet()) check++;
- if (DoDelete.isSet()) check++;
- if (DoDiff.isSet()) check++;
- if (DoExtract.isSet()) check++;
- if (DoList.isSet()) check++;
- if (DoUpdate.isSet()) check++;
+ if (DoAppend.isSet()) {
+ mode = TAR_APPEND;
+ check++;
+ }
+ if (DoCreate.isSet()) {
+ mode = TAR_CREATE;
+ check++;
+ }
+ if (DoConcat.isSet()) {
+ mode = TAR_CONCAT;
+ check++;
+ }
+ if (DoDelete.isSet()) {
+ mode = TAR_DELETE;
+ check++;
+ }
+ if (DoDiff.isSet()) {
+ mode = TAR_DIFF;
+ check++;
+ }
+ if (DoExtract.isSet()) {
+ mode = TAR_EXTRACT;
+ check++;
+ }
+ if (DoList.isSet()) {
+ mode = TAR_LIST;
+ check++;
+ }
+ if (DoUpdate.isSet()) {
+ mode = TAR_UPDATE;
+ check++;
+ }
return check == 1;
}
}
Modified: trunk/fs/src/fs/org/jnode/fs/command/archive/UnzipCommand.java
===================================================================
--- trunk/fs/src/fs/org/jnode/fs/command/archive/UnzipCommand.java 2009-04-02 19:01:01 UTC (rev 5210)
+++ trunk/fs/src/fs/org/jnode/fs/command/archive/UnzipCommand.java 2009-04-03 02:05:01 UTC (rev 5211)
@@ -37,6 +37,6 @@
}
public void execute() {
- //setup();
+ super.execute();
}
}
Modified: trunk/fs/src/fs/org/jnode/fs/command/archive/Zip.java
===================================================================
--- trunk/fs/src/fs/org/jnode/fs/command/archive/Zip.java 2009-04-02 19:01:01 UTC (rev 5210)
+++ trunk/fs/src/fs/org/jnode/fs/command/archive/Zip.java 2009-04-03 02:05:01 UTC (rev 5211)
@@ -36,23 +36,6 @@
public class Zip extends ArchiveCommand {
- private static final int BUFFER_SIZE = 4096;
-
- private static final int V_DEBUG = 10;
- private static final int V_NOTICE = 1;
- private static final int V_WARN = 0;
- private static final int V_ERROR = -1;
-
- /* ZIP_DELETE searches the archive for matching entries and removes them
- * ZIP_EXTRACT dumps the contents of the zip archive to the file system
- * ZIP_ADD searches the filesystem for matching filenames and adds them to the archive
- * ZIP_MOVE searches the filesystem for matching filesnames and adds them to the archive, deleting the original
- * files when the archive is created.
- * ZIP_REFRESH searches the archive for matching entries, then searches the filesystem for files that match
- * the entries that have a more recent mod time. If there are any they replace that entry in the archive.
- * ZIP_UPDATE searches the filesystem for matching filesnames, adds them if they dont already exist, if they do
- * exist, replace only if the filesystem mod time is more recent
- */
private static final int ZIP_DELETE = 1;
private static final int ZIP_REFRESH = 2;
private static final int ZIP_MOVE = 3;
@@ -60,98 +43,19 @@
private static final int ZIP_UPDATE = 5;
private static final int ZIP_EXTRACT = 6;
- private File zfile;
- private File tmpFile;
- private ZipOutputStream zout;
+ private File archive;
private int mode;
- private int verbosity = 0;
public Zip(String s) {
super(s);
}
- /*
- Zip( File zfile , CommandInput stdin , CommandOutput stdout , CommandOutput stderr ) {
- inr = stdin.getReader();
- outw = stdout.getPrintWriter();
- errw = stderr.getPrintWriter();
- this.zfile = zfile;
- }
- void add( String[] files ) throws IOException {
- setupOutputStream();
- // if the zip file already exists we have to copy the entries into the out stream, unless
- // one of the given files matches an entry. If so, then either ask the user to overwrite
- // or if this is an update operation, then check the mod times.
-
- File file;
- ZipEntry entry;
- InputStream in;
-
- for(String name : files) {
- file = new File(name);
- if(!file.exists()) {
- error(file+" does not exist.");
- continue;
- }
- if((in = new FileInputStream(file)) == null) {
- error("Could not open stream");
- continue;
- }
-
- entry = new ZipEntry(name);
- zout.putNextEntry(entry);
- processStream(in,zout);
- zout.closeEntry();
- }
- zout.finish();
- zout.close();
+ public void execute() {
+ super.execute();
}
void extract() throws IOException {
- if(!zfile.exists()) {
- error("Cannot find file: "+zfile);
- return;
- }
- ZipFile archive = new ZipFile(zfile);
- Enumeration<? extends ZipEntry> entries = archive.entries();
- ZipEntry entry;
- File file;
- InputStream in;
- OutputStream out;
-
- while(entries.hasMoreElements()) {
- entry = entries.nextElement();
- file = new File(entry.getName());
- if(file.exists()) {
- if(prompt_yn(file+" exists. Overwrite? ")) {
- file.delete();
- } else {
- continue;
- }
- }
- file.createNewFile();
- out = new FileOutputStream(file);
- in = archive.getInputStream(entry);
- if(in == null || out == null) {
- error("Could not open streams");
- continue;
- }
- processStream(in,out);
- in.close();
- out.close();
- }
}
-
- void close() throws IOException {
- zout.close();
- }
-
- private void setupOutputStream() throws IOException {
- tmpFile = new File(zfile.getName()+".tmp");
- tmpFile.createNewFile();
- zout = new ZipOutputStream( new FileOutputStream(tmpFile) );
- }
- */
}
Modified: trunk/fs/src/fs/org/jnode/fs/command/archive/ZipCommand.java
===================================================================
--- trunk/fs/src/fs/org/jnode/fs/command/archive/ZipCommand.java 2009-04-02 19:01:01 UTC (rev 5210)
+++ trunk/fs/src/fs/org/jnode/fs/command/archive/ZipCommand.java 2009-04-03 02:05:01 UTC (rev 5211)
@@ -52,6 +52,6 @@
}
public void execute() {
- //setup();
+ super.execute();
}
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|