|
@@ -83,6 +83,8 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|
TRY(Core::System::pledge("stdio rpath"));
|
|
TRY(Core::System::pledge("stdio rpath"));
|
|
|
|
|
|
bool follow = false;
|
|
bool follow = false;
|
|
|
|
+ size_t wanted_byte_count = 0;
|
|
|
|
+ bool byte_mode = false;
|
|
size_t wanted_line_count = DEFAULT_LINE_COUNT;
|
|
size_t wanted_line_count = DEFAULT_LINE_COUNT;
|
|
bool start_from_end = true;
|
|
bool start_from_end = true;
|
|
StringView file;
|
|
StringView file;
|
|
@@ -114,6 +116,30 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|
return true;
|
|
return true;
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
+ args_parser.add_option(Core::ArgsParser::Option {
|
|
|
|
+ .argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
|
|
|
|
+ .help_string = "output the last NUM bytes; or use -c +NUM to"
|
|
|
|
+ " output starting with byte NUM",
|
|
|
|
+ .long_name = "bytes",
|
|
|
|
+ .short_name = 'c',
|
|
|
|
+ .value_name = "[+]NUM",
|
|
|
|
+ .accept_value = [&](StringView bytes) -> ErrorOr<bool> {
|
|
|
|
+ Optional<size_t> value;
|
|
|
|
+ if (bytes.starts_with('+')) {
|
|
|
|
+ value = bytes.substring_view(1, bytes.length() - 1).to_number<size_t>();
|
|
|
|
+ start_from_end = false;
|
|
|
|
+ } else {
|
|
|
|
+ value = bytes.to_number<size_t>();
|
|
|
|
+ }
|
|
|
|
+ if (!value.has_value()) {
|
|
|
|
+ warnln("Invalid number: {}", bytes);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ wanted_byte_count = value.value();
|
|
|
|
+ byte_mode = true;
|
|
|
|
+ return true;
|
|
|
|
+ },
|
|
|
|
+ });
|
|
args_parser.add_positional_argument(file, "File path", "file", Core::ArgsParser::Required::No);
|
|
args_parser.add_positional_argument(file, "File path", "file", Core::ArgsParser::Required::No);
|
|
args_parser.parse(arguments);
|
|
args_parser.parse(arguments);
|
|
|
|
|
|
@@ -135,6 +161,26 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|
size_t line_index = 0;
|
|
size_t line_index = 0;
|
|
StringBuilder line;
|
|
StringBuilder line;
|
|
|
|
|
|
|
|
+ if (byte_mode) {
|
|
|
|
+ if (start_from_end) {
|
|
|
|
+ if (wanted_byte_count > bytes.size()) {
|
|
|
|
+ out("{}", StringView { bytes });
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ out("{}", StringView { bytes }.substring_view(bytes.size() - wanted_byte_count, wanted_byte_count));
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (wanted_byte_count > bytes.size())
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ if (wanted_byte_count > 0)
|
|
|
|
+ out("{}", StringView { bytes }.substring_view(wanted_byte_count - 1, bytes.size() - wanted_byte_count + 1));
|
|
|
|
+ else
|
|
|
|
+ out("{}", StringView { bytes });
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (!line_count && wanted_line_count) {
|
|
if (!line_count && wanted_line_count) {
|
|
out("{}", StringView { bytes });
|
|
out("{}", StringView { bytes });
|
|
continue;
|
|
continue;
|
|
@@ -180,7 +226,23 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
- auto pos = TRY(find_seek_pos(*f, wanted_line_count, start_from_end));
|
|
|
|
|
|
+ off_t pos = 0;
|
|
|
|
+ if (byte_mode) {
|
|
|
|
+ auto file_size = TRY(f->size());
|
|
|
|
+ if (wanted_byte_count > file_size)
|
|
|
|
+ wanted_byte_count = file_size;
|
|
|
|
+ if (start_from_end) {
|
|
|
|
+ TRY(f->seek(wanted_byte_count * -1, SeekMode::FromEndPosition));
|
|
|
|
+ } else {
|
|
|
|
+ if (wanted_byte_count > 0 && wanted_byte_count < file_size)
|
|
|
|
+ TRY(f->seek(wanted_byte_count - 1, SeekMode::SetPosition));
|
|
|
|
+ else
|
|
|
|
+ TRY(f->seek(0, SeekMode::FromEndPosition));
|
|
|
|
+ }
|
|
|
|
+ pos = TRY(f->tell());
|
|
|
|
+ } else {
|
|
|
|
+ pos = TRY(find_seek_pos(*f, wanted_line_count, start_from_end));
|
|
|
|
+ }
|
|
TRY(tail_from_pos(*f, pos));
|
|
TRY(tail_from_pos(*f, pos));
|
|
|
|
|
|
if (follow) {
|
|
if (follow) {
|