diff --git a/Userland/Utilities/tail.cpp b/Userland/Utilities/tail.cpp index d78e8850b02..d93e381d02b 100644 --- a/Userland/Utilities/tail.cpp +++ b/Userland/Utilities/tail.cpp @@ -83,6 +83,8 @@ ErrorOr serenity_main(Main::Arguments arguments) TRY(Core::System::pledge("stdio rpath")); bool follow = false; + size_t wanted_byte_count = 0; + bool byte_mode = false; size_t wanted_line_count = DEFAULT_LINE_COUNT; bool start_from_end = true; StringView file; @@ -114,6 +116,30 @@ ErrorOr serenity_main(Main::Arguments arguments) 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 { + Optional value; + if (bytes.starts_with('+')) { + value = bytes.substring_view(1, bytes.length() - 1).to_number(); + start_from_end = false; + } else { + value = bytes.to_number(); + } + 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.parse(arguments); @@ -135,6 +161,26 @@ ErrorOr serenity_main(Main::Arguments arguments) size_t line_index = 0; 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) { out("{}", StringView { bytes }); continue; @@ -180,7 +226,23 @@ ErrorOr serenity_main(Main::Arguments arguments) 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)); if (follow) {