1 /*
2  *  The ManaPlus Client
3  *  Copyright (C) 2007-2009  The Mana World Development Team
4  *  Copyright (C) 2009-2010  The Mana Developers
5  *  Copyright (C) 2011-2019  The ManaPlus Developers
6  *  Copyright (C) 2019-2021  Andrei Karas
7  *
8  *  This file is part of The ManaPlus Client.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "utils/stringutils.h"
25 
26 #include "const/utils/utf8.h"
27 
28 #ifdef DYECMD
29 #include "utils/cast.h"
30 #else  // DYECMD
31 #include "resources/iteminfo.h"
32 #include "resources/db/itemdb.h"
33 #endif  // DYECMD
34 
35 #include "utils/gettext.h"
36 #include "utils/foreach.h"
37 
38 #include <algorithm>
39 #include <sstream>
40 
41 #ifdef WIN32
42 #include <sys/time.h>
43 #endif  // WIN32
44 
45 #include "debug.h"
46 
trim(std::string & str)47 std::string &trim(std::string &str)
48 {
49     size_t pos = str.find_last_not_of(' ');
50     if (pos != std::string::npos)
51     {
52         str.erase(pos + 1);
53         pos = str.find_first_not_of(' ');
54 
55         if (pos != std::string::npos)
56             str.erase(0, pos);
57     }
58     else
59     {
60         // There is nothing else but whitespace in the string
61         str.clear();
62     }
63     return str;
64 }
65 
toLower(std::string & str)66 std::string &toLower(std::string &str)
67 {
68     std::transform(str.begin(), str.end(), str.begin(), &tolower);
69     return str;
70 }
71 
toUpper(std::string & str)72 std::string &toUpper(std::string &str)
73 {
74     std::transform(str.begin(), str.end(), str.begin(), &toupper);
75     return str;
76 }
77 
atox(const std::string & str)78 unsigned int atox(const std::string &str)
79 {
80     unsigned int value = 0;
81     if (sscanf(str.c_str(), "0x%06x", &value) != 0)
82         return value;
83     return 0;
84 }
85 
ipToString(const uint32_t address)86 const char *ipToString(const uint32_t address)
87 {
88     static char asciiIP[18];
89 
90     snprintf(asciiIP, sizeof(asciiIP), "%i.%i.%i.%i",
91             CAST_U8(address),
92             CAST_U8(address >> 8),
93             CAST_U8(address >> 16),
94             CAST_U8(address >> 24));
95     asciiIP[17] = 0;
96 
97     return asciiIP;
98 }
99 
strprintf(const char * const format,...)100 std::string strprintf(const char *const format, ...)
101 {
102     char buf[257];
103     va_list args;
104     va_start(args, format);
105     size_t nb = vsnprintf(buf, 256, format, args);
106     buf[256] = 0;
107     va_end(args);
108     if (nb < 256)
109         return buf;
110 
111     // The static size was not big enough, try again with a dynamic allocation.
112     ++nb;
113     char *buf2 = new char[nb];
114     va_start(args, format);
115     vsnprintf(buf2, nb, format, args);
116     va_end(args);
117     std::string res(buf2);
118     delete [] buf2;
119     return res;
120 }
121 
removeColors(std::string msg)122 std::string removeColors(std::string msg)
123 {
124     for (unsigned int f = 0; f < msg.length() - 2 && msg.length() > 2; f++)
125     {
126         while (msg.length() > f + 2 && msg.at(f) == '#'
127                && msg.at(f + 1) == '#')
128         {
129             msg = msg.erase(f, 3);
130         }
131     }
132     return msg;
133 }
134 
compareStrI(const std::string & a,const std::string & b)135 int compareStrI(const std::string &a, const std::string &b)
136 {
137     std::string::const_iterator itA = a.begin();
138     const std::string::const_iterator endA = a.end();
139     std::string::const_iterator itB = b.begin();
140     const std::string::const_iterator endB = b.end();
141 
142     for (; itA < endA && itB < endB; ++itA, ++itB)
143     {
144         const int comp = tolower(*itA) - tolower(*itB);
145         if (comp != 0)
146             return comp;
147     }
148 
149     // Check string lengths
150     if (itA == endA && itB == endB)
151         return 0;
152     else if (itA == endA)
153         return -1;
154     else
155         return 1;
156 }
157 
findSameSubstring(const std::string & restrict str1,const std::string & restrict str2)158 const std::string findSameSubstring(const std::string &restrict str1,
159                                     const std::string &restrict str2)
160 {
161     const int minLength = str1.length() > str2.length()
162         ? CAST_S32(str2.length()) : CAST_S32(str1.length());
163     for (int f = 0; f < minLength; f ++)
164     {
165         if (str1.at(f) != str2.at(f))
166             return str1.substr(0, f);
167     }
168     return str1.substr(0, minLength);
169 }
170 
findSameSubstringI(const std::string & restrict s1,const std::string & restrict s2)171 const std::string findSameSubstringI(const std::string &restrict s1,
172                                      const std::string &restrict s2)
173 {
174     std::string str1 = s1;
175     std::string str2 = s2;
176     toLower(str1);
177     toLower(str2);
178 
179     const size_t minLength = str1.length() > str2.length()
180         ? str2.length() : str1.length();
181     for (size_t f = 0; f < minLength; f ++)
182     {
183         if (str1.at(f) != str2.at(f))
184             return s1.substr(0, f);
185     }
186     return s1.substr(0, minLength);
187 }
188 
findI(std::string str,std::string subStr)189 size_t findI(std::string str, std::string subStr)
190 {
191     str = toLower(str);
192     subStr = toLower(subStr);
193     return str.find(subStr);
194 }
195 
findI(std::string text,const StringVect & list)196 size_t findI(std::string text, const StringVect &list)
197 {
198     toLower(text);
199     FOR_EACH (StringVectCIter, i, list)
200     {
201         std::string subStr = *i;
202         subStr = toLower(subStr);
203         const size_t idx = text.find(subStr);
204         if (idx != std::string::npos)
205             return idx;
206     }
207     return std::string::npos;
208 }
209 
findAny(const std::string & restrict text,const std::string & restrict chars,const size_t pos)210 size_t findAny(const std::string &restrict text,
211                const std::string &restrict chars,
212                const size_t pos)
213 {
214     size_t idx = std::string::npos;
215     const size_t sz = chars.size();
216     for (size_t f = 0; f < sz; f ++)
217     {
218         const size_t idx2 = text.find(chars[f], pos);
219         if (idx2 != std::string::npos && idx2 < idx)
220             idx = idx2;
221     }
222     return idx;
223 }
224 
225 namespace
226 {
227     unsigned int base = 94;
228     unsigned int start = 33;
229 }  // namespace
230 
encodeStr(unsigned int value,const unsigned int size)231 const std::string encodeStr(unsigned int value, const unsigned int size)
232 {
233     std::string buf;
234 
235     do
236     {
237         buf += CAST_S8(value % base + start);
238         value /= base;
239     }
240     while (value != 0U);
241 
242     while (buf.length() < size)
243         buf += CAST_S8(start);
244     return buf;
245 }
246 
247 
decodeStr(const std::string & str)248 unsigned int decodeStr(const std::string &str)
249 {
250     if (str.empty())
251         return 0;
252 
253     int res = str[0] - start;
254     int mult = 1;
255     for (size_t f = 1; f < str.length(); f ++)
256     {
257         mult *= base;
258         res = res + (str[f] - start) * mult;
259     }
260     return res;
261 }
262 
extractNameFromSprite(std::string str)263 std::string extractNameFromSprite(std::string str)
264 {
265     const size_t pos1 = str.rfind('.');
266     if (pos1 != std::string::npos)
267     {
268         size_t pos2 = str.rfind('/');
269         const size_t pos3 = str.rfind('\\');
270         if (pos3 != std::string::npos)
271         {
272             if (pos2 == std::string::npos || pos3 > pos2)
273                 pos2 = pos3;
274         }
275         if (pos2 == std::string::npos)
276             pos2 = CAST_SIZE(-1);
277 
278         const int size = CAST_S32(pos1) - CAST_S32(pos2) - 1;
279         if (size > 0)
280             str = str.substr(pos2 + 1, size);
281     }
282     return str;
283 }
284 
removeSpriteIndex(std::string str)285 std::string removeSpriteIndex(std::string str)
286 {
287     const size_t pos1 = str.rfind('[');
288 
289     if (pos1 != std::string::npos)
290     {
291         size_t pos2 = str.rfind('/');
292         const size_t pos3 = str.rfind('\\');
293         if (pos3 != std::string::npos)
294         {
295             if (pos2 == std::string::npos || pos3 > pos2)
296                 pos2 = pos3;
297         }
298         if (pos2 == std::string::npos)
299             pos2 = CAST_SIZE(-1);
300 
301         const int size = CAST_S32(pos1) - CAST_S32(pos2) - 1;
302         if (size > 0)
303             str = str.substr(pos2 + 1, size);
304     }
305     return str;
306 }
307 
getSafeUtf8String(const std::string & text)308 const char* getSafeUtf8String(const std::string &text)
309 {
310     const size_t sz = text.size();
311     const size_t size = sz + UTF8_MAX_SIZE;
312     char *const buf = new char[size];
313     memcpy(buf, text.c_str(), sz);
314     memset(buf + sz, 0, UTF8_MAX_SIZE);
315     return buf;
316 }
317 
getSafeUtf8String(std::string text,char * const buf)318 void getSafeUtf8String(std::string text, char *const buf)
319 {
320     if (buf == nullptr)
321         return;
322     const size_t sz = text.size();
323     const size_t size = sz + UTF8_MAX_SIZE;
324     if (size > 65500)
325     {
326         text = text.substr(0, 65500);
327         const size_t sz1 = text.size();
328         memcpy(buf, text.c_str(), sz1);
329         memset(buf + sz1, 0, UTF8_MAX_SIZE);
330     }
331     else
332     {
333         memcpy(buf, text.c_str(), sz);
334         memset(buf + sz, 0, UTF8_MAX_SIZE);
335     }
336 }
337 
getFileName(const std::string & path)338 std::string getFileName(const std::string &path)
339 {
340     size_t pos1 = path.rfind('/');
341     const size_t pos2 = path.rfind('\\');
342     if (pos1 == std::string::npos ||
343         (pos2 != std::string::npos && pos2 > pos1))
344     {
345         pos1 = pos2;
346     }
347 
348     if (pos1 == std::string::npos)
349         return path;
350     return path.substr(pos1 + 1);
351 }
352 
getFileDir(const std::string & path)353 std::string getFileDir(const std::string &path)
354 {
355     size_t pos1 = path.rfind('/');
356     const size_t pos2 = path.rfind('\\');
357     if (pos1 == std::string::npos ||
358         (pos2 != std::string::npos && pos2 > pos1))
359     {
360         pos1 = pos2;
361     }
362 
363     if (pos1 == std::string::npos)
364         return path;
365     return path.substr(0, pos1);
366 }
367 
replaceAll(std::string & context,const std::string & restrict from,const std::string & restrict to)368 std::string& replaceAll(std::string& context,
369                         const std::string &restrict from,
370                         const std::string &restrict to)
371 {
372     if (from.empty())
373         return context;
374     size_t lookHere = 0;
375     size_t foundHere;
376     const size_t fromSize = from.size();
377     const size_t toSize = to.size();
378     while ((foundHere = context.find(from, lookHere)) != std::string::npos)
379     {
380         context.replace(foundHere, fromSize, to);
381         lookHere = foundHere + toSize;
382     }
383     return context;
384 }
385 
replaceRecursiveAll(std::string & context,const std::string & restrict from,const char to)386 void replaceRecursiveAll(std::string& context,
387                          const std::string &restrict from,
388                          const char to)
389 {
390     size_t lookHere = 0;
391     size_t foundHere;
392     const size_t fromSize = from.size();
393     while ((foundHere = context.find(from, lookHere)) != std::string::npos)
394     {
395         context.replace(foundHere, fromSize, 1, to);
396         lookHere = foundHere;
397     }
398 }
399 
getBoolFromString(const std::string & text)400 bool getBoolFromString(const std::string &text)
401 {
402     std::string txt = text;
403     toLower(trim(txt));
404     if (txt == "true" || txt == "yes" || txt == "on" || txt == "1")
405         return true;
406     else if (txt == "false" || txt == "no" || txt == "off" || txt == "0")
407         return false;
408     else
409         return static_cast<bool>(atoi(txt.c_str()));
410 }
411 
replaceSpecialChars(std::string & text)412 void replaceSpecialChars(std::string &text)
413 {
414     size_t pos1 = text.find('&');
415     while (pos1 != std::string::npos)
416     {
417         const size_t idx = pos1 + 1;
418         const size_t sz = text.size();
419         if (idx >= sz)
420             break;
421 
422         size_t f;
423         for (f = idx; f < sz; f ++)
424         {
425             if (text[f] < '0' || text[f] > '9')
426                 break;
427         }
428         if (idx + 1 < f && text[f] == ';')
429         {
430             std::string str(" ");
431             str[0] = CAST_S8(atoi(text.substr(
432                 idx, f - idx).c_str()));
433             text = text.substr(0, pos1).append(str).append(text.substr(f + 1));
434             pos1 += 1;
435         }
436         else
437         {
438             pos1 = f + 1;
439         }
440 
441         pos1 = text.find('&', pos1);
442     }
443 }
444 
normalize(const std::string & name)445 std::string normalize(const std::string &name)
446 {
447     std::string normalized = name;
448     return toLower(trim(normalized));
449 }
450 
splitToIntSet(std::set<int> & tokens,const std::string & text,const char separator)451 void splitToIntSet(std::set<int> &tokens,
452                    const std::string &text,
453                    const char separator)
454 {
455     std::stringstream ss(text);
456     std::string item;
457     while (std::getline(ss, item, separator))
458         tokens.insert(atoi(item.c_str()));
459 }
460 
splitToIntList(const std::string & text,const char separator)461 std::list<int> splitToIntList(const std::string &text,
462                               const char separator)
463 {
464     std::list<int> tokens;
465     std::stringstream ss(text);
466     std::string item;
467     while (std::getline(ss, item, separator))
468         tokens.push_back(atoi(item.c_str()));
469 
470     return tokens;
471 }
472 
splitToStringList(const std::string & text,const char separator)473 std::list<std::string> splitToStringList(const std::string &text,
474                                          const char separator)
475 {
476     std::list<std::string> tokens;
477     std::stringstream ss(text);
478     std::string item;
479     while (std::getline(ss, item, separator))
480         tokens.push_back(item);
481 
482     return tokens;
483 }
484 
splitToStringVector(StringVect & tokens,const std::string & text,const char separator)485 void splitToStringVector(StringVect &tokens,
486                          const std::string &text,
487                          const char separator)
488 {
489     std::stringstream ss(text);
490     std::string item;
491     while (std::getline(ss, item, separator))
492     {
493         item = trim(item);
494         if (!item.empty())
495             tokens.push_back(item);
496     }
497 }
498 
splitToStringSet(std::set<std::string> & tokens,const std::string & text,const char separator)499 void splitToStringSet(std::set<std::string> &tokens,
500                       const std::string &text,
501                       const char separator)
502 {
503     std::stringstream ss(text);
504     std::string item;
505     while (std::getline(ss, item, separator))
506     {
507         item = trim(item);
508         if (!item.empty())
509             tokens.insert(item);
510     }
511 }
512 
splitToIntVector(STD_VECTOR<int> & tokens,const std::string & text,const char separator)513 void splitToIntVector(STD_VECTOR<int> &tokens,
514                       const std::string &text,
515                       const char separator)
516 {
517     std::stringstream ss(text);
518     std::string item;
519     while (std::getline(ss, item, separator))
520     {
521         item = trim(item);
522         if (!item.empty())
523             tokens.push_back(atoi(item.c_str()));
524     }
525 }
526 
combineDye(std::string file,const std::string & dye)527 std::string combineDye(std::string file,
528                        const std::string &dye)
529 {
530     if (dye.empty())
531         return file;
532     const size_t pos = file.find_last_of('|');
533     if (pos != std::string::npos)
534         return file.substr(0, pos).append("|").append(dye);
535     return file.append("|").append(dye);
536 }
537 
combineDye2(std::string file,const std::string & dye)538 std::string combineDye2(std::string file,
539                         const std::string &dye)
540 {
541     if (dye.empty())
542         return file;
543 
544     const size_t pos = file.find_last_of('|');
545     if (pos != std::string::npos)
546     {
547         const std::string dye1 = file.substr(pos + 1);
548         std::string str;
549         file = file.substr(0, pos);
550         const std::list<std::string> list1 = splitToStringList(dye1, ';');
551         const std::list<std::string> list2 = splitToStringList(dye, ';');
552         for (std::list<std::string>::const_iterator it1 = list1.begin(),
553              it2 = list2.begin(), it1_end = list1.end(), it2_end = list2.end();
554              it1 != it1_end && it2 != it2_end; ++it1, ++it2)
555         {
556             str.append(*it1).append(":").append(*it2).append(";");
557         }
558         return file.append("|").append(str);
559     }
560     return file;
561 }
562 
combineDye3(std::string file,const std::string & dye)563 std::string combineDye3(std::string file,
564                         const std::string &dye)
565 {
566     if (dye.empty())
567         return file;
568 
569     const size_t pos = file.find_last_of('|');
570     if (pos != std::string::npos)
571     {
572         const std::string dye1 = file.substr(pos + 1);
573         std::string str;
574         file = file.substr(0, pos);
575         const std::list<std::string> list1 = splitToStringList(dye1, ';');
576         const std::list<std::string> list2 = splitToStringList(dye, ';');
577         for (std::list<std::string>::const_iterator it1 = list1.begin(),
578              it2 = list2.begin(), it1_end = list1.end(), it2_end = list2.end();
579              it1 != it1_end && it2 != it2_end; ++it1, ++it2)
580         {
581             str.append(*it1).append(":").append(*it2).append(";");
582         }
583         return file.append("|").append(str);
584     }
585     if (file.empty())
586         return file;
587     return file.append("|").append(dye);
588 }
589 
packList(const std::list<std::string> & list)590 std::string packList(const std::list<std::string> &list)
591 {
592     std::list<std::string>::const_iterator i = list.begin();
593     std::string str;
594     while (i != list.end())
595     {
596         str.append(*i).append("|");
597         ++ i;
598     }
599     const size_t sz = str.size();
600     if (sz > 1)
601         str = str.substr(0, sz - 1);
602     return str;
603 }
604 
unpackList(const std::string & str)605 std::list<std::string> unpackList(const std::string &str)
606 {
607     return splitToStringList(str, '|');
608 }
609 
stringToHexPath(const std::string & str)610 std::string stringToHexPath(const std::string &str)
611 {
612     if (str.empty())
613         return "";
614 
615     std::string hex = strprintf("%%%2x/", CAST_U32(str[0]));
616     for (unsigned f = 1, fsz = CAST_U32(str.size());
617          f < fsz; f ++)
618     {
619         hex.append(strprintf("%%%2x", CAST_U32(str[f])));
620     }
621     return hex;
622 }
623 
deleteCharLeft(std::string & str,unsigned * const pos)624 void deleteCharLeft(std::string &str,
625                     unsigned *const pos)
626 {
627     if (pos == nullptr)
628         return;
629 
630     while (*pos > 0)
631     {
632         (*pos)--;
633         const int v = str[*pos];
634         str.erase(*pos, 1);
635         if ((v & 192) != 128)
636             break;
637     }
638 }
639 
findLast(const std::string & restrict str1,const std::string & restrict str2)640 bool findLast(const std::string &restrict str1,
641               const std::string &restrict str2)
642 {
643     const size_t s1 = str1.size();
644     const size_t s2 = str2.size();
645     if (s1 < s2)
646         return false;
647     if (str1.substr(s1 - s2) == str2)
648         return true;
649     return false;
650 }
651 
findFirst(const std::string & restrict str1,const std::string & restrict str2)652 bool findFirst(const std::string &restrict str1,
653                const std::string &restrict str2)
654 {
655     const size_t s1 = str1.size();
656     const size_t s2 = str2.size();
657     if (s1 < s2)
658         return false;
659     if (str1.substr(0, s2) == str2)
660         return true;
661     return false;
662 }
663 
findCutLast(std::string & restrict str1,const std::string & restrict str2)664 bool findCutLast(std::string &restrict str1,
665                  const std::string &restrict str2)
666 {
667     const size_t s1 = str1.size();
668     const size_t s2 = str2.size();
669     if (s1 < s2)
670         return false;
671     if (str1.substr(s1 - s2) == str2)
672     {
673         str1 = str1.substr(0, s1 - s2);
674         return true;
675     }
676     return false;
677 }
678 
cutLast(std::string & restrict str1,const std::string & restrict str2)679 void cutLast(std::string &restrict str1,
680              const std::string &restrict str2)
681 {
682     const size_t s1 = str1.size();
683     const size_t s2 = str2.size();
684     if (s1 < s2)
685         return;
686     if (str1.substr(s1 - s2) == str2)
687         str1 = str1.substr(0, s1 - s2);
688 }
689 
findCutFirst(std::string & restrict str1,const std::string & restrict str2)690 bool findCutFirst(std::string &restrict str1,
691                   const std::string &restrict str2)
692 {
693     const size_t s1 = str1.size();
694     const size_t s2 = str2.size();
695     if (s1 < s2)
696         return false;
697     if (str1.substr(0, s2) == str2)
698     {
699         str1 = str1.substr(s2);
700         return true;
701     }
702     return false;
703 }
704 
cutFirst(std::string & restrict str1,const std::string & restrict str2)705 void cutFirst(std::string &restrict str1,
706               const std::string &restrict str2)
707 {
708     const size_t s1 = str1.size();
709     const size_t s2 = str2.size();
710     if (s1 < s2)
711         return;
712     if (str1.substr(0, s2) == str2)
713         str1 = str1.substr(s2);
714 }
715 
removeProtocol(std::string & url)716 std::string &removeProtocol(std::string &url)
717 {
718     const size_t i = url.find("://");
719     if (i != std::string::npos)
720         url = url.substr(i + 3);
721     return url;
722 }
723 
strStartWith(const std::string & restrict str1,const std::string & restrict str2)724 bool strStartWith(const std::string &restrict str1,
725                   const std::string &restrict str2)
726 {
727     const size_t sz2 = str2.size();
728     if (str1.size() < sz2)
729         return false;
730     return str1.substr(0, sz2) == str2;
731 }
732 
getDateString()733 std::string getDateString()
734 {
735     char buffer[80];
736     time_t rawtime;
737     time(&rawtime);
738     const tm *const timeinfo = localtime(&rawtime);
739 
740     strftime(buffer, 79, "%Y-%m-%d", timeinfo);
741     return std::string(buffer);
742 }
743 
getDateTimeString()744 std::string getDateTimeString()
745 {
746     char buffer[80];
747     time_t rawtime;
748     time(&rawtime);
749     const tm *const timeinfo = localtime(&rawtime);
750 
751     strftime(buffer, 79, "%Y-%m-%d %H:%M:%S", timeinfo);
752     return std::string(buffer);
753 }
754 
parseBoolean(const std::string & value)755 signed char parseBoolean(const std::string &value)
756 {
757     std::string txt = value;
758     toLower(trim(txt));
759     if (txt == "true" || txt == "yes" || txt == "on" || txt == "1")
760         return 1;
761     else if (txt == "false" || txt == "no" || txt == "off" || txt == "0")
762         return 0;
763     else
764         return -1;
765 }
766 
encodeLinkText(std::string data)767 std::string encodeLinkText(std::string data)
768 {
769     return replaceAll(data, "|", "\342\235\230");
770 }
771 
decodeLinkText(std::string data)772 std::string decodeLinkText(std::string data)
773 {
774     return replaceAll(data, "\342\235\230", "|");
775 }
776 
toStringPrint(const unsigned int val)777 std::string toStringPrint(const unsigned int val)
778 {
779     static char str[100];
780     snprintf(str, sizeof(str), "%u 0x%x", val, val);
781     str[99] = 0;
782     return str;
783 }
784 
toString(uint32_t num)785 std::string toString(uint32_t num)
786 {
787     char buf[30];
788     buf[29] = '\0';
789     size_t idx = 28;
790     do
791         buf[idx--] = CAST_8((num % 10) + '0');
792     while ((num /= 10) != 0);
793     return buf + idx + 1;
794 }
795 
toString(uint64_t num)796 std::string toString(uint64_t num)
797 {
798     char buf[100];
799     buf[99] = '\0';
800     size_t idx = 98;
801     do
802         buf[idx--] = CAST_8((num % 10) + '0');
803     while ((num /= 10) != 0);
804     return buf + idx + 1;
805 }
806 
toString(uint16_t num)807 std::string toString(uint16_t num)
808 {
809     char buf[10];
810     buf[9] = '\0';
811     size_t idx = 8;
812     do
813         buf[idx--] = CAST_8((num % 10) + '0');
814     while ((num /= 10) != 0);
815     return buf + idx + 1;
816 }
817 
toString(unsigned char num)818 std::string toString(unsigned char num)
819 {
820     char buf[5];
821     buf[4] = '\0';
822     size_t idx = 3;
823     do
824         buf[idx--] = CAST_8((num % 10) + '0');
825     while ((num /= 10) != 0);
826     return buf + idx + 1;
827 }
828 
toString(int32_t num)829 std::string toString(int32_t num)
830 {
831     char buf[30];
832     bool useSign(false);
833     buf[29] = '\0';
834     size_t idx = 28;
835 
836     if (num < 0)
837     {
838         useSign = true;
839         num = -num;
840     }
841     do
842         buf[idx--] = CAST_8((num % 10) + '0');
843     while ((num /= 10) != 0);
844     if (useSign)
845         buf[idx--] = '-';
846     return buf + idx + 1;
847 }
848 
toString(const float num)849 std::string toString(const float num)
850 {
851     return strprintf("%f", num);
852 }
853 
toString(const double num)854 std::string toString(const double num)
855 {
856     return strprintf("%f", static_cast<float>(num));
857 }
858 
isDigit(const std::string & str)859 bool isDigit(const std::string &str)
860 {
861     if (str.empty())
862         return false;
863     const size_t sz = str.size();
864     for (size_t f = 0; f < sz; f ++)
865     {
866         const char &chr = str[f];
867         if (chr < '0' || chr > '9')
868             return false;
869     }
870     return true;
871 }
872 
secureChatCommand(std::string & str)873 void secureChatCommand(std::string &str)
874 {
875     if (str[0] == '/' || str[0] == '@' || str[0] == '#')
876         str = "_" + str;
877 }
878 
parse2Int(const std::string & args,int & x,int & y)879 bool parse2Int(const std::string &args,
880                int &x,
881                int &y)
882 {
883     bool isValid = false;
884     size_t pos = args.find(' ');
885     if (pos == std::string::npos)
886         pos = args.find(',');
887     if (pos != std::string::npos)
888     {
889         if (pos + 1 < args.length())
890         {
891             x = atoi(args.substr(0, pos).c_str());
892             y = atoi(args.substr(pos + 1, args.length()).c_str());
893             isValid = true;
894         }
895     }
896     return isValid;
897 }
898 
parse2Str(const std::string & args,std::string & str1,std::string & str2)899 bool parse2Str(const std::string &args,
900                std::string &str1,
901                std::string &str2)
902 {
903     bool isValid = false;
904     size_t pos = args.find(' ');
905     if (pos == std::string::npos)
906         pos = args.find(',');
907     if (pos != std::string::npos)
908     {
909         if (pos + 1 < args.length())
910         {
911             str1 = args.substr(0, pos);
912             str2 = args.substr(pos + 1, args.length());
913             isValid = true;
914         }
915     }
916     return isValid;
917 }
918 
parseNumber(const std::string & str)919 uint32_t parseNumber(const std::string &str)
920 {
921     uint32_t i = 0;
922     int idx = 0;
923     if (strStartWith(str, "0x"))
924         idx = 2;
925     else if (str[0] == 'h' || str[0] == 'x')
926         idx = 1;
927     if (idx > 0)
928         sscanf(str.substr(idx).c_str(), "%10x", &i);
929     else
930         i = atoi(str.c_str());
931     return i;
932 }
933 
removeToken(std::string & str,const std::string & token)934 std::string removeToken(std::string &str,
935                         const std::string &token)
936 {
937     const size_t idx = str.find(token);
938     if (idx > 0 && idx != std::string::npos)
939         str = str.substr(idx + 1);
940     else
941         str.clear();
942     return str;
943 }
944 
timeToStr(const uint32_t time)945 std::string timeToStr(const uint32_t time)
946 {
947     char buf[101];
948     const time_t tempTime = time;
949     tm *const timeInfo = localtime(&tempTime);
950     if (strftime(&buf[0], 100, "%Y-%m-%d_%H-%M-%S", timeInfo) != 0U)
951         return std::string(buf);
952     return "unknown";
953 }
954 
timeDiffToString(int timeDiff)955 std::string timeDiffToString(int timeDiff)
956 {
957     std::string str;
958 
959     const int weeks = timeDiff / 60 / 60 / 24 / 7;
960     if (weeks > 0)
961     {
962         // TRANSLATORS: uptime command
963         str = strprintf(ngettext(N_("%d week"), N_("%d weeks"),
964             weeks), weeks);
965         timeDiff -= weeks * 60 * 60 * 24 * 7;
966     }
967 
968     const int days = timeDiff / 60 / 60 / 24;
969     if (days > 0)
970     {
971         if (!str.empty())
972             str.append(", ");
973         // TRANSLATORS: uptime command
974         str.append(strprintf(ngettext(N_("%d day"), N_("%d days"),
975             days), days));
976         timeDiff -= days * 60 * 60 * 24;
977     }
978     const int hours = timeDiff / 60 / 60;
979     if (hours > 0)
980     {
981         if (!str.empty())
982             str.append(", ");
983         // TRANSLATORS: uptime command
984         str.append(strprintf(ngettext(N_("%d hour"), N_("%d hours"),
985             hours), hours));
986         timeDiff -= hours * 60 * 60;
987     }
988     const int min = timeDiff / 60;
989     if (min > 0)
990     {
991         if (!str.empty())
992             str.append(", ");
993         // TRANSLATORS: uptime command
994         str.append(strprintf(ngettext(N_("%d minute"), N_("%d minutes"),
995             min), min));
996         timeDiff -= min * 60;
997     }
998 
999     if (timeDiff > 0)
1000     {
1001         if (!str.empty())
1002             str.append(", ");
1003         // TRANSLATORS: uptime command
1004         str.append(strprintf(ngettext(N_("%d second"), N_("%d seconds"),
1005             timeDiff), timeDiff));
1006     }
1007     if (str.empty())
1008     {
1009         // TRANSLATORS: uptime command
1010         str.append(strprintf(ngettext(N_("%d second"), N_("%d seconds"),
1011             0), 0));
1012     }
1013     return str;
1014 }
1015 
escapeString(std::string str)1016 std::string escapeString(std::string str)
1017 {
1018     replaceAll(str, "\"", "\\\"");
1019     return "\"" + str + "\"";
1020 }
1021 
sanitizePath(std::string & path)1022 void sanitizePath(std::string &path)
1023 {
1024 #ifdef WIN32
1025     const char sepStr = '\\';
1026     const std::string sep2Str = "\\\\";
1027     const std::string sepWrongStr = "/";
1028 #else
1029     const char sepStr = '/';
1030     const std::string sep2Str = "//";
1031     const std::string sepWrongStr = "\\";
1032 #endif
1033     replaceRecursiveAll(path, sepWrongStr, sepStr);
1034     replaceRecursiveAll(path, sep2Str, sepStr);
1035 }
1036 
pathJoin(std::string str1,const std::string & str2)1037 std::string pathJoin(std::string str1,
1038                      const std::string &str2)
1039 {
1040 #ifdef WIN32
1041     const char sep = '\\';
1042     std::string sepStr = "\\";
1043 #else
1044     const char sep = '/';
1045     std::string sepStr = "/";
1046 #endif
1047 
1048     if (str1.empty())
1049     {
1050         if (str2[0] == sep)
1051             return str2;
1052         return sepStr.append(str2);
1053     }
1054     const size_t sz1 = str1.size();
1055     if (str2.empty())
1056     {
1057         if (str1[sz1 - 1] == sep)
1058             return str1;
1059         return str1.append(sepStr);
1060     }
1061     if (str1[sz1 - 1] == sep)
1062     {
1063         if (str2[0] == sep)
1064             return str1.append(str2.substr(1));
1065         return str1.append(str2);
1066     }
1067     else
1068     {
1069         if (str2[0] == sep)
1070             return str1.append(str2);
1071         return str1.append(sepStr).append(str2);
1072     }
1073 }
1074 
pathJoin(std::string str1,const std::string & str2,const std::string & str3)1075 std::string pathJoin(std::string str1,
1076                      const std::string &str2,
1077                      const std::string &str3)
1078 {
1079 #ifdef WIN32
1080     const char sep = '\\';
1081     std::string sepStr = "\\";
1082 #else
1083     const char sep = '/';
1084     std::string sepStr = "/";
1085 #endif
1086 
1087     if (str1.empty())
1088     {
1089         return pathJoin(str2, str3);
1090     }
1091     size_t sz1 = str1.size();
1092     if (str2.empty())
1093     {
1094         return pathJoin(str1, str3);
1095     }
1096     if (str3.empty())
1097     {
1098         return pathJoin(str1, str2);
1099     }
1100     if (str1[sz1 - 1] == sep)
1101     {
1102         if (str2[0] == sep)
1103             str1.append(str2.substr(1));
1104         else
1105             str1.append(str2);
1106     }
1107     else
1108     {
1109         if (str2[0] == sep)
1110             str1.append(str2);
1111         else
1112             str1.append(sepStr).append(str2);
1113     }
1114 
1115     sz1 = str1.size();
1116     if (str1[sz1 - 1] == sep)
1117     {
1118         if (str3[0] == sep)
1119             return str1.append(str3.substr(1));
1120         return str1.append(str3);
1121     }
1122     else
1123     {
1124         if (str3[0] == sep)
1125             return str1.append(str3);
1126         return str1.append(sepStr).append(str3);
1127     }
1128 }
1129 
urlJoin(std::string str1,const std::string & str2)1130 std::string urlJoin(std::string str1,
1131                     const std::string &str2)
1132 {
1133     const char sep = '/';
1134     std::string sepStr = "/";
1135 
1136     if (str1.empty())
1137     {
1138         if (str2[0] == sep)
1139             return str2;
1140         return sepStr.append(str2);
1141     }
1142     const size_t sz1 = str1.size();
1143     if (str2.empty())
1144     {
1145         if (str1[sz1 - 1] == sep)
1146             return str1;
1147         return str1.append(sepStr);
1148     }
1149     if (str1[sz1 - 1] == sep)
1150     {
1151         if (str2[0] == sep)
1152             return str1.append(str2.substr(1));
1153         return str1.append(str2);
1154     }
1155     else
1156     {
1157         if (str2[0] == sep)
1158             return str1.append(str2);
1159         return str1.append(sepStr).append(str2);
1160     }
1161 }
1162 
rfindSepatator(const std::string & str1)1163 size_t rfindSepatator(const std::string &str1)
1164 {
1165     const size_t idx1 = str1.rfind('/');
1166     const size_t idx2 = str1.rfind('\\');
1167     if (idx1 != std::string::npos)
1168     {   // idx1
1169         if (idx2 != std::string::npos)
1170         {   // idx1, idx2
1171             if (idx1 >= idx2)
1172                 return idx1;
1173             else
1174                 return idx2;
1175         }
1176         else
1177         {   // idx1, not idx2
1178             return idx1;
1179         }
1180     }
1181     else
1182     {   // not idx1
1183         if (idx2 != std::string::npos)
1184         {   // not idx1, idx2
1185             return idx2;
1186         }
1187         else
1188         {   // not idx1, not idx2
1189             return std::string::npos;
1190         }
1191     }
1192 }
1193 
1194 #ifndef DYECMD
replaceItemLinks(std::string & msg)1195 void replaceItemLinks(std::string &msg)
1196 {
1197     // Check for item link
1198     size_t start2 = msg.find('[');
1199     size_t sz = msg.size();
1200     while (start2 + 1 < sz &&
1201            start2 != std::string::npos &&
1202            msg[start2 + 1] != '@')
1203     {
1204         const size_t end = msg.find(']', start2);
1205         if (start2 + 1 != end &&
1206             end != std::string::npos)
1207         {
1208             // Catch multiple embeds and ignore them
1209             // so it doesn't crash the client.
1210             while ((msg.find('[', start2 + 1) != std::string::npos) &&
1211                    (msg.find('[', start2 + 1) < end))
1212             {
1213                 start2 = msg.find('[', start2 + 1);
1214             }
1215 
1216             if (start2 + 1 < sz &&
1217                 end < sz &&
1218                 end > start2 + 1)
1219             {
1220                 std::string itemStr = msg.substr(start2 + 1, end - start2 - 1);
1221 
1222                 StringVect parts;
1223                 splitToStringVector(parts, itemStr, ',');
1224                 if (parts.empty())
1225                     return;
1226 
1227                 const ItemInfo &itemInfo = ItemDB::get(parts[0]);
1228                 const int itemId = itemInfo.getId();
1229                 if (itemId != 0)
1230                 {
1231                     std::string temp = strprintf("@@%d", itemId);
1232                     std::string name = parts[0];
1233                     msg.erase(start2 + 1, end - start2 - 1);
1234                     parts.erase(parts.begin());
1235                     if (!parts.empty())
1236                         name.clear();
1237 
1238                     FOR_EACH (StringVectCIter, it, parts)
1239                     {
1240                         std:: string str = *it;
1241                         trim(str);
1242                         const ItemInfo &itemInfo2 = ItemDB::get(str);
1243                         const int cardId = itemInfo2.getId();
1244                         if (cardId != 0)
1245                             temp.append(strprintf(",%d", cardId));
1246                     }
1247                     temp.append("|");
1248                     temp.append(name);
1249                     temp.append("@@");
1250                     msg.insert(start2 + 1, temp);
1251                     sz = msg.size();
1252                 }
1253             }
1254         }
1255         start2 = msg.find('[', start2 + 1);
1256     }
1257 }
1258 #else  // DYECMD
1259 
replaceItemLinks(std::string & msg A_UNUSED)1260 void replaceItemLinks(std::string &msg A_UNUSED)
1261 {
1262 }
1263 #endif  // DYECMD
1264