NeoMutt  2024-04-25-91-gb0e085
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
node_expando.c
Go to the documentation of this file.
1
30#include "config.h"
31#include <ctype.h>
32#include <limits.h>
33#include <stdio.h>
34#include "mutt/lib.h"
35#include "node_expando.h"
36#include "color/lib.h"
37#include "definition.h"
38#include "format.h"
39#include "helpers.h"
40#include "mutt_thread.h"
41#include "node.h"
42#include "parse.h"
43#include "render.h"
44
50{
51 struct NodeExpandoPrivate *priv = mutt_mem_calloc(1, sizeof(struct NodeExpandoPrivate));
52
53 // NOTE(g0mb4): Expando definition should contain this
54 priv->color = -1;
55
56 return priv;
57}
58
64{
65 if (!ptr || !*ptr)
66 return;
67
68 FREE(ptr);
69}
70
80struct ExpandoNode *node_expando_new(const char *start, const char *end,
81 struct ExpandoFormat *fmt, int did, int uid)
82{
83 struct ExpandoNode *node = node_new();
84
85 node->type = ENT_EXPANDO;
86 node->start = start;
87 node->end = end;
88
89 node->did = did;
90 node->uid = uid;
92
93 node->format = fmt;
94
97
98 return node;
99}
100
106void node_expando_set_color(const struct ExpandoNode *node, int cid)
107{
108 if (!node || (node->type != ENT_EXPANDO) || !node->ndata)
109 return;
110
111 struct NodeExpandoPrivate *priv = node->ndata;
112
113 priv->color = cid;
114}
115
121void node_expando_set_has_tree(const struct ExpandoNode *node, bool has_tree)
122{
123 if (!node || (node->type != ENT_EXPANDO) || !node->ndata)
124 return;
125
126 struct NodeExpandoPrivate *priv = node->ndata;
127
128 priv->has_tree = has_tree;
129}
130
140struct ExpandoFormat *parse_format(const char *start, const char *end,
141 struct ExpandoParseError *error)
142{
143 if (start == end)
144 return NULL;
145
146 struct ExpandoFormat *fmt = mutt_mem_calloc(1, sizeof(struct ExpandoFormat));
147
148 fmt->leader = ' ';
149 fmt->start = start;
150 fmt->end = end;
152 fmt->min_cols = 0;
153 fmt->max_cols = INT_MAX;
154
155 if (*start == '-')
156 {
158 start++;
159 }
160 else if (*start == '=')
161 {
163 start++;
164 }
165
166 if (*start == '0')
167 {
168 fmt->leader = '0';
169 start++;
170 }
171
172 if (isdigit(*start))
173 {
174 unsigned short number = 0;
175 const char *end_ptr = mutt_str_atous(start, &number);
176
177 // NOTE(g0mb4): start is NOT null-terminated
178 if (!end_ptr || (end_ptr > end) || (number == USHRT_MAX))
179 {
180 error->position = start;
181 snprintf(error->message, sizeof(error->message), _("Invalid number: %s"), start);
182 FREE(&fmt);
183 return NULL;
184 }
185
186 fmt->min_cols = number;
187 start = end_ptr;
188 };
189
190 if (*start == '.')
191 {
192 start++;
193
194 if (!isdigit(*start))
195 {
196 error->position = start;
197 // L10N: Expando format expected a number
198 snprintf(error->message, sizeof(error->message), _("Number is expected"));
199 FREE(&fmt);
200 return NULL;
201 }
202
203 unsigned short number = 0;
204 const char *end_ptr = mutt_str_atous(start, &number);
205
206 // NOTE(g0mb4): start is NOT null-terminated
207 if (!end_ptr || (end_ptr > end) || (number == USHRT_MAX))
208 {
209 error->position = start;
210 snprintf(error->message, sizeof(error->message), _("Invalid number: %s"), start);
211 FREE(&fmt);
212 return NULL;
213 }
214
215 fmt->max_cols = number;
216 start = end_ptr;
217 }
218
219 if (*start == '_')
220 {
221 fmt->lower = true;
222 start++;
223 }
224
225 return fmt;
226}
227
237struct ExpandoNode *node_expando_parse(const char *str, const char **parsed_until,
238 const struct ExpandoDefinition *defs,
239 ExpandoParserFlags flags,
240 struct ExpandoParseError *error)
241{
242 const struct ExpandoDefinition *definition = defs;
243
244 const char *format_end = skip_until_classic_expando(str);
245 const char *expando_end = skip_classic_expando(format_end, defs);
246 char expando[128] = { 0 };
247 const int expando_len = expando_end - format_end;
248 mutt_strn_copy(expando, format_end, expando_len, sizeof(expando));
249
250 struct ExpandoFormat *fmt = parse_format(str, format_end, error);
251 if (error->position)
252 {
253 FREE(&fmt);
254 return NULL;
255 }
256
257 while (definition && definition->short_name)
258 {
259 if (mutt_str_equal(definition->short_name, expando))
260 {
261 if (definition->parse && !(flags & EP_NO_CUSTOM_PARSE))
262 {
263 FREE(&fmt);
264 return definition->parse(str, parsed_until, definition->did,
265 definition->uid, flags, error);
266 }
267 else
268 {
269 *parsed_until = expando_end;
270 return node_expando_new(format_end, expando_end, fmt, definition->did,
271 definition->uid);
272 }
273 }
274
275 definition++;
276 }
277
278 error->position = format_end;
279 // L10N: e.g. "Unknown expando: %Q"
280 snprintf(error->message, sizeof(error->message), _("Unknown expando: %%%.*s"),
281 expando_len, format_end);
282 FREE(&fmt);
283 return NULL;
284}
285
296struct ExpandoNode *node_expando_parse_enclosure(const char *str, const char **parsed_until,
297 int did, int uid, char terminator,
298 struct ExpandoParseError *error)
299{
300 const char *format_end = skip_until_classic_expando(str);
301
302 format_end++; // skip opening char
303
304 const char *expando_end = skip_until_ch(format_end, terminator);
305
306 if (*expando_end != terminator)
307 {
308 error->position = expando_end;
309 snprintf(error->message, sizeof(error->message),
310 // L10N: Expando is missing a terminator character
311 // e.g. "%[..." is missing the final ']'
312 _("Expando is missing terminator: '%c'"), terminator);
313 return NULL;
314 }
315
316 // revert skipping for fmt
317 struct ExpandoFormat *fmt = parse_format(str, format_end - 1, error);
318 if (error->position)
319 {
320 FREE(&fmt);
321 return NULL;
322 }
323
324 *parsed_until = expando_end + 1;
325 return node_expando_new(format_end, expando_end, fmt, did, uid);
326}
327
333void add_color(struct Buffer *buf, enum ColorId cid)
334{
335 ASSERT(cid < MT_COLOR_MAX);
336
338 buf_addch(buf, cid);
339}
340
344int node_expando_render(const struct ExpandoNode *node,
345 const struct ExpandoRenderData *rdata, struct Buffer *buf,
346 int max_cols, void *data, MuttFormatFlags flags)
347{
348 ASSERT(node->type == ENT_EXPANDO);
349
350 struct Buffer *buf_expando = buf_pool_get();
351
352 const struct ExpandoRenderData *rd_match = find_get_string(rdata, node->did, node->uid);
353 if (rd_match)
354 {
355 rd_match->get_string(node, data, flags, max_cols, buf_expando);
356 }
357 else
358 {
359 rd_match = find_get_number(rdata, node->did, node->uid);
360 ASSERT(rd_match && "Unknown UID");
361
362 const long num = rd_match->get_number(node, data, flags);
363 buf_printf(buf_expando, "%ld", num);
364 }
365
366 int total_cols = 0;
367
368 const struct NodeExpandoPrivate *priv = node->ndata;
369
370 if (priv->color > -1)
371 add_color(buf, priv->color);
372
373 struct Buffer *tmp = buf_pool_get();
374 const struct ExpandoFormat *fmt = node->format;
375 if (fmt)
376 {
377 max_cols = MIN(max_cols, fmt->max_cols);
378 int min_cols = MIN(max_cols, fmt->min_cols);
379 total_cols += format_string(tmp, min_cols, max_cols, fmt->justification,
380 fmt->leader, buf_string(buf_expando),
381 buf_len(buf_expando), priv->has_tree);
382 if (fmt->lower)
384 }
385 else
386 {
387 total_cols += format_string(tmp, 0, max_cols, JUSTIFY_LEFT, 0, buf_string(buf_expando),
388 buf_len(buf_expando), priv->has_tree);
389 }
390 buf_addstr(buf, buf_string(tmp));
391 buf_pool_release(&tmp);
392
393 if (priv->color > -1)
395
396 buf_pool_release(&buf_expando);
397 return total_cols;
398}
const char * mutt_str_atous(const char *str, unsigned short *dst)
Convert ASCII string to an unsigned short.
Definition: atoi.c:266
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:161
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:491
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:241
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:226
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:96
Color and attribute parsing.
ColorId
List of all colored objects.
Definition: color.h:40
@ MT_COLOR_MAX
Definition: color.h:94
@ MT_COLOR_INDEX
Index: default colour.
Definition: color.h:83
Define an Expando format string.
#define EP_NO_CUSTOM_PARSE
Don't use the custom parser.
Definition: definition.h:44
uint8_t ExpandoParserFlags
Flags for expando_parse(), e.g. EP_CONDITIONAL.
Definition: definition.h:41
const char * skip_until_ch(const char *start, char terminator)
Search a string for a terminator character.
Definition: helpers.c:94
const char * skip_classic_expando(const char *str, const struct ExpandoDefinition *defs)
Skip over the text of an Expando.
Definition: helpers.c:144
const char * skip_until_classic_expando(const char *start)
Search through string until we reach an Expando character.
Definition: helpers.c:128
const struct ExpandoRenderData * find_get_number(const struct ExpandoRenderData *rdata, int did, int uid)
Find a get_number() callback function.
Definition: helpers.c:47
const struct ExpandoRenderData * find_get_string(const struct ExpandoRenderData *rdata, int did, int uid)
Find a get_string() callback function.
Definition: helpers.c:71
void buf_lower_special(struct Buffer *buf)
Convert to lowercase, excluding special characters.
Definition: helpers.c:176
Shared code.
Expando Parsing.
int format_string(struct Buffer *buf, int min_cols, int max_cols, enum FormatJustify justify, char pad_char, const char *str, size_t n, bool arboreal)
Format a string, like snprintf()
Definition: format.c:108
Simple string formatting.
@ JUSTIFY_RIGHT
Right justify the text.
Definition: format.h:36
@ JUSTIFY_LEFT
Left justify the text.
Definition: format.h:34
@ JUSTIFY_CENTER
Centre the text.
Definition: format.h:35
int node_expando_render(const struct ExpandoNode *node, const struct ExpandoRenderData *rdata, struct Buffer *buf, int max_cols, void *data, MuttFormatFlags flags)
Render an Expando Node - Implements ExpandoNode::render() -.
Definition: node_expando.c:344
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:51
#define FREE(x)
Definition: memory.h:45
#define MIN(a, b)
Definition: memory.h:32
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:660
char * mutt_strn_copy(char *dest, const char *src, size_t len, size_t dsize)
Copy a sub-string into a buffer.
Definition: string.c:360
Create/manipulate threading in emails.
@ MUTT_SPECIAL_INDEX
Colour indicator.
Definition: mutt_thread.h:73
struct ExpandoNode * node_new(void)
Create a new empty ExpandoNode.
Definition: node.c:39
Basic Expando Node.
@ ENT_EXPANDO
Expando, e.g. 'n'.
Definition: node.h:39
void node_expando_private_free(void **ptr)
Free Expando private data - Implements ExpandoNode::ndata_free()
Definition: node_expando.c:63
void node_expando_set_color(const struct ExpandoNode *node, int cid)
Set the colour for an Expando.
Definition: node_expando.c:106
void node_expando_set_has_tree(const struct ExpandoNode *node, bool has_tree)
Set the has_tree flag for an Expando.
Definition: node_expando.c:121
struct ExpandoFormat * parse_format(const char *start, const char *end, struct ExpandoParseError *error)
Parse a format string.
Definition: node_expando.c:140
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_expando_new(const char *start, const char *end, struct ExpandoFormat *fmt, int did, int uid)
Create a new Expando ExpandoNode.
Definition: node_expando.c:80
void add_color(struct Buffer *buf, enum ColorId cid)
Add a colour code to a buffer.
Definition: node_expando.c:333
struct NodeExpandoPrivate * node_expando_private_new(void)
Create new Expando private data.
Definition: node_expando.c:49
struct ExpandoNode * node_expando_parse_enclosure(const char *str, const char **parsed_until, int did, int uid, char terminator, struct ExpandoParseError *error)
Parse an enclosed Expando.
Definition: node_expando.c:296
Expando Node for an Expando.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
Render Expandos using Data.
uint8_t MuttFormatFlags
Flags for expando_render(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: render.h:32
#define ASSERT(COND)
Definition: signal2.h:58
String manipulation buffer.
Definition: buffer.h:36
Definition of a format string.
Definition: definition.h:52
short uid
Unique ID in domain.
Definition: definition.h:56
short did
Domain ID.
Definition: definition.h:55
struct ExpandoNode *(* parse)(const char *str, const char **parsed_until, int did, int uid, ExpandoParserFlags flags, struct ExpandoParseError *error)
Definition: definition.h:70
const char * short_name
Short Expando name, e.g. "n".
Definition: definition.h:53
Formatting information for an Expando.
Definition: node.h:53
char leader
Leader character, 0 or space.
Definition: node.h:57
enum FormatJustify justification
Justification: left, centre, right.
Definition: node.h:56
const char * start
Start of Expando specifier string.
Definition: node.h:59
int min_cols
Minimum number of screen columns.
Definition: node.h:54
int max_cols
Maximum number of screen columns.
Definition: node.h:55
bool lower
Display in lower case.
Definition: node.h:58
const char * end
End of Expando specifier string.
Definition: node.h:60
Basic Expando Node.
Definition: node.h:69
int uid
Unique ID, e.g. ED_EMA_SIZE.
Definition: node.h:73
int(* render)(const struct ExpandoNode *node, const struct ExpandoRenderData *rdata, struct Buffer *buf, int max_cols, void *data, MuttFormatFlags flags)
Definition: node.h:96
void * ndata
Private node data.
Definition: node.h:82
struct ExpandoFormat * format
Formatting info.
Definition: node.h:75
const char * end
End of string data.
Definition: node.h:80
int did
Domain ID, e.g. ED_EMAIL.
Definition: node.h:72
enum ExpandoNodeType type
Type of Node, e.g. ENT_EXPANDO.
Definition: node.h:70
const char * start
Start of string data.
Definition: node.h:79
void(* ndata_free)(void **ptr)
Function to free the private node data.
Definition: node.h:83
Buffer for parsing errors.
Definition: parse.h:34
const char * position
Position of error in original string.
Definition: parse.h:36
char message[256]
Error message.
Definition: parse.h:35
void(* get_string)(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, int max_cols, struct Buffer *buf)
Definition: render.h:65
long(* get_number)(const struct ExpandoNode *node, void *data, MuttFormatFlags flags)
Definition: render.h:79
Private data for an Expando.
Definition: node_expando.h:40
int color
ColorId to use.
Definition: node_expando.h:41
bool has_tree
Contains tree characters, used in $index_format's s.
Definition: node_expando.h:42