1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
/*
* 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/Stream.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;
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.parse(args);
auto file = TRY(Core::Stream::File::open_file_or_standard_stream(path, Core::Stream::OpenMode::Read));
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(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;
}
|