00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "debug.h"
00014 #include "core/alloc_func.hpp"
00015 #include "core/math_func.hpp"
00016 #include "string_func.h"
00017
00018 #include "table/control_codes.h"
00019
00020 #include <stdarg.h>
00021 #include <ctype.h>
00022
00023 #ifdef _MSC_VER
00024 #include <errno.h>
00025 #endif
00026
00027 #ifdef WITH_ICU
00028
00029 #include <unicode/ustring.h>
00030 #include "language.h"
00031 #include "gfx_func.h"
00032 #endif
00033
00044 static int CDECL vseprintf(char *str, const char *last, const char *format, va_list ap)
00045 {
00046 ptrdiff_t diff = last - str;
00047 if (diff < 0) return 0;
00048 return min((int)diff, vsnprintf(str, diff + 1, format, ap));
00049 }
00050
00051 void ttd_strlcat(char *dst, const char *src, size_t size)
00052 {
00053 assert(size > 0);
00054 while (size > 0 && *dst != '\0') {
00055 size--;
00056 dst++;
00057 }
00058
00059 ttd_strlcpy(dst, src, size);
00060 }
00061
00062
00063 void ttd_strlcpy(char *dst, const char *src, size_t size)
00064 {
00065 assert(size > 0);
00066 while (--size > 0 && *src != '\0') {
00067 *dst++ = *src++;
00068 }
00069 *dst = '\0';
00070 }
00071
00072
00073 char *strecat(char *dst, const char *src, const char *last)
00074 {
00075 assert(dst <= last);
00076 while (*dst != '\0') {
00077 if (dst == last) return dst;
00078 dst++;
00079 }
00080
00081 return strecpy(dst, src, last);
00082 }
00083
00084
00085 char *strecpy(char *dst, const char *src, const char *last)
00086 {
00087 assert(dst <= last);
00088 while (dst != last && *src != '\0') {
00089 *dst++ = *src++;
00090 }
00091 *dst = '\0';
00092
00093 if (dst == last && *src != '\0') {
00094 #ifdef STRGEN
00095 error("String too long for destination buffer");
00096 #else
00097 DEBUG(misc, 0, "String too long for destination buffer");
00098 #endif
00099 }
00100 return dst;
00101 }
00102
00103
00104 char *CDECL str_fmt(const char *str, ...)
00105 {
00106 char buf[4096];
00107 va_list va;
00108
00109 va_start(va, str);
00110 int len = vseprintf(buf, lastof(buf), str, va);
00111 va_end(va);
00112 char *p = MallocT<char>(len + 1);
00113 memcpy(p, buf, len + 1);
00114 return p;
00115 }
00116
00117
00118 void str_validate(char *str, const char *last, bool allow_newlines, bool ignore)
00119 {
00120
00121
00122 char *dst = str;
00123 while (str <= last && *str != '\0') {
00124 size_t len = Utf8EncodedCharLen(*str);
00125
00126
00127
00128
00129
00130 if ((len == 0 && str + 4 > last) || str + len > last) break;
00131
00132 WChar c;
00133 len = Utf8Decode(&c, str);
00134
00135
00136
00137 if (c == '\0') break;
00138
00139 if (IsPrintable(c) && (c < SCC_SPRITE_START || c > SCC_SPRITE_END)) {
00140
00141
00142
00143 do {
00144 *dst++ = *str++;
00145 } while (--len != 0);
00146 } else if (allow_newlines && c == '\n') {
00147 *dst++ = *str++;
00148 } else {
00149 if (allow_newlines && c == '\r' && str[1] == '\n') {
00150 str += len;
00151 continue;
00152 }
00153
00154 str += len;
00155 if (!ignore) *dst++ = '?';
00156
00157
00158
00159
00160
00161 if (c == SCC_SETX) {
00162 str++;
00163 } else if (c == SCC_SETXY) {
00164 str += 2;
00165 }
00166 }
00167 }
00168
00169 *dst = '\0';
00170 }
00171
00172 bool StrValid(const char *str, const char *last)
00173 {
00174
00175
00176 while (str <= last && *str != '\0') {
00177 size_t len = Utf8EncodedCharLen(*str);
00178
00179
00180
00181
00182 if (len == 0 || str + len > last) return false;
00183
00184 WChar c;
00185 len = Utf8Decode(&c, str);
00186 if (!IsPrintable(c) || (c >= SCC_SPRITE_START && c <= SCC_SPRITE_END)) {
00187 return false;
00188 }
00189
00190 str += len;
00191 }
00192
00193 return *str == '\0';
00194 }
00195
00196 void str_strip_colours(char *str)
00197 {
00198 char *dst = str;
00199 WChar c;
00200 size_t len;
00201
00202 for (len = Utf8Decode(&c, str); c != '\0'; len = Utf8Decode(&c, str)) {
00203 if (c < SCC_BLUE || c > SCC_BLACK) {
00204
00205
00206
00207 do {
00208 *dst++ = *str++;
00209 } while (--len != 0);
00210 } else {
00211
00212 str += len;
00213 }
00214 }
00215 *dst = '\0';
00216 }
00217
00224 size_t Utf8StringLength(const char *s)
00225 {
00226 size_t len = 0;
00227 const char *t = s;
00228 while (Utf8Consume(&t) != 0) len++;
00229 return len;
00230 }
00231
00232
00243 void strtolower(char *str)
00244 {
00245 for (; *str != '\0'; str++) *str = tolower(*str);
00246 }
00247
00255 bool IsValidChar(WChar key, CharSetFilter afilter)
00256 {
00257 switch (afilter) {
00258 case CS_ALPHANUMERAL: return IsPrintable(key);
00259 case CS_NUMERAL: return (key >= '0' && key <= '9');
00260 case CS_NUMERAL_SPACE: return (key >= '0' && key <= '9') || key == ' ';
00261 case CS_ALPHA: return IsPrintable(key) && !(key >= '0' && key <= '9');
00262 case CS_HEXADECIMAL: return (key >= '0' && key <= '9') || (key >= 'a' && key <= 'f') || (key >= 'A' && key <= 'F');
00263 }
00264
00265 return false;
00266 }
00267
00268 #ifdef WIN32
00269
00270 #if (__MINGW32_MAJOR_VERSION < 3) || ((__MINGW32_MAJOR_VERSION == 3) && (__MINGW32_MINOR_VERSION < 14))
00271 int CDECL snprintf(char *str, size_t size, const char *format, ...)
00272 {
00273 va_list ap;
00274 int ret;
00275
00276 va_start(ap, format);
00277 ret = vsnprintf(str, size, format, ap);
00278 va_end(ap);
00279 return ret;
00280 }
00281 #endif
00282
00283 #ifdef _MSC_VER
00284
00291 int CDECL vsnprintf(char *str, size_t size, const char *format, va_list ap)
00292 {
00293 if (size == 0) return 0;
00294
00295 errno = 0;
00296 int ret = _vsnprintf(str, size, format, ap);
00297
00298 if (ret < 0) {
00299 if (errno != ERANGE) {
00300
00301
00302 NOT_REACHED();
00303 }
00304 } else if ((size_t)ret < size) {
00305
00306
00307
00308 return ret;
00309 }
00310
00311
00312
00313 str[size - 1] = '\0';
00314 return (int)size;
00315 }
00316 #endif
00317
00318 #endif
00319
00329 int CDECL seprintf(char *str, const char *last, const char *format, ...)
00330 {
00331 va_list ap;
00332
00333 va_start(ap, format);
00334 int ret = vseprintf(str, last, format, ap);
00335 va_end(ap);
00336 return ret;
00337 }
00338
00339
00347 char *md5sumToString(char *buf, const char *last, const uint8 md5sum[16])
00348 {
00349 char *p = buf;
00350
00351 for (uint i = 0; i < 16; i++) {
00352 p += seprintf(p, last, "%02X", md5sum[i]);
00353 }
00354
00355 return p;
00356 }
00357
00358
00359
00360
00361
00368 size_t Utf8Decode(WChar *c, const char *s)
00369 {
00370 assert(c != NULL);
00371
00372 if (!HasBit(s[0], 7)) {
00373
00374 *c = s[0];
00375 return 1;
00376 } else if (GB(s[0], 5, 3) == 6) {
00377 if (IsUtf8Part(s[1])) {
00378
00379 *c = GB(s[0], 0, 5) << 6 | GB(s[1], 0, 6);
00380 if (*c >= 0x80) return 2;
00381 }
00382 } else if (GB(s[0], 4, 4) == 14) {
00383 if (IsUtf8Part(s[1]) && IsUtf8Part(s[2])) {
00384
00385 *c = GB(s[0], 0, 4) << 12 | GB(s[1], 0, 6) << 6 | GB(s[2], 0, 6);
00386 if (*c >= 0x800) return 3;
00387 }
00388 } else if (GB(s[0], 3, 5) == 30) {
00389 if (IsUtf8Part(s[1]) && IsUtf8Part(s[2]) && IsUtf8Part(s[3])) {
00390
00391 *c = GB(s[0], 0, 3) << 18 | GB(s[1], 0, 6) << 12 | GB(s[2], 0, 6) << 6 | GB(s[3], 0, 6);
00392 if (*c >= 0x10000 && *c <= 0x10FFFF) return 4;
00393 }
00394 }
00395
00396
00397 *c = '?';
00398 return 1;
00399 }
00400
00401
00408 size_t Utf8Encode(char *buf, WChar c)
00409 {
00410 if (c < 0x80) {
00411 *buf = c;
00412 return 1;
00413 } else if (c < 0x800) {
00414 *buf++ = 0xC0 + GB(c, 6, 5);
00415 *buf = 0x80 + GB(c, 0, 6);
00416 return 2;
00417 } else if (c < 0x10000) {
00418 *buf++ = 0xE0 + GB(c, 12, 4);
00419 *buf++ = 0x80 + GB(c, 6, 6);
00420 *buf = 0x80 + GB(c, 0, 6);
00421 return 3;
00422 } else if (c < 0x110000) {
00423 *buf++ = 0xF0 + GB(c, 18, 3);
00424 *buf++ = 0x80 + GB(c, 12, 6);
00425 *buf++ = 0x80 + GB(c, 6, 6);
00426 *buf = 0x80 + GB(c, 0, 6);
00427 return 4;
00428 }
00429
00430
00431 *buf = '?';
00432 return 1;
00433 }
00434
00442 size_t Utf8TrimString(char *s, size_t maxlen)
00443 {
00444 size_t length = 0;
00445
00446 for (const char *ptr = strchr(s, '\0'); *s != '\0';) {
00447 size_t len = Utf8EncodedCharLen(*s);
00448
00449 if (len == 0) len = 1;
00450
00451
00452
00453 if (length + len >= maxlen || (s + len > ptr)) break;
00454 s += len;
00455 length += len;
00456 }
00457
00458 *s = '\0';
00459 return length;
00460 }
00461
00462 #ifdef DEFINE_STRNDUP
00463 #include "core/math_func.hpp"
00464 char *strndup(const char *s, size_t len)
00465 {
00466 len = min(strlen(s), len);
00467 char *tmp = CallocT<char>(len + 1);
00468 memcpy(tmp, s, len);
00469 return tmp;
00470 }
00471 #endif
00472
00473 #ifdef DEFINE_STRCASESTR
00474 char *strcasestr(const char *haystack, const char *needle)
00475 {
00476 size_t hay_len = strlen(haystack);
00477 size_t needle_len = strlen(needle);
00478 while (hay_len >= needle_len) {
00479 if (strncasecmp(haystack, needle, needle_len) == 0) return const_cast<char *>(haystack);
00480
00481 haystack++;
00482 hay_len--;
00483 }
00484
00485 return NULL;
00486 }
00487 #endif
00488
00496 int strnatcmp(const char *s1, const char *s2)
00497 {
00498 #ifdef WITH_ICU
00499 if (_current_collator != NULL) {
00500 UErrorCode status = U_ZERO_ERROR;
00501 int result;
00502
00503
00504 #if U_ICU_VERSION_MAJOR_NUM > 4 || (U_ICU_VERSION_MAJOR_NUM == 4 && U_ICU_VERSION_MINOR_NUM >= 2)
00505
00506 result = _current_collator->compareUTF8(s1, s2, status);
00507 #else
00508 UChar buffer1[DRAW_STRING_BUFFER];
00509 u_strFromUTF8Lenient(buffer1, lengthof(buffer1), NULL, s1, -1, &status);
00510 UChar buffer2[DRAW_STRING_BUFFER];
00511 u_strFromUTF8Lenient(buffer2, lengthof(buffer2), NULL, s2, -1, &status);
00512
00513 result = _current_collator->compare(buffer1, buffer2, status);
00514 #endif
00515 if (U_SUCCESS(status)) return result;
00516 }
00517
00518 #endif
00519
00520
00521 return strcasecmp(s1, s2);
00522 }