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. |