[cmake-developers] [PATCH v5] SystemTools: Teach GetEnv to use correct encoding on Windows
Dāvis
davispuh at gmail.com
Wed Jul 13 22:17:00 EDT 2016
From: Dāvis Mosāns <davispuh at gmail.com>
On Windows getenv (and putenv) uses ANSI codepage so it needs to be encoded
to internally used encoding (eg. UTF-8). Here we use _wgetenv (and _wputenv)
instead and encode that.
Change-Id: I8cb91f2386eb0efe3ef0a3132d1603217d710b60
---
SystemTools.cxx | 190 +++++++++++++++++++++++++++++++++++++++--------------
SystemTools.hxx.in | 2 +
2 files changed, 143 insertions(+), 49 deletions(-)
diff --git a/SystemTools.cxx b/SystemTools.cxx
index db4d7af..79d3b96 100644
--- a/SystemTools.cxx
+++ b/SystemTools.cxx
@@ -388,6 +388,64 @@ class SystemToolsTranslationMap :
{
};
+/* Order by environment key only (VAR from VAR=VALUE). */
+#if defined(_WIN32)
+typedef wchar_t envchar;
+struct kwsysEnvCompare
+{
+ bool operator() (const wchar_t* l, const wchar_t* r) const
+ {
+ const wchar_t* leq = wcschr(l, '=');
+ const wchar_t* req = wcschr(r, '=');
+ size_t llen = leq? (leq-l) : wcslen(l);
+ size_t rlen = req? (req-r) : wcslen(r);
+ if(llen == rlen)
+ {
+ return wcsncmp(l,r,llen) < 0;
+ }
+ else
+ {
+ return wcscmp(l,r) < 0;
+ }
+ }
+};
+#else
+typedef char envchar;
+struct kwsysEnvCompare
+{
+ bool operator() (const char* l, const char* r) const
+ {
+ const char* leq = strchr(l, '=');
+ const char* req = strchr(r, '=');
+ size_t llen = leq? (leq-l) : strlen(l);
+ size_t rlen = req? (req-r) : strlen(r);
+ if(llen == rlen)
+ {
+ return strncmp(l,r,llen) < 0;
+ }
+ else
+ {
+ return strcmp(l,r) < 0;
+ }
+ }
+};
+#endif
+
+typedef std::set<const envchar*, kwsysEnvCompare> kwsysEnvSetType;
+
+class kwsysUnPutEnv :
+ public kwsysEnvSetType
+{
+ public:
+ ~kwsysUnPutEnv() {
+ for(kwsysEnvSetType::iterator i = this->begin(); i != this->end(); ++i) {
+ free(const_cast<envchar*>(*i));
+ }
+ }
+};
+
+static kwsysUnPutEnv kwsysUnPutEnvInstance;
+
#ifdef _WIN32
struct SystemToolsPathCaseCmp
{
@@ -406,6 +464,9 @@ struct SystemToolsPathCaseCmp
class SystemToolsPathCaseMap:
public std::map<std::string, std::string,
SystemToolsPathCaseCmp> {};
+
+class SystemToolsEnvMap :
+ public std::map<std::string,std::string> {};
#endif
// adds the elements of the env variable path to the arg passed in
@@ -458,7 +519,18 @@ void SystemTools::GetPath(std::vector<std::string>& path, const char* env)
const char* SystemTools::GetEnv(const char* key)
{
- return getenv(key);
+ const char *v = 0;
+#if defined(_WIN32)
+ std::string env;
+ if (SystemTools::GetEnv(key, env)) {
+ std::string& menv = (*EnvMap)[key];
+ menv = env;
+ v = menv.c_str();
+ }
+#else
+ v = getenv(key);
+#endif
+ return v;
}
const char* SystemTools::GetEnv(const std::string& key)
@@ -468,16 +540,21 @@ const char* SystemTools::GetEnv(const std::string& key)
bool SystemTools::GetEnv(const char* key, std::string& result)
{
+#if defined(_WIN32)
+ const std::wstring wkey = Encoding::ToWide(key);
+ const wchar_t* wv = _wgetenv(wkey.c_str());
+ if (wv) {
+ result = Encoding::ToNarrow(wv);
+ return true;
+ }
+#else
const char* v = getenv(key);
- if(v)
- {
+ if (v) {
result = v;
return true;
- }
- else
- {
- return false;
- }
+ }
+#endif
+ return false;
}
bool SystemTools::GetEnv(const std::string& key, std::string& result)
@@ -534,6 +611,7 @@ static int kwsysUnPutEnv(const std::string& env)
int err = 0;
size_t pos = env.find('=');
size_t const len = pos == env.npos ? env.size() : pos;
+ pos = len;
# ifdef KWSYS_PUTENV_EMPTY
size_t const sz = len + 2;
# else
@@ -547,19 +625,31 @@ static int kwsysUnPutEnv(const std::string& env)
}
strncpy(buf, env.c_str(), len);
# ifdef KWSYS_PUTENV_EMPTY
- buf[len] = '=';
- buf[len+1] = 0;
- if(putenv(buf) < 0)
- {
- err = errno;
- }
+ buf[pos] = '=';
+ pos++;
+# endif
+ buf[pos] = 0;
+# if defined(_WIN32)
+ const std::wstring wbuf = Encoding::ToWide(buf);
+ wchar_t *newbuf = wcsdup(wbuf.c_str());
+ std::pair<kwsysEnvSetType::iterator,bool> val = kwsysUnPutEnvInstance.insert(newbuf);
+ const int r = _wputenv(*val.first);
# else
- buf[len] = 0;
- if(putenv(buf) < 0 && errno != EINVAL)
+ char* newbuf = strdup(buf);
+ std::pair<kwsysEnvSetType::iterator,bool> val = kwsysUnPutEnvInstance.insert(newbuf);
+ const int r = putenv(*val.first);
+# endif
+ if (!val.second) {
+ free(newbuf);
+ }
+# ifdef KWSYS_PUTENV_EMPTY
+ if(r < 0)
+# else
+ if(r < 0 && errno != EINVAL)
+# endif
{
err = errno;
}
-# endif
if(buf != local_buf)
{
free(buf);
@@ -639,49 +729,33 @@ bool SystemTools::UnPutEnv(const std::string& env)
# pragma warning disable 444 /* base has non-virtual destructor */
# endif
-/* Order by environment key only (VAR from VAR=VALUE). */
-struct kwsysEnvCompare
-{
- bool operator() (const char* l, const char* r) const
- {
- const char* leq = strchr(l, '=');
- const char* req = strchr(r, '=');
- size_t llen = leq? (leq-l) : strlen(l);
- size_t rlen = req? (req-r) : strlen(r);
- if(llen == rlen)
- {
- return strncmp(l,r,llen) < 0;
- }
- else
- {
- return strcmp(l,r) < 0;
- }
- }
-};
-
-class kwsysEnv: public std::set<const char*, kwsysEnvCompare>
+class kwsysEnv: public kwsysEnvSetType
{
class Free
{
- const char* Env;
+ const envchar* Env;
public:
- Free(const char* env): Env(env) {}
- ~Free() { free(const_cast<char*>(this->Env)); }
+ Free(const envchar* env): Env(env) {}
+ ~Free() { free(const_cast<envchar*>(this->Env)); }
};
public:
- typedef std::set<const char*, kwsysEnvCompare> derived;
~kwsysEnv()
{
- for(derived::iterator i = this->begin(); i != this->end(); ++i)
+ for(kwsysEnvSetType::iterator i = this->begin(); i != this->end(); ++i)
{
+#if defined(_WIN32)
+ const std::string s = Encoding::ToNarrow(*i);
+ kwsysUnPutEnv(s.c_str());
+#else
kwsysUnPutEnv(*i);
- free(const_cast<char*>(*i));
+#endif
+ free(const_cast<envchar*>(*i));
}
}
- const char* Release(const char* env)
+ const envchar* Release(const envchar* env)
{
- const char* old = 0;
- derived::iterator i = this->find(env);
+ const envchar* old = 0;
+ kwsysEnvSetType::iterator i = this->find(env);
if(i != this->end())
{
old = *i;
@@ -691,15 +765,30 @@ public:
}
bool Put(const char* env)
{
- Free oldEnv(this->Release(env));
- static_cast<void>(oldEnv);
+#if defined(_WIN32)
+ const std::wstring wEnv = Encoding::ToWide(env);
+ wchar_t* newEnv = wcsdup(wEnv.c_str());
+#else
char* newEnv = strdup(env);
+#endif
+ Free oldEnv(this->Release(newEnv));
+ static_cast<void>(oldEnv);
+#if defined(_WIN32)
+ this->insert(newEnv);
+ return _wputenv(newEnv) == 0;
+#else
this->insert(newEnv);
return putenv(newEnv) == 0;
+#endif
}
bool UnPut(const char* env)
{
+#if defined(_WIN32)
+ const std::wstring wEnv = Encoding::ToWide(env);
+ Free oldEnv(this->Release(wEnv.c_str()));
+#else
Free oldEnv(this->Release(env));
+#endif
static_cast<void>(oldEnv);
return kwsysUnPutEnv(env) == 0;
}
@@ -5387,6 +5476,7 @@ static unsigned int SystemToolsManagerCount;
SystemToolsTranslationMap *SystemTools::TranslationMap;
#ifdef _WIN32
SystemToolsPathCaseMap *SystemTools::PathCaseMap;
+SystemToolsEnvMap *SystemTools::EnvMap;
#endif
#ifdef __CYGWIN__
SystemToolsTranslationMap *SystemTools::Cyg2Win32Map;
@@ -5437,6 +5527,7 @@ void SystemTools::ClassInitialize()
SystemTools::TranslationMap = new SystemToolsTranslationMap;
#ifdef _WIN32
SystemTools::PathCaseMap = new SystemToolsPathCaseMap;
+ SystemTools::EnvMap = new SystemToolsEnvMap;
#endif
#ifdef __CYGWIN__
SystemTools::Cyg2Win32Map = new SystemToolsTranslationMap;
@@ -5496,6 +5587,7 @@ void SystemTools::ClassFinalize()
delete SystemTools::TranslationMap;
#ifdef _WIN32
delete SystemTools::PathCaseMap;
+ delete SystemTools::EnvMap;
#endif
#ifdef __CYGWIN__
delete SystemTools::Cyg2Win32Map;
diff --git a/SystemTools.hxx.in b/SystemTools.hxx.in
index c9b18b7..8f01e75 100644
--- a/SystemTools.hxx.in
+++ b/SystemTools.hxx.in
@@ -53,6 +53,7 @@ namespace @KWSYS_NAMESPACE@
class SystemToolsTranslationMap;
class SystemToolsPathCaseMap;
+class SystemToolsEnvMap;
/** \class SystemToolsManager
* \brief Use to make sure SystemTools is initialized before it is used
@@ -991,6 +992,7 @@ private:
static SystemToolsTranslationMap *TranslationMap;
#ifdef _WIN32
static SystemToolsPathCaseMap *PathCaseMap;
+ static SystemToolsEnvMap *EnvMap;
#endif
#ifdef __CYGWIN__
static SystemToolsTranslationMap *Cyg2Win32Map;
--
2.9.0
More information about the cmake-developers
mailing list