1 // Copyright (c) 2005-2007 Andre Merzky
2 // Copyright (c) 2005-2018 Hartmut Kaiser
3 // Copyright (c) 2011 Bryce Lelbach
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7
8 // make inspect happy: hpxinspect:nodeprecatedname:boost::is_any_of
9
10 #include <hpx/config.hpp>
11
12 // System Header Files
13 #include <cerrno>
14 #include <cstdio>
15 #include <cstdlib>
16 #include <cstring>
17
18 #include <fstream>
19 #include <iostream>
20 #include <list>
21 #include <mutex>
22 #include <string>
23 #include <vector>
24 #include <utility>
25
26 #include <hpx/exception.hpp>
27 #include <hpx/util/assert.hpp>
28 #include <hpx/util/ini.hpp>
29 #include <hpx/util/register_locks.hpp>
30 #include <hpx/util/unlock_guard.hpp>
31 #include <hpx/runtime/serialization/serialize.hpp>
32 #include <hpx/runtime/serialization/map.hpp>
33
34 #include <boost/algorithm/string/classification.hpp>
35 #include <boost/algorithm/string/split.hpp>
36 #include <boost/algorithm/string/replace.hpp>
37 #include <boost/regex.hpp>
38
39 #ifdef __APPLE__
40 #include <crt_externs.h>
41 #define environ (*_NSGetEnviron())
42 #elif !defined(HPX_WINDOWS)
43 extern char **environ;
44 #endif
45
46 ///////////////////////////////////////////////////////////////////////////////
47 namespace hpx { namespace util
48 {
49
50 ///////////////////////////////////////////////////////////////////////////////
51 // example ini line: line # comment
52 const char pattern_comment[] = "^([^#]*)(#.*)$";
53
54 // example uses ini line: [sec.ssec]
55 const char pattern_section[] = "^\\[([^\\]]+)\\]$";
56
57 // example uses ini line: sec.ssec.key = val
58 const char pattern_qualified_entry[] =
59 "^([^\\s=]+)\\.([^\\s=\\.]+)\\s*=\\s*(.*[^\\s]?)\\s*$";
60
61 // example uses ini line: key = val
62 const char pattern_entry[] = "^([^\\s=]+)\\s*=\\s*(.*[^\\s]?)\\s*$";
63
64 ///////////////////////////////////////////////////////////////////////////////
65
66 namespace detail
67 {
68 ///////////////////////////////////////////////////////////////////////////
69 inline std::string
trim_whitespace(std::string const & s)70 trim_whitespace (std::string const &s)
71 {
72 typedef std::string::size_type size_type;
73
74 size_type first = s.find_first_not_of (" \t\r\n");
75 if (std::string::npos == first)
76 return (std::string ());
77
78 size_type last = s.find_last_not_of (" \t\r\n");
79 return s.substr (first, last - first + 1);
80 }
81 } // namespace
82
83 ///////////////////////////////////////////////////////////////////////////////
section()84 section::section ()
85 : root_(this_())
86 {
87 regex_init();
88 }
89
section(std::string const & filename,section * root)90 section::section (std::string const& filename, section* root)
91 : root_(nullptr != root ? root : this_()), name_(filename)
92 {
93 read(filename);
94 }
95
section(const section & in)96 section::section (const section & in)
97 : root_(this_()), name_(in.get_name()), parent_name_(in.get_parent_name())
98 {
99 regex_init();
100
101 entry_map const& e = in.get_entries();
102 entry_map::const_iterator end = e.end();
103 for (entry_map::const_iterator i = e.begin (); i != end; ++i)
104 add_entry(i->first, i->second);
105
106 section_map s = in.get_sections();
107 section_map::iterator send = s.end();
108 for (section_map::iterator si = s.begin(); si != send; ++si)
109 add_section(si->first, si->second, get_root());
110 }
111
operator =(section const & rhs)112 section& section::operator=(section const& rhs)
113 {
114 if (this != &rhs) {
115 std::unique_lock<mutex_type> l(mtx_);
116
117 root_ = this;
118 parent_name_ = rhs.get_parent_name();
119 name_ = rhs.get_name();
120
121 entry_map const& e = rhs.get_entries();
122 entry_map::const_iterator end = e.end();
123 for (entry_map::const_iterator i = e.begin (); i != end; ++i)
124 add_entry(l, i->first, i->first, i->second);
125
126 section_map s = rhs.get_sections();
127 section_map::iterator send = s.end();
128 for (section_map::iterator si = s.begin(); si != send; ++si)
129 add_section(l, si->first, si->second, get_root());
130 }
131 return *this;
132 }
133
clone_from(section const & rhs,section * root)134 section& section::clone_from(section const& rhs, section* root)
135 {
136 if (this != &rhs) {
137 std::unique_lock<mutex_type> l(mtx_);
138
139 root_ = root ? root : this;
140 parent_name_ = rhs.get_parent_name();
141 name_ = rhs.get_name();
142
143 entry_map const& e = rhs.get_entries();
144 entry_map::const_iterator end = e.end();
145 for (entry_map::const_iterator i = e.begin (); i != end; ++i)
146 add_entry(l, i->first, i->first, i->second);
147
148 section_map s = rhs.get_sections();
149 section_map::iterator send = s.end();
150 for (section_map::iterator si = s.begin(); si != send; ++si)
151 add_section(l, si->first, si->second, get_root());
152 }
153 return *this;
154 }
155
read(std::string const & filename)156 void section::read (std::string const& filename)
157 {
158 #if defined(__AIX__) && defined(__GNUC__)
159 // NEVER ask why... seems to be some weird stdlib initialization problem
160 // If you don't call getline() here the while(getline...) loop below will
161 // crash with a bad_cast exception. Stupid AIX...
162 std::string l1;
163 std::ifstream i1;
164 i1.open(filename.c_str(), std::ios::in);
165 std::getline(i1, l1);
166 i1.close();
167 #endif
168
169 // build ini - open file and parse each line
170 std::ifstream input(filename.c_str (), std::ios::in);
171 if (!input.is_open())
172 line_msg("Cannot open file: ", filename);
173
174 // initialize
175 if (!regex_init())
176 line_msg ("Cannot init regex for ", filename);
177
178 // read file
179 std::string line;
180 std::vector <std::string> lines;
181 while (std::getline(input, line))
182 lines.push_back (line);
183
184 // parse file
185 parse(filename, lines, false);
186 }
187
force_entry(std::string & str)188 bool force_entry(std::string& str)
189 {
190 std::string::size_type p = str.find_last_of("!");
191 if (p != std::string::npos && str.find_first_not_of(" \t", p+1) == std::string::npos)
192 {
193 str = str.substr(0, p); // remove forcing modifier ('!')
194 return true;
195 }
196 return false;
197 }
198
199 // parse file
parse(std::string const & sourcename,std::vector<std::string> const & lines,bool verify_existing,bool weed_out_comments,bool replace_existing)200 void section::parse (std::string const& sourcename,
201 std::vector<std::string> const& lines, bool verify_existing,
202 bool weed_out_comments, bool replace_existing)
203 {
204 int linenum = 0;
205 section* current = this;
206
207 boost::regex regex_comment (pattern_comment, boost::regex::perl
208 | boost::regex::icase);
209 boost::regex regex_section (pattern_section, boost::regex::perl
210 | boost::regex::icase);
211 boost::regex regex_qualified_entry
212 (pattern_qualified_entry, boost::regex::perl | boost::regex::icase);
213 boost::regex regex_entry (pattern_entry, boost::regex::perl | boost::regex::icase);
214
215 std::vector<std::string>::const_iterator end = lines.end();
216 for (std::vector<std::string>::const_iterator it = lines.begin();
217 it != end; ++it)
218 {
219 ++linenum;
220
221 // remove trailing new lines and white spaces
222 std::string line(detail::trim_whitespace (*it));
223
224 // skip if empty line
225 if (line.empty())
226 continue;
227
228 // weed out comments
229 if (weed_out_comments)
230 {
231 boost::smatch what_comment;
232 if (boost::regex_match (line, what_comment, regex_comment))
233 {
234 HPX_ASSERT(3 == what_comment.size());
235
236 line = detail::trim_whitespace (what_comment[1]);
237 if (line.empty())
238 continue;
239 }
240 }
241
242 // no comments anymore: line is either section, key=val,
243 // or garbage/empty
244 boost::smatch what;
245 if (boost::regex_match(line, what, regex_qualified_entry))
246 {
247 // found a entry line
248 if (4 != what.size()) //-V112
249 {
250 line_msg("Cannot parse key/value in: ", sourcename, linenum, line);
251 }
252
253 section* s = current; // save the section we're in
254 current = this; // start adding sections at the root
255
256 // got the section name. It might be hierarchical, so split it up, and
257 // for each elem, check if we have it. If not, create it, and add
258 std::string sec_name (what[1]);
259 std::string::size_type pos = 0;
260 for (std::string::size_type pos1 = sec_name.find_first_of('.');
261 std::string::npos != pos1;
262 pos1 = sec_name.find_first_of ('.', pos = pos1 + 1))
263 {
264 current = current->add_section_if_new(
265 sec_name.substr(pos, pos1 - pos));
266 }
267
268 current = current->add_section_if_new(sec_name.substr(pos));
269
270 // add key/val to this section
271 std::unique_lock<mutex_type> l(current->mtx_);
272
273 std::string key(what[2]);
274 if (!force_entry(key) && verify_existing && !current->has_entry(l, key))
275 {
276 line_msg ("Attempt to initialize unknown entry: ", sourcename,
277 linenum, line);
278 }
279
280 if (replace_existing || !current->has_entry(l, key))
281 {
282 current->add_entry (l, sec_name + "." + key, key, what[3]);
283 }
284
285 // restore the old section
286 current = s;
287 }
288
289 else if (boost::regex_match(line, what, regex_section))
290 {
291 // found a section line
292 if (2 != what.size())
293 {
294 line_msg("Cannot parse section in: ", sourcename, linenum, line);
295 }
296
297 current = this; // start adding sections at the root
298
299 // got the section name. It might be hierarchical, so split it up, and
300 // for each elem, check if we have it. If not, create it, and add
301 std::string sec_name (what[1]);
302 std::string::size_type pos = 0;
303 for (std::string::size_type pos1 = sec_name.find_first_of('.');
304 std::string::npos != pos1;
305 pos1 = sec_name.find_first_of ('.', pos = pos1 + 1))
306 {
307 current = current->add_section_if_new(
308 sec_name.substr(pos, pos1 - pos));
309 }
310
311 current = current->add_section_if_new(sec_name.substr(pos));
312 }
313
314 // did not match section, so might be key/val entry
315 else if ( boost::regex_match (line, what, regex_entry) )
316 {
317 // found a entry line
318 if (3 != what.size())
319 {
320 line_msg("Cannot parse key/value in: ", sourcename, linenum, line);
321 }
322
323 // add key/val to current section
324 std::unique_lock<mutex_type> l(current->mtx_);
325
326 std::string key(what[1]);
327 if (!force_entry(key) && verify_existing && !current->has_entry(l, key))
328 {
329 line_msg ("Attempt to initialize unknown entry: ", sourcename,
330 linenum, line);
331 }
332
333 if (replace_existing || !current->has_entry(l, key))
334 {
335 current->add_entry (l, key, key, what[2]);
336 }
337 }
338 else
339 {
340 // Hmm, is not a section, is not an entry, is not empty - must be
341 // an error!
342 line_msg ("Cannot parse line at: ", sourcename, linenum, line);
343 }
344 }
345 }
346
347 ///////////////////////////////////////////////////////////////////////////////
add_section(std::unique_lock<mutex_type> & l,std::string const & sec_name,section & sec,section * root)348 void section::add_section (std::unique_lock<mutex_type>& l,
349 std::string const& sec_name, section& sec, section* root)
350 {
351 // setting name and root
352 sec.name_ = sec_name;
353 sec.parent_name_ = get_full_name();
354
355 section& newsec = sections_[sec_name];
356 newsec.clone_from(sec, (nullptr != root) ? root : get_root());
357 }
358
359 ///////////////////////////////////////////////////////////////////////////
add_section_if_new(std::unique_lock<mutex_type> & l,std::string const & sec_name)360 section* section::add_section_if_new(std::unique_lock<mutex_type>& l,
361 std::string const& sec_name)
362 {
363 // do we know this one?
364 if (!has_section(l, sec_name))
365 {
366 // no - add it!
367 section sec;
368 add_section(l, sec_name, sec, get_root());
369 }
370
371 return get_section(l, sec_name);
372 }
373
has_section(std::unique_lock<mutex_type> & l,std::string const & sec_name) const374 bool section::has_section (std::unique_lock<mutex_type>& l,
375 std::string const& sec_name) const
376 {
377 std::string::size_type i = sec_name.find(".");
378 if (i != std::string::npos)
379 {
380 std::string cor_sec_name = sec_name.substr (0, i);
381
382 section_map::const_iterator it = sections_.find(cor_sec_name);
383 if (it != sections_.end())
384 {
385 std::string sub_sec_name = sec_name.substr(i+1);
386 return (*it).second.has_section(sub_sec_name);
387 }
388 return false;
389 }
390 return sections_.find(sec_name) != sections_.end();
391 }
392
get_section(std::unique_lock<mutex_type> & l,std::string const & sec_name)393 section* section::get_section (std::unique_lock<mutex_type>& l,
394 std::string const& sec_name)
395 {
396 std::string::size_type i = sec_name.find (".");
397 if (i != std::string::npos)
398 {
399 std::string cor_sec_name = sec_name.substr (0, i);
400 section_map::iterator it = sections_.find(cor_sec_name);
401 if (it != sections_.end())
402 {
403 std::string sub_sec_name = sec_name.substr(i+1);
404 return (*it).second.get_section(sub_sec_name);
405 }
406
407 std::string name(get_name());
408 if (name.empty())
409 name = "<root>";
410
411 HPX_THROW_EXCEPTION(bad_parameter, "section::get_section",
412 "No such section (" + sec_name + ") in section: " + name);
413 return nullptr;
414 }
415
416 section_map::iterator it = sections_.find(sec_name);
417 if (it != sections_.end())
418 return &((*it).second);
419
420 HPX_THROW_EXCEPTION(bad_parameter, "section::get_section",
421 "No such section (" + sec_name + ") in section: " + get_name());
422 return nullptr;
423 }
424
get_section(std::unique_lock<mutex_type> & l,std::string const & sec_name) const425 section const* section::get_section (std::unique_lock<mutex_type>& l,
426 std::string const& sec_name) const
427 {
428 std::string::size_type i = sec_name.find (".");
429 if (i != std::string::npos)
430 {
431 std::string cor_sec_name = sec_name.substr (0, i);
432 section_map::const_iterator it = sections_.find(cor_sec_name);
433 if (it != sections_.end())
434 {
435 std::string sub_sec_name = sec_name.substr(i+1);
436 return (*it).second.get_section(sub_sec_name);
437 }
438
439 std::string name(get_name());
440 if (name.empty())
441 name = "<root>";
442
443 HPX_THROW_EXCEPTION(bad_parameter, "section::get_section",
444 "No such section (" + sec_name + ") in section: " + name);
445 return nullptr;
446 }
447
448 section_map::const_iterator it = sections_.find(sec_name);
449 if (it != sections_.end())
450 return &((*it).second);
451
452 HPX_THROW_EXCEPTION(bad_parameter, "section::get_section",
453 "No such section (" + sec_name + ") in section: " + get_name());
454 return nullptr;
455 }
456
add_entry(std::unique_lock<mutex_type> & l,std::string const & fullkey,std::string const & key,std::string val)457 void section::add_entry (std::unique_lock<mutex_type>& l,
458 std::string const& fullkey, std::string const& key, std::string val)
459 {
460 // first expand the full property name in the value (avoids infinite recursion)
461 expand_only(l, val, std::string::size_type(-1), get_full_name() + "." + key);
462
463 std::string::size_type i = key.find_last_of(".");
464 if (i != std::string::npos)
465 {
466 section* current = root_;
467
468 // make sure all sections in key exist
469 std::string sec_name = key.substr(0, i);
470
471 std::string::size_type pos = 0;
472 for (std::string::size_type pos1 = sec_name.find_first_of('.');
473 std::string::npos != pos1;
474 pos1 = sec_name.find_first_of('.', pos = pos1 + 1))
475 {
476 current = current->add_section_if_new(l,
477 sec_name.substr(pos, pos1 - pos));
478 }
479
480 current = current->add_section_if_new(l, sec_name.substr(pos));
481
482 // now add this entry to the section
483 current->add_entry(l, fullkey, key.substr(i+1), val);
484 }
485 else
486 {
487 entry_map::iterator it = entries_.find(key);
488 if (it != entries_.end())
489 {
490 it->second.first = std::move(val);
491 if (!it->second.second.empty())
492 {
493 std::string value = it->second.first;
494 entry_changed_func f = it->second.second;
495
496 hpx::util::unlock_guard<std::unique_lock<mutex_type>> ul(l);
497 f(fullkey, value);
498 }
499 }
500 else
501 {
502 // just add this entry to the section
503 entries_[key] = entry_type(val, entry_changed_func());
504 }
505 }
506 }
507
add_entry(std::unique_lock<mutex_type> & l,std::string const & fullkey,std::string const & key,entry_type const & val)508 void section::add_entry (std::unique_lock<mutex_type>& l,
509 std::string const& fullkey, std::string const& key, entry_type const& val)
510 {
511 std::string::size_type i = key.find_last_of(".");
512 if (i != std::string::npos)
513 {
514 section* current = root_;
515
516 // make sure all sections in key exist
517 std::string sec_name = key.substr(0, i);
518
519 std::string::size_type pos = 0;
520 for (std::string::size_type pos1 = sec_name.find_first_of('.');
521 std::string::npos != pos1;
522 pos1 = sec_name.find_first_of('.', pos = pos1 + 1))
523 {
524 current = current->add_section_if_new(l,
525 sec_name.substr(pos, pos1 - pos));
526 }
527
528 current = current->add_section_if_new(l, sec_name.substr(pos));
529
530 // now add this entry to the section
531 current->add_entry(l, fullkey, key.substr(i+1), val);
532 }
533 else
534 {
535 entry_map::iterator it = entries_.find(key);
536 if (it != entries_.end())
537 {
538 it->second = val;
539 if (!it->second.second.empty())
540 {
541 std::string value = it->second.first;
542 entry_changed_func f = it->second.second;
543
544 hpx::util::unlock_guard<std::unique_lock<mutex_type>> ul(l);
545 f(fullkey, value);
546 }
547 }
548 else
549 {
550 // just add this entry to the section
551 std::pair<entry_map::iterator, bool> p = entries_.insert(
552 entry_map::value_type(key, val));
553 HPX_ASSERT(p.second);
554
555 if (!p.first->second.second.empty())
556 {
557 std::string key = p.first->first;
558 std::string value = p.first->second.first;
559 entry_changed_func f = p.first->second.second;
560
561 hpx::util::unlock_guard<std::unique_lock<mutex_type>> ul(l);
562 f(key, value);
563 }
564 }
565 }
566 }
567
568 ///////////////////////////////////////////////////////////////////////////////
569 template <typename F1, typename F2>
570 class compose_callback_impl
571 {
572 public:
573 template <typename A1, typename A2>
compose_callback_impl(A1 && f1,A2 && f2)574 compose_callback_impl(A1 && f1, A2 && f2)
575 : f1_(std::forward<A1>(f1))
576 , f2_(std::forward<A2>(f2))
577 {}
578
operator ()(std::string const & k,std::string const & v) const579 void operator()(std::string const& k, std::string const& v) const
580 {
581 f1_(k, v);
582 f2_(k, v);
583 }
584
585 private:
586 F1 f1_;
587 F2 f2_;
588 };
589
590 template <typename F1, typename F2>
591 static HPX_FORCEINLINE
592 util::function_nonser<void(std::string const&, std::string const&)>
compose_callback(F1 && f1,F2 && f2)593 compose_callback(F1 && f1, F2 && f2)
594 {
595 if (!f1)
596 return std::forward<F2>(f2);
597 else if (!f2)
598 return std::forward<F1>(f1);
599
600 // otherwise create a combined callback
601 typedef compose_callback_impl<
602 typename util::decay<F1>::type, typename util::decay<F2>::type
603 > result_type;
604 return result_type(std::forward<F1>(f1), std::forward<F2>(f2));
605 }
606
add_notification_callback(std::unique_lock<mutex_type> & l,std::string const & key,entry_changed_func const & callback)607 void section::add_notification_callback(std::unique_lock<mutex_type>& l,
608 std::string const& key, entry_changed_func const& callback)
609 {
610 std::string::size_type i = key.find_last_of(".");
611 if (i != std::string::npos)
612 {
613 section* current = root_;
614
615 // make sure all sections in key exist
616 std::string sec_name = key.substr(0, i);
617
618 std::string::size_type pos = 0;
619 for (std::string::size_type pos1 = sec_name.find_first_of('.');
620 std::string::npos != pos1;
621 pos1 = sec_name.find_first_of('.', pos = pos1 + 1))
622 {
623 current = current->add_section_if_new(l,
624 sec_name.substr(pos, pos1 - pos));
625 }
626
627 current = current->add_section_if_new(l, sec_name.substr(pos));
628
629 // now add this entry to the section
630 current->add_notification_callback(l, key.substr(i+1), callback);
631 }
632 else
633 {
634 // just add this entry to the section
635 entry_map::iterator it = entries_.find(key);
636 if (it != entries_.end())
637 {
638 it->second.second = compose_callback(callback, it->second.second);
639 }
640 else
641 {
642 entries_[key] = entry_type("", callback);
643 }
644 }
645 }
646
has_entry(std::unique_lock<mutex_type> & l,std::string const & key) const647 bool section::has_entry (std::unique_lock<mutex_type>& l, std::string const& key) const
648 {
649 std::string::size_type i = key.find (".");
650 if (i != std::string::npos)
651 {
652 std::string sub_sec = key.substr(0, i);
653 std::string sub_key = key.substr(i+1, key.size() - i);
654 if (has_section(l, sub_sec))
655 {
656 section_map::const_iterator cit = sections_.find(sub_sec);
657 HPX_ASSERT(cit != sections_.end());
658 return (*cit).second.has_entry(sub_key);
659 }
660 return false;
661 }
662 return entries_.find(key) != entries_.end();
663 }
664
get_entry(std::unique_lock<mutex_type> & l,std::string const & key) const665 std::string section::get_entry (std::unique_lock<mutex_type>& l,
666 std::string const& key) const
667 {
668 std::string::size_type i = key.find (".");
669 if (i != std::string::npos)
670 {
671 std::string sub_sec = key.substr(0, i);
672 std::string sub_key = key.substr(i+1, key.size() - i);
673 if (has_section(l, sub_sec))
674 {
675 section_map::const_iterator cit = sections_.find(sub_sec);
676 HPX_ASSERT(cit != sections_.end());
677 return (*cit).second.get_entry(sub_key);
678 }
679
680 HPX_THROW_EXCEPTION(bad_parameter, "section::get_entry",
681 "No such key (" + key + ") in section: " + get_name());
682 return "";
683 }
684
685 if (entries_.find(key) != entries_.end())
686 {
687 entry_map::const_iterator cit = entries_.find(key);
688 HPX_ASSERT(cit != entries_.end());
689 return expand(l, (*cit).second.first);
690 }
691
692 HPX_THROW_EXCEPTION(bad_parameter, "section::get_entry",
693 "No such section (" + key + ") in section: " + get_name());
694 return "";
695 }
696
get_entry(std::unique_lock<mutex_type> & l,std::string const & key,std::string const & default_val) const697 std::string section::get_entry (std::unique_lock<mutex_type>& l,
698 std::string const& key, std::string const& default_val) const
699 {
700 typedef std::vector<std::string> string_vector;
701
702 string_vector split_key;
703 boost::split(split_key, key, boost::is_any_of("."));
704
705 std::string sk = split_key.back();
706 split_key.pop_back();
707
708 section const* cur_section = this;
709 for (string_vector::const_iterator iter = split_key.begin(),
710 end = split_key.end(); iter != end; ++iter)
711 {
712 section_map::const_iterator next = cur_section->sections_.find(*iter);
713 if (cur_section->sections_.end() == next)
714 return expand(l, default_val);
715 cur_section = &next->second;
716 }
717
718 entry_map::const_iterator entry = cur_section->entries_.find(sk);
719 if (cur_section->entries_.end() == entry)
720 return expand(l, default_val);
721
722 return expand(l, entry->second.first);
723 }
724
indent(int ind,std::ostream & strm)725 inline void indent (int ind, std::ostream& strm)
726 {
727 for (int i = 0; i < ind; ++i)
728 strm << " ";
729 }
730
dump(int ind,std::ostream & strm) const731 void section::dump(int ind, std::ostream& strm) const
732 {
733 std::unique_lock<mutex_type> l(mtx_);
734
735 bool header = false;
736 if (0 == ind)
737 header = true;
738
739 ++ind;
740 if (header)
741 {
742 if(get_root() == this) {
743 strm << "============================\n";
744 }
745 else {
746 strm << "============================[\n"
747 << get_name() << "\n" << "]\n";
748 }
749 }
750
751 entry_map::const_iterator eend = entries_.end();
752 for (entry_map::const_iterator i = entries_.begin(); i != eend; ++i)
753 {
754 indent (ind, strm);
755
756 const std::string expansion = expand(l, i->second.first);
757
758 // Check if the expanded entry is different from the actual entry.
759 if (expansion != i->second.first)
760 // If the expansion is different from the real entry, then print
761 // it out.
762 strm << "'" << i->first << "' : '" << i->second.first << "' -> '"
763 << expansion << "'\n";
764 else
765 strm << "'" << i->first << "' : '" << i->second.first << "'\n";
766 }
767
768 section_map::const_iterator send = sections_.end();
769 for (section_map::const_iterator i = sections_.begin(); i != send; ++i)
770 {
771 indent (ind, strm);
772 strm << "[" << i->first << "]\n";
773 (*i).second.dump (ind, strm);
774 }
775
776 if (header)
777 strm << "============================\n";
778
779 strm << std::flush;
780 }
781
merge(std::string const & filename)782 void section::merge(std::string const& filename)
783 {
784 section tmp(filename, root_);
785 merge(tmp);
786 }
787
merge(section & second)788 void section::merge(section& second)
789 {
790 std::unique_lock<mutex_type> l(mtx_);
791
792 // merge entries: keep own entries, and add other entries
793 entry_map const& s_entries = second.get_entries();
794 entry_map::const_iterator end = s_entries.end();
795 for (entry_map::const_iterator i = s_entries.begin(); i != end; ++i)
796 entries_[i->first] = i->second;
797
798 // merge subsection known in first section
799 section_map::iterator send = sections_.end();
800 for (section_map::iterator i = sections_.begin(); i != send; ++i)
801 {
802 // is there something to merge with?
803 if (second.has_section(l, i->first))
804 i->second.merge (second.sections_[i->first]);
805 }
806
807 // merge subsection known in second section
808 section_map s = second.get_sections();
809 section_map::iterator secend = s.end();
810 for (section_map::iterator i = s.begin (); i != secend; ++i)
811 {
812 // if THIS knows the section, we already merged it above
813 if (!has_section(l, i->first))
814 {
815 // it is not known here, so we can't merge, but have to add it.
816 add_section (l, i->first, i->second, get_root());
817 }
818 }
819 }
820
821 /////////////////////////////////////////////////////////////////////////////////
regex_init(void)822 bool section::regex_init (void)
823 {
824 return true;
825 }
826
line_msg(std::string msg,std::string const & file,int lnum,std::string const & line)827 void section::line_msg(std::string msg, std::string const& file,
828 int lnum, std::string const& line)
829 {
830 msg += " " + file;
831 if (lnum > 0)
832 msg += ": line " + std::to_string(lnum);
833 if (!line.empty())
834 msg += " (offending entry: " + line + ")";
835
836 HPX_THROW_EXCEPTION(no_success, "section::line_msg", msg);
837 }
838
839 ///////////////////////////////////////////////////////////////////////////////
840 // find the matching closing brace starting from 'begin', escaped braces will
841 // be un-escaped
842 inline std::string::size_type
find_next(char const * ch,std::string & value,std::string::size_type begin=static_cast<std::string::size_type> (-1))843 find_next(char const* ch, std::string& value,
844 std::string::size_type begin = static_cast<std::string::size_type>(-1))
845 {
846 std::string::size_type end = value.find_first_of(ch, begin+1);
847 while (end != std::string::npos) {
848 if (end != 0 && value[end-1] != '\\')
849 break;
850 value.replace(end-1, 2, ch);
851 end = value.find_first_of(ch, end);
852 }
853 return end;
854 }
855
856 ///////////////////////////////////////////////////////////////////////////////
expand(std::unique_lock<mutex_type> & l,std::string & value,std::string::size_type begin) const857 void section::expand(std::unique_lock<mutex_type>& l, std::string& value,
858 std::string::size_type begin) const
859 {
860 std::string::size_type p = value.find_first_of("$", begin+1);
861 while (p != std::string::npos && value.size()-1 != p)
862 {
863 if ('[' == value[p+1])
864 expand_bracket(l, value, p);
865 else if ('{' == value[p+1])
866 expand_brace(l, value, p);
867 p = value.find_first_of("$", p+1);
868 }
869 }
870
expand_bracket(std::unique_lock<mutex_type> & l,std::string & value,std::string::size_type begin) const871 void section::expand_bracket(std::unique_lock<mutex_type>& l, std::string& value,
872 std::string::size_type begin) const
873 {
874 // expand all keys embedded inside this key
875 expand(l, value, begin);
876
877 // now expand the key itself
878 std::string::size_type end = find_next("]", value, begin+1);
879 if (end != std::string::npos)
880 {
881 std::string to_expand = value.substr(begin+2, end-begin-2);
882 std::string::size_type colon = find_next(":", to_expand);
883 if (colon == std::string::npos) {
884 value.replace(begin, end-begin+1, root_->get_entry(l, to_expand,
885 std::string("")));
886 }
887 else {
888 value.replace(begin, end-begin+1,
889 root_->get_entry(l, to_expand.substr(0, colon),
890 to_expand.substr(colon+1)));
891 }
892 }
893 }
894
expand_brace(std::unique_lock<mutex_type> & l,std::string & value,std::string::size_type begin) const895 void section::expand_brace(std::unique_lock<mutex_type>& l, std::string& value,
896 std::string::size_type begin) const
897 {
898 // expand all keys embedded inside this key
899 expand(l, value, begin);
900
901 // now expand the key itself
902 std::string::size_type end = find_next("}", value, begin+1);
903 if (end != std::string::npos)
904 {
905 std::string to_expand = value.substr(begin+2, end-begin-2);
906 std::string::size_type colon = find_next(":", to_expand);
907 if (colon == std::string::npos) {
908 char* env = getenv(to_expand.c_str());
909 value.replace(begin, end-begin+1, nullptr != env ? env : "");
910 }
911 else {
912 char* env = getenv(to_expand.substr(0, colon).c_str());
913 value.replace(begin, end-begin+1,
914 nullptr != env ? std::string(env) : to_expand.substr(colon+1));
915 }
916 }
917 }
918
expand(std::unique_lock<mutex_type> & l,std::string value) const919 std::string section::expand (std::unique_lock<mutex_type>& l, std::string value) const
920 {
921 expand(l, value, std::string::size_type(-1));
922 return value;
923 }
924
925 ///////////////////////////////////////////////////////////////////////////////
expand_only(std::unique_lock<mutex_type> & l,std::string & value,std::string::size_type begin,std::string const & expand_this) const926 void section::expand_only(std::unique_lock<mutex_type>& l, std::string& value,
927 std::string::size_type begin, std::string const& expand_this) const
928 {
929 std::string::size_type p = value.find_first_of("$", begin+1);
930 while (p != std::string::npos && value.size()-1 != p) {
931 if ('[' == value[p+1])
932 expand_bracket_only(l, value, p, expand_this);
933 else if ('{' == value[p+1])
934 expand_brace_only(l, value, p, expand_this);
935 p = value.find_first_of("$", p+1);
936 }
937 }
938
expand_bracket_only(std::unique_lock<mutex_type> & l,std::string & value,std::string::size_type begin,std::string const & expand_this) const939 void section::expand_bracket_only(std::unique_lock<mutex_type>& l,
940 std::string& value, std::string::size_type begin,
941 std::string const& expand_this) const
942 {
943 // expand all keys embedded inside this key
944 expand_only(l, value, begin, expand_this);
945
946 // now expand the key itself
947 std::string::size_type end = find_next("]", value, begin+1);
948 if (end != std::string::npos)
949 {
950 std::string to_expand = value.substr(begin+2, end-begin-2);
951 std::string::size_type colon = find_next(":", to_expand);
952 if (colon == std::string::npos) {
953 if (to_expand == expand_this) {
954 value.replace(begin, end-begin+1,
955 root_->get_entry(l, to_expand, std::string("")));
956 }
957 }
958 else if (to_expand.substr(0, colon) == expand_this) {
959 value.replace(begin, end-begin+1,
960 root_->get_entry(l, to_expand.substr(0, colon),
961 to_expand.substr(colon+1)));
962 }
963 }
964 }
965
expand_brace_only(std::unique_lock<mutex_type> & l,std::string & value,std::string::size_type begin,std::string const & expand_this) const966 void section::expand_brace_only(std::unique_lock<mutex_type>& l,
967 std::string& value, std::string::size_type begin,
968 std::string const& expand_this) const
969 {
970 // expand all keys embedded inside this key
971 expand_only(l, value, begin, expand_this);
972
973 // now expand the key itself
974 std::string::size_type end = find_next("}", value, begin+1);
975 if (end != std::string::npos)
976 {
977 std::string to_expand = value.substr(begin+2, end-begin-2);
978 std::string::size_type colon = find_next(":", to_expand);
979 if (colon == std::string::npos) {
980 char* env = getenv(to_expand.c_str());
981 value.replace(begin, end-begin+1, nullptr != env ? env : "");
982 }
983 else {
984 char* env = getenv(to_expand.substr(0, colon).c_str());
985 value.replace(begin, end-begin+1,
986 nullptr != env ? std::string(env) : to_expand.substr(colon+1));
987 }
988 }
989 }
990
expand_only(std::unique_lock<mutex_type> & l,std::string value,std::string const & expand_this) const991 std::string section::expand_only(std::unique_lock<mutex_type>& l,
992 std::string value, std::string const& expand_this) const
993 {
994 expand_only(l, value, std::string::size_type(-1), expand_this);
995 return value;
996 }
997
998 ///////////////////////////////////////////////////////////////////////////////
999 // explicit instantiation for the correct archive types
serialize(serialization::output_archive & ar,section::entry_type const & data,unsigned int version)1000 HPX_EXPORT void serialize(serialization::output_archive& ar,
1001 section::entry_type const& data, unsigned int version)
1002 {
1003 ar << data.first;
1004 // do not handle callback function
1005 }
1006
serialize(serialization::input_archive & ar,section::entry_type & data,unsigned int version)1007 HPX_EXPORT void serialize(serialization::input_archive& ar,
1008 section::entry_type& data, unsigned int version)
1009 {
1010 ar >> data.first;
1011 // do not handle callback function
1012 }
1013
1014 ///////////////////////////////////////////////////////////////////////////////
1015 template <typename Archive>
save(Archive & ar,const unsigned int version) const1016 void section::save(Archive& ar, const unsigned int version) const
1017 {
1018 ar << name_;
1019 ar << parent_name_;
1020 ar << entries_;
1021 ar << sections_;
1022 }
1023
1024 template <typename Archive>
load(Archive & ar,const unsigned int version)1025 void section::load(Archive& ar, const unsigned int version)
1026 {
1027 ar >> name_;
1028 ar >> parent_name_;
1029 ar >> entries_;
1030 ar >> sections_;
1031
1032 set_root(this, true); // make this the current root
1033 }
1034
1035 // explicit instantiation for the correct archive types
1036 template HPX_EXPORT void
1037 section::save(serialization::output_archive&, const unsigned int version) const;
1038
1039 template HPX_EXPORT void
1040 section::load(serialization::input_archive&, const unsigned int version);
1041
1042 }} // namespace hpx::util
1043
1044