epubgrep/src/options.cpp

315 lines
10 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 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 "helpers.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 <fmt/format.h>
#include <fmt/ostream.h> // For compatibility with fmt 4.
#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 fmt::format;
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("Turn off colors and other decorations.")
.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())
("debug",
translate("Enable debug output.") .str().data())
("json",
translate("Output JSON instead of plain text.") .str().data())
;
po::options_description options_hidden("Hidden options");
options_hidden.add_options()
("input-file", po::value<std::vector<std::string>>()->required()
->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()
{
fs::path path{helpers::get_env("XDG_CONFIG_HOME")};
if (path.empty())
{
path = helpers::get_env("HOME");
if (!path.empty())
{
path /= ".config";
}
}
if (!path.empty())
{
const auto old_path{path / "epubgrep.conf"};
auto new_path{path / "epubgrep" / "epubgrep.conf"};
if (fs::exists(old_path))
{
fs::create_directory(path /= "epubgrep");
fs::rename(old_path, new_path);
}
return new_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.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;
opts.debug = vm.count("debug") > 0;
opts.json = vm.count("json") > 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;
}
std::ostream &operator<<(std::ostream &out, const options &opts)
{
const std::string regex_kind{[&opts]
{
switch (opts.regex)
{
case regex_kind::basic:
{
return "basic";
break;
}
case regex_kind::extended:
{
return "extended";
break;
}
case regex_kind::perl:
{
return "perl";
break;
}
}
return "error";
}()};
out << format("help={0:} version={1:} regex={2:s} grep={3:} "
"ignore_case={4:} ",
opts.help, opts.version, regex_kind, opts.grep,
opts.ignore_case);
out << "regexp={";
for (const auto &regexp : opts.regexp)
{
if (regexp != *opts.regexp.begin())
{
out << ", ";
}
out << '"' << regexp << '"';
}
out << "} ";
out << format("raw={0:} context={1:d} nocolor={2:} no_fn_fs={3:} "
"no_fn_epub={4:} recursive={5:} dereference_recursive={6:} ",
opts.raw, opts.context, opts.nocolor, opts.no_fn_fs,
opts.no_fn_epub, opts.recursive, opts.dereference_recursive);
out << "input_file={";
for (const auto &input_file : opts.input_file)
{
if (input_file != *opts.input_file.begin())
{
out << ", ";
}
out << '"' << input_file << '"';
}
out << "} ";
out << format("ignore_archive={0:} debug={1:}", opts.ignore_archive_errors,
opts.debug);
return out;
}
} // namespace epubgrep::options