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