Coverage Report

Created: 2026-07-15 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wt/src/web/WebUtils.C
Line
Count
Source
1
/*
2
 * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3
 *
4
 * See the LICENSE file for terms of use.
5
 */
6
7
#include "WebUtils.h"
8
#include "DomElement.h"
9
#include "thirdparty/rapidxml/rapidxml.hpp"
10
#include "Wt/WException.h"
11
#include "Wt/WString.h"
12
#include "Wt/WStringUtil.h"
13
#include "Wt/Utils.h"
14
15
#include <boost/algorithm/string.hpp>
16
#include <boost/version.hpp>
17
18
#include <cctype>
19
#include <cfloat>
20
#include <cstdio>
21
#include <fstream>
22
#include <iomanip>
23
#include <regex>
24
#include <unordered_map>
25
26
#ifdef WT_WIN32
27
#define WIN32_LEAN_AND_MEAN
28
#define NOMINMAX
29
#include <windows.h>
30
#define snprintf _snprintf
31
#else
32
#include <cstdlib>
33
#endif // WIN32
34
35
#if !defined(WT_NO_SPIRIT) && BOOST_VERSION >= 104700 && (BOOST_VERSION < 107600 || BOOST_VERSION >= 107900)
36
#  define SPIRIT_FLOAT_FORMAT
37
#endif
38
39
#ifdef SPIRIT_FLOAT_FORMAT
40
#include <boost/config/warning_disable.hpp>
41
#include <boost/spirit/include/karma.hpp>
42
#else
43
#include <boost/math/special_functions/fpclassify.hpp>
44
#include <boost/math/special_functions/sign.hpp>
45
#endif // SPIRIT_FLOAT_FORMAT
46
47
#include <boost/spirit/include/qi_parse.hpp>
48
#include <boost/spirit/include/qi_numeric.hpp>
49
50
// Qnx gcc 4.4.2
51
#ifdef isnan
52
#undef isnan
53
#endif
54
#ifdef isinf
55
#undef isinf
56
#endif
57
58
namespace Wt {
59
  namespace Utils {
60
61
std::string append(const std::string& s, char c)
62
0
{
63
0
  if (s.empty() || s[s.length() - 1] != c)
64
0
    return s + c;
65
0
  else
66
0
    return s;
67
0
}
68
69
std::string prepend(const std::string& s, char c)
70
0
{
71
0
  if (s.empty() || s[0] != c)
72
0
    return c + s;
73
0
  else
74
0
    return s;
75
0
}
76
77
std::string& replace(std::string& s, char c, const std::string& r)
78
0
{
79
0
  std::string::size_type p = 0;
80
81
0
  while ((p = s.find(c, p)) != std::string::npos) {
82
0
    s.replace(p, 1, r);
83
0
    p += r.length();
84
0
  }
85
86
0
  return s;
87
0
}
88
89
std::string& replace(std::string& s, const std::string& k, const std::string& r)
90
0
{
91
0
  std::string::size_type p = 0;
92
93
0
  while ((p = s.find(k, p)) != std::string::npos) {
94
0
    s.replace(p, k.length(), r);
95
0
    p += r.length();
96
0
  }
97
98
0
  return s;
99
0
}
100
101
std::string lowerCase(const std::string& s)
102
134k
{
103
134k
  std::string result = s;
104
5.81M
  for (unsigned i = 0; i < result.length(); ++i)
105
5.68M
    result[i] = tolower(result[i]);
106
134k
  return result;
107
134k
}
108
109
void sanitizeUnicode(EscapeOStream& sout, const std::string& text)
110
0
{
111
0
  char buf[4];
112
113
0
  for (const char *c = text.c_str(); *c;) {
114
0
    char *b = buf;
115
    // but copy_check_utf8() does not declare the following ranges illegal:
116
    //  U+D800-U+DFFF
117
    //  U+FFFE-U+FFFF
118
0
    Wt::rapidxml::xml_document<>::copy_check_utf8(c, b);
119
0
    for (char *i = buf; i < b; ++i)
120
0
      sout << *i;
121
0
  }
122
0
}
123
124
std::string eraseWord(const std::string& s, const std::string& w)
125
0
{
126
0
  std::string::size_type p;
127
0
  std::string::size_type pos = 0;
128
129
0
  while ((p = s.find(w, pos)) != std::string::npos) {
130
0
    std::string::size_type e = p + w.length();
131
0
    if ((p == 0          || s[p-1] == ' ') &&
132
0
        (e == s.length() || s[e] == ' ')) {
133
0
      std::string ss = s;
134
0
      ss.erase(ss.begin() + p, ss.begin() + e);
135
0
      if (p > 1)
136
0
        ss.erase(ss.begin() + (p - 1));
137
0
      else if (e < ss.length())
138
0
        ss.erase(ss.begin() + p);
139
140
0
      return ss;
141
0
    }
142
143
0
    pos = p + 1;
144
0
  }
145
146
0
  return s;
147
0
}
148
149
std::string addWord(const std::string& s, const std::string& w)
150
0
{
151
0
  if (s.empty())
152
0
    return w;
153
0
  else
154
0
    return s + ' ' + w;
155
0
}
156
157
4.59k
char *itoa(int value, char *result, int base) {
158
4.59k
  char* out = result;
159
4.59k
  int quotient = value;
160
161
4.59k
  if (quotient < 0)
162
0
    quotient = -quotient;
163
164
13.9k
  do {
165
13.9k
    *out =
166
13.9k
      "0123456789abcdefghijklmnopqrstuvwxyz"[quotient % base];
167
13.9k
    ++out;
168
13.9k
    quotient /= base;
169
13.9k
  } while (quotient);
170
171
4.59k
  if (value < 0 && base == 10)
172
0
    *out++ = '-';
173
174
4.59k
  std::reverse(result, out);
175
4.59k
  *out = 0;
176
177
4.59k
  return result;
178
4.59k
}
179
180
0
char *utoa(unsigned int value, char* result, int base) {
181
0
  char* out = result;
182
0
  unsigned int quotient = value;
183
184
0
  do {
185
0
    *out =
186
0
      "0123456789abcdefghijklmnopqrstuvwxyz"[quotient % base];
187
0
    ++out;
188
0
    quotient /= base;
189
0
  } while (quotient);
190
191
0
  std::reverse(result, out);
192
0
  *out = 0;
193
194
0
  return result;
195
0
}
196
197
0
char *lltoa(long long value, char *result, int base) {
198
0
  char* out = result;
199
0
  long long quotient = value;
200
201
0
  if (quotient < 0)
202
0
    quotient = -quotient;
203
204
0
  do {
205
0
    *out =
206
0
      "0123456789abcdefghijklmnopqrstuvwxyz"[ quotient % base ];
207
0
    ++out;
208
0
    quotient /= base;
209
0
  } while (quotient);
210
211
0
  if (value < 0 && base == 10)
212
0
    *out++ = '-';
213
0
  std::reverse(result, out);
214
0
  *out = 0;
215
216
0
  return result;
217
0
}
218
219
0
char *pad_itoa(int value, int length, char *result) {
220
0
  static const int exp[] = { 1, 10, 100, 1000, 10000, 100000, 100000 };
221
222
0
  result[length] = 0;
223
224
0
  for (int i = 0; i < length; ++i) {
225
0
    int b = exp[length - i - 1];
226
0
    if (value >= b)
227
0
      result[i] = '0' + (value / b) % 10;
228
0
    else
229
0
      result[i] = '0';
230
0
  }
231
232
0
  return result;
233
0
}
234
235
#ifdef SPIRIT_FLOAT_FORMAT
236
namespace {
237
  using namespace boost::spirit;
238
  using namespace boost::spirit::karma;
239
240
  // adjust rendering for JS flaots
241
  template <typename T, int Precision>
242
  struct JavaScriptPolicy : karma::real_policies<T>
243
  {
244
    // not 'nan', but 'NaN'
245
    template <typename CharEncoding, typename Tag, typename OutputIterator>
246
    static bool nan (OutputIterator& sink, WT_MAYBE_UNUSED T n, WT_MAYBE_UNUSED bool forceSign)
247
0
    {
248
0
      return string_inserter<CharEncoding, Tag>::call(sink, "NaN");
249
0
    }
Unexecuted instantiation: WebUtils.C:bool Wt::Utils::(anonymous namespace)::JavaScriptPolicy<double, 7>::nan<boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::karma::detail::output_iterator<char*, mpl_::int_<0>, boost::spirit::unused_type> >(boost::spirit::karma::detail::output_iterator<char*, mpl_::int_<0>, boost::spirit::unused_type>&, double, bool)
Unexecuted instantiation: WebUtils.C:bool Wt::Utils::(anonymous namespace)::JavaScriptPolicy<double, 15>::nan<boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::karma::detail::output_iterator<char*, mpl_::int_<0>, boost::spirit::unused_type> >(boost::spirit::karma::detail::output_iterator<char*, mpl_::int_<0>, boost::spirit::unused_type>&, double, bool)
250
251
    // not 'inf', but 'Infinity'
252
    template <typename CharEncoding, typename Tag, typename OutputIterator>
253
    static bool inf (OutputIterator& sink, T n, bool force_sign)
254
0
    {
255
0
      return sign_inserter::call(sink, false, (n<0), force_sign) &&
256
0
        string_inserter<CharEncoding, Tag>::call(sink, "Infinity");
257
0
    }
Unexecuted instantiation: WebUtils.C:bool Wt::Utils::(anonymous namespace)::JavaScriptPolicy<double, 7>::inf<boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::karma::detail::output_iterator<char*, mpl_::int_<0>, boost::spirit::unused_type> >(boost::spirit::karma::detail::output_iterator<char*, mpl_::int_<0>, boost::spirit::unused_type>&, double, bool)
Unexecuted instantiation: WebUtils.C:bool Wt::Utils::(anonymous namespace)::JavaScriptPolicy<double, 15>::inf<boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::karma::detail::output_iterator<char*, mpl_::int_<0>, boost::spirit::unused_type> >(boost::spirit::karma::detail::output_iterator<char*, mpl_::int_<0>, boost::spirit::unused_type>&, double, bool)
258
259
0
    static int floatfield(T t) {
260
0
      return (t != 0.0) && ((std::abs(t) < 0.001) || (std::abs(t) > 1E8)) ?
261
0
        karma::real_policies<T>::fmtflags::scientific :
262
0
        karma::real_policies<T>::fmtflags::fixed;
263
0
    }
Unexecuted instantiation: WebUtils.C:Wt::Utils::(anonymous namespace)::JavaScriptPolicy<double, 7>::floatfield(double)
Unexecuted instantiation: WebUtils.C:Wt::Utils::(anonymous namespace)::JavaScriptPolicy<double, 15>::floatfield(double)
264
265
    // 7 significant numbers; about float precision
266
0
    static unsigned precision(T) { return Precision; }
Unexecuted instantiation: WebUtils.C:Wt::Utils::(anonymous namespace)::JavaScriptPolicy<double, 7>::precision(double)
Unexecuted instantiation: WebUtils.C:Wt::Utils::(anonymous namespace)::JavaScriptPolicy<double, 15>::precision(double)
267
268
  };
269
270
  typedef real_generator<double, JavaScriptPolicy<double, 7> >
271
    KarmaJavaScriptReal;
272
  typedef real_generator<double, JavaScriptPolicy<double, 15> >
273
    KarmaJavaScriptDouble;
274
275
}
276
277
static inline char *generic_double_to_str(double d, int precision, char *buf)
278
0
{
279
0
  using namespace boost::spirit;
280
0
  using namespace boost::spirit::karma;
281
0
  char *p = buf;
282
0
  if (d != 0) {
283
0
      if (fabs(d) < DBL_MIN) {
284
0
        std::stringstream ss;
285
0
        ss.imbue(std::locale("C"));
286
0
        ss << std::setprecision(precision) << d;
287
0
        std::string str = ss.str();
288
0
        memcpy(p, str.c_str(), str.length());
289
0
        p += str.length();
290
0
      } else {
291
0
        if (precision <= 7)
292
0
          generate(p, KarmaJavaScriptReal(), d);
293
0
        else
294
0
          generate(p, KarmaJavaScriptDouble(), d);
295
0
      }
296
0
  }  else
297
0
    *p++ = '0';
298
0
  *p = '\0';
299
0
  return buf;
300
0
}
301
#else
302
static inline char *generic_double_to_str(double d, char *buf)
303
{
304
  if (!boost::math::isnan(d)) {
305
    if (!boost::math::isinf(d)) {
306
      sprintf(buf, "%.7g", d);
307
    } else {
308
      if (d > 0) {
309
        sprintf(buf, "Infinity");
310
      } else {
311
        sprintf(buf, "-Infinity");
312
      }
313
    }
314
  } else {
315
    sprintf(buf, "NaN");
316
  }
317
  return buf;
318
}
319
#endif
320
321
char *round_css_str(double d, int digits, char *buf)
322
0
{
323
0
  static const int exp[] = { 1, 10, 100, 1000, 10000, 100000, 1000000 };
324
325
0
  long long i
326
0
    = static_cast<long long>(d * exp[digits] + (d > 0 ? 0.49 : -0.49));
327
328
0
  lltoa(i, buf);
329
0
  char *num = buf;
330
331
0
  if (num[0] == '-')
332
0
    ++num;
333
0
  int len = std::strlen(num);
334
335
0
  if (len <= digits) {
336
0
    int shift = digits + 1 - len;
337
0
    for (int i = digits + 1; i >= 0; --i) {
338
0
      if (i >= shift)
339
0
        num[i] = num[i - shift];
340
0
      else
341
0
        num[i] = '0';
342
0
    }
343
0
    len = digits + 1;
344
0
  }
345
346
0
  int dotPos = (std::max)(len - digits, 0);
347
348
0
  for (int i = digits + 1; i >= 0; --i)
349
0
    num[dotPos + i + 1] = num[dotPos + i];
350
351
0
  num[dotPos] = '.';
352
353
0
  return buf;
354
0
}
355
356
0
char *round_js_str(double d, int digits, char *buf) {
357
0
#ifdef SPIRIT_FLOAT_FORMAT
358
0
  return generic_double_to_str(d, digits, buf);
359
#else
360
  return generic_double_to_str(d, buf);
361
#endif
362
0
}
363
364
std::string urlEncode(const std::string& url, const std::string& allowed)
365
0
{
366
0
  return DomElement::urlEncodeS(url, allowed);
367
0
}
368
369
std::string dataUrlDecode(WT_MAYBE_UNUSED const std::string& url,
370
                          WT_MAYBE_UNUSED std::vector<unsigned char>& data)
371
0
{
372
0
  return std::string();
373
0
}
374
375
void inplaceUrlDecode(std::string &text)
376
274k
{
377
  // Note: there is a Java-too duplicate of this function in Wt/Utils.C
378
274k
  std::size_t j = 0;
379
380
3.38M
  for (std::size_t i = 0; i < text.length(); ++i) {
381
3.10M
    char c = text[i];
382
383
3.10M
    if (c == '+') {
384
621
      text[j++] = ' ';
385
3.10M
    } else if (c == '%' && i + 2 < text.length()) {
386
94.3k
      std::string h = text.substr(i + 1, 2);
387
94.3k
      char *e = 0;
388
94.3k
      int hval = std::strtol(h.c_str(), &e, 16);
389
390
94.3k
      if (*e == 0) {
391
1.09k
        text[j++] = (char)hval;
392
1.09k
        i += 2;
393
93.2k
      } else {
394
        // not a proper %XX with XX hexadecimal format
395
93.2k
        text[j++] = c;
396
93.2k
      }
397
94.3k
    } else
398
3.01M
      text[j++] = c;
399
3.10M
  }
400
401
274k
  text.erase(j);
402
274k
}
403
404
namespace {
405
static const std::unordered_map<wchar_t, wchar_t> asciiReplaceMap = {
406
  {L'à', L'a'}, {L'á', L'a'}, {L'â', L'a'}, {L'ã', L'a'}, {L'ä', L'a'},
407
  {L'å', L'a'}, {L'ā', L'a'}, {L'ă', L'a'}, {L'ą', L'a'}, {L'ǎ', L'a'},
408
  {L'ǻ', L'a'}, {L'ạ', L'a'}, {L'ả', L'a'}, {L'ấ', L'a'}, {L'ầ', L'a'},
409
  {L'ẩ', L'a'}, {L'ẫ', L'a'}, {L'ậ', L'a'}, {L'ắ', L'a'}, {L'ằ', L'a'},
410
  {L'ẳ', L'a'}, {L'ẵ', L'a'}, {L'ặ', L'a'},
411
  {L'è', L'e'}, {L'é', L'e'}, {L'ê', L'e'}, {L'ë', L'e'}, {L'ē', L'e'},
412
  {L'ĕ', L'e'}, {L'ė', L'e'}, {L'ę', L'e'}, {L'ě', L'e'}, {L'ẹ', L'e'},
413
  {L'ẻ', L'e'}, {L'ẽ', L'e'}, {L'ế', L'e'}, {L'ề', L'e'}, {L'ể', L'e'},
414
  {L'ễ', L'e'}, {L'ệ', L'e'},
415
  {L'ì', L'i'}, {L'í', L'i'}, {L'î', L'i'}, {L'ï', L'i'}, {L'ĩ', L'i'},
416
  {L'ī', L'i'}, {L'ĭ', L'i'}, {L'į', L'i'}, {L'ǐ', L'i'}, {L'ỉ', L'i'},
417
  {L'ị', L'i'},
418
  {L'ò', L'o'}, {L'ó', L'o'}, {L'ô', L'o'}, {L'õ', L'o'}, {L'ö', L'o'},
419
  {L'ø', L'o'}, {L'ō', L'o'}, {L'ŏ', L'o'}, {L'ő', L'o'}, {L'ǒ', L'o'},
420
  {L'ǿ', L'o'}, {L'ọ', L'o'}, {L'ỏ', L'o'}, {L'ố', L'o'}, {L'ồ', L'o'},
421
  {L'ổ', L'o'}, {L'ỗ', L'o'}, {L'ộ', L'o'}, {L'ớ', L'o'}, {L'ờ', L'o'},
422
  {L'ở', L'o'}, {L'ỡ', L'o'}, {L'ợ', L'o'},
423
  {L'ù', L'u'}, {L'ú', L'u'}, {L'û', L'u'}, {L'ü', L'u'}, {L'ũ', L'u'},
424
  {L'ū', L'u'}, {L'ŭ', L'u'}, {L'ů', L'u'}, {L'ű', L'u'}, {L'ų', L'u'},
425
  {L'ǔ', L'u'}, {L'ụ', L'u'}, {L'ủ', L'u'}, {L'ứ', L'u'}, {L'ừ', L'u'},
426
  {L'ử', L'u'}, {L'ữ', L'u'}, {L'ự', L'u'},
427
  {L'ý', L'y'}, {L'ÿ', L'y'}, {L'ŷ', L'y'}, {L'ỳ', L'y'}, {L'ỵ', L'y'},
428
  {L'ỷ', L'y'}, {L'ỹ', L'y'},
429
430
  {L'À', L'A'}, {L'Á', L'A'}, {L'Â', L'A'}, {L'Ã', L'A'}, {L'Ä', L'A'},
431
  {L'Å', L'A'}, {L'Ā', L'A'}, {L'Ă', L'A'}, {L'Ą', L'A'}, {L'Ǎ', L'A'},
432
  {L'Ǻ', L'A'}, {L'Ạ', L'A'}, {L'Ả', L'A'}, {L'Ấ', L'A'}, {L'Ầ', L'A'},
433
  {L'Ẩ', L'A'}, {L'Ẫ', L'A'}, {L'Ậ', L'A'}, {L'Ắ', L'A'}, {L'Ằ', L'A'},
434
  {L'Ẳ', L'A'}, {L'Ẵ', L'A'}, {L'Ặ', L'A'},
435
  {L'È', L'E'}, {L'É', L'E'}, {L'Ê', L'E'}, {L'Ë', L'E'}, {L'Ē', L'E'},
436
  {L'Ĕ', L'E'}, {L'Ė', L'E'}, {L'Ę', L'E'}, {L'Ě', L'E'}, {L'Ẹ', L'E'},
437
  {L'Ẻ', L'E'}, {L'Ẽ', L'E'}, {L'Ế', L'E'}, {L'Ề', L'E'}, {L'Ể', L'E'},
438
  {L'Ễ', L'E'}, {L'Ệ', L'E'},
439
  {L'Ì', L'I'}, {L'Í', L'I'}, {L'Î', L'I'}, {L'Ï', L'I'}, {L'Ĩ', L'I'},
440
  {L'Ī', L'I'}, {L'Ĭ', L'I'}, {L'Į', L'I'}, {L'Ǐ', L'I'}, {L'Ỉ', L'I'},
441
  {L'Ị', L'I'},
442
  {L'Ò', L'O'}, {L'Ó', L'O'}, {L'Ô', L'O'}, {L'Õ', L'O'}, {L'Ö', L'O'},
443
  {L'Ø', L'O'}, {L'Ō', L'O'}, {L'ŏ', L'O'}, {L'Ő', L'O'}, {L'Ǒ', L'O'},
444
  {L'Ǿ', L'O'}, {L'Ọ', L'O'}, {L'Ỏ', L'O'}, {L'Ố', L'O'}, {L'Ồ', L'O'},
445
  {L'Ổ', L'O'}, {L'Ỗ', L'O'}, {L'Ộ', L'O'}, {L'Ớ', L'O'}, {L'Ờ', L'O'},
446
  {L'Ở', L'O'}, {L'Ỡ', L'O'}, {L'Ợ', L'O'},
447
  {L'Ù', L'U'}, {L'Ú', L'U'}, {L'Û', L'U'}, {L'Ü', L'U'}, {L'Ũ', L'U'},
448
  {L'Ū', L'U'}, {L'Ŭ', L'U'}, {L'Ů', L'U'}, {L'Ű', L'U'}, {L'Ų', L'U'},
449
  {L'Ǔ', L'U'}, {L'Ụ', L'U'}, {L'Ủ', L'U'}, {L'Ứ', L'U'}, {L'Ừ', L'U'},
450
  {L'Ử', L'U'}, {L'Ữ', L'U'}, {L'Ự', L'U'},
451
  {L'Ý', L'Y'}, {L'Ÿ', L'Y'}, {L'Ŷ', L'Y'}, {L'Ỳ', L'Y'}, {L'Ỵ', L'Y'},
452
  {L'Ỷ', L'Y'}, {L'Ỹ', L'Y'},
453
454
  {L'ç', L'c'}, {L'ć', L'c'}, {L'ĉ', L'c'}, {L'ċ', L'c'}, {L'č', L'c'},
455
  {L'Ç', L'C'}, {L'Ć', L'C'}, {L'Ĉ', L'C'}, {L'Ċ', L'C'}, {L'Č', L'C'},
456
  {L'ď', L'd'}, {L'đ', L'd'}, {L'Ď', L'D'}, {L'Đ', L'D'},
457
  {L'ĝ', L'g'}, {L'ğ', L'g'}, {L'ġ', L'g'}, {L'ģ', L'g'},
458
  {L'Ĝ', L'G'}, {L'Ğ', L'G'}, {L'Ġ', L'G'}, {L'Ģ', L'G'},
459
  {L'ĥ', L'h'}, {L'ħ', L'h'}, {L'Ĥ', L'H'}, {L'Ħ', L'H'},
460
  {L'ĵ', L'j'}, {L'Ĵ', L'J'},
461
  {L'ķ', L'k'}, {L'Ķ', L'K'},
462
  {L'ĺ', L'l'}, {L'ļ', L'l'}, {L'ľ', L'l'}, {L'ŀ', L'l'}, {L'ł', L'l'},
463
  {L'Ĺ', L'L'}, {L'Ļ', L'L'}, {L'Ľ', L'L'}, {L'Ŀ', L'L'}, {L'Ł', L'L'},
464
  {L'ñ', L'n'}, {L'ń', L'n'}, {L'ņ', L'n'}, {L'ň', L'n'}, {L'ʼn', L'n'},
465
  {L'Ñ', L'N'}, {L'Ń', L'N'}, {L'Ņ', L'N'}, {L'Ň', L'N'},
466
  {L'ŕ', L'r'}, {L'ŗ', L'r'}, {L'ř', L'r'},
467
  {L'Ŕ', L'R'}, {L'Ŗ', L'R'}, {L'Ř', L'R'},
468
  {L'ś', L's'}, {L'ŝ', L's'}, {L'ş', L's'}, {L'š', L's'}, {L'ș', L's'},
469
  {L'Ś', L'S'}, {L'Ŝ', L'S'}, {L'Ş', L'S'}, {L'Š', L'S'}, {L'Ș', L'S'},
470
  {L'ţ', L't'}, {L'ť', L't'}, {L'ŧ', L't'}, {L'ț', L't'},
471
  {L'Ţ', L'T'}, {L'Ť', L'T'}, {L'Ŧ', L'T'}, {L'Ț', L'T'},
472
  {L'ŵ', L'w'}, {L'Ŵ', L'W'},
473
  {L'ź', L'z'}, {L'ż', L'z'}, {L'ž', L'z'},
474
  {L'Ź', L'Z'}, {L'Ż', L'Z'}, {L'Ž', L'Z'},
475
476
  {L'‘', L'\''}, {L'’', L'\''}, {L'‚', L'\''}, {L'‛', L'\''},
477
  {L'“', L'"'},  {L'”', L'"'},  {L'„', L'"'},  {L'‟', L'"'},
478
  {L'‹', L'<'},  {L'›', L'>'},
479
  {L'«', L'"'},  {L'»', L'"'},
480
  {L'‐', L'-'},  {L'‑', L'-'},  {L'–', L'-'},  {L'—', L'-'},
481
  {L'―', L'-'},  {L'⸗', L'-'},
482
  {L'˜', L'~'},
483
  {L'·', L'.'},  {L'•', L'.'}
484
};
485
}
486
487
0
std::string toAscii(const std::wstring& input) {
488
0
  if (input.empty()) {
489
0
    return "";
490
0
  }
491
492
0
  std::wstring s = input;
493
0
  for (auto& c : s) {
494
0
    auto it = asciiReplaceMap.find(c);
495
0
    if (it != asciiReplaceMap.end()) {
496
0
      c = it->second;
497
0
    }
498
0
}
499
500
  // Remove any remaining bytes that are outside the standard 7-bit ASCII range
501
0
  std::wregex nonAsciiRegex(L"[^\\x00-\\x7F]");
502
0
  return Wt::toUTF8(std::regex_replace(s, nonAsciiRegex, L"?"));
503
0
}
504
505
std::string EncodeHttpHeaderField(const std::string &fieldname,
506
                                  const WString &fieldValue)
507
0
{
508
  // This implements RFC 5987
509
0
  return fieldname + "*=UTF-8''" + urlEncode(fieldValue.toUTF8());
510
0
}
511
512
std::string readFile(const std::string& fname)
513
0
{
514
0
  std::ifstream f(fname.c_str(), std::ios::in | std::ios::binary);
515
516
0
  if (!f)
517
0
    throw WException("Could not load " + fname);
518
519
0
  f.seekg(0, std::ios::end);
520
0
  int length = f.tellg();
521
0
  f.seekg(0, std::ios::beg);
522
523
0
  std::unique_ptr<char[]> ftext(new char[length + 1]);
524
0
  f.read(ftext.get(), length);
525
0
  ftext[length] = 0;
526
527
0
  return std::string(ftext.get());
528
0
}
529
530
WString formatFloat(const WString &format, double value)
531
0
{
532
0
  std::string f = format.toUTF8();
533
0
  int buflen = f.length() + 15;
534
535
0
  char *buf = new char[buflen];
536
537
0
  snprintf(buf, buflen, f.c_str(), value);
538
0
  buf[buflen - 1] = 0;
539
540
0
  WString result = WT_USTRING::fromUTF8(buf);
541
542
0
  delete[] buf;
543
544
0
  return result;
545
0
}
546
547
template<typename ResultType, typename SpiritType>
548
ResultType convert(const char *fname, const SpiritType &t, const std::string& v)
549
2.04k
{
550
8.33k
  auto is_space = [](char c) { return c == ' '; };
Unexecuted instantiation: Wt::Utils::convert<long, boost::spirit::terminal<boost::spirit::tag::long_> >(char const*, boost::spirit::terminal<boost::spirit::tag::long_> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::{lambda(char)#1}::operator()(char) const
Unexecuted instantiation: Wt::Utils::convert<unsigned long, boost::spirit::terminal<boost::spirit::tag::ulong_> >(char const*, boost::spirit::terminal<boost::spirit::tag::ulong_> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::{lambda(char)#1}::operator()(char) const
Wt::Utils::convert<long long, boost::spirit::terminal<boost::spirit::tag::long_long> >(char const*, boost::spirit::terminal<boost::spirit::tag::long_long> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::{lambda(char)#1}::operator()(char) const
Line
Count
Source
550
3.78k
  auto is_space = [](char c) { return c == ' '; };
Unexecuted instantiation: Wt::Utils::convert<unsigned long long, boost::spirit::terminal<boost::spirit::tag::ulong_long> >(char const*, boost::spirit::terminal<boost::spirit::tag::ulong_long> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::{lambda(char)#1}::operator()(char) const
Wt::Utils::convert<int, boost::spirit::terminal<boost::spirit::tag::int_> >(char const*, boost::spirit::terminal<boost::spirit::tag::int_> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::{lambda(char)#1}::operator()(char) const
Line
Count
Source
550
4.55k
  auto is_space = [](char c) { return c == ' '; };
Unexecuted instantiation: Wt::Utils::convert<double, boost::spirit::terminal<boost::spirit::tag::double_> >(char const*, boost::spirit::terminal<boost::spirit::tag::double_> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::{lambda(char)#1}::operator()(char) const
Unexecuted instantiation: Wt::Utils::convert<float, boost::spirit::terminal<boost::spirit::tag::float_> >(char const*, boost::spirit::terminal<boost::spirit::tag::float_> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)::{lambda(char)#1}::operator()(char) const
551
2.04k
  auto it = std::find_if_not(v.cbegin(), v.cend(), is_space);
552
2.04k
  ResultType result{0};
553
2.04k
  bool success =
554
2.04k
      it < v.cend() &&
555
2.02k
      boost::spirit::qi::parse(it, v.cend(), t, result) &&
556
1.60k
      std::all_of(it, v.cend(), is_space);
557
2.04k
  if (!success)
558
1.61k
    throw std::invalid_argument(std::string(fname) + "() of " + v + " failed");
559
429
  return result;
560
2.04k
}
Unexecuted instantiation: long Wt::Utils::convert<long, boost::spirit::terminal<boost::spirit::tag::long_> >(char const*, boost::spirit::terminal<boost::spirit::tag::long_> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
Unexecuted instantiation: unsigned long Wt::Utils::convert<unsigned long, boost::spirit::terminal<boost::spirit::tag::ulong_> >(char const*, boost::spirit::terminal<boost::spirit::tag::ulong_> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
long long Wt::Utils::convert<long long, boost::spirit::terminal<boost::spirit::tag::long_long> >(char const*, boost::spirit::terminal<boost::spirit::tag::long_long> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
Line
Count
Source
549
1.10k
{
550
1.10k
  auto is_space = [](char c) { return c == ' '; };
551
1.10k
  auto it = std::find_if_not(v.cbegin(), v.cend(), is_space);
552
1.10k
  ResultType result{0};
553
1.10k
  bool success =
554
1.10k
      it < v.cend() &&
555
1.10k
      boost::spirit::qi::parse(it, v.cend(), t, result) &&
556
909
      std::all_of(it, v.cend(), is_space);
557
1.10k
  if (!success)
558
1.10k
    throw std::invalid_argument(std::string(fname) + "() of " + v + " failed");
559
0
  return result;
560
1.10k
}
Unexecuted instantiation: unsigned long long Wt::Utils::convert<unsigned long long, boost::spirit::terminal<boost::spirit::tag::ulong_long> >(char const*, boost::spirit::terminal<boost::spirit::tag::ulong_long> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
int Wt::Utils::convert<int, boost::spirit::terminal<boost::spirit::tag::int_> >(char const*, boost::spirit::terminal<boost::spirit::tag::int_> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
Line
Count
Source
549
941
{
550
941
  auto is_space = [](char c) { return c == ' '; };
551
941
  auto it = std::find_if_not(v.cbegin(), v.cend(), is_space);
552
941
  ResultType result{0};
553
941
  bool success =
554
941
      it < v.cend() &&
555
913
      boost::spirit::qi::parse(it, v.cend(), t, result) &&
556
696
      std::all_of(it, v.cend(), is_space);
557
941
  if (!success)
558
512
    throw std::invalid_argument(std::string(fname) + "() of " + v + " failed");
559
429
  return result;
560
941
}
Unexecuted instantiation: double Wt::Utils::convert<double, boost::spirit::terminal<boost::spirit::tag::double_> >(char const*, boost::spirit::terminal<boost::spirit::tag::double_> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
Unexecuted instantiation: float Wt::Utils::convert<float, boost::spirit::terminal<boost::spirit::tag::float_> >(char const*, boost::spirit::terminal<boost::spirit::tag::float_> const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
561
562
long stol(const std::string& v)
563
0
{
564
0
  return convert<long>("stol", boost::spirit::long_, v);
565
0
}
566
567
unsigned long stoul(const std::string& v)
568
0
{
569
0
  return convert<unsigned long>("stoul", boost::spirit::ulong_, v);
570
0
}
571
572
long long stoll(const std::string& v)
573
1.10k
{
574
1.10k
  return convert<long long>("stoll", boost::spirit::long_long, v);
575
1.10k
}
576
577
unsigned long long stoull(const std::string& v)
578
0
{
579
0
  return convert<unsigned long long>("stoull", boost::spirit::ulong_long, v);
580
0
}
581
582
int stoi(const std::string& v)
583
941
{
584
941
  return convert<int>("stoi", boost::spirit::int_, v);
585
941
}
586
587
double stod(const std::string& v)
588
0
{
589
0
  return convert<double>("stod", boost::spirit::double_, v);
590
0
}
591
592
float stof(const std::string& v)
593
0
{
594
0
  return convert<float>("stof", boost::spirit::float_, v);
595
0
}
596
597
void fixSelfClosingTags(Wt::rapidxml::xml_node<> *x_node)
598
8.85k
{
599
17.3k
  for (Wt::rapidxml::xml_node<> *x_child = x_node->first_node(); x_child;
600
8.85k
       x_child = x_child->next_sibling())
601
8.50k
    fixSelfClosingTags(x_child);
602
603
8.85k
  if (!x_node->first_node()
604
8.16k
      && x_node->value_size() == 0
605
5.39k
      && !Wt::DomElement::isSelfClosingTag
606
5.39k
      (std::string(x_node->name(), x_node->name_size()))) {
607
    // We need to add an emtpy data node since <div /> is illegal HTML
608
    // (but valid XML / XHTML)
609
3.87k
    Wt::rapidxml::xml_node<> *empty = x_node->document()->allocate_node(Wt::rapidxml::node_data);
610
3.87k
    x_node->append_node(empty);
611
3.87k
  }
612
8.85k
}
613
614
  }
615
}