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>
19
20#include <algorithm>
21#include <cctype>
22#include <cstdio>
23#include <cstring>
24#include <map>
25#include <memory>
26
27using namespace yarp::os::impl;
28using namespace yarp::os;
29
30namespace {
31YARP_OS_LOG_COMPONENT(PROPERTY, "yarp.os.Property" )
32}
33
35{
36public:
38 std::unique_ptr<Property> backing;
39
40 PropertyItem() = default;
41
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{
101public:
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 {
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 {
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 {
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 {
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
936Property::Property() :
937 Searchable(),
938 Portable(),
939 mPriv(new Private(this))
940{
941}
942
943#ifndef YARP_NO_DEPRECATED // Since YARP 3.3
944Property::Property(int hash_size) :
945 Searchable(),
946 Portable(),
947 mPriv(new Private(this))
948{
949 YARP_UNUSED(hash_size);
950}
951#endif
952
953Property::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
969Property::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
979Property::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
1015void Property::put(const std::string& key, const std::string& value)
1016{
1017 mPriv->put(key, value);
1018}
1019
1020void Property::put(const std::string& key, const Value& value)
1021{
1022 mPriv->put(key, value);
1023}
1024
1025
1026void Property::put(const std::string& key, Value* value)
1027{
1028 mPriv->put(key, value);
1029}
1030
1031void Property::put(const std::string& key, int value)
1032{
1033 put(key, Value::makeInt32(value));
1034}
1035
1036void Property::put(const std::string& key, double value)
1037{
1038 put(key, Value::makeFloat64(value));
1039}
1040
1041bool Property::check(const std::string& key) const
1042{
1043 return mPriv->check(key);
1044}
1045
1046void Property::unput(const std::string& key)
1047{
1048 mPriv->unput(key);
1049}
1050
1051Value& Property::find(const std::string& key) const
1052{
1053 return mPriv->get(key);
1054}
1055
1056
1058{
1059 mPriv->clear();
1060}
1061
1062
1063void Property::fromString(const std::string& txt, bool wipe)
1064{
1065 mPriv->fromString(txt, wipe);
1066}
1067
1068
1069std::string Property::toString() const
1070{
1071 return mPriv->toString();
1072}
1073
1074void 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
1083void Property::fromCommand(int argc, const char* argv[], bool skipFirst, bool wipe)
1084{
1085 fromCommand(argc, const_cast<char**>(argv), skipFirst, wipe);
1086}
1087
1088void Property::fromArguments(const char* arguments, bool wipe)
1089{
1090 mPriv->fromArguments(arguments, wipe);
1091}
1092
1093bool Property::fromConfigDir(const std::string& dirname, const std::string& section, bool wipe)
1094{
1095 return mPriv->fromConfigDir(dirname, section, wipe);
1096}
1097
1098bool Property::fromConfigFile(const std::string& fname, bool wipe)
1099{
1100 Property p;
1101 return fromConfigFile(fname, p, wipe);
1102}
1103
1104
1105bool Property::fromConfigFile(const std::string& fname, Searchable& env, bool wipe)
1106{
1107 return mPriv->fromConfigFile(fname, env, wipe);
1108}
1109
1110void Property::fromConfig(const char* txt, bool wipe)
1111{
1112 Property p;
1113 fromConfig(txt, p, wipe);
1114}
1115
1116void 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
1142Bottle& 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
1170void 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
1233{
1234 return mPriv->addGroup(key);
1235}
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 & operator=(const PropertyItem &rhs)
Definition: Property.cpp:51
PropertyItem & operator=(PropertyItem &&rhs) noexcept=default
PropertyItem(const PropertyItem &rhs)
Definition: Property.cpp:42
void flush()
Definition: Property.cpp:83
std::unique_ptr< Property > backing
Definition: Property.cpp:38
PropertyItem()=default
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
Value & get(const std::string &key) const
Definition: Property.cpp:187
Bottle * getBottle(const std::string &key) const
Definition: Property.cpp:238
void parseArguments(char *azParam, int *argc, char **argv, int max_arg)
Definition: Property.cpp:874
Property & addGroup(const std::string &key)
Definition: Property.cpp:160
std::string toString() const
Definition: Property.cpp:746
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
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
Bottle & putBottle(const char *key)
Definition: Property.cpp:229
void fromString(const std::string &txt, bool wipe=true)
Definition: Property.cpp:253
std::map< std::string, PropertyItem > data
Definition: Property.cpp:102
PropertyItem * getProp(const std::string &key, bool create=true)
Definition: Property.cpp:119
bool fromConfigFile(const std::string &fname, Searchable &env, bool wipe=true)
Definition: Property.cpp:405
PropertyItem * getPropNoCreate(const std::string &key) const
Definition: Property.cpp:110
Bottle & putBottleCompat(const char *key, const Bottle &val)
Definition: Property.cpp:209
bool readDir(const std::string &dirname, yarp::os::impl::DIR *&dir, std::string &result, const std::string &section=std::string())
Definition: Property.cpp:348
Bottle & putBottle(const char *key, const Bottle &val)
Definition: Property.cpp:220
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
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:64
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:25
A class for storing options and configuration information.
Definition: Property.h:33
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
bool check(const std::string &key) const override
Check if there exists a property of the given name.
Definition: Property.cpp:1041
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
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:56
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.
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.
An InputStream that reads from a string.
void add(const std::string &txt)
A single value (typically within a Bottle).
Definition: Value.h:43
static Value * makeFloat64(yarp::conf::float64_t x)
Create a 64-bit floating point Value.
Definition: Value.cpp:416
virtual bool isList() const
Checks if value is a list.
Definition: Value.cpp:162
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
#define yCError(component,...)
Definition: LogComponent.h:213
#define yCAssert(component, x)
Definition: LogComponent.h:240
#define yCTrace(component,...)
Definition: LogComponent.h:84
#define yCWarning(component,...)
Definition: LogComponent.h:192
#define yCDebug(component,...)
Definition: LogComponent.h:128
#define YARP_OS_LOG_COMPONENT(name, name_string)
Definition: LogComponent.h:29
std::string get_string(const std::string &key, bool *found=nullptr)
Read a string from an environment variable.
Definition: environment.h:66
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:26
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