Toolbox snapshot
The Reactive C++ Toolbox
Loading...
Searching...
No Matches
VarSub.cpp
Go to the documentation of this file.
1// The Reactive C++ Toolbox.
2// Copyright (C) 2013-2019 Swirly Cloud Limited
3// Copyright (C) 2021 Reactive Markets Limited
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17#include "VarSub.hpp"
18
19#include <cstdlib> // getenv()
20
21namespace toolbox {
22inline namespace util {
23using namespace std;
24
25string get_env(const string& name)
26{
27 const char* const val{getenv(name.c_str())};
28 return val ? string{val} : string{};
29}
30
31VarSub::~VarSub() = default;
32
33// Copy.
34VarSub::VarSub(const VarSub&) = default;
35VarSub& VarSub::operator=(const VarSub&) = default;
36
37// Move.
39VarSub& VarSub::operator=(VarSub&&) noexcept = default;
40
41bool VarSub::substitute(string& s, const size_t i, size_t j, set<string>* outer) const
42{
43 // Position of last substitution.
44 size_t last{0};
45 // Names substituted at 'last' position.
47
48 int state{0};
49 while (j < s.size()) {
50 if (state == '\\') {
51 state = 0;
52 // Remove backslash. This shifts all remaining characters one place to the left,
53 // so there is no need to increment j.
54 s.erase(j - 1, 1);
55 continue;
56 }
57 const auto ch = s[j];
58 if (state == '$') {
59 state = 0;
60 if (ch == '{') {
61 if (j > last) {
62 // Position has advanced.
63 last = j;
64 inner.clear();
65 }
66 // Reverse to '$'.
67 --j;
68 // Descend: search for closing brace and substitute.
69 if (!substitute(s, j, j + 2, &inner)) {
70 return false;
71 }
72 continue;
73 }
74 }
75 switch (ch) {
76 case '$':
77 case '\\':
78 state = ch;
79 break;
80 case '}':
81 // If outer is null then the closing brace was found at the top level. I.e. there is no
82 // matching opening brace.
83 if (outer) {
84 // Substitute variable.
85 const auto n = j - i;
86 auto name = s.substr(i + 2, n - 2);
87 if (outer->count(name) == 0) {
88 s.replace(i, n + 1, fn_(name));
89 outer->insert(std::move(name));
90 } else {
91 // Loop detected: this name has already been substituted at this position.
92 s.erase(i, n + 1);
93 }
94 // Ascend: matched closing brace.
95 return true;
96 }
97 break;
98 }
99 ++j;
100 }
101 // Ascend: no closing brace.
102 return false;
103}
104
105} // namespace util
106} // namespace toolbox
VarSub(std::function< std::string(const std::string &)> fn=get_env)
Definition VarSub.hpp:34
VarSub & operator=(const VarSub &)
STL namespace.
string get_env(const string &name)
Definition VarSub.cpp:25
constexpr auto bind() noexcept
Definition Slot.hpp:92