Rework option parsing, change --no-filename.
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
Options are now better accessible, --no-filename accepts the values filesystem, in-epub or all.
This commit is contained in:
parent
c376ce8466
commit
e64591f204
|
@ -58,9 +58,11 @@ Print _NUMBER_ words of context around matches.
|
||||||
*--nocolor*::
|
*--nocolor*::
|
||||||
Do not color matches.
|
Do not color matches.
|
||||||
|
|
||||||
*--no-filename*::
|
*--no-filename* _WHICH_::
|
||||||
Suppress the mentioning of EPUB file names on output. File names inside the
|
|
||||||
EPUB, chapters and page numbers will still be output.
|
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*:
|
*-r*, *--recursive*:
|
||||||
Read all files under each directory, recursively, following symbolic links only
|
Read all files under each directory, recursively, following symbolic links only
|
||||||
|
|
99
src/main.cpp
99
src/main.cpp
|
@ -21,8 +21,6 @@
|
||||||
|
|
||||||
#include <boost/locale/generator.hpp>
|
#include <boost/locale/generator.hpp>
|
||||||
#include <boost/locale/message.hpp>
|
#include <boost/locale/message.hpp>
|
||||||
#include <boost/program_options/errors.hpp>
|
|
||||||
#include <boost/program_options/variables_map.hpp>
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <fmt/ostream.h> // For compatibility with fmt 4.
|
#include <fmt/ostream.h> // For compatibility with fmt 4.
|
||||||
#include <termcolor/termcolor.hpp>
|
#include <termcolor/termcolor.hpp>
|
||||||
|
@ -42,7 +40,6 @@
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
namespace po = boost::program_options;
|
|
||||||
using namespace epubgrep;
|
using namespace epubgrep;
|
||||||
|
|
||||||
using boost::locale::translate;
|
using boost::locale::translate;
|
||||||
|
@ -63,10 +60,10 @@ int main(int argc, char *argv[])
|
||||||
cout.imbue(std::locale());
|
cout.imbue(std::locale());
|
||||||
cerr.imbue(std::locale());
|
cerr.imbue(std::locale());
|
||||||
|
|
||||||
po::variables_map vm;
|
options::options opts;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
vm = options::parse_options(argc, argv);
|
opts = options::parse_options(argc, argv);
|
||||||
}
|
}
|
||||||
catch (std::exception &e)
|
catch (std::exception &e)
|
||||||
{ // Exceptions we can't recover from or ones we don't know.
|
{ // 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;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vm.count("help") + vm.count("version") > 0)
|
if (opts.help || opts.version)
|
||||||
{
|
{
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -83,31 +80,26 @@ int main(int argc, char *argv[])
|
||||||
int return_code{EXIT_SUCCESS};
|
int return_code{EXIT_SUCCESS};
|
||||||
|
|
||||||
vector<fs::path> input_files;
|
vector<fs::path> input_files;
|
||||||
if (vm.count("input-file") == 0)
|
if (opts.input_file.empty())
|
||||||
{
|
{
|
||||||
cout << "NO INPUT FILE\n";
|
cout << "NO INPUT FILE\n";
|
||||||
// TODO: Read data from stdin.
|
// TODO: Read data from stdin.
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
for (const auto &filepath : vm["input-file"].as<vector<string>>())
|
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);
|
input_files.emplace_back(filepath);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bool follow_symlinks{false};
|
|
||||||
if (vm.count("dereference-recursive") > 0)
|
|
||||||
{
|
|
||||||
follow_symlinks = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto files_in_dir{
|
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(),
|
input_files.insert(input_files.end(), files_in_dir.begin(),
|
||||||
files_in_dir.end());
|
files_in_dir.end());
|
||||||
}
|
}
|
||||||
|
@ -130,45 +122,25 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
search::options opts;
|
search::settings search_settings;
|
||||||
if (vm.count("basic-regexp") > 0)
|
search_settings.regex = opts.regex;
|
||||||
{
|
search_settings.grep_like = opts.grep;
|
||||||
opts.regex = search::regex_kind::basic;
|
search_settings.ignore_case = opts.ignore_case;
|
||||||
}
|
search_settings.raw = opts.raw;
|
||||||
if (vm.count("extended-regexp") > 0)
|
search_settings.context = opts.context;
|
||||||
{
|
|
||||||
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<std::uint64_t>();
|
|
||||||
|
|
||||||
vector<vector<search::match>> matches_all;
|
vector<vector<search::match>> matches_all;
|
||||||
vector<std::future<int>> futurepool;
|
vector<std::future<int>> futurepool;
|
||||||
|
|
||||||
auto search_file{
|
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<vector<string>>())
|
for (const auto ®ex : opts.regexp)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
matches_all.emplace_back(
|
matches_all.emplace_back(
|
||||||
search::search(filepath, regex, opts));
|
search::search(filepath, regex, search_settings));
|
||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
|
@ -231,43 +203,54 @@ int main(int argc, char *argv[])
|
||||||
fs::path last_epub;
|
fs::path last_epub;
|
||||||
for (const auto &match : matches_file)
|
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 (match.epub_filepath != last_epub)
|
||||||
{
|
{
|
||||||
if (vm.count("nocolor") == 0)
|
if (!opts.nocolor)
|
||||||
{
|
{
|
||||||
cout << termcolor::yellow;
|
cout << termcolor::yellow;
|
||||||
}
|
}
|
||||||
|
|
||||||
cout << format(translate(" In {0:s}: \n").str().data(),
|
cout << format(translate(" In {0:s}: \n").str().data(),
|
||||||
fs::relative(match.epub_filepath));
|
fs::relative(match.epub_filepath));
|
||||||
last_epub = match.epub_filepath;
|
last_epub = match.epub_filepath;
|
||||||
|
if (!opts.nocolor)
|
||||||
if (vm.count("nocolor") == 0)
|
|
||||||
{
|
{
|
||||||
cout << termcolor::reset;
|
cout << termcolor::reset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cout << match.filepath;
|
|
||||||
|
vector<string> prefix;
|
||||||
|
if (!opts.no_fn_epub)
|
||||||
|
{
|
||||||
|
prefix.emplace_back(match.filepath);
|
||||||
|
}
|
||||||
if (!match.headline.empty())
|
if (!match.headline.empty())
|
||||||
{
|
{
|
||||||
cout << ", " << match.headline;
|
prefix.emplace_back(match.headline);
|
||||||
}
|
}
|
||||||
if (!match.page.empty())
|
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;
|
cout << ": " << match.context.first;
|
||||||
if (vm.count("nocolor") == 0)
|
if (!opts.nocolor)
|
||||||
{
|
{
|
||||||
cout << termcolor::bright_magenta << match.text
|
cout << termcolor::bright_magenta;
|
||||||
<< termcolor::reset;
|
|
||||||
}
|
}
|
||||||
else
|
cout << match.text;
|
||||||
|
if (!opts.nocolor)
|
||||||
{
|
{
|
||||||
cout << match.text;
|
cout << termcolor::reset;
|
||||||
}
|
}
|
||||||
cout << match.context.second << '\n';
|
cout << match.context.second << '\n';
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,9 @@
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace epubgrep::options
|
namespace epubgrep::options
|
||||||
{
|
{
|
||||||
|
@ -41,7 +43,7 @@ namespace po = boost::program_options;
|
||||||
using boost::locale::translate;
|
using boost::locale::translate;
|
||||||
using std::cout;
|
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"));
|
po::options_description options_visible(translate("Available options"));
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
@ -73,9 +75,9 @@ po::variables_map parse_options(int argc, char *argv[])
|
||||||
translate("Print NUMBER words of context around matches.")
|
translate("Print NUMBER words of context around matches.")
|
||||||
.str().data())
|
.str().data())
|
||||||
("nocolor", translate("Do not color matches.") .str().data())
|
("nocolor", translate("Do not color matches.") .str().data())
|
||||||
("no-filename",
|
("no-filename",po::value<std::string>()->value_name(translate("WHICH")),
|
||||||
translate("Suppress the mentioning of EPUB file names on output.")
|
translate("Suppress the mentioning of file names on output. "
|
||||||
.str().data())
|
"WHICH is ‘filesystem’, ‘in-epub’ or ‘all’.").str().data())
|
||||||
("recursive,r",
|
("recursive,r",
|
||||||
translate("Read all files under each directory, recursively.")
|
translate("Read all files under each directory, recursively.")
|
||||||
.str().data())
|
.str().data())
|
||||||
|
@ -139,7 +141,7 @@ po::variables_map parse_options(int argc, char *argv[])
|
||||||
"conditions.\n");
|
"conditions.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return vm;
|
return parse_again(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::path get_config_path()
|
fs::path get_config_path()
|
||||||
|
@ -172,4 +174,62 @@ fs::path get_config_path()
|
||||||
return "epubgrep.conf";
|
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<std::string>() == "filesystem")
|
||||||
|
{
|
||||||
|
opts.no_fn_fs = true;
|
||||||
|
}
|
||||||
|
else if (vm["no-filename"].as<std::string>() == "in-epub")
|
||||||
|
{
|
||||||
|
opts.no_fn_epub = true;
|
||||||
|
}
|
||||||
|
else if (vm["no-filename"].as<std::string>() == "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<std::vector<std::string>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.count("input-file") > 0)
|
||||||
|
{
|
||||||
|
opts.input_file = vm["input-file"].as<std::vector<std::string>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return opts;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace epubgrep::options
|
} // namespace epubgrep::options
|
||||||
|
|
|
@ -21,13 +21,43 @@
|
||||||
|
|
||||||
#include <boost/program_options/variables_map.hpp>
|
#include <boost/program_options/variables_map.hpp>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace epubgrep::options
|
namespace epubgrep::options
|
||||||
{
|
{
|
||||||
|
|
||||||
namespace po = boost::program_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<std::string> 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<std::string> input_file;
|
||||||
|
};
|
||||||
|
|
||||||
//! Parse options and return them.
|
//! 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.
|
* @brief Returns the path of the config file.
|
||||||
|
@ -38,6 +68,9 @@ namespace po = boost::program_options;
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] fs::path get_config_path();
|
[[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
|
} // namespace epubgrep::options
|
||||||
|
|
||||||
#endif // EPUBGREP_OPTIONS_HPP
|
#endif // EPUBGREP_OPTIONS_HPP
|
||||||
|
|
|
@ -32,23 +32,23 @@ namespace epubgrep::search
|
||||||
using std::string;
|
using std::string;
|
||||||
|
|
||||||
std::vector<match> search(const fs::path &filepath, std::string_view regex,
|
std::vector<match> search(const fs::path &filepath, std::string_view regex,
|
||||||
const options &opts)
|
const settings &opts)
|
||||||
{
|
{
|
||||||
boost::regex::flag_type flags{};
|
boost::regex::flag_type flags{};
|
||||||
|
|
||||||
switch (opts.regex)
|
switch (opts.regex)
|
||||||
{
|
{
|
||||||
case regex_kind::basic:
|
case options::regex_kind::basic:
|
||||||
{
|
{
|
||||||
flags = opts.grep_like ? boost::regex::grep : boost::regex::basic;
|
flags = opts.grep_like ? boost::regex::grep : boost::regex::basic;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case regex_kind::extended:
|
case options::regex_kind::extended:
|
||||||
{
|
{
|
||||||
flags = opts.grep_like ? boost::regex::egrep : boost::regex::extended;
|
flags = opts.grep_like ? boost::regex::egrep : boost::regex::extended;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case regex_kind::perl:
|
case options::regex_kind::perl:
|
||||||
{
|
{
|
||||||
flags = boost::regex::perl;
|
flags = boost::regex::perl;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#define EPUBGREP_SEARCH_HPP
|
#define EPUBGREP_SEARCH_HPP
|
||||||
|
|
||||||
#include "fs-compat.hpp"
|
#include "fs-compat.hpp"
|
||||||
|
#include "options.hpp"
|
||||||
|
|
||||||
#include <boost/regex.hpp>
|
#include <boost/regex.hpp>
|
||||||
|
|
||||||
|
@ -42,16 +43,9 @@ struct match
|
||||||
std::string page; //!< The page number, if available.
|
std::string page; //!< The page number, if available.
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class regex_kind
|
struct settings
|
||||||
{
|
{
|
||||||
basic,
|
options::regex_kind regex{options::regex_kind::basic};
|
||||||
extended,
|
|
||||||
perl
|
|
||||||
};
|
|
||||||
|
|
||||||
struct options
|
|
||||||
{
|
|
||||||
regex_kind regex{regex_kind::basic};
|
|
||||||
bool grep_like{false};
|
bool grep_like{false};
|
||||||
bool ignore_case{false};
|
bool ignore_case{false};
|
||||||
bool raw{false};
|
bool raw{false};
|
||||||
|
@ -61,7 +55,7 @@ struct options
|
||||||
//! Search file, return matches.
|
//! Search file, return matches.
|
||||||
[[nodiscard]] std::vector<match> search(const fs::path &filepath,
|
[[nodiscard]] std::vector<match> search(const fs::path &filepath,
|
||||||
std::string_view regex,
|
std::string_view regex,
|
||||||
const options &opts);
|
const settings &opts);
|
||||||
|
|
||||||
//! Strip HTML, remove newlines, condense spaces.
|
//! Strip HTML, remove newlines, condense spaces.
|
||||||
void cleanup_text(std::string &text);
|
void cleanup_text(std::string &text);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "fs-compat.hpp"
|
#include "fs-compat.hpp"
|
||||||
|
#include "options.hpp"
|
||||||
#include "search.hpp"
|
#include "search.hpp"
|
||||||
|
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
@ -21,13 +22,13 @@ SCENARIO("Searching works")
|
||||||
SECTION("search() doesn't fail and returns the right lines")
|
SECTION("search() doesn't fail and returns the right lines")
|
||||||
{
|
{
|
||||||
std::vector<epubgrep::search::match> matches;
|
std::vector<epubgrep::search::match> matches;
|
||||||
epubgrep::search::options opts;
|
epubgrep::search::settings opts;
|
||||||
|
|
||||||
WHEN("We search for ‘📙+\\w?’ using extended regular expressions")
|
WHEN("We search for ‘📙+\\w?’ using extended regular expressions")
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
opts.regex = epubgrep::search::regex_kind::extended;
|
opts.regex = epubgrep::options::regex_kind::extended;
|
||||||
matches = epubgrep::search::search(zipfile, "📙+\\w?", opts);
|
matches = epubgrep::search::search(zipfile, "📙+\\w?", opts);
|
||||||
}
|
}
|
||||||
catch (const std::exception &)
|
catch (const std::exception &)
|
||||||
|
@ -104,7 +105,7 @@ SCENARIO("Searching works")
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
opts.context = 1;
|
opts.context = 1;
|
||||||
opts.regex = epubgrep::search::regex_kind::extended;
|
opts.regex = epubgrep::options::regex_kind::extended;
|
||||||
matches = epubgrep::search::search(
|
matches = epubgrep::search::search(
|
||||||
zipfile, R"(work\s[\w]+\.\W[\w']+\Wstay)", opts);
|
zipfile, R"(work\s[\w]+\.\W[\w']+\Wstay)", opts);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue