|
1 /* $Id$ */ |
|
2 |
|
3 #include "stdafx.h" |
|
4 #include "openttd.h" |
|
5 #include "functions.h" |
|
6 #include "string.h" |
|
7 #include "macros.h" |
|
8 #include "table/control_codes.h" |
|
9 |
|
10 #include <stdarg.h> |
|
11 #include <ctype.h> // required for tolower() |
|
12 |
|
13 void ttd_strlcat(char *dst, const char *src, size_t size) |
|
14 { |
|
15 assert(size > 0); |
|
16 for (; size > 0 && *dst != '\0'; --size, ++dst) {} |
|
17 assert(size > 0); |
|
18 while (--size > 0 && *src != '\0') *dst++ = *src++; |
|
19 *dst = '\0'; |
|
20 } |
|
21 |
|
22 |
|
23 void ttd_strlcpy(char *dst, const char *src, size_t size) |
|
24 { |
|
25 assert(size > 0); |
|
26 while (--size > 0 && *src != '\0') *dst++ = *src++; |
|
27 *dst = '\0'; |
|
28 } |
|
29 |
|
30 |
|
31 char* strecat(char* dst, const char* src, const char* last) |
|
32 { |
|
33 assert(dst <= last); |
|
34 for (; *dst != '\0'; ++dst) |
|
35 if (dst == last) return dst; |
|
36 for (; *src != '\0' && dst != last; ++dst, ++src) *dst = *src; |
|
37 *dst = '\0'; |
|
38 return strecpy(dst, src, last); |
|
39 } |
|
40 |
|
41 |
|
42 char* strecpy(char* dst, const char* src, const char* last) |
|
43 { |
|
44 assert(dst <= last); |
|
45 for (; *src != '\0' && dst != last; ++dst, ++src) *dst = *src; |
|
46 *dst = '\0'; |
|
47 #if 1 |
|
48 if (dst == last && *src != '\0') { |
|
49 error("String too long for destination buffer"); |
|
50 } |
|
51 #endif |
|
52 return dst; |
|
53 } |
|
54 |
|
55 |
|
56 char* CDECL str_fmt(const char* str, ...) |
|
57 { |
|
58 char buf[4096]; |
|
59 va_list va; |
|
60 int len; |
|
61 char* p; |
|
62 |
|
63 va_start(va, str); |
|
64 len = vsnprintf(buf, lengthof(buf), str, va); |
|
65 va_end(va); |
|
66 p = malloc(len + 1); |
|
67 if (p != NULL) memcpy(p, buf, len + 1); |
|
68 return p; |
|
69 } |
|
70 |
|
71 |
|
72 void str_validate(char *str) |
|
73 { |
|
74 char *dst = str; |
|
75 WChar c; |
|
76 size_t len; |
|
77 |
|
78 for (len = Utf8Decode(&c, str); c != '\0'; len = Utf8Decode(&c, str)) { |
|
79 if (IsPrintable(c) && (c < SCC_SPRITE_START || c > SCC_SPRITE_END || |
|
80 IsValidChar(c - SCC_SPRITE_START, CS_ALPHANUMERAL))) { |
|
81 /* Copy the character back. Even if dst is current the same as str |
|
82 * (i.e. no characters have been changed) this is quicker than |
|
83 * moving the pointers ahead by len */ |
|
84 do { |
|
85 *dst++ = *str++; |
|
86 } while (--len != 0); |
|
87 } else { |
|
88 /* Replace the undesirable character with a question mark */ |
|
89 str += len; |
|
90 *dst++ = '?'; |
|
91 } |
|
92 } |
|
93 |
|
94 *dst = '\0'; |
|
95 } |
|
96 |
|
97 |
|
98 void str_strip_colours(char *str) |
|
99 { |
|
100 char *dst = str; |
|
101 WChar c; |
|
102 size_t len; |
|
103 |
|
104 for (len = Utf8Decode(&c, str); c != '\0'; len = Utf8Decode(&c, str)) { |
|
105 if (c < SCC_BLUE || c > SCC_BLACK) { |
|
106 /* Copy the character back. Even if dst is current the same as str |
|
107 * (i.e. no characters have been changed) this is quicker than |
|
108 * moving the pointers ahead by len */ |
|
109 do { |
|
110 *dst++ = *str++; |
|
111 } while (--len != 0); |
|
112 } else { |
|
113 /* Just skip (strip) the colour codes */ |
|
114 str += len; |
|
115 } |
|
116 } |
|
117 *dst = '\0'; |
|
118 } |
|
119 |
|
120 /** Convert a given ASCII string to lowercase. |
|
121 * NOTE: only support ASCII characters, no UTF8 fancy. As currently |
|
122 * the function is only used to lowercase data-filenames if they are |
|
123 * not found, this is sufficient. If more, or general functionality is |
|
124 * needed, look to r7271 where it was removed because it was broken when |
|
125 * using certain locales: eg in Turkish the uppercase 'I' was converted to |
|
126 * '?', so just revert to the old functionality */ |
|
127 void strtolower(char *str) |
|
128 { |
|
129 for (; *str != '\0'; str++) *str = tolower(*str); |
|
130 } |
|
131 |
|
132 /** |
|
133 * Only allow certain keys. You can define the filter to be used. This makes |
|
134 * sure no invalid keys can get into an editbox, like BELL. |
|
135 * @param key character to be checked |
|
136 * @param afilter the filter to use |
|
137 * @return true or false depending if the character is printable/valid or not |
|
138 */ |
|
139 bool IsValidChar(WChar key, CharSetFilter afilter) |
|
140 { |
|
141 switch (afilter) { |
|
142 case CS_ALPHANUMERAL: return IsPrintable(key); |
|
143 case CS_NUMERAL: return (key >= '0' && key <= '9'); |
|
144 case CS_ALPHA: return IsPrintable(key) && !(key >= '0' && key <= '9'); |
|
145 } |
|
146 |
|
147 return false; |
|
148 } |
|
149 |
|
150 #ifdef WIN32 |
|
151 int CDECL snprintf(char *str, size_t size, const char *format, ...) |
|
152 { |
|
153 va_list ap; |
|
154 int ret; |
|
155 |
|
156 va_start(ap, format); |
|
157 ret = vsnprintf(str, size, format, ap); |
|
158 va_end(ap); |
|
159 return ret; |
|
160 } |
|
161 |
|
162 #ifdef _MSC_VER |
|
163 int CDECL vsnprintf(char *str, size_t size, const char *format, va_list ap) |
|
164 { |
|
165 int ret; |
|
166 ret = _vsnprintf(str, size, format, ap); |
|
167 if (ret < 0) str[size - 1] = '\0'; |
|
168 return ret; |
|
169 } |
|
170 #endif /* _MSC_VER */ |
|
171 |
|
172 #endif /* WIN32 */ |
|
173 |
|
174 |
|
175 /* UTF-8 handling routines */ |
|
176 |
|
177 |
|
178 /* Decode and consume the next UTF-8 encoded character |
|
179 * @param c Buffer to place decoded character. |
|
180 * @param s Character stream to retrieve character from. |
|
181 * @return Number of characters in the sequence. |
|
182 */ |
|
183 size_t Utf8Decode(WChar *c, const char *s) |
|
184 { |
|
185 assert(c != NULL); |
|
186 |
|
187 if (!HASBIT(s[0], 7)) { |
|
188 /* Single byte character: 0xxxxxxx */ |
|
189 *c = s[0]; |
|
190 return 1; |
|
191 } else if (GB(s[0], 5, 3) == 6) { |
|
192 if (IsUtf8Part(s[1])) { |
|
193 /* Double byte character: 110xxxxx 10xxxxxx */ |
|
194 *c = GB(s[0], 0, 5) << 6 | GB(s[1], 0, 6); |
|
195 if (*c >= 0x80) return 2; |
|
196 } |
|
197 } else if (GB(s[0], 4, 4) == 14) { |
|
198 if (IsUtf8Part(s[1]) && IsUtf8Part(s[2])) { |
|
199 /* Triple byte character: 1110xxxx 10xxxxxx 10xxxxxx */ |
|
200 *c = GB(s[0], 0, 4) << 12 | GB(s[1], 0, 6) << 6 | GB(s[2], 0, 6); |
|
201 if (*c >= 0x800) return 3; |
|
202 } |
|
203 } else if (GB(s[0], 3, 5) == 30) { |
|
204 if (IsUtf8Part(s[1]) && IsUtf8Part(s[2]) && IsUtf8Part(s[3])) { |
|
205 /* 4 byte character: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ |
|
206 *c = GB(s[0], 0, 3) << 18 | GB(s[1], 0, 6) << 12 | GB(s[2], 0, 6) << 6 | GB(s[3], 0, 6); |
|
207 if (*c >= 0x10000 && *c <= 0x10FFFF) return 4; |
|
208 } |
|
209 } |
|
210 |
|
211 //DEBUG(misc, 1, "[utf8] invalid UTF-8 sequence"); |
|
212 *c = '?'; |
|
213 return 1; |
|
214 } |
|
215 |
|
216 |
|
217 /* Encode a unicode character and place it in the buffer |
|
218 * @param buf Buffer to place character. |
|
219 * @param c Unicode character to encode. |
|
220 * @return Number of characters in the encoded sequence. |
|
221 */ |
|
222 size_t Utf8Encode(char *buf, WChar c) |
|
223 { |
|
224 if (c < 0x80) { |
|
225 *buf = c; |
|
226 return 1; |
|
227 } else if (c < 0x800) { |
|
228 *buf++ = 0xC0 + GB(c, 6, 5); |
|
229 *buf = 0x80 + GB(c, 0, 6); |
|
230 return 2; |
|
231 } else if (c < 0x10000) { |
|
232 *buf++ = 0xE0 + GB(c, 12, 4); |
|
233 *buf++ = 0x80 + GB(c, 6, 6); |
|
234 *buf = 0x80 + GB(c, 0, 6); |
|
235 return 3; |
|
236 } else if (c < 0x110000) { |
|
237 *buf++ = 0xF0 + GB(c, 18, 3); |
|
238 *buf++ = 0x80 + GB(c, 12, 6); |
|
239 *buf++ = 0x80 + GB(c, 6, 6); |
|
240 *buf = 0x80 + GB(c, 0, 6); |
|
241 return 4; |
|
242 } |
|
243 |
|
244 //DEBUG(misc, 1, "[utf8] can't UTF-8 encode value 0x%X", c); |
|
245 *buf = '?'; |
|
246 return 1; |
|
247 } |