1
0

xs_regex.h 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. /* copyright (c) 2022 - 2026 grunfink et al. / MIT license */
  2. #ifndef _XS_REGEX_H
  3. #define _XS_REGEX_H
  4. int xs_regex_match(const char *str, const char *rx);
  5. xs_list *xs_regex_split_n(const char *str, const char *rx, int count);
  6. #define xs_regex_split(str, rx) xs_regex_split_n(str, rx, XS_ALL)
  7. xs_list *xs_regex_select_n(const char *str, const char *rx, int count);
  8. #define xs_regex_select(str, rx) xs_regex_select_n(str, rx, XS_ALL)
  9. xs_list *xs_regex_replace_in(xs_str *str, const char *rx, const char *rep, int count);
  10. #define xs_regex_replace_i(str, rx, rep) xs_regex_replace_in(str, rx, rep, XS_ALL)
  11. #define xs_regex_replace_n(str, rx, rep, count) xs_regex_replace_in(xs_dup(str), rx, rep, count)
  12. #define xs_regex_replace(str, rx, rep) xs_regex_replace_in(xs_dup(str), rx, rep, XS_ALL)
  13. #ifdef XS_IMPLEMENTATION
  14. #ifdef __TINYC__
  15. /* fix a compilation error in tcc */
  16. #define _REGEX_NELTS(n)
  17. #endif
  18. #include <regex.h>
  19. xs_list *xs_regex_split_n(const char *str, const char *rx, int count)
  20. /* splits str using regex as a separator, at most count times.
  21. Always returns a list:
  22. len == 0: regcomp error
  23. len == 1: full string (no matches)
  24. len == odd: first part [ separator / next part ]...
  25. */
  26. {
  27. regex_t re;
  28. regmatch_t rm;
  29. int offset = 0;
  30. xs_list *list = xs_list_new();
  31. const char *p = str;
  32. if (regcomp(&re, rx, REG_EXTENDED))
  33. return list;
  34. while (count > 0 && !regexec(&re, p, 1, &rm, offset > 0 ? REG_NOTBOL : 0)) {
  35. /* add first the leading part of the string */
  36. xs *s1 = xs_str_new_sz(p, rm.rm_so);
  37. list = xs_list_append(list, xs_is_string(s1) ? s1 : "");
  38. /* add now the matched text as the separator */
  39. xs *s2 = xs_str_new_sz(p + rm.rm_so, rm.rm_eo - rm.rm_so);
  40. list = xs_list_append(list, xs_is_string(s2) ? s2 : "");
  41. /* move forward */
  42. offset += rm.rm_eo;
  43. count--;
  44. p = str + offset;
  45. }
  46. /* add the rest of the string */
  47. list = xs_list_append(list, xs_is_string(p) ? p : "");
  48. regfree(&re);
  49. return list;
  50. }
  51. xs_list *xs_regex_select_n(const char *str, const char *rx, int count)
  52. /* selects all matches and return them as a list */
  53. {
  54. xs_list *list = xs_list_new();
  55. xs *split = NULL;
  56. const xs_val *v;
  57. int n = 0;
  58. /* split */
  59. split = xs_regex_split_n(str, rx, count);
  60. /* now iterate to get only the 'separators' (odd ones) */
  61. xs_list_foreach(split, v) {
  62. if (n & 0x1)
  63. list = xs_list_append(list, v);
  64. n++;
  65. }
  66. return list;
  67. }
  68. xs_list *xs_regex_replace_in(xs_str *str, const char *rx, const char *rep, int count)
  69. /* replaces all matches with the rep string. If it contains unescaped &,
  70. they are replaced with the match */
  71. {
  72. xs_str *s = xs_str_new(NULL);
  73. xs *split = xs_regex_split_n(str, rx, count);
  74. const xs_val *v;
  75. int n = 0;
  76. int pholder = !!strchr(rep, '&');
  77. xs_list_foreach(split, v) {
  78. if (n & 0x1) {
  79. if (pholder) {
  80. /* rep has a placeholder; process char by char */
  81. const char *p = rep;
  82. while (*p) {
  83. if (*p == '&')
  84. s = xs_str_cat(s, v);
  85. else {
  86. if (*p == '\\')
  87. p++;
  88. if (!*p)
  89. break;
  90. s = xs_append_m(s, p, 1);
  91. }
  92. p++;
  93. }
  94. }
  95. else
  96. s = xs_str_cat(s, rep);
  97. }
  98. else
  99. s = xs_str_cat(s, v);
  100. n++;
  101. }
  102. xs_free(str);
  103. return s;
  104. }
  105. int xs_regex_match(const char *str, const char *rx)
  106. /* returns if str matches the regex at least once */
  107. {
  108. xs *l = xs_regex_select_n(str, rx, 1);
  109. return xs_list_len(l) == 1;
  110. }
  111. #endif /* XS_IMPLEMENTATION */
  112. #endif /* XS_REGEX_H */