2019-05-12 00:02:31 +02:00
|
|
|
/* This file is part of remwharead.
|
|
|
|
* Copyright © 2019 tastytea <tastytea@tastytea.de>
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU 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 General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2019-08-06 17:14:32 +02:00
|
|
|
#include "remwharead_cli.hpp"
|
2019-11-28 00:49:28 +01:00
|
|
|
#include "sqlite.hpp"
|
2019-09-30 13:20:36 +02:00
|
|
|
#include "time.hpp"
|
|
|
|
#include "version.hpp"
|
|
|
|
#include <Poco/Util/HelpFormatter.h>
|
|
|
|
#include <Poco/Util/Option.h>
|
|
|
|
#include <iostream>
|
2019-10-10 14:36:59 +02:00
|
|
|
#include <memory>
|
2019-05-12 00:02:31 +02:00
|
|
|
|
2019-09-25 03:58:29 +02:00
|
|
|
using namespace remwharead_cli;
|
2019-05-12 00:02:31 +02:00
|
|
|
using std::cout;
|
|
|
|
using std::cerr;
|
|
|
|
using std::endl;
|
2019-08-06 17:14:32 +02:00
|
|
|
using Poco::Util::Option;
|
|
|
|
using Poco::Util::OptionCallback;
|
|
|
|
using Poco::Util::HelpFormatter;
|
2019-05-12 20:19:00 +02:00
|
|
|
|
2019-08-06 17:14:32 +02:00
|
|
|
App::App()
|
2020-01-27 06:32:28 +01:00
|
|
|
: _exit_requested{false}
|
|
|
|
, _argument_error{false}
|
|
|
|
, _format{export_format::undefined}
|
|
|
|
, _timespan{{time_point(), system_clock::now()}}
|
|
|
|
, _archive{true}
|
|
|
|
, _regex{false}
|
2019-05-12 20:19:00 +02:00
|
|
|
{}
|
|
|
|
|
2019-08-06 17:14:32 +02:00
|
|
|
void App::defineOptions(OptionSet& options)
|
2019-05-12 00:02:31 +02:00
|
|
|
{
|
2019-08-06 17:14:32 +02:00
|
|
|
options.addOption(
|
|
|
|
Option("help", "h", "Show this help message.")
|
2019-10-10 14:36:59 +02:00
|
|
|
.argument("option", false)
|
|
|
|
.callback(OptionCallback<App>(this, &App::handle_options)));
|
2019-08-06 17:14:32 +02:00
|
|
|
options.addOption(
|
|
|
|
Option("version", "V", "Print version, copyright and license.")
|
2019-10-10 14:36:59 +02:00
|
|
|
.callback(OptionCallback<App>(this, &App::handle_options)));
|
2019-08-06 17:14:32 +02:00
|
|
|
options.addOption(
|
|
|
|
Option("tags", "t", "Add tags to URI, delimited by commas.")
|
2019-08-06 19:02:22 +02:00
|
|
|
.argument("tags")
|
2019-08-06 17:14:32 +02:00
|
|
|
.callback(OptionCallback<App>(this, &App::handle_options)));
|
|
|
|
options.addOption(
|
2019-09-23 03:16:58 +02:00
|
|
|
Option("export", "e", "Export to format. See manpage for a list.")
|
2019-08-06 19:02:22 +02:00
|
|
|
.argument("format")
|
2019-08-06 17:14:32 +02:00
|
|
|
.callback(OptionCallback<App>(this, &App::handle_options)));
|
|
|
|
options.addOption(
|
|
|
|
Option("file", "f", "Save output to file.")
|
2019-08-06 19:02:22 +02:00
|
|
|
.argument("file")
|
2019-08-06 17:14:32 +02:00
|
|
|
.callback(OptionCallback<App>(this, &App::handle_options)));
|
|
|
|
options.addOption(
|
|
|
|
Option("time-span", "T",
|
|
|
|
"Only export entries between YYYY-MM-DD,YYYY-MM-DD.")
|
2019-08-06 19:02:22 +02:00
|
|
|
.argument("times")
|
2019-08-06 17:14:32 +02:00
|
|
|
.callback(OptionCallback<App>(this, &App::handle_options)));
|
|
|
|
options.addOption(
|
|
|
|
Option("search-tags", "s",
|
|
|
|
"Search in tags. Format: tag1 AND tag2 OR tag3.")
|
2019-08-06 19:02:22 +02:00
|
|
|
.argument("expression")
|
2019-08-06 17:14:32 +02:00
|
|
|
.callback(OptionCallback<App>(this, &App::handle_options)));
|
|
|
|
options.addOption(
|
|
|
|
Option("search-all", "S",
|
|
|
|
"Search in tags, title, description and full text.")
|
2019-08-06 19:02:22 +02:00
|
|
|
.argument("expression")
|
2019-08-06 17:14:32 +02:00
|
|
|
.callback(OptionCallback<App>(this, &App::handle_options)));
|
|
|
|
options.addOption(
|
2019-08-08 10:13:49 +02:00
|
|
|
Option("regex", "r", "Use regular expression for search.")
|
|
|
|
.callback(OptionCallback<App>(this, &App::handle_options)));
|
2019-08-06 17:14:32 +02:00
|
|
|
options.addOption(
|
|
|
|
Option("no-archive", "N", "Do not archive URI.")
|
|
|
|
.callback(OptionCallback<App>(this, &App::handle_options)));
|
2019-11-28 00:49:28 +01:00
|
|
|
options.addOption(
|
|
|
|
Option("delete", "d",
|
|
|
|
"Remove all entries with this URI from database.")
|
|
|
|
.argument("URI")
|
|
|
|
.callback(OptionCallback<App>(this, &App::handle_options)));
|
2019-08-06 17:14:32 +02:00
|
|
|
}
|
2019-05-12 00:02:31 +02:00
|
|
|
|
2019-10-10 14:36:59 +02:00
|
|
|
void App::handle_options(const std::string &name, const std::string &value)
|
2019-08-06 17:14:32 +02:00
|
|
|
{
|
|
|
|
if (name == "help")
|
2019-05-12 00:02:31 +02:00
|
|
|
{
|
2019-10-10 14:36:59 +02:00
|
|
|
_exit_requested = true;
|
|
|
|
print_help(value);
|
|
|
|
stopOptionsProcessing();
|
2019-08-06 17:14:32 +02:00
|
|
|
}
|
|
|
|
else if (name == "version")
|
|
|
|
{
|
2019-10-10 14:36:59 +02:00
|
|
|
_exit_requested = true;
|
|
|
|
print_version();
|
|
|
|
stopOptionsProcessing();
|
2019-08-06 17:14:32 +02:00
|
|
|
}
|
2019-10-10 14:36:59 +02:00
|
|
|
else if (name == "tags")
|
2019-08-06 17:14:32 +02:00
|
|
|
{
|
|
|
|
size_t pos_end = 0;
|
|
|
|
size_t pos_start = 0;
|
|
|
|
while (pos_end != std::string::npos)
|
2019-05-12 00:02:31 +02:00
|
|
|
{
|
2019-08-06 17:14:32 +02:00
|
|
|
pos_end = value.find(',', pos_start);
|
|
|
|
string buffer = value.substr(pos_start, pos_end - pos_start);
|
|
|
|
while (*buffer.begin() == ' ') // Remove leading spaces.
|
|
|
|
{
|
|
|
|
buffer.erase(buffer.begin());
|
|
|
|
}
|
|
|
|
while (*buffer.rbegin() == ' ') // Remove trailing spaces.
|
|
|
|
{
|
|
|
|
buffer.erase(buffer.end() - 1);
|
|
|
|
}
|
2019-08-07 19:41:58 +02:00
|
|
|
if (!buffer.empty())
|
|
|
|
{
|
|
|
|
_tags.push_back(buffer);
|
|
|
|
}
|
2019-08-06 17:14:32 +02:00
|
|
|
pos_start = pos_end + 1;
|
2019-05-12 00:02:31 +02:00
|
|
|
}
|
2019-08-06 17:14:32 +02:00
|
|
|
}
|
|
|
|
else if (name == "export")
|
|
|
|
{
|
|
|
|
if (value == "csv")
|
2019-05-12 00:02:31 +02:00
|
|
|
{
|
2019-08-06 17:14:32 +02:00
|
|
|
_format = export_format::csv;
|
2019-05-12 00:02:31 +02:00
|
|
|
}
|
2019-08-06 17:14:32 +02:00
|
|
|
else if (value == "asciidoc" || value == "adoc")
|
2019-05-22 09:43:58 +02:00
|
|
|
{
|
2019-08-06 17:14:32 +02:00
|
|
|
_format = export_format::asciidoc;
|
2019-05-22 09:43:58 +02:00
|
|
|
}
|
2019-08-06 17:14:32 +02:00
|
|
|
else if (value == "bookmarks")
|
2019-05-12 00:02:31 +02:00
|
|
|
{
|
2019-08-06 17:14:32 +02:00
|
|
|
_format = export_format::bookmarks;
|
2019-05-12 00:02:31 +02:00
|
|
|
}
|
2019-08-06 17:14:32 +02:00
|
|
|
else if (value == "simple")
|
2019-05-12 00:02:31 +02:00
|
|
|
{
|
2019-08-06 17:14:32 +02:00
|
|
|
_format = export_format::simple;
|
2019-05-12 00:02:31 +02:00
|
|
|
}
|
2019-09-03 17:47:39 +02:00
|
|
|
else if (value == "json")
|
|
|
|
{
|
|
|
|
_format = export_format::json;
|
|
|
|
}
|
2019-09-06 00:53:58 +02:00
|
|
|
else if (value == "rss")
|
|
|
|
{
|
|
|
|
_format = export_format::rss;
|
|
|
|
}
|
2019-11-27 21:53:48 +01:00
|
|
|
else if (value == "link")
|
|
|
|
{
|
|
|
|
_format = export_format::link;
|
|
|
|
}
|
2019-11-28 09:03:52 +01:00
|
|
|
else if (value == "rofi")
|
|
|
|
{
|
|
|
|
_format = export_format::rofi;
|
|
|
|
}
|
2019-08-06 17:14:32 +02:00
|
|
|
else
|
2019-05-12 00:02:31 +02:00
|
|
|
{
|
2019-08-06 17:14:32 +02:00
|
|
|
cerr << "Error: Unknown format.\n";
|
|
|
|
_argument_error = true;
|
2019-05-12 00:02:31 +02:00
|
|
|
}
|
2019-08-06 17:14:32 +02:00
|
|
|
}
|
|
|
|
else if (name == "file")
|
|
|
|
{
|
|
|
|
_file = value;
|
|
|
|
}
|
|
|
|
else if (name == "time-span")
|
|
|
|
{
|
|
|
|
size_t pos = value.find(',');
|
|
|
|
if (pos != std::string::npos)
|
2019-05-13 02:33:43 +02:00
|
|
|
{
|
2019-08-06 17:14:32 +02:00
|
|
|
_timespan =
|
|
|
|
{
|
|
|
|
string_to_timepoint(value.substr(0, pos)),
|
|
|
|
string_to_timepoint(value.substr(pos + 1))
|
|
|
|
};
|
2019-05-13 02:33:43 +02:00
|
|
|
}
|
2019-08-06 17:14:32 +02:00
|
|
|
else
|
2019-05-13 02:33:43 +02:00
|
|
|
{
|
2019-08-06 17:14:32 +02:00
|
|
|
cerr << "Error: Time span must be in format: "
|
|
|
|
"YYYY-MM-DD,YYYY-MM-DD.\n";
|
|
|
|
_argument_error = true;
|
2019-05-13 02:33:43 +02:00
|
|
|
}
|
2019-05-12 00:02:31 +02:00
|
|
|
}
|
2019-08-06 17:14:32 +02:00
|
|
|
else if (name == "search-tags")
|
|
|
|
{
|
|
|
|
_search_tags = value;
|
|
|
|
}
|
|
|
|
else if (name == "search-all")
|
|
|
|
{
|
|
|
|
_search_all = value;
|
|
|
|
}
|
|
|
|
else if (name == "no-archive")
|
2019-05-12 00:02:31 +02:00
|
|
|
{
|
2019-08-06 17:14:32 +02:00
|
|
|
_archive = false;
|
2019-05-12 00:02:31 +02:00
|
|
|
}
|
2019-08-06 17:14:32 +02:00
|
|
|
else if (name == "regex")
|
|
|
|
{
|
|
|
|
_regex = true;
|
|
|
|
}
|
2019-11-28 00:49:28 +01:00
|
|
|
else if (name == "delete")
|
|
|
|
{
|
|
|
|
Database db;
|
|
|
|
cout << "Deleted " << db.remove(value) << " entries.\n";
|
|
|
|
_exit_requested = true;
|
|
|
|
}
|
2019-08-06 17:14:32 +02:00
|
|
|
}
|
2019-05-12 00:02:31 +02:00
|
|
|
|
2019-10-10 14:36:59 +02:00
|
|
|
void App::print_help(const string &option)
|
2019-08-06 17:14:32 +02:00
|
|
|
{
|
2019-10-10 14:36:59 +02:00
|
|
|
std::unique_ptr<HelpFormatter> helpFormatter;
|
|
|
|
OptionSet oneoption;
|
|
|
|
|
|
|
|
if (option.empty())
|
|
|
|
{
|
|
|
|
helpFormatter = std::make_unique<HelpFormatter>(options());
|
|
|
|
helpFormatter->setCommand(commandName());
|
|
|
|
helpFormatter->setUsage("[-t tags] [-N] URI\n"
|
|
|
|
"-e format [-f file] [-T start,end] "
|
2019-11-28 00:49:28 +01:00
|
|
|
"[[-s|-S] expression] [-r]\n"
|
|
|
|
"-d URI");
|
2019-10-10 14:36:59 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
oneoption.addOption(options().getOption(option));
|
|
|
|
helpFormatter = std::make_unique<HelpFormatter>(oneoption);
|
|
|
|
}
|
|
|
|
|
|
|
|
helpFormatter->format(cout);
|
2019-08-06 17:14:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void App::print_version()
|
|
|
|
{
|
2020-01-27 04:50:33 +01:00
|
|
|
cout << "remwharead " << remwharead::version << endl <<
|
2019-08-06 17:14:32 +02:00
|
|
|
"Copyright (C) 2019 tastytea <tastytea@tastytea.de>\n"
|
|
|
|
"License GPLv3: GNU GPL version 3 "
|
|
|
|
"<https://www.gnu.org/licenses/gpl-3.0.html>.\n"
|
|
|
|
"This program comes with ABSOLUTELY NO WARRANTY. This is free software,"
|
|
|
|
"\nand you are welcome to redistribute it under certain conditions.\n";
|
2019-05-12 00:02:31 +02:00
|
|
|
}
|