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

neomutt / neomutt / 17366932035

31 Aug 2025 12:06PM UTC coverage: 50.099% (+0.05%) from 50.049%
17366932035

push

github

web-flow
tweak observer event types (#4023)

9132 of 18228 relevant lines covered (50.1%)

272.36 hits per line

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

22.31
/parse/extract.c
1
/**
2
 * @file
3
 * Text parser
4
 *
5
 * @authors
6
 * Copyright (C) 2019 Naveen Nathan <naveen@lastninja.net>
7
 * Copyright (C) 2019-2023 Richard Russon <rich@flatcap.org>
8
 * Copyright (C) 2020 Pietro Cerutti <gahr@gahr.ch>
9
 *
10
 * @copyright
11
 * This program is free software: you can redistribute it and/or modify it under
12
 * the terms of the GNU General Public License as published by the Free Software
13
 * Foundation, either version 2 of the License, or (at your option) any later
14
 * version.
15
 *
16
 * This program is distributed in the hope that it will be useful, but WITHOUT
17
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
19
 * details.
20
 *
21
 * You should have received a copy of the GNU General Public License along with
22
 * this program.  If not, see <http://www.gnu.org/licenses/>.
23
 */
24

25
/**
26
 * @page parse_extract Text parser
27
 *
28
 * Text parser
29
 */
30

31
#include "config.h"
32
#include <stdio.h>
33
#include <string.h>
34
#include <sys/types.h>
35
#include "mutt/lib.h"
36
#include "config/lib.h"
37
#include "core/lib.h"
38
#include "extract.h"
39

40
/**
41
 * parse_extract_token - Extract one token from a string
42
 * @param dest  Buffer for the result
43
 * @param tok   Buffer containing tokens
44
 * @param flags Flags, see #TokenFlags
45
 * @retval  0 Success
46
 * @retval -1 Error
47
 */
48
int parse_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
1,205✔
49
{
50
  if (!dest || !tok)
1,205✔
51
    return -1;
52

53
  char ch;
54
  char qc = '\0'; /* quote char */
55
  char *pc = NULL;
56

57
  buf_reset(dest);
1,204✔
58

59
  SKIPWS(tok->dptr);
1,217✔
60
  while ((ch = *tok->dptr))
7,621✔
61
  {
62
    if (qc == '\0')
7,385✔
63
    {
64
      if (mutt_isspace(ch) && !(flags & TOKEN_SPACE))
7,384✔
65
        break;
66
      if ((ch == '#') && !(flags & TOKEN_COMMENT))
6,632✔
67
        break;
68
      if ((ch == '+') && (flags & TOKEN_PLUS))
6,632✔
69
        break;
70
      if ((ch == '-') && (flags & TOKEN_MINUS))
6,629✔
71
        break;
72
      if ((ch == '=') && (flags & TOKEN_EQUAL))
6,628✔
73
        break;
74
      if ((ch == '?') && (flags & TOKEN_QUESTION))
6,532✔
75
        break;
76
      if ((ch == ';') && !(flags & TOKEN_SEMICOLON))
6,418✔
77
        break;
78
      if ((flags & TOKEN_PATTERN) && strchr("~%=!|", ch))
6,418✔
79
        break;
80
    }
81

82
    tok->dptr++;
6,418✔
83

84
    if (ch == qc)
6,418✔
85
    {
86
      qc = 0; /* end of quote */
87
    }
88
    else if (!qc && ((ch == '\'') || (ch == '"')) && !(flags & TOKEN_QUOTE))
6,417✔
89
    {
90
      qc = ch;
91
    }
92
    else if ((ch == '\\') && (qc != '\''))
6,416✔
93
    {
94
      if (tok->dptr[0] == '\0')
×
95
        return -1; /* premature end of token */
96
      switch (ch = *tok->dptr++)
×
97
      {
98
        case 'c':
×
99
        case 'C':
100
          if (tok->dptr[0] == '\0')
×
101
            return -1; /* premature end of token */
102
          buf_addch(dest, (mutt_toupper(tok->dptr[0]) - '@') & 0x7f);
×
103
          tok->dptr++;
×
104
          break;
×
105
        case 'e':
×
106
          buf_addch(dest, '\033'); // Escape
×
107
          break;
×
108
        case 'f':
×
109
          buf_addch(dest, '\f');
×
110
          break;
×
111
        case 'n':
×
112
          buf_addch(dest, '\n');
×
113
          break;
×
114
        case 'r':
×
115
          buf_addch(dest, '\r');
×
116
          break;
×
117
        case 't':
×
118
          buf_addch(dest, '\t');
×
119
          break;
×
120
        default:
×
121
          if (mutt_isdigit(ch) && mutt_isdigit(tok->dptr[0]) &&
×
122
              mutt_isdigit(tok->dptr[1]))
×
123
          {
124
            buf_addch(dest, (ch << 6) + (tok->dptr[0] << 3) + tok->dptr[1] - 3504);
×
125
            tok->dptr += 2;
×
126
          }
127
          else
128
          {
129
            buf_addch(dest, ch);
×
130
          }
131
      }
132
    }
133
    else if ((ch == '^') && (flags & TOKEN_CONDENSE))
6,416✔
134
    {
135
      if (tok->dptr[0] == '\0')
×
136
        return -1; /* premature end of token */
137
      ch = *tok->dptr++;
×
138
      if (ch == '^')
×
139
      {
140
        buf_addch(dest, ch);
×
141
      }
142
      else if (ch == '[')
×
143
      {
144
        buf_addch(dest, '\033'); // Escape
×
145
      }
146
      else if (mutt_isalpha(ch))
×
147
      {
148
        buf_addch(dest, mutt_toupper(ch) - '@');
×
149
      }
150
      else
151
      {
152
        buf_addch(dest, '^');
×
153
        buf_addch(dest, ch);
×
154
      }
155
    }
156
    else if ((ch == '`') && (!qc || (qc == '"')))
6,416✔
157
    {
×
158
      FILE *fp = NULL;
1✔
159
      pid_t pid;
160

161
      pc = tok->dptr;
162
      do
163
      {
164
        pc = strpbrk(pc, "\\`");
1✔
165
        if (pc)
1✔
166
        {
167
          /* skip any quoted chars */
168
          if (*pc == '\\')
×
169
          {
170
            if (*(pc + 1))
×
171
              pc += 2;
×
172
            else
173
              pc = NULL;
174
          }
175
        }
176
      } while (pc && (pc[0] != '`'));
×
177
      if (!pc)
1✔
178
      {
179
        mutt_debug(LL_DEBUG1, "mismatched backticks\n");
1✔
180
        return -1;
1✔
181
      }
182
      struct Buffer *cmd = buf_pool_get();
×
183
      *pc = '\0';
×
184
      if (flags & TOKEN_BACKTICK_VARS)
×
185
      {
186
        /* recursively extract tokens to interpolate variables */
187
        parse_extract_token(cmd, tok,
×
188
                            TOKEN_QUOTE | TOKEN_SPACE | TOKEN_COMMENT |
189
                                TOKEN_SEMICOLON | TOKEN_NOSHELL);
190
      }
191
      else
192
      {
193
        buf_strcpy(cmd, tok->dptr);
×
194
      }
195
      *pc = '`';
×
196
      pid = filter_create(buf_string(cmd), NULL, &fp, NULL, NeoMutt->env);
×
197
      if (pid < 0)
×
198
      {
199
        mutt_debug(LL_DEBUG1, "unable to fork command: %s\n", buf_string(cmd));
×
200
        buf_pool_release(&cmd);
×
201
        return -1;
×
202
      }
203

204
      tok->dptr = pc + 1;
×
205

206
      /* read line */
207
      char *expn = NULL;
×
208
      size_t expn_len = 0;
×
209
      expn = mutt_file_read_line(expn, &expn_len, fp, NULL, MUTT_RL_NO_FLAGS);
×
210
      mutt_file_fclose(&fp);
×
211
      int rc = filter_wait(pid);
×
212
      if (rc != 0)
×
213
      {
214
        mutt_debug(LL_DEBUG1, "backticks exited code %d for command: %s\n", rc,
×
215
                   buf_string(cmd));
216
      }
217
      buf_pool_release(&cmd);
×
218

219
      /* if we got output, make a new string consisting of the shell output
220
       * plus whatever else was left on the original line */
221
      /* BUT: If this is inside a quoted string, directly add output to
222
       * the token */
223
      if (expn)
×
224
      {
225
        if (qc)
×
226
        {
227
          buf_addstr(dest, expn);
×
228
        }
229
        else
230
        {
231
          struct Buffer *copy = buf_pool_get();
×
232
          buf_strcpy(copy, expn);
×
233
          buf_addstr(copy, tok->dptr);
×
234
          buf_copy(tok, copy);
×
235
          buf_seek(tok, 0);
×
236
          buf_pool_release(&copy);
×
237
        }
238
        FREE(&expn);
×
239
      }
240
    }
241
    else if ((ch == '$') && (!qc || (qc == '"')) &&
6,415✔
242
             ((tok->dptr[0] == '{') || mutt_isalpha(tok->dptr[0])))
×
243
    {
×
244
      const char *env = NULL;
245
      char *var = NULL;
×
246

247
      if (tok->dptr[0] == '{')
×
248
      {
249
        pc = strchr(tok->dptr, '}');
×
250
        if (pc)
×
251
        {
252
          var = mutt_strn_dup(tok->dptr + 1, pc - (tok->dptr + 1));
×
253
          tok->dptr = pc + 1;
×
254

255
          if ((flags & TOKEN_NOSHELL))
×
256
          {
257
            buf_addch(dest, ch);
×
258
            buf_addch(dest, '{');
×
259
            buf_addstr(dest, var);
×
260
            buf_addch(dest, '}');
×
261
            FREE(&var);
×
262
          }
263
        }
264
      }
265
      else
266
      {
267
        for (pc = tok->dptr; mutt_isalnum(*pc) || (pc[0] == '_'); pc++)
×
268
          ; // do nothing
269

270
        var = mutt_strn_dup(tok->dptr, pc - tok->dptr);
×
271
        tok->dptr = pc;
×
272
      }
273
      if (var)
×
274
      {
275
        struct Buffer *result = buf_pool_get();
×
276
        int rc = cs_subset_str_string_get(NeoMutt->sub, var, result);
×
277

278
        if (CSR_RESULT(rc) == CSR_SUCCESS)
×
279
        {
280
          buf_addstr(dest, buf_string(result));
×
281
        }
282
        else if (!(flags & TOKEN_NOSHELL) && (env = mutt_str_getenv(var)))
×
283
        {
284
          buf_addstr(dest, env);
×
285
        }
286
        else
287
        {
288
          buf_addch(dest, ch);
×
289
          buf_addstr(dest, var);
×
290
        }
291
        FREE(&var);
×
292
        buf_pool_release(&result);
×
293
      }
294
    }
295
    else
296
    {
297
      buf_addch(dest, ch);
6,415✔
298
    }
299
  }
300

301
  SKIPWS(tok->dptr);
1,955✔
302
  return 0;
303
}
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