001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.apache.commons.compress.archivers.ar;
020
021 import java.io.File;
022 import java.util.Date;
023
024 import org.apache.commons.compress.archivers.ArchiveEntry;
025
026 /**
027 * Represents an archive entry in the "ar" format.
028 *
029 * Each AR archive starts with "!<arch>" followed by a LF. After these 8 bytes
030 * the archive entries are listed. The format of an entry header is as it follows:
031 *
032 * <pre>
033 * START BYTE END BYTE NAME FORMAT LENGTH
034 * 0 15 File name ASCII 16
035 * 16 27 Modification timestamp Decimal 12
036 * 28 33 Owner ID Decimal 6
037 * 34 39 Group ID Decimal 6
038 * 40 47 File mode Octal 8
039 * 48 57 File size (bytes) Decimal 10
040 * 58 59 File magic \140\012 2
041 * </pre>
042 *
043 * This specifies that an ar archive entry header contains 60 bytes.
044 *
045 * Due to the limitation of the file name length to 16 bytes GNU and
046 * BSD has their own variants of this format. Currently Commons
047 * Compress can read but not write the GNU variant and doesn't support
048 * the BSD variant at all.
049 *
050 * @see <a href="http://www.freebsd.org/cgi/man.cgi?query=ar&sektion=5">ar man page</a>
051 *
052 * @Immutable
053 */
054 public class ArArchiveEntry implements ArchiveEntry {
055
056 /** The header for each entry */
057 public static final String HEADER = "!<arch>\n";
058
059 /** The trailer for each entry */
060 public static final String TRAILER = "`\012";
061
062 /**
063 * SVR4/GNU adds a trailing / to names; BSD does not.
064 * They also vary in how names longer than 16 characters are represented.
065 * (Not yet fully supported by this implementation)
066 */
067 private final String name;
068 private final int userId;
069 private final int groupId;
070 private final int mode;
071 private static final int DEFAULT_MODE = 33188; // = (octal) 0100644
072 private final long lastModified;
073 private final long length;
074
075 /**
076 * Create a new instance using a couple of default values.
077 *
078 * <p>Sets userId and groupId to 0, the octal file mode to 644 and
079 * the last modified time to the current time.</p>
080 *
081 * @param name name of the entry
082 * @param length length of the entry in bytes
083 */
084 public ArArchiveEntry(String name, long length) {
085 this(name, length, 0, 0, DEFAULT_MODE,
086 System.currentTimeMillis() / 1000);
087 }
088
089 /**
090 * Create a new instance.
091 *
092 * @param name name of the entry
093 * @param length length of the entry in bytes
094 * @param userId numeric user id
095 * @param groupId numeric group id
096 * @param mode file mode
097 * @param lastModified last modified time in seconds since the epoch
098 */
099 public ArArchiveEntry(String name, long length, int userId, int groupId,
100 int mode, long lastModified) {
101 this.name = name;
102 this.length = length;
103 this.userId = userId;
104 this.groupId = groupId;
105 this.mode = mode;
106 this.lastModified = lastModified;
107 }
108
109 /**
110 * Create a new instance using the attributes of the given file
111 */
112 public ArArchiveEntry(File inputFile, String entryName) {
113 // TODO sort out mode
114 this(entryName, inputFile.isFile() ? inputFile.length() : 0,
115 0, 0, DEFAULT_MODE, inputFile.lastModified() / 1000);
116 }
117
118 /** {@inheritDoc} */
119 public long getSize() {
120 return this.getLength();
121 }
122
123 /** {@inheritDoc} */
124 public String getName() {
125 return name;
126 }
127
128 public int getUserId() {
129 return userId;
130 }
131
132 public int getGroupId() {
133 return groupId;
134 }
135
136 public int getMode() {
137 return mode;
138 }
139
140 /**
141 * Last modified time in seconds since the epoch.
142 */
143 public long getLastModified() {
144 return lastModified;
145 }
146
147 /** {@inheritDoc} */
148 public Date getLastModifiedDate() {
149 return new Date(1000 * getLastModified());
150 }
151
152 public long getLength() {
153 return length;
154 }
155
156 /** {@inheritDoc} */
157 public boolean isDirectory() {
158 return false;
159 }
160
161 /** {@inheritDoc} */
162 @Override
163 public int hashCode() {
164 final int prime = 31;
165 int result = 1;
166 result = prime * result + ((name == null) ? 0 : name.hashCode());
167 return result;
168 }
169
170 /** {@inheritDoc} */
171 @Override
172 public boolean equals(Object obj) {
173 if (this == obj) {
174 return true;
175 }
176 if (obj == null || getClass() != obj.getClass()) {
177 return false;
178 }
179 ArArchiveEntry other = (ArArchiveEntry) obj;
180 if (name == null) {
181 if (other.name != null) {
182 return false;
183 }
184 } else if (!name.equals(other.name)) {
185 return false;
186 }
187 return true;
188 }
189 }