Serenity Operating System
at master 132 lines 3.8 kB view raw
1/* 2 * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2022, Eli Youngs <eli.m.youngs@gmail.com> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <AK/Array.h> 9#include <LibCore/ArgsParser.h> 10#include <LibCore/File.h> 11#include <LibCore/System.h> 12#include <LibMain/Main.h> 13#include <ctype.h> 14#include <string.h> 15 16static constexpr size_t LINE_LENGTH_BYTES = 16; 17 18enum class State { 19 Print, 20 PrintFiller, 21 SkipPrint 22}; 23 24ErrorOr<int> serenity_main(Main::Arguments args) 25{ 26 TRY(Core::System::pledge("stdio rpath")); 27 28 Core::ArgsParser args_parser; 29 StringView path; 30 bool verbose = false; 31 Optional<size_t> max_bytes; 32 Optional<size_t> seek_to; 33 34 args_parser.add_positional_argument(path, "Input", "input", Core::ArgsParser::Required::No); 35 args_parser.add_option(verbose, "Display all input data", "verbose", 'v'); 36 args_parser.add_option(max_bytes, "Truncate to a fixed number of bytes", nullptr, 'n', "bytes"); 37 args_parser.add_option(seek_to, "Seek to a byte offset", "seek", 's', "offset"); 38 args_parser.parse(args); 39 40 auto file = TRY(Core::File::open_file_or_standard_stream(path, Core::File::OpenMode::Read)); 41 if (seek_to.has_value()) 42 TRY(file->seek(seek_to.value(), SeekMode::SetPosition)); 43 44 auto print_line = [](Bytes line) { 45 VERIFY(line.size() <= LINE_LENGTH_BYTES); 46 for (size_t i = 0; i < LINE_LENGTH_BYTES; ++i) { 47 if (i < line.size()) 48 out("{:02x} ", line[i]); 49 else 50 out(" "); 51 52 if (i == 7) 53 out(" "); 54 } 55 56 out(" |"); 57 58 for (auto const& byte : line) { 59 if (isprint(byte)) 60 putchar(byte); 61 else 62 putchar('.'); 63 } 64 65 putchar('|'); 66 putchar('\n'); 67 }; 68 69 Array<u8, BUFSIZ> contents; 70 Bytes bytes; 71 Bytes previous_line; 72 static_assert(LINE_LENGTH_BYTES * 2 <= contents.size(), "Buffer is too small?!"); 73 size_t total_bytes_read = 0; 74 75 auto state = State::Print; 76 bool is_input_remaining = true; 77 while (is_input_remaining) { 78 auto bytes_to_read = contents.size() - bytes.size(); 79 80 if (max_bytes.has_value()) { 81 auto bytes_remaining = max_bytes.value() - total_bytes_read; 82 if (bytes_remaining < bytes_to_read) { 83 bytes_to_read = bytes_remaining; 84 is_input_remaining = false; 85 } 86 } 87 88 bytes = contents.span().slice(0, bytes_to_read); 89 bytes = TRY(file->read_some(bytes)); 90 91 total_bytes_read += bytes.size(); 92 93 if (bytes.size() < bytes_to_read) { 94 is_input_remaining = false; 95 } 96 97 while (bytes.size() > LINE_LENGTH_BYTES) { 98 auto current_line = bytes.slice(0, LINE_LENGTH_BYTES); 99 bytes = bytes.slice(LINE_LENGTH_BYTES); 100 101 if (verbose) { 102 print_line(current_line); 103 continue; 104 } 105 106 bool is_same_contents = (current_line == previous_line); 107 if (!is_same_contents) 108 state = State::Print; 109 else if (is_same_contents && (state != State::SkipPrint)) 110 state = State::PrintFiller; 111 112 // Coalesce repeating lines 113 switch (state) { 114 case State::Print: 115 print_line(current_line); 116 break; 117 case State::PrintFiller: 118 outln("*"); 119 state = State::SkipPrint; 120 break; 121 case State::SkipPrint: 122 break; 123 } 124 previous_line = current_line; 125 } 126 } 127 128 if (bytes.size() > 0) 129 print_line(bytes); 130 131 return 0; 132}