/jEdit/tags/jedit-4-3-pre15/installer/TarEntry.java
# · Java · 729 lines · 356 code · 112 blank · 261 comment · 28 complexity · 2c5b9c2b57b2b7b215598707f9bd28b9 MD5 · raw file
- /*
- ** Authored by Timothy Gerard Endres
- ** <mailto:time@gjt.org> <http://www.trustice.com>
- **
- ** This work has been placed into the public domain.
- ** You may use this work in any way and for any purpose you wish.
- **
- ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
- ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
- ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
- ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
- ** REDISTRIBUTION OF THIS SOFTWARE.
- **
- */
- package installer;
- import java.io.*;
- import java.util.Date;
- /**
- *
- * 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>
- *
- * @see TarHeader
- *
- */
- public
- class TarEntry
- extends Object
- {
- /**
- * If this entry represents a File, this references it.
- */
- protected File file;
- /**
- * This is the entry's header information.
- */
- protected TarHeader header;
- /**
- * 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.initialize();
- this.nameTarHeader( this.header, name );
- }
- /**
- * 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 )
- throws InvalidHeaderException
- {
- this.initialize();
- this.getFileTarHeader( this.header, file );
- }
- /**
- * 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 )
- throws InvalidHeaderException
- {
- this.initialize();
- this.parseTarHeader( this.header, headerBuf );
- }
- /**
- * Initialization code common to all constructors.
- */
- private void
- initialize()
- {
- this.file = null;
- this.header = new TarHeader();
- }
- /**
- * 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.header.name.toString().equals
- ( it.header.name.toString() );
- }
- /**
- * 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.header.name.toString().startsWith
- ( this.header.name.toString() );
- }
- /**
- * Get this entry's header.
- *
- * @return This entry's TarHeader.
- */
- public TarHeader
- getHeader()
- {
- return this.header;
- }
- /**
- * Get this entry's name.
- *
- * @return This entry's name.
- */
- public String
- getName()
- {
- return this.header.name.toString();
- }
- /**
- * Set this entry's name.
- *
- * @param name This entry's new name.
- */
- public void
- setName( String name )
- {
- this.header.name =
- new StringBuffer( name );
- }
- /**
- * Get this entry's user id.
- *
- * @return This entry's user id.
- */
- public int
- getUserId()
- {
- return this.header.userId;
- }
- /**
- * Set this entry's user id.
- *
- * @param userId This entry's new user id.
- */
- public void
- setUserId( int userId )
- {
- this.header.userId = userId;
- }
- /**
- * Get this entry's group id.
- *
- * @return This entry's group id.
- */
- public int
- getGroupId()
- {
- return this.header.groupId;
- }
- /**
- * Set this entry's group id.
- *
- * @param groupId This entry's new group id.
- */
- public void
- setGroupId( int groupId )
- {
- this.header.groupId = groupId;
- }
- /**
- * Get this entry's user name.
- *
- * @return This entry's user name.
- */
- public String
- getUserName()
- {
- return this.header.userName.toString();
- }
- /**
- * Set this entry's user name.
- *
- * @param userName This entry's new user name.
- */
- public void
- setUserName( String userName )
- {
- this.header.userName =
- new StringBuffer( userName );
- }
- /**
- * Get this entry's group name.
- *
- * @return This entry's group name.
- */
- public String
- getGroupName()
- {
- return this.header.groupName.toString();
- }
- /**
- * Set this entry's group name.
- *
- * @param groupName This entry's new group name.
- */
- public void
- setGroupName( String groupName )
- {
- this.header.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.header.modTime = time / 1000;
- }
- /**
- * Set this entry's modification time.
- *
- * @param time This entry's new modification time.
- */
- public void
- setModTime( Date time )
- {
- this.header.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.header.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.header.size;
- }
- /**
- * Set this entry's file size.
- *
- * @param size This entry's new file size.
- */
- public void
- setSize( long size )
- {
- this.header.size = size;
- }
- /**
- * Convenience method that will modify an entry's name directly
- * in place in an entry header buffer byte array.
- *
- * @param outbuf The buffer containing the entry header to modify.
- * @param newName The new name to place into the header buffer.
- */
- public void
- adjustEntryName( byte[] outbuf, String newName )
- {
- int offset = 0;
- offset = TarHeader.getNameBytes
- ( new StringBuffer( newName ),
- outbuf, offset, TarHeader.NAMELEN );
- }
- /**
- * 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.header != null )
- {
- if ( this.header.linkFlag == TarHeader.LF_DIR )
- return true;
- if ( this.header.name.toString().endsWith( "/" ) )
- return true;
- }
- return false;
- }
- /**
- * Fill in a TarHeader with information from a File.
- *
- * @param hdr The TarHeader to fill in.
- * @param file The file from which to get the header information.
- */
- public void
- getFileTarHeader( TarHeader hdr, File file )
- throws InvalidHeaderException
- {
- 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 ( File.separatorChar == '\\' )
- // Per Patrick Beard:
- String Win32Prefix = "windows";
- if ( osname.toLowerCase().startsWith( 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.
-
- for ( ; name.startsWith( "/" ) ; )
- name = name.substring( 1 );
- hdr.linkName = new StringBuffer( "" );
- hdr.name = new StringBuffer( name );
- if ( file.isDirectory() )
- {
- hdr.mode = 040755;
- hdr.linkFlag = TarHeader.LF_DIR;
- if ( hdr.name.charAt( hdr.name.length() - 1 ) != '/' )
- hdr.name.append( "/" );
- }
- else
- {
- hdr.mode = 0100644;
- hdr.linkFlag = TarHeader.LF_NORMAL;
- }
- // UNDONE When File lets us get the userName, use it!
- hdr.size = file.length();
- hdr.modTime = file.lastModified() / 1000;
- hdr.checkSum = 0;
- hdr.devMajor = 0;
- hdr.devMinor = 0;
- }
- /**
- * 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()
- throws InvalidHeaderException
- {
- 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;
- }
- /**
- * Compute the checksum of a tar entry header.
- *
- * @param buf The tar entry's header buffer.
- * @return The computed checksum.
- */
- public long
- computeCheckSum( byte[] buf )
- {
- long sum = 0;
- for ( int i = 0 ; i < buf.length ; ++i )
- {
- sum += 255 & buf[ i ];
- }
- return sum;
- }
- /**
- * 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 = TarHeader.getNameBytes
- ( this.header.name, outbuf, offset, TarHeader.NAMELEN );
- offset = TarHeader.getOctalBytes
- ( this.header.mode, outbuf, offset, TarHeader.MODELEN );
- offset = TarHeader.getOctalBytes
- ( this.header.userId, outbuf, offset, TarHeader.UIDLEN );
- offset = TarHeader.getOctalBytes
- ( this.header.groupId, outbuf, offset, TarHeader.GIDLEN );
- long size = this.header.size;
- offset = TarHeader.getLongOctalBytes
- ( size, outbuf, offset, TarHeader.SIZELEN );
- offset = TarHeader.getLongOctalBytes
- ( this.header.modTime, outbuf, offset, TarHeader.MODTIMELEN );
- int csOffset = offset;
- for ( int c = 0 ; c < TarHeader.CHKSUMLEN ; ++c )
- outbuf[ offset++ ] = (byte) ' ';
- outbuf[ offset++ ] = this.header.linkFlag;
- offset = TarHeader.getNameBytes
- ( this.header.linkName, outbuf, offset, TarHeader.NAMELEN );
- offset = TarHeader.getNameBytes
- ( this.header.magic, outbuf, offset, TarHeader.MAGICLEN );
- offset = TarHeader.getNameBytes
- ( this.header.userName, outbuf, offset, TarHeader.UNAMELEN );
- offset = TarHeader.getNameBytes
- ( this.header.groupName, outbuf, offset, TarHeader.GNAMELEN );
- offset = TarHeader.getOctalBytes
- ( this.header.devMajor, outbuf, offset, TarHeader.DEVLEN );
- offset = TarHeader.getOctalBytes
- ( this.header.devMinor, outbuf, offset, TarHeader.DEVLEN );
- for ( ; offset < outbuf.length ; )
- outbuf[ offset++ ] = 0;
- long checkSum = this.computeCheckSum( outbuf );
- TarHeader.getCheckSumOctalBytes
- ( checkSum, outbuf, csOffset, TarHeader.CHKSUMLEN );
- }
- /**
- * Parse an entry's TarHeader information from a header buffer.
- *
- * @param hdr The TarHeader to fill in from the buffer information.
- * @param header The tar entry header buffer to get information from.
- */
- public void
- parseTarHeader( TarHeader hdr, byte[] header )
- throws InvalidHeaderException
- {
- int offset = 0;
- hdr.name =
- TarHeader.parseName( header, offset, TarHeader.NAMELEN );
- offset += TarHeader.NAMELEN;
- hdr.mode = (int)
- TarHeader.parseOctal( header, offset, TarHeader.MODELEN );
- offset += TarHeader.MODELEN;
- hdr.userId = (int)
- TarHeader.parseOctal( header, offset, TarHeader.UIDLEN );
- offset += TarHeader.UIDLEN;
- hdr.groupId = (int)
- TarHeader.parseOctal( header, offset, TarHeader.GIDLEN );
- offset += TarHeader.GIDLEN;
- hdr.size =
- TarHeader.parseOctal( header, offset, TarHeader.SIZELEN );
- offset += TarHeader.SIZELEN;
- hdr.modTime =
- TarHeader.parseOctal( header, offset, TarHeader.MODTIMELEN );
- offset += TarHeader.MODTIMELEN;
- hdr.checkSum = (int)
- TarHeader.parseOctal( header, offset, TarHeader.CHKSUMLEN );
- offset += TarHeader.CHKSUMLEN;
- hdr.linkFlag = header[ offset++ ];
- hdr.linkName =
- TarHeader.parseName( header, offset, TarHeader.NAMELEN );
- offset += TarHeader.NAMELEN;
- hdr.magic =
- TarHeader.parseName( header, offset, TarHeader.MAGICLEN );
- offset += TarHeader.MAGICLEN;
- hdr.userName =
- TarHeader.parseName( header, offset, TarHeader.UNAMELEN );
- offset += TarHeader.UNAMELEN;
- hdr.groupName =
- TarHeader.parseName( header, offset, TarHeader.GNAMELEN );
- offset += TarHeader.GNAMELEN;
- hdr.devMajor = (int)
- TarHeader.parseOctal( header, offset, TarHeader.DEVLEN );
- offset += TarHeader.DEVLEN;
- hdr.devMinor = (int)
- TarHeader.parseOctal( header, offset, TarHeader.DEVLEN );
- }
- /**
- * Fill in a TarHeader given only the entry's name.
- *
- * @param hdr The TarHeader to fill in.
- * @param name The tar entry name.
- */
- public void
- nameTarHeader( TarHeader hdr, String name )
- {
- boolean isDir = name.endsWith( "/" );
- hdr.checkSum = 0;
- hdr.devMajor = 0;
- hdr.devMinor = 0;
- hdr.name = new StringBuffer( name );
- hdr.mode = isDir ? 040755 : 0100644;
- hdr.userId = 0;
- hdr.groupId = 0;
- hdr.size = 0;
- hdr.checkSum = 0;
- hdr.modTime =
- (new java.util.Date()).getTime() / 1000;
- hdr.linkFlag =
- isDir ? TarHeader.LF_DIR : TarHeader.LF_NORMAL;
- hdr.linkName = new StringBuffer( "" );
- hdr.userName = new StringBuffer( "" );
- hdr.groupName = new StringBuffer( "" );
- hdr.devMajor = 0;
- hdr.devMinor = 0;
- }
- }