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 if (
static_cast<unsigned char>(
c) >= 128)
54 return isnewline(
c) || isspace(
static_cast<unsigned char>(
c));
66 s.remove_prefix(std::min(
s.find_first_not_of(
" \t\r\n"),
s.size()));
71 size_t first_to_trim =
s.find_last_not_of(
" \t\r\n") + 1;
72 s =
s.substr(0, first_to_trim);
84 std::vector<std::string>
split(std::string_view
s,
const char sep,
const int flags)
86 std::vector<std::string> res;
88 res.emplace_back(
item);
93 std::set<std::string>
split_set(std::string_view
s,
char sep,
const int flags)
95 std::set<std::string> res;
103 const char separator,
const std::string& left,
104 const std::string& right,
const int flags)
106 std::vector< std::string > res;
107 std::vector<char> part;
108 bool in_parenthesis =
false;
109 std::vector<std::string::const_iterator> square_left;
110 std::vector<std::string::const_iterator> square_right;
111 std::vector< std::string > square_expansion;
114 std::string rp=right;
116 std::string::const_iterator i1 = val.begin();
117 std::string::const_iterator i2;
118 std::string::const_iterator j1;
126 if (i1 == val.end())
return res;
129 ERR_GENERAL <<
"Separator must be specified for square bracket split function.";
133 if(left.size()!=right.size()){
134 ERR_GENERAL <<
"Left and Right Parenthesis lists not same length";
139 if(i2 == val.end() || (!in_parenthesis && *i2 == separator)) {
141 std::size_t size_square_exp = 0;
142 for (std::size_t
i=0;
i < square_left.size();
i++) {
143 std::string tmp_val(square_left[
i]+1,square_right[
i]);
144 std::vector< std::string > tmp =
split(tmp_val);
145 for(
const std::string& piece : tmp) {
146 std::size_t found_tilde = piece.find_first_of(
'~');
147 if (found_tilde == std::string::npos) {
148 std::size_t found_asterisk = piece.find_first_of(
'*');
149 if (found_asterisk == std::string::npos) {
150 std::string tmp2(piece);
152 square_expansion.push_back(tmp2);
155 std::string s_begin = piece.substr(0,found_asterisk);
157 std::string s_end = piece.substr(found_asterisk+1);
159 for (
int ast=std::stoi(s_end); ast>0; --ast)
160 square_expansion.push_back(s_begin);
164 std::string s_begin = piece.substr(0,found_tilde);
166 int begin = std::stoi(s_begin);
167 std::size_t padding = 0, padding_end = 0;
168 while (padding<s_begin.size() && s_begin[padding]==
'0') {
171 std::string s_end = piece.substr(found_tilde+1);
173 int end = std::stoi(s_end);
174 while (padding_end<s_end.size() && s_end[padding_end]==
'0') {
177 if (padding*padding_end > 0 && s_begin.size() != s_end.size()) {
178 ERR_GENERAL <<
"Square bracket padding sizes not matching: "
179 << s_begin <<
" and " << s_end <<
".";
181 if (padding_end > padding) padding = padding_end;
183 int increment = (end >= begin ? 1 : -1);
185 for (
int k=begin; k!=end; k+=increment) {
186 std::string pb = std::to_string(k);
187 for (std::size_t
p=pb.size();
p<=padding;
p++)
188 pb = std::string(
"0") + pb;
189 square_expansion.push_back(pb);
193 if (
i*square_expansion.size() != (
i+1)*size_square_exp ) {
194 std::string tmp2(i1, i2);
195 ERR_GENERAL <<
"Square bracket lengths do not match up: " << tmp2;
198 size_square_exp = square_expansion.size();
203 std::size_t j_max = 0;
204 if (!square_left.empty())
205 j_max = square_expansion.size() / square_left.size();
209 for (std::size_t
i=0;
i < square_left.size();
i++) {
210 std::string tmp_val(j1, square_left[
i]);
211 new_val.append(tmp_val);
212 std::size_t k = j+
i*j_max;
213 if (k < square_expansion.size())
214 new_val.append(square_expansion[k]);
215 j1 = square_right[
i]+1;
217 std::string tmp_val(j1, i2);
218 new_val.append(tmp_val);
220 boost::trim_right(new_val);
222 res.push_back(new_val);
235 square_right.clear();
236 square_expansion.clear();
239 if(!part.empty() && *i2 == part.back()) {
241 if (*i2 ==
']') square_right.push_back(i2);
243 in_parenthesis =
false;
248 for(std::size_t
i=0;
i < lp.size();
i++) {
251 square_left.push_back(i2);
253 part.push_back(rp[
i]);
261 in_parenthesis =
true;
272 const std::string& val
276 ,
const std::string& default_value)
279 std::vector< std::string > v =
split(val, major, flags);
282 std::map< std::string, std::string > res;
285 std::size_t pos =
i->find_first_of(minor);
286 std::string key, value;
288 if(pos == std::string::npos) {
290 value = default_value;
292 key =
i->substr(0, pos);
293 value =
i->substr(pos + 1);
303 const char separator,
const std::string& left,
304 const std::string& right,
const int flags)
306 std::vector< std::string > res;
307 std::vector<char> part;
308 bool in_parenthesis =
false;
311 std::string rp=right;
313 std::string::const_iterator i1 = val.begin();
314 std::string::const_iterator i2;
321 if(left.size()!=right.size()){
322 ERR_GENERAL <<
"Left and Right Parenthesis lists not same length";
326 while (i2 != val.end()) {
327 if(!in_parenthesis && separator && *i2 == separator){
328 std::string new_val(i1, i2);
330 boost::trim_right(new_val);
332 res.push_back(new_val);
341 if(!part.empty() && *i2 == part.back()){
343 if(!separator && part.empty()){
344 std::string new_val(i1, i2);
347 res.push_back(new_val);
352 in_parenthesis =
false;
358 for(std::size_t
i=0;
i < lp.size();
i++){
360 if (!separator && part.empty()){
361 std::string new_val(i1, i2);
364 res.push_back(new_val);
370 part.push_back(rp[
i]);
378 in_parenthesis =
true;
381 std::string new_val(i1, i2);
385 res.push_back(std::move(new_val));
395 int apply_modifier(
const int number,
const std::string &amount,
const int minimum ) {
399 value = std::stoi(amount);
400 }
catch(
const std::invalid_argument&) {}
401 if(amount[amount.size()-1] ==
'%') {
405 if (( minimum > 0 ) && ( value < minimum ))
410 std::string
escape(
const std::string &str,
const char *special_chars)
412 std::string::size_type pos = str.find_first_of(special_chars);
413 if (pos == std::string::npos) {
417 std::string res = str;
419 res.insert(pos, 1,
'\\');
420 pos = res.find_first_of(special_chars, pos + 2);
421 }
while (pos != std::string::npos);
427 std::string::size_type pos = str.find(
'\\');
428 if (pos == std::string::npos) {
432 std::string res = str;
435 pos = res.find(
'\\', pos + 1);
436 }
while (pos != std::string::npos);
442 static const std::string nonresv_str =
445 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
447 "abcdefghijklmnopqrstuvwxyz"
449 static const std::set<char> nonresv(nonresv_str.begin(), nonresv_str.end());
451 std::ostringstream res;
456 if(nonresv.count(
c) != 0) {
463 res << static_cast<int>(
c);
470 if (str.empty())
return def;
473 if (str ==
"yes")
return true;
474 if (str ==
"no"|| str ==
"false" || str ==
"off" || str ==
"0" || str ==
"0.0")
483 std::ostringstream ss;
484 ss << std::boolalpha << value;
491 std::ostringstream oss;
498 std::ostringstream oss;
501 oss << std::abs(val);
506 std::streamsize oldprec = ss.precision();
518 input = std::round(input);
524 if (input >= 999.5) {
531 ss.precision(oldprec);
535 const double multiplier = base2 ? 1024 : 1000;
537 typedef std::array<std::string, 9> strings9;
544 strings9::const_iterator prefix;
546 strings9 tmp { {
"",
"",
"",
"",
"",
"",
"",
"",
"" } };
548 prefix = prefixes.begin();
549 }
else if (input < 1.0) {
562 prefix = prefixes.begin();
563 while (input < 1.0 && *prefix != prefixes.back()) {
584 prefix = prefixes.begin();
585 while (input > multiplier && *prefix != prefixes.back()) {
591 std::stringstream ss;
596 << (base2 && (!(*prefix).empty()) ?
_(
"infix_binary^i") :
"")
602 return ((
c ==
'_') || (
c ==
'-'));
606 return ((
c ==
'?') || (
c ==
'*'));
610 const std::size_t alnum = std::count_if(username.begin(), username.end(), isalnum);
611 const std::size_t valid_char =
613 if ((alnum + valid_char != username.size())
614 || valid_char == username.size() || username.empty() )
622 const std::size_t alnum = std::count_if(username.begin(), username.end(), isalnum);
623 const std::size_t valid_char =
625 const std::size_t wild_char =
627 if ((alnum + valid_char + wild_char != username.size())
628 || valid_char == username.size() || username.empty() )
637 std::vector<std::string> matches;
638 const std::size_t last_space = text.rfind(
" ");
640 if (last_space == text.size() -1) {
646 std::string semiword;
647 if (last_space == std::string::npos) {
652 semiword.assign(text, last_space + 1, text.size());
655 std::string best_match = semiword;
656 for (std::vector<std::string>::const_iterator word = wordlist.begin();
657 word != wordlist.end(); ++word)
659 if (word->size() < semiword.size()
660 || !std::equal(semiword.begin(), semiword.end(), word->begin(),
665 if (matches.empty()) {
669 while (toupper(best_match[j]) == toupper((*word)[j])) j++;
670 if (best_match.begin() + j < best_match.end()) {
671 best_match.erase(best_match.begin() + j, best_match.end());
674 matches.push_back(*word);
676 if(!matches.empty()) {
677 text.replace(last_space + 1, best_match.size(), best_match);
684 return (
c ==
' ' ||
c ==
',' ||
c ==
':' ||
c ==
'\'' ||
c ==
'"' ||
c ==
'-');
687 bool word_match(
const std::string& message,
const std::string& word) {
688 std::size_t first = message.find(word);
689 if (first == std::string::npos)
return false;
691 std::size_t next = first + word.size();
700 const bool wild_matching = (!match.empty() && (match[0] ==
'*' || match[0] ==
'+'));
701 const std::string::size_type solid_begin = match.find_first_not_of(
"*+");
702 const bool have_solids = (solid_begin != std::string::npos);
705 const std::string::size_type plus_count = std::count(match.begin(), match.end(),
'+');
706 return match.empty() ? str.empty() : str.length() >= plus_count;
707 }
else if(str.empty()) {
711 const std::string::size_type solid_end = match.find_first_of(
"*+", solid_begin);
712 const std::string::size_type solid_len = (solid_end == std::string::npos)
713 ? match.length() - solid_begin : solid_end - solid_begin;
716 std::string::size_type current = match[0] ==
'+' ? 1 : 0;
721 const std::string::size_type test_len = str.length() - current;
722 for(std::string::size_type
i=0;
i < solid_len && matches; ++
i) {
723 char solid_c = match[solid_begin +
i];
724 if(
i > test_len || !(solid_c ==
'?' || solid_c == str[current+
i])) {
730 const std::string consumed_match = (solid_begin+solid_len < match.length())
731 ? match.substr(solid_end) :
"";
732 const std::string consumed_str = (solid_len < test_len)
733 ? str.substr(current+solid_len) :
"";
736 }
while(wild_matching && !matches && ++current < str.length());
742 std::replace(str.begin(), str.end(),
'*',
'%');
746 while((
n = str.find(
"_",
n)) != std::string::npos)
748 str.replace(
n, 1,
"\\_");
754 std::string
indent(
const std::string&
string, std::size_t indent_size)
756 if(indent_size == 0) {
760 const std::string
indent(indent_size,
' ');
766 const std::vector<std::string>& lines =
split(
string,
'\x0A', 0);
769 for(std::size_t lno = 0; lno < lines.size();) {
770 const std::string&
line = lines[lno];
773 if(!
line.empty() &&
line !=
"\x0D") {
779 if(++lno < lines.size()) {
789 std::vector<std::string> res;
791 std::string::const_iterator i1 = val.begin();
792 std::string::const_iterator i2 = val.begin();
794 while (i2 != val.end()) {
798 if (i2 != val.end()) ++i2;
799 }
else if (*i2 ==
c) {
800 std::string new_val(i1, i2);
804 res.push_back(std::move(new_val));
807 while(i2 != val.end() && *i2 ==
' ')
817 std::string new_val(i1, i2);
821 res.push_back(new_val);
834 std::pair<std::string, std::optional<std::string>> parse_range_internal_separator(
const std::string& str)
838 static const auto separator = std::string{
"-"};
843 auto pos = str.find(separator, 1);
844 auto length = separator.size();
846 if(pos != std::string::npos && pos + length < str.size()) {
847 return {str.substr(0, pos), str.substr(pos + length)};
850 return {str, std::nullopt};
856 auto [
a,
b] = parse_range_internal_separator(str);
857 std::pair<int, int> res{0, 0};
859 if(
a ==
"-infinity" &&
b) {
862 res.first = std::numeric_limits<int>::min();
864 res.first = std::stoi(
a);
868 res.second = res.first;
869 }
else if(*
b ==
"infinity") {
870 res.second = std::numeric_limits<int>::max();
872 res.second = std::stoi(*
b);
873 if(res.second < res.first) {
874 res.second = res.first;
877 }
catch(
const std::invalid_argument&) {
886 auto [
a,
b] = parse_range_internal_separator(str);
887 std::pair<double, double> res{0, 0};
889 if(
a ==
"-infinity" &&
b) {
892 static_assert(std::numeric_limits<double>::is_iec559,
893 "Don't know how negative infinity is treated on this architecture");
894 res.first = -std::numeric_limits<double>::infinity();
896 res.first = std::stod(
a);
900 res.second = res.first;
901 }
else if(*
b ==
"infinity") {
902 res.second = std::numeric_limits<double>::infinity();
904 res.second = std::stod(*
b);
905 if(res.second < res.first) {
906 res.second = res.first;
909 }
catch(
const std::invalid_argument&) {
919 if(std::any_of(to_return.begin(), to_return.end(), [](
const std::pair<int, int>& r) { return r.first < 0; })) {
920 ERR_GENERAL <<
"Invalid range (expected values to be zero or positive): " << str;
929 std::vector<std::pair<double, double>> to_return;
939 std::vector<std::pair<int, int>> to_return;
949 const std::size_t prev_size = str.length();
953 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::pair< std::string, unsigned > item
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::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(const std::string &val, const char separator, const std::string &left, const std::string &right, const int flags)
Splits a string based either on a separator, except then the text appears within specified parenthesi...
bool chars_equal_insensitive(char a, char b)
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