]> git.lyx.org Git - features.git/blob - src/support/lstrings.cpp
Fix bug #7741: incorrect locale when starting R from LyX?
[features.git] / src / support / lstrings.cpp
1 /**
2  * \file lstrings.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Jean-Marc Lasgouttes
8  * \author Dekel Tsur
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "support/lstrings.h"
16
17 #include "support/convert.h"
18 #include "support/gettext.h"
19 #include "support/qstring_helpers.h"
20 #include "support/textutils.h"
21
22 #include <boost/tokenizer.hpp>
23 #include "support/lassert.h"
24
25 #include <QString>
26 #include <QVector>
27
28 #include <cstdio>
29 #include <algorithm>
30
31 using namespace std;
32
33 namespace lyx {
34
35 // Using this allows us to have docstring default arguments in headers
36 // without #include "support/docstring" there.
37 docstring const & empty_docstring()
38 {
39         static docstring s;
40         return s;
41 }
42
43 // Using this allows us to have string default arguments in headers
44 // without #include <string>
45 string const & empty_string()
46 {
47         static string s;
48         return s;
49 }
50
51 namespace {
52 /**
53  * Convert a QChar into a UCS4 character.
54  * This is a hack (it does only make sense for the common part of the UCS4
55  * and UTF16 encodings) and should not be used.
56  * This does only exist because of performance reasons (a real conversion
57  * using iconv is too slow on windows).
58  */
59 inline char_type qchar_to_ucs4(QChar const & qchar)
60 {
61         LASSERT(is_utf16(static_cast<char_type>(qchar.unicode())), /**/);
62         return static_cast<char_type>(qchar.unicode());
63 }
64
65 /**
66  * Convert a UCS4 character into a QChar.
67  * This is a hack (it does only make sense for the common part of the UCS4
68  * and UTF16 encodings) and should not be used.
69  * This does only exist because of performance reasons (a real conversion
70  * using iconv is too slow on windows).
71  */
72 inline QChar const ucs4_to_qchar(char_type const ucs4)
73 {
74         LASSERT(is_utf16(ucs4), /**/);
75         return QChar(static_cast<unsigned short>(ucs4));
76 }
77
78 /// Maximum valid UCS4 code point
79 char_type const ucs4_max = 0x10ffff;
80 } // anon namespace
81
82
83 bool isLetterChar(char_type c)
84 {
85         if (!is_utf16(c)) {
86                 if (c > ucs4_max)
87                         // outside the UCS4 range
88                         return false;
89                 // assume that all non-utf16 characters are letters
90                 return true;
91         }
92         return ucs4_to_qchar(c).isLetter();
93 }
94
95
96 bool isLower(char_type c)
97 {
98         if (!is_utf16(c))
99                 return false;
100         return ucs4_to_qchar(c).isLower();
101 }
102
103
104 bool isAlphaASCII(char_type c)
105 {
106         return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
107 }
108
109
110 bool isPrintable(char_type c)
111 {
112         if (!is_utf16(c)) {
113                 if (c > ucs4_max)
114                         // outside the UCS4 range
115                         return false;
116                 // assume that all non-utf16 characters are printable
117                 return true;
118         }
119         // Not yet recognized by QChar::isPrint()
120         // See https://bugreports.qt-project.org/browse/QTBUG-12144
121         // LATIN CAPITAL LETTER SHARP S
122         else if (c == 0x1e9e)
123                 return true;
124         return ucs4_to_qchar(c).isPrint();
125 }
126
127
128 bool isPrintableNonspace(char_type c)
129 {
130         if (!is_utf16(c)) {
131                 if (c > ucs4_max)
132                         // outside the UCS4 range
133                         return false;
134                 // assume that all non-utf16 characters are printable and
135                 // no space
136                 return true;
137         }
138         QChar const qc = ucs4_to_qchar(c);
139         return qc.isPrint() && !qc.isSpace();
140 }
141
142
143 bool isSpace(char_type c)
144 {
145         if (!is_utf16(c)) {
146                 // assume that no non-utf16 character is a space
147                 // c outside the UCS4 range is catched as well
148                 return false;
149         }
150         QChar const qc = ucs4_to_qchar(c);
151         return qc.isSpace();
152 }
153
154
155 bool isNumber(char_type c)
156 {
157         if (!is_utf16(c))
158                 // assume that no non-utf16 character is a numeral
159                 // c outside the UCS4 range is catched as well
160                 return false;
161         return ucs4_to_qchar(c).isNumber();
162 }
163
164
165 bool isDigitASCII(char_type c)
166 {
167         return '0' <= c && c <= '9';
168 }
169
170
171 bool isAlnumASCII(char_type c)
172 {
173         return isAlphaASCII(c) || isDigitASCII(c);
174 }
175
176
177 namespace support {
178
179 int compare_no_case(docstring const & s, docstring const & s2)
180 {
181         docstring::const_iterator p = s.begin();
182         docstring::const_iterator p2 = s2.begin();
183
184         while (p != s.end() && p2 != s2.end()) {
185                 char_type const lc1 = lowercase(*p);
186                 char_type const lc2 = lowercase(*p2);
187                 if (lc1 != lc2)
188                         return (lc1 < lc2) ? -1 : 1;
189                 ++p;
190                 ++p2;
191         }
192
193         if (s.size() == s2.size())
194                 return 0;
195         if (s.size() < s2.size())
196                 return -1;
197         return 1;
198 }
199
200
201 namespace {
202
203 template<typename Char>
204 Char ascii_tolower(Char c) {
205         if (c >= 'A' && c <= 'Z')
206                 return c - 'A' + 'a';
207         return c;
208 }
209
210 }
211
212
213 int compare_ascii_no_case(string const & s, string const & s2)
214 {
215         string::const_iterator p = s.begin();
216         string::const_iterator p2 = s2.begin();
217
218         while (p != s.end() && p2 != s2.end()) {
219                 int const lc1 = ascii_tolower(*p);
220                 int const lc2 = ascii_tolower(*p2);
221                 if (lc1 != lc2)
222                         return (lc1 < lc2) ? -1 : 1;
223                 ++p;
224                 ++p2;
225         }
226
227         if (s.size() == s2.size())
228                 return 0;
229         if (s.size() < s2.size())
230                 return -1;
231         return 1;
232 }
233
234
235 int compare_ascii_no_case(docstring const & s, docstring const & s2)
236 {
237         docstring::const_iterator p = s.begin();
238         docstring::const_iterator p2 = s2.begin();
239
240         while (p != s.end() && p2 != s2.end()) {
241                 char_type const lc1 = ascii_tolower(*p);
242                 char_type const lc2 = ascii_tolower(*p2);
243                 if (lc1 != lc2)
244                         return (lc1 < lc2) ? -1 : 1;
245                 ++p;
246                 ++p2;
247         }
248
249         if (s.size() == s2.size())
250                 return 0;
251         if (s.size() < s2.size())
252                 return -1;
253         return 1;
254 }
255
256
257 bool isStrInt(string const & str)
258 {
259         if (str.empty())
260                 return false;
261
262         // Remove leading and trailing white space chars.
263         string const tmpstr = trim(str);
264         if (tmpstr.empty())
265                 return false;
266
267         string::const_iterator cit = tmpstr.begin();
268         if ((*cit) == '-')
269                 ++cit;
270
271         string::const_iterator end = tmpstr.end();
272         for (; cit != end; ++cit)
273                 if (!isDigitASCII(*cit))
274                         return false;
275
276         return true;
277 }
278
279
280 bool isStrUnsignedInt(string const & str)
281 {
282         if (str.empty())
283                 return false;
284
285         // Remove leading and trailing white space chars.
286         string const tmpstr = trim(str);
287         if (tmpstr.empty())
288                 return false;
289
290         string::const_iterator cit = tmpstr.begin();
291         string::const_iterator end = tmpstr.end();
292         for (; cit != end; ++cit)
293                 if (!isDigitASCII(*cit))
294                         return false;
295
296         return true;
297 }
298
299
300 bool isStrDbl(string const & str)
301 {
302         if (str.empty())
303                 return false;
304
305         // Remove leading and trailing white space chars.
306         string const tmpstr = trim(str);
307         if (tmpstr.empty())
308                 return false;
309         //      if (tmpstr.count('.') > 1) return false;
310
311         string::const_iterator cit = tmpstr.begin();
312         bool found_dot = false;
313         if (*cit == '-')
314                 ++cit;
315         string::const_iterator end = tmpstr.end();
316         for (; cit != end; ++cit) {
317                 if (!isDigitASCII(*cit) && *cit != '.')
318                         return false;
319                 if ('.' == (*cit)) {
320                         if (found_dot)
321                                 return false;
322                         found_dot = true;
323                 }
324         }
325         return true;
326 }
327
328
329 bool hasDigitASCII(docstring const & str)
330 {
331         docstring::const_iterator cit = str.begin();
332         docstring::const_iterator const end = str.end();
333         for (; cit != end; ++cit)
334                 if (isDigitASCII(*cit))
335                         return true;
336         return false;
337 }
338
339
340 bool isHexChar(char_type c)
341 {
342         return c == '0' ||
343                 c == '1' ||
344                 c == '2' ||
345                 c == '3' ||
346                 c == '4' ||
347                 c == '5' ||
348                 c == '6' ||
349                 c == '7' ||
350                 c == '8' ||
351                 c == '9' ||
352                 c == 'a' || c == 'A' ||
353                 c == 'b' || c == 'B' ||
354                 c == 'c' || c == 'C' ||
355                 c == 'd' || c == 'D' ||
356                 c == 'e' || c == 'E' ||
357                 c == 'f' || c == 'F';
358 }
359
360
361 bool isHex(docstring const & str)
362 {
363         int index = 0;
364
365         if (str.length() > 2 && str[0] == '0' &&
366             (str[1] == 'x' || str[1] == 'X'))
367                 index = 2;
368
369         int const len = str.length();
370
371         for (; index < len; ++index) {
372                 if (!isHexChar(str[index]))
373                         return false;
374         }
375         return true;
376 }
377
378
379 int hexToInt(docstring const & str)
380 {
381         string s = to_ascii(str);
382         int h;
383         sscanf(s.c_str(), "%x", &h);
384         return h;
385 }
386
387
388 bool isAscii(docstring const & str)
389 {
390         int const len = str.length();
391         for (int i = 0; i < len; ++i)
392                 if (str[i] >= 0x80)
393                         return false;
394         return true;
395 }
396
397
398 bool isAscii(string const & str)
399 {
400         int const len = str.length();
401         for (int i = 0; i < len; ++i)
402                 if (static_cast<unsigned char>(str[i]) >= 0x80)
403                         return false;
404         return true;
405 }
406
407
408 char lowercase(char c)
409 {
410         LASSERT(static_cast<unsigned char>(c) < 0x80, /**/);
411         return char(tolower(c));
412 }
413
414
415 char uppercase(char c)
416 {
417         LASSERT(static_cast<unsigned char>(c) < 0x80, /**/);
418         return char(toupper(c));
419 }
420
421
422 char_type lowercase(char_type c)
423 {
424         if (!is_utf16(c))
425                 // We don't know how to lowercase a non-utf16 char
426                 return c;
427         return qchar_to_ucs4(ucs4_to_qchar(c).toLower());
428 }
429
430
431 char_type uppercase(char_type c)
432 {
433         if (!is_utf16(c))
434                 // We don't know how to uppercase a non-utf16 char
435                 return c;
436         return qchar_to_ucs4(ucs4_to_qchar(c).toUpper());
437 }
438
439
440 bool isLowerCase(char_type ch) {
441         return lowercase(ch) == ch;
442 }
443
444
445 bool isUpperCase(char_type ch) {
446         return uppercase(ch) == ch;
447 }
448
449
450 namespace {
451
452 // since we cannot use tolower and toupper directly in the
453 // calls to transform yet, we use these helper clases. (Lgb)
454
455 struct local_lowercase {
456         char_type operator()(char_type c) const {
457                 return lowercase(c);
458         }
459 };
460
461 struct local_uppercase {
462         char_type operator()(char_type c) const {
463                 return uppercase(c);
464         }
465 };
466
467 template<typename Char> struct local_ascii_lowercase {
468         Char operator()(Char c) const { return ascii_tolower(c); }
469 };
470
471 } // end of anon namespace
472
473
474 docstring const lowercase(docstring const & a)
475 {
476         docstring tmp(a);
477         transform(tmp.begin(), tmp.end(), tmp.begin(), local_lowercase());
478         return tmp;
479 }
480
481
482 docstring const uppercase(docstring const & a)
483 {
484         docstring tmp(a);
485         transform(tmp.begin(), tmp.end(), tmp.begin(), local_uppercase());
486         return tmp;
487 }
488
489
490 string const ascii_lowercase(string const & a)
491 {
492         string tmp(a);
493         transform(tmp.begin(), tmp.end(), tmp.begin(),
494                   local_ascii_lowercase<char>());
495         return tmp;
496 }
497
498
499 docstring const ascii_lowercase(docstring const & a)
500 {
501         docstring tmp(a);
502         transform(tmp.begin(), tmp.end(), tmp.begin(),
503                   local_ascii_lowercase<char_type>());
504         return tmp;
505 }
506
507
508 char_type superscript(char_type c)
509 {
510         switch (c) {
511                 case    '2': return 0x00b2;
512                 case    '3': return 0x00b3;
513                 case    '1': return 0x00b9;
514                 case    '0': return 0x2070;
515                 case    'i': return 0x2071;
516                 case    '4': return 0x2074;
517                 case    '5': return 0x2075;
518                 case    '6': return 0x2076;
519                 case    '7': return 0x2077;
520                 case    '8': return 0x2078;
521                 case    '9': return 0x2079;
522                 case    '+': return 0x207a;
523                 case    '-': return 0x207b;
524                 case    '=': return 0x207c;
525                 case    '(': return 0x207d;
526                 case    ')': return 0x207e;
527                 case    'n': return 0x207f;
528                 case    'h': return 0x02b0;
529                 case 0x0266: return 0x02b1; // LATIN SMALL LETTER H WITH HOOK
530                 case    'j': return 0x02b2;
531                 case    'r': return 0x02b3;
532                 case 0x0279: return 0x02b4; // LATIN SMALL LETTER TURNED R
533                 case 0x027b: return 0x02b5; // LATIN SMALL LETTER TURNED R WITH HOOK
534                 case 0x0281: return 0x02b6; // LATIN SMALL LETTER CAPITAL INVERTED R
535                 case    'w': return 0x02b7;
536                 case    'y': return 0x02b8;
537 //              case 0x0294: return 0x02c0; // LATIN LETTER GLOTTAL STOP)
538 //              case 0x0295: return 0x02c1; // LATIN LETTER PHARYNGEAL VOICED FRICATIVE
539                                             // (= LATIN LETTER REVERSED GLOTTAL STOP)
540                 case    'l': return 0x02e1;
541                 case    's': return 0x02e2;
542                 case    'x': return 0x02e3;
543 //              case 0x0295: return 0x02e4; // LATIN SMALL LETTER REVERSED GLOTTAL STOP
544                 case    'A': return 0x1d2c;
545                 case 0x00c6: return 0x1d2d; // LATIN CAPITAL LETTER AE
546                 case    'B': return 0x1d2e;
547                 case    'D': return 0x1d30;
548                 case    'E': return 0x1d31;
549                 case    'G': return 0x1d33;
550                 case    'H': return 0x1d34;
551                 case    'I': return 0x1d35;
552                 case    'J': return 0x1d36;
553                 case    'K': return 0x1d37;
554                 case    'L': return 0x1d38;
555                 case    'M': return 0x1d39;
556                 case    'N': return 0x1d3a;
557                 case    'O': return 0x1d3c;
558                 case    'P': return 0x1d3e;
559                 case    'R': return 0x1d3f;
560                 case    'T': return 0x1d40;
561                 case    'U': return 0x1d41;
562                 case    'W': return 0x1d42;
563                 case    'a': return 0x1d43;
564                 case 0x0250: return 0x1d44; // LATIN SMALL LETTER TURNED A
565                 case 0x0251: return 0x1d45; // LATIN SMALL LETTER ALPHA
566                 case    'b': return 0x1d47;
567                 case    'd': return 0x1d48;
568                 case    'e': return 0x1d49;
569                 case 0x0259: return 0x1d4a; // LATIN SMALL LETTER SCHWA
570                 case 0x025b: return 0x1d4b; // LATIN SMALL LETTER OPEN E
571                 case 0x1d08: return 0x1d4c; // LATIN SMALL LETTER TURNED OPEN E
572                 case    'g': return 0x1d4d;
573                 case 0x1d09: return 0x1d4e; // LATIN SMALL LETTER TURNED I
574                 case    'k': return 0x1d4f;
575                 case    'm': return 0x1d50;
576                 case 0x014b: return 0x1d51; // LATIN SMALL LETTER ENG
577                 case    'o': return 0x1d52;
578                 case 0x0254: return 0x1d53; // LATIN SMALL LETTER OPEN O
579                 case 0x1d16: return 0x1d54; // LATIN SMALL LETTER TOP HALF O
580                 case 0x1d17: return 0x1d55; // LATIN SMALL LETTER BOTTOM HALF O
581                 case    'p': return 0x1d56;
582                 case    't': return 0x1d57;
583                 case    'u': return 0x1d58;
584                 case 0x1d1d: return 0x1d59; // LATIN SMALL LETTER SIDEWAYS U
585                 case 0x1d1f: return 0x1d5a; // LATIN SMALL LETTER SIDEWAYS TURNED M
586                 case    'v': return 0x1d5b;
587                 case 0x03b2: return 0x1d5d; // GREEK SMALL LETTER BETA
588                 case 0x03b3: return 0x1d5e; // GREEK SMALL LETTER GAMMA
589                 case 0x03b4: return 0x1d5f; // GREEK SMALL LETTER DELTA
590                 case 0x03c6: return 0x1d60; // GREEK SMALL LETTER PHI
591                 case 0x03c7: return 0x1d61; // GREEK SMALL LETTER CHI
592         }
593         return c;
594 }
595
596
597 char_type subscript(char_type c)
598 {
599         switch (c) {
600                 case    'i': return 0x1d62;
601                 case    'r': return 0x1d63;
602                 case    'u': return 0x1d64;
603                 case    'v': return 0x1d65;
604                 case 0x03b2: return 0x1d66; // GREEK SMALL LETTER BETA
605                 case 0x03b3: return 0x1d67; // GREEK SMALL LETTER GAMMA
606                 case 0x03c1: return 0x1d68; // GREEK SMALL LETTER RHO
607                 case 0x03c6: return 0x1d69; // GREEK SMALL LETTER PHI
608                 case 0x03c7: return 0x1d6a; // GREEK SMALL LETTER CHI
609                 case    '0': return 0x2080;
610                 case    '1': return 0x2081;
611                 case    '2': return 0x2082;
612                 case    '3': return 0x2083;
613                 case    '4': return 0x2084;
614                 case    '5': return 0x2085;
615                 case    '6': return 0x2086;
616                 case    '7': return 0x2087;
617                 case    '8': return 0x2088;
618                 case    '9': return 0x2089;
619                 case    '+': return 0x208a;
620                 case    '-': return 0x208b;
621                 case    '=': return 0x208c;
622                 case    '(': return 0x208d;
623                 case    ')': return 0x208e;
624                 case    'a': return 0x2090;
625                 case    'e': return 0x2091;
626                 case    'o': return 0x2092;
627                 case    'x': return 0x2093;
628                 case 0x0259: return 0x2093; // LATIN SMALL LETTER SCHWA
629         }
630         return c;
631 }
632
633
634 bool prefixIs(docstring const & a, char_type c)
635 {
636         if (a.empty())
637                 return false;
638         return a[0] == c;
639 }
640
641
642 bool prefixIs(string const & a, string const & pre)
643 {
644         size_t const prelen = pre.length();
645         size_t const alen = a.length();
646         return prelen <= alen && !a.empty() && a.compare(0, prelen, pre) == 0;
647 }
648
649
650 bool prefixIs(docstring const & a, docstring const & pre)
651 {
652         size_t const prelen = pre.length();
653         size_t const alen = a.length();
654         return prelen <= alen && !a.empty() && a.compare(0, prelen, pre) == 0;
655 }
656
657
658 bool suffixIs(string const & a, char c)
659 {
660         if (a.empty())
661                 return false;
662         return a[a.length() - 1] == c;
663 }
664
665
666 bool suffixIs(docstring const & a, char_type c)
667 {
668         if (a.empty())
669                 return false;
670         return a[a.length() - 1] == c;
671 }
672
673
674 bool suffixIs(string const & a, string const & suf)
675 {
676         size_t const suflen = suf.length();
677         size_t const alen = a.length();
678         return suflen <= alen && a.compare(alen - suflen, suflen, suf) == 0;
679 }
680
681
682 bool suffixIs(docstring const & a, docstring const & suf)
683 {
684         size_t const suflen = suf.length();
685         size_t const alen = a.length();
686         return suflen <= alen && a.compare(alen - suflen, suflen, suf) == 0;
687 }
688
689
690 bool containsOnly(string const & s, string const & cset)
691 {
692         return s.find_first_not_of(cset) == string::npos;
693 }
694
695
696 // ale970405+lasgoutt-970425
697 // rewritten to use new string (Lgb)
698 string const token(string const & a, char delim, int n)
699 {
700         if (a.empty())
701                 return string();
702
703         size_t k = 0;
704         size_t i = 0;
705
706         // Find delimiter or end of string
707         for (; n--;) {
708                 if ((i = a.find(delim, i)) == string::npos)
709                         break;
710                 else
711                         ++i; // step delim
712         }
713
714         // i is now the n'th delim (or string::npos)
715         if (i == string::npos)
716                 return string();
717
718         k = a.find(delim, i);
719         // k is now the n'th + 1 delim (or string::npos)
720
721         return a.substr(i, k - i);
722 }
723
724
725 docstring const token(docstring const & a, char_type delim, int n)
726 {
727         if (a.empty())
728                 return docstring();
729
730         size_t k = 0;
731         size_t i = 0;
732
733         // Find delimiter or end of string
734         for (; n--;) {
735                 if ((i = a.find(delim, i)) == docstring::npos)
736                         break;
737                 else
738                         ++i; // step delim
739         }
740
741         // i is now the n'th delim (or string::npos)
742         if (i == docstring::npos)
743                 return docstring();
744
745         k = a.find(delim, i);
746         // k is now the n'th + 1 delim (or string::npos)
747
748         return a.substr(i, k - i);
749 }
750
751
752 // this could probably be faster and/or cleaner, but it seems to work (JMarc)
753 // rewritten to use new string (Lgb)
754 int tokenPos(string const & a, char delim, string const & tok)
755 {
756         int i = 0;
757         string str = a;
758         string tmptok;
759
760         while (!str.empty()) {
761                 str = split(str, tmptok, delim);
762                 if (tok == tmptok)
763                         return i;
764                 ++i;
765         }
766         return -1;
767 }
768
769
770 // this could probably be faster and/or cleaner, but it seems to work (JMarc)
771 // rewritten to use new string (Lgb)
772 int tokenPos(docstring const & a, char_type delim, docstring const & tok)
773 {
774         int i = 0;
775         docstring str = a;
776         docstring tmptok;
777
778         while (!str.empty()) {
779                 str = split(str, tmptok, delim);
780                 if (tok == tmptok)
781                         return i;
782                 ++i;
783         }
784         return -1;
785 }
786
787
788 namespace {
789
790 /// Substitute all \a oldchar with \a newchar
791 template<typename Ch> inline
792 basic_string<Ch> const subst_char(basic_string<Ch> const & a,
793                 Ch oldchar, Ch newchar)
794 {
795         typedef basic_string<Ch> String;
796         String tmp(a);
797         typename String::iterator lit = tmp.begin();
798         typename String::iterator end = tmp.end();
799         for (; lit != end; ++lit)
800                 if ((*lit) == oldchar)
801                         (*lit) = newchar;
802         return tmp;
803 }
804
805
806 /// Substitute all \a oldchar with \a newchar
807 docstring const subst_char(docstring const & a,
808         docstring::value_type oldchar, docstring::value_type newchar)
809 {
810         docstring tmp(a);
811         docstring::iterator lit = tmp.begin();
812         docstring::iterator end = tmp.end();
813         for (; lit != end; ++lit)
814                 if ((*lit) == oldchar)
815                         (*lit) = newchar;
816         return tmp;
817 }
818
819
820 /// substitutes all instances of \a oldstr with \a newstr
821 template<typename String> inline
822 String const subst_string(String const & a,
823                 String const & oldstr, String const & newstr)
824 {
825         LASSERT(!oldstr.empty(), /**/);
826         String lstr = a;
827         size_t i = 0;
828         size_t const olen = oldstr.length();
829         while ((i = lstr.find(oldstr, i)) != string::npos) {
830                 lstr.replace(i, olen, newstr);
831                 i += newstr.length(); // We need to be sure that we dont
832                 // use the same i over and over again.
833         }
834         return lstr;
835 }
836
837
838 docstring const subst_string(docstring const & a,
839                 docstring const & oldstr, docstring const & newstr)
840 {
841         LASSERT(!oldstr.empty(), /**/);
842         docstring lstr = a;
843         size_t i = 0;
844         size_t const olen = oldstr.length();
845         while ((i = lstr.find(oldstr, i)) != string::npos) {
846                 lstr.replace(i, olen, newstr);
847                 i += newstr.length(); // We need to be sure that we dont
848                 // use the same i over and over again.
849         }
850         return lstr;
851 }
852
853 }
854
855
856 string const subst(string const & a, char oldchar, char newchar)
857 {
858         return subst_char(a, oldchar, newchar);
859 }
860
861
862 docstring const subst(docstring const & a,
863                 char_type oldchar, char_type newchar)
864 {
865         return subst_char(a, oldchar, newchar);
866 }
867
868
869 string const subst(string const & a,
870                 string const & oldstr, string const & newstr)
871 {
872         return subst_string(a, oldstr, newstr);
873 }
874
875
876 docstring const subst(docstring const & a,
877                 docstring const & oldstr, docstring const & newstr)
878 {
879         return subst_string(a, oldstr, newstr);
880 }
881
882
883 /// Count all occurences of char \a chr inside \a str
884 int count_char(docstring const & str, docstring::value_type chr)
885 {
886         int count = 0;
887         docstring::const_iterator lit = str.begin();
888         docstring::const_iterator end = str.end();
889         for (; lit != end; ++lit)
890                 if ((*lit) == chr)
891                         count++;
892         return count;
893 }
894
895
896 docstring const trim(docstring const & a, char const * p)
897 {
898         LASSERT(p, /**/);
899
900         if (a.empty() || !*p)
901                 return a;
902
903         docstring s = from_ascii(p);
904         size_t r = a.find_last_not_of(s);
905         size_t l = a.find_first_not_of(s);
906
907         // Is this the minimal test? (lgb)
908         if (r == docstring::npos && l == docstring::npos)
909                 return docstring();
910
911         return a.substr(l, r - l + 1);
912 }
913
914
915 string const trim(string const & a, char const * p)
916 {
917         LASSERT(p, /**/);
918
919         if (a.empty() || !*p)
920                 return a;
921
922         size_t r = a.find_last_not_of(p);
923         size_t l = a.find_first_not_of(p);
924
925         // Is this the minimal test? (lgb)
926         if (r == string::npos && l == string::npos)
927                 return string();
928
929         return a.substr(l, r - l + 1);
930 }
931
932
933 string const rtrim(string const & a, char const * p)
934 {
935         LASSERT(p, /**/);
936
937         if (a.empty() || !*p)
938                 return a;
939
940         size_t r = a.find_last_not_of(p);
941
942         // Is this test really needed? (Lgb)
943         if (r == string::npos)
944                 return string();
945
946         return a.substr(0, r + 1);
947 }
948
949
950 docstring const rtrim(docstring const & a, char const * p)
951 {
952         LASSERT(p, /**/);
953
954         if (a.empty() || !*p)
955                 return a;
956
957         size_t r = a.find_last_not_of(from_ascii(p));
958
959         // Is this test really needed? (Lgb)
960         if (r == docstring::npos)
961                 return docstring();
962
963         return a.substr(0, r + 1);
964 }
965
966
967 string const ltrim(string const & a, char const * p)
968 {
969         LASSERT(p, /**/);
970         if (a.empty() || !*p)
971                 return a;
972         size_t l = a.find_first_not_of(p);
973         if (l == string::npos)
974                 return string();
975         return a.substr(l, string::npos);
976 }
977
978
979 docstring const ltrim(docstring const & a, char const * p)
980 {
981         LASSERT(p, /**/);
982         if (a.empty() || !*p)
983                 return a;
984         size_t l = a.find_first_not_of(from_ascii(p));
985         if (l == docstring::npos)
986                 return docstring();
987         return a.substr(l, docstring::npos);
988 }
989
990 namespace {
991
992 template<typename String, typename Char> inline
993 String const doSplit(String const & a, String & piece, Char delim)
994 {
995         String tmp;
996         size_t i = a.find(delim);
997         if (i == a.length() - 1) {
998                 piece = a.substr(0, i);
999         } else if (i != String::npos) {
1000                 piece = a.substr(0, i);
1001                 tmp = a.substr(i + 1);
1002         } else if (i == 0) {
1003                 piece.erase();
1004                 tmp = a.substr(i + 1);
1005         } else {
1006                 piece = a;
1007         }
1008         return tmp;
1009 }
1010
1011 template<typename Char> inline
1012 docstring const doSplit(docstring const & a, docstring & piece, Char delim)
1013 {
1014         docstring tmp;
1015         size_t i = a.find(delim);
1016         if (i == a.length() - 1) {
1017                 piece = a.substr(0, i);
1018         } else if (i != docstring::npos) {
1019                 piece = a.substr(0, i);
1020                 tmp = a.substr(i + 1);
1021         } else if (i == 0) {
1022                 piece.erase();
1023                 tmp = a.substr(i + 1);
1024         } else {
1025                 piece = a;
1026         }
1027         return tmp;
1028 }
1029
1030 } // anon
1031
1032
1033 string const split(string const & a, string & piece, char delim)
1034 {
1035         return doSplit(a, piece, delim);
1036 }
1037
1038
1039 docstring const split(docstring const & a, docstring & piece, char_type delim)
1040 {
1041         return doSplit(a, piece, delim);
1042 }
1043
1044
1045 string const split(string const & a, char delim)
1046 {
1047         string tmp;
1048         size_t i = a.find(delim);
1049         if (i != string::npos) // found delim
1050                 tmp = a.substr(i + 1);
1051         return tmp;
1052 }
1053
1054
1055 // ale970521
1056 string const rsplit(string const & a, string & piece, char delim)
1057 {
1058         string tmp;
1059         size_t i = a.rfind(delim);
1060         if (i != string::npos) { // delimiter was found
1061                 piece = a.substr(0, i);
1062                 tmp = a.substr(i + 1);
1063         } else { // delimiter was not found
1064                 piece.erase();
1065         }
1066         return tmp;
1067 }
1068
1069
1070 docstring const rsplit(docstring const & a, char_type delim)
1071 {
1072         docstring tmp;
1073         size_t i = a.rfind(delim);
1074         if (i != string::npos)
1075                 tmp = a.substr(i + 1);
1076         return tmp;
1077 }
1078
1079
1080 docstring const escape(docstring const & lab)
1081 {
1082         char_type hexdigit[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
1083                                    '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
1084         docstring enc;
1085         for (size_t i = 0; i < lab.length(); ++i) {
1086                 char_type c = lab[i];
1087                 if (c >= 128 || c == '=' || c == '%' || c == '#' || c == '$'
1088                     || c == '}' || c == '{' || c == ']' || c == '[' || c == '&') {
1089                         // Although char_type is a 32 bit type we know that
1090                         // UCS4 occupies only 21 bits, so we don't need to
1091                         // encode bigger values. Test for 2^24 because we
1092                         // can encode that with the 6 hex digits that are
1093                         // needed for 21 bits anyway.
1094                         LASSERT(c < (1 << 24), /**/);
1095                         enc += '=';
1096                         enc += hexdigit[(c>>20) & 15];
1097                         enc += hexdigit[(c>>16) & 15];
1098                         enc += hexdigit[(c>>12) & 15];
1099                         enc += hexdigit[(c>> 8) & 15];
1100                         enc += hexdigit[(c>> 4) & 15];
1101                         enc += hexdigit[ c      & 15];
1102                 } else {
1103                         enc += c;
1104                 }
1105         }
1106         return enc;
1107 }
1108
1109
1110 namespace {
1111
1112 // this doesn't check whether str is empty, so do that first.
1113 vector<docstring> wrapToVec(docstring const & str, int ind,
1114                             size_t const width)
1115 {
1116         docstring s = trim(str);
1117         if (s.empty())
1118                 return vector<docstring>();
1119
1120         docstring indent;
1121         if (ind < 0) {
1122                 indent.insert(0, -ind, ' ');
1123                 ind = 0;
1124         } else if (ind > 0)
1125                 s.insert(0, ind, ' ');
1126
1127         vector<docstring> retval;
1128         while (s.size() > width) {
1129                 // find the last space within the first 'width' chars
1130                 size_t const i = s.find_last_of(' ', width - 1);
1131                 if (i == docstring::npos || i <= size_t(ind)) {
1132                         // no space found
1133                         s = s.substr(0, width - 3) + "...";
1134                         break;
1135                 }
1136                 retval.push_back(s.substr(0, i));
1137                 s = indent + s.substr(i);
1138                 ind = indent.size();
1139         }
1140         if (!s.empty())
1141                 retval.push_back(s);
1142         return retval;
1143 }
1144
1145 }
1146
1147
1148 docstring wrap(docstring const & str, int const ind, size_t const width)
1149 {
1150         docstring s = trim(str);
1151         if (s.empty())
1152                 return docstring();
1153
1154         vector<docstring> const svec = wrapToVec(str, ind, width);
1155         return getStringFromVector(svec, from_ascii("\n"));
1156 }
1157
1158
1159 docstring wrapParas(docstring const & str, int const indent,
1160                     size_t const width, size_t const maxlines)
1161 {
1162         docstring const dots = from_ascii("...");
1163         if (str.empty())
1164                 return docstring();
1165
1166         vector<docstring> const pars = getVectorFromString(str, from_ascii("\n"), true);
1167         vector<docstring> retval;
1168
1169         vector<docstring>::const_iterator it = pars.begin();
1170         vector<docstring>::const_iterator const en = pars.end();
1171         for (; it != en; ++it) {
1172                 vector<docstring> tmp = wrapToVec(*it, indent, width);
1173                 size_t const nlines = tmp.size();
1174                 if (nlines == 0)
1175                         continue;
1176                 size_t const curlines = retval.size();
1177                 if (maxlines > 0 && curlines + nlines > maxlines) {
1178                         tmp.resize(maxlines - curlines);
1179                         docstring last = tmp.back();
1180                         size_t const lsize = last.size();
1181                         if (lsize > width - 3) {
1182                                 size_t const i = last.find_last_of(' ', width - 3);
1183                                 if (i == docstring::npos || i <= size_t(indent))
1184                                         // no space found
1185                                         last = last.substr(0, lsize - 3) + dots;
1186                                 else
1187                                         last = last.substr(0, i) + dots;
1188                         } else
1189                                 last += dots;
1190                         tmp.pop_back();
1191                         tmp.push_back(last);
1192                 }
1193                 retval.insert(retval.end(), tmp.begin(), tmp.end());
1194                 if (maxlines > 0 && retval.size() >= maxlines)
1195                         break;
1196         }
1197         return getStringFromVector(retval, from_ascii("\n"));
1198 }
1199
1200
1201 namespace {
1202
1203 template<typename String> vector<String> const
1204 getVectorFromStringT(String const & str, String const & delim, bool keepempty)
1205 {
1206 // Lars would like this code to go, but for now his replacement (below)
1207 // doesn't fullfil the same function. I have, therefore, reactivated the
1208 // old code for now. Angus 11 Nov 2002.
1209 #if 1
1210         vector<String> vec;
1211         if (str.empty())
1212                 return vec;
1213         String keys = rtrim(str);
1214         while (true) {
1215                 size_t const idx = keys.find(delim);
1216                 if (idx == String::npos) {
1217                         vec.push_back(ltrim(keys));
1218                         break;
1219                 }
1220                 String const key = trim(keys.substr(0, idx));
1221                 if (!key.empty() || keepempty)
1222                         vec.push_back(key);
1223                 size_t const start = idx + delim.size();
1224                 keys = keys.substr(start);
1225         }
1226         return vec;
1227 #else
1228         typedef boost::char_separator<typename String::value_type> Separator;
1229         typedef boost::tokenizer<Separator, typename String::const_iterator, String> Tokenizer;
1230         Separator sep(delim.c_str());
1231         Tokenizer tokens(str, sep);
1232         return vector<String>(tokens.begin(), tokens.end());
1233 #endif
1234 }
1235
1236
1237 template<typename String> const String
1238         getStringFromVector(vector<String> const & vec, String const & delim)
1239 {
1240         String str;
1241         typename vector<String>::const_iterator it = vec.begin();
1242         typename vector<String>::const_iterator en = vec.end();
1243         for (; it != en; ++it) {
1244                 String item = trim(*it);
1245                 if (item.empty())
1246                         continue;
1247                 if (!str.empty())
1248                         str += delim;
1249                 str += item;
1250         }
1251         return str;
1252 }
1253
1254 } // namespace anon
1255
1256
1257 vector<string> const getVectorFromString(string const & str,
1258                                          string const & delim,
1259                                          bool keepempty)
1260 {
1261         return getVectorFromStringT<string>(str, delim, keepempty);
1262 }
1263
1264
1265 vector<docstring> const getVectorFromString(docstring const & str,
1266                                             docstring const & delim,
1267                                             bool keepempty)
1268 {
1269         return getVectorFromStringT<docstring>(str, delim, keepempty);
1270 }
1271
1272
1273 string const getStringFromVector(vector<string> const & vec,
1274                                  string const & delim)
1275 {
1276         return getStringFromVector<string>(vec, delim);
1277 }
1278
1279
1280 docstring const getStringFromVector(vector<docstring> const & vec,
1281                                     docstring const & delim)
1282 {
1283         return getStringFromVector<docstring>(vec, delim);
1284 }
1285
1286
1287 int findToken(char const * const str[], string const & search_token)
1288 {
1289         int i = 0;
1290
1291         while (str[i][0] && str[i] != search_token)
1292                 ++i;
1293         if (!str[i][0])
1294                 i = -1;
1295         return i;
1296 }
1297
1298
1299 template<>
1300 docstring bformat(docstring const & fmt, int arg1)
1301 {
1302         LASSERT(contains(fmt, from_ascii("%1$d")), /**/);
1303         docstring const str = subst(fmt, from_ascii("%1$d"), convert<docstring>(arg1));
1304         return subst(str, from_ascii("%%"), from_ascii("%"));
1305 }
1306
1307
1308 template<>
1309 docstring bformat(docstring const & fmt, long arg1)
1310 {
1311         LASSERT(contains(fmt, from_ascii("%1$d")), /**/);
1312         docstring const str = subst(fmt, from_ascii("%1$d"), convert<docstring>(arg1));
1313         return subst(str, from_ascii("%%"), from_ascii("%"));
1314 }
1315
1316
1317 template<>
1318 docstring bformat(docstring const & fmt, unsigned int arg1)
1319 {
1320         LASSERT(contains(fmt, from_ascii("%1$d")), /**/);
1321         docstring const str = subst(fmt, from_ascii("%1$d"), convert<docstring>(arg1));
1322         return subst(str, from_ascii("%%"), from_ascii("%"));
1323 }
1324
1325
1326 template<>
1327 docstring bformat(docstring const & fmt, docstring arg1)
1328 {
1329         LASSERT(contains(fmt, from_ascii("%1$s")), /**/);
1330         docstring const str = subst(fmt, from_ascii("%1$s"), arg1);
1331         return subst(str, from_ascii("%%"), from_ascii("%"));
1332 }
1333
1334
1335 template<>
1336 docstring bformat(docstring const & fmt, char * arg1)
1337 {
1338         LASSERT(contains(fmt, from_ascii("%1$s")), /**/);
1339         docstring const str = subst(fmt, from_ascii("%1$s"), from_ascii(arg1));
1340         return subst(str, from_ascii("%%"), from_ascii("%"));
1341 }
1342
1343
1344 template<>
1345 docstring bformat(docstring const & fmt, docstring arg1, docstring arg2)
1346 {
1347         LASSERT(contains(fmt, from_ascii("%1$s")), /**/);
1348         LASSERT(contains(fmt, from_ascii("%2$s")), /**/);
1349         docstring str = subst(fmt, from_ascii("%1$s"), arg1);
1350         str = subst(str, from_ascii("%2$s"), arg2);
1351         return subst(str, from_ascii("%%"), from_ascii("%"));
1352 }
1353
1354
1355 template<>
1356 docstring bformat(docstring const & fmt, docstring arg1, int arg2)
1357 {
1358         LASSERT(contains(fmt, from_ascii("%1$s")), /**/);
1359         LASSERT(contains(fmt, from_ascii("%2$d")), /**/);
1360         docstring str = subst(fmt, from_ascii("%1$s"), arg1);
1361         str = subst(str, from_ascii("%2$d"), convert<docstring>(arg2));
1362         return subst(str, from_ascii("%%"), from_ascii("%"));
1363 }
1364
1365
1366 template<>
1367 docstring bformat(docstring const & fmt, char const * arg1, docstring arg2)
1368 {
1369         LASSERT(contains(fmt, from_ascii("%1$s")), /**/);
1370         LASSERT(contains(fmt, from_ascii("%2$s")), /**/);
1371         docstring str = subst(fmt, from_ascii("%1$s"), from_ascii(arg1));
1372         str = subst(fmt, from_ascii("%2$s"), arg2);
1373         return subst(str, from_ascii("%%"), from_ascii("%"));
1374 }
1375
1376
1377 template<>
1378 docstring bformat(docstring const & fmt, int arg1, int arg2)
1379 {
1380         LASSERT(contains(fmt, from_ascii("%1$d")), /**/);
1381         LASSERT(contains(fmt, from_ascii("%2$d")), /**/);
1382         docstring str = subst(fmt, from_ascii("%1$d"), convert<docstring>(arg1));
1383         str = subst(str, from_ascii("%2$d"), convert<docstring>(arg2));
1384         return subst(str, from_ascii("%%"), from_ascii("%"));
1385 }
1386
1387
1388 template<>
1389 docstring bformat(docstring const & fmt, docstring arg1, docstring arg2, docstring arg3)
1390 {
1391         LASSERT(contains(fmt, from_ascii("%1$s")), /**/);
1392         LASSERT(contains(fmt, from_ascii("%2$s")), /**/);
1393         LASSERT(contains(fmt, from_ascii("%3$s")), /**/);
1394         docstring str = subst(fmt, from_ascii("%1$s"), arg1);
1395         str = subst(str, from_ascii("%2$s"), arg2);
1396         str = subst(str, from_ascii("%3$s"), arg3);
1397         return subst(str, from_ascii("%%"), from_ascii("%"));
1398 }
1399
1400
1401 template<>
1402 docstring bformat(docstring const & fmt,
1403                docstring arg1, docstring arg2, docstring arg3, docstring arg4)
1404 {
1405         LASSERT(contains(fmt, from_ascii("%1$s")), /**/);
1406         LASSERT(contains(fmt, from_ascii("%2$s")), /**/);
1407         LASSERT(contains(fmt, from_ascii("%3$s")), /**/);
1408         LASSERT(contains(fmt, from_ascii("%4$s")), /**/);
1409         docstring str = subst(fmt, from_ascii("%1$s"), arg1);
1410         str = subst(str, from_ascii("%2$s"), arg2);
1411         str = subst(str, from_ascii("%3$s"), arg3);
1412         str = subst(str, from_ascii("%4$s"), arg4);
1413         return subst(str, from_ascii("%%"), from_ascii("%"));
1414 }
1415
1416 } // namespace support
1417 } // namespace lyx