strlib.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999
  1. // Copyright (c) Athena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #include "../common/cbasetypes.h"
  4. #include "../common/malloc.h"
  5. #include "../common/showmsg.h"
  6. #include "strlib.h"
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #define J_MAX_MALLOC_SIZE 65535
  11. // escapes a string in-place (' -> \' , \ -> \\ , % -> _)
  12. char* jstrescape (char* pt)
  13. {
  14. //copy from here
  15. char *ptr;
  16. int i = 0, j = 0;
  17. //copy string to temporary
  18. CREATE(ptr, char, J_MAX_MALLOC_SIZE);
  19. strcpy(ptr,pt);
  20. while (ptr[i] != '\0') {
  21. switch (ptr[i]) {
  22. case '\'':
  23. pt[j++] = '\\';
  24. pt[j++] = ptr[i++];
  25. break;
  26. case '\\':
  27. pt[j++] = '\\';
  28. pt[j++] = ptr[i++];
  29. break;
  30. case '%':
  31. pt[j++] = '_'; i++;
  32. break;
  33. default:
  34. pt[j++] = ptr[i++];
  35. }
  36. }
  37. pt[j++] = '\0';
  38. aFree(ptr);
  39. return pt;
  40. }
  41. // escapes a string into a provided buffer
  42. char* jstrescapecpy (char* pt, const char* spt)
  43. {
  44. //copy from here
  45. //WARNING: Target string pt should be able to hold strlen(spt)*2, as each time
  46. //a escape character is found, the target's final length increases! [Skotlex]
  47. int i =0, j=0;
  48. if (!spt) { //Return an empty string [Skotlex]
  49. pt[0] = '\0';
  50. return &pt[0];
  51. }
  52. while (spt[i] != '\0') {
  53. switch (spt[i]) {
  54. case '\'':
  55. pt[j++] = '\\';
  56. pt[j++] = spt[i++];
  57. break;
  58. case '\\':
  59. pt[j++] = '\\';
  60. pt[j++] = spt[i++];
  61. break;
  62. case '%':
  63. pt[j++] = '_'; i++;
  64. break;
  65. default:
  66. pt[j++] = spt[i++];
  67. }
  68. }
  69. pt[j++] = '\0';
  70. return &pt[0];
  71. }
  72. // escapes exactly 'size' bytes of a string into a provided buffer
  73. int jmemescapecpy (char* pt, const char* spt, int size)
  74. {
  75. //copy from here
  76. int i =0, j=0;
  77. while (i < size) {
  78. switch (spt[i]) {
  79. case '\'':
  80. pt[j++] = '\\';
  81. pt[j++] = spt[i++];
  82. break;
  83. case '\\':
  84. pt[j++] = '\\';
  85. pt[j++] = spt[i++];
  86. break;
  87. case '%':
  88. pt[j++] = '_'; i++;
  89. break;
  90. default:
  91. pt[j++] = spt[i++];
  92. }
  93. }
  94. // copy size is 0 ~ (j-1)
  95. return j;
  96. }
  97. // Function to suppress control characters in a string.
  98. int remove_control_chars(char* str)
  99. {
  100. int i;
  101. int change = 0;
  102. for(i = 0; str[i]; i++) {
  103. if (ISCNTRL(str[i])) {
  104. str[i] = '_';
  105. change = 1;
  106. }
  107. }
  108. return change;
  109. }
  110. // Removes characters identified by ISSPACE from the start and end of the string
  111. // NOTE: make sure the string is not const!!
  112. char* trim(char* str)
  113. {
  114. size_t start;
  115. size_t end;
  116. if( str == NULL )
  117. return str;
  118. // get start position
  119. for( start = 0; str[start] && ISSPACE(str[start]); ++start )
  120. ;
  121. // get end position
  122. for( end = strlen(str); start < end && str[end-1] && ISSPACE(str[end-1]); --end )
  123. ;
  124. // trim
  125. if( start == end )
  126. *str = '\0';// empty string
  127. else
  128. {// move string with nul terminator
  129. str[end] = '\0';
  130. memmove(str,str+start,end-start+1);
  131. }
  132. return str;
  133. }
  134. // Converts one or more consecutive occurences of the delimiters into a single space
  135. // and removes such occurences from the beginning and end of string
  136. // NOTE: make sure the string is not const!!
  137. char* normalize_name(char* str,const char* delims)
  138. {
  139. char* in = str;
  140. char* out = str;
  141. int put_space = 0;
  142. if( str == NULL || delims == NULL )
  143. return str;
  144. // trim start of string
  145. while( *in && strchr(delims,*in) )
  146. ++in;
  147. while( *in )
  148. {
  149. if( put_space )
  150. {// replace trim characters with a single space
  151. *out = ' ';
  152. ++out;
  153. }
  154. // copy non trim characters
  155. while( *in && !strchr(delims,*in) )
  156. {
  157. *out = *in;
  158. ++out;
  159. ++in;
  160. }
  161. // skip trim characters
  162. while( *in && strchr(delims,*in) )
  163. ++in;
  164. put_space = 1;
  165. }
  166. *out = '\0';
  167. return str;
  168. }
  169. //stristr: Case insensitive version of strstr, code taken from
  170. //http://www.daniweb.com/code/snippet313.html, Dave Sinkula
  171. //
  172. const char* stristr(const char* haystack, const char* needle)
  173. {
  174. if ( !*needle )
  175. {
  176. return haystack;
  177. }
  178. for ( ; *haystack; ++haystack )
  179. {
  180. if ( TOUPPER(*haystack) == TOUPPER(*needle) )
  181. {
  182. // matched starting char -- loop through remaining chars
  183. const char *h, *n;
  184. for ( h = haystack, n = needle; *h && *n; ++h, ++n )
  185. {
  186. if ( TOUPPER(*h) != TOUPPER(*n) )
  187. {
  188. break;
  189. }
  190. }
  191. if ( !*n ) // matched all of 'needle' to null termination
  192. {
  193. return haystack; // return the start of the match
  194. }
  195. }
  196. }
  197. return 0;
  198. }
  199. #ifdef __WIN32
  200. char* _strtok_r(char *s1, const char *s2, char **lasts)
  201. {
  202. char *ret;
  203. if (s1 == NULL)
  204. s1 = *lasts;
  205. while(*s1 && strchr(s2, *s1))
  206. ++s1;
  207. if(*s1 == '\0')
  208. return NULL;
  209. ret = s1;
  210. while(*s1 && !strchr(s2, *s1))
  211. ++s1;
  212. if(*s1)
  213. *s1++ = '\0';
  214. *lasts = s1;
  215. return ret;
  216. }
  217. #endif
  218. #if !(defined(WIN32) && defined(_MSC_VER) && _MSC_VER >= 1400) && !defined(CYGWIN)
  219. /* Find the length of STRING, but scan at most MAXLEN characters.
  220. If no '\0' terminator is found in that many characters, return MAXLEN. */
  221. size_t strnlen (const char* string, size_t maxlen)
  222. {
  223. const char* end = memchr (string, '\0', maxlen);
  224. return end ? (size_t) (end - string) : maxlen;
  225. }
  226. #endif
  227. //----------------------------------------------------
  228. // E-mail check: return 0 (not correct) or 1 (valid).
  229. //----------------------------------------------------
  230. int e_mail_check(char* email)
  231. {
  232. char ch;
  233. char* last_arobas;
  234. size_t len = strlen(email);
  235. // athena limits
  236. if (len < 3 || len > 39)
  237. return 0;
  238. // part of RFC limits (official reference of e-mail description)
  239. if (strchr(email, '@') == NULL || email[len-1] == '@')
  240. return 0;
  241. if (email[len-1] == '.')
  242. return 0;
  243. last_arobas = strrchr(email, '@');
  244. if (strstr(last_arobas, "@.") != NULL || strstr(last_arobas, "..") != NULL)
  245. return 0;
  246. for(ch = 1; ch < 32; ch++)
  247. if (strchr(last_arobas, ch) != NULL)
  248. return 0;
  249. if (strchr(last_arobas, ' ') != NULL || strchr(last_arobas, ';') != NULL)
  250. return 0;
  251. // all correct
  252. return 1;
  253. }
  254. //--------------------------------------------------
  255. // Return numerical value of a switch configuration
  256. // on/off, english, français, deutsch, español
  257. //--------------------------------------------------
  258. int config_switch(const char* str)
  259. {
  260. if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0)
  261. return 1;
  262. if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0)
  263. return 0;
  264. return (int)strtol(str, NULL, 0);
  265. }
  266. /// always nul-terminates the string
  267. char* safestrncpy(char* dst, const char* src, size_t n)
  268. {
  269. char* ret;
  270. ret = strncpy(dst, src, n);
  271. if( ret != NULL )
  272. ret[n - 1] = '\0';
  273. return ret;
  274. }
  275. /// doesn't crash on null pointer
  276. size_t safestrnlen(const char* string, size_t maxlen)
  277. {
  278. return ( string != NULL ) ? strnlen(string, maxlen) : 0;
  279. }
  280. /// Works like snprintf, but always nul-terminates the buffer.
  281. /// Returns the size of the string (without nul-terminator)
  282. /// or -1 if the buffer is too small.
  283. ///
  284. /// @param buf Target buffer
  285. /// @param sz Size of the buffer (including nul-terminator)
  286. /// @param fmt Format string
  287. /// @param ... Format arguments
  288. /// @return The size of the string or -1 if the buffer is too small
  289. int safesnprintf(char* buf, size_t sz, const char* fmt, ...)
  290. {
  291. va_list ap;
  292. int ret;
  293. va_start(ap,fmt);
  294. ret = vsnprintf(buf, sz, fmt, ap);
  295. va_end(ap);
  296. if( ret < 0 || (size_t)ret >= sz )
  297. {// overflow
  298. buf[sz-1] = '\0';// always nul-terminate
  299. return -1;
  300. }
  301. return ret;
  302. }
  303. /// Returns the line of the target position in the string.
  304. /// Lines start at 1.
  305. int strline(const char* str, size_t pos)
  306. {
  307. const char* target;
  308. int line;
  309. if( str == NULL || pos == 0 )
  310. return 1;
  311. target = str+pos;
  312. for( line = 1; ; ++line )
  313. {
  314. str = strchr(str, '\n');
  315. if( str == NULL || target <= str )
  316. break;// found target line
  317. ++str;// skip newline
  318. }
  319. return line;
  320. }
  321. /////////////////////////////////////////////////////////////////////
  322. /// Parses a delim-separated string.
  323. /// Starts parsing at startoff and fills the pos array with position pairs.
  324. /// out_pos[0] and out_pos[1] are the start and end of line.
  325. /// Other position pairs are the start and end of fields.
  326. /// Returns the number of fields found or -1 if an error occurs.
  327. ///
  328. /// out_pos can be NULL.
  329. /// If a line terminator is found, the end position is placed there.
  330. /// out_pos[2] and out_pos[3] for the first field, out_pos[4] and out_pos[5]
  331. /// for the seconds field and so on.
  332. /// Unfilled positions are set to -1.
  333. ///
  334. /// @param str String to parse
  335. /// @param len Length of the string
  336. /// @param startoff Where to start parsing
  337. /// @param delim Field delimiter
  338. /// @param out_pos Array of resulting positions
  339. /// @param npos Size of the pos array
  340. /// @param opt Options that determine the parsing behaviour
  341. /// @return Number of fields found in the string or -1 if an error occured
  342. int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, int npos, enum e_svopt opt)
  343. {
  344. int i;
  345. int count;
  346. enum {
  347. START_OF_FIELD,
  348. PARSING_FIELD,
  349. PARSING_C_ESCAPE,
  350. END_OF_FIELD,
  351. TERMINATE,
  352. END
  353. } state;
  354. // check pos/npos
  355. if( out_pos == NULL ) npos = 0;
  356. for( i = 0; i < npos; ++i )
  357. out_pos[i] = -1;
  358. // check opt
  359. if( delim == '\n' && (opt&(SV_TERMINATE_CRLF|SV_TERMINATE_LF)) )
  360. {
  361. ShowError("sv_parse: delimiter '\\n' is not compatible with options SV_TERMINATE_LF or SV_TERMINATE_CRLF.\n");
  362. return -1;// error
  363. }
  364. if( delim == '\r' && (opt&(SV_TERMINATE_CRLF|SV_TERMINATE_CR)) )
  365. {
  366. ShowError("sv_parse: delimiter '\\r' is not compatible with options SV_TERMINATE_CR or SV_TERMINATE_CRLF.\n");
  367. return -1;// error
  368. }
  369. // check str
  370. if( str == NULL )
  371. return 0;// nothing to parse
  372. #define IS_END() ( i >= len )
  373. #define IS_DELIM() ( str[i] == delim )
  374. #define IS_TERMINATOR() ( \
  375. ((opt&SV_TERMINATE_LF) && str[i] == '\n') || \
  376. ((opt&SV_TERMINATE_CR) && str[i] == '\r') || \
  377. ((opt&SV_TERMINATE_CRLF) && i+1 < len && str[i] == '\r' && str[i+1] == '\n') )
  378. #define IS_C_ESCAPE() ( (opt&SV_ESCAPE_C) && str[i] == '\\' )
  379. #define SET_FIELD_START() if( npos > count*2+2 ) out_pos[count*2+2] = i
  380. #define SET_FIELD_END() if( npos > count*2+3 ) out_pos[count*2+3] = i; ++count
  381. i = startoff;
  382. count = 0;
  383. state = START_OF_FIELD;
  384. if( npos > 0 ) out_pos[0] = startoff;// start
  385. while( state != END )
  386. {
  387. if( npos > 1 ) out_pos[1] = i;// end
  388. switch( state )
  389. {
  390. case START_OF_FIELD:// record start of field and start parsing it
  391. SET_FIELD_START();
  392. state = PARSING_FIELD;
  393. break;
  394. case PARSING_FIELD:// skip field character
  395. if( IS_END() || IS_DELIM() || IS_TERMINATOR() )
  396. state = END_OF_FIELD;
  397. else if( IS_C_ESCAPE() )
  398. state = PARSING_C_ESCAPE;
  399. else
  400. ++i;// normal character
  401. break;
  402. case PARSING_C_ESCAPE:// skip escape sequence (validates it too)
  403. {
  404. ++i;// '\\'
  405. if( IS_END() )
  406. {
  407. ShowError("sv_parse: empty escape sequence\n");
  408. return -1;
  409. }
  410. if( str[i] == 'x' )
  411. {// hex escape
  412. ++i;// 'x'
  413. if( IS_END() || !ISXDIGIT(str[i]) )
  414. {
  415. ShowError("sv_parse: \\x with no following hex digits\n");
  416. return -1;
  417. }
  418. do{
  419. ++i;// hex digit
  420. }while( !IS_END() && ISXDIGIT(str[i]));
  421. }
  422. else if( str[i] == '0' || str[i] == '1' || str[i] == '2' )
  423. {// octal escape
  424. ++i;// octal digit
  425. if( !IS_END() && str[i] >= '0' && str[i] <= '7' )
  426. ++i;// octal digit
  427. if( !IS_END() && str[i] >= '0' && str[i] <= '7' )
  428. ++i;// octal digit
  429. }
  430. else if( strchr(SV_ESCAPE_C_SUPPORTED, str[i]) )
  431. {// supported escape character
  432. ++i;
  433. }
  434. else
  435. {
  436. ShowError("sv_parse: unknown escape sequence \\%c\n", str[i]);
  437. return -1;
  438. }
  439. state = PARSING_FIELD;
  440. break;
  441. }
  442. case END_OF_FIELD:// record end of field and continue
  443. SET_FIELD_END();
  444. if( IS_END() )
  445. state = END;
  446. else if( IS_DELIM() )
  447. {
  448. ++i;// delim
  449. state = START_OF_FIELD;
  450. }
  451. else if( IS_TERMINATOR() )
  452. state = TERMINATE;
  453. else
  454. state = START_OF_FIELD;
  455. break;
  456. case TERMINATE:
  457. #if 0
  458. // skip line terminator
  459. if( (opt&SV_TERMINATE_CRLF) && i+1 < len && str[i] == '\r' && str[i+1] == '\n' )
  460. i += 2;// CRLF
  461. else
  462. ++i;// CR or LF
  463. #endif
  464. state = END;
  465. break;
  466. }
  467. }
  468. #undef IS_END
  469. #undef IS_DELIM
  470. #undef IS_TERMINATOR
  471. #undef IS_C_ESCAPE
  472. #undef SET_FIELD_START
  473. #undef SET_FIELD_END
  474. return count;
  475. }
  476. /// Splits a delim-separated string.
  477. /// WARNING: this function modifies the input string
  478. /// Starts splitting at startoff and fills the out_fields array.
  479. /// out_fields[0] is the start of the next line.
  480. /// Other entries are the start of fields (nul-teminated).
  481. /// Returns the number of fields found or -1 if an error occurs.
  482. ///
  483. /// out_fields can be NULL.
  484. /// Fields that don't fit in out_fields are not nul-terminated.
  485. /// Extra entries in out_fields are filled with the end of the last field (empty string).
  486. ///
  487. /// @param str String to parse
  488. /// @param len Length of the string
  489. /// @param startoff Where to start parsing
  490. /// @param delim Field delimiter
  491. /// @param out_fields Array of resulting fields
  492. /// @param nfields Size of the field array
  493. /// @param opt Options that determine the parsing behaviour
  494. /// @return Number of fields found in the string or -1 if an error occured
  495. int sv_split(char* str, int len, int startoff, char delim, char** out_fields, int nfields, enum e_svopt opt)
  496. {
  497. int pos[1024];
  498. int i;
  499. int done;
  500. char* end;
  501. int ret = sv_parse(str, len, startoff, delim, pos, ARRAYLENGTH(pos), opt);
  502. if( ret == -1 || out_fields == NULL || nfields <= 0 )
  503. return ret; // nothing to do
  504. // next line
  505. end = str + pos[1];
  506. if( end[0] == '\0' )
  507. {
  508. *out_fields = end;
  509. }
  510. else if( (opt&SV_TERMINATE_LF) && end[0] == '\n' )
  511. {
  512. if( !(opt&SV_KEEP_TERMINATOR) )
  513. end[0] = '\0';
  514. *out_fields = end + 1;
  515. }
  516. else if( (opt&SV_TERMINATE_CRLF) && end[0] == '\r' && end[1] == '\n' )
  517. {
  518. if( !(opt&SV_KEEP_TERMINATOR) )
  519. end[0] = end[1] = '\0';
  520. *out_fields = end + 2;
  521. }
  522. else if( (opt&SV_TERMINATE_LF) && end[0] == '\r' )
  523. {
  524. if( !(opt&SV_KEEP_TERMINATOR) )
  525. end[0] = '\0';
  526. *out_fields = end + 1;
  527. }
  528. else
  529. {
  530. ShowError("sv_split: unknown line delimiter 0x02%x.\n", (unsigned char)end[0]);
  531. return -1;// error
  532. }
  533. ++out_fields;
  534. --nfields;
  535. // fields
  536. i = 2;
  537. done = 0;
  538. while( done < ret && nfields > 0 )
  539. {
  540. if( i < ARRAYLENGTH(pos) )
  541. {// split field
  542. *out_fields = str + pos[i];
  543. end = str + pos[i+1];
  544. *end = '\0';
  545. // next field
  546. i += 2;
  547. ++done;
  548. ++out_fields;
  549. --nfields;
  550. }
  551. else
  552. {// get more fields
  553. sv_parse(str, len, pos[i-1] + 1, delim, pos, ARRAYLENGTH(pos), opt);
  554. i = 2;
  555. }
  556. }
  557. // remaining fields
  558. for( i = 0; i < nfields; ++i )
  559. out_fields[i] = end;
  560. return ret;
  561. }
  562. /// Escapes src to out_dest according to the format of the C compiler.
  563. /// Returns the length of the escaped string.
  564. /// out_dest should be len*4+1 in size.
  565. ///
  566. /// @param out_dest Destination buffer
  567. /// @param src Source string
  568. /// @param len Length of the source string
  569. /// @param escapes Extra characters to be escaped
  570. /// @return Length of the escaped string
  571. size_t sv_escape_c(char* out_dest, const char* src, size_t len, const char* escapes)
  572. {
  573. size_t i;
  574. size_t j;
  575. if( out_dest == NULL )
  576. return 0;// nothing to do
  577. if( src == NULL )
  578. {// nothing to escape
  579. *out_dest = 0;
  580. return 0;
  581. }
  582. if( escapes == NULL )
  583. escapes = "";
  584. for( i = 0, j = 0; i < len; ++i )
  585. {
  586. switch( src[i] )
  587. {
  588. case '\0':// octal 0
  589. out_dest[j++] = '\\';
  590. out_dest[j++] = '0';
  591. out_dest[j++] = '0';
  592. out_dest[j++] = '0';
  593. break;
  594. case '\r':// carriage return
  595. out_dest[j++] = '\\';
  596. out_dest[j++] = 'r';
  597. break;
  598. case '\n':// line feed
  599. out_dest[j++] = '\\';
  600. out_dest[j++] = 'n';
  601. break;
  602. case '\\':// escape character
  603. out_dest[j++] = '\\';
  604. out_dest[j++] = '\\';
  605. break;
  606. default:
  607. if( strchr(escapes,src[i]) )
  608. {// escapes to octal
  609. out_dest[j++] = '\\';
  610. out_dest[j++] = '0'+((char)(((unsigned char)src[i]&0700)>>6));
  611. out_dest[j++] = '0'+((char)(((unsigned char)src[i]&0070)>>3));
  612. out_dest[j++] = '0'+((char)(((unsigned char)src[i]&0007) ));
  613. }
  614. else
  615. out_dest[j++] = src[i];
  616. break;
  617. }
  618. }
  619. out_dest[j] = 0;
  620. return j;
  621. }
  622. /// Unescapes src to out_dest according to the format of the C compiler.
  623. /// Returns the length of the unescaped string.
  624. /// out_dest should be len+1 in size and can be the same buffer as src.
  625. ///
  626. /// @param out_dest Destination buffer
  627. /// @param src Source string
  628. /// @param len Length of the source string
  629. /// @return Length of the escaped string
  630. size_t sv_unescape_c(char* out_dest, const char* src, size_t len)
  631. {
  632. static unsigned char low2hex[256] = {
  633. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x0?
  634. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x1?
  635. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x2?
  636. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,// 0x3?
  637. 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x4?
  638. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x5?
  639. 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x6?
  640. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x7?
  641. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x8?
  642. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x9?
  643. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xA?
  644. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xB?
  645. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xC?
  646. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xD?
  647. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xE?
  648. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF?
  649. };
  650. size_t i;
  651. size_t j;
  652. for( i = 0, j = 0; i < len; )
  653. {
  654. if( src[i] == '\\' )
  655. {
  656. ++i;// '\\'
  657. if( i >= len )
  658. ShowWarning("sv_unescape_c: empty escape sequence\n");
  659. else if( src[i] == 'x' )
  660. {// hex escape sequence
  661. unsigned char c = 0;
  662. unsigned char inrange = 1;
  663. ++i;// 'x'
  664. if( i >= len || !ISXDIGIT(src[i]) )
  665. {
  666. ShowWarning("sv_unescape_c: \\x with no following hex digits\n");
  667. continue;
  668. }
  669. do{
  670. if( c > 0x0F && inrange )
  671. {
  672. ShowWarning("sv_unescape_c: hex escape sequence out of range\n");
  673. inrange = 0;
  674. }
  675. c = (c<<8)|low2hex[(unsigned char)src[i++]];// hex digit
  676. }while( i >= len || !ISXDIGIT(src[i]) );
  677. out_dest[j++] = (char)c;
  678. }
  679. else if( src[i] == '0' || src[i] == '1' || src[i] == '2' || src[i] == '3' )
  680. {// octal escape sequence (255=0377)
  681. unsigned char c = src[i]-'0';
  682. ++i;// '0', '1', '2' or '3'
  683. if( i < len && src[i] >= '0' && src[i] <= '9' )
  684. {
  685. c = (c<<3)|(src[i]-'0');
  686. ++i;// octal digit
  687. }
  688. if( i < len && src[i] >= '0' && src[i] <= '9' )
  689. {
  690. c = (c<<3)|(src[i]-'0');
  691. ++i;// octal digit
  692. }
  693. out_dest[j++] = (char)c;
  694. }
  695. else
  696. {// other escape sequence
  697. if( strchr(SV_ESCAPE_C_SUPPORTED, src[i]) == NULL )
  698. ShowWarning("sv_parse: unknown escape sequence \\%c\n", src[i]);
  699. switch( src[i] )
  700. {
  701. case 'a': out_dest[j++] = '\a'; break;
  702. case 'b': out_dest[j++] = '\b'; break;
  703. case 't': out_dest[j++] = '\t'; break;
  704. case 'n': out_dest[j++] = '\n'; break;
  705. case 'v': out_dest[j++] = '\v'; break;
  706. case 'f': out_dest[j++] = '\f'; break;
  707. case 'r': out_dest[j++] = '\r'; break;
  708. case '?': out_dest[j++] = '\?'; break;
  709. default: out_dest[j++] = src[i]; break;
  710. }
  711. ++i;// escaped character
  712. }
  713. }
  714. else
  715. out_dest[j++] = src[i++];// normal character
  716. }
  717. out_dest[j] = 0;
  718. return j;
  719. }
  720. /// Opens and parses a file containing delim-separated columns, feeding them to the specified callback function row by row.
  721. /// Tracks the progress of the operation (current line number, number of successfully processed rows).
  722. /// Returns 'true' if it was able to process the specified file, or 'false' if it could not be read.
  723. ///
  724. /// @param directory Directory
  725. /// @param filename File to process
  726. /// @param delim Field delimiter
  727. /// @param mincols Minimum number of columns of a valid row
  728. /// @param maxcols Maximum number of columns of a valid row
  729. /// @param parseproc User-supplied row processing function
  730. /// @return true on success, false if file could not be opened
  731. bool sv_readdb(const char* directory, const char* filename, char delim, int mincols, int maxcols, int maxrows, bool (*parseproc)(char* fields[], int columns, int current))
  732. {
  733. FILE* fp;
  734. int lines = 0;
  735. int entries = 0;
  736. char* fields[64]; // room for 63 fields ([0] is reserved)
  737. int columns;
  738. char path[1024], line[1024];
  739. if( maxcols > ARRAYLENGTH(fields)-1 )
  740. {
  741. ShowError("sv_readdb: Insufficient column storage in parser for file \"%s\" (want %d, have only %d). Increase the capacity in the source code please.\n", path, maxcols, ARRAYLENGTH(fields)-1);
  742. return false;
  743. }
  744. // open file
  745. snprintf(path, sizeof(path), "%s/%s", directory, filename);
  746. fp = fopen(path, "r");
  747. if( fp == NULL )
  748. {
  749. ShowError("sv_readdb: can't read %s\n", path);
  750. return false;
  751. }
  752. // process rows one by one
  753. while( fgets(line, sizeof(line), fp) )
  754. {
  755. lines++;
  756. if( line[0] == '/' && line[1] == '/' )
  757. continue;
  758. //TODO: strip trailing // comment
  759. //TODO: strip trailing whitespace
  760. if( line[0] == '\0' || line[0] == '\n' )
  761. continue;
  762. columns = sv_split(line, strlen(line), 0, delim, fields, ARRAYLENGTH(fields), SV_NOESCAPE_NOTERMINATE);
  763. if( columns < mincols )
  764. {
  765. ShowError("sv_readdb: Insufficient columns in line %d of \"%s\" (found %d, need at least %d).\n", lines, path, columns, mincols);
  766. continue; // not enough columns
  767. }
  768. if( columns > maxcols )
  769. {
  770. ShowError("sv_readdb: Too many columns in line %d of \"%s\" (found %d, maximum is %d).\n", lines, path, columns, maxcols );
  771. continue; // too many columns
  772. }
  773. if( entries == maxrows )
  774. {
  775. ShowError("sv_readdb: Reached the maximum allowed number of entries (%d) when parsing file \"%s\".\n", maxrows, path);
  776. break;
  777. }
  778. // parse this row
  779. if( !parseproc(fields+1, columns, entries) )
  780. {
  781. ShowError("sv_readdb: Could not process contents of line %d of \"%s\".\n", lines, path);
  782. continue; // invalid row contents
  783. }
  784. // success!
  785. entries++;
  786. }
  787. fclose(fp);
  788. ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", entries, path);
  789. return true;
  790. }
  791. /////////////////////////////////////////////////////////////////////
  792. // StringBuf - dynamic string
  793. //
  794. // @author MouseJstr (original)
  795. /// Allocates a StringBuf
  796. StringBuf* StringBuf_Malloc()
  797. {
  798. StringBuf* self;
  799. CREATE(self, StringBuf, 1);
  800. StringBuf_Init(self);
  801. return self;
  802. }
  803. /// Initializes a previously allocated StringBuf
  804. void StringBuf_Init(StringBuf* self)
  805. {
  806. self->max_ = 1024;
  807. self->ptr_ = self->buf_ = (char*)aMallocA(self->max_ + 1);
  808. }
  809. /// Appends the result of printf to the StringBuf
  810. int StringBuf_Printf(StringBuf* self, const char* fmt, ...)
  811. {
  812. int len;
  813. va_list ap;
  814. va_start(ap, fmt);
  815. len = StringBuf_Vprintf(self, fmt, ap);
  816. va_end(ap);
  817. return len;
  818. }
  819. /// Appends the result of vprintf to the StringBuf
  820. int StringBuf_Vprintf(StringBuf* self, const char* fmt, va_list ap)
  821. {
  822. int n, size, off;
  823. for(;;)
  824. {
  825. /* Try to print in the allocated space. */
  826. size = self->max_ - (self->ptr_ - self->buf_);
  827. n = vsnprintf(self->ptr_, size, fmt, ap);
  828. /* If that worked, return the length. */
  829. if( n > -1 && n < size )
  830. {
  831. self->ptr_ += n;
  832. return (int)(self->ptr_ - self->buf_);
  833. }
  834. /* Else try again with more space. */
  835. self->max_ *= 2; // twice the old size
  836. off = (int)(self->ptr_ - self->buf_);
  837. self->buf_ = (char*)aRealloc(self->buf_, self->max_ + 1);
  838. self->ptr_ = self->buf_ + off;
  839. }
  840. }
  841. /// Appends the contents of another StringBuf to the StringBuf
  842. int StringBuf_Append(StringBuf* self, const StringBuf* sbuf)
  843. {
  844. int available = self->max_ - (self->ptr_ - self->buf_);
  845. int needed = (int)(sbuf->ptr_ - sbuf->buf_);
  846. if( needed >= available )
  847. {
  848. int off = (int)(self->ptr_ - self->buf_);
  849. self->max_ += needed;
  850. self->buf_ = (char*)aRealloc(self->buf_, self->max_ + 1);
  851. self->ptr_ = self->buf_ + off;
  852. }
  853. memcpy(self->ptr_, sbuf->buf_, needed);
  854. self->ptr_ += needed;
  855. return (int)(self->ptr_ - self->buf_);
  856. }
  857. // Appends str to the StringBuf
  858. int StringBuf_AppendStr(StringBuf* self, const char* str)
  859. {
  860. int available = self->max_ - (self->ptr_ - self->buf_);
  861. int needed = (int)strlen(str);
  862. if( needed >= available )
  863. {// not enough space, expand the buffer (minimum expansion = 1024)
  864. int off = (int)(self->ptr_ - self->buf_);
  865. self->max_ += max(needed, 1024);
  866. self->buf_ = (char*)aRealloc(self->buf_, self->max_ + 1);
  867. self->ptr_ = self->buf_ + off;
  868. }
  869. memcpy(self->ptr_, str, needed);
  870. self->ptr_ += needed;
  871. return (int)(self->ptr_ - self->buf_);
  872. }
  873. // Returns the length of the data in the Stringbuf
  874. int StringBuf_Length(StringBuf* self)
  875. {
  876. return (int)(self->ptr_ - self->buf_);
  877. }
  878. /// Returns the data in the StringBuf
  879. char* StringBuf_Value(StringBuf* self)
  880. {
  881. *self->ptr_ = '\0';
  882. return self->buf_;
  883. }
  884. /// Clears the contents of the StringBuf
  885. void StringBuf_Clear(StringBuf* self)
  886. {
  887. self->ptr_ = self->buf_;
  888. }
  889. /// Destroys the StringBuf
  890. void StringBuf_Destroy(StringBuf* self)
  891. {
  892. aFree(self->buf_);
  893. self->ptr_ = self->buf_ = 0;
  894. self->max_ = 0;
  895. }
  896. // Frees a StringBuf returned by StringBuf_Malloc
  897. void StringBuf_Free(StringBuf* self)
  898. {
  899. StringBuf_Destroy(self);
  900. aFree(self);
  901. }