001/*
002** Authored by Timothy Gerard Endres
003** <mailto:time@gjt.org>  <http://www.trustice.com>
004** 
005** This work has been placed into the public domain.
006** You may use this work in any way and for any purpose you wish.
007**
008** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
009** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
010** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
011** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
012** REDISTRIBUTION OF THIS SOFTWARE. 
013** 
014*/
015
016package com.ice.tar;
017
018import java.io.*;
019import java.util.Date;
020
021
022/**
023 *
024 * This class represents an entry in a Tar archive. It consists
025 * of the entry's header, as well as the entry's File. Entries
026 * can be instantiated in one of three ways, depending on how
027 * they are to be used.
028 * <p>
029 * TarEntries that are created from the header bytes read from
030 * an archive are instantiated with the TarEntry( byte[] )
031 * constructor. These entries will be used when extracting from
032 * or listing the contents of an archive. These entries have their
033 * header filled in using the header bytes. They also set the File
034 * to null, since they reference an archive entry not a file.
035 * <p>
036 * TarEntries that are created from Files that are to be written
037 * into an archive are instantiated with the TarEntry( File )
038 * constructor. These entries have their header filled in using
039 * the File's information. They also keep a reference to the File
040 * for convenience when writing entries.
041 * <p>
042 * Finally, TarEntries can be constructed from nothing but a name.
043 * This allows the programmer to construct the entry by hand, for
044 * instance when only an InputStream is available for writing to
045 * the archive, and the header information is constructed from
046 * other information. In this case the header fields are set to
047 * defaults and the File is set to null.
048 *
049 * <pre>
050 *
051 * Original Unix Tar Header:
052 *
053 * Field  Field     Field
054 * Width  Name      Meaning
055 * -----  --------- ---------------------------
056 *   100  name      name of file
057 *     8  mode      file mode
058 *     8  uid       owner user ID
059 *     8  gid       owner group ID
060 *    12  size      length of file in bytes
061 *    12  mtime     modify time of file
062 *     8  chksum    checksum for header
063 *     1  link      indicator for links
064 *   100  linkname  name of linked file
065 *
066 * </pre>
067 *
068 * <pre>
069 *
070 * POSIX "ustar" Style Tar Header:
071 *
072 * Field  Field     Field
073 * Width  Name      Meaning
074 * -----  --------- ---------------------------
075 *   100  name      name of file
076 *     8  mode      file mode
077 *     8  uid       owner user ID
078 *     8  gid       owner group ID
079 *    12  size      length of file in bytes
080 *    12  mtime     modify time of file
081 *     8  chksum    checksum for header
082 *     1  typeflag  type of file
083 *   100  linkname  name of linked file
084 *     6  magic     USTAR indicator
085 *     2  version   USTAR version
086 *    32  uname     owner user name
087 *    32  gname     owner group name
088 *     8  devmajor  device major number
089 *     8  devminor  device minor number
090 *   155  prefix    prefix for file name
091 *
092 * struct posix_header
093 *   {                     byte offset
094 *   char name[100];            0
095 *   char mode[8];            100
096 *   char uid[8];             108
097 *   char gid[8];             116
098 *   char size[12];           124
099 *   char mtime[12];          136
100 *   char chksum[8];          148
101 *   char typeflag;           156
102 *   char linkname[100];      157
103 *   char magic[6];           257
104 *   char version[2];         263
105 *   char uname[32];          265
106 *   char gname[32];          297
107 *   char devmajor[8];        329
108 *   char devminor[8];        337
109 *   char prefix[155];        345
110 *   };                       500
111 *
112 * </pre>
113 *
114 * Note that while the class does recognize GNU formatted headers,
115 * it does not perform proper processing of GNU archives. I hope
116 * to add the GNU support someday.
117 *
118 * Directory "size" fix contributed by:
119 * Bert Becker <becker@informatik.hu-berlin.de>
120 *
121 * @see TarHeader
122 * @author Timothy Gerard Endres, <time@gjt.org>
123 */
124
125public
126class           TarEntry
127extends         Object
128implements      Cloneable
129        {
130        /**
131         * If this entry represents a File, this references it.
132         */
133        protected File                          file;
134
135        /**
136         * This is the entry's header information.
137         */
138        protected TarHeader                     header;
139
140        /**
141         * Set to true if this is a "old-unix" format entry.
142         */
143        protected boolean                       unixFormat;
144
145        /**
146         * Set to true if this is a 'ustar' format entry.
147         */
148        protected boolean                       ustarFormat;
149
150        /**
151         * Set to true if this is a GNU 'ustar' format entry.
152         */
153        protected boolean                       gnuFormat;
154
155
156        /**
157         * The default constructor is protected for use only by subclasses.
158         */
159        protected
160        TarEntry()
161                {
162                }
163
164        /**
165         * Construct an entry with only a name. This allows the programmer
166         * to construct the entry's header "by hand". File is set to null.
167         */
168        public
169        TarEntry( String name )
170                {
171                this.initialize();
172                this.nameTarHeader( this.header, name );
173                }
174
175        /**
176         * Construct an entry for a file. File is set to file, and the
177         * header is constructed from information from the file.
178         *
179         * @param file The file that the entry represents.
180         */
181        public
182        TarEntry( File file )
183                throws InvalidHeaderException
184                {
185                this.initialize();
186                this.getFileTarHeader( this.header, file );
187                }
188
189        /**
190         * Construct an entry from an archive's header bytes. File is set
191         * to null.
192         *
193         * @param headerBuf The header bytes from a tar archive entry.
194         */
195        public
196        TarEntry( byte[] headerBuf )
197                throws InvalidHeaderException
198                {
199                this.initialize();
200                this.parseTarHeader( this.header, headerBuf );
201                }
202
203        /**
204         * Initialization code common to all constructors.
205         */
206        private void
207        initialize()
208                {
209                this.file = null;
210                this.header = new TarHeader();
211
212                this.gnuFormat = false;
213                this.ustarFormat = true; // REVIEW What we prefer to use...
214                this.unixFormat = false;
215                }
216
217        /**
218         * Clone the entry.
219         */
220        public Object
221        clone()
222                {
223                TarEntry entry = null;
224
225                try {
226                        entry = (TarEntry) super.clone();
227
228                        if ( this.header != null )
229                                {
230                                entry.header = (TarHeader) this.header.clone();
231                                }
232
233                        if ( this.file != null )
234                                {
235                                entry.file = new File( this.file.getAbsolutePath() );
236                                }
237                        }
238                catch ( CloneNotSupportedException ex )
239                        {
240                        ex.printStackTrace( System.err );
241                        }
242
243                return entry;
244                }
245
246        /**
247         * Returns true if this entry's header is in "ustar" format.
248         *
249         * @return True if the entry's header is in "ustar" format.
250         */
251        public boolean
252        isUSTarFormat()
253                {
254                return this.ustarFormat;
255                }
256
257        /**
258         * Sets this entry's header format to "ustar".
259         */
260        public void
261        setUSTarFormat()
262                {
263                this.ustarFormat = true;
264                this.gnuFormat = false;
265                this.unixFormat = false;
266                }
267
268        /**
269         * Returns true if this entry's header is in the GNU 'ustar' format.
270         *
271         * @return True if the entry's header is in the GNU 'ustar' format.
272         */
273        public boolean
274        isGNUTarFormat()
275                {
276                return this.gnuFormat;
277                }
278
279        /**
280         * Sets this entry's header format to GNU "ustar".
281         */
282        public void
283        setGNUTarFormat()
284                {
285                this.gnuFormat = true;
286                this.ustarFormat = false;
287                this.unixFormat = false;
288                }
289
290        /**
291         * Returns true if this entry's header is in the old "unix-tar" format.
292         *
293         * @return True if the entry's header is in the old "unix-tar" format.
294         */
295        public boolean
296        isUnixTarFormat()
297                {
298                return this.unixFormat;
299                }
300
301        /**
302         * Sets this entry's header format to "unix-style".
303         */
304        public void
305        setUnixTarFormat()
306                {
307                this.unixFormat = true;
308                this.ustarFormat = false;
309                this.gnuFormat = false;
310                }
311
312        /**
313         * Determine if the two entries are equal. Equality is determined
314         * by the header names being equal.
315         *
316         * @return it Entry to be checked for equality.
317         * @return True if the entries are equal.
318         */
319        public boolean
320        equals( TarEntry it )
321                {
322                return
323                        this.header.name.toString().equals
324                                ( it.header.name.toString() );
325                }
326
327        /**
328         * Determine if the given entry is a descendant of this entry.
329         * Descendancy is determined by the name of the descendant
330         * starting with this entry's name.
331         *
332         * @param desc Entry to be checked as a descendent of this.
333         * @return True if entry is a descendant of this.
334         */
335        public boolean
336        isDescendent( TarEntry desc )
337                {
338                return
339                        desc.header.name.toString().startsWith
340                                ( this.header.name.toString() );
341                }
342
343        /**
344         * Get this entry's header.
345         *
346         * @return This entry's TarHeader.
347         */
348        public TarHeader
349        getHeader()
350                {
351                return this.header;
352                }
353
354        /**
355         * Get this entry's name.
356         *
357         * @return This entry's name.
358         */
359        public String
360        getName()
361                {
362                return this.header.name.toString();
363                }
364
365        /**
366         * Set this entry's name.
367         *
368         * @param name This entry's new name.
369         */
370        public void
371        setName( String name )
372                {
373                this.header.name =
374                        new StringBuffer( name );
375                }
376
377        /**
378         * Get this entry's user id.
379         *
380         * @return This entry's user id.
381         */
382        public int
383        getUserId()
384                {
385                return this.header.userId;
386                }
387
388        /**
389         * Set this entry's user id.
390         *
391         * @param userId This entry's new user id.
392         */
393        public void
394        setUserId( int userId )
395                {
396                this.header.userId = userId;
397                }
398
399        /**
400         * Get this entry's group id.
401         *
402         * @return This entry's group id.
403         */
404        public int
405        getGroupId()
406                {
407                return this.header.groupId;
408                }
409
410        /**
411         * Set this entry's group id.
412         *
413         * @param groupId This entry's new group id.
414         */
415        public void
416        setGroupId( int groupId )
417                {
418                this.header.groupId = groupId;
419                }
420
421        /**
422         * Get this entry's user name.
423         *
424         * @return This entry's user name.
425         */
426        public String
427        getUserName()
428                {
429                return this.header.userName.toString();
430                }
431
432        /**
433         * Set this entry's user name.
434         *
435         * @param userName This entry's new user name.
436         */
437        public void
438        setUserName( String userName )
439                {
440                this.header.userName =
441                        new StringBuffer( userName );
442                }
443
444        /**
445         * Get this entry's group name.
446         *
447         * @return This entry's group name.
448         */
449        public String
450        getGroupName()
451                {
452                return this.header.groupName.toString();
453                }
454
455        /**
456         * Set this entry's group name.
457         *
458         * @param groupName This entry's new group name.
459         */
460        public void
461        setGroupName( String groupName )
462                {
463                this.header.groupName =
464                        new StringBuffer( groupName );
465                }
466
467        /**
468         * Convenience method to set this entry's group and user ids.
469         *
470         * @param userId This entry's new user id.
471         * @param groupId This entry's new group id.
472         */
473        public void
474        setIds( int userId, int groupId )
475                {
476                this.setUserId( userId );
477                this.setGroupId( groupId );
478                }
479
480        /**
481         * Convenience method to set this entry's group and user names.
482         *
483         * @param userName This entry's new user name.
484         * @param groupName This entry's new group name.
485         */
486        public void
487        setNames( String userName, String groupName )
488                {
489                this.setUserName( userName );
490                this.setGroupName( groupName );
491                }
492
493        /**
494         * Set this entry's modification time. The parameter passed
495         * to this method is in "Java time".
496         *
497         * @param time This entry's new modification time.
498         */
499        public void
500        setModTime( long time )
501                {
502                this.header.modTime = time / 1000;
503                }
504
505        /**
506         * Set this entry's modification time.
507         *
508         * @param time This entry's new modification time.
509         */
510        public void
511        setModTime( Date time )
512                {
513                this.header.modTime = time.getTime() / 1000;
514                }
515
516        /**
517         * Set this entry's modification time.
518         *
519         * @param time This entry's new modification time.
520         */
521        public Date
522        getModTime()
523                {
524                return new Date( this.header.modTime * 1000 );
525                }
526
527        /**
528         * Get this entry's file.
529         *
530         * @return This entry's file.
531         */
532        public File
533        getFile()
534                {
535                return this.file;
536                }
537
538        /**
539         * Get this entry's file size.
540         *
541         * @return This entry's file size.
542         */
543        public long
544        getSize()
545                {
546                return this.header.size;
547                }
548
549        /**
550         * Set this entry's file size.
551         *
552         * @param size This entry's new file size.
553         */
554        public void
555        setSize( long size )
556                {
557                this.header.size = size;
558                }
559
560        /**
561         * Return whether or not this entry represents a directory.
562         *
563         * @return True if this entry is a directory.
564         */
565        public boolean
566        isDirectory()
567                {
568                if ( this.file != null )
569                        return this.file.isDirectory();
570
571                if ( this.header != null )
572                        {
573                        if ( this.header.linkFlag == TarHeader.LF_DIR )
574                                return true;
575
576                        if ( this.header.name.toString().endsWith( "/" ) )
577                                return true;
578                        }
579
580                return false;
581                }
582
583        /**
584         * Fill in a TarHeader with information from a File.
585         *
586         * @param hdr The TarHeader to fill in.
587         * @param file The file from which to get the header information.
588         */
589        public void
590        getFileTarHeader( TarHeader hdr, File file )
591                throws InvalidHeaderException
592                {
593                this.file = file;
594
595                String name = file.getPath();
596                String osname = System.getProperty( "os.name" );
597                if ( osname != null )
598                        {
599                        // Strip off drive letters!
600                        // REVIEW Would a better check be "(File.separator == '\')"?
601
602                        // String Win32Prefix = "Windows";
603                        // String prefix = osname.substring( 0, Win32Prefix.length() );
604                        // if ( prefix.equalsIgnoreCase( Win32Prefix ) )
605
606                        // if ( File.separatorChar == '\\' )
607
608                        // Windows OS check was contributed by
609                        // Patrick Beard <beard@netscape.com>
610                        String Win32Prefix = "windows";
611                        if ( osname.toLowerCase().startsWith( Win32Prefix ) )
612                                {
613                                if ( name.length() > 2 )
614                                        {
615                                        char ch1 = name.charAt(0);
616                                        char ch2 = name.charAt(1);
617                                        if ( ch2 == ':'
618                                                && ( (ch1 >= 'a' && ch1 <= 'z')
619                                                        || (ch1 >= 'A' && ch1 <= 'Z') ) )
620                                                {
621                                                name = name.substring( 2 );
622                                                }
623                                        }
624                                }
625                        }
626
627                name = name.replace( File.separatorChar, '/' );
628
629                // No absolute pathnames
630                // Windows (and Posix?) paths can start with "\\NetworkDrive\",
631                // so we loop on starting /'s.
632                
633                for ( ; name.startsWith( "/" ) ; )
634                        name = name.substring( 1 );
635
636                hdr.linkName = new StringBuffer( "" );
637
638                hdr.name = new StringBuffer( name );
639
640                if ( file.isDirectory() )
641                        {
642                        hdr.size = 0;
643                        hdr.mode = 040755;
644                        hdr.linkFlag = TarHeader.LF_DIR;
645                        if ( hdr.name.charAt( hdr.name.length() - 1 ) != '/' )
646                                hdr.name.append( "/" );
647                        }
648                else
649                        {
650                        hdr.size = file.length();
651                        hdr.mode = 0100644;
652                        hdr.linkFlag = TarHeader.LF_NORMAL;
653                        }
654
655                // UNDONE When File lets us get the userName, use it!
656
657                hdr.modTime = file.lastModified() / 1000;
658                hdr.checkSum = 0;
659                hdr.devMajor = 0;
660                hdr.devMinor = 0;
661                }
662
663        /**
664         * If this entry represents a file, and the file is a directory, return
665         * an array of TarEntries for this entry's children.
666         *
667         * @return An array of TarEntry's for this entry's children.
668         */
669        public TarEntry[]
670        getDirectoryEntries()
671                throws InvalidHeaderException
672                {
673                if ( this.file == null
674                                || ! this.file.isDirectory() )
675                        {
676                        return new TarEntry[0];
677                        }
678
679                String[] list = this.file.list();
680
681                TarEntry[] result = new TarEntry[ list.length ];
682
683                for ( int i = 0 ; i < list.length ; ++i )
684                        {
685                        result[i] =
686                                new TarEntry
687                                        ( new File( this.file, list[i] ) );
688                        }
689
690                return result;
691                }
692
693        /**
694         * Compute the checksum of a tar entry header.
695         *
696         * @param buf The tar entry's header buffer.
697         * @return The computed checksum.
698         */
699        public long
700        computeCheckSum( byte[] buf )
701                {
702                long sum = 0;
703
704                for ( int i = 0 ; i < buf.length ; ++i )
705                        {
706                        sum += 255 & buf[ i ];
707                        }
708
709                return sum;
710                }
711
712        /**
713         * Write an entry's header information to a header buffer.
714         * This method can throw an InvalidHeaderException
715         *
716         * @param outbuf The tar entry header buffer to fill in.
717         * @throws InvalidHeaderException If the name will not fit in the header.
718         */
719        public void
720        writeEntryHeader( byte[] outbuf )
721                throws InvalidHeaderException
722                {
723                int offset = 0;
724
725                if ( this.isUnixTarFormat() )
726                        {
727                        if ( this.header.name.length() > 100 )
728                                throw new InvalidHeaderException
729                                        ( "file path is greater than 100 characters, "
730                                                + this.header.name );
731                        }
732
733                offset = TarHeader.getFileNameBytes( this.header.name.toString(), outbuf );
734
735                offset = TarHeader.getOctalBytes
736                        ( this.header.mode, outbuf, offset, TarHeader.MODELEN );
737
738                offset = TarHeader.getOctalBytes
739                        ( this.header.userId, outbuf, offset, TarHeader.UIDLEN );
740
741                offset = TarHeader.getOctalBytes
742                        ( this.header.groupId, outbuf, offset, TarHeader.GIDLEN );
743
744                long size = this.header.size;
745
746                offset = TarHeader.getLongOctalBytes
747                        ( size, outbuf, offset, TarHeader.SIZELEN );
748
749                offset = TarHeader.getLongOctalBytes
750                        ( this.header.modTime, outbuf, offset, TarHeader.MODTIMELEN );
751
752                int csOffset = offset;
753                for ( int c = 0 ; c < TarHeader.CHKSUMLEN ; ++c )
754                        outbuf[ offset++ ] = (byte) ' ';
755
756                outbuf[ offset++ ] = this.header.linkFlag;
757
758                offset = TarHeader.getNameBytes
759                        ( this.header.linkName, outbuf, offset, TarHeader.NAMELEN );
760
761                if ( this.unixFormat )
762                        {
763                        for ( int i = 0 ; i < TarHeader.MAGICLEN ; ++i )
764                                outbuf[ offset++ ] = 0;
765                        }
766                else
767                        {
768                        offset = TarHeader.getNameBytes
769                                ( this.header.magic, outbuf, offset, TarHeader.MAGICLEN );
770                        }
771
772                offset = TarHeader.getNameBytes
773                        ( this.header.userName, outbuf, offset, TarHeader.UNAMELEN );
774
775                offset = TarHeader.getNameBytes
776                        ( this.header.groupName, outbuf, offset, TarHeader.GNAMELEN );
777
778                offset = TarHeader.getOctalBytes
779                        ( this.header.devMajor, outbuf, offset, TarHeader.DEVLEN );
780
781                offset = TarHeader.getOctalBytes
782                        ( this.header.devMinor, outbuf, offset, TarHeader.DEVLEN );
783
784                for ( ; offset < outbuf.length ; )
785                        outbuf[ offset++ ] = 0;
786
787                long checkSum = this.computeCheckSum( outbuf );
788
789                TarHeader.getCheckSumOctalBytes
790                        ( checkSum, outbuf, csOffset, TarHeader.CHKSUMLEN );
791                }
792
793        /**
794         * Parse an entry's TarHeader information from a header buffer.
795         *
796         * Old unix-style code contributed by David Mehringer <dmehring@astro.uiuc.edu>.
797         *
798         * @param hdr The TarHeader to fill in from the buffer information.
799         * @param header The tar entry header buffer to get information from.
800         */
801        public void
802        parseTarHeader( TarHeader hdr, byte[] headerBuf )
803                throws InvalidHeaderException
804                {
805                int offset = 0;
806
807                //
808                // NOTE Recognize archive header format.
809                //
810                if (       headerBuf[257] == 0
811                                && headerBuf[258] == 0
812                                && headerBuf[259] == 0
813                                && headerBuf[260] == 0
814                                && headerBuf[261] == 0 )
815                        {
816                        this.unixFormat = true;
817                        this.ustarFormat = false;
818                        this.gnuFormat = false;
819                        }
820                else if (  headerBuf[257] == 'u'
821                                && headerBuf[258] == 's'
822                                && headerBuf[259] == 't'
823                                && headerBuf[260] == 'a'
824                                && headerBuf[261] == 'r'
825                                && headerBuf[262] == 0 )
826                        {
827                        this.ustarFormat = true;
828                        this.gnuFormat = false;
829                        this.unixFormat = false;
830                        }
831                else if (  headerBuf[257] == 'u'
832                                && headerBuf[258] == 's'
833                                && headerBuf[259] == 't'
834                                && headerBuf[260] == 'a'
835                                && headerBuf[261] == 'r'
836                                && headerBuf[262] != 0
837                                && headerBuf[263] != 0 )
838                        {
839                        // REVIEW
840                        this.gnuFormat = true;
841                        this.unixFormat = false;
842                        this.ustarFormat = false;
843                        }
844                else
845                        {
846                        StringBuffer buf = new StringBuffer( 128 );
847
848                        buf.append( "header magic is not 'ustar' or unix-style zeros, it is '" );
849                        buf.append( headerBuf[257] );
850                        buf.append( headerBuf[258] );
851                        buf.append( headerBuf[259] );
852                        buf.append( headerBuf[260] );
853                        buf.append( headerBuf[261] );
854                        buf.append( headerBuf[262] );
855                        buf.append( headerBuf[263] );
856                        buf.append( "', or (dec) " );
857                        buf.append( (int)headerBuf[257] );
858                        buf.append( ", " );
859                        buf.append( (int)headerBuf[258] );
860                        buf.append( ", " );
861                        buf.append( (int)headerBuf[259] );
862                        buf.append( ", " );
863                        buf.append( (int)headerBuf[260] );
864                        buf.append( ", " );
865                        buf.append( (int)headerBuf[261] );
866                        buf.append( ", " );
867                        buf.append( (int)headerBuf[262] );
868                        buf.append( ", " );
869                        buf.append( (int)headerBuf[263] );
870
871                        throw new InvalidHeaderException( buf.toString() );
872                        }
873
874                hdr.name = TarHeader.parseFileName( headerBuf );
875
876                offset = TarHeader.NAMELEN;
877
878                hdr.mode = (int)
879                        TarHeader.parseOctal( headerBuf, offset, TarHeader.MODELEN );
880
881                offset += TarHeader.MODELEN;
882
883                hdr.userId = (int)
884                        TarHeader.parseOctal( headerBuf, offset, TarHeader.UIDLEN );
885
886                offset += TarHeader.UIDLEN;
887
888                hdr.groupId = (int)
889                        TarHeader.parseOctal( headerBuf, offset, TarHeader.GIDLEN );
890
891                offset += TarHeader.GIDLEN;
892
893                hdr.size =
894                        TarHeader.parseOctal( headerBuf, offset, TarHeader.SIZELEN );
895
896                offset += TarHeader.SIZELEN;
897
898                hdr.modTime =
899                        TarHeader.parseOctal( headerBuf, offset, TarHeader.MODTIMELEN );
900
901                offset += TarHeader.MODTIMELEN;
902
903                hdr.checkSum = (int)
904                        TarHeader.parseOctal( headerBuf, offset, TarHeader.CHKSUMLEN );
905
906                offset += TarHeader.CHKSUMLEN;
907
908                hdr.linkFlag = headerBuf[ offset++ ];
909
910                hdr.linkName =
911                        TarHeader.parseName( headerBuf, offset, TarHeader.NAMELEN );
912
913                offset += TarHeader.NAMELEN;
914
915                if ( this.ustarFormat )
916                        {
917                        hdr.magic =
918                                TarHeader.parseName( headerBuf, offset, TarHeader.MAGICLEN );
919
920                        offset += TarHeader.MAGICLEN;
921
922                        hdr.userName =
923                                TarHeader.parseName( headerBuf, offset, TarHeader.UNAMELEN );
924
925                        offset += TarHeader.UNAMELEN;
926
927                        hdr.groupName =
928                                TarHeader.parseName( headerBuf, offset, TarHeader.GNAMELEN );
929
930                        offset += TarHeader.GNAMELEN;
931
932                        hdr.devMajor = (int)
933                                TarHeader.parseOctal( headerBuf, offset, TarHeader.DEVLEN );
934
935                        offset += TarHeader.DEVLEN;
936
937                        hdr.devMinor = (int)
938                                TarHeader.parseOctal( headerBuf, offset, TarHeader.DEVLEN );
939                        }
940                else
941                        {
942                        hdr.devMajor = 0;
943                        hdr.devMinor = 0;
944                        hdr.magic = new StringBuffer( "" );
945                        hdr.userName = new StringBuffer( "" );
946                        hdr.groupName = new StringBuffer( "" );
947                        }
948                }
949
950        /**
951         * Fill in a TarHeader given only the entry's name.
952         *
953         * @param hdr The TarHeader to fill in.
954         * @param name The tar entry name.
955         */
956        public void
957        nameTarHeader( TarHeader hdr, String name )
958                {
959                boolean isDir = name.endsWith( "/" );
960
961                this.gnuFormat = false;
962                this.ustarFormat = true;
963                this.unixFormat = false;
964
965                hdr.checkSum = 0;
966                hdr.devMajor = 0;
967                hdr.devMinor = 0;
968
969                hdr.name = new StringBuffer( name );
970                hdr.mode = isDir ? 040755 : 0100644;
971                hdr.userId = 0;
972                hdr.groupId = 0;
973                hdr.size = 0;
974                hdr.checkSum = 0;
975
976                hdr.modTime =
977                        (new java.util.Date()).getTime() / 1000;
978
979                hdr.linkFlag =
980                        isDir ? TarHeader.LF_DIR : TarHeader.LF_NORMAL;
981
982                hdr.linkName = new StringBuffer( "" );
983                hdr.userName = new StringBuffer( "" );
984                hdr.groupName = new StringBuffer( "" );
985
986                hdr.devMajor = 0;
987                hdr.devMinor = 0;
988                }
989
990        public String
991        toString()
992                {
993                StringBuffer result = new StringBuffer( 128 );
994                return result.
995                        append( "[TarEntry name=" ).
996                        append( this.getName() ).
997                        append( ", isDir=" ).
998                        append( this.isDirectory() ).
999                        append( ", size=" ).
1000                        append( this.getSize() ).
1001                        append( ", userId=" ).
1002                        append( this.getUserId() ).
1003                        append( ", user=" ).
1004                        append( this.getUserName() ).
1005                        append( ", groupId=" ).
1006                        append( this.getGroupId() ).
1007                        append( ", group=" ).
1008                        append( this.getGroupName() ).
1009                        append( "]" ).
1010                        toString();
1011                }
1012
1013        }
1014