The Battle for Wesnoth  1.15.2+dev
string_utils.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 by David White <dave@whitevine.net>
3  Copyright (C) 2005 by Guillaume Melquiond <guillaume.melquiond@gmail.com>
4  Copyright (C) 2005 - 2018 by Philippe Plantier <ayin@anathas.org>
5  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY.
13 
14  See the COPYING file for more details.
15 */
16 
17 /**
18  * @file
19  * Various string-routines.
20  */
21 
22 #include "gettext.hpp"
23 #include "log.hpp"
26 #include "utils/general.hpp"
27 #include <cassert>
28 #include <array>
29 #include <limits>
30 #include <stdexcept>
31 
32 #include <boost/algorithm/string.hpp>
33 
34 static lg::log_domain log_engine("engine");
35 #define ERR_GENERAL LOG_STREAM(err, lg::general())
36 #define ERR_NG LOG_STREAM(err, log_engine)
37 
38 namespace utils {
39 
40 bool isnewline(const char c)
41 {
42  return c == '\r' || c == '\n';
43 }
44 
45 // Make sure that we can use Mac, DOS, or Unix style text files on any system
46 // and they will work, by making sure the definition of whitespace is consistent
47 bool portable_isspace(const char c)
48 {
49  // returns true only on ASCII spaces
50  if (static_cast<unsigned char>(c) >= 128)
51  return false;
52  return isnewline(c) || isspace(static_cast<unsigned char>(c));
53 }
54 
55 // Make sure we regard '\r' and '\n' as a space, since Mac, Unix, and DOS
56 // all consider these differently.
57 bool notspace(const char c)
58 {
59  return !portable_isspace(c);
60 }
61 
62 /**
63  * Splits a (comma-)separated string into a vector of pieces.
64  * @param[in] val A (comma-)separated string.
65  * @param[in] c The separator character (usually a comma).
66  * @param[in] flags Flags controlling how the split is done.
67  * This is a bit field with two settings (both on by default):
68  * REMOVE_EMPTY causes empty pieces to be skipped/removed.
69  * STRIP_SPACES causes the leading and trailing spaces of each piece to be ignored/stripped.
70  *
71  * Basic method taken from http://stackoverflow.com/a/236803
72  */
73 std::vector<std::string> split(const std::string& val, const char c, const int flags)
74 {
75  std::vector<std::string> res;
76 
77  std::stringstream ss;
78  ss.str(val);
79 
80  std::string item;
81  while(std::getline(ss, item, c)) {
82  if(flags & STRIP_SPACES) {
83  boost::trim(item);
84  }
85 
86  if(!(flags & REMOVE_EMPTY) || !item.empty()) {
87  res.push_back(std::move(item));
88  }
89  }
90 
91  return res;
92 }
93 
94 std::vector<std::string> square_parenthetical_split(const std::string& val,
95  const char separator, const std::string& left,
96  const std::string& right,const int flags)
97 {
98  std::vector< std::string > res;
99  std::vector<char> part;
100  bool in_parenthesis = false;
101  std::vector<std::string::const_iterator> square_left;
102  std::vector<std::string::const_iterator> square_right;
103  std::vector< std::string > square_expansion;
104 
105  std::string lp=left;
106  std::string rp=right;
107 
108  std::string::const_iterator i1 = val.begin();
109  std::string::const_iterator i2;
110  std::string::const_iterator j1;
111  if (flags & STRIP_SPACES) {
112  while (i1 != val.end() && portable_isspace(*i1))
113  ++i1;
114  }
115  i2=i1;
116  j1=i1;
117 
118  if (i1 == val.end()) return res;
119 
120  if (!separator) {
121  ERR_GENERAL << "Separator must be specified for square bracket split function." << std::endl;
122  return res;
123  }
124 
125  if(left.size()!=right.size()){
126  ERR_GENERAL << "Left and Right Parenthesis lists not same length" << std::endl;
127  return res;
128  }
129 
130  while (true) {
131  if(i2 == val.end() || (!in_parenthesis && *i2 == separator)) {
132  //push back square contents
133  std::size_t size_square_exp = 0;
134  for (std::size_t i=0; i < square_left.size(); i++) {
135  std::string tmp_val(square_left[i]+1,square_right[i]);
136  std::vector< std::string > tmp = split(tmp_val);
137  for(const std::string& piece : tmp) {
138  std::size_t found_tilde = piece.find_first_of('~');
139  if (found_tilde == std::string::npos) {
140  std::size_t found_asterisk = piece.find_first_of('*');
141  if (found_asterisk == std::string::npos) {
142  std::string tmp2(piece);
143  boost::trim(tmp2);
144  square_expansion.push_back(tmp2);
145  }
146  else { //'*' multiple expansion
147  std::string s_begin = piece.substr(0,found_asterisk);
148  boost::trim(s_begin);
149  std::string s_end = piece.substr(found_asterisk+1);
150  boost::trim(s_end);
151  for (int ast=std::stoi(s_end); ast>0; --ast)
152  square_expansion.push_back(s_begin);
153  }
154  }
155  else { //expand number range
156  std::string s_begin = piece.substr(0,found_tilde);
157  boost::trim(s_begin);
158  int begin = std::stoi(s_begin);
159  std::size_t padding = 0, padding_end = 0;
160  while (padding<s_begin.size() && s_begin[padding]=='0') {
161  padding++;
162  }
163  std::string s_end = piece.substr(found_tilde+1);
164  boost::trim(s_end);
165  int end = std::stoi(s_end);
166  while (padding_end<s_end.size() && s_end[padding_end]=='0') {
167  padding_end++;
168  }
169  if (padding*padding_end > 0 && s_begin.size() != s_end.size()) {
170  ERR_GENERAL << "Square bracket padding sizes not matching: "
171  << s_begin << " and " << s_end <<".\n";
172  }
173  if (padding_end > padding) padding = padding_end;
174 
175  int increment = (end >= begin ? 1 : -1);
176  end+=increment; //include end in expansion
177  for (int k=begin; k!=end; k+=increment) {
178  std::string pb = std::to_string(k);
179  for (std::size_t p=pb.size(); p<=padding; p++)
180  pb = std::string("0") + pb;
181  square_expansion.push_back(pb);
182  }
183  }
184  }
185  if (i*square_expansion.size() != (i+1)*size_square_exp ) {
186  std::string tmp2(i1, i2);
187  ERR_GENERAL << "Square bracket lengths do not match up: " << tmp2 << std::endl;
188  return res;
189  }
190  size_square_exp = square_expansion.size();
191  }
192 
193  //combine square contents and rest of string for comma zone block
194  std::size_t j = 0;
195  std::size_t j_max = 0;
196  if (!square_left.empty())
197  j_max = square_expansion.size() / square_left.size();
198  do {
199  j1 = i1;
200  std::string new_val;
201  for (std::size_t i=0; i < square_left.size(); i++) {
202  std::string tmp_val(j1, square_left[i]);
203  new_val.append(tmp_val);
204  std::size_t k = j+i*j_max;
205  if (k < square_expansion.size())
206  new_val.append(square_expansion[k]);
207  j1 = square_right[i]+1;
208  }
209  std::string tmp_val(j1, i2);
210  new_val.append(tmp_val);
211  if (flags & STRIP_SPACES)
212  boost::trim_right(new_val);
213  if (!(flags & REMOVE_EMPTY) || !new_val.empty())
214  res.push_back(new_val);
215  j++;
216  } while (j<j_max);
217 
218  if (i2 == val.end()) //escape loop
219  break;
220  ++i2;
221  if (flags & STRIP_SPACES) { //strip leading spaces
222  while (i2 != val.end() && portable_isspace(*i2))
223  ++i2;
224  }
225  i1=i2;
226  square_left.clear();
227  square_right.clear();
228  square_expansion.clear();
229  continue;
230  }
231  if(!part.empty() && *i2 == part.back()) {
232  part.pop_back();
233  if (*i2 == ']') square_right.push_back(i2);
234  if (part.empty())
235  in_parenthesis = false;
236  ++i2;
237  continue;
238  }
239  bool found=false;
240  for(std::size_t i=0; i < lp.size(); i++) {
241  if (*i2 == lp[i]){
242  if (*i2 == '[')
243  square_left.push_back(i2);
244  ++i2;
245  part.push_back(rp[i]);
246  found=true;
247  break;
248  }
249  }
250  if(!found){
251  ++i2;
252  } else
253  in_parenthesis = true;
254  }
255 
256  if(!part.empty()){
257  ERR_GENERAL << "Mismatched parenthesis:\n"<<val<< std::endl;
258  }
259 
260  return res;
261 }
262 
263 std::map<std::string, std::string> map_split(
264  const std::string& val
265  , char major
266  , char minor
267  , int flags
268  , const std::string& default_value)
269 {
270  //first split by major so that we get a vector with the key-value pairs
271  std::vector< std::string > v = split(val, major, flags);
272 
273  //now split by minor to extract keys and values
274  std::map< std::string, std::string > res;
275 
276  for( std::vector< std::string >::iterator i = v.begin(); i != v.end(); ++i) {
277  std::size_t pos = i->find_first_of(minor);
278  std::string key, value;
279 
280  if(pos == std::string::npos) {
281  key = (*i);
282  value = default_value;
283  } else {
284  key = i->substr(0, pos);
285  value = i->substr(pos + 1);
286  }
287 
288  res[key] = value;
289  }
290 
291  return res;
292 }
293 
294 std::vector<std::string> parenthetical_split(const std::string& val,
295  const char separator, const std::string& left,
296  const std::string& right,const int flags)
297 {
298  std::vector< std::string > res;
299  std::vector<char> part;
300  bool in_parenthesis = false;
301 
302  std::string lp=left;
303  std::string rp=right;
304 
305  std::string::const_iterator i1 = val.begin();
306  std::string::const_iterator i2;
307  if (flags & STRIP_SPACES) {
308  while (i1 != val.end() && portable_isspace(*i1))
309  ++i1;
310  }
311  i2=i1;
312 
313  if(left.size()!=right.size()){
314  ERR_GENERAL << "Left and Right Parenthesis lists not same length" << std::endl;
315  return res;
316  }
317 
318  while (i2 != val.end()) {
319  if(!in_parenthesis && separator && *i2 == separator){
320  std::string new_val(i1, i2);
321  if (flags & STRIP_SPACES)
322  boost::trim_right(new_val);
323  if (!(flags & REMOVE_EMPTY) || !new_val.empty())
324  res.push_back(new_val);
325  ++i2;
326  if (flags & STRIP_SPACES) {
327  while (i2 != val.end() && portable_isspace(*i2))
328  ++i2;
329  }
330  i1=i2;
331  continue;
332  }
333  if(!part.empty() && *i2 == part.back()){
334  part.pop_back();
335  if(!separator && part.empty()){
336  std::string new_val(i1, i2);
337  if (flags & STRIP_SPACES)
338  boost::trim(new_val);
339  res.push_back(new_val);
340  ++i2;
341  i1=i2;
342  }else{
343  if (part.empty())
344  in_parenthesis = false;
345  ++i2;
346  }
347  continue;
348  }
349  bool found=false;
350  for(std::size_t i=0; i < lp.size(); i++){
351  if (*i2 == lp[i]){
352  if (!separator && part.empty()){
353  std::string new_val(i1, i2);
354  if (flags & STRIP_SPACES)
355  boost::trim(new_val);
356  res.push_back(new_val);
357  ++i2;
358  i1=i2;
359  }else{
360  ++i2;
361  }
362  part.push_back(rp[i]);
363  found=true;
364  break;
365  }
366  }
367  if(!found){
368  ++i2;
369  } else
370  in_parenthesis = true;
371  }
372 
373  std::string new_val(i1, i2);
374  if (flags & STRIP_SPACES)
375  boost::trim(new_val);
376  if (!(flags & REMOVE_EMPTY) || !new_val.empty())
377  res.push_back(std::move(new_val));
378 
379  if(!part.empty()){
380  ERR_GENERAL << "Mismatched parenthesis:\n"<<val<< std::endl;
381  }
382 
383  return res;
384 }
385 
386 // Modify a number by string representing integer difference, or optionally %
387 int apply_modifier( const int number, const std::string &amount, const int minimum ) {
388  // wassert( amount.empty() == false );
389  int value = 0;
390  try {
391  value = std::stoi(amount);
392  } catch(const std::invalid_argument&) {}
393  if(amount[amount.size()-1] == '%') {
394  value = div100rounded(number * value);
395  }
396  value += number;
397  if (( minimum > 0 ) && ( value < minimum ))
398  value = minimum;
399  return value;
400 }
401 
402 std::string escape(const std::string &str, const char *special_chars)
403 {
404  std::string::size_type pos = str.find_first_of(special_chars);
405  if (pos == std::string::npos) {
406  // Fast path, possibly involving only reference counting.
407  return str;
408  }
409  std::string res = str;
410  do {
411  res.insert(pos, 1, '\\');
412  pos = res.find_first_of(special_chars, pos + 2);
413  } while (pos != std::string::npos);
414  return res;
415 }
416 
417 std::string unescape(const std::string &str)
418 {
419  std::string::size_type pos = str.find('\\');
420  if (pos == std::string::npos) {
421  // Fast path, possibly involving only reference counting.
422  return str;
423  }
424  std::string res = str;
425  do {
426  res.erase(pos, 1);
427  pos = res.find('\\', pos + 1);
428  } while (pos != std::string::npos);
429  return str;
430 }
431 
432 std::string urlencode(const std::string &str)
433 {
434  static const std::string nonresv_str =
435  "-."
436  "0123456789"
437  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
438  "_"
439  "abcdefghijklmnopqrstuvwxyz"
440  "~";
441  static const std::set<char> nonresv(nonresv_str.begin(), nonresv_str.end());
442 
443  std::ostringstream res;
444  res << std::hex;
445  res.fill('0');
446 
447  for(char c : str) {
448  if(nonresv.count(c) != 0) {
449  res << c;
450  continue;
451  }
452 
453  res << '%';
454  res.width(2);
455  res << static_cast<int>(c);
456  }
457 
458  return res.str();
459 }
460 
461 bool string_bool(const std::string& str, bool def) {
462  if (str.empty()) return def;
463 
464  // yes/no is the standard, test it first
465  if (str == "yes") return true;
466  if (str == "no"|| str == "false" || str == "off" || str == "0" || str == "0.0")
467  return false;
468 
469  // all other non-empty string are considered as true
470  return true;
471 }
472 
473 std::string bool_string(const bool value)
474 {
475  std::ostringstream ss;
476  ss << std::boolalpha << value;
477 
478  return ss.str();
479 }
480 
481 std::string signed_value(int val)
482 {
483  std::ostringstream oss;
484  oss << (val >= 0 ? "+" : font::unicode_minus) << std::abs(val);
485  return oss.str();
486 }
487 
488 std::string half_signed_value(int val)
489 {
490  std::ostringstream oss;
491  if (val < 0)
492  oss << font::unicode_minus;
493  oss << std::abs(val);
494  return oss.str();
495 }
496 
497 static void si_string_impl_stream_write(std::stringstream &ss, double input) {
498  std::streamsize oldprec = ss.precision();
499 #ifdef _MSC_VER
500  // For MSVC, default mode misbehaves, so we use fixed instead.
501  ss.precision(1);
502  ss << std::fixed
503  << input;
504 #else
505  // In default mode, precision sets the number of significant figures.
506 
507  // 999.5 and above will render as 1000+, however, only numbers above 1000 will use 4 digits
508  // Rounding everything from 100 up (at which point we stop using decimals anyway) avoids this.
509  if (input >= 100) {
510  input = std::round(input);
511  }
512 
513  // When in binary mode, numbers of up to 1023.9999 can be passed
514  // We should render those with 4 digits, instead of as 1e+3.
515  // Input should be an integer number now, but doubles can do strange things, so check the halfway point instead.
516  if (input >= 999.5) {
517  ss.precision(4);
518  } else {
519  ss.precision(3);
520  }
521  ss << input;
522 #endif
523  ss.precision(oldprec);
524 }
525 
526 std::string si_string(double input, bool base2, const std::string& unit) {
527  const double multiplier = base2 ? 1024 : 1000;
528 
529  typedef std::array<std::string, 9> strings9;
530 
531  if(input < 0){
532  return font::unicode_minus + si_string(std::abs(input), base2, unit);
533  }
534 
535  strings9 prefixes;
536  strings9::const_iterator prefix;
537  if (input == 0.0) {
538  strings9 tmp { { "","","","","","","","","" } };
539  prefixes = tmp;
540  prefix = prefixes.begin();
541  } else if (input < 1.0) {
542  strings9 tmp { {
543  "",
544  _("prefix_milli^m"),
545  _("prefix_micro^µ"),
546  _("prefix_nano^n"),
547  _("prefix_pico^p"),
548  _("prefix_femto^f"),
549  _("prefix_atto^a"),
550  _("prefix_zepto^z"),
551  _("prefix_yocto^y")
552  } };
553  prefixes = tmp;
554  prefix = prefixes.begin();
555  while (input < 1.0 && *prefix != prefixes.back()) {
556  input *= multiplier;
557  ++prefix;
558  }
559  } else {
560  strings9 tmp { {
561  "",
562  (base2 ?
563  _("prefix_kibi^K") :
564  _("prefix_kilo^k")
565  ),
566  _("prefix_mega^M"),
567  _("prefix_giga^G"),
568  _("prefix_tera^T"),
569  _("prefix_peta^P"),
570  _("prefix_exa^E"),
571  _("prefix_zetta^Z"),
572  _("prefix_yotta^Y")
573  } };
574  prefixes = tmp;
575  prefix = prefixes.begin();
576  while (input > multiplier && *prefix != prefixes.back()) {
577  input /= multiplier;
578  ++prefix;
579  }
580  }
581 
582  std::stringstream ss;
583  si_string_impl_stream_write(ss, input);
584  ss << ' '
585  << *prefix
586  << (base2 && (!(*prefix).empty()) ? _("infix_binary^i") : "")
587  << unit;
588  return ss.str();
589 }
590 
591 static bool is_username_char(char c) {
592  return ((c == '_') || (c == '-'));
593 }
594 
595 static bool is_wildcard_char(char c) {
596  return ((c == '?') || (c == '*'));
597 }
598 
599 bool isvalid_username(const std::string& username) {
600  const std::size_t alnum = std::count_if(username.begin(), username.end(), isalnum);
601  const std::size_t valid_char =
602  std::count_if(username.begin(), username.end(), is_username_char);
603  if ((alnum + valid_char != username.size())
604  || valid_char == username.size() || username.empty() )
605  {
606  return false;
607  }
608  return true;
609 }
610 
611 bool isvalid_wildcard(const std::string& username) {
612  const std::size_t alnum = std::count_if(username.begin(), username.end(), isalnum);
613  const std::size_t valid_char =
614  std::count_if(username.begin(), username.end(), is_username_char);
615  const std::size_t wild_char =
616  std::count_if(username.begin(), username.end(), is_wildcard_char);
617  if ((alnum + valid_char + wild_char != username.size())
618  || valid_char == username.size() || username.empty() )
619  {
620  return false;
621  }
622  return true;
623 }
624 
625 
626 bool word_completion(std::string& text, std::vector<std::string>& wordlist) {
627  std::vector<std::string> matches;
628  const std::size_t last_space = text.rfind(" ");
629  // If last character is a space return.
630  if (last_space == text.size() -1) {
631  wordlist = matches;
632  return false;
633  }
634 
635  bool text_start;
636  std::string semiword;
637  if (last_space == std::string::npos) {
638  text_start = true;
639  semiword = text;
640  } else {
641  text_start = false;
642  semiword.assign(text, last_space + 1, text.size());
643  }
644 
645  std::string best_match = semiword;
646  for (std::vector<std::string>::const_iterator word = wordlist.begin();
647  word != wordlist.end(); ++word)
648  {
649  if (word->size() < semiword.size()
650  || !std::equal(semiword.begin(), semiword.end(), word->begin(),
652  {
653  continue;
654  }
655  if (matches.empty()) {
656  best_match = *word;
657  } else {
658  int j = 0;
659  while (toupper(best_match[j]) == toupper((*word)[j])) j++;
660  if (best_match.begin() + j < best_match.end()) {
661  best_match.erase(best_match.begin() + j, best_match.end());
662  }
663  }
664  matches.push_back(*word);
665  }
666  if(!matches.empty()) {
667  text.replace(last_space + 1, best_match.size(), best_match);
668  }
669  wordlist = matches;
670  return text_start;
671 }
672 
673 static bool is_word_boundary(char c) {
674  return (c == ' ' || c == ',' || c == ':' || c == '\'' || c == '"' || c == '-');
675 }
676 
677 bool word_match(const std::string& message, const std::string& word) {
678  std::size_t first = message.find(word);
679  if (first == std::string::npos) return false;
680  if (first == 0 || is_word_boundary(message[first - 1])) {
681  std::size_t next = first + word.size();
682  if (next == message.size() || is_word_boundary(message[next])) {
683  return true;
684  }
685  }
686  return false;
687 }
688 
689 bool wildcard_string_match(const std::string& str, const std::string& match) {
690  const bool wild_matching = (!match.empty() && (match[0] == '*' || match[0] == '+'));
691  const std::string::size_type solid_begin = match.find_first_not_of("*+");
692  const bool have_solids = (solid_begin != std::string::npos);
693  // Check the simple cases first
694  if(!have_solids) {
695  const std::string::size_type plus_count = std::count(match.begin(), match.end(), '+');
696  return match.empty() ? str.empty() : str.length() >= plus_count;
697  } else if(str.empty()) {
698  return false;
699  }
700 
701  const std::string::size_type solid_end = match.find_first_of("*+", solid_begin);
702  const std::string::size_type solid_len = (solid_end == std::string::npos)
703  ? match.length() - solid_begin : solid_end - solid_begin;
704  // Since + always consumes at least one character, increment current if the match
705  // begins with one
706  std::string::size_type current = match[0] == '+' ? 1 : 0;
707  bool matches;
708  do {
709  matches = true;
710  // Now try to place the str into the solid space
711  const std::string::size_type test_len = str.length() - current;
712  for(std::string::size_type i=0; i < solid_len && matches; ++i) {
713  char solid_c = match[solid_begin + i];
714  if(i > test_len || !(solid_c == '?' || solid_c == str[current+i])) {
715  matches = false;
716  }
717  }
718  if(matches) {
719  // The solid space matched, now consume it and attempt to find more
720  const std::string consumed_match = (solid_begin+solid_len < match.length())
721  ? match.substr(solid_end) : "";
722  const std::string consumed_str = (solid_len < test_len)
723  ? str.substr(current+solid_len) : "";
724  matches = wildcard_string_match(consumed_str, consumed_match);
725  }
726  } while(wild_matching && !matches && ++current < str.length());
727  return matches;
728 }
729 
730 std::string indent(const std::string& string, std::size_t indent_size)
731 {
732  if(indent_size == 0) {
733  return string;
734  }
735 
736  const std::string indent(indent_size, ' ');
737 
738  if(string.empty()) {
739  return indent;
740  }
741 
742  const std::vector<std::string>& lines = split(string, '\x0A', 0);
743  std::string res;
744 
745  for(std::size_t lno = 0; lno < lines.size();) {
746  const std::string& line = lines[lno];
747 
748  // Lines containing only a carriage return count as empty.
749  if(!line.empty() && line != "\x0D") {
750  res += indent;
751  }
752 
753  res += line;
754 
755  if(++lno < lines.size()) {
756  res += '\x0A';
757  }
758  }
759 
760  return res;
761 }
762 
763 std::vector<std::string> quoted_split(const std::string& val, char c, int flags, char quote)
764 {
765  std::vector<std::string> res;
766 
767  std::string::const_iterator i1 = val.begin();
768  std::string::const_iterator i2 = val.begin();
769 
770  while (i2 != val.end()) {
771  if (*i2 == quote) {
772  // Ignore quoted character
773  ++i2;
774  if (i2 != val.end()) ++i2;
775  } else if (*i2 == c) {
776  std::string new_val(i1, i2);
777  if (flags & STRIP_SPACES)
778  boost::trim(new_val);
779  if (!(flags & REMOVE_EMPTY) || !new_val.empty())
780  res.push_back(std::move(new_val));
781  ++i2;
782  if (flags & STRIP_SPACES) {
783  while(i2 != val.end() && *i2 == ' ')
784  ++i2;
785  }
786 
787  i1 = i2;
788  } else {
789  ++i2;
790  }
791  }
792 
793  std::string new_val(i1, i2);
794  if (flags & STRIP_SPACES)
795  boost::trim(new_val);
796  if (!(flags & REMOVE_EMPTY) || !new_val.empty())
797  res.push_back(new_val);
798 
799  return res;
800 }
801 
802 std::pair<int, int> parse_range(const std::string& str)
803 {
804  const std::string::const_iterator dash = std::find(str.begin(), str.end(), '-');
805  const std::string a(str.begin(), dash);
806  const std::string b = dash != str.end() ? std::string(dash + 1, str.end()) : a;
807  std::pair<int,int> res {0,0};
808  try {
809  if (b == "infinity") {
810  res = std::make_pair(std::stoi(a), std::numeric_limits<int>::max());
811  } else {
812  res = std::make_pair(std::stoi(a), std::stoi(b));
813  }
814 
815  if (res.second < res.first) {
816  res.second = res.first;
817  }
818  } catch(const std::invalid_argument&) {
819  ERR_GENERAL << "Invalid range: "<< str << std::endl;
820  }
821 
822  return res;
823 }
824 
825 std::vector<std::pair<int, int>> parse_ranges(const std::string& str)
826 {
827  std::vector<std::pair<int, int>> to_return;
828  for(const std::string& r : utils::split(str)) {
829  to_return.push_back(parse_range(r));
830  }
831 
832  return to_return;
833 }
834 
835 void ellipsis_truncate(std::string& str, const std::size_t size)
836 {
837  const std::size_t prev_size = str.length();
838 
839  utf8::truncate(str, size);
840 
841  if(str.length() != prev_size) {
842  str += font::ellipsis;
843  }
844 }
845 
846 } // end namespace utils
bool isvalid_wildcard(const std::string &username)
Check if the username pattern contains only valid characters.
std::pair< int, int > parse_range(const std::string &str)
std::string urlencode(const std::string &str)
Percent-escape characters in a UTF-8 string intended to be part of a URL.
bool isvalid_username(const std::string &username)
Check if the username contains only valid characters.
This class represents a single unit of a specific type.
Definition: unit.hpp:99
#define a
std::string unescape(const std::string &str)
Remove all escape characters (backslash)
bool wildcard_string_match(const std::string &str, const std::string &match)
Match using &#39;*&#39; as any number of characters (including none), &#39;+&#39; as one or more characters, and &#39;?&#39; as any one character.
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. ...
#define ERR_GENERAL
bool notspace(const char c)
static bool is_username_char(char c)
bool chars_equal_insensitive(char a, char b)
Definition: general.hpp:21
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.
std::string quote(const std::string &str)
Surround the string &#39;str&#39; with double quotes.
std::vector< std::string > split(const std::string &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
#define b
std::string half_signed_value(int val)
Sign with Unicode "−" if negative.
int div100rounded(int num)
Guarantees portable results for division by 100; round half up, to the nearest integer.
Definition: math.hpp:37
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:91
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
const std::string unicode_minus
Definition: constants.cpp:38
std::vector< std::pair< int, int > > parse_ranges(const std::string &str)
const std::string ellipsis
Definition: constants.cpp:35
std::string si_string(double input, bool base2, const std::string &unit)
Convert into a string with an SI-postfix.
static bool is_wildcard_char(char c)
REMOVE_EMPTY: remove empty elements.
std::string & truncate(std::string &str, const std::size_t size)
Truncates a UTF-8 string to the specified number of characters.
Definition: unicode.cpp:117
lu_byte right
Definition: lparser.cpp:1027
std::string escape(const std::string &str, const char *special_chars)
Prepends a configurable set of characters with a backslash.
std::size_t i
Definition: function.cpp:933
mock_party p
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...
std::string bool_string(const bool value)
Converts a bool value to &#39;true&#39; or &#39;false&#39;.
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 isnewline(const char c)
#define next(ls)
Definition: llex.cpp:32
static lg::log_domain log_engine("engine")
bool find(E event, F functor)
Tests whether an event handler is available.
int apply_modifier(const int number, const std::string &amount, const int minimum)
lu_byte left
Definition: lparser.cpp:1026
static bool is_word_boundary(char c)
std::string indent(const std::string &string, std::size_t indent_size)
Indent a block of text.
Standard logging facilities (interface).
static const char * match(MatchState *ms, const char *s, const char *p)
Definition: lstrlib.cpp:425
bool portable_isspace(const char c)
std::string signed_value(int val)
Convert into a signed value (using the Unicode "−" and +0 convention.
mock_char c
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
bool word_completion(std::string &text, std::vector< std::string > &wordlist)
Try to complete the last word of &#39;text&#39; with the &#39;wordlist&#39;.
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< std::string, unsigned > item
Definition: help_impl.hpp:371
static void si_string_impl_stream_write(std::stringstream &ss, double input)
bool word_match(const std::string &message, const std::string &word)
Check if a message contains a word.
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...