001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one or more
003 *  contributor license agreements.  See the NOTICE file distributed with
004 *  this work for additional information regarding copyright ownership.
005 *  The ASF licenses this file to You under the Apache License, Version 2.0
006 *  (the "License"); you may not use this file except in compliance with
007 *  the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 */
017
018package org.apache.commons.compress.archivers;
019
020import java.io.BufferedInputStream;
021import java.io.IOException;
022import java.io.InputStream;
023import java.nio.file.Files;
024import java.nio.file.Path;
025import java.nio.file.Paths;
026import java.util.Enumeration;
027import java.util.Locale;
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.ZipArchiveEntry;
033import org.apache.commons.compress.archivers.zip.ZipFile;
034
035/**
036 * Simple command line application that lists the contents of an archive.
037 *
038 * <p>
039 * The name of the archive must be given as a command line argument.
040 * </p>
041 * <p>
042 * The optional second argument defines the archive type, in case the format is not recognized.
043 * </p>
044 *
045 * @since 1.1
046 */
047public final class Lister {
048
049    private static final ArchiveStreamFactory FACTORY = ArchiveStreamFactory.DEFAULT;
050
051    private static <T extends ArchiveInputStream<? extends E>, E extends ArchiveEntry> T createArchiveInputStream(final String[] args,
052            final InputStream inputStream) throws ArchiveException {
053        if (args.length > 1) {
054            return FACTORY.createArchiveInputStream(args[1], inputStream);
055        }
056        return FACTORY.createArchiveInputStream(inputStream);
057    }
058
059    private static String detectFormat(final Path file) throws ArchiveException, IOException {
060        try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(file))) {
061            return ArchiveStreamFactory.detect(inputStream);
062        }
063    }
064
065    /**
066     * Runs this class from the command line.
067     * <p>
068     * The name of the archive must be given as a command line argument.
069     * </p>
070     * <p>
071     * The optional second argument defines the archive type, in case the format is not recognized.
072     * </p>
073     *
074     * @param args name of the archive and optional argument archive type.
075     * @throws ArchiveException Archiver related Exception.
076     * @throws IOException      an I/O exception.
077     */
078    public static void main(final String... args) throws ArchiveException, IOException {
079        if (args == null || args.length == 0) {
080            usage();
081            return;
082        }
083        new Lister(false, args).go();
084    }
085
086    private static void usage() {
087        System.err.println("Parameters: archive-name [archive-type]\n");
088        System.err.println("The magic archive-type 'zipfile' prefers ZipFile over ZipArchiveInputStream");
089        System.err.println("The magic archive-type 'tarfile' prefers TarFile over TarArchiveInputStream");
090    }
091
092    private final boolean quiet;
093
094    private final String[] args;
095
096    /**
097     * Constructs a new instance.
098     *
099     * @deprecated No replacement.
100     */
101    @Deprecated
102    public Lister() {
103        this(false, "");
104    }
105
106    Lister(final boolean quiet, final String... args) {
107        this.quiet = quiet;
108        this.args = args.clone();
109        Objects.requireNonNull(args[0], "args[0]");
110    }
111
112    void go() throws ArchiveException, IOException {
113        list(Paths.get(args[0]), args);
114    }
115
116    private void list(final Path file, final String... args) throws ArchiveException, IOException {
117        println("Analyzing " + file);
118        if (!Files.isRegularFile(file)) {
119            System.err.println(file + " doesn't exist or is a directory");
120        }
121        final String format = (args.length > 1 ? args[1] : detectFormat(file)).toLowerCase(Locale.ROOT);
122        println("Detected format " + format);
123        switch (format) {
124        case ArchiveStreamFactory.SEVEN_Z:
125            list7z(file);
126            break;
127        case ArchiveStreamFactory.ZIP:
128            listZipUsingZipFile(file);
129            break;
130        case ArchiveStreamFactory.TAR:
131            listZipUsingTarFile(file);
132            break;
133        default:
134            listStream(file, args);
135        }
136    }
137
138    private  void list7z(final Path file) throws IOException {
139        try (SevenZFile sevenZFile = SevenZFile.builder().setPath(file).get()) {
140            println("Created " + sevenZFile);
141            ArchiveEntry entry;
142            while ((entry = sevenZFile.getNextEntry()) != null) {
143                println(entry.getName() == null ? sevenZFile.getDefaultName() + " (entry name was null)" : entry.getName());
144            }
145        }
146    }
147
148    private  void listStream(final Path file, final String[] args) throws ArchiveException, IOException {
149        try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(file));
150                ArchiveInputStream<?> archiveInputStream = createArchiveInputStream(args, inputStream)) {
151            println("Created " + archiveInputStream.toString());
152            ArchiveEntry entry;
153            while ((entry = archiveInputStream.getNextEntry()) != null) {
154                println(entry);
155            }
156        }
157    }
158
159    private  void listZipUsingTarFile(final Path file) throws IOException {
160        try (TarFile tarFile = new TarFile(file)) {
161            println("Created " + tarFile);
162            tarFile.getEntries().forEach(this::println);
163        }
164    }
165
166    private  void listZipUsingZipFile(final Path file) throws IOException {
167        try (ZipFile zipFile = ZipFile.builder().setPath(file).get()) {
168            println("Created " + zipFile);
169            for (final Enumeration<ZipArchiveEntry> en = zipFile.getEntries(); en.hasMoreElements();) {
170                println(en.nextElement());
171            }
172        }
173    }
174
175    private void println(final ArchiveEntry entry) {
176        println(entry.getName());
177    }
178
179    private void println(final String line) {
180        if (!quiet) {
181            System.out.println(line);
182        }
183    }
184
185}