diff --git a/Base/usr/share/man/man1/sizefmt.md b/Base/usr/share/man/man1/sizefmt.md new file mode 100644 index 0000000000..d729cd1bd3 --- /dev/null +++ b/Base/usr/share/man/man1/sizefmt.md @@ -0,0 +1,35 @@ +## Name + +sizefmt - Print the size of a number with a suffix, in bytes + +## Synopsis + +```**sh +$ sizefmt [integer-with-suffix] +``` + +## Description + +`sizefmt` prints the 'real' size of a number with a suffix (possibly). +It can parse just a single character suffix (k for kilo, for example), or base 2 or 10 +suffixes (KB for Kilobytes, or KiB for Kibibytes, for example). + +## Arguments + +* `integer-with-suffix`: a number with a suffix (possibly). + +## Examples + +```sh +# prints 10000000 for 10 million bytes +$ sizefmt 10MB + +# truncate a file /tmp/test_file with size of 10 Kibibytes +$ truncate -s $(sizefmt 10KiB) /tmp/test_file + +# truncate a file /tmp/test_file2 with size of 10 Megabytes +$ truncate -s $(sizefmt 10MB) /tmp/test_file + +# truncate a file /tmp/test_file3 with size of 2 Kibibytes +$ truncate -s $(sizefmt 2KiB) /tmp/test_file3 +``` diff --git a/Userland/Utilities/sizefmt.cpp b/Userland/Utilities/sizefmt.cpp new file mode 100644 index 0000000000..4320199cd9 --- /dev/null +++ b/Userland/Utilities/sizefmt.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static ErrorOr use_integer_with_char_suffix(StringView argument, size_t suffix_length) +{ + if (suffix_length >= argument.length()) + return Error::from_string_literal("Invalid value was specified"); + argument = argument.substring_view(0, argument.length() - suffix_length); + + Optional numeric_optional = argument.to_number(); + if (numeric_optional.has_value()) + return numeric_optional.release_value(); + return Error::from_string_literal("Invalid value was specified"); +} + +static ErrorOr handle_char_suffix(char suffix, AK::HumanReadableBasedOn human_readable_based_on) +{ + u64 suffix_multiplier = 1; + switch (suffix) { + case 'k': + case 'K': + suffix_multiplier = (human_readable_based_on == AK::HumanReadableBasedOn::Base2 ? KiB : KB); + break; + case 'M': + suffix_multiplier = (human_readable_based_on == AK::HumanReadableBasedOn::Base2 ? MiB : MB); + break; + case 'G': + suffix_multiplier = (human_readable_based_on == AK::HumanReadableBasedOn::Base2 ? GiB : GB); + break; + case 'T': + suffix_multiplier = (human_readable_based_on == AK::HumanReadableBasedOn::Base2 ? TiB : TB); + break; + default: + return Error::from_string_literal("Unknown size suffix"); + } + return suffix_multiplier; +} + +static ErrorOr multiply_number_with_suffix(u64 numeric_value_without_suffix, char suffix, AK::HumanReadableBasedOn human_readable_based_on) +{ + auto suffix_multiplier = TRY(handle_char_suffix(suffix, human_readable_based_on)); + if (Checked::multiplication_would_overflow(numeric_value_without_suffix, suffix_multiplier)) + return Error::from_string_literal("Numeric value multiplication would overflow"); + return numeric_value_without_suffix * suffix_multiplier; +} + +static ErrorOr handle_number(StringView argument) +{ + if (auto number_with_no_suffix = use_integer_with_char_suffix(argument, 0); !number_with_no_suffix.is_error()) + return number_with_no_suffix.release_value(); + + if (argument.ends_with("iB"sv)) { + auto numeric_value_without_suffix = TRY(use_integer_with_char_suffix(argument, 3)); + auto suffix = argument[argument.length() - 3]; + return multiply_number_with_suffix(numeric_value_without_suffix, suffix, AK::HumanReadableBasedOn::Base2); + } + + if (argument.ends_with("B"sv)) { + auto numeric_value_without_suffix = TRY(use_integer_with_char_suffix(argument, 2)); + auto suffix = argument[argument.length() - 2]; + return multiply_number_with_suffix(numeric_value_without_suffix, suffix, AK::HumanReadableBasedOn::Base10); + } + + return Error::from_string_literal("Invalid value was specified"); +} + +ErrorOr serenity_main(Main::Arguments arguments) +{ + TRY(Core::System::pledge("stdio rpath")); + + StringView argument; + + Core::ArgsParser args_parser; + args_parser.set_general_help( + "Show the 'real' size of a number with a suffix (possibly)."); + args_parser.add_positional_argument(argument, "Number with possibly a suffix", "number"); + args_parser.parse(arguments); + + if (argument.is_null() || argument.is_empty()) + return Error::from_string_literal("Invalid value"); + + outln("{}", TRY(handle_number(argument))); + return 0; +}