NeoMutt  2025-12-11-694-ga89709
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
regex.c
Go to the documentation of this file.
1
24
36
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 {
132 regerror(rc, reg->regex, err->data, err->dsize);
133 buf_fix_dptr(err);
134 }
135 regex_free(&reg);
136 return NULL;
137 }
138
139 return reg;
140}
141
145static int regex_string_set(void *var, struct ConfigDef *cdef,
146 const char *value, struct Buffer *err)
147{
148 /* Store empty regexes as NULL */
149 if (value && (value[0] == '\0'))
150 value = NULL;
151
152 struct Regex *r = NULL;
153
154 int rc = CSR_SUCCESS;
155
156 if (var)
157 {
158 struct Regex *curval = *(struct Regex **) var;
159 if (curval && mutt_str_equal(value, curval->pattern))
161
162 if (startup_only(cdef, err))
164
165 if (value)
166 {
167 r = regex_new(value, cdef->type, err);
168 if (!r)
169 return CSR_ERR_INVALID;
170 }
171
172 if (cdef->validator)
173 {
174 rc = cdef->validator(cdef, (intptr_t) r, err);
175
176 if (CSR_RESULT(rc) != CSR_SUCCESS)
177 {
178 regex_free(&r);
179 return rc | CSR_INV_VALIDATOR;
180 }
181 }
182
183 regex_destroy(var, cdef);
184
185 *(struct Regex **) var = r;
186
187 if (!r)
188 rc |= CSR_SUC_EMPTY;
189 }
190 else
191 {
192 if (cdef->type & D_INTERNAL_INITIAL_SET)
193 FREE(&cdef->initial);
194
196 cdef->initial = (intptr_t) mutt_str_dup(value);
197 }
198
199 return rc;
200}
201
205static int regex_string_get(void *var, const struct ConfigDef *cdef, struct Buffer *result)
206{
207 const char *str = NULL;
208
209 if (var)
210 {
211 struct Regex *r = *(struct Regex **) var;
212 if (r)
213 str = r->pattern;
214 }
215 else
216 {
217 str = (char *) cdef->initial;
218 }
219
220 if (!str)
221 return CSR_SUCCESS | CSR_SUC_EMPTY; /* empty string */
222
223 buf_addstr(result, str);
224 return CSR_SUCCESS;
225}
226
230static int regex_native_set(void *var, const struct ConfigDef *cdef,
231 intptr_t value, struct Buffer *err)
232{
233 int rc;
234
235 if (regex_equal(*(struct Regex **) var, (struct Regex *) value))
237
238 if (startup_only(cdef, err))
240
241 if (cdef->validator)
242 {
243 rc = cdef->validator(cdef, value, err);
244
245 if (CSR_RESULT(rc) != CSR_SUCCESS)
246 return rc | CSR_INV_VALIDATOR;
247 }
248
249 rc = CSR_SUCCESS;
250 struct Regex *orig = (struct Regex *) value;
251 struct Regex *r = NULL;
252
253 if (orig && orig->pattern)
254 {
255 const uint32_t flags = orig->pat_not ? D_REGEX_ALLOW_NOT : 0;
256 r = regex_new(orig->pattern, flags, err);
257 if (!r)
258 rc = CSR_ERR_INVALID;
259 }
260 else
261 {
262 rc |= CSR_SUC_EMPTY;
263 }
264
265 if (CSR_RESULT(rc) == CSR_SUCCESS)
266 {
267 regex_free(var);
268 *(struct Regex **) var = r;
269 }
270
271 return rc;
272}
273
277static intptr_t regex_native_get(void *var, const struct ConfigDef *cdef, struct Buffer *err)
278{
279 struct Regex *r = *(struct Regex **) var;
280
281 return (intptr_t) r;
282}
283
287static bool regex_has_been_set(void *var, const struct ConfigDef *cdef)
288{
289 const char *initial = (const char *) cdef->initial;
290
291 struct Regex *currx = *(struct Regex **) var;
292 const char *curval = currx ? currx->pattern : NULL;
293
294 return !mutt_str_equal(initial, curval);
295}
296
300static int regex_reset(void *var, const struct ConfigDef *cdef, struct Buffer *err)
301{
302 struct Regex *r = NULL;
303 const char *initial = (const char *) cdef->initial;
304
305 struct Regex *currx = *(struct Regex **) var;
306 const char *curval = currx ? currx->pattern : NULL;
307
308 int rc = CSR_SUCCESS;
309 if (!currx)
310 rc |= CSR_SUC_EMPTY;
311
312 if (mutt_str_equal(initial, curval))
313 return rc | CSR_SUC_NO_CHANGE;
314
315 if (startup_only(cdef, err))
317
318 if (initial)
319 {
320 r = regex_new(initial, cdef->type, err);
321 if (!r)
322 return CSR_ERR_CODE;
323 }
324
325 if (cdef->validator)
326 {
327 rc = cdef->validator(cdef, (intptr_t) r, err);
328
329 if (CSR_RESULT(rc) != CSR_SUCCESS)
330 {
331 regex_destroy(&r, cdef);
332 return rc | CSR_INV_VALIDATOR;
333 }
334 }
335
336 if (!r)
337 rc |= CSR_SUC_EMPTY;
338
339 regex_destroy(var, cdef);
340
341 *(struct Regex **) var = r;
342 return rc;
343}
344
348const struct ConfigSetType CstRegex = {
349 DT_REGEX,
350 "regex",
355 NULL, // string_plus_equals
356 NULL, // string_minus_equals
360};
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition buffer.c:182
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:226
const struct ConfigSetType CstRegex
Config type representing a regular expression.
Definition regex.c:348
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
void regex_free(struct Regex **ptr)
Free a Regex object.
Definition regex.c:68
A collection of config items.
static bool startup_only(const struct ConfigDef *cdef, struct Buffer *err)
Validator function for D_ON_STARTUP.
Definition set.h:296
#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)
Extract the result code from CSR_* flags.
Definition set.h:53
#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?
Definition regex.c:287
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:277
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:230
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:300
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:205
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:145
bool mutt_mb_is_lower(const char *s)
Does a multi-byte string contain only lowercase characters?
Definition mbyte.c:355
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
Convenience wrapper for the library headers.
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:665
Type representing a regular expression.
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition regex3.h:49
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
int(* validator)(const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
Definition set.h:82
intptr_t initial
Initial value.
Definition set.h:68
uint32_t type
Variable type, e.g. DT_STRING.
Definition set.h:67
Cached regular expression.
Definition regex3.h:85
char * pattern
printable version
Definition regex3.h:86
bool pat_not
do not match
Definition regex3.h:88
regex_t * regex
compiled expression
Definition regex3.h:87
Constants for all the config types.
#define D_REGEX_ALLOW_NOT
Regex can begin with '!'.
Definition types.h:107
#define D_REGEX_MATCH_CASE
Case-sensitive matching.
Definition types.h:106
#define D_INTERNAL_INITIAL_SET
Config item must have its initial value freed.
Definition types.h:90
@ DT_REGEX
regular expressions
Definition types.h:41
#define D_REGEX_NOSUB
Do not report what was matched (REG_NOSUB)
Definition types.h:108