    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    }