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