mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-09 17:44:56 +09:00
tail: Support skip from start mode
This commit adds the possibility to skip the first NUM lines of a file by calling `tail -n +NUM filename`.
This commit is contained in:
parent
7beabc8e91
commit
e8bdb7e5f8
Notes:
sideshowbarker
2024-07-17 04:41:05 +09:00
Author: https://github.com/fdellwing
Commit: e8bdb7e5f8
Pull-request: https://github.com/SerenityOS/serenity/pull/23182
Reviewed-by: https://github.com/tcl3
1 changed files with 68 additions and 12 deletions
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2024, Fabian Dellwing <fabian@dellwing.net>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -20,27 +21,39 @@ static ErrorOr<void> tail_from_pos(Core::File& file, off_t startline)
|
|||
return {};
|
||||
}
|
||||
|
||||
static ErrorOr<off_t> find_seek_pos(Core::File& file, int wanted_lines)
|
||||
static ErrorOr<off_t> find_seek_pos(Core::File& file, int wanted_lines, bool start_from_end)
|
||||
{
|
||||
// Rather than reading the whole file, start at the end and work backwards,
|
||||
// stopping when we've found the number of lines we want.
|
||||
off_t pos = TRY(file.seek(0, SeekMode::FromEndPosition));
|
||||
|
||||
off_t end = pos;
|
||||
int lines = 0;
|
||||
|
||||
for (; pos >= 1; pos--) {
|
||||
TRY(file.seek(pos - 1, SeekMode::SetPosition));
|
||||
if (start_from_end) {
|
||||
off_t pos = TRY(file.seek(0, SeekMode::FromEndPosition));
|
||||
off_t end = pos;
|
||||
for (; pos >= 1; pos--) {
|
||||
TRY(file.seek(pos - 1, SeekMode::SetPosition));
|
||||
|
||||
auto ch = TRY(file.read_value<u8>());
|
||||
if (ch == '\n' && (end - pos) > 0) {
|
||||
lines++;
|
||||
if (lines == wanted_lines)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
off_t file_size = TRY(file.size());
|
||||
off_t pos = 0;
|
||||
|
||||
for (; pos < file_size; pos++) {
|
||||
auto ch = TRY(file.read_value<u8>());
|
||||
if (ch == '\n' && (end - pos) > 0) {
|
||||
if (ch == '\n') {
|
||||
lines++;
|
||||
if (lines == wanted_lines)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pos;
|
||||
return pos + 1;
|
||||
}
|
||||
|
||||
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
|
@ -49,12 +62,35 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||
|
||||
bool follow = false;
|
||||
size_t wanted_line_count = DEFAULT_LINE_COUNT;
|
||||
bool start_from_end = true;
|
||||
StringView file;
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
args_parser.set_general_help("Print the end ('tail') of a file.");
|
||||
args_parser.add_option(follow, "Output data as it is written to the file", "follow", 'f');
|
||||
args_parser.add_option(wanted_line_count, "Fetch the specified number of lines", "lines", 'n', "number");
|
||||
args_parser.add_option(Core::ArgsParser::Option {
|
||||
.argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
|
||||
.help_string = "output the last NUM lines, instead of the last 10;"
|
||||
" or use -n +NUM to output starting with line NUM",
|
||||
.long_name = "lines",
|
||||
.short_name = 'n',
|
||||
.value_name = "[+]NUM",
|
||||
.accept_value = [&](StringView lines) -> ErrorOr<bool> {
|
||||
Optional<size_t> value;
|
||||
if (lines.starts_with('+')) {
|
||||
value = lines.substring_view(1, lines.length() - 1).to_number<size_t>();
|
||||
start_from_end = false;
|
||||
} else {
|
||||
value = lines.to_number<size_t>();
|
||||
}
|
||||
if (!value.has_value()) {
|
||||
warnln("Invalid number: {}", lines);
|
||||
return false;
|
||||
}
|
||||
wanted_line_count = value.value();
|
||||
return true;
|
||||
},
|
||||
});
|
||||
args_parser.add_positional_argument(file, "File path", "file", Core::ArgsParser::Required::No);
|
||||
args_parser.parse(arguments);
|
||||
|
||||
|
@ -81,6 +117,26 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!start_from_end) {
|
||||
if (wanted_line_count > line_count) {
|
||||
continue;
|
||||
}
|
||||
if (wanted_line_count == 0) {
|
||||
out("{}", StringView { bytes });
|
||||
continue;
|
||||
}
|
||||
for (size_t i = 0; i < bytes.size(); i++) {
|
||||
auto ch = bytes.at(i);
|
||||
if (ch == '\n') {
|
||||
line_index++;
|
||||
}
|
||||
if (line_index >= wanted_line_count)
|
||||
line.append(ch);
|
||||
}
|
||||
out("{}", line.to_byte_string().substring_view(1, line.length() - 1));
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < bytes.size(); i++) {
|
||||
auto ch = bytes.at(i);
|
||||
line.append(ch);
|
||||
|
@ -98,7 +154,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||
return 0;
|
||||
}
|
||||
|
||||
auto pos = TRY(find_seek_pos(*f, wanted_line_count));
|
||||
auto pos = TRY(find_seek_pos(*f, wanted_line_count, start_from_end));
|
||||
TRY(tail_from_pos(*f, pos));
|
||||
|
||||
if (follow) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue