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