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