[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