]> git.mxchange.org Git - simgear.git/blob - simgear/misc/strutils.cxx
25480271069b75128d4f4261faadf3e37fb9255b
[simgear.git] / simgear / misc / strutils.cxx
1 // String utilities.
2 //
3 // Written by Bernie Bright, started 1998
4 //
5 // Copyright (C) 1998  Bernie Bright - bbright@bigpond.net.au
6 //
7 // This library is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU Library General Public
9 // License as published by the Free Software Foundation; either
10 // version 2 of the License, or (at your option) any later version.
11 //
12 // This library is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // Library General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23 #include <ctype.h>
24 #include <cstring>
25 #include <sstream>
26
27 #include "strutils.hxx"
28
29 #include <simgear/debug/logstream.hxx>
30
31 using std::string;
32 using std::vector;
33 using std::stringstream;
34
35 namespace simgear {
36     namespace strutils {
37
38         /*
39          * utf8ToLatin1() convert utf8 to latin, useful for accent character (i.e éâàîè...)
40          */
41         template <typename Iterator> size_t get_length (Iterator p) {
42                 unsigned char c = static_cast<unsigned char> (*p);
43                 if (c < 0x80) return 1;
44                 else if (!(c & 0x20)) return 2;
45                 else if (!(c & 0x10)) return 3;
46                 else if (!(c & 0x08)) return 4;
47                 else if (!(c & 0x04)) return 5;
48                 else return 6;
49         }
50
51         typedef unsigned int value_type;
52         template <typename Iterator> value_type get_value (Iterator p) {
53                 size_t len = get_length (p);
54                 if (len == 1) return *p;
55                 value_type res = static_cast<unsigned char> ( *p & (0xff >> (len + 1))) << ((len - 1) * 6 );
56                 for (--len; len; --len)
57                         res |= (static_cast<unsigned char> (*(++p)) - 0x80) << ((len - 1) * 6);
58                 return res;
59         }
60
61         string utf8ToLatin1( string& s_utf8 ) {
62                 string s_latin1;
63                 for (string::iterator p = s_utf8.begin(); p != s_utf8.end(); ++p) {
64                         value_type value = get_value<string::iterator&>(p);
65                         if (value > 0xff) SG_LOG(SG_IO, SG_WARN, "utf8ToLatin1: wrong char value: " << value);
66                         s_latin1 += static_cast<char>(value);
67                 }
68                 return s_latin1;
69         }
70
71         /**
72          * 
73          */
74         static vector<string>
75         split_whitespace( const string& str, int maxsplit )
76         {
77             vector<string> result;
78             string::size_type len = str.length();
79             string::size_type i = 0;
80             string::size_type j;
81             int countsplit = 0;
82
83             while (i < len)
84             {
85                 while (i < len && isspace((unsigned char)str[i]))
86                 {
87                     ++i;
88                 }
89
90                 j = i;
91
92                 while (i < len && !isspace((unsigned char)str[i]))
93                 {
94                     ++i;
95                 }
96
97                 if (j < i)
98                 {
99                     result.push_back( str.substr(j, i-j) );
100                     ++countsplit;
101                     while (i < len && isspace((unsigned char)str[i]))
102                     {
103                         ++i;
104                     }
105
106                     if (maxsplit && (countsplit >= maxsplit) && i < len)
107                     {
108                         result.push_back( str.substr( i, len-i ) );
109                         i = len;
110                     }
111                 }
112             }
113
114             return result;
115         }
116
117         /**
118          * 
119          */
120         vector<string>
121         split( const string& str, const char* sep, int maxsplit )
122         {
123             if (sep == 0)
124                 return split_whitespace( str, maxsplit );
125
126             vector<string> result;
127             int n = std::strlen( sep );
128             if (n == 0)
129             {
130                 // Error: empty separator string
131                 return result;
132             }
133             const char* s = str.c_str();
134             string::size_type len = str.length();
135             string::size_type i = 0;
136             string::size_type j = 0;
137             int splitcount = 0;
138
139             while (i+n <= len)
140             {
141                 if (s[i] == sep[0] && (n == 1 || std::memcmp(s+i, sep, n) == 0))
142                 {
143                     result.push_back( str.substr(j,i-j) );
144                     i = j = i + n;
145                     ++splitcount;
146                     if (maxsplit && (splitcount >= maxsplit))
147                         break;
148                 }
149                 else
150                 {
151                     ++i;
152                 }
153             }
154
155             result.push_back( str.substr(j,len-j) );
156             return result;
157         }
158
159         /**
160          * The lstrip(), rstrip() and strip() functions are implemented
161          * in do_strip() which uses an additional parameter to indicate what
162          * type of strip should occur.
163          */
164         const int LEFTSTRIP = 0;
165         const int RIGHTSTRIP = 1;
166         const int BOTHSTRIP = 2;
167
168         static string
169         do_strip( const string& s, int striptype )
170         {
171             string::size_type len = s.length();
172             if( len == 0 ) // empty string is trivial
173                 return s;
174             string::size_type i = 0;
175             if (striptype != RIGHTSTRIP)
176             {
177                 while (i < len && isspace(s[i]))
178                 {
179                     ++i;
180                 }
181             }
182
183             string::size_type j = len;
184             if (striptype != LEFTSTRIP)
185             {
186                 do
187                 {
188                     --j;
189                 }
190                 while (j >= 1 && isspace(s[j]));
191                 ++j;
192             }
193
194             if (i == 0 && j == len)
195             {
196                 return s;
197             }
198             else
199             {
200                 return s.substr( i, j - i );
201             }
202         }
203
204         string
205         lstrip( const string& s )
206         {
207             return do_strip( s, LEFTSTRIP );
208         }
209
210         string
211         rstrip( const string& s )
212         {
213             return do_strip( s, RIGHTSTRIP );
214         }
215
216         string
217         strip( const string& s )
218         {
219             return do_strip( s, BOTHSTRIP );
220         }
221
222         string 
223         rpad( const string & s, string::size_type length, char c )
224         {
225             string::size_type l = s.length();
226             if( l >= length ) return s;
227             string reply = s;
228             return reply.append( length-l, c );
229         }
230
231         string 
232         lpad( const string & s, size_t length, char c )
233         {
234             string::size_type l = s.length();
235             if( l >= length ) return s;
236             string reply = s;
237             return reply.insert( 0, length-l, c );
238         }
239
240         bool
241         starts_with( const string & s, const string & substr )
242         {
243           return s.compare(0, substr.length(), substr) == 0;
244         }
245
246         bool
247         ends_with( const string & s, const string & substr )
248         {
249           if( substr.length() > s.length() )
250             return false;
251           return s.compare( s.length() - substr.length(),
252                             substr.length(),
253                             substr ) == 0;
254         }
255
256     string simplify(const string& s)
257     {
258         string result; // reserve size of 's'?
259         string::const_iterator it = s.begin(),
260             end = s.end();
261     
262     // advance to first non-space char - simplifes logic in main loop,
263     // since we can always prepend a single space when we see a 
264     // space -> non-space transition
265         for (; (it != end) && isspace(*it); ++it) { /* nothing */ }
266         
267         bool lastWasSpace = false;
268         for (; it != end; ++it) {
269             char c = *it;
270             if (isspace(c)) {
271                 lastWasSpace = true;
272                 continue;
273             }
274             
275             if (lastWasSpace) {
276                 result.push_back(' ');
277             }
278             
279             lastWasSpace = false;
280             result.push_back(c);
281         }
282         
283         return result;
284     }
285     
286     int to_int(const std::string& s, int base)
287     {
288         stringstream ss(s);
289         switch (base) {
290         case 8:      ss >> std::oct; break;
291         case 16:     ss >> std::hex; break;
292         default: break;
293         }
294         
295         int result;
296         ss >> result;
297         return result;
298     }
299     
300     int compare_versions(const string& v1, const string& v2)
301     {
302         vector<string> v1parts(split(v1, "."));
303         vector<string> v2parts(split(v2, "."));
304
305         int lastPart = std::min(v1parts.size(), v2parts.size());
306         for (int part=0; part < lastPart; ++part) {
307             int part1 = to_int(v1parts[part]);
308             int part2 = to_int(v2parts[part]);
309
310             if (part1 != part2) {
311                 return part1 - part2;
312             }
313         } // of parts iteration
314
315         // reached end - longer wins
316         return v1parts.size() - v2parts.size();
317     }
318     
319     string join(const string_list& l, const string& joinWith)
320     {
321         string result;
322         unsigned int count = l.size();
323         for (unsigned int i=0; i < count; ++i) {
324             result += l[i];
325             if (i < (count - 1)) {
326                 result += joinWith;
327             }
328         }
329         
330         return result;
331     }
332     
333     string uppercase(const string &s) {
334       string rslt(s);
335       for(string::iterator p = rslt.begin(); p != rslt.end(); p++){
336         *p = toupper(*p);
337       }
338       return rslt;
339     }
340
341     string lowercase(const string &s) {
342       string rslt(s);
343       for(string::iterator p = rslt.begin(); p != rslt.end(); p++){
344         *p = tolower(*p);
345       }
346       return rslt;
347     }
348
349     void lowercase(string &s) {
350       for(string::iterator p = s.begin(); p != s.end(); p++){
351         *p = tolower(*p);
352       }
353     }
354     
355 #if defined(SG_WINDOWS)
356
357 #include <windows.h>
358     
359 static WCharVec convertMultiByteToWString(DWORD encoding, const std::string& a)
360 {
361     WCharVec result;
362     DWORD flags = 0;
363     int requiredWideChars = MultiByteToWideChar(encoding, flags, 
364                         a.c_str(), a.size(),
365                         NULL, 0);
366     result.resize(requiredWideChars);
367     MultiByteToWideChar(encoding, flags, a.c_str(), a.size(),
368                         result.data(), result.size());
369     return result;
370 }
371
372 WCharVec convertUtf8ToWString(const std::string& a)
373 {
374     return convertMultiByteToWString(CP_UTF8, a);
375 }
376
377 #endif
378
379 std::string convertWindowsLocal8BitToUtf8(const std::string& a)
380 {
381 #ifdef SG_WINDOWS
382     DWORD flags = 0;
383     WCharVec wideString = convertMultiByteToWString(CP_ACP, a);
384    
385     // convert down to UTF-8
386     std::vector<char> result;
387     int requiredUTF8Chars = WideCharToMultiByte(CP_UTF8, flags,
388                                                 wideString.data(), wideString.size(),
389                                                 NULL, 0, NULL, NULL);
390     result.resize(requiredUTF8Chars);
391     WideCharToMultiByte(CP_UTF8, flags,
392                         wideString.data(), wideString.size(),
393                         result.data(), result.size(), NULL, NULL);
394     return std::string(result.data(), result.size());
395 #else
396     return a;
397 #endif
398 }
399
400
401
402 static const std::string base64_chars =
403 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
404 "abcdefghijklmnopqrstuvwxyz"
405 "0123456789+/";
406
407 static const unsigned char base64_decode_map[128] =
408 {
409     127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
410     127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
411     127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
412     127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
413     127, 127, 127,  62, 127, 127, 127,  63,  52,  53,
414     54,  55,  56,  57,  58,  59,  60,  61, 127, 127,
415     127,  64, 127, 127, 127,   0,   1,   2,   3,   4,
416     5,   6,   7,   8,   9,  10,  11,  12,  13,  14,
417     15,  16,  17,  18,  19,  20,  21,  22,  23,  24,
418     25, 127, 127, 127, 127, 127, 127,  26,  27,  28,
419     29,  30,  31,  32,  33,  34,  35,  36,  37,  38,
420     39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
421     49,  50,  51, 127, 127, 127, 127, 127
422 };
423  
424         
425 static inline bool is_base64(unsigned char c) {
426   return (isalnum(c) || (c == '+') || (c == '/'));
427 }
428
429 static bool is_whitespace(unsigned char c) {
430     return ((c == ' ') || (c == '\r') || (c == '\n'));
431 }
432
433 void decodeBase64(const std::string& encoded_string, std::vector<unsigned char>& ret)
434 {
435   int in_len = encoded_string.size();
436   int i = 0;
437   int j = 0;
438   int in_ = 0;
439   unsigned char char_array_4[4], char_array_3[3];
440   
441   while (in_len-- && ( encoded_string[in_] != '=')) {
442     if (is_whitespace( encoded_string[in_])) {
443         in_++; 
444         continue;
445     }
446     
447     if (!is_base64(encoded_string[in_])) {
448         break;
449     }
450     
451     char_array_4[i++] = encoded_string[in_]; in_++;
452     if (i ==4) {
453       for (i = 0; i <4; i++)
454         char_array_4[i] = base64_decode_map[char_array_4[i]];
455       
456       char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
457       char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
458       char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
459       
460       for (i = 0; (i < 3); i++)
461         ret.push_back(char_array_3[i]);
462       i = 0;
463     }
464   }
465   
466   if (i) {
467     for (j = i; j <4; j++)
468       char_array_4[j] = 0;
469     
470     for (j = 0; j <4; j++)
471       char_array_4[j] = base64_decode_map[char_array_4[j]];
472     
473     char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
474     char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
475     char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
476     
477     for (j = 0; (j < i - 1); j++) ret.push_back(char_array_3[j]);
478   }
479 }  
480
481 const char hexChar[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
482
483 std::string encodeHex(const std::string& bytes)
484 {
485   std::string hex;
486   size_t count = bytes.size();
487   for (unsigned int i=0; i<count;++i) {
488       unsigned char c = bytes[i];
489       hex.push_back(hexChar[c >> 4]);
490       hex.push_back(hexChar[c & 0x0f]);
491   }
492   
493   return hex;
494 }
495
496 std::string encodeHex(const unsigned char* rawBytes, unsigned int length)
497 {
498   std::string hex;
499   for (unsigned int i=0; i<length;++i) {
500       unsigned char c = *rawBytes++;
501       hex.push_back(hexChar[c >> 4]);
502       hex.push_back(hexChar[c & 0x0f]);
503   }
504   
505   return hex;
506 }
507
508 //------------------------------------------------------------------------------
509 std::string unescape(const char* s)
510 {
511   std::string r;
512   while( *s )
513   {
514     if( *s != '\\' )
515     {
516       r += *s++;
517       continue;
518     }
519
520     if( !*++s )
521       break;
522
523     if (*s == '\\') {
524         r += '\\';
525     } else if (*s == 'n') {
526         r += '\n';
527     } else if (*s == 'r') {
528         r += '\r';
529     } else if (*s == 't') {
530         r += '\t';
531     } else if (*s == 'v') {
532         r += '\v';
533     } else if (*s == 'f') {
534         r += '\f';
535     } else if (*s == 'a') {
536         r += '\a';
537     } else if (*s == 'b') {
538         r += '\b';
539     } else if (*s == 'x') {
540         if (!*++s)
541             break;
542         int v = 0;
543         for (int i = 0; i < 2 && isxdigit(*s); i++, s++)
544             v = v * 16 + (isdigit(*s) ? *s - '0' : 10 + tolower(*s) - 'a');
545         r += v;
546         continue;
547
548     } else if (*s >= '0' && *s <= '7') {
549         int v = *s++ - '0';
550         for (int i = 0; i < 3 && *s >= '0' && *s <= '7'; i++, s++)
551             v = v * 8 + *s - '0';
552         r += v;
553         continue;
554
555     } else {
556         r += *s;
557     }
558     s++;
559   }
560   return r;
561 }
562
563 string sanitizePrintfFormat(const string& input)
564 {
565     string::size_type i = input.find("%n");
566     if (i != string::npos) {
567         SG_LOG(SG_IO, SG_WARN, "sanitizePrintfFormat: bad format string:" << input);
568         return string();
569     }
570     
571     return input;
572 }
573
574 } // end namespace strutils
575     
576 } // end namespace simgear