Merge branch 'develop' into main

This commit is contained in:
tastytea 2019-07-25 03:44:44 +02:00
commit 7155d9ca4f
Signed by: tastytea
GPG Key ID: CFC39497F1B26E07
6 changed files with 77 additions and 21 deletions

View File

@ -2,7 +2,7 @@
:doctype: manpage
:Author: tastytea
:Email: tastytea@tastytea.de
:Date: 2019-07-21
:Date: 2019-07-25
:Revision: 0.0.0
:man source: remwharead
:man manual: General Commands Manual
@ -15,7 +15,7 @@ remwharead - Saves URIs of things you want to remember in a database
*remwharead* [*-t* _tags_] [*-N*] _URI_
*remwharead* *-e* _format_ [*-f* _file_] [*-T* _start_,_end_] [[*-s*|*-S*] _expression_]
*remwharead* *-e* _format_ [*-f* _file_] [*-T* _start_,_end_] [[*-s*|*-S*] _expression_] [*-r*]
== DESCRIPTION
@ -55,6 +55,10 @@ insensitive.
Search in tags, title, description and full text. See _SEARCH EXPRESSIONS_. Case
insensitive.
*-r*, *--regex*::
Use regular expressions for search, case insensitive. With *--search-tags*,
every tag is enclosed by _^_ and _$_.
*-N*, *--no-archive*::
Do not archive URI.
@ -86,6 +90,11 @@ Print version, copyright and license.
`remwharead -e csv -s "grub AND boot"`
====
.Output all articles by Jan Müller, consider different spellings.
====
`remwharead -e simple -S 'Jan[[:space:]]+M(ü|ue)ller' -r`
====
=== Display database
*remwharead* does not provide an interface to display the database. However, you

View File

@ -88,11 +88,11 @@ int main(const int argc, const char *argv[])
db.retrieve(opts.span[0], opts.span[1]);
if (!opts.search_tags.empty())
{
entries = search_tags(entries, opts.search_tags);
entries = search_tags(entries, opts.search_tags, opts.regex);
}
else if (!opts.search_all.empty())
{
entries = search_all(entries, opts.search_all);
entries = search_all(entries, opts.search_all, opts.regex);
}
switch (opts.format)

View File

@ -58,6 +58,8 @@ const options parse_options(const int argc, const char *argv[])
("S", "search-all",
"Search in tags, title, description and full text.",
"", &opts.search_all);
op.add<popl::Switch>
("r", "regex", "Use regular expression for search.", &opts.regex);
auto option_noarchive = op.add<popl::Switch>
("N", "no-archive", "Do not archive URI.");
auto option_help = op.add<popl::Switch>
@ -71,7 +73,7 @@ const options parse_options(const int argc, const char *argv[])
cout << "Usage: " << argv[0] << " [-t tags] [-N] URI\n"
<< " " << argv[0]
<< " -e format [-f file] [-T start,end] "
<< "[[-s|-S] expression]\n";
<< "[[-s|-S] expression] [-r]\n";
cout << op;
return options(0);
}

View File

@ -41,6 +41,7 @@ typedef struct options
string uri;
string search_tags;
string search_all;
bool regex = false;
bool archive = true;
uint8_t status_code = 0;

View File

@ -21,6 +21,7 @@
#include "search.hpp"
using std::regex;
using std::regex_constants::icase;
using std::regex_search;
using std::smatch;
using std::find;
@ -71,7 +72,8 @@ const string to_lowercase(const string &str)
}
const vector<Database::entry>
search_tags(const vector<Database::entry> &entries, string expression)
search_tags(const vector<Database::entry> &entries, string expression,
const bool is_re)
{
vector<vector<string>> searchlist = parse_expression(expression);
vector<Database::entry> result;
@ -81,11 +83,23 @@ search_tags(const vector<Database::entry> &entries, string expression)
for (const Database::entry &entry : entries)
{ // Add entry to result if all tags in an OR-slice match.
bool matched = true;
for (const string &tag : tags_or)
{
const auto it = find_if(entry.tags.begin(), entry.tags.end(),
[&tag](const string &s)
{ return to_lowercase(s) == tag; });
[&tag, is_re](const string &s)
{
if (is_re)
{
const regex re("^" + tag + "$",
icase);
return regex_search(s, re);
}
else
{
return to_lowercase(s) == tag;
}
});
if (it == entry.tags.end())
{
matched = false;
@ -102,10 +116,11 @@ search_tags(const vector<Database::entry> &entries, string expression)
}
const vector<Database::entry>
search_all(const vector<Database::entry> &entries, string expression)
search_all(const vector<Database::entry> &entries, string expression,
const bool is_re)
{
vector<vector<string>> searchlist = parse_expression(expression);
vector<Database::entry> result = search_tags(entries, expression);
vector<Database::entry> result = search_tags(entries, expression, is_re);
for (const vector<string> &terms_or : searchlist)
{
@ -125,19 +140,46 @@ search_all(const vector<Database::entry> &entries, string expression)
for (const string &term : terms_or)
{
if (to_lowercase(entry.title).find(term) == string::npos)
{
matched_title = false;
}
const string title = to_lowercase(entry.title);
const string description = to_lowercase(entry.description);
const string fulltext = to_lowercase(entry.fulltext);
if (to_lowercase(entry.description).find(term) == string::npos)
// Set matched_* to false if term is not found.
if (is_re)
{
matched_description = false;
}
const regex re(term, icase);
if (to_lowercase(entry.fulltext).find(term) == string::npos)
if(!regex_search(title, re))
{
matched_title = false;
}
if(!regex_search(description, re))
{
matched_description = false;
}
if(!regex_search(fulltext, re))
{
matched_fulltext = false;
}
}
else
{
matched_fulltext = false;
if (title.find(term) == string::npos)
{
matched_title = false;
}
if (description.find(term) == string::npos)
{
matched_description = false;
}
if (fulltext.find(term) == string::npos)
{
matched_fulltext = false;
}
}
}
if (matched_title == true

View File

@ -29,10 +29,12 @@ const string to_lowercase(const string &str);
//! Seach database entries for tags.
const vector<Database::entry>
search_tags(const vector<Database::entry> &entries, string expression);
search_tags(const vector<Database::entry> &entries, string expression,
const bool is_re);
//! Search tags, title, description and full text.
const vector<Database::entry>
search_all(const vector<Database::entry> &entries, string expression);
search_all(const vector<Database::entry> &entries, string expression,
const bool is_re);
#endif // REMWHAREAD_SEARCH_HPP