/*============================================================================= Boost.Wave: A Standard compliant C++ preprocessor library http://www.boost.org/ Copyright (c) 2001-2005 Hartmut Kaiser. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) =============================================================================*/ #if !defined(CPP_include_paths_HPP_AF620DA4_B3D2_4221_AD91_8A1ABFFB6944_INCLUDED) #define CPP_include_paths_HPP_AF620DA4_B3D2_4221_AD91_8A1ABFFB6944_INCLUDED #include #include #include #include #include #include #include /////////////////////////////////////////////////////////////////////////////// namespace boost { namespace wave { namespace util { /////////////////////////////////////////////////////////////////////////////// // // include_paths - controlling the include path search order // // General notes: // // Any directories specified with the 'add_include_path()' function before // the function 'set_sys_include_delimiter()' is called are searched only // for the case of '#include "file"' directives, they are not searched for // '#include ' directives. If additional directories are specified // with the 'add_include_path()' function after a call to the function // 'set_sys_include_delimiter()', these directories are searched for all // '#include' directives. // // In addition, a call to the function 'set_sys_include_delimiter()' // inhibits the use of the current directory as the first search directory // for '#include "file"' directives. Therefore, the current directory is // searched only if it is requested explicitly with a call to the function // 'add_include_path(".")'. // // Calling both functions, the 'set_sys_include_delimiter()' and // 'add_include_path(".")' allows you to control precisely which // directories are searched before the current one and which are searched // after. // /////////////////////////////////////////////////////////////////////////////// class include_paths { typedef std::list > include_list_type; typedef include_list_type::value_type include_value_type; #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 typedef std::set pragma_once_set_t; #endif public: include_paths() : was_sys_include_path(false), current_dir(boost::filesystem::initial_path()), current_rel_dir(boost::filesystem::initial_path()) {} bool add_include_path(char const *path_, bool is_system = false) { return add_include_path(path_, (is_system || was_sys_include_path) ? system_include_paths : user_include_paths); } void set_sys_include_delimiter() { was_sys_include_path = true; } bool find_include_file (std::string &s, std::string &dir, bool is_system, char const *current_file) const; void set_current_directory(char const *path_); void init_initial_path() { boost::filesystem::initial_path(); } protected: bool find_include_file (std::string &s, std::string &dir, include_list_type const &pathes, char const *) const; bool add_include_path(char const *path_, include_list_type &pathes_); private: include_list_type user_include_paths; include_list_type system_include_paths; bool was_sys_include_path; // saw a set_sys_include_delimiter() boost::filesystem::path current_dir; boost::filesystem::path current_rel_dir; #if BOOST_WAVE_SUPPORT_PRAGMA_ONCE != 0 public: bool has_pragma_once(std::string const &filename) { return pragma_once_files.find(filename) != pragma_once_files.end(); } bool add_pragma_once_header(std::string const &filename) { return pragma_once_files.insert(filename).second; } private: pragma_once_set_t pragma_once_files; #endif }; /////////////////////////////////////////////////////////////////////////////// // Add an include path to one of the search lists (user include path or system // include path). inline bool include_paths::add_include_path ( char const *path_, include_list_type &pathes_) { namespace fs = boost::filesystem; if (path_) { fs::path newpath = fs::complete(fs::path(path_, fs::native), current_dir); if (!fs::exists(newpath) || !fs::is_directory(newpath)) { // the given path does not form a name of a valid file system directory // item return false; } pathes_.push_back (include_value_type(newpath, path_)); return true; } return false; } /////////////////////////////////////////////////////////////////////////////// // Find an include file by traversing the list of include directories inline bool include_paths::find_include_file (std::string &s, std::string &dir, include_list_type const &pathes, char const *current_file) const { namespace fs = boost::filesystem; typedef include_list_type::const_iterator const_include_list_iter_t; const_include_list_iter_t it = pathes.begin(); const_include_list_iter_t include_paths_end = pathes.end(); #if BOOST_WAVE_SUPPORT_INCLUDE_NEXT != 0 if (0 != current_file) { // re-locate the directory of the current file (#include_next handling) // #include_next does not distinguish between and "file" // inclusion, nor does it check that the file you specify has the same // name as the current file. It simply looks for the file named, starting // with the directory in the search path after the one where the current // file was found. fs::path file_path (current_file, fs::native); for (/**/; it != include_paths_end; ++it) { fs::path currpath ((*it).first.string(), fs::native); if (std::equal(currpath.begin(), currpath.end(), file_path.begin())) { ++it; // start searching with the next directory break; } } } #endif for (/**/; it != include_paths_end; ++it) { fs::path currpath ((*it).first.string(), fs::native); currpath /= fs::path(s, fs::native); // append filename if (fs::exists(currpath)) { fs::path dirpath ((*it).second, fs::native); dirpath /= fs::path(s, fs::native); dir = dirpath.string(); s = currpath.normalize().string(); // found the required file return true; } } return false; } /////////////////////////////////////////////////////////////////////////////// // Find an include file by searching the user and system includes in the // correct sequence (as it was configured by the user of the driver program) inline bool include_paths::find_include_file (std::string &s, std::string &dir, bool is_system, char const *current_file) const { namespace fs = boost::filesystem; // if not system include (<...>), then search current directory first if (!is_system) { if (!was_sys_include_path) { // set_sys_include_delimiter() not called // first have a look at the current directory fs::path currpath (current_dir.string(), fs::native); currpath /= fs::path(s, fs::native); if (fs::exists(currpath) && 0 == current_file) { // if 0 != current_path (#include_next handling) it can't be // the file in the current directory fs::path dirpath (current_rel_dir.string(), fs::native); dirpath /= fs::path(s, fs::native); dir = dirpath.string(); s = currpath.normalize().string(); // found in local directory return true; } // iterate all user include file directories to find the file if (find_include_file(s, dir, user_include_paths, current_file)) return true; // ... fall through } else { // if set_sys_include_delimiter() was called, then user include files // are searched in the user search path only return find_include_file(s, dir, user_include_paths, current_file); } // if nothing found, fall through // ... } // iterate all system include file directories to find the file return find_include_file (s, dir, system_include_paths, current_file); } /////////////////////////////////////////////////////////////////////////////// // Set current directory from a given file name inline void include_paths::set_current_directory(char const *path_) { namespace fs = boost::filesystem; fs::path filepath (path_, fs::native); fs::path filename = fs::complete(filepath, current_dir); if (fs::exists(filename) && fs::is_directory(filename)) { current_dir = filename; current_rel_dir = filepath; } else { current_dir = filename.branch_path(); current_rel_dir = filepath.branch_path(); } } /////////////////////////////////////////////////////////////////////////////// } // namespace util } // namespace wave } // namespace boost #endif // !defined(CPP_include_paths_HPP_AF620DA4_B3D2_4221_AD91_8A1ABFFB6944_INCLUDED)