]> git.lyx.org Git - features.git/blob - src/support/lstrings.C
Replace LString.h with support/std_string.h,
[features.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/std_string.h"
15 #include "lstrings.h"
16 #include "LAssert.h"
17 #include "support/std_sstream.h"
18 #include "debug.h"
19 #include "BoostFormat.h"
20 #include "lyxlib.h"
21
22 #include <boost/regex.hpp>
23 #include <boost/tokenizer.hpp>
24
25 #include <algorithm>
26
27 #include <cctype>
28 #include <cstdlib>
29
30 using std::count;
31 using std::transform;
32 using std::vector;
33
34 #ifndef CXX_GLOBAL_CSTD
35 using std::atof;
36 using std::isdigit;
37 using std::strlen;
38 using std::tolower;
39 using std::toupper;
40 #endif
41
42 namespace lyx {
43 namespace support {
44
45 int compare_no_case(string const & s, string const & s2)
46 {
47         string::const_iterator p = s.begin();
48         string::const_iterator p2 = s2.begin();
49
50         while (p != s.end() && p2 != s2.end()) {
51                 int const lc1 = tolower(*p);
52                 int const lc2 = tolower(*p2);
53                 if (lc1 != lc2)
54                         return (lc1 < lc2) ? -1 : 1;
55                 ++p;
56                 ++p2;
57         }
58
59         if (s.size() == s2.size())
60                 return 0;
61         if (s.size() < s2.size())
62                 return -1;
63         return 1;
64 }
65
66
67 namespace {
68         int ascii_tolower(int c) {
69                 if (c >= 'A' && c <= 'Z')
70                         return c - 'A' + 'a';
71                 return c;
72         }
73 }
74
75
76 int compare_ascii_no_case(string const & s, string const & s2)
77 {
78         string::const_iterator p = s.begin();
79         string::const_iterator p2 = s2.begin();
80
81         while (p != s.end() && p2 != s2.end()) {
82                 int const lc1 = ascii_tolower(*p);
83                 int const lc2 = ascii_tolower(*p2);
84                 if (lc1 != lc2)
85                         return (lc1 < lc2) ? -1 : 1;
86                 ++p;
87                 ++p2;
88         }
89
90         if (s.size() == s2.size())
91                 return 0;
92         if (s.size() < s2.size())
93                 return -1;
94         return 1;
95 }
96
97
98 int compare_no_case(string const & s, string const & s2, unsigned int len)
99 {
100         string::const_iterator p = s.begin();
101         string::const_iterator p2 = s2.begin();
102         unsigned int i = 0;
103         while (i < len && p != s.end() && p2 != s2.end()) {
104                 int const lc1 = tolower(*p);
105                 int const lc2 = tolower(*p2);
106                 if (lc1 != lc2)
107                         return (lc1 < lc2) ? -1 : 1;
108                 ++i;
109                 ++p;
110                 ++p2;
111         }
112
113         if (s.size() >= len && s2.size() >= len)
114                 return 0;
115         if (s.size() < s2.size())
116                 return -1;
117         return 1;
118 }
119
120
121 bool isStrInt(string const & str)
122 {
123         if (str.empty()) return false;
124
125         // Remove leading and trailing white space chars.
126         string const tmpstr = trim(str);
127         if (tmpstr.empty()) return false;
128
129         string::const_iterator cit = tmpstr.begin();
130         if ((*cit) == '-') ++cit;
131         string::const_iterator end = tmpstr.end();
132         for (; cit != end; ++cit) {
133                 if (!isdigit((*cit))) return false;
134         }
135         return true;
136 }
137
138
139 bool isStrUnsignedInt(string const & str)
140 {
141         if (str.empty()) return false;
142
143         // Remove leading and trailing white space chars.
144         string const tmpstr = trim(str);
145         if (tmpstr.empty()) return false;
146
147         string::const_iterator cit = tmpstr.begin();
148         string::const_iterator end = tmpstr.end();
149         for (; cit != end; ++cit) {
150                 if (!isdigit((*cit))) return false;
151         }
152         return true;
153 }
154
155
156 int strToInt(string const & str)
157 {
158         if (isStrInt(str)) {
159                 // Remove leading and trailing white space chars.
160                 string const tmpstr = trim(str);
161                 // Do the conversion proper.
162                 return atoi(tmpstr);
163         } else {
164                 return 0;
165         }
166 }
167
168
169 unsigned int strToUnsignedInt(string const & str)
170 {
171         if (isStrUnsignedInt(str)) {
172                 // Remove leading and trailing white space chars.
173                 string const tmpstr = trim(str);
174                 // Do the conversion proper.
175                 return atoi(tmpstr);
176         } else {
177                 return 0;
178         }
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 double strToDbl(string const & str)
213 {
214         if (isStrDbl(str)) {
215                 // Remove leading and trailing white space chars.
216                 string const tmpstr = trim(str);
217                 // Do the conversion proper.
218                 return ::atof(tmpstr.c_str());
219         } else {
220                 return 0.0;
221         }
222 }
223
224
225 char lowercase(char c)
226 {
227         return char(tolower(c));
228 }
229
230
231 char uppercase(char c)
232 {
233         return char(toupper(c));
234 }
235
236
237 namespace {
238
239 // since we cannot use std::tolower and std::toupper directly in the
240 // calls to std::transform yet, we use these helper clases. (Lgb)
241
242 struct local_lowercase {
243         char operator()(char c) const {
244                 return tolower(c);
245         }
246 };
247
248 struct local_uppercase {
249         char operator()(char c) const {
250                 return toupper(c);
251         }
252 };
253
254 struct local_ascii_lowercase {
255         char operator()(char c) const {
256                 return ascii_tolower(c);
257         }
258 };
259
260 } // end of anon namespace
261
262 string const lowercase(string const & a)
263 {
264         string tmp(a);
265         transform(tmp.begin(), tmp.end(), tmp.begin(), local_lowercase());
266         return tmp;
267 }
268
269 string const uppercase(string const & a)
270 {
271         string tmp(a);
272         transform(tmp.begin(), tmp.end(), tmp.begin(), local_uppercase());
273         return tmp;
274 }
275
276
277 string const ascii_lowercase(string const & a)
278 {
279         string tmp(a);
280         transform(tmp.begin(), tmp.end(), tmp.begin(),
281                   local_ascii_lowercase());
282         return tmp;
283 }
284
285
286 bool prefixIs(string const & a, string const & pre)
287 {
288         string::size_type const prelen = pre.length();
289         string::size_type const alen = a.length();
290
291         if (prelen > alen || a.empty())
292                 return false;
293         else {
294 #if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD)
295                 return ::strncmp(a.c_str(), pre.c_str(), prelen) == 0;
296 #else
297                 return a.compare(0, prelen, pre) == 0;
298 #endif
299         }
300 }
301
302
303 bool suffixIs(string const & a, char c)
304 {
305         if (a.empty()) return false;
306         return a[a.length() - 1] == c;
307 }
308
309
310 bool suffixIs(string const & a, string const & suf)
311 {
312         string::size_type const suflen = suf.length();
313         string::size_type const alen = a.length();
314
315         if (suflen > alen) {
316                 return false;
317         } else {
318 #if !defined(USE_INCLUDED_STRING) && !defined(STD_STRING_IS_GOOD)
319                 string tmp(a, alen - suflen);
320                 return ::strncmp(tmp.c_str(), suf.c_str(), suflen) == 0;
321 #else
322                 return a.compare(alen - suflen, suflen, suf) == 0;
323 #endif
324         }
325 }
326
327
328 bool contains(string const & a, string const & b)
329 {
330         if (a.empty())
331                 return false;
332         return a.find(b) != string::npos;
333 }
334
335
336 bool contains(string const & a, char b)
337 {
338         if (a.empty())
339                 return false;
340         return a.find(b) != string::npos;
341 }
342
343
344 bool containsOnly(string const & s, string const & cset)
345 {
346         return s.find_first_not_of(cset) == string::npos;
347 }
348
349
350 // ale970405+lasgoutt-970425
351 // rewritten to use new string (Lgb)
352 string const token(string const & a, char delim, int n)
353 {
354         if (a.empty()) return string();
355
356         string::size_type k = 0;
357         string::size_type i = 0;
358
359         // Find delimiter or end of string
360         for (; n--;)
361                 if ((i = a.find(delim, i)) == string::npos)
362                         break;
363                 else
364                         ++i; // step delim
365         // i is now the n'th delim (or string::npos)
366         if (i == string::npos) return string();
367         k = a.find(delim, i);
368         // k is now the n'th + 1 delim (or string::npos)
369
370         return a.substr(i, k - i);
371 }
372
373
374 // this could probably be faster and/or cleaner, but it seems to work (JMarc)
375 // rewritten to use new string (Lgb)
376 int tokenPos(string const & a, char delim, string const & tok)
377 {
378         int i = 0;
379         string str(a);
380         string tmptok;
381
382         while (!str.empty()) {
383                 str = split(str, tmptok, delim);
384                 if (tok == tmptok)
385                         return i;
386                 ++i;
387         }
388         return -1;
389 }
390
391
392 bool regexMatch(string const & a, string const & pattern)
393 {
394         // We massage the pattern a bit so that the usual
395         // shell pattern we all are used to will work.
396         // One nice thing about using a real regex is that
397         // things like "*.*[^~]" will work also.
398         // build the regex string.
399         string regex(pattern);
400         regex = subst(regex, ".", "\\.");
401         regex = subst(regex, "*", ".*");
402         boost::regex reg(STRCONV(regex));
403         return boost::regex_match(STRCONV(a), reg);
404 }
405
406
407 string const subst(string const & a, char oldchar, char newchar)
408 {
409         string tmp(a);
410         string::iterator lit = tmp.begin();
411         string::iterator end = tmp.end();
412         for (; lit != end; ++lit)
413                 if ((*lit) == oldchar)
414                         (*lit) = newchar;
415         return tmp;
416 }
417
418
419 string const subst(string const & a,
420                    string const & oldstr, string const & newstr)
421 {
422         string lstr = a;
423         string::size_type i = 0;
424         string::size_type const olen = oldstr.length();
425         while ((i = lstr.find(oldstr, i)) != string::npos) {
426                 lstr.replace(i, olen, newstr);
427                 i += newstr.length(); // We need to be sure that we dont
428                 // use the same i over and over again.
429         }
430         return lstr;
431 }
432
433
434 string const trim(string const & a, char const * p)
435 {
436         Assert(p);
437
438         if (a.empty() || !*p)
439                 return a;
440
441         string::size_type r = a.find_last_not_of(p);
442         string::size_type l = a.find_first_not_of(p);
443
444         // Is this the minimal test? (lgb)
445         if (r == string::npos && l == string::npos)
446                 return string();
447
448         return a.substr(l, r - l + 1);
449 }
450
451
452 string const rtrim(string const & a, char const * p)
453 {
454         Assert(p);
455
456         if (a.empty() || !*p)
457                 return a;
458
459         string::size_type r = a.find_last_not_of(p);
460
461         // Is this test really needed? (Lgb)
462         if (r == string::npos)
463                 return string();
464
465         return a.substr(0, r + 1);
466 }
467
468
469 string const ltrim(string const & a, char const * p)
470 {
471         Assert(p);
472
473         if (a.empty() || !*p)
474                 return a;
475
476         string::size_type l = a.find_first_not_of(p);
477
478         if (l == string::npos)
479                 return string();
480
481         return a.substr(l, string::npos);
482 }
483
484
485 string const split(string const & a, string & piece, char delim)
486 {
487         string tmp;
488         string::size_type i = a.find(delim);
489         if (i == a.length() - 1) {
490                 piece = a.substr(0, i);
491         } else if (i != string::npos) {
492                 piece = a.substr(0, i);
493                 tmp = a.substr(i + 1);
494         } else if (i == 0) {
495                 piece.erase();
496                 tmp = a.substr(i + 1);
497         } else {
498                 piece = a;
499         }
500         return tmp;
501 }
502
503
504 string const split(string const & a, char delim)
505 {
506         string tmp;
507         string::size_type i = a.find(delim);
508         if (i != string::npos) // found delim
509                 tmp = a.substr(i + 1);
510         return tmp;
511 }
512
513
514 // ale970521
515 string const rsplit(string const & a, string & piece, char delim)
516 {
517         string tmp;
518         string::size_type i = a.rfind(delim);
519         if (i != string::npos) { // delimiter was found
520                 piece = a.substr(0, i);
521                 tmp = a.substr(i + 1);
522         } else { // delimiter was not found
523                 piece.erase();
524         }
525         return tmp;
526 }
527
528
529 // This function escapes 8-bit characters and other problematic
530 // characters that cause problems in latex labels.
531 string const escape(string const & lab)
532 {
533         char hexdigit[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
534                               '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
535         string enc;
536         for (string::size_type i = 0; i < lab.length(); ++i) {
537                 unsigned char c= lab[i];
538                 if (c >= 128 || c == '=' || c == '%') {
539                         enc += '=';
540                         enc += hexdigit[c>>4];
541                         enc += hexdigit[c & 15];
542                 } else {
543                         enc += c;
544                 }
545         }
546         return enc;
547 }
548
549
550 /// gives a vector of stringparts which have the delimiter delim
551 vector<string> const getVectorFromString(string const & str,
552                                          string const & delim)
553 {
554 // Lars would like this code to go, but for now his replacement (below)
555 // doesn't fullfil the same function. I have, therefore, reactivated the
556 // old code for now. Angus 11 Nov 2002.
557 #if 1
558         vector<string> vec;
559         if (str.empty())
560                 return vec;
561         string keys(rtrim(str));
562         for(;;) {
563                 string::size_type const idx = keys.find(delim);
564                 if (idx == string::npos) {
565                         vec.push_back(ltrim(keys));
566                         break;
567                 }
568                 string const key = trim(keys.substr(0, idx));
569                 if (!key.empty())
570                         vec.push_back(key);
571                 string::size_type const start = idx + delim.size();
572                 keys = keys.substr(start);
573         }
574         return vec;
575 #else
576         boost::char_separator<char> sep(delim.c_str());
577         boost::tokenizer<boost::char_separator<char> > tokens(str, sep);
578 #ifndef USE_INCLUDED_STRING
579         return vector<string>(tokens.begin(), tokens.end());
580 #else
581         vector<string> vec;
582         using boost::tokenizer;
583         using boost::char_separator;
584
585         tokenizer<char_separator<char> >::iterator it = tokens.begin();
586         tokenizer<char_separator<char> >::iterator end = tokens.end();
587         for (; it != end; ++it) {
588                 vec.push_back(STRCONV((*it)));
589         }
590         return vec;
591 #endif
592 #endif
593 }
594
595
596 // the same vice versa
597 string const getStringFromVector(vector<string> const & vec,
598                                  string const & delim)
599 {
600         string str;
601         int i = 0;
602         for (vector<string>::const_iterator it = vec.begin();
603              it != vec.end(); ++it) {
604                 string item = trim(*it);
605                 if (item.empty())
606                         continue;
607                 if (i++ > 0)
608                         str += delim;
609                 str += item;
610         }
611         return str;
612 }
613
614
615 #if USE_BOOST_FORMAT
616
617 string bformat(string const & fmt, string const & arg1)
618 {
619         return STRCONV((boost::format(STRCONV(fmt)) % STRCONV(arg1)).str());
620 }
621
622
623 string bformat(string const & fmt, string const & arg1, string const & arg2)
624 {
625         return STRCONV((boost::format(STRCONV(fmt)) % STRCONV(arg1)
626                 % STRCONV(arg2)).str());
627 }
628
629
630 string bformat(string const & fmt, int arg1, int arg2)
631 {
632         return STRCONV((boost::format(STRCONV(fmt)) % arg1 % arg2).str());
633 }
634
635
636 string bformat(string const & fmt, string const & arg1, string const & arg2,
637         string const & arg3)
638 {
639         return STRCONV((boost::format(STRCONV(fmt)) % STRCONV(arg1)
640                 % STRCONV(arg2) % STRCONV(arg3)).str());
641 }
642
643
644 string bformat(string const & fmt, string const & arg1, string const & arg2,
645         string const & arg3, string const & arg4)
646 {
647         return STRCONV((boost::format(STRCONV(fmt)) % STRCONV(arg1)
648                 % STRCONV(arg2) % STRCONV(arg3) % STRCONV(arg4)).str());
649 }
650
651
652 string bformat(string const & fmt, string const & arg1, string const & arg2,
653         string const & arg3, string const & arg4, string const & arg5)
654 {
655         return STRCONV((boost::format(STRCONV(fmt)) % STRCONV(arg1)
656                 % STRCONV(arg2) % STRCONV(arg3) % STRCONV(arg4)
657                 % STRCONV(arg5)).str());
658 }
659
660 #else
661
662 string bformat(string const & fmt, string const & arg1)
663 {
664         Assert(contains(fmt, "%1$s"));
665         string const str = subst(fmt, "%1$s", arg1);
666         return subst(str, "%%", "%");
667 }
668
669
670 string bformat(string const & fmt, string const & arg1, string const & arg2)
671 {
672         Assert(contains(fmt, "%1$s"));
673         Assert(contains(fmt, "%2$s"));
674         string str = subst(fmt, "%1$s", arg1);
675         str = subst(str, "%2$s", arg2);
676         return subst(str, "%%", "%");
677 }
678
679
680 string bformat(string const & fmt, int arg1, int arg2)
681 {
682         Assert(contains(fmt, "%1$d"));
683         Assert(contains(fmt, "%2$d"));
684         string str = subst(fmt, "%1$d", arg1);
685         str = subst(str, "%2$d", arg2);
686         return subst(str, "%%", "%");
687 }
688
689
690 string bformat(string const & fmt, string const & arg1, string const & arg2,
691         string const & arg3)
692 {
693         Assert(contains(fmt, "%1$s"));
694         Assert(contains(fmt, "%2$s"));
695         Assert(contains(fmt, "%3$s"));
696         string str = subst(fmt, "%1$s", arg1);
697         str = subst(str, "%2$s", arg2);
698         str = subst(str, "%3$s", arg3);
699         return subst(str, "%%", "%");
700 }
701
702
703 string bformat(string const & fmt, string const & arg1, string const & arg2,
704         string const & arg3, string const & arg4)
705 {
706         Assert(contains(fmt, "%1$s"));
707         Assert(contains(fmt, "%2$s"));
708         Assert(contains(fmt, "%3$s"));
709         Assert(contains(fmt, "%4$s"));
710         string str = subst(fmt, "%1$s", arg1);
711         str = subst(str, "%2$s", arg2);
712         str = subst(str, "%3$s", arg3);
713         str = subst(str, "%4$s", arg4);
714         return subst(str, "%%", "%");
715 }
716
717
718 string bformat(string const & fmt, string const & arg1, string const & arg2,
719         string const & arg3, string const & arg4, string const & arg5)
720 {
721         Assert(contains(fmt, "%1$s"));
722         Assert(contains(fmt, "%2$s"));
723         Assert(contains(fmt, "%3$s"));
724         Assert(contains(fmt, "%4$s"));
725         Assert(contains(fmt, "%5$s"));
726         string str = subst(fmt, "%1$s", arg1);
727         str = subst(str, "%2$s", arg2);
728         str = subst(str, "%3$s", arg3);
729         str = subst(str, "%4$s", arg4);
730         str = subst(str, "%5$s", arg5);
731         return subst(str, "%%", "%");
732 }
733
734 #endif
735
736 } // namespace support
737 } // namespace lyx