NeoMutt  2024-04-25-76-g20fe7b
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
parse.c File Reference

Expando Parsing. More...

#include "config.h"
#include <stdbool.h>
#include <stdio.h>
#include "mutt/lib.h"
#include "parse.h"
#include "definition.h"
#include "helpers.h"
#include "node.h"
#include "node_condbool.h"
#include "node_condition.h"
#include "node_expando.h"
#include "node_padding.h"
#include "node_text.h"
+ Include dependency graph for parse.c:

Go to the source code of this file.

Functions

static const char * skip_until_if_true_end (const char *start, char end_terminator)
 Search for the end of an 'if true' condition.
 
static const char * skip_until_if_false_end (const char *start, char end_terminator)
 Search for the end of an 'if false' condition.
 
struct ExpandoNodenode_parse (const char *str, const char *end, enum ExpandoConditionStart condition_start, const char **parsed_until, const struct ExpandoDefinition *defs, struct ExpandoParseError *error)
 Parse a format string into ExpandoNodes.
 
void node_tree_parse (struct ExpandoNode **root, const char *string, const struct ExpandoDefinition *defs, struct ExpandoParseError *error)
 Parse a format string into ExpandoNodes.
 

Detailed Description

Expando Parsing.

Authors
  • Tóth János
  • Richard Russon

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file parse.c.

Function Documentation

◆ skip_until_if_true_end()

static const char * skip_until_if_true_end ( const char *  start,
char  end_terminator 
)
static

Search for the end of an 'if true' condition.

Parameters
startStart of string
end_terminatorTerminator character
Return values
ptrPosition of terminator character, or end-of-string

Definition at line 50 of file parse.c.

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}
+ Here is the caller graph for this function:

◆ skip_until_if_false_end()

static const char * skip_until_if_false_end ( const char *  start,
char  end_terminator 
)
static

Search for the end of an 'if false' condition.

Parameters
startStart of string
end_terminatorTerminator character
Return values
ptrPosition of terminator character, or end-of-string

Definition at line 85 of file parse.c.

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}
+ Here is the caller graph for this function:

◆ node_parse()

struct ExpandoNode * node_parse ( const char *  str,
const char *  end,
enum ExpandoConditionStart  condition_start,
const char **  parsed_until,
const struct ExpandoDefinition defs,
struct ExpandoParseError error 
)

Parse a format string into ExpandoNodes.

Parameters
[in]strStart of string to parse
[in]endEnd of string to parse
[in]condition_startFlag for conditional expandos
[out]parsed_untilFirst character after parsed string
[in]defsExpando definitions
[out]errorBuffer for errors
Return values
ptrTree of ExpandoNodes representing the format string

Definition at line 124 of file parse.c.

129{
130 while (*str && (end ? (str <= end) : 1))
131 {
132 // %X -> expando
133 // if there is condition like <X..., the `%` is implicit
134 if ((*str == '%') ||
135 ((condition_start == CON_START) && ((*str == '?') || (*str == '<'))))
136 {
137 str++;
138
139 // %% -> "%"s
140 if (*str == '%')
141 {
142 *parsed_until = str + 1;
143 return node_text_new(str, str + 1);
144 }
145 // conditional
146 else if ((*str == '?') || (*str == '<'))
147 {
148 bool old_style = (*str == '?');
149 char end_terminator = old_style ? '?' : '>';
150
151 const char *cond_end = skip_until_ch(str, '?');
152 const char *next = NULL;
153 struct ExpandoNode *condition = node_parse(str, cond_end, CON_START,
154 &next, defs, error);
155 if (!condition)
156 return NULL;
157
158 if (*next != '?')
159 {
160 error->position = next;
161 snprintf(error->message, sizeof(error->message),
162 // L10N: Expando is missing a terminator character
163 // e.g. "%[..." is missing the final ']'
164 _("Conditional expando is missing '%c'"), '?');
165 node_free(&condition);
166 return NULL;
167 }
168
169 str = next + 1;
170
171 const char *start_true = str;
172 // nested if-else only allowed in the new style
173 const char *end_true = skip_until_if_true_end(str, end_terminator);
174 bool only_true = (*end_true == end_terminator);
175 bool invalid = ((*end_true != '&') && !only_true);
176
177 if (invalid)
178 {
179 error->position = end_true;
180 snprintf(error->message, sizeof(error->message),
181 // L10N: Expando is missing a terminator character
182 // e.g. "%[..." is missing the final ']'
183 _("Conditional expando is missing '&' or '%c'"), end_terminator);
184 node_free(&condition);
185 return NULL;
186 }
187
188 const char *if_true_parsed = NULL;
189 struct ExpandoNode *node_true = NULL;
190
191 while (start_true < end_true)
192 {
193 struct ExpandoNode *node = node_parse(start_true, end_true, CON_NO_CONDITION,
194 &if_true_parsed, defs, error);
195 if (!node)
196 {
197 node_free(&condition);
198 return NULL;
199 }
200
201 node_append(&node_true, node);
202
203 start_true = if_true_parsed;
204 }
205
206 if ((start_true == end_true) && !node_true)
207 {
208 node_true = node_new();
209 }
210
211 if (only_true)
212 {
213 *parsed_until = end_true + 1;
214 return node_condition_new(condition, node_true, NULL);
215 }
216 else
217 {
218 const char *start_false = end_true + 1;
219 // nested if-else only allowed in the new style
220 const char *end_false = skip_until_if_false_end(start_false, end_terminator);
221
222 if (*end_false != end_terminator)
223 {
224 error->position = start_false;
225 snprintf(error->message, sizeof(error->message),
226 // L10N: Expando is missing a terminator character
227 // e.g. "%[..." is missing the final ']'
228 _("Conditional expando is missing '%c'"), end_terminator);
229 node_free(&node_true);
230 node_free(&condition);
231 return NULL;
232 }
233
234 const char *if_false_parsed = NULL;
235 struct ExpandoNode *node_false = NULL;
236
237 while (start_false < end_false)
238 {
239 struct ExpandoNode *node = node_parse(start_false, end_false, CON_NO_CONDITION,
240 &if_false_parsed, defs, error);
241 if (!node)
242 {
243 node_free(&node_true);
244 node_free(&condition);
245 return NULL;
246 }
247
248 node_append(&node_false, node);
249
250 start_false = if_false_parsed;
251 }
252
253 if ((start_false == end_false) && !node_false)
254 {
255 node_false = node_new();
256 }
257
258 *parsed_until = end_false + 1;
259 return node_condition_new(condition, node_true, node_false);
260 }
261 }
262 else // expando
263 {
264 ExpandoParserFlags flags = (condition_start == CON_START) ? EP_CONDITIONAL : EP_NO_FLAGS;
265 struct ExpandoNode *node = NULL;
266 if (flags & EP_CONDITIONAL)
267 {
268 node = node_condbool_parse(str, parsed_until, defs, flags, error);
269 }
270 else
271 {
272 node = node_expando_parse(str, parsed_until, defs, flags, error);
273 }
274
275 if (!node || error->position)
276 {
277 node_free(&node);
278 return NULL;
279 }
280
281 return node;
282 }
283 }
284 else // text
285 {
286 return node_text_parse(str, end, parsed_until);
287 }
288 }
289
290 ASSERT(false && "Internal parsing error"); // LCOV_EXCL_LINE
291 return NULL;
292}
#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
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 *error)
Parse a format string into ExpandoNodes.
Definition: parse.c:124
struct ExpandoNode * node_condbool_parse(const char *str, const char **parsed_until, const struct ExpandoDefinition *defs, ExpandoParserFlags flags, struct ExpandoParseError *error)
Parse a CondBool format string - Implements ExpandoDefinition::parse() -.
Definition: node_condbool.c:67
#define _(a)
Definition: message.h:28
struct ExpandoNode * node_new(void)
Create a new empty ExpandoNode.
Definition: node.c:39
void node_append(struct ExpandoNode **root, struct ExpandoNode *new_node)
Append an ExpandoNode to an existing node.
Definition: node.c:116
void node_free(struct ExpandoNode **ptr)
Free an ExpandoNode and its private data.
Definition: node.c:48
struct ExpandoNode * node_condition_new(struct ExpandoNode *condition, struct ExpandoNode *node_true, struct ExpandoNode *node_false)
Create a new Condition Expando Node.
@ CON_NO_CONDITION
Parser is not currently in a condition.
@ CON_START
Parser is working on a condition.
struct ExpandoNode * node_expando_parse(const char *str, const char **parsed_until, const struct ExpandoDefinition *defs, ExpandoParserFlags flags, struct ExpandoParseError *error)
Parse an Expando format string.
Definition: node_expando.c:237
struct ExpandoNode * node_text_new(const char *start, const char *end)
Create a new Text ExpandoNode.
Definition: node_text.c:58
struct ExpandoNode * node_text_parse(const char *str, const char *end, const char **parsed_until)
Extract a block of text.
Definition: node_text.c:104
#define ASSERT(COND)
Definition: signal2.h:58
Basic Expando Node.
Definition: node.h:69
const char * end
End of string data.
Definition: node.h:80
struct ExpandoNode * next
Linked list.
Definition: node.h:71
const char * position
Position of error in original string.
Definition: parse.h:36
char message[256]
Error message.
Definition: parse.h:35
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ node_tree_parse()

void node_tree_parse ( struct ExpandoNode **  root,
const char *  string,
const struct ExpandoDefinition defs,
struct ExpandoParseError error 
)

Parse a format string into ExpandoNodes.

Parameters
[in,out]rootParent ExpandoNode
[in]stringString to parse
[in]defsExpando definitions
[out]errorBuffer for errors

Definition at line 301 of file parse.c.

303{
304 if (!string || !*string)
305 {
306 node_append(root, node_new());
307 return;
308 }
309
310 const char *end = NULL;
311 const char *start = string;
312
313 while (*start)
314 {
315 struct ExpandoNode *node = node_parse(start, NULL, CON_NO_CONDITION, &end, defs, error);
316 if (!node)
317 break;
318
319 node_append(root, node);
320 start = end;
321 }
322
323 node_padding_repad(root);
324}
void node_padding_repad(struct ExpandoNode **parent)
Rearrange Padding in a tree of ExpandoNodes.
Definition: node_padding.c:275
const char * start
Start of string data.
Definition: node.h:79
+ Here is the call graph for this function:
+ Here is the caller graph for this function: