NeoMutt  2025-01-09-117-gace867
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
regex.c
Go to the documentation of this file.
1
37#include "config.h"
38#include <stdbool.h>
39#include <stddef.h>
40#include <stdint.h>
41#include "mutt/lib.h"
42#include "regex2.h"
43#include "set.h"
44#include "types.h"
45
52bool regex_equal(const struct Regex *a, const struct Regex *b)
53{
54 if (!a && !b) /* both empty */
55 return true;
56 if (!a ^ !b) /* one is empty, but not the other */
57 return false;
58 if (a->pat_not != b->pat_not)
59 return false;
60
61 return mutt_str_equal(a->pattern, b->pattern);
62}
63
68void regex_free(struct Regex **ptr)
69{
70 if (!ptr || !*ptr)
71 return;
72
73 struct Regex *rx = *ptr;
74 FREE(&rx->pattern);
75 if (rx->regex)
76 regfree(rx->regex);
77 FREE(&rx->regex);
78
79 FREE(ptr);
80}
81
85static void regex_destroy(void *var, const struct ConfigDef *cdef)
86{
87 struct Regex **r = var;
88 if (!*r)
89 return;
90
91 regex_free(r);
92}
93
102struct Regex *regex_new(const char *str, uint32_t flags, struct Buffer *err)
103{
104 if (!str)
105 return NULL;
106
107 int rflags = 0;
108 struct Regex *reg = MUTT_MEM_CALLOC(1, struct Regex);
109
110 reg->regex = MUTT_MEM_CALLOC(1, regex_t);
111 reg->pattern = mutt_str_dup(str);
112
113 /* Should we use smart case matching? */
114 if (((flags & D_REGEX_MATCH_CASE) == 0) && mutt_mb_is_lower(str))
115 rflags |= REG_ICASE;
116
117 if ((flags & D_REGEX_NOSUB))
118 rflags |= REG_NOSUB;
119
120 /* Is a prefix of '!' allowed? */
121 if (((flags & D_REGEX_ALLOW_NOT) != 0) && (str[0] == '!'))
122 {
123 reg->pat_not = true;
124 str++;
125 }
126
127 int rc = REG_COMP(reg->regex, str, rflags);
128 if (rc != 0)
129 {
130 if (err)
131 regerror(rc, reg->regex, err->data, err->dsize);
132 regex_free(&reg);
133 return NULL;
134 }
135
136 return reg;
137}
138
142static int regex_string_set(void *var, struct ConfigDef *cdef,
143 const char *value, struct Buffer *err)
144{
145 /* Store empty regexes as NULL */
146 if (value && (value[0] == '\0'))
147 value = NULL;
148
149 struct Regex *r = NULL;
150
151 int rc = CSR_SUCCESS;
152
153 if (var)
154 {
155 struct Regex *curval = *(struct Regex **) var;
156 if (curval && mutt_str_equal(value, curval->pattern))
158
159 if (startup_only(cdef, err))
161
162 if (value)
163 {
164 r = regex_new(value, cdef->type, err);
165 if (!r)
166 return CSR_ERR_INVALID;
167 }
168
169 if (cdef->validator)
170 {
171 rc = cdef->validator(cdef, (intptr_t) r, err);
172
173 if (CSR_RESULT(rc) != CSR_SUCCESS)
174 {
175 regex_free(&r);
176 return rc | CSR_INV_VALIDATOR;
177 }
178 }
179
180 regex_destroy(var, cdef);
181
182 *(struct Regex **) var = r;
183
184 if (!r)
185 rc |= CSR_SUC_EMPTY;
186 }
187 else
188 {
189 if (cdef->type & D_INTERNAL_INITIAL_SET)
190 FREE(&cdef->initial);
191
193 cdef->initial = (intptr_t) mutt_str_dup(value);
194 }
195
196 return rc;
197}
198
202static int regex_string_get(void *var, const struct ConfigDef *cdef, struct Buffer *result)
203{
204 const char *str = NULL;
205
206 if (var)
207 {
208 struct Regex *r = *(struct Regex **) var;
209 if (r)
210 str = r->pattern;
211 }
212 else
213 {
214 str = (char *) cdef->initial;
215 }
216
217 if (!str)
218 return CSR_SUCCESS | CSR_SUC_EMPTY; /* empty string */
219
220 buf_addstr(result, str);
221 return CSR_SUCCESS;
222}
223
227static int regex_native_set(void *var, const struct ConfigDef *cdef,
228 intptr_t value, struct Buffer *err)
229{
230 int rc;
231
232 if (regex_equal(*(struct Regex **) var, (struct Regex *) value))
234
235 if (startup_only(cdef, err))
237
238 if (cdef->validator)
239 {
240 rc = cdef->validator(cdef, value, err);
241
242 if (CSR_RESULT(rc) != CSR_SUCCESS)
243 return rc | CSR_INV_VALIDATOR;
244 }
245
246 rc = CSR_SUCCESS;
247 struct Regex *orig = (struct Regex *) value;
248 struct Regex *r = NULL;
249
250 if (orig && orig->pattern)
251 {
252 const uint32_t flags = orig->pat_not ? D_REGEX_ALLOW_NOT : 0;
253 r = regex_new(orig->pattern, flags, err);
254 if (!r)
255 rc = CSR_ERR_INVALID;
256 }
257 else
258 {
259 rc |= CSR_SUC_EMPTY;
260 }
261
262 if (CSR_RESULT(rc) == CSR_SUCCESS)
263 {
264 regex_free(var);
265 *(struct Regex **) var = r;
266 }
267
268 return rc;
269}
270
274static intptr_t regex_native_get(void *var, const struct ConfigDef *cdef, struct Buffer *err)
275{
276 struct Regex *r = *(struct Regex **) var;
277
278 return (intptr_t) r;
279}
280
284static bool regex_has_been_set(void *var, const struct ConfigDef *cdef)
285{
286 const char *initial = (const char *) cdef->initial;
287
288 struct Regex *currx = *(struct Regex **) var;
289 const char *curval = currx ? currx->pattern : NULL;
290
291 return !mutt_str_equal(initial, curval);
292}
293
297static int regex_reset(void *var, const struct ConfigDef *cdef, struct Buffer *err)
298{
299 struct Regex *r = NULL;
300 const char *initial = (const char *) cdef->initial;
301
302 struct Regex *currx = *(struct Regex **) var;
303 const char *curval = currx ? currx->pattern : NULL;
304
305 int rc = CSR_SUCCESS;
306 if (!currx)
307 rc |= CSR_SUC_EMPTY;
308
309 if (mutt_str_equal(initial, curval))
310 return rc | CSR_SUC_NO_CHANGE;
311
312 if (startup_only(cdef, err))
314
315 if (initial)
316 {
317 r = regex_new(initial, cdef->type, err);
318 if (!r)
319 return CSR_ERR_CODE;
320 }
321
322 if (cdef->validator)
323 {
324 rc = cdef->validator(cdef, (intptr_t) r, err);
325
326 if (CSR_RESULT(rc) != CSR_SUCCESS)
327 {
328 regex_destroy(&r, cdef);
329 return rc | CSR_INV_VALIDATOR;
330 }
331 }
332
333 if (!r)
334 rc |= CSR_SUC_EMPTY;
335
336 regex_destroy(var, cdef);
337
338 *(struct Regex **) var = r;
339 return rc;
340}
341
345const struct ConfigSetType CstRegex = {
346 DT_REGEX,
347 "regex",
352 NULL, // string_plus_equals
353 NULL, // string_minus_equals
357};
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:226
struct Regex * regex_new(const char *str, uint32_t flags, struct Buffer *err)
Create an Regex from a string.
Definition: regex.c:102
bool regex_equal(const struct Regex *a, const struct Regex *b)
Compare two regexes.
Definition: regex.c:52
const struct ConfigSetType CstRegex
Config type representing a regular expression.
Definition: regex.c:345
void regex_free(struct Regex **ptr)
Free a Regex object.
Definition: regex.c:68
static bool startup_only(const struct ConfigDef *cdef, struct Buffer *err)
Validator function for D_ON_STARTUP.
Definition: set.h:293
#define CSR_ERR_INVALID
Value hasn't been set.
Definition: set.h:36
#define CSR_INV_VALIDATOR
Value was rejected by the validator.
Definition: set.h:46
#define CSR_SUC_NO_CHANGE
The value hasn't changed.
Definition: set.h:42
#define CSR_ERR_CODE
Problem with the code.
Definition: set.h:34
#define CSR_RESULT(x)
Definition: set.h:50
#define CSR_SUC_EMPTY
Value is empty/unset.
Definition: set.h:40
#define CSR_SUCCESS
Action completed successfully.
Definition: set.h:33
static void regex_destroy(void *var, const struct ConfigDef *cdef)
Destroy a Regex object - Implements ConfigSetType::destroy() -.
Definition: regex.c:85
static bool regex_has_been_set(void *var, const struct ConfigDef *cdef)
Is the config value different to its initial value? - Implements ConfigSetType::has_been_set() -.
Definition: regex.c:284
static intptr_t regex_native_get(void *var, const struct ConfigDef *cdef, struct Buffer *err)
Get a Regex object from a Regex config item - Implements ConfigSetType::native_get() -.
Definition: regex.c:274
static int regex_native_set(void *var, const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
Set a Regex config item by Regex object - Implements ConfigSetType::native_set() -.
Definition: regex.c:227
static int regex_reset(void *var, const struct ConfigDef *cdef, struct Buffer *err)
Reset a Regex to its initial value - Implements ConfigSetType::reset() -.
Definition: regex.c:297
static int regex_string_get(void *var, const struct ConfigDef *cdef, struct Buffer *result)
Get a Regex as a string - Implements ConfigSetType::string_get() -.
Definition: regex.c:202
static int regex_string_set(void *var, struct ConfigDef *cdef, const char *value, struct Buffer *err)
Set a Regex by string - Implements ConfigSetType::string_set() -.
Definition: regex.c:142
bool mutt_mb_is_lower(const char *s)
Does a multi-byte string contain only lowercase characters?
Definition: mbyte.c:354
#define FREE(x)
Definition: memory.h:55
#define MUTT_MEM_CALLOC(n, type)
Definition: memory.h:40
Convenience wrapper for the library headers.
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:254
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:661
Parse the 'set' command.
Type representing a regular expression.
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition: regex3.h:50
String manipulation buffer.
Definition: buffer.h:36
size_t dsize
Length of data.
Definition: buffer.h:39
char * data
Pointer to data.
Definition: buffer.h:37
Definition: set.h:62
int(* validator)(const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
Definition: set.h:79
intptr_t initial
Initial value.
Definition: set.h:65
uint32_t type
Variable type, e.g. DT_STRING.
Definition: set.h:64
Cached regular expression.
Definition: regex3.h:86
char * pattern
printable version
Definition: regex3.h:87
bool pat_not
do not match
Definition: regex3.h:89
regex_t * regex
compiled expression
Definition: regex3.h:88
Constants for all the config types.
#define D_REGEX_ALLOW_NOT
Regex can begin with '!'.
Definition: types.h:106
#define D_REGEX_MATCH_CASE
Case-sensitive matching.
Definition: types.h:105
#define D_INTERNAL_INITIAL_SET
Config item must have its initial value freed.
Definition: types.h:89
@ DT_REGEX
regular expressions
Definition: types.h:41
#define D_REGEX_NOSUB
Do not report what was matched (REG_NOSUB)
Definition: types.h:107