source: other-projects/trunk/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/tar/TarEntry.java@ 14627

Last change on this file since 14627 was 14627, checked in by oranfry, 17 years ago

initial import of the gs3-release-maker

File size: 18.4 KB
Line 
1/*
2 * Copyright 2000-2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18/*
19 * This package is based on the work done by Timothy Gerard Endres
20 * ([email protected]) to whom the Ant project is very grateful for his great code.
21 */
22
23package org.apache.tools.tar;
24
25import java.io.File;
26import java.util.Date;
27import java.util.Locale;
28
29/**
30 * This class represents an entry in a Tar archive. It consists
31 * of the entry's header, as well as the entry's File. Entries
32 * can be instantiated in one of three ways, depending on how
33 * they are to be used.
34 * <p>
35 * TarEntries that are created from the header bytes read from
36 * an archive are instantiated with the TarEntry( byte[] )
37 * constructor. These entries will be used when extracting from
38 * or listing the contents of an archive. These entries have their
39 * header filled in using the header bytes. They also set the File
40 * to null, since they reference an archive entry not a file.
41 * <p>
42 * TarEntries that are created from Files that are to be written
43 * into an archive are instantiated with the TarEntry( File )
44 * constructor. These entries have their header filled in using
45 * the File's information. They also keep a reference to the File
46 * for convenience when writing entries.
47 * <p>
48 * Finally, TarEntries can be constructed from nothing but a name.
49 * This allows the programmer to construct the entry by hand, for
50 * instance when only an InputStream is available for writing to
51 * the archive, and the header information is constructed from
52 * other information. In this case the header fields are set to
53 * defaults and the File is set to null.
54 *
55 * <p>
56 * The C structure for a Tar Entry's header is:
57 * <pre>
58 * struct header {
59 * char name[NAMSIZ];
60 * char mode[8];
61 * char uid[8];
62 * char gid[8];
63 * char size[12];
64 * char mtime[12];
65 * char chksum[8];
66 * char linkflag;
67 * char linkname[NAMSIZ];
68 * char magic[8];
69 * char uname[TUNMLEN];
70 * char gname[TGNMLEN];
71 * char devmajor[8];
72 * char devminor[8];
73 * } header;
74 * </pre>
75 *
76 */
77
78public class TarEntry implements TarConstants {
79 /** The entry's name. */
80 private StringBuffer name;
81
82 /** The entry's permission mode. */
83 private int mode;
84
85 /** The entry's user id. */
86 private int userId;
87
88 /** The entry's group id. */
89 private int groupId;
90
91 /** The entry's size. */
92 private long size;
93
94 /** The entry's modification time. */
95 private long modTime;
96
97 /** The entry's checksum. */
98 private int checkSum;
99
100 /** The entry's link flag. */
101 private byte linkFlag;
102
103 /** The entry's link name. */
104 private StringBuffer linkName;
105
106 /** The entry's magic tag. */
107 private StringBuffer magic;
108
109 /** The entry's user name. */
110 private StringBuffer userName;
111
112 /** The entry's group name. */
113 private StringBuffer groupName;
114
115 /** The entry's major device number. */
116 private int devMajor;
117
118 /** The entry's minor device number. */
119 private int devMinor;
120
121 /** The entry's file reference */
122 private File file;
123
124 /** Maximum length of a user's name in the tar file */
125 public static final int MAX_NAMELEN = 31;
126
127 /** Default permissions bits for directories */
128 public static final int DEFAULT_DIR_MODE = 040755;
129
130 /** Default permissions bits for files */
131 public static final int DEFAULT_FILE_MODE = 0100644;
132
133 /** Convert millis to seconds */
134 public static final int MILLIS_PER_SECOND = 1000;
135
136 /**
137 * Construct an empty entry and prepares the header values.
138 */
139 private TarEntry () {
140 this.magic = new StringBuffer(TMAGIC);
141 this.name = new StringBuffer();
142 this.linkName = new StringBuffer();
143
144 String user = System.getProperty("user.name", "");
145
146 if (user.length() > MAX_NAMELEN) {
147 user = user.substring(0, MAX_NAMELEN);
148 }
149
150 this.userId = 0;
151 this.groupId = 0;
152 this.userName = new StringBuffer(user);
153 this.groupName = new StringBuffer("");
154 this.file = null;
155 }
156
157 /**
158 * Construct an entry with only a name. This allows the programmer
159 * to construct the entry's header "by hand". File is set to null.
160 *
161 * @param name the entry name
162 */
163 public TarEntry(String name) {
164 this();
165
166 boolean isDir = name.endsWith("/");
167
168 this.devMajor = 0;
169 this.devMinor = 0;
170 this.name = new StringBuffer(name);
171 this.mode = isDir ? DEFAULT_DIR_MODE : DEFAULT_FILE_MODE;
172 this.linkFlag = isDir ? LF_DIR : LF_NORMAL;
173 this.userId = 0;
174 this.groupId = 0;
175 this.size = 0;
176 this.checkSum = 0;
177 this.modTime = (new Date()).getTime() / MILLIS_PER_SECOND;
178 this.linkName = new StringBuffer("");
179 this.userName = new StringBuffer("");
180 this.groupName = new StringBuffer("");
181 this.devMajor = 0;
182 this.devMinor = 0;
183
184 }
185
186 /**
187 * Construct an entry with a name an a link flag.
188 *
189 * @param name the entry name
190 * @param linkFlag the entry link flag.
191 */
192 public TarEntry(String name, byte linkFlag) {
193 this(name);
194 this.linkFlag = linkFlag;
195 }
196
197 /**
198 * Construct an entry for a file. File is set to file, and the
199 * header is constructed from information from the file.
200 *
201 * @param file The file that the entry represents.
202 */
203 public TarEntry(File file) {
204 this();
205
206 this.file = file;
207
208 String name = file.getPath();
209 String osname = System.getProperty("os.name").toLowerCase(Locale.US);
210
211 if (osname != null) {
212
213 // Strip off drive letters!
214 // REVIEW Would a better check be "(File.separator == '\')"?
215
216 if (osname.startsWith("windows")) {
217 if (name.length() > 2) {
218 char ch1 = name.charAt(0);
219 char ch2 = name.charAt(1);
220
221 if (ch2 == ':'
222 && ((ch1 >= 'a' && ch1 <= 'z')
223 || (ch1 >= 'A' && ch1 <= 'Z'))) {
224 name = name.substring(2);
225 }
226 }
227 } else if (osname.indexOf("netware") > -1) {
228 int colon = name.indexOf(':');
229 if (colon != -1) {
230 name = name.substring(colon + 1);
231 }
232 }
233 }
234
235 name = name.replace(File.separatorChar, '/');
236
237 // No absolute pathnames
238 // Windows (and Posix?) paths can start with "\\NetworkDrive\",
239 // so we loop on starting /'s.
240 while (name.startsWith("/")) {
241 name = name.substring(1);
242 }
243
244 this.linkName = new StringBuffer("");
245 this.name = new StringBuffer(name);
246
247 if (file.isDirectory()) {
248 this.mode = DEFAULT_DIR_MODE;
249 this.linkFlag = LF_DIR;
250
251 if (this.name.charAt(this.name.length() - 1) != '/') {
252 this.name.append("/");
253 }
254 } else {
255 this.mode = DEFAULT_FILE_MODE;
256 this.linkFlag = LF_NORMAL;
257 }
258
259 this.size = file.length();
260 this.modTime = file.lastModified() / MILLIS_PER_SECOND;
261 this.checkSum = 0;
262 this.devMajor = 0;
263 this.devMinor = 0;
264 }
265
266 /**
267 * Construct an entry from an archive's header bytes. File is set
268 * to null.
269 *
270 * @param headerBuf The header bytes from a tar archive entry.
271 */
272 public TarEntry(byte[] headerBuf) {
273 this();
274 this.parseTarHeader(headerBuf);
275 }
276
277 /**
278 * Determine if the two entries are equal. Equality is determined
279 * by the header names being equal.
280 *
281 * @param it Entry to be checked for equality.
282 * @return True if the entries are equal.
283 */
284 public boolean equals(TarEntry it) {
285 return this.getName().equals(it.getName());
286 }
287
288 /**
289 * Determine if the two entries are equal. Equality is determined
290 * by the header names being equal.
291 *
292 * @param it Entry to be checked for equality.
293 * @return True if the entries are equal.
294 */
295 public boolean equals(Object it) {
296 if (it == null || getClass() != it.getClass()) {
297 return false;
298 }
299 return equals((TarEntry) it);
300 }
301
302 /**
303 * Hashcodes are based on entry names.
304 *
305 * @return the entry hashcode
306 */
307 public int hashCode() {
308 return getName().hashCode();
309 }
310
311 /**
312 * Determine if the given entry is a descendant of this entry.
313 * Descendancy is determined by the name of the descendant
314 * starting with this entry's name.
315 *
316 * @param desc Entry to be checked as a descendent of this.
317 * @return True if entry is a descendant of this.
318 */
319 public boolean isDescendent(TarEntry desc) {
320 return desc.getName().startsWith(this.getName());
321 }
322
323 /**
324 * Get this entry's name.
325 *
326 * @return This entry's name.
327 */
328 public String getName() {
329 return this.name.toString();
330 }
331
332 /**
333 * Set this entry's name.
334 *
335 * @param name This entry's new name.
336 */
337 public void setName(String name) {
338 this.name = new StringBuffer(name);
339 }
340
341 /**
342 * Set the mode for this entry
343 *
344 * @param mode the mode for this entry
345 */
346 public void setMode(int mode) {
347 this.mode = mode;
348 }
349
350 /**
351 * Get this entry's link name.
352 *
353 * @return This entry's link name.
354 */
355 public String getLinkName() {
356 return this.linkName.toString();
357 }
358
359 /**
360 * Get this entry's user id.
361 *
362 * @return This entry's user id.
363 */
364 public int getUserId() {
365 return this.userId;
366 }
367
368 /**
369 * Set this entry's user id.
370 *
371 * @param userId This entry's new user id.
372 */
373 public void setUserId(int userId) {
374 this.userId = userId;
375 }
376
377 /**
378 * Get this entry's group id.
379 *
380 * @return This entry's group id.
381 */
382 public int getGroupId() {
383 return this.groupId;
384 }
385
386 /**
387 * Set this entry's group id.
388 *
389 * @param groupId This entry's new group id.
390 */
391 public void setGroupId(int groupId) {
392 this.groupId = groupId;
393 }
394
395 /**
396 * Get this entry's user name.
397 *
398 * @return This entry's user name.
399 */
400 public String getUserName() {
401 return this.userName.toString();
402 }
403
404 /**
405 * Set this entry's user name.
406 *
407 * @param userName This entry's new user name.
408 */
409 public void setUserName(String userName) {
410 this.userName = new StringBuffer(userName);
411 }
412
413 /**
414 * Get this entry's group name.
415 *
416 * @return This entry's group name.
417 */
418 public String getGroupName() {
419 return this.groupName.toString();
420 }
421
422 /**
423 * Set this entry's group name.
424 *
425 * @param groupName This entry's new group name.
426 */
427 public void setGroupName(String groupName) {
428 this.groupName = new StringBuffer(groupName);
429 }
430
431 /**
432 * Convenience method to set this entry's group and user ids.
433 *
434 * @param userId This entry's new user id.
435 * @param groupId This entry's new group id.
436 */
437 public void setIds(int userId, int groupId) {
438 this.setUserId(userId);
439 this.setGroupId(groupId);
440 }
441
442 /**
443 * Convenience method to set this entry's group and user names.
444 *
445 * @param userName This entry's new user name.
446 * @param groupName This entry's new group name.
447 */
448 public void setNames(String userName, String groupName) {
449 this.setUserName(userName);
450 this.setGroupName(groupName);
451 }
452
453 /**
454 * Set this entry's modification time. The parameter passed
455 * to this method is in "Java time".
456 *
457 * @param time This entry's new modification time.
458 */
459 public void setModTime(long time) {
460 this.modTime = time / MILLIS_PER_SECOND;
461 }
462
463 /**
464 * Set this entry's modification time.
465 *
466 * @param time This entry's new modification time.
467 */
468 public void setModTime(Date time) {
469 this.modTime = time.getTime() / MILLIS_PER_SECOND;
470 }
471
472 /**
473 * Set this entry's modification time.
474 *
475 * @return time This entry's new modification time.
476 */
477 public Date getModTime() {
478 return new Date(this.modTime * MILLIS_PER_SECOND);
479 }
480
481 /**
482 * Get this entry's file.
483 *
484 * @return This entry's file.
485 */
486 public File getFile() {
487 return this.file;
488 }
489
490 /**
491 * Get this entry's mode.
492 *
493 * @return This entry's mode.
494 */
495 public int getMode() {
496 return this.mode;
497 }
498
499 /**
500 * Get this entry's file size.
501 *
502 * @return This entry's file size.
503 */
504 public long getSize() {
505 return this.size;
506 }
507
508 /**
509 * Set this entry's file size.
510 *
511 * @param size This entry's new file size.
512 */
513 public void setSize(long size) {
514 this.size = size;
515 }
516
517
518 /**
519 * Indicate if this entry is a GNU long name block
520 *
521 * @return true if this is a long name extension provided by GNU tar
522 */
523 public boolean isGNULongNameEntry() {
524 return linkFlag == LF_GNUTYPE_LONGNAME
525 && name.toString().equals(GNU_LONGLINK);
526 }
527
528 /**
529 * Return whether or not this entry represents a directory.
530 *
531 * @return True if this entry is a directory.
532 */
533 public boolean isDirectory() {
534 if (this.file != null) {
535 return this.file.isDirectory();
536 }
537
538 if (this.linkFlag == LF_DIR) {
539 return true;
540 }
541
542 if (this.getName().endsWith("/")) {
543 return true;
544 }
545
546 return false;
547 }
548
549 /**
550 * If this entry represents a file, and the file is a directory, return
551 * an array of TarEntries for this entry's children.
552 *
553 * @return An array of TarEntry's for this entry's children.
554 */
555 public TarEntry[] getDirectoryEntries() {
556 if (this.file == null || !this.file.isDirectory()) {
557 return new TarEntry[0];
558 }
559
560 String[] list = this.file.list();
561 TarEntry[] result = new TarEntry[list.length];
562
563 for (int i = 0; i < list.length; ++i) {
564 result[i] = new TarEntry(new File(this.file, list[i]));
565 }
566
567 return result;
568 }
569
570 /**
571 * Write an entry's header information to a header buffer.
572 *
573 * @param outbuf The tar entry header buffer to fill in.
574 */
575 public void writeEntryHeader(byte[] outbuf) {
576 int offset = 0;
577
578 offset = TarUtils.getNameBytes(this.name, outbuf, offset, NAMELEN);
579 offset = TarUtils.getOctalBytes(this.mode, outbuf, offset, MODELEN);
580 offset = TarUtils.getOctalBytes(this.userId, outbuf, offset, UIDLEN);
581 offset = TarUtils.getOctalBytes(this.groupId, outbuf, offset, GIDLEN);
582 offset = TarUtils.getLongOctalBytes(this.size, outbuf, offset, SIZELEN);
583 offset = TarUtils.getLongOctalBytes(this.modTime, outbuf, offset, MODTIMELEN);
584
585 int csOffset = offset;
586
587 for (int c = 0; c < CHKSUMLEN; ++c) {
588 outbuf[offset++] = (byte) ' ';
589 }
590
591 outbuf[offset++] = this.linkFlag;
592 offset = TarUtils.getNameBytes(this.linkName, outbuf, offset, NAMELEN);
593 offset = TarUtils.getNameBytes(this.magic, outbuf, offset, MAGICLEN);
594 offset = TarUtils.getNameBytes(this.userName, outbuf, offset, UNAMELEN);
595 offset = TarUtils.getNameBytes(this.groupName, outbuf, offset, GNAMELEN);
596 offset = TarUtils.getOctalBytes(this.devMajor, outbuf, offset, DEVLEN);
597 offset = TarUtils.getOctalBytes(this.devMinor, outbuf, offset, DEVLEN);
598
599 while (offset < outbuf.length) {
600 outbuf[offset++] = 0;
601 }
602
603 long checkSum = TarUtils.computeCheckSum(outbuf);
604
605 TarUtils.getCheckSumOctalBytes(checkSum, outbuf, csOffset, CHKSUMLEN);
606 }
607
608 /**
609 * Parse an entry's header information from a header buffer.
610 *
611 * @param header The tar entry header buffer to get information from.
612 */
613 public void parseTarHeader(byte[] header) {
614 int offset = 0;
615
616 this.name = TarUtils.parseName(header, offset, NAMELEN);
617 offset += NAMELEN;
618 this.mode = (int) TarUtils.parseOctal(header, offset, MODELEN);
619 offset += MODELEN;
620 this.userId = (int) TarUtils.parseOctal(header, offset, UIDLEN);
621 offset += UIDLEN;
622 this.groupId = (int) TarUtils.parseOctal(header, offset, GIDLEN);
623 offset += GIDLEN;
624 this.size = TarUtils.parseOctal(header, offset, SIZELEN);
625 offset += SIZELEN;
626 this.modTime = TarUtils.parseOctal(header, offset, MODTIMELEN);
627 offset += MODTIMELEN;
628 this.checkSum = (int) TarUtils.parseOctal(header, offset, CHKSUMLEN);
629 offset += CHKSUMLEN;
630 this.linkFlag = header[offset++];
631 this.linkName = TarUtils.parseName(header, offset, NAMELEN);
632 offset += NAMELEN;
633 this.magic = TarUtils.parseName(header, offset, MAGICLEN);
634 offset += MAGICLEN;
635 this.userName = TarUtils.parseName(header, offset, UNAMELEN);
636 offset += UNAMELEN;
637 this.groupName = TarUtils.parseName(header, offset, GNAMELEN);
638 offset += GNAMELEN;
639 this.devMajor = (int) TarUtils.parseOctal(header, offset, DEVLEN);
640 offset += DEVLEN;
641 this.devMinor = (int) TarUtils.parseOctal(header, offset, DEVLEN);
642 }
643}
Note: See TracBrowser for help on using the repository browser.