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