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 */ 019package org.apache.commons.compress.archivers.ar; 020 021import java.io.File; 022import java.util.Date; 023 024import 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. It fully supports 048 * the BSD variant. 049 * 050 * @see <a href="http://www.freebsd.org/cgi/man.cgi?query=ar&sektion=5">ar man page</a> 051 * 052 * @Immutable 053 */ 054public 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(final String name, final 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(final String name, final long length, final int userId, final int groupId, 100 final int mode, final 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 * @param inputFile the file to create an entry from 112 * @param entryName the name of the entry 113 */ 114 public ArArchiveEntry(final File inputFile, final String entryName) { 115 // TODO sort out mode 116 this(entryName, inputFile.isFile() ? inputFile.length() : 0, 117 0, 0, DEFAULT_MODE, inputFile.lastModified() / 1000); 118 } 119 120 @Override 121 public long getSize() { 122 return this.getLength(); 123 } 124 125 @Override 126 public String getName() { 127 return name; 128 } 129 130 public int getUserId() { 131 return userId; 132 } 133 134 public int getGroupId() { 135 return groupId; 136 } 137 138 public int getMode() { 139 return mode; 140 } 141 142 /** 143 * Last modified time in seconds since the epoch. 144 * @return the last modified date 145 */ 146 public long getLastModified() { 147 return lastModified; 148 } 149 150 @Override 151 public Date getLastModifiedDate() { 152 return new Date(1000 * getLastModified()); 153 } 154 155 public long getLength() { 156 return length; 157 } 158 159 @Override 160 public boolean isDirectory() { 161 return false; 162 } 163 164 @Override 165 public int hashCode() { 166 final int prime = 31; 167 int result = 1; 168 result = prime * result + (name == null ? 0 : name.hashCode()); 169 return result; 170 } 171 172 @Override 173 public boolean equals(final Object obj) { 174 if (this == obj) { 175 return true; 176 } 177 if (obj == null || getClass() != obj.getClass()) { 178 return false; 179 } 180 final ArArchiveEntry other = (ArArchiveEntry) obj; 181 if (name == null) { 182 if (other.name != null) { 183 return false; 184 } 185 } else if (!name.equals(other.name)) { 186 return false; 187 } 188 return true; 189 } 190}