2.52 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
  7xs_list *xs_regex_split_n(const char *str, const char *rx, int count);
  8#define xs_regex_split(str, rx) xs_regex_split_n(str, rx, XS_ALL)
  9xs_list *xs_regex_select_n(const char *str, const char *rx, int count);
 10#define xs_regex_select(str, rx) xs_regex_select_n(str, rx, XS_ALL)
 11xs_list *xs_regex_replace_in(xs_str *str, const char *rx, const char *rep, int count);
 12#define xs_regex_replace_i(str, rx, rep) xs_regex_replace_in(str, rx, rep, XS_ALL)
 13#define xs_regex_replace_n(str, rx, rep, count) xs_regex_replace_in(xs_dup(str), rx, rep, count)
 14#define xs_regex_replace(str, rx, rep) xs_regex_replace_in(xs_dup(str), rx, rep, XS_ALL)
 15
 16#ifdef XS_IMPLEMENTATION
 17
 18#include <regex.h>
 19
 20xs_list *xs_regex_split_n(const char *str, const char *rx, int count)
 21/* splits str by regex */
 22{
 23    regex_t re;
 24    regmatch_t rm;
 25    int offset = 0;
 26    xs_list *list = NULL;
 27    const char *p;
 28
 29    if (regcomp(&re, rx, REG_EXTENDED))
 30        return NULL;
 31
 32    list = xs_list_new();
 33
 34    while (count > 0 && !regexec(&re, (p = str + offset), 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, s1);
 38
 39        /* add now the matched text as the separator */
 40        xs *s2 = xs_str_new_sz(p + rm.rm_so, rm.rm_eo - rm.rm_so);
 41        list = xs_list_append(list, s2);
 42
 43        /* move forward */
 44        offset += rm.rm_eo;
 45
 46        count--;
 47    }
 48
 49    /* add the rest of the string */
 50    list = xs_list_append(list, p);
 51
 52    regfree(&re);
 53
 54    return list;
 55}
 56
 57
 58xs_list *xs_regex_select_n(const char *str, const char *rx, int count)
 59/* selects all matches and return them as a list */
 60{
 61    xs_list *list = xs_list_new();
 62    xs *split = NULL;
 63    xs_list *p;
 64    xs_val *v;
 65    int n = 0;
 66
 67    /* split */
 68    split = xs_regex_split_n(str, rx, count);
 69
 70    /* now iterate to get only the 'separators' (odd ones) */
 71    p = split;
 72    while (xs_list_iter(&p, &v)) {
 73        if (n & 0x1)
 74            list = xs_list_append(list, v);
 75
 76        n++;
 77    }
 78
 79    return list;
 80}
 81
 82
 83xs_list *xs_regex_replace_in(xs_str *str, const char *rx, const char *rep, int count)
 84/* replaces all matches with the rep string. If it contains unescaped &,
 85   they are replaced with the match */
 86{
 87    xs_str *s = xs_str_new(NULL);
 88    xs *split = xs_regex_split_n(str, rx, count);
 89    xs_list *p;
 90    xs_val *v;
 91    int n = 0;
 92    int pholder = !!strchr(rep, '&');
 93
 94    p = split;
 95    while (xs_list_iter(&p, &v)) {
 96        if (n & 0x1) {
 97            if (pholder) {
 98                /* rep has a placeholder; process char by char */
 99                const char *p = rep;
100
101                while (*p) {
102                    if (*p == '&')
103                        s = xs_str_cat(s, v);
104                    else {
105                        if (*p == '\\')
106                            p++;
107
108                        if (!*p)
109                            break;
110
111                        s = xs_append_m(s, p, 1);
112                    }
113
114                    p++;
115                }
116            }
117            else
118                s = xs_str_cat(s, rep);
119        }
120        else
121            s = xs_str_cat(s, v);
122
123        n++;
124    }
125
126    xs_free(str);
127
128    return s;
129}
130
131#endif /* XS_IMPLEMENTATION */
132
133#endif /* XS_REGEX_H */