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; }