244 lines
7.7 KiB
C++
244 lines
7.7 KiB
C++
/* This file is part of epubgrep.
|
||
* Copyright © 2021 tastytea <tastytea@tastytea.de>
|
||
*
|
||
* This program is free software: you can redistribute it and/or modify
|
||
* it under the terms of the GNU Affero General Public License as published by
|
||
* the Free Software Foundation, version 3.
|
||
*
|
||
* This program is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU Affero General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU Affero General Public License
|
||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
#include "options.hpp"
|
||
|
||
#include "fs-compat.hpp"
|
||
#include "version.hpp"
|
||
|
||
#include <boost/locale/message.hpp>
|
||
#include <boost/program_options/options_description.hpp>
|
||
#include <boost/program_options/parsers.hpp>
|
||
#include <boost/program_options/positional_options.hpp>
|
||
#include <boost/program_options/value_semantic.hpp>
|
||
#include <boost/program_options/variables_map.hpp>
|
||
|
||
#include <cstdint>
|
||
#include <cstdlib>
|
||
#include <exception>
|
||
#include <fstream>
|
||
#include <iostream>
|
||
#include <stdexcept>
|
||
#include <string>
|
||
#include <vector>
|
||
|
||
namespace epubgrep::options
|
||
{
|
||
|
||
namespace po = boost::program_options;
|
||
|
||
using boost::locale::translate;
|
||
using std::cout;
|
||
|
||
options parse_options(int argc, char *argv[])
|
||
{
|
||
po::options_description options_visible(translate("Available options"));
|
||
// clang-format off
|
||
options_visible.add_options()
|
||
("help,h",
|
||
translate("Display this help and exit.").str().data())
|
||
("version,V",
|
||
translate("Display version information and exit.").str().data())
|
||
("basic-regexp,G",
|
||
translate("PATTERN is a basic regular expression (default).")
|
||
.str().data())
|
||
("extended-regexp,E",
|
||
translate("PATTERN is an extended regular expression.").str().data())
|
||
("grep",
|
||
translate("Use grep-variation of regular expressions with -G and -E.")
|
||
.str().data())
|
||
("perl-regexp,P",
|
||
translate("PATTERN is a Perl regular expression.").str().data())
|
||
("ignore-case,i",
|
||
translate("Ignore case distinctions in pattern and data.")
|
||
.str().data())
|
||
("regexp,e", po::value<std::vector<std::string>>()
|
||
->value_name(translate("PATTERN"))->composing()->required(),
|
||
translate("Use additional PATTERN for matching.").str().data())
|
||
("raw,a",
|
||
translate("Do not clean up text before searching.").str().data())
|
||
("context,C", po::value<std::uint64_t>()
|
||
->value_name(translate("NUMBER"))->default_value(0),
|
||
translate("Print NUMBER words of context around matches.")
|
||
.str().data())
|
||
("nocolor", translate("Do not color matches.") .str().data())
|
||
("no-filename",po::value<std::string>()->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())
|
||
("dereference-recursive,R",
|
||
translate("Read all files under each directory, recursively, "
|
||
"following symlinks.") .str().data())
|
||
("ignore-archive-errors",
|
||
translate("Ignore errors about wrong file formats.") .str().data())
|
||
;
|
||
|
||
po::options_description options_hidden("Hidden options");
|
||
options_hidden.add_options()
|
||
("input-file", po::value<std::vector<std::string>>()
|
||
->value_name("FILE"), "Input file to search.")
|
||
;
|
||
// clang-format on
|
||
po::options_description options_all("Allowed options");
|
||
options_all.add(options_visible).add(options_hidden);
|
||
|
||
po::positional_options_description positional_desc;
|
||
positional_desc.add("regexp", 1).add("input-file", -1);
|
||
|
||
po::variables_map vm;
|
||
po::store(po::command_line_parser(argc, argv)
|
||
.options(options_all)
|
||
.positional(positional_desc)
|
||
.run(),
|
||
vm);
|
||
|
||
std::ifstream configfile(get_config_path());
|
||
po::store(po::parse_config_file(configfile, options_visible, true), vm);
|
||
configfile.close();
|
||
|
||
try
|
||
{
|
||
po::notify(vm);
|
||
}
|
||
catch (const boost::program_options::required_option &e)
|
||
{
|
||
if (vm.count("help") + vm.count("version") == 0)
|
||
{
|
||
cout << options_visible;
|
||
std::rethrow_exception(std::current_exception());
|
||
}
|
||
}
|
||
|
||
if (vm.count("help") != 0)
|
||
{
|
||
cout << translate("Usage: epubgrep [OPTION]… PATTERN [FILE]…\n");
|
||
cout << options_visible;
|
||
cout << translate("\nYou can access the full manual "
|
||
"with `man epubgrep`.\n");
|
||
}
|
||
else if (vm.count("version") != 0)
|
||
{
|
||
cout << "epubgrep " << epubgrep::version << '\n';
|
||
cout << translate(
|
||
"Copyright © 2021 tastytea <tastytea@tastytea.de>\n"
|
||
"License AGPL-3.0-only <https://gnu.org/licenses/agpl.html>.\n"
|
||
"This program comes with ABSOLUTELY NO WARRANTY. This is "
|
||
"free software,\n"
|
||
"and you are welcome to redistribute it under certain "
|
||
"conditions.\n");
|
||
}
|
||
|
||
return parse_again(vm);
|
||
}
|
||
|
||
fs::path get_config_path()
|
||
{
|
||
const auto get_env{[](const std::string &name)
|
||
{
|
||
const char *env = std::getenv(name.c_str());
|
||
if (env != nullptr)
|
||
{
|
||
return env;
|
||
}
|
||
|
||
return "";
|
||
}};
|
||
|
||
fs::path path{get_env("XDG_CONFIG_HOME")};
|
||
if (path.empty())
|
||
{
|
||
path = get_env("HOME");
|
||
if (!path.empty())
|
||
{
|
||
path /= ".config";
|
||
}
|
||
}
|
||
if (!path.empty())
|
||
{
|
||
return path /= "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.raw = vm.count("raw") > 0;
|
||
if (vm.count("context") > 0)
|
||
{
|
||
opts.context = vm["context"].as<std::uint64_t>();
|
||
}
|
||
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;
|
||
opts.ignore_archive_errors = vm.count("ignore-archive-errors") > 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
|