substitution.cpp
Go to the documentation of this file.
1 // Parse the $(...) substitution args
2 // Author: Max Schwarz <max.schwarz@uni-bonn.de>
3 
4 #include "substitution.h"
5 #include "substitution_python.h"
6 #include "string_utils.h"
7 
8 #include "launch_config.h"
9 #include "../package_registry.h"
10 
11 #include <boost/algorithm/string/trim.hpp>
12 #include <boost/filesystem.hpp>
13 
14 #include <cstdarg>
15 
16 namespace fs = boost::filesystem;
17 
18 namespace rosmon
19 {
20 namespace launch
21 {
22 
23 using Handler = std::function<std::string(const std::string&, const std::string&)>;
24 using HandlerMap = std::map<std::string, Handler>;
25 
27 {
31 };
32 
33 namespace substitutions
34 {
35  std::string anon(const std::string& name, ParseContext& context)
36  {
37  std::string base = name;
38  boost::trim(base);
39 
40  return context.anonName(base);
41  }
42 
43  std::string arg(const std::string& name, const ParseContext& context)
44  {
45  auto it = context.arguments().find(name);
46  if(it == context.arguments().end())
47  throw SubstitutionException::format("$(arg {}): Unknown arg", name);
48 
49  std::string value = it->second;
50 
51  if(value == UNSET_MARKER)
52  {
54  "$(arg {}): Accessing unset argument '{}', please specify as parameter.", name, name
55  );
56  }
57 
58  return value;
59  }
60 
61  std::string dirname(const ParseContext& context)
62  {
63  fs::path launch_file = context.filename();
64  return fs::absolute(launch_file).parent_path().string();
65  }
66 
67  std::string env(const std::string& name)
68  {
69  const char* envval = getenv(name.c_str());
70  if(!envval)
71  throw SubstitutionException::format("$(env {}): Environment variable not set!", name);
72 
73  return envval;
74  }
75 
76  std::string optenv(const std::string& name, const std::string& defaultValue)
77  {
78  const char* envval = getenv(name.c_str());
79  if(envval)
80  return envval;
81 
82  return defaultValue;
83  }
84 
85  std::string find_stupid(const std::string& name)
86  {
87  std::string path = PackageRegistry::getPath(name);
88  if(!path.empty())
89  return path;
90 
91  throw SubstitutionException::format("$(find {}): Could not find package", name);
92  }
93 }
94 
95 static std::string parseOneElement(const std::string& input, const HandlerMap& handlers, bool strict, bool* found)
96 {
98  std::size_t begin = 0;
99 
100  for(std::size_t i = 0; i < input.size(); ++i)
101  {
102  char c = input[i];
103 
104  switch(state)
105  {
106  case PARSER_IDLE:
107  if(c == '$')
108  state = PARSER_DOLLAR;
109  break;
110  case PARSER_DOLLAR:
111  if(c == '(')
112  {
113  state = PARSER_INSIDE;
114  begin = i+1;
115  }
116  break;
117  case PARSER_INSIDE:
118  if(c == '$')
119  state = PARSER_DOLLAR;
120  else if(c == ')')
121  {
122  // We found something!
123  std::string contents = input.substr(begin, i - begin);
124  std::string after = input.substr(i+1);
125 
126  // Split into name and args
127  auto pos = contents.find(' ');
128  std::string name = contents.substr(0, pos);
129  std::string args;
130  if(pos != std::string::npos)
131  args = contents.substr(pos+1);
132 
133  // Remove leading/trailing whitespace and simplify any
134  // internal whitespace
136 
137  auto it = handlers.find(name);
138  if(it != handlers.end())
139  {
140  std::string replacement = it->second(args, after);
141 
142  std::stringstream ss;
143  ss << input.substr(0, begin-2);
144  ss << replacement;
145  ss << after;
146 
147  *found = true;
148  return ss.str();
149  }
150 
151  if(strict)
152  {
153  throw SubstitutionException::format("Unknown substitution arg '{}'", name);
154  }
155 
156  state = PARSER_IDLE;
157  }
158  break;
159  }
160  }
161 
162  *found = false;
163  return input;
164 }
165 
166 std::string parseSubstitutionArgs(const std::string& input, ParseContext& context)
167 {
168  bool found = false;
169  std::string buffer = input;
170 
171  // $(eval ) is only allowed if it spans the entire value, so handle that here.
172  if(buffer.size() > 6 && buffer.substr(0,6) == "$(eval" && buffer[buffer.size()-1] == ')')
173  {
174  return evaluatePython(buffer.substr(7, buffer.size() - 7 - 1), context);
175  }
176 
177  HandlerMap simpleHandlers = {
178  {"anon", [&context](const std::string& args, const std::string&) -> std::string{
179  return substitutions::anon(args, context);
180  }},
181  {"arg", [&context](const std::string& args, const std::string&) -> std::string{
182  return substitutions::arg(args, context);
183  }},
184  {"dirname", [&context](const std::string&, const std::string&) -> std::string{
185  return substitutions::dirname(context);
186  }},
187  {"env", [](const std::string& args, const std::string&) -> std::string{
188  return substitutions::env(args);
189  }},
190  {"optenv", [](const std::string& args, const std::string&) -> std::string{
191  auto pos = args.find(' ');
192  std::string defaultValue;
193  std::string name = args;
194  if(pos != std::string::npos)
195  {
196  defaultValue = args.substr(pos + 1);
197  name = args.substr(0, pos);
198  }
199 
200  return substitutions::optenv(name, defaultValue);
201  }},
202  };
203 
204  do
205  {
206  buffer = parseOneElement(buffer, simpleHandlers, false, &found);
207  }
208  while(found);
209 
210  HandlerMap findHandlers = {
211  {"find", [](const std::string& args, const std::string& after) -> std::string{
212  // Extract path after $(find ...)
213  auto pos = after.find(' ');
214  std::string filename = after.substr(0, pos);
215  boost::trim(filename);
216 
217  if(!filename.empty())
218  {
219  std::string path = PackageRegistry::findPathToFile(args, filename);
220  if(!path.empty())
221  return path;
222  }
223 
224  // Fallback to "rospack find"
225  std::string path = PackageRegistry::getPath(args);
226  if(!path.empty())
227  return path;
228 
229  throw SubstitutionException::format("$(find {}): Could not find package", args);
230  }},
231  };
232 
233  do
234  {
235  buffer = parseOneElement(buffer, findHandlers, true, &found);
236  }
237  while(found);
238 
239  return buffer;
240 }
241 
242 }
243 }
std::string anon(const std::string &name, ParseContext &context)
std::string simplifyWhitespace(const std::string &input)
Compress any sequence of whitespace to single spaces.
static std::string parseOneElement(const std::string &input, const HandlerMap &handlers, bool strict, bool *found)
std::string anonName(const std::string &base)
std::map< std::string, Handler > HandlerMap
std::function< std::string(const std::string &, const std::string &)> Handler
std::string evaluatePython(const std::string &input, ParseContext &context)
Evaluate a $(eval ...) python expression.
static SubstitutionException format(const char *format, const Args &...args)
Definition: substitution.h:33
const char * UNSET_MARKER
state
Definition: basic.py:139
std::string dirname(const ParseContext &context)
const std::string & filename() const
Definition: launch_config.h:67
static std::string getPath(const std::string &package)
static std::string findPathToFile(const std::string &package, const std::string &name)
Find path to a package file.
std::string optenv(const std::string &name, const std::string &defaultValue)
std::string env(const std::string &name)
std::string arg(const std::string &name, const ParseContext &context)
std::string parseSubstitutionArgs(const std::string &input, ParseContext &context)
std::string find_stupid(const std::string &name)
$(find ...) which always gives rospack find results
const std::map< std::string, std::string > & arguments() const
Definition: launch_config.h:96


rosmon_core
Author(s): Max Schwarz
autogenerated on Sat Jan 9 2021 03:35:43