NeoMutt  2024-04-16-36-g75b6fb
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 <stddef.h>
39#include <stdbool.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(const struct ConfigSet *cs, 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, sizeof(struct Regex));
109
110 reg->regex = mutt_mem_calloc(1, sizeof(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(const struct ConfigSet *cs, 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(cs, 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(cs, 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(const struct ConfigSet *cs, void *var,
203 const struct ConfigDef *cdef, struct Buffer *result)
204{
205 const char *str = NULL;
206
207 if (var)
208 {
209 struct Regex *r = *(struct Regex **) var;
210 if (r)
211 str = r->pattern;
212 }
213 else
214 {
215 str = (char *) cdef->initial;
216 }
217
218 if (!str)
219 return CSR_SUCCESS | CSR_SUC_EMPTY; /* empty string */
220
221 buf_addstr(result, str);
222 return CSR_SUCCESS;
223}
224
228static int regex_native_set(const struct ConfigSet *cs, void *var,
229 const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
230{
231 int rc;
232
233 if (regex_equal(*(struct Regex **) var, (struct Regex *) value))
235
236 if (startup_only(cdef, err))
238
239 if (cdef->validator)
240 {
241 rc = cdef->validator(cs, cdef, value, err);
242
243 if (CSR_RESULT(rc) != CSR_SUCCESS)
244 return rc | CSR_INV_VALIDATOR;
245 }
246
247 rc = CSR_SUCCESS;
248 struct Regex *orig = (struct Regex *) value;
249 struct Regex *r = NULL;
250
251 if (orig && orig->pattern)
252 {
253 const uint32_t flags = orig->pat_not ? D_REGEX_ALLOW_NOT : 0;
254 r = regex_new(orig->pattern, flags, err);
255 if (!r)
256 rc = CSR_ERR_INVALID;
257 }
258 else
259 {
260 rc |= CSR_SUC_EMPTY;
261 }
262
263 if (CSR_RESULT(rc) == CSR_SUCCESS)
264 {
265 regex_free(var);
266 *(struct Regex **) var = r;
267 }
268
269 return rc;
270}
271
275static intptr_t regex_native_get(const struct ConfigSet *cs, void *var,
276 const struct ConfigDef *cdef, struct Buffer *err)
277{
278 struct Regex *r = *(struct Regex **) var;
279
280 return (intptr_t) r;
281}
282
286static int regex_reset(const struct ConfigSet *cs, void *var,
287 const struct ConfigDef *cdef, struct Buffer *err)
288{
289 struct Regex *r = NULL;
290 const char *initial = (const char *) cdef->initial;
291
292 struct Regex *currx = *(struct Regex **) var;
293 const char *curval = currx ? currx->pattern : NULL;
294
295 int rc = CSR_SUCCESS;
296 if (!currx)
297 rc |= CSR_SUC_EMPTY;
298
299 if (mutt_str_equal(initial, curval))
300 return rc | CSR_SUC_NO_CHANGE;
301
302 if (startup_only(cdef, err))
304
305 if (initial)
306 {
307 r = regex_new(initial, cdef->type, err);
308 if (!r)
309 return CSR_ERR_CODE;
310 }
311
312 if (cdef->validator)
313 {
314 rc = cdef->validator(cs, cdef, (intptr_t) r, err);
315
316 if (CSR_RESULT(rc) != CSR_SUCCESS)
317 {
318 regex_destroy(cs, &r, cdef);
319 return rc | CSR_INV_VALIDATOR;
320 }
321 }
322
323 if (!r)
324 rc |= CSR_SUC_EMPTY;
325
326 regex_destroy(cs, var, cdef);
327
328 *(struct Regex **) var = r;
329 return rc;
330}
331
335const struct ConfigSetType CstRegex = {
336 DT_REGEX,
337 "regex",
342 NULL, // string_plus_equals
343 NULL, // string_minus_equals
346};
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
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:335
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:296
#define CSR_ERR_INVALID
Value hasn't been set.
Definition: set.h:38
#define CSR_INV_VALIDATOR
Value was rejected by the validator.
Definition: set.h:48
#define CSR_SUC_NO_CHANGE
The value hasn't changed.
Definition: set.h:44
#define CSR_ERR_CODE
Problem with the code.
Definition: set.h:36
#define CSR_RESULT(x)
Definition: set.h:52
#define CSR_SUC_EMPTY
Value is empty/unset.
Definition: set.h:42
#define CSR_SUCCESS
Action completed successfully.
Definition: set.h:35
static void regex_destroy(const struct ConfigSet *cs, void *var, const struct ConfigDef *cdef)
Destroy a Regex object - Implements ConfigSetType::destroy() -.
Definition: regex.c:85
static intptr_t regex_native_get(const struct ConfigSet *cs, 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:275
static int regex_native_set(const struct ConfigSet *cs, 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:228
static int regex_reset(const struct ConfigSet *cs, void *var, const struct ConfigDef *cdef, struct Buffer *err)
Reset a Regex to its initial value - Implements ConfigSetType::reset() -.
Definition: regex.c:286
static int regex_string_get(const struct ConfigSet *cs, 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(const struct ConfigSet *cs, 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
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
#define FREE(x)
Definition: memory.h:45
Convenience wrapper for the library headers.
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:253
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:654
Parse the 'set' command.
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
Definition: set.h:64
int(* validator)(const struct ConfigSet *cs, const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
Definition: set.h:82
intptr_t initial
Initial value.
Definition: set.h:67
uint32_t type
Variable type, e.g. DT_STRING.
Definition: set.h:66
Container for lots of config items.
Definition: set.h:252
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:42
#define D_REGEX_NOSUB
Do not report what was matched (REG_NOSUB)
Definition: types.h:108