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