1717*****************************************************************************/
1818
1919#include < cstdlib>
20- #include < climits>
21- #include < cstring>
2220#include < cassert>
21+ #include < filesystem>
2322#include < sstream>
2423#include < stdexcept>
25- #include < sys/stat.h>
24+
25+ #ifdef _WIN32
26+ #ifndef NOMINMAX
27+ #define NOMINMAX
28+ #endif
29+ #include < windows.h>
30+ #include < system_error>
31+ #include < vector>
32+ #else
2633#include < dlfcn.h>
27- #include < libgen.h >
34+ #endif
2835
2936#include < diffpy/version.hpp>
3037#include < diffpy/runtimepath.hpp>
@@ -37,6 +44,8 @@ using namespace std;
3744
3845namespace {
3946
47+ namespace fs = std::filesystem;
48+
4049const char * runtimerelpath =
4150#ifdef DIFFPYRUNTIMERELPATH
4251 STRINGIFY (DIFFPYRUNTIMERELPATH )
@@ -48,75 +57,84 @@ const char* runtimerelpath =
4857#endif
4958;
5059
51- bool isdir (const string& d)
60+ // Locate the module containing this code, including an embedding extension.
61+ fs::path modulepath ()
5262{
53- struct stat sd;
54- return (stat (d.c_str (), &sd) == 0 && S_ISDIR (sd.st_mode ));
63+ static const char anchor = 0 ;
64+ #ifdef _WIN32
65+ HMODULE module = nullptr ;
66+ if (!GetModuleHandleExW (
67+ GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
68+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT ,
69+ reinterpret_cast <LPCWSTR >(&anchor), &module ))
70+ {
71+ throw system_error (GetLastError (), system_category (),
72+ " Cannot locate the libdiffpy module" );
73+ }
74+ vector<wchar_t > buffer (256 );
75+ for (;;)
76+ {
77+ DWORD size = GetModuleFileNameW (module , buffer.data (),
78+ static_cast <DWORD >(buffer.size ()));
79+ if (size == 0 )
80+ {
81+ throw system_error (GetLastError (), system_category (),
82+ " Cannot resolve the libdiffpy module path" );
83+ }
84+ if (size < buffer.size ())
85+ {
86+ return fs::path (wstring (buffer.data (), size));
87+ }
88+ buffer.resize (2 * buffer.size ());
89+ }
90+ #else
91+ Dl_info info = {};
92+ if (!dladdr (&anchor, &info) || !info.dli_fname )
93+ {
94+ throw runtime_error (" Cannot resolve the libdiffpy module path" );
95+ }
96+ return fs::path (info.dli_fname );
97+ #endif
5598}
5699
57100
58- // / throw runtime error if directory does not exist .
59- void ensureIsDir (const std::string& d )
101+ // Validate an explicit directory without falling back to packaged data .
102+ string canonicalDirectory (const fs::path& directory )
60103{
61- if (isdir (d)) return ;
62- string emsg (" Directory '@D' does not exist." );
63- emsg = emsg.replace (emsg.find_first_of (' @' ), 2 , d);
64- throw runtime_error (emsg);
104+ if (!fs::is_directory (directory))
105+ {
106+ throw runtime_error (" Directory '" + directory.string () +
107+ " ' does not exist." );
108+ }
109+ return fs::canonical (directory).generic_string ();
65110}
66111
67112
68- const string& diffpyruntime ()
113+ string diffpyruntime ()
69114{
70- char fpb[PATH_MAX ];
71- static string librt;
72- static bool did_librt = false ;
73- static string envrt;
74- static bool did_envrt = false ;
75115 // check the DIFFPYRUNTIME environment variable.
76- char * pe = getenv (" DIFFPYRUNTIME" );
116+ const char * pe = getenv (" DIFFPYRUNTIME" );
77117 if (pe && *pe != ' \0 ' )
78118 {
79- static string dprt;
80- did_envrt = did_envrt && (dprt == pe);
81- if (!did_envrt)
82- {
83- dprt = pe;
84- envrt = pe;
85- size_t pt = envrt.find_last_not_of (' /' );
86- if (pt == string::npos) envrt = envrt.substr (0 , 1 );
87- else envrt.erase (pt + 1 );
88- ensureIsDir (envrt);
89- envrt = realpath (envrt.c_str (), fpb);
90- did_envrt = true ;
91- }
92- return envrt;
119+ return canonicalDirectory (pe);
93120 }
94- if (did_librt) return librt;
95- // here we need to resolve path of the libdiffpy shared library
96- Dl_info i;
97- dladdr (reinterpret_cast <void *>(diffpyruntime), &i);
121+ static const fs::path module = fs::absolute (modulepath ());
98122 // first candidate is resolved in relative data path
99- strncpy (fpb, i.dli_fname , PATH_MAX );
100- string d1 = string (dirname (fpb)) + " /" + runtimerelpath;
101- if (isdir (d1))
123+ const fs::path d1 = module .parent_path () / runtimerelpath;
124+ if (fs::is_directory (d1))
102125 {
103- librt = realpath (d1.c_str (), fpb);
104- did_librt = true ;
105- return librt;
126+ return fs::canonical (d1).generic_string ();
106127 }
107128 // second candidate is resolved with respect to physical library location
108129 // according to the source tree layout
109- string d2 = dirname ( realpath (i. dli_fname , fpb));
110- d2 += " / ../../src/runtime" ;
111- if (isdir (d2))
130+ const fs::path d2 = fs::canonical ( module ). parent_path () /
131+ " ../../src/runtime" ;
132+ if (fs::is_directory (d2))
112133 {
113- librt = realpath (d2.c_str (), fpb);
114- did_librt = true ;
115- return librt;
134+ return fs::canonical (d2).generic_string ();
116135 }
117136 // nothing worked - throw exception about the first candidate path.
118- ensureIsDir (d1);
119- return librt;
137+ return canonicalDirectory (d1);
120138}
121139
122140} // namespace
0 commit comments