    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 "!&lt;arch&gt;" 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}