KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > installer > TarEntry


1 /*
2 ** Authored by Timothy Gerard Endres
3 ** <mailto:time@gjt.org> <http://www.trustice.com>
4 **
5 ** This work has been placed into the public domain.
6 ** You may use this work in any way and for any purpose you wish.
7 **
8 ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
9 ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
10 ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
11 ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
12 ** REDISTRIBUTION OF THIS SOFTWARE.
13 **
14 */

15
16 package installer;
17
18 import java.io.*;
19 import java.util.Date JavaDoc;
20
21
22 /**
23  *
24  * This class represents an entry in a Tar archive. It consists
25  * of the entry's header, as well as the entry's File. Entries
26  * can be instantiated in one of three ways, depending on how
27  * they are to be used.
28  * <p>
29  * TarEntries that are created from the header bytes read from
30  * an archive are instantiated with the TarEntry( byte[] )
31  * constructor. These entries will be used when extracting from
32  * or listing the contents of an archive. These entries have their
33  * header filled in using the header bytes. They also set the File
34  * to null, since they reference an archive entry not a file.
35  * <p>
36  * TarEntries that are created from Files that are to be written
37  * into an archive are instantiated with the TarEntry( File )
38  * constructor. These entries have their header filled in using
39  * the File's information. They also keep a reference to the File
40  * for convenience when writing entries.
41  * <p>
42  * Finally, TarEntries can be constructed from nothing but a name.
43  * This allows the programmer to construct the entry by hand, for
44  * instance when only an InputStream is available for writing to
45  * the archive, and the header information is constructed from
46  * other information. In this case the header fields are set to
47  * defaults and the File is set to null.
48  *
49  * <p>
50  * The C structure for a Tar Entry's header is:
51  * <pre>
52  * struct header {
53  * char name[NAMSIZ];
54  * char mode[8];
55  * char uid[8];
56  * char gid[8];
57  * char size[12];
58  * char mtime[12];
59  * char chksum[8];
60  * char linkflag;
61  * char linkname[NAMSIZ];
62  * char magic[8];
63  * char uname[TUNMLEN];
64  * char gname[TGNMLEN];
65  * char devmajor[8];
66  * char devminor[8];
67  * } header;
68  * </pre>
69  *
70  * @see TarHeader
71  *
72  */

73
74 public
75 class TarEntry
76 extends Object JavaDoc
77     {
78     /**
79      * If this entry represents a File, this references it.
80      */

81     protected File file;
82
83     /**
84      * This is the entry's header information.
85      */

86     protected TarHeader header;
87
88     /**
89      * Construct an entry with only a name. This allows the programmer
90      * to construct the entry's header "by hand". File is set to null.
91      */

92     public
93     TarEntry( String JavaDoc name )
94         {
95         this.initialize();
96         this.nameTarHeader( this.header, name );
97         }
98
99     /**
100      * Construct an entry for a file. File is set to file, and the
101      * header is constructed from information from the file.
102      *
103      * @param file The file that the entry represents.
104      */

105     public
106     TarEntry( File file )
107         throws InvalidHeaderException
108         {
109         this.initialize();
110         this.getFileTarHeader( this.header, file );
111         }
112
113     /**
114      * Construct an entry from an archive's header bytes. File is set
115      * to null.
116      *
117      * @param headerBuf The header bytes from a tar archive entry.
118      */

119     public
120     TarEntry( byte[] headerBuf )
121         throws InvalidHeaderException
122         {
123         this.initialize();
124         this.parseTarHeader( this.header, headerBuf );
125         }
126
127     /**
128      * Initialization code common to all constructors.
129      */

130     private void
131     initialize()
132         {
133         this.file = null;
134         this.header = new TarHeader();
135         }
136
137     /**
138      * Determine if the two entries are equal. Equality is determined
139      * by the header names being equal.
140      *
141      * @return it Entry to be checked for equality.
142      * @return True if the entries are equal.
143      */

144     public boolean
145     equals( TarEntry it )
146         {
147         return
148             this.header.name.toString().equals
149                 ( it.header.name.toString() );
150         }
151
152     /**
153      * Determine if the given entry is a descendant of this entry.
154      * Descendancy is determined by the name of the descendant
155      * starting with this entry's name.
156      *
157      * @param desc Entry to be checked as a descendent of this.
158      * @return True if entry is a descendant of this.
159      */

160     public boolean
161     isDescendent( TarEntry desc )
162         {
163         return
164             desc.header.name.toString().startsWith
165                 ( this.header.name.toString() );
166         }
167
168     /**
169      * Get this entry's header.
170      *
171      * @return This entry's TarHeader.
172      */

173     public TarHeader
174     getHeader()
175         {
176         return this.header;
177         }
178
179     /**
180      * Get this entry's name.
181      *
182      * @return This entry's name.
183      */

184     public String JavaDoc
185     getName()
186         {
187         return this.header.name.toString();
188         }
189
190     /**
191      * Set this entry's name.
192      *
193      * @param name This entry's new name.
194      */

195     public void
196     setName( String JavaDoc name )
197         {
198         this.header.name =
199             new StringBuffer JavaDoc( name );
200         }
201
202     /**
203      * Get this entry's user id.
204      *
205      * @return This entry's user id.
206      */

207     public int
208     getUserId()
209         {
210         return this.header.userId;
211         }
212
213     /**
214      * Set this entry's user id.
215      *
216      * @param userId This entry's new user id.
217      */

218     public void
219     setUserId( int userId )
220         {
221         this.header.userId = userId;
222         }
223
224     /**
225      * Get this entry's group id.
226      *
227      * @return This entry's group id.
228      */

229     public int
230     getGroupId()
231         {
232         return this.header.groupId;
233         }
234
235     /**
236      * Set this entry's group id.
237      *
238      * @param groupId This entry's new group id.
239      */

240     public void
241     setGroupId( int groupId )
242         {
243         this.header.groupId = groupId;
244         }
245
246     /**
247      * Get this entry's user name.
248      *
249      * @return This entry's user name.
250      */

251     public String JavaDoc
252     getUserName()
253         {
254         return this.header.userName.toString();
255         }
256
257     /**
258      * Set this entry's user name.
259      *
260      * @param userName This entry's new user name.
261      */

262     public void
263     setUserName( String JavaDoc userName )
264         {
265         this.header.userName =
266             new StringBuffer JavaDoc( userName );
267         }
268
269     /**
270      * Get this entry's group name.
271      *
272      * @return This entry's group name.
273      */

274     public String JavaDoc
275     getGroupName()
276         {
277         return this.header.groupName.toString();
278         }
279
280     /**
281      * Set this entry's group name.
282      *
283      * @param groupName This entry's new group name.
284      */

285     public void
286     setGroupName( String JavaDoc groupName )
287         {
288         this.header.groupName =
289             new StringBuffer JavaDoc( groupName );
290         }
291
292     /**
293      * Convenience method to set this entry's group and user ids.
294      *
295      * @param userId This entry's new user id.
296      * @param groupId This entry's new group id.
297      */

298     public void
299     setIds( int userId, int groupId )
300         {
301         this.setUserId( userId );
302         this.setGroupId( groupId );
303         }
304
305     /**
306      * Convenience method to set this entry's group and user names.
307      *
308      * @param userName This entry's new user name.
309      * @param groupName This entry's new group name.
310      */

311     public void
312     setNames( String JavaDoc userName, String JavaDoc groupName )
313         {
314         this.setUserName( userName );
315         this.setGroupName( groupName );
316         }
317
318     /**
319      * Set this entry's modification time. The parameter passed
320      * to this method is in "Java time".
321      *
322      * @param time This entry's new modification time.
323      */

324     public void
325     setModTime( long time )
326         {
327         this.header.modTime = time / 1000;
328         }
329
330     /**
331      * Set this entry's modification time.
332      *
333      * @param time This entry's new modification time.
334      */

335     public void
336     setModTime( Date JavaDoc time )
337         {
338         this.header.modTime = time.getTime() / 1000;
339         }
340
341     /**
342      * Set this entry's modification time.
343      *
344      * @param time This entry's new modification time.
345      */

346     public Date JavaDoc
347     getModTime()
348         {
349         return new Date JavaDoc( this.header.modTime * 1000 );
350         }
351
352     /**
353      * Get this entry's file.
354      *
355      * @return This entry's file.
356      */

357     public File
358     getFile()
359         {
360         return this.file;
361         }
362
363     /**
364      * Get this entry's file size.
365      *
366      * @return This entry's file size.
367      */

368     public long
369     getSize()
370         {
371         return this.header.size;
372         }
373
374     /**
375      * Set this entry's file size.
376      *
377      * @param size This entry's new file size.
378      */

379     public void
380     setSize( long size )
381         {
382         this.header.size = size;
383         }
384
385     /**
386      * Convenience method that will modify an entry's name directly
387      * in place in an entry header buffer byte array.
388      *
389      * @param outbuf The buffer containing the entry header to modify.
390      * @param newName The new name to place into the header buffer.
391      */

392     public void
393     adjustEntryName( byte[] outbuf, String JavaDoc newName )
394         {
395         int offset = 0;
396         offset = TarHeader.getNameBytes
397             ( new StringBuffer JavaDoc( newName ),
398                 outbuf, offset, TarHeader.NAMELEN );
399         }
400
401     /**
402      * Return whether or not this entry represents a directory.
403      *
404      * @return True if this entry is a directory.
405      */

406     public boolean
407     isDirectory()
408         {
409         if ( this.file != null )
410             return this.file.isDirectory();
411
412         if ( this.header != null )
413             {
414             if ( this.header.linkFlag == TarHeader.LF_DIR )
415                 return true;
416
417             if ( this.header.name.toString().endsWith( "/" ) )
418                 return true;
419             }
420
421         return false;
422         }
423
424     /**
425      * Fill in a TarHeader with information from a File.
426      *
427      * @param hdr The TarHeader to fill in.
428      * @param file The file from which to get the header information.
429      */

430     public void
431     getFileTarHeader( TarHeader hdr, File file )
432         throws InvalidHeaderException
433         {
434         this.file = file;
435
436         String JavaDoc name = file.getPath();
437         String JavaDoc osname = System.getProperty( "os.name" );
438         if ( osname != null )
439             {
440             // Strip off drive letters!
441
// REVIEW Would a better check be "(File.separator == '\')"?
442

443             // String Win32Prefix = "Windows";
444
// String prefix = osname.substring( 0, Win32Prefix.length() );
445
// if ( prefix.equalsIgnoreCase( Win32Prefix ) )
446

447             // if ( File.separatorChar == '\\' )
448

449             // Per Patrick Beard:
450
String JavaDoc Win32Prefix = "windows";
451             if ( osname.toLowerCase().startsWith( Win32Prefix ) )
452                 {
453                 if ( name.length() > 2 )
454                     {
455                     char ch1 = name.charAt(0);
456                     char ch2 = name.charAt(1);
457                     if ( ch2 == ':'
458                         && ( (ch1 >= 'a' && ch1 <= 'z')
459                             || (ch1 >= 'A' && ch1 <= 'Z') ) )
460                         {
461                         name = name.substring( 2 );
462                         }
463                     }
464                 }
465             }
466
467         name = name.replace( File.separatorChar, '/' );
468
469         // No absolute pathnames
470
// Windows (and Posix?) paths can start with "\\NetworkDrive\",
471
// so we loop on starting /'s.
472

473         for ( ; name.startsWith( "/" ) ; )
474             name = name.substring( 1 );
475
476         hdr.linkName = new StringBuffer JavaDoc( "" );
477
478         hdr.name = new StringBuffer JavaDoc( name );
479
480         if ( file.isDirectory() )
481             {
482             hdr.mode = 040755;
483             hdr.linkFlag = TarHeader.LF_DIR;
484             if ( hdr.name.charAt( hdr.name.length() - 1 ) != '/' )
485                 hdr.name.append( "/" );
486             }
487         else
488             {
489             hdr.mode = 0100644;
490             hdr.linkFlag = TarHeader.LF_NORMAL;
491             }
492
493         // UNDONE When File lets us get the userName, use it!
494

495         hdr.size = file.length();
496         hdr.modTime = file.lastModified() / 1000;
497         hdr.checkSum = 0;
498         hdr.devMajor = 0;
499         hdr.devMinor = 0;
500         }
501
502     /**
503      * If this entry represents a file, and the file is a directory, return
504      * an array of TarEntries for this entry's children.
505      *
506      * @return An array of TarEntry's for this entry's children.
507      */

508     public TarEntry[]
509     getDirectoryEntries()
510         throws InvalidHeaderException
511         {
512         if ( this.file == null
513                 || ! this.file.isDirectory() )
514             {
515             return new TarEntry[0];
516             }
517
518         String JavaDoc[] list = this.file.list();
519
520         TarEntry[] result = new TarEntry[ list.length ];
521
522         for ( int i = 0 ; i < list.length ; ++i )
523             {
524             result[i] =
525                 new TarEntry
526                     ( new File( this.file, list[i] ) );
527             }
528
529         return result;
530         }
531
532     /**
533      * Compute the checksum of a tar entry header.
534      *
535      * @param buf The tar entry's header buffer.
536      * @return The computed checksum.
537      */

538     public long
539     computeCheckSum( byte[] buf )
540         {
541         long sum = 0;
542
543         for ( int i = 0 ; i < buf.length ; ++i )
544             {
545             sum += 255 & buf[ i ];
546             }
547
548         return sum;
549         }
550
551     /**
552      * Write an entry's header information to a header buffer.
553      *
554      * @param outbuf The tar entry header buffer to fill in.
555      */

556     public void
557     writeEntryHeader( byte[] outbuf )
558         {
559         int offset = 0;
560
561         offset = TarHeader.getNameBytes
562             ( this.header.name, outbuf, offset, TarHeader.NAMELEN );
563
564         offset = TarHeader.getOctalBytes
565             ( this.header.mode, outbuf, offset, TarHeader.MODELEN );
566
567         offset = TarHeader.getOctalBytes
568             ( this.header.userId, outbuf, offset, TarHeader.UIDLEN );
569
570         offset = TarHeader.getOctalBytes
571             ( this.header.groupId, outbuf, offset, TarHeader.GIDLEN );
572
573         long size = this.header.size;
574
575         offset = TarHeader.getLongOctalBytes
576             ( size, outbuf, offset, TarHeader.SIZELEN );
577
578         offset = TarHeader.getLongOctalBytes
579             ( this.header.modTime, outbuf, offset, TarHeader.MODTIMELEN );
580
581         int csOffset = offset;
582         for ( int c = 0 ; c < TarHeader.CHKSUMLEN ; ++c )
583             outbuf[ offset++ ] = (byte) ' ';
584
585         outbuf[ offset++ ] = this.header.linkFlag;
586
587         offset = TarHeader.getNameBytes
588             ( this.header.linkName, outbuf, offset, TarHeader.NAMELEN );
589
590         offset = TarHeader.getNameBytes
591             ( this.header.magic, outbuf, offset, TarHeader.MAGICLEN );
592
593         offset = TarHeader.getNameBytes
594             ( this.header.userName, outbuf, offset, TarHeader.UNAMELEN );
595
596         offset = TarHeader.getNameBytes
597             ( this.header.groupName, outbuf, offset, TarHeader.GNAMELEN );
598
599         offset = TarHeader.getOctalBytes
600             ( this.header.devMajor, outbuf, offset, TarHeader.DEVLEN );
601
602         offset = TarHeader.getOctalBytes
603             ( this.header.devMinor, outbuf, offset, TarHeader.DEVLEN );
604
605         for ( ; offset < outbuf.length ; )
606             outbuf[ offset++ ] = 0;
607
608         long checkSum = this.computeCheckSum( outbuf );
609
610         TarHeader.getCheckSumOctalBytes
611             ( checkSum, outbuf, csOffset, TarHeader.CHKSUMLEN );
612         }
613
614     /**
615      * Parse an entry's TarHeader information from a header buffer.
616      *
617      * @param hdr The TarHeader to fill in from the buffer information.
618      * @param header The tar entry header buffer to get information from.
619      */

620     public void
621     parseTarHeader( TarHeader hdr, byte[] header )
622         throws InvalidHeaderException
623         {
624         int offset = 0;
625
626         hdr.name =
627             TarHeader.parseName( header, offset, TarHeader.NAMELEN );
628
629         offset += TarHeader.NAMELEN;
630
631         hdr.mode = (int)
632             TarHeader.parseOctal( header, offset, TarHeader.MODELEN );
633
634         offset += TarHeader.MODELEN;
635
636         hdr.userId = (int)
637             TarHeader.parseOctal( header, offset, TarHeader.UIDLEN );
638
639         offset += TarHeader.UIDLEN;
640
641         hdr.groupId = (int)
642             TarHeader.parseOctal( header, offset, TarHeader.GIDLEN );
643
644         offset += TarHeader.GIDLEN;
645
646         hdr.size =
647             TarHeader.parseOctal( header, offset, TarHeader.SIZELEN );
648
649         offset += TarHeader.SIZELEN;
650
651         hdr.modTime =
652             TarHeader.parseOctal( header, offset, TarHeader.MODTIMELEN );
653
654         offset += TarHeader.MODTIMELEN;
655
656         hdr.checkSum = (int)
657             TarHeader.parseOctal( header, offset, TarHeader.CHKSUMLEN );
658
659         offset += TarHeader.CHKSUMLEN;
660
661         hdr.linkFlag = header[ offset++ ];
662
663         hdr.linkName =
664             TarHeader.parseName( header, offset, TarHeader.NAMELEN );
665
666         offset += TarHeader.NAMELEN;
667
668         hdr.magic =
669             TarHeader.parseName( header, offset, TarHeader.MAGICLEN );
670
671         offset += TarHeader.MAGICLEN;
672
673         hdr.userName =
674             TarHeader.parseName( header, offset, TarHeader.UNAMELEN );
675
676         offset += TarHeader.UNAMELEN;
677
678         hdr.groupName =
679             TarHeader.parseName( header, offset, TarHeader.GNAMELEN );
680
681         offset += TarHeader.GNAMELEN;
682
683         hdr.devMajor = (int)
684             TarHeader.parseOctal( header, offset, TarHeader.DEVLEN );
685
686         offset += TarHeader.DEVLEN;
687
688         hdr.devMinor = (int)
689             TarHeader.parseOctal( header, offset, TarHeader.DEVLEN );
690         }
691
692     /**
693      * Fill in a TarHeader given only the entry's name.
694      *
695      * @param hdr The TarHeader to fill in.
696      * @param name The tar entry name.
697      */

698     public void
699     nameTarHeader( TarHeader hdr, String JavaDoc name )
700         {
701         boolean isDir = name.endsWith( "/" );
702
703         hdr.checkSum = 0;
704         hdr.devMajor = 0;
705         hdr.devMinor = 0;
706
707         hdr.name = new StringBuffer JavaDoc( name );
708         hdr.mode = isDir ? 040755 : 0100644;
709         hdr.userId = 0;
710         hdr.groupId = 0;
711         hdr.size = 0;
712         hdr.checkSum = 0;
713
714         hdr.modTime =
715             (new java.util.Date JavaDoc()).getTime() / 1000;
716
717         hdr.linkFlag =
718             isDir ? TarHeader.LF_DIR : TarHeader.LF_NORMAL;
719
720         hdr.linkName = new StringBuffer JavaDoc( "" );
721         hdr.userName = new StringBuffer JavaDoc( "" );
722         hdr.groupName = new StringBuffer JavaDoc( "" );
723
724         hdr.devMajor = 0;
725         hdr.devMinor = 0;
726         }
727
728     }
729
730
Popular Tags