From 5b34b4af143179475a53afffaf80b8d619e21001 Mon Sep 17 00:00:00 2001 From: Liav A Date: Sat, 2 Sep 2023 00:04:54 +0300 Subject: [PATCH] Utilities: Merge the gunzip utility with gzip Now both /bin/zcat and /bin/gunzip are symlinks to /bin/gzip, and we essentially running it in decompression mode through these symlinks. This ensures we don't maintain 2 versions of code to decompress Gzipped data anymore, and handle the use case of gzipped-streaming input only once in the codebase. --- .github/CODEOWNERS | 1 - Base/usr/share/man/man1/gunzip.md | 23 +----------- Base/usr/share/man/man1/gzip.md | 5 +-- Base/usr/share/man/man1/unzip.md | 1 - Base/usr/share/man/man1/zcat.md | 1 + Meta/Lagom/CMakeLists.txt | 1 - Userland/Utilities/CMakeLists.txt | 6 ++-- Userland/Utilities/gunzip.cpp | 55 ---------------------------- Userland/Utilities/gzip.cpp | 59 ++++++++++++++++++++++++------- 9 files changed, 55 insertions(+), 97 deletions(-) mode change 100644 => 120000 Base/usr/share/man/man1/gunzip.md create mode 120000 Base/usr/share/man/man1/zcat.md delete mode 100644 Userland/Utilities/gunzip.cpp diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6d4476bffb7..03f15f055fa 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -35,7 +35,6 @@ /Userland/Services/SQLServer @trflynn89 /Userland/Services/WebDriver @trflynn89 /Userland/Shell @alimpfard -/Userland/Utilities/gunzip.cpp @timschumi /Userland/Utilities/gzip.cpp @timschumi /Userland/Utilities/lzcat.cpp @timschumi /Userland/Utilities/readelf.cpp @BertalanD diff --git a/Base/usr/share/man/man1/gunzip.md b/Base/usr/share/man/man1/gunzip.md deleted file mode 100644 index 3e9106058ec..00000000000 --- a/Base/usr/share/man/man1/gunzip.md +++ /dev/null @@ -1,22 +0,0 @@ -## Name - -gunzip - -## Synopsis - -```sh -$ gunzip [--keep] [--stdout] -``` - -## Options - -* `-k`, `--keep`: Keep (don't delete) input files -* `-c`, `--stdout`: Write to stdout, keep original files unchanged - -## Arguments - -* `FILE`: File to decompress - -## See also -* [`gzip`(1)](help://man/1/gzip) -* [`tar`(1)](help://man/1/tar) diff --git a/Base/usr/share/man/man1/gunzip.md b/Base/usr/share/man/man1/gunzip.md new file mode 120000 index 00000000000..548787f228a --- /dev/null +++ b/Base/usr/share/man/man1/gunzip.md @@ -0,0 +1 @@ +gzip.md \ No newline at end of file diff --git a/Base/usr/share/man/man1/gzip.md b/Base/usr/share/man/man1/gzip.md index 7c70435c399..d4a3745925b 100644 --- a/Base/usr/share/man/man1/gzip.md +++ b/Base/usr/share/man/man1/gzip.md @@ -1,11 +1,13 @@ ## Name -gzip +gzip, gunzip, zcat ## Synopsis ```sh $ gzip [--keep] [--stdout] [--decompress] +$ gunzip [--keep] [--stdout] +$ zcat ``` ## Options @@ -19,5 +21,4 @@ $ gzip [--keep] [--stdout] [--decompress] * `FILES`: Files ## See also -* [`gunzip`(1)](help://man/1/gunzip) * [`tar`(1)](help://man/1/tar) diff --git a/Base/usr/share/man/man1/unzip.md b/Base/usr/share/man/man1/unzip.md index 3e53eba9b73..c15880e9d92 100644 --- a/Base/usr/share/man/man1/unzip.md +++ b/Base/usr/share/man/man1/unzip.md @@ -40,5 +40,4 @@ Archive: archive.unzip ## See also * [`zip`(1)](help://man/1/zip) -* [`gunzip`(1)](help://man/1/gunzip) * [`tar`(1)](help://man/1/tar) diff --git a/Base/usr/share/man/man1/zcat.md b/Base/usr/share/man/man1/zcat.md new file mode 120000 index 00000000000..548787f228a --- /dev/null +++ b/Base/usr/share/man/man1/zcat.md @@ -0,0 +1 @@ +gzip.md \ No newline at end of file diff --git a/Meta/Lagom/CMakeLists.txt b/Meta/Lagom/CMakeLists.txt index 63cc62c3b57..ccdcfdd587b 100644 --- a/Meta/Lagom/CMakeLists.txt +++ b/Meta/Lagom/CMakeLists.txt @@ -558,7 +558,6 @@ if (BUILD_LAGOM) endif() lagom_utility(gml-format SOURCES ../../Userland/Utilities/gml-format.cpp LIBS LibGUI LibMain) - lagom_utility(gunzip SOURCES ../../Userland/Utilities/gunzip.cpp LIBS LibCompress LibMain) lagom_utility(gzip SOURCES ../../Userland/Utilities/gzip.cpp LIBS LibCompress LibMain) # Work around bug in JetBrains distributed CMake 3.27.2 where this causes infinite recursion in diff --git a/Userland/Utilities/CMakeLists.txt b/Userland/Utilities/CMakeLists.txt index f6374bd90c9..d8f43111b84 100644 --- a/Userland/Utilities/CMakeLists.txt +++ b/Userland/Utilities/CMakeLists.txt @@ -2,7 +2,7 @@ file(GLOB CMD_SOURCES CONFIGURE_DEPENDS "*.cpp") list(APPEND SPECIAL_TARGETS test install) list(APPEND REQUIRED_TARGETS arp base64 basename cat chmod chown clear comm cp cut date dd df diff dirname dmesg du echo env expr false - file find grep groups gunzip head host hostname id ifconfig kill killall ln logout ls mkdir mount mv network-settings nproc + file find grep groups head host hostname id ifconfig kill killall ln logout ls mkdir mount mv network-settings nproc patch pgrep pidof ping pkill pmap ps readlink realpath reboot rm rmdir sed route seq shutdown sleep sort stat stty su tail test touch tr true umount uname uniq uptime w wc which whoami xargs yes ) @@ -70,7 +70,8 @@ endif() install(CODE "file(CREATE_LINK grep ${CMAKE_INSTALL_PREFIX}/bin/egrep SYMBOLIC)") install(CODE "file(CREATE_LINK grep ${CMAKE_INSTALL_PREFIX}/bin/fgrep SYMBOLIC)") install(CODE "file(CREATE_LINK grep ${CMAKE_INSTALL_PREFIX}/bin/rgrep SYMBOLIC)") -install(CODE "file(CREATE_LINK gunzip ${CMAKE_INSTALL_PREFIX}/bin/zcat SYMBOLIC)") +install(CODE "file(CREATE_LINK gzip ${CMAKE_INSTALL_PREFIX}/bin/gunzip SYMBOLIC)") +install(CODE "file(CREATE_LINK gzip ${CMAKE_INSTALL_PREFIX}/bin/zcat SYMBOLIC)") target_link_libraries(abench PRIVATE LibAudio LibFileSystem) target_link_libraries(aconv PRIVATE LibAudio LibFileSystem) @@ -99,7 +100,6 @@ target_link_libraries(functrace PRIVATE LibDebug LibELF LibX86) target_link_libraries(glsl-compiler PRIVATE LibGLSL) target_link_libraries(gml-format PRIVATE LibGUI) target_link_libraries(grep PRIVATE LibFileSystem LibRegex LibURL) -target_link_libraries(gunzip PRIVATE LibCompress) target_link_libraries(gzip PRIVATE LibCompress) target_link_libraries(headless-browser PRIVATE LibCrypto LibFileSystem LibGemini LibGfx LibHTTP LibImageDecoderClient LibTLS LibWeb LibWebView LibWebSocket LibIPC LibJS LibDiff LibURL) target_link_libraries(icc PRIVATE LibGfx LibVideo LibURL) diff --git a/Userland/Utilities/gunzip.cpp b/Userland/Utilities/gunzip.cpp deleted file mode 100644 index 8af3542c6f5..00000000000 --- a/Userland/Utilities/gunzip.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * Copyright (c) 2023, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -ErrorOr serenity_main(Main::Arguments args) -{ - Vector filenames; - bool keep_input_files { false }; - bool write_to_stdout { false }; - - Core::ArgsParser args_parser; - // NOTE: If the user run this program via the /bin/zcat symlink, - // then emulate gzip decompression to stdout. - if (args.argc > 0 && args.strings[0] == "zcat"sv) { - write_to_stdout = true; - } else { - args_parser.add_option(keep_input_files, "Keep (don't delete) input files", "keep", 'k'); - args_parser.add_option(write_to_stdout, "Write to stdout, keep original files unchanged", "stdout", 'c'); - } - args_parser.add_positional_argument(filenames, "File to decompress", "FILE"); - args_parser.parse(args); - - if (write_to_stdout) - keep_input_files = true; - - for (auto filename : filenames) { - - ByteString input_filename; - ByteString output_filename; - if (filename.ends_with(".gz"sv)) { - input_filename = filename; - output_filename = filename.substring_view(0, filename.length() - 3); - } else { - input_filename = ByteString::formatted("{}.gz", filename); - output_filename = filename; - } - - auto output_stream = write_to_stdout ? TRY(Core::File::standard_output()) : TRY(Core::File::open(output_filename, Core::File::OpenMode::Write)); - TRY(Compress::GzipDecompressor::decompress_file(input_filename, move(output_stream))); - - if (!keep_input_files) - TRY(Core::System::unlink(input_filename)); - } - return 0; -} diff --git a/Userland/Utilities/gzip.cpp b/Userland/Utilities/gzip.cpp index 81fd3873c36..c8d789eb58c 100644 --- a/Userland/Utilities/gzip.cpp +++ b/Userland/Utilities/gzip.cpp @@ -4,6 +4,8 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include #include #include #include @@ -23,34 +25,67 @@ ErrorOr serenity_main(Main::Arguments arguments) args_parser.add_option(keep_input_files, "Keep (don't delete) input files", "keep", 'k'); args_parser.add_option(write_to_stdout, "Write to stdout, keep original files unchanged", "stdout", 'c'); args_parser.add_option(decompress, "Decompress", "decompress", 'd'); - args_parser.add_positional_argument(filenames, "Files", "FILES"); + args_parser.add_positional_argument(filenames, "Files", "FILES", Core::ArgsParser::Required::No); args_parser.parse(arguments); + auto program_name = LexicalPath::basename(arguments.strings[0]); + + // NOTE: If the user run this program via the /bin/zcat or /bin/gunzip symlink, + // then emulate gzip decompression. + if (program_name == "zcat"sv || program_name == "gunzip"sv) + decompress = true; + + if (program_name == "zcat"sv) + write_to_stdout = true; + + if (filenames.is_empty()) { + filenames.append("-"sv); + write_to_stdout = true; + } + if (write_to_stdout) keep_input_files = true; for (auto const& input_filename : filenames) { - ByteString output_filename; - if (decompress) { + OwnPtr output_stream; + + if (write_to_stdout) { + output_stream = TRY(Core::File::standard_output()); + } else if (decompress) { if (!input_filename.ends_with(".gz"sv)) { warnln("unknown suffix for: {}, skipping", input_filename); continue; } - output_filename = input_filename.substring_view(0, input_filename.length() - ".gz"sv.length()); + + auto output_filename = input_filename.substring_view(0, input_filename.length() - ".gz"sv.length()); + output_stream = TRY(Core::File::open(output_filename, Core::File::OpenMode::Write)); } else { - output_filename = ByteString::formatted("{}.gz", input_filename); + auto output_filename = ByteString::formatted("{}.gz", input_filename); + output_stream = TRY(Core::File::open(output_filename, Core::File::OpenMode::Write)); } - auto output_stream = write_to_stdout ? TRY(Core::File::standard_output()) : TRY(Core::File::open(output_filename, Core::File::OpenMode::Write)); + VERIFY(output_stream); - if (decompress) - TRY(Compress::GzipDecompressor::decompress_file(input_filename, move(output_stream))); - else - TRY(Compress::GzipCompressor::compress_file(input_filename, move(output_stream))); + NonnullOwnPtr input_file = TRY(Core::File::open_file_or_standard_stream(input_filename, Core::File::OpenMode::Read)); - if (!keep_input_files) { + // Buffer reads, which yields a significant performance improvement. + NonnullOwnPtr input_stream = TRY(Core::InputBufferedFile::create(move(input_file), 1 * MiB)); + + if (decompress) { + input_stream = TRY(try_make(move(input_stream))); + } else { + output_stream = TRY(try_make(output_stream.release_nonnull())); + } + + auto buffer = TRY(ByteBuffer::create_uninitialized(1 * MiB)); + + while (!input_stream->is_eof()) { + auto span = TRY(input_stream->read_some(buffer)); + TRY(output_stream->write_until_depleted(span)); + } + + if (!keep_input_files) TRY(Core::System::unlink(input_filename)); - } } return 0;