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

antonvw / wex / 24562595136

17 Apr 2026 11:23AM UTC coverage: 64.307% (+0.1%) from 64.189%
24562595136

push

github

web-flow
1222 support variables to be used in register calculation (#1223)

* support variables to be used in register calculation

* fixed some tests

* changes after review

* another fix after review

18725 of 31967 branches covered (58.58%)

Branch coverage included in aggregate %.

14920 of 20352 relevant lines covered (73.31%)

1462.69 hits per line

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

67.25
/src/ex/variable.cpp
1
////////////////////////////////////////////////////////////////////////////////
2
// Name:      variable.cpp
3
// Purpose:   Implementation of class wex::variable
4
// Author:    Anton van Wezenbeek
5
// Copyright: (c) 2018-2026 Anton van Wezenbeek
6
////////////////////////////////////////////////////////////////////////////////
7

8
#include <wex/core/chrono.h>
9
#include <wex/core/log.h>
10
#include <wex/core/regex.h>
11
#include <wex/ex/ex.h>
12
#include <wex/ex/macro-mode.h>
13
#include <wex/ex/macros.h>
14
#include <wex/ex/variable.h>
15
#include <wex/factory/process.h>
16
#include <wex/syntax/stc.h>
17
#include <wex/ui/frame.h>
18
#include <wx/app.h>
19

20
#include <utility>
21

22
// Several types of variables are supported.
23
// See xml file.
24
enum class wex::variable::input_t
25
{
26
  BUILTIN,     // a builtin variable like "Created"
27
  ENVIRONMENT, // an environment variable like ENV
28
  FIXED,       // fixed value from macros xml file
29
  INPUT,       // input always from user
30
  INPUT_ONCE,  // input once from user when starting, save value in xml file
31
  INPUT_ONCE_EMPTY, // input once from user if value is empty, save value in xml
32
                    // file
33
  INPUT_SAVE,       // input always from user, save value in xml file
34
  PROCESS,          // value is output from contents as a runnable process
35
  TEMPLATE          // read value from a template file
36
};
37

38
wex::variable::variable(std::string name)
13✔
39
  : m_name(std::move(name))
13✔
40
  , m_type(input_t::INPUT_SAVE)
26✔
41
{
42
}
13✔
43

44
wex::variable::variable(const pugi::xml_node& node)
1,104✔
45
  : m_name(node.attribute("name").value())
1,104✔
46
  , m_type(input_t::INPUT_SAVE)
1,104✔
47
  , m_value(node.text().get())
1,104✔
48
  , m_format(node.attribute("format").value())
1,104✔
49
  , m_prefix(node.attribute("prefix").value())
3,312✔
50
{
51
  if (const std::string type = node.attribute("type").value(); !type.empty())
1,104!
52
  {
53
    if (type == "BUILTIN")
1,104✔
54
    {
55
      m_type = input_t::BUILTIN;
647✔
56
    }
57
    else if (type == "ENVIRONMENT")
457✔
58
    {
59
      m_type = input_t::ENVIRONMENT;
91✔
60
    }
61
    else if (type == "FIXED")
366✔
62
    {
63
      m_type = input_t::FIXED;
1✔
64
    }
65
    else if (type == "INPUT")
365✔
66
    {
67
      m_type = input_t::INPUT;
1✔
68
    }
69
    else if (type == "INPUT-SAVE")
364✔
70
    {
71
      m_type = input_t::INPUT_SAVE;
91✔
72
    }
73
    else if (type == "INPUT-ONCE")
273✔
74
    {
75
      m_type = input_t::INPUT_ONCE;
45✔
76
    }
77
    else if (type == "INPUT-ONCE-EMPTY")
228✔
78
    {
79
      m_type = input_t::INPUT_ONCE_EMPTY;
135✔
80
    }
81
    else if (type == "PROCESS")
93✔
82
    {
83
      m_type = input_t::PROCESS;
46✔
84
    }
85
    else if (type == "TEMPLATE")
47✔
86
    {
87
      m_type = input_t::TEMPLATE;
46✔
88
    }
89
    else
90
    {
91
      log("variable") << m_name << "type:" << type << "unknown";
2✔
92
    }
93
  }
1,104✔
94
}
1,104✔
95

96
bool wex::variable::check_link(std::string& value) const
115✔
97
{
98
  if (regex v(regex_valid_names()); v.match(m_value) == 3)
115!
99
  {
100
    if (
×
101
      const auto& it = ex::get_macros().get_variables().find(v[1]);
×
102
      it != ex::get_macros().get_variables().end())
×
103
    {
104
      if (!it->second.expand(value))
×
105
      {
106
        if (!is_input())
×
107
        {
108
          log("variable") << m_name << "(" << v[0] << ") could not be expanded";
×
109
        }
110
      }
111
      else
112
      {
113
        if (!value.empty())
×
114
        {
115
          log::trace("variable")
×
116
            << m_name << "(" << v[0] << ") expanded:" << value;
×
117
          return true;
×
118
        }
119
      }
120
    }
121
    else
122
    {
123
      log("variable") << m_name << "(" << v[0] << ") is not found";
×
124
    }
125
  }
115!
126

127
  return false;
115✔
128
}
129

130
bool wex::variable::expand(ex* ex)
49✔
131
{
132
  std::string value;
49✔
133

134
  if (check_link(value))
49!
135
  {
136
    m_value = value;
×
137
    value.clear();
×
138
  }
139

140
  if (!expand(value, ex))
49✔
141
  {
142
    if (!is_input())
1!
143
    // Now only show log status if this is no input variable,
144
    // as it was cancelled in that case.
145
    {
146
      log::status(_("Could not expand variable")) << m_name;
1✔
147
    }
148

149
    return false;
1✔
150
  }
151

152
  // Add to stc component, if present.
153
  if (ex != nullptr && ex->get_stc() != nullptr)
48!
154
  {
155
    if (ex->get_stc()->GetReadOnly() || ex->get_stc()->is_hexmode())
47!
156
    {
157
      return false;
×
158
    }
159

160
    auto commented(value);
47✔
161

162
    // If there is a prefix, make a comment out of it.
163
    if (!m_prefix.empty())
47!
164
    {
165
      commented = ex->get_stc()->get_lexer().make_comment(
×
166
        m_prefix == "WRAP" ? std::string() : m_prefix,
×
167
        value);
×
168
    }
169

170
    ex->get_stc()->add_text(commented);
47✔
171
  }
47✔
172

173
  m_value = value;
48✔
174

175
  log::trace("variable") << m_name << "expanded to:" << value;
48✔
176

177
  if (m_type == input_t::INPUT_ONCE_EMPTY && !m_value.empty())
48!
178
  {
179
    m_ask_for_input = false;
×
180
  }
181
  else if (m_type == input_t::INPUT_ONCE)
48!
182
  {
183
    m_ask_for_input = false;
×
184
  }
185

186
  return true;
48✔
187
}
49✔
188

189
bool wex::variable::expand(std::string& value, ex* ex) const
66✔
190
{
191
  check_link(value);
66✔
192

193
  switch (m_type)
66!
194
  {
195
    case input_t::BUILTIN:
53✔
196
      if (!expand_builtin(ex, value))
53!
197
      {
198
        return false;
×
199
      }
200
      break;
53✔
201

202
    case input_t::ENVIRONMENT:
4✔
203
      if (wxString val; !wxGetEnv(m_name, &val))
4!
204
      {
205
        return false;
×
206
      }
207
      else
208
      {
209
        value = val;
4✔
210
      }
4!
211
      break;
4✔
212

213
    case input_t::FIXED:
1✔
214
      if (m_value.empty())
1!
215
      {
216
        return false;
×
217
      }
218
      value = m_value;
1✔
219
      break;
1✔
220

221
    case input_t::INPUT:
6✔
222
    case input_t::INPUT_ONCE:
223
    case input_t::INPUT_ONCE_EMPTY:
224
    case input_t::INPUT_SAVE:
225
      if (!expand_input(value))
6!
226
      {
227
        return false;
×
228
      }
229
      break;
6✔
230

231
    case input_t::PROCESS:
1✔
232
      if (m_value.empty())
1!
233
      {
234
        return false;
×
235
      }
236

237
      if (
1✔
238
        factory::process p;
1✔
239
        p.system(
3!
240
          m_value + (!m_argument.empty() ? " " + m_argument : std::string())) !=
2!
241
        0)
242
      {
243
        return false;
×
244
      }
245
      /* NOLINTNEXTLINE */
246
      else
247
      {
248
        value = p.std_out();
1✔
249
        m_argument.clear();
1✔
250
      }
1!
251
      break;
1✔
252

253
    case input_t::TEMPLATE:
1✔
254
      if (!ex::get_macros().mode().expand(ex, *this, value))
1!
255
      {
256
        return false;
1✔
257
      }
258
      break;
×
259

260
    default:
×
261
      assert(0);
×
262
      break;
263
  }
264

265
  return true;
65✔
266
}
267

268
bool wex::variable::expand_builtin(ex* ex, std::string& expanded) const
53✔
269
{
270
  if (m_name == "Date")
53✔
271
  {
272
    expanded = (m_format.empty() ? now("%Y-%m-%d") : now(m_format));
13!
273
  }
274
  else if (m_name == "Datetime")
48✔
275
  {
276
    expanded = (m_format.empty() ? now("%Y-%m-%d %H:%M:%S") : now(m_format));
9!
277
  }
278
  else if (m_name == "Time")
45✔
279
  {
280
    expanded = (m_format.empty() ? now("%H:%M:%S") : now(m_format));
9!
281
  }
282
  else if (m_name == "Year")
42✔
283
  {
284
    expanded = (m_format.empty() ? now("%Y") : now(m_format));
19!
285
  }
286
  else if (ex != nullptr)
35!
287
  {
288
    if (m_name == "Cb")
35✔
289
    {
290
      expanded = ex->get_stc()->get_lexer().comment_begin();
4✔
291
    }
292
    else if (m_name == "Cc")
31✔
293
    {
294
      const int line     = ex->get_stc()->get_current_line();
3✔
295
      const int startPos = ex->get_stc()->PositionFromLine(line);
3✔
296
      const int endPos   = ex->get_stc()->GetLineEndPosition(line);
3✔
297
      expanded           = ex->get_stc()->get_lexer().comment_complete(
9✔
298
        ex->get_stc()->GetTextRange(startPos, endPos).ToStdString());
9✔
299
    }
300
    else if (m_name == "Ce")
28✔
301
    {
302
      expanded = ex->get_stc()->get_lexer().comment_end();
4✔
303
    }
304
    else if (m_name == "Cl")
24✔
305
    {
306
      expanded = ex->get_stc()->get_lexer().make_comment(std::string(), false);
4✔
307
    }
308
    else if (m_name == "Created")
20✔
309
    {
310
      if (
4!
311
        path file(ex->get_stc()->path()); ex->get_stc()->path().stat().is_ok())
4✔
312
      {
313
        expanded =
314
          (m_format.empty() ? file.stat().get_creation_time_str() :
8!
315
                              file.stat().get_creation_time_str(m_format));
4!
316
      }
317
      else
318
      {
319
        expanded = (m_format.empty() ? now("%Y-%m-%d") : now(m_format));
×
320
      }
4✔
321
    }
322
    else if (m_name == "Filename")
16✔
323
    {
324
      expanded = ex->get_stc()->path().name();
3✔
325
    }
326
    else if (m_name == "Fullname")
13✔
327
    {
328
      expanded = ex->get_stc()->path().filename();
3✔
329
    }
330
    else if (m_name == "Fullpath")
10✔
331
    {
332
      expanded = ex->get_stc()->path().string();
3✔
333
    }
334
    else if (m_name == "Nl")
7✔
335
    {
336
      expanded = ex->get_stc()->eol();
4✔
337
    }
338
    else if (m_name == "Path")
3!
339
    {
340
      expanded = ex->get_stc()->path().parent_path();
3✔
341
    }
342
    else
343
    {
344
      return false;
×
345
    }
346
  }
347

348
  return true;
53✔
349
}
350

351
bool wex::variable::expand_input(std::string& expanded) const
6✔
352
{
353
  auto* frame = dynamic_cast<wex::frame*>(wxTheApp->GetTopWindow());
6!
354

355
  if (frame->stc_entry_dialog_component() == nullptr)
6!
356
  {
357
    expanded = m_value;
6✔
358
    return true;
6✔
359
  }
360

361
  if (m_ask_for_input)
×
362
  {
363
    const auto use(!expanded.empty() ? expanded : m_value);
×
364

365
    frame->stc_entry_dialog_title(m_name);
×
366
    frame->stc_entry_dialog_component()->SetWrapMode(wxSTC_WRAP_WORD);
×
367
    frame->stc_entry_dialog_component()->set_text(use);
×
368
    frame->stc_entry_dialog_component()->SetFocus();
×
369

370
    bool ended = false;
×
371

372
    if (wxIsBusy())
×
373
    {
374
      ended = true;
×
375
      wxEndBusyCursor();
×
376
    }
377

378
    const int result = frame->stc_entry_dialog_show(true);
×
379

380
    if (ended)
×
381
    {
382
      wxBeginBusyCursor();
×
383
    }
384

385
    if (result == wxID_CANCEL)
×
386
    {
387
      return false;
×
388
    }
389

390
    const auto& value(frame->stc_entry_dialog_component()->get_text());
×
391

392
    if (value.empty())
×
393
    {
394
      return false;
×
395
    }
396

397
    expanded = value;
×
398
  }
×
399
  else
400
  {
401
    expanded = m_value;
×
402
  }
403

404
  return true;
×
405
}
406

407
bool wex::variable::is_builtin() const
66✔
408
{
409
  return m_type == input_t::BUILTIN;
66✔
410
}
411

412
bool wex::variable::is_input() const
291✔
413
{
414
  return m_type == input_t::INPUT || m_type == input_t::INPUT_ONCE ||
291✔
415
         m_type == input_t::INPUT_ONCE_EMPTY || m_type == input_t::INPUT_SAVE;
582!
416
}
417

418
bool wex::variable::is_template() const
5✔
419
{
420
  return m_type == input_t::TEMPLATE;
5✔
421
}
422

423
std::string wex::variable::regex_valid_names()
5,245✔
424
{
425
  return "(@)([a-zA-Z][a-zA-Z0-9:]+)(@)";
10,490✔
426
}
427

428
void wex::variable::save(pugi::xml_node& node, const std::string* value)
30✔
429
{
430
  assert(!m_name.empty());
30!
431

432
  if (!node.attribute("name"))
30✔
433
  {
434
    node.append_attribute("name") = m_name;
8✔
435
  }
436

437
  if (!node.attribute("type"))
30✔
438
  {
439
    pugi::xml_attribute type = node.append_attribute("type");
8✔
440

441
    switch (m_type)
8!
442
    {
443
      case input_t::BUILTIN:
1✔
444
        type.set_value("BUILTIN");
1✔
445
        break;
1✔
446
      case input_t::ENVIRONMENT:
1✔
447
        type.set_value("ENVIRONMENT");
1✔
448
        break;
1✔
449
      case input_t::FIXED:
1✔
450
        type.set_value("FIXED");
1✔
451
        break;
1✔
452
      case input_t::INPUT:
1✔
453
        type.set_value("INPUT");
1✔
454
        break;
1✔
455
      case input_t::INPUT_ONCE:
×
456
        type.set_value("INPUT-ONCE");
×
457
        break;
×
458
      case input_t::INPUT_ONCE_EMPTY:
×
459
        type.set_value("INPUT-ONCE-EMPTY");
×
460
        break;
×
461
      case input_t::INPUT_SAVE:
2✔
462
        type.set_value("INPUT-SAVE");
2✔
463
        break;
2✔
464
      case input_t::PROCESS:
1✔
465
        type.set_value("PROCESS");
1✔
466
        break;
1✔
467
      case input_t::TEMPLATE:
1✔
468
        type.set_value("TEMPLATE");
1✔
469
        break;
1✔
470

471
      default:
×
472
        assert(0);
×
473
        break;
474
    }
475
  }
476

477
  if (!m_prefix.empty() && !node.attribute("prefix"))
30!
478
  {
479
    node.append_attribute("prefix") = m_prefix;
×
480
  }
481

482
  if (value != nullptr)
30!
483
  {
484
    m_value = *value;
×
485
  }
486

487
  if (
30✔
488
    !m_value.empty() && m_type != input_t::BUILTIN &&
53✔
489
    m_type != input_t::INPUT && m_type != input_t::PROCESS)
53✔
490
  {
491
    node.text().set(m_value);
6✔
492
  }
493
}
30✔
494

495
void wex::variable::set_argument(const std::string& val)
1✔
496
{
497
  m_argument = val;
1✔
498

499
  log::trace("variable") << "argument:" << m_argument;
1✔
500
}
1✔
501

502
void wex::variable::set_ask_for_input(bool value)
319✔
503
{
504
  if (!value || is_input() && m_type != input_t::INPUT_ONCE_EMPTY)
319✔
505
  {
506
    m_ask_for_input = value;
67✔
507
  }
508
}
319✔
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