~ubuntu-branches/ubuntu/quantal/mysql-workbench/quantal

« back to all changes in this revision

Viewing changes to library/base/string_utilities.cpp

  • Committer: Package Import Robot
  • Author(s): Dmitry Smirnov
  • Date: 2012-03-01 21:57:30 UTC
  • Revision ID: package-import@ubuntu.com-20120301215730-o7y8av8y38n162ro
Tags: upstream-5.2.38+dfsg
ImportĀ upstreamĀ versionĀ 5.2.38+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* 
 
2
 * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU General Public License as
 
6
 * published by the Free Software Foundation; version 2 of the
 
7
 * License.
 
8
 * 
 
9
 * This program is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 
12
 * GNU General Public License for more details.
 
13
 * 
 
14
 * You should have received a copy of the GNU General Public License
 
15
 * along with this program; if not, write to the Free Software
 
16
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 
17
 * 02110-1301  USA
 
18
 */
 
19
 
 
20
#include "base/string_utilities.h"
 
21
#include "base/file_functions.h"
 
22
#include "base/log.h"
 
23
 
 
24
#include <stdexcept>
 
25
#include <functional>
 
26
#include <locale>
 
27
#include <algorithm>
 
28
#include <math.h>
 
29
#include <errno.h>
 
30
#include <string.h>
 
31
#include <boost/function.hpp>
 
32
#include <boost/bind.hpp>
 
33
 
 
34
DEFAULT_LOG_DOMAIN(DOMAIN_BASE);
 
35
 
 
36
using namespace std;
 
37
 
 
38
namespace base
 
39
{
 
40
#ifdef _WIN32
 
41
 
 
42
//--------------------------------------------------------------------------------------------------
 
43
 
 
44
/**
 
45
 * Converts an UTF-8 encoded string to an UTF-16 string.
 
46
 */
 
47
std::wstring string_to_wstring(const std::string &s)
 
48
{
 
49
  int required;
 
50
 
 
51
  required= MultiByteToWideChar(CP_UTF8, 0, s.data(), -1, NULL, 0);
 
52
  if (required == 0)
 
53
    return std::wstring();
 
54
 
 
55
  // Required contains the length for the result string including the terminating 0.
 
56
  WCHAR *buffer = g_new(WCHAR, required);
 
57
  MultiByteToWideChar(CP_UTF8, 0, s.data(), -1, buffer, required);
 
58
  std::wstring converted(buffer);
 
59
  g_free(buffer);
 
60
 
 
61
  return converted;
 
62
}
 
63
 
 
64
//--------------------------------------------------------------------------------------------------
 
65
 
 
66
/**
 
67
 * Converts an UTF-16 encoded string to an UTF-8 string.
 
68
 */
 
69
std::string wstring_to_string(const std::wstring &s)
 
70
{
 
71
  char* converted;
 
72
  int required = WideCharToMultiByte(CP_UTF8, 0, s.c_str(), -1, NULL, 0, NULL, NULL);
 
73
  converted = (char*) g_malloc(required);
 
74
  WideCharToMultiByte(CP_UTF8, 0, s.c_str(), -1, converted, required, NULL, NULL);
 
75
  std::string result = converted;
 
76
  g_free(converted);
 
77
 
 
78
  return result;
 
79
}
 
80
 
 
81
std::wstring path_from_utf8(const std::string &s)
 
82
{
 
83
  return string_to_wstring(s);
 
84
}
 
85
#else
 
86
std::string path_from_utf8(const std::string &s)
 
87
{
 
88
  return s;
 
89
}
 
90
 
 
91
#endif
 
92
 
 
93
//--------------------------------------------------------------------------------------------------
 
94
 
 
95
std::string string_to_path_for_open(const std::string &s)
 
96
{
 
97
#ifdef _WIN32
 
98
  std::wstring ws = string_to_wstring(s);
 
99
  int buflen = GetShortPathNameW(ws.c_str(), NULL, 0);
 
100
  if (buflen > 0)
 
101
  {
 
102
    wchar_t *buffer = g_new(wchar_t, buflen);
 
103
    if (GetShortPathNameW(ws.c_str(), buffer, buflen) > 0)
 
104
    {
 
105
      char *buffer2;
 
106
      buflen = WideCharToMultiByte(CP_UTF8, 0, buffer, buflen, NULL, 0, 0, 0);
 
107
      buffer2 = g_new(char, buflen);
 
108
      if (WideCharToMultiByte(CP_UTF8, 0, buffer, buflen, buffer2, buflen, 0, 0) == 0)
 
109
      {
 
110
        std::string path(buffer2);
 
111
        g_free(buffer2);
 
112
        g_free(buffer);
 
113
        return path;
 
114
      }
 
115
      g_free(buffer2);
 
116
    }
 
117
    g_free(buffer);
 
118
  }
 
119
  return s;
 
120
#else
 
121
  return s;
 
122
#endif
 
123
}
 
124
 
 
125
//--------------------------------------------------------------------------------------------------
 
126
  
 
127
inline bool is_invalid_filesystem_char(int ch)
 
128
{
 
129
  static const char invalids[] = "/?<>\\:*|\"^";
 
130
  
 
131
  return memchr(invalids, ch, sizeof(invalids)-1) != NULL;
 
132
}
 
133
  
 
134
std::string sanitize_file_name(const std::string &s)
 
135
{
 
136
  static const char *invalid_filenames[] = {
 
137
    "com1", "com2", "com3", "com4", "com5", "com6",
 
138
    "com7", "com8", "com9", "lpt1", "lpt2", "lpt3", "lpt4", 
 
139
    "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "con", "nul", "prn",
 
140
    ".", "..", 
 
141
    NULL
 
142
  };
 
143
  std::string out;
 
144
  
 
145
  for (std::string::const_iterator c = s.begin(); c != s.end(); ++c)
 
146
  {
 
147
    // utf-8 has the high-bit = 1, so we just copy those verbatim
 
148
    if (isalnum(*c) || (unsigned char)*c >= 128 || (ispunct(*c) && !is_invalid_filesystem_char(*c)))
 
149
      out.push_back(*c);
 
150
    else
 
151
      out.push_back('_');
 
152
  }
 
153
  
 
154
  // not valid under windows
 
155
  if (!out.empty() && (out[out.size()-1] == ' ' || out[out.size()-1] == '.'))
 
156
    out[out.size()-1] = '_';
 
157
  
 
158
  for (const char **fn = invalid_filenames; *fn; ++fn)
 
159
  {
 
160
    if (strcmp(out.c_str(), *fn) == 0)
 
161
    {
 
162
      out.append("_");
 
163
      break;
 
164
    }
 
165
  }
 
166
  
 
167
  return out;
 
168
}
 
169
 
 
170
//--------------------------------------------------------------------------------------------------
 
171
 
 
172
string trim_right(const string& s, const string& t)
 
173
{
 
174
  string d(s);
 
175
  string::size_type i (d.find_last_not_of(t));
 
176
  if (i == string::npos)
 
177
    return "";
 
178
  else
 
179
    return d.erase(d.find_last_not_of(t) + 1) ;
 
180
}  
 
181
 
 
182
//--------------------------------------------------------------------------------------------------
 
183
 
 
184
string trim_left(const string& s, const string& t)
 
185
{
 
186
  string d(s);
 
187
  return d.erase(0, s.find_first_not_of(t)) ;
 
188
}  
 
189
 
 
190
//--------------------------------------------------------------------------------------------------
 
191
 
 
192
string trim(const string& s, const string& t)
 
193
{
 
194
  string d(s);
 
195
  return trim_left(trim_right(d, t), t) ;
 
196
}  
 
197
 
 
198
//--------------------------------------------------------------------------------------------------
 
199
 
 
200
/**
 
201
 * Simple case conversion routine, which returns a new string.
 
202
 */
 
203
string tolower(const string& s)
 
204
{
 
205
  char *str_down= g_utf8_strdown(s.c_str(), g_utf8_strlen(s.c_str(), -1));
 
206
  std::string result(str_down);
 
207
  g_free(str_down);
 
208
  return result;
 
209
}
 
210
 
 
211
//--------------------------------------------------------------------------------------------------
 
212
 
 
213
string toupper(const string& s)
 
214
{
 
215
  char *str_up= g_utf8_strup(s.c_str(), g_utf8_strlen(s.c_str(), -1));
 
216
  std::string result(str_up);
 
217
  g_free(str_up);
 
218
  return result;
 
219
}
 
220
 
 
221
//--------------------------------------------------------------------------------------------------
 
222
 
 
223
std::string truncate_text(const std::string& s, int max_length)
 
224
{
 
225
  if ((int) s.length() > max_length)
 
226
  {
 
227
    std::string shortened(s.substr(0, max_length));
 
228
    const char *prev = g_utf8_find_prev_char(shortened.c_str(), shortened.c_str() + (max_length - 1));
 
229
    if (prev)
 
230
    {
 
231
      shortened.resize(prev - shortened.c_str(), 0);
 
232
      shortened.append("...");
 
233
    }
 
234
    return shortened;
 
235
  }
 
236
  return s;
 
237
}
 
238
  
 
239
//--------------------------------------------------------------------------------------------------
 
240
 
 
241
std::string sanitize_utf8(const std::string& s)
 
242
{
 
243
  const char *end = 0;
 
244
  if (!g_utf8_validate(s.data(), s.size(), &end))
 
245
    return std::string(s.data(), end);
 
246
  return s;
 
247
}
 
248
  
 
249
//--------------------------------------------------------------------------------------------------
 
250
 
 
251
std::vector<std::string> split(const std::string &s, const std::string &sep, int count)
 
252
{
 
253
  std::vector<std::string> parts;
 
254
  std::string ss= s;
 
255
 
 
256
  std::string::size_type p;
 
257
 
 
258
  if (s.empty())
 
259
    return parts;
 
260
 
 
261
  if (count == 0)
 
262
    count= -1;
 
263
 
 
264
  p= ss.find(sep);
 
265
  while (!ss.empty() && p != std::string::npos && (count < 0 || count > 0))
 
266
  {
 
267
    parts.push_back(ss.substr(0, p));
 
268
    ss= ss.substr(p+sep.size());
 
269
 
 
270
    --count;
 
271
    p= ss.find(sep);
 
272
  }
 
273
  parts.push_back(ss);
 
274
 
 
275
  return parts;
 
276
}
 
277
 
 
278
//--------------------------------------------------------------------------------------------------
 
279
 
 
280
std::vector<std::string> split_token_list(const std::string &s, int sep)
 
281
{
 
282
  std::vector<std::string> parts;
 
283
  std::string ss= s;
 
284
  
 
285
  std::string::size_type end = s.size(), pe, p = 0;
 
286
  
 
287
  if (!s.empty())
 
288
  {
 
289
    bool done;
 
290
 
 
291
    while (p < end)
 
292
    {
 
293
      switch (s[p])
 
294
      {
 
295
        case '\'':
 
296
          pe = p+1;
 
297
          done = false;
 
298
          // keep going until we find closing '
 
299
          while (pe < end && !done)
 
300
          {
 
301
            switch (s[pe++])
 
302
            {
 
303
              case '\'':
 
304
                if (pe < end && s[pe] == '\'')
 
305
                  pe++;
 
306
                else
 
307
                  done = true;
 
308
                break;
 
309
              case '\\':
 
310
                if (pe < end)
 
311
                  pe++;
 
312
                break;
 
313
            }
 
314
          }
 
315
          parts.push_back(s.substr(p, pe-p));
 
316
          p = pe;
 
317
          // skip whitespace
 
318
          while (p < end && (s[p] == ' ' || s[p] == '\t' || s[p] == '\r' || s[p] == '\n')) p++;
 
319
          if (p < end)
 
320
          {
 
321
            if (s[p] != sep)
 
322
              log_debug("Error splitting string list");
 
323
            else
 
324
              p++;
 
325
          }
 
326
          break;
 
327
 
 
328
        case '"':
 
329
          pe = p+1;
 
330
          done = false;
 
331
          // keep going until we find closing "
 
332
          while (pe < end && !done)
 
333
          {
 
334
            switch (s[pe++])
 
335
            {
 
336
              case '"':
 
337
                if (pe < end && s[pe] == '"')
 
338
                  pe++;
 
339
                else
 
340
                  done = true;
 
341
                break;
 
342
              case '\\':
 
343
                if (pe < end)
 
344
                  pe++;
 
345
                break;
 
346
            }
 
347
          }
 
348
          parts.push_back(s.substr(p, pe-p));
 
349
          p = pe;
 
350
          // skip whitespace
 
351
          while (p < end && (s[p] == ' ' || s[p] == '\t' || s[p] == '\r' || s[p] == '\n')) p++;
 
352
          if (p < end)
 
353
          {
 
354
            if (s[0] != sep)
 
355
              log_debug("Error splitting string list");
 
356
            else
 
357
              p++;
 
358
          }
 
359
          break;
 
360
 
 
361
        case ' ':
 
362
        case '\t':
 
363
          p++;
 
364
          break;
 
365
 
 
366
        default:
 
367
          // skip until separator
 
368
          pe = p+1;
 
369
          while (pe < end)
 
370
          {
 
371
            if (s[pe] == sep)
 
372
              break;
 
373
            pe++;
 
374
          }
 
375
          parts.push_back(trim_right(s.substr(p, pe-p)));
 
376
          p = pe+1;
 
377
          break;
 
378
      }
 
379
    }
 
380
  }
 
381
 
 
382
  return parts;
 
383
}
 
384
  
 
385
//--------------------------------------------------------------------------------------------------
 
386
  
 
387
bool partition(const std::string &s, const std::string &sep, std::string &left, std::string &right)
 
388
{
 
389
  std::string::size_type p = s.find(sep);
 
390
  if (p != std::string::npos)
 
391
  {
 
392
    left = s.substr(0, p);
 
393
    right = s.substr(p + sep.size());
 
394
    return true;
 
395
  }
 
396
  left = s;
 
397
  right = "";
 
398
  return false;
 
399
}
 
400
  
 
401
//--------------------------------------------------------------------------------------------------
 
402
  
 
403
/**
 
404
 * Returns a string containing all characters beginning at "start" from the given string "id", which form
 
405
 * a valid, unqualified identifier. The returned identifier does not contain any quoting anymore.
 
406
 * Note: this function is UTF-8 safe as it skips over all characters except some which are guaranteed
 
407
 *       not to be part of any valid UTF-8 sequence.
 
408
 *
 
409
 * @param id The string to examine.
 
410
 * @param start The start position to search from.
 
411
 *
 
412
 * @result Returns the first found identifier starting at "start" or an empty string if nothing was 
 
413
 *         found. Parameter "start" points to the first character after the found identifier.
 
414
 */
 
415
string get_identifier(const string& id, string::const_iterator& start)
 
416
{
 
417
  string::const_iterator token_end= id.end();
 
418
  bool is_symbol_quoted= false;
 
419
  for (string::const_iterator i= start, i_end= token_end; i != i_end; ++i)
 
420
  {
 
421
    if (i_end != token_end)
 
422
      break;
 
423
    switch (*i)
 
424
    {
 
425
      case '.':
 
426
        if (!is_symbol_quoted)
 
427
          token_end= i;
 
428
        break;
 
429
      case ' ':
 
430
        if (!is_symbol_quoted)
 
431
          token_end= i;
 
432
        break;
 
433
      case '\'':
 
434
      case '"':
 
435
      case '`':
 
436
        if (*i == *start)
 
437
        {
 
438
          if (i != start)
 
439
            token_end= i + 1;
 
440
          else
 
441
            is_symbol_quoted= true;
 
442
        }
 
443
        break;
 
444
    }
 
445
  }
 
446
 
 
447
  if (token_end - start < 2)
 
448
    is_symbol_quoted= false;
 
449
  string result(start, token_end);
 
450
  start= token_end;
 
451
  if (is_symbol_quoted)
 
452
    return result.substr(1, result.size() - 2);
 
453
 
 
454
  return result;
 
455
}
 
456
 
 
457
//--------------------------------------------------------------------------------------------------
 
458
 
 
459
/**
 
460
 * Splits the given string into identifier parts assuming a format as allowed by the MySQL syntax for
 
461
 * qualified identifiers, e.g. part1.part2.part3 (any of the parts might be quoted).
 
462
 * In addition to the traditional syntax also these enhancements are supported:
 
463
 * - Unlimited level of nesting.
 
464
 * - Quoting might be done using single quotes, double quotes and back ticks.
 
465
 *
 
466
 * If an identifier is not separated by a dot from the rest of the input then this is considered
 
467
 * invalid input and ignored. Only identifiers found until that syntax violation are returned.
 
468
 */
 
469
vector<string> split_qualified_identifier(const string& id)
 
470
{
 
471
  vector<string> result;
 
472
  string::const_iterator iterator= id.begin();
 
473
  string token;
 
474
  do
 
475
  {
 
476
    token= get_identifier(id, iterator);
 
477
    if (token == "")
 
478
      break;
 
479
    result.push_back(token);
 
480
  } while ((iterator != id.end()) && (*iterator++ == '.'));
 
481
  
 
482
  return result;
 
483
}
 
484
 
 
485
//--------------------------------------------------------------------------------------------------
 
486
 
 
487
/**
 
488
 * Removes the first path part from @path and returns this part as well as the shortend path.
 
489
 */
 
490
std::string pop_path_front(std::string &path)
 
491
{
 
492
  std::string::size_type p= path.find('/');
 
493
  std::string res;
 
494
  if (p == std::string::npos || p == path.length()-1)
 
495
  {
 
496
    res= path;
 
497
    path.clear();
 
498
    return res;
 
499
  }
 
500
  res= path.substr(0, p);
 
501
  path= path.substr(p+1);
 
502
  return res;
 
503
}
 
504
 
 
505
//--------------------------------------------------------------------------------------------------
 
506
 
 
507
/**
 
508
 * Removes the last path part from @path and returns this part as well as the shortend path.
 
509
 */
 
510
std::string pop_path_back(std::string &path)
 
511
{
 
512
  std::string::size_type p= path.rfind('/');
 
513
  std::string res;
 
514
  if (p == std::string::npos || p == path.length()-1)
 
515
  {
 
516
    res= path;
 
517
    path.clear();
 
518
    return res;
 
519
  }
 
520
  res= path.substr(p+1);
 
521
  path= path.substr(0, p);
 
522
  return res;
 
523
}
 
524
  
 
525
//--------------------------------------------------------------------------------------------------
 
526
 
 
527
/**
 
528
 * Helper routine to format a string into an STL string using the printf parameter syntax.
 
529
 */
 
530
std::string strfmt(const char *fmt, ...)
 
531
{
 
532
  va_list args;
 
533
  char *tmp;
 
534
  std::string ret;
 
535
  
 
536
  va_start(args, fmt);
 
537
  tmp= g_strdup_vprintf(fmt, args);
 
538
  va_end(args);
 
539
  
 
540
  ret= tmp;
 
541
  g_free(tmp);
 
542
  
 
543
  return ret;
 
544
}
 
545
 
 
546
 
 
547
//--------------------------------------------------------------------------------------------------
 
548
 
 
549
BASELIBRARY_PUBLIC_FUNC std::string sizefmt(int64_t s, bool metric)
 
550
{
 
551
  float one_kb;
 
552
  const char* unit;
 
553
  if (metric)
 
554
  {
 
555
    one_kb = 1000;
 
556
    unit = "B";
 
557
  }
 
558
  else
 
559
  {
 
560
    one_kb = 1024;
 
561
    unit = "iB";   // http://en.wikipedia.org/wiki/Binary_prefix
 
562
  }
 
563
 
 
564
  if (s < one_kb)
 
565
    return strfmt("%iB", (int) s);
 
566
  else
 
567
  {
 
568
    float value = s / one_kb;
 
569
    if (value < one_kb)
 
570
      return strfmt("%.02fK%s", value, unit);
 
571
    else
 
572
    {
 
573
      value /= one_kb;
 
574
      if (value < one_kb)
 
575
        return strfmt("%.02fM%s", value, unit);
 
576
      else
 
577
      {
 
578
        value /= one_kb;
 
579
        if (value < one_kb)
 
580
          return strfmt("%.02fG%s", value, unit);
 
581
        else
 
582
        {
 
583
          value /= one_kb;
 
584
          if (value < one_kb)
 
585
            return strfmt("%.02fT%s", value, unit);
 
586
          else
 
587
            return strfmt("%.02fP%s", value / one_kb, unit);
 
588
        }
 
589
      }
 
590
    }
 
591
  }
 
592
 
 
593
}
 
594
 
 
595
//--------------------------------------------------------------------------------------------------
 
596
 
 
597
/**
 
598
 * Helper routine to strip a string into an STL string using the printf parameter syntax.
 
599
 */
 
600
std::string strip_text(const std::string &text, bool left, bool right)
 
601
{//TODO sigc rewrite it in std/boost way
 
602
  std::locale loc;
 
603
  boost::function<bool (std::string::value_type)> is_space=
 
604
    boost::bind(&std::isspace<std::string::value_type>, _1, loc);
 
605
 
 
606
  std::string::const_iterator l_edge= !left ? text.begin() :
 
607
    std::find_if(text.begin(), text.end(), boost::bind(std::logical_not<bool>(), boost::bind(is_space,_1)));
 
608
  std::string::const_reverse_iterator r_edge= !right ? text.rbegin() :
 
609
    std::find_if(text.rbegin(), text.rend(), boost::bind(std::logical_not<bool>(), boost::bind(is_space,_1)));
 
610
 
 
611
  return std::string(l_edge, r_edge.base());
 
612
}
 
613
 
 
614
//--------------------------------------------------------------------------------------------------
 
615
 
 
616
/**
 
617
 * Add the given extension to the filename, if necessary.
 
618
 * 
 
619
 */
 
620
std::string normalize_path_extension(std::string filename, std::string extension)
 
621
{
 
622
  if (!extension.empty() && !filename.empty())
 
623
  {
 
624
    std::string::size_type p = filename.rfind('.');
 
625
    std::string old_extension = p != std::string::npos ? filename.substr(p) : "";
 
626
 
 
627
    if (old_extension.find('/') != std::string::npos || old_extension.find('\\') != std::string::npos)
 
628
      old_extension.clear();
 
629
  
 
630
    if (!extension.empty() && extension[0] != '.')
 
631
      extension = "."+extension;
 
632
 
 
633
    if (old_extension.empty())
 
634
      filename.append(extension);
 
635
    else
 
636
    {
 
637
      if (old_extension != extension)
 
638
        filename = filename.substr(0, p).append(extension);
 
639
    }      
 
640
  }
 
641
  return filename;
 
642
}
 
643
 
 
644
/**
 
645
 * Removes all unnecessary path separators as well as "./" combinations.
 
646
 * If there is a parent-dir entry (../) then this as well as the directly prefacing
 
647
 * dir entry is removed.
 
648
 */
 
649
std::string normalize_path(const std::string path)
 
650
{
 
651
  // First convert all separators to the one that is used on the platform (no mix)
 
652
  // and ease so at the same time further processing here.
 
653
  std::string result;
 
654
  std::string separator(1, G_DIR_SEPARATOR);
 
655
  
 
656
  result= path;
 
657
  replace(result, "\\", separator);
 
658
  replace(result, "/", separator);
 
659
  
 
660
  std::string double_separator = separator + separator;
 
661
  while (result.find(double_separator) != std::string::npos)
 
662
    replace(result, double_separator, separator);
 
663
  
 
664
  // Sanity check. Return *after* we have converted the slashs. This is part of the normalization.
 
665
  if (result.size() < 2)
 
666
    return result;
 
667
  
 
668
  std::vector<std::string> parts= split(result, separator);
 
669
  
 
670
  // Construct result backwards while examining the path parts.
 
671
  result= "";
 
672
  int pending_count= 0;
 
673
  for (int i= parts.size() - 1; i >= 0; i--)
 
674
  {
 
675
    if (parts[i].compare(".") == 0)
 
676
      // References to the current directory can be removed without further change.
 
677
      continue;
 
678
    
 
679
    if (parts[i].compare("..") == 0)
 
680
    {
 
681
      // An entry that points back to the parent dir.
 
682
      // Ignore this and keep track for later removal of the parent dir.
 
683
      pending_count++;
 
684
    }
 
685
    else
 
686
      if (pending_count > 0)
 
687
      {
 
688
        // If this is a normal dir entry and we have pending parent-dir redirections
 
689
        // then go one step up by removing (ignoring) this entry.
 
690
        pending_count--;
 
691
      }
 
692
      else
 
693
        result = separator + parts[i] + result;
 
694
  }
 
695
  
 
696
  // Don't return the leading separator.
 
697
  return result.substr(1);
 
698
}
 
699
 
 
700
std::string expand_tilde(const std::string &path)
 
701
{
 
702
  if (!path.empty() && path[0] == '~' && (path.size() == 1 || path[1] == G_DIR_SEPARATOR))
 
703
  {
 
704
    const char *homedir = g_getenv("HOME");
 
705
    if (!homedir)
 
706
      homedir = g_get_home_dir();
 
707
    
 
708
    return std::string(homedir).append(path.substr(1));
 
709
  }
 
710
  return path;
 
711
}
 
712
  
 
713
//--------------------------------------------------------------------------------------------------
 
714
 
 
715
/**
 
716
 * Tests if t begins with part.
 
717
 */
 
718
bool starts_with(const std::string& s, const std::string& part)
 
719
{
 
720
  return (s.substr(0, part.length()) == part);
 
721
}
 
722
 
 
723
//--------------------------------------------------------------------------------------------------
 
724
 
 
725
void replace(std::string& value, const std::string& search, const std::string& replacement)
 
726
{
 
727
  std::string::size_type next;
 
728
 
 
729
  for (next = value.find(search); next != std::string::npos; next = value.find(search,next))
 
730
  {
 
731
    value.replace(next,search.length(), replacement);
 
732
    next += replacement.length();
 
733
  }
 
734
}
 
735
 
 
736
//--------------------------------------------------------------------------------------------------
 
737
 
 
738
/**
 
739
 * Write text data to file, converting to \r\n if in Windows.
 
740
 */
 
741
void set_text_file_contents(const std::string &filename, const std::string &data)
 
742
{
 
743
#ifdef _WIN32
 
744
  // Opening a file in text mode will automatically convert \n to \r\n.
 
745
  FILE *f = base_fopen(filename.c_str(), "w+t");
 
746
  if (!f)
 
747
    throw std::runtime_error(g_strerror(errno));
 
748
 
 
749
  size_t bytes_written= fwrite(data.data(), 1, data.size(), f);
 
750
  fclose(f);
 
751
  if (bytes_written != data.size())
 
752
    throw std::runtime_error(g_strerror(errno));
 
753
#else
 
754
  GError *error = NULL;
 
755
  g_file_set_contents(filename.c_str(), data.data(), data.size(), &error);
 
756
  if (error)
 
757
  {
 
758
    std::string msg = error->message;
 
759
    g_error_free(error);
 
760
    throw std::runtime_error(msg);
 
761
  }
 
762
#endif
 
763
}
 
764
 
 
765
//--------------------------------------------------------------------------------------------------
 
766
 
 
767
/**
 
768
 * Read text data from file, converting to \n if necessary.
 
769
 */
 
770
std::string get_text_file_contents(const std::string &filename)
 
771
{
 
772
  FILE *f = base_fopen(filename.c_str(), "r");
 
773
  if (!f)
 
774
    throw std::runtime_error(g_strerror(errno));
 
775
 
 
776
  std::string text;
 
777
  char buffer[4098];
 
778
  size_t c;
 
779
 
 
780
  while ((c = fread(buffer, 1, sizeof(buffer), f)) > 0)
 
781
  {
 
782
    char *bufptr = buffer;
 
783
    char *eobuf = buffer + c;
 
784
    while (bufptr < eobuf)
 
785
    {
 
786
      char *eol = (char*)memchr(bufptr, '\r', eobuf - bufptr);
 
787
      if (eol)
 
788
      {
 
789
        // if \r is in string, we append everyting up to it and then add \n
 
790
        text.append(bufptr, eol-bufptr);
 
791
        text.append("\n");
 
792
        bufptr = eol+1;
 
793
        if (*bufptr == '\n') // make sure it is \r\n and not only \r
 
794
          bufptr++;
 
795
      }
 
796
      else
 
797
      {
 
798
        // no \r found, append the whole thing and go for more
 
799
        text.append(bufptr);
 
800
        break;
 
801
      }
 
802
    }
 
803
  }
 
804
 
 
805
  if (c == (size_t)-1)
 
806
  {
 
807
    int err = errno;
 
808
    fclose(f);
 
809
    throw std::runtime_error(g_strerror(err));
 
810
  }
 
811
 
 
812
  fclose(f);
 
813
 
 
814
  return text;
 
815
}
 
816
 
 
817
//--------------------------------------------------------------------------------------------------
 
818
 
 
819
/** Escape a string to be used in a SQL query
 
820
 * Same code as used by mysql. Handles null bytes in the middle of the string.
 
821
 */
 
822
std::string escape_sql_string(const std::string &s)
 
823
{
 
824
  std::string result;
 
825
  result.reserve(s.size());
 
826
  
 
827
  for (std::string::const_iterator ch= s.begin(); ch != s.end(); ++ch)
 
828
  {
 
829
    char escape= 0;
 
830
    
 
831
    switch (*ch) 
 
832
    {
 
833
      case 0:                             /* Must be escaped for 'mysql' */
 
834
        escape= '0';
 
835
        break;
 
836
      case '\n':                          /* Must be escaped for logs */
 
837
        escape= 'n';
 
838
        break;
 
839
      case '\r':
 
840
        escape= 'r';
 
841
        break;
 
842
      case '\\':
 
843
        escape= '\\';
 
844
        break;
 
845
      case '\'':
 
846
        escape= '\'';
 
847
        break;
 
848
      case '"':                           /* Better safe than sorry */
 
849
        escape= '"';
 
850
        break;
 
851
      case '\032':                        /* This gives problems on Win32 */
 
852
        escape= 'Z';
 
853
        break;
 
854
    }
 
855
    if (escape)
 
856
    {
 
857
      result.push_back('\\');
 
858
      result.push_back(escape);
 
859
    }
 
860
    else
 
861
      result.push_back(*ch);
 
862
  }
 
863
  return result;
 
864
}
 
865
 
 
866
//--------------------------------------------------------------------------------------------------
 
867
 
 
868
// NOTE: This is not the same as escape_sql_string, as embedded ` are escaped as ``, not \`
 
869
std::string escape_backticks(const std::string &s)
 
870
{
 
871
  std::string result;
 
872
  result.reserve(s.size());
 
873
  
 
874
  for (std::string::const_iterator ch= s.begin(); ch != s.end(); ++ch)
 
875
  {
 
876
    char escape= 0;
 
877
    
 
878
    switch (*ch) 
 
879
    {
 
880
      case 0:                             /* Must be escaped for 'mysql' */
 
881
        escape= '0';
 
882
        break;
 
883
      case '\n':                          /* Must be escaped for logs */
 
884
        escape= 'n';
 
885
        break;
 
886
      case '\r':
 
887
        escape= 'r';
 
888
        break;
 
889
      case '\\':
 
890
        escape= '\\';
 
891
        break;
 
892
      case '\'':
 
893
        escape= '\'';
 
894
        break;
 
895
      case '"':                           /* Better safe than sorry */
 
896
        escape= '"';
 
897
        break;
 
898
      case '\032':                        /* This gives problems on Win32 */
 
899
        escape= 'Z';
 
900
        break;
 
901
      case '`':
 
902
        // special case
 
903
        result.push_back('`');
 
904
        break;        
 
905
    }
 
906
    if (escape)
 
907
    {
 
908
      result.push_back('\\');
 
909
      result.push_back(escape);
 
910
    }
 
911
    else
 
912
      result.push_back(*ch);
 
913
  }
 
914
  return result;
 
915
}
 
916
 
 
917
//--------------------------------------------------------------------------------------------------
 
918
 
 
919
/**
 
920
 * Parses the given command line (which must be a usual mysql start command) and extracts the
 
921
 * value for the given parameter. The function can only return options of the form "option-name = option-value"
 
922
 * (both quoted and unquoted).
 
923
 */
 
924
std::string extract_option_from_command_line(const std::string& option, const std::string &command_line)
 
925
{
 
926
  std::string result;
 
927
  size_t position = command_line.find(option);
 
928
  if (position != std::string::npos)
 
929
  {
 
930
    position += option.size(); // Skip option name and find equal sign.
 
931
    while (position < command_line.size() && command_line[position] != '=')
 
932
      position++;
 
933
 
 
934
    if (command_line[position] == '=')
 
935
    {
 
936
      position++;
 
937
 
 
938
      // Skip any white space.
 
939
      while (position < command_line.size() && command_line[position] == ' ')
 
940
        position++;
 
941
 
 
942
      char terminator;
 
943
      if (command_line[position] == '"' || command_line[position] == '\'')
 
944
        terminator = command_line[position++];
 
945
      else
 
946
        terminator = ' ';
 
947
 
 
948
      size_t end_position = command_line.find(terminator, position);
 
949
      if (end_position == std::string::npos)
 
950
      {
 
951
        // Terminator not found means the string was either not properly terminated (if quoted)
 
952
        // or contains no space char. In this case take everything we can get.
 
953
        if (terminator != ' ')
 
954
          position++;
 
955
        result = command_line.substr(position);
 
956
      }
 
957
      else
 
958
        result = command_line.substr(position, end_position - position);
 
959
    }
 
960
  }
 
961
  return result;
 
962
}
 
963
 
 
964
//--------------------------------------------------------------------------------------------------
 
965
 
 
966
/**
 
967
 * Splits the given font description and returns its details in the provided fields.
 
968
 *
 
969
 * @return True if successful, otherwise false.
 
970
 */
 
971
bool parse_font_description(const std::string &fontspec, std::string &font, int &size, bool &bold,
 
972
                            bool &italic)
 
973
{
 
974
  std::vector<std::string> parts= split(fontspec, " ");
 
975
  font = fontspec;
 
976
  size = 12;
 
977
  bold = false;
 
978
  italic = false;
 
979
  
 
980
  if (parts.empty())
 
981
    return false;
 
982
  
 
983
  if (!parts.empty() && sscanf(parts.back().c_str(), "%i", &size) == 1)
 
984
    parts.pop_back();
 
985
  
 
986
  for (int i= 0; i < 2 && !parts.empty(); i++)
 
987
  {
 
988
    if (g_strcasecmp(parts.back().c_str(), "bold")==0)
 
989
    {
 
990
      bold = true;
 
991
      parts.pop_back();
 
992
    }
 
993
    
 
994
    if (g_strcasecmp(parts.back().c_str(), "italic")==0)
 
995
    {
 
996
      italic = true;
 
997
      parts.pop_back();
 
998
    }
 
999
  }
 
1000
  
 
1001
  if (!parts.empty())
 
1002
  {
 
1003
    font= parts[0];
 
1004
    for (unsigned int i= 1; i < parts.size(); i++)
 
1005
      font+= " " + parts[i];
 
1006
  }
 
1007
  return true;  
 
1008
}
 
1009
 
 
1010
//--------------------------------------------------------------------------------------------------
 
1011
 
 
1012
std::string unquote_identifier(const std::string& identifier)
 
1013
{
 
1014
  int start = 0;
 
1015
  int size = identifier.size();
 
1016
 
 
1017
  if (identifier[0] == '"' || identifier[0] == '`')
 
1018
    start++;
 
1019
 
 
1020
  if (identifier[size-1] == '"' || identifier[size-1] == '`')
 
1021
    size--;
 
1022
 
 
1023
  size-=start;
 
1024
 
 
1025
  return identifier.substr(start, size);
 
1026
}
 
1027
 
 
1028
//--------------------------------------------------------------------------------------------------
 
1029
  
 
1030
std::string quote_identifier(const std::string& identifier, const char quote_char)
 
1031
{
 
1032
  return quote_char + identifier + quote_char;
 
1033
}
 
1034
 
 
1035
//--------------------------------------------------------------------------------------------------
 
1036
 
 
1037
/**
 
1038
 * Quotes the given identifier, but only if it needs to be quoted.
 
1039
 * http://dev.mysql.com/doc/refman/5.1/en/identifiers.html specifies what is allowed in unquoted identifiers.
 
1040
 * Leading numbers are not strictly forbidden but discouraged as they may lead to ambiguous behavior.
 
1041
 */
 
1042
std::string quote_identifier_if_needed(const std::string &ident, const char quote_char)
 
1043
{
 
1044
  bool needs_quotation= false;
 
1045
  for (std::string::const_iterator i= ident.begin(); i != ident.end(); ++i)
 
1046
  {
 
1047
    if ((*i >= 'a' && *i <= 'z') || (*i >= 'A' && *i <= 'Z') || (*i >= '0' && *i <= '9')
 
1048
      || (*i == '_') || (*i == '$') || ((unsigned char)(*i) > 0x7F))
 
1049
      continue;
 
1050
    needs_quotation = true;
 
1051
    break;
 
1052
  }
 
1053
  if (needs_quotation)
 
1054
    return quote_char + ident + quote_char;
 
1055
  else
 
1056
    return ident;
 
1057
}
 
1058
 
 
1059
//--------------------------------------------------------------------------------------------------
 
1060
 
 
1061
EolHelpers::Eol_format EolHelpers::detect(const std::string &text)
 
1062
{
 
1063
  std::string::size_type pos= text.find_first_of("\r\n");
 
1064
  if (std::string::npos == pos)
 
1065
    return default_eol_format();
 
1066
  if ('\r' == text[pos])
 
1067
    return ('\n' == text[pos+1]) ? eol_crlf : eol_cr;
 
1068
  else
 
1069
    return eol_lf;
 
1070
}
 
1071
 
 
1072
int EolHelpers::count_lines(const std::string &text)
 
1073
{
 
1074
  Eol_format eol_format= detect(text);
 
1075
  char eol_sym= (eol_cr == eol_format) ? '\r' : '\n';
 
1076
  return std::count(text.begin(), text.end(), eol_sym);
 
1077
}
 
1078
 
 
1079
bool EolHelpers::check(const std::string &text)
 
1080
{
 
1081
  std::string::size_type pos= text.find_first_of("\n\r");
 
1082
  if (std::string::npos == pos)
 
1083
    return true;
 
1084
  Eol_format eol_format= detect(text);
 
1085
  if (eol_lf == eol_format)
 
1086
  {
 
1087
    if (text.find("\r") != std::string::npos)
 
1088
      return false;
 
1089
  }
 
1090
  else if (eol_cr == eol_format)
 
1091
  {
 
1092
    if (text.find("\n") != std::string::npos)
 
1093
      return false;
 
1094
  }
 
1095
  else if (eol_crlf == eol_format)
 
1096
  {
 
1097
    do
 
1098
    {
 
1099
      if (('\n' == text[pos]) || ('\n' != text[pos+1]))
 
1100
        return false;
 
1101
      ++pos;
 
1102
      ++pos;
 
1103
      pos= text.find_first_of("\n\r", pos);
 
1104
    }
 
1105
    while (std::string::npos != pos);
 
1106
  }
 
1107
  return true;
 
1108
}
 
1109
 
 
1110
void EolHelpers::conv(const std::string &src_text, Eol_format src_eol_format, std::string &dest_text, Eol_format dest_eol_format)
 
1111
{
 
1112
  if (src_eol_format == dest_eol_format)
 
1113
    throw std::logic_error("source and target line ending formats coincide, no need to convert");
 
1114
 
 
1115
  const std::string &src_eol= eol(src_eol_format);
 
1116
  const std::string &dest_eol= eol(dest_eol_format);
 
1117
  std::string::size_type src_eol_length= src_eol.size();
 
1118
 
 
1119
  if (dest_eol.size() != src_eol.size())
 
1120
  {
 
1121
    dest_text.clear();
 
1122
    int line_count= count_lines(src_text);
 
1123
    size_t dest_size= src_text.size() + line_count * (dest_eol.size() - src_eol.size());
 
1124
    dest_text.reserve(dest_size);
 
1125
    std::string::size_type prev_pos= 0;
 
1126
    std::string::size_type pos= 0;
 
1127
    while ((pos= src_text.find(src_eol, pos)) != std::string::npos)
 
1128
    {
 
1129
      dest_text.append(src_text, prev_pos, pos-prev_pos).append(dest_eol);
 
1130
      pos+= src_eol_length;
 
1131
      prev_pos= pos;
 
1132
    }
 
1133
    dest_text.append(src_text, prev_pos, std::string::npos);
 
1134
  }
 
1135
  else
 
1136
  {
 
1137
    dest_text= src_text;
 
1138
    std::string::size_type pos= 0;
 
1139
    while ((pos= dest_text.find(src_eol, pos)) != std::string::npos)
 
1140
    {
 
1141
      dest_text.replace(pos, src_eol_length, dest_eol);
 
1142
      pos+= src_eol_length;
 
1143
    }
 
1144
  }
 
1145
}
 
1146
 
 
1147
void EolHelpers::fix(const std::string &src_text, std::string &dest_text, Eol_format eol_format)
 
1148
{
 
1149
  const std::string &dest_eol= eol(eol_format);
 
1150
  std::string::size_type dest_eol_length= dest_eol.size();
 
1151
 
 
1152
  dest_text.clear();
 
1153
  if (eol_crlf == eol_format)
 
1154
  {
 
1155
    int cr_count= std::count(src_text.begin(), src_text.end(), '\r');
 
1156
    int lf_count= std::count(src_text.begin(), src_text.end(), '\n');
 
1157
    int crlf_count= 0;
 
1158
    {
 
1159
      std::string::size_type pos= 0;
 
1160
      while ((pos= src_text.find(dest_eol, pos)) != std::string::npos)
 
1161
      {
 
1162
        ++crlf_count;
 
1163
        pos+= dest_eol_length;
 
1164
      }
 
1165
    }
 
1166
    size_t dest_size= src_text.size() + (cr_count - crlf_count) + (lf_count - crlf_count);
 
1167
    dest_text.reserve(dest_size);
 
1168
  }
 
1169
 
 
1170
  std::string::size_type prev_pos= 0;
 
1171
  std::string::size_type pos= 0;
 
1172
  std::string crlf= "\r\n";
 
1173
  while ((pos= src_text.find_first_of(crlf, pos)) != std::string::npos)
 
1174
  {
 
1175
    dest_text.append(src_text, prev_pos, pos-prev_pos).append(dest_eol);
 
1176
    if (('\r' == src_text[pos]) && ('\n' == src_text[pos+1]))
 
1177
      ++pos;
 
1178
    ++pos;
 
1179
    prev_pos= pos;
 
1180
  }
 
1181
  dest_text.append(src_text, prev_pos, std::string::npos);
 
1182
}
 
1183
 
 
1184
//--------------------------------------------------------------------------------------------------
 
1185
 
 
1186
} // namespace base