diff --git a/src/db/db/dbLayoutToNetlistReader.cc b/src/db/db/dbLayoutToNetlistReader.cc index 127cbc4b7..706999790 100644 --- a/src/db/db/dbLayoutToNetlistReader.cc +++ b/src/db/db/dbLayoutToNetlistReader.cc @@ -60,7 +60,7 @@ typedef l2n_std_format::keys skeys; typedef l2n_std_format::keys lkeys; LayoutToNetlistStandardReader::LayoutToNetlistStandardReader (tl::InputStream &stream) - : m_stream (stream), m_path (stream.absolute_path ()), m_dbu (0.0), + : m_stream (stream), m_path (stream.absolute_file_path ()), m_dbu (0.0), m_progress (tl::to_string (tr ("Reading L2N database")), 1000) { m_progress.set_format (tl::to_string (tr ("%.0fk lines"))); diff --git a/src/db/db/dbNetlistSpiceReader.cc b/src/db/db/dbNetlistSpiceReader.cc index 7e8563aa8..16684a39b 100644 --- a/src/db/db/dbNetlistSpiceReader.cc +++ b/src/db/db/dbNetlistSpiceReader.cc @@ -183,7 +183,7 @@ SpiceReaderStream::line_number () const std::string SpiceReaderStream::source () const { - return mp_stream->absolute_path (); + return mp_stream->absolute_file_path (); } bool @@ -495,7 +495,7 @@ SpiceCircuitDict::read (tl::InputStream &stream) m_global_net_names.clear (); m_global_nets.clear (); - m_file_id = file_id (stream.absolute_path ()); + m_file_id = file_id (stream.absolute_file_path ()); while (! at_end ()) { read_card (); diff --git a/src/db/db/dbTechnology.cc b/src/db/db/dbTechnology.cc index 84bd4118e..d28d00739 100644 --- a/src/db/db/dbTechnology.cc +++ b/src/db/db/dbTechnology.cc @@ -479,10 +479,10 @@ std::string Technology::correct_path (const std::string &fp) const { std::string bp = base_path (); - if (bp.empty ()) { + if (bp.empty () || ! tl::InputStream::is_file_path (fp) || ! tl::InputStream::is_file_path (bp)) { return fp; } else { - return tl::relative_path (bp, fp); + return tl::relative_path (tl::InputStream::as_file_path (bp), tl::InputStream::as_file_path (fp)); } } @@ -494,7 +494,11 @@ Technology::load (const std::string &fn) xml_struct.parse (source, *this); // use the tech file's path as the default base path - set_default_base_path (tl::absolute_path (fn)); + if (tl::InputStream::is_file_path (fn)) { + set_default_base_path (tl::absolute_path (fn)); + } else { + set_default_base_path (std::string ()); + } set_tech_file_path (fn); } @@ -515,10 +519,10 @@ Technology::build_effective_path (const std::string &p) const return p; } - if (tl::is_absolute (p)) { + if (tl::InputStream::is_absolute (p)) { return p; } else { - return tl::combine_path (bp, p); + return tl::InputStream::combine (bp, p); } } diff --git a/src/lay/lay/layMainWindow.cc b/src/lay/lay/layMainWindow.cc index a3f667791..f7444453c 100644 --- a/src/lay/lay/layMainWindow.cc +++ b/src/lay/lay/layMainWindow.cc @@ -2981,7 +2981,7 @@ void MainWindow::add_mru (const std::string &fn_rel, const std::string &tech) { std::vector > new_mru; - std::string fn (tl::InputStream::absolute_path (fn_rel)); + std::string fn (tl::InputStream::absolute_file_path (fn_rel)); for (auto mru = m_mru.begin (); mru != m_mru.end (); ++mru) { if (mru->first != fn /* delete non-existing files: && tl::is_readable (mru->first) */) { @@ -3025,7 +3025,7 @@ MainWindow::add_to_other_mru (const std::string &fn_rel, const std::string &cfg) } std::vector new_mru; - std::string fn (tl::InputStream::absolute_path (fn_rel)); + std::string fn (tl::InputStream::absolute_file_path (fn_rel)); for (auto mru = mru_ptr->begin (); mru != mru_ptr->end (); ++mru) { if (*mru != fn /* delete non-existing files: && tl::is_readable (*mru) */) { diff --git a/src/lay/lay/laySession.cc b/src/lay/lay/laySession.cc index bed76e234..72494b1d3 100644 --- a/src/lay/lay/laySession.cc +++ b/src/lay/lay/laySession.cc @@ -66,7 +66,7 @@ Session::fetch (const lay::MainWindow &mw) if (lh) { m_layouts.push_back (SessionLayoutDescriptor ()); m_layouts.back ().name = *l; - m_layouts.back ().file_path = tl::InputStream::absolute_path (lh->filename ()); + m_layouts.back ().file_path = tl::InputStream::absolute_file_path (lh->filename ()); m_layouts.back ().load_options = lh->load_options (); m_layouts.back ().save_options = lh->save_options (); m_layouts.back ().save_options_valid = lh->save_options_valid (); @@ -89,7 +89,7 @@ Session::fetch (const lay::MainWindow &mw) const rdb::Database *rdb = view->get_rdb (j); if (rdb && ! rdb->filename ().empty ()) { - view_desc.rdb_filenames.push_back (tl::InputStream::absolute_path (rdb->filename ())); + view_desc.rdb_filenames.push_back (tl::InputStream::absolute_file_path (rdb->filename ())); } } @@ -98,7 +98,7 @@ Session::fetch (const lay::MainWindow &mw) const db::LayoutToNetlist *l2ndb = view->get_l2ndb (j); if (l2ndb && ! l2ndb->filename ().empty ()) { - view_desc.l2ndb_filenames.push_back (tl::InputStream::absolute_path (l2ndb->filename ())); + view_desc.l2ndb_filenames.push_back (tl::InputStream::absolute_file_path (l2ndb->filename ())); } } diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc index 893f83eb5..0b19845a1 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc @@ -1084,7 +1084,7 @@ LEFDEFReaderState::read_single_map_file (const std::string &path, std::map purpose_translation; @@ -2011,7 +2011,7 @@ LEFDEFImporter::get_mask (long m) void LEFDEFImporter::read (tl::InputStream &stream, db::Layout &layout, LEFDEFReaderState &state) { - tl::log << tl::to_string (tr ("Reading LEF/DEF file")) << " " << stream.absolute_path (); + tl::log << tl::to_string (tr ("Reading LEF/DEF file")) << " " << stream.absolute_file_path (); m_fn = stream.filename (); diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc index 8fe4c3fa5..3f04e2399 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc @@ -120,7 +120,7 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti std::string base_path; if (! effective_options.paths_relative_to_cwd ()) { - base_path = tl::dirname (m_stream.absolute_path ()); + base_path = tl::dirname (m_stream.absolute_file_path ()); } db::LEFDEFReaderState state (&effective_options, layout, base_path); @@ -179,7 +179,7 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti if (effective_options.read_lef_with_def ()) { - std::string input_dir = tl::absolute_path (m_stream.absolute_path ()); + std::string input_dir = tl::absolute_path (m_stream.absolute_file_path ()); if (tl::file_exists (input_dir)) { diff --git a/src/rdb/rdb/rdb.cc b/src/rdb/rdb/rdb.cc index 64f8192f4..125f18ec7 100644 --- a/src/rdb/rdb/rdb.cc +++ b/src/rdb/rdb/rdb.cc @@ -1786,7 +1786,7 @@ Database::load (std::string fn) reader.read (*this); } - set_filename (stream.absolute_path ()); + set_filename (stream.absolute_file_path ()); set_name (stream.filename ()); reset_modified (); diff --git a/src/tl/tl/tlFileUtils.cc b/src/tl/tl/tlFileUtils.cc index 34aff09ae..342edd070 100644 --- a/src/tl/tl/tlFileUtils.cc +++ b/src/tl/tl/tlFileUtils.cc @@ -254,7 +254,6 @@ static std::vector split_filename (const std::string &fn) const char *cp0 = cp; ++cp; while (*cp && *cp != '.') { - // backslash escaping (ineffective on Windows because that is a path separator) if (*cp == '\\' && cp[1]) { ++cp; } diff --git a/src/tl/tl/tlStream.cc b/src/tl/tl/tlStream.cc index f83be7bfb..2d575fdc2 100644 --- a/src/tl/tl/tlStream.cc +++ b/src/tl/tl/tlStream.cc @@ -425,32 +425,124 @@ InputStream::~InputStream () } } -std::string InputStream::absolute_path (const std::string &abstract_path) +std::string InputStream::absolute_file_path (const std::string &abstract_path) { // TODO: align this implementation with InputStream ctor tl::Extractor ex (abstract_path.c_str ()); -#if defined(HAVE_QT) if (ex.test (":")) { return abstract_path; - } else -#endif -#if defined(HAVE_CURL) || defined(HAVE_QT) - if (ex.test ("http:") || ex.test ("https:")) { - return abstract_path; - } else -#endif - if (ex.test ("pipe:")) { + } else if (ex.test ("http:") || ex.test ("https:") || ex.test ("pipe:") || ex.test ("data:")) { return abstract_path; } else if (ex.test ("file:")) { tl::URI uri (abstract_path); - return tl::absolute_path (uri.path ()); + return tl::absolute_file_path (uri.path ()); } else { return tl::absolute_file_path (abstract_path); } } -const char * +bool InputStream::is_absolute (const std::string &abstract_path) +{ + // TODO: align this implementation with InputStream ctor + + tl::Extractor ex (abstract_path.c_str ()); + if (ex.test (":")) { + return true; + } else if (ex.test ("http:") || ex.test ("https:") || ex.test ("pipe:") || ex.test ("data:")) { + return true; + } else if (ex.test ("file:")) { + tl::URI uri (abstract_path); + return tl::is_absolute (uri.path ()); + } else { + return tl::is_absolute (abstract_path); + } +} + +bool InputStream::is_file_path (const std::string &abstract_path) +{ + tl::Extractor ex (abstract_path.c_str ()); + if (ex.test (":")) { + return false; + } else if (ex.test ("http:") || ex.test ("https:") || ex.test ("pipe:") || ex.test ("data:")) { + return false; + } else { + return true; + } +} + +std::string InputStream::as_file_path (const std::string &abstract_path) +{ + tl::Extractor ex (abstract_path.c_str ()); + if (ex.test (":")) { + return std::string (); + } else if (ex.test ("http:") || ex.test ("https:") || ex.test ("pipe:") || ex.test ("data:")) { + return std::string (); + } else if (ex.test ("file:")) { + tl::URI uri (abstract_path); + return uri.path (); + } else { + return abstract_path; + } +} + +std::string InputStream::combine (const std::string &path1, const std::string &path2) +{ + if (is_absolute (path2)) { + return path2; + } + + tl::Extractor ex (path1); + if (ex.test (":")) { + return path1 + "/" + path2; + } else if (ex.test ("pipe:") || ex.test ("data:")) { + // ignore un-combinable first parts + return path2; + } + + tl::URI uri1 (path1); + tl::URI uri2 (path2); + + if (uri1.scheme ().empty ()) { + if (uri2.scheme ().empty ()) { + return tl::combine_path (path1, path2); + } else { + return tl::combine_path (path1, uri2.path ()); + } + } else { + if (uri2.scheme ().empty ()) { + uri1.set_path (uri1.path () + "/" + tl::replaced (path2, "\\", "/")); + } else { + uri1.set_path (uri1.path () + "/" + uri2.path ()); + } + return uri1.to_abstract_path (); + } +} + +std::string InputStream::relative_path (const std::string &path1, const std::string &path2) +{ + // TODO: align this implementation with InputStream ctor + + tl::Extractor ex (path2); + if (ex.test (":")) { + return path2; + } else if (ex.test ("pipe:") || ex.test ("data:")) { + return path2; + } + + tl::URI uri1 (path1); + tl::URI uri2 (path2); + + // NOTE: only file schemes are supported as of now + if ((uri1.scheme ().empty () || uri1.scheme () == "file") && + (uri2.scheme ().empty () || uri2.scheme () == "file")) { + return tl::relative_path (uri1.path (), uri2.path ()); + } + + return path2; +} + +const char * InputStream::get (size_t n, bool bypass_inflate) { // if deflating, employ the deflate filter to get the data diff --git a/src/tl/tl/tlStream.h b/src/tl/tl/tlStream.h index c738c84ff..a69412aae 100644 --- a/src/tl/tl/tlStream.h +++ b/src/tl/tl/tlStream.h @@ -531,7 +531,7 @@ public: * * Returns an empty string if no absolute path is available. */ - std::string absolute_path () const + std::string absolute_file_path () const { return mp_delegate->absolute_path (); } @@ -549,9 +549,34 @@ public: void close (); /** - * @brief Gets the absolute path for a given URL + * @brief Gets the absolute, abstract path for a given abstract path */ - static std::string absolute_path (const std::string &path); + static std::string absolute_file_path (const std::string &apath); + + /** + * @brief Gets the absolute path for a given abstract path + */ + static bool is_absolute (const std::string &apath); + + /** + * @brief Gets a value indicating whether the path is a file path + */ + static bool is_file_path (const std::string &apath); + + /** + * @brief Gets the file path (no scheme) if it applies to the given abstract path + */ + static std::string as_file_path (const std::string &apath); + + /** + * @brief Combines two abstract paths + */ + static std::string combine (const std::string &apath1, const std::string &apath2); + + /** + * @brief Returns the relative abstract path of path2 vs. path1 + */ + static std::string relative_path (const std::string &apath1, const std::string &apath2); /** * @brief Gets the base reader (delegate) @@ -621,7 +646,7 @@ public: virtual std::string absolute_path () const { - return m_inflating_stream.absolute_path (); + return m_inflating_stream.absolute_file_path (); } virtual std::string filename () const diff --git a/src/tl/unit_tests/tlStreamTests.cc b/src/tl/unit_tests/tlStreamTests.cc index 156f276c2..8b310e6fc 100644 --- a/src/tl/unit_tests/tlStreamTests.cc +++ b/src/tl/unit_tests/tlStreamTests.cc @@ -479,3 +479,111 @@ TEST(RefuseToWrite) } } +TEST(AbstractPathFunctions) +{ + EXPECT_EQ (tl::InputStream::absolute_file_path (""), tl::absolute_file_path (".")); + EXPECT_EQ (tl::InputStream::absolute_file_path ("."), tl::absolute_file_path (".")); + EXPECT_EQ (tl::InputStream::absolute_file_path ("pipe:xyz"), "pipe:xyz"); + EXPECT_EQ (tl::InputStream::absolute_file_path ("data:xyz"), "data:xyz"); + EXPECT_EQ (tl::InputStream::absolute_file_path ("https:xyz"), "https:xyz"); + EXPECT_EQ (tl::InputStream::absolute_file_path ("http:xyz"), "http:xyz"); + EXPECT_EQ (tl::InputStream::absolute_file_path (":xyz"), ":xyz"); + EXPECT_EQ (tl::InputStream::absolute_file_path ("file:xyz"), tl::absolute_file_path ("xyz")); + EXPECT_EQ (tl::InputStream::absolute_file_path ("xyz"), tl::absolute_file_path ("xyz")); + EXPECT_EQ (tl::InputStream::absolute_file_path ("xyz/uvw"), tl::absolute_file_path ("xyz/uvw")); + EXPECT_EQ (tl::InputStream::absolute_file_path ("/xyz/uvw"), "/xyz/uvw"); + tl::file_utils_force_windows (); + EXPECT_EQ (tl::InputStream::absolute_file_path ("xyz\\uvw"), tl::absolute_file_path ("xyz\\uvw")); + EXPECT_EQ (tl::InputStream::absolute_file_path ("\\\\server\\xyz\\uvw"), "\\\\server\\xyz\\uvw"); + EXPECT_EQ (tl::InputStream::absolute_file_path ("c:\\xyz\\uvw"), "c:\\xyz\\uvw"); + tl::file_utils_force_reset (); + + EXPECT_EQ (tl::InputStream::is_absolute (""), false); + EXPECT_EQ (tl::InputStream::is_absolute ("."), false); + EXPECT_EQ (tl::InputStream::is_absolute ("pipe:xyz"), true); + EXPECT_EQ (tl::InputStream::is_absolute ("data:xyz"), true); + EXPECT_EQ (tl::InputStream::is_absolute ("https:xyz"), true); + EXPECT_EQ (tl::InputStream::is_absolute ("http:xyz"), true); + EXPECT_EQ (tl::InputStream::is_absolute (":xyz"), true); + EXPECT_EQ (tl::InputStream::is_absolute ("file:xyz"), false); + EXPECT_EQ (tl::InputStream::is_absolute ("xyz"), false); + EXPECT_EQ (tl::InputStream::is_absolute ("xyz/uvw"), false); + EXPECT_EQ (tl::InputStream::is_absolute ("/xyz/uvw"), true); + tl::file_utils_force_windows (); + EXPECT_EQ (tl::InputStream::is_absolute ("xyz\\uvw"), false); + EXPECT_EQ (tl::InputStream::is_absolute ("\\\\server\\xyz\\uvw"), true); + EXPECT_EQ (tl::InputStream::is_absolute ("c:\\xyz\\uvw"), true); + tl::file_utils_force_reset (); + + tl::file_utils_force_windows (); + EXPECT_EQ (tl::InputStream::combine ("a", ""), "a"); + EXPECT_EQ (tl::InputStream::combine ("", "b"), "\\b"); + EXPECT_EQ (tl::InputStream::combine ("a", "b"), "a\\b"); + EXPECT_EQ (tl::InputStream::combine ("a", "b/c"), "a\\b/c"); + EXPECT_EQ (tl::InputStream::combine ("a", "b\\c"), "a\\b\\c"); + EXPECT_EQ (tl::InputStream::combine ("a", "data:abc"), "data:abc"); + EXPECT_EQ (tl::InputStream::combine ("data:a", "b"), "b"); + EXPECT_EQ (tl::InputStream::combine ("pipe:a", "b"), "b"); + EXPECT_EQ (tl::InputStream::combine (":a", "b"), ":a/b"); + EXPECT_EQ (tl::InputStream::combine ("https://a", "b"), "https://a/b"); + EXPECT_EQ (tl::InputStream::combine ("https://a", "https:b"), "https:b"); + EXPECT_EQ (tl::InputStream::combine ("a", "https:b"), "https:b"); + EXPECT_EQ (tl::InputStream::combine ("a", "file:b"), "a\\b"); + EXPECT_EQ (tl::InputStream::combine ("a", "file:\\b"), "file:\\b"); + EXPECT_EQ (tl::InputStream::combine ("file:a", "file:b"), "file:a/b"); + EXPECT_EQ (tl::InputStream::combine ("file:a", "file:b/c"), "file:a/b/c"); + EXPECT_EQ (tl::InputStream::combine ("file:a", "b\\c"), "file:a/b/c"); + tl::file_utils_force_linux (); + EXPECT_EQ (tl::InputStream::combine ("a", "b"), "a/b"); + EXPECT_EQ (tl::InputStream::combine ("", "b"), "/b"); + EXPECT_EQ (tl::InputStream::combine ("a", "b/c"), "a/b/c"); + EXPECT_EQ (tl::InputStream::combine ("a", "data:abc"), "data:abc"); + EXPECT_EQ (tl::InputStream::combine ("data:a", "b"), "b"); + EXPECT_EQ (tl::InputStream::combine ("pipe:a", "b"), "b"); + EXPECT_EQ (tl::InputStream::combine (":a", "b"), ":a/b"); + EXPECT_EQ (tl::InputStream::combine ("https://a", "b"), "https://a/b"); + EXPECT_EQ (tl::InputStream::combine ("https://a", "https:b"), "https:b"); + EXPECT_EQ (tl::InputStream::combine ("a", "https:b"), "https:b"); + EXPECT_EQ (tl::InputStream::combine ("a", "file:b"), "a/b"); + EXPECT_EQ (tl::InputStream::combine ("a", "file:/b"), "file:/b"); + EXPECT_EQ (tl::InputStream::combine ("file:a", "file:b"), "file:a/b"); + EXPECT_EQ (tl::InputStream::combine ("file:a", "file:b/c"), "file:a/b/c"); + EXPECT_EQ (tl::InputStream::combine ("file:a", "b/c"), "file:a/b/c"); + tl::file_utils_force_reset (); + + tl::file_utils_force_linux (); + EXPECT_EQ (tl::InputStream::relative_path ("", "file:/a/b/c"), "/a/b/c"); + EXPECT_EQ (tl::InputStream::relative_path (".", "file:/a/b/c"), "/a/b/c"); + EXPECT_EQ (tl::InputStream::relative_path ("https://x", "a/b/c"), "a/b/c"); + EXPECT_EQ (tl::InputStream::relative_path ("file:/a/b", "file:/a/b/c"), "c"); + EXPECT_EQ (tl::InputStream::relative_path ("/a/b", "/a/b/c"), "c"); + EXPECT_EQ (tl::InputStream::relative_path ("/a/b", "/x/b/c"), "/x/b/c"); + EXPECT_EQ (tl::InputStream::relative_path ("file:/a/b", "file:/a/b/c"), "c"); + EXPECT_EQ (tl::InputStream::relative_path ("/a/b", "/a/b/c"), "c"); + tl::file_utils_force_windows (); + EXPECT_EQ (tl::InputStream::relative_path ("/a/b", "/a/b/c"), "c"); + EXPECT_EQ (tl::InputStream::relative_path ("/a/b", "\\a\\b\\c\\d"), "c\\d"); + tl::file_utils_force_reset (); + + EXPECT_EQ (tl::InputStream::is_file_path (""), true); + EXPECT_EQ (tl::InputStream::is_file_path (":abc"), false); + EXPECT_EQ (tl::InputStream::is_file_path ("pipe:abc"), false); + EXPECT_EQ (tl::InputStream::is_file_path ("data:abc"), false); + EXPECT_EQ (tl::InputStream::is_file_path ("http:abc"), false); + EXPECT_EQ (tl::InputStream::is_file_path ("file:abc"), true); + EXPECT_EQ (tl::InputStream::is_file_path ("a/b/c"), true); + tl::file_utils_force_windows (); + EXPECT_EQ (tl::InputStream::is_file_path ("a\\b\\c"), true); + tl::file_utils_force_reset (); + + EXPECT_EQ (tl::InputStream::as_file_path (""), std::string ()); + EXPECT_EQ (tl::InputStream::as_file_path (":abc"), std::string ()); + EXPECT_EQ (tl::InputStream::as_file_path ("pipe:abc"), std::string ()); + EXPECT_EQ (tl::InputStream::as_file_path ("data:abc"), std::string ()); + EXPECT_EQ (tl::InputStream::as_file_path ("http:abc"), std::string ()); + EXPECT_EQ (tl::InputStream::as_file_path ("file:abc"), "abc"); + EXPECT_EQ (tl::InputStream::as_file_path ("a/b/c"), "a/b/c"); + tl::file_utils_force_windows (); + EXPECT_EQ (tl::InputStream::as_file_path ("a\\b\\c"), "a\\b\\c"); + tl::file_utils_force_reset (); +}