33 #include <boost/algorithm/string.hpp>
36 #define ERR_GENERAL LOG_STREAM(err, lg::general())
37 #define ERR_NG LOG_STREAM(err, log_engine)
43 return c ==
'\r' ||
c ==
'\n';
51 if (
static_cast<unsigned char>(
c) >= 128)
53 return isnewline(
c) || isspace(
static_cast<unsigned char>(
c));
65 s.remove_prefix(std::min(
s.find_first_not_of(
" \t\r\n"),
s.size()));
70 std::size_t first_to_trim =
s.find_last_not_of(
" \t\r\n") + 1;
71 s =
s.substr(0, first_to_trim);
83 std::vector<std::string>
split(std::string_view
s,
const char sep,
const int flags)
85 std::vector<std::string> res;
87 res.emplace_back(item);
92 std::set<std::string>
split_set(std::string_view
s,
char sep,
const int flags)
94 std::set<std::string> res;
101 std::vector<std::string_view>
split_view(std::string_view
s,
const char sep,
const int flags)
103 std::vector<std::string_view> res;
111 const char separator,
const std::string& left,
112 const std::string& right,
const int flags)
114 std::vector< std::string > res;
115 std::vector<char> part;
116 bool in_parenthesis =
false;
117 std::vector<std::string::const_iterator> square_left;
118 std::vector<std::string::const_iterator> square_right;
119 std::vector< std::string > square_expansion;
122 std::string rp=right;
124 std::string::const_iterator i1 = val.begin();
125 std::string::const_iterator i2;
126 std::string::const_iterator j1;
134 if (i1 == val.end())
return res;
137 ERR_GENERAL <<
"Separator must be specified for square bracket split function.";
141 if(left.size()!=right.size()){
142 ERR_GENERAL <<
"Left and Right Parenthesis lists not same length";
147 if(i2 == val.end() || (!in_parenthesis && *i2 == separator)) {
149 std::size_t size_square_exp = 0;
150 for (std::size_t
i=0;
i < square_left.size();
i++) {
151 std::string tmp_val(square_left[
i]+1,square_right[
i]);
152 std::vector< std::string > tmp =
split(tmp_val);
153 for(
const std::string& piece : tmp) {
154 std::size_t found_tilde = piece.find_first_of(
'~');
155 if (found_tilde == std::string::npos) {
156 std::size_t found_asterisk = piece.find_first_of(
'*');
157 if (found_asterisk == std::string::npos) {
158 std::string tmp2(piece);
160 square_expansion.push_back(tmp2);
163 std::string s_begin = piece.substr(0,found_asterisk);
165 std::string s_end = piece.substr(found_asterisk+1);
167 for (
int ast=std::stoi(s_end); ast>0; --ast)
168 square_expansion.push_back(s_begin);
172 std::string s_begin = piece.substr(0,found_tilde);
174 int begin = std::stoi(s_begin);
175 std::size_t padding = 0, padding_end = 0;
176 while (padding<s_begin.size() && s_begin[padding]==
'0') {
179 std::string s_end = piece.substr(found_tilde+1);
181 int end = std::stoi(s_end);
182 while (padding_end<s_end.size() && s_end[padding_end]==
'0') {
185 if (padding*padding_end > 0 && s_begin.size() != s_end.size()) {
186 ERR_GENERAL <<
"Square bracket padding sizes not matching: "
187 << s_begin <<
" and " << s_end <<
".";
189 if (padding_end > padding) padding = padding_end;
191 int increment = (end >= begin ? 1 : -1);
193 for (
int k=begin; k!=end; k+=increment) {
194 std::string pb = std::to_string(k);
195 for (std::size_t
p=pb.size();
p<=padding;
p++)
196 pb = std::string(
"0") + pb;
197 square_expansion.push_back(pb);
201 if (
i*square_expansion.size() != (
i+1)*size_square_exp ) {
202 std::string tmp2(i1, i2);
203 ERR_GENERAL <<
"Square bracket lengths do not match up: " << tmp2;
206 size_square_exp = square_expansion.size();
211 std::size_t j_max = 0;
212 if (!square_left.empty())
213 j_max = square_expansion.size() / square_left.size();
217 for (std::size_t
i=0;
i < square_left.size();
i++) {
218 std::string tmp_val(j1, square_left[
i]);
219 new_val.append(tmp_val);
220 std::size_t k = j+
i*j_max;
221 if (k < square_expansion.size())
222 new_val.append(square_expansion[k]);
223 j1 = square_right[
i]+1;
225 std::string tmp_val(j1, i2);
226 new_val.append(tmp_val);
228 boost::trim_right(new_val);
230 res.push_back(new_val);
243 square_right.clear();
244 square_expansion.clear();
247 if(!part.empty() && *i2 == part.back()) {
249 if (*i2 ==
']') square_right.push_back(i2);
251 in_parenthesis =
false;
256 for(std::size_t
i=0;
i < lp.size();
i++) {
259 square_left.push_back(i2);
261 part.push_back(rp[
i]);
269 in_parenthesis =
true;
280 const std::string& val
284 ,
const std::string& default_value)
287 std::vector< std::string > v =
split(val, major, flags);
290 std::map< std::string, std::string > res;
293 std::size_t pos =
i->find_first_of(minor);
294 std::string key, value;
296 if(pos == std::string::npos) {
298 value = default_value;
300 key =
i->substr(0, pos);
301 value =
i->substr(pos + 1);
311 const char separator, std::string_view left,
312 std::string_view right,
const int flags)
314 std::vector< std::string > res;
315 std::vector<char> part;
316 bool in_parenthesis =
false;
318 std::string_view::const_iterator i1 = val.begin();
319 std::string_view::const_iterator i2;
326 if(left.size()!=right.size()){
327 ERR_GENERAL <<
"Left and Right Parenthesis lists not same length";
331 while (i2 != val.end()) {
332 if(!in_parenthesis && separator && *i2 == separator){
333 std::string new_val(i1, i2);
335 boost::trim_right(new_val);
337 res.push_back(new_val);
346 if(!part.empty() && *i2 == part.back()){
348 if(!separator && part.empty()){
349 std::string new_val(i1, i2);
352 res.push_back(new_val);
357 in_parenthesis =
false;
363 for(std::size_t
i=0;
i < left.size();
i++){
365 if (!separator && part.empty()){
366 std::string new_val(i1, i2);
369 res.push_back(new_val);
375 part.push_back(right[
i]);
383 in_parenthesis =
true;
386 std::string new_val(i1, i2);
390 res.push_back(std::move(new_val));
400 int apply_modifier(
const int number,
const std::string &amount,
const int minimum ) {
404 value = std::stoi(amount);
405 }
catch(
const std::invalid_argument&) {}
406 if(amount[amount.size()-1] ==
'%') {
410 if (( minimum > 0 ) && ( value < minimum ))
415 std::string
escape(
const std::string &str,
const char *special_chars)
417 std::string::size_type pos = str.find_first_of(special_chars);
418 if (pos == std::string::npos) {
422 std::string res = str;
424 res.insert(pos, 1,
'\\');
425 pos = res.find_first_of(special_chars, pos + 2);
426 }
while (pos != std::string::npos);
432 std::string::size_type pos = str.find(
'\\');
433 if (pos == std::string::npos) {
437 std::string res = str;
440 pos = res.find(
'\\', pos + 1);
441 }
while (pos != std::string::npos);
447 static const std::string nonresv_str =
450 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
452 "abcdefghijklmnopqrstuvwxyz"
454 static const std::set<char> nonresv(nonresv_str.begin(), nonresv_str.end());
456 std::ostringstream res;
461 if(nonresv.count(
c) != 0) {
468 res << static_cast<int>(
c);
475 if (str.empty())
return def;
478 if (str ==
"yes")
return true;
479 if (str ==
"no"|| str ==
"false" || str ==
"off" || str ==
"0" || str ==
"0.0")
488 std::ostringstream ss;
489 ss << std::boolalpha << value;
496 std::ostringstream oss;
503 std::ostringstream oss;
506 oss << std::abs(val);
511 std::streamsize oldprec = ss.precision();
523 input = std::round(input);
529 if (input >= 999.5) {
536 ss.precision(oldprec);
540 const double multiplier = base2 ? 1024 : 1000;
542 typedef std::array<std::string, 9> strings9;
549 strings9::const_iterator prefix;
551 strings9 tmp { {
"",
"",
"",
"",
"",
"",
"",
"",
"" } };
553 prefix = prefixes.begin();
554 }
else if (input < 1.0) {
567 prefix = prefixes.begin();
568 while (input < 1.0 && *prefix != prefixes.back()) {
589 prefix = prefixes.begin();
590 while (input > multiplier && *prefix != prefixes.back()) {
596 std::stringstream ss;
601 << (base2 && (!(*prefix).empty()) ?
_(
"infix_binary^i") :
"")
607 return ((
c ==
'_') || (
c ==
'-'));
611 return ((
c ==
'?') || (
c ==
'*'));
615 const std::size_t alnum = std::count_if(username.begin(), username.end(), isalnum);
616 const std::size_t valid_char =
618 if ((alnum + valid_char != username.size())
619 || valid_char == username.size() || username.empty() )
627 const std::size_t alnum = std::count_if(username.begin(), username.end(), isalnum);
628 const std::size_t valid_char =
630 const std::size_t wild_char =
632 if ((alnum + valid_char + wild_char != username.size())
633 || valid_char == username.size() || username.empty() )
642 std::vector<std::string> matches;
643 const std::size_t last_space = text.rfind(
" ");
645 if (last_space == text.size() -1) {
651 std::string semiword;
652 if (last_space == std::string::npos) {
657 semiword.assign(text, last_space + 1, text.size());
660 std::string best_match = semiword;
661 for (std::vector<std::string>::const_iterator word = wordlist.begin();
662 word != wordlist.end(); ++word)
664 if (word->size() < semiword.size()
665 || !std::equal(semiword.begin(), semiword.end(), word->begin(),
666 [](
char a,
char b) { return tolower(a) == tolower(b); }))
670 if (matches.empty()) {
674 while (toupper(best_match[j]) == toupper((*word)[j])) j++;
675 if (best_match.begin() + j < best_match.end()) {
676 best_match.erase(best_match.begin() + j, best_match.end());
679 matches.push_back(*word);
681 if(!matches.empty()) {
682 text.replace(last_space + 1, best_match.size(), best_match);
689 return (
c ==
' ' ||
c ==
',' ||
c ==
':' ||
c ==
'\'' ||
c ==
'"' ||
c ==
'-');
692 bool word_match(
const std::string& message,
const std::string& word) {
693 std::size_t first = message.find(word);
694 if (first == std::string::npos)
return false;
696 std::size_t next = first + word.size();
705 const bool wild_matching = (!match.empty() && (match[0] ==
'*' || match[0] ==
'+'));
706 const std::string::size_type solid_begin = match.find_first_not_of(
"*+");
707 const bool have_solids = (solid_begin != std::string::npos);
710 const std::string::size_type plus_count = std::count(match.begin(), match.end(),
'+');
711 return match.empty() ? str.empty() : str.length() >= plus_count;
712 }
else if(str.empty()) {
716 const std::string::size_type solid_end = match.find_first_of(
"*+", solid_begin);
717 const std::string::size_type solid_len = (solid_end == std::string::npos)
718 ? match.length() - solid_begin : solid_end - solid_begin;
721 std::string::size_type current = match[0] ==
'+' ? 1 : 0;
726 const std::string::size_type test_len = str.length() - current;
727 for(std::string::size_type
i=0;
i < solid_len && matches; ++
i) {
728 char solid_c = match[solid_begin +
i];
729 if(
i > test_len || !(solid_c ==
'?' || solid_c == str[current+
i])) {
735 const std::string consumed_match = (solid_begin+solid_len < match.length())
736 ? match.substr(solid_end) :
"";
737 const std::string consumed_str = (solid_len < test_len)
738 ? str.substr(current+solid_len) :
"";
741 }
while(wild_matching && !matches && ++current < str.length());
747 std::replace(str.begin(), str.end(),
'*',
'%');
751 while((
n = str.find(
"_",
n)) != std::string::npos)
753 str.replace(
n, 1,
"\\_");
759 std::string
indent(
const std::string&
string, std::size_t indent_size)
761 if(indent_size == 0) {
765 const std::string
indent(indent_size,
' ');
771 const std::vector<std::string>& lines =
split(
string,
'\x0A', 0);
774 for(std::size_t lno = 0; lno < lines.size();) {
775 const std::string&
line = lines[lno];
778 if(!
line.empty() &&
line !=
"\x0D") {
784 if(++lno < lines.size()) {
794 std::vector<std::string> res;
796 std::string::const_iterator i1 = val.begin();
797 std::string::const_iterator i2 = val.begin();
799 while (i2 != val.end()) {
803 if (i2 != val.end()) ++i2;
804 }
else if (*i2 ==
c) {
805 std::string new_val(i1, i2);
809 res.push_back(std::move(new_val));
812 while(i2 != val.end() && *i2 ==
' ')
822 std::string new_val(i1, i2);
826 res.push_back(new_val);
839 std::pair<std::string, utils::optional<std::string>> parse_range_internal_separator(
const std::string& str)
843 static const auto separator = std::string{
"-"};
848 auto pos = str.find(separator, 1);
849 auto length = separator.size();
851 if(pos != std::string::npos && pos + length < str.size()) {
852 return {str.substr(0, pos), str.substr(pos + length)};
855 return {str, utils::nullopt};
861 auto [a,
b] = parse_range_internal_separator(str);
862 std::pair<int, int> res{0, 0};
864 if(a ==
"-infinity" &&
b) {
867 res.first = std::numeric_limits<int>::min();
869 res.first = std::stoi(a);
873 res.second = res.first;
874 }
else if(*
b ==
"infinity") {
875 res.second = std::numeric_limits<int>::max();
877 res.second = std::stoi(*
b);
878 if(res.second < res.first) {
879 res.second = res.first;
882 }
catch(
const std::invalid_argument&) {
891 auto [a,
b] = parse_range_internal_separator(str);
892 std::pair<double, double> res{0, 0};
894 if(a ==
"-infinity" &&
b) {
897 static_assert(std::numeric_limits<double>::is_iec559,
898 "Don't know how negative infinity is treated on this architecture");
899 res.first = -std::numeric_limits<double>::infinity();
901 res.first = std::stod(a);
905 res.second = res.first;
906 }
else if(*
b ==
"infinity") {
907 res.second = std::numeric_limits<double>::infinity();
909 res.second = std::stod(*
b);
910 if(res.second < res.first) {
911 res.second = res.first;
914 }
catch(
const std::invalid_argument&) {
924 if(std::any_of(to_return.begin(), to_return.end(), [](
const std::pair<int, int>& r) { return r.first < 0; })) {
925 ERR_GENERAL <<
"Invalid range (expected values to be zero or positive): " << str;
934 std::vector<std::pair<double, double>> to_return;
944 std::vector<std::pair<int, int>> to_return;
954 const std::size_t prev_size = str.length();
958 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(const std::string &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...
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.
std::pair< int, int > parse_range(const std::string &str)
Recognises the following patterns, and returns a {min, max} pair.
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...
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'.
std::string urlencode(const std::string &str)
Percent-escape characters in a UTF-8 string intended to be part of a URL.
static bool is_word_boundary(char c)
std::string quote(const std::string &str)
Surround the string 'str' with double quotes.
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...
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 wildcard_string_match(const std::string &str, const std::string &match)
Match using '*' as any number of characters (including none), '+' as one or more characters,...
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.
std::string unescape(const std::string &str)
Remove all escape characters (backslash)
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.
bool isnewline(const char c)
bool notspace(const char c)
std::string escape(const std::string &str, const char *special_chars)
Prepends a configurable set of characters with a 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 signed_value(int val)
Convert into a signed value (using the Unicode "−" and +0 convention.
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::pair< double, double > parse_range_real(const std::string &str)
Recognises similar patterns to parse_range, and returns a {min, max} pair.
std::string::const_iterator iterator
static lg::log_domain log_engine("engine")
static map_location::direction n
static map_location::direction s