]> git.lyx.org Git - lyx.git/blob - src/support/lstrings.C
Change MenuBackend and the other menuclasses to store a docstring. Do the required...
[lyx.git] / src / support / lstrings.C
1 /**
2  * \file lstrings.C
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 #include "support/lyxlib.h"
16 #include "support/convert.h"
17
18 #include "debug.h"
19
20 #include <boost/tokenizer.hpp>
21 #include <boost/assert.hpp>
22
23 #ifndef I_AM_NOT_AFRAID_OF_HEADER_LIBRARIES
24 #if USE_BOOST_FORMAT
25 #include <boost/format.hpp>
26 #endif
27 #endif
28
29 #include <cctype>
30 #include <cstdlib>
31
32 #include <algorithm>
33 #include <sstream>
34
35 using std::transform;
36 using std::string;
37 using std::vector;
38
39 #ifndef CXX_GLOBAL_CSTD
40 using std::isdigit;
41 using std::tolower;
42 using std::toupper;
43 #endif
44
45
46 namespace lyx {
47 namespace support {
48
49 int compare_no_case(string const & s, string const & s2)
50 {
51         string::const_iterator p = s.begin();
52         string::const_iterator p2 = s2.begin();
53
54         while (p != s.end() && p2 != s2.end()) {
55                 int const lc1 = tolower(*p);
56                 int const lc2 = tolower(*p2);
57                 if (lc1 != lc2)
58                         return (lc1 < lc2) ? -1 : 1;
59                 ++p;
60                 ++p2;
61         }
62
63         if (s.size() == s2.size())
64                 return 0;
65         if (s.size() < s2.size())
66                 return -1;
67         return 1;
68 }
69
70
71 int compare_no_case(docstring const & s, docstring const & s2)
72 {
73         docstring::const_iterator p = s.begin();
74         docstring::const_iterator p2 = s2.begin();
75
76         while (p != s.end() && p2 != s2.end()) {
77                 int const lc1 = tolower(*p);
78                 int const lc2 = tolower(*p2);
79                 if (lc1 != lc2)
80                         return (lc1 < lc2) ? -1 : 1;
81                 ++p;
82                 ++p2;
83         }
84
85         if (s.size() == s2.size())
86                 return 0;
87         if (s.size() < s2.size())
88                 return -1;
89         return 1;
90 }
91
92
93 namespace {
94         int ascii_tolower(int c) {
95                 if (c >= 'A' && c <= 'Z')
96                         return c - 'A' + 'a';
97                 return c;
98         }
99 }
100
101
102 int compare_ascii_no_case(string const & s, string const & s2)
103 {
104         string::const_iterator p = s.begin();
105         string::const_iterator p2 = s2.begin();
106
107         while (p != s.end() && p2 != s2.end()) {
108                 int const lc1 = ascii_tolower(*p);
109                 int const lc2 = ascii_tolower(*p2);
110                 if (lc1 != lc2)
111                         return (lc1 < lc2) ? -1 : 1;
112                 ++p;
113                 ++p2;
114         }
115
116         if (s.size() == s2.size())
117                 return 0;
118         if (s.size() < s2.size())
119                 return -1;
120         return 1;
121 }
122
123
124 int compare_no_case(string const & s, string const & s2, unsigned int len)
125 {
126         string::const_iterator p = s.begin();
127         string::const_iterator p2 = s2.begin();
128         unsigned int i = 0;
129         while (i < len && p != s.end() && p2 != s2.end()) {
130                 int const lc1 = tolower(*p);
131                 int const lc2 = tolower(*p2);
132                 if (lc1 != lc2)
133                         return (lc1 < lc2) ? -1 : 1;
134                 ++i;
135                 ++p;
136                 ++p2;
137         }
138
139         if (s.size() >= len && s2.size() >= len)
140                 return 0;
141         if (s.size() < s2.size())
142                 return -1;
143         return 1;
144 }
145
146
147 bool isStrInt(string const & str)
148 {
149         if (str.empty()) return false;
150
151         // Remove leading and trailing white space chars.
152         string const tmpstr = trim(str);
153         if (tmpstr.empty()) return false;
154
155         string::const_iterator cit = tmpstr.begin();
156         if ((*cit) == '-') ++cit;
157         string::const_iterator end = tmpstr.end();
158         for (; cit != end; ++cit) {
159                 if (!isdigit((*cit))) return false;
160         }
161         return true;
162 }
163
164
165 bool isStrUnsignedInt(string const & str)
166 {
167         if (str.empty()) return false;
168
169         // Remove leading and trailing white space chars.
170         string const tmpstr = trim(str);
171         if (tmpstr.empty()) return false;
172
173         string::const_iterator cit = tmpstr.begin();
174         string::const_iterator end = tmpstr.end();
175         for (; cit != end; ++cit) {
176                 if (!isdigit((*cit))) return false;
177         }
178         return true;
179 }
180
181
182 bool isStrDbl(string const & str)
183 {
184         if (str.empty()) return false;
185
186         // Remove leading and trailing white space chars.
187         string const tmpstr = trim(str);
188         if (tmpstr.empty()) return false;
189         //      if (1 < tmpstr.count('.')) return false;
190
191         string::const_iterator cit = tmpstr.begin();
192         bool found_dot(false);
193         if ((*cit) == '-') ++cit;
194         string::const_iterator end = tmpstr.end();
195         for (; cit != end; ++cit) {
196                 if (!isdigit((*cit))
197                     && '.' != (*cit)) {
198                         return false;
199                 }
200                 if ('.' == (*cit)) {
201                         if (found_dot) {
202                                 return false;
203                         } else {
204                                 found_dot = true;
205                         }
206                 }
207         }
208         return true;
209 }
210
211
212 char lowercase(char c)
213 {
214         return char(tolower(c));
215 }
216
217
218 char uppercase(char c)
219 {
220         return char(toupper(c));
221 }
222
223 // FIXME for lowercase() and uppercase() function below:
224 // 1) std::tolower() and std::toupper() are templates that
225 // compile fine with char_type. With the test (c >= 256) we
226 // do not trust these function to do the right thing with
227 // unicode char.
228 // 2) these functions use the current locale, which is wrong
229 // if it is not latin1 based (latin1 is a subset of UCS4).
230
231 char_type lowercase(char_type c)
232 {
233         if (c >= 256)
234                 return c;
235
236         return tolower(c);
237 }
238
239
240 char_type uppercase(char_type c)
241 {
242         if (c >= 256)
243                 return c;
244
245         return toupper(c);
246 }
247
248
249 namespace {
250
251 // since we cannot use std::tolower and std::toupper directly in the
252 // calls to std::transform yet, we use these helper clases. (Lgb)
253
254 struct local_lowercase {
255         char operator()(char c) const {
256                 return tolower(c);
257         }
258 };
259
260 struct local_uppercase {
261         char operator()(char c) const {
262                 return toupper(c);
263         }
264 };
265
266 struct local_ascii_lowercase {
267         char operator()(char c) const {
268                 return ascii_tolower(c);
269         }
270 };
271
272 } // end of anon namespace
273
274 string const lowercase(string const & a)
275 {
276         string tmp(a);
277         transform(tmp.begin(), tmp.end(), tmp.begin(), local_lowercase());
278         return tmp;
279 }
280
281 string const uppercase(string const & a)
282 {
283         string tmp(a);
284         transform(tmp.begin(), tmp.end(), tmp.begin(), local_uppercase());
285         return tmp;
286 }
287
288
289 string const ascii_lowercase(string const & a)
290 {
291         string tmp(a);
292         transform(tmp.begin(), tmp.end(), tmp.begin(),
293                   local_ascii_lowercase());
294         return tmp;
295 }
296
297
298 bool prefixIs(string const & a, string const & pre)
299 {
300         string::size_type const prelen = pre.length();
301         string::size_type const alen = a.length();
302
303         if (prelen > alen || a.empty())
304                 return false;
305         else {
306 #if defined(STD_STRING_IS_GOOD)
307                 return a.compare(0, prelen, pre) == 0;
308 #else
309                 return ::strncmp(a.c_str(), pre.c_str(), prelen) == 0;
310 #endif
311         }
312 }
313
314
315 bool suffixIs(string const & a, char c)
316 {
317         if (a.empty()) return false;
318         return a[a.length() - 1] == c;
319 }
320
321
322 bool suffixIs(string const & a, string const & suf)
323 {
324         string::size_type const suflen = suf.length();
325         string::size_type const alen = a.length();
326
327         if (suflen > alen) {
328                 return false;
329         } else {
330 #if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD)
331                 string tmp(a, alen - suflen);
332                 return ::strncmp(tmp.c_str(), suf.c_str(), suflen) == 0;
333 #else
334                 return a.compare(alen - suflen, suflen, suf) == 0;
335 #endif
336         }
337 }
338
339
340 bool containsOnly(string const & s, string const & cset)
341 {
342         return s.find_first_not_of(cset) == string::npos;
343 }
344
345
346 // ale970405+lasgoutt-970425
347 // rewritten to use new string (Lgb)
348 string const token(string const & a, char delim, int n)
349 {
350         if (a.empty()) return string();
351
352         string::size_type k = 0;
353         string::size_type i = 0;
354
355         // Find delimiter or end of string
356         for (; n--;)
357                 if ((i = a.find(delim, i)) == string::npos)
358                         break;
359                 else
360                         ++i; // step delim
361         // i is now the n'th delim (or string::npos)
362         if (i == string::npos) return string();
363         k = a.find(delim, i);
364         // k is now the n'th + 1 delim (or string::npos)
365
366         return a.substr(i, k - i);
367 }
368
369
370 docstring const token(docstring const & a, char_type delim, int n)
371 {
372         if (a.empty()) return docstring();
373
374         string::size_type k = 0;
375         string::size_type i = 0;
376
377         // Find delimiter or end of string
378         for (; n--;)
379                 if ((i = a.find(delim, i)) == docstring::npos)
380                         break;
381                 else
382                         ++i; // step delim
383         // i is now the n'th delim (or string::npos)
384         if (i == docstring::npos) return docstring();
385         k = a.find(delim, i);
386         // k is now the n'th + 1 delim (or string::npos)
387
388         return a.substr(i, k - i);
389 }
390
391
392 // this could probably be faster and/or cleaner, but it seems to work (JMarc)
393 // rewritten to use new string (Lgb)
394 int tokenPos(string const & a, char delim, string const & tok)
395 {
396         int i = 0;
397         string str(a);
398         string tmptok;
399
400         while (!str.empty()) {
401                 str = split(str, tmptok, delim);
402                 if (tok == tmptok)
403                         return i;
404                 ++i;
405         }
406         return -1;
407 }
408
409
410 namespace {
411
412 template<typename Ch> inline
413 std::basic_string<Ch> const subst_char(std::basic_string<Ch> const & a,
414                 Ch oldchar, Ch newchar)
415 {
416         typedef std::basic_string<Ch> String;
417         String tmp(a);
418         typename String::iterator lit = tmp.begin();
419         typename String::iterator end = tmp.end();
420         for (; lit != end; ++lit)
421                 if ((*lit) == oldchar)
422                         (*lit) = newchar;
423         return tmp;
424 }
425
426
427 template<typename String> inline
428 String const subst_string(String const & a,
429                 String const & oldstr, String const & newstr)
430 {
431         BOOST_ASSERT(!oldstr.empty());
432         String lstr = a;
433         typename String::size_type i = 0;
434         typename String::size_type const olen = oldstr.length();
435         while ((i = lstr.find(oldstr, i)) != string::npos) {
436                 lstr.replace(i, olen, newstr);
437                 i += newstr.length(); // We need to be sure that we dont
438                 // use the same i over and over again.
439         }
440         return lstr;
441 }
442
443 }
444
445
446 string const subst(string const & a, char oldchar, char newchar)
447 {
448         return subst_char(a, oldchar, newchar);
449 }
450
451
452 docstring const subst(docstring const & a,
453                 char_type oldchar, char_type newchar)
454 {
455         return subst_char(a, oldchar, newchar);
456 }
457
458
459 string const subst(string const & a,
460                 string const & oldstr, string const & newstr)
461 {
462         return subst_string(a, oldstr, newstr);
463 }
464
465
466 docstring const subst(docstring const & a,
467                 docstring const & oldstr, docstring const & newstr)
468 {
469         return subst_string(a, oldstr, newstr);
470 }
471
472
473 string const trim(string const & a, char const * p)
474 {
475         BOOST_ASSERT(p);
476
477         if (a.empty() || !*p)
478                 return a;
479
480         string::size_type r = a.find_last_not_of(p);
481         string::size_type l = a.find_first_not_of(p);
482
483         // Is this the minimal test? (lgb)
484         if (r == string::npos && l == string::npos)
485                 return string();
486
487         return a.substr(l, r - l + 1);
488 }
489
490
491 string const rtrim(string const & a, char const * p)
492 {
493         BOOST_ASSERT(p);
494
495         if (a.empty() || !*p)
496                 return a;
497
498         string::size_type r = a.find_last_not_of(p);
499
500         // Is this test really needed? (Lgb)
501         if (r == string::npos)
502                 return string();
503
504         return a.substr(0, r + 1);
505 }
506
507
508 string const ltrim(string const & a, char const * p)
509 {
510         BOOST_ASSERT(p);
511
512         if (a.empty() || !*p)
513                 return a;
514
515         string::size_type l = a.find_first_not_of(p);
516
517         if (l == string::npos)
518                 return string();
519
520         return a.substr(l, string::npos);
521 }
522
523
524 string const split(string const & a, string & piece, char delim)
525 {
526         string tmp;
527         string::size_type i = a.find(delim);
528         if (i == a.length() - 1) {
529                 piece = a.substr(0, i);
530         } else if (i != string::npos) {
531                 piece = a.substr(0, i);
532                 tmp = a.substr(i + 1);
533         } else if (i == 0) {
534                 piece.erase();
535                 tmp = a.substr(i + 1);
536         } else {
537                 piece = a;
538         }
539         return tmp;
540 }
541
542
543 string const split(string const & a, char delim)
544 {
545         string tmp;
546         string::size_type i = a.find(delim);
547         if (i != string::npos) // found delim
548                 tmp = a.substr(i + 1);
549         return tmp;
550 }
551
552
553 // ale970521
554 string const rsplit(string const & a, string & piece, char delim)
555 {
556         string tmp;
557         string::size_type i = a.rfind(delim);
558         if (i != string::npos) { // delimiter was found
559                 piece = a.substr(0, i);
560                 tmp = a.substr(i + 1);
561         } else { // delimiter was not found
562                 piece.erase();
563         }
564         return tmp;
565 }
566
567
568 // This function escapes 8-bit characters and other problematic
569 // characters that cause problems in latex labels.
570 string const escape(string const & lab)
571 {
572         char hexdigit[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
573                               '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
574         string enc;
575         for (string::size_type i = 0; i < lab.length(); ++i) {
576                 unsigned char c= lab[i];
577                 if (c >= 128 || c == '=' || c == '%') {
578                         enc += '=';
579                         enc += hexdigit[c>>4];
580                         enc += hexdigit[c & 15];
581                 } else {
582                         enc += c;
583                 }
584         }
585         return enc;
586 }
587
588
589 /// gives a vector of stringparts which have the delimiter delim
590 vector<string> const getVectorFromString(string const & str,
591                                          string const & delim)
592 {
593 // Lars would like this code to go, but for now his replacement (below)
594 // doesn't fullfil the same function. I have, therefore, reactivated the
595 // old code for now. Angus 11 Nov 2002.
596 #if 1
597         vector<string> vec;
598         if (str.empty())
599                 return vec;
600         string keys = rtrim(str);
601         for(;;) {
602                 string::size_type const idx = keys.find(delim);
603                 if (idx == string::npos) {
604                         vec.push_back(ltrim(keys));
605                         break;
606                 }
607                 string const key = trim(keys.substr(0, idx));
608                 if (!key.empty())
609                         vec.push_back(key);
610                 string::size_type const start = idx + delim.size();
611                 keys = keys.substr(start);
612         }
613         return vec;
614 #else
615         boost::char_separator<char> sep(delim.c_str());
616         boost::tokenizer<boost::char_separator<char> > tokens(str, sep);
617         return vector<string>(tokens.begin(), tokens.end());
618 #endif
619 }
620
621
622 // the same vice versa
623 string const getStringFromVector(vector<string> const & vec,
624                                  string const & delim)
625 {
626         string str;
627         int i = 0;
628         for (vector<string>::const_iterator it = vec.begin();
629              it != vec.end(); ++it) {
630                 string item = trim(*it);
631                 if (item.empty())
632                         continue;
633                 if (i++ > 0)
634                         str += delim;
635                 str += item;
636         }
637         return str;
638 }
639
640
641 int findToken(char const * const str[], string const & search_token)
642 {
643         int i = 0;
644
645         while (str[i][0] && str[i] != search_token)
646                 ++i;
647         if (!str[i][0])
648                 i = -1;
649         return i;
650 }
651
652
653 docstring const externalLineEnding(docstring const & str)
654 {
655 #if defined(__APPLE__)
656         // The MAC clipboard uses \r for lineendings, and we use \n
657         return subst(str, '\n', '\r');
658 #elif defined (_WIN32) || (defined (__CYGWIN__) && defined (X_DISPLAY_MISSING))
659         // Windows clipboard uses \r\n for lineendings, and we use \n
660         return subst(str, lyx::from_ascii("\n"), lyx::from_ascii("\r\n"));
661 #else
662         return str;
663 #endif
664 }
665
666
667 docstring const internalLineEnding(docstring const & str)
668 {
669         docstring const s = subst(str,
670                         lyx::from_ascii("\r\n"), lyx::from_ascii("\n"));
671         return subst(s, '\r', '\n');
672 }
673
674
675 #ifndef I_AM_NOT_AFRAID_OF_HEADER_LIBRARIES
676 #if USE_BOOST_FORMAT
677
678 template<>
679 string bformat(string const & fmt, int arg1)
680 {
681         return (boost::format(fmt) % arg1).str();
682 }
683
684
685 template<>
686 string bformat(string const & fmt, long arg1)
687 {
688         return (boost::format(fmt) % arg1).str();
689 }
690
691
692 template<>
693 string bformat(string const & fmt, unsigned int arg1)
694 {
695         return (boost::format(fmt) % arg1).str();
696 }
697
698
699 template<>
700 string bformat<string>(string const & fmt, string arg1)
701 {
702         return (boost::format(fmt) % arg1).str();
703 }
704
705
706 template<>
707 string bformat(string const & fmt, char * arg1)
708 {
709         return (boost::format(fmt) % arg1).str();
710 }
711
712
713 template<>
714 string bformat(string const & fmt, int arg1, int arg2)
715 {
716         return (boost::format(fmt) % arg1 % arg2).str();
717 }
718
719
720 template<>
721 string bformat(string const & fmt, string arg1, string arg2)
722 {
723         return (boost::format(fmt) % arg1 % arg2).str();
724 }
725
726
727 template<>
728 string bformat(string const & fmt, char const * arg1, string arg2)
729 {
730         return (boost::format(fmt) % arg1 % arg2).str();
731 }
732
733
734 template<>
735 string bformat(string const & fmt, string arg1, string arg2, string arg3)
736 {
737         return (boost::format(fmt) % arg1 % arg2 % arg3).str();
738 }
739
740
741 template<>
742 string bformat(string const & fmt,
743                string arg1, string arg2, string arg3, string arg4)
744 {
745         return (boost::format(fmt) % arg1 % arg2 % arg3 % arg4).str();
746 }
747
748 #else
749
750 template<>
751 string bformat(string const & fmt, int arg1)
752 {
753         BOOST_ASSERT(contains(fmt, "%1$d"));
754         string const str = subst(fmt, "%1$d", convert<string>(arg1));
755         return subst(str, "%%", "%");
756 }
757
758
759 template<>
760 string bformat(string const & fmt, long arg1)
761 {
762         BOOST_ASSERT(contains(fmt, "%1$d"));
763         string const str = subst(fmt, "%1$d", convert<string>(arg1));
764         return subst(str, "%%", "%");
765 }
766
767
768 template<>
769 string bformat(string const & fmt, unsigned int arg1)
770 {
771         BOOST_ASSERT(contains(fmt, "%1$d"));
772         string const str = subst(fmt, "%1$d", convert<string>(arg1));
773         return subst(str, "%%", "%");
774 }
775
776
777 template<>
778 string bformat(string const & fmt, string arg1)
779 {
780         BOOST_ASSERT(contains(fmt, "%1$s"));
781         string const str = subst(fmt, "%1$s", arg1);
782         return subst(str, "%%", "%");
783 }
784
785
786 template<>
787 string bformat(string const & fmt, char * arg1)
788 {
789         BOOST_ASSERT(contains(fmt, "%1$s"));
790         string const str = subst(fmt, "%1$s", arg1);
791         return subst(str, "%%", "%");
792 }
793 template<>
794 string bformat(string const & fmt, string arg1, string arg2)
795 {
796         BOOST_ASSERT(contains(fmt, "%1$s"));
797         BOOST_ASSERT(contains(fmt, "%2$s"));
798         string str = subst(fmt, "%1$s", arg1);
799         str = subst(str, "%2$s", arg2);
800         return subst(str, "%%", "%");
801 }
802
803
804 template<>
805 string bformat(string const & fmt, char const * arg1, string arg2)
806 {
807         BOOST_ASSERT(contains(fmt, "%1$s"));
808         BOOST_ASSERT(contains(fmt, "%2$s"));
809         string str = subst(fmt, "%1$s", arg1);
810         str = subst(fmt, "%2$s", arg2);
811         return subst(str, "%%", "%");
812 }
813
814
815 template<>
816 string bformat(string const & fmt, int arg1, int arg2)
817 {
818         BOOST_ASSERT(contains(fmt, "%1$d"));
819         BOOST_ASSERT(contains(fmt, "%2$d"));
820         string str = subst(fmt, "%1$d", convert<string>(arg1));
821         str = subst(str, "%2$d", convert<string>(arg2));
822         return subst(str, "%%", "%");
823 }
824
825
826 template<>
827 string bformat(string const & fmt, string arg1, string arg2, string arg3)
828 {
829         BOOST_ASSERT(contains(fmt, "%1$s"));
830         BOOST_ASSERT(contains(fmt, "%2$s"));
831         BOOST_ASSERT(contains(fmt, "%3$s"));
832         string str = subst(fmt, "%1$s", arg1);
833         str = subst(str, "%2$s", arg2);
834         str = subst(str, "%3$s", arg3);
835         return subst(str, "%%", "%");
836 }
837
838
839 template<>
840 string bformat(string const & fmt,
841                string arg1, string arg2, string arg3, string arg4)
842 {
843         BOOST_ASSERT(contains(fmt, "%1$s"));
844         BOOST_ASSERT(contains(fmt, "%2$s"));
845         BOOST_ASSERT(contains(fmt, "%3$s"));
846         BOOST_ASSERT(contains(fmt, "%4$s"));
847         string str = subst(fmt, "%1$s", arg1);
848         str = subst(str, "%2$s", arg2);
849         str = subst(str, "%3$s", arg3);
850         str = subst(str, "%4$s", arg4);
851         return subst(str, "%%", "%");
852 }
853
854 #endif
855 #endif
856
857 } // namespace support
858 } // namespace lyx