PageRenderTime 27ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/src/core/wee-string.c

#
C | 1863 lines | 1328 code | 260 blank | 275 comment | 445 complexity | 28122a804e67be1d4cbe5745cc1fb069 MD5 | raw file
Possible License(s): GPL-3.0
  1. /*
  2. * Copyright (C) 2003-2012 Sebastien Helleu <flashcode@flashtux.org>
  3. *
  4. * This file is part of WeeChat, the extensible chat client.
  5. *
  6. * WeeChat is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * WeeChat is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with WeeChat. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. /*
  20. * wee-string.c: string functions for WeeChat
  21. */
  22. #ifdef HAVE_CONFIG_H
  23. #include "config.h"
  24. #endif
  25. #include <stdlib.h>
  26. #include <errno.h>
  27. #include <stdarg.h>
  28. #include <string.h>
  29. #include <ctype.h>
  30. #include <wctype.h>
  31. #include <regex.h>
  32. #include <wchar.h>
  33. #ifdef HAVE_ICONV
  34. #include <iconv.h>
  35. #endif
  36. #ifndef ICONV_CONST
  37. #ifdef ICONV_2ARG_IS_CONST
  38. #define ICONV_CONST const
  39. #else
  40. #define ICONV_CONST
  41. #endif
  42. #endif
  43. #include "weechat.h"
  44. #include "wee-string.h"
  45. #include "wee-config.h"
  46. #include "wee-hashtable.h"
  47. #include "wee-utf8.h"
  48. #include "../gui/gui-color.h"
  49. #include "../plugins/plugin.h"
  50. /*
  51. * string_strndup: define strndup function for systems where this function does
  52. * not exist (FreeBSD and maybe other)
  53. */
  54. char *
  55. string_strndup (const char *string, int length)
  56. {
  57. char *result;
  58. if ((int)strlen (string) < length)
  59. return strdup (string);
  60. result = malloc (length + 1);
  61. if (!result)
  62. return NULL;
  63. memcpy (result, string, length);
  64. result[length] = '\0';
  65. return result;
  66. }
  67. /*
  68. * string_tolower: locale independant string conversion to lower case
  69. */
  70. void
  71. string_tolower (char *string)
  72. {
  73. while (string && string[0])
  74. {
  75. if ((string[0] >= 'A') && (string[0] <= 'Z'))
  76. string[0] += ('a' - 'A');
  77. string = utf8_next_char (string);
  78. }
  79. }
  80. /*
  81. * string_toupper: locale independant string conversion to upper case
  82. */
  83. void
  84. string_toupper (char *string)
  85. {
  86. while (string && string[0])
  87. {
  88. if ((string[0] >= 'a') && (string[0] <= 'z'))
  89. string[0] -= ('a' - 'A');
  90. string = utf8_next_char (string);
  91. }
  92. }
  93. /*
  94. * string_strcasecmp: locale and case independent string comparison
  95. */
  96. int
  97. string_strcasecmp (const char *string1, const char *string2)
  98. {
  99. int diff;
  100. if (!string1 || !string2)
  101. return (string1) ? 1 : ((string2) ? -1 : 0);
  102. while (string1[0] && string2[0])
  103. {
  104. diff = utf8_charcasecmp (string1, string2);
  105. if (diff != 0)
  106. return diff;
  107. string1 = utf8_next_char (string1);
  108. string2 = utf8_next_char (string2);
  109. }
  110. return (string1[0]) ? 1 : ((string2[0]) ? -1 : 0);
  111. }
  112. /*
  113. * string_strcasecmp_range: locale and case independent string comparison
  114. * using range to compare case:
  115. * - range = 26: A-Z ==> a-z
  116. * - range = 29: A-Z [ \ ] ==> a-z { | }
  117. * - range = 30: A-Z [ \ ] ^ ==> a-z { | } ~
  118. * (ranges 29 and 30 are used by some protocols like
  119. * IRC)
  120. */
  121. int
  122. string_strcasecmp_range (const char *string1, const char *string2, int range)
  123. {
  124. int diff;
  125. if (!string1 || !string2)
  126. return (string1) ? 1 : ((string2) ? -1 : 0);
  127. while (string1[0] && string2[0])
  128. {
  129. diff = utf8_charcasecmp_range (string1, string2, range);
  130. if (diff != 0)
  131. return diff;
  132. string1 = utf8_next_char (string1);
  133. string2 = utf8_next_char (string2);
  134. }
  135. return (string1[0]) ? 1 : ((string2[0]) ? -1 : 0);
  136. }
  137. /*
  138. * string_strncasecmp: locale and case independent string comparison
  139. * with max length
  140. */
  141. int
  142. string_strncasecmp (const char *string1, const char *string2, int max)
  143. {
  144. int count, diff;
  145. if (!string1 || !string2)
  146. return (string1) ? 1 : ((string2) ? -1 : 0);
  147. count = 0;
  148. while ((count < max) && string1[0] && string2[0])
  149. {
  150. diff = utf8_charcasecmp (string1, string2);
  151. if (diff != 0)
  152. return diff;
  153. string1 = utf8_next_char (string1);
  154. string2 = utf8_next_char (string2);
  155. count++;
  156. }
  157. if (count >= max)
  158. return 0;
  159. else
  160. return (string1[0]) ? 1 : ((string2[0]) ? -1 : 0);
  161. }
  162. /*
  163. * string_strncasecmp_range: locale and case independent string comparison
  164. * with max length, using range to compare case:
  165. * - range = 26: A-Z ==> a-z
  166. * - range = 29: A-Z [ \ ] ==> a-z { | }
  167. * - range = 30: A-Z [ \ ] ^ ==> a-z { | } ~
  168. * (ranges 29 and 30 are used by some protocols like
  169. * IRC)
  170. */
  171. int
  172. string_strncasecmp_range (const char *string1, const char *string2, int max,
  173. int range)
  174. {
  175. int count, diff;
  176. if (!string1 || !string2)
  177. return (string1) ? 1 : ((string2) ? -1 : 0);
  178. count = 0;
  179. while ((count < max) && string1[0] && string2[0])
  180. {
  181. diff = utf8_charcasecmp_range (string1, string2, range);
  182. if (diff != 0)
  183. return diff;
  184. string1 = utf8_next_char (string1);
  185. string2 = utf8_next_char (string2);
  186. count++;
  187. }
  188. if (count >= max)
  189. return 0;
  190. else
  191. return (string1[0]) ? 1 : ((string2[0]) ? -1 : 0);
  192. }
  193. /*
  194. * string_strcmp_ignore_chars: compare 2 strings, ignoring ignore some chars
  195. */
  196. int
  197. string_strcmp_ignore_chars (const char *string1, const char *string2,
  198. const char *chars_ignored, int case_sensitive)
  199. {
  200. int diff;
  201. if (!string1 && !string2)
  202. return 0;
  203. if (!string1 && string2)
  204. return -1;
  205. if (string1 && !string2)
  206. return 1;
  207. while (string1 && string1[0] && string2 && string2[0])
  208. {
  209. /* skip ignored chars */
  210. while (string1 && string1[0] && strchr (chars_ignored, string1[0]))
  211. {
  212. string1 = utf8_next_char (string1);
  213. }
  214. while (string2 && string2[0] && strchr (chars_ignored, string2[0]))
  215. {
  216. string2 = utf8_next_char (string2);
  217. }
  218. /* end of one (or both) string(s) ? */
  219. if ((!string1 || !string1[0]) && (!string2 || !string2[0]))
  220. return 0;
  221. if ((!string1 || !string1[0]) && string2 && string2[0])
  222. return -1;
  223. if (string1 && string1[0] && (!string2 || !string2[0]))
  224. return 1;
  225. /* look at diff */
  226. diff = (case_sensitive) ?
  227. (int)string1[0] - (int)string2[0] : utf8_charcasecmp (string1, string2);
  228. if (diff != 0)
  229. return diff;
  230. string1 = utf8_next_char (string1);
  231. string2 = utf8_next_char (string2);
  232. /* skip ignored chars */
  233. while (string1 && string1[0] && strchr (chars_ignored, string1[0]))
  234. {
  235. string1 = utf8_next_char (string1);
  236. }
  237. while (string2 && string2[0] && strchr (chars_ignored, string2[0]))
  238. {
  239. string2 = utf8_next_char (string2);
  240. }
  241. }
  242. if ((!string1 || !string1[0]) && string2 && string2[0])
  243. return -1;
  244. if (string1 && string1[0] && (!string2 || !string2[0]))
  245. return 1;
  246. return 0;
  247. }
  248. /*
  249. * string_strcasestr: locale and case independent string search
  250. */
  251. char *
  252. string_strcasestr (const char *string, const char *search)
  253. {
  254. int length_search;
  255. length_search = utf8_strlen (search);
  256. if (!string || !search || (length_search == 0))
  257. return NULL;
  258. while (string[0])
  259. {
  260. if (string_strncasecmp (string, search, length_search) == 0)
  261. return (char *)string;
  262. string = utf8_next_char (string);
  263. }
  264. return NULL;
  265. }
  266. /*
  267. * string_match: return 1 if string matches a mask
  268. * mask can begin or end with "*", no other "*" are allowed
  269. * inside mask
  270. */
  271. int
  272. string_match (const char *string, const char *mask, int case_sensitive)
  273. {
  274. char last, *mask2;
  275. int len_string, len_mask, rc;
  276. if (!mask || !mask[0])
  277. return 0;
  278. /* if mask is "*", then any string matches */
  279. if (strcmp (mask, "*") == 0)
  280. return 1;
  281. len_string = strlen (string);
  282. len_mask = strlen (mask);
  283. last = mask[len_mask - 1];
  284. /* mask begins with "*" */
  285. if ((mask[0] == '*') && (last != '*'))
  286. {
  287. /* not enough chars in string to match */
  288. if (len_string < len_mask - 1)
  289. return 0;
  290. /* check if end of string matches */
  291. if ((case_sensitive && (strcmp (string + len_string - (len_mask - 1),
  292. mask + 1) == 0))
  293. || (!case_sensitive && (string_strcasecmp (string + len_string - (len_mask - 1),
  294. mask + 1) == 0)))
  295. return 1;
  296. /* no match */
  297. return 0;
  298. }
  299. /* mask ends with "*" */
  300. if ((mask[0] != '*') && (last == '*'))
  301. {
  302. /* not enough chars in string to match */
  303. if (len_string < len_mask - 1)
  304. return 0;
  305. /* check if beginning of string matches */
  306. if ((case_sensitive && (strncmp (string, mask, len_mask - 1) == 0))
  307. || (!case_sensitive && (string_strncasecmp (string,
  308. mask,
  309. len_mask - 1) == 0)))
  310. return 1;
  311. /* no match */
  312. return 0;
  313. }
  314. /* mask begins and ends with "*" */
  315. if ((mask[0] == '*') && (last == '*'))
  316. {
  317. /* not enough chars in string to match */
  318. if (len_string < len_mask - 1)
  319. return 0;
  320. /* keep only relevant chars in mask for searching string */
  321. mask2 = string_strndup (mask + 1, len_mask - 2);
  322. if (!mask2)
  323. return 0;
  324. /* search string */
  325. rc = ((case_sensitive && strstr (string, mask2))
  326. || (!case_sensitive && string_strcasestr (string, mask2))) ?
  327. 1 : 0;
  328. /* free and return */
  329. free (mask2);
  330. return rc;
  331. }
  332. /* no "*" at all, compare strings */
  333. if ((case_sensitive && (strcmp (string, mask) == 0))
  334. || (!case_sensitive && (string_strcasecmp (string, mask) == 0)))
  335. return 1;
  336. /* no match */
  337. return 0;
  338. }
  339. /*
  340. * string_replace: replace a string by new one in a string
  341. * note: returned value has to be free() after use
  342. */
  343. char *
  344. string_replace (const char *string, const char *search, const char *replace)
  345. {
  346. const char *pos;
  347. char *new_string;
  348. int length1, length2, length_new, count;
  349. if (!string || !search || !replace)
  350. return NULL;
  351. length1 = strlen (search);
  352. length2 = strlen (replace);
  353. /* count number of strings to replace */
  354. count = 0;
  355. pos = string;
  356. while (pos && pos[0] && (pos = strstr (pos, search)))
  357. {
  358. count++;
  359. pos += length1;
  360. }
  361. /* easy: no string to replace! */
  362. if (count == 0)
  363. return strdup (string);
  364. /* compute needed memory for new string */
  365. length_new = strlen (string) - (count * length1) + (count * length2) + 1;
  366. /* allocate new string */
  367. new_string = malloc (length_new);
  368. if (!new_string)
  369. return strdup (string);
  370. /* replace all occurrences */
  371. new_string[0] = '\0';
  372. while (string && string[0])
  373. {
  374. pos = strstr (string, search);
  375. if (pos)
  376. {
  377. strncat (new_string, string, pos - string);
  378. strcat (new_string, replace);
  379. pos += length1;
  380. }
  381. else
  382. strcat (new_string, string);
  383. string = pos;
  384. }
  385. return new_string;
  386. }
  387. /*
  388. * string_expand_home: expand home in a PATH
  389. * (for example: "~/file.txt" => "/home/xxx/file.txt")
  390. * note: returned value has to be free() after use
  391. */
  392. char *
  393. string_expand_home (const char *path)
  394. {
  395. char *ptr_home, *str;
  396. int length;
  397. if (!path)
  398. return NULL;
  399. if (!path[0] || (path[0] != '~')
  400. || ((path[1] && path[1] != DIR_SEPARATOR_CHAR)))
  401. {
  402. return strdup (path);
  403. }
  404. ptr_home = getenv ("HOME");
  405. length = strlen (ptr_home) + strlen (path + 1) + 1;
  406. str = malloc (length);
  407. if (!str)
  408. return strdup (path);
  409. snprintf (str, length, "%s%s", ptr_home, path + 1);
  410. return str;
  411. }
  412. /*
  413. * string_remove_quotes: remove quotes at beginning/end of string
  414. * (ignore spaces if there are before first quote or
  415. * after last quote)
  416. * note: returned value has to be free() after use
  417. */
  418. char *
  419. string_remove_quotes (const char *string, const char *quotes)
  420. {
  421. int length;
  422. const char *pos_start, *pos_end;
  423. if (!string || !quotes)
  424. return NULL;
  425. if (!string[0])
  426. return strdup (string);
  427. pos_start = string;
  428. while (pos_start[0] == ' ')
  429. {
  430. pos_start++;
  431. }
  432. length = strlen (string);
  433. pos_end = string + length - 1;
  434. while ((pos_end[0] == ' ') && (pos_end > pos_start))
  435. {
  436. pos_end--;
  437. }
  438. if (!pos_start[0] || !pos_end[0] || (pos_end <= pos_start))
  439. return strdup (string);
  440. if (strchr (quotes, pos_start[0]) && (pos_end[0] == pos_start[0]))
  441. {
  442. if (pos_end == (pos_start + 1))
  443. return strdup ("");
  444. return string_strndup (pos_start + 1, pos_end - pos_start - 1);
  445. }
  446. return strdup (string);
  447. }
  448. /*
  449. * string_strip: strip chars at beginning and/or end of string
  450. * note: returned value has to be free() after use
  451. */
  452. char *
  453. string_strip (const char *string, int left, int right, const char *chars)
  454. {
  455. const char *ptr_start, *ptr_end;
  456. if (!string)
  457. return NULL;
  458. if (!string[0])
  459. return strdup (string);
  460. ptr_start = string;
  461. ptr_end = string + strlen (string) - 1;
  462. if (left)
  463. {
  464. while (ptr_start[0] && strchr (chars, ptr_start[0]))
  465. {
  466. ptr_start++;
  467. }
  468. if (!ptr_start[0])
  469. return strdup (ptr_start);
  470. }
  471. if (right)
  472. {
  473. while ((ptr_end >= ptr_start) && strchr (chars, ptr_end[0]))
  474. {
  475. ptr_end--;
  476. }
  477. if (ptr_end < ptr_start)
  478. return strdup ("");
  479. }
  480. return string_strndup (ptr_start, ptr_end - ptr_start + 1);
  481. }
  482. /*
  483. * string_convert_hex_chars: convert hex chars (\x??) to value
  484. * note: returned value has to be free() after use
  485. */
  486. char *
  487. string_convert_hex_chars (const char *string)
  488. {
  489. char *output, hex_str[8], *error;
  490. int pos_output;
  491. long number;
  492. output = malloc (strlen (string) + 1);
  493. if (output)
  494. {
  495. pos_output = 0;
  496. while (string && string[0])
  497. {
  498. if (string[0] == '\\')
  499. {
  500. string++;
  501. switch (string[0])
  502. {
  503. case '\\':
  504. output[pos_output++] = '\\';
  505. string++;
  506. break;
  507. case 'x':
  508. case 'X':
  509. if (isxdigit ((unsigned char)string[1])
  510. && isxdigit ((unsigned char)string[2]))
  511. {
  512. snprintf (hex_str, sizeof (hex_str),
  513. "0x%c%c", string[1], string[2]);
  514. number = strtol (hex_str, &error, 16);
  515. if (error && !error[0])
  516. {
  517. output[pos_output++] = number;
  518. string += 3;
  519. }
  520. else
  521. {
  522. output[pos_output++] = '\\';
  523. output[pos_output++] = string[0];
  524. string++;
  525. }
  526. }
  527. else
  528. {
  529. output[pos_output++] = string[0];
  530. string++;
  531. }
  532. break;
  533. default:
  534. output[pos_output++] = '\\';
  535. output[pos_output++] = string[0];
  536. string++;
  537. break;
  538. }
  539. }
  540. else
  541. {
  542. output[pos_output++] = string[0];
  543. string++;
  544. }
  545. }
  546. output[pos_output] = '\0';
  547. }
  548. return output;
  549. }
  550. /*
  551. * string_is_word_char: return 1 if given character is a "word character"
  552. */
  553. int
  554. string_is_word_char (const char *string)
  555. {
  556. wint_t c = utf8_wide_char (string);
  557. if (c == WEOF)
  558. return 0;
  559. if (iswalnum (c))
  560. return 1;
  561. switch (c)
  562. {
  563. case '-':
  564. case '_':
  565. case '|':
  566. return 1;
  567. }
  568. /* not a 'word char' */
  569. return 0;
  570. }
  571. /*
  572. * string_mask_to_regex: convert a mask (string with only "*" as wildcard) to a
  573. * regex, paying attention to special chars in a regex
  574. */
  575. char *
  576. string_mask_to_regex (const char *mask)
  577. {
  578. char *result;
  579. const char *ptr_mask;
  580. int index_result;
  581. char *regex_special_char = ".[]{}()?+";
  582. if (!mask)
  583. return NULL;
  584. result = malloc ((strlen (mask) * 2) + 1);
  585. if (!result)
  586. return NULL;
  587. result[0] = '\0';
  588. index_result = 0;
  589. ptr_mask = mask;
  590. while (ptr_mask[0])
  591. {
  592. /* '*' in string ? then replace by '.*' */
  593. if (ptr_mask[0] == '*')
  594. {
  595. result[index_result++] = '.';
  596. result[index_result++] = '*';
  597. }
  598. /* special regex char in string ? escape it with '\' */
  599. else if (strchr (regex_special_char, ptr_mask[0]))
  600. {
  601. result[index_result++] = '\\';
  602. result[index_result++] = ptr_mask[0];
  603. }
  604. /* standard char, just copy it */
  605. else
  606. result[index_result++] = ptr_mask[0];
  607. ptr_mask++;
  608. }
  609. /* add final '\0' */
  610. result[index_result] = '\0';
  611. return result;
  612. }
  613. /*
  614. * string_regex_flags: get pointer on string after flags and return mask with
  615. * flags to compile regex
  616. * format of flags is:
  617. * (?eins-eins)string
  618. * flags:
  619. * e: POSIX extended regex (REG_EXTENDED)
  620. * i: case insensitive (REG_ICASE)
  621. * n: match-any-character operators don't match a newline (REG_NEWLINE)
  622. * s: support for substring addressing of matches is not required (REG_NOSUB)
  623. * examples (with default_flags = REG_EXTENDED):
  624. * "(?i)toto" : regex "toto", flags = REG_EXTENDED | REG_ICASE
  625. * "(?i)toto" : regex "toto", flags = REG_EXTENDED | REG_ICASE
  626. * "(?i-e)toto": regex "toto", flags = REG_ICASE
  627. */
  628. const char *
  629. string_regex_flags (const char *regex, int default_flags, int *flags)
  630. {
  631. const char *ptr_regex, *ptr_flags;
  632. int set_flag, flag;
  633. char *pos;
  634. ptr_regex = regex;
  635. if (flags)
  636. *flags = default_flags;
  637. while (strncmp (ptr_regex, "(?", 2) == 0)
  638. {
  639. pos = strchr (ptr_regex, ')');
  640. if (!pos)
  641. break;
  642. if (!isalpha ((unsigned char)ptr_regex[2]) && (ptr_regex[2] != '-'))
  643. break;
  644. if (flags)
  645. {
  646. set_flag = 1;
  647. for (ptr_flags = ptr_regex + 2; ptr_flags < pos; ptr_flags++)
  648. {
  649. flag = 0;
  650. switch (ptr_flags[0])
  651. {
  652. case '-':
  653. set_flag = 0;
  654. break;
  655. case 'e':
  656. flag = REG_EXTENDED;
  657. break;
  658. case 'i':
  659. flag = REG_ICASE;
  660. break;
  661. case 'n':
  662. flag = REG_NEWLINE;
  663. break;
  664. case 's':
  665. flag = REG_NOSUB;
  666. break;
  667. }
  668. if (flag > 0)
  669. {
  670. if (set_flag)
  671. *flags |= flag;
  672. else
  673. *flags &= ~flag;
  674. }
  675. }
  676. }
  677. ptr_regex = pos + 1;
  678. }
  679. return ptr_regex;
  680. }
  681. /*
  682. * string_regcomp: compile a regex using optional flags at beginning of string
  683. * (for format of flags in regex, see string_regex_flags())
  684. */
  685. int
  686. string_regcomp (void *preg, const char *regex, int default_flags)
  687. {
  688. const char *ptr_regex;
  689. int flags;
  690. ptr_regex = string_regex_flags (regex, default_flags, &flags);
  691. return regcomp ((regex_t *)preg, ptr_regex, flags);
  692. }
  693. /*
  694. * string_has_highlight: return 1 if string contains a highlight (using list of
  695. * words to highlight)
  696. * return 0 if no highlight is found in string
  697. */
  698. int
  699. string_has_highlight (const char *string, const char *highlight_words)
  700. {
  701. char *msg, *highlight, *match, *match_pre, *match_post, *msg_pos;
  702. char *pos, *pos_end, *ptr_str, *ptr_string_ref;
  703. int end, length, startswith, endswith, wildcard_start, wildcard_end, flags;
  704. if (!string || !string[0] || !highlight_words || !highlight_words[0])
  705. return 0;
  706. msg = strdup (string);
  707. if (!msg)
  708. return 0;
  709. string_tolower (msg);
  710. highlight = strdup (highlight_words);
  711. if (!highlight)
  712. {
  713. free (msg);
  714. return 0;
  715. }
  716. string_tolower (highlight);
  717. pos = highlight;
  718. end = 0;
  719. while (!end)
  720. {
  721. ptr_string_ref = (char *)string;
  722. flags = 0;
  723. pos = (char *)string_regex_flags (pos, REG_ICASE, &flags);
  724. pos_end = strchr (pos, ',');
  725. if (!pos_end)
  726. {
  727. pos_end = strchr (pos, '\0');
  728. end = 1;
  729. }
  730. /* error parsing string! */
  731. if (!pos_end)
  732. {
  733. free (msg);
  734. free (highlight);
  735. return 0;
  736. }
  737. if (flags & REG_ICASE)
  738. {
  739. for (ptr_str = pos; ptr_str < pos_end; ptr_str++)
  740. {
  741. if ((ptr_str[0] >= 'A') && (ptr_str[0] <= 'Z'))
  742. ptr_str[0] += ('a' - 'A');
  743. }
  744. ptr_string_ref = msg;
  745. }
  746. length = pos_end - pos;
  747. pos_end[0] = '\0';
  748. if (length > 0)
  749. {
  750. if ((wildcard_start = (pos[0] == '*')))
  751. {
  752. pos++;
  753. length--;
  754. }
  755. if ((wildcard_end = (*(pos_end - 1) == '*')))
  756. {
  757. *(pos_end - 1) = '\0';
  758. length--;
  759. }
  760. }
  761. if (length > 0)
  762. {
  763. msg_pos = ptr_string_ref;
  764. /* highlight found! */
  765. while ((match = strstr (msg_pos, pos)) != NULL)
  766. {
  767. match_pre = utf8_prev_char (ptr_string_ref, match);
  768. if (!match_pre)
  769. match_pre = match - 1;
  770. match_post = match + length;
  771. startswith = ((match == ptr_string_ref) || (!string_is_word_char (match_pre)));
  772. endswith = ((!match_post[0]) || (!string_is_word_char (match_post)));
  773. if ((wildcard_start && wildcard_end) ||
  774. (!wildcard_start && !wildcard_end &&
  775. startswith && endswith) ||
  776. (wildcard_start && endswith) ||
  777. (wildcard_end && startswith))
  778. {
  779. free (msg);
  780. free (highlight);
  781. return 1;
  782. }
  783. msg_pos = match_post;
  784. }
  785. }
  786. if (!end)
  787. pos = pos_end + 1;
  788. }
  789. free (msg);
  790. free (highlight);
  791. /* no highlight found */
  792. return 0;
  793. }
  794. /*
  795. * string_has_highlight_regex_compiled: return 1 if string contains a highlight
  796. * using a regular expression (any match
  797. * in string must be surrounded by word
  798. * chars)
  799. */
  800. int
  801. string_has_highlight_regex_compiled (const char *string, regex_t *regex)
  802. {
  803. int rc, startswith, endswith;
  804. regmatch_t regex_match;
  805. const char *match_pre;
  806. if (!string || !regex)
  807. return 0;
  808. while (string && string[0])
  809. {
  810. rc = regexec (regex, string, 1, &regex_match, 0);
  811. if ((rc != 0) || (regex_match.rm_so < 0) || (regex_match.rm_eo < 0))
  812. break;
  813. startswith = (regex_match.rm_so == 0);
  814. if (!startswith)
  815. {
  816. match_pre = utf8_prev_char (string, string + regex_match.rm_so);
  817. startswith = !string_is_word_char (match_pre);
  818. }
  819. endswith = 0;
  820. if (startswith)
  821. {
  822. endswith = ((regex_match.rm_eo == (int)strlen (string))
  823. || !string_is_word_char (string + regex_match.rm_eo));
  824. }
  825. if (startswith && endswith)
  826. return 1;
  827. string += regex_match.rm_eo;
  828. }
  829. /* no highlight found */
  830. return 0;
  831. }
  832. /*
  833. * string_has_highlight_regex: return 1 if string contains a highlight
  834. * using a regular expression (any match in string
  835. * must be surrounded by word chars)
  836. */
  837. int
  838. string_has_highlight_regex (const char *string, const char *regex)
  839. {
  840. regex_t reg;
  841. int rc;
  842. if (!string || !regex || !regex[0])
  843. return 0;
  844. if (string_regcomp (&reg, regex, REG_EXTENDED | REG_ICASE) != 0)
  845. return 0;
  846. rc = string_has_highlight_regex_compiled (string, &reg);
  847. regfree (&reg);
  848. return rc;
  849. }
  850. /*
  851. * string_split: split a string according to separators
  852. * examples:
  853. * string_split ("abc de fghi", " ", 0, 0, NULL)
  854. * ==> array[0] = "abc"
  855. * array[1] = "de"
  856. * array[2] = "fghi"
  857. * array[3] = NULL
  858. * string_split ("abc de fghi", " ", 1, 0, NULL)
  859. * ==> array[0] = "abc de fghi"
  860. * array[1] = "de fghi"
  861. * array[2] = "fghi"
  862. * array[3] = NULL
  863. */
  864. char **
  865. string_split (const char *string, const char *separators, int keep_eol,
  866. int num_items_max, int *num_items)
  867. {
  868. int i, j, n_items;
  869. char *string2, **array;
  870. char *ptr, *ptr1, *ptr2;
  871. if (num_items != NULL)
  872. *num_items = 0;
  873. if (!string || !string[0] || !separators || !separators[0])
  874. return NULL;
  875. string2 = string_strip (string, 1, (keep_eol == 2) ? 0 : 1, separators);
  876. if (!string2 || !string2[0])
  877. return NULL;
  878. /* calculate number of items */
  879. ptr = string2;
  880. i = 1;
  881. while ((ptr = strpbrk (ptr, separators)))
  882. {
  883. while (ptr[0] && (strchr (separators, ptr[0]) != NULL))
  884. {
  885. ptr++;
  886. }
  887. i++;
  888. }
  889. n_items = i;
  890. if ((num_items_max != 0) && (n_items > num_items_max))
  891. n_items = num_items_max;
  892. array = malloc ((n_items + 1) * sizeof (array[0]));
  893. if (!array)
  894. return NULL;
  895. ptr1 = string2;
  896. for (i = 0; i < n_items; i++)
  897. {
  898. while (ptr1[0] && (strchr (separators, ptr1[0]) != NULL))
  899. {
  900. ptr1++;
  901. }
  902. if (i == (n_items - 1))
  903. {
  904. ptr2 = strpbrk (ptr1, separators);
  905. if (!ptr2)
  906. ptr2 = strchr (ptr1, '\0');
  907. }
  908. else
  909. {
  910. if ((ptr2 = strpbrk (ptr1, separators)) == NULL)
  911. {
  912. if ((ptr2 = strchr (ptr1, '\r')) == NULL)
  913. {
  914. if ((ptr2 = strchr (ptr1, '\n')) == NULL)
  915. {
  916. ptr2 = strchr (ptr1, '\0');
  917. }
  918. }
  919. }
  920. }
  921. if ((ptr1 == NULL) || (ptr2 == NULL))
  922. {
  923. array[i] = NULL;
  924. }
  925. else
  926. {
  927. if (ptr2 - ptr1 > 0)
  928. {
  929. if (keep_eol)
  930. {
  931. array[i] = strdup (ptr1);
  932. if (!array[i])
  933. {
  934. for (j = 0; j < n_items; j++)
  935. {
  936. if (array[j])
  937. free (array[j]);
  938. }
  939. free (array);
  940. free (string2);
  941. return NULL;
  942. }
  943. }
  944. else
  945. {
  946. array[i] = malloc (ptr2 - ptr1 + 1);
  947. if (!array[i])
  948. {
  949. for (j = 0; j < n_items; j++)
  950. {
  951. if (array[j])
  952. free (array[j]);
  953. }
  954. free (array);
  955. free (string2);
  956. return NULL;
  957. }
  958. strncpy (array[i], ptr1, ptr2 - ptr1);
  959. array[i][ptr2 - ptr1] = '\0';
  960. }
  961. ptr1 = ++ptr2;
  962. }
  963. else
  964. {
  965. array[i] = NULL;
  966. }
  967. }
  968. }
  969. array[i] = NULL;
  970. if (num_items != NULL)
  971. *num_items = i;
  972. free (string2);
  973. return array;
  974. }
  975. /*
  976. * string_free_split: free a split string
  977. */
  978. void
  979. string_free_split (char **split_string)
  980. {
  981. int i;
  982. if (split_string)
  983. {
  984. for (i = 0; split_string[i]; i++)
  985. free (split_string[i]);
  986. free (split_string);
  987. }
  988. }
  989. /*
  990. * string_build_with_split_string: build a string with a split string
  991. * note: returned value has to be free() after
  992. * use
  993. */
  994. char *
  995. string_build_with_split_string (const char **split_string,
  996. const char *separator)
  997. {
  998. int i, length, length_separator;
  999. char *result;
  1000. if (!split_string)
  1001. return NULL;
  1002. length = 0;
  1003. length_separator = (separator) ? strlen (separator) : 0;
  1004. for (i = 0; split_string[i]; i++)
  1005. {
  1006. length += strlen (split_string[i]) + length_separator;
  1007. }
  1008. result = malloc (length + 1);
  1009. if (result)
  1010. {
  1011. result[0] = '\0';
  1012. for (i = 0; split_string[i]; i++)
  1013. {
  1014. strcat (result, split_string[i]);
  1015. if (separator && split_string[i + 1])
  1016. strcat (result, separator);
  1017. }
  1018. }
  1019. return result;
  1020. }
  1021. /*
  1022. * string_split_command: split a list of commands separated by 'separator'
  1023. * and ecscaped with '\'
  1024. * - empty commands are removed
  1025. * - spaces on the left of each commands are stripped
  1026. * Result must be freed with free_multi_command
  1027. */
  1028. char **
  1029. string_split_command (const char *command, char separator)
  1030. {
  1031. int nb_substr, arr_idx, str_idx, type;
  1032. char **array, **array2;
  1033. char *buffer, *p;
  1034. const char *ptr;
  1035. if (!command || !command[0])
  1036. return NULL;
  1037. nb_substr = 1;
  1038. ptr = command;
  1039. while ( (p = strchr(ptr, separator)) != NULL)
  1040. {
  1041. nb_substr++;
  1042. ptr = ++p;
  1043. }
  1044. array = malloc ((nb_substr + 1) * sizeof (array[0]));
  1045. if (!array)
  1046. return NULL;
  1047. buffer = malloc (strlen(command) + 1);
  1048. if (!buffer)
  1049. {
  1050. free (array);
  1051. return NULL;
  1052. }
  1053. ptr = command;
  1054. str_idx = 0;
  1055. arr_idx = 0;
  1056. while(*ptr != '\0')
  1057. {
  1058. type = 0;
  1059. if (*ptr == ';')
  1060. {
  1061. if (ptr == command)
  1062. type = 1;
  1063. else if ( *(ptr-1) != '\\')
  1064. type = 1;
  1065. else if ( *(ptr-1) == '\\')
  1066. type = 2;
  1067. }
  1068. if (type == 1)
  1069. {
  1070. buffer[str_idx] = '\0';
  1071. str_idx = -1;
  1072. p = buffer;
  1073. /* strip white spaces a the begining of the line */
  1074. while (*p == ' ') p++;
  1075. if (p && p[0])
  1076. array[arr_idx++] = strdup (p);
  1077. }
  1078. else if (type == 2)
  1079. buffer[--str_idx] = *ptr;
  1080. else
  1081. buffer[str_idx] = *ptr;
  1082. str_idx++;
  1083. ptr++;
  1084. }
  1085. buffer[str_idx] = '\0';
  1086. p = buffer;
  1087. while (*p == ' ') p++;
  1088. if (p && p[0])
  1089. array[arr_idx++] = strdup (p);
  1090. array[arr_idx] = NULL;
  1091. free (buffer);
  1092. array2 = realloc (array, (arr_idx + 1) * sizeof(array[0]));
  1093. if (!array2)
  1094. {
  1095. if (array)
  1096. free (array);
  1097. return NULL;
  1098. }
  1099. return array2;
  1100. }
  1101. /*
  1102. * string_free_split_command : free a list of commands split
  1103. * with string_split_command
  1104. */
  1105. void
  1106. string_free_split_command (char **split_command)
  1107. {
  1108. int i;
  1109. if (split_command)
  1110. {
  1111. for (i = 0; split_command[i]; i++)
  1112. free (split_command[i]);
  1113. free (split_command);
  1114. }
  1115. }
  1116. /*
  1117. * string_iconv: convert string to another charset
  1118. * note: returned value has to be free() after use
  1119. */
  1120. char *
  1121. string_iconv (int from_utf8, const char *from_code, const char *to_code,
  1122. const char *string)
  1123. {
  1124. char *outbuf;
  1125. #ifdef HAVE_ICONV
  1126. iconv_t cd;
  1127. char *inbuf, *ptr_inbuf, *ptr_outbuf, *next_char;
  1128. char *ptr_inbuf_shift;
  1129. int done;
  1130. size_t err, inbytesleft, outbytesleft;
  1131. if (from_code && from_code[0] && to_code && to_code[0]
  1132. && (string_strcasecmp(from_code, to_code) != 0))
  1133. {
  1134. cd = iconv_open (to_code, from_code);
  1135. if (cd == (iconv_t)(-1))
  1136. outbuf = strdup (string);
  1137. else
  1138. {
  1139. inbuf = strdup (string);
  1140. if (!inbuf)
  1141. return NULL;
  1142. ptr_inbuf = inbuf;
  1143. inbytesleft = strlen (inbuf);
  1144. outbytesleft = inbytesleft * 4;
  1145. outbuf = malloc (outbytesleft + 2);
  1146. if (!outbuf)
  1147. return inbuf;
  1148. ptr_outbuf = outbuf;
  1149. ptr_inbuf_shift = NULL;
  1150. done = 0;
  1151. while (!done)
  1152. {
  1153. err = iconv (cd, (ICONV_CONST char **)(&ptr_inbuf), &inbytesleft,
  1154. &ptr_outbuf, &outbytesleft);
  1155. if (err == (size_t)(-1))
  1156. {
  1157. switch (errno)
  1158. {
  1159. case EINVAL:
  1160. done = 1;
  1161. break;
  1162. case E2BIG:
  1163. done = 1;
  1164. break;
  1165. case EILSEQ:
  1166. if (from_utf8)
  1167. {
  1168. next_char = utf8_next_char (ptr_inbuf);
  1169. if (next_char)
  1170. {
  1171. inbytesleft -= next_char - ptr_inbuf;
  1172. ptr_inbuf = next_char;
  1173. }
  1174. else
  1175. {
  1176. inbytesleft--;
  1177. ptr_inbuf++;
  1178. }
  1179. }
  1180. else
  1181. {
  1182. ptr_inbuf++;
  1183. inbytesleft--;
  1184. }
  1185. ptr_outbuf[0] = '?';
  1186. ptr_outbuf++;
  1187. outbytesleft--;
  1188. break;
  1189. }
  1190. }
  1191. else
  1192. {
  1193. if (!ptr_inbuf_shift)
  1194. {
  1195. ptr_inbuf_shift = ptr_inbuf;
  1196. ptr_inbuf = NULL;
  1197. inbytesleft = 0;
  1198. }
  1199. else
  1200. done = 1;
  1201. }
  1202. }
  1203. if (ptr_inbuf_shift)
  1204. ptr_inbuf = ptr_inbuf_shift;
  1205. ptr_outbuf[0] = '\0';
  1206. free (inbuf);
  1207. iconv_close (cd);
  1208. }
  1209. }
  1210. else
  1211. outbuf = strdup (string);
  1212. #else
  1213. /* make C compiler happy */
  1214. (void) from_utf8;
  1215. (void) from_code;
  1216. (void) to_code;
  1217. outbuf = strdup (string);
  1218. #endif /* HAVE_ICONV */
  1219. return outbuf;
  1220. }
  1221. /*
  1222. * string_iconv_to_internal: convert user string (input, script, ..) to
  1223. * WeeChat internal storage charset
  1224. * note: returned value has to be free() after use
  1225. */
  1226. char *
  1227. string_iconv_to_internal (const char *charset, const char *string)
  1228. {
  1229. char *input, *output;
  1230. if (!string)
  1231. return NULL;
  1232. input = strdup (string);
  1233. if (!input)
  1234. return NULL;
  1235. /*
  1236. * optimized for UTF-8: if charset is NULL => we use term charset => if
  1237. * this charset is already UTF-8, then no iconv is needed
  1238. */
  1239. if (local_utf8 && (!charset || !charset[0]))
  1240. return input;
  1241. if (utf8_has_8bits (input) && utf8_is_valid (input, NULL))
  1242. return input;
  1243. output = string_iconv (0,
  1244. (charset && charset[0]) ?
  1245. charset : weechat_local_charset,
  1246. WEECHAT_INTERNAL_CHARSET,
  1247. input);
  1248. if (!output)
  1249. return input;
  1250. utf8_normalize (output, '?');
  1251. free (input);
  1252. return output;
  1253. }
  1254. /*
  1255. * string_iconv_from_internal: convert internal string to terminal charset,
  1256. * for display
  1257. * note: returned value has to be free() after use
  1258. */
  1259. char *
  1260. string_iconv_from_internal (const char *charset, const char *string)
  1261. {
  1262. char *input, *output;
  1263. if (!string)
  1264. return NULL;
  1265. input = strdup (string);
  1266. if (!input)
  1267. return NULL;
  1268. /*
  1269. * optimized for UTF-8: if charset is NULL => we use term charset => if
  1270. * this charset is already UTF-8, then no iconv needed
  1271. */
  1272. if (local_utf8 && (!charset || !charset[0]))
  1273. return input;
  1274. utf8_normalize (input, '?');
  1275. output = string_iconv (1,
  1276. WEECHAT_INTERNAL_CHARSET,
  1277. (charset && charset[0]) ?
  1278. charset : weechat_local_charset,
  1279. input);
  1280. if (!output)
  1281. return input;
  1282. free (input);
  1283. return output;
  1284. }
  1285. /*
  1286. * string_iconv_fprintf: encode to terminal charset, then call fprintf on a file
  1287. * return 1 if ok, 0 if error
  1288. */
  1289. int
  1290. string_iconv_fprintf (FILE *file, const char *data, ...)
  1291. {
  1292. char *buf2;
  1293. int rc, num_written;
  1294. rc = 0;
  1295. weechat_va_format (data);
  1296. if (vbuffer)
  1297. {
  1298. buf2 = string_iconv_from_internal (NULL, vbuffer);
  1299. num_written = fprintf (file, "%s", (buf2) ? buf2 : vbuffer);
  1300. rc = (num_written == (int)strlen ((buf2) ? buf2 : vbuffer)) ? 1 : 0;
  1301. if (buf2)
  1302. free (buf2);
  1303. free (vbuffer);
  1304. }
  1305. return rc;
  1306. }
  1307. /*
  1308. * string_format_size: format a string with size and unit name (bytes, KB, MB, GB)
  1309. * note: returned value has to be free() after use
  1310. */
  1311. char *
  1312. string_format_size (unsigned long long size)
  1313. {
  1314. char *unit_name[] = { N_("bytes"), N_("KB"), N_("MB"), N_("GB") };
  1315. char *unit_format[] = { "%.0f", "%.1f", "%.02f", "%.02f" };
  1316. float unit_divide[] = { 1, 1024, 1024*1024, 1024*1024*1024 };
  1317. char format_size[128], str_size[128];
  1318. int num_unit;
  1319. str_size[0] = '\0';
  1320. if (size < 1024*10)
  1321. num_unit = 0;
  1322. else if (size < 1024*1024)
  1323. num_unit = 1;
  1324. else if (size < 1024*1024*1024)
  1325. num_unit = 2;
  1326. else
  1327. num_unit = 3;
  1328. snprintf (format_size, sizeof (format_size),
  1329. "%s %%s",
  1330. unit_format[num_unit]);
  1331. snprintf (str_size, sizeof (str_size),
  1332. format_size,
  1333. ((float)size) / ((float)(unit_divide[num_unit])),
  1334. (size <= 1) ? _("byte") : _(unit_name[num_unit]));
  1335. return strdup (str_size);
  1336. }
  1337. /*
  1338. * string_convbase64_8x3_to_6x4 : convert 3 bytes of 8 bits in 4 bytes of 6 bits
  1339. */
  1340. void
  1341. string_convbase64_8x3_to_6x4 (const char *from, char *to)
  1342. {
  1343. unsigned char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  1344. "abcdefghijklmnopqrstuvwxyz0123456789+/";
  1345. to[0] = base64_table [ (from[0] & 0xfc) >> 2 ];
  1346. to[1] = base64_table [ ((from[0] & 0x03) << 4) + ((from[1] & 0xf0) >> 4) ];
  1347. to[2] = base64_table [ ((from[1] & 0x0f) << 2) + ((from[2] & 0xc0) >> 6) ];
  1348. to[3] = base64_table [ from[2] & 0x3f ];
  1349. }
  1350. /*
  1351. * string_encode_base64: encode a string in base64
  1352. * length is number of bytes in "from" to convert
  1353. * (commonly strlen(from))
  1354. */
  1355. void
  1356. string_encode_base64 (const char *from, int length, char *to)
  1357. {
  1358. const char *ptr_from;
  1359. char *ptr_to;
  1360. ptr_from = from;
  1361. ptr_to = to;
  1362. while (length >= 3)
  1363. {
  1364. string_convbase64_8x3_to_6x4 (ptr_from, ptr_to);
  1365. ptr_from += 3 * sizeof (*ptr_from);
  1366. ptr_to += 4 * sizeof (*ptr_to);
  1367. length -= 3;
  1368. }
  1369. if (length > 0)
  1370. {
  1371. char rest[3] = { 0, 0, 0 };
  1372. switch (length)
  1373. {
  1374. case 1 :
  1375. rest[0] = ptr_from[0];
  1376. string_convbase64_8x3_to_6x4 (rest, ptr_to);
  1377. ptr_to[2] = ptr_to[3] = '=';
  1378. break;
  1379. case 2 :
  1380. rest[0] = ptr_from[0];
  1381. rest[1] = ptr_from[1];
  1382. string_convbase64_8x3_to_6x4 (rest, ptr_to);
  1383. ptr_to[3] = '=';
  1384. break;
  1385. }
  1386. ptr_to[4] = 0;
  1387. }
  1388. else
  1389. ptr_to[0] = '\0';
  1390. }
  1391. /*
  1392. * string_convbase64_6x4_to_8x3 : convert 4 bytes of 6 bits to 3 bytes of 8 bits
  1393. */
  1394. void
  1395. string_convbase64_6x4_to_8x3 (const unsigned char *from, unsigned char *to)
  1396. {
  1397. to[0] = from[0] << 2 | from[1] >> 4;
  1398. to[1] = from[1] << 4 | from[2] >> 2;
  1399. to[2] = ((from[2] << 6) & 0xc0) | from[3];
  1400. }
  1401. /*
  1402. * string_decode_base64: decode a base64 string
  1403. * return length of string in *to
  1404. * (does not count final \0)
  1405. */
  1406. int
  1407. string_decode_base64 (const char *from, char *to)
  1408. {
  1409. const char *ptr_from;
  1410. int length, to_length, i;
  1411. char *ptr_to;
  1412. unsigned char c, in[4], out[3];
  1413. unsigned char base64_table[]="|$$$}rstuvwxyz{$$$$$$$>?"
  1414. "@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
  1415. ptr_from = from;
  1416. ptr_to = to;
  1417. ptr_to[0] = '\0';
  1418. to_length = 0;
  1419. while (ptr_from && ptr_from[0])
  1420. {
  1421. length = 0;
  1422. for (i = 0; i < 4; i++)
  1423. {
  1424. c = 0;
  1425. while (ptr_from[0] && (c == 0))
  1426. {
  1427. c = (unsigned char) ptr_from[0];
  1428. ptr_from++;
  1429. c = ((c < 43) || (c > 122)) ? 0 : base64_table[c - 43];
  1430. if (c)
  1431. c = (c == '$') ? 0 : c - 61;
  1432. }
  1433. if (ptr_from[0])
  1434. {
  1435. length++;
  1436. if (c)
  1437. in[i] = c - 1;
  1438. }
  1439. else
  1440. {
  1441. in[i] = '\0';
  1442. break;
  1443. }
  1444. }
  1445. if (length)
  1446. {
  1447. string_convbase64_6x4_to_8x3 (in, out);
  1448. for (i = 0; i < length - 1; i++)
  1449. {
  1450. ptr_to[0] = out[i];
  1451. ptr_to++;
  1452. to_length++;
  1453. }
  1454. }
  1455. }
  1456. ptr_to[0] = '\0';
  1457. return to_length;
  1458. }
  1459. /*
  1460. * string_is_command_char: return 1 if first char of string is a command char,
  1461. * otherwise 0
  1462. */
  1463. int
  1464. string_is_command_char (const char *string)
  1465. {
  1466. const char *ptr_command_chars;
  1467. if (!string)
  1468. return 0;
  1469. if (string[0] == '/')
  1470. return 1;
  1471. ptr_command_chars = CONFIG_STRING(config_look_command_chars);
  1472. if (!ptr_command_chars || !ptr_command_chars[0])
  1473. return 0;
  1474. while (ptr_command_chars && ptr_command_chars[0])
  1475. {
  1476. if (utf8_charcmp (ptr_command_chars, string) == 0)
  1477. return 1;
  1478. ptr_command_chars = utf8_next_char (ptr_command_chars);
  1479. }
  1480. return 0;
  1481. }
  1482. /*
  1483. * string_input_for_buffer: return pointer to input text for buffer (pointer
  1484. * inside "string" argument)
  1485. * or return NULL if it's a command
  1486. * (by default, a command starts with a single '/')
  1487. */
  1488. const char *
  1489. string_input_for_buffer (const char *string)
  1490. {
  1491. char *pos_slash, *pos_space, *next_char;
  1492. /* special case for C comments pasted in input line */
  1493. if (strncmp (string, "/*", 2) == 0)
  1494. return string;
  1495. /*
  1496. * special case if string starts with '/': to allow to paste a path line
  1497. * "/path/to/file.txt", we check if next '/' is after a space or not
  1498. */
  1499. if (string[0] == '/')
  1500. {
  1501. pos_slash = strchr (string + 1, '/');
  1502. pos_space = strchr (string + 1, ' ');
  1503. /*
  1504. * if there's no other '/' of if '/' is after first space,
  1505. * then it is a command, and return NULL
  1506. */
  1507. if (!pos_slash || (pos_space && pos_slash > pos_space))
  1508. return NULL;
  1509. return (string[1] == '/') ? string + 1 : string;
  1510. }
  1511. /* if string does not start with a command char, then it's not command */
  1512. if (!string_is_command_char (string))
  1513. return string;
  1514. /* check if first char is doubled: if yes, then it's not a command */
  1515. next_char = utf8_next_char (string);
  1516. if (!next_char || !next_char[0])
  1517. return string;
  1518. if (utf8_charcmp (string, next_char) == 0)
  1519. return next_char;
  1520. /* string is a command */
  1521. return NULL;
  1522. }
  1523. /*
  1524. * string_replace_with_hashtable: replace ${codes} with value from hashtable
  1525. * "errors" is set with number of keys not found
  1526. * in hashtable
  1527. */
  1528. char *
  1529. string_replace_with_hashtable (const char *string,
  1530. struct t_hashtable *hashtable,
  1531. int *errors)
  1532. {
  1533. int length, length_value, index_string, index_result;
  1534. char *result, *result2, *key;
  1535. const char *pos_end_name, *ptr_value;
  1536. *errors = 0;
  1537. if (!string)
  1538. return NULL;
  1539. if (!hashtable)
  1540. return strdup (string);
  1541. length = strlen (string) + 1;
  1542. result = malloc (length);
  1543. if (result)
  1544. {
  1545. index_string = 0;
  1546. index_result = 0;
  1547. while (string[index_string])
  1548. {
  1549. if ((string[index_string] == '\\')
  1550. && (string[index_string + 1] == '$'))
  1551. {
  1552. index_string++;
  1553. result[index_result++] = string[index_string++];
  1554. }
  1555. else if ((string[index_string] == '$')
  1556. && (string[index_string + 1] == '{'))
  1557. {
  1558. pos_end_name = strchr (string + index_string + 2, '}');
  1559. if (pos_end_name)
  1560. {
  1561. key = string_strndup (string + index_string + 2,
  1562. pos_end_name - (string + index_string + 2));
  1563. if (key)
  1564. {
  1565. ptr_value = (const char *)hashtable_get (hashtable, key);
  1566. if (ptr_value)
  1567. {
  1568. length_value = strlen (ptr_value);
  1569. length += length_value;
  1570. result2 = realloc (result, length);
  1571. if (!result2)
  1572. {
  1573. if (result)
  1574. free (result);
  1575. free (key);
  1576. return NULL;
  1577. }
  1578. result = result2;
  1579. strcpy (result + index_result, ptr_value);
  1580. index_result += length_value;
  1581. index_string += pos_end_name - string -
  1582. index_string + 1;
  1583. }
  1584. else
  1585. {
  1586. result[index_result++] = string[index_string++];
  1587. (*errors)++;
  1588. }
  1589. free (key);
  1590. }
  1591. else
  1592. result[index_result++] = string[index_string++];
  1593. }
  1594. else
  1595. result[index_result++] = string[index_string++];
  1596. }
  1597. else
  1598. result[index_result++] = string[index_string++];
  1599. }
  1600. result[index_result] = '\0';
  1601. }
  1602. return result;
  1603. }