epubgrep/src/options.cpp

315 lines
10 KiB
C++
Raw Normal View History

/* 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"
2021-05-31 18:50:41 +02:00
#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.
2021-05-24 07:50:50 +02:00
#include <cstdint>
2021-05-20 09:05:39 +02:00
#include <cstdlib>
#include <exception>
2021-05-20 09:05:39 +02:00
#include <fstream>
#include <iostream>
#include <stdexcept>
2021-05-20 09:05:39 +02:00
#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()
2021-05-20 11:51:08 +02:00
("help,h",
translate("Display this help and exit.").str().data())
2021-05-20 11:51:08 +02:00
("version,V",
translate("Display version information and exit.").str().data())
("basic-regexp,G",
translate("PATTERN is a basic regular expression (default).")
.str().data())
2021-05-20 11:51:08 +02:00
("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())
2021-05-20 11:51:08 +02:00
("perl-regexp,P",
translate("PATTERN is a Perl regular expression.").str().data())
2021-05-20 11:51:08 +02:00
("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())
2021-05-24 07:50:50 +02:00
("raw,a",
2021-05-24 16:01:41 +02:00
translate("Do not clean up text before searching.").str().data())
2021-05-24 08:14:29 +02:00
("context,C", po::value<std::uint64_t>()
2021-05-24 08:23:21 +02:00
->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;
2021-05-20 11:43:35 +02:00
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);
2021-05-20 09:05:39 +02:00
std::ifstream configfile(get_config_path());
po::store(po::parse_config_file(configfile, options_visible, true), vm);
2021-05-20 09:05:39 +02:00
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;
2021-05-24 06:03:32 +02:00
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);
}
2021-05-20 10:20:19 +02:00
fs::path get_config_path()
2021-05-20 09:05:39 +02:00
{
2021-05-31 18:50:41 +02:00
fs::path path{helpers::get_env("XDG_CONFIG_HOME")};
2021-05-20 09:05:39 +02:00
if (path.empty())
{
2021-05-31 18:50:41 +02:00
path = helpers::get_env("HOME");
2021-05-20 09:05:39 +02:00
if (!path.empty())
{
2021-05-20 10:20:19 +02:00
path /= ".config";
2021-05-20 09:05:39 +02:00
}
}
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;
2021-05-20 09:05:39 +02:00
}
2021-05-20 10:20:19 +02:00
return "epubgrep.conf";
2021-05-20 09:05:39 +02:00
}
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