    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 *   https://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
    020package org.apache.commons.compress.archivers;
    021
    022import java.io.BufferedInputStream;
    023import java.io.IOException;
    024import java.io.InputStream;
    025import java.nio.file.Files;
    026import java.nio.file.Path;
    027import java.nio.file.Paths;
    028import java.util.Objects;
    029
    030import org.apache.commons.compress.archivers.sevenz.SevenZFile;
    031import org.apache.commons.compress.archivers.tar.TarFile;
    032import org.apache.commons.compress.archivers.zip.ZipFile;
    033import org.apache.commons.lang3.ArrayUtils;
    034import org.apache.commons.lang3.StringUtils;
    035
    036/**
    037 * Simple command line application that lists the contents of an archive.
    038 *
    039 * <p>
    040 * The name of the archive must be given as a command line argument.
    041 * </p>
    042 * <p>
    043 * The optional second argument defines the archive type, in case the format is not recognized.
    044 * </p>
    045 *
    046 * @since 1.1
    047 */
    048public final class Lister {
    049
    050    private static final ArchiveStreamFactory FACTORY = ArchiveStreamFactory.DEFAULT;
    051
    052    private static <T extends ArchiveInputStream<? extends E>, E extends ArchiveEntry> T createArchiveInputStream(final String[] args,
    053            final InputStream inputStream) throws ArchiveException {
    054        if (args.length > 1) {
    055            return FACTORY.createArchiveInputStream(args[1], inputStream);
    056        }
    057        return FACTORY.createArchiveInputStream(inputStream);
    058    }
    059
    060    private static String detectFormat(final Path file) throws ArchiveException, IOException {
    061        try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(file))) {
    062            return ArchiveStreamFactory.detect(inputStream);
    063        }
    064    }
    065
    066    /**
    067     * Runs this class from the command line.
    068     * <p>
    069     * The name of the archive must be given as a command line argument.
    070     * </p>
    071     * <p>
    072     * The optional second argument defines the archive type, in case the format is not recognized.
    073     * </p>
    074     *
    075     * @param args name of the archive and optional argument archive type.
    076     * @throws ArchiveException Archiver related Exception.
    077     * @throws IOException      an I/O exception.
    078     */
    079    public static void main(final String... args) throws ArchiveException, IOException {
    080        if (ArrayUtils.isEmpty(args)) {
    081            usage();
    082            return;
    083        }
    084        new Lister(false, args).go();
    085    }
    086
    087    private static void usage() {
    088        System.err.println("Parameters: archive-name [archive-type]\n");
    089        System.err.println("The magic archive-type 'zipfile' prefers ZipFile over ZipArchiveInputStream");
    090        System.err.println("The magic archive-type 'tarfile' prefers TarFile over TarArchiveInputStream");
    091    }
    092
    093    private final boolean quiet;
    094
    095    private final String[] args;
    096
    097    /**
    098     * Constructs a new instance.
    099     *
    100     * @deprecated No replacement.
    101     */
    102    @Deprecated
    103    public Lister() {
    104        this(false, "");
    105    }
    106
    107    Lister(final boolean quiet, final String... args) {
    108        this.quiet = quiet;
    109        this.args = args.clone();
    110        Objects.requireNonNull(args[0], "args[0]");
    111    }
    112
    113    void go() throws ArchiveException, IOException {
    114        list(Paths.get(args[0]), args);
    115    }
    116
    117    private void list(final Path file, final String... args) throws ArchiveException, IOException {
    118        println("Analyzing " + file);
    119        if (!Files.isRegularFile(file)) {
    120            System.err.println(file + " doesn't exist or is a directory");
    121        }
    122        final String format = StringUtils.toRootLowerCase(args.length > 1 ? args[1] : detectFormat(file));
    123        println("Detected format " + format);
    124        switch (format) {
    125        case ArchiveStreamFactory.SEVEN_Z:
    126            list7z(file);
    127            break;
    128        case ArchiveStreamFactory.ZIP:
    129            listZipUsingZipFile(file);
    130            break;
    131        case ArchiveStreamFactory.TAR:
    132            listZipUsingTarFile(file);
    133            break;
    134        default:
    135            listStream(file, args);
    136        }
    137    }
    138
    139    private  void list7z(final Path file) throws IOException {
    140        try (SevenZFile sevenZFile = SevenZFile.builder().setPath(file).get()) {
    141            println("Created " + sevenZFile);
    142            ArchiveEntry entry;
    143            while ((entry = sevenZFile.getNextEntry()) != null) {
    144                println(entry.getName() == null ? sevenZFile.getDefaultName() + " (entry name was null)" : entry.getName());
    145            }
    146        }
    147    }
    148
    149    private  void listStream(final Path file, final String[] args) throws ArchiveException, IOException {
    150        try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(file));
    151                ArchiveInputStream<?> archiveInputStream = createArchiveInputStream(args, inputStream)) {
    152            println("Created " + archiveInputStream.toString());
    153            archiveInputStream.forEach(this::println);
    154        }
    155    }
    156
    157    private  void listZipUsingTarFile(final Path file) throws IOException {
    158        try (TarFile tarFile = new TarFile(file)) {
    159            println("Created " + tarFile);
    160            tarFile.getEntries().forEach(this::println);
    161        }
    162    }
    163
    164    private  void listZipUsingZipFile(final Path file) throws IOException {
    165        try (ZipFile zipFile = ZipFile.builder().setPath(file).get()) {
    166            println("Created " + zipFile);
    167            zipFile.stream().forEach(this::println);
    168        }
    169    }
    170
    171    private void println(final ArchiveEntry entry) {
    172        println(entry.getName());
    173    }
    174
    175    private void println(final String line) {
    176        if (!quiet) {
    177            System.out.println(line);
    178        }
    179    }
    180
    181}