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