From 7f99f3bf29cd9fc085db636eaf777d4a7071ca88 Mon Sep 17 00:00:00 2001 From: Tim Ledbetter Date: Sat, 5 Aug 2023 18:38:55 +0100 Subject: [PATCH] fortune: Use simple formatting when stdout isn't connected to a terminal This changes the default behavior, so that, by default, color codes, hyperlinks and additional spacing are only emitted when standard output is connected to a terminal. The default coloring behavior can be overridden with the `--color` option. Valid arguments for this option are: 'always', 'never' and 'auto' (default). --- Base/usr/share/man/man1/fortune.md | 6 +++- Userland/Utilities/fortune.cpp | 50 +++++++++++++++++++++++++----- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/Base/usr/share/man/man1/fortune.md b/Base/usr/share/man/man1/fortune.md index 933f9f5042..bf50aa4963 100644 --- a/Base/usr/share/man/man1/fortune.md +++ b/Base/usr/share/man/man1/fortune.md @@ -5,13 +5,17 @@ fortune ## Synopsis ```sh -$ fortune [path] +$ fortune [--color when] [path] ``` ## Description Open a fortune cookie, receive a free quote for the day! +## Options + +* `--color when`: Chose when to color the output. Valid options are always, never and auto (default). When color is set to auto, color codes will be emitted when stdout is a terminal + ## Arguments * `path`: Path to JSON file with quotes (/res/fortunes.json by default) diff --git a/Userland/Utilities/fortune.cpp b/Userland/Utilities/fortune.cpp index cff26026fd..62bf908230 100644 --- a/Userland/Utilities/fortune.cpp +++ b/Userland/Utilities/fortune.cpp @@ -76,8 +76,28 @@ ErrorOr serenity_main(Main::Arguments arguments) StringView path = "/res/fortunes.json"sv; + Optional force_color; + Core::ArgsParser args_parser; args_parser.set_general_help("Open a fortune cookie, receive a free quote for the day!"); + args_parser.add_option(Core::ArgsParser::Option { + .argument_mode = Core::ArgsParser::OptionArgumentMode::Required, + .help_string = "Chose when to color the output. Valid options are always, never and auto (default). When color is set to auto, color codes will be emitted when stdout is a terminal", + .long_name = "color", + .value_name = "when", + .accept_value = [&force_color](StringView color_when_string) { + if (color_when_string.equals_ignoring_ascii_case("always"sv)) { + force_color = true; + } else if (color_when_string.equals_ignoring_ascii_case("never"sv)) { + force_color = false; + } else if (!color_when_string.equals_ignoring_ascii_case("auto"sv)) { + warnln("Unknown argument '{}'. Valid arguments for --color are always, never and auto", color_when_string); + return false; + } + + return true; + }, + }); args_parser.add_positional_argument(path, "Path to JSON file with quotes (/res/fortunes.json by default)", "path", Core::ArgsParser::Required::No); args_parser.parse(arguments); @@ -102,20 +122,34 @@ ErrorOr serenity_main(Main::Arguments arguments) u32 i = get_random_uniform(quotes.size()); auto const& chosen_quote = quotes[i]; auto datetime = Core::DateTime::from_timestamp(chosen_quote.utc_time()); + auto stdout_is_tty = TRY(Core::System::isatty(STDOUT_FILENO)); + auto show_color = force_color.has_value() ? force_color.value() : stdout_is_tty; - outln(); // Tasteful spacing + if (stdout_is_tty) { + outln(); // Tasteful spacing + out("\033]8;;{}\033\\", chosen_quote.url()); // Begin link + } + + if (show_color) { + out("\033[34m({})\033[m", datetime.to_deprecated_string()); + out(" \033[34;1m<{}>\033[m", chosen_quote.author()); + out(" \033[32m{}\033[m", chosen_quote.quote()); + } else { + out("({})", datetime.to_deprecated_string()); + out(" <{}>", chosen_quote.author()); + out(" {}", chosen_quote.quote()); + } + + if (stdout_is_tty) + out("\033]8;;\033\\"); // End link - out("\033]8;;{}\033\\", chosen_quote.url()); // Begin link - out("\033[34m({})\033[m", datetime.to_deprecated_string()); // Datetime - out(" \033[34;1m<{}>\033[m", chosen_quote.author()); // Author - out(" \033[32m{}\033[m", chosen_quote.quote()); // Quote itself - out("\033]8;;\033\\"); // End link outln(); if (chosen_quote.context().has_value()) - outln("\033[38;5;242m({})\033[m", chosen_quote.context().value()); // Some context + outln("{}", chosen_quote.context().value()); - outln(); // Tasteful spacing + if (stdout_is_tty) + outln(); // Tasteful spacing return 0; }