Merge pull request #1331 from KLayout/issue-1321

Implemented tilde expansion, internal stream path names are now absol…
This commit is contained in:
Matthias Köfferlein 2023-04-13 22:54:12 +02:00 committed by GitHub
commit fc014efe80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 88 additions and 15 deletions

View File

@ -500,7 +500,7 @@ TEST(11_ErrorOnCircuitRedefinition)
msg = ex.msg (); msg = ex.msg ();
} }
EXPECT_EQ (tl::replaced (msg, path, "?"), "Redefinition of circuit SUBCKT in ?, line 20"); EXPECT_EQ (tl::replaced (msg, tl::absolute_file_path (path), "?"), "Redefinition of circuit SUBCKT in ?, line 20");
} }
TEST(12_IgnoreDuplicateGlobals) TEST(12_IgnoreDuplicateGlobals)
@ -580,7 +580,7 @@ TEST(14_IncludeWithError)
reader.read (is, nl); reader.read (is, nl);
EXPECT_EQ (true, false); // must not happen EXPECT_EQ (true, false); // must not happen
} catch (tl::Exception &ex) { } catch (tl::Exception &ex) {
EXPECT_EQ (ex.msg (), "'M' element must have four nodes in " + std::string (tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader14x.cir")) + ", line 3"); EXPECT_EQ (ex.msg (), "'M' element must have four nodes in " + std::string (tl::absolute_file_path (tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader14x.cir"))) + ", line 3");
} }
} }

View File

@ -24,6 +24,7 @@
#include "tlStream.h" #include "tlStream.h"
#include "tlLog.h" #include "tlLog.h"
#include "tlInternational.h" #include "tlInternational.h"
#include "tlEnv.h"
#include <cctype> #include <cctype>
@ -52,6 +53,7 @@
# include <dirent.h> # include <dirent.h>
# include <libproc.h> # include <libproc.h>
# include <dlfcn.h> # include <dlfcn.h>
# include <pwd.h>
#else #else
@ -59,6 +61,7 @@
# include <unistd.h> # include <unistd.h>
# include <dirent.h> # include <dirent.h>
# include <dlfcn.h> # include <dlfcn.h>
# include <pwd.h>
#endif #endif
@ -629,7 +632,7 @@ bool chdir (const std::string &path)
static std::pair<std::string, bool> absolute_path_of_existing (const std::string &s) static std::pair<std::string, bool> absolute_path_of_existing (const std::string &s)
{ {
#if defined (_WIN32) #if defined(_WIN32)
wchar_t *fp = _wfullpath (NULL, tl::to_wstring (s).c_str (), 0); wchar_t *fp = _wfullpath (NULL, tl::to_wstring (s).c_str (), 0);
if (fp == NULL) { if (fp == NULL) {
@ -657,6 +660,11 @@ static std::pair<std::string, bool> absolute_path_of_existing (const std::string
bool is_absolute (const std::string &s) bool is_absolute (const std::string &s)
{ {
// ~ paths are always absolute, because the home directory is
if (s.size () > 0 && s[0] == '~') {
return true;
}
std::vector<std::string> parts = split_path (s); std::vector<std::string> parts = split_path (s);
if (parts.size () > 1 && is_drive (parts [0])) { if (parts.size () > 1 && is_drive (parts [0])) {
return is_part_with_separator (parts [1]); return is_part_with_separator (parts [1]);
@ -669,6 +677,11 @@ bool is_absolute (const std::string &s)
std::string absolute_file_path (const std::string &s) std::string absolute_file_path (const std::string &s)
{ {
// ~ paths are always absolute, because the home directory is
if (s.size () > 0 && s[0] == '~') {
return get_home_path () + std::string (s, 1);
}
std::vector<std::string> parts = split_path (s); std::vector<std::string> parts = split_path (s);
if (parts.empty ()) { if (parts.empty ()) {
return current_dir (); return current_dir ();
@ -854,10 +867,36 @@ bool is_same_file (const std::string &a, const std::string &b)
#endif #endif
} }
std::string
get_home_path ()
{
#if !defined(_WIN32)
if (tl::has_env ("HOME")) {
return tl::get_env ("HOME");
} else {
struct passwd *pwd = getpwuid (getuid ());
if (pwd) {
return std::string (pwd->pw_dir);
}
}
tl::warn << tl::to_string (tr ("Unable to get home directory (set HOME environment variable)"));
#else
if (tl::has_env ("HOMEDRIVE") && tl::has_env ("HOMEPATH")) {
return tl::get_env ("HOMEDRIVE") + tl::get_env ("HOMEPATH");
} else if (tl::has_env ("HOMESHARE") && tl::has_env ("HOMEPATH")) {
return tl::get_env ("HOMESHARE") + tl::get_env ("HOMEPATH");
} else if (tl::has_env ("USERPROFILE")) {
return tl::get_env ("USERPROFILE");
}
tl::warn << tl::to_string (tr ("Unable to get home directory (no HOMEDRIVE/HOMEPATH, HOMESHARE/HOMEPATH or USERPROFILE environment variables)"));
#endif
return std::string (".");
}
static std::string static std::string
get_inst_path_internal () get_inst_path_internal ()
{ {
#ifdef _WIN32 #if defined(_WIN32)
wchar_t buffer[MAX_PATH]; wchar_t buffer[MAX_PATH];
int len; int len;

View File

@ -194,6 +194,11 @@ bool TL_PUBLIC chdir (const std::string &path);
*/ */
std::vector<std::string> TL_PUBLIC split_path (const std::string &p, bool keep_last = false); std::vector<std::string> TL_PUBLIC split_path (const std::string &p, bool keep_last = false);
/**
* @brief Gets the home directory path
*/
std::string TL_PUBLIC get_home_path ();
/** /**
* @brief Gets the path of the currently running process * @brief Gets the path of the currently running process
*/ */

View File

@ -666,15 +666,15 @@ TextInputStream::reset ()
InputFile::InputFile (const std::string &path) InputFile::InputFile (const std::string &path)
: m_fd (-1) : m_fd (-1)
{ {
m_source = path; m_source = tl::absolute_file_path (path);;
#if defined(_WIN32) #if defined(_WIN32)
int fd = _wopen (tl::to_wstring (path).c_str (), _O_BINARY | _O_RDONLY | _O_SEQUENTIAL); int fd = _wopen (tl::to_wstring (m_source).c_str (), _O_BINARY | _O_RDONLY | _O_SEQUENTIAL);
if (fd < 0) { if (fd < 0) {
throw FileOpenErrorException (m_source, errno); throw FileOpenErrorException (m_source, errno);
} }
m_fd = fd; m_fd = fd;
#else #else
int fd = open (path.c_str (), O_RDONLY); int fd = open (m_source.c_str (), O_RDONLY);
if (fd < 0) { if (fd < 0) {
throw FileOpenErrorException (m_source, errno); throw FileOpenErrorException (m_source, errno);
} }
@ -747,15 +747,15 @@ InputFile::filename () const
InputZLibFile::InputZLibFile (const std::string &path) InputZLibFile::InputZLibFile (const std::string &path)
: mp_d (new ZLibFilePrivate ()) : mp_d (new ZLibFilePrivate ())
{ {
m_source = path; m_source = tl::absolute_file_path (path);
#if defined(_WIN32) #if defined(_WIN32)
int fd = _wopen (tl::to_wstring (path).c_str (), _O_BINARY | _O_RDONLY | _O_SEQUENTIAL); int fd = _wopen (tl::to_wstring (m_source).c_str (), _O_BINARY | _O_RDONLY | _O_SEQUENTIAL);
if (fd < 0) { if (fd < 0) {
throw FileOpenErrorException (m_source, errno); throw FileOpenErrorException (m_source, errno);
} }
mp_d->zs = gzdopen (fd, "rb"); mp_d->zs = gzdopen (fd, "rb");
#else #else
mp_d->zs = gzopen (tl::string_to_system (path).c_str (), "rb"); mp_d->zs = gzopen (tl::string_to_system (m_source).c_str (), "rb");
#endif #endif
if (mp_d->zs == NULL) { if (mp_d->zs == NULL) {
throw FileOpenErrorException (m_source, errno); throw FileOpenErrorException (m_source, errno);
@ -1035,10 +1035,10 @@ OutputStream::seek (size_t pos)
// OutputFileBase implementation // OutputFileBase implementation
OutputFileBase::OutputFileBase (const std::string &path, int keep_backups) OutputFileBase::OutputFileBase (const std::string &path, int keep_backups)
: m_keep_backups (keep_backups), m_path (path), m_has_error (false) : m_keep_backups (keep_backups), m_path (tl::absolute_file_path (path)), m_has_error (false)
{ {
if (tl::file_exists (path)) { if (tl::file_exists (m_path)) {
m_backup_path = path + ".~backup"; m_backup_path = m_path + ".~backup";
if (tl::file_exists (m_backup_path)) { if (tl::file_exists (m_backup_path)) {
if (! tl::rm_file (m_backup_path)) { if (! tl::rm_file (m_backup_path)) {
tl::warn << tl::sprintf (tl::to_string (tr ("Could not create backup file: unable to remove existing file '%s'")), m_backup_path); tl::warn << tl::sprintf (tl::to_string (tr ("Could not create backup file: unable to remove existing file '%s'")), m_backup_path);
@ -1046,8 +1046,8 @@ OutputFileBase::OutputFileBase (const std::string &path, int keep_backups)
} }
} }
if (! m_backup_path.empty ()) { if (! m_backup_path.empty ()) {
if (! tl::rename_file (path, tl::filename (m_backup_path))) { if (! tl::rename_file (m_path, tl::filename (m_backup_path))) {
tl::warn << tl::sprintf (tl::to_string (tr ("Could not create backup file: unable to rename original file '%s' to backup file")), path, m_backup_path); tl::warn << tl::sprintf (tl::to_string (tr ("Could not create backup file: unable to rename original file '%s' to backup file")), m_path, m_backup_path);
m_backup_path = std::string (); m_backup_path = std::string ();
} }
} }

View File

@ -401,6 +401,8 @@ TEST (10)
EXPECT_EQ (tl::extension_last ("\\hello\\.world.gz"), "gz"); EXPECT_EQ (tl::extension_last ("\\hello\\.world.gz"), "gz");
EXPECT_EQ (tl::extension_last ("/hello//world/"), ""); EXPECT_EQ (tl::extension_last ("/hello//world/"), "");
EXPECT_EQ (tl::is_absolute ("~/world"), true);
EXPECT_EQ (tl::is_absolute ("~"), true);
EXPECT_EQ (tl::is_absolute ("world"), false); EXPECT_EQ (tl::is_absolute ("world"), false);
EXPECT_EQ (tl::is_absolute ("world/"), false); EXPECT_EQ (tl::is_absolute ("world/"), false);
EXPECT_EQ (tl::is_absolute ("hello//world/"), false); EXPECT_EQ (tl::is_absolute ("hello//world/"), false);
@ -796,3 +798,21 @@ TEST (18)
EXPECT_EQ (is.read_all (), "hello, world!\n"); EXPECT_EQ (is.read_all (), "hello, world!\n");
} }
} }
// get_home_path
TEST (19)
{
std::string home = tl::get_home_path ();
// no specific value, just something ...
EXPECT_EQ (home.size () > 5, true);
#if defined(HAVE_QT)
EXPECT_EQ (tl::replaced (home, "\\", "/"), tl::replaced (tl::to_string (QDir::homePath ()), "\\", "/"));
#endif
}
// absolute path with "~" expansion
TEST (20)
{
EXPECT_EQ (tl::absolute_file_path ("~"), tl::get_home_path ());
EXPECT_EQ (tl::absolute_file_path (tl::combine_path ("~", "test")), tl::combine_path (tl::get_home_path (), "test"));
}

View File

@ -167,6 +167,7 @@ END
nl = RBA::Netlist::new nl = RBA::Netlist::new
input = File.join($ut_testsrc, "testdata", "algo", "nreader6.cir") input = File.join($ut_testsrc, "testdata", "algo", "nreader6.cir")
input = File.absolute_path(input)
mydelegate = MyNetlistSpiceReaderDelegate::new mydelegate = MyNetlistSpiceReaderDelegate::new
@ -185,6 +186,14 @@ END
rescue Exception => ex rescue Exception => ex
msg = ex.message msg = ex.message
end end
# normalize paths for Windows
msg = msg.gsub("\\", "/")
input = input.gsub("\\", "/")
puts "INFO: msg is '#{msg}'"
puts "INFO: input is '#{input}'"
assert_equal(msg.sub(input, "<INPUT>"), "Nothing implemented in <INPUT>, line 22 in Netlist::read") assert_equal(msg.sub(input, "<INPUT>"), "Nothing implemented in <INPUT>, line 22 in Netlist::read")
assert_equal(nl.description, "Read by MyDelegate") assert_equal(nl.description, "Read by MyDelegate")