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