• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

neomutt / neomutt / 24018442577

05 Apr 2026 02:04AM UTC coverage: 42.389% (-0.2%) from 42.543%
24018442577

push

github

flatcap
unify capitalisation of Message-ID

12096 of 28536 relevant lines covered (42.39%)

2838.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.43
/expando/node_condition.c
1
/**
2
 * @file
3
 * Expando Node for a Condition
4
 *
5
 * @authors
6
 * Copyright (C) 2023-2024 Tóth János <gomba007@gmail.com>
7
 * Copyright (C) 2023-2024 Richard Russon <rich@flatcap.org>
8
 *
9
 * @copyright
10
 * This program is free software: you can redistribute it and/or modify it under
11
 * the terms of the GNU General Public License as published by the Free Software
12
 * Foundation, either version 2 of the License, or (at your option) any later
13
 * version.
14
 *
15
 * This program is distributed in the hope that it will be useful, but WITHOUT
16
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18
 * details.
19
 *
20
 * You should have received a copy of the GNU General Public License along with
21
 * this program.  If not, see <http://www.gnu.org/licenses/>.
22
 */
23

24
/**
25
 * @page expando_node_condition Condition Node
26
 *
27
 * Expando Node for a Condition
28
 */
29

30
#include "config.h"
31
#include <stdbool.h>
32
#include <stdio.h>
33
#include "mutt/lib.h"
34
#include "node_condition.h"
35
#include "definition.h"
36
#include "format.h"
37
#include "helpers.h"
38
#include "node.h"
39
#include "node_condbool.h"
40
#include "node_container.h"
41
#include "node_expando.h"
42
#include "node_text.h"
43
#include "parse.h"
44
#include "render.h"
45

46
/**
47
 * node_condition_render - Render a Conditional Node - Implements ExpandoNode::render() - @ingroup expando_render
48
 */
49
static int node_condition_render(const struct ExpandoNode *node,
21✔
50
                                 const struct ExpandoRenderCallback *erc,
51
                                 struct Buffer *buf, int max_cols, void *data,
52
                                 MuttFormatFlags flags)
53
{
54
  ASSERT(node->type == ENT_CONDITION);
21✔
55

56
  const struct ExpandoNode *node_cond = node_get_child(node, ENC_CONDITION);
21✔
57

58
  // Discard any text returned, just use the return value as a bool
59
  struct Buffer *buf_cond = buf_pool_get();
21✔
60
  int rc_cond = node_cond->render(node_cond, erc, buf_cond, max_cols, data, flags);
21✔
61

62
  int rc = 0;
63
  buf_reset(buf_cond);
21✔
64

65
  if (rc_cond == true)
21✔
66
  {
67
    const struct ExpandoNode *node_true = node_get_child(node, ENC_TRUE);
12✔
68
    rc = node_render(node_true, erc, buf_cond, max_cols, data, flags);
12✔
69
  }
70
  else
71
  {
72
    const struct ExpandoNode *node_false = node_get_child(node, ENC_FALSE);
9✔
73
    rc = node_render(node_false, erc, buf_cond, max_cols, data, flags);
9✔
74
  }
75

76
  const struct ExpandoFormat *fmt = node->format;
21✔
77
  if (!fmt)
21✔
78
  {
79
    buf_addstr(buf, buf_string(buf_cond));
36✔
80
    buf_pool_release(&buf_cond);
18✔
81
    return rc;
18✔
82
  }
83

84
  struct Buffer *tmp = buf_pool_get();
3✔
85

86
  int min_cols = MAX(fmt->min_cols, fmt->max_cols);
3✔
87
  min_cols = MIN(min_cols, max_cols);
3✔
88
  if (fmt->max_cols >= 0)
3✔
89
    max_cols = MIN(max_cols, fmt->max_cols);
2✔
90
  rc = format_string(tmp, min_cols, max_cols, fmt->justification, ' ',
6✔
91
                     buf_string(buf_cond), buf_len(buf_cond), true);
92
  if (fmt->lower)
3✔
93
    buf_lower_special(tmp);
1✔
94

95
  buf_addstr(buf, buf_string(tmp));
6✔
96
  buf_pool_release(&tmp);
3✔
97
  buf_pool_release(&buf_cond);
3✔
98

99
  return rc;
3✔
100
}
101

102
/**
103
 * node_condition_new - Create a new Condition Expando Node
104
 * @param node_cond  Expando Node that will be tested
105
 * @param node_true  Node tree for the 'true' case
106
 * @param node_false Node tree for the 'false' case
107
 * @param fmt        Formatting info
108
 * @retval ptr New Condition Expando Node
109
 */
110
struct ExpandoNode *node_condition_new(struct ExpandoNode *node_cond,
17,640✔
111
                                       struct ExpandoNode *node_true,
112
                                       struct ExpandoNode *node_false,
113
                                       struct ExpandoFormat *fmt)
114
{
115
  ASSERT(node_cond);
17,640✔
116

117
  struct ExpandoNode *node = node_new();
17,640✔
118

119
  node->type = ENT_CONDITION;
17,640✔
120
  node->render = node_condition_render;
17,640✔
121

122
  ARRAY_SET(&node->children, ENC_CONDITION, node_cond);
17,640✔
123
  ARRAY_SET(&node->children, ENC_TRUE, node_true);
17,640✔
124
  ARRAY_SET(&node->children, ENC_FALSE, node_false);
17,640✔
125

126
  node->format = fmt;
17,640✔
127

128
  return node;
17,640✔
129
}
130

131
/**
132
 * node_condition_parse - Parse a conditional Expando
133
 * @param[in]  str          String to parse
134
 * @param[in]  term_chars   Terminator characters, e.g. #NTE_GREATER
135
 * @param[in]  defs         Expando definitions
136
 * @param[out] parsed_until First character after parsed string
137
 * @param[out] err          Buffer for errors
138
 * @retval ptr Expando Node
139
 */
140
struct ExpandoNode *node_condition_parse(const char *str, NodeTextTermFlags term_chars,
64,354✔
141
                                         const struct ExpandoDefinition *defs,
142
                                         const char **parsed_until,
143
                                         struct ExpandoParseError *err)
144
{
145
  if (!str || (str[0] != '%'))
64,354✔
146
    return NULL;
147

148
  str++; // Skip %
64,351✔
149

150
  struct ExpandoFormat *fmt = NULL;
64,351✔
151
  struct ExpandoNode *node_cond = NULL;
64,351✔
152
  struct ExpandoNode *node_true = NULL;
64,351✔
153
  struct ExpandoNode *node_false = NULL;
64,351✔
154

155
  //----------------------------------------------------------------------------
156
  // Parse the format (optional)
157
  fmt = parse_format(str, parsed_until, err);
64,351✔
158
  if (err->position)
64,351✔
159
    goto fail;
5✔
160

161
  str = *parsed_until;
64,346✔
162

163
  if ((str[0] != '<') && (str[0] != '?'))
64,346✔
164
    goto fail;
46,685✔
165

166
  const bool old_style = (str[0] == '?'); // %?X?...&...?
167
  str++;
17,661✔
168

169
  //----------------------------------------------------------------------------
170
  // Parse the condition
171
  // Try long name first if it looks like %<{NAME}?...>
172
  if (str[0] == '{')
17,661✔
173
  {
174
    node_cond = parse_long_name(str + 1, defs, EP_CONDITIONAL, NULL, parsed_until, err);
15✔
175
    if (node_cond)
15✔
176
    {
177
      if ((*parsed_until)[0] != '}')
10✔
178
      {
179
        err->position = *parsed_until;
×
180
        snprintf(err->message, sizeof(err->message), _("Expando is missing closing '}'"));
×
181
        goto fail;
×
182
      }
183
      (*parsed_until)++; // Skip the '}'
10✔
184
    }
185
    else if (err->position)
5✔
186
    {
187
      goto fail;
×
188
    }
189
    else
190
    {
191
      // Report an error if the content looks like a long name
192
      const char *name_start = str + 1;
193
      const char *end = name_start + strspn(name_start, "abcdefghijklmnopqrstuvwxyz0123456789-");
5✔
194
      if (end != name_start)
5✔
195
      {
196
        err->position = name_start;
4✔
197
        if (*end != '}')
4✔
198
          snprintf(err->message, sizeof(err->message), _("Expando is missing closing '}'"));
2✔
199
        else
200
          // L10N: e.g. "Unknown expando: %{bad}"
201
          snprintf(err->message, sizeof(err->message), _("Unknown expando: %%{%.*s}"),
2✔
202
                   (int) (end - name_start), name_start);
203
        goto fail;
4✔
204
      }
205
      // Doesn't look like a long name, fall through to parse_short_name
206
    }
207
  }
208

209
  if (!node_cond)
17,657✔
210
  {
211
    node_cond = parse_short_name(str, defs, EP_CONDITIONAL, NULL, parsed_until, err);
17,647✔
212
    if (!node_cond)
17,647✔
213
      goto fail;
6✔
214
  }
215

216
  if (node_cond->type == ENT_EXPANDO)
17,651✔
217
  {
218
    node_cond->type = ENT_CONDBOOL;
17,634✔
219
    node_cond->render = node_condbool_render;
17,634✔
220
  }
221

222
  str = *parsed_until; // Skip the expando
17,651✔
223
  if (str[0] != '?')
17,651✔
224
  {
225
    err->position = str;
1✔
226
    snprintf(err->message, sizeof(err->message),
1✔
227
             // L10N: Expando is missing a terminator character
228
             //       e.g. "%[..." is missing the final ']'
229
             _("Conditional expando is missing '%c'"), '?');
1✔
230
    goto fail;
1✔
231
  }
232
  str++; // Skip the '?'
17,650✔
233

234
  //----------------------------------------------------------------------------
235
  // Parse the 'true' clause (optional)
236
  const NodeTextTermFlags term_true = term_chars | NTE_AMPERSAND |
17,650✔
237
                                      (old_style ? NTE_QUESTION : NTE_GREATER);
238

239
  node_true = node_container_new();
17,650✔
240
  node_parse_many(node_true, str, term_true, defs, parsed_until, err);
17,650✔
241
  if (err->position)
17,650✔
242
    goto fail;
2✔
243

244
  str = *parsed_until;
17,648✔
245

246
  //----------------------------------------------------------------------------
247
  // Parse the 'false' clause (optional)
248

249
  node_false = NULL;
17,648✔
250
  if (str[0] == '&')
17,648✔
251
  {
252
    str++;
3,327✔
253
    const NodeTextTermFlags term_false = term_chars | (old_style ? NTE_QUESTION : NTE_GREATER);
3,327✔
254

255
    node_false = node_container_new();
3,327✔
256
    node_parse_many(node_false, str, term_false, defs, parsed_until, err);
3,327✔
257
    if (err->position)
3,327✔
258
      goto fail;
2✔
259

260
    str = *parsed_until;
3,325✔
261
  }
262

263
  //----------------------------------------------------------------------------
264
  // Check for the terminator character
265
  const char terminator = old_style ? '?' : '>';
17,646✔
266

267
  if (str[0] != terminator)
17,646✔
268
  {
269
    err->position = str;
6✔
270
    snprintf(err->message, sizeof(err->message),
6✔
271
             // L10N: Expando is missing a terminator character
272
             //       e.g. "%[..." is missing the final ']'
273
             _("Conditional expando is missing '%c'"), '?');
6✔
274
    goto fail;
6✔
275
  }
276

277
  *parsed_until = str + 1;
17,640✔
278

279
  return node_condition_new(node_cond, node_true, node_false, fmt);
17,640✔
280

281
fail:
46,711✔
282
  FREE(&fmt);
46,711✔
283
  node_free(&node_cond);
46,711✔
284
  node_free(&node_true);
46,711✔
285
  node_free(&node_false);
46,711✔
286
  return NULL;
46,711✔
287
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc