NeoMutt  2024-10-02-37-gfa9146
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
parse.c
Go to the documentation of this file.
1
30#include "config.h"
31#include <stdbool.h>
32#include <stdio.h>
33#include "mutt/lib.h"
34#include "parse.h"
35#include "definition.h"
36#include "helpers.h"
37#include "node.h"
38#include "node_condbool.h"
39#include "node_condition.h"
40#include "node_container.h"
41#include "node_expando.h"
42#include "node_text.h"
43
50static const char *skip_until_if_true_end(const char *start, char end_terminator)
51{
52 int ctr = 0;
53 char prev = '\0';
54 while (*start)
55 {
56 if ((ctr == 0) && (((*start == end_terminator) && (prev != '%')) || (*start == '&')))
57 {
58 break;
59 }
60
61 // handle nested if-else-s
62 if ((prev == '%') && (*start == '<'))
63 {
64 ctr++;
65 }
66
67 if ((*start == '>') && (prev != '%'))
68 {
69 ctr--;
70 }
71
72 prev = *start;
73 start++;
74 }
75
76 return start;
77}
78
85static const char *skip_until_if_false_end(const char *start, char end_terminator)
86{
87 int ctr = 0;
88 char prev = '\0';
89 while (*start)
90 {
91 if ((ctr == 0) && (*start == end_terminator) && (prev != '%'))
92 {
93 break;
94 }
95
96 // handle nested if-else-s
97 if ((prev == '%') && (*start == '<'))
98 {
99 ctr++;
100 }
101
102 if ((*start == '>') && (prev != '%'))
103 {
104 ctr--;
105 }
106
107 prev = *start;
108 start++;
109 }
110
111 return start;
112}
113
124struct ExpandoNode *node_parse(const char *str, const char *end,
125 enum ExpandoConditionStart condition_start,
126 const char **parsed_until,
127 const struct ExpandoDefinition *defs,
128 struct ExpandoParseError *err)
129{
130 if (!str || (*str == '\0'))
131 return NULL;
132
133 while (*str && (end ? (str <= end) : true))
134 {
135 // %X -> expando
136 // if there is condition like <X..., the `%` is implicit
137 if ((*str == '%') ||
138 ((condition_start == CON_START) && ((*str == '?') || (*str == '<'))))
139 {
140 str++;
141
142 // %% -> "%"s
143 if (*str == '%')
144 {
145 *parsed_until = str + 1;
146 return node_text_new(str, str + 1);
147 }
148 // conditional
149 else if ((*str == '?') || (*str == '<'))
150 {
151 bool old_style = (*str == '?');
152 char end_terminator = old_style ? '?' : '>';
153
154 const char *cond_end = skip_until_ch(str, '?');
155 const char *next = NULL;
156 struct ExpandoNode *node_cond = node_parse(str, cond_end, CON_START,
157 &next, defs, err);
158 if (!node_cond)
159 return NULL;
160
161 if (*next != '?')
162 {
163 err->position = next;
164 snprintf(err->message, sizeof(err->message),
165 // L10N: Expando is missing a terminator character
166 // e.g. "%[..." is missing the final ']'
167 _("Conditional expando is missing '%c'"), '?');
168 node_free(&node_cond);
169 return NULL;
170 }
171
172 str = next + 1;
173
174 const char *start_true = str;
175 // nested if-else only allowed in the new style
176 const char *end_true = skip_until_if_true_end(str, end_terminator);
177 bool only_true = (*end_true == end_terminator);
178 bool invalid = ((*end_true != '&') && !only_true);
179
180 if (invalid)
181 {
182 err->position = end_true;
183 snprintf(err->message, sizeof(err->message),
184 // L10N: Expando is missing a terminator character
185 // e.g. "%[..." is missing the final ']'
186 _("Conditional expando is missing '&' or '%c'"), end_terminator);
187 node_free(&node_cond);
188 return NULL;
189 }
190
191 const char *if_true_parsed = NULL;
192 struct ExpandoNode *node_true = node_container_new();
193
194 while (start_true < end_true)
195 {
196 struct ExpandoNode *node = node_parse(start_true, end_true, CON_NO_CONDITION,
197 &if_true_parsed, defs, err);
198 if (!node)
199 {
200 node_free(&node_true);
201 node_free(&node_cond);
202 return NULL;
203 }
204
205 node_add_child(node_true, node);
206
207 start_true = if_true_parsed;
208 }
209
210 if (only_true)
211 {
212 *parsed_until = end_true + 1;
213 return node_condition_new(node_cond, node_true, NULL);
214 }
215 else
216 {
217 const char *start_false = end_true + 1;
218 // nested if-else only allowed in the new style
219 const char *end_false = skip_until_if_false_end(start_false, end_terminator);
220
221 if (*end_false != end_terminator)
222 {
223 err->position = start_false;
224 snprintf(err->message, sizeof(err->message),
225 // L10N: Expando is missing a terminator character
226 // e.g. "%[..." is missing the final ']'
227 _("Conditional expando is missing '%c'"), end_terminator);
228 node_free(&node_true);
229 node_free(&node_cond);
230 return NULL;
231 }
232
233 const char *if_false_parsed = NULL;
234 struct ExpandoNode *node_false = node_container_new();
235
236 while (start_false < end_false)
237 {
238 struct ExpandoNode *node = node_parse(start_false, end_false, CON_NO_CONDITION,
239 &if_false_parsed, defs, err);
240 if (!node)
241 {
242 node_free(&node_true);
243 node_free(&node_false);
244 node_free(&node_cond);
245 return NULL;
246 }
247
248 node_add_child(node_false, node);
249
250 start_false = if_false_parsed;
251 }
252
253 *parsed_until = end_false + 1;
254 return node_condition_new(node_cond, node_true, node_false);
255 }
256 }
257 else // expando
258 {
259 ExpandoParserFlags flags = (condition_start == CON_START) ? EP_CONDITIONAL : EP_NO_FLAGS;
260 struct ExpandoNode *node = NULL;
261 if (flags & EP_CONDITIONAL)
262 {
263 node = node_condbool_parse(str, defs, flags, parsed_until, err);
264 }
265 else
266 {
267 node = node_expando_parse(str, defs, flags, parsed_until, err);
268 }
269
270 if (!node || err->position)
271 {
272 node_free(&node);
273 return NULL;
274 }
275
276 return node;
277 }
278 }
279 else // text
280 {
281 return node_text_parse(str, end, parsed_until);
282 }
283 }
284
285 ASSERT(false && "Internal parsing error"); // LCOV_EXCL_LINE
286 return NULL;
287}
Define an Expando format string.
#define EP_NO_FLAGS
No flags are set.
Definition: definition.h:42
uint8_t ExpandoParserFlags
Flags for expando_parse(), e.g. EP_CONDITIONAL.
Definition: definition.h:41
#define EP_CONDITIONAL
Expando is being used as a condition.
Definition: definition.h:43
const char * skip_until_ch(const char *start, char terminator)
Search a string for a terminator character.
Definition: helpers.c:94
Shared code.
static const char * skip_until_if_true_end(const char *start, char end_terminator)
Search for the end of an 'if true' condition.
Definition: parse.c:50
static const char * skip_until_if_false_end(const char *start, char end_terminator)
Search for the end of an 'if false' condition.
Definition: parse.c:85
struct ExpandoNode * node_parse(const char *str, const char *end, enum ExpandoConditionStart condition_start, const char **parsed_until, const struct ExpandoDefinition *defs, struct ExpandoParseError *err)
Parse a format string into ExpandoNodes.
Definition: parse.c:124
Expando Parsing.
struct ExpandoNode * node_condbool_parse(const char *str, const struct ExpandoDefinition *defs, ExpandoParserFlags flags, const char **parsed_until, struct ExpandoParseError *err)
Parse a CondBool format string - Implements ExpandoDefinition::parse() -.
Definition: node_condbool.c:63
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
void node_add_child(struct ExpandoNode *node, struct ExpandoNode *child)
Add a child to an ExpandoNode.
Definition: node.c:76
void node_free(struct ExpandoNode **ptr)
Free an ExpandoNode and its private data.
Definition: node.c:48
Basic Expando Node.
Expando Node for a Conditional Boolean.
struct ExpandoNode * node_condition_new(struct ExpandoNode *condition, struct ExpandoNode *node_true, struct ExpandoNode *node_false)
Create a new Condition Expando Node.
Expando Node for a Condition.
ExpandoConditionStart
Signals node_parse() if the parsing started in a conditional statement or not.
@ CON_NO_CONDITION
Parser is not currently in a condition.
@ CON_START
Parser is working on a condition.
struct ExpandoNode * node_container_new(void)
Create a new Container ExpandoNode.
Expando Node for a Container.
struct ExpandoNode * node_expando_parse(const char *str, const struct ExpandoDefinition *defs, ExpandoParserFlags flags, const char **parsed_until, struct ExpandoParseError *err)
Parse an Expando format string.
Definition: node_expando.c:229
Expando Node for an Expando.
struct ExpandoNode * node_text_new(const char *start, const char *end)
Create a new Text ExpandoNode.
Definition: node_text.c:57
struct ExpandoNode * node_text_parse(const char *str, const char *end, const char **parsed_until)
Extract a block of text.
Definition: node_text.c:102
Expando Node for Text.
#define ASSERT(COND)
Definition: signal2.h:58
Definition of a format string.
Definition: definition.h:52
Basic Expando Node.
Definition: node.h:67
Buffer for parsing errors.
Definition: parse.h:35
const char * position
Position of error in original string.
Definition: parse.h:37
char message[256]
Error message.
Definition: parse.h:36