1 /*
2 * This software is licensed under the terms of the MIT License.
3 * See COPYING for further information.
4 * ---
5 * Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
6 * Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
7 */
8
9 #include "taisei.h"
10
11 #include "stringops.h"
12 #include "miscmath.h"
13 #include "assert.h"
14
strendswith(const char * s,const char * e)15 bool strendswith(const char *s, const char *e) {
16 int ls = strlen(s);
17 int le = strlen(e);
18
19 if(le > ls)
20 return false;
21
22 int i; for(i = 0; i < le; ++i)
23 if(s[ls-i-1] != e[le-i-1])
24 return false;
25
26 return true;
27 }
28
strstartswith(const char * s,const char * p)29 bool strstartswith(const char *s, const char *p) {
30 int ls = strlen(s);
31 int lp = strlen(p);
32
33 if(ls < lp)
34 return false;
35
36 return !strncmp(s, p, lp);
37 }
38
strendswith_any(const char * s,const char ** earray)39 bool strendswith_any(const char *s, const char **earray) {
40 for(const char **e = earray; *e; ++e) {
41 if(strendswith(s, *e)) {
42 return true;
43 }
44 }
45
46 return false;
47 }
48
strstartswith_any(const char * s,const char ** earray)49 bool strstartswith_any(const char *s, const char **earray) {
50 for(const char **e = earray; *e; ++e) {
51 if(strstartswith(s, *e)) {
52 return true;
53 }
54 }
55
56 return false;
57 }
58
stralloc(char ** dest,const char * src)59 void stralloc(char **dest, const char *src) {
60 free(*dest);
61
62 if(src) {
63 *dest = malloc(strlen(src)+1);
64 strcpy(*dest, src);
65 } else {
66 *dest = NULL;
67 }
68 }
69
strjoin(const char * first,...)70 char* strjoin(const char *first, ...) {
71 va_list args;
72 size_t size = strlen(first) + 1;
73 char *str = malloc(size);
74
75 strcpy(str, first);
76 va_start(args, first);
77
78 for(;;) {
79 char *next = va_arg(args, char*);
80
81 if(!next) {
82 break;
83 }
84
85 size += strlen(next);
86 str = realloc(str, size);
87 strcat(str, next);
88 }
89
90 va_end(args);
91 return str;
92 }
93
vstrfmt(const char * fmt,va_list args)94 char* vstrfmt(const char *fmt, va_list args) {
95 size_t written = 0;
96 size_t fmtlen = strlen(fmt);
97 size_t asize = 1;
98 char *out = NULL;
99
100 while(asize <= fmtlen)
101 asize *= 2;
102
103 for(;;) {
104 out = realloc(out, asize);
105
106 va_list nargs;
107 va_copy(nargs, args);
108 written = vsnprintf(out, asize, fmt, nargs);
109 va_end(nargs);
110
111 if(written < asize) {
112 break;
113 }
114
115 asize = written + 1;
116 }
117
118 if(asize > strlen(out) + 1) {
119 out = realloc(out, strlen(out) + 1);
120 }
121
122 return out;
123 }
124
strfmt(const char * fmt,...)125 char* strfmt(const char *fmt, ...) {
126 va_list args;
127 va_start(args, fmt);
128 char *str = vstrfmt(fmt, args);
129 va_end(args);
130 return str;
131 }
132
strftimealloc(const char * fmt,const struct tm * timeinfo)133 char* strftimealloc(const char *fmt, const struct tm *timeinfo) {
134 size_t sz_allocated = 64;
135
136 while(true) {
137 char str[sz_allocated];
138
139 if(strftime(str, sz_allocated, fmt, timeinfo)) {
140 return strdup(str);
141 }
142
143 sz_allocated *= 2;
144 };
145 }
146
strip_trailing_slashes(char * buf)147 void strip_trailing_slashes(char *buf) {
148 for(char *c = buf + strlen(buf) - 1; c >= buf && (*c == '/' || *c == '\\'); c--)
149 *c = 0;
150 }
151
strappend(char ** dst,char * src)152 char* strappend(char **dst, char *src) {
153 if(!*dst) {
154 return *dst = strdup(src);
155 }
156
157 *dst = realloc(*dst, strlen(*dst) + strlen(src) + 1);
158 strcat(*dst, src);
159 return *dst;
160 }
161
162
163 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
164 #define UCS4_ID "UCS-4BE"
165 #else
166 #define UCS4_ID "UCS-4LE"
167 #endif
168
ucs4chr(const uint32_t * ucs4,uint32_t chr)169 uint32_t* ucs4chr(const uint32_t *ucs4, uint32_t chr) {
170 for(; *ucs4 != chr; ++ucs4) {
171 if(!*ucs4) {
172 return NULL;
173 }
174 }
175
176 return (uint32_t*)ucs4;
177 }
178
ucs4len(const uint32_t * ucs4)179 size_t ucs4len(const uint32_t *ucs4) {
180 size_t len;
181 for(len = 0; *ucs4; ++len, ++ucs4);
182 return len;
183 }
184
utf8_to_ucs4(const char * utf8,size_t bufsize,uint32_t buf[bufsize])185 void utf8_to_ucs4(const char *utf8, size_t bufsize, uint32_t buf[bufsize]) {
186 uint32_t *bufptr = buf;
187 assert(bufsize > 0);
188
189 while(*utf8) {
190 *bufptr++ = utf8_getch(&utf8);
191 assert(bufptr < buf + bufsize);
192 }
193
194 *bufptr++ = 0;
195 }
196
utf8_to_ucs4_alloc(const char * utf8)197 uint32_t* utf8_to_ucs4_alloc(const char *utf8) {
198 uint32_t *ucs4 = (uint32_t*)(void*)SDL_iconv_string(UCS4_ID, "UTF-8", utf8, strlen(utf8) + 1);
199 assert(ucs4 != NULL);
200 return ucs4;
201 }
202
ucs4_to_utf8(const uint32_t * ucs4,size_t bufsize,char buf[bufsize])203 void ucs4_to_utf8(const uint32_t *ucs4, size_t bufsize, char buf[bufsize]) {
204 assert(bufsize > ucs4len(ucs4));
205 char *temp = ucs4_to_utf8_alloc(ucs4);
206 memcpy(buf, temp, bufsize);
207 free(temp);
208 }
209
ucs4_to_utf8_alloc(const uint32_t * ucs4)210 char* ucs4_to_utf8_alloc(const uint32_t *ucs4) {
211 char *utf8 = SDL_iconv_string("UTF-8", UCS4_ID, (const char*)ucs4, sizeof(uint32_t) * (ucs4len(ucs4) + 1));
212 assert(utf8 != NULL);
213 return utf8;
214 }
215
216 /*
217 * public domain strtok_r() by Charlie Gordon
218 *
219 * from comp.lang.c 9/14/2007
220 *
221 * http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684
222 *
223 * (Declaration that it's public domain):
224 * http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c
225 */
strtok_r(char * str,const char * delim,char ** nextp)226 char* strtok_r(char *str, const char *delim, char **nextp) {
227 char *ret;
228
229 if(str == NULL) {
230 str = *nextp;
231 }
232
233 str += strspn(str, delim);
234
235 if(*str == '\0') {
236 return NULL;
237 }
238
239 ret = str;
240 str += strcspn(str, delim);
241
242 if(*str) {
243 *str++ = '\0';
244 }
245
246 *nextp = str;
247 return ret;
248 }
249
filename_timestamp(char * buf,size_t buf_size,SystemTime systime)250 size_t filename_timestamp(char *buf, size_t buf_size, SystemTime systime) {
251 assert(buf_size >= FILENAME_TIMESTAMP_MIN_BUF_SIZE);
252
253 char buf_msecs[4];
254 char buf_datetime[FILENAME_TIMESTAMP_MIN_BUF_SIZE - sizeof(buf_msecs)];
255 attr_unused size_t written;
256
257 written = snprintf(buf_msecs, sizeof(buf_msecs), "%03u", (uint)(systime.tv_nsec / 1000000));
258 assert(written == sizeof(buf_msecs) - 1);
259 written = strftime(buf_datetime, sizeof(buf_datetime), "%Y%m%d_%H-%M-%S", localtime(&systime.tv_sec));
260 assert(written != 0);
261
262 return snprintf(buf, buf_size, "%s-%s", buf_datetime, buf_msecs);
263 }
264
utf8_getch(const char ** src)265 uint32_t utf8_getch(const char **src) {
266 // Ported from SDL_ttf and slightly modified
267
268 assert(*src != NULL);
269
270 const uint8_t *p = *(const uint8_t**)src;
271 size_t left = 0;
272 bool overlong = false;
273 uint32_t ch = UNICODE_UNKNOWN;
274
275 if(**src == 0) {
276 return UNICODE_UNKNOWN;
277 }
278
279 if(p[0] >= 0xFC) {
280 if((p[0] & 0xFE) == 0xFC) {
281 if(p[0] == 0xFC && (p[1] & 0xFC) == 0x80) {
282 overlong = true;
283 }
284 ch = p[0] & 0x01;
285 left = 5;
286 }
287 } else if(p[0] >= 0xF8) {
288 if((p[0] & 0xFC) == 0xF8) {
289 if(p[0] == 0xF8 && (p[1] & 0xF8) == 0x80) {
290 overlong = true;
291 }
292 ch = p[0] & 0x03;
293 left = 4;
294 }
295 } else if(p[0] >= 0xF0) {
296 if((p[0] & 0xF8) == 0xF0) {
297 if(p[0] == 0xF0 && (p[1] & 0xF0) == 0x80) {
298 overlong = true;
299 }
300 ch = p[0] & 0x07;
301 left = 3;
302 }
303 } else if(p[0] >= 0xE0) {
304 if((p[0] & 0xF0) == 0xE0) {
305 if(p[0] == 0xE0 && (p[1] & 0xE0) == 0x80) {
306 overlong = true;
307 }
308 ch = p[0] & 0x0F;
309 left = 2;
310 }
311 } else if(p[0] >= 0xC0) {
312 if((p[0] & 0xE0) == 0xC0) {
313 if((p[0] & 0xDE) == 0xC0) {
314 overlong = true;
315 }
316 ch = p[0] & 0x1F;
317 left = 1;
318 }
319 } else {
320 if((p[0] & 0x80) == 0x00) {
321 ch = p[0];
322 }
323 }
324
325 ++*src;
326
327 while(left > 0 && **src != 0) {
328 ++p;
329
330 if((p[0] & 0xC0) != 0x80) {
331 ch = UNICODE_UNKNOWN;
332 break;
333 }
334
335 ch <<= 6;
336 ch |= (p[0] & 0x3F);
337
338 ++*src;
339 --left;
340 }
341
342 if(
343 overlong ||
344 left > 0 ||
345 (ch >= 0xD800 && ch <= 0xDFFF) ||
346 (ch == 0xFFFE || ch == 0xFFFF) ||
347 ch > 0x10FFFF
348 ) {
349 ch = UNICODE_UNKNOWN;
350 }
351
352 return ch;
353 }
354
format_huge_num(uint digits,uint64_t num,size_t bufsize,char * buf)355 void format_huge_num(uint digits, uint64_t num, size_t bufsize, char *buf) {
356 if(digits == 0) {
357 digits = digitcnt(num);
358 }
359
360 num = umin(upow10(digits) - 1, num);
361
362 div_t separators = div(digits, 3);
363 attr_unused uint len = digits + (separators.quot + !!separators.rem);
364 assert(bufsize >= len);
365
366 char *p = buf;
367
368 // WARNING: i must be signed here
369 for(int i = 0; i < digits; ++i) {
370 if(i && !((i - separators.rem) % 3)) {
371 *p++ = ',';
372 }
373
374 uint64_t divisor = upow10(digits - i - 1);
375 *p++ = '0' + num / divisor;
376 num %= divisor;
377 }
378
379 *p = 0;
380 assert(p == buf + len - 1);
381 }
382
hexdigest(uint8_t * input,size_t input_size,char * output,size_t output_size)383 void hexdigest(uint8_t *input, size_t input_size, char *output, size_t output_size) {
384 assert(output_size > input_size * 2);
385
386 static char charmap[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
387 uint8_t *end = input + input_size;
388
389 while(input < end) {
390 uint8_t byte = *input++;
391 *output++ = charmap[byte >> 4];
392 *output++ = charmap[byte & 0xf];
393 }
394
395 *output = 0;
396 }
397
expand_escape_sequences(char * str)398 void expand_escape_sequences(char *str) {
399 bool in_escape = false;
400 char *p = str;
401
402 for(p = str; *p; ++p) {
403 if(in_escape) {
404 switch(*p) {
405 case 'n': p[-1] = '\n'; break;
406 case 't': p[-1] = '\t'; break;
407 case 'r': p[-1] = '\r'; break;
408 default: p[-1] = *p; break;
409 }
410
411 memmove(p, p + 1, strlen(p + 1) + 1);
412 --p;
413 in_escape = false;
414 } else if(*p == '\\') {
415 in_escape = true;
416 }
417 }
418
419 if(in_escape) {
420 p[-1] = 0;
421 }
422 }
423