]> git.lyx.org Git - lyx.git/blob - src/support/lstrings.cpp
* lstring.cpp: parts already implemented in qstring_helpers.cpp
[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/qstring_helpers.h"
19 #include "support/textutils.h"
20
21 #include <boost/tokenizer.hpp>
22 #include "support/assert.h"
23
24 #include <QString>
25 #include <QVector>
26
27 #include <algorithm>
28
29 using namespace std;
30
31 namespace lyx {
32
33 // Using this allows us to have docstring default arguments in headers
34 // without #include "support/docstring" there.
35 docstring const & empty_docstring()
36 {
37         static docstring s;
38         return s;
39 }
40
41 // Using this allows us to have string default arguments in headers
42 // without #include <string>
43 string const & empty_string()
44 {
45         static string s;
46         return s;
47 }
48
49 /**
50  * Convert a QChar into a UCS4 character.
51  * This is a hack (it does only make sense for the common part of the UCS4
52  * and UTF16 encodings) and should not be used.
53  * This does only exist because of performance reasons (a real conversion
54  * using iconv is too slow on windows).
55  */
56 static inline char_type qchar_to_ucs4(QChar const & qchar)
57 {
58         LASSERT(is_utf16(static_cast<char_type>(qchar.unicode())), /**/);
59         return static_cast<char_type>(qchar.unicode());
60 }
61
62
63 /**
64  * Convert a UCS4 character into a QChar.
65  * This is a hack (it does only make sense for the common part of the UCS4
66  * and UTF16 encodings) and should not be used.
67  * This does only exist because of performance reasons (a real conversion
68  * using iconv is too slow on windows).
69  */
70 static inline QChar const ucs4_to_qchar(char_type const ucs4)
71 {
72         LASSERT(is_utf16(ucs4), /**/);
73         return QChar(static_cast<unsigned short>(ucs4));
74 }
75
76
77 namespace {
78         /// Maximum valid UCS4 code point
79         char_type const ucs4_max = 0x10ffff;
80 }
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 isAlphaASCII(char_type c)
97 {
98         return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
99 }
100
101
102 bool isPrintable(char_type c)
103 {
104         if (!is_utf16(c)) {
105                 if (c > ucs4_max)
106                         // outside the UCS4 range
107                         return false;
108                 // assume that all non-utf16 characters are printable
109                 return true;
110         }
111         return ucs4_to_qchar(c).isPrint();
112 }
113
114
115 bool isPrintableNonspace(char_type c)
116 {
117         if (!is_utf16(c)) {
118                 if (c > ucs4_max)
119                         // outside the UCS4 range
120                         return false;
121                 // assume that all non-utf16 characters are printable and
122                 // no space
123                 return true;
124         }
125         QChar const qc = ucs4_to_qchar(c);
126         return qc.isPrint() && !qc.isSpace();
127 }
128
129
130 bool isSpace(char_type c)
131 {
132         if (!is_utf16(c)) {
133                 // assume that no non-utf16 character is a space
134                 // c outside the UCS4 range is catched as well
135                 return false;
136         }
137         QChar const qc = ucs4_to_qchar(c);
138         return qc.isSpace();
139 }
140
141
142 bool isDigit(char_type c)
143 {
144         if (!is_utf16(c))
145                 // assume that no non-utf16 character is a digit
146                 // c outside the UCS4 range is catched as well
147                 return false;
148         return ucs4_to_qchar(c).isDigit();
149 }
150
151
152 bool isDigitASCII(char_type c)
153 {
154         return '0' <= c && c <= '9';
155 }
156
157 namespace support {
158
159 int compare_no_case(docstring const & s, docstring const & s2)
160 {
161         docstring::const_iterator p = s.begin();
162         docstring::const_iterator p2 = s2.begin();
163
164         while (p != s.end() && p2 != s2.end()) {
165                 char_type const lc1 = lowercase(*p);
166                 char_type const lc2 = lowercase(*p2);
167                 if (lc1 != lc2)
168                         return (lc1 < lc2) ? -1 : 1;
169                 ++p;
170                 ++p2;
171         }
172
173         if (s.size() == s2.size())
174                 return 0;
175         if (s.size() < s2.size())
176                 return -1;
177         return 1;
178 }
179
180
181 namespace {
182
183 template<typename Char>
184 Char ascii_tolower(Char c) {
185         if (c >= 'A' && c <= 'Z')
186                 return c - 'A' + 'a';
187         return c;
188 }
189
190 }
191
192
193 int compare_ascii_no_case(string const & s, string const & s2)
194 {
195         string::const_iterator p = s.begin();
196         string::const_iterator p2 = s2.begin();
197
198         while (p != s.end() && p2 != s2.end()) {
199                 int const lc1 = ascii_tolower(*p);
200                 int const lc2 = ascii_tolower(*p2);
201                 if (lc1 != lc2)
202                         return (lc1 < lc2) ? -1 : 1;
203                 ++p;
204                 ++p2;
205         }
206
207         if (s.size() == s2.size())
208                 return 0;
209         if (s.size() < s2.size())
210                 return -1;
211         return 1;
212 }
213
214
215 int compare_ascii_no_case(docstring const & s, docstring const & s2)
216 {
217         docstring::const_iterator p = s.begin();
218         docstring::const_iterator p2 = s2.begin();
219
220         while (p != s.end() && p2 != s2.end()) {
221                 char_type const lc1 = ascii_tolower(*p);
222                 char_type const lc2 = ascii_tolower(*p2);
223                 if (lc1 != lc2)
224                         return (lc1 < lc2) ? -1 : 1;
225                 ++p;
226                 ++p2;
227         }
228
229         if (s.size() == s2.size())
230                 return 0;
231         if (s.size() < s2.size())
232                 return -1;
233         return 1;
234 }
235
236
237 bool isStrInt(string const & str)
238 {
239         if (str.empty())
240                 return false;
241
242         // Remove leading and trailing white space chars.
243         string const tmpstr = trim(str);
244         if (tmpstr.empty())
245                 return false;
246
247         string::const_iterator cit = tmpstr.begin();
248         if ((*cit) == '-')
249                 ++cit;
250
251         string::const_iterator end = tmpstr.end();
252         for (; cit != end; ++cit)
253                 if (!isdigit((*cit)))
254                         return false;
255
256         return true;
257 }
258
259
260 bool isStrUnsignedInt(string const & str)
261 {
262         if (str.empty())
263                 return false;
264
265         // Remove leading and trailing white space chars.
266         string const tmpstr = trim(str);
267         if (tmpstr.empty())
268                 return false;
269
270         string::const_iterator cit = tmpstr.begin();
271         string::const_iterator end = tmpstr.end();
272         for (; cit != end; ++cit)
273                 if (!isdigit((*cit)))
274                         return false;
275
276         return true;
277 }
278
279
280 bool isStrDbl(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         //      if (tmpstr.count('.') > 1) return false;
290
291         string::const_iterator cit = tmpstr.begin();
292         bool found_dot = false;
293         if (*cit == '-')
294                 ++cit;
295         string::const_iterator end = tmpstr.end();
296         for (; cit != end; ++cit) {
297                 if (!isdigit(*cit) && *cit != '.')
298                         return false;
299                 if ('.' == (*cit)) {
300                         if (found_dot)
301                                 return false;
302                         found_dot = true;
303                 }
304         }
305         return true;
306 }
307
308
309 static bool isHexChar(char_type c)
310 {
311         return c == '0' ||
312                 c == '1' ||
313                 c == '2' ||
314                 c == '3' ||
315                 c == '4' ||
316                 c == '5' ||
317                 c == '6' ||
318                 c == '7' ||
319                 c == '8' ||
320                 c == '9' ||
321                 c == 'a' || c == 'A' ||
322                 c == 'b' || c == 'B' ||
323                 c == 'c' || c == 'C' ||
324                 c == 'd' || c == 'D' ||
325                 c == 'e' || c == 'E' ||
326                 c == 'f' || c == 'F';
327 }
328
329
330 bool isHex(docstring const & str)
331 {
332         int index = 0;
333
334         if (str.length() > 2 && str[0] == '0' &&
335             (str[1] == 'x' || str[1] == 'X'))
336                 index = 2;
337
338         int const len = str.length();
339
340         for (; index < len; ++index) {
341                 if (!isHexChar(str[index]))
342                         return false;
343         }
344         return true;
345 }
346
347
348 int hexToInt(docstring const & str)
349 {
350         string s = to_ascii(str);
351         int h;
352         sscanf(s.c_str(), "%x", &h);
353         return h;
354 }
355
356
357 bool isAscii(docstring const & str)
358 {
359         int const len = str.length();
360         for (int i = 0; i < len; ++i)
361                 if (str[i] >= 0x80)
362                         return false;
363         return true;
364 }
365
366
367 bool isAscii(string const & str)
368 {
369         int const len = str.length();
370         for (int i = 0; i < len; ++i)
371                 if (static_cast<unsigned char>(str[i]) >= 0x80)
372                         return false;
373         return true;
374 }
375
376
377 char lowercase(char c)
378 {
379         LASSERT(static_cast<unsigned char>(c) < 0x80, /**/);
380         return char(tolower(c));
381 }
382
383
384 char uppercase(char c)
385 {
386         LASSERT(static_cast<unsigned char>(c) < 0x80, /**/);
387         return char(toupper(c));
388 }
389
390
391 char_type lowercase(char_type c)
392 {
393         if (!is_utf16(c))
394                 // We don't know how to lowercase a non-utf16 char
395                 return c;
396         return qchar_to_ucs4(ucs4_to_qchar(c).toLower());
397 }
398
399
400 char_type uppercase(char_type c)
401 {
402         if (!is_utf16(c))
403                 // We don't know how to uppercase a non-utf16 char
404                 return c;
405         return qchar_to_ucs4(ucs4_to_qchar(c).toUpper());
406 }
407
408
409 namespace {
410
411 // since we cannot use tolower and toupper directly in the
412 // calls to transform yet, we use these helper clases. (Lgb)
413
414 struct local_lowercase {
415         char_type operator()(char_type c) const {
416                 if (!is_utf16(c))
417                         // We don't know how to lowercase a non-utf16 char
418                         return c;
419                 return qchar_to_ucs4(ucs4_to_qchar(c).toLower());
420         }
421 };
422
423 struct local_uppercase {
424         char_type operator()(char_type c) const {
425                 if (!is_utf16(c))
426                         // We don't know how to uppercase a non-utf16 char
427                         return c;
428                 return qchar_to_ucs4(ucs4_to_qchar(c).toUpper());
429         }
430 };
431
432 template<typename Char> struct local_ascii_lowercase {
433         Char operator()(Char c) const { return ascii_tolower(c); }
434 };
435
436 } // end of anon namespace
437
438 docstring const lowercase(docstring const & a)
439 {
440         docstring tmp(a);
441         transform(tmp.begin(), tmp.end(), tmp.begin(), local_lowercase());
442         return tmp;
443 }
444
445
446 docstring const uppercase(docstring const & a)
447 {
448         docstring tmp(a);
449         transform(tmp.begin(), tmp.end(), tmp.begin(), local_uppercase());
450         return tmp;
451 }
452
453
454 string const ascii_lowercase(string const & a)
455 {
456         string tmp(a);
457         transform(tmp.begin(), tmp.end(), tmp.begin(),
458                   local_ascii_lowercase<char>());
459         return tmp;
460 }
461
462
463 docstring const ascii_lowercase(docstring const & a)
464 {
465         docstring tmp(a);
466         transform(tmp.begin(), tmp.end(), tmp.begin(),
467                   local_ascii_lowercase<char_type>());
468         return tmp;
469 }
470
471
472 bool prefixIs(docstring const & a, char_type c)
473 {
474         if (a.empty())
475                 return false;
476         return a[0] == c;
477 }
478
479
480 bool prefixIs(string const & a, string const & pre)
481 {
482         size_t const prelen = pre.length();
483         size_t const alen = a.length();
484         return prelen <= alen && !a.empty() && a.compare(0, prelen, pre) == 0;
485 }
486
487
488 bool prefixIs(docstring const & a, docstring const & pre)
489 {
490         size_t const prelen = pre.length();
491         size_t const alen = a.length();
492         return prelen <= alen && !a.empty() && a.compare(0, prelen, pre) == 0;
493 }
494
495
496 bool suffixIs(string const & a, char c)
497 {
498         if (a.empty()) return false;
499         return a[a.length() - 1] == c;
500 }
501
502
503 bool suffixIs(docstring const & a, char_type c)
504 {
505         if (a.empty())
506                 return false;
507         return a[a.length() - 1] == c;
508 }
509
510
511 bool suffixIs(string const & a, string const & suf)
512 {
513         size_t const suflen = suf.length();
514         size_t const alen = a.length();
515         return suflen <= alen && a.compare(alen - suflen, suflen, suf) == 0;
516 }
517
518
519 bool containsOnly(string const & s, string const & cset)
520 {
521         return s.find_first_not_of(cset) == string::npos;
522 }
523
524
525 // ale970405+lasgoutt-970425
526 // rewritten to use new string (Lgb)
527 string const token(string const & a, char delim, int n)
528 {
529         if (a.empty())
530                 return string();
531
532         size_t k = 0;
533         size_t i = 0;
534
535         // Find delimiter or end of string
536         for (; n--;) {
537                 if ((i = a.find(delim, i)) == string::npos)
538                         break;
539                 else
540                         ++i; // step delim
541         }
542
543         // i is now the n'th delim (or string::npos)
544         if (i == string::npos)
545                 return string();
546
547         k = a.find(delim, i);
548         // k is now the n'th + 1 delim (or string::npos)
549
550         return a.substr(i, k - i);
551 }
552
553
554 docstring const token(docstring const & a, char_type delim, int n)
555 {
556         if (a.empty())
557                 return docstring();
558
559         size_t k = 0;
560         size_t i = 0;
561
562         // Find delimiter or end of string
563         for (; n--;) {
564                 if ((i = a.find(delim, i)) == docstring::npos)
565                         break;
566                 else
567                         ++i; // step delim
568         }
569
570         // i is now the n'th delim (or string::npos)
571         if (i == docstring::npos)
572                 return docstring();
573
574         k = a.find(delim, i);
575         // k is now the n'th + 1 delim (or string::npos)
576
577         return a.substr(i, k - i);
578 }
579
580
581 // this could probably be faster and/or cleaner, but it seems to work (JMarc)
582 // rewritten to use new string (Lgb)
583 int tokenPos(string const & a, char delim, string const & tok)
584 {
585         int i = 0;
586         string str = a;
587         string tmptok;
588
589         while (!str.empty()) {
590                 str = split(str, tmptok, delim);
591                 if (tok == tmptok)
592                         return i;
593                 ++i;
594         }
595         return -1;
596 }
597
598
599 // this could probably be faster and/or cleaner, but it seems to work (JMarc)
600 // rewritten to use new string (Lgb)
601 int tokenPos(docstring const & a, char_type delim, docstring const & tok)
602 {
603         int i = 0;
604         docstring str = a;
605         docstring tmptok;
606
607         while (!str.empty()) {
608                 str = split(str, tmptok, delim);
609                 if (tok == tmptok)
610                         return i;
611                 ++i;
612         }
613         return -1;
614 }
615
616
617 namespace {
618
619 /// Substitute all \a oldchar with \a newchar
620 template<typename Ch> inline
621 basic_string<Ch> const subst_char(basic_string<Ch> const & a,
622                 Ch oldchar, Ch newchar)
623 {
624         typedef basic_string<Ch> String;
625         String tmp(a);
626         typename String::iterator lit = tmp.begin();
627         typename String::iterator end = tmp.end();
628         for (; lit != end; ++lit)
629                 if ((*lit) == oldchar)
630                         (*lit) = newchar;
631         return tmp;
632 }
633
634 /// Substitute all \a oldchar with \a newchar
635 docstring const subst_char(docstring const & a,
636         docstring::value_type oldchar, docstring::value_type newchar)
637 {
638         docstring tmp(a);
639         docstring::iterator lit = tmp.begin();
640         docstring::iterator end = tmp.end();
641         for (; lit != end; ++lit)
642                 if ((*lit) == oldchar)
643                         (*lit) = newchar;
644         return tmp;
645 }
646
647
648 /// substitutes all instances of \a oldstr with \a newstr
649 template<typename String> inline
650 String const subst_string(String const & a,
651                 String const & oldstr, String const & newstr)
652 {
653         LASSERT(!oldstr.empty(), /**/);
654         String lstr = a;
655         size_t i = 0;
656         size_t const olen = oldstr.length();
657         while ((i = lstr.find(oldstr, i)) != string::npos) {
658                 lstr.replace(i, olen, newstr);
659                 i += newstr.length(); // We need to be sure that we dont
660                 // use the same i over and over again.
661         }
662         return lstr;
663 }
664
665 docstring const subst_string(docstring const & a,
666                 docstring const & oldstr, docstring const & newstr)
667 {
668         LASSERT(!oldstr.empty(), /**/);
669         docstring lstr = a;
670         size_t i = 0;
671         size_t const olen = oldstr.length();
672         while ((i = lstr.find(oldstr, i)) != string::npos) {
673                 lstr.replace(i, olen, newstr);
674                 i += newstr.length(); // We need to be sure that we dont
675                 // use the same i over and over again.
676         }
677         return lstr;
678 }
679
680 }
681
682
683 string const subst(string const & a, char oldchar, char newchar)
684 {
685         return subst_char(a, oldchar, newchar);
686 }
687
688
689 docstring const subst(docstring const & a,
690                 char_type oldchar, char_type newchar)
691 {
692         return subst_char(a, oldchar, newchar);
693 }
694
695
696 string const subst(string const & a,
697                 string const & oldstr, string const & newstr)
698 {
699         return subst_string(a, oldstr, newstr);
700 }
701
702
703 docstring const subst(docstring const & a,
704                 docstring const & oldstr, docstring const & newstr)
705 {
706         return subst_string(a, oldstr, newstr);
707 }
708
709
710 docstring const trim(docstring const & a, char const * p)
711 {
712         LASSERT(p, /**/);
713
714         if (a.empty() || !*p)
715                 return a;
716
717         docstring s = from_ascii(p);
718         size_t r = a.find_last_not_of(s);
719         size_t l = a.find_first_not_of(s);
720
721         // Is this the minimal test? (lgb)
722         if (r == docstring::npos && l == docstring::npos)
723                 return docstring();
724
725         return a.substr(l, r - l + 1);
726 }
727
728
729 string const trim(string const & a, char const * p)
730 {
731         LASSERT(p, /**/);
732
733         if (a.empty() || !*p)
734                 return a;
735
736         size_t r = a.find_last_not_of(p);
737         size_t l = a.find_first_not_of(p);
738
739         // Is this the minimal test? (lgb)
740         if (r == string::npos && l == string::npos)
741                 return string();
742
743         return a.substr(l, r - l + 1);
744 }
745
746
747 string const rtrim(string const & a, char const * p)
748 {
749         LASSERT(p, /**/);
750
751         if (a.empty() || !*p)
752                 return a;
753
754         size_t r = a.find_last_not_of(p);
755
756         // Is this test really needed? (Lgb)
757         if (r == string::npos)
758                 return string();
759
760         return a.substr(0, r + 1);
761 }
762
763
764 docstring const rtrim(docstring const & a, char const * p)
765 {
766         LASSERT(p, /**/);
767
768         if (a.empty() || !*p)
769                 return a;
770
771         size_t r = a.find_last_not_of(from_ascii(p));
772
773         // Is this test really needed? (Lgb)
774         if (r == docstring::npos)
775                 return docstring();
776
777         return a.substr(0, r + 1);
778 }
779
780
781 string const ltrim(string const & a, char const * p)
782 {
783         LASSERT(p, /**/);
784         if (a.empty() || !*p)
785                 return a;
786         size_t l = a.find_first_not_of(p);
787         if (l == string::npos)
788                 return string();
789         return a.substr(l, string::npos);
790 }
791
792
793 docstring const ltrim(docstring const & a, char const * p)
794 {
795         LASSERT(p, /**/);
796         if (a.empty() || !*p)
797                 return a;
798         size_t l = a.find_first_not_of(from_ascii(p));
799         if (l == docstring::npos)
800                 return docstring();
801         return a.substr(l, docstring::npos);
802 }
803
804 namespace {
805
806 template<typename String, typename Char> inline
807 String const doSplit(String const & a, String & piece, Char delim)
808 {
809         String tmp;
810         size_t i = a.find(delim);
811         if (i == a.length() - 1) {
812                 piece = a.substr(0, i);
813         } else if (i != String::npos) {
814                 piece = a.substr(0, i);
815                 tmp = a.substr(i + 1);
816         } else if (i == 0) {
817                 piece.erase();
818                 tmp = a.substr(i + 1);
819         } else {
820                 piece = a;
821         }
822         return tmp;
823 }
824
825 template<typename Char> inline
826 docstring const doSplit(docstring const & a, docstring & piece, Char delim)
827 {
828         docstring tmp;
829         size_t i = a.find(delim);
830         if (i == a.length() - 1) {
831                 piece = a.substr(0, i);
832         } else if (i != docstring::npos) {
833                 piece = a.substr(0, i);
834                 tmp = a.substr(i + 1);
835         } else if (i == 0) {
836                 piece.erase();
837                 tmp = a.substr(i + 1);
838         } else {
839                 piece = a;
840         }
841         return tmp;
842 }
843
844 } // anon
845
846
847 string const split(string const & a, string & piece, char delim)
848 {
849         return doSplit(a, piece, delim);
850 }
851
852
853 docstring const split(docstring const & a, docstring & piece, char_type delim)
854 {
855         return doSplit(a, piece, delim);
856 }
857
858
859 string const split(string const & a, char delim)
860 {
861         string tmp;
862         size_t i = a.find(delim);
863         if (i != string::npos) // found delim
864                 tmp = a.substr(i + 1);
865         return tmp;
866 }
867
868
869 // ale970521
870 string const rsplit(string const & a, string & piece, char delim)
871 {
872         string tmp;
873         size_t i = a.rfind(delim);
874         if (i != string::npos) { // delimiter was found
875                 piece = a.substr(0, i);
876                 tmp = a.substr(i + 1);
877         } else { // delimiter was not found
878                 piece.erase();
879         }
880         return tmp;
881 }
882
883
884 docstring const escape(docstring const & lab)
885 {
886         char_type hexdigit[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
887                                    '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
888         docstring enc;
889         for (size_t i = 0; i < lab.length(); ++i) {
890                 char_type c = lab[i];
891                 if (c >= 128 || c == '=' || c == '%') {
892                         // Although char_type is a 32 bit type we know that
893                         // UCS4 occupies only 21 bits, so we don't need to
894                         // encode bigger values. Test for 2^24 because we
895                         // can encode that with the 6 hex digits that are
896                         // needed for 21 bits anyway.
897                         LASSERT(c < (1 << 24), /**/);
898                         enc += '=';
899                         enc += hexdigit[(c>>20) & 15];
900                         enc += hexdigit[(c>>16) & 15];
901                         enc += hexdigit[(c>>12) & 15];
902                         enc += hexdigit[(c>> 8) & 15];
903                         enc += hexdigit[(c>> 4) & 15];
904                         enc += hexdigit[ c      & 15];
905                 } else {
906                         enc += c;
907                 }
908         }
909         return enc;
910 }
911
912
913 namespace {
914
915 template<typename String> vector<String> const
916 getVectorFromStringT(String const & str, String const & delim)
917 {
918 // Lars would like this code to go, but for now his replacement (below)
919 // doesn't fullfil the same function. I have, therefore, reactivated the
920 // old code for now. Angus 11 Nov 2002.
921 #if 1
922         vector<String> vec;
923         if (str.empty())
924                 return vec;
925         String keys = rtrim(str);
926         while (true) {
927                 size_t const idx = keys.find(delim);
928                 if (idx == String::npos) {
929                         vec.push_back(ltrim(keys));
930                         break;
931                 }
932                 String const key = trim(keys.substr(0, idx));
933                 if (!key.empty())
934                         vec.push_back(key);
935                 size_t const start = idx + delim.size();
936                 keys = keys.substr(start);
937         }
938         return vec;
939 #else
940         typedef boost::char_separator<typename String::value_type> Separator;
941         typedef boost::tokenizer<Separator, typename String::const_iterator, String> Tokenizer;
942         Separator sep(delim.c_str());
943         Tokenizer tokens(str, sep);
944         return vector<String>(tokens.begin(), tokens.end());
945 #endif
946 }
947
948 } // namespace anon
949
950
951 vector<string> const getVectorFromString(string const & str,
952                                          string const & delim)
953 {
954         return getVectorFromStringT<string>(str, delim);
955 }
956
957
958 vector<docstring> const getVectorFromString(docstring const & str,
959                                             docstring const & delim)
960 {
961         return getVectorFromStringT<docstring>(str, delim);
962 }
963
964
965 // the same vice versa
966 string const getStringFromVector(vector<string> const & vec,
967                                  string const & delim)
968 {
969         string str;
970         int i = 0;
971         for (vector<string>::const_iterator it = vec.begin();
972              it != vec.end(); ++it) {
973                 string item = trim(*it);
974                 if (item.empty())
975                         continue;
976                 if (i++ > 0)
977                         str += delim;
978                 str += item;
979         }
980         return str;
981 }
982
983
984 int findToken(char const * const str[], string const & search_token)
985 {
986         int i = 0;
987
988         while (str[i][0] && str[i] != search_token)
989                 ++i;
990         if (!str[i][0])
991                 i = -1;
992         return i;
993 }
994
995
996 docstring const externalLineEnding(docstring const & str)
997 {
998 #if defined(__APPLE__)
999         // The MAC clipboard uses \r for lineendings, and we use \n
1000         return subst(str, '\n', '\r');
1001 #elif defined (_WIN32) || (defined (__CYGWIN__) && defined (X_DISPLAY_MISSING))
1002         // Windows clipboard uses \r\n for lineendings, and we use \n
1003         return subst(str, from_ascii("\n"), from_ascii("\r\n"));
1004 #else
1005         return str;
1006 #endif
1007 }
1008
1009
1010 docstring const internalLineEnding(docstring const & str)
1011 {
1012         docstring const s = subst(str, from_ascii("\r\n"), from_ascii("\n"));
1013         return subst(s, '\r', '\n');
1014 }
1015
1016
1017 template<>
1018 docstring bformat(docstring const & fmt, int arg1)
1019 {
1020         LASSERT(contains(fmt, from_ascii("%1$d")), /**/);
1021         docstring const str = subst(fmt, from_ascii("%1$d"), convert<docstring>(arg1));
1022         return subst(str, from_ascii("%%"), from_ascii("%"));
1023 }
1024
1025
1026 template<>
1027 docstring bformat(docstring const & fmt, long arg1)
1028 {
1029         LASSERT(contains(fmt, from_ascii("%1$d")), /**/);
1030         docstring const str = subst(fmt, from_ascii("%1$d"), convert<docstring>(arg1));
1031         return subst(str, from_ascii("%%"), from_ascii("%"));
1032 }
1033
1034
1035 template<>
1036 docstring bformat(docstring const & fmt, unsigned int arg1)
1037 {
1038         LASSERT(contains(fmt, from_ascii("%1$d")), /**/);
1039         docstring const str = subst(fmt, from_ascii("%1$d"), convert<docstring>(arg1));
1040         return subst(str, from_ascii("%%"), from_ascii("%"));
1041 }
1042
1043
1044 template<>
1045 docstring bformat(docstring const & fmt, docstring arg1)
1046 {
1047         LASSERT(contains(fmt, from_ascii("%1$s")), /**/);
1048         docstring const str = subst(fmt, from_ascii("%1$s"), arg1);
1049         return subst(str, from_ascii("%%"), from_ascii("%"));
1050 }
1051
1052
1053 template<>
1054 docstring bformat(docstring const & fmt, char * arg1)
1055 {
1056         LASSERT(contains(fmt, from_ascii("%1$s")), /**/);
1057         docstring const str = subst(fmt, from_ascii("%1$s"), from_ascii(arg1));
1058         return subst(str, from_ascii("%%"), from_ascii("%"));
1059 }
1060
1061
1062 template<>
1063 docstring bformat(docstring const & fmt, docstring arg1, docstring arg2)
1064 {
1065         LASSERT(contains(fmt, from_ascii("%1$s")), /**/);
1066         LASSERT(contains(fmt, from_ascii("%2$s")), /**/);
1067         docstring str = subst(fmt, from_ascii("%1$s"), arg1);
1068         str = subst(str, from_ascii("%2$s"), arg2);
1069         return subst(str, from_ascii("%%"), from_ascii("%"));
1070 }
1071
1072
1073 template<>
1074 docstring bformat(docstring const & fmt, char const * arg1, docstring arg2)
1075 {
1076         LASSERT(contains(fmt, from_ascii("%1$s")), /**/);
1077         LASSERT(contains(fmt, from_ascii("%2$s")), /**/);
1078         docstring str = subst(fmt, from_ascii("%1$s"), from_ascii(arg1));
1079         str = subst(fmt, from_ascii("%2$s"), arg2);
1080         return subst(str, from_ascii("%%"), from_ascii("%"));
1081 }
1082
1083
1084 template<>
1085 docstring bformat(docstring const & fmt, int arg1, int arg2)
1086 {
1087         LASSERT(contains(fmt, from_ascii("%1$d")), /**/);
1088         LASSERT(contains(fmt, from_ascii("%2$d")), /**/);
1089         docstring str = subst(fmt, from_ascii("%1$d"), convert<docstring>(arg1));
1090         str = subst(str, from_ascii("%2$d"), convert<docstring>(arg2));
1091         return subst(str, from_ascii("%%"), from_ascii("%"));
1092 }
1093
1094
1095 template<>
1096 docstring bformat(docstring const & fmt, docstring arg1, docstring arg2, docstring arg3)
1097 {
1098         LASSERT(contains(fmt, from_ascii("%1$s")), /**/);
1099         LASSERT(contains(fmt, from_ascii("%2$s")), /**/);
1100         LASSERT(contains(fmt, from_ascii("%3$s")), /**/);
1101         docstring str = subst(fmt, from_ascii("%1$s"), arg1);
1102         str = subst(str, from_ascii("%2$s"), arg2);
1103         str = subst(str, from_ascii("%3$s"), arg3);
1104         return subst(str, from_ascii("%%"), from_ascii("%"));
1105 }
1106
1107
1108 template<>
1109 docstring bformat(docstring const & fmt,
1110                docstring arg1, docstring arg2, docstring arg3, docstring arg4)
1111 {
1112         LASSERT(contains(fmt, from_ascii("%1$s")), /**/);
1113         LASSERT(contains(fmt, from_ascii("%2$s")), /**/);
1114         LASSERT(contains(fmt, from_ascii("%3$s")), /**/);
1115         LASSERT(contains(fmt, from_ascii("%4$s")), /**/);
1116         docstring str = subst(fmt, from_ascii("%1$s"), arg1);
1117         str = subst(str, from_ascii("%2$s"), arg2);
1118         str = subst(str, from_ascii("%3$s"), arg3);
1119         str = subst(str, from_ascii("%4$s"), arg4);
1120         return subst(str, from_ascii("%%"), from_ascii("%"));
1121 }
1122
1123 } // namespace support
1124 } // namespace lyx