/* This file is part of epubgrep. * Copyright © 2021 tastytea * * 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 . */ #include "options.hpp" #include "fs-compat.hpp" #include "helpers.hpp" #include "version.hpp" #include #include #include #include #include #include #include #include // For compatibility with fmt 4. #include #include #include #include #include #include #include #include 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>() ->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() ->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()->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>()->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 \n" "License AGPL-3.0-only .\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(); } 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; 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>(); } if (vm.count("input-file") > 0) { opts.input_file = vm["input-file"].as>(); } 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 ®exp : 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