KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > tar > TarEntry


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */

18
19 /*
20  * This package is based on the work done by Timothy Gerard Endres
21  * (time@ice.com) to whom the Ant project is very grateful for his great code.
22  */

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

78
79 public class TarEntry implements TarConstants {
80     /** The entry's name. */
81     private StringBuffer JavaDoc name;
82
83     /** The entry's permission mode. */
84     private int mode;
85
86     /** The entry's user id. */
87     private int userId;
88
89     /** The entry's group id. */
90     private int groupId;
91
92     /** The entry's size. */
93     private long size;
94
95     /** The entry's modification time. */
96     private long modTime;
97
98     /** The entry's link flag. */
99     private byte linkFlag;
100
101     /** The entry's link name. */
102     private StringBuffer JavaDoc linkName;
103
104     /** The entry's magic tag. */
105     private StringBuffer JavaDoc magic;
106
107     /** The entry's user name. */
108     private StringBuffer JavaDoc userName;
109
110     /** The entry's group name. */
111     private StringBuffer JavaDoc groupName;
112
113     /** The entry's major device number. */
114     private int devMajor;
115
116     /** The entry's minor device number. */
117     private int devMinor;
118
119     /** The entry's file reference */
120     private File JavaDoc file;
121
122     /** Maximum length of a user's name in the tar file */
123     public static final int MAX_NAMELEN = 31;
124
125     /** Default permissions bits for directories */
126     public static final int DEFAULT_DIR_MODE = 040755;
127
128     /** Default permissions bits for files */
129     public static final int DEFAULT_FILE_MODE = 0100644;
130
131     /** Convert millis to seconds */
132     public static final int MILLIS_PER_SECOND = 1000;
133
134     /**
135      * Construct an empty entry and prepares the header values.
136      */

137     private TarEntry () {
138         this.magic = new StringBuffer JavaDoc(TMAGIC);
139         this.name = new StringBuffer JavaDoc();
140         this.linkName = new StringBuffer JavaDoc();
141
142         String JavaDoc user = System.getProperty("user.name", "");
143
144         if (user.length() > MAX_NAMELEN) {
145             user = user.substring(0, MAX_NAMELEN);
146         }
147
148         this.userId = 0;
149         this.groupId = 0;
150         this.userName = new StringBuffer JavaDoc(user);
151         this.groupName = new StringBuffer JavaDoc("");
152         this.file = null;
153     }
154
155     /**
156      * Construct an entry with only a name. This allows the programmer
157      * to construct the entry's header "by hand". File is set to null.
158      *
159      * @param name the entry name
160      */

161     public TarEntry(String JavaDoc name) {
162         this();
163
164         boolean isDir = name.endsWith("/");
165
166         this.devMajor = 0;
167         this.devMinor = 0;
168         this.name = new StringBuffer JavaDoc(name);
169         this.mode = isDir ? DEFAULT_DIR_MODE : DEFAULT_FILE_MODE;
170         this.linkFlag = isDir ? LF_DIR : LF_NORMAL;
171         this.userId = 0;
172         this.groupId = 0;
173         this.size = 0;
174         this.modTime = (new Date JavaDoc()).getTime() / MILLIS_PER_SECOND;
175         this.linkName = new StringBuffer JavaDoc("");
176         this.userName = new StringBuffer JavaDoc("");
177         this.groupName = new StringBuffer JavaDoc("");
178         this.devMajor = 0;
179         this.devMinor = 0;
180
181     }
182
183     /**
184      * Construct an entry with a name an a link flag.
185      *
186      * @param name the entry name
187      * @param linkFlag the entry link flag.
188      */

189     public TarEntry(String JavaDoc name, byte linkFlag) {
190         this(name);
191         this.linkFlag = linkFlag;
192     }
193
194     /**
195      * Construct an entry for a file. File is set to file, and the
196      * header is constructed from information from the file.
197      *
198      * @param file The file that the entry represents.
199      */

200     public TarEntry(File JavaDoc file) {
201         this();
202
203         this.file = file;
204
205         String JavaDoc fileName = file.getPath();
206         String JavaDoc osname = System.getProperty("os.name").toLowerCase(Locale.US);
207
208         if (osname != null) {
209
210             // Strip off drive letters!
211
// REVIEW Would a better check be "(File.separator == '\')"?
212

213             if (osname.startsWith("windows")) {
214                 if (fileName.length() > 2) {
215                     char ch1 = fileName.charAt(0);
216                     char ch2 = fileName.charAt(1);
217
218                     if (ch2 == ':'
219                             && ((ch1 >= 'a' && ch1 <= 'z')
220                                 || (ch1 >= 'A' && ch1 <= 'Z'))) {
221                         fileName = fileName.substring(2);
222                     }
223                 }
224             } else if (osname.indexOf("netware") > -1) {
225                 int colon = fileName.indexOf(':');
226                 if (colon != -1) {
227                     fileName = fileName.substring(colon + 1);
228                 }
229             }
230         }
231
232         fileName = fileName.replace(File.separatorChar, '/');
233
234         // No absolute pathnames
235
// Windows (and Posix?) paths can start with "\\NetworkDrive\",
236
// so we loop on starting /'s.
237
while (fileName.startsWith("/")) {
238             fileName = fileName.substring(1);
239         }
240
241         this.linkName = new StringBuffer JavaDoc("");
242         this.name = new StringBuffer JavaDoc(fileName);
243
244         if (file.isDirectory()) {
245             this.mode = DEFAULT_DIR_MODE;
246             this.linkFlag = LF_DIR;
247
248             if (this.name.charAt(this.name.length() - 1) != '/') {
249                 this.name.append("/");
250             }
251         } else {
252             this.mode = DEFAULT_FILE_MODE;
253             this.linkFlag = LF_NORMAL;
254         }
255
256         this.size = file.length();
257         this.modTime = file.lastModified() / MILLIS_PER_SECOND;
258         this.devMajor = 0;
259         this.devMinor = 0;
260     }
261
262     /**
263      * Construct an entry from an archive's header bytes. File is set
264      * to null.
265      *
266      * @param headerBuf The header bytes from a tar archive entry.
267      */

268     public TarEntry(byte[] headerBuf) {
269         this();
270         this.parseTarHeader(headerBuf);
271     }
272
273     /**
274      * Determine if the two entries are equal. Equality is determined
275      * by the header names being equal.
276      *
277      * @param it Entry to be checked for equality.
278      * @return True if the entries are equal.
279      */

280     public boolean equals(TarEntry it) {
281         return this.getName().equals(it.getName());
282     }
283
284     /**
285      * Determine if the two entries are equal. Equality is determined
286      * by the header names being equal.
287      *
288      * @param it Entry to be checked for equality.
289      * @return True if the entries are equal.
290      */

291     public boolean equals(Object JavaDoc it) {
292         if (it == null || getClass() != it.getClass()) {
293             return false;
294         }
295         return equals((TarEntry) it);
296     }
297
298     /**
299      * Hashcodes are based on entry names.
300      *
301      * @return the entry hashcode
302      */

303     public int hashCode() {
304         return getName().hashCode();
305     }
306
307     /**
308      * Determine if the given entry is a descendant of this entry.
309      * Descendancy is determined by the name of the descendant
310      * starting with this entry's name.
311      *
312      * @param desc Entry to be checked as a descendent of this.
313      * @return True if entry is a descendant of this.
314      */

315     public boolean isDescendent(TarEntry desc) {
316         return desc.getName().startsWith(this.getName());
317     }
318
319     /**
320      * Get this entry's name.
321      *
322      * @return This entry's name.
323      */

324     public String JavaDoc getName() {
325         return this.name.toString();
326     }
327
328     /**
329      * Set this entry's name.
330      *
331      * @param name This entry's new name.
332      */

333     public void setName(String JavaDoc name) {
334         this.name = new StringBuffer JavaDoc(name);
335     }
336
337     /**
338      * Set the mode for this entry
339      *
340      * @param mode the mode for this entry
341      */

342     public void setMode(int mode) {
343         this.mode = mode;
344     }
345
346     /**
347      * Get this entry's link name.
348      *
349      * @return This entry's link name.
350      */

351     public String JavaDoc getLinkName() {
352         return this.linkName.toString();
353     }
354
355     /**
356      * Get this entry's user id.
357      *
358      * @return This entry's user id.
359      */

360     public int getUserId() {
361         return this.userId;
362     }
363
364     /**
365      * Set this entry's user id.
366      *
367      * @param userId This entry's new user id.
368      */

369     public void setUserId(int userId) {
370         this.userId = userId;
371     }
372
373     /**
374      * Get this entry's group id.
375      *
376      * @return This entry's group id.
377      */

378     public int getGroupId() {
379         return this.groupId;
380     }
381
382     /**
383      * Set this entry's group id.
384      *
385      * @param groupId This entry's new group id.
386      */

387     public void setGroupId(int groupId) {
388         this.groupId = groupId;
389     }
390
391     /**
392      * Get this entry's user name.
393      *
394      * @return This entry's user name.
395      */

396     public String JavaDoc getUserName() {
397         return this.userName.toString();
398     }
399
400     /**
401      * Set this entry's user name.
402      *
403      * @param userName This entry's new user name.
404      */

405     public void setUserName(String JavaDoc userName) {
406         this.userName = new StringBuffer JavaDoc(userName);
407     }
408
409     /**
410      * Get this entry's group name.
411      *
412      * @return This entry's group name.
413      */

414     public String JavaDoc getGroupName() {
415         return this.groupName.toString();
416     }
417
418     /**
419      * Set this entry's group name.
420      *
421      * @param groupName This entry's new group name.
422      */

423     public void setGroupName(String JavaDoc groupName) {
424         this.groupName = new StringBuffer JavaDoc(groupName);
425     }
426
427     /**
428      * Convenience method to set this entry's group and user ids.
429      *
430      * @param userId This entry's new user id.
431      * @param groupId This entry's new group id.
432      */

433     public void setIds(int userId, int groupId) {
434         this.setUserId(userId);
435         this.setGroupId(groupId);
436     }
437
438     /**
439      * Convenience method to set this entry's group and user names.
440      *
441      * @param userName This entry's new user name.
442      * @param groupName This entry's new group name.
443      */

444     public void setNames(String JavaDoc userName, String JavaDoc groupName) {
445         this.setUserName(userName);
446         this.setGroupName(groupName);
447     }
448
449     /**
450      * Set this entry's modification time. The parameter passed
451      * to this method is in "Java time".
452      *
453      * @param time This entry's new modification time.
454      */

455     public void setModTime(long time) {
456         this.modTime = time / MILLIS_PER_SECOND;
457     }
458
459     /**
460      * Set this entry's modification time.
461      *
462      * @param time This entry's new modification time.
463      */

464     public void setModTime(Date JavaDoc time) {
465         this.modTime = time.getTime() / MILLIS_PER_SECOND;
466     }
467
468     /**
469      * Set this entry's modification time.
470      *
471      * @return time This entry's new modification time.
472      */

473     public Date JavaDoc getModTime() {
474         return new Date JavaDoc(this.modTime * MILLIS_PER_SECOND);
475     }
476
477     /**
478      * Get this entry's file.
479      *
480      * @return This entry's file.
481      */

482     public File JavaDoc getFile() {
483         return this.file;
484     }
485
486     /**
487      * Get this entry's mode.
488      *
489      * @return This entry's mode.
490      */

491     public int getMode() {
492         return this.mode;
493     }
494
495     /**
496      * Get this entry's file size.
497      *
498      * @return This entry's file size.
499      */

500     public long getSize() {
501         return this.size;
502     }
503
504     /**
505      * Set this entry's file size.
506      *
507      * @param size This entry's new file size.
508      */

509     public void setSize(long size) {
510         this.size = size;
511     }
512
513
514     /**
515      * Indicate if this entry is a GNU long name block
516      *
517      * @return true if this is a long name extension provided by GNU tar
518      */

519     public boolean isGNULongNameEntry() {
520         return linkFlag == LF_GNUTYPE_LONGNAME
521                            && name.toString().equals(GNU_LONGLINK);
522     }
523
524     /**
525      * Return whether or not this entry represents a directory.
526      *
527      * @return True if this entry is a directory.
528      */

529     public boolean isDirectory() {
530         if (this.file != null) {
531             return this.file.isDirectory();
532         }
533
534         if (this.linkFlag == LF_DIR) {
535             return true;
536         }
537
538         if (this.getName().endsWith("/")) {
539             return true;
540         }
541
542         return false;
543     }
544
545     /**
546      * If this entry represents a file, and the file is a directory, return
547      * an array of TarEntries for this entry's children.
548      *
549      * @return An array of TarEntry's for this entry's children.
550      */

551     public TarEntry[] getDirectoryEntries() {
552         if (this.file == null || !this.file.isDirectory()) {
553             return new TarEntry[0];
554         }
555
556         String JavaDoc[] list = this.file.list();
557         TarEntry[] result = new TarEntry[list.length];
558
559         for (int i = 0; i < list.length; ++i) {
560             result[i] = new TarEntry(new File JavaDoc(this.file, list[i]));
561         }
562
563         return result;
564     }
565
566     /**
567      * Write an entry's header information to a header buffer.
568      *
569      * @param outbuf The tar entry header buffer to fill in.
570      */

571     public void writeEntryHeader(byte[] outbuf) {
572         int offset = 0;
573
574         offset = TarUtils.getNameBytes(this.name, outbuf, offset, NAMELEN);
575         offset = TarUtils.getOctalBytes(this.mode, outbuf, offset, MODELEN);
576         offset = TarUtils.getOctalBytes(this.userId, outbuf, offset, UIDLEN);
577         offset = TarUtils.getOctalBytes(this.groupId, outbuf, offset, GIDLEN);
578         offset = TarUtils.getLongOctalBytes(this.size, outbuf, offset, SIZELEN);
579         offset = TarUtils.getLongOctalBytes(this.modTime, outbuf, offset, MODTIMELEN);
580
581         int csOffset = offset;
582
583         for (int c = 0; c < CHKSUMLEN; ++c) {
584             outbuf[offset++] = (byte) ' ';
585         }
586
587         outbuf[offset++] = this.linkFlag;
588         offset = TarUtils.getNameBytes(this.linkName, outbuf, offset, NAMELEN);
589         offset = TarUtils.getNameBytes(this.magic, outbuf, offset, MAGICLEN);
590         offset = TarUtils.getNameBytes(this.userName, outbuf, offset, UNAMELEN);
591         offset = TarUtils.getNameBytes(this.groupName, outbuf, offset, GNAMELEN);
592         offset = TarUtils.getOctalBytes(this.devMajor, outbuf, offset, DEVLEN);
593         offset = TarUtils.getOctalBytes(this.devMinor, outbuf, offset, DEVLEN);
594
595         while (offset < outbuf.length) {
596             outbuf[offset++] = 0;
597         }
598
599         long chk = TarUtils.computeCheckSum(outbuf);
600
601         TarUtils.getCheckSumOctalBytes(chk, outbuf, csOffset, CHKSUMLEN);
602     }
603
604     /**
605      * Parse an entry's header information from a header buffer.
606      *
607      * @param header The tar entry header buffer to get information from.
608      */

609     public void parseTarHeader(byte[] header) {
610         int offset = 0;
611
612         this.name = TarUtils.parseName(header, offset, NAMELEN);
613         offset += NAMELEN;
614         this.mode = (int) TarUtils.parseOctal(header, offset, MODELEN);
615         offset += MODELEN;
616         this.userId = (int) TarUtils.parseOctal(header, offset, UIDLEN);
617         offset += UIDLEN;
618         this.groupId = (int) TarUtils.parseOctal(header, offset, GIDLEN);
619         offset += GIDLEN;
620         this.size = TarUtils.parseOctal(header, offset, SIZELEN);
621         offset += SIZELEN;
622         this.modTime = TarUtils.parseOctal(header, offset, MODTIMELEN);
623         offset += MODTIMELEN;
624         offset += CHKSUMLEN;
625         this.linkFlag = header[offset++];
626         this.linkName = TarUtils.parseName(header, offset, NAMELEN);
627         offset += NAMELEN;
628         this.magic = TarUtils.parseName(header, offset, MAGICLEN);
629         offset += MAGICLEN;
630         this.userName = TarUtils.parseName(header, offset, UNAMELEN);
631         offset += UNAMELEN;
632         this.groupName = TarUtils.parseName(header, offset, GNAMELEN);
633         offset += GNAMELEN;
634         this.devMajor = (int) TarUtils.parseOctal(header, offset, DEVLEN);
635         offset += DEVLEN;
636         this.devMinor = (int) TarUtils.parseOctal(header, offset, DEVLEN);
637     }
638 }
639
Popular Tags