PageRenderTime 61ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/src/common/strlib.c

https://gitlab.com/evol/hercules
C | 1156 lines | 850 code | 111 blank | 195 comment | 277 complexity | 7708376a31a67da639738f3bf0e39b62 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.0
  1. /**
  2. * This file is part of Hercules.
  3. * http://herc.ws - http://github.com/HerculesWS/Hercules
  4. *
  5. * Copyright (C) 2012-2015 Hercules Dev Team
  6. * Copyright (C) Athena Dev Teams
  7. *
  8. * Hercules is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #define HERCULES_CORE
  22. #include "strlib.h"
  23. #include "common/cbasetypes.h"
  24. #include "common/memmgr.h"
  25. #include "common/showmsg.h"
  26. #include <errno.h>
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #define J_MAX_MALLOC_SIZE 65535
  30. struct strlib_interface strlib_s;
  31. struct stringbuf_interface stringbuf_s;
  32. struct sv_interface sv_s;
  33. struct strlib_interface *strlib;
  34. struct stringbuf_interface *StrBuf;
  35. struct sv_interface *sv;
  36. // escapes a string in-place (' -> \' , \ -> \\ , % -> _)
  37. char* jstrescape (char* pt) {
  38. //copy from here
  39. char *ptr;
  40. int i = 0, j = 0;
  41. //copy string to temporary
  42. CREATE(ptr, char, J_MAX_MALLOC_SIZE);
  43. strcpy(ptr,pt);
  44. while (ptr[i] != '\0') {
  45. switch (ptr[i]) {
  46. case '\'':
  47. pt[j++] = '\\';
  48. pt[j++] = ptr[i++];
  49. break;
  50. case '\\':
  51. pt[j++] = '\\';
  52. pt[j++] = ptr[i++];
  53. break;
  54. case '%':
  55. pt[j++] = '_'; i++;
  56. break;
  57. default:
  58. pt[j++] = ptr[i++];
  59. }
  60. }
  61. pt[j++] = '\0';
  62. aFree(ptr);
  63. return pt;
  64. }
  65. // escapes a string into a provided buffer
  66. char* jstrescapecpy (char* pt, const char* spt)
  67. {
  68. //copy from here
  69. //WARNING: Target string pt should be able to hold strlen(spt)*2, as each time
  70. //a escape character is found, the target's final length increases! [Skotlex]
  71. int i =0, j=0;
  72. if (!spt) {
  73. //Return an empty string [Skotlex]
  74. pt[0] = '\0';
  75. return &pt[0];
  76. }
  77. while (spt[i] != '\0') {
  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. pt[j++] = '\0';
  95. return &pt[0];
  96. }
  97. // escapes exactly 'size' bytes of a string into a provided buffer
  98. int jmemescapecpy (char* pt, const char* spt, int size)
  99. {
  100. //copy from here
  101. int i =0, j=0;
  102. while (i < size) {
  103. switch (spt[i]) {
  104. case '\'':
  105. pt[j++] = '\\';
  106. pt[j++] = spt[i++];
  107. break;
  108. case '\\':
  109. pt[j++] = '\\';
  110. pt[j++] = spt[i++];
  111. break;
  112. case '%':
  113. pt[j++] = '_'; i++;
  114. break;
  115. default:
  116. pt[j++] = spt[i++];
  117. }
  118. }
  119. // copy size is 0 ~ (j-1)
  120. return j;
  121. }
  122. // Function to suppress control characters in a string.
  123. int strlib_remove_control_chars(char *str)
  124. {
  125. int i;
  126. int change = 0;
  127. for(i = 0; str[i]; i++) {
  128. if (ISCNTRL(str[i])) {
  129. str[i] = '_';
  130. change = 1;
  131. }
  132. }
  133. return change;
  134. }
  135. // Removes characters identified by ISSPACE from the start and end of the string
  136. // NOTE: make sure the string is not const!!
  137. char *strlib_trim(char *str)
  138. {
  139. size_t start;
  140. size_t end;
  141. if( str == NULL )
  142. return str;
  143. // get start position
  144. for( start = 0; str[start] && ISSPACE(str[start]); ++start )
  145. ;
  146. // get end position
  147. for( end = strlen(str); start < end && str[end-1] && ISSPACE(str[end-1]); --end )
  148. ;
  149. // trim
  150. if( start == end )
  151. *str = '\0';// empty string
  152. else
  153. {// move string with null-terminator
  154. str[end] = '\0';
  155. memmove(str,str+start,end-start+1);
  156. }
  157. return str;
  158. }
  159. // Converts one or more consecutive occurrences of the delimiters into a single space
  160. // and removes such occurrences from the beginning and end of string
  161. // NOTE: make sure the string is not const!!
  162. char *strlib_normalize_name(char *str, const char *delims)
  163. {
  164. char* in = str;
  165. char* out = str;
  166. int put_space = 0;
  167. if( str == NULL || delims == NULL )
  168. return str;
  169. // trim start of string
  170. while( *in && strchr(delims,*in) )
  171. ++in;
  172. while( *in )
  173. {
  174. if( put_space )
  175. {// replace trim characters with a single space
  176. *out = ' ';
  177. ++out;
  178. }
  179. // copy non trim characters
  180. while( *in && !strchr(delims,*in) )
  181. {
  182. *out = *in;
  183. ++out;
  184. ++in;
  185. }
  186. // skip trim characters
  187. while( *in && strchr(delims,*in) )
  188. ++in;
  189. put_space = 1;
  190. }
  191. *out = '\0';
  192. return str;
  193. }
  194. //stristr: Case insensitive version of strstr, code taken from
  195. //http://www.daniweb.com/code/snippet313.html, Dave Sinkula
  196. //
  197. const char *strlib_stristr(const char *haystack, const char *needle)
  198. {
  199. if ( !*needle )
  200. {
  201. return haystack;
  202. }
  203. for ( ; *haystack; ++haystack )
  204. {
  205. if ( TOUPPER(*haystack) == TOUPPER(*needle) )
  206. {
  207. // matched starting char -- loop through remaining chars
  208. const char *h, *n;
  209. for ( h = haystack, n = needle; *h && *n; ++h, ++n )
  210. {
  211. if ( TOUPPER(*h) != TOUPPER(*n) )
  212. {
  213. break;
  214. }
  215. }
  216. if ( !*n ) // matched all of 'needle' to null termination
  217. {
  218. return haystack; // return the start of the match
  219. }
  220. }
  221. }
  222. return 0;
  223. }
  224. char* strlib_strtok_r(char *s1, const char *s2, char **lasts)
  225. {
  226. #ifdef __WIN32
  227. char *ret;
  228. if (s1 == NULL)
  229. s1 = *lasts;
  230. while(*s1 && strchr(s2, *s1))
  231. ++s1;
  232. if(*s1 == '\0')
  233. return NULL;
  234. ret = s1;
  235. while(*s1 && !strchr(s2, *s1))
  236. ++s1;
  237. if(*s1)
  238. *s1++ = '\0';
  239. *lasts = s1;
  240. return ret;
  241. #else
  242. return strtok_r(s1, s2, lasts);
  243. #endif
  244. }
  245. size_t strlib_strnlen(const char *string, size_t maxlen)
  246. {
  247. // TODO: The _MSC_VER check can probably be removed (we no longer support VS
  248. // versions <= 2003, do we?), but this implementation might be still necessary
  249. // for NetBSD 5.x and possibly some Solaris versions.
  250. #if !(defined(WIN32) && defined(_MSC_VER) && _MSC_VER >= 1400) && !defined(HAVE_STRNLEN)
  251. /* Find the length of STRING, but scan at most MAXLEN characters.
  252. * If no '\0' terminator is found in that many characters, return MAXLEN.
  253. */
  254. const char* end = (const char*)memchr(string, '\0', maxlen);
  255. return end ? (size_t) (end - string) : maxlen;
  256. #else
  257. return strnlen(string, maxlen);
  258. #endif
  259. }
  260. //----------------------------------------------------
  261. // E-mail check: return 0 (not correct) or 1 (valid).
  262. //----------------------------------------------------
  263. int strlib_e_mail_check(char *email)
  264. {
  265. char ch;
  266. char* last_arobas;
  267. size_t len = strlen(email);
  268. // athena limits
  269. if (len < 3 || len > 39)
  270. return 0;
  271. // part of RFC limits (official reference of e-mail description)
  272. if (strchr(email, '@') == NULL || email[len-1] == '@')
  273. return 0;
  274. if (email[len-1] == '.')
  275. return 0;
  276. last_arobas = strrchr(email, '@');
  277. if (strstr(last_arobas, "@.") != NULL || strstr(last_arobas, "..") != NULL)
  278. return 0;
  279. for(ch = 1; ch < 32; ch++)
  280. if (strchr(last_arobas, ch) != NULL)
  281. return 0;
  282. if (strchr(last_arobas, ' ') != NULL || strchr(last_arobas, ';') != NULL)
  283. return 0;
  284. // all correct
  285. return 1;
  286. }
  287. //--------------------------------------------------
  288. // Return numerical value of a switch configuration
  289. // on/off, yes/no, true/false, number
  290. //--------------------------------------------------
  291. int strlib_config_switch(const char *str) {
  292. size_t len = strlen(str);
  293. if ((len == 2 && strcmpi(str, "on") == 0)
  294. || (len == 3 && strcmpi(str, "yes") == 0)
  295. || (len == 4 && strcmpi(str, "true") == 0)
  296. // || (len == 3 && strcmpi(str, "oui") == 0) // Uncomment and edit to add your own localized versions
  297. )
  298. return 1;
  299. if ((len == 3 && strcmpi(str, "off") == 0)
  300. || (len == 2 && strcmpi(str, "no") == 0)
  301. || (len == 5 && strcmpi(str, "false") == 0)
  302. // || (len == 3 && strcmpi(str, "non") == 0) // Uncomment and edit to add your own localized versions
  303. )
  304. return 0;
  305. return (int)strtol(str, NULL, 0);
  306. }
  307. /// strncpy that always null-terminates the string
  308. char *strlib_safestrncpy(char *dst, const char *src, size_t n)
  309. {
  310. if( n > 0 )
  311. {
  312. char* d = dst;
  313. const char* s = src;
  314. d[--n] = '\0';/* null-terminate string */
  315. for( ; n > 0; --n )
  316. {
  317. if( (*d++ = *s++) == '\0' )
  318. {/* null-pad remaining bytes */
  319. while( --n > 0 )
  320. *d++ = '\0';
  321. break;
  322. }
  323. }
  324. }
  325. return dst;
  326. }
  327. /// doesn't crash on null pointer
  328. size_t strlib_safestrnlen(const char *string, size_t maxlen)
  329. {
  330. return ( string != NULL ) ? strnlen(string, maxlen) : 0;
  331. }
  332. /// Works like snprintf, but always null-terminates the buffer.
  333. /// Returns the size of the string (without null-terminator)
  334. /// or -1 if the buffer is too small.
  335. ///
  336. /// @param buf Target buffer
  337. /// @param sz Size of the buffer (including null-terminator)
  338. /// @param fmt Format string
  339. /// @param ... Format arguments
  340. /// @return The size of the string or -1 if the buffer is too small
  341. int strlib_safesnprintf(char *buf, size_t sz, const char *fmt, ...) __attribute__((format(printf, 3, 4)));
  342. int strlib_safesnprintf(char *buf, size_t sz, const char *fmt, ...)
  343. {
  344. va_list ap;
  345. int ret;
  346. va_start(ap,fmt);
  347. ret = vsnprintf(buf, sz, fmt, ap);
  348. va_end(ap);
  349. if (ret < 0 || (size_t)ret >= sz) { // overflow
  350. buf[sz-1] = '\0';// always null-terminate
  351. return -1;
  352. }
  353. return ret;
  354. }
  355. /// Returns the line of the target position in the string.
  356. /// Lines start at 1.
  357. int strlib_strline(const char *str, size_t pos)
  358. {
  359. const char* target;
  360. int line;
  361. if( str == NULL || pos == 0 )
  362. return 1;
  363. target = str+pos;
  364. for( line = 1; ; ++line )
  365. {
  366. str = strchr(str, '\n');
  367. if( str == NULL || target <= str )
  368. break;// found target line
  369. ++str;// skip newline
  370. }
  371. return line;
  372. }
  373. /// Produces the hexadecimal representation of the given input.
  374. /// The output buffer must be at least count*2+1 in size.
  375. /// Returns true on success, false on failure.
  376. ///
  377. /// @param output Output string
  378. /// @param input Binary input buffer
  379. /// @param count Number of bytes to convert
  380. bool strlib_bin2hex(char *output, unsigned char *input, size_t count)
  381. {
  382. char toHex[] = "0123456789abcdef";
  383. size_t i;
  384. for( i = 0; i < count; ++i )
  385. {
  386. *output++ = toHex[(*input & 0xF0) >> 4];
  387. *output++ = toHex[(*input & 0x0F) >> 0];
  388. ++input;
  389. }
  390. *output = '\0';
  391. return true;
  392. }
  393. /////////////////////////////////////////////////////////////////////
  394. /// Parses a single field in a delim-separated string.
  395. /// The delimiter after the field is skipped.
  396. ///
  397. /// @param svstate Parse state
  398. /// @return 1 if a field was parsed, 0 if already done, -1 on error.
  399. int sv_parse_next(struct s_svstate* svstate)
  400. {
  401. enum {
  402. START_OF_FIELD,
  403. PARSING_FIELD,
  404. PARSING_C_ESCAPE,
  405. END_OF_FIELD,
  406. TERMINATE,
  407. END
  408. } state;
  409. const char* str;
  410. int len;
  411. enum e_svopt opt;
  412. char delim;
  413. int i;
  414. if( svstate == NULL )
  415. return -1;// error
  416. str = svstate->str;
  417. len = svstate->len;
  418. opt = svstate->opt;
  419. delim = svstate->delim;
  420. // check opt
  421. if( delim == '\n' && (opt&(SV_TERMINATE_CRLF|SV_TERMINATE_LF)) )
  422. {
  423. ShowError("sv_parse_next: delimiter '\\n' is not compatible with options SV_TERMINATE_LF or SV_TERMINATE_CRLF.\n");
  424. return -1;// error
  425. }
  426. if( delim == '\r' && (opt&(SV_TERMINATE_CRLF|SV_TERMINATE_CR)) )
  427. {
  428. ShowError("sv_parse_next: delimiter '\\r' is not compatible with options SV_TERMINATE_CR or SV_TERMINATE_CRLF.\n");
  429. return -1;// error
  430. }
  431. if( svstate->done || str == NULL )
  432. {
  433. svstate->done = true;
  434. return 0;// nothing to parse
  435. }
  436. #define IS_END() ( i >= len )
  437. #define IS_DELIM() ( str[i] == delim )
  438. #define IS_TERMINATOR() ( \
  439. ((opt&SV_TERMINATE_LF) && str[i] == '\n') || \
  440. ((opt&SV_TERMINATE_CR) && str[i] == '\r') || \
  441. ((opt&SV_TERMINATE_CRLF) && i+1 < len && str[i] == '\r' && str[i+1] == '\n') )
  442. #define IS_C_ESCAPE() ( (opt&SV_ESCAPE_C) && str[i] == '\\' )
  443. #define SET_FIELD_START() svstate->start = i
  444. #define SET_FIELD_END() svstate->end = i
  445. i = svstate->off;
  446. state = START_OF_FIELD;
  447. while( state != END )
  448. {
  449. switch( state )
  450. {
  451. case START_OF_FIELD:// record start of field and start parsing it
  452. SET_FIELD_START();
  453. state = PARSING_FIELD;
  454. break;
  455. case PARSING_FIELD:// skip field character
  456. if( IS_END() || IS_DELIM() || IS_TERMINATOR() )
  457. state = END_OF_FIELD;
  458. else if( IS_C_ESCAPE() )
  459. state = PARSING_C_ESCAPE;
  460. else
  461. ++i;// normal character
  462. break;
  463. case PARSING_C_ESCAPE:// skip escape sequence (validates it too)
  464. {
  465. ++i;// '\\'
  466. if( IS_END() )
  467. {
  468. ShowError("sv_parse_next: empty escape sequence\n");
  469. return -1;
  470. }
  471. if( str[i] == 'x' )
  472. {// hex escape
  473. ++i;// 'x'
  474. if( IS_END() || !ISXDIGIT(str[i]) )
  475. {
  476. ShowError("sv_parse_next: \\x with no following hex digits\n");
  477. return -1;
  478. }
  479. do{
  480. ++i;// hex digit
  481. }while( !IS_END() && ISXDIGIT(str[i]));
  482. }
  483. else if( str[i] == '0' || str[i] == '1' || str[i] == '2' )
  484. {// octal escape
  485. ++i;// octal digit
  486. if( !IS_END() && str[i] >= '0' && str[i] <= '7' )
  487. ++i;// octal digit
  488. if( !IS_END() && str[i] >= '0' && str[i] <= '7' )
  489. ++i;// octal digit
  490. }
  491. else if( strchr(SV_ESCAPE_C_SUPPORTED, str[i]) )
  492. {// supported escape character
  493. ++i;
  494. }
  495. else
  496. {
  497. ShowError("sv_parse_next: unknown escape sequence \\%c\n", str[i]);
  498. return -1;
  499. }
  500. state = PARSING_FIELD;
  501. break;
  502. }
  503. case END_OF_FIELD:// record end of field and stop
  504. SET_FIELD_END();
  505. state = END;
  506. if( IS_END() )
  507. ;// nothing else
  508. else if( IS_DELIM() )
  509. ++i;// delim
  510. else if( IS_TERMINATOR() )
  511. state = TERMINATE;
  512. break;
  513. case TERMINATE:
  514. #if 0
  515. // skip line terminator
  516. if( (opt&SV_TERMINATE_CRLF) && i+1 < len && str[i] == '\r' && str[i+1] == '\n' )
  517. i += 2;// CRLF
  518. else
  519. ++i;// CR or LF
  520. #endif
  521. svstate->done = true;
  522. state = END;
  523. break;
  524. }
  525. }
  526. if( IS_END() )
  527. svstate->done = true;
  528. svstate->off = i;
  529. #undef IS_END
  530. #undef IS_DELIM
  531. #undef IS_TERMINATOR
  532. #undef IS_C_ESCAPE
  533. #undef SET_FIELD_START
  534. #undef SET_FIELD_END
  535. return 1;
  536. }
  537. /// Parses a delim-separated string.
  538. /// Starts parsing at startoff and fills the pos array with position pairs.
  539. /// out_pos[0] and out_pos[1] are the start and end of line.
  540. /// Other position pairs are the start and end of fields.
  541. /// Returns the number of fields found or -1 if an error occurs.
  542. ///
  543. /// out_pos can be NULL.
  544. /// If a line terminator is found, the end position is placed there.
  545. /// out_pos[2] and out_pos[3] for the first field, out_pos[4] and out_pos[5]
  546. /// for the seconds field and so on.
  547. /// Unfilled positions are set to -1.
  548. ///
  549. /// @param str String to parse
  550. /// @param len Length of the string
  551. /// @param startoff Where to start parsing
  552. /// @param delim Field delimiter
  553. /// @param out_pos Array of resulting positions
  554. /// @param npos Size of the pos array
  555. /// @param opt Options that determine the parsing behavior
  556. /// @return Number of fields found in the string or -1 if an error occurred
  557. int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, int npos, enum e_svopt opt) {
  558. struct s_svstate svstate;
  559. int count;
  560. // initialize
  561. if( out_pos == NULL ) npos = 0;
  562. for( count = 0; count < npos; ++count )
  563. out_pos[count] = -1;
  564. svstate.str = str;
  565. svstate.len = len;
  566. svstate.off = startoff;
  567. svstate.opt = opt;
  568. svstate.delim = delim;
  569. svstate.done = false;
  570. // parse
  571. count = 0;
  572. if( npos > 0 ) out_pos[0] = startoff;
  573. while( !svstate.done ) {
  574. ++count;
  575. if( sv_parse_next(&svstate) <= 0 )
  576. return -1;// error
  577. if( npos > count*2 ) out_pos[count*2] = svstate.start;
  578. if( npos > count*2+1 ) out_pos[count*2+1] = svstate.end;
  579. }
  580. if( npos > 1 ) out_pos[1] = svstate.off;
  581. return count;
  582. }
  583. /// Splits a delim-separated string.
  584. /// WARNING: this function modifies the input string
  585. /// Starts splitting at startoff and fills the out_fields array.
  586. /// out_fields[0] is the start of the next line.
  587. /// Other entries are the start of fields (null-terminated).
  588. /// Returns the number of fields found or -1 if an error occurs.
  589. ///
  590. /// out_fields can be NULL.
  591. /// Fields that don't fit in out_fields are not null-terminated.
  592. /// Extra entries in out_fields are filled with the end of the last field (empty string).
  593. ///
  594. /// @param str String to parse
  595. /// @param len Length of the string
  596. /// @param startoff Where to start parsing
  597. /// @param delim Field delimiter
  598. /// @param out_fields Array of resulting fields
  599. /// @param nfields Size of the field array
  600. /// @param opt Options that determine the parsing behavior
  601. /// @return Number of fields found in the string or -1 if an error occurred
  602. int sv_split(char* str, int len, int startoff, char delim, char** out_fields, int nfields, enum e_svopt opt) {
  603. int pos[1024];
  604. int i;
  605. int done;
  606. char* end;
  607. int ret = sv_parse(str, len, startoff, delim, pos, ARRAYLENGTH(pos), opt);
  608. if( ret == -1 || out_fields == NULL || nfields <= 0 )
  609. return ret; // nothing to do
  610. // next line
  611. end = str + pos[1];
  612. if( end[0] == '\0' ) {
  613. *out_fields = end;
  614. } else if( (opt&SV_TERMINATE_LF) && end[0] == '\n' ) {
  615. if( !(opt&SV_KEEP_TERMINATOR) )
  616. end[0] = '\0';
  617. *out_fields = end + 1;
  618. } else if( (opt&SV_TERMINATE_CRLF) && end[0] == '\r' && end[1] == '\n' ) {
  619. if( !(opt&SV_KEEP_TERMINATOR) )
  620. end[0] = end[1] = '\0';
  621. *out_fields = end + 2;
  622. } else if( (opt&SV_TERMINATE_CR) && end[0] == '\r' ) {
  623. if( !(opt&SV_KEEP_TERMINATOR) )
  624. end[0] = '\0';
  625. *out_fields = end + 1;
  626. } else {
  627. ShowError("sv_split: unknown line delimiter 0x02%x.\n", (unsigned char)end[0]);
  628. return -1;// error
  629. }
  630. ++out_fields;
  631. --nfields;
  632. // fields
  633. i = 2;
  634. done = 0;
  635. while( done < ret && nfields > 0 ) {
  636. if( i < ARRAYLENGTH(pos) ) { // split field
  637. *out_fields = str + pos[i];
  638. end = str + pos[i+1];
  639. *end = '\0';
  640. // next field
  641. i += 2;
  642. ++done;
  643. ++out_fields;
  644. --nfields;
  645. } else { // get more fields
  646. sv_parse(str, len, pos[i-1] + 1, delim, pos, ARRAYLENGTH(pos), opt);
  647. i = 2;
  648. }
  649. }
  650. // remaining fields
  651. for( i = 0; i < nfields; ++i )
  652. out_fields[i] = end;
  653. return ret;
  654. }
  655. /// Escapes src to out_dest according to the format of the C compiler.
  656. /// Returns the length of the escaped string.
  657. /// out_dest should be len*4+1 in size.
  658. ///
  659. /// @param out_dest Destination buffer
  660. /// @param src Source string
  661. /// @param len Length of the source string
  662. /// @param escapes Extra characters to be escaped
  663. /// @return Length of the escaped string
  664. size_t sv_escape_c(char* out_dest, const char* src, size_t len, const char* escapes) {
  665. size_t i;
  666. size_t j;
  667. if( out_dest == NULL )
  668. return 0;// nothing to do
  669. if( src == NULL ) { // nothing to escape
  670. *out_dest = 0;
  671. return 0;
  672. }
  673. if( escapes == NULL )
  674. escapes = "";
  675. for( i = 0, j = 0; i < len; ++i ) {
  676. switch( src[i] ) {
  677. case '\0':// octal 0
  678. out_dest[j++] = '\\';
  679. out_dest[j++] = '0';
  680. out_dest[j++] = '0';
  681. out_dest[j++] = '0';
  682. break;
  683. case '\r':// carriage return
  684. out_dest[j++] = '\\';
  685. out_dest[j++] = 'r';
  686. break;
  687. case '\n':// line feed
  688. out_dest[j++] = '\\';
  689. out_dest[j++] = 'n';
  690. break;
  691. case '\\':// escape character
  692. out_dest[j++] = '\\';
  693. out_dest[j++] = '\\';
  694. break;
  695. default:
  696. if( strchr(escapes,src[i]) ) {// escape
  697. out_dest[j++] = '\\';
  698. switch( src[i] ) {
  699. case '\a': out_dest[j++] = 'a'; break;
  700. case '\b': out_dest[j++] = 'b'; break;
  701. case '\t': out_dest[j++] = 't'; break;
  702. case '\v': out_dest[j++] = 'v'; break;
  703. case '\f': out_dest[j++] = 'f'; break;
  704. case '\?': out_dest[j++] = '?'; break;
  705. default:// to octal
  706. out_dest[j++] = '0'+((char)(((unsigned char)src[i]&0700)>>6));
  707. out_dest[j++] = '0'+((char)(((unsigned char)src[i]&0070)>>3));
  708. out_dest[j++] = '0'+((char)(((unsigned char)src[i]&0007) ));
  709. break;
  710. }
  711. }
  712. else
  713. out_dest[j++] = src[i];
  714. break;
  715. }
  716. }
  717. out_dest[j] = 0;
  718. return j;
  719. }
  720. /// Unescapes src to out_dest according to the format of the C compiler.
  721. /// Returns the length of the unescaped string.
  722. /// out_dest should be len+1 in size and can be the same buffer as src.
  723. ///
  724. /// @param out_dest Destination buffer
  725. /// @param src Source string
  726. /// @param len Length of the source string
  727. /// @return Length of the escaped string
  728. size_t sv_unescape_c(char* out_dest, const char* src, size_t len) {
  729. static unsigned char low2hex[256] = {
  730. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x0?
  731. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x1?
  732. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x2?
  733. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,// 0x3?
  734. 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x4?
  735. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x5?
  736. 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x6?
  737. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x7?
  738. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x8?
  739. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0x9?
  740. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xA?
  741. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xB?
  742. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xC?
  743. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xD?
  744. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0xE?
  745. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF?
  746. };
  747. size_t i;
  748. size_t j;
  749. for( i = 0, j = 0; i < len; ) {
  750. if( src[i] == '\\' ) {
  751. ++i;// '\\'
  752. if( i >= len )
  753. ShowWarning("sv_unescape_c: empty escape sequence\n");
  754. else if( src[i] == 'x' ) {// hex escape sequence
  755. unsigned char c = 0;
  756. unsigned char inrange = 1;
  757. ++i;// 'x'
  758. if( i >= len || !ISXDIGIT(src[i]) ) {
  759. ShowWarning("sv_unescape_c: \\x with no following hex digits\n");
  760. continue;
  761. }
  762. do {
  763. if( c > 0x0F && inrange ) {
  764. ShowWarning("sv_unescape_c: hex escape sequence out of range\n");
  765. inrange = 0;
  766. }
  767. c = (c<<4)|low2hex[(unsigned char)src[i]];// hex digit
  768. ++i;
  769. } while( i < len && ISXDIGIT(src[i]) );
  770. out_dest[j++] = (char)c;
  771. } else if( src[i] == '0' || src[i] == '1' || src[i] == '2' || src[i] == '3' ) {// octal escape sequence (255=0377)
  772. unsigned char c = src[i]-'0';
  773. ++i;// '0', '1', '2' or '3'
  774. if( i < len && src[i] >= '0' && src[i] <= '7' ) {
  775. c = (c<<3)|(src[i]-'0');
  776. ++i;// octal digit
  777. }
  778. if( i < len && src[i] >= '0' && src[i] <= '7' ) {
  779. c = (c<<3)|(src[i]-'0');
  780. ++i;// octal digit
  781. }
  782. out_dest[j++] = (char)c;
  783. } else { // other escape sequence
  784. if( strchr(SV_ESCAPE_C_SUPPORTED, src[i]) == NULL )
  785. ShowWarning("sv_unescape_c: unknown escape sequence \\%c\n", src[i]);
  786. switch( src[i] ) {
  787. case 'a': out_dest[j++] = '\a'; break;
  788. case 'b': out_dest[j++] = '\b'; break;
  789. case 't': out_dest[j++] = '\t'; break;
  790. case 'n': out_dest[j++] = '\n'; break;
  791. case 'v': out_dest[j++] = '\v'; break;
  792. case 'f': out_dest[j++] = '\f'; break;
  793. case 'r': out_dest[j++] = '\r'; break;
  794. case '?': out_dest[j++] = '\?'; break;
  795. default: out_dest[j++] = src[i]; break;
  796. }
  797. ++i;// escaped character
  798. }
  799. } else
  800. out_dest[j++] = src[i++];// normal character
  801. }
  802. out_dest[j] = 0;
  803. return j;
  804. }
  805. /// Skips a C escape sequence (starting with '\\').
  806. const char* skip_escaped_c(const char* p) {
  807. if( p && *p == '\\' ) {
  808. ++p;
  809. switch( *p ) {
  810. case 'x':// hexadecimal
  811. ++p;
  812. while( ISXDIGIT(*p) )
  813. ++p;
  814. break;
  815. case '0':
  816. case '1':
  817. case '2':
  818. case '3':// octal
  819. ++p;
  820. if( *p >= '0' && *p <= '7' )
  821. ++p;
  822. if( *p >= '0' && *p <= '7' )
  823. ++p;
  824. break;
  825. default:
  826. if( *p && strchr(SV_ESCAPE_C_SUPPORTED, *p) )
  827. ++p;
  828. }
  829. }
  830. return p;
  831. }
  832. /// Opens and parses a file containing delim-separated columns, feeding them to the specified callback function row by row.
  833. /// Tracks the progress of the operation (current line number, number of successfully processed rows).
  834. /// Returns 'true' if it was able to process the specified file, or 'false' if it could not be read.
  835. ///
  836. /// @param directory Directory
  837. /// @param filename File to process
  838. /// @param delim Field delimiter
  839. /// @param mincols Minimum number of columns of a valid row
  840. /// @param maxcols Maximum number of columns of a valid row
  841. /// @param parseproc User-supplied row processing function
  842. /// @return true on success, false if file could not be opened
  843. 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)) {
  844. FILE* fp;
  845. int lines = 0;
  846. int entries = 0;
  847. char** fields; // buffer for fields ([0] is reserved)
  848. int columns, fields_length;
  849. char path[1024], line[1024];
  850. snprintf(path, sizeof(path), "%s/%s", directory, filename);
  851. // open file
  852. if( (fp = fopen(path, "r")) == NULL ) {
  853. ShowError("sv_readdb: can't read %s\n", path);
  854. return false;
  855. }
  856. // allocate enough memory for the maximum requested amount of columns plus the reserved one
  857. fields_length = maxcols+1;
  858. fields = (char**)aMalloc(fields_length*sizeof(char*));
  859. // process rows one by one
  860. while( fgets(line, sizeof(line), fp) ) {
  861. char *match;
  862. lines++;
  863. if ((match = strstr(line, "//") ) != NULL) {
  864. // strip comments
  865. match[0] = 0;
  866. }
  867. //TODO: strip trailing whitespace
  868. if( line[0] == '\0' || line[0] == '\n' || line[0] == '\r')
  869. continue;
  870. columns = sv_split(line, (int)strlen(line), 0, delim, fields, fields_length, (e_svopt)(SV_TERMINATE_LF|SV_TERMINATE_CRLF));
  871. if( columns < mincols ) {
  872. ShowError("sv_readdb: Insufficient columns in line %d of \"%s\" (found %d, need at least %d).\n", lines, path, columns, mincols);
  873. continue; // not enough columns
  874. }
  875. if( columns > maxcols ) {
  876. ShowError("sv_readdb: Too many columns in line %d of \"%s\" (found %d, maximum is %d).\n", lines, path, columns, maxcols );
  877. continue; // too many columns
  878. }
  879. if( entries == maxrows ) {
  880. ShowError("sv_readdb: Reached the maximum allowed number of entries (%d) when parsing file \"%s\".\n", maxrows, path);
  881. break;
  882. }
  883. // parse this row
  884. if( !parseproc(fields+1, columns, entries) ) {
  885. ShowError("sv_readdb: Could not process contents of line %d of \"%s\".\n", lines, path);
  886. continue; // invalid row contents
  887. }
  888. // success!
  889. entries++;
  890. }
  891. aFree(fields);
  892. fclose(fp);
  893. ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", entries, path);
  894. return true;
  895. }
  896. /////////////////////////////////////////////////////////////////////
  897. // StringBuf - dynamic string
  898. //
  899. // @author MouseJstr (original)
  900. /// Allocates a StringBuf
  901. StringBuf* StringBuf_Malloc(void) {
  902. StringBuf* self;
  903. CREATE(self, StringBuf, 1);
  904. StrBuf->Init(self);
  905. return self;
  906. }
  907. /// Initializes a previously allocated StringBuf
  908. void StringBuf_Init(StringBuf* self) {
  909. self->max_ = 1024;
  910. self->ptr_ = self->buf_ = (char*)aMalloc(self->max_ + 1);
  911. }
  912. /// Appends the result of printf to the StringBuf
  913. int StringBuf_Printf(StringBuf *self, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
  914. int StringBuf_Printf(StringBuf *self, const char *fmt, ...) {
  915. int len;
  916. va_list ap;
  917. va_start(ap, fmt);
  918. len = StrBuf->Vprintf(self, fmt, ap);
  919. va_end(ap);
  920. return len;
  921. }
  922. /// Appends the result of vprintf to the StringBuf
  923. int StringBuf_Vprintf(StringBuf* self, const char* fmt, va_list ap) {
  924. for(;;) {
  925. va_list apcopy;
  926. int n, off;
  927. /* Try to print in the allocated space. */
  928. size_t size = self->max_ - (self->ptr_ - self->buf_);
  929. va_copy(apcopy, ap);
  930. n = vsnprintf(self->ptr_, size, fmt, apcopy);
  931. va_end(apcopy);
  932. /* If that worked, return the length. */
  933. if( n > -1 && (size_t)n < size ) {
  934. self->ptr_ += n;
  935. return (int)(self->ptr_ - self->buf_);
  936. }
  937. /* Else try again with more space. */
  938. self->max_ *= 2; // twice the old size
  939. off = (int)(self->ptr_ - self->buf_);
  940. self->buf_ = (char*)aRealloc(self->buf_, self->max_ + 1);
  941. self->ptr_ = self->buf_ + off;
  942. }
  943. }
  944. /// Appends the contents of another StringBuf to the StringBuf
  945. int StringBuf_Append(StringBuf* self, const StringBuf* sbuf) {
  946. size_t available = self->max_ - (self->ptr_ - self->buf_);
  947. size_t needed = sbuf->ptr_ - sbuf->buf_;
  948. if( needed >= available ) {
  949. size_t off = (self->ptr_ - self->buf_);
  950. self->max_ += needed;
  951. self->buf_ = (char*)aRealloc(self->buf_, self->max_ + 1);
  952. self->ptr_ = self->buf_ + off;
  953. }
  954. memcpy(self->ptr_, sbuf->buf_, needed);
  955. self->ptr_ += needed;
  956. return (int)(self->ptr_ - self->buf_);
  957. }
  958. // Appends str to the StringBuf
  959. int StringBuf_AppendStr(StringBuf* self, const char* str) {
  960. size_t available = self->max_ - (self->ptr_ - self->buf_);
  961. size_t needed = strlen(str);
  962. if( needed >= available ) {
  963. // not enough space, expand the buffer (minimum expansion = 1024)
  964. size_t off = (self->ptr_ - self->buf_);
  965. self->max_ += max(needed, 1024);
  966. self->buf_ = (char*)aRealloc(self->buf_, self->max_ + 1);
  967. self->ptr_ = self->buf_ + off;
  968. }
  969. memcpy(self->ptr_, str, needed);
  970. self->ptr_ += needed;
  971. return (int)(self->ptr_ - self->buf_);
  972. }
  973. // Returns the length of the data in the Stringbuf
  974. int StringBuf_Length(StringBuf* self) {
  975. return (int)(self->ptr_ - self->buf_);
  976. }
  977. /// Returns the data in the StringBuf
  978. char* StringBuf_Value(StringBuf* self) {
  979. *self->ptr_ = '\0';
  980. return self->buf_;
  981. }
  982. /// Clears the contents of the StringBuf
  983. void StringBuf_Clear(StringBuf* self) {
  984. self->ptr_ = self->buf_;
  985. }
  986. /// Destroys the StringBuf
  987. void StringBuf_Destroy(StringBuf* self) {
  988. aFree(self->buf_);
  989. self->ptr_ = self->buf_ = 0;
  990. self->max_ = 0;
  991. }
  992. // Frees a StringBuf returned by StringBuf_Malloc
  993. void StringBuf_Free(StringBuf* self) {
  994. StrBuf->Destroy(self);
  995. aFree(self);
  996. }
  997. void strlib_defaults(void) {
  998. /* connect */
  999. strlib = &strlib_s;
  1000. StrBuf = &stringbuf_s;
  1001. sv = &sv_s;
  1002. /* link~u! */
  1003. strlib->jstrescape = jstrescape;
  1004. strlib->jstrescapecpy = jstrescapecpy;
  1005. strlib->jmemescapecpy = jmemescapecpy;
  1006. strlib->remove_control_chars_ = strlib_remove_control_chars;
  1007. strlib->trim_ = strlib_trim;
  1008. strlib->normalize_name_ = strlib_normalize_name;
  1009. strlib->stristr_ = strlib_stristr;
  1010. #if !(defined(WIN32) && defined(_MSC_VER) && _MSC_VER >= 1400) && !defined(HAVE_STRNLEN)
  1011. strlib->strnlen_ = strlib_strnlen;
  1012. #else
  1013. strlib->strnlen_ = NULL;
  1014. #endif
  1015. #ifdef WIN32
  1016. strlib->strtok_r_ = strlib_strtok_r;
  1017. #else
  1018. strlib->strtok_r_ = NULL;
  1019. #endif
  1020. strlib->e_mail_check_ = strlib_e_mail_check;
  1021. strlib->config_switch_ = strlib_config_switch;
  1022. strlib->safestrncpy_ = strlib_safestrncpy;
  1023. strlib->safestrnlen_ = strlib_safestrnlen;
  1024. strlib->safesnprintf_ = strlib_safesnprintf;
  1025. strlib->strline_ = strlib_strline;
  1026. strlib->bin2hex_ = strlib_bin2hex;
  1027. StrBuf->Malloc = StringBuf_Malloc;
  1028. StrBuf->Init = StringBuf_Init;
  1029. StrBuf->Printf = StringBuf_Printf;
  1030. StrBuf->Vprintf = StringBuf_Vprintf;
  1031. StrBuf->Append = StringBuf_Append;
  1032. StrBuf->AppendStr = StringBuf_AppendStr;
  1033. StrBuf->Length = StringBuf_Length;
  1034. StrBuf->Value = StringBuf_Value;
  1035. StrBuf->Clear = StringBuf_Clear;
  1036. StrBuf->Destroy = StringBuf_Destroy;
  1037. StrBuf->Free = StringBuf_Free;
  1038. sv->parse_next = sv_parse_next;
  1039. sv->parse = sv_parse;
  1040. sv->split = sv_split;
  1041. sv->escape_c = sv_escape_c;
  1042. sv->unescape_c = sv_unescape_c;
  1043. sv->skip_escaped_c = skip_escaped_c;
  1044. sv->readdb = sv_readdb;
  1045. }