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}