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

Expando Parsing. More...

#include "config.h"
#include <assert.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.
 
static 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 51 of file parse.c.

52{
53 int ctr = 0;
54 char prev = '\0';
55 while (*start)
56 {
57 if ((ctr == 0) && (((*start == end_terminator) && (prev != '%')) || (*start == '&')))
58 {
59 break;
60 }
61
62 // handle nested if-else-s
63 if ((prev == '%') && (*start == '<'))
64 {
65 ctr++;
66 }
67
68 if ((*start == '>') && (prev != '%'))
69 {
70 ctr--;
71 }
72
73 prev = *start;
74 start++;
75 }
76
77 return start;
78}
+ 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 86 of file parse.c.

87{
88 int ctr = 0;
89 char prev = '\0';
90 while (*start)
91 {
92 if ((ctr == 0) && (*start == end_terminator) && (prev != '%'))
93 {
94 break;
95 }
96
97 // handle nested if-else-s
98 if ((prev == '%') && (*start == '<'))
99 {
100 ctr++;
101 }
102
103 if ((*start == '>') && (prev != '%'))
104 {
105 ctr--;
106 }
107
108 prev = *start;
109 start++;
110 }
111
112 return start;
113}
+ Here is the caller graph for this function:

◆ node_parse()

static 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 
)
static

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 125 of file parse.c.

130{
131 while (*str && (end ? (str <= end) : 1))
132 {
133 // %X -> expando
134 // if there is condition like <X..., the `%` is implicit
135 if ((*str == '%') ||
136 ((condition_start == CON_START) && ((*str == '?') || (*str == '<'))))
137 {
138 str++;
139
140 // %% -> "%"s
141 if (*str == '%')
142 {
143 *parsed_until = str + 1;
144 return node_text_new(str, str + 1);
145 }
146 // conditional
147 else if ((*str == '?') || (*str == '<'))
148 {
149 bool old_style = (*str == '?');
150 char end_terminator = old_style ? '?' : '>';
151
152 const char *cond_end = skip_until_ch(str, '?');
153 const char *next = NULL;
154 struct ExpandoNode *condition = node_parse(str, cond_end, CON_START,
155 &next, defs, error);
156 if (!condition)
157 return NULL;
158
159 if (*next != '?')
160 {
161 error->position = next;
162 snprintf(error->message, sizeof(error->message),
163 // L10N: Expando is missing a terminator character
164 // e.g. "%[..." is missing the final ']'
165 _("Conditional expando is missing '%c'"), '?');
166 node_free(&condition);
167 return NULL;
168 }
169
170 str = next + 1;
171
172 const char *if_true_start = str;
173 // nested if-else only allowed in the new style
174 const char *if_true_end = skip_until_if_true_end(str, end_terminator);
175 bool only_true = (*if_true_end == end_terminator);
176 bool invalid = ((*if_true_end != '&') && !only_true);
177
178 if (invalid)
179 {
180 error->position = if_true_end;
181 snprintf(error->message, sizeof(error->message),
182 // L10N: Expando is missing a terminator character
183 // e.g. "%[..." is missing the final ']'
184 _("Conditional expando is missing '&' or '%c'"), end_terminator);
185 node_free(&condition);
186 return NULL;
187 }
188
189 const char *if_true_parsed = NULL;
190 struct ExpandoNode *if_true_tree = NULL;
191
192 while (if_true_start < if_true_end)
193 {
194 struct ExpandoNode *node = node_parse(if_true_start, if_true_end, CON_NO_CONDITION,
195 &if_true_parsed, defs, error);
196 if (!node)
197 {
198 node_free(&condition);
199 return NULL;
200 }
201
202 node_append(&if_true_tree, node);
203
204 if_true_start = if_true_parsed;
205 }
206
207 if ((if_true_start == if_true_end) && !if_true_tree)
208 {
209 if_true_tree = node_new();
210 }
211
212 if (only_true)
213 {
214 *parsed_until = if_true_end + 1;
215 return node_condition_new(condition, if_true_tree, NULL);
216 }
217 else
218 {
219 const char *if_false_start = if_true_end + 1;
220 // nested if-else only allowed in the new style
221 const char *if_false_end = skip_until_if_false_end(if_false_start, end_terminator);
222
223 if (*if_false_end != end_terminator)
224 {
225 error->position = if_false_start;
226 snprintf(error->message, sizeof(error->message),
227 // L10N: Expando is missing a terminator character
228 // e.g. "%[..." is missing the final ']'
229 _("Conditional expando is missing '%c'"), end_terminator);
230 node_free(&if_true_tree);
231 node_free(&condition);
232 return NULL;
233 }
234
235 const char *if_false_parsed = NULL;
236 struct ExpandoNode *if_false_tree = NULL;
237
238 while (if_false_start < if_false_end)
239 {
240 struct ExpandoNode *node = node_parse(if_false_start, if_false_end, CON_NO_CONDITION,
241 &if_false_parsed, defs, error);
242 if (!node)
243 {
244 node_free(&if_true_tree);
245 node_free(&condition);
246 return NULL;
247 }
248
249 node_append(&if_false_tree, node);
250
251 if_false_start = if_false_parsed;
252 }
253
254 if ((if_false_start == if_false_end) && !if_false_tree)
255 {
256 if_false_tree = node_new();
257 }
258
259 *parsed_until = if_false_end + 1;
260 return node_condition_new(condition, if_true_tree, if_false_tree);
261 }
262 }
263 // expando
264 else
265 {
266 ExpandoParserFlags flags = (condition_start == CON_START) ? EP_CONDITIONAL : EP_NO_FLAGS;
267 struct ExpandoNode *node = NULL;
268 if (flags & EP_CONDITIONAL)
269 {
270 node = node_condbool_parse(str, parsed_until, defs, flags, error);
271 }
272 else
273 {
274 node = node_expando_parse(str, parsed_until, defs, flags, error);
275 }
276
277 if (!node || error->position)
278 {
279 node_free(&node);
280 return NULL;
281 }
282
283 return node;
284 }
285 }
286 // text
287 else
288 {
289 return node_text_parse(str, end, parsed_until);
290 }
291 }
292
293 assert(false && "Internal parsing error"); // LCOV_EXCL_LINE
294 return NULL;
295}
#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:95
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:51
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:86
static 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:125
struct ExpandoNode * node_condbool_parse(const char *s, 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:68
#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:133
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 *if_true_tree, struct ExpandoNode *if_false_tree)
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:238
struct ExpandoNode * node_text_new(const char *start, const char *end)
Create a new Text ExpandoNode.
Definition: node_text.c:59
struct ExpandoNode * node_text_parse(const char *str, const char *end, const char **parsed_until)
Extract a block of text.
Definition: node_text.c:105
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
char message[128]
Error message.
Definition: parse.h:35
const char * position
Position of error in original string.
Definition: parse.h:36
+ 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 304 of file parse.c.

306{
307 if (!string || !*string)
308 {
309 node_append(root, node_new());
310 return;
311 }
312
313 const char *end = NULL;
314 const char *start = string;
315
316 while (*start)
317 {
318 struct ExpandoNode *node = node_parse(start, NULL, CON_NO_CONDITION, &end, defs, error);
319 if (!node)
320 break;
321
322 node_append(root, node);
323 start = end;
324 }
325
326 node_padding_repad(root);
327}
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: