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