34 #include <boost/algorithm/string.hpp>
37 #define ERR_GENERAL LOG_STREAM(err, lg::general())
38 #define ERR_NG LOG_STREAM(err, log_engine)
44 return c ==
'\r' ||
c ==
'\n';
52 return c ==
'\r' ||
c ==
'\n' ||
c ==
' ' ||
c ==
'\t' ||
c ==
'\v' ||
c ==
'\f';
64 s.remove_prefix(std::min(
s.find_first_not_of(
" \t\r\n"),
s.size()));
69 std::size_t first_to_trim =
s.find_last_not_of(
" \t\r\n") + 1;
70 s =
s.substr(0, first_to_trim);
82 std::vector<std::string>
split(std::string_view
s,
const char sep,
const int flags)
84 std::vector<std::string> res;
86 res.emplace_back(item);
91 std::set<std::string>
split_set(std::string_view
s,
char sep,
const int flags)
93 std::set<std::string> res;
100 std::vector<std::string_view>
split_view(std::string_view
s,
const char sep,
const int flags)
102 std::vector<std::string_view> res;
110 std::size_t get_padding(std::string_view str)
113 return (str.size() > 1 && str[0] ==
'0') ? (str.size() - 1) : 0;
118 const char separator,
const std::string& left,
119 const std::string& right,
const int flags)
121 std::vector< std::string > res;
127 ERR_GENERAL <<
"Separator must be specified for square bracket split function.";
130 if (left.size() != right.size()) {
131 ERR_GENERAL <<
"Left and Right Parenthesis lists not same length";
135 std::string lp = left;
136 std::string rp = right;
137 std::vector<char> part;
138 bool in_parenthesis =
false;
139 std::vector<std::string::const_iterator> square_left;
140 std::vector<std::string::const_iterator> square_right;
141 std::vector< std::string > square_expansion;
143 std::string::const_iterator i1 = val.begin();
144 std::string::const_iterator i2;
145 std::string::const_iterator j1;
150 if (i1 == val.end())
return res;
156 const std::string complex_markers = separator + std::string(
"[");
157 if (val.find_first_of(complex_markers) == std::string::npos)
159 std::string mutable_val(i1, val.end());
161 boost::trim_right(mutable_val);
163 return { mutable_val };
167 if(i2 == val.end() || (!in_parenthesis && *i2 == separator)) {
169 std::size_t size_square_exp = 0;
170 for (std::size_t
i=0;
i < square_left.size();
i++) {
171 std::string tmp_val(square_left[
i]+1,square_right[
i]);
172 std::vector< std::string > tmp =
split(tmp_val);
173 for(
const std::string& piece : tmp) {
174 std::size_t found_tilde = piece.find_first_of(
'~');
175 if (found_tilde == std::string::npos) {
176 std::size_t found_asterisk = piece.find_first_of(
'*');
177 if (found_asterisk == std::string::npos) {
178 std::string tmp2(piece);
180 square_expansion.push_back(tmp2);
183 std::string s_begin = piece.substr(0,found_asterisk);
185 std::string s_end = piece.substr(found_asterisk+1);
187 for (
int ast=
std::stoi(s_end); ast>0; --ast)
188 square_expansion.push_back(s_begin);
192 std::string s_begin = piece.substr(0,found_tilde);
195 std::string s_end = piece.substr(found_tilde+1);
199 std::size_t padding = std::max(get_padding(s_begin), get_padding(s_end));
200 if (padding > 0 && s_begin.size() != s_end.size()) {
201 ERR_GENERAL <<
"Square bracket padding sizes not matching: "
202 << s_begin <<
" and " << s_end <<
".";
205 int increment = (end >= begin ? 1 : -1);
207 for (
int k=begin; k!=end; k+=increment) {
208 std::string pb = std::to_string(k);
209 for (std::size_t
p=pb.size();
p<=padding;
p++)
210 pb = std::string(
"0") + pb;
211 square_expansion.push_back(pb);
215 if (
i*square_expansion.size() != (
i+1)*size_square_exp ) {
216 std::string tmp2(i1, i2);
217 ERR_GENERAL <<
"Square bracket lengths do not match up: " << tmp2;
220 size_square_exp = square_expansion.size();
225 std::size_t j_max = 0;
226 if (!square_left.empty())
227 j_max = square_expansion.size() / square_left.size();
231 for (std::size_t
i=0;
i < square_left.size();
i++) {
232 std::string tmp_val(j1, square_left[
i]);
233 new_val.append(tmp_val);
234 std::size_t k = j+
i*j_max;
235 if (k < square_expansion.size())
236 new_val.append(square_expansion[k]);
237 j1 = square_right[
i]+1;
239 std::string tmp_val(j1, i2);
240 new_val.append(tmp_val);
242 boost::trim_right(new_val);
244 res.push_back(new_val);
257 square_right.clear();
258 square_expansion.clear();
261 if(!part.empty() && *i2 == part.back()) {
263 if (*i2 ==
']') square_right.push_back(i2);
265 in_parenthesis =
false;
270 for(std::size_t
i=0;
i < lp.size();
i++) {
273 square_left.push_back(i2);
275 part.push_back(rp[
i]);
283 in_parenthesis =
true;
294 const std::string& val
298 ,
const std::string& default_value)
301 std::vector< std::string > v =
split(val, major, flags);
304 std::map< std::string, std::string > res;
307 std::size_t pos =
i->find_first_of(minor);
308 std::string key, value;
310 if(pos == std::string::npos) {
312 value = default_value;
314 key =
i->substr(0, pos);
315 value =
i->substr(pos + 1);
325 const char separator, std::string_view left,
326 std::string_view right,
const int flags)
328 std::vector< std::string > res;
329 std::vector<char> part;
330 bool in_parenthesis =
false;
332 std::string_view::const_iterator i1 = val.begin();
333 std::string_view::const_iterator i2;
340 if(left.size()!=right.size()){
341 ERR_GENERAL <<
"Left and Right Parenthesis lists not same length";
345 while (i2 != val.end()) {
346 if(!in_parenthesis && separator && *i2 == separator){
347 std::string new_val(i1, i2);
349 boost::trim_right(new_val);
351 res.push_back(new_val);
360 if(!part.empty() && *i2 == part.back()){
362 if(!separator && part.empty()){
363 std::string new_val(i1, i2);
366 res.push_back(new_val);
371 in_parenthesis =
false;
377 for(std::size_t
i=0;
i < left.size();
i++){
379 if (!separator && part.empty()){
380 std::string new_val(i1, i2);
383 res.push_back(new_val);
389 part.push_back(right[
i]);
397 in_parenthesis =
true;
400 std::string new_val(i1, i2);
404 res.push_back(std::move(new_val));
414 int apply_modifier(
const int number,
const std::string &amount,
const int minimum ) {
419 }
catch(
const std::invalid_argument&) {}
420 if(amount[amount.size()-1] ==
'%') {
424 if (( minimum > 0 ) && ( value < minimum ))
429 std::string
escape(std::string_view str,
const char *special_chars)
431 std::string::size_type pos = str.find_first_of(special_chars);
432 if (pos == std::string::npos) {
433 return std::string(str);
435 std::string res = std::string(str);
437 res.insert(pos, 1,
'\\');
438 pos = res.find_first_of(special_chars, pos + 2);
439 }
while (pos != std::string::npos);
445 std::string::size_type pos = str.find(
'\\');
446 if (pos == std::string::npos) {
447 return std::string(str);
449 std::string res = std::string(str);
452 pos = res.find(
'\\', pos + 1);
453 }
while (pos != std::string::npos);
459 static const std::string nonresv_str =
462 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
464 "abcdefghijklmnopqrstuvwxyz"
466 static const std::set<char> nonresv(nonresv_str.begin(), nonresv_str.end());
468 std::ostringstream res;
473 if(nonresv.count(
c) != 0) {
480 res << static_cast<int>(
c);
487 if (str.empty())
return def;
490 if (str ==
"yes")
return true;
491 if (str ==
"no"|| str ==
"false" || str ==
"off" || str ==
"0" || str ==
"0.0")
500 std::ostringstream ss;
501 ss << std::boolalpha << value;
508 std::ostringstream oss;
515 std::ostringstream oss;
518 oss << std::abs(val);
523 std::streamsize oldprec = ss.precision();
535 input = std::round(input);
541 if (input >= 999.5) {
548 ss.precision(oldprec);
552 const double multiplier = base2 ? 1024 : 1000;
554 typedef std::array<std::string, 9> strings9;
561 strings9::const_iterator prefix;
563 strings9 tmp { {
"",
"",
"",
"",
"",
"",
"",
"",
"" } };
565 prefix = prefixes.begin();
566 }
else if (input < 1.0) {
579 prefix = prefixes.begin();
580 while (input < 1.0 && *prefix != prefixes.back()) {
601 prefix = prefixes.begin();
602 while (input > multiplier && *prefix != prefixes.back()) {
608 std::stringstream ss;
613 << (base2 && (!(*prefix).empty()) ?
_(
"infix_binary^i") :
"")
619 return ((
c ==
'_') || (
c ==
'-'));
623 return ((
c ==
'?') || (
c ==
'*'));
627 const std::size_t alnum = std::count_if(username.begin(), username.end(), isalnum);
628 const std::size_t valid_char =
630 if ((alnum + valid_char != username.size())
631 || valid_char == username.size() || username.empty() )
639 const std::size_t alnum = std::count_if(username.begin(), username.end(), isalnum);
640 const std::size_t valid_char =
642 const std::size_t wild_char =
644 if ((alnum + valid_char + wild_char != username.size())
645 || valid_char == username.size() || username.empty() )
654 std::vector<std::string> matches;
655 const std::size_t last_space = text.rfind(
" ");
657 if (last_space == text.size() -1) {
663 std::string semiword;
664 if (last_space == std::string::npos) {
669 semiword.assign(text, last_space + 1, text.size());
672 std::string best_match = semiword;
673 for (std::vector<std::string>::const_iterator word = wordlist.begin();
674 word != wordlist.end(); ++word)
676 if (word->size() < semiword.size()
677 || !std::equal(semiword.begin(), semiword.end(), word->begin(),
678 [](
char a,
char b) { return tolower(a) == tolower(b); }))
682 if (matches.empty()) {
686 while (toupper(best_match[j]) == toupper((*word)[j])) j++;
687 if (best_match.begin() + j < best_match.end()) {
688 best_match.erase(best_match.begin() + j, best_match.end());
691 matches.push_back(*word);
693 if(!matches.empty()) {
694 text.replace(last_space + 1, best_match.size(), best_match);
701 return (
c ==
' ' ||
c ==
',' ||
c ==
':' ||
c ==
'\'' ||
c ==
'"' ||
c ==
'-');
704 bool word_match(
const std::string& message,
const std::string& word) {
705 std::size_t first = message.find(word);
706 if (first == std::string::npos)
return false;
708 std::size_t next = first + word.size();
718 auto s_first = str.cbegin();
719 auto p_first = pat.cbegin();
720 const auto s_last = str.cend();
721 const auto p_last = pat.cend();
726 auto is_star_or_plus = [](
char x) noexcept {
return x ==
'*' || x ==
'+'; };
727 auto match_char = [](
char s,
char p) noexcept {
return s ==
p ||
p ==
'?'; };
729 const auto first_wild =
std::find_if(p_first, p_last, is_star_or_plus);
731 if(first_wild == p_last) {
732 return std::equal(s_first, s_last, p_first, p_last, match_char);
735 const auto wild_cat =
static_cast<int>(*first_wild ==
'+');
737 if(first_wild != p_first) {
738 const auto n_chars = std::distance(p_first, first_wild);
740 if(std::distance(s_first, s_last) < (n_chars + wild_cat)) {
744 std::tie(s_first, p_first) = std::mismatch(s_first, s_first + n_chars, p_first, match_char);
746 if(p_first != first_wild) {
751 if(s_first == s_last) {
752 return std::all_of(p_first, p_last, [](
char c) {
return c ==
'*'; });
761 const auto next_wild =
std::find_if(p_first, p_last, is_star_or_plus);
763 if(next_wild == p_last) {
764 return boost::ends_with(std::pair{s_first, s_last}, std::pair{p_first, p_last}, match_char);
767 if(next_wild != p_first) {
768 auto [sub_f, sub_l] = std::default_searcher{p_first, next_wild, match_char}(s_first, s_last);
770 if(sub_f == s_last) {
778 auto is_wildcard = [](
char x) noexcept {
return x ==
'*' || x ==
'+' || x ==
'?'; };
779 const auto next_non_wild = std::find_if_not(p_first, p_last, is_wildcard);
780 const auto required_chars = std::distance(p_first, next_non_wild) - std::count(p_first, next_non_wild,
'*');
782 if(std::distance(s_first, s_last) < required_chars) {
786 s_first += required_chars;
787 p_first = next_non_wild;
789 if(p_first == p_last) {
797 std::replace(str.begin(), str.end(),
'*',
'%');
801 while((
n = str.find(
"_",
n)) != std::string::npos)
803 str.replace(
n, 1,
"\\_");
809 std::string
indent(
const std::string&
string, std::size_t indent_size)
811 if(indent_size == 0) {
815 std::string
indent(indent_size,
' ');
821 const std::vector<std::string>& lines =
split(
string,
'\x0A', 0);
824 for(std::size_t lno = 0; lno < lines.size();) {
825 const std::string&
line = lines[lno];
828 if(!
line.empty() &&
line !=
"\x0D") {
834 if(++lno < lines.size()) {
842 std::vector<std::string>
quoted_split(
const std::string& val,
char c,
int flags,
char quote)
844 std::vector<std::string> res;
846 std::string::const_iterator i1 = val.begin();
847 std::string::const_iterator i2 = val.begin();
849 while (i2 != val.end()) {
853 if (i2 != val.end()) ++i2;
854 }
else if (*i2 ==
c) {
855 std::string new_val(i1, i2);
859 res.push_back(std::move(new_val));
862 while(i2 != val.end() && *i2 ==
' ')
872 std::string new_val(i1, i2);
876 res.push_back(new_val);
889 std::pair<std::string_view, utils::optional<std::string_view>> parse_range_internal_separator(std::string_view str)
893 static const auto separator = std::string{
"-"};
898 auto pos = str.find(separator, 1);
899 auto length = separator.size();
901 if(pos != std::string::npos && pos + length < str.size()) {
902 return {str.substr(0, pos), str.substr(pos + length)};
905 return {str, utils::nullopt};
911 auto [a,
b] = parse_range_internal_separator(str);
912 std::pair<int, int> res{0, 0};
914 if(a ==
"-infinity" &&
b) {
917 res.first = std::numeric_limits<int>::min();
923 res.second = res.first;
924 }
else if(*
b ==
"infinity") {
925 res.second = std::numeric_limits<int>::max();
928 if(res.second < res.first) {
929 res.second = res.first;
932 }
catch(
const std::invalid_argument&) {
941 auto [a,
b] = parse_range_internal_separator(str);
942 std::pair<double, double> res{0, 0};
944 if(a ==
"-infinity" &&
b) {
947 static_assert(std::numeric_limits<double>::is_iec559,
948 "Don't know how negative infinity is treated on this architecture");
949 res.first = -std::numeric_limits<double>::infinity();
955 res.second = res.first;
956 }
else if(*
b ==
"infinity") {
957 res.second = std::numeric_limits<double>::infinity();
960 if(res.second < res.first) {
961 res.second = res.first;
964 }
catch(
const std::invalid_argument&) {
974 if(std::any_of(to_return.begin(), to_return.end(), [](
const std::pair<int, int>& r) { return r.first < 0; })) {
975 ERR_GENERAL <<
"Invalid range (expected values to be zero or positive): " << str;
984 std::vector<std::pair<double, double>> to_return;
994 std::vector<std::pair<int, int>> to_return;
1004 const std::size_t prev_size = str.length();
1008 if(str.length() != prev_size) {
This class represents a single unit of a specific type.
static std::string _(const char *str)
Standard logging facilities (interface).
constexpr int div100rounded(int num)
Guarantees portable results for division by 100; round half up, to the nearest integer.
void line(int from_x, int from_y, int to_x, int to_y)
Draw a line.
const std::string ellipsis
const std::string unicode_minus
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
std::string & truncate(std::string &str, const std::size_t size)
Truncates a UTF-8 string to the specified number of characters.
@ STRIP_SPACES
REMOVE_EMPTY: remove empty elements.
std::vector< std::string_view > split_view(std::string_view s, const char sep, const int flags)
std::string si_string(double input, bool base2, const std::string &unit)
Convert into a string with an SI-postfix.
void trim(std::string_view &s)
std::string indent(const std::string &string, std::size_t indent_size)
Indent a block of text.
std::map< std::string, std::string > map_split(const std::string &val, char major, char minor, int flags, const std::string &default_value)
Splits a string based on two separators into a map.
bool isvalid_wildcard(const std::string &username)
Check if the username pattern contains only valid characters.
std::set< std::string > split_set(std::string_view s, char sep, const int flags)
std::vector< std::string > quoted_split(const std::string &val, char c, int flags, char quote)
This function is identical to split(), except it does not split when it otherwise would if the previo...
int stoi(std::string_view str)
Same interface as std::stoi and meant as a drop in replacement, except:
static void si_string_impl_stream_write(std::stringstream &ss, double input)
std::vector< std::pair< int, int > > parse_ranges_int(const std::string &str)
Handles a comma-separated list of inputs to parse_range.
auto * find_if(Container &container, const Predicate &predicate)
Convenience wrapper for using find_if on a container without needing to comare to end()
std::vector< std::string > parenthetical_split(std::string_view val, const char separator, std::string_view left, std::string_view right, const int flags)
Splits a string based either on a separator, except then the text appears within specified parenthesi...
bool wildcard_string_match(std::string_view str, std::string_view pat) noexcept
Performs pattern matching with wildcards.
std::string half_signed_value(int val)
Sign with Unicode "−" if negative.
std::string bool_string(const bool value)
Converts a bool value to 'true' or 'false'.
static bool is_word_boundary(char c)
void ellipsis_truncate(std::string &str, const std::size_t size)
Truncates a string to a given utf-8 character count and then appends an ellipsis.
std::vector< std::pair< int, int > > parse_ranges_unsigned(const std::string &str)
Handles a comma-separated list of inputs to parse_range, in a context that does not expect negative v...
std::string urlencode(std::string_view str)
Percent-escape characters in a UTF-8 string intended to be part of a URL.
void to_sql_wildcards(std::string &str, bool underscores)
Converts '*' to '' and optionally escapes '_'.
void split_foreach(std::string_view s, char sep, const int flags, const F &f)
bool string_bool(const std::string &str, bool def)
Convert no, false, off, 0, 0.0 to false, empty to def, and others to true.
bool isvalid_username(const std::string &username)
Check if the username contains only valid characters.
double stod(std::string_view str)
Same interface as std::stod and meant as a drop in replacement, except:
bool portable_isspace(const char c)
int apply_modifier(const int number, const std::string &amount, const int minimum)
std::vector< std::string > square_parenthetical_split(const std::string &val, const char separator, const std::string &left, const std::string &right, const int flags)
Similar to parenthetical_split, but also expands embedded square brackets.
std::pair< int, int > parse_range(std::string_view str)
Recognises the following patterns, and returns a {min, max} pair.
bool isnewline(const char c)
bool notspace(const char c)
std::string unescape(std::string_view str)
Remove all escape characters (backslash)
std::vector< std::pair< double, double > > parse_ranges_real(const std::string &str)
bool word_match(const std::string &message, const std::string &word)
Check if a message contains a word.
std::string escape(std::string_view str, const char *special_chars)
Prepends a configurable set of characters with a backslash.
std::string signed_value(int val)
Convert into a signed value (using the Unicode "−" and +0 convention.
std::pair< double, double > parse_range_real(std::string_view str)
Recognises similar patterns to parse_range, and returns a {min, max} pair.
std::vector< std::string > split(const config_attribute_value &val)
bool word_completion(std::string &text, std::vector< std::string > &wordlist)
Try to complete the last word of 'text' with the 'wordlist'.
static bool is_wildcard_char(char c)
static bool is_username_char(char c)
std::string::const_iterator iterator
static lg::log_domain log_engine("engine")
static map_location::direction n
static map_location::direction s