And here's another take on it, using the old crafts:
- not requiring Spirit, or indeed Boost
- not hardwiring the interface to
std::string
(instead allowing any combination of input iterators and output iterator)
- handling
%%
"properly" as a single %
1
The essence:
#include <string>
#include <algorithm>
static std::string safe_getenv(std::string const& macro) {
auto var = getenv(macro.c_str());
return var? var : macro;
}
template <typename It, typename Out>
Out expand_env(It f, It l, Out o)
{
bool in_var = false;
std::string accum;
while (f!=l)
{
switch(auto ch = *f++)
{
case '%':
if (in_var || (*f!='%'))
{
in_var = !in_var;
if (in_var)
accum.clear();
else
{
accum = safe_getenv(accum);
o = std::copy(begin(accum), end(accum), o);
}
break;
} else
++f; // %% -> %
default:
if (in_var)
accum += ch;
else
*o++ = ch;
}
}
return o;
}
#include <iterator>
std::string expand_env(std::string const& input)
{
std::string result;
expand_env(begin(input), end(input), std::back_inserter(result));
return result;
}
#include <iostream>
#include <sstream>
#include <list>
int main()
{
// same use case as first answer, show `%%` escape
std::cout << "'" << expand_env("Greeti%%ng is %HOME% world!") << "'
";
// can be done streaming, to any container
std::istringstream iss("Greeti%%ng is %HOME% world!");
std::list<char> some_target;
std::istreambuf_iterator<char> f(iss), l;
expand_env(f, l, std::back_inserter(some_target));
std::cout << "Streaming results: '" << std::string(begin(some_target), end(some_target)) << "'
";
// some more edge cases uses to validate the algorithm (note `%%` doesn't
// act as escape if the first ends a 'pending' variable)
std::cout << "'" << expand_env("") << "'
";
std::cout << "'" << expand_env("%HOME%") << "'
";
std::cout << "'" << expand_env(" %HOME%") << "'
";
std::cout << "'" << expand_env("%HOME% ") << "'
";
std::cout << "'" << expand_env("%HOME%%HOME%") << "'
";
std::cout << "'" << expand_env(" %HOME%%HOME% ") << "'
";
std::cout << "'" << expand_env(" %HOME% %HOME% ") << "'
";
}
Which, on my box, prints:
'Greeti%ng is /home/sehe world!'
Streaming results: 'Greeti%ng is /home/sehe world!'
''
'/home/sehe'
' /home/sehe'
'/home/sehe '
'/home/sehe/home/sehe'
' /home/sehe/home/sehe '
' /home/sehe /home/sehe '
1 Of course, "properly" is subjective. At the very least, I think this
- would be useful (how else would you configure a value legitimitely containing
%
?)
- is how cmd.exe does it on Windows
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…