1 /*=============================================================================
2     Boost.Wave: A Standard compliant C++ preprocessor library
3 
4     http://www.boost.org/
5 
6     Copyright (c) 2001-2012 Hartmut Kaiser. Distributed under the Boost
7     Software License, Version 1.0. (See accompanying file
8     LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9 =============================================================================*/
10 
11 #if !defined(CPP_INCLUDE_PATHS_HPP_AF620DA4_B3D2_4221_AD91_8A1ABFFB6944_INCLUDED)
12 #define CPP_INCLUDE_PATHS_HPP_AF620DA4_B3D2_4221_AD91_8A1ABFFB6944_INCLUDED
13 
14 #include <string>
15 #include <list>
16 #include <utility>
17 
18 #include <boost/wave/wave_config.hpp>
19 #include <boost/wave/util/filesystem_compatibility.hpp>
20 
21 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
22 #include <boost/multi_index_container.hpp>
23 #include <boost/multi_index/member.hpp>
24 #include <boost/multi_index/ordered_index.hpp>
25 #endif
26 
27 #if BOOST_WAVE_SERIALIZATION != 0
28 #include <boost/serialization/serialization.hpp>
29 #include <boost/serialization/utility.hpp>
30 #include <boost/serialization/collections_save_imp.hpp>
31 #include <boost/serialization/collections_load_imp.hpp>
32 #include <boost/serialization/split_free.hpp>
33 #endif
34 
35 #include <boost/filesystem/path.hpp>
36 #include <boost/filesystem/operations.hpp>
37 
38 // this must occur after all of the includes and before any code appears
39 #ifdef BOOST_HAS_ABI_HEADERS
40 #include BOOST_ABI_PREFIX
41 #endif
42 
43 ///////////////////////////////////////////////////////////////////////////////
44 namespace boost { namespace wave { namespace util {
45 
46 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
47 ///////////////////////////////////////////////////////////////////////////////
48 //  Tags for accessing both sides of a bidirectional map
49 struct from {};
50 struct to {};
51 
52 ///////////////////////////////////////////////////////////////////////////////
53 //  The class template bidirectional_map wraps the specification
54 //  of a bidirectional map based on multi_index_container.
55 template<typename FromType, typename ToType>
56 struct bidirectional_map
57 {
58     typedef std::pair<FromType, ToType> value_type;
59 
60 #if defined(BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS) || \
61     (defined(BOOST_MSVC) && \
62         ( (BOOST_MSVC < 1300) || (BOOST_MSVC == 1600) )) || \
63     (defined(BOOST_INTEL_CXX_VERSION) && \
64         (defined(_MSC_VER) && (BOOST_INTEL_CXX_VERSION <= 700)))
65 
66     BOOST_STATIC_CONSTANT(unsigned, from_offset = offsetof(value_type, first));
67     BOOST_STATIC_CONSTANT(unsigned, to_offset   = offsetof(value_type, second));
68 
69     typedef boost::multi_index::multi_index_container<
70         value_type,
71         boost::multi_index::indexed_by<
72             boost::multi_index::ordered_unique<
73                 boost::multi_index::tag<from>,
74                 boost::multi_index::member_offset<value_type, FromType, from_offset>
75             >,
76             boost::multi_index::ordered_non_unique<
77                 boost::multi_index::tag<to>,
78                 boost::multi_index::member_offset<value_type, ToType, to_offset>
79             >
80         >
81     > type;
82 
83 #else
84 
85   typedef boost::multi_index::multi_index_container<
86       value_type,
87       boost::multi_index::indexed_by<
88           boost::multi_index::ordered_unique<
89               boost::multi_index::tag<from>,
90               boost::multi_index::member<value_type, FromType, &value_type::first>
91           >,
92           boost::multi_index::ordered_non_unique<
93               boost::multi_index::tag<to>,
94               boost::multi_index::member<value_type, ToType, &value_type::second>
95           >
96       >
97   > type;
98 
99 #endif
100 };
101 #endif // BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
102 
103 #if BOOST_WAVE_SERIALIZATION != 0
104 struct load_filepos
105 {
get_lineboost::wave::util::load_filepos106     static unsigned int get_line() { return 0; }
get_columnboost::wave::util::load_filepos107     static unsigned int get_column() { return 0; }
get_fileboost::wave::util::load_filepos108     static std::string get_file() { return "<loading-state>"; }
109 };
110 #endif
111 
112 ///////////////////////////////////////////////////////////////////////////////
113 //
114 //  include_paths - controlling the include path search order
115 //
116 //  General notes:
117 //
118 //      Any directories specified with the 'add_include_path()' function before
119 //      the function 'set_sys_include_delimiter()' is called are searched only
120 //      for the case of '#include "file"' directives, they are not searched for
121 //      '#include <file>' directives. If additional directories are specified
122 //      with the 'add_include_path()' function after a call to the function
123 //      'set_sys_include_delimiter()', these directories are searched for all
124 //      '#include' directives.
125 //
126 //      In addition, a call to the function 'set_sys_include_delimiter()'
127 //      inhibits the use of the current directory as the first search directory
128 //      for '#include "file"' directives. Therefore, the current directory is
129 //      searched only if it is requested explicitly with a call to the function
130 //      'add_include_path(".")'.
131 //
132 //      Calling both functions, the 'set_sys_include_delimiter()' and
133 //      'add_include_path(".")' allows you to control precisely which
134 //      directories are searched before the current one and which are searched
135 //      after.
136 //
137 ///////////////////////////////////////////////////////////////////////////////
138 class include_paths
139 {
140 private:
141     typedef std::list<std::pair<boost::filesystem::path, std::string> >
142         include_list_type;
143     typedef include_list_type::value_type include_value_type;
144 
145 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
146     typedef bidirectional_map<std::string, std::string>::type
147         pragma_once_set_type;
148 #endif
149 
150 public:
include_paths()151     include_paths()
152     :   was_sys_include_path(false),
153         current_dir(initial_path()),
154         current_rel_dir(initial_path())
155     {}
156 
add_include_path(char const * path_,bool is_system=false)157     bool add_include_path(char const *path_, bool is_system = false)
158     {
159         return add_include_path(path_, (is_system || was_sys_include_path) ?
160             system_include_paths : user_include_paths);
161     }
set_sys_include_delimiter()162     void set_sys_include_delimiter() { was_sys_include_path = true; }
163     bool find_include_file (std::string &s, std::string &dir, bool is_system,
164         char const *current_file) const;
165     void set_current_directory(char const *path_);
get_current_directory() const166     boost::filesystem::path get_current_directory() const
167         { return current_dir; }
168 
169 protected:
170     bool find_include_file (std::string &s, std::string &dir,
171         include_list_type const &pathes, char const *) const;
172     bool add_include_path(char const *path_, include_list_type &pathes_);
173 
174 private:
175     include_list_type user_include_paths;
176     include_list_type system_include_paths;
177     bool was_sys_include_path;          // saw a set_sys_include_delimiter()
178     boost::filesystem::path current_dir;
179     boost::filesystem::path current_rel_dir;
180 
181 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
182 public:
has_pragma_once(std::string const & filename)183     bool has_pragma_once(std::string const &filename)
184     {
185         using boost::multi_index::get;
186         return get<from>(pragma_once_files).find(filename) != pragma_once_files.end();
187     }
add_pragma_once_header(std::string const & filename,std::string const & guard_name)188     bool add_pragma_once_header(std::string const &filename,
189         std::string const& guard_name)
190     {
191         typedef pragma_once_set_type::value_type value_type;
192         return pragma_once_files.insert(value_type(filename, guard_name)).second;
193     }
remove_pragma_once_header(std::string const & guard_name)194     bool remove_pragma_once_header(std::string const& guard_name)
195     {
196         typedef pragma_once_set_type::index_iterator<to>::type to_iterator;
197         typedef std::pair<to_iterator, to_iterator> range_type;
198 
199         range_type r = pragma_once_files.get<to>().equal_range(guard_name);
200         if (r.first != r.second) {
201             using boost::multi_index::get;
202             get<to>(pragma_once_files).erase(r.first, r.second);
203             return true;
204         }
205         return false;
206     }
207 
208 private:
209     pragma_once_set_type pragma_once_files;
210 #endif
211 
212 #if BOOST_WAVE_SERIALIZATION != 0
213 public:
214     BOOST_STATIC_CONSTANT(unsigned int, version = 0x10);
215     BOOST_STATIC_CONSTANT(unsigned int, version_mask = 0x0f);
216 
217 private:
218     friend class boost::serialization::access;
219     template<typename Archive>
save(Archive & ar,const unsigned int version) const220     void save(Archive & ar, const unsigned int version) const
221     {
222         using namespace boost::serialization;
223 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
224         ar & make_nvp("pragma_once_files", pragma_once_files);
225 #endif
226         ar & make_nvp("user_include_paths", user_include_paths);
227         ar & make_nvp("system_include_paths", system_include_paths);
228         ar & make_nvp("was_sys_include_path", was_sys_include_path);
229     }
230     template<typename Archive>
load(Archive & ar,const unsigned int loaded_version)231     void load(Archive & ar, const unsigned int loaded_version)
232     {
233         using namespace boost::serialization;
234         if (version != (loaded_version & ~version_mask)) {
235             BOOST_WAVE_THROW(preprocess_exception, incompatible_config,
236                 "cpp_include_path state version", load_filepos());
237             return;
238         }
239 
240 #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0
241         ar & make_nvp("pragma_once_files", pragma_once_files);
242 #endif
243         // verify that the old include paths match the current ones
244         include_list_type user_paths, system_paths;
245         ar & make_nvp("user_include_paths", user_paths);
246         ar & make_nvp("system_include_paths", system_paths);
247 
248         if (user_paths != user_include_paths)
249         {
250             BOOST_WAVE_THROW(preprocess_exception, incompatible_config,
251                 "user include paths", load_filepos());
252             return;
253         }
254         if (system_paths != system_include_paths)
255         {
256             BOOST_WAVE_THROW(preprocess_exception, incompatible_config,
257                 "system include paths", load_filepos());
258             return;
259         }
260 
261         ar & make_nvp("was_sys_include_path", was_sys_include_path);
262     }
263     BOOST_SERIALIZATION_SPLIT_MEMBER()
264 #endif
265 };
266 
267 ///////////////////////////////////////////////////////////////////////////////
268 //  Add an include path to one of the search lists (user include path or system
269 //  include path).
270 inline
add_include_path(char const * path_,include_list_type & pathes_)271 bool include_paths::add_include_path (
272     char const *path_, include_list_type &pathes_)
273 {
274     namespace fs = boost::filesystem;
275     if (path_) {
276     fs::path newpath = util::complete_path(create_path(path_), current_dir);
277 
278         if (!fs::exists(newpath) || !fs::is_directory(newpath)) {
279         // the given path does not form a name of a valid file system directory
280         // item
281             return false;
282         }
283 
284         pathes_.push_back (include_value_type(newpath, path_));
285         return true;
286     }
287     return false;
288 }
289 
290 ///////////////////////////////////////////////////////////////////////////////
291 //  Find an include file by traversing the list of include directories
292 inline
find_include_file(std::string & s,std::string & dir,include_list_type const & pathes,char const * current_file) const293 bool include_paths::find_include_file (std::string &s, std::string &dir,
294     include_list_type const &pathes, char const *current_file) const
295 {
296     namespace fs = boost::filesystem;
297     typedef include_list_type::const_iterator const_include_list_iter_t;
298 
299     const_include_list_iter_t it = pathes.begin();
300     const_include_list_iter_t include_paths_end = pathes.end();
301 
302 #if BOOST_WAVE_SUPPORT_INCLUDE_NEXT != 0
303     if (0 != current_file) {
304     // re-locate the directory of the current file (#include_next handling)
305 
306     // #include_next does not distinguish between <file> and "file"
307     // inclusion, nor does it check that the file you specify has the same
308     // name as the current file.  It simply looks for the file named, starting
309     // with the directory in the search path after the one where the current
310     // file was found.
311 
312         fs::path file_path (create_path(current_file));
313         for (/**/; it != include_paths_end; ++it) {
314             fs::path currpath (create_path((*it).first.string()));
315             if (std::equal(currpath.begin(), currpath.end(), file_path.begin()))
316             {
317                 ++it;     // start searching with the next directory
318                 break;
319             }
320         }
321     }
322 #endif
323 
324     for (/**/; it != include_paths_end; ++it) {
325         fs::path currpath (create_path(s));
326         if (!currpath.has_root_directory()) {
327             currpath = create_path((*it).first.string());
328             currpath /= create_path(s);      // append filename
329         }
330 
331         if (fs::exists(currpath)) {
332             fs::path dirpath (create_path(s));
333             if (!dirpath.has_root_directory()) {
334                 dirpath = create_path((*it).second);
335                 dirpath /= create_path(s);
336             }
337 
338             dir = dirpath.string();
339             s = normalize(currpath).string();    // found the required file
340             return true;
341         }
342     }
343     return false;
344 }
345 
346 ///////////////////////////////////////////////////////////////////////////////
347 //  Find an include file by searching the user and system includes in the
348 //  correct sequence (as it was configured by the user of the driver program)
349 inline bool
find_include_file(std::string & s,std::string & dir,bool is_system,char const * current_file) const350 include_paths::find_include_file (std::string &s, std::string &dir,
351     bool is_system, char const *current_file) const
352 {
353     namespace fs = boost::filesystem;
354 
355 // if not system include (<...>), then search current directory first
356     if (!is_system) {
357         if (!was_sys_include_path) {  // set_sys_include_delimiter() not called
358         // first have a look at the current directory
359             fs::path currpath (create_path(s));
360             if (!currpath.has_root_directory()) {
361                 currpath = create_path(current_dir.string());
362                 currpath /= create_path(s);
363             }
364 
365             if (fs::exists(currpath) && 0 == current_file) {
366             // if 0 != current_path (#include_next handling) it can't be
367             // the file in the current directory
368                 fs::path dirpath (create_path(s));
369                 if (!dirpath.has_root_directory()) {
370                     dirpath = create_path(current_rel_dir.string());
371                     dirpath /= create_path(s);
372                 }
373 
374                 dir = dirpath.string();
375                 s = normalize(currpath).string();    // found in local directory
376                 return true;
377             }
378 
379         // iterate all user include file directories to find the file
380             if (find_include_file(s, dir, user_include_paths, current_file))
381                 return true;
382 
383         // ... fall through
384         }
385         else {
386         //  if set_sys_include_delimiter() was called, then user include files
387         //  are searched in the user search path only
388             return find_include_file(s, dir, user_include_paths, current_file);
389         }
390 
391     // if nothing found, fall through
392     // ...
393     }
394 
395 // iterate all system include file directories to find the file
396     return find_include_file (s, dir, system_include_paths, current_file);
397 }
398 
399 ///////////////////////////////////////////////////////////////////////////////
400 //  Set current directory from a given file name
401 
402 inline bool
as_relative_to(boost::filesystem::path const & path,boost::filesystem::path const & base,boost::filesystem::path & result)403 as_relative_to(boost::filesystem::path const& path,
404     boost::filesystem::path const& base, boost::filesystem::path& result)
405 {
406     if (path.has_root_path()) {
407         if (path.root_path() == base.root_path())
408             return as_relative_to(path.relative_path(), base.relative_path(), result);
409 
410         result = path;    // that's our result
411     }
412     else {
413         if (base.has_root_path()) {
414             // cannot find relative path from a relative path and a rooted base
415             return false;
416         }
417         else {
418             typedef boost::filesystem::path::const_iterator path_iterator;
419             path_iterator path_it = path.begin();
420             path_iterator base_it = base.begin();
421             while (path_it != path.end() && base_it != base.end() ) {
422                 if (*path_it != *base_it)
423                     break;
424                 ++path_it; ++base_it;
425             }
426 
427             for (/**/; base_it != base.end(); ++base_it)
428                 result /= "..";
429 
430             for (/**/; path_it != path.end(); ++path_it)
431                 result /= *path_it;
432         }
433     }
434     return true;
435 }
436 
437 ///////////////////////////////////////////////////////////////////////////////
438 inline
set_current_directory(char const * path_)439 void include_paths::set_current_directory(char const *path_)
440 {
441     namespace fs = boost::filesystem;
442 
443     fs::path filepath (create_path(path_));
444     fs::path filename = util::complete_path(filepath, current_dir);
445     if (fs::exists(filename) && fs::is_directory(filename)) {
446         current_rel_dir.clear();
447         if (!as_relative_to(filepath, current_dir, current_rel_dir))
448             current_rel_dir = filepath;
449         current_dir = filename;
450     }
451     else {
452         current_rel_dir.clear();
453         if (!as_relative_to(branch_path(filepath), current_dir, current_rel_dir))
454             current_rel_dir = branch_path(filepath);
455         current_dir = branch_path(filename);
456     }
457 }
458 
459 ///////////////////////////////////////////////////////////////////////////////
460 }}}   // namespace boost::wave::util
461 
462 #if BOOST_WAVE_SERIALIZATION != 0
463 ///////////////////////////////////////////////////////////////////////////////
464 namespace boost { namespace serialization {
465 
466 ///////////////////////////////////////////////////////////////////////////////
467 //  Serialization support for boost::filesystem::path
468 template<class Archive>
save(Archive & ar,boost::filesystem::path const & p,const unsigned int)469 inline void save (Archive & ar, boost::filesystem::path const& p,
470     const unsigned int /* file_version */)
471 {
472     using namespace boost::serialization;
473     std::string path_str(p.native_file_string());
474     ar & make_nvp("filepath", path_str);
475 }
476 
477 template<class Archive>
load(Archive & ar,boost::filesystem::path & p,const unsigned int)478 inline void load (Archive & ar, boost::filesystem::path &p,
479     const unsigned int /* file_version */)
480 {
481     using namespace boost::serialization;
482     std::string path_str;
483     ar & make_nvp("filepath", path_str);
484     p = wave::util::create_path(path_str);
485 }
486 
487 // split non-intrusive serialization function member into separate
488 // non intrusive save/load member functions
489 template<class Archive>
serialize(Archive & ar,boost::filesystem::path & p,const unsigned int file_version)490 inline void serialize (Archive & ar, boost::filesystem::path &p,
491     const unsigned int file_version)
492 {
493     boost::serialization::split_free(ar, p, file_version);
494 }
495 
496 ///////////////////////////////////////////////////////////////////////////////
497 //  Serialization support for the used multi_index
498 template<class Archive>
save(Archive & ar,const typename boost::wave::util::bidirectional_map<std::string,std::string>::type & t,const unsigned int)499 inline void save (Archive & ar,
500     const typename boost::wave::util::bidirectional_map<
501         std::string, std::string
502     >::type &t,
503     const unsigned int /* file_version */)
504 {
505     boost::serialization::stl::save_collection<
506         Archive,
507         typename boost::wave::util::bidirectional_map<
508             std::string, std::string
509         >::type
510     >(ar, t);
511 }
512 
513 template<class Archive>
load(Archive & ar,typename boost::wave::util::bidirectional_map<std::string,std::string>::type & t,const unsigned int)514 inline void load (Archive & ar,
515     typename boost::wave::util::bidirectional_map<std::string, std::string>::type &t,
516     const unsigned int /* file_version */)
517 {
518     typedef typename boost::wave::util::bidirectional_map<
519             std::string, std::string
520         >::type map_type;
521     boost::serialization::stl::load_collection<
522         Archive, map_type,
523         boost::serialization::stl::archive_input_unique<Archive, map_type>,
524         boost::serialization::stl::no_reserve_imp<map_type>
525     >(ar, t);
526 }
527 
528 // split non-intrusive serialization function member into separate
529 // non intrusive save/load member functions
530 template<class Archive>
serialize(Archive & ar,typename boost::wave::util::bidirectional_map<std::string,std::string>::type & t,const unsigned int file_version)531 inline void serialize (Archive & ar,
532     typename boost::wave::util::bidirectional_map<
533         std::string, std::string
534     >::type &t,
535     const unsigned int file_version)
536 {
537     boost::serialization::split_free(ar, t, file_version);
538 }
539 
540 ///////////////////////////////////////////////////////////////////////////////
541 }}  // namespace boost::serialization
542 
543 BOOST_CLASS_VERSION(boost::wave::util::include_paths,
544     boost::wave::util::include_paths::version);
545 
546 #endif  // BOOST_WAVE_SERIALIZATION != 0
547 
548 // the suffix header occurs after all of the code
549 #ifdef BOOST_HAS_ABI_HEADERS
550 #include BOOST_ABI_SUFFIX
551 #endif
552 
553 #endif // !defined(CPP_INCLUDE_PATHS_HPP_AF620DA4_B3D2_4221_AD91_8A1ABFFB6944_INCLUDED)
554