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