stdstring.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. /* Copyright (C) 2010-2018 The RetroArch team
  2. *
  3. * ---------------------------------------------------------------------------------------
  4. * The following license statement only applies to this file (stdstring.c).
  5. * ---------------------------------------------------------------------------------------
  6. *
  7. * Permission is hereby granted, free of charge,
  8. * to any person obtaining a copy of this software and associated documentation files (the "Software"),
  9. * to deal in the Software without restriction, including without limitation the rights to
  10. * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
  11. * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  16. * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  18. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  19. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. */
  22. #include <stdint.h>
  23. #include <ctype.h>
  24. #include <string/stdstring.h>
  25. #include <encodings/utf.h>
  26. char *string_to_upper(char *s)
  27. {
  28. char *cs = (char *)s;
  29. for ( ; *cs != '\0'; cs++)
  30. *cs = toupper((unsigned char)*cs);
  31. return s;
  32. }
  33. char *string_to_lower(char *s)
  34. {
  35. char *cs = (char *)s;
  36. for ( ; *cs != '\0'; cs++)
  37. *cs = tolower((unsigned char)*cs);
  38. return s;
  39. }
  40. char *string_ucwords(char *s)
  41. {
  42. char *cs = (char *)s;
  43. for ( ; *cs != '\0'; cs++)
  44. {
  45. if (*cs == ' ')
  46. *(cs+1) = toupper((unsigned char)*(cs+1));
  47. }
  48. s[0] = toupper((unsigned char)s[0]);
  49. return s;
  50. }
  51. char *string_replace_substring(const char *in,
  52. const char *pattern, const char *replacement)
  53. {
  54. size_t numhits, pattern_len, replacement_len, outlen;
  55. const char *inat = NULL;
  56. const char *inprev = NULL;
  57. char *out = NULL;
  58. char *outat = NULL;
  59. /* if either pattern or replacement is NULL,
  60. * duplicate in and let caller handle it. */
  61. if (!pattern || !replacement)
  62. return strdup(in);
  63. pattern_len = strlen(pattern);
  64. replacement_len = strlen(replacement);
  65. numhits = 0;
  66. inat = in;
  67. while ((inat = strstr(inat, pattern)))
  68. {
  69. inat += pattern_len;
  70. numhits++;
  71. }
  72. outlen = strlen(in) - pattern_len*numhits + replacement_len*numhits;
  73. out = (char *)malloc(outlen+1);
  74. if (!out)
  75. return NULL;
  76. outat = out;
  77. inat = in;
  78. inprev = in;
  79. while ((inat = strstr(inat, pattern)))
  80. {
  81. memcpy(outat, inprev, inat-inprev);
  82. outat += inat-inprev;
  83. memcpy(outat, replacement, replacement_len);
  84. outat += replacement_len;
  85. inat += pattern_len;
  86. inprev = inat;
  87. }
  88. strcpy(outat, inprev);
  89. return out;
  90. }
  91. /* Remove leading whitespaces */
  92. char *string_trim_whitespace_left(char *const s)
  93. {
  94. if(s && *s)
  95. {
  96. size_t len = strlen(s);
  97. char *current = s;
  98. while(*current && isspace((unsigned char)*current))
  99. {
  100. ++current;
  101. --len;
  102. }
  103. if(s != current)
  104. memmove(s, current, len + 1);
  105. }
  106. return s;
  107. }
  108. /* Remove trailing whitespaces */
  109. char *string_trim_whitespace_right(char *const s)
  110. {
  111. if(s && *s)
  112. {
  113. size_t len = strlen(s);
  114. char *current = s + len - 1;
  115. while(current != s && isspace((unsigned char)*current))
  116. {
  117. --current;
  118. --len;
  119. }
  120. current[isspace((unsigned char)*current) ? 0 : 1] = '\0';
  121. }
  122. return s;
  123. }
  124. /* Remove leading and trailing whitespaces */
  125. char *string_trim_whitespace(char *const s)
  126. {
  127. string_trim_whitespace_right(s); /* order matters */
  128. string_trim_whitespace_left(s);
  129. return s;
  130. }
  131. char *word_wrap(char* buffer, const char *string, int line_width, bool unicode, unsigned max_lines)
  132. {
  133. unsigned i = 0;
  134. unsigned len = (unsigned)strlen(string);
  135. unsigned lines = 1;
  136. while (i < len)
  137. {
  138. unsigned counter;
  139. int pos = (int)(&buffer[i] - buffer);
  140. /* copy string until the end of the line is reached */
  141. for (counter = 1; counter <= (unsigned)line_width; counter++)
  142. {
  143. const char *character;
  144. unsigned char_len;
  145. unsigned j = i;
  146. /* check if end of string reached */
  147. if (i == len)
  148. {
  149. buffer[i] = 0;
  150. return buffer;
  151. }
  152. character = utf8skip(&string[i], 1);
  153. char_len = (unsigned)(character - &string[i]);
  154. if (!unicode)
  155. counter += char_len - 1;
  156. do
  157. {
  158. buffer[i] = string[i];
  159. char_len--;
  160. i++;
  161. } while(char_len);
  162. /* check for newlines embedded in the original input
  163. * and reset the index */
  164. if (buffer[j] == '\n')
  165. {
  166. lines++;
  167. counter = 1;
  168. }
  169. }
  170. /* check for whitespace */
  171. if (string[i] == ' ')
  172. {
  173. if ((max_lines == 0 || lines < max_lines))
  174. {
  175. buffer[i] = '\n';
  176. i++;
  177. lines++;
  178. }
  179. }
  180. else
  181. {
  182. int k;
  183. /* check for nearest whitespace back in string */
  184. for (k = i; k > 0; k--)
  185. {
  186. if (string[k] != ' ' || (max_lines != 0 && lines >= max_lines))
  187. continue;
  188. buffer[k] = '\n';
  189. /* set string index back to character after this one */
  190. i = k + 1;
  191. lines++;
  192. break;
  193. }
  194. if (&buffer[i] - buffer == pos)
  195. return buffer;
  196. }
  197. }
  198. buffer[i] = 0;
  199. return buffer;
  200. }
  201. /* Splits string into tokens seperated by 'delim'
  202. * > Returned token string must be free()'d
  203. * > Returns NULL if token is not found
  204. * > After each call, 'str' is set to the position after the
  205. * last found token
  206. * > Tokens *include* empty strings
  207. * Usage example:
  208. * char *str = "1,2,3,4,5,6,7,,,10,";
  209. * char **str_ptr = &str;
  210. * char *token = NULL;
  211. * while((token = string_tokenize(str_ptr, ",")))
  212. * {
  213. * printf("%s\n", token);
  214. * free(token);
  215. * token = NULL;
  216. * }
  217. */
  218. char* string_tokenize(char **str, const char *delim)
  219. {
  220. /* Taken from https://codereview.stackexchange.com/questions/216956/strtok-function-thread-safe-supports-empty-tokens-doesnt-change-string# */
  221. char *str_ptr = NULL;
  222. char *delim_ptr = NULL;
  223. char *token = NULL;
  224. size_t token_len = 0;
  225. /* Sanity checks */
  226. if (!str || string_is_empty(delim))
  227. return NULL;
  228. str_ptr = *str;
  229. /* Note: we don't check string_is_empty() here,
  230. * empty strings are valid */
  231. if (!str_ptr)
  232. return NULL;
  233. /* Search for delimiter */
  234. delim_ptr = strstr(str_ptr, delim);
  235. if (delim_ptr)
  236. token_len = delim_ptr - str_ptr;
  237. else
  238. token_len = strlen(str_ptr);
  239. /* Allocate token string */
  240. token = (char *)malloc((token_len + 1) * sizeof(char));
  241. if (!token)
  242. return NULL;
  243. /* Copy token */
  244. strlcpy(token, str_ptr, (token_len + 1) * sizeof(char));
  245. token[token_len] = '\0';
  246. /* Update input string pointer */
  247. *str = delim_ptr ? delim_ptr + strlen(delim) : NULL;
  248. return token;
  249. }
  250. /* Removes every instance of character 'c' from 'str' */
  251. void string_remove_all_chars(char *str, char c)
  252. {
  253. char *read_ptr = NULL;
  254. char *write_ptr = NULL;
  255. if (string_is_empty(str))
  256. return;
  257. read_ptr = str;
  258. write_ptr = str;
  259. while (*read_ptr != '\0')
  260. {
  261. *write_ptr = *read_ptr++;
  262. write_ptr += (*write_ptr != c) ? 1 : 0;
  263. }
  264. *write_ptr = '\0';
  265. }
  266. /* Replaces every instance of character 'find' in 'str'
  267. * with character 'replace' */
  268. void string_replace_all_chars(char *str, char find, char replace)
  269. {
  270. char *str_ptr = str;
  271. if (string_is_empty(str))
  272. return;
  273. while((str_ptr = strchr(str_ptr, find)) != NULL)
  274. *str_ptr++ = replace;
  275. }
  276. /* Converts string to unsigned integer.
  277. * Returns 0 if string is invalid */
  278. unsigned string_to_unsigned(const char *str)
  279. {
  280. const char *ptr = NULL;
  281. if (string_is_empty(str))
  282. return 0;
  283. for (ptr = str; *ptr != '\0'; ptr++)
  284. {
  285. if (!isdigit(*ptr))
  286. return 0;
  287. }
  288. return (unsigned)strtoul(str, NULL, 10);
  289. }
  290. /* Converts hexadecimal string to unsigned integer.
  291. * Handles optional leading '0x'.
  292. * Returns 0 if string is invalid */
  293. unsigned string_hex_to_unsigned(const char *str)
  294. {
  295. const char *hex_str = str;
  296. const char *ptr = NULL;
  297. size_t len;
  298. if (string_is_empty(str))
  299. return 0;
  300. /* Remove leading '0x', if required */
  301. len = strlen(str);
  302. if (len >= 2)
  303. if ((str[0] == '0') &&
  304. ((str[1] == 'x') || (str[1] == 'X')))
  305. hex_str = str + 2;
  306. if (string_is_empty(hex_str))
  307. return 0;
  308. /* Check for valid characters */
  309. for (ptr = hex_str; *ptr != '\0'; ptr++)
  310. {
  311. if (!isxdigit(*ptr))
  312. return 0;
  313. }
  314. return (unsigned)strtoul(hex_str, NULL, 16);
  315. }