diff --git a/backward.hpp b/backward.hpp index 06c9b3b..3ca1824 100644 --- a/backward.hpp +++ b/backward.hpp @@ -397,6 +397,16 @@ template T &move(T &v) { return v; } } // namespace backward #endif // BACKWARD_ATLEAST_CXX11 +namespace backward { +namespace details { +#if defined(BACKWARD_SYSTEM_WINDOWS) +const char kBackwardPathDelimiter[] = ";"; +#else +const char kBackwardPathDelimiter[] = ":"; +#endif +} // namespace details +} // namespace backward + namespace backward { namespace system_tag { @@ -587,6 +597,29 @@ private: struct demangler : public demangler_impl {}; +// Split a string on the platform's PATH delimiter. Example: if delimiter +// is ":" then: +// "" --> [] +// ":" --> ["",""] +// "::" --> ["","",""] +// "/a/b/c" --> ["/a/b/c"] +// "/a/b/c:/d/e/f" --> ["/a/b/c","/d/e/f"] +// etc. +inline std::vector split_source_prefixes(const std::string &s) { + std::vector out; + size_t last = 0; + size_t next = 0; + size_t delimiter_size = sizeof(kBackwardPathDelimiter)-1; + while ((next = s.find(kBackwardPathDelimiter, last)) != std::string::npos) { + out.push_back(s.substr(last, next-last)); + last = next + delimiter_size; + } + if (last <= s.length()) { + out.push_back(s.substr(last)); + } + return out; +} + } // namespace details /*************** A TRACE ***************/ @@ -3360,8 +3393,22 @@ public: typedef std::vector> lines_t; SourceFile() {} - SourceFile(const std::string &path) - : _file(new std::ifstream(path.c_str())) {} + SourceFile(const std::string &path) { + // 1. If BACKWARD_CXX_SOURCE_PREFIXES is set then assume it contains + // a colon-separated list of path prefixes. Try prepending each + // to the given path until a valid file is found. + const std::vector& prefixes = get_paths_from_env_variable(); + for (size_t i = 0; i < prefixes.size(); ++i) { + // Double slashes (//) should not be a problem. + std::string new_path = prefixes[i] + '/' + path; + _file.reset(new std::ifstream(new_path.c_str())); + if (is_open()) break; + } + // 2. If no valid file found then fallback to opening the path as-is. + if (!_file || !is_open()) { + _file.reset(new std::ifstream(path.c_str())); + } + } bool is_open() const { return _file->is_open(); } lines_t &get_lines(unsigned line_start, unsigned line_count, lines_t &lines) { @@ -3457,6 +3504,20 @@ private: details::handle> _file; + std::vector get_paths_from_env_variable_impl() { + std::vector paths; + const char* prefixes_str = std::getenv("BACKWARD_CXX_SOURCE_PREFIXES"); + if (prefixes_str && prefixes_str[0]) { + paths = details::split_source_prefixes(prefixes_str); + } + return paths; + } + + const std::vector& get_paths_from_env_variable() { + static std::vector paths = get_paths_from_env_variable_impl(); + return paths; + } + #ifdef BACKWARD_ATLEAST_CXX11 SourceFile(const SourceFile &) = delete; SourceFile &operator=(const SourceFile &) = delete;