You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ant.apache.org by st...@locus.apache.org on 2000/02/10 19:04:30 UTC
cvs commit: jakarta-ant/src/main/org/apache/tools/tar TarBuffer.java TarConstants.java TarEntry.java TarInputStream.java TarOutputStream.java TarUtils.java
stefano 00/02/10 10:04:30
Modified: . bootstrap.bat bootstrap.sh build.xml
src/main/org/apache/tools/ant/taskdefs Zip.java
defaults.properties
Added: src/main/org/apache/tools/ant/taskdefs Tar.java
src/main/org/apache/tools/tar TarBuffer.java
TarConstants.java TarEntry.java TarInputStream.java
TarOutputStream.java TarUtils.java
Log:
added Tar task + implementation classes
Revision Changes Path
1.5 +9 -5 jakarta-ant/bootstrap.bat
Index: bootstrap.bat
===================================================================
RCS file: /home/cvs/jakarta-ant/bootstrap.bat,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- bootstrap.bat 2000/02/08 03:43:09 1.4
+++ bootstrap.bat 2000/02/10 18:04:28 1.5
@@ -2,7 +2,7 @@
echo BOOTSTRAPPING ANT DISTRIBUTION
set C=%CLASSPATH%;lib/xml.jar
-set SRCDIR=src\main\org\apache\tools\ant
+set SRCDIR=src\main\org\apache\tools
set TMPDIR=tmp
if "%OS%" == "Windows_NT" goto nt
@@ -18,14 +18,18 @@
echo ** COMPILING ANT CLASSES
-rem Compile the classes into the temp directory
-javac -classpath "%C%" -d %TMPDIR% %SRCDIR%\*.java
-
rem Reset classpath to include base ant class files
set C=%TMPDIR%;%C%
rem Compile sub classes into the temp directory
-javac -classpath "%C%" -d %TMPDIR% %SRCDIR%\taskdefs\*.java
+javac -classpath "%C%" -d %TMPDIR% %SRCDIR%\tar\*.java
+
+rem Compile the classes into the temp directory
+javac -classpath "%C%" -d %TMPDIR% %SRCDIR%\ant\*.java
+
+rem Compile sub classes into the temp directory
+javac -classpath "%C%" -d %TMPDIR% %SRCDIR%\ant\taskdefs\*.java
+
echo ** COPYING REQUIRED FILES
1.8 +4 -3 jakarta-ant/bootstrap.sh
Index: bootstrap.sh
===================================================================
RCS file: /home/cvs/jakarta-ant/bootstrap.sh,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- bootstrap.sh 2000/02/08 23:45:48 1.7
+++ bootstrap.sh 2000/02/10 18:04:28 1.8
@@ -2,7 +2,7 @@
. $HOME/.antrc
fi
-SRCDIR=src/main/org/apache/tools/ant
+SRCDIR=src/main/org/apache/tools
CLASSDIR=classes
CLASSPATH=${CLASSPATH}:${JAVA_HOME}/lib/classes.zip:${JAVA_HOME}/lib/tools.jar
CLASSPATH=${CLASSPATH}:lib/xml.jar:src/main:${CLASSDIR}
@@ -12,8 +12,9 @@
export CLASSPATH
echo $CLASSPATH
-javac -d ${CLASSDIR} ${SRCDIR}/*.java
-javac -d ${CLASSDIR} ${SRCDIR}/taskdefs/*.java
+javac -d ${CLASSDIR} ${SRCDIR}/tar/*.java
+javac -d ${CLASSDIR} ${SRCDIR}/ant/*.java
+javac -d ${CLASSDIR} ${SRCDIR}/ant/taskdefs/*.java
cp src/main/org/apache/tools/ant/taskdefs/defaults.properties ${CLASSDIR}/org/apache/tools/ant/taskdefs
cp src/main/org/apache/tools/ant/parser.properties ${CLASSDIR}/org/apache/tools/ant
1.8 +18 -5 jakarta-ant/build.xml
Index: build.xml
===================================================================
RCS file: /home/cvs/jakarta-ant/build.xml,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- build.xml 2000/02/06 21:29:23 1.7
+++ build.xml 2000/02/10 18:04:28 1.8
@@ -7,6 +7,7 @@
<project name="Ant" default="main" basedir=".">
<target name="init">
+ <property name="Name" value="Ant"/>
<property name="name" value="ant"/>
<property name="version" value="1.0-rc1"/>
@@ -22,7 +23,7 @@
<property name="dist.dir" value="../dist/ant"/>
<property name="classpath" value="lib/xml.jar"/>
- <property name="packages" value="org.apache.tools.ant.*"/>
+ <property name="packages" value="org.apache.tools.*"/>
<property name="manifest" value="src/etc/manifest"/>
<property name="build.compiler" value="classic"/>
@@ -79,9 +80,8 @@
destdir="${build.javadocs}"
author="true"
version="true"
- use="true"
- windowtitle="${name} API"
- doctitle="${name}"
+ windowtitle="${Name} API"
+ doctitle="${Name}"
bottom="Copyright © 2000 Apache Software Foundation. All Rights Reserved."
/>
</target>
@@ -111,10 +111,23 @@
<copyfile src="README" dest="${dist.dir}/README"/>
<copyfile src="TODO" dest="${dist.dir}/TODO"/>
<copyfile src="LICENSE" dest="${dist.dir}/LICENSE"/>
+ </target>
- <jar jarfile="${name}.jar" basedir="${dist.dir}" includes="**"/>
+ <!-- =================================================================== -->
+ <!-- Packages the distribution with ZIP -->
+ <!-- =================================================================== -->
+ <target name="dist-zip" depends="dist">
+ <zip zipfile="${Name}-${version}.zip" basedir="${dist.dir}" includes="**"/>
</target>
+ <!-- =================================================================== -->
+ <!-- Packages the distribution with TAR-GZIP -->
+ <!-- =================================================================== -->
+ <target name="dist-tgz" depends="dist">
+ <tar tarfile="${Name}-${version}.tar" basedir="${dist.dir}" includes="**"/>
+ <gzip zipfile="${Name}-${version}.tar.gz" src="${Name}-${version}.tar"/>
+ </target>
+
<!-- =================================================================== -->
<!-- Cleans up generated stuff -->
<!-- =================================================================== -->
1.4 +7 -6 jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Zip.java
Index: Zip.java
===================================================================
RCS file: /home/cvs/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Zip.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- Zip.java 2000/02/09 20:51:48 1.3
+++ Zip.java 2000/02/10 18:04:29 1.4
@@ -76,16 +76,17 @@
protected String archiveType = "zip";
/**
- This is the name/location of where to
- create the .zip file.
- */
+ * This is the name/location of where to
+ * create the .zip file.
+ */
public void setZipfile(String zipFilename) {
zipFile = project.resolveFile(zipFilename);
}
+
/**
- This is the base directory to look in for
- things to zip.
- */
+ * This is the base directory to look in for
+ * things to zip.
+ */
public void setBasedir(String baseDirname) {
baseDir = project.resolveFile(baseDirname);
}
1.4 +1 -0 jakarta-ant/src/main/org/apache/tools/ant/taskdefs/defaults.properties
Index: defaults.properties
===================================================================
RCS file: /home/cvs/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/defaults.properties,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- defaults.properties 2000/01/27 03:51:14 1.3
+++ defaults.properties 2000/02/10 18:04:29 1.4
@@ -22,5 +22,6 @@
taskdef=org.apache.tools.ant.taskdefs.Taskdef
ant=org.apache.tools.ant.taskdefs.Ant
exec=org.apache.tools.ant.taskdefs.Exec
+tar=org.apache.tools.ant.taskdefs.Tar
# remove the task below once everyone has migrated
javadoc2=org.apache.tools.ant.taskdefs.Javadoc
1.1 jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Tar.java
Index: Tar.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.tools.ant.taskdefs;
import java.io.*;
import org.apache.tools.ant.*;
import org.apache.tools.tar.*;
/**
* Creates a TAR archive.
*
* @author Stefano Mazzocchi <a href="mailto:stefano@apache.org">stefano@apache.org</a>
*/
public class Tar extends MatchingTask {
File tarFile;
File baseDir;
/**
* This is the name/location of where to create the tar file.
*/
public void setTarfile(String tarFilename) {
tarFile = project.resolveFile(tarFilename);
}
/**
* This is the base directory to look in for things to tar.
*/
public void setBasedir(String baseDirname) {
baseDir = project.resolveFile(baseDirname);
}
public void execute() throws BuildException {
project.log("Building tar: "+ tarFile.getAbsolutePath());
if (baseDir == null) {
throw new BuildException("basedir attribute must be set!");
}
if (!baseDir.exists()) {
throw new BuildException("basedir does not exist!");
}
DirectoryScanner ds = super.getDirectoryScanner(baseDir);
String[] files = ds.getIncludedFiles();
try {
TarOutputStream tOut = new TarOutputStream(new FileOutputStream(tarFile));
tOut.setDebug(true);
for (int i = 0; i < files.length; i++) {
File f = new File(baseDir,files[i]);
String name = files[i].replace(File.separatorChar,'/');
tarFile(f, tOut, name);
}
// close up
tOut.close();
} catch (IOException ioe) {
String msg = "Problem creating TAR: " + ioe.getMessage();
throw new BuildException(msg);
}
}
protected void tarFile(File file, TarOutputStream tOut, String vPath)
throws IOException
{
FileInputStream fIn = new FileInputStream(file);
TarEntry te = new TarEntry(vPath);
te.setSize(file.length());
te.setModTime(file.lastModified() / 1000);
tOut.putNextEntry(te);
byte[] buffer = new byte[8 * 1024];
int count = 0;
do {
tOut.write(buffer, 0, count);
count = fIn.read(buffer, 0, buffer.length);
} while (count != -1);
tOut.closeEntry();
fIn.close();
}
}
1.1 jakarta-ant/src/main/org/apache/tools/tar/TarBuffer.java
Index: TarBuffer.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
/*
* This package is based on the work done by Timothy Gerard Endres
* (time@ice.com) to whom the Ant project is very grateful for his great code.
*/
package org.apache.tools.tar;
import java.io.*;
/**
* The TarBuffer class implements the tar archive concept
* of a buffered input stream. This concept goes back to the
* days of blocked tape drives and special io devices. In the
* Java universe, the only real function that this class
* performs is to ensure that files have the correct "block"
* size, or other tars will complain.
* <p>
* You should never have a need to access this class directly.
* TarBuffers are created by Tar IO Streams.
*
* @author Timothy Gerard Endres <a href="mailto:time@ice.com">time@ice.com</a>
*/
public class TarBuffer {
public static final int DEFAULT_RCDSIZE = (512);
public static final int DEFAULT_BLKSIZE = (DEFAULT_RCDSIZE * 20);
private InputStream inStream;
private OutputStream outStream;
private byte[] blockBuffer;
private int currBlkIdx;
private int currRecIdx;
private int blockSize;
private int recordSize;
private int recsPerBlock;
private boolean debug;
public TarBuffer(InputStream inStream) {
this(inStream, TarBuffer.DEFAULT_BLKSIZE);
}
public TarBuffer(InputStream inStream, int blockSize) {
this(inStream, blockSize, TarBuffer.DEFAULT_RCDSIZE);
}
public TarBuffer(InputStream inStream, int blockSize, int recordSize) {
this.inStream = inStream;
this.outStream = null;
this.initialize(blockSize, recordSize);
}
public TarBuffer(OutputStream outStream) {
this(outStream, TarBuffer.DEFAULT_BLKSIZE);
}
public TarBuffer(OutputStream outStream, int blockSize) {
this(outStream, blockSize, TarBuffer.DEFAULT_RCDSIZE);
}
public TarBuffer(OutputStream outStream, int blockSize, int recordSize) {
this.inStream = null;
this.outStream = outStream;
this.initialize(blockSize, recordSize);
}
/**
* Initialization common to all constructors.
*/
private void initialize(int blockSize, int recordSize) {
this.debug = false;
this.blockSize = blockSize;
this.recordSize = recordSize;
this.recsPerBlock = (this.blockSize / this.recordSize);
this.blockBuffer = new byte[this.blockSize];
if (this.inStream != null) {
this.currBlkIdx = -1;
this.currRecIdx = this.recsPerBlock;
} else {
this.currBlkIdx = 0;
this.currRecIdx = 0;
}
}
/**
* Get the TAR Buffer's block size. Blocks consist of multiple records.
*/
public int getBlockSize() {
return this.blockSize;
}
/**
* Get the TAR Buffer's record size.
*/
public int getRecordSize() {
return this.recordSize;
}
/**
* Set the debugging flag for the buffer.
*
* @param debug If true, print debugging output.
*/
public void setDebug(boolean debug) {
this.debug = debug;
}
/**
* Determine if an archive record indicate End of Archive. End of
* archive is indicated by a record that consists entirely of null bytes.
*
* @param record The record data to check.
*/
public boolean isEOFRecord(byte[] record) {
for (int i = 0, sz = this.getRecordSize(); i < sz; ++i) {
if (record[i] != 0) {
return false;
}
}
return true;
}
/**
* Skip over a record on the input stream.
*/
public void skipRecord() throws IOException {
if (this.debug) {
System.err.println("SkipRecord: recIdx = " + this.currRecIdx
+ " blkIdx = " + this.currBlkIdx);
}
if (this.inStream == null) {
throw new IOException("reading (via skip) from an output buffer");
}
if (this.currRecIdx >= this.recsPerBlock) {
if (!this.readBlock()) {
return; // UNDONE
}
}
this.currRecIdx++;
}
/**
* Read a record from the input stream and return the data.
*
* @return The record data.
*/
public byte[] readRecord() throws IOException {
if (this.debug) {
System.err.println("ReadRecord: recIdx = " + this.currRecIdx
+ " blkIdx = " + this.currBlkIdx);
}
if (this.inStream == null) {
throw new IOException("reading from an output buffer");
}
if (this.currRecIdx >= this.recsPerBlock) {
if (!this.readBlock()) {
return null;
}
}
byte[] result = new byte[this.recordSize];
System.arraycopy(this.blockBuffer,
(this.currRecIdx * this.recordSize), result, 0,
this.recordSize);
this.currRecIdx++;
return result;
}
/**
* @return false if End-Of-File, else true
*/
private boolean readBlock() throws IOException {
if (this.debug) {
System.err.println("ReadBlock: blkIdx = " + this.currBlkIdx);
}
if (this.inStream == null) {
throw new IOException("reading from an output buffer");
}
this.currRecIdx = 0;
int offset = 0;
int bytesNeeded = this.blockSize;
while (bytesNeeded > 0) {
long numBytes = this.inStream.read(this.blockBuffer, offset,
bytesNeeded);
//
// NOTE
// We have fit EOF, and the block is not full!
//
// This is a broken archive. It does not follow the standard
// blocking algorithm. However, because we are generous, and
// it requires little effort, we will simply ignore the error
// and continue as if the entire block were read. This does
// not appear to break anything upstream. We used to return
// false in this case.
//
// Thanks to 'Yohann.Roussel@alcatel.fr' for this fix.
//
if (numBytes == -1) {
break;
}
offset += numBytes;
bytesNeeded -= numBytes;
if (numBytes != this.blockSize) {
if (this.debug) {
System.err.println("ReadBlock: INCOMPLETE READ "
+ numBytes + " of " + this.blockSize
+ " bytes read.");
}
}
}
this.currBlkIdx++;
return true;
}
/**
* Get the current block number, zero based.
*
* @return The current zero based block number.
*/
public int getCurrentBlockNum() {
return this.currBlkIdx;
}
/**
* Get the current record number, within the current block, zero based.
* Thus, current offset = (currentBlockNum * recsPerBlk) + currentRecNum.
*
* @return The current zero based record number.
*/
public int getCurrentRecordNum() {
return this.currRecIdx - 1;
}
/**
* Write an archive record to the archive.
*
* @param record The record data to write to the archive.
*/
public void writeRecord(byte[] record) throws IOException {
if (this.debug) {
System.err.println("WriteRecord: recIdx = " + this.currRecIdx
+ " blkIdx = " + this.currBlkIdx);
}
if (this.outStream == null) {
throw new IOException("writing to an input buffer");
}
if (record.length != this.recordSize) {
throw new IOException("record to write has length '"
+ record.length
+ "' which is not the record size of '"
+ this.recordSize + "'");
}
if (this.currRecIdx >= this.recsPerBlock) {
this.writeBlock();
}
System.arraycopy(record, 0, this.blockBuffer,
(this.currRecIdx * this.recordSize),
this.recordSize);
this.currRecIdx++;
}
/**
* Write an archive record to the archive, where the record may be
* inside of a larger array buffer. The buffer must be "offset plus
* record size" long.
*
* @param buf The buffer containing the record data to write.
* @param offset The offset of the record data within buf.
*/
public void writeRecord(byte[] buf, int offset) throws IOException {
if (this.debug) {
System.err.println("WriteRecord: recIdx = " + this.currRecIdx
+ " blkIdx = " + this.currBlkIdx);
}
if (this.outStream == null) {
throw new IOException("writing to an input buffer");
}
if ((offset + this.recordSize) > buf.length) {
throw new IOException("record has length '" + buf.length
+ "' with offset '" + offset
+ "' which is less than the record size of '"
+ this.recordSize + "'");
}
if (this.currRecIdx >= this.recsPerBlock) {
this.writeBlock();
}
System.arraycopy(buf, offset, this.blockBuffer,
(this.currRecIdx * this.recordSize),
this.recordSize);
this.currRecIdx++;
}
/**
* Write a TarBuffer block to the archive.
*/
private void writeBlock() throws IOException {
if (this.debug) {
System.err.println("WriteBlock: blkIdx = " + this.currBlkIdx);
}
if (this.outStream == null) {
throw new IOException("writing to an input buffer");
}
this.outStream.write(this.blockBuffer, 0, this.blockSize);
this.outStream.flush();
this.currRecIdx = 0;
this.currBlkIdx++;
}
/**
* Flush the current data block if it has any data in it.
*/
private void flushBlock() throws IOException {
if (this.debug) {
System.err.println("TarBuffer.flushBlock() called.");
}
if (this.outStream == null) {
throw new IOException("writing to an input buffer");
}
if (this.currRecIdx > 0) {
this.writeBlock();
}
}
/**
* Close the TarBuffer. If this is an output buffer, also flush the
* current block before closing.
*/
public void close() throws IOException {
if (this.debug) {
System.err.println("TarBuffer.closeBuffer().");
}
if (this.outStream != null) {
this.flushBlock();
if (this.outStream != System.out
&& this.outStream != System.err) {
this.outStream.close();
this.outStream = null;
}
} else if (this.inStream != null) {
if (this.inStream != System.in) {
this.inStream.close();
this.inStream = null;
}
}
}
}
1.1 jakarta-ant/src/main/org/apache/tools/tar/TarConstants.java
Index: TarConstants.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
/*
* This package is based on the work done by Timothy Gerard Endres
* (time@ice.com) to whom the Ant project is very grateful for his great code.
*/
package org.apache.tools.tar;
/**
* This interface contains all the definitions used in the package.
*
* @author Timothy Gerard Endres <a href="mailto:time@ice.com">time@ice.com</a>
* @author Stefano Mazzocchi <a href="mailto:stefano@apache.org">stefano@apache.org</a>
*/
public interface TarConstants {
/**
* The length of the name field in a header buffer.
*/
public static final int NAMELEN = 100;
/**
* The length of the mode field in a header buffer.
*/
public static final int MODELEN = 8;
/**
* The length of the user id field in a header buffer.
*/
public static final int UIDLEN = 8;
/**
* The length of the group id field in a header buffer.
*/
public static final int GIDLEN = 8;
/**
* The length of the checksum field in a header buffer.
*/
public static final int CHKSUMLEN = 8;
/**
* The length of the size field in a header buffer.
*/
public static final int SIZELEN = 12;
/**
* The length of the magic field in a header buffer.
*/
public static final int MAGICLEN = 8;
/**
* The length of the modification time field in a header buffer.
*/
public static final int MODTIMELEN = 12;
/**
* The length of the user name field in a header buffer.
*/
public static final int UNAMELEN = 32;
/**
* The length of the group name field in a header buffer.
*/
public static final int GNAMELEN = 32;
/**
* The length of the devices field in a header buffer.
*/
public static final int DEVLEN = 8;
/**
* LF_ constants represent the "link flag" of an entry, or more commonly,
* the "entry type". This is the "old way" of indicating a normal file.
*/
public static final byte LF_OLDNORM = 0;
/**
* Normal file type.
*/
public static final byte LF_NORMAL = (byte) '0';
/**
* Link file type.
*/
public static final byte LF_LINK = (byte) '1';
/**
* Symbolic link file type.
*/
public static final byte LF_SYMLINK = (byte) '2';
/**
* Character device file type.
*/
public static final byte LF_CHR = (byte) '3';
/**
* Block device file type.
*/
public static final byte LF_BLK = (byte) '4';
/**
* Directory file type.
*/
public static final byte LF_DIR = (byte) '5';
/**
* FIFO (pipe) file type.
*/
public static final byte LF_FIFO = (byte) '6';
/**
* Contiguous file type.
*/
public static final byte LF_CONTIG = (byte) '7';
/**
* The magic tag representing a POSIX tar archive.
*/
public static final String TMAGIC = "ustar";
/**
* The magic tag representing a GNU tar archive.
*/
public static final String GNU_TMAGIC = "ustar ";
}
1.1 jakarta-ant/src/main/org/apache/tools/tar/TarEntry.java
Index: TarEntry.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
/*
* This package is based on the work done by Timothy Gerard Endres
* (time@ice.com) to whom the Ant project is very grateful for his great code.
*/
package org.apache.tools.tar;
import java.io.*;
import java.util.*;
/**
* This class represents an entry in a Tar archive. It consists
* of the entry's header, as well as the entry's File. Entries
* can be instantiated in one of three ways, depending on how
* they are to be used.
* <p>
* TarEntries that are created from the header bytes read from
* an archive are instantiated with the TarEntry( byte[] )
* constructor. These entries will be used when extracting from
* or listing the contents of an archive. These entries have their
* header filled in using the header bytes. They also set the File
* to null, since they reference an archive entry not a file.
* <p>
* TarEntries that are created from Files that are to be written
* into an archive are instantiated with the TarEntry( File )
* constructor. These entries have their header filled in using
* the File's information. They also keep a reference to the File
* for convenience when writing entries.
* <p>
* Finally, TarEntries can be constructed from nothing but a name.
* This allows the programmer to construct the entry by hand, for
* instance when only an InputStream is available for writing to
* the archive, and the header information is constructed from
* other information. In this case the header fields are set to
* defaults and the File is set to null.
*
* <p>
* The C structure for a Tar Entry's header is:
* <pre>
* struct header {
* char name[NAMSIZ];
* char mode[8];
* char uid[8];
* char gid[8];
* char size[12];
* char mtime[12];
* char chksum[8];
* char linkflag;
* char linkname[NAMSIZ];
* char magic[8];
* char uname[TUNMLEN];
* char gname[TGNMLEN];
* char devmajor[8];
* char devminor[8];
* } header;
* </pre>
*
* @author Timothy Gerard Endres <a href="mailto:time@ice.com">time@ice.com</a>
* @author Stefano Mazzocchi <a href="mailto:stefano@apache.org">stefano@apache.org</a>
*/
public class TarEntry implements TarConstants {
private StringBuffer name; /** The entry's name. */
private int mode; /** The entry's permission mode. */
private int userId; /** The entry's user id. */
private int groupId; /** The entry's group id. */
private long size; /** The entry's size. */
private long modTime; /** The entry's modification time. */
private int checkSum; /** The entry's checksum. */
private byte linkFlag; /** The entry's link flag. */
private StringBuffer linkName; /** The entry's link name. */
private StringBuffer magic; /** The entry's magic tag. */
private StringBuffer userName; /** The entry's user name. */
private StringBuffer groupName; /** The entry's group name. */
private int devMajor; /** The entry's major device number. */
private int devMinor; /** The entry's minor device number. */
private File file; /** The entry's file reference */
/**
* Construct an empty entry and prepares the header values.
*/
private TarEntry () {
this.magic = new StringBuffer(TMAGIC);
this.name = new StringBuffer();
this.linkName = new StringBuffer();
String user = System.getProperty("user.name", "");
if (user.length() > 31) {
user = user.substring(0, 31);
}
this.userId = 0;
this.groupId = 0;
this.userName = new StringBuffer(user);
this.groupName = new StringBuffer("");
this.file = null;
}
/**
* Construct an entry with only a name. This allows the programmer
* to construct the entry's header "by hand". File is set to null.
*/
public TarEntry(String name) {
this();
boolean isDir = name.endsWith("/");
this.checkSum = 0;
this.devMajor = 0;
this.devMinor = 0;
this.name = new StringBuffer(name);
this.mode = isDir ? 040755 : 0100644;
this.linkFlag = isDir ? LF_DIR : LF_NORMAL;
this.userId = 0;
this.groupId = 0;
this.size = 0;
this.checkSum = 0;
this.modTime = (new Date()).getTime() / 1000;
this.linkName = new StringBuffer("");
this.userName = new StringBuffer("");
this.groupName = new StringBuffer("");
this.devMajor = 0;
this.devMinor = 0;
}
/**
* Construct an entry for a file. File is set to file, and the
* header is constructed from information from the file.
*
* @param file The file that the entry represents.
*/
public TarEntry(File file) {
this();
this.file = file;
String name = file.getPath();
String osname = System.getProperty("os.name");
if (osname != null) {
// Strip off drive letters!
// REVIEW Would a better check be "(File.separator == '\')"?
String Win32Prefix = "Windows";
String prefix = osname.substring(0, Win32Prefix.length());
if (prefix.equalsIgnoreCase(Win32Prefix)) {
if (name.length() > 2) {
char ch1 = name.charAt(0);
char ch2 = name.charAt(1);
if (ch2 == ':'
&& ((ch1 >= 'a' && ch1 <= 'z')
|| (ch1 >= 'A' && ch1 <= 'Z'))) {
name = name.substring(2);
}
}
}
}
name = name.replace(File.separatorChar, '/');
// No absolute pathnames
// Windows (and Posix?) paths can start with "\\NetworkDrive\",
// so we loop on starting /'s.
while (name.startsWith("/")) {
name = name.substring(1);
}
this.linkName = new StringBuffer("");
this.name = new StringBuffer(name);
if (file.isDirectory()) {
this.mode = 040755;
this.linkFlag = LF_DIR;
if (this.name.charAt(this.name.length() - 1) != '/') {
this.name.append("/");
}
} else {
this.mode = 0100644;
this.linkFlag = LF_NORMAL;
}
if (this.name.length() > NAMELEN) {
throw new RuntimeException("file name '" + this.name
+ "' is too long ( > "
+ NAMELEN + " bytes)");
// UNDONE When File lets us get the userName, use it!
}
this.size = file.length();
this.modTime = file.lastModified() / 1000;
this.checkSum = 0;
this.devMajor = 0;
this.devMinor = 0;
}
/**
* Construct an entry from an archive's header bytes. File is set
* to null.
*
* @param headerBuf The header bytes from a tar archive entry.
*/
public TarEntry(byte[] headerBuf) {
this();
this.parseTarHeader(headerBuf);
}
/**
* Determine if the two entries are equal. Equality is determined
* by the header names being equal.
*
* @return it Entry to be checked for equality.
* @return True if the entries are equal.
*/
public boolean equals(TarEntry it) {
return this.getName().equals(it.getName());
}
/**
* Determine if the given entry is a descendant of this entry.
* Descendancy is determined by the name of the descendant
* starting with this entry's name.
*
* @param desc Entry to be checked as a descendent of this.
* @return True if entry is a descendant of this.
*/
public boolean isDescendent(TarEntry desc) {
return desc.getName().startsWith(this.getName());
}
/**
* Get this entry's name.
*
* @return This entry's name.
*/
public String getName() {
return this.name.toString();
}
/**
* Set this entry's name.
*
* @param name This entry's new name.
*/
public void setName(String name) {
this.name = new StringBuffer(name);
}
/**
* Get this entry's user id.
*
* @return This entry's user id.
*/
public int getUserId() {
return this.userId;
}
/**
* Set this entry's user id.
*
* @param userId This entry's new user id.
*/
public void setUserId(int userId) {
this.userId = userId;
}
/**
* Get this entry's group id.
*
* @return This entry's group id.
*/
public int getGroupId() {
return this.groupId;
}
/**
* Set this entry's group id.
*
* @param groupId This entry's new group id.
*/
public void setGroupId(int groupId) {
this.groupId = groupId;
}
/**
* Get this entry's user name.
*
* @return This entry's user name.
*/
public String getUserName() {
return this.userName.toString();
}
/**
* Set this entry's user name.
*
* @param userName This entry's new user name.
*/
public void setUserName(String userName) {
this.userName = new StringBuffer(userName);
}
/**
* Get this entry's group name.
*
* @return This entry's group name.
*/
public String getGroupName() {
return this.groupName.toString();
}
/**
* Set this entry's group name.
*
* @param groupName This entry's new group name.
*/
public void setGroupName(String groupName) {
this.groupName = new StringBuffer(groupName);
}
/**
* Convenience method to set this entry's group and user ids.
*
* @param userId This entry's new user id.
* @param groupId This entry's new group id.
*/
public void setIds(int userId, int groupId) {
this.setUserId(userId);
this.setGroupId(groupId);
}
/**
* Convenience method to set this entry's group and user names.
*
* @param userName This entry's new user name.
* @param groupName This entry's new group name.
*/
public void setNames(String userName, String groupName) {
this.setUserName(userName);
this.setGroupName(groupName);
}
/**
* Set this entry's modification time. The parameter passed
* to this method is in "Java time".
*
* @param time This entry's new modification time.
*/
public void setModTime(long time) {
this.modTime = time / 1000;
}
/**
* Set this entry's modification time.
*
* @param time This entry's new modification time.
*/
public void setModTime(Date time) {
this.modTime = time.getTime() / 1000;
}
/**
* Set this entry's modification time.
*
* @param time This entry's new modification time.
*/
public Date getModTime() {
return new Date(this.modTime * 1000);
}
/**
* Get this entry's file.
*
* @return This entry's file.
*/
public File getFile() {
return this.file;
}
/**
* Get this entry's file size.
*
* @return This entry's file size.
*/
public long getSize() {
return this.size;
}
/**
* Set this entry's file size.
*
* @param size This entry's new file size.
*/
public void setSize(long size) {
this.size = size;
}
/**
* Return whether or not this entry represents a directory.
*
* @return True if this entry is a directory.
*/
public boolean isDirectory() {
if (this.file != null) {
return this.file.isDirectory();
}
if (this.linkFlag == LF_DIR) {
return true;
}
if (this.getName().endsWith("/")) {
return true;
}
return false;
}
/**
* If this entry represents a file, and the file is a directory, return
* an array of TarEntries for this entry's children.
*
* @return An array of TarEntry's for this entry's children.
*/
public TarEntry[] getDirectoryEntries() {
if (this.file == null ||!this.file.isDirectory()) {
return new TarEntry[0];
}
String[] list = this.file.list();
TarEntry[] result = new TarEntry[list.length];
for (int i = 0; i < list.length; ++i) {
result[i] = new TarEntry(new File(this.file, list[i]));
}
return result;
}
/**
* Write an entry's header information to a header buffer.
*
* @param outbuf The tar entry header buffer to fill in.
*/
public void writeEntryHeader(byte[] outbuf) {
int offset = 0;
offset = TarUtils.getNameBytes(this.name, outbuf, offset, NAMELEN);
offset = TarUtils.getOctalBytes(this.mode, outbuf, offset, MODELEN);
offset = TarUtils.getOctalBytes(this.userId, outbuf, offset, UIDLEN);
offset = TarUtils.getOctalBytes(this.groupId, outbuf, offset, GIDLEN);
offset = TarUtils.getLongOctalBytes(this.size, outbuf, offset, SIZELEN);
offset = TarUtils.getLongOctalBytes(this.modTime, outbuf, offset, MODTIMELEN);
int csOffset = offset;
for (int c = 0; c < CHKSUMLEN; ++c) {
outbuf[offset++] = (byte) ' ';
}
outbuf[offset++] = this.linkFlag;
offset = TarUtils.getNameBytes(this.linkName, outbuf, offset, NAMELEN);
offset = TarUtils.getNameBytes(this.magic, outbuf, offset, MAGICLEN);
offset = TarUtils.getNameBytes(this.userName, outbuf, offset, UNAMELEN);
offset = TarUtils.getNameBytes(this.groupName, outbuf, offset, GNAMELEN);
offset = TarUtils.getOctalBytes(this.devMajor, outbuf, offset, DEVLEN);
offset = TarUtils.getOctalBytes(this.devMinor, outbuf, offset, DEVLEN);
while (offset < outbuf.length) {
outbuf[offset++] = 0;
}
long checkSum = TarUtils.computeCheckSum(outbuf);
TarUtils.getCheckSumOctalBytes(checkSum, outbuf, csOffset, CHKSUMLEN);
}
/**
* Parse an entry's header information from a header buffer.
*
* @param header The tar entry header buffer to get information from.
*/
public void parseTarHeader(byte[] header) {
int offset = 0;
this.name = TarUtils.parseName(header, offset, NAMELEN);
offset += NAMELEN;
this.mode = (int) TarUtils.parseOctal(header, offset, MODELEN);
offset += MODELEN;
this.userId = (int) TarUtils.parseOctal(header, offset, UIDLEN);
offset += UIDLEN;
this.groupId = (int) TarUtils.parseOctal(header, offset, GIDLEN);
offset += GIDLEN;
this.size = TarUtils.parseOctal(header, offset, SIZELEN);
offset += SIZELEN;
this.modTime = TarUtils.parseOctal(header, offset, MODTIMELEN);
offset += MODTIMELEN;
this.checkSum = (int) TarUtils.parseOctal(header, offset, CHKSUMLEN);
offset += CHKSUMLEN;
this.linkFlag = header[offset++];
this.linkName = TarUtils.parseName(header, offset, NAMELEN);
offset += NAMELEN;
this.magic = TarUtils.parseName(header, offset, MAGICLEN);
offset += MAGICLEN;
this.userName = TarUtils.parseName(header, offset, UNAMELEN);
offset += UNAMELEN;
this.groupName = TarUtils.parseName(header, offset, GNAMELEN);
offset += GNAMELEN;
this.devMajor = (int) TarUtils.parseOctal(header, offset, DEVLEN);
offset += DEVLEN;
this.devMinor = (int) TarUtils.parseOctal(header, offset, DEVLEN);
}
}
1.1 jakarta-ant/src/main/org/apache/tools/tar/TarInputStream.java
Index: TarInputStream.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
/*
* This package is based on the work done by Timothy Gerard Endres
* (time@ice.com) to whom the Ant project is very grateful for his great code.
*/
package org.apache.tools.tar;
import java.io.*;
/**
* The TarInputStream reads a UNIX tar archive as an InputStream.
* methods are provided to position at each successive entry in
* the archive, and the read each entry as a normal input stream
* using read().
*
* @author Timothy Gerard Endres <a href="mailto:time@ice.com">time@ice.com</a>
* @author Stefano Mazzocchi <a href="mailto:stefano@apache.org">stefano@apache.org</a>
*/
public class TarInputStream extends FilterInputStream {
protected boolean debug;
protected boolean hasHitEOF;
protected int entrySize;
protected int entryOffset;
protected byte[] oneBuf;
protected byte[] readBuf;
protected TarBuffer buffer;
protected TarEntry currEntry;
public TarInputStream(InputStream is) {
this(is, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE);
}
public TarInputStream(InputStream is, int blockSize) {
this(is, blockSize, TarBuffer.DEFAULT_RCDSIZE);
}
public TarInputStream(InputStream is, int blockSize, int recordSize) {
super(is);
this.buffer = new TarBuffer(is, blockSize, recordSize);
this.readBuf = null;
this.oneBuf = new byte[1];
this.debug = false;
this.hasHitEOF = false;
}
/**
* Sets the debugging flag.
*
* @param debugF True to turn on debugging.
*/
public void setDebug(boolean debug) {
this.debug = debug;
this.buffer.setDebug(debug);
}
/**
* Closes this stream. Calls the TarBuffer's close() method.
*/
public void close() throws IOException {
this.buffer.close();
}
/**
* Get the record size being used by this stream's TarBuffer.
*
* @return The TarBuffer record size.
*/
public int getRecordSize() {
return this.buffer.getRecordSize();
}
/**
* Get the available data that can be read from the current
* entry in the archive. This does not indicate how much data
* is left in the entire archive, only in the current entry.
* This value is determined from the entry's size header field
* and the amount of data already read from the current entry.
*
*
* @return The number of available bytes for the current entry.
*/
public int available() throws IOException {
return this.entrySize - this.entryOffset;
}
/**
* Skip bytes in the input buffer. This skips bytes in the
* current entry's data, not the entire archive, and will
* stop at the end of the current entry's data if the number
* to skip extends beyond that point.
*
* @param numToSkip The number of bytes to skip.
*/
public void skip(int numToSkip) throws IOException {
// REVIEW
// This is horribly inefficient, but it ensures that we
// properly skip over bytes via the TarBuffer...
//
byte[] skipBuf = new byte[8 * 1024];
for (int num = numToSkip; num > 0; ) {
int numRead = this.read(skipBuf, 0,
(num > skipBuf.length ? skipBuf.length
: num));
if (numRead == -1) {
break;
}
num -= numRead;
}
}
/**
* Since we do not support marking just yet, we return false.
*
* @return False.
*/
public boolean markSupported() {
return false;
}
/**
* Since we do not support marking just yet, we do nothing.
*
* @param markLimit The limit to mark.
*/
public void mark(int markLimit) {}
/**
* Since we do not support marking just yet, we do nothing.
*/
public void reset() {}
/**
* Get the next entry in this tar archive. This will skip
* over any remaining data in the current entry, if there
* is one, and place the input stream at the header of the
* next entry, and read the header and instantiate a new
* TarEntry from the header bytes and return that entry.
* If there are no more entries in the archive, null will
* be returned to indicate that the end of the archive has
* been reached.
*
* @return The next TarEntry in the archive, or null.
*/
public TarEntry getNextEntry() throws IOException {
if (this.hasHitEOF) {
return null;
}
if (this.currEntry != null) {
int numToSkip = this.entrySize - this.entryOffset;
if (this.debug) {
System.err.println("TarInputStream: SKIP currENTRY '"
+ this.currEntry.getName() + "' SZ "
+ this.entrySize + " OFF "
+ this.entryOffset + " skipping "
+ numToSkip + " bytes");
}
if (numToSkip > 0) {
this.skip(numToSkip);
}
this.readBuf = null;
}
byte[] headerBuf = this.buffer.readRecord();
if (headerBuf == null) {
if (this.debug) {
System.err.println("READ NULL RECORD");
}
this.hasHitEOF = true;
} else if (this.buffer.isEOFRecord(headerBuf)) {
if (this.debug) {
System.err.println("READ EOF RECORD");
}
this.hasHitEOF = true;
}
if (this.hasHitEOF) {
this.currEntry = null;
} else {
this.currEntry = new TarEntry(headerBuf);
if (!(headerBuf[257] == 'u' && headerBuf[258] == 's'
&& headerBuf[259] == 't' && headerBuf[260] == 'a'
&& headerBuf[261] == 'r')) {
this.entrySize = 0;
this.entryOffset = 0;
this.currEntry = null;
throw new IOException("bad header in block "
+ this.buffer.getCurrentBlockNum()
+ " record "
+ this.buffer.getCurrentRecordNum()
+ ", " +
"header magic is not 'ustar', but '"
+ headerBuf[257]
+ headerBuf[258]
+ headerBuf[259]
+ headerBuf[260]
+ headerBuf[261]
+ "', or (dec) "
+ ((int) headerBuf[257])
+ ", "
+ ((int) headerBuf[258])
+ ", "
+ ((int) headerBuf[259])
+ ", "
+ ((int) headerBuf[260])
+ ", "
+ ((int) headerBuf[261]));
}
if (this.debug) {
System.err.println("TarInputStream: SET CURRENTRY '"
+ this.currEntry.getName()
+ "' size = "
+ this.currEntry.getSize());
}
this.entryOffset = 0;
// REVIEW How do we resolve this discrepancy?!
this.entrySize = (int) this.currEntry.getSize();
}
return this.currEntry;
}
/**
* Reads a byte from the current tar archive entry.
*
* This method simply calls read( byte[], int, int ).
*
* @return The byte read, or -1 at EOF.
*/
public int read() throws IOException {
int num = this.read(this.oneBuf, 0, 1);
if (num == -1) {
return num;
} else {
return (int) this.oneBuf[0];
}
}
/**
* Reads bytes from the current tar archive entry.
*
* This method simply calls read( byte[], int, int ).
*
* @param buf The buffer into which to place bytes read.
* @return The number of bytes read, or -1 at EOF.
*/
public int read(byte[] buf) throws IOException {
return this.read(buf, 0, buf.length);
}
/**
* Reads bytes from the current tar archive entry.
*
* This method is aware of the boundaries of the current
* entry in the archive and will deal with them as if they
* were this stream's start and EOF.
*
* @param buf The buffer into which to place bytes read.
* @param offset The offset at which to place bytes read.
* @param numToRead The number of bytes to read.
* @return The number of bytes read, or -1 at EOF.
*/
public int read(byte[] buf, int offset, int numToRead) throws IOException {
int totalRead = 0;
if (this.entryOffset >= this.entrySize) {
return -1;
}
if ((numToRead + this.entryOffset) > this.entrySize) {
numToRead = (this.entrySize - this.entryOffset);
}
if (this.readBuf != null) {
int sz = (numToRead > this.readBuf.length) ? this.readBuf.length
: numToRead;
System.arraycopy(this.readBuf, 0, buf, offset, sz);
if (sz >= this.readBuf.length) {
this.readBuf = null;
} else {
int newLen = this.readBuf.length - sz;
byte[] newBuf = new byte[newLen];
System.arraycopy(this.readBuf, sz, newBuf, 0, newLen);
this.readBuf = newBuf;
}
totalRead += sz;
numToRead -= sz;
offset += sz;
}
while (numToRead > 0) {
byte[] rec = this.buffer.readRecord();
if (rec == null) {
// Unexpected EOF!
throw new IOException("unexpected EOF with " + numToRead
+ " bytes unread");
}
int sz = numToRead;
int recLen = rec.length;
if (recLen > sz) {
System.arraycopy(rec, 0, buf, offset, sz);
this.readBuf = new byte[recLen - sz];
System.arraycopy(rec, sz, this.readBuf, 0, recLen - sz);
} else {
sz = recLen;
System.arraycopy(rec, 0, buf, offset, recLen);
}
totalRead += sz;
numToRead -= sz;
offset += sz;
}
this.entryOffset += totalRead;
return totalRead;
}
/**
* Copies the contents of the current tar archive entry directly into
* an output stream.
*
* @param out The OutputStream into which to write the entry's data.
*/
public void copyEntryContents(OutputStream out) throws IOException {
byte[] buf = new byte[32 * 1024];
while (true) {
int numRead = this.read(buf, 0, buf.length);
if (numRead == -1) {
break;
}
out.write(buf, 0, numRead);
}
}
}
1.1 jakarta-ant/src/main/org/apache/tools/tar/TarOutputStream.java
Index: TarOutputStream.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
/*
* This package is based on the work done by Timothy Gerard Endres
* (time@ice.com) to whom the Ant project is very grateful for his great code.
*/
package org.apache.tools.tar;
import java.io.*;
/**
* The TarOutputStream writes a UNIX tar archive as an OutputStream.
* Methods are provided to put entries, and then write their contents
* by writing to this stream using write().
*
* @author Timothy Gerard Endres <a href="mailto:time@ice.com">time@ice.com</a>
*/
public class TarOutputStream extends FilterOutputStream {
protected boolean debug;
protected int currSize;
protected int currBytes;
protected byte[] oneBuf;
protected byte[] recordBuf;
protected int assemLen;
protected byte[] assemBuf;
protected TarBuffer buffer;
public TarOutputStream(OutputStream os) {
this(os, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE);
}
public TarOutputStream(OutputStream os, int blockSize) {
this(os, blockSize, TarBuffer.DEFAULT_RCDSIZE);
}
public TarOutputStream(OutputStream os, int blockSize, int recordSize) {
super(os);
this.buffer = new TarBuffer(os, blockSize, recordSize);
this.debug = false;
this.assemLen = 0;
this.assemBuf = new byte[recordSize];
this.recordBuf = new byte[recordSize];
this.oneBuf = new byte[1];
}
/**
* Sets the debugging flag.
*
* @param debugF True to turn on debugging.
*/
public void setDebug(boolean debugF) {
this.debug = debugF;
}
/**
* Sets the debugging flag in this stream's TarBuffer.
*
* @param debugF True to turn on debugging.
*/
public void setBufferDebug(boolean debug) {
this.buffer.setDebug(debug);
}
/**
* Ends the TAR archive without closing the underlying OutputStream.
* The result is that the EOF record of nulls is written.
*/
public void finish() throws IOException {
this.writeEOFRecord();
}
/**
* Ends the TAR archive and closes the underlying OutputStream.
* This means that finish() is called followed by calling the
* TarBuffer's close().
*/
public void close() throws IOException {
this.finish();
this.buffer.close();
}
/**
* Get the record size being used by this stream's TarBuffer.
*
* @return The TarBuffer record size.
*/
public int getRecordSize() {
return this.buffer.getRecordSize();
}
/**
* Put an entry on the output stream. This writes the entry's
* header record and positions the output stream for writing
* the contents of the entry. Once this method is called, the
* stream is ready for calls to write() to write the entry's
* contents. Once the contents are written, closeEntry()
* <B>MUST</B> be called to ensure that all buffered data
* is completely written to the output stream.
*
* @param entry The TarEntry to be written to the archive.
*/
public void putNextEntry(TarEntry entry) throws IOException {
entry.writeEntryHeader(this.recordBuf);
this.buffer.writeRecord(this.recordBuf);
this.currBytes = 0;
if (entry.isDirectory()) {
this.currSize = 0;
} else {
this.currSize = (int) entry.getSize();
}
}
/**
* Close an entry. This method MUST be called for all file
* entries that contain data. The reason is that we must
* buffer data written to the stream in order to satisfy
* the buffer's record based writes. Thus, there may be
* data fragments still being assembled that must be written
* to the output stream before this entry is closed and the
* next entry written.
*/
public void closeEntry() throws IOException {
if (this.assemLen > 0) {
for (int i = this.assemLen; i < this.assemBuf.length; ++i) {
this.assemBuf[i] = 0;
}
this.buffer.writeRecord(this.assemBuf);
this.currBytes += this.assemLen;
this.assemLen = 0;
}
if (this.currBytes < this.currSize) {
throw new IOException("entry closed at '" + this.currBytes
+ "' before the '" + this.currSize
+ "' bytes specified in the header were written");
}
}
/**
* Writes a byte to the current tar archive entry.
*
* This method simply calls read( byte[], int, int ).
*
* @param b The byte written.
*/
public void write(int b) throws IOException {
this.oneBuf[0] = (byte) b;
this.write(this.oneBuf, 0, 1);
}
/**
* Writes bytes to the current tar archive entry.
*
* This method simply calls read( byte[], int, int ).
*
* @param wBuf The buffer to write to the archive.
* @return The number of bytes read, or -1 at EOF.
*/
public void write(byte[] wBuf) throws IOException {
this.write(wBuf, 0, wBuf.length);
}
/**
* Writes bytes to the current tar archive entry. This method
* is aware of the current entry and will throw an exception if
* you attempt to write bytes past the length specified for the
* current entry. The method is also (painfully) aware of the
* record buffering required by TarBuffer, and manages buffers
* that are not a multiple of recordsize in length, including
* assembling records from small buffers.
*
* This method simply calls read( byte[], int, int ).
*
* @param wBuf The buffer to write to the archive.
* @param wOffset The offset in the buffer from which to get bytes.
* @param numToWrite The number of bytes to write.
*/
public void write(byte[] wBuf, int wOffset, int numToWrite) throws IOException {
if ((this.currBytes + numToWrite) > this.currSize) {
throw new IOException("request to write '" + numToWrite
+ "' bytes exceeds size in header of '"
+ this.currSize + "' bytes");
//
// We have to deal with assembly!!!
// The programmer can be writing little 32 byte chunks for all
// we know, and we must assemble complete records for writing.
// REVIEW Maybe this should be in TarBuffer? Could that help to
// eliminate some of the buffer copying.
//
}
if (this.assemLen > 0) {
if ((this.assemLen + numToWrite) >= this.recordBuf.length) {
int aLen = this.recordBuf.length - this.assemLen;
System.arraycopy(this.assemBuf, 0, this.recordBuf, 0,
this.assemLen);
System.arraycopy(wBuf, wOffset, this.recordBuf,
this.assemLen, aLen);
this.buffer.writeRecord(this.recordBuf);
this.currBytes += this.recordBuf.length;
wOffset += aLen;
numToWrite -= aLen;
this.assemLen = 0;
} else {
System.arraycopy(wBuf, wOffset, this.assemBuf, this.assemLen,
numToWrite);
wOffset += numToWrite;
this.assemLen += numToWrite;
numToWrite -= numToWrite;
}
}
//
// When we get here we have EITHER:
// o An empty "assemble" buffer.
// o No bytes to write (numToWrite == 0)
//
while (numToWrite > 0) {
if (numToWrite < this.recordBuf.length) {
System.arraycopy(wBuf, wOffset, this.assemBuf, this.assemLen,
numToWrite);
this.assemLen += numToWrite;
break;
}
this.buffer.writeRecord(wBuf, wOffset);
int num = this.recordBuf.length;
this.currBytes += num;
numToWrite -= num;
wOffset += num;
}
}
/**
* Write an EOF (end of archive) record to the tar archive.
* An EOF record consists of a record of all zeros.
*/
private void writeEOFRecord() throws IOException {
for (int i = 0; i < this.recordBuf.length; ++i) {
this.recordBuf[i] = 0;
}
this.buffer.writeRecord(this.recordBuf);
}
}
1.1 jakarta-ant/src/main/org/apache/tools/tar/TarUtils.java
Index: TarUtils.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
/*
* This package is based on the work done by Timothy Gerard Endres
* (time@ice.com) to whom the Ant project is very grateful for his great code.
*/
package org.apache.tools.tar;
/**
* This class provides static utility methods to work with byte streams.
*
* @author Timothy Gerard Endres <a href="mailto:time@ice.com">time@ice.com</a>
* @author Stefano Mazzocchi <a href="mailto:stefano@apache.org">stefano@apache.org</a>
*/
public class TarUtils {
/**
* Parse an octal string from a header buffer. This is used for the
* file permission mode value.
*
* @param header The header buffer from which to parse.
* @param offset The offset into the buffer from which to parse.
* @param length The number of header bytes to parse.
* @return The long value of the octal string.
*/
public static long parseOctal(byte[] header, int offset, int length) {
long result = 0;
boolean stillPadding = true;
int end = offset + length;
for (int i = offset; i < end; ++i) {
if (header[i] == 0) {
break;
}
if (header[i] == (byte) ' ' || header[i] == '0') {
if (stillPadding) {
continue;
}
if (header[i] == (byte) ' ') {
break;
}
}
stillPadding = false;
result = (result << 3) + (header[i] - '0');
}
return result;
}
/**
* Parse an entry name from a header buffer.
*
* @param header The header buffer from which to parse.
* @param offset The offset into the buffer from which to parse.
* @param length The number of header bytes to parse.
* @return The header's entry name.
*/
public static StringBuffer parseName(byte[] header, int offset, int length) {
StringBuffer result = new StringBuffer(length);
int end = offset + length;
for (int i = offset; i < end; ++i) {
if (header[i] == 0) {
break;
}
result.append((char) header[i]);
}
return result;
}
/**
* Determine the number of bytes in an entry name.
*
* @param header The header buffer from which to parse.
* @param offset The offset into the buffer from which to parse.
* @param length The number of header bytes to parse.
* @return The number of bytes in a header's entry name.
*/
public static int getNameBytes(StringBuffer name, byte[] buf, int offset, int length) {
int i;
for (i = 0; i < length && i < name.length(); ++i) {
buf[offset + i] = (byte) name.charAt(i);
}
for (; i < length; ++i) {
buf[offset + i] = 0;
}
return offset + length;
}
/**
* Parse an octal integer from a header buffer.
*
* @param header The header buffer from which to parse.
* @param offset The offset into the buffer from which to parse.
* @param length The number of header bytes to parse.
* @return The integer value of the octal bytes.
*/
public static int getOctalBytes(long value, byte[] buf, int offset, int length) {
byte[] result = new byte[length];
int idx = length - 1;
buf[offset + idx] = 0;
--idx;
buf[offset + idx] = (byte) ' ';
--idx;
if (value == 0) {
buf[offset + idx] = (byte) '0';
--idx;
} else {
for (long val = value; idx >= 0 && val > 0; --idx) {
buf[offset + idx] = (byte) ((byte) '0' + (byte) (val & 7));
val = val >> 3;
}
}
for (; idx >= 0; --idx) {
buf[offset + idx] = (byte) ' ';
}
return offset + length;
}
/**
* Parse an octal long integer from a header buffer.
*
* @param header The header buffer from which to parse.
* @param offset The offset into the buffer from which to parse.
* @param length The number of header bytes to parse.
* @return The long value of the octal bytes.
*/
public static int getLongOctalBytes(long value, byte[] buf, int offset, int length) {
byte[] temp = new byte[length + 1];
getOctalBytes(value, temp, 0, length + 1);
System.arraycopy(temp, 0, buf, offset, length);
return offset + length;
}
/**
* Parse the checksum octal integer from a header buffer.
*
* @param header The header buffer from which to parse.
* @param offset The offset into the buffer from which to parse.
* @param length The number of header bytes to parse.
* @return The integer value of the entry's checksum.
*/
public static int getCheckSumOctalBytes(long value, byte[] buf, int offset, int length) {
getOctalBytes(value, buf, offset, length);
buf[offset + length - 1] = (byte) ' ';
buf[offset + length - 2] = 0;
return offset + length;
}
/**
* Compute the checksum of a tar entry header.
*
* @param buf The tar entry's header buffer.
* @return The computed checksum.
*/
public static long computeCheckSum(byte[] buf) {
long sum = 0;
for (int i = 0; i < buf.length; ++i) {
sum += 255 & buf[i];
}
return sum;
}
}