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