hash.c: env encoding fallback on Windows
* hash.c (env_str_new, env_path_str_new): make default string UTF-8 for the case conversion is not possible. [Bug #8822] * hash.c (get_env_cstr): convert non-ASCII string to UTF-8 string. * hash.c (ruby_setenv): use wide char version to put environment variable to deal with non-ASCII value. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52896 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
613737eee9
commit
5e3467c441
10
ChangeLog
10
ChangeLog
@ -1,3 +1,13 @@
|
|||||||
|
Sat Dec 5 17:26:24 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||||
|
|
||||||
|
* hash.c (env_str_new, env_path_str_new): make default string
|
||||||
|
UTF-8 for the case conversion is not possible. [Bug #8822]
|
||||||
|
|
||||||
|
* hash.c (get_env_cstr): convert non-ASCII string to UTF-8 string.
|
||||||
|
|
||||||
|
* hash.c (ruby_setenv): use wide char version to put environment
|
||||||
|
variable to deal with non-ASCII value.
|
||||||
|
|
||||||
Sat Dec 5 09:56:50 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
Sat Dec 5 09:56:50 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||||
|
|
||||||
* ruby_atomic.h (ATOMIC_CAS): old value to be swapped should be
|
* ruby_atomic.h (ATOMIC_CAS): old value to be swapped should be
|
||||||
|
80
hash.c
80
hash.c
@ -2875,7 +2875,7 @@ extern char **environ;
|
|||||||
static VALUE
|
static VALUE
|
||||||
env_str_transcode(VALUE str, rb_encoding *enc)
|
env_str_transcode(VALUE str, rb_encoding *enc)
|
||||||
{
|
{
|
||||||
return rb_str_conv_enc_opts(str, rb_utf8_encoding(), enc,
|
return rb_str_conv_enc_opts(str, NULL, enc,
|
||||||
ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE, Qnil);
|
ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE, Qnil);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -2884,7 +2884,7 @@ static VALUE
|
|||||||
env_str_new(const char *ptr, long len)
|
env_str_new(const char *ptr, long len)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
VALUE str = env_str_transcode(rb_str_new(ptr, len), rb_locale_encoding());
|
VALUE str = env_str_transcode(rb_utf8_str_new(ptr, len), rb_locale_encoding());
|
||||||
#else
|
#else
|
||||||
VALUE str = rb_locale_str_new(ptr, len);
|
VALUE str = rb_locale_str_new(ptr, len);
|
||||||
#endif
|
#endif
|
||||||
@ -2897,7 +2897,7 @@ static VALUE
|
|||||||
env_path_str_new(const char *ptr)
|
env_path_str_new(const char *ptr)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
VALUE str = env_str_transcode(rb_str_new_cstr(ptr), rb_filesystem_encoding());
|
VALUE str = env_str_transcode(rb_utf8_str_new_cstr(ptr), rb_filesystem_encoding());
|
||||||
#else
|
#else
|
||||||
VALUE str = rb_filesystem_str_new_cstr(ptr);
|
VALUE str = rb_filesystem_str_new_cstr(ptr);
|
||||||
#endif
|
#endif
|
||||||
@ -2914,14 +2914,28 @@ env_str_new2(const char *ptr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
get_env_cstr(VALUE str, const char *name)
|
get_env_cstr(
|
||||||
|
#ifdef _WIN32
|
||||||
|
volatile VALUE *pstr,
|
||||||
|
#else
|
||||||
|
VALUE str,
|
||||||
|
#endif
|
||||||
|
const char *name)
|
||||||
{
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
VALUE str = *pstr;
|
||||||
|
#endif
|
||||||
char *var;
|
char *var;
|
||||||
rb_encoding *enc = rb_enc_get(str);
|
rb_encoding *enc = rb_enc_get(str);
|
||||||
if (!rb_enc_asciicompat(enc)) {
|
if (!rb_enc_asciicompat(enc)) {
|
||||||
rb_raise(rb_eArgError, "bad environment variable %s: ASCII incompatible encoding: %s",
|
rb_raise(rb_eArgError, "bad environment variable %s: ASCII incompatible encoding: %s",
|
||||||
name, rb_enc_name(enc));
|
name, rb_enc_name(enc));
|
||||||
}
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (!rb_enc_str_asciionly_p(str)) {
|
||||||
|
*pstr = str = rb_str_conv_enc(str, NULL, rb_utf8_encoding());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
var = RSTRING_PTR(str);
|
var = RSTRING_PTR(str);
|
||||||
if (memchr(var, '\0', RSTRING_LEN(str))) {
|
if (memchr(var, '\0', RSTRING_LEN(str))) {
|
||||||
rb_raise(rb_eArgError, "bad environment variable %s: contains null byte", name);
|
rb_raise(rb_eArgError, "bad environment variable %s: contains null byte", name);
|
||||||
@ -2929,8 +2943,13 @@ get_env_cstr(VALUE str, const char *name)
|
|||||||
return var;
|
return var;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define get_env_ptr(var, val) \
|
||||||
|
(var = get_env_cstr(&(val), #var))
|
||||||
|
#else
|
||||||
#define get_env_ptr(var, val) \
|
#define get_env_ptr(var, val) \
|
||||||
(var = get_env_cstr(val, #var))
|
(var = get_env_cstr(val, #var))
|
||||||
|
#endif
|
||||||
|
|
||||||
static inline const char *
|
static inline const char *
|
||||||
env_name(volatile VALUE *s)
|
env_name(volatile VALUE *s)
|
||||||
@ -3024,7 +3043,7 @@ rb_f_getenv(VALUE obj, VALUE name)
|
|||||||
static VALUE
|
static VALUE
|
||||||
env_fetch(int argc, VALUE *argv)
|
env_fetch(int argc, VALUE *argv)
|
||||||
{
|
{
|
||||||
VALUE key;
|
VALUE key, name;
|
||||||
long block_given;
|
long block_given;
|
||||||
const char *nam, *env;
|
const char *nam, *env;
|
||||||
|
|
||||||
@ -3034,7 +3053,8 @@ env_fetch(int argc, VALUE *argv)
|
|||||||
if (block_given && argc == 2) {
|
if (block_given && argc == 2) {
|
||||||
rb_warn("block supersedes default value argument");
|
rb_warn("block supersedes default value argument");
|
||||||
}
|
}
|
||||||
nam = env_name(key);
|
name = key;
|
||||||
|
nam = env_name(name);
|
||||||
env = getenv(nam);
|
env = getenv(nam);
|
||||||
if (!env) {
|
if (!env) {
|
||||||
if (block_given) return rb_yield(key);
|
if (block_given) return rb_yield(key);
|
||||||
@ -3102,10 +3122,10 @@ envix(const char *nam)
|
|||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
static size_t
|
static size_t
|
||||||
getenvsize(const char* p)
|
getenvsize(const WCHAR* p)
|
||||||
{
|
{
|
||||||
const char* porg = p;
|
const WCHAR* porg = p;
|
||||||
while (*p++) p += strlen(p) + 1;
|
while (*p++) p += lstrlenW(p) + 1;
|
||||||
return p - porg + 1;
|
return p - porg + 1;
|
||||||
}
|
}
|
||||||
static size_t
|
static size_t
|
||||||
@ -3140,27 +3160,52 @@ void
|
|||||||
ruby_setenv(const char *name, const char *value)
|
ruby_setenv(const char *name, const char *value)
|
||||||
{
|
{
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
|
# if defined(MINGW_HAS_SECURE_API) || RUBY_MSVCRT_VERSION >= 80
|
||||||
|
# define HAVE__WPUTENV_S 1
|
||||||
|
# endif
|
||||||
VALUE buf;
|
VALUE buf;
|
||||||
|
WCHAR *wname;
|
||||||
|
WCHAR *wvalue = 0;
|
||||||
int failed = 0;
|
int failed = 0;
|
||||||
|
int len;
|
||||||
check_envname(name);
|
check_envname(name);
|
||||||
|
len = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0);
|
||||||
if (value) {
|
if (value) {
|
||||||
char* p = GetEnvironmentStringsA();
|
WCHAR* p = GetEnvironmentStringsW();
|
||||||
size_t n;
|
size_t n;
|
||||||
|
int len2;
|
||||||
if (!p) goto fail; /* never happen */
|
if (!p) goto fail; /* never happen */
|
||||||
n = strlen(name) + 2 + strlen(value) + getenvsize(p);
|
n = lstrlen(name) + 2 + strlen(value) + getenvsize(p);
|
||||||
FreeEnvironmentStringsA(p);
|
FreeEnvironmentStringsW(p);
|
||||||
if (n >= getenvblocksize()) {
|
if (n >= getenvblocksize()) {
|
||||||
goto fail; /* 2 for '=' & '\0' */
|
goto fail; /* 2 for '=' & '\0' */
|
||||||
}
|
}
|
||||||
buf = rb_sprintf("%s=%s", name, value);
|
len2 = MultiByteToWideChar(CP_UTF8, 0, value, -1, NULL, 0);
|
||||||
|
wname = ALLOCV_N(WCHAR, buf, len + len2);
|
||||||
|
wvalue = wname + len;
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, len);
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, value, -1, wvalue, len2);
|
||||||
|
#ifndef HAVE__WPUTENV_S
|
||||||
|
wname[len-1] = L'=';
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
buf = rb_sprintf("%s=", name);
|
wname = ALLOCV_N(WCHAR, buf, len + 1);
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, len);
|
||||||
|
wvalue = wname + len;
|
||||||
|
*wvalue = L'\0';
|
||||||
|
#ifndef HAVE__WPUTENV_S
|
||||||
|
wname[len-1] = L'=';
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
failed = putenv(RSTRING_PTR(buf));
|
#ifndef HAVE__WPUTENV_S
|
||||||
|
failed = _wputenv(wname);
|
||||||
|
#else
|
||||||
|
failed = _wputenv_s(wname, wvalue);
|
||||||
|
#endif
|
||||||
|
ALLOCV_END(buf);
|
||||||
/* even if putenv() failed, clean up and try to delete the
|
/* even if putenv() failed, clean up and try to delete the
|
||||||
* variable from the system area. */
|
* variable from the system area. */
|
||||||
rb_str_resize(buf, 0);
|
|
||||||
if (!value || !*value) {
|
if (!value || !*value) {
|
||||||
/* putenv() doesn't handle empty value */
|
/* putenv() doesn't handle empty value */
|
||||||
if (!SetEnvironmentVariable(name, value) &&
|
if (!SetEnvironmentVariable(name, value) &&
|
||||||
@ -3797,8 +3842,9 @@ static VALUE
|
|||||||
env_assoc(VALUE env, VALUE key)
|
env_assoc(VALUE env, VALUE key)
|
||||||
{
|
{
|
||||||
const char *s, *e;
|
const char *s, *e;
|
||||||
|
VALUE name = key;
|
||||||
|
|
||||||
s = env_name(key);
|
s = env_name(name);
|
||||||
e = getenv(s);
|
e = getenv(s);
|
||||||
if (e) return rb_assoc_new(key, rb_tainted_str_new2(e));
|
if (e) return rb_assoc_new(key, rb_tainted_str_new2(e));
|
||||||
return Qnil;
|
return Qnil;
|
||||||
|
@ -482,5 +482,16 @@ class TestEnv < Test::Unit::TestCase
|
|||||||
500.times(&doit)
|
500.times(&doit)
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if Encoding.find("locale") == Encoding::UTF_8
|
||||||
|
def test_utf8
|
||||||
|
text = "testing åáâäãāあ"
|
||||||
|
test = ENV["test"]
|
||||||
|
ENV["test"] = text
|
||||||
|
assert_equal text, ENV["test"]
|
||||||
|
ensure
|
||||||
|
ENV["test"] = test
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user