str.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795
  1. /** @file str.c
  2. * @brief String related functions
  3. *
  4. * This file implements some often used string functions.
  5. * Some functions are more portable versions of standard
  6. * functions but others are original ones.
  7. */
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <assert.h>
  11. #include <stdio.h>
  12. #include <stdarg.h>
  13. #include "faux/ctype.h"
  14. #include "faux/str.h"
  15. /** @brief Free the memory allocated for the string.
  16. *
  17. * Safely free the memory allocated for the string. You can use NULL
  18. * pointer with this function. POSIX's free() checks for the NULL pointer
  19. * but not all systems do so.
  20. *
  21. * @param [in] str String to free
  22. */
  23. void faux_str_free(char *str)
  24. {
  25. faux_free(str);
  26. }
  27. /** @brief Duplicates the string.
  28. *
  29. * Duplicates the string. Same as standard strdup() function. Allocates
  30. * memory with malloc(). Checks for NULL pointer.
  31. *
  32. * @warning Resulting string must be freed by faux_str_free().
  33. *
  34. * @param [in] str String to duplicate.
  35. * @return Pointer to allocated string or NULL.
  36. */
  37. char *faux_str_dup(const char *str)
  38. {
  39. if (!str)
  40. return NULL;
  41. return strdup(str);
  42. }
  43. /** @brief Duplicates the first n bytes of the string.
  44. *
  45. * Duplicates at most n bytes of the string. Allocates
  46. * memory with malloc(). Checks for NULL pointer. Function will allocate
  47. * n + 1 bytes to store string and terminating null byte.
  48. *
  49. * @warning Resulting string must be freed by faux_str_free().
  50. *
  51. * @param [in] str String to duplicate.
  52. * @param [in] n Number of bytes to copy.
  53. * @return Pointer to allocated string or NULL.
  54. */
  55. char *faux_str_dupn(const char *str, size_t n)
  56. {
  57. char *res = NULL;
  58. size_t len = 0;
  59. if (!str)
  60. return NULL;
  61. len = strlen(str);
  62. len = (len < n) ? len : n;
  63. res = faux_zmalloc(len + 1);
  64. if (!res)
  65. return NULL;
  66. strncpy(res, str, len);
  67. res[len] = '\0';
  68. return res;
  69. }
  70. /** @brief Generates lowercase copy of input string.
  71. *
  72. * Allocates the copy of input string and convert that copy to lowercase.
  73. *
  74. * @warning Resulting string must be freed by faux_str_free().
  75. *
  76. * @param [in] str String to convert.
  77. * @return Pointer to lowercase string copy or NULL.
  78. */
  79. char *faux_str_tolower(const char *str)
  80. {
  81. char *res = faux_str_dup(str);
  82. char *p = res;
  83. if (!res)
  84. return NULL;
  85. while (*p) {
  86. *p = faux_ctype_tolower(*p);
  87. p++;
  88. }
  89. return res;
  90. }
  91. /** @brief Generates uppercase copy of input string.
  92. *
  93. * Allocates the copy of input string and convert that copy to uppercase.
  94. *
  95. * @warning Resulting string must be freed by faux_str_free().
  96. *
  97. * @param [in] str String to convert.
  98. * @return Pointer to lowercase string copy or NULL.
  99. */
  100. char *faux_str_toupper(const char *str)
  101. {
  102. char *res = faux_str_dup(str);
  103. char *p = res;
  104. if (!res)
  105. return NULL;
  106. while (*p) {
  107. *p = faux_ctype_toupper(*p);
  108. p++;
  109. }
  110. return res;
  111. }
  112. /** @brief Add n bytes of text to existent string.
  113. *
  114. * Concatenate two strings. Add n bytes of second string to the end of the
  115. * first one. The first argument is address of string pointer. The pointer
  116. * can be changed due to realloc() features. The first pointer can be NULL.
  117. * In this case the memory will be malloc()-ed and stored to the first pointer.
  118. *
  119. * @param [in,out] str Address of first string pointer.
  120. * @param [in] text Text to add to the first string.
  121. * @param [in] n Number of bytes to add.
  122. * @return Pointer to resulting string or NULL.
  123. */
  124. char *faux_str_catn(char **str, const char *text, size_t n)
  125. {
  126. size_t str_len = 0;
  127. size_t text_len = 0;
  128. char *res = NULL;
  129. char *p = NULL;
  130. if (!text)
  131. return *str;
  132. str_len = (*str) ? strlen(*str) : 0;
  133. text_len = strlen(text);
  134. text_len = (text_len < n) ? text_len : n;
  135. res = realloc(*str, str_len + text_len + 1);
  136. if (!res)
  137. return NULL;
  138. p = res + str_len;
  139. strncpy(p, text, text_len);
  140. p[text_len] = '\0';
  141. *str = res;
  142. return res;
  143. }
  144. /** @brief Add some text to existent string.
  145. *
  146. * Concatenate two strings. Add second string to the end of the first one.
  147. * The first argument is address of string pointer. The pointer can be
  148. * changed due to realloc() features. The first pointer can be NULL. In this
  149. * case the memory will be malloc()-ed and stored to the first pointer.
  150. *
  151. * @param [in,out] str Address of first string pointer.
  152. * @param [in] text Text to add to the first string.
  153. * @return Pointer to resulting string or NULL.
  154. */
  155. char *faux_str_cat(char **str, const char *text)
  156. {
  157. size_t len = 0;
  158. if (!text)
  159. return *str;
  160. len = strlen(text);
  161. return faux_str_catn(str, text, len);
  162. }
  163. /** @brief Add multiply text strings to existent string.
  164. *
  165. * Concatenate multiply strings. Add next string to the end of the previous one.
  166. * The first argument is address of string pointer. The pointer can be
  167. * changed due to realloc() features. The first pointer can be NULL. In this
  168. * case the memory will be malloc()-ed and stored to the first pointer.
  169. * The last argument must be 'NULL'. It marks the last argument within
  170. * variable arguments list.
  171. *
  172. * @warning If last argument is not 'NULL' then behaviour is undefined.
  173. *
  174. * @param [in,out] str Address of first string pointer.
  175. * @param [in] text Text to add to the first string.
  176. * @return Pointer to resulting string or NULL.
  177. */
  178. char *faux_str_vcat(char **str, ...)
  179. {
  180. va_list ap;
  181. const char *arg = NULL;
  182. char *retval = *str;
  183. va_start(ap, str);
  184. while ((arg = va_arg(ap, const char *))) {
  185. retval = faux_str_cat(str, arg);
  186. }
  187. va_end(ap);
  188. return retval;
  189. }
  190. /** @brief Allocates memory and sprintf() to it.
  191. *
  192. * Function tries to find out necessary amount of memory for specified format
  193. * string and arguments. Format is same as for sprintf() function. Then
  194. * function allocates memory for resulting string and sprintf() to it. So
  195. * user doesn't need to allocate buffer himself. Function returns allocated
  196. * string that need to be freed by faux_str_free() function later.
  197. *
  198. * @warning The returned pointer must be free by faux_str_free().
  199. *
  200. * @param [in] fmt Format string like the sprintf()'s fmt.
  201. * @param [in] arg Number of arguments.
  202. * @return Allocated resulting string or NULL on error.
  203. */
  204. char *faux_str_sprintf(const char *fmt, ...)
  205. {
  206. int size = 1;
  207. char calc_buf[1] = "";
  208. char *line = NULL;
  209. va_list ap;
  210. // Calculate buffer size
  211. va_start(ap, fmt);
  212. size = vsnprintf(calc_buf, size, fmt, ap);
  213. va_end(ap);
  214. // The snprintf() prior to 2.0.6 glibc version returns -1 if string
  215. // was truncated. The later glibc returns required buffer size.
  216. // The calc_buf can be NULL and size can be 0 for recent glibc but
  217. // probably some exotic implementations can break on it. So use
  218. // minimal buffer with length = 1.
  219. if (size < 0)
  220. return NULL;
  221. size++; // Additional byte for '\0'
  222. line = faux_zmalloc(size);
  223. if (!line) // Memory problems
  224. return NULL;
  225. // Format real string
  226. va_start(ap, fmt);
  227. size = vsnprintf(line, size, fmt, ap);
  228. va_end(ap);
  229. if (size < 0) { // Some problems
  230. faux_str_free(line);
  231. return NULL;
  232. }
  233. return line;
  234. }
  235. /** @brief Service function to compare to chars in right way.
  236. *
  237. * The problem is char type can be signed or unsigned on different
  238. * platforms. So stright comparision can return different results.
  239. *
  240. * @param [in] char1 First char
  241. * @param [in] char2 Second char
  242. * @return
  243. * < 0 if char1 < char2
  244. * = 0 if char1 = char2
  245. * > 0 if char1 > char2
  246. */
  247. static int faux_str_cmp_chars(char char1, char char2)
  248. {
  249. unsigned char ch1 = (unsigned char)char1;
  250. unsigned char ch2 = (unsigned char)char2;
  251. return (int)ch1 - (int)ch2;
  252. }
  253. /** @brief Compare n first characters of two strings ignoring case.
  254. *
  255. * The difference beetween this function an standard strncasecmp() is
  256. * faux function uses faux ctype functions. It can be important for
  257. * portability.
  258. *
  259. * @param [in] str1 First string to compare.
  260. * @param [in] str2 Second string to compare.
  261. * @param [in] n Number of characters to compare.
  262. * @return < 0, 0, > 0, see the strcasecmp().
  263. */
  264. int faux_str_casecmpn(const char *str1, const char *str2, size_t n)
  265. {
  266. const char *p1 = str1;
  267. const char *p2 = str2;
  268. size_t num = n;
  269. while (*p1 != '\0' && *p2 != '\0' && num != 0) {
  270. int res = faux_str_cmp_chars(
  271. faux_ctype_tolower(*p1), faux_ctype_tolower(*p2));
  272. if (res != 0)
  273. return res;
  274. p1++;
  275. p2++;
  276. num--;
  277. }
  278. if (0 == n) // It means n first characters are equal.
  279. return 0;
  280. return faux_str_cmp_chars(
  281. faux_ctype_tolower(*p1), faux_ctype_tolower(*p2));
  282. }
  283. /** @brief Compare two strings ignoring case.
  284. *
  285. * The difference beetween this function an standard strcasecmp() is
  286. * faux function uses faux ctype functions. It can be important for
  287. * portability.
  288. *
  289. * @param [in] str1 First string to compare.
  290. * @param [in] str2 Second string to compare.
  291. * @return < 0, 0, > 0, see the strcasecmp().
  292. */
  293. int faux_str_casecmp(const char *str1, const char *str2)
  294. {
  295. const char *p1 = str1;
  296. const char *p2 = str2;
  297. while (*p1 != '\0' && *p2 != '\0') {
  298. int res = faux_str_cmp_chars(
  299. faux_ctype_tolower(*p1), faux_ctype_tolower(*p2));
  300. if (res != 0)
  301. return res;
  302. p1++;
  303. p2++;
  304. }
  305. return faux_str_cmp_chars(
  306. faux_ctype_tolower(*p1), faux_ctype_tolower(*p2));
  307. }
  308. /** @brief Finds the first occurrence of the substring in the string
  309. *
  310. * Function is a faux version of strcasestr() function.
  311. *
  312. * @param [in] haystack String to find substring in it.
  313. * @param [in] needle Substring to find.
  314. * @return
  315. * Pointer to first occurence of substring in the string.
  316. * NULL on error
  317. */
  318. char *faux_str_casestr(const char *haystack, const char *needle)
  319. {
  320. const char *ptr = haystack;
  321. size_t ptr_len = 0;
  322. size_t needle_len = 0;
  323. assert(haystack);
  324. assert(needle);
  325. if (!haystack || !needle)
  326. return NULL;
  327. ptr_len = strlen(haystack);
  328. needle_len = strlen(needle);
  329. while ((*ptr != '\0') && (ptr_len >= needle_len)) {
  330. int res = faux_str_casecmpn(ptr, needle, needle_len);
  331. if (0 == res)
  332. return (char *)ptr;
  333. ptr++;
  334. ptr_len--;
  335. }
  336. return NULL; // Not found
  337. }
  338. /** Prepare string for embedding to C-code (make escaping).
  339. *
  340. * @warning The returned pointer must be freed by faux_str_free().
  341. * @param [in] src String for escaping.
  342. * @return Escaped string or NULL on error.
  343. */
  344. char *faux_str_c_esc(const char *src)
  345. {
  346. const char *src_ptr = src;
  347. char *dst = NULL;
  348. char *dst_ptr = NULL;
  349. char *escaped = NULL;
  350. size_t src_len = 0;
  351. size_t dst_len = 0;
  352. assert(src);
  353. if (!src)
  354. return NULL;
  355. src_len = strlen(src);
  356. // Calculate max destination string size.
  357. // The worst case is when each src character will be replaced by
  358. // something like '\xff'. So it's 4 dst chars for 1 src one.
  359. dst_len = (src_len * 4) + 1; // one byte for '\0'
  360. dst = faux_zmalloc(dst_len);
  361. assert(dst);
  362. if (!dst)
  363. return NULL;
  364. dst_ptr = dst;
  365. while (*src_ptr != '\0') {
  366. char *esc = NULL; // escaped replacement
  367. char buf[5]; // longest 'char' (4 bytes) + '\0'
  368. size_t len = 0;
  369. switch (*src_ptr) {
  370. case '\n':
  371. esc = "\\n";
  372. break;
  373. case '\"':
  374. esc = "\\\"";
  375. break;
  376. case '\\':
  377. esc = "\\\\";
  378. break;
  379. case '\'':
  380. esc = "\\\'";
  381. break;
  382. case '\r':
  383. esc = "\\r";
  384. break;
  385. case '\t':
  386. esc = "\\t";
  387. break;
  388. default:
  389. // Check is the symbol control character. Control
  390. // characters has codes from 0x00 to 0x1f.
  391. if (((unsigned char)*src_ptr & 0xe0) == 0) { // control
  392. snprintf(buf, sizeof(buf), "\\x%02x",
  393. (unsigned char)*src_ptr);
  394. buf[4] = '\0'; // for safety
  395. } else {
  396. buf[0] = *src_ptr; // Common character
  397. buf[1] = '\0';
  398. }
  399. esc = buf;
  400. break;
  401. }
  402. len = strlen(esc);
  403. memcpy(dst_ptr, esc, len); // zmalloc() nullify the rest
  404. dst_ptr += len;
  405. src_ptr++;
  406. }
  407. escaped = faux_str_dup(dst); // Free some memory
  408. faux_str_free(dst); // 'dst' size >= 'escaped' size
  409. return escaped;
  410. }
  411. #define BYTE_CONV_LEN 4 // Length of one byte converted to string
  412. /** Prepare binary block for embedding to C-code.
  413. *
  414. * @warning The returned pointer must be freed by faux_str_free().
  415. * @param [in] src Binary block for conversion.
  416. * @return C-string or NULL on error.
  417. */
  418. char *faux_str_c_bin(const char *src, size_t n)
  419. {
  420. const char *src_ptr = src;
  421. char *dst = NULL;
  422. char *dst_ptr = NULL;
  423. size_t dst_len = 0;
  424. assert(src);
  425. if (!src)
  426. return NULL;
  427. // Calculate destination string size.
  428. // Each src character will be replaced by
  429. // something like '\xff'. So it's 4 dst chars for 1 src char.
  430. dst_len = (n * BYTE_CONV_LEN) + 1; // one byte for '\0'
  431. dst = faux_zmalloc(dst_len);
  432. assert(dst);
  433. if (!dst)
  434. return NULL;
  435. dst_ptr = dst;
  436. while (src_ptr < (src + n)) {
  437. char buf[BYTE_CONV_LEN + 1]; // longest 'char' (4 bytes) + '\0'
  438. snprintf(buf, sizeof(buf), "\\x%02x", (unsigned char)*src_ptr);
  439. memcpy(dst_ptr, buf, BYTE_CONV_LEN); // zmalloc() nullify the rest
  440. dst_ptr += BYTE_CONV_LEN;
  441. src_ptr++;
  442. }
  443. return dst;
  444. }
  445. /** @brief Search the n-th chars of string for one of the specified chars.
  446. *
  447. * The function search for any of specified characters within string.
  448. * The search is limited to first n characters of the string. If
  449. * terminating '\0' is before n-th character then search will stop on
  450. * it. Can be used with raw memory block.
  451. *
  452. * @param [in] str String (or memory block) to search in.
  453. * @param [in] chars_to_string Chars enumeration to search for.
  454. * @param [in] n Maximum number of bytes to search within.
  455. * @return Pointer to the first occurence of one of specified chars.
  456. * NULL on error.
  457. */
  458. char *faux_str_charsn(const char *str, const char *chars_to_search, size_t n)
  459. {
  460. const char *current_char = str;
  461. size_t len = n;
  462. assert(str);
  463. assert(chars_to_search);
  464. if (!str || !chars_to_search)
  465. return NULL;
  466. while ((*current_char != '\0') && (len > 0)) {
  467. if (strchr(chars_to_search, *current_char))
  468. return (char *)current_char;
  469. current_char++;
  470. len--;
  471. }
  472. return NULL;
  473. }
  474. /** @brief Search string for one of the specified chars.
  475. *
  476. * The function search for any of specified characters within string.
  477. *
  478. * @param [in] str String to search in.
  479. * @param [in] chars_to_string Chars enumeration to search for.
  480. * @return Pointer to the first occurence of one of specified chars.
  481. * NULL on error.
  482. */
  483. char *faux_str_chars(const char *str, const char *chars_to_search)
  484. {
  485. assert(str);
  486. if (!str)
  487. return NULL;
  488. return faux_str_charsn(str, chars_to_search, strlen(str));
  489. }
  490. /** @brief Remove escaping. Convert string to internal view.
  491. *
  492. * Find backslashes (before escaped symbols) and remove it. Escaped symbol
  493. * will not be analyzed so `\\` will lead to `\`.
  494. *
  495. * @param [in] string Escaped string.
  496. * @param [in] len Length of string to de-escape.
  497. * @return Allocated de-escaped string
  498. * @warning Returned value must be freed by faux_str_free() later.
  499. */
  500. static char *faux_str_deesc(const char *string, size_t len)
  501. {
  502. const char *s = string;
  503. char *res = NULL;
  504. char *p = NULL;
  505. bool_t escaped = BOOL_FALSE;
  506. assert(string);
  507. if (!string)
  508. return NULL;
  509. if (0 == len)
  510. return NULL;
  511. res = faux_zmalloc(len + 1);
  512. assert(res);
  513. if (!res)
  514. return NULL;
  515. p = res;
  516. while ((*s != '\0') && (s < (string +len))) {
  517. if (('\\' == *s) && !escaped) {
  518. escaped = BOOL_TRUE;
  519. s++;
  520. continue;
  521. }
  522. escaped = BOOL_FALSE;
  523. *p = *s;
  524. s++;
  525. p++;
  526. }
  527. *p = '\0';
  528. return res;
  529. }
  530. /*--------------------------------------------------------- */
  531. /** @brief Find next word or quoted substring within string
  532. *
  533. * The quotation can be of several different kinds.
  534. *
  535. * The first kind is standard double quoting. In this case the internal (within
  536. * quotation) `"` and `\` symbols must be escaped. But symbols will be deescaped
  537. * before writing to internal buffers.
  538. *
  539. * The second kind of quotation is alternative quotation. Any symbol can become
  540. * quote sign. For example "`" and "'" can be considered as a quotes. To use
  541. * some symbols as a quote them must be specified by `alt_quotes` function
  542. * parameter. The single symbol can be considered as a start of quotation or
  543. * a sequence of the same symbols can be considered as a start of quotation. In
  544. * this case the end of quotation is a sequence of the same symbols. The same
  545. * symbol can appear inside quotation but number of symbols (sequence) must be
  546. * less than opening quote sequence. The example of alternatively quoted string
  547. * is ```some text``and anothe`r```. The backslash has no special meaning inside
  548. * quoted string.
  549. *
  550. * The substring can be unquoted string without spaces. The space, backslash and
  551. * quote can be escaped by backslash.
  552. *
  553. * Parts of text with different quotes can be glued together to get single
  554. * substring like this: aaa"inside dbl quote"bbb``alt quote"`here``ccc.
  555. *
  556. * @param [in] str String to parse.
  557. * @param [out] saveptr Pointer to first symbol after found substring.
  558. * @param [in] alt_quotes Possible alternative quotes.
  559. * @param [out] qclosed Flag is quote closed.
  560. * @return Allocated buffer with found substring (without quotes).
  561. * @warning Returned alocated buffer must be freed later by faux_str_free()
  562. */
  563. char *faux_str_nextword(const char *str, const char **saveptr,
  564. const char *alt_quotes, bool_t *qclosed)
  565. {
  566. const char *string = str;
  567. const char *word = NULL;
  568. size_t len = 0;
  569. const char dbl_quote = '"';
  570. bool_t dbl_quoted = BOOL_FALSE;
  571. char alt_quote = '\0';
  572. unsigned int alt_quote_num = 0; // Number of opening alt quotes
  573. bool_t alt_quoted = BOOL_FALSE;
  574. char *result = NULL;
  575. // Find the start of a word (not including an opening quote)
  576. while (*string && isspace(*string))
  577. string++;
  578. word = string; // Suppose not quoted string
  579. while (*string != '\0') {
  580. // Standard double quotation
  581. if (dbl_quoted) {
  582. // End of word
  583. if (*string == dbl_quote) {
  584. if (len > 0) {
  585. char *s = faux_str_deesc(word, len);
  586. faux_str_cat(&result, s);
  587. faux_str_free(s);
  588. }
  589. dbl_quoted = BOOL_FALSE;
  590. string++;
  591. word = string;
  592. len = 0;
  593. // Escaping
  594. } else if (*string == '\\') {
  595. // Skip escaping
  596. string++;
  597. len++;
  598. // Skip escaped symbol
  599. if (*string) {
  600. string++;
  601. len++;
  602. }
  603. } else {
  604. string++;
  605. len++;
  606. }
  607. // Alternative multi quotation
  608. } else if (alt_quoted) {
  609. unsigned int qnum = alt_quote_num;
  610. while (string && (*string == alt_quote) && qnum) {
  611. string++;
  612. len++;
  613. qnum--;
  614. }
  615. if (0 == qnum) { // End of word was found
  616. // Quotes themselfs are not a part of a word
  617. len -= alt_quote_num;
  618. if (len > 0)
  619. faux_str_catn(&result, word, len);
  620. alt_quoted = BOOL_FALSE;
  621. word = string;
  622. len = 0;
  623. } else if (qnum == alt_quote_num) { // No quote syms
  624. string++;
  625. len++;
  626. }
  627. // Not quoted
  628. } else {
  629. char *p = NULL;
  630. // Start of a double quoted string
  631. if (*string == dbl_quote) {
  632. if (len > 0) {
  633. char *s = faux_str_deesc(word, len);
  634. faux_str_cat(&result, s);
  635. faux_str_free(s);
  636. }
  637. dbl_quoted = BOOL_TRUE;
  638. string++;
  639. word = string;
  640. len = 0;
  641. // Start of alt quoted string
  642. } else if (alt_quotes && (p = strchr(alt_quotes, *string))) {
  643. if (len > 0) {
  644. char *s = faux_str_deesc(word, len);
  645. faux_str_cat(&result, s);
  646. faux_str_free(s);
  647. }
  648. alt_quoted = BOOL_TRUE;
  649. alt_quote = *string;
  650. alt_quote_num = 0;
  651. while (string && (*string == alt_quote)) {
  652. string++;
  653. alt_quote_num++; // Count starting quotes
  654. }
  655. word = string;
  656. len = 0;
  657. // End of word
  658. } else if (isspace(*string)) {
  659. if (len > 0) {
  660. char *s = faux_str_deesc(word, len);
  661. faux_str_cat(&result, s);
  662. faux_str_free(s);
  663. }
  664. word = string;
  665. len = 0;
  666. break;
  667. // Escaping
  668. } else if (*string == '\\') {
  669. // Skip escaping
  670. string++;
  671. len++;
  672. // Skip escaped symbol
  673. if (*string) {
  674. string++;
  675. len++;
  676. }
  677. } else {
  678. string++;
  679. len++;
  680. }
  681. }
  682. }
  683. if (len > 0) {
  684. if (alt_quoted) {
  685. faux_str_catn(&result, word, len);
  686. } else {
  687. char *s = faux_str_deesc(word, len);
  688. faux_str_cat(&result, s);
  689. faux_str_free(s);
  690. }
  691. }
  692. if (saveptr)
  693. *saveptr = string;
  694. if (qclosed)
  695. *qclosed = ! (dbl_quoted || alt_quoted);
  696. return result;
  697. }