ladybird/Userland/Utilities/hexdump.cpp
Tim Schumacher d5871f5717 AK: Rename Stream::{read,write} to Stream::{read_some,write_some}
Similar to POSIX read, the basic read and write functions of AK::Stream
do not have a lower limit of how much data they read or write (apart
from "none at all").

Rename the functions to "read some [data]" and "write some [data]" (with
"data" being omitted, since everything here is reading and writing data)
to make them sufficiently distinct from the functions that ensure to
use the entire buffer (which should be the go-to function for most
usages).

No functional changes, just a lot of new FIXMEs.
2023-03-13 15:16:20 +00:00

132 lines
3.7 KiB
C++

/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2022, Eli Youngs <eli.m.youngs@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Array.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <LibCore/System.h>
#include <LibMain/Main.h>
#include <ctype.h>
#include <string.h>
static constexpr size_t LINE_LENGTH_BYTES = 16;
enum class State {
Print,
PrintFiller,
SkipPrint
};
ErrorOr<int> serenity_main(Main::Arguments args)
{
TRY(Core::System::pledge("stdio rpath"));
Core::ArgsParser args_parser;
StringView path;
bool verbose = false;
Optional<size_t> max_bytes;
Optional<size_t> seek_to;
args_parser.add_positional_argument(path, "Input", "input", Core::ArgsParser::Required::No);
args_parser.add_option(verbose, "Display all input data", "verbose", 'v');
args_parser.add_option(max_bytes, "Truncate to a fixed number of bytes", nullptr, 'n', "bytes");
args_parser.add_option(seek_to, "Seek to a byte offset", "seek", 's', "offset");
args_parser.parse(args);
auto file = TRY(Core::File::open_file_or_standard_stream(path, Core::File::OpenMode::Read));
if (seek_to.has_value())
TRY(file->seek(seek_to.value(), SeekMode::SetPosition));
auto print_line = [](Bytes line) {
VERIFY(line.size() <= LINE_LENGTH_BYTES);
for (size_t i = 0; i < LINE_LENGTH_BYTES; ++i) {
if (i < line.size())
out("{:02x} ", line[i]);
else
out(" ");
if (i == 7)
out(" ");
}
out(" |");
for (auto const& byte : line) {
if (isprint(byte))
putchar(byte);
else
putchar('.');
}
putchar('|');
putchar('\n');
};
Array<u8, BUFSIZ> contents;
Bytes bytes;
Bytes previous_line;
static_assert(LINE_LENGTH_BYTES * 2 <= contents.size(), "Buffer is too small?!");
size_t total_bytes_read = 0;
auto state = State::Print;
bool is_input_remaining = true;
while (is_input_remaining) {
auto bytes_to_read = contents.size() - bytes.size();
if (max_bytes.has_value()) {
auto bytes_remaining = max_bytes.value() - total_bytes_read;
if (bytes_remaining < bytes_to_read) {
bytes_to_read = bytes_remaining;
is_input_remaining = false;
}
}
bytes = contents.span().slice(0, bytes_to_read);
bytes = TRY(file->read_some(bytes));
total_bytes_read += bytes.size();
if (bytes.size() < bytes_to_read) {
is_input_remaining = false;
}
while (bytes.size() > LINE_LENGTH_BYTES) {
auto current_line = bytes.slice(0, LINE_LENGTH_BYTES);
bytes = bytes.slice(LINE_LENGTH_BYTES);
if (verbose) {
print_line(current_line);
continue;
}
bool is_same_contents = (current_line == previous_line);
if (!is_same_contents)
state = State::Print;
else if (is_same_contents && (state != State::SkipPrint))
state = State::PrintFiller;
// Coalesce repeating lines
switch (state) {
case State::Print:
print_line(current_line);
break;
case State::PrintFiller:
outln("*");
state = State::SkipPrint;
break;
case State::SkipPrint:
break;
}
previous_line = current_line;
}
}
if (bytes.size() > 0)
print_line(bytes);
return 0;
}