YARP
Yet Another Robot Platform
Property.cpp
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3  * SPDX-FileCopyrightText: 2006-2010 RobotCub Consortium
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <yarp/os/Property.h>
8 
10 #include <yarp/conf/string.h>
11 
12 #include <yarp/os/Bottle.h>
13 #include <yarp/os/NetType.h>
18 #include <yarp/os/ResourceFinder.h>
19 
20 #include <algorithm>
21 #include <cctype>
22 #include <cstdio>
23 #include <cstring>
24 #include <map>
25 #include <memory>
26 
27 using namespace yarp::os::impl;
28 using namespace yarp::os;
29 
30 namespace {
31 YARP_OS_LOG_COMPONENT(PROPERTY, "yarp.os.Property" )
32 }
33 
35 {
36 public:
38  std::unique_ptr<Property> backing;
39 
40  PropertyItem() = default;
41 
42  PropertyItem(const PropertyItem& rhs) :
43  bot(rhs.bot),
44  backing(nullptr)
45  {
46  if (rhs.backing) {
47  backing = std::make_unique<Property>(*(rhs.backing));
48  }
49  }
50 
52  {
53  if (&rhs != this) {
54  bot = rhs.bot;
55  if (rhs.backing) {
56  backing = std::make_unique<Property>(*(rhs.backing));
57  }
58  }
59  return *this;
60  }
61 
62  PropertyItem(PropertyItem&& rhs) noexcept = default;
63  PropertyItem& operator=(PropertyItem&& rhs) noexcept = default;
64 
65  ~PropertyItem() = default;
66 
67  void clear()
68  {
69  backing.reset();
70  }
71 
72  /*
73  * The const version of the processBuffered() method performs a const_cast,
74  * and calls the non-const version. This allows to call it in const methods.
75  * Conceptually this is not completely wrong because it does not modify
76  * the external state of the class, but just some internal representation.
77  */
78  void flush() const
79  {
80  const_cast<PropertyItem*>(this)->flush();
81  }
82 
83  void flush()
84  {
85  if (backing) {
86  Bottle flatten(backing->toString());
87  bot.append(flatten);
88  clear();
89  }
90  }
91 
92  std::string toString() const
93  {
94  flush();
95  return bot.toString();
96  }
97 };
98 
100 {
101 public:
102  std::map<std::string, PropertyItem> data;
104 
105  explicit Private(Property* owner) :
106  owner(owner)
107  {
108  }
109 
110  PropertyItem* getPropNoCreate(const std::string& key) const
111  {
112  auto it = data.find(key);
113  if (it == data.end()) {
114  return nullptr;
115  }
116  return const_cast<PropertyItem*>(&(it->second));
117  }
118 
119  PropertyItem* getProp(const std::string& key, bool create = true)
120  {
121  auto entry = data.find(key);
122  if (entry == data.end()) {
123  if (!create) {
124  return nullptr;
125  }
126  data[key] = PropertyItem();
127  entry = data.find(key);
128  }
129  yCAssert(PROPERTY, entry != data.end());
130  return &(entry->second);
131  }
132 
133  void put(const std::string& key, const std::string& val)
134  {
135  PropertyItem* p = getProp(key, true);
136  p->clear();
137  p->bot.clear();
138  p->bot.addString(key);
139  p->bot.addString(val);
140  }
141 
142  void put(const std::string& key, const Value& bit)
143  {
144  PropertyItem* p = getProp(key, true);
145  p->clear();
146  p->bot.clear();
147  p->bot.addString(key);
148  p->bot.add(bit);
149  }
150 
151  void put(const std::string& key, Value* bit)
152  {
153  PropertyItem* p = getProp(key, true);
154  p->clear();
155  p->bot.clear();
156  p->bot.addString(key);
157  p->bot.add(bit);
158  }
159 
160  Property& addGroup(const std::string& key)
161  {
162  PropertyItem* p = getProp(key, true);
163  p->clear();
164  p->bot.clear();
165  p->bot.addString(key);
166  p->backing = std::make_unique<Property>();
167  return *(p->backing);
168  }
169 
170  void unput(const std::string& key)
171  {
172  data.erase(key);
173  }
174 
175  bool check(const std::string& key) const
176  {
177  PropertyItem* p = getPropNoCreate(key);
178  if (owner->getMonitor() != nullptr) {
179  SearchReport report;
180  report.key = key;
181  report.isFound = (p != nullptr);
182  owner->reportToMonitor(report);
183  }
184  return p != nullptr;
185  }
186 
187  Value& get(const std::string& key) const
188  {
189  PropertyItem* p = getPropNoCreate(key);
190  if (p != nullptr) {
191  p->flush();
192  if (owner->getMonitor() != nullptr) {
193  SearchReport report;
194  report.key = key;
195  report.isFound = true;
196  report.value = p->bot.get(1).toString();
197  owner->reportToMonitor(report);
198  }
199  return p->bot.get(1);
200  }
201  if (owner->getMonitor() != nullptr) {
202  SearchReport report;
203  report.key = key;
204  owner->reportToMonitor(report);
205  }
206  return Value::getNullValue();
207  }
208 
209  Bottle& putBottleCompat(const char* key, const Bottle& val)
210  {
211  if (val.get(1).asString() == "=") {
212  Bottle b;
213  b.add(val.get(0));
214  b.append(val.tail().tail());
215  return putBottle(key, b);
216  }
217  return putBottle(key, val);
218  }
219 
220  Bottle& putBottle(const char* key, const Bottle& val)
221  {
222  PropertyItem* p = getProp(key, true);
223  p->clear();
224  p->bot = val;
225  return p->bot;
226  }
227 
228 
229  Bottle& putBottle(const char* key)
230  {
231  PropertyItem* p = getProp(key, true);
232  p->clear();
233  p->bot.clear();
234  return p->bot;
235  }
236 
237 
238  Bottle* getBottle(const std::string& key) const
239  {
240  PropertyItem* p = getPropNoCreate(key);
241  if (p != nullptr) {
242  p->flush();
243  return &(p->bot);
244  }
245  return nullptr;
246  }
247 
248  void clear()
249  {
250  data.clear();
251  }
252 
253  void fromString(const std::string& txt, bool wipe = true)
254  {
255  Bottle bot;
256  bot.fromString(txt);
257  fromBottle(bot, wipe);
258  }
259 
260  void fromCommand(int argc, char* argv[], bool wipe = true)
261  {
262  std::string tag;
263  Bottle accum;
264  Bottle total;
265  bool qualified = false;
266  for (int i = 0; i < argc; i++) {
267  std::string work = argv[i];
268  bool isTag = false;
269  if (work.length() >= 2) {
270  if (work[0] == '-' && work[1] == '-') {
271  work = work.substr(2, work.length() - 2);
272  isTag = true;
273  if (work.find("::") != std::string::npos) {
274  qualified = true;
275  }
276  }
277  }
278  if (isTag) {
279  if (!tag.empty()) {
280  total.addList().copy(accum);
281  }
282  tag = work;
283  accum.clear();
284  } else {
285  if (work.find('\\') != std::string::npos) {
286  // Specifically when reading from the command
287  // line, we will allow windows-style paths.
288  // Hence we have to break the "\" character
289  std::string buf;
290  for (char i : work) {
291  buf += i;
292  if (i == '\\') {
293  buf += i;
294  }
295  }
296  work = buf;
297  }
298  }
299  accum.add(Value::makeValue(work));
300  }
301  if (!tag.empty()) {
302  total.addList().copy(accum);
303  }
304  if (!qualified) {
305  fromBottle(total, wipe);
306  return;
307  }
308  if (wipe) {
309  clear();
310  }
311  Bottle* cursor = nullptr;
312  for (size_t i = 0; i < total.size(); i++) {
313  cursor = nullptr;
314  Bottle* term = total.get(i).asList();
315  if (term == nullptr) {
316  continue;
317  }
318  std::string key = term->get(0).asString();
319  std::string base = key;
320  while (key.length() > 0) {
321  base = key;
322  size_t at = key.find("::");
323  if (at != std::string::npos) {
324  base = key.substr(0, at);
325  key = key.substr(at + 2);
326  } else {
327  key = "";
328  }
329  Bottle& result = (cursor != nullptr) ? (cursor->findGroup(base)) : owner->findGroup(base);
330  if (result.isNull()) {
331  if (cursor == nullptr) {
332  cursor = &putBottle((base).c_str());
333  } else {
334  cursor = &cursor->addList();
335  }
336  cursor->addString(base);
337  } else {
338  cursor = &result;
339  }
340  }
341  if (cursor != nullptr) {
342  cursor->copy(*term);
343  cursor->get(0) = Value(base);
344  }
345  }
346  }
347 
348  bool readDir(const std::string& dirname, yarp::os::impl::DIR*& dir, std::string& result, const std::string& section = std::string())
349  {
350  bool ok = true;
351  yCDebug(PROPERTY, "reading directory %s", dirname.c_str());
352 
353  yarp::os::impl::dirent** namelist;
354  yarp::os::impl::closedir(dir);
355  dir = nullptr;
356  int n = yarp::os::impl::scandir(dirname.c_str(), &namelist, nullptr, yarp::os::impl::alphasort);
357  if (n < 0) {
358  return false;
359  }
360  for (int i = 0; i < n; i++) {
361  std::string name = namelist[i]->d_name;
362  free(namelist[i]);
363  auto len = static_cast<int>(name.length());
364  if (len < 4) {
365  continue;
366  }
367  if (name.substr(len - 4) != ".ini") {
368  continue;
369  }
370  std::string fname = std::string(dirname) + "/" + name;
371  std::replace(fname.begin(), fname.end(), '\\', '/');
372  if (section.empty()) {
373  ok = ok && readFile(fname, result, false);
374  result += "\n[]\n"; // reset any nested sections
375  } else {
376  result.append("[include ").append(section).append(" \"").append(fname).append("\" \"").append(fname).append("\"]\n");
377  }
378  }
379  free(namelist);
380  return ok;
381  }
382 
383  bool readFile(const std::string& fname, std::string& result, bool allowDir)
384  {
385  if (allowDir) {
386  yarp::os::impl::DIR* dir = yarp::os::impl::opendir(fname.c_str());
387  if (dir != nullptr) {
388  return readDir(fname, dir, result);
389  }
390  }
391  yCDebug(PROPERTY, "reading file %s", fname.c_str());
392  FILE* fin = fopen(fname.c_str(), "r");
393  if (fin == nullptr) {
394  return false;
395  }
396  char buf[25600];
397  while (fgets(buf, sizeof(buf) - 1, fin) != nullptr) {
398  result += buf;
399  }
400  fclose(fin);
401  fin = nullptr;
402  return true;
403  }
404 
405  bool fromConfigFile(const std::string& fname, Searchable& env, bool wipe = true)
406  {
407  std::string searchPath = env.check("CONFIG_PATH",
408  Value(""),
409  "path to search for config files")
410  .toString();
411 
412  yCDebug(PROPERTY, "looking for %s, search path: %s", fname.c_str(), searchPath.c_str());
413 
414  std::string pathPrefix;
415  std::string txt;
416 
417  bool ok = true;
418  if (!readFile(fname, txt, true)) {
419  ok = false;
420  for (const auto& s : yarp::conf::string::split(searchPath, ';')) {
421  std::string trial = s;
422  trial += '/';
423  trial += fname;
424 
425  yCDebug(PROPERTY, "looking for %s as %s", fname.c_str(), trial.c_str());
426 
427  txt = "";
428  if (readFile(trial, txt, true)) {
429  ok = true;
430  pathPrefix = s;
431  pathPrefix += '/';
432  break;
433  }
434  }
435  }
436 
437  std::string path;
438  size_t index = fname.rfind('/');
439  if (index == std::string::npos) {
440  index = fname.rfind('\\');
441  }
442  if (index != std::string::npos) {
443  path = fname.substr(0, index);
444  }
445 
446  if (!ok) {
447  yCError(PROPERTY, "cannot read from %s", fname.c_str());
448  return false;
449  }
450 
451  Property envExtended;
452  envExtended.fromString(env.toString());
453  if (!path.empty()) {
454  if (searchPath.length() > 0) {
455  searchPath += ";";
456  }
457  searchPath += pathPrefix;
458  searchPath += path;
459  envExtended.put("CONFIG_PATH", searchPath);
460  }
461 
462  fromConfig(txt.c_str(), envExtended, wipe);
463  return true;
464  }
465 
466  bool fromConfigDir(const std::string& dirname, const std::string& section, bool wipe = true)
467  {
468  Property p;
469  if (section.empty()) {
470  return fromConfigFile(dirname, p, wipe);
471  }
472 
473  yCDebug(PROPERTY, "looking for %s", dirname.c_str());
474 
475  yarp::os::impl::DIR* dir = yarp::os::impl::opendir(dirname.c_str());
476  if (dir == nullptr) {
477  yCError(PROPERTY, "cannot read from %s", dirname.c_str());
478  return false;
479  }
480 
481  std::string txt;
482  if (!readDir(dirname, dir, txt, section)) {
483  yCError(PROPERTY, "cannot read from %s", dirname.c_str());
484  return false;
485  }
486 
487  fromConfig(txt.c_str(), p, wipe);
488  return true;
489  }
490 
491  void fromConfig(const char* txt, Searchable& env, bool wipe = true)
492  {
493  StringInputStream sis;
494  sis.add(txt);
495  sis.add("\n");
496  if (wipe) {
497  clear();
498  }
499  std::string tag;
500  Bottle accum;
501  bool done = false;
502  do {
503  bool isTag = false;
504  bool including = false;
505  std::string buf;
506  bool good = true;
507  buf = sis.readLine('\n', &good);
508  while (good && !BottleImpl::isComplete(buf.c_str())) {
509  buf += sis.readLine('\n', &good);
510  }
511  if (!good) {
512  done = true;
513  }
514  if (!done) {
515  including = false;
516 
517  if (buf.find("//") != std::string::npos || buf.find('#') != std::string::npos) {
518  bool quoted = false;
519  bool prespace = true;
520  int comment = 0;
521  for (unsigned int i = 0; i < buf.length(); i++) {
522  char ch = buf[i];
523  if (ch == '\"') {
524  quoted = !quoted;
525  }
526  if (!quoted) {
527  if (ch == '/') {
528  comment++;
529  if (comment == 2) {
530  buf = buf.substr(0, i - 1);
531  break;
532  }
533  } else {
534  comment = 0;
535  if (ch == '#' && prespace) {
536  if (i == 0) {
537  buf = "";
538  } else {
539  buf = buf.substr(0, i - 1);
540  }
541  break;
542  }
543  }
544  prespace = (ch == ' ' || ch == '\t');
545  } else {
546  comment = 0;
547  prespace = false;
548  }
549  }
550  }
551 
552  // expand any environment references
553  buf = expand(buf.c_str(), env, *owner);
554 
555  if (buf.length() > 0 && buf[0] == '[') {
556  size_t stop = buf.find(']');
557  if (stop != std::string::npos) {
558  buf = buf.substr(1, stop - 1);
559  size_t space = buf.find(' ');
560  if (space != std::string::npos) {
561  Bottle bot(buf);
562  // BEGIN Handle include option
563  if (bot.size() > 1) {
564  if (bot.get(0).toString() == "import")
565  {
566  including = true;
567  // close an open group if an [include something] tag is found
568  if (!tag.empty()) {
569  if (accum.size() >= 1) {
570  putBottleCompat(tag.c_str(), accum);
571  }
572  tag = "";
573  }
574  std::string contextName = bot.get(1).toString();
575  std::string fileName = bot.get(2).toString();
577  rf.setDefaultContext(contextName);
578  rf.setDefaultConfigFile(fileName);
579  bool b = rf.configure(0,nullptr);
580  if (b)
581  {
582  tag = "";
583  std::string fname = rf.findFile(fileName);
584  yCTrace(PROPERTY, "Importing: %s\n", fname.c_str());
585  fromConfigFile(fname, env, false);
586  }
587  else
588  {
589  yCWarning(PROPERTY, "Unable to import file: %s from context %s\n", fileName.c_str(), contextName.c_str());
590  }
591  } else
592  if (bot.get(0).toString() == "include") {
593  including = true;
594  // close an open group if an [include something] tag is found
595  if (!tag.empty()) {
596  if (accum.size() >= 1) {
597  putBottleCompat(tag.c_str(), accum);
598  }
599  tag = "";
600  }
601  if (bot.size() > 2) {
602  std::string subName;
603  std::string fname;
604  if (bot.size() == 3) {
605  // [include section "filename"]
606  subName = bot.get(1).toString();
607  fname = bot.get(2).toString();
608 
609 
610  } else if (bot.size() == 4) {
611  // [include type section "filename"]
612  std::string key;
613  key = bot.get(1).toString();
614  subName = bot.get(2).toString();
615  fname = bot.get(3).toString();
616  Bottle* target = getBottle(key);
617  if (target == nullptr) {
618  Bottle init;
619  init.addString(key.c_str());
620  init.addString(subName.c_str());
621  putBottleCompat(key.c_str(),
622  init);
623  } else {
624  target->addString(subName.c_str());
625  }
626  } else {
627  yCError(PROPERTY, "bad include");
628  return;
629  }
630 
631 
632  Property p;
633  if (getBottle(subName) != nullptr) {
634  p.fromString(getBottle(subName)->tail().toString());
635  yCTrace(PROPERTY,
636  ">>> prior p %s\n",
637  p.toString().c_str());
638  }
639  p.fromConfigFile(fname, env, false);
640  accum.fromString(p.toString());
641  tag = subName;
642  yCTrace(PROPERTY, ">>> tag %s accum %s\n",
643  tag.c_str(),
644  accum.toString().c_str());
645  if (!tag.empty()) {
646  if (accum.size() >= 1) {
647  Bottle b;
648  b.addString(tag.c_str());
649  //Bottle& subList = b.addList();
650  //subList.copy(accum);
651  b.append(accum);
652  putBottleCompat(tag.c_str(),
653  b);
654  }
655  tag = "";
656  }
657  } else {
658  tag = "";
659  std::string fname = bot.get(1).toString();
660  yCTrace(PROPERTY, "Including %s\n", fname.c_str());
661  fromConfigFile(fname, env, false);
662  }
663  }
664  }
665  // END handle include option
666  // BEGIN handle group
667  if (bot.size() == 2 && !including) {
668  buf = bot.get(1).toString();
669  std::string key = bot.get(0).toString();
670  Bottle* target = getBottle(key);
671  if (target == nullptr) {
672  Bottle init;
673  init.addString(key);
674  init.addString(buf);
675  putBottleCompat(key.c_str(), init);
676  } else {
677  target->addString(buf);
678  }
679  }
680  // END handle group
681  }
682  if (!including) {
683  isTag = true;
684  }
685  }
686  }
687  }
688  if (!isTag && !including) {
689  Bottle bot;
690  bot.fromString(buf);
691  if (bot.size() >= 1) {
692  if (tag.empty()) {
693  putBottleCompat(bot.get(0).toString().c_str(), bot);
694  } else {
695  if (bot.get(1).asString() == "=") {
696  Bottle& b = accum.addList();
697  for (size_t i = 0; i < bot.size(); i++) {
698  if (i != 1) {
699  b.add(bot.get(i));
700  }
701  }
702  } else {
703  accum.addList().copy(bot);
704  }
705  }
706  }
707  }
708  if (isTag || done) {
709  if (!tag.empty()) {
710  if (accum.size() >= 1) {
711  putBottleCompat(tag.c_str(), accum);
712  }
713  tag = "";
714  }
715  tag = buf;
716  accum.clear();
717  accum.addString(tag);
718  if (!tag.empty()) {
719  if (getBottle(tag) != nullptr) {
720  // merge data
721  accum.append(getBottle(tag)->tail());
722  yCTrace(PROPERTY,
723  "MERGE %s, got %s\n",
724  tag.c_str(),
725  accum.toString().c_str());
726  }
727  }
728  }
729  } while (!done);
730  }
731 
732  void fromBottle(Bottle& bot, bool wipe = true)
733  {
734  if (wipe) {
735  clear();
736  }
737  for (size_t i = 0; i < bot.size(); i++) {
738  Value& bb = bot.get(i);
739  if (bb.isList()) {
740  Bottle* sub = bb.asList();
741  putBottle(bb.asList()->get(0).toString().c_str(), *sub);
742  }
743  }
744  }
745 
746  std::string toString() const
747  {
748  Bottle bot;
749  for (const auto& it : data) {
750  const PropertyItem& rec = it.second;
751  Bottle& sub = bot.addList();
752  rec.flush();
753  sub.copy(rec.bot);
754  }
755  return bot.toString();
756  }
757 
758  // expand any environment variables found
759  std::string expand(const char* txt, Searchable& env, Searchable& env2)
760  {
761  yCTrace(PROPERTY, "expanding %s\n", txt);
762  std::string input = txt;
763  if (input.find('$') == std::string::npos) {
764  // no variables present for sure
765  return txt;
766  }
767  // check if variables present
768  std::string output;
769  std::string var;
770  bool inVar = false;
771  bool varHasParen = false;
772  bool quoted = false;
773  for (size_t i = 0; i <= input.length(); i++) {
774  char ch = 0;
775  if (i < input.length()) {
776  ch = input[i];
777  }
778  if (quoted) {
779  if (!inVar) {
780  output += '\\';
781  if (ch != 0) {
782  output += ch;
783  }
784  } else {
785  if (ch != 0) {
786  var += ch;
787  }
788  }
789  quoted = false;
790  continue;
791  }
792  if (ch == '\\') {
793  quoted = true;
794  continue;
795  }
796 
797  if (inVar) {
798  if ((isalnum(ch) != 0) || (ch == '_')) {
799  var += ch;
800  continue;
801  }
802  if (ch == '(' || ch == '{') {
803  if (var.length() == 0) {
804  // ok, just ignore
805  varHasParen = true;
806  continue;
807  }
808  }
809  inVar = false;
810  yCTrace(PROPERTY, "VARIABLE %s\n", var.c_str());
811  std::string add = yarp::conf::environment::get_string(var);
812  if (add.empty()) {
813  add = env.find(var).toString();
814  }
815  if (add.empty()) {
816  add = env2.find(var).toString();
817  }
818  if (add.empty()) {
819  if (var == "__YARP__") {
820  add = "1";
821  }
822  }
823  if (add.find('\\') != std::string::npos) {
824  // Specifically when reading from the command
825  // line, we will allow windows-style paths.
826  // Hence we have to break the "\" character
827  std::string buf;
828  for (char c : add) {
829  buf += c;
830  if (c == '\\') {
831  buf += c;
832  }
833  }
834  add = buf;
835  }
836  output += add;
837  var = "";
838  if (varHasParen && (ch == '}' || ch == ')')) {
839  continue;
840  // don't need current char
841  }
842  }
843 
844  if (!inVar) {
845  if (ch == '$') {
846  inVar = true;
847  varHasParen = false;
848  continue;
849  }
850  if (ch != 0) {
851  output += ch;
852  }
853  }
854  }
855  return output;
856  }
857 
858  void fromArguments(const char* command, bool wipe = true)
859  {
860  char** szarg = new char*[128 + 1]; // maximum 128 arguments
861  char* szcmd = new char[strlen(command) + 1];
862  strcpy(szcmd, command);
863  int nargs = 0;
864  parseArguments(szcmd, &nargs, szarg, 128);
865  szarg[nargs] = nullptr;
866  fromCommand(nargs, szarg, wipe);
867  // clear allocated memory for arguments
868  delete[] szcmd;
869  szcmd = nullptr;
870  delete[] szarg;
871  szarg = nullptr;
872  }
873 
874  void parseArguments(char* azParam, int* argc, char** argv, int max_arg)
875  {
876  char* pNext = azParam;
877  size_t i;
878  int j;
879  int quoted = 0;
880  size_t len = strlen(azParam);
881 
882  // Protect spaces inside quotes, but lose the quotes
883  for (i = 0; i < len; i++) {
884  if ((quoted == 0) && ('"' == azParam[i])) {
885  quoted = 1;
886  azParam[i] = ' ';
887  } else if (((quoted) != 0) && ('"' == azParam[i])) {
888  quoted = 0;
889  azParam[i] = ' ';
890  } else if (((quoted) != 0) && (' ' == azParam[i])) {
891  azParam[i] = '\1';
892  }
893  }
894 
895  // init
896  memset(argv, 0x00, sizeof(char*) * max_arg);
897  *argc = 1;
898  argv[0] = azParam;
899 
900  while ((nullptr != pNext) && (*argc < max_arg)) {
901  splitArguments(pNext, &(argv[*argc]));
902  pNext = argv[*argc];
903 
904  if (nullptr != argv[*argc]) {
905  *argc += 1;
906  }
907  }
908 
909  for (j = 0; j < *argc; j++) {
910  len = strlen(argv[j]);
911  for (i = 0; i < len; i++) {
912  if ('\1' == argv[j][i]) {
913  argv[j][i] = ' ';
914  }
915  }
916  }
917  }
918 
919  void splitArguments(char* line, char** args)
920  {
921  char* pTmp = strchr(line, ' ');
922  if (pTmp != nullptr) {
923  *pTmp = '\0';
924  pTmp++;
925  while (*pTmp == ' ') {
926  pTmp++;
927  }
928  if (*pTmp == '\0') {
929  pTmp = nullptr;
930  }
931  }
932  *args = pTmp;
933  }
934 };
935 
937  Searchable(),
938  Portable(),
939  mPriv(new Private(this))
940 {
941 }
942 
943 #ifndef YARP_NO_DEPRECATED // Since YARP 3.3
944 Property::Property(int hash_size) :
945  Searchable(),
946  Portable(),
947  mPriv(new Private(this))
948 {
949  YARP_UNUSED(hash_size);
950 }
951 #endif
952 
953 Property::Property(const char* str) :
954  Searchable(),
955  Portable(),
956  mPriv(new Private(this))
957 {
958  fromString(str);
959 }
960 
962  Searchable(static_cast<const Searchable&>(prop)),
963  Portable(static_cast<const Portable&>(prop)),
964  mPriv(new Private(this))
965 {
966  fromString(prop.toString());
967 }
968 
969 Property::Property(Property&& prop) noexcept :
970  Searchable(std::move(static_cast<Searchable&>(prop))),
971  Portable(std::move(static_cast<Portable&>(prop))),
972  mPriv(prop.mPriv)
973 {
974  mPriv->owner = this;
975 
976  prop.mPriv = nullptr;
977 }
978 
979 Property::Property(std::initializer_list<std::pair<std::string, yarp::os::Value>> values) :
980  Searchable(),
981  Portable(),
982  mPriv(new Private(this))
983 {
984  for (const auto& val : values) {
985  put(val.first, val.second);
986  }
987 }
988 
990 {
991  delete mPriv;
992 }
993 
995 {
996  if (&rhs != this) {
997  Searchable::operator=(static_cast<const Searchable&>(rhs));
998  Portable::operator=(static_cast<const Portable&>(rhs));
999  mPriv->data = rhs.mPriv->data;
1000  mPriv->owner = this;
1001  }
1002  return *this;
1003 }
1004 
1006 {
1007  Searchable::operator=(std::move(static_cast<Searchable&>(rhs)));
1008  Portable::operator=(std::move(static_cast<Portable&>(rhs)));
1009  std::swap(mPriv, rhs.mPriv);
1010  mPriv->owner = this;
1011  rhs.mPriv->owner = &rhs;
1012  return *this;
1013 }
1014 
1015 void Property::put(const std::string& key, const std::string& value)
1016 {
1017  mPriv->put(key, value);
1018 }
1019 
1020 void Property::put(const std::string& key, const Value& value)
1021 {
1022  mPriv->put(key, value);
1023 }
1024 
1025 
1026 void Property::put(const std::string& key, Value* value)
1027 {
1028  mPriv->put(key, value);
1029 }
1030 
1031 void Property::put(const std::string& key, int value)
1032 {
1033  put(key, Value::makeInt32(value));
1034 }
1035 
1036 void Property::put(const std::string& key, double value)
1037 {
1038  put(key, Value::makeFloat64(value));
1039 }
1040 
1041 bool Property::check(const std::string& key) const
1042 {
1043  return mPriv->check(key);
1044 }
1045 
1046 void Property::unput(const std::string& key)
1047 {
1048  mPriv->unput(key);
1049 }
1050 
1051 Value& Property::find(const std::string& key) const
1052 {
1053  return mPriv->get(key);
1054 }
1055 
1056 
1058 {
1059  mPriv->clear();
1060 }
1061 
1062 
1063 void Property::fromString(const std::string& txt, bool wipe)
1064 {
1065  mPriv->fromString(txt, wipe);
1066 }
1067 
1068 
1069 std::string Property::toString() const
1070 {
1071  return mPriv->toString();
1072 }
1073 
1074 void Property::fromCommand(int argc, char* argv[], bool skipFirst, bool wipe)
1075 {
1076  if (skipFirst) {
1077  argc--;
1078  argv++;
1079  }
1080  mPriv->fromCommand(argc, argv, wipe);
1081 }
1082 
1083 void Property::fromCommand(int argc, const char* argv[], bool skipFirst, bool wipe)
1084 {
1085  fromCommand(argc, const_cast<char**>(argv), skipFirst, wipe);
1086 }
1087 
1088 void Property::fromArguments(const char* arguments, bool wipe)
1089 {
1090  mPriv->fromArguments(arguments, wipe);
1091 }
1092 
1093 bool Property::fromConfigDir(const std::string& dirname, const std::string& section, bool wipe)
1094 {
1095  return mPriv->fromConfigDir(dirname, section, wipe);
1096 }
1097 
1098 bool Property::fromConfigFile(const std::string& fname, bool wipe)
1099 {
1100  Property p;
1101  return fromConfigFile(fname, p, wipe);
1102 }
1103 
1104 
1105 bool Property::fromConfigFile(const std::string& fname, Searchable& env, bool wipe)
1106 {
1107  return mPriv->fromConfigFile(fname, env, wipe);
1108 }
1109 
1110 void Property::fromConfig(const char* txt, bool wipe)
1111 {
1112  Property p;
1113  fromConfig(txt, p, wipe);
1114 }
1115 
1116 void Property::fromConfig(const char* txt, Searchable& env, bool wipe)
1117 {
1118  mPriv->fromConfig(txt, env, wipe);
1119 }
1120 
1121 
1123 {
1124  // for now just delegate to Bottle
1125  Bottle b;
1126  bool ok = b.read(reader);
1127  if (ok) {
1128  fromString(b.toString());
1129  }
1130  return ok;
1131 }
1132 
1133 
1135 {
1136  // for now just delegate to Bottle
1137  Bottle b(toString());
1138  return b.write(writer);
1139 }
1140 
1141 
1142 Bottle& Property::findGroup(const std::string& key) const
1143 {
1144  Bottle* result = mPriv->getBottle(key);
1145  if (getMonitor() != nullptr) {
1146  SearchReport report;
1147  report.key = key;
1148  report.isGroup = true;
1149  if (result != nullptr) {
1150  report.isFound = true;
1151  report.value = result->toString();
1152  }
1153  reportToMonitor(report);
1154  if (result != nullptr) {
1155  std::string context = getMonitorContext();
1156  context += ".";
1157  context += key;
1158  result->setMonitor(getMonitor(),
1159  context.c_str()); // pass on any monitoring
1160  }
1161  }
1162 
1163  if (result != nullptr) {
1164  return *result;
1165  }
1166  return Bottle::getNullBottle();
1167 }
1168 
1169 
1170 void Property::fromQuery(const char* url, bool wipe)
1171 {
1172  if (wipe) {
1173  clear();
1174  }
1175  std::string str = url;
1176  str += "&";
1177  std::string buf;
1178  std::string key;
1179  std::string val;
1180  int code = 0;
1181  int coding = 0;
1182 
1183  for (char ch : str) {
1184  if (ch == '=') {
1185  key = buf;
1186  val = "";
1187  buf = "";
1188  yCTrace(PROPERTY, "adding key %s\n", key.c_str());
1189  } else if (ch == '&') {
1190  yCTrace(PROPERTY, "adding val %s\n", val.c_str());
1191  val = buf;
1192  buf = "";
1193  if (!key.empty() && !val.empty()) {
1194  put(key, val);
1195  }
1196  key = "";
1197  } else if (ch == '?') {
1198  buf = "";
1199  } else {
1200  if (ch == '+') {
1201  ch = ' ';
1202  } else if (ch == '%') {
1203  coding = 2;
1204  } else {
1205  if (coding != 0) {
1206  int hex = 0;
1207  if (ch >= '0' && ch <= '9') {
1208  hex = ch - '0';
1209  }
1210  if (ch >= 'A' && ch <= 'F') {
1211  hex = ch - 'A' + 10;
1212  }
1213  if (ch >= 'a' && ch <= 'f') {
1214  hex = ch - 'a' + 10;
1215  }
1216  code *= 16;
1217  code += hex;
1218  coding--;
1219  if (coding == 0) {
1220  ch = code;
1221  }
1222  }
1223  }
1224  if (coding == 0) {
1225  buf += ch;
1226  }
1227  }
1228  }
1229 }
1230 
1231 
1232 Property& yarp::os::Property::addGroup(const std::string& key)
1233 {
1234  return mPriv->addGroup(key);
1235 }
void parseArguments(char *io_pLine, int *o_pArgc, char **o_pArgv)
Breaks up a line into multiple arguments.
Definition: Run.cpp:2406
void clear()
Definition: Property.cpp:67
std::string toString() const
Definition: Property.cpp:92
~PropertyItem()=default
Bottle bot
Definition: Property.cpp:37
PropertyItem(PropertyItem &&rhs) noexcept=default
void flush() const
Definition: Property.cpp:78
PropertyItem(const PropertyItem &rhs)
Definition: Property.cpp:42
void flush()
Definition: Property.cpp:83
std::unique_ptr< Property > backing
Definition: Property.cpp:38
PropertyItem & operator=(PropertyItem &&rhs) noexcept=default
PropertyItem()=default
PropertyItem & operator=(const PropertyItem &rhs)
Definition: Property.cpp:51
void unput(const std::string &key)
Definition: Property.cpp:170
void fromCommand(int argc, char *argv[], bool wipe=true)
Definition: Property.cpp:260
void put(const std::string &key, const Value &bit)
Definition: Property.cpp:142
Private(Property *owner)
Definition: Property.cpp:105
PropertyItem * getPropNoCreate(const std::string &key) const
Definition: Property.cpp:110
void parseArguments(char *azParam, int *argc, char **argv, int max_arg)
Definition: Property.cpp:874
std::string toString() const
Definition: Property.cpp:746
Property & addGroup(const std::string &key)
Definition: Property.cpp:160
bool check(const std::string &key) const
Definition: Property.cpp:175
void put(const std::string &key, const std::string &val)
Definition: Property.cpp:133
void fromArguments(const char *command, bool wipe=true)
Definition: Property.cpp:858
bool fromConfigDir(const std::string &dirname, const std::string &section, bool wipe=true)
Definition: Property.cpp:466
Bottle & putBottle(const char *key, const Bottle &val)
Definition: Property.cpp:220
void fromConfig(const char *txt, Searchable &env, bool wipe=true)
Definition: Property.cpp:491
void fromBottle(Bottle &bot, bool wipe=true)
Definition: Property.cpp:732
void fromString(const std::string &txt, bool wipe=true)
Definition: Property.cpp:253
Bottle & putBottle(const char *key)
Definition: Property.cpp:229
Bottle & putBottleCompat(const char *key, const Bottle &val)
Definition: Property.cpp:209
std::map< std::string, PropertyItem > data
Definition: Property.cpp:102
bool fromConfigFile(const std::string &fname, Searchable &env, bool wipe=true)
Definition: Property.cpp:405
PropertyItem * getProp(const std::string &key, bool create=true)
Definition: Property.cpp:119
Bottle * getBottle(const std::string &key) const
Definition: Property.cpp:238
bool readDir(const std::string &dirname, yarp::os::impl::DIR *&dir, std::string &result, const std::string &section=std::string())
Definition: Property.cpp:348
std::string expand(const char *txt, Searchable &env, Searchable &env2)
Definition: Property.cpp:759
void put(const std::string &key, Value *bit)
Definition: Property.cpp:151
bool readFile(const std::string &fname, std::string &result, bool allowDir)
Definition: Property.cpp:383
Value & get(const std::string &key) const
Definition: Property.cpp:187
void splitArguments(char *line, char **args)
Definition: Property.cpp:919
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:74
static Bottle & getNullBottle()
A special Bottle with no content.
Definition: Bottle.cpp:342
void add(const Value &value)
Add a Value to the bottle, at the end of the list.
Definition: Bottle.cpp:336
void fromString(const std::string &text)
Initializes bottle from a string.
Definition: Bottle.cpp:204
void append(const Bottle &alt)
Append the content of the given bottle to the current list.
Definition: Bottle.cpp:380
Bottle & addList()
Places an empty nested list in the bottle, at the end of the list.
Definition: Bottle.cpp:182
size_type size() const
Gets the number of elements in the bottle.
Definition: Bottle.cpp:251
bool read(ConnectionReader &reader) override
Set the bottle's value based on input from a network connection.
Definition: Bottle.cpp:240
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition: Bottle.cpp:246
Bottle & findGroup(const std::string &key) const override
Gets a list corresponding to a given keyword.
Definition: Bottle.cpp:302
void copy(const Bottle &alt, size_type first=0, size_type len=npos)
Copy all or part of another Bottle.
Definition: Bottle.cpp:266
Bottle tail() const
Get all but the first element of a bottle.
Definition: Bottle.cpp:388
void clear()
Empties the bottle of any objects it contains.
Definition: Bottle.cpp:121
bool write(ConnectionWriter &writer) const override
Output a representation of the bottle to a network connection.
Definition: Bottle.cpp:230
bool isNull() const override
Checks if the object is invalid.
Definition: Bottle.cpp:370
void addString(const char *str)
Places a string in the bottle, at the end of the list.
Definition: Bottle.cpp:170
std::string toString() const override
Gives a human-readable textual representation of the bottle.
Definition: Bottle.cpp:211
An interface for reading from a network connection.
An interface for writing to a network connection.
std::string readLine(const char terminal='\n', bool *success=nullptr)
Read a block of text terminated with a specific marker (or EOF).
Definition: InputStream.cpp:54
This is a base class for objects that can be both read from and be written to the YARP network.
Definition: Portable.h:26
A class for storing options and configuration information.
Definition: Property.h:34
Value & find(const std::string &key) const override
Gets a value corresponding to a given keyword.
Definition: Property.cpp:1051
std::string toString() const override
Return a standard text representation of the content of the object.
Definition: Property.cpp:1069
void fromString(const std::string &txt, bool wipe=true)
Interprets a string as a list of properties.
Definition: Property.cpp:1063
bool fromConfigFile(const std::string &fname, bool wipe=true)
Interprets a file as a list of properties.
Definition: Property.cpp:1098
void put(const std::string &key, const std::string &value)
Associate the given key with the given string.
Definition: Property.cpp:1015
void fromConfig(const char *txt, bool wipe=true)
Parses text in the configuration format described in fromConfigFile().
Definition: Property.cpp:1110
virtual Bottle & findGroup(const std::string &key) const=0
Gets a list corresponding to a given keyword.
Property & operator=(const Property &prop)
Copy assignment operator.
Definition: Property.cpp:994
bool write(ConnectionWriter &writer) const override
Write this object to a network connection.
Definition: Property.cpp:1134
bool read(ConnectionReader &reader) override
Read this object from a network connection.
Definition: Property.cpp:1122
bool fromConfigDir(const std::string &dirname, const std::string &section=std::string(), bool wipe=true)
Interprets all files in a directory as lists of properties as described in fromConfigFile().
Definition: Property.cpp:1093
void clear()
Remove all associations.
Definition: Property.cpp:1057
Property()
Constructor.
Definition: Property.cpp:936
virtual bool check(const std::string &key) const=0
Check if there exists a property of the given name.
void fromArguments(const char *arguments, bool wipe=true)
Interprets a list of command arguments as a list of properties.
Definition: Property.cpp:1088
~Property() override
Destructor.
Definition: Property.cpp:989
Property & addGroup(const std::string &key)
Add a nested group.
Definition: Property.cpp:1232
Bottle & findGroup(const std::string &key) const override
Gets a list corresponding to a given keyword.
Definition: Property.cpp:1142
void fromQuery(const char *url, bool wipe=true)
Parses text in a url.
Definition: Property.cpp:1170
void unput(const std::string &key)
Remove the association from the given key to a value, if present.
Definition: Property.cpp:1046
void fromCommand(int argc, char *argv[], bool skipFirst=true, bool wipe=true)
Interprets a list of command arguments as a list of properties.
Definition: Property.cpp:1074
Helper class for finding config files and other external resources.
bool setDefaultContext(const std::string &contextName)
Sets the context for the current ResourceFinder object.
bool configure(int argc, char *argv[], bool skipFirstArgument=true)
Sets up the ResourceFinder.
std::string findFile(const std::string &name)
Find the full path to a file.
bool setDefaultConfigFile(const std::string &fname)
Provide a default value for the configuration file (can be overridden from command line with the –fro...
A base class for nested structures that can be searched.
Definition: Searchable.h:66
virtual Value & find(const std::string &key) const =0
Gets a value corresponding to a given keyword.
Searchable & operator=(const Searchable &rhs)=default
Copy assignment operator.
virtual bool check(const std::string &key) const =0
Check if there exists a property of the given name.
virtual std::string toString() const =0
Return a standard text representation of the content of the object.
An InputStream that reads from a string.
void add(const std::string &txt)
A single value (typically within a Bottle).
Definition: Value.h:45
static Value * makeFloat64(yarp::conf::float64_t x)
Create a 64-bit floating point Value.
Definition: Value.cpp:416
static Value * makeValue(const std::string &txt)
Create a Value from a text description.
Definition: Value.cpp:456
virtual bool isList() const
Checks if value is a list.
Definition: Value.cpp:162
static Value & getNullValue()
Return an invalid, "null" Value.
Definition: Value.cpp:466
virtual Bottle * asList() const
Get list value.
Definition: Value.cpp:240
std::string toString() const override
Return a standard text representation of the content of the object.
Definition: Value.cpp:356
static Value * makeInt32(std::int32_t x)
Create a 32-bit integer Value.
Definition: Value.cpp:401
virtual std::string asString() const
Get string value.
Definition: Value.cpp:234
static bool isComplete(const char *txt)
Definition: BottleImpl.cpp:255
std::string toString(const T &value)
convert an arbitrary type to string.
#define yCError(component,...)
Definition: LogComponent.h:154
#define yCAssert(component, x)
Definition: LogComponent.h:169
#define yCTrace(component,...)
Definition: LogComponent.h:85
#define yCWarning(component,...)
Definition: LogComponent.h:143
#define yCDebug(component,...)
Definition: LogComponent.h:109
#define YARP_OS_LOG_COMPONENT(name, name_string)
Definition: LogComponent.h:35
std::string get_string(const std::string &key, bool *found=nullptr)
Read a string from an environment variable.
Definition: environment.h:68
ContainerT split(const typename ContainerT::value_type &s, std::basic_regex< typename ContainerT::value_type::value_type > regex)
Utility to split a string by a separator, into a vector of strings.
Definition: string.h:27
The components from which ports and connections are built.
An interface to the operating system, including Port based communication.
#define YARP_UNUSED(var)
Definition: api.h:162