1 #include <math.h>
2 #include <stdlib.h>
3 #include <stdarg.h>
4 #include <string.h>
5 #include <ctype.h>
6 #include <iconv.h>
7 #include <errno.h>
8 #include "asc.h"
9 #include "errors.h"
10 #include "md5.h"
11 #include "io/elfilewrapper.h"
12 #ifdef MAP_EDITOR
13 # include "map_editor/misc.h"
14 #else
15 # include "misc.h"
16 
17 #endif //MAP_EDITOR
18 // find the first occurance of needle in haystack, and return the distance to
19 // that string. If beggining is 1, it returns the offset to the beginning of
20 // the string otherwise it returns the offset to the end of the string. Needle
21 // must be null-terminated. hyastack need not be, but must be at least max_len
22 // bytes long
get_string_occurance(const char * needle,const char * haystack,const Uint32 max_len,const char beginning)23 Sint32 get_string_occurance (const char* needle, const char* haystack, const Uint32 max_len, const char beginning)
24 {
25 	const Uint32 n_len = strlen(needle);
26 	Uint32 istart, i;
27 	Uint32 search_len;
28 
29 	if (max_len < n_len) {
30 		return -1;
31 	}
32 
33 	for (istart = 0, search_len = max_len - n_len; istart <= search_len; istart++)
34 	{
35 		for (i = 0; i < n_len; i++)
36 		{
37 			if (tolower(haystack[istart+i]) != tolower(needle[i])) {
38 				break;
39 			}
40 		}
41 		if (i >= n_len)
42 		{
43 			// We found the string. return the beginning if asked
44 			if (beginning) {
45 				return istart;
46 			}
47 			// return the end of the string occurence, but skip
48 			// space and equal signs
49 			while ((istart+i < max_len) && (haystack[istart+i] == ' ' || haystack[istart+i] == '=')) {
50 				i++;
51 			}
52 			return istart+i;
53 		}
54 	}
55 	return -1;
56 }
57 
58 #ifndef FASTER_MAP_LOAD
59 // This function returns an integer, after the needle in the haystack
60 // string. If the string is not found, after max_len, the function returns -1.
61 // The function is NOT case sensitive
get_integer_after_string(const char * needle,const char * haystack,Uint32 max_len)62 Sint32 get_integer_after_string (const char *needle, const char *haystack, Uint32 max_len)
63 {
64 	Sint32 n_end = get_string_occurance (needle, haystack, max_len, 0);
65 	Uint32 istart;
66 
67 	if (n_end < 0)
68 	{
69 		// needle not found
70 		return -1;
71 	}
72 
73 	istart = n_end;
74 	while (istart < max_len)
75 	{
76 		if (haystack[istart] == '\n')
77 			// no integer on this line
78 			return -1;
79 		if (isdigit (haystack[istart]) || haystack[istart] == '+' || haystack[istart] == '-'){
80 			// we've probably found a number
81 			//return atoi (&haystack[istart]);
82 			char temp[1<<sizeof(int)];	//Wasteful, but it will reserve enough space for MAX_INT as a string. If we change to atol or similar, use sizeof(long) instead
83 			int len = min2i(max_len-istart, (1<<sizeof(int))-1);
84 			memcpy(temp, &haystack[istart], len-1);
85 			temp[len] = '\0';
86 			return atoi (temp);
87 		}
88 		istart++;
89 	}
90 
91 	// no integer after needle
92 	return -1;
93 }
94 
95 // This function returns a float, after the source string in the destination
96 // string. If the string is not found, after max_len, the function returns
97 // -1.0f. The function is NOT case sensitive
get_float_after_string(const char * needle,const char * haystack,Uint32 max_len)98 float get_float_after_string (const char *needle, const char *haystack, Uint32 max_len)
99 {
100 	Sint32 n_end = get_string_occurance (needle, haystack, max_len, 0);
101 	Uint32 istart;
102 
103 	if (n_end < 0)
104 	{
105 		// needle not found
106 		return -1.0f;
107 	}
108 
109 	istart = n_end;
110 	while (istart < max_len)
111 	{
112 		if (haystack[istart] == '\n')
113 			// no number on this line
114 			return -1.0f;
115 		if (isdigit (haystack[istart]) || haystack[istart] == '+' || haystack[istart] == '-' || haystack[istart] == '.'){
116 			// we've probably found a number
117 			//return atof (&haystack[istart]);
118 			//char temp[max_len-istart+1];	//Wasteful, if the float doesn't go to the end of the line, but it will reserve enough space
119 			char temp[200];	//It'd be better not to use an arbitrary constant, but we can't use run-time size on compilers like MSVC
120 			memcpy(temp, &haystack[istart], min2i(max_len-istart, sizeof(temp)));
121 			temp[min2i(max_len-istart, sizeof(temp))] = '\0';
122 			return atof (temp);
123 		}
124 		istart++;
125 	}
126 
127 	// no number after needle
128 	return -1.0f;
129 }
130 #endif // FASTER_MAP_LOAD
131 
132 // File utilities
clean_file_name(char * dest,const char * src,Uint32 max_len)133 Uint32 clean_file_name (char *dest, const char *src, Uint32 max_len)
134 {
135 	char *dptr, *dend = dest + (max_len-1);
136 	const char *sptr;
137 
138 	for (dptr = dest, sptr = src; dptr < dend && *sptr; dptr++, sptr++)
139 		*dptr = *sptr == '\\' ? '/' : tolower(*sptr);
140 	// always place a null at the end
141 	*dptr = '\0';
142 
143 	return dptr-dest;
144 }
145 
safe_strncpy(char * dest,const char * source,const size_t len)146 char* safe_strncpy(char *dest, const char * source, const size_t len)
147 {
148 	if (len > 0)
149 	{
150 		strncpy(dest, source, len - 1);
151 		dest[len - 1] = '\0';
152 	}
153 	return dest;
154 }
155 
safe_strncpy2(char * dest,const char * source,const size_t dest_len,const size_t src_len)156 char* safe_strncpy2(char *dest, const char * source, const size_t dest_len, const size_t src_len)
157 {
158 	if (dest_len > 0)
159 	{
160 		if (src_len >= dest_len)
161 		{
162 			strncpy(dest, source, dest_len - 1);
163 			dest[dest_len - 1] = '\0';
164 		}
165 		else
166 		{
167 			strncpy(dest, source, src_len);
168 			dest[src_len] = '\0';
169 		}
170 	}
171 	return dest;
172 }
173 
safe_strcat(char * dest,const char * src,size_t len)174 char* safe_strcat (char* dest, const char* src, size_t len)
175 {
176 	size_t start_pos = strlen (dest);
177 	if (start_pos < len)
178 		safe_strncpy (dest+start_pos, src, len-start_pos);
179 	return dest;
180 }
181 
safe_snprintf(char * dest,const size_t len,const char * format,...)182 int safe_snprintf(char *dest, const size_t len, const char* format, ...)
183 {
184 	int ret;
185 	if (len > 0)
186 	{
187 		va_list ap;
188 		va_start(ap, format);
189 #ifdef __MINGW32__
190 		ret = vsnprintf(dest, len, format, ap);
191 #else
192  #if defined(WINDOWS) && (defined(__MINGW32__) || defined(_MSC_VER))
193 		ret = _vsnprintf(dest, len, format, ap);
194  #else
195 		ret = vsnprintf(dest, len, format, ap);
196  #endif
197 #endif
198 		va_end(ap);
199 		dest[len - 1] = '\0';
200 		if ((ret < 0) || (ret >= len))
201 			return len;
202 		return ret;
203 	}
204 	return 0;
205 }
206 
my_UTF8Toisolat1(char ** dest,size_t * lu,char ** src,size_t * l)207 static int my_UTF8Toisolat1(char **dest, size_t * lu, char **src, size_t * l)
208 {
209 	iconv_t t=iconv_open("ISO_8859-1","UTF-8");
210 
211 	iconv(t, src, l, dest, lu);
212 
213 	iconv_close(t);
214 	return 1;
215 }
216 
my_xmlStrncopy(char ** out,const char * in,int len)217 int my_xmlStrncopy(char ** out, const char * in, int len)
218 {
219 	if(in) {
220 		size_t lin=0;
221 		size_t lout=0;
222 		int l1=0;
223 		int l2=0;
224 		int retval=1;
225 		char *inbuf;
226 		char *inbuf2;
227 		char *outbuf;
228 		char *outbuf2;
229 
230 		lin=strlen(in);
231 		l2=xmlUTF8Strlen((xmlChar*)in);
232 
233 		if(l2<0) lout=l1;
234 		else if (len>0 && len<l2) lout=len;
235 		else lout=l2;
236 
237 		inbuf=inbuf2=(char *)malloc((lin+1)*sizeof(char));
238 		outbuf=outbuf2=(char *)malloc((lout+1)*sizeof(char));
239 
240 		memcpy(inbuf,in,lin);
241 
242 		l1=lin;
243 		l2=lout;
244 
245 		if(my_UTF8Toisolat1(&outbuf2,&lout,&inbuf2,&lin)<0) {
246 			retval=-1;
247 		}
248 
249 		free(inbuf);
250 
251 		outbuf[l2]=0;
252 
253 		if(*out) {
254 			memcpy(*out,outbuf,l2+1);
255 			free(outbuf);
256 		} else {
257 			*out=outbuf;
258 		}
259 
260 		return retval<0?-1:l2;
261 	} else return -1;
262 }
263 
264 #ifndef MAP_EDITOR
265 
safe_strcasestr(const char * haystack,size_t haystack_len,const char * needle,size_t needle_len)266 char* safe_strcasestr (const char* haystack, size_t haystack_len, const char* needle, size_t needle_len)
267 {
268 	if (haystack_len >= needle_len)
269 	{
270 		const char* res;
271 		size_t istart;
272 		size_t imax = haystack_len - needle_len;
273 
274 		for (istart = 0, res = haystack; istart <= imax && *res; istart++, res++)
275 		{
276 			if (strncasecmp (res, needle, needle_len) == 0)
277 				return (char*) res;
278 		}
279 	}
280 
281 	return NULL;
282 }
283 
284 // is this string more then one character and all alpha in it are CAPS?
my_isupper(const char * src,int len)285 Sint32 my_isupper(const char *src, int len)
286 {
287 	int alpha=0;
288 	if (len < 0)	len=strlen(src);
289 	if(!src || !src[0] || !src[1] || !src[2] || len == 0) return 0;
290 	while(*src && len > 0)
291 		{
292             if(isalpha((unsigned char)*src)) alpha++;
293             if((isdigit((unsigned char)*src)&&alpha<len/2) || *src != toupper(*src)) return 0;    //at least one lower
294             src++;
295 			len--;
296 		}
297 	return 1;	// is all upper or all num
298 }
299 
my_tolower(char * src)300 char *my_tolower (char *src)
301 {
302 	char *dest = src;
303 
304 	if (dest == NULL || dest[0] == '\0')
305 		return dest;
306 	while (*src)
307 	{
308 		*src = tolower (*src);
309 		src++;
310 	}
311 
312 	return dest;
313 }
314 
315 /*XML*/
316 
xmlGetFloat(const xmlNode * n,const char * c,float def_val)317 float xmlGetFloat(const xmlNode *n, const char* c, float def_val)
318 {
319 	char* t = (char*)xmlGetProp(n, (const xmlChar*)c);
320 	float f = t ? atof(t) : def_val;
321 	xmlFree(t);
322 	return f;
323 }
324 
xmlGetInt(const xmlNode * n,const char * c)325 int xmlGetInt(const xmlNode *n, const char* c)
326 {
327 	char *t = (char*)xmlGetProp(n, (const xmlChar*)c);
328 	int i = t ? atoi(t) : 0;
329 	xmlFree(t);
330 	return i;
331 }
332 
333 /* return true if digest calculated */
get_file_digest(const char * filename,Uint8 digest[16])334 int get_file_digest(const char * filename, Uint8 digest[16])
335 {
336 	MD5 md5;
337 	el_file_ptr file = NULL;
338 
339 	file = el_open(filename);
340 
341 	memset (digest, 0, 16);
342 
343 	if (file == NULL)
344 	{
345 		LOG_ERROR("MD5Digest: Unable to open %s (%d)", filename, errno);
346 		return 0;
347 	}
348 
349 	if (el_get_pointer(file) == NULL)
350 	{
351 		el_close(file);
352 		return 0;
353 	}
354 
355 	MD5Open(&md5);
356 	MD5Digest(&md5, el_get_pointer(file), el_get_size(file));
357 	MD5Close(&md5, digest);
358 
359 	el_close(file);
360 
361 	return 1;
362 }
363 
find_description_index(const dict_elem dict[],const char * elem,const char * desc)364 int find_description_index (const dict_elem dict[], const char *elem, const char *desc) {
365 	int idx = 0;
366 	const char *key;
367 
368 	while ((key = dict[idx].desc) != NULL) {
369 		if (strcasecmp (key, elem) == 0)
370 			return dict[idx].index;
371 		idx++;
372 	}
373 
374 	LOG_ERROR("Unknown %s \"%s\"\n", desc, elem);
375 	return -1;
376 }
377 
get_string_value(char * buf,size_t maxlen,const xmlNode * node)378 void get_string_value(char *buf, size_t maxlen, const xmlNode *node)
379 {
380 	if (!node)
381 	{
382 		LOG_ERROR("Node is null!");
383 		buf[0] = '\0';
384 		return;
385 	}
386 
387 	if (!node->children)
388 		buf[0] = '\0';
389 	else
390 		safe_strncpy(buf, (const char*)node->children->content, maxlen);
391 }
392 
get_item_string_value(char * buf,size_t maxlen,const xmlNode * item,const unsigned char * name)393 void get_item_string_value(char *buf, size_t maxlen, const xmlNode *item,
394 	const unsigned char *name)
395 {
396 	const xmlNode *node;
397 
398 	if (!item)
399 	{
400 		LOG_ERROR("Item is null!");
401 		buf[0] = '\0';
402 		return;
403 	}
404 
405 	// look for this entry in the children
406 	for (node = item->children; node; node = node->next)
407 	{
408 		if (node->type == XML_ELEMENT_NODE
409 			&& xmlStrcasecmp(node->name, name) == 0)
410 		{
411 			get_string_value(buf, maxlen, node);
412 			return;
413 		}
414 	}
415 }
416 
get_bool_value(const xmlNode * node)417 int get_bool_value(const xmlNode *node)
418 {
419 	const xmlChar *tval;
420 
421 	if (!node)
422 	{
423 		LOG_ERROR("Node is null!");
424 		return 0;
425 	}
426 
427 	if (!node->children)
428 		return 0;
429 
430 	tval = node->children->content;
431 	return (xmlStrcasecmp(tval, (xmlChar*)"yes") == 0) ||
432 		(xmlStrcasecmp(tval, (xmlChar*)"true") == 0) ||
433 		(xmlStrcasecmp(tval, (xmlChar*)"1") == 0);
434 }
435 
get_int_value(const xmlNode * node)436 int get_int_value(const xmlNode *node)
437 {
438 	if (!node)
439 	{
440 		LOG_ERROR("Node is null!");
441 		return 0;
442 	}
443 
444 	if (!node->children)
445 		return 0;
446 
447 	return atoi((const char*)node->children->content);
448 }
449 
get_float_value(const xmlNode * node)450 double get_float_value(const xmlNode *node)
451 {
452 	if (!node)
453 	{
454 		LOG_ERROR("Node is null!");
455 		return 0.0;
456 	}
457 
458 	if (!node->children)
459 		return 0.0;
460 
461 	return atof((const char*)node->children->content);
462 }
463 
get_int_property(const xmlNode * node,const char * prop)464 int get_int_property(const xmlNode *node, const char *prop)
465 {
466 	const xmlAttr *attr;
467 
468 	if (!node)
469 	{
470 		LOG_ERROR("Node is null!");
471 		return 0;
472 	}
473 
474 	for (attr = node->properties; attr; attr = attr->next)
475 	{
476 		if (attr->type == XML_ATTRIBUTE_NODE &&
477 			xmlStrcasecmp(attr->name, (const xmlChar*)prop) == 0)
478 		{
479 			return atoi((const char*)attr->children->content);
480 		}
481 	}
482 
483 	return -1;
484 }
485 
get_property(const xmlNode * node,const char * prop,const char * desc,const dict_elem dict[])486 int get_property(const xmlNode *node, const char *prop, const char *desc,
487 	const dict_elem dict[])
488 {
489 	const xmlAttr *attr;
490 
491 	if (!node)
492 	{
493 		LOG_ERROR("Node is null!");
494 		return 0;
495 	}
496 
497 	for (attr = node->properties; attr; attr = attr->next)
498 	{
499 		if (attr->type == XML_ATTRIBUTE_NODE &&
500 			xmlStrcasecmp (attr->name, (const xmlChar*)prop) == 0)
501 		{
502 			return find_description_index(dict,
503 				(const char*)attr->children->content, desc);
504 		}
505 	}
506 
507 	LOG_ERROR("Unable to find property %s in node %s\n", prop, node->name);
508 	return -1;
509 }
510 
get_string_property(const xmlNode * node,const char * prop)511 const char *get_string_property(const xmlNode *node, const char *prop)
512 {
513 	const xmlAttr *attr;
514 
515 	if (node == NULL)
516 	{
517 		LOG_ERROR("Node is null!");
518 		return "";
519 	}
520 
521 	for (attr = node->properties; attr; attr = attr->next)
522 	{
523 		if (attr->type == XML_ATTRIBUTE_NODE &&
524 			xmlStrcasecmp (attr->name, (xmlChar *)prop) == 0)
525 		{
526 			return (const char*)attr->children->content;
527 		}
528 	}
529 
530 #ifdef	DEBUG_XML
531 	// don't normally report this, or optional properties will report errors
532 	LOG_ERROR("Unable to find property %s in node %s\n", prop, node->name);
533 #endif	//DEBUG_XML
534 	return "";
535 }
536 
append_char(char ** s,char c,int * len,int * max_len)537 void append_char(char** s, char c, int* len, int* max_len)
538 {
539 	if (*len >= *max_len)
540 	{
541 		*s = (char*) realloc(*s, *max_len + APPEND_CHAR_BLOCK);
542 		*max_len += APPEND_CHAR_BLOCK;
543 	}
544 	(*s)[(*len)++] = c;
545 }
546 
toUTF8(const char * str,int len)547 xmlChar* toUTF8 (const char* str, int len)
548 {
549 	int out_size = 2*len;
550 	int out_len;
551 	xmlChar* out = calloc (out_size, sizeof (xmlChar));
552 
553 	while (1)
554 	{
555 		int in_len = len;
556 		out_len = out_size;
557 
558 		if (isolat1ToUTF8 (out, &out_len, BAD_CAST str, &in_len) < 0)
559 		{
560 			// Conversion error
561 			free (out);
562 			return NULL;
563 		}
564 		if (in_len >= len)
565 			break;
566 
567 		out_size *= 2;
568 		out = realloc (out, out_size * sizeof (xmlChar));
569 	}
570 
571 	if (out_len >= out_size)
572 		// drats, no space to store a terminator
573 		out = realloc (out, (out_size + 1) * sizeof (xmlChar));
574 	out[out_len] = '\0';
575 
576 	return out;
577 }
578 
fromUTF8(const xmlChar * str,int len)579 char* fromUTF8 (const xmlChar* str, int len)
580 {
581 	int out_size = len+1;
582 	int out_len = out_size;
583 	int in_len = len;
584 	char* out = calloc (out_size, 1);
585 
586 	if (UTF8Toisolat1 (BAD_CAST out, &out_len, str, &in_len) < 0)
587 	{
588 		// Conversion error
589 		free (out);
590 		return NULL;
591 	}
592 
593 	out[out_len] = '\0';
594 
595 	return out;
596 }
597 
598 
599 /* whether you pass in a NULL pointer or your own allocated memory for
600  * out_str, you need to free the memory yourself */
substitute_char_with_string(const char * str,char ** out_str,char to_sub,const char * with_sub)601 char *substitute_char_with_string(const char *str, char **out_str, char to_sub, const char* with_sub)
602 {
603 	int amp_count = 0;
604 	const char *start_ptr;
605 	char *end_ptr;
606 	int out_len = 0;
607 	size_t alloc_len = 0;
608 
609 	for (start_ptr = str; (start_ptr = strchr(start_ptr, to_sub)) != NULL; start_ptr++)
610 		amp_count++;
611 
612 	alloc_len = strlen(str) + amp_count*(strlen(with_sub)-1) + 1;
613 	*out_str = (char *)realloc(*out_str, alloc_len);
614 	**out_str = '\0';
615 
616 	for (start_ptr = str; (end_ptr = strchr(start_ptr, to_sub)) != NULL; )
617 	{
618 		while (start_ptr < end_ptr)
619 			(*out_str)[out_len++] = *start_ptr++;
620 		(*out_str)[out_len] = '\0';
621 
622 		safe_strcat(*out_str, with_sub, alloc_len);
623 		out_len = strlen(*out_str);
624 		start_ptr++;
625 	}
626 	safe_strcat(*out_str, start_ptr, alloc_len);
627 	return *out_str;
628 }
629 
630 
631 /* Remove whte space from the end of the supplied string.
632  * The string must be writable.
633  * Return the pointer to the string.
634  */
rtrim_string(char * the_string)635 char * rtrim_string(char *the_string)
636 {
637 	if (the_string != NULL)
638 	{
639 		size_t i = strlen(the_string);
640 		while ((i > 0) && isspace(the_string[i-1]))
641 			--i;
642 		the_string[i] = '\0';
643 
644 	}
645 	return the_string;
646 }
647 
648 #endif // !MAP_EDITOR
649