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.File;
022import java.io.IOException;
023import java.io.InputStream;
024import java.nio.file.Files;
025import java.util.Enumeration;
026import java.util.Objects;
027
028import org.apache.commons.compress.archivers.sevenz.SevenZFile;
029import org.apache.commons.compress.archivers.tar.TarFile;
030import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
031import org.apache.commons.compress.archivers.zip.ZipFile;
032
033/**
034 * Simple command line application that lists the contents of an archive.
035 *
036 * <p>
037 * The name of the archive must be given as a command line argument.
038 * </p>
039 * <p>
040 * The optional second argument defines the archive type, in case the format is not recognized.
041 * </p>
042 *
043 * @since 1.1
044 */
045public final class Lister {
046
047    private static final ArchiveStreamFactory FACTORY = ArchiveStreamFactory.DEFAULT;
048
049    private static <T extends ArchiveInputStream<? extends E>, E extends ArchiveEntry> T createArchiveInputStream(final String[] args,
050            final InputStream inputStream) throws ArchiveException {
051        if (args.length > 1) {
052            return FACTORY.createArchiveInputStream(args[1], inputStream);
053        }
054        return FACTORY.createArchiveInputStream(inputStream);
055    }
056
057    private static String detectFormat(final File file) throws ArchiveException, IOException {
058        try (final InputStream inputStream = new BufferedInputStream(Files.newInputStream(file.toPath()))) {
059            return ArchiveStreamFactory.detect(inputStream);
060        }
061    }
062
063    private static void list7z(final File file) throws IOException {
064        try (SevenZFile sevenZFile = new SevenZFile(file)) {
065            System.out.println("Created " + sevenZFile);
066            ArchiveEntry entry;
067            while ((entry = sevenZFile.getNextEntry()) != null) {
068                final String name = entry.getName() == null ? sevenZFile.getDefaultName() + " (entry name was null)" : entry.getName();
069                System.out.println(name);
070            }
071        }
072    }
073
074    private static void listStream(final File file, final String[] args) throws ArchiveException, IOException {
075        try (final InputStream inputStream = new BufferedInputStream(Files.newInputStream(file.toPath()));
076                final ArchiveInputStream<?> archiveInputStream = createArchiveInputStream(args, inputStream)) {
077            System.out.println("Created " + archiveInputStream.toString());
078            ArchiveEntry entry;
079            while ((entry = archiveInputStream.getNextEntry()) != null) {
080                System.out.println(entry.getName());
081            }
082        }
083    }
084
085    private static void listZipUsingTarFile(final File file) throws IOException {
086        try (TarFile tarFile = new TarFile(file)) {
087            System.out.println("Created " + tarFile);
088            tarFile.getEntries().forEach(en -> System.out.println(en.getName()));
089        }
090    }
091
092    private static void listZipUsingZipFile(final File file) throws IOException {
093        try (ZipFile zipFile = new ZipFile(file)) {
094            System.out.println("Created " + zipFile);
095            for (final Enumeration<ZipArchiveEntry> en = zipFile.getEntries(); en.hasMoreElements();) {
096                System.out.println(en.nextElement().getName());
097            }
098        }
099    }
100
101    /**
102     * Runs this class from the command line.
103     * <p>
104     * The name of the archive must be given as a command line argument.
105     * </p>
106     * <p>
107     * The optional second argument defines the archive type, in case the format is not recognized.
108     * </p>
109     *
110     * @param args name of the archive and optional argument archive type.
111     * @throws ArchiveException Archiver related Exception.
112     * @throws IOException      an I/O exception.
113     */
114    public static void main(final String[] args) throws ArchiveException, IOException {
115        if (args == null || args.length == 0) {
116            usage();
117            return;
118        }
119        Objects.requireNonNull(args[0], "args[0]");
120        System.out.println("Analysing " + args[0]);
121        final File file = new File(args[0]);
122        if (!file.isFile()) {
123            System.err.println(file + " doesn't exist or is a directory");
124        }
125        final String format = args.length > 1 ? args[1] : detectFormat(file);
126        if (ArchiveStreamFactory.SEVEN_Z.equalsIgnoreCase(format)) {
127            list7z(file);
128        } else if ("zipfile".equals(format)) {
129            listZipUsingZipFile(file);
130        } else if ("tarfile".equals(format)) {
131            listZipUsingTarFile(file);
132        } else {
133            listStream(file, args);
134        }
135    }
136
137    private static void usage() {
138        System.out.println("Parameters: archive-name [archive-type]\n");
139        System.out.println("The magic archive-type 'zipfile' prefers ZipFile over ZipArchiveInputStream");
140        System.out.println("The magic archive-type 'tarfile' prefers TarFile over TarArchiveInputStream");
141    }
142
143}