From e64591f204ecd1b364373ed1ad0e1dc8a154653f Mon Sep 17 00:00:00 2001 From: tastytea Date: Thu, 27 May 2021 17:20:00 +0200 Subject: [PATCH] Rework option parsing, change --no-filename. Options are now better accessible, --no-filename accepts the values filesystem, in-epub or all. --- man/epubgrep.1.adoc | 8 ++-- src/main.cpp | 99 ++++++++++++++++++------------------------- src/options.cpp | 70 +++++++++++++++++++++++++++--- src/options.hpp | 35 ++++++++++++++- src/search.cpp | 8 ++-- src/search.hpp | 14 ++---- tests/test_search.cpp | 7 +-- 7 files changed, 157 insertions(+), 84 deletions(-) diff --git a/man/epubgrep.1.adoc b/man/epubgrep.1.adoc index 020bd13..71ce86e 100644 --- a/man/epubgrep.1.adoc +++ b/man/epubgrep.1.adoc @@ -58,9 +58,11 @@ Print _NUMBER_ words of context around matches. *--nocolor*:: Do not color matches. -*--no-filename*:: -Suppress the mentioning of EPUB file names on output. File names inside the -EPUB, chapters and page numbers will still be output. +*--no-filename* _WHICH_:: + +Suppress the mentioning of file names on output. _WHICH_ is ‘filesystem’ for the +file names on your file systems, ‘in-epub’ for the file names inside the EPUB or +‘all’. Chapters and page numbers will still be output. *-r*, *--recursive*: Read all files under each directory, recursively, following symbolic links only diff --git a/src/main.cpp b/src/main.cpp index eedc6f4..8dc0e52 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,8 +21,6 @@ #include #include -#include -#include #include #include // For compatibility with fmt 4. #include @@ -42,7 +40,6 @@ int main(int argc, char *argv[]) { - namespace po = boost::program_options; using namespace epubgrep; using boost::locale::translate; @@ -63,10 +60,10 @@ int main(int argc, char *argv[]) cout.imbue(std::locale()); cerr.imbue(std::locale()); - po::variables_map vm; + options::options opts; try { - vm = options::parse_options(argc, argv); + opts = options::parse_options(argc, argv); } catch (std::exception &e) { // Exceptions we can't recover from or ones we don't know. @@ -75,7 +72,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - if (vm.count("help") + vm.count("version") > 0) + if (opts.help || opts.version) { return EXIT_SUCCESS; } @@ -83,31 +80,26 @@ int main(int argc, char *argv[]) int return_code{EXIT_SUCCESS}; vector input_files; - if (vm.count("input-file") == 0) + if (opts.input_file.empty()) { cout << "NO INPUT FILE\n"; // TODO: Read data from stdin. return EXIT_FAILURE; } - for (const auto &filepath : vm["input-file"].as>()) + for (const auto &filepath : opts.input_file) { - if (vm.count("recursive") + vm.count("dereference-recursive") == 0) + if (!opts.recursive && !opts.dereference_recursive) { input_files.emplace_back(filepath); } else { - bool follow_symlinks{false}; - if (vm.count("dereference-recursive") > 0) - { - follow_symlinks = true; - } - try { auto files_in_dir{ - files::list_recursive(filepath, follow_symlinks)}; + files::list_recursive(filepath, + opts.dereference_recursive)}; input_files.insert(input_files.end(), files_in_dir.begin(), files_in_dir.end()); } @@ -130,45 +122,25 @@ int main(int argc, char *argv[]) } } - search::options opts; - if (vm.count("basic-regexp") > 0) - { - opts.regex = search::regex_kind::basic; - } - if (vm.count("extended-regexp") > 0) - { - opts.regex = search::regex_kind::extended; - } - if (vm.count("perl-regexp") > 0) - { - opts.regex = search::regex_kind::perl; - } - if (vm.count("grep") > 0) - { - opts.grep_like = true; - } - if (vm.count("ignore-case") > 0) - { - opts.ignore_case = true; - } - if (vm.count("raw") > 0) - { - opts.raw = true; - } - opts.context = vm["context"].as(); + search::settings search_settings; + search_settings.regex = opts.regex; + search_settings.grep_like = opts.grep; + search_settings.ignore_case = opts.ignore_case; + search_settings.raw = opts.raw; + search_settings.context = opts.context; vector> matches_all; vector> futurepool; auto search_file{ - [&vm, &matches_all, &opts](fs::path filepath) + [&opts, &matches_all, &search_settings](fs::path filepath) { - for (const auto ®ex : vm["regexp"].as>()) + for (const auto ®ex : opts.regexp) { try { matches_all.emplace_back( - search::search(filepath, regex, opts)); + search::search(filepath, regex, search_settings)); } catch (const std::exception &e) { @@ -231,43 +203,54 @@ int main(int argc, char *argv[]) fs::path last_epub; for (const auto &match : matches_file) { - if (input_files.size() <= 1 || vm.count("no-filename") == 0) + if (input_files.size() > 1 && !opts.no_fn_fs) { if (match.epub_filepath != last_epub) { - if (vm.count("nocolor") == 0) + if (!opts.nocolor) { cout << termcolor::yellow; } - cout << format(translate(" In {0:s}: \n").str().data(), fs::relative(match.epub_filepath)); last_epub = match.epub_filepath; - - if (vm.count("nocolor") == 0) + if (!opts.nocolor) { cout << termcolor::reset; } } } - cout << match.filepath; + + vector prefix; + if (!opts.no_fn_epub) + { + prefix.emplace_back(match.filepath); + } if (!match.headline.empty()) { - cout << ", " << match.headline; + prefix.emplace_back(match.headline); } if (!match.page.empty()) { - cout << ", page " << match.page; + prefix.emplace_back("page " + match.page); + } + for (const auto &part : prefix) + { + cout << part; + if (part != *(prefix.rbegin())) + { + cout << ", "; + } } cout << ": " << match.context.first; - if (vm.count("nocolor") == 0) + if (!opts.nocolor) { - cout << termcolor::bright_magenta << match.text - << termcolor::reset; + cout << termcolor::bright_magenta; } - else + cout << match.text; + if (!opts.nocolor) { - cout << match.text; + cout << termcolor::reset; } cout << match.context.second << '\n'; } diff --git a/src/options.cpp b/src/options.cpp index 293c0ad..dedf331 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -31,7 +31,9 @@ #include #include #include +#include #include +#include namespace epubgrep::options { @@ -41,7 +43,7 @@ namespace po = boost::program_options; using boost::locale::translate; using std::cout; -po::variables_map parse_options(int argc, char *argv[]) +options parse_options(int argc, char *argv[]) { po::options_description options_visible(translate("Available options")); // clang-format off @@ -73,9 +75,9 @@ po::variables_map parse_options(int argc, char *argv[]) translate("Print NUMBER words of context around matches.") .str().data()) ("nocolor", translate("Do not color matches.") .str().data()) - ("no-filename", - translate("Suppress the mentioning of EPUB file names on output.") - .str().data()) + ("no-filename",po::value()->value_name(translate("WHICH")), + translate("Suppress the mentioning of file names on output. " + "WHICH is ‘filesystem’, ‘in-epub’ or ‘all’.").str().data()) ("recursive,r", translate("Read all files under each directory, recursively.") .str().data()) @@ -139,7 +141,7 @@ po::variables_map parse_options(int argc, char *argv[]) "conditions.\n"); } - return vm; + return parse_again(vm); } fs::path get_config_path() @@ -172,4 +174,62 @@ fs::path get_config_path() return "epubgrep.conf"; } +options parse_again(const po::variables_map &vm) +{ + options opts; + + opts.help = vm.count("help") > 0; + opts.version = vm.count("version") > 0; + if (vm.count("basic-regexp") > 0) + { + opts.regex = regex_kind::basic; + } + if (vm.count("extended-regexp") > 0) + { + opts.regex = regex_kind::extended; + } + if (vm.count("perl-regexp") > 0) + { + opts.regex = regex_kind::perl; + } + opts.grep = vm.count("grep") > 0; + opts.ignore_case = vm.count("ignore-case") > 0; + opts.nocolor = vm.count("nocolor") > 0; + if (vm.count("no-filename") > 0) + { + if (vm["no-filename"].as() == "filesystem") + { + opts.no_fn_fs = true; + } + else if (vm["no-filename"].as() == "in-epub") + { + opts.no_fn_epub = true; + } + else if (vm["no-filename"].as() == "all") + { + opts.no_fn_fs = true; + opts.no_fn_epub = true; + } + else + { + throw std::runtime_error{"'--no-filename' must be either " + "‘filesystem’, ‘in-epub’ or ‘all’."}; + } + } + opts.recursive = vm.count("recursive") > 0; + opts.dereference_recursive = vm.count("dereference-recursive") > 0; + + if (vm.count("regexp") > 0) + { + opts.regexp = vm["regexp"].as>(); + } + + if (vm.count("input-file") > 0) + { + opts.input_file = vm["input-file"].as>(); + } + + return opts; +} + } // namespace epubgrep::options diff --git a/src/options.hpp b/src/options.hpp index 26e1f12..71eb3d3 100644 --- a/src/options.hpp +++ b/src/options.hpp @@ -21,13 +21,43 @@ #include +#include +#include +#include +#include + namespace epubgrep::options { namespace po = boost::program_options; +enum class regex_kind +{ + basic, + extended, + perl +}; + +struct options +{ + bool help{false}; + bool version{false}; + regex_kind regex{regex_kind::basic}; + bool grep{false}; + bool ignore_case{false}; + std::vector regexp; + bool raw{false}; + std::uint64_t context{0}; + bool nocolor{false}; + bool no_fn_fs{false}; + bool no_fn_epub{false}; + bool recursive{false}; + bool dereference_recursive{false}; + std::vector input_file; +}; + //! Parse options and return them. -[[nodiscard]] po::variables_map parse_options(int argc, char *argv[]); +[[nodiscard]] options parse_options(int argc, char *argv[]); /*! * @brief Returns the path of the config file. @@ -38,6 +68,9 @@ namespace po = boost::program_options; */ [[nodiscard]] fs::path get_config_path(); +//! Parse variables map and return nice options struct. +[[nodiscard]] options parse_again(const po::variables_map &vm); + } // namespace epubgrep::options #endif // EPUBGREP_OPTIONS_HPP diff --git a/src/search.cpp b/src/search.cpp index 647e08e..7cd5891 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -32,23 +32,23 @@ namespace epubgrep::search using std::string; std::vector search(const fs::path &filepath, std::string_view regex, - const options &opts) + const settings &opts) { boost::regex::flag_type flags{}; switch (opts.regex) { - case regex_kind::basic: + case options::regex_kind::basic: { flags = opts.grep_like ? boost::regex::grep : boost::regex::basic; break; } - case regex_kind::extended: + case options::regex_kind::extended: { flags = opts.grep_like ? boost::regex::egrep : boost::regex::extended; break; } - case regex_kind::perl: + case options::regex_kind::perl: { flags = boost::regex::perl; break; diff --git a/src/search.hpp b/src/search.hpp index 48544a2..0981729 100644 --- a/src/search.hpp +++ b/src/search.hpp @@ -18,6 +18,7 @@ #define EPUBGREP_SEARCH_HPP #include "fs-compat.hpp" +#include "options.hpp" #include @@ -42,16 +43,9 @@ struct match std::string page; //!< The page number, if available. }; -enum class regex_kind +struct settings { - basic, - extended, - perl -}; - -struct options -{ - regex_kind regex{regex_kind::basic}; + options::regex_kind regex{options::regex_kind::basic}; bool grep_like{false}; bool ignore_case{false}; bool raw{false}; @@ -61,7 +55,7 @@ struct options //! Search file, return matches. [[nodiscard]] std::vector search(const fs::path &filepath, std::string_view regex, - const options &opts); + const settings &opts); //! Strip HTML, remove newlines, condense spaces. void cleanup_text(std::string &text); diff --git a/tests/test_search.cpp b/tests/test_search.cpp index 3d23227..d4859d9 100644 --- a/tests/test_search.cpp +++ b/tests/test_search.cpp @@ -1,4 +1,5 @@ #include "fs-compat.hpp" +#include "options.hpp" #include "search.hpp" #include @@ -21,13 +22,13 @@ SCENARIO("Searching works") SECTION("search() doesn't fail and returns the right lines") { std::vector matches; - epubgrep::search::options opts; + epubgrep::search::settings opts; WHEN("We search for ‘📙+\\w?’ using extended regular expressions") { try { - opts.regex = epubgrep::search::regex_kind::extended; + opts.regex = epubgrep::options::regex_kind::extended; matches = epubgrep::search::search(zipfile, "📙+\\w?", opts); } catch (const std::exception &) @@ -104,7 +105,7 @@ SCENARIO("Searching works") try { opts.context = 1; - opts.regex = epubgrep::search::regex_kind::extended; + opts.regex = epubgrep::options::regex_kind::extended; matches = epubgrep::search::search( zipfile, R"(work\s[\w]+\.\W[\w']+\Wstay)", opts); }