Darkvater@5165: /* $Id$ */ Darkvater@5165: Darkvater@5165: #include "stdafx.h" Darkvater@5165: #include "openttd.h" Darkvater@5165: #include "functions.h" Darkvater@5165: #include "string.h" Darkvater@5165: #include "macros.h" Darkvater@5165: #include "table/control_codes.h" Darkvater@5165: Darkvater@5165: #include Darkvater@5165: #include // required for tolower() Darkvater@5165: Darkvater@5165: void ttd_strlcat(char *dst, const char *src, size_t size) Darkvater@5165: { Darkvater@5165: assert(size > 0); Darkvater@5165: for (; size > 0 && *dst != '\0'; --size, ++dst) {} Darkvater@5165: assert(size > 0); Darkvater@5165: while (--size > 0 && *src != '\0') *dst++ = *src++; Darkvater@5165: *dst = '\0'; Darkvater@5165: } Darkvater@5165: Darkvater@5165: Darkvater@5165: void ttd_strlcpy(char *dst, const char *src, size_t size) Darkvater@5165: { Darkvater@5165: assert(size > 0); Darkvater@5165: while (--size > 0 && *src != '\0') *dst++ = *src++; Darkvater@5165: *dst = '\0'; Darkvater@5165: } Darkvater@5165: Darkvater@5165: Darkvater@5165: char* strecat(char* dst, const char* src, const char* last) Darkvater@5165: { Darkvater@5165: assert(dst <= last); Darkvater@5165: for (; *dst != '\0'; ++dst) Darkvater@5165: if (dst == last) return dst; Darkvater@5165: for (; *src != '\0' && dst != last; ++dst, ++src) *dst = *src; Darkvater@5165: *dst = '\0'; Darkvater@5165: return strecpy(dst, src, last); Darkvater@5165: } Darkvater@5165: Darkvater@5165: Darkvater@5165: char* strecpy(char* dst, const char* src, const char* last) Darkvater@5165: { Darkvater@5165: assert(dst <= last); Darkvater@5165: for (; *src != '\0' && dst != last; ++dst, ++src) *dst = *src; Darkvater@5165: *dst = '\0'; Darkvater@5165: #if 1 Darkvater@5165: if (dst == last && *src != '\0') { Darkvater@5165: error("String too long for destination buffer"); Darkvater@5165: } Darkvater@5165: #endif Darkvater@5165: return dst; Darkvater@5165: } Darkvater@5165: Darkvater@5165: Darkvater@5165: char* CDECL str_fmt(const char* str, ...) Darkvater@5165: { Darkvater@5165: char buf[4096]; Darkvater@5165: va_list va; Darkvater@5165: int len; Darkvater@5165: char* p; Darkvater@5165: Darkvater@5165: va_start(va, str); Darkvater@5165: len = vsnprintf(buf, lengthof(buf), str, va); Darkvater@5165: va_end(va); Darkvater@5165: p = malloc(len + 1); Darkvater@5165: if (p != NULL) memcpy(p, buf, len + 1); Darkvater@5165: return p; Darkvater@5165: } Darkvater@5165: Darkvater@5165: Darkvater@5165: void str_validate(char *str) Darkvater@5165: { Darkvater@5165: char *dst = str; Darkvater@5165: WChar c; Darkvater@5165: size_t len; Darkvater@5165: Darkvater@5165: for (len = Utf8Decode(&c, str); c != '\0'; len = Utf8Decode(&c, str)) { Darkvater@5165: if (IsPrintable(c) && (c < SCC_SPRITE_START || c > SCC_SPRITE_END || Darkvater@5165: IsValidChar(c - SCC_SPRITE_START, CS_ALPHANUMERAL))) { Darkvater@5165: /* Copy the character back. Even if dst is current the same as str Darkvater@5165: * (i.e. no characters have been changed) this is quicker than Darkvater@5165: * moving the pointers ahead by len */ Darkvater@5165: do { Darkvater@5165: *dst++ = *str++; Darkvater@5165: } while (--len != 0); Darkvater@5165: } else { Darkvater@5165: /* Replace the undesirable character with a question mark */ Darkvater@5165: str += len; Darkvater@5165: *dst++ = '?'; Darkvater@5165: } Darkvater@5165: } Darkvater@5165: Darkvater@5165: *dst = '\0'; Darkvater@5165: } Darkvater@5165: Darkvater@5165: Darkvater@5165: void str_strip_colours(char *str) Darkvater@5165: { Darkvater@5165: char *dst = str; Darkvater@5165: WChar c; Darkvater@5165: size_t len; Darkvater@5165: Darkvater@5165: for (len = Utf8Decode(&c, str); c != '\0'; len = Utf8Decode(&c, str)) { Darkvater@5165: if (c < SCC_BLUE || c > SCC_BLACK) { Darkvater@5165: /* Copy the character back. Even if dst is current the same as str Darkvater@5165: * (i.e. no characters have been changed) this is quicker than Darkvater@5165: * moving the pointers ahead by len */ Darkvater@5165: do { Darkvater@5165: *dst++ = *str++; Darkvater@5165: } while (--len != 0); Darkvater@5165: } else { Darkvater@5165: /* Just skip (strip) the colour codes */ Darkvater@5165: str += len; Darkvater@5165: } Darkvater@5165: } Darkvater@5165: *dst = '\0'; Darkvater@5165: } Darkvater@5165: Darkvater@5165: /** Convert a given ASCII string to lowercase. Darkvater@5165: * NOTE: only support ASCII characters, no UTF8 fancy. As currently Darkvater@5165: * the function is only used to lowercase data-filenames if they are Darkvater@5165: * not found, this is sufficient. If more, or general functionality is Darkvater@5165: * needed, look to r7271 where it was removed because it was broken when Darkvater@5165: * using certain locales: eg in Turkish the uppercase 'I' was converted to Darkvater@5165: * '?', so just revert to the old functionality */ Darkvater@5165: void strtolower(char *str) Darkvater@5165: { Darkvater@5165: for (; *str != '\0'; str++) *str = tolower(*str); Darkvater@5165: } Darkvater@5165: Darkvater@5165: /** Darkvater@5165: * Only allow certain keys. You can define the filter to be used. This makes Darkvater@5165: * sure no invalid keys can get into an editbox, like BELL. Darkvater@5165: * @param key character to be checked Darkvater@5165: * @param afilter the filter to use Darkvater@5165: * @return true or false depending if the character is printable/valid or not Darkvater@5165: */ Darkvater@5165: bool IsValidChar(WChar key, CharSetFilter afilter) Darkvater@5165: { Darkvater@5165: switch (afilter) { Darkvater@5165: case CS_ALPHANUMERAL: return IsPrintable(key); Darkvater@5165: case CS_NUMERAL: return (key >= '0' && key <= '9'); Darkvater@5165: case CS_ALPHA: return IsPrintable(key) && !(key >= '0' && key <= '9'); Darkvater@5165: } Darkvater@5165: Darkvater@5165: return false; Darkvater@5165: } Darkvater@5165: Darkvater@5165: #ifdef WIN32 Darkvater@5165: int CDECL snprintf(char *str, size_t size, const char *format, ...) Darkvater@5165: { Darkvater@5165: va_list ap; Darkvater@5165: int ret; Darkvater@5165: Darkvater@5165: va_start(ap, format); Darkvater@5165: ret = vsnprintf(str, size, format, ap); Darkvater@5165: va_end(ap); Darkvater@5165: return ret; Darkvater@5165: } Darkvater@5165: Darkvater@5165: #ifdef _MSC_VER Darkvater@5165: int CDECL vsnprintf(char *str, size_t size, const char *format, va_list ap) Darkvater@5165: { Darkvater@5165: int ret; Darkvater@5165: ret = _vsnprintf(str, size, format, ap); Darkvater@5165: if (ret < 0) str[size - 1] = '\0'; Darkvater@5165: return ret; Darkvater@5165: } Darkvater@5165: #endif /* _MSC_VER */ Darkvater@5165: Darkvater@5165: #endif /* WIN32 */ Darkvater@5165: Darkvater@5165: Darkvater@5165: /* UTF-8 handling routines */ Darkvater@5165: Darkvater@5165: Darkvater@5165: /* Decode and consume the next UTF-8 encoded character Darkvater@5165: * @param c Buffer to place decoded character. Darkvater@5165: * @param s Character stream to retrieve character from. Darkvater@5165: * @return Number of characters in the sequence. Darkvater@5165: */ Darkvater@5165: size_t Utf8Decode(WChar *c, const char *s) Darkvater@5165: { Darkvater@5165: assert(c != NULL); Darkvater@5165: Darkvater@5165: if (!HASBIT(s[0], 7)) { Darkvater@5165: /* Single byte character: 0xxxxxxx */ Darkvater@5165: *c = s[0]; Darkvater@5165: return 1; Darkvater@5165: } else if (GB(s[0], 5, 3) == 6) { Darkvater@5165: if (IsUtf8Part(s[1])) { Darkvater@5165: /* Double byte character: 110xxxxx 10xxxxxx */ Darkvater@5165: *c = GB(s[0], 0, 5) << 6 | GB(s[1], 0, 6); Darkvater@5165: if (*c >= 0x80) return 2; Darkvater@5165: } Darkvater@5165: } else if (GB(s[0], 4, 4) == 14) { Darkvater@5165: if (IsUtf8Part(s[1]) && IsUtf8Part(s[2])) { Darkvater@5165: /* Triple byte character: 1110xxxx 10xxxxxx 10xxxxxx */ Darkvater@5165: *c = GB(s[0], 0, 4) << 12 | GB(s[1], 0, 6) << 6 | GB(s[2], 0, 6); Darkvater@5165: if (*c >= 0x800) return 3; Darkvater@5165: } Darkvater@5165: } else if (GB(s[0], 3, 5) == 30) { Darkvater@5165: if (IsUtf8Part(s[1]) && IsUtf8Part(s[2]) && IsUtf8Part(s[3])) { Darkvater@5165: /* 4 byte character: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ Darkvater@5165: *c = GB(s[0], 0, 3) << 18 | GB(s[1], 0, 6) << 12 | GB(s[2], 0, 6) << 6 | GB(s[3], 0, 6); Darkvater@5165: if (*c >= 0x10000 && *c <= 0x10FFFF) return 4; Darkvater@5165: } Darkvater@5165: } Darkvater@5165: Darkvater@5568: //DEBUG(misc, 1, "[utf8] invalid UTF-8 sequence"); Darkvater@5165: *c = '?'; Darkvater@5165: return 1; Darkvater@5165: } Darkvater@5165: Darkvater@5165: Darkvater@5165: /* Encode a unicode character and place it in the buffer Darkvater@5165: * @param buf Buffer to place character. Darkvater@5165: * @param c Unicode character to encode. Darkvater@5165: * @return Number of characters in the encoded sequence. Darkvater@5165: */ Darkvater@5165: size_t Utf8Encode(char *buf, WChar c) Darkvater@5165: { Darkvater@5165: if (c < 0x80) { Darkvater@5165: *buf = c; Darkvater@5165: return 1; Darkvater@5165: } else if (c < 0x800) { Darkvater@5165: *buf++ = 0xC0 + GB(c, 6, 5); Darkvater@5165: *buf = 0x80 + GB(c, 0, 6); Darkvater@5165: return 2; Darkvater@5165: } else if (c < 0x10000) { Darkvater@5165: *buf++ = 0xE0 + GB(c, 12, 4); Darkvater@5165: *buf++ = 0x80 + GB(c, 6, 6); Darkvater@5165: *buf = 0x80 + GB(c, 0, 6); Darkvater@5165: return 3; Darkvater@5165: } else if (c < 0x110000) { Darkvater@5165: *buf++ = 0xF0 + GB(c, 18, 3); Darkvater@5165: *buf++ = 0x80 + GB(c, 12, 6); Darkvater@5165: *buf++ = 0x80 + GB(c, 6, 6); Darkvater@5165: *buf = 0x80 + GB(c, 0, 6); Darkvater@5165: return 4; Darkvater@5165: } Darkvater@5165: Darkvater@5568: //DEBUG(misc, 1, "[utf8] can't UTF-8 encode value 0x%X", c); Darkvater@5165: *buf = '?'; Darkvater@5165: return 1; Darkvater@5165: }