Ver código fonte

unzip: Add option to list files of an archive

Romain Chardiny 2 anos atrás
pai
commit
dc65a2f2b8

+ 2 - 2
Userland/Libraries/LibArchive/Zip.cpp

@@ -75,7 +75,7 @@ Optional<Zip> Zip::try_create(ReadonlyBytes buffer)
     };
 }
 
-ErrorOr<bool> Zip::for_each_member(Function<IterationDecision(ZipMember const&)> callback)
+ErrorOr<bool> Zip::for_each_member(Function<ErrorOr<IterationDecision>(ZipMember const&)> callback)
 {
     size_t member_offset = m_members_start_offset;
     for (size_t i = 0; i < m_member_count; i++) {
@@ -94,7 +94,7 @@ ErrorOr<bool> Zip::for_each_member(Function<IterationDecision(ZipMember const&)>
         member.modification_date = central_directory_record.modification_date;
         member.is_directory = central_directory_record.external_attributes & zip_directory_external_attribute || member.name.bytes_as_string_view().ends_with('/'); // FIXME: better directory detection
 
-        if (callback(member) == IterationDecision::Break)
+        if (TRY(callback(member)) == IterationDecision::Break)
             return false;
 
         member_offset += central_directory_record.size();

+ 1 - 1
Userland/Libraries/LibArchive/Zip.h

@@ -253,7 +253,7 @@ struct ZipMember {
 class Zip {
 public:
     static Optional<Zip> try_create(ReadonlyBytes buffer);
-    ErrorOr<bool> for_each_member(Function<IterationDecision(ZipMember const&)>);
+    ErrorOr<bool> for_each_member(Function<ErrorOr<IterationDecision>(ZipMember const&)>);
 
 private:
     static bool find_end_of_central_directory_offset(ReadonlyBytes, size_t& offset);

+ 25 - 0
Userland/Utilities/unzip.cpp

@@ -11,6 +11,7 @@
 #include <LibArchive/Zip.h>
 #include <LibCompress/Deflate.h>
 #include <LibCore/ArgsParser.h>
+#include <LibCore/DateTime.h>
 #include <LibCore/Directory.h>
 #include <LibCore/File.h>
 #include <LibCore/MappedFile.h>
@@ -104,10 +105,12 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
 {
     StringView zip_file_path;
     bool quiet { false };
+    bool list_files { false };
     StringView output_directory_path;
     Vector<StringView> file_filters;
 
     Core::ArgsParser args_parser;
+    args_parser.add_option(list_files, "Only list files in the archive", "list", 'l');
     args_parser.add_option(output_directory_path, "Directory to receive the archive content", "output-directory", 'd', "path");
     args_parser.add_option(quiet, "Be less verbose", "quiet", 'q');
     args_parser.add_positional_argument(zip_file_path, "File to unzip", "path", Core::ArgsParser::Required::Yes);
@@ -140,6 +143,28 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
         TRY(Core::System::chdir(output_directory_path));
     }
 
+    if (list_files) {
+        outln("  Length     Date      Time     Name");
+        outln("--------- ---------- --------   ----");
+        u32 members_count = 0;
+        u64 total_size = 0;
+        TRY(zip_file->for_each_member([&](auto zip_member) -> ErrorOr<IterationDecision> {
+            members_count++;
+
+            auto time = time_from_packed_dos(zip_member.modification_date, zip_member.modification_time);
+            auto time_str = TRY(Core::DateTime::from_timestamp(time.seconds_since_epoch()).to_string());
+
+            total_size += zip_member.uncompressed_size;
+
+            outln("{:>9} {}   {}", zip_member.uncompressed_size, time_str, zip_member.name);
+
+            return IterationDecision::Continue;
+        }));
+        outln("---------                       ----");
+        outln("{:>9}                       {} files", total_size, members_count);
+        return 0;
+    }
+
     Vector<Archive::ZipMember> zip_directories;
 
     auto success = TRY(zip_file->for_each_member([&](auto zip_member) {