1
0
mirror of https://github.com/SerenityOS/serenity synced 2024-07-09 01:50:46 +00:00

grep: Hyperlink filenames in tty

As the newly created function has been also applied to printing the
number of matched file lines, file names will now also be colored
with the `--count` option set. :^)
This commit is contained in:
Karol Kosek 2023-09-14 16:10:03 +02:00 committed by Andreas Kling
parent db4a654e9f
commit f2f98b7938
2 changed files with 84 additions and 14 deletions

View File

@ -5,7 +5,7 @@ grep
## Synopsis
```sh
$ grep [--recursive] [--extended-regexp] [--fixed-strings] [--regexp Pattern] [--file File] [-i] [--line-numbers] [--invert-match] [--quiet] [--no-messages] [--binary-mode ] [--text] [-I] [--color WHEN] [--count] [file...]
$ grep [--recursive] [--extended-regexp] [--fixed-strings] [--regexp Pattern] [--file File] [-i] [--line-numbers] [--invert-match] [--quiet] [--no-messages] [--binary-mode ] [--text] [-I] [--color WHEN] [--no-hyperlinks] [--count] [file...]
```
## Options
@ -24,6 +24,7 @@ $ grep [--recursive] [--extended-regexp] [--fixed-strings] [--regexp Pattern] [-
* `-a`, `--text`: Treat binary files as text (same as --binary-mode text)
* `-I`: Ignore binary files (same as --binary-mode skip)
* `--color WHEN`: When to use colored output for the matching text ([auto], never, always)
* `--no-hyperlinks`: Disable hyperlinks
* `-c`, `--count`: Output line count instead of line contents
## Arguments

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2020, Emanuel Sprung <emanuel.sprung@gmail.com>
* Copyright (c) 2023, Karol Kosek <krkk@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -9,6 +10,7 @@
#include <AK/LexicalPath.h>
#include <AK/ScopeGuard.h>
#include <AK/StringBuilder.h>
#include <AK/URL.h>
#include <AK/Vector.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/DirIterator.h>
@ -56,6 +58,61 @@ static DeprecatedString escape_characters(StringView string, StringView characte
return builder.to_deprecated_string();
}
static DeprecatedString& hostname()
{
static DeprecatedString s_hostname;
if (s_hostname.is_empty()) {
auto result = Core::System::gethostname();
if (result.is_error())
s_hostname = "localhost";
else
s_hostname = result.release_value();
}
return s_hostname;
}
enum class PrintType {
Path = 1 << 0,
LineNumbers = 1 << 1,
};
AK_ENUM_BITWISE_OPERATORS(PrintType)
static void append_formatted_path(StringBuilder& builder, StringView path, Optional<size_t> line_number, PrintType print_type, bool with_hyperlinks, bool with_color)
{
if (path == '-') {
path = "(standard input)"sv;
with_hyperlinks = false;
}
if (with_hyperlinks) {
auto full_path_or_error = FileSystem::real_path(path);
if (!full_path_or_error.is_error()) {
auto fullpath = full_path_or_error.release_value();
auto url = URL::create_with_file_scheme(fullpath.to_deprecated_string(), {}, hostname());
if (has_flag(print_type, PrintType::LineNumbers) && line_number.has_value())
url.set_query(MUST(String::formatted("line_number={}", *line_number)));
builder.appendff("\033]8;;{}\033\\", url.serialize());
}
}
if (has_flag(print_type, PrintType::Path)) {
if (with_color)
builder.appendff("\033[34m{}\033[39m", path);
else
builder.append(path);
}
if (has_flag(print_type, PrintType::LineNumbers) && line_number.has_value()) {
if (has_flag(print_type, PrintType::Path))
builder.append(':');
if (with_color)
builder.appendff("\033[35m{}\033[39m", *line_number);
else
builder.appendff("{}", *line_number);
}
if (with_hyperlinks)
builder.append("\033]8;;\033\\"sv);
}
ErrorOr<int> serenity_main(Main::Arguments args)
{
TRY(Core::System::pledge("stdio rpath"));
@ -75,7 +132,9 @@ ErrorOr<int> serenity_main(Main::Arguments args)
bool invert_match = false;
bool quiet_mode = false;
bool suppress_errors = false;
bool colored_output = isatty(STDOUT_FILENO);
bool is_a_tty = isatty(STDOUT_FILENO) == 1;
bool colored_output = is_a_tty;
bool disable_hyperlinks = !is_a_tty;
bool count_lines = false;
size_t matched_line_count = 0;
@ -145,6 +204,7 @@ ErrorOr<int> serenity_main(Main::Arguments args)
return true;
},
});
args_parser.add_option(disable_hyperlinks, "Disable hyperlinks", "no-hyperlinks", 0);
args_parser.add_option(count_lines, "Output line count instead of line contents", "count", 'c');
args_parser.add_positional_argument(files, "File(s) to process", "file", Core::ArgsParser::Required::No);
args_parser.parse(args);
@ -200,12 +260,21 @@ ErrorOr<int> serenity_main(Main::Arguments args)
}
if (is_binary && binary_mode == BinaryFileMode::Binary) {
outln(colored_output ? "binary file \x1B[34m{}\x1B[0m matches"sv : "binary file {} matches"sv, filename);
StringBuilder filename_builder;
append_formatted_path(filename_builder, filename, {}, PrintType::Path, !disable_hyperlinks, colored_output);
outln("binary file {} matches"sv, filename_builder.string_view());
} else {
if ((result.matches.size() || invert_match) && print_filename)
out(colored_output ? "\x1B[34m{}:\x1B[0m"sv : "{}:"sv, filename);
if ((result.matches.size() || invert_match) && line_numbers)
out(colored_output ? "\x1B[35m{}:\x1B[0m"sv : "{}:"sv, line_number);
PrintType print_type { 0 };
if (print_filename)
print_type |= PrintType::Path;
if (line_numbers)
print_type |= PrintType::LineNumbers;
if ((result.matches.size() || invert_match) && has_any_flag(print_type, PrintType::Path | PrintType::LineNumbers)) {
StringBuilder filename_builder;
append_formatted_path(filename_builder, filename, line_number, print_type, !disable_hyperlinks, colored_output);
out("{}:"sv, filename_builder.string_view());
}
for (auto& match : result.matches) {
auto pre_match_length = match.global_offset - last_printed_char_pos;
@ -226,12 +295,10 @@ ErrorOr<int> serenity_main(Main::Arguments args)
auto exit_status = ExitStatus::NoLinesMatched;
auto handle_file = [&matches, binary_mode, count_lines, quiet_mode,
auto handle_file = [&matches, binary_mode, count_lines, quiet_mode, disable_hyperlinks, colored_output,
&matched_line_count, &exit_status](StringView filename, bool print_filename) -> ErrorOr<void> {
auto file = TRY(Core::File::open_file_or_standard_stream(filename, Core::File::OpenMode::Read));
auto buffered_file = TRY(Core::InputBufferedFile::create(move(file)));
if (filename == '-')
filename = "stdin"sv;
for (size_t line_number = 1; TRY(buffered_file->can_read_line()); ++line_number) {
Array<u8, PAGE_SIZE> buffer;
@ -249,10 +316,12 @@ ErrorOr<int> serenity_main(Main::Arguments args)
}
if (count_lines && !quiet_mode) {
if (print_filename)
outln("{}:{}", filename, matched_line_count);
else
outln("{}", matched_line_count);
if (print_filename) {
StringBuilder filename_builder;
append_formatted_path(filename_builder, filename, {}, PrintType::Path, !disable_hyperlinks, colored_output);
out("{}:", filename_builder.string_view());
}
outln("{}", matched_line_count);
matched_line_count = 0;
}