The Battle for Wesnoth  1.13.11+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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 http://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  size_t size_square_exp = 0;
133  for (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  size_t found_tilde = piece.find_first_of('~');
138  if (found_tilde == std::string::npos) {
139  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  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 (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  size_t j = 0;
194  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 (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  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(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  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(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 std::pair<string_view, string_view> vertical_split(const std::string& val)
386 {
387  // Count the number of lines.
388  int num_lines = std::count(val.begin(), val.end(), '\n') + 1;
389 
390  if(num_lines < 2) {
391  throw std::logic_error("utils::vertical_split: the string contains only one line");
392  }
393 
394  // Split the string at the point where we have encountered
395  // (number of lines / 2 - 1) line separators.
396  int split_point = 0;
397  int num_found_line_separators = 0;
398  for(size_t i = 0; i < val.size(); ++i) {
399  if(val[i] == '\n') {
400  ++num_found_line_separators;
401  if(num_found_line_separators >= num_lines / 2 - 1) {
402  split_point = i;
403  break;
404  }
405  }
406  }
407 
408  assert(split_point != 0);
409 
410  return { string_view(val.data(), split_point),
411  string_view(&val[split_point + 1], val.size() - (split_point + 1)) };
412 }
413 
414 // Modify a number by string representing integer difference, or optionally %
415 int apply_modifier( const int number, const std::string &amount, const int minimum ) {
416  // wassert( amount.empty() == false );
417  int value = 0;
418  try {
419  value = std::stoi(amount);
420  } catch(std::invalid_argument&) {}
421  if(amount[amount.size()-1] == '%') {
422  value = div100rounded(number * value);
423  }
424  value += number;
425  if (( minimum > 0 ) && ( value < minimum ))
426  value = minimum;
427  return value;
428 }
429 
430 std::string escape(const std::string &str, const char *special_chars)
431 {
432  std::string::size_type pos = str.find_first_of(special_chars);
433  if (pos == std::string::npos) {
434  // Fast path, possibly involving only reference counting.
435  return str;
436  }
437  std::string res = str;
438  do {
439  res.insert(pos, 1, '\\');
440  pos = res.find_first_of(special_chars, pos + 2);
441  } while (pos != std::string::npos);
442  return res;
443 }
444 
446 {
447  std::string::size_type pos = str.find('\\');
448  if (pos == std::string::npos) {
449  // Fast path, possibly involving only reference counting.
450  return str;
451  }
452  std::string res = str;
453  do {
454  res.erase(pos, 1);
455  pos = res.find('\\', pos + 1);
456  } while (pos != std::string::npos);
457  return str;
458 }
459 
461 {
462  static const std::string nonresv_str =
463  "-."
464  "0123456789"
465  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
466  "_"
467  "abcdefghijklmnopqrstuvwxyz"
468  "~";
469  static const std::set<char> nonresv(nonresv_str.begin(), nonresv_str.end());
470 
471  std::ostringstream res;
472  res << std::hex;
473  res.fill('0');
474 
475  for(char c : str) {
476  if(nonresv.count(c) != 0) {
477  res << c;
478  continue;
479  }
480 
481  res << '%';
482  res.width(2);
483  res << int(c);
484  }
485 
486  return res.str();
487 }
488 
489 bool string_bool(const std::string& str, bool def) {
490  if (str.empty()) return def;
491 
492  // yes/no is the standard, test it first
493  if (str == "yes") return true;
494  if (str == "no"|| str == "false" || str == "off" || str == "0" || str == "0.0")
495  return false;
496 
497  // all other non-empty string are considered as true
498  return true;
499 }
500 
501 std::string bool_string(const bool value)
502 {
503  std::ostringstream ss;
504  ss << std::boolalpha << value;
505 
506  return ss.str();
507 }
508 
510 {
511  std::ostringstream oss;
512  oss << (val >= 0 ? "+" : font::unicode_minus) << std::abs(val);
513  return oss.str();
514 }
515 
517 {
518  std::ostringstream oss;
519  if (val < 0)
520  oss << font::unicode_minus;
521  oss << std::abs(val);
522  return oss.str();
523 }
524 
525 static void si_string_impl_stream_write(std::stringstream &ss, double input) {
526  std::streamsize oldprec = ss.precision();
527 #ifdef _MSC_VER
528  // Visual C++ makes 'precision' set the number of decimal places.
529  // Other platforms make it set the number of significant figures
530  ss.precision(1);
531  ss << std::fixed
532  << input;
533 #else
534  // Workaround to display 1023 KiB instead of 1.02e3 KiB
535  if (input >= 1000)
536  ss.precision(4);
537  else
538  ss.precision(3);
539  ss << input;
540 #endif
541  ss.precision(oldprec);
542 }
543 
544 std::string si_string(double input, bool base2, const std::string& unit) {
545  const double multiplier = base2 ? 1024 : 1000;
546 
547  typedef std::array<std::string, 9> strings9;
548 
549  strings9 prefixes;
550  strings9::const_iterator prefix;
551  if (input == 0.0) {
552  strings9 tmp { { "","","","","","","","","" } };
553  prefixes = tmp;
554  prefix = prefixes.begin();
555  } else if (input < 1.0) {
556  strings9 tmp { {
557  "",
558  _("prefix_milli^m"),
559  _("prefix_micro^µ"),
560  _("prefix_nano^n"),
561  _("prefix_pico^p"),
562  _("prefix_femto^f"),
563  _("prefix_atto^a"),
564  _("prefix_zepto^z"),
565  _("prefix_yocto^y")
566  } };
567  prefixes = tmp;
568  prefix = prefixes.begin();
569  while (input < 1.0 && *prefix != prefixes.back()) {
570  input *= multiplier;
571  ++prefix;
572  }
573  } else {
574  strings9 tmp { {
575  "",
576  (base2 ?
577  _("prefix_kibi^K") :
578  _("prefix_kilo^k")
579  ),
580  _("prefix_mega^M"),
581  _("prefix_giga^G"),
582  _("prefix_tera^T"),
583  _("prefix_peta^P"),
584  _("prefix_exa^E"),
585  _("prefix_zetta^Z"),
586  _("prefix_yotta^Y")
587  } };
588  prefixes = tmp;
589  prefix = prefixes.begin();
590  while (input > multiplier && *prefix != prefixes.back()) {
591  input /= multiplier;
592  ++prefix;
593  }
594  }
595 
596  std::stringstream ss;
597  si_string_impl_stream_write(ss, input);
598  ss << ' '
599  << *prefix
600  << (base2 && (!(*prefix).empty()) ? _("infix_binary^i") : "")
601  << unit;
602  return ss.str();
603 }
604 
605 static bool is_username_char(char c) {
606  return ((c == '_') || (c == '-'));
607 }
608 
609 static bool is_wildcard_char(char c) {
610  return ((c == '?') || (c == '*'));
611 }
612 
613 bool isvalid_username(const std::string& username) {
614  const size_t alnum = std::count_if(username.begin(), username.end(), isalnum);
615  const size_t valid_char =
616  std::count_if(username.begin(), username.end(), is_username_char);
617  if ((alnum + valid_char != username.size())
618  || valid_char == username.size() || username.empty() )
619  {
620  return false;
621  }
622  return true;
623 }
624 
625 bool isvalid_wildcard(const std::string& username) {
626  const size_t alnum = std::count_if(username.begin(), username.end(), isalnum);
627  const size_t valid_char =
628  std::count_if(username.begin(), username.end(), is_username_char);
629  const size_t wild_char =
630  std::count_if(username.begin(), username.end(), is_wildcard_char);
631  if ((alnum + valid_char + wild_char != username.size())
632  || valid_char == username.size() || username.empty() )
633  {
634  return false;
635  }
636  return true;
637 }
638 
639 
640 bool word_completion(std::string& text, std::vector<std::string>& wordlist) {
641  std::vector<std::string> matches;
642  const size_t last_space = text.rfind(" ");
643  // If last character is a space return.
644  if (last_space == text.size() -1) {
645  wordlist = matches;
646  return false;
647  }
648 
649  bool text_start;
650  std::string semiword;
651  if (last_space == std::string::npos) {
652  text_start = true;
653  semiword = text;
654  } else {
655  text_start = false;
656  semiword.assign(text, last_space + 1, text.size());
657  }
658 
659  std::string best_match = semiword;
660  for (std::vector<std::string>::const_iterator word = wordlist.begin();
661  word != wordlist.end(); ++word)
662  {
663  if (word->size() < semiword.size()
664  || !std::equal(semiword.begin(), semiword.end(), word->begin(),
666  {
667  continue;
668  }
669  if (matches.empty()) {
670  best_match = *word;
671  } else {
672  int j = 0;
673  while (toupper(best_match[j]) == toupper((*word)[j])) j++;
674  if (best_match.begin() + j < best_match.end()) {
675  best_match.erase(best_match.begin() + j, best_match.end());
676  }
677  }
678  matches.push_back(*word);
679  }
680  if(!matches.empty()) {
681  text.replace(last_space + 1, best_match.size(), best_match);
682  }
683  wordlist = matches;
684  return text_start;
685 }
686 
687 static bool is_word_boundary(char c) {
688  return (c == ' ' || c == ',' || c == ':' || c == '\'' || c == '"' || c == '-');
689 }
690 
691 bool word_match(const std::string& message, const std::string& word) {
692  size_t first = message.find(word);
693  if (first == std::string::npos) return false;
694  if (first == 0 || is_word_boundary(message[first - 1])) {
695  size_t next = first + word.size();
696  if (next == message.size() || is_word_boundary(message[next])) {
697  return true;
698  }
699  }
700  return false;
701 }
702 
704  const bool wild_matching = (!match.empty() && match[0] == '*');
705  const std::string::size_type solid_begin = match.find_first_not_of('*');
706  const bool have_solids = (solid_begin != std::string::npos);
707  // Check the simple case first
708  if(str.empty() || !have_solids) {
709  return wild_matching || str == match;
710  }
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;
714  std::string::size_type current = 0;
715  bool matches;
716  do {
717  matches = true;
718  // Now try to place the str into the solid space
719  const std::string::size_type test_len = str.length() - current;
720  for(std::string::size_type i=0; i < solid_len && matches; ++i) {
721  char solid_c = match[solid_begin + i];
722  if(i > test_len || !(solid_c == '?' || solid_c == str[current+i])) {
723  matches = false;
724  }
725  }
726  if(matches) {
727  // The solid space matched, now consume it and attempt to find more
728  const std::string consumed_match = (solid_begin+solid_len < match.length())
729  ? match.substr(solid_end) : "";
730  const std::string consumed_str = (solid_len < test_len)
731  ? str.substr(current+solid_len) : "";
732  matches = wildcard_string_match(consumed_str, consumed_match);
733  }
734  } while(wild_matching && !matches && ++current < str.length());
735  return matches;
736 }
737 
738 std::string indent(const std::string& string, size_t indent_size)
739 {
740  if(indent_size == 0) {
741  return string;
742  }
743 
744  const std::string indent(indent_size, ' ');
745 
746  if(string.empty()) {
747  return indent;
748  }
749 
750  const std::vector<std::string>& lines = split(string, '\x0A', 0);
751  std::string res;
752 
753  for(size_t lno = 0; lno < lines.size();) {
754  const std::string& line = lines[lno];
755 
756  // Lines containing only a carriage return count as empty.
757  if(!line.empty() && line != "\x0D") {
758  res += indent;
759  }
760 
761  res += line;
762 
763  if(++lno < lines.size()) {
764  res += '\x0A';
765  }
766  }
767 
768  return res;
769 }
770 
771 std::vector<std::string> quoted_split(const std::string& val, char c, int flags, char quote)
772 {
773  std::vector<std::string> res;
774 
775  std::string::const_iterator i1 = val.begin();
776  std::string::const_iterator i2 = val.begin();
777 
778  while (i2 != val.end()) {
779  if (*i2 == quote) {
780  // Ignore quoted character
781  ++i2;
782  if (i2 != val.end()) ++i2;
783  } else if (*i2 == c) {
784  std::string new_val(i1, i2);
785  if (flags & STRIP_SPACES)
786  boost::trim(new_val);
787  if (!(flags & REMOVE_EMPTY) || !new_val.empty())
788  res.push_back(std::move(new_val));
789  ++i2;
790  if (flags & STRIP_SPACES) {
791  while(i2 != val.end() && *i2 == ' ')
792  ++i2;
793  }
794 
795  i1 = i2;
796  } else {
797  ++i2;
798  }
799  }
800 
801  std::string new_val(i1, i2);
802  if (flags & STRIP_SPACES)
803  boost::trim(new_val);
804  if (!(flags & REMOVE_EMPTY) || !new_val.empty())
805  res.push_back(new_val);
806 
807  return res;
808 }
809 
810 std::pair<int, int> parse_range(const std::string& str)
811 {
812  const std::string::const_iterator dash = std::find(str.begin(), str.end(), '-');
813  const std::string a(str.begin(), dash);
814  const std::string b = dash != str.end() ? std::string(dash + 1, str.end()) : a;
815  std::pair<int,int> res {0,0};
816  try {
817  res = std::make_pair(std::stoi(a), std::stoi(b));
818  if (res.second < res.first) {
819  res.second = res.first;
820  }
821  } catch(std::invalid_argument&) {}
822 
823  return res;
824 }
825 
826 std::vector<std::pair<int, int>> parse_ranges(const std::string& str)
827 {
828  std::vector< std::pair< int, int >> to_return;
829  std::vector<std::string> strs = utils::split(str);
830  std::vector<std::string>::const_iterator i, i_end=strs.end();
831  for(i = strs.begin(); i != i_end; ++i) {
832  to_return.push_back(parse_range(*i));
833  }
834  return to_return;
835 }
836 
837 
838 void ellipsis_truncate(std::string& str, const size_t size)
839 {
840  const size_t prev_size = str.length();
841 
842  utf8::truncate(str, size);
843 
844  if(str.length() != prev_size) {
845  str += font::ellipsis;
846  }
847 }
848 
849 } // 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::vector< char_t > string
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:100
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< string_view, string_view > vertical_split(const std::string &val)
Splits a string into two parts as evenly as possible based on lines.
#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 '*' as any number of characters (including none), and '?' as any one character.
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...
#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
basic_string_view< char, std::char_traits< char > > string_view
std::string quote(const std::string &str)
Surround the string 'str' 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
void ellipsis_truncate(std::string &str, const size_t size)
Truncates a string to a given utf-8 character count and then appends an ellipsis. ...
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:38
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:89
const std::string unicode_minus
Definition: constants.cpp:37
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.
utf8::string & truncate(utf8::string &str, const 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 indent(const std::string &string, size_t indent_size)
Indent a block of text.
std::string escape(const std::string &str, const char *special_chars)
Prepends a configurable set of characters with a backslash.
size_t size(const utf8::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
mock_party p
std::string bool_string(const bool value)
Converts a bool value to 'true' or 'false'.
size_t i
Definition: function.cpp:933
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.
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 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)
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 'text' with the 'wordlist'.
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...