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