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