Merge pull request #3696 from xhaggi/epg-past-data
[vuplus_xbmc] / xbmc / utils / StringUtils.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20 //-----------------------------------------------------------------------
21 //
22 //  File:      StringUtils.cpp
23 //
24 //  Purpose:   ATL split string utility
25 //  Author:    Paul J. Weiss
26 //
27 //  Modified to use J O'Leary's CStdString class by kraqh3d
28 //
29 //------------------------------------------------------------------------
30
31
32 #include "StringUtils.h"
33 #include "utils/RegExp.h"
34 #include "utils/fstrcmp.h"
35 #include <locale>
36
37 #include <math.h>
38 #include <sstream>
39 #include <time.h>
40
41 #define FORMAT_BLOCK_SIZE 2048 // # of bytes to increment per try
42
43 using namespace std;
44
45 const char* ADDON_GUID_RE = "^(\\{){0,1}[0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12}(\\}){0,1}$";
46
47 /* empty string for use in returns by ref */
48 const CStdString StringUtils::EmptyString = "";
49 const std::string StringUtils::Empty = "";
50 CStdString StringUtils::m_lastUUID = "";
51
52 string StringUtils::Format(const char *fmt, ...)
53 {
54   va_list args;
55   va_start(args, fmt);
56   string str = FormatV(fmt, args);
57   va_end(args);
58
59   return str;
60 }
61
62 string StringUtils::FormatV(const char *fmt, va_list args)
63 {
64   if (fmt == NULL)
65     return "";
66
67   int size = FORMAT_BLOCK_SIZE;
68   va_list argCopy;
69
70   char *cstr = reinterpret_cast<char*>(malloc(sizeof(char) * size));
71   if (cstr == NULL)
72     return "";
73
74   while (1) 
75   {
76     va_copy(argCopy, args);
77
78     int nActual = vsnprintf(cstr, size, fmt, argCopy);
79     va_end(argCopy);
80
81     if (nActual > -1 && nActual < size) // We got a valid result
82     {
83       string str(cstr, nActual);
84       free(cstr);
85       return str;
86     }
87     if (nActual > -1)                   // Exactly what we will need (glibc 2.1)
88       size = nActual + 1;
89     else                                // Let's try to double the size (glibc 2.0)
90       size *= 2;
91
92     char *new_cstr = reinterpret_cast<char*>(realloc(cstr, sizeof(char) * size));
93     if (new_cstr == NULL)
94     {
95       free(cstr);
96       return "";
97     }
98
99     cstr = new_cstr;
100   }
101
102   free(cstr);
103   return "";
104 }
105
106 wstring StringUtils::Format(const wchar_t *fmt, ...)
107 {
108   va_list args;
109   va_start(args, fmt);
110   wstring str = FormatV(fmt, args);
111   va_end(args);
112   
113   return str;
114 }
115
116 wstring StringUtils::FormatV(const wchar_t *fmt, va_list args)
117 {
118   if (fmt == NULL)
119     return L"";
120   
121   int size = FORMAT_BLOCK_SIZE;
122   va_list argCopy;
123   
124   wchar_t *cstr = reinterpret_cast<wchar_t*>(malloc(sizeof(wchar_t) * size));
125   if (cstr == NULL)
126     return L"";
127   
128   while (1)
129   {
130     va_copy(argCopy, args);
131     
132     int nActual = vswprintf(cstr, size, fmt, argCopy);
133     va_end(argCopy);
134     
135     if (nActual > -1 && nActual < size) // We got a valid result
136     {
137       wstring str(cstr, nActual);
138       free(cstr);
139       return str;
140     }
141     if (nActual > -1)                   // Exactly what we will need (glibc 2.1)
142       size = nActual + 1;
143     else                                // Let's try to double the size (glibc 2.0)
144       size *= 2;
145     
146     wchar_t *new_cstr = reinterpret_cast<wchar_t*>(realloc(cstr, sizeof(wchar_t) * size));
147     if (new_cstr == NULL)
148     {
149       free(cstr);
150       return L"";
151     }
152     
153     cstr = new_cstr;
154   }
155   
156   return L"";
157 }
158
159 void StringUtils::ToUpper(string &str)
160 {
161   transform(str.begin(), str.end(), str.begin(), ::toupper);
162 }
163
164 void StringUtils::ToUpper(wstring &str)
165 {
166   transform(str.begin(), str.end(), str.begin(), ::towupper);
167 }
168
169 void StringUtils::ToLower(string &str)
170 {
171   transform(str.begin(), str.end(), str.begin(), ::tolower);
172 }
173
174 void StringUtils::ToLower(wstring &str)
175 {
176   transform(str.begin(), str.end(), str.begin(), ::towlower);
177 }
178
179 bool StringUtils::EqualsNoCase(const std::string &str1, const std::string &str2)
180 {
181   return EqualsNoCase(str1.c_str(), str2.c_str());
182 }
183
184 bool StringUtils::EqualsNoCase(const std::string &str1, const char *s2)
185 {
186   return EqualsNoCase(str1.c_str(), s2);
187 }
188
189 bool StringUtils::EqualsNoCase(const char *s1, const char *s2)
190 {
191   char c2; // we need only one char outside the loop
192   do
193   {
194     const char c1 = *s1++; // const local variable should help compiler to optimize
195     c2 = *s2++;
196     if (c1 != c2 && ::tolower(c1) != ::tolower(c2)) // This includes the possibility that one of the characters is the null-terminator, which implies a string mismatch.
197       return false;
198   } while (c2 != '\0'); // At this point, we know c1 == c2, so there's no need to test them both.
199   return true;
200 }
201
202 int StringUtils::CompareNoCase(const std::string &str1, const std::string &str2)
203 {
204   return CompareNoCase(str1.c_str(), str2.c_str());
205 }
206
207 int StringUtils::CompareNoCase(const char *s1, const char *s2)
208 {
209   char c2; // we need only one char outside the loop
210   do
211   {
212     const char c1 = *s1++; // const local variable should help compiler to optimize
213     c2 = *s2++;
214     if (c1 != c2 && ::tolower(c1) != ::tolower(c2)) // This includes the possibility that one of the characters is the null-terminator, which implies a string mismatch.
215       return ::tolower(c1) - ::tolower(c2);
216   } while (c2 != '\0'); // At this point, we know c1 == c2, so there's no need to test them both.
217   return 0;
218 }
219
220 string StringUtils::Left(const string &str, size_t count)
221 {
222   count = max((size_t)0, min(count, str.size()));
223   return str.substr(0, count);
224 }
225
226 string StringUtils::Mid(const string &str, size_t first, size_t count /* = string::npos */)
227 {
228   if (first + count > str.size())
229     count = str.size() - first;
230   
231   if (first > str.size())
232     return string();
233   
234   ASSERT(first + count <= str.size());
235   
236   return str.substr(first, count);
237 }
238
239 string StringUtils::Right(const string &str, size_t count)
240 {
241   count = max((size_t)0, min(count, str.size()));
242   return str.substr(str.size() - count);
243 }
244
245 std::string& StringUtils::Trim(std::string &str)
246 {
247   TrimLeft(str);
248   return TrimRight(str);
249 }
250
251 std::string& StringUtils::Trim(std::string &str, const char* const chars)
252 {
253   TrimLeft(str, chars);
254   return TrimRight(str, chars);
255 }
256
257 // hack to ensure that std::string::iterator will be dereferenced as _unsigned_ char
258 // without this hack "TrimX" functions failed on Win32 with UTF-8 strings
259 static int isspace_c(char c)
260 {
261   return ::isspace((unsigned char)c);
262 }
263
264 std::string& StringUtils::TrimLeft(std::string &str)
265 {
266   str.erase(str.begin(), ::find_if(str.begin(), str.end(), ::not1(::ptr_fun(isspace_c))));
267   return str;
268 }
269
270 std::string& StringUtils::TrimLeft(std::string &str, const char* const chars)
271 {
272   size_t nidx = str.find_first_not_of(chars);
273   str.erase(0, nidx);
274   return str;
275 }
276
277 std::string& StringUtils::TrimRight(std::string &str)
278 {
279   str.erase(::find_if(str.rbegin(), str.rend(), ::not1(::ptr_fun(isspace_c))).base(), str.end());
280   return str;
281 }
282
283 std::string& StringUtils::TrimRight(std::string &str, const char* const chars)
284 {
285   size_t nidx = str.find_last_not_of(chars);
286   str.erase(str.npos == nidx ? 0 : ++nidx);
287   return str;
288 }
289
290 std::string& StringUtils::RemoveDuplicatedSpacesAndTabs(std::string& str)
291 {
292   std::string::iterator it = str.begin();
293   bool onSpace = false;
294   while(it != str.end())
295   {
296     if (*it == '\t')
297       *it = ' ';
298
299     if (*it == ' ')
300     {
301       if (onSpace)
302       {
303         it = str.erase(it);
304         continue;
305       }
306       else
307         onSpace = true;
308     }
309     else
310       onSpace = false;
311
312     ++it;
313   }
314   return str;
315 }
316
317 int StringUtils::Replace(string &str, char oldChar, char newChar)
318 {
319   int replacedChars = 0;
320   for (string::iterator it = str.begin(); it != str.end(); it++)
321   {
322     if (*it == oldChar)
323     {
324       *it = newChar;
325       replacedChars++;
326     }
327   }
328   
329   return replacedChars;
330 }
331
332 int StringUtils::Replace(std::string &str, const std::string &oldStr, const std::string &newStr)
333 {
334   if (oldStr.empty())
335     return 0;
336
337   int replacedChars = 0;
338   size_t index = 0;
339   
340   while (index < str.size() && (index = str.find(oldStr, index)) != string::npos)
341   {
342     str.replace(index, oldStr.size(), newStr);
343     index += newStr.size();
344     replacedChars++;
345   }
346   
347   return replacedChars;
348 }
349
350 int StringUtils::Replace(std::wstring &str, const std::wstring &oldStr, const std::wstring &newStr)
351 {
352   if (oldStr.empty())
353     return 0;
354
355   int replacedChars = 0;
356   size_t index = 0;
357
358   while (index < str.size() && (index = str.find(oldStr, index)) != string::npos)
359   {
360     str.replace(index, oldStr.size(), newStr);
361     index += newStr.size();
362     replacedChars++;
363   }
364
365   return replacedChars;
366 }
367
368 bool StringUtils::StartsWith(const std::string &str1, const std::string &str2)
369 {
370   return str1.compare(0, str2.size(), str2) == 0;
371 }
372
373 bool StringUtils::StartsWith(const std::string &str1, const char *s2)
374 {
375   return StartsWith(str1.c_str(), s2);
376 }
377
378 bool StringUtils::StartsWith(const char *s1, const char *s2)
379 {
380   while (*s2 != '\0')
381   {
382     if (*s1 != *s2)
383       return false;
384     s1++;
385     s2++;
386   }
387   return true;
388 }
389
390 bool StringUtils::StartsWithNoCase(const std::string &str1, const std::string &str2)
391 {
392   return StartsWithNoCase(str1.c_str(), str2.c_str());
393 }
394
395 bool StringUtils::StartsWithNoCase(const std::string &str1, const char *s2)
396 {
397   return StartsWithNoCase(str1.c_str(), s2);
398 }
399
400 bool StringUtils::StartsWithNoCase(const char *s1, const char *s2)
401 {
402   while (*s2 != '\0')
403   {
404     if (::tolower(*s1) != ::tolower(*s2))
405       return false;
406     s1++;
407     s2++;
408   }
409   return true;
410 }
411
412 bool StringUtils::EndsWith(const std::string &str1, const std::string &str2)
413 {
414   if (str1.size() < str2.size())
415     return false;
416   return str1.compare(str1.size() - str2.size(), str2.size(), str2) == 0;
417 }
418
419 bool StringUtils::EndsWith(const std::string &str1, const char *s2)
420 {
421   size_t len2 = strlen(s2);
422   if (str1.size() < len2)
423     return false;
424   return str1.compare(str1.size() - len2, len2, s2) == 0;
425 }
426
427 bool StringUtils::EndsWithNoCase(const std::string &str1, const std::string &str2)
428 {
429   if (str1.size() < str2.size())
430     return false;
431   const char *s1 = str1.c_str() + str1.size() - str2.size();
432   const char *s2 = str2.c_str();
433   while (*s2 != '\0')
434   {
435     if (::tolower(*s1) != ::tolower(*s2))
436       return false;
437     s1++;
438     s2++;
439   }
440   return true;
441 }
442
443 bool StringUtils::EndsWithNoCase(const std::string &str1, const char *s2)
444 {
445   size_t len2 = strlen(s2);
446   if (str1.size() < len2)
447     return false;
448   const char *s1 = str1.c_str() + str1.size() - len2;
449   while (*s2 != '\0')
450   {
451     if (::tolower(*s1) != ::tolower(*s2))
452       return false;
453     s1++;
454     s2++;
455   }
456   return true;
457 }
458
459 void StringUtils::JoinString(const CStdStringArray &strings, const CStdString& delimiter, CStdString& result)
460 {
461   result = "";
462   for(CStdStringArray::const_iterator it = strings.begin(); it != strings.end(); it++ )
463     result += (*it) + delimiter;
464
465   if(result != "")
466     result.erase(result.size()-delimiter.size(), delimiter.size());
467 }
468
469 CStdString StringUtils::JoinString(const CStdStringArray &strings, const CStdString& delimiter)
470 {
471   CStdString result;
472   JoinString(strings, delimiter, result);
473   return result;
474 }
475
476 CStdString StringUtils::Join(const vector<string> &strings, const CStdString& delimiter)
477 {
478   CStdStringArray strArray;
479   for (unsigned int index = 0; index < strings.size(); index++)
480     strArray.push_back(strings.at(index));
481
482   return JoinString(strArray, delimiter);
483 }
484
485 // Splits the string input into pieces delimited by delimiter.
486 // if 2 delimiters are in a row, it will include the empty string between them.
487 // added MaxStrings parameter to restrict the number of returned substrings (like perl and python)
488 int StringUtils::SplitString(const CStdString& input, const CStdString& delimiter, CStdStringArray &results, unsigned int iMaxStrings /* = 0 */)
489 {
490   size_t iPos = std::string::npos;
491   size_t newPos = std::string::npos;
492   size_t sizeS2 = delimiter.size();
493   size_t isize = input.size();
494
495   results.clear();
496
497   vector<unsigned int> positions;
498
499   newPos = input.find(delimiter, 0);
500
501   if (newPos == std::string::npos)
502   {
503     results.push_back(input);
504     return 1;
505   }
506
507   while (newPos != std::string::npos)
508   {
509     positions.push_back(newPos);
510     iPos = newPos;
511     newPos = input.find(delimiter, iPos + sizeS2);
512   }
513
514   // numFound is the number of delimiters which is one less
515   // than the number of substrings
516   unsigned int numFound = positions.size();
517   if (iMaxStrings > 0 && numFound >= iMaxStrings)
518     numFound = iMaxStrings - 1;
519
520   for ( unsigned int i = 0; i <= numFound; i++ )
521   {
522     CStdString s;
523     if ( i == 0 )
524     {
525       if ( i == numFound )
526         s = input;
527       else
528         s = input.substr(i, positions[i]);
529     }
530     else
531     {
532       size_t offset = positions[i - 1] + sizeS2;
533       if ( offset < isize )
534       {
535         if ( i == numFound )
536           s = input.substr(offset);
537         else if ( i > 0 )
538           s = input.substr( positions[i - 1] + sizeS2,
539                          positions[i] - positions[i - 1] - sizeS2 );
540       }
541     }
542     results.push_back(s);
543   }
544   // return the number of substrings
545   return results.size();
546 }
547
548 CStdStringArray StringUtils::SplitString(const CStdString& input, const CStdString& delimiter, unsigned int iMaxStrings /* = 0 */)
549 {
550   CStdStringArray result;
551   SplitString(input, delimiter, result, iMaxStrings);
552   return result;
553 }
554
555 vector<string> StringUtils::Split(const std::string& input, const std::string& delimiter, unsigned int iMaxStrings /* = 0 */)
556 {
557   CStdStringArray result;
558   SplitString(input, delimiter, result, iMaxStrings);
559
560   vector<string> strArray;
561   for (unsigned int index = 0; index < result.size(); index++)
562     strArray.push_back(result.at(index));
563
564   return strArray;
565 }
566
567 // returns the number of occurrences of strFind in strInput.
568 int StringUtils::FindNumber(const CStdString& strInput, const CStdString &strFind)
569 {
570   size_t pos = strInput.find(strFind, 0);
571   int numfound = 0;
572   while (pos != std::string::npos)
573   {
574     numfound++;
575     pos = strInput.find(strFind, pos + 1);
576   }
577   return numfound;
578 }
579
580 // Compares separately the numeric and alphabetic parts of a string.
581 // returns negative if left < right, positive if left > right
582 // and 0 if they are identical (essentially calculates left - right)
583 int64_t StringUtils::AlphaNumericCompare(const wchar_t *left, const wchar_t *right)
584 {
585   wchar_t *l = (wchar_t *)left;
586   wchar_t *r = (wchar_t *)right;
587   wchar_t *ld, *rd;
588   wchar_t lc, rc;
589   int64_t lnum, rnum;
590   const collate<wchar_t>& coll = use_facet< collate<wchar_t> >( locale() );
591   int cmp_res = 0;
592   while (*l != 0 && *r != 0)
593   {
594     // check if we have a numerical value
595     if (*l >= L'0' && *l <= L'9' && *r >= L'0' && *r <= L'9')
596     {
597       ld = l;
598       lnum = 0;
599       while (*ld >= L'0' && *ld <= L'9' && ld < l + 15)
600       { // compare only up to 15 digits
601         lnum *= 10;
602         lnum += *ld++ - '0';
603       }
604       rd = r;
605       rnum = 0;
606       while (*rd >= L'0' && *rd <= L'9' && rd < r + 15)
607       { // compare only up to 15 digits
608         rnum *= 10;
609         rnum += *rd++ - L'0';
610       }
611       // do we have numbers?
612       if (lnum != rnum)
613       { // yes - and they're different!
614         return lnum - rnum;
615       }
616       l = ld;
617       r = rd;
618       continue;
619     }
620     // do case less comparison
621     lc = *l;
622     if (lc >= L'A' && lc <= L'Z')
623       lc += L'a'-L'A';
624     rc = *r;
625     if (rc >= L'A' && rc <= L'Z')
626       rc += L'a'- L'A';
627
628     // ok, do a normal comparison, taking current locale into account. Add special case stuff (eg '(' characters)) in here later
629     if ((cmp_res = coll.compare(&lc, &lc + 1, &rc, &rc + 1)) != 0)
630     {
631       return cmp_res;
632     }
633     l++; r++;
634   }
635   if (*r)
636   { // r is longer
637     return -1;
638   }
639   else if (*l)
640   { // l is longer
641     return 1;
642   }
643   return 0; // files are the same
644 }
645
646 int StringUtils::DateStringToYYYYMMDD(const CStdString &dateString)
647 {
648   CStdStringArray days;
649   int splitCount = StringUtils::SplitString(dateString, "-", days);
650   if (splitCount == 1)
651     return atoi(days[0].c_str());
652   else if (splitCount == 2)
653     return atoi(days[0].c_str())*100+atoi(days[1].c_str());
654   else if (splitCount == 3)
655     return atoi(days[0].c_str())*10000+atoi(days[1].c_str())*100+atoi(days[2].c_str());
656   else
657     return -1;
658 }
659
660 long StringUtils::TimeStringToSeconds(const CStdString &timeString)
661 {
662   CStdString strCopy(timeString);
663   StringUtils::Trim(strCopy);
664   if(StringUtils::EndsWithNoCase(strCopy, " min"))
665   {
666     // this is imdb format of "XXX min"
667     return 60 * atoi(strCopy.c_str());
668   }
669   else
670   {
671     CStdStringArray secs;
672     StringUtils::SplitString(strCopy, ":", secs);
673     int timeInSecs = 0;
674     for (unsigned int i = 0; i < 3 && i < secs.size(); i++)
675     {
676       timeInSecs *= 60;
677       timeInSecs += atoi(secs[i]);
678     }
679     return timeInSecs;
680   }
681 }
682
683 CStdString StringUtils::SecondsToTimeString(long lSeconds, TIME_FORMAT format)
684 {
685   int hh = lSeconds / 3600;
686   lSeconds = lSeconds % 3600;
687   int mm = lSeconds / 60;
688   int ss = lSeconds % 60;
689
690   if (format == TIME_FORMAT_GUESS)
691     format = (hh >= 1) ? TIME_FORMAT_HH_MM_SS : TIME_FORMAT_MM_SS;
692   CStdString strHMS;
693   if (format & TIME_FORMAT_HH)
694     strHMS += StringUtils::Format("%02.2i", hh);
695   else if (format & TIME_FORMAT_H)
696     strHMS += StringUtils::Format("%i", hh);
697   if (format & TIME_FORMAT_MM)
698     strHMS += StringUtils::Format(strHMS.empty() ? "%02.2i" : ":%02.2i", mm);
699   if (format & TIME_FORMAT_SS)
700     strHMS += StringUtils::Format(strHMS.empty() ? "%02.2i" : ":%02.2i", ss);
701   return strHMS;
702 }
703
704 bool StringUtils::IsNaturalNumber(const CStdString& str)
705 {
706   size_t i = 0, n = 0;
707   // allow whitespace,digits,whitespace
708   while (i < str.size() && isspace((unsigned char) str[i]))
709     i++;
710   while (i < str.size() && isdigit((unsigned char) str[i]))
711   {
712     i++; n++;
713   }
714   while (i < str.size() && isspace((unsigned char) str[i]))
715     i++;
716   return i == str.size() && n > 0;
717 }
718
719 bool StringUtils::IsInteger(const CStdString& str)
720 {
721   size_t i = 0, n = 0;
722   // allow whitespace,-,digits,whitespace
723   while (i < str.size() && isspace((unsigned char) str[i]))
724     i++;
725   if (i < str.size() && str[i] == '-')
726     i++;
727   while (i < str.size() && isdigit((unsigned char) str[i]))
728   {
729     i++; n++;
730   }
731   while (i < str.size() && isspace((unsigned char) str[i]))
732     i++;
733   return i == str.size() && n > 0;
734 }
735
736 int StringUtils::asciidigitvalue(char chr)
737 {
738   if (!isasciidigit(chr))
739     return -1;
740
741   return chr - '0';
742 }
743
744 int StringUtils::asciixdigitvalue(char chr)
745 {
746   int v = asciidigitvalue(chr);
747   if (v >= 0)
748     return v;
749   if (chr >= 'a' && chr <= 'f')
750     return chr - 'a' + 10;
751   if (chr >= 'A' && chr <= 'F')
752     return chr - 'A' + 10;
753
754   return -1;
755 }
756
757
758 void StringUtils::RemoveCRLF(CStdString& strLine)
759 {
760   StringUtils::TrimRight(strLine, "\n\r");
761 }
762
763 CStdString StringUtils::SizeToString(int64_t size)
764 {
765   CStdString strLabel;
766   const char prefixes[] = {' ','k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'};
767   unsigned int i = 0;
768   double s = (double)size;
769   while (i < sizeof(prefixes)/sizeof(prefixes[0]) && s >= 1000.0)
770   {
771     s /= 1024.0;
772     i++;
773   }
774
775   if (!i)
776     strLabel = StringUtils::Format("%.0lf %cB ", s, prefixes[i]);
777   else if (s >= 100.0)
778     strLabel = StringUtils::Format("%.1lf %cB", s, prefixes[i]);
779   else
780     strLabel = StringUtils::Format("%.2lf %cB", s, prefixes[i]);
781
782   return strLabel;
783 }
784
785 // return -1 if not, else return the utf8 char length.
786 int IsUTF8Letter(const unsigned char *str)
787 {
788   // reference:
789   // unicode -> utf8 table: http://www.utf8-chartable.de/
790   // latin characters in unicode: http://en.wikipedia.org/wiki/Latin_characters_in_Unicode
791   unsigned char ch = str[0];
792   if (!ch)
793     return -1;
794   if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
795     return 1;
796   if (!(ch & 0x80))
797     return -1;
798   unsigned char ch2 = str[1];
799   if (!ch2)
800     return -1;
801   // check latin 1 letter table: http://en.wikipedia.org/wiki/C1_Controls_and_Latin-1_Supplement
802   if (ch == 0xC3 && ch2 >= 0x80 && ch2 <= 0xBF && ch2 != 0x97 && ch2 != 0xB7)
803     return 2;
804   // check latin extended A table: http://en.wikipedia.org/wiki/Latin_Extended-A
805   if (ch >= 0xC4 && ch <= 0xC7 && ch2 >= 0x80 && ch2 <= 0xBF)
806     return 2;
807   // check latin extended B table: http://en.wikipedia.org/wiki/Latin_Extended-B
808   // and International Phonetic Alphabet: http://en.wikipedia.org/wiki/IPA_Extensions_(Unicode_block)
809   if (((ch == 0xC8 || ch == 0xC9) && ch2 >= 0x80 && ch2 <= 0xBF)
810       || (ch == 0xCA && ch2 >= 0x80 && ch2 <= 0xAF))
811     return 2;
812   return -1;
813 }
814
815 size_t StringUtils::FindWords(const char *str, const char *wordLowerCase)
816 {
817   // NOTE: This assumes word is lowercase!
818   unsigned char *s = (unsigned char *)str;
819   do
820   {
821     // start with a compare
822     unsigned char *c = s;
823     unsigned char *w = (unsigned char *)wordLowerCase;
824     bool same = true;
825     while (same && *c && *w)
826     {
827       unsigned char lc = *c++;
828       if (lc >= 'A' && lc <= 'Z')
829         lc += 'a'-'A';
830
831       if (lc != *w++) // different
832         same = false;
833     }
834     if (same && *w == 0)  // only the same if word has been exhausted
835       return (const char *)s - str;
836
837     // otherwise, skip current word (composed by latin letters) or number
838     int l;
839     if (*s >= '0' && *s <= '9')
840     {
841       ++s;
842       while (*s >= '0' && *s <= '9') ++s;
843     }
844     else if ((l = IsUTF8Letter(s)) > 0)
845     {
846       s += l;
847       while ((l = IsUTF8Letter(s)) > 0) s += l;
848     }
849     else
850       ++s;
851     while (*s && *s == ' ') s++;
852
853     // and repeat until we're done
854   } while (*s);
855
856   return CStdString::npos;
857 }
858
859 // assumes it is called from after the first open bracket is found
860 int StringUtils::FindEndBracket(const CStdString &str, char opener, char closer, int startPos)
861 {
862   int blocks = 1;
863   for (unsigned int i = startPos; i < str.size(); i++)
864   {
865     if (str[i] == opener)
866       blocks++;
867     else if (str[i] == closer)
868     {
869       blocks--;
870       if (!blocks)
871         return i;
872     }
873   }
874
875   return (int)CStdString::npos;
876 }
877
878 void StringUtils::WordToDigits(CStdString &word)
879 {
880   static const char word_to_letter[] = "22233344455566677778889999";
881   StringUtils::ToLower(word);
882   for (unsigned int i = 0; i < word.size(); ++i)
883   { // NB: This assumes ascii, which probably needs extending at some  point.
884     char letter = word[i];
885     if ((letter >= 'a' && letter <= 'z')) // assume contiguous letter range
886     {
887       word[i] = word_to_letter[letter-'a'];
888     }
889     else if (letter < '0' || letter > '9') // We want to keep 0-9!
890     {
891       word[i] = ' ';  // replace everything else with a space
892     }
893   }
894 }
895
896 CStdString StringUtils::CreateUUID()
897 {
898   /* This function generate a DCE 1.1, ISO/IEC 11578:1996 and IETF RFC-4122
899   * Version 4 conform local unique UUID based upon random number generation.
900   */
901   char UuidStrTmp[40];
902   char *pUuidStr = UuidStrTmp;
903   int i;
904
905   static bool m_uuidInitialized = false;
906   if (!m_uuidInitialized)
907   {
908     /* use current time as the seed for rand()*/
909     srand(time(NULL));
910     m_uuidInitialized = true;
911   }
912
913   /*Data1 - 8 characters.*/
914   for(i = 0; i < 8; i++, pUuidStr++)
915     ((*pUuidStr = (rand() % 16)) < 10) ? *pUuidStr += 48 : *pUuidStr += 55;
916
917   /*Data2 - 4 characters.*/
918   *pUuidStr++ = '-';
919   for(i = 0; i < 4; i++, pUuidStr++)
920     ((*pUuidStr = (rand() % 16)) < 10) ? *pUuidStr += 48 : *pUuidStr += 55;
921
922   /*Data3 - 4 characters.*/
923   *pUuidStr++ = '-';
924   for(i = 0; i < 4; i++, pUuidStr++)
925     ((*pUuidStr = (rand() % 16)) < 10) ? *pUuidStr += 48 : *pUuidStr += 55;
926
927   /*Data4 - 4 characters.*/
928   *pUuidStr++ = '-';
929   for(i = 0; i < 4; i++, pUuidStr++)
930     ((*pUuidStr = (rand() % 16)) < 10) ? *pUuidStr += 48 : *pUuidStr += 55;
931
932   /*Data5 - 12 characters.*/
933   *pUuidStr++ = '-';
934   for(i = 0; i < 12; i++, pUuidStr++)
935     ((*pUuidStr = (rand() % 16)) < 10) ? *pUuidStr += 48 : *pUuidStr += 55;
936
937   *pUuidStr = '\0';
938
939   m_lastUUID = UuidStrTmp;
940   return UuidStrTmp;
941 }
942
943 bool StringUtils::ValidateUUID(const CStdString &uuid)
944 {
945   CRegExp guidRE;
946   guidRE.RegComp(ADDON_GUID_RE);
947   return (guidRE.RegFind(uuid.c_str()) == 0);
948 }
949
950 double StringUtils::CompareFuzzy(const CStdString &left, const CStdString &right)
951 {
952   return (0.5 + fstrcmp(left.c_str(), right.c_str(), 0.0) * (left.length() + right.length())) / 2.0;
953 }
954
955 int StringUtils::FindBestMatch(const CStdString &str, const CStdStringArray &strings, double &matchscore)
956 {
957   int best = -1;
958   matchscore = 0;
959
960   int i = 0;
961   for (CStdStringArray::const_iterator it = strings.begin(); it != strings.end(); it++, i++)
962   {
963     int maxlength = max(str.length(), it->length());
964     double score = StringUtils::CompareFuzzy(str, *it) / maxlength;
965     if (score > matchscore)
966     {
967       matchscore = score;
968       best = i;
969     }
970   }
971   return best;
972 }
973
974 bool StringUtils::ContainsKeyword(const CStdString &str, const CStdStringArray &keywords)
975 {
976   for (CStdStringArray::const_iterator it = keywords.begin(); it != keywords.end(); it++)
977   {
978     if (str.find(*it) != str.npos)
979       return true;
980   }
981   return false;
982 }
983
984 size_t StringUtils::utf8_strlen(const char *s)
985 {
986   size_t length = 0;
987   while (*s)
988   {
989     if ((*s++ & 0xC0) != 0x80)
990       length++;
991   }
992   return length;
993 }
994
995 std::string StringUtils::Paramify(const std::string &param)
996 {
997   std::string result = param;
998   // escape backspaces
999   StringUtils::Replace(result, "\\", "\\\\");
1000   // escape double quotes
1001   StringUtils::Replace(result, "\"", "\\\"");
1002
1003   // add double quotes around the whole string
1004   return "\"" + result + "\"";
1005 }
1006
1007 void StringUtils::Tokenize(const std::string& input, std::vector<std::string>& tokens, const std::string& delimiters)
1008 {
1009   // Tokenize ripped from http://www.linuxselfhelp.com/HOWTO/C++Programming-HOWTO-7.html
1010   // Skip delimiters at beginning.
1011   string::size_type lastPos = input.find_first_not_of(delimiters, 0);
1012   // Find first "non-delimiter".
1013   string::size_type pos = input.find_first_of(delimiters, lastPos);
1014
1015   while (string::npos != pos || string::npos != lastPos)
1016   {
1017     // Found a token, add it to the vector.
1018     tokens.push_back(input.substr(lastPos, pos - lastPos));
1019     // Skip delimiters.  Note the "not_of"
1020     lastPos = input.find_first_not_of(delimiters, pos);
1021     // Find next "non-delimiter"
1022     pos = input.find_first_of(delimiters, lastPos);
1023   }
1024 }