gzip.cpp 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. /*
  2. * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/ByteString.h>
  7. #include <AK/LexicalPath.h>
  8. #include <LibCompress/Gzip.h>
  9. #include <LibCore/ArgsParser.h>
  10. #include <LibCore/File.h>
  11. #include <LibCore/MappedFile.h>
  12. #include <LibCore/System.h>
  13. #include <LibMain/Main.h>
  14. #include <unistd.h>
  15. ErrorOr<int> serenity_main(Main::Arguments arguments)
  16. {
  17. Vector<StringView> filenames;
  18. bool keep_input_files { false };
  19. bool write_to_stdout { false };
  20. bool decompress { false };
  21. Core::ArgsParser args_parser;
  22. args_parser.add_option(keep_input_files, "Keep (don't delete) input files", "keep", 'k');
  23. args_parser.add_option(write_to_stdout, "Write to stdout, keep original files unchanged", "stdout", 'c');
  24. args_parser.add_option(decompress, "Decompress", "decompress", 'd');
  25. args_parser.add_positional_argument(filenames, "Files", "FILES", Core::ArgsParser::Required::No);
  26. args_parser.parse(arguments);
  27. auto program_name = LexicalPath::basename(arguments.strings[0]);
  28. // NOTE: If the user run this program via the /bin/zcat or /bin/gunzip symlink,
  29. // then emulate gzip decompression.
  30. if (program_name == "zcat"sv || program_name == "gunzip"sv)
  31. decompress = true;
  32. if (program_name == "zcat"sv)
  33. write_to_stdout = true;
  34. if (filenames.is_empty()) {
  35. filenames.append("-"sv);
  36. write_to_stdout = true;
  37. }
  38. if (write_to_stdout)
  39. keep_input_files = true;
  40. for (auto const& input_filename : filenames) {
  41. OwnPtr<Stream> output_stream;
  42. if (write_to_stdout) {
  43. output_stream = TRY(Core::File::standard_output());
  44. } else if (decompress) {
  45. if (!input_filename.ends_with(".gz"sv)) {
  46. warnln("unknown suffix for: {}, skipping", input_filename);
  47. continue;
  48. }
  49. auto output_filename = input_filename.substring_view(0, input_filename.length() - ".gz"sv.length());
  50. output_stream = TRY(Core::File::open(output_filename, Core::File::OpenMode::Write));
  51. } else {
  52. auto output_filename = ByteString::formatted("{}.gz", input_filename);
  53. output_stream = TRY(Core::File::open(output_filename, Core::File::OpenMode::Write));
  54. }
  55. VERIFY(output_stream);
  56. NonnullOwnPtr<Core::File> input_file = TRY(Core::File::open_file_or_standard_stream(input_filename, Core::File::OpenMode::Read));
  57. // Buffer reads, which yields a significant performance improvement.
  58. NonnullOwnPtr<Stream> input_stream = TRY(Core::InputBufferedFile::create(move(input_file), 1 * MiB));
  59. if (decompress) {
  60. input_stream = TRY(try_make<Compress::GzipDecompressor>(move(input_stream)));
  61. } else {
  62. output_stream = TRY(try_make<Compress::GzipCompressor>(output_stream.release_nonnull()));
  63. }
  64. auto buffer = TRY(ByteBuffer::create_uninitialized(1 * MiB));
  65. while (!input_stream->is_eof()) {
  66. auto span = TRY(input_stream->read_some(buffer));
  67. TRY(output_stream->write_until_depleted(span));
  68. }
  69. if (!keep_input_files)
  70. TRY(Core::System::unlink(input_filename));
  71. }
  72. return 0;
  73. }