From a9ac3da508239be8a736e1ec19cc0f9766a834d1 Mon Sep 17 00:00:00 2001 From: Jurjen Bokma Date: Sun, 11 May 2025 12:20:42 +0200 Subject: [PATCH] Accept sizes with k/M/G/T/E suffixes. --- CmdlineParser.cc | 69 ++++++++++++++++++++++++++++++++++++++++++++++++ CmdlineParser.hh | 4 +++ rdfind.1 | 3 +++ rdfind.cc | 9 ++++--- 4 files changed, 82 insertions(+), 3 deletions(-) diff --git a/CmdlineParser.cc b/CmdlineParser.cc index 6a930df..ea9f5e0 100644 --- a/CmdlineParser.cc +++ b/CmdlineParser.cc @@ -10,6 +10,7 @@ #include #include #include +#include // project #include "CmdlineParser.hh" @@ -97,3 +98,71 @@ Parser::current_arg_is(const char* what) const { return 0 == std::strcmp(get_current_arg(), what); } + +namespace { + + enum FILESIZE : long long + { + k = 1024, + K = k, + M = k * k, + G = k * M, + T = k * G, + P = k * T, + E = k * P, + // too big for signed long long // Z = k * E, + // too big for signed long long // Y = k * Z, + // too big for signed long long // R = k * Y, + // too big for signed long long // Q = k * R, + }; + + + Fileinfo::filesizetype value_of_file_size_suffix(char suffix) + { + switch (toupper(suffix)) + { + // No 'B': too ambiguous. + case 'K': return FILESIZE::k; + case 'M': return FILESIZE::M; + case 'G': return FILESIZE::G; + case 'T': return FILESIZE::T; + case 'P': return FILESIZE::P; + case 'E': return FILESIZE::E; + // case 'Z': return FILESIZE::Z; + // case 'Y': return FILESIZE::Y; + // case 'R': return FILESIZE::R; + // case 'Q': return FILESIZE::Q; + default: throw std::runtime_error( + "The program only understands file size suffixes " + "K, M, G, T, P or E. Lower case works too."); + }; + } + +} + +Fileinfo::filesizetype long Parser::read_file_size(char const *text) +{ + + using filesizetype = Fileinfo::filesizetype; + + size_t pos = 0; + filesizetype const without_suffix = std::stoll(text, &pos); + + size_t const len = strlen(text); + filesizetype const multiplier = + (len - pos > 1) ? + throw std::runtime_error( + "The program only understands single-letter file size suffixes.") + : + (len > pos) ? + value_of_file_size_suffix(text[pos]) + : + 1; + + filesizetype const retval = multiplier * without_suffix; + + if (retval / multiplier != without_suffix) + throw std::runtime_error("File size overflows long long."); + + return retval; +} diff --git a/CmdlineParser.hh b/CmdlineParser.hh index 84210e0..54ea74b 100644 --- a/CmdlineParser.hh +++ b/CmdlineParser.hh @@ -7,6 +7,8 @@ #ifndef RDFIND_CMDLINEPARSER_HH_ #define RDFIND_CMDLINEPARSER_HH_ +#include "Fileinfo.hh" //for filesizetype + /** * Command line parser, designed to be easy to use for rdfind. * It will signal user errors by a helpful printout followed by @@ -49,6 +51,8 @@ public: bool current_arg_is(const char* what) const; + static Fileinfo::filesizetype read_file_size(char const *text); + private: const int m_argc{}; const char** m_argv{}; diff --git a/rdfind.1 b/rdfind.1 index 17a50ee..5e250bb 100644 --- a/rdfind.1 +++ b/rdfind.1 @@ -64,6 +64,9 @@ Ignore empty files. Setting this to true (the default) is equivalent to .BR \-minsize " "\fIN\fR Ignores files with less than N bytes. Default is 1, meaning empty files are ignored. + +File and buffer sizes can be given with suffix k, M, G etc. up to E, +meaning powers of 1024. .TP .BR \-maxsize " "\fIN\fR Ignores files with N bytes or more. Default is 0, which means this check diff --git a/rdfind.cc b/rdfind.cc index 71d7588..06002c9 100644 --- a/rdfind.cc +++ b/rdfind.cc @@ -58,6 +58,9 @@ usage() << " false implies -minsize 0)\n" << " -minsize N (N=1) ignores files with size less than N " "bytes\n" + << " For -minsize, -maxsize and -buffersize,\n" + " Suffixes 'K', 'M', 'G' etc. up to 'E'\n" + " are accepted. Factors are 1024, not 1000.\n" << " -maxsize N (N=0) ignores files with size N " "bytes and larger (use 0 to disable this check).\n" << " -followsymlinks true |(false) follow symlinks\n" @@ -153,13 +156,13 @@ parseOptions(Parser& parser) o.minimumfilesize = 0; } } else if (parser.try_parse_string("-minsize")) { - const long long minsize = std::stoll(parser.get_parsed_string()); + const long long minsize = Parser::read_file_size(parser.get_parsed_string()); if (minsize < 0) { throw std::runtime_error("negative value of minsize not allowed"); } o.minimumfilesize = minsize; } else if (parser.try_parse_string("-maxsize")) { - const long long maxsize = std::stoll(parser.get_parsed_string()); + const long long maxsize = Parser::read_file_size(parser.get_parsed_string()); if (maxsize < 0) { throw std::runtime_error("negative value of maxsize not allowed"); } @@ -199,7 +202,7 @@ parseOptions(Parser& parser) std::exit(EXIT_FAILURE); } } else if (parser.try_parse_string("-buffersize")) { - const long buffersize = std::stoll(parser.get_parsed_string()); + const long buffersize = Parser::read_file_size(parser.get_parsed_string()); constexpr long max_buffersize = 128 << 20; if (buffersize <= 0) { std::cerr << "a negative or zero buffersize is not allowed\n";