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.
This commit is contained in:
Liav A 2023-09-02 00:04:54 +03:00 committed by Andrew Kaster
parent cd486a7040
commit 5b34b4af14
Notes: sideshowbarker 2024-07-17 07:48:42 +09:00
9 changed files with 55 additions and 97 deletions

1
.github/CODEOWNERS vendored
View file

@ -35,7 +35,6 @@
/Userland/Services/SQLServer @trflynn89 /Userland/Services/SQLServer @trflynn89
/Userland/Services/WebDriver @trflynn89 /Userland/Services/WebDriver @trflynn89
/Userland/Shell @alimpfard /Userland/Shell @alimpfard
/Userland/Utilities/gunzip.cpp @timschumi
/Userland/Utilities/gzip.cpp @timschumi /Userland/Utilities/gzip.cpp @timschumi
/Userland/Utilities/lzcat.cpp @timschumi /Userland/Utilities/lzcat.cpp @timschumi
/Userland/Utilities/readelf.cpp @BertalanD /Userland/Utilities/readelf.cpp @BertalanD

View file

@ -1,22 +0,0 @@
## Name
gunzip
## Synopsis
```sh
$ gunzip [--keep] [--stdout] <FILE...>
```
## 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)

View file

@ -0,0 +1 @@
gzip.md

View file

@ -1,11 +1,13 @@
## Name ## Name
gzip gzip, gunzip, zcat
## Synopsis ## Synopsis
```sh ```sh
$ gzip [--keep] [--stdout] [--decompress] <FILES...> $ gzip [--keep] [--stdout] [--decompress] <FILES...>
$ gunzip [--keep] [--stdout] <FILES...>
$ zcat <FILES...>
``` ```
## Options ## Options
@ -19,5 +21,4 @@ $ gzip [--keep] [--stdout] [--decompress] <FILES...>
* `FILES`: Files * `FILES`: Files
## See also ## See also
* [`gunzip`(1)](help://man/1/gunzip)
* [`tar`(1)](help://man/1/tar) * [`tar`(1)](help://man/1/tar)

View file

@ -40,5 +40,4 @@ Archive: archive.unzip
## See also ## See also
* [`zip`(1)](help://man/1/zip) * [`zip`(1)](help://man/1/zip)
* [`gunzip`(1)](help://man/1/gunzip)
* [`tar`(1)](help://man/1/tar) * [`tar`(1)](help://man/1/tar)

View file

@ -0,0 +1 @@
gzip.md

View file

@ -558,7 +558,6 @@ if (BUILD_LAGOM)
endif() endif()
lagom_utility(gml-format SOURCES ../../Userland/Utilities/gml-format.cpp LIBS LibGUI LibMain) 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) 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 # Work around bug in JetBrains distributed CMake 3.27.2 where this causes infinite recursion in

View file

@ -2,7 +2,7 @@ file(GLOB CMD_SOURCES CONFIGURE_DEPENDS "*.cpp")
list(APPEND SPECIAL_TARGETS test install) list(APPEND SPECIAL_TARGETS test install)
list(APPEND REQUIRED_TARGETS 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 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 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 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/egrep SYMBOLIC)")
install(CODE "file(CREATE_LINK grep ${CMAKE_INSTALL_PREFIX}/bin/fgrep 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 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(abench PRIVATE LibAudio LibFileSystem)
target_link_libraries(aconv 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(glsl-compiler PRIVATE LibGLSL)
target_link_libraries(gml-format PRIVATE LibGUI) target_link_libraries(gml-format PRIVATE LibGUI)
target_link_libraries(grep PRIVATE LibFileSystem LibRegex LibURL) target_link_libraries(grep PRIVATE LibFileSystem LibRegex LibURL)
target_link_libraries(gunzip PRIVATE LibCompress)
target_link_libraries(gzip 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(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) target_link_libraries(icc PRIVATE LibGfx LibVideo LibURL)

View file

@ -1,55 +0,0 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
* Copyright (c) 2023, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibCompress/Gzip.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <LibCore/System.h>
#include <LibMain/Main.h>
#include <unistd.h>
ErrorOr<int> serenity_main(Main::Arguments args)
{
Vector<StringView> 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;
}

View file

@ -4,6 +4,8 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/ByteString.h>
#include <AK/LexicalPath.h>
#include <LibCompress/Gzip.h> #include <LibCompress/Gzip.h>
#include <LibCore/ArgsParser.h> #include <LibCore/ArgsParser.h>
#include <LibCore/File.h> #include <LibCore/File.h>
@ -23,34 +25,67 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
args_parser.add_option(keep_input_files, "Keep (don't delete) input files", "keep", 'k'); 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(write_to_stdout, "Write to stdout, keep original files unchanged", "stdout", 'c');
args_parser.add_option(decompress, "Decompress", "decompress", 'd'); 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); 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) if (write_to_stdout)
keep_input_files = true; keep_input_files = true;
for (auto const& input_filename : filenames) { for (auto const& input_filename : filenames) {
ByteString output_filename; OwnPtr<Stream> output_stream;
if (decompress) {
if (write_to_stdout) {
output_stream = TRY(Core::File::standard_output());
} else if (decompress) {
if (!input_filename.ends_with(".gz"sv)) { if (!input_filename.ends_with(".gz"sv)) {
warnln("unknown suffix for: {}, skipping", input_filename); warnln("unknown suffix for: {}, skipping", input_filename);
continue; 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 { } 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) NonnullOwnPtr<Core::File> input_file = TRY(Core::File::open_file_or_standard_stream(input_filename, Core::File::OpenMode::Read));
TRY(Compress::GzipDecompressor::decompress_file(input_filename, move(output_stream)));
else
TRY(Compress::GzipCompressor::compress_file(input_filename, move(output_stream)));
if (!keep_input_files) { // Buffer reads, which yields a significant performance improvement.
NonnullOwnPtr<Stream> input_stream = TRY(Core::InputBufferedFile::create(move(input_file), 1 * MiB));
if (decompress) {
input_stream = TRY(try_make<Compress::GzipDecompressor>(move(input_stream)));
} else {
output_stream = TRY(try_make<Compress::GzipCompressor>(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)); TRY(Core::System::unlink(input_filename));
}
} }
return 0; return 0;