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