aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/emulator/beam/break.c2
-rw-r--r--erts/emulator/beam/dist.c20
-rwxr-xr-xerts/emulator/beam/erl_bif_info.c2
-rw-r--r--erts/emulator/beam/erl_bif_os.c106
-rw-r--r--erts/emulator/beam/erl_bif_port.c16
-rw-r--r--erts/emulator/beam/erl_init.c6
-rw-r--r--erts/emulator/beam/erl_unicode.c104
-rwxr-xr-xerts/emulator/beam/global.h8
-rw-r--r--erts/emulator/beam/io.c6
-rw-r--r--erts/emulator/beam/sys.h7
-rw-r--r--erts/emulator/beam/utils.c24
-rw-r--r--erts/emulator/sys/unix/sys.c41
-rw-r--r--erts/emulator/sys/win32/erl_win_sys.h4
-rwxr-xr-xerts/emulator/sys/win32/sys.c10
-rw-r--r--erts/emulator/sys/win32/sys_env.c570
-rw-r--r--erts/etc/common/erlexec.c171
-rw-r--r--erts/preloaded/ebin/init.beambin48064 -> 48292 bytes
-rw-r--r--erts/preloaded/src/init.erl6
-rw-r--r--lib/kernel/test/file_name_SUITE.erl52
19 files changed, 738 insertions, 417 deletions
diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c
index 6f5020dc14..93aa2fb8d0 100644
--- a/erts/emulator/beam/break.c
+++ b/erts/emulator/beam/break.c
@@ -678,7 +678,7 @@ erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
erts_sys_prepare_crash_dump();
- if (erts_sys_getenv("ERL_CRASH_DUMP",&dumpnamebuf[0],&dumpnamebufsize) != 0)
+ if (erts_sys_getenv_raw("ERL_CRASH_DUMP",&dumpnamebuf[0],&dumpnamebufsize) != 0)
dumpname = "erl_crash.dump";
else
dumpname = &dumpnamebuf[0];
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index 01fd63d5d9..025258e8de 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -968,16 +968,16 @@ erts_dsig_send_group_leader(ErtsDSigData *dsdp, Eterm leader, Eterm remote)
#define VALGRIND_PRINTF_XML VALGRIND_PRINTF
#endif
-# define PURIFY_MSG(msg) \
- do { \
- char buf__[1]; size_t bufsz__ = sizeof(buf__); \
- if (erts_sys_getenv("VALGRIND_LOG_XML", buf__, &bufsz__) >= 0) { \
- VALGRIND_PRINTF_XML("<erlang_error_log>" \
- "%s, line %d: %s</erlang_error_log>\n", \
- __FILE__, __LINE__, msg); \
- } else { \
- VALGRIND_PRINTF("%s, line %d: %s", __FILE__, __LINE__, msg); \
- } \
+# define PURIFY_MSG(msg) \
+ do { \
+ char buf__[1]; size_t bufsz__ = sizeof(buf__); \
+ if (erts_sys_getenv_raw("VALGRIND_LOG_XML", buf__, &bufsz__) >= 0) { \
+ VALGRIND_PRINTF_XML("<erlang_error_log>" \
+ "%s, line %d: %s</erlang_error_log>\n", \
+ __FILE__, __LINE__, msg); \
+ } else { \
+ VALGRIND_PRINTF("%s, line %d: %s", __FILE__, __LINE__, msg); \
+ } \
} while (0)
#else
# define PURIFY_MSG(msg)
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index f56b00287f..45dc5fb11c 100755
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -1697,7 +1697,7 @@ static int check_if_xml(void)
{
char buf[1];
size_t bufsz = sizeof(buf);
- return erts_sys_getenv("VALGRIND_LOG_XML", buf, &bufsz) >= 0;
+ return erts_sys_getenv_raw("VALGRIND_LOG_XML", buf, &bufsz) >= 0;
}
#else
#define check_if_xml() 0
diff --git a/erts/emulator/beam/erl_bif_os.c b/erts/emulator/beam/erl_bif_os.c
index 58d48199fa..d076640a8b 100644
--- a/erts/emulator/beam/erl_bif_os.c
+++ b/erts/emulator/beam/erl_bif_os.c
@@ -71,15 +71,13 @@ BIF_RETTYPE os_getenv_0(BIF_ALIST_0)
Eterm* hp;
Eterm ret;
Eterm str;
- int len;
init_getenv_state(&state);
ret = NIL;
while ((cp = getenv_string(&state)) != NULL) {
- len = strlen(cp);
- hp = HAlloc(BIF_P, len*2+2);
- str = buf_to_intlist(&hp, cp, len, NIL);
+ str = erts_convert_native_to_filename(BIF_P,(byte *)cp);
+ hp = HAlloc(BIF_P, 2);
ret = CONS(hp, str, ret);
}
@@ -88,32 +86,30 @@ BIF_RETTYPE os_getenv_0(BIF_ALIST_0)
return ret;
}
-
+#define STATIC_BUF_SIZE 1024
BIF_RETTYPE os_getenv_1(BIF_ALIST_1)
{
Process* p = BIF_P;
- Eterm key = BIF_ARG_1;
Eterm str;
- int len, res;
+ Sint len;
+ int res;
char *key_str, *val;
- char buf[1024];
+ char buf[STATIC_BUF_SIZE];
size_t val_size = sizeof(buf);
- len = is_string(key);
- if (!len) {
+ key_str = erts_convert_filename_to_native(BIF_ARG_1,buf,STATIC_BUF_SIZE,
+ ERTS_ALC_T_TMP,1,0,&len);
+
+ if (!key_str) {
BIF_ERROR(p, BADARG);
}
- /* Leave at least one byte in buf for value */
- key_str = len < sizeof(buf)-2 ? &buf[0] : erts_alloc(ERTS_ALC_T_TMP, len+1);
- if (intlist_to_buf(key, key_str, len) != len)
- erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__);
- key_str[len] = '\0';
if (key_str != &buf[0])
val = &buf[0];
else {
- val_size -= len + 1;
- val = &buf[len + 1];
+ /* len includes zero byte */
+ val_size -= len;
+ val = &buf[len];
}
res = erts_sys_getenv(key_str, val, &val_size);
@@ -121,7 +117,6 @@ BIF_RETTYPE os_getenv_1(BIF_ALIST_1)
no_var:
str = am_false;
} else {
- Eterm* hp;
if (res > 0) {
val = erts_alloc(ERTS_ALC_T_TMP, val_size);
while (1) {
@@ -134,9 +129,7 @@ BIF_RETTYPE os_getenv_1(BIF_ALIST_1)
val = erts_realloc(ERTS_ALC_T_TMP, val, val_size);
}
}
- if (val_size)
- hp = HAlloc(p, val_size*2);
- str = buf_to_intlist(&hp, val, val_size, NIL);
+ str = erts_convert_native_to_filename(p,(byte *)val);
}
if (key_str != &buf[0])
erts_free(ERTS_ALC_T_TMP, key_str);
@@ -147,46 +140,43 @@ BIF_RETTYPE os_getenv_1(BIF_ALIST_1)
BIF_RETTYPE os_putenv_2(BIF_ALIST_2)
{
- Process* p = BIF_P;
- Eterm key = BIF_ARG_1;
- Eterm value = BIF_ARG_2;
- char def_buf[1024];
- char *buf = NULL;
- int sep_ix, i, key_len, value_len, tot_len;
- key_len = is_string(key);
- if (!key_len) {
- error:
- if (buf)
- erts_free(ERTS_ALC_T_TMP, (void *) buf);
- BIF_ERROR(p, BADARG);
+ char def_buf_key[STATIC_BUF_SIZE];
+ char def_buf_value[STATIC_BUF_SIZE];
+ char *key_buf, *value_buf;
+
+ key_buf = erts_convert_filename_to_native(BIF_ARG_1,def_buf_key,
+ STATIC_BUF_SIZE,
+ ERTS_ALC_T_TMP,0,0,NULL);
+ if (!key_buf) {
+ BIF_ERROR(BIF_P, BADARG);
}
- if (is_nil(value))
- value_len = 0;
- else {
- value_len = is_string(value);
- if (!value_len)
- goto error;
+ value_buf = erts_convert_filename_to_native(BIF_ARG_2,def_buf_value,
+ STATIC_BUF_SIZE,
+ ERTS_ALC_T_TMP,1,0,
+ NULL);
+ if (!value_buf) {
+ if (key_buf != def_buf_key) {
+ erts_free(ERTS_ALC_T_TMP, key_buf);
+ }
+ BIF_ERROR(BIF_P, BADARG);
+ }
+
+
+ if (erts_sys_putenv(key_buf, value_buf)) {
+ if (key_buf != def_buf_key) {
+ erts_free(ERTS_ALC_T_TMP, key_buf);
+ }
+ if (value_buf != def_buf_value) {
+ erts_free(ERTS_ALC_T_TMP, value_buf);
+ }
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ if (key_buf != def_buf_key) {
+ erts_free(ERTS_ALC_T_TMP, key_buf);
}
- tot_len = key_len + 1 + value_len + 1;
- if (tot_len <= sizeof(def_buf))
- buf = &def_buf[0];
- else
- buf = erts_alloc(ERTS_ALC_T_TMP, tot_len);
- i = intlist_to_buf(key, buf, key_len);
- if (i != key_len)
- erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__);
- sep_ix = i;
- buf[i++] = '=';
- if (is_not_nil(value))
- i += intlist_to_buf(value, &buf[i], value_len);
- if (i != key_len + 1 + value_len)
- erl_exit(1, "%s:%d: Internal error\n", __FILE__, __LINE__);
- buf[i] = '\0';
- if (erts_sys_putenv(buf, sep_ix)) {
- goto error;
+ if (value_buf != def_buf_value) {
+ erts_free(ERTS_ALC_T_TMP, value_buf);
}
- if (buf != &def_buf[0])
- erts_free(ERTS_ALC_T_TMP, (void *) buf);
BIF_RET(am_true);
}
diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c
index 2a1b01b107..f9009166c0 100644
--- a/erts/emulator/beam/erl_bif_port.c
+++ b/erts/emulator/beam/erl_bif_port.c
@@ -718,7 +718,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump)
} else if (option == am_arg0) {
char *a0;
- if ((a0 = erts_convert_filename_to_native(*tp, ERTS_ALC_T_TMP, 1)) == NULL) {
+ if ((a0 = erts_convert_filename_to_native(*tp, NULL, 0, ERTS_ALC_T_TMP, 1, 1, NULL)) == NULL) {
goto badarg;
}
if (opts.argv == NULL) {
@@ -845,7 +845,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump)
goto badarg;
}
name = tp[1];
- if ((name_buf = erts_convert_filename_to_native(name,ERTS_ALC_T_TMP,0)) == NULL) {
+ if ((name_buf = erts_convert_filename_to_native(name, NULL, 0, ERTS_ALC_T_TMP,0,1, NULL)) == NULL) {
goto badarg;
}
opts.spawn_type = ERTS_SPAWN_EXECUTABLE;
@@ -909,7 +909,7 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump)
}
opts.wd = (char *) dir;
} else {
- if ((opts.wd = erts_convert_filename_to_native(edir,ERTS_ALC_T_TMP,0)) == NULL) {
+ if ((opts.wd = erts_convert_filename_to_native(edir, NULL, 0, ERTS_ALC_T_TMP,0,1,NULL)) == NULL) {
goto badarg;
}
}
@@ -1000,7 +1000,7 @@ static char **convert_args(Eterm l)
pp[i++] = erts_default_arg0;
while (is_list(l)) {
str = CAR(list_val(l));
- if ((b = erts_convert_filename_to_native(str,ERTS_ALC_T_TMP,1)) == NULL) {
+ if ((b = erts_convert_filename_to_native(str,NULL,0,ERTS_ALC_T_TMP,1,1,NULL)) == NULL) {
int j;
for (j = 1; j < i; ++j)
erts_free(ERTS_ALC_T_TMP, pp[j]);
@@ -1035,8 +1035,9 @@ static byte* convert_environment(Process* p, Eterm env)
Eterm* hp;
Uint heap_size;
int n;
- Uint size;
+ Sint size;
byte* bytes;
+ int encoding = erts_get_native_filename_encoding();
if ((n = list_length(env)) < 0) {
return NULL;
@@ -1079,7 +1080,8 @@ static byte* convert_environment(Process* p, Eterm env)
if (is_not_nil(env)) {
goto done;
}
- if (erts_iolist_size(all, &size)) {
+
+ if ((size = erts_native_filename_need(all,encoding)) < 0) {
goto done;
}
@@ -1087,7 +1089,7 @@ static byte* convert_environment(Process* p, Eterm env)
* Put the result in a binary (no risk for a memory leak that way).
*/
(void) erts_new_heap_binary(p, NULL, size, &bytes);
- io_list_to_buf(all, (char*)bytes, size);
+ erts_native_filename_put(all,encoding,bytes);
done:
erts_free(ERTS_ALC_T_TMP, temp_heap);
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 02164728fe..1eb3dba240 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -623,7 +623,7 @@ early_init(int *argc, char **argv) /*
envbufsz = sizeof(envbuf);
- /* erts_sys_getenv() not initialized yet; need erts_sys_getenv__() */
+ /* erts_sys_getenv(_raw)() not initialized yet; need erts_sys_getenv__() */
if (erts_sys_getenv__("ERL_THREAD_POOL_SIZE", envbuf, &envbufsz) == 0)
erts_async_max_threads = atoi(envbuf);
else
@@ -846,13 +846,13 @@ erl_start(int argc, char **argv)
int ncpu = early_init(&argc, argv);
envbufsz = sizeof(envbuf);
- if (erts_sys_getenv(ERL_MAX_ETS_TABLES_ENV, envbuf, &envbufsz) == 0)
+ if (erts_sys_getenv_raw(ERL_MAX_ETS_TABLES_ENV, envbuf, &envbufsz) == 0)
user_requested_db_max_tabs = atoi(envbuf);
else
user_requested_db_max_tabs = 0;
envbufsz = sizeof(envbuf);
- if (erts_sys_getenv("ERL_FULLSWEEP_AFTER", envbuf, &envbufsz) == 0) {
+ if (erts_sys_getenv_raw("ERL_FULLSWEEP_AFTER", envbuf, &envbufsz) == 0) {
Uint16 max_gen_gcs = atoi(envbuf);
erts_smp_atomic32_set_nob(&erts_max_gen_gcs,
(erts_aint32_t) max_gen_gcs);
diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c
index 6d5eae73b0..127db4d4f6 100644
--- a/erts/emulator/beam/erl_unicode.c
+++ b/erts/emulator/beam/erl_unicode.c
@@ -2027,12 +2027,14 @@ BIF_RETTYPE binary_to_existing_atom_2(BIF_ALIST_2)
* string routines, that will certainly fail on some OS.
*/
-char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int allow_empty)
+char *erts_convert_filename_to_native(Eterm name, char *statbuf, size_t statbuf_size, ErtsAlcType_t alloc_type, int allow_empty, int allow_atom, Sint *used)
{
int encoding = erts_get_native_filename_encoding();
char* name_buf = NULL;
- if (is_atom(name) || is_list(name) || (allow_empty && is_nil(name))) {
+ if ((allow_atom && is_atom(name)) ||
+ is_list(name) ||
+ (allow_empty && is_nil(name))) {
Sint need;
if ((need = erts_native_filename_need(name,encoding)) < 0) {
return NULL;
@@ -2042,7 +2044,13 @@ char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int
} else {
++need;
}
- name_buf = (char *) erts_alloc(alloc_type, need);
+ if (used)
+ *used = (Sint) need;
+ if (need > statbuf_size) {
+ name_buf = (char *) erts_alloc(alloc_type, need);
+ } else {
+ name_buf = statbuf;
+ }
erts_native_filename_put(name,encoding,(byte *)name_buf);
name_buf[need-1] = 0;
if (encoding == ERL_FILENAME_WIN_WCHAR) {
@@ -2058,14 +2066,26 @@ char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int
bytes = erts_get_aligned_binary_bytes(name, &temp_alloc);
if (encoding != ERL_FILENAME_WIN_WCHAR) {
/*Add 0 termination only*/
- name_buf = (char *) erts_alloc(alloc_type, size+1);
+ if (used)
+ *used = (Sint) size+1;
+ if (size+1 > statbuf_size) {
+ name_buf = (char *) erts_alloc(alloc_type, size+1);
+ } else {
+ name_buf = statbuf;
+ }
memcpy(name_buf,bytes,size);
name_buf[size]=0;
} else if (erts_analyze_utf8(bytes,size,&err_pos,&num_chars,NULL) != ERTS_UTF8_OK ||
erts_get_user_requested_filename_encoding() == ERL_FILENAME_LATIN1) {
byte *p;
/* What to do now? Maybe latin1, so just take byte for byte instead */
- name_buf = (char *) erts_alloc(alloc_type, (size+1)*2);
+ if (used)
+ *used = (Sint) (size+1)*2;
+ if ((size+1)*2 > statbuf_size) {
+ name_buf = (char *) erts_alloc(alloc_type, (size+1)*2);
+ } else {
+ name_buf = statbuf;
+ }
p = (byte *) name_buf;
while (size--) {
*p++ = *bytes++;
@@ -2074,7 +2094,13 @@ char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int
*p++ = 0;
*p++ = 0;
} else { /* WIN_WCHAR and valid UTF8 */
- name_buf = (char *) erts_alloc(alloc_type, (num_chars+1)*2);
+ if (used)
+ *used = (Sint) (num_chars+1)*2;
+ if ((num_chars+1)*2 > statbuf_size) {
+ name_buf = (char *) erts_alloc(alloc_type, (num_chars+1)*2);
+ } else {
+ name_buf = statbuf;
+ }
erts_copy_utf8_to_utf16_little((byte *) name_buf, bytes, num_chars);
name_buf[num_chars*2] = 0;
name_buf[num_chars*2+1] = 0;
@@ -2086,6 +2112,71 @@ char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int
return name_buf;
}
+static int filename_len_16bit(byte *str)
+{
+ byte *p = str;
+ while(*p != '\0' || p[1] != '\0') {
+ p += 2;
+ }
+ return (p - str);
+}
+Eterm erts_convert_native_to_filename(Process *p, byte *bytes)
+{
+ Uint size,num_chars;
+ Eterm *hp;
+ byte *err_pos;
+ Uint num_built; /* characters */
+ Uint num_eaten; /* bytes */
+ Eterm ret;
+ int mac = 0;
+
+ switch (erts_get_native_filename_encoding()) {
+ case ERL_FILENAME_LATIN1:
+ goto noconvert;
+ case ERL_FILENAME_UTF8_MAC:
+ mac = 1;
+ case ERL_FILENAME_UTF8:
+ size = strlen((char *) bytes);
+ if (erts_analyze_utf8(bytes,size,&err_pos,&num_chars,NULL) != ERTS_UTF8_OK) {
+ goto noconvert;
+ }
+ num_built = 0;
+ num_eaten = 0;
+ if (mac) {
+ ret = do_utf8_to_list_normalize(p, num_chars, bytes, size);
+ } else {
+ ret = do_utf8_to_list(p, num_chars, bytes, size, num_chars, &num_built, &num_eaten, NIL);
+ }
+ return ret;
+ case ERL_FILENAME_WIN_WCHAR:
+ size=filename_len_16bit(bytes);
+ if ((size % 2) != 0) { /* Panic fixup to avoid crashing the emulator */
+ size--;
+ hp = HAlloc(p, size+2);
+ ret = CONS(hp,make_small((Uint) bytes[size]),NIL);
+ hp += 2;
+ } else {
+ hp = HAlloc(p, size);
+ ret = NIL;
+ }
+ bytes += size-1;
+ while (size > 0) {
+ Uint x = ((Uint) *bytes--) << 8;
+ x |= ((Uint) *bytes--);
+ size -= 2;
+ ret = CONS(hp,make_small(x),ret);
+ hp += 2;
+ }
+ return ret;
+ default:
+ goto noconvert;
+ }
+ noconvert:
+ size = strlen((char *) bytes);
+ hp = HAlloc(p, 2 * size);
+ return erts_bin_bytes_to_list(NIL, hp, bytes, size, 0);
+}
+
Sint erts_native_filename_need(Eterm ioterm, int encoding)
{
@@ -2619,3 +2710,4 @@ BIF_RETTYPE file_native_name_encoding_0(BIF_ALIST_0)
BIF_RET(am_undefined);
}
}
+
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 1b15c4ac3b..dbf95f5bd7 100755
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -1386,8 +1386,12 @@ Sint erts_native_filename_need(Eterm ioterm, int encoding);
void erts_copy_utf8_to_utf16_little(byte *target, byte *bytes, int num_chars);
int erts_analyze_utf8(byte *source, Uint size,
byte **err_pos, Uint *num_chars, int *left);
-char *erts_convert_filename_to_native(Eterm name, ErtsAlcType_t alloc_type, int allow_empty);
-
+char *erts_convert_filename_to_native(Eterm name, char *statbuf,
+ size_t statbuf_size,
+ ErtsAlcType_t alloc_type,
+ int allow_empty, int allow_atom,
+ Sint *used /* out */);
+Eterm erts_convert_native_to_filename(Process *p, byte *bytes);
#define ERTS_UTF8_OK 0
#define ERTS_UTF8_INCOMPLETE 1
#define ERTS_UTF8_ERROR 2
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index 204bff299e..35b194f927 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -1314,7 +1314,7 @@ void init_io(void)
pdl_init();
- if (erts_sys_getenv("ERL_MAX_PORTS", maxports, &maxportssize) == 0)
+ if (erts_sys_getenv_raw("ERL_MAX_PORTS", maxports, &maxportssize) == 0)
erts_max_ports = atoi(maxports);
else
erts_max_ports = sys_max_files();
@@ -5227,11 +5227,11 @@ int null_func(void)
int
erl_drv_putenv(char *key, char *value)
{
- return erts_write_env(key, value);
+ return erts_sys_putenv_raw(key, value);
}
int
erl_drv_getenv(char *key, char *value, size_t *value_size)
{
- return erts_sys_getenv(key, value, value_size);
+ return erts_sys_getenv_raw(key, value, value_size);
}
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index 7b2bb81f62..2406c52f14 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -703,18 +703,21 @@ int sys_double_to_chars(double, char*);
void sys_get_pid(char *);
/* erts_sys_putenv() returns, 0 on success and a value != 0 on failure. */
-int erts_sys_putenv(char *key_value, int sep_ix);
+int erts_sys_putenv(char *key, char *value);
+/* Simple variant used from drivers, raw eightbit interface */
+int erts_sys_putenv_raw(char *key, char *value);
/* erts_sys_getenv() returns 0 on success (length of value string in
*size), a value > 0 if value buffer is too small (*size is set to needed
size), and a value < 0 on failure. */
int erts_sys_getenv(char *key, char *value, size_t *size);
+/* Simple variant used from drivers, raw eightbit interface */
+int erts_sys_getenv_raw(char *key, char *value, size_t *size);
/* erts_sys_getenv__() is only allowed to be used in early init phase */
int erts_sys_getenv__(char *key, char *value, size_t *size);
/* Easier to use, but not as efficient, environment functions */
char *erts_read_env(char *key);
void erts_free_read_env(void *value);
-int erts_write_env(char *key, char *value);
/* utils.c */
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index a36d15204e..bd708ceee6 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -3409,7 +3409,7 @@ erts_read_env(char *key)
char *value = erts_alloc(ERTS_ALC_T_TMP, value_len);
int res;
while (1) {
- res = erts_sys_getenv(key, value, &value_len);
+ res = erts_sys_getenv_raw(key, value, &value_len);
if (res <= 0)
break;
value = erts_realloc(ERTS_ALC_T_TMP, value, value_len);
@@ -3428,28 +3428,6 @@ erts_free_read_env(void *value)
erts_free(ERTS_ALC_T_TMP, value);
}
-int
-erts_write_env(char *key, char *value)
-{
- int ix, res;
- size_t key_len = sys_strlen(key), value_len = sys_strlen(value);
- char *key_value = erts_alloc_fnf(ERTS_ALC_T_TMP,
- key_len + 1 + value_len + 1);
- if (!key_value) {
- errno = ENOMEM;
- return -1;
- }
- sys_memcpy((void *) key_value, (void *) key, key_len);
- ix = key_len;
- key_value[ix++] = '=';
- sys_memcpy((void *) key_value, (void *) value, value_len);
- ix += value_len;
- key_value[ix] = '\0';
- res = erts_sys_putenv(key_value, key_len);
- erts_free(ERTS_ALC_T_TMP, key_value);
- return res;
-}
-
/*
* To be used to silence unused result warnings, but do not abuse it.
*/
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index bf69f3bf90..0e8395ea8a 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -551,7 +551,7 @@ erl_sys_init(void)
size_t bindirsz = sizeof(bindir);
Uint csp_path_sz;
- res = erts_sys_getenv("BINDIR", bindir, &bindirsz);
+ res = erts_sys_getenv_raw("BINDIR", bindir, &bindirsz);
if (res != 0) {
if (res < 0)
erl_exit(-1,
@@ -708,7 +708,7 @@ prepare_crash_dump(void)
}
envsz = sizeof(env);
- i = erts_sys_getenv("ERL_CRASH_DUMP_NICE", env, &envsz);
+ i = erts_sys_getenv_raw("ERL_CRASH_DUMP_NICE", env, &envsz);
if (i >= 0) {
int nice_val;
nice_val = i != 0 ? 0 : atoi(env);
@@ -719,7 +719,7 @@ prepare_crash_dump(void)
}
envsz = sizeof(env);
- i = erts_sys_getenv("ERL_CRASH_DUMP_SECONDS", env, &envsz);
+ i = erts_sys_getenv_raw("ERL_CRASH_DUMP_SECONDS", env, &envsz);
if (i >= 0) {
unsigned sec;
sec = (unsigned) i != 0 ? 0 : atoi(env);
@@ -1356,9 +1356,9 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* op
int no_vfork;
size_t no_vfork_sz = sizeof(no_vfork);
- no_vfork = (erts_sys_getenv("ERL_NO_VFORK",
- (char *) &no_vfork,
- &no_vfork_sz) >= 0);
+ no_vfork = (erts_sys_getenv_raw("ERL_NO_VFORK",
+ (char *) &no_vfork,
+ &no_vfork_sz) >= 0);
#endif
switch (opts->read_write) {
@@ -2362,21 +2362,31 @@ void sys_get_pid(char *buffer){
}
int
-erts_sys_putenv(char *buffer, int sep_ix)
+erts_sys_putenv_raw(char *key, char *value) {
+ return erts_sys_putenv(key, value);
+}
+int
+erts_sys_putenv(char *key, char *value)
{
int res;
char *env;
+ Uint need = strlen(key) + strlen(value) + 2;
+
#ifdef HAVE_COPYING_PUTENV
- env = buffer;
+ env = erts_alloc(ERTS_ALC_T_TMP, need);
#else
- Uint sz = strlen(buffer)+1;
- env = erts_alloc(ERTS_ALC_T_PUTENV_STR, sz);
- erts_smp_atomic_add_nob(&sys_misc_mem_sz, sz);
- strcpy(env,buffer);
+ env = erts_alloc(ERTS_ALC_T_PUTENV_STR, need);
+ erts_smp_atomic_add_nob(&sys_misc_mem_sz, need);
#endif
+ strcpy(env,key);
+ strcat(env,"=");
+ strcat(env,value);
erts_smp_rwmtx_rwlock(&environ_rwmtx);
res = putenv(env);
erts_smp_rwmtx_rwunlock(&environ_rwmtx);
+#ifdef HAVE_COPYING_PUTENV
+ erts_free(ERTS_ALC_T_TMP, env);
+#endif
return res;
}
@@ -2403,6 +2413,11 @@ erts_sys_getenv__(char *key, char *value, size_t *size)
}
int
+erts_sys_getenv_raw(char *key, char *value, size_t *size) {
+ return erts_sys_getenv(key, value, size);
+}
+
+int
erts_sys_getenv(char *key, char *value, size_t *size)
{
int res;
@@ -3001,7 +3016,7 @@ erl_sys_args(int* argc, char** argv)
if (erts_use_kernel_poll) {
char no_kp[10];
size_t no_kp_sz = sizeof(no_kp);
- int res = erts_sys_getenv("ERL_NO_KERNEL_POLL", no_kp, &no_kp_sz);
+ int res = erts_sys_getenv_raw("ERL_NO_KERNEL_POLL", no_kp, &no_kp_sz);
if (res > 0
|| (res == 0
&& sys_strcmp("false", no_kp) != 0
diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h
index 03298a6c54..1e7b555f42 100644
--- a/erts/emulator/sys/win32/erl_win_sys.h
+++ b/erts/emulator/sys/win32/erl_win_sys.h
@@ -171,8 +171,8 @@ extern clock_t sys_times(SysTimes *buffer);
extern char *win_build_environment(char *);
typedef struct {
- char *environment_strings;
- char *next_string;
+ WCHAR *environment_strings;
+ WCHAR *next_string;
} GETENV_STATE;
void erts_sys_env_init(void);
diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c
index acbbfc2ce9..29e2749800 100755
--- a/erts/emulator/sys/win32/sys.c
+++ b/erts/emulator/sys/win32/sys.c
@@ -1204,9 +1204,7 @@ spawn_start(ErlDrvPort port_num, char* name, SysDriverOpts* opts)
*/
DEBUGF(("Spawning \"%s\"\n", name));
- envir = win_build_environment(envir); /* Still an ansi environment, could be
- converted to unicode for spawn_executable, but
- that is not done (yet) */
+ envir = win_build_environment(envir); /* Always a unicode environment */
ok = create_child_process(name,
hChildStdin,
hChildStdout,
@@ -1485,7 +1483,8 @@ create_child_process
NULL,
NULL,
TRUE,
- createFlags | staticCreateFlags,
+ createFlags | staticCreateFlags |
+ CREATE_UNICODE_ENVIRONMENT,
env,
wd,
&siStartInfo,
@@ -1613,7 +1612,8 @@ create_child_process
NULL,
NULL,
TRUE,
- createFlags | staticCreateFlags,
+ createFlags | staticCreateFlags |
+ CREATE_UNICODE_ENVIRONMENT,
env,
(WCHAR *) wd,
&siStartInfo,
diff --git a/erts/emulator/sys/win32/sys_env.c b/erts/emulator/sys/win32/sys_env.c
index 064745d418..658a77c824 100644
--- a/erts/emulator/sys/win32/sys_env.c
+++ b/erts/emulator/sys/win32/sys_env.c
@@ -1,271 +1,299 @@
-/*
- * %CopyrightBegin%
- *
- * Copyright Ericsson AB 2002-2011. All Rights Reserved.
- *
- * The contents of this file are subject to the Erlang Public License,
- * Version 1.1, (the "License"); you may not use this file except in
- * compliance with the License. You should have received a copy of the
- * Erlang Public License along with this software. If not, it can be
- * retrieved online at http://www.erlang.org/.
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and limitations
- * under the License.
- *
- * %CopyrightEnd%
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "sys.h"
-#include "erl_sys_driver.h"
-#include "erl_alloc.h"
-
-static char* merge_environment(char *current, char *add);
-static char* arg_to_env(char **arg);
-static char** env_to_arg(char *env);
-static char** find_arg(char **arg, char *str);
-static int compare(const void *a, const void *b);
-
-static erts_smp_rwmtx_t environ_rwmtx;
-
-void
-erts_sys_env_init(void)
-{
- erts_smp_rwmtx_init(&environ_rwmtx, "environ");
-}
-
-int
-erts_sys_putenv(char *key_value, int sep_ix)
-{
- int res;
- char sep = key_value[sep_ix];
- ASSERT(sep == '=');
- key_value[sep_ix] = '\0';
- erts_smp_rwmtx_rwlock(&environ_rwmtx);
- res = (SetEnvironmentVariable((LPCTSTR) key_value,
- (LPCTSTR) &key_value[sep_ix+1]) ? 0 : 1);
- erts_smp_rwmtx_rwunlock(&environ_rwmtx);
- key_value[sep_ix] = sep;
- return res;
-}
-
-int
-erts_sys_getenv__(char *key, char *value, size_t *size)
-{
- size_t req_size = 0;
- int res = 0;
- DWORD new_size;
-
- SetLastError(0);
- new_size = GetEnvironmentVariable((LPCTSTR) key,
- (LPTSTR) value,
- (DWORD) *size);
- res = !new_size && GetLastError() == ERROR_ENVVAR_NOT_FOUND ? -1 : 0;
- if (res < 0)
- return res;
- res = new_size > *size ? 1 : 0;
- *size = new_size;
- return res;
-}
-
-int
-erts_sys_getenv(char *key, char *value, size_t *size)
-{
- int res;
- erts_smp_rwmtx_rlock(&environ_rwmtx);
- res = erts_sys_getenv__(key, value, size);
- erts_smp_rwmtx_runlock(&environ_rwmtx);
- return res;
-}
-
-struct win32_getenv_state {
- char *env;
- char *next;
-};
-
-
-void init_getenv_state(GETENV_STATE *state)
-{
- erts_smp_rwmtx_rlock(&environ_rwmtx);
- state->environment_strings = (char *) GetEnvironmentStrings();
- state->next_string = state->environment_strings;
-}
-
-char *getenv_string(GETENV_STATE *state)
-{
- ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rlocked(&environ_rwmtx));
- if (state->next_string[0] == '\0')
- return NULL;
- else {
- char *res = state->next_string;
- state->next_string += sys_strlen(res) + 1;
- return res;
- }
-}
-
-void fini_getenv_state(GETENV_STATE *state)
-{
- FreeEnvironmentStrings(state->environment_strings);
- state->environment_strings = state->next_string = NULL;
- erts_smp_rwmtx_runlock(&environ_rwmtx);
-}
-
-char*
-win_build_environment(char* new_env)
-{
- if (new_env == NULL) {
- return NULL;
- } else {
- char *tmp, *merged;
-
- erts_smp_rwmtx_rlock(&environ_rwmtx);
- tmp = GetEnvironmentStrings();
- merged = merge_environment(tmp, new_env);
-
- FreeEnvironmentStrings(tmp);
- erts_smp_rwmtx_runlock(&environ_rwmtx);
- return merged;
- }
-}
-
-static char*
-merge_environment(char *old, char *add)
-{
- char **a_arg = env_to_arg(add);
- char **c_arg = env_to_arg(old);
- char *ret;
- int i, j;
-
- for(i = 0; c_arg[i] != NULL; ++i)
- ;
-
- for(j = 0; a_arg[j] != NULL; ++j)
- ;
-
- c_arg = erts_realloc(ERTS_ALC_T_TMP,
- c_arg, (i+j+1) * sizeof(char *));
-
- for(j = 0; a_arg[j] != NULL; ++j){
- char **tmp;
- char *current = a_arg[j];
- char *eq_p = strchr(current,'=');
- int unset = (eq_p!=NULL && eq_p[1]=='\0');
-
- if ((tmp = find_arg(c_arg, current)) != NULL) {
- if (!unset) {
- *tmp = current;
- } else {
- *tmp = c_arg[--i];
- c_arg[i] = NULL;
- }
- } else if (!unset) {
- c_arg[i++] = current;
- c_arg[i] = NULL;
- }
- }
- ret = arg_to_env(c_arg);
- erts_free(ERTS_ALC_T_TMP, c_arg);
- erts_free(ERTS_ALC_T_TMP, a_arg);
- return ret;
-}
-
-static char**
-find_arg(char **arg, char *str)
-{
- char *tmp;
- int len;
-
- if ((tmp = strchr(str, '=')) != NULL) {
- tmp++;
- len = tmp - str;
- while (*arg != NULL){
- if (_strnicmp(*arg, str, len) == 0){
- return arg;
- }
- ++arg;
- }
- }
- return NULL;
-}
-
-static int
-compare(const void *a, const void *b)
-{
- char *s1 = *((char **) a);
- char *s2 = *((char **) b);
- char *e1 = strchr(s1,'=');
- char *e2 = strchr(s2,'=');
- int ret;
- int len;
-
- if(!e1)
- e1 = s1 + strlen(s1);
- if(!e2)
- e2 = s2 + strlen(s2);
-
- if((e1 - s1) > (e2 - s2))
- len = (e2 - s2);
- else
- len = (e1 - s1);
-
- ret = _strnicmp(s1,s2,len);
- if (ret == 0)
- return ((e1 - s1) - (e2 - s2));
- else
- return ret;
-}
-
-static char**
-env_to_arg(char *env)
-{
- char **ret;
- char *tmp;
- int i;
- int num_strings = 0;
-
- for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1) {
- ++num_strings;
- }
- ret = erts_alloc(ERTS_ALC_T_TMP, sizeof(char *) * (num_strings + 1));
- i = 0;
- for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1){
- ret[i++] = tmp;
- }
- ret[i] = NULL;
- return ret;
-}
-
-static char*
-arg_to_env(char **arg)
-{
- char *block;
- char *ptr;
- int i;
- int totlen = 1; /* extra '\0' */
-
- for(i = 0; arg[i] != NULL; ++i) {
- totlen += strlen(arg[i])+1;
- }
-
- /* sort the environment vector */
- qsort(arg, i, sizeof(char *), &compare);
-
- if (totlen == 1){
- block = erts_alloc(ERTS_ALC_T_ENVIRONMENT, 2);
- block[0] = block[1] = '\0';
- } else {
- block = erts_alloc(ERTS_ALC_T_ENVIRONMENT, totlen);
- ptr = block;
- for(i=0; arg[i] != NULL; ++i){
- strcpy(ptr, arg[i]);
- ptr += strlen(ptr)+1;
- }
- *ptr = '\0';
- }
- return block;
-}
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2002-2011. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_sys_driver.h"
+#include "erl_alloc.h"
+
+static WCHAR *merge_environment(WCHAR *current, WCHAR *add);
+static WCHAR *arg_to_env(WCHAR **arg);
+static WCHAR **env_to_arg(WCHAR *env);
+static WCHAR **find_arg(WCHAR **arg, WCHAR *str);
+static int compare(const void *a, const void *b);
+
+static erts_smp_rwmtx_t environ_rwmtx;
+
+void
+erts_sys_env_init(void)
+{
+ erts_smp_rwmtx_init(&environ_rwmtx, "environ");
+}
+
+int
+erts_sys_putenv_raw(char *key, char *value)
+{
+ int res;
+ erts_smp_rwmtx_rwlock(&environ_rwmtx);
+ res = (SetEnvironmentVariable((LPCTSTR) key,
+ (LPCTSTR) value) ? 0 : 1);
+ erts_smp_rwmtx_rwunlock(&environ_rwmtx);
+ return res;
+}
+
+int
+erts_sys_putenv(char *key, char *value)
+{
+ int res;
+ WCHAR *wkey = (WCHAR *) key;
+ WCHAR *wvalue = (WCHAR *) value;
+ erts_smp_rwmtx_rwlock(&environ_rwmtx);
+ res = (SetEnvironmentVariableW(wkey,
+ wvalue) ? 0 : 1);
+ erts_smp_rwmtx_rwunlock(&environ_rwmtx);
+ return res;
+}
+
+int
+erts_sys_getenv(char *key, char *value, size_t *size)
+{
+ size_t req_size = 0;
+ int res = 0;
+ DWORD new_size;
+ WCHAR *wkey = (WCHAR *) key;
+ WCHAR *wvalue = (WCHAR *) value;
+ DWORD wsize = *size / (sizeof(WCHAR) / sizeof(char));
+
+ SetLastError(0);
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ new_size = GetEnvironmentVariableW(wkey,
+ wvalue,
+ (DWORD) wsize);
+ res = !new_size && GetLastError() == ERROR_ENVVAR_NOT_FOUND ? -1 : 0;
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ if (res < 0)
+ return res;
+ res = new_size > wsize ? 1 : 0;
+ *size = new_size * (sizeof(WCHAR) / sizeof(char));
+ return res;
+}
+int
+erts_sys_getenv__(char *key, char *value, size_t *size)
+{
+ size_t req_size = 0;
+ int res = 0;
+ DWORD new_size;
+
+ SetLastError(0);
+ new_size = GetEnvironmentVariable((LPCTSTR) key,
+ (LPTSTR) value,
+ (DWORD) *size);
+ res = !new_size && GetLastError() == ERROR_ENVVAR_NOT_FOUND ? -1 : 0;
+ if (res < 0)
+ return res;
+ res = new_size > *size ? 1 : 0;
+ *size = new_size;
+ return res;
+}
+
+int
+erts_sys_getenv_raw(char *key, char *value, size_t *size)
+{
+ int res;
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ res = erts_sys_getenv__(key, value, size);
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ return res;
+}
+
+void init_getenv_state(GETENV_STATE *state)
+{
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ state->environment_strings = GetEnvironmentStringsW();
+ state->next_string = state->environment_strings;
+}
+
+char *getenv_string(GETENV_STATE *state)
+{
+ ERTS_SMP_LC_ASSERT(erts_smp_lc_rwmtx_is_rlocked(&environ_rwmtx));
+ if (state->next_string[0] == L'\0') {
+ return NULL;
+ } else {
+ WCHAR *res = state->next_string;
+ state->next_string += wcslen(res) + 1;
+ return (char *) res;
+ }
+}
+
+void fini_getenv_state(GETENV_STATE *state)
+{
+ FreeEnvironmentStringsW(state->environment_strings);
+ state->environment_strings = state->next_string = NULL;
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+}
+
+char*
+win_build_environment(char* new_env)
+{
+ if (new_env == NULL) {
+ return NULL;
+ } else {
+ WCHAR *tmp, *merged, *tmp_new;
+
+ tmp_new = (WCHAR *) new_env;
+
+ erts_smp_rwmtx_rlock(&environ_rwmtx);
+ tmp = GetEnvironmentStringsW();
+ merged = merge_environment(tmp, tmp_new);
+
+ FreeEnvironmentStringsW(tmp);
+ erts_smp_rwmtx_runlock(&environ_rwmtx);
+ return (char *) merged;
+ }
+}
+
+static WCHAR *
+merge_environment(WCHAR *old, WCHAR *add)
+{
+ WCHAR **a_arg = env_to_arg(add);
+ WCHAR **c_arg = env_to_arg(old);
+ WCHAR *ret;
+ int i, j;
+
+ for(i = 0; c_arg[i] != NULL; ++i)
+ ;
+
+ for(j = 0; a_arg[j] != NULL; ++j)
+ ;
+
+ c_arg = erts_realloc(ERTS_ALC_T_TMP,
+ c_arg, (i+j+1) * sizeof(WCHAR *));
+
+ for(j = 0; a_arg[j] != NULL; ++j){
+ WCHAR **tmp;
+ WCHAR *current = a_arg[j];
+ WCHAR *eq_p = wcschr(current,L'=');
+ int unset = (eq_p!=NULL && eq_p[1]==L'\0');
+
+ if ((tmp = find_arg(c_arg, current)) != NULL) {
+ if (!unset) {
+ *tmp = current;
+ } else {
+ *tmp = c_arg[--i];
+ c_arg[i] = NULL;
+ }
+ } else if (!unset) {
+ c_arg[i++] = current;
+ c_arg[i] = NULL;
+ }
+ }
+ ret = arg_to_env(c_arg);
+ erts_free(ERTS_ALC_T_TMP, c_arg);
+ erts_free(ERTS_ALC_T_TMP, a_arg);
+ return ret;
+}
+
+static WCHAR**
+find_arg(WCHAR **arg, WCHAR *str)
+{
+ WCHAR *tmp;
+ int len;
+
+ if ((tmp = wcschr(str, L'=')) != NULL) {
+ tmp++;
+ len = tmp - str;
+ while (*arg != NULL){
+ if (_wcsnicmp(*arg, str, len) == 0){
+ return arg;
+ }
+ ++arg;
+ }
+ }
+ return NULL;
+}
+
+static int
+compare(const void *a, const void *b)
+{
+ WCHAR *s1 = *((WCHAR **) a);
+ WCHAR *s2 = *((WCHAR **) b);
+ WCHAR *e1 = wcschr(s1,L'=');
+ WCHAR *e2 = wcschr(s2,L'=');
+ int ret;
+ int len;
+
+ if(!e1)
+ e1 = s1 + wcslen(s1);
+ if(!e2)
+ e2 = s2 + wcslen(s2);
+
+ if((e1 - s1) > (e2 - s2))
+ len = (e2 - s2);
+ else
+ len = (e1 - s1);
+
+ ret = _wcsnicmp(s1,s2,len);
+ if (ret == 0)
+ return ((e1 - s1) - (e2 - s2));
+ else
+ return ret;
+}
+
+static WCHAR**
+env_to_arg(WCHAR *env)
+{
+ WCHAR **ret;
+ WCHAR *tmp;
+ int i;
+ int num_strings = 0;
+
+ for(tmp = env; *tmp != '\0'; tmp += wcslen(tmp)+1) {
+ ++num_strings;
+ }
+ ret = erts_alloc(ERTS_ALC_T_TMP, sizeof(WCHAR *) * (num_strings + 1));
+ i = 0;
+ for(tmp = env; *tmp != '\0'; tmp += wcslen(tmp)+1){
+ ret[i++] = tmp;
+ }
+ ret[i] = NULL;
+ return ret;
+}
+
+static WCHAR *
+arg_to_env(WCHAR **arg)
+{
+ WCHAR *block;
+ WCHAR *ptr;
+ int i;
+ int totlen = 1; /* extra '\0' */
+
+ for(i = 0; arg[i] != NULL; ++i) {
+ totlen += wcslen(arg[i])+1;
+ }
+
+ /* sort the environment vector */
+ qsort(arg, i, sizeof(WCHAR *), &compare);
+
+ if (totlen == 1){
+ block = erts_alloc(ERTS_ALC_T_ENVIRONMENT, 2 * sizeof(WCHAR));
+ block[0] = block[1] = '\0';
+ } else {
+ block = erts_alloc(ERTS_ALC_T_ENVIRONMENT, totlen * sizeof(WCHAR));
+ ptr = block;
+ for(i=0; arg[i] != NULL; ++i){
+ wcscpy(ptr, arg[i]);
+ ptr += wcslen(ptr)+1;
+ }
+ *ptr = '\0';
+ }
+ return block;
+}
diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c
index 790b0ed400..cba7429fab 100644
--- a/erts/etc/common/erlexec.c
+++ b/erts/etc/common/erlexec.c
@@ -245,7 +245,9 @@ static char* config_script = NULL; /* used by option -start_erl and -config */
static HANDLE this_module_handle;
static int run_werl;
-
+static WCHAR *utf8_to_utf16(unsigned char *bytes);
+static char *utf16_to_utf8(WCHAR *wstr);
+static WCHAR *latin1_to_utf16(char *str);
#endif
/*
@@ -263,8 +265,12 @@ static void
set_env(char *key, char *value)
{
#ifdef __WIN32__
- if (!SetEnvironmentVariable((LPCTSTR) key, (LPCTSTR) value))
+ WCHAR *wkey = latin1_to_utf16(key);
+ WCHAR *wvalue = utf8_to_utf16(value);
+ if (!SetEnvironmentVariableW(wkey, wvalue))
error("SetEnvironmentVariable(\"%s\", \"%s\") failed!", key, value);
+ efree(wkey);
+ efree(wvalue);
#else
size_t size = strlen(key) + 1 + strlen(value) + 1;
char *str = emalloc(size);
@@ -277,25 +283,33 @@ set_env(char *key, char *value)
#endif
}
+
static char *
get_env(char *key)
{
#ifdef __WIN32__
DWORD size = 32;
- char *value = NULL;
+ WCHAR *value = NULL;
+ WCHAR *wkey = latin1_to_utf16(key);
+ char *res;
while (1) {
DWORD nsz;
if (value)
efree(value);
- value = emalloc(size);
+ value = emalloc(size*sizeof(WCHAR));
SetLastError(0);
- nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size);
+ nsz = GetEnvironmentVariableW(wkey, value, size);
if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
efree(value);
+ efree(wkey);
return NULL;
}
- if (nsz <= size)
- return value;
+ if (nsz <= size) {
+ efree(wkey);
+ res = utf16_to_utf8(value);
+ efree(value);
+ return res;
+ }
size = nsz;
}
#else
@@ -2080,4 +2094,147 @@ possibly_quote(char* arg)
return narg;
}
+/*
+ * Unicode helpers to handle environment and command line parameters on
+ * Windows. We internally handle all environment variables in UTF8,
+ * but put and get the environment using the WCHAR (limited UTF16) interface
+ *
+ * These are simplified to only handle Unicode characters that can fit in
+ * Windows simplified UTF16, i.e. characters that fit in 16 bits.
+ */
+
+static int utf8_len(unsigned char first)
+{
+ if ((first & ((unsigned char) 0x80)) == 0) {
+ return 1;
+ } else if ((first & ((unsigned char) 0xE0)) == 0xC0) {
+ return 2;
+ } else if ((first & ((unsigned char) 0xF0)) == 0xE0) {
+ return 3;
+ } else if ((first & ((unsigned char) 0xF8)) == 0xF0) {
+ return 4;
+ }
+ return 1; /* will be a '?' */
+}
+
+static WCHAR *utf8_to_utf16(unsigned char *bytes)
+{
+ unsigned int unipoint;
+ unsigned char *tmp = bytes;
+ WCHAR *target, *res;
+ int num = 0;
+
+ while (*tmp) {
+ num++;
+ tmp += utf8_len(*tmp);
+ }
+ res = target = emalloc((num + 1) * sizeof(WCHAR));
+ while (*bytes) {
+ if (((*bytes) & ((unsigned char) 0x80)) == 0) {
+ unipoint = (Uint) *bytes;
+ ++bytes;
+ } else if (((*bytes) & ((unsigned char) 0xE0)) == 0xC0) {
+ unipoint =
+ (((Uint) ((*bytes) & ((unsigned char) 0x1F))) << 6) |
+ ((Uint) (bytes[1] & ((unsigned char) 0x3F)));
+ bytes += 2;
+ } else if (((*bytes) & ((unsigned char) 0xF0)) == 0xE0) {
+ unipoint =
+ (((Uint) ((*bytes) & ((unsigned char) 0xF))) << 12) |
+ (((Uint) (bytes[1] & ((unsigned char) 0x3F))) << 6) |
+ ((Uint) (bytes[2] & ((unsigned char) 0x3F)));
+ if (unipoint > 0xFFFF) {
+ unipoint = (unsigned int) '?';
+ }
+ bytes +=3;
+ } else if (((*bytes) & ((unsigned char) 0xF8)) == 0xF0) {
+ unipoint = (unsigned int) '?'; /* Cannot put in a wchar */
+ bytes += 4;
+ } else {
+ unipoint = (unsigned int) '?';
+ }
+ *target++ = (WCHAR) unipoint;
+ }
+ *target = L'\0';
+ return res;
+}
+
+static int put_utf8(WCHAR ch, unsigned char *target, int sz, int *pos)
+{
+ Uint x = (Uint) ch;
+ if (x < 0x80) {
+ if (*pos >= sz) {
+ return -1;
+ }
+ target[(*pos)++] = (unsigned char) x;
+ }
+ else if (x < 0x800) {
+ if (((*pos) + 1) >= sz) {
+ return -1;
+ }
+ target[(*pos)++] = (((unsigned char) (x >> 6)) |
+ ((unsigned char) 0xC0));
+ target[(*pos)++] = (((unsigned char) (x & 0x3F)) |
+ ((unsigned char) 0x80));
+ } else {
+ if ((x >= 0xD800 && x <= 0xDFFF) ||
+ (x == 0xFFFE) ||
+ (x == 0xFFFF)) { /* Invalid unicode range */
+ return -1;
+ }
+ if (((*pos) + 2) >= sz) {
+ return -1;
+ }
+
+ target[(*pos)++] = (((unsigned char) (x >> 12)) |
+ ((unsigned char) 0xE0));
+ target[(*pos)++] = ((((unsigned char) (x >> 6)) & 0x3F) |
+ ((unsigned char) 0x80));
+ target[(*pos)++] = (((unsigned char) (x & 0x3F)) |
+ ((unsigned char) 0x80));
+ }
+ return 0;
+}
+
+static int need_bytes_for_utf8(WCHAR x)
+{
+ if (x < 0x80)
+ return 1;
+ else if (x < 0x800)
+ return 2;
+ else
+ return 3;
+}
+
+static WCHAR *latin1_to_utf16(char *str)
+{
+ int len = strlen(str);
+ int i;
+ WCHAR *wstr = emalloc((len+1) * sizeof(WCHAR));
+ for(i=0;i<len;++i)
+ wstr[i] = (WCHAR) str[i];
+ wstr[len] = L'\0';
+ return wstr;
+}
+
+static char *utf16_to_utf8(WCHAR *wstr)
+{
+ int len = wcslen(wstr);
+ char *result;
+ int i,pos;
+ int reslen = 0;
+ for(i=0;i<len;++i) {
+ reslen += need_bytes_for_utf8(wstr[i]);
+ }
+ result = emalloc(reslen+1);
+ pos = 0;
+ for(i=0;i<len;++i) {
+ if (put_utf8((int) wstr[i], result, reslen, &pos) < 0) {
+ break;
+ }
+ }
+ result[pos] = '\0';
+ return result;
+}
+
#endif
diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam
index cc170b86b2..9f2d369af2 100644
--- a/erts/preloaded/ebin/init.beam
+++ b/erts/preloaded/ebin/init.beam
Binary files differ
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index c9c434dea0..708991e261 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -1252,7 +1252,11 @@ get_arguments([]) ->
[].
to_strings([H|T]) when is_atom(H) -> [atom_to_list(H)|to_strings(T)];
-to_strings([H|T]) when is_binary(H) -> [binary_to_list(H)|to_strings(T)];
+to_strings([H|T]) when is_binary(H) -> [try
+ unicode:characters_to_list(H,file:native_name_encoding())
+ catch
+ _:_ -> binary_to_list(H)
+ end|to_strings(T)];
to_strings([]) -> [].
get_argument(Arg,Flags) ->
diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl
index 53bcb1162d..be33ec2c06 100644
--- a/lib/kernel/test/file_name_SUITE.erl
+++ b/lib/kernel/test/file_name_SUITE.erl
@@ -74,7 +74,7 @@
init_per_suite/1,end_per_suite/1,
init_per_group/2,end_per_group/2,
init_per_testcase/2, end_per_testcase/2]).
--export([normal/1,icky/1,very_icky/1,normalize/1]).
+-export([normal/1,icky/1,very_icky/1,normalize/1,home_dir/1]).
init_per_testcase(_Func, Config) ->
@@ -88,7 +88,7 @@ end_per_testcase(_Func, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [normal, icky, very_icky, normalize].
+ [normal, icky, very_icky, normalize, home_dir].
groups() ->
[].
@@ -105,6 +105,54 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+home_dir(suite) ->
+ [];
+home_dir(doc) ->
+ ["Check that Erlang can be started with unicode named home directory"];
+home_dir(Config) when is_list(Config) ->
+ try
+ Name=[960,945,964,961,953,954],
+ Priv = ?config(priv_dir, Config),
+ UniMode = file:native_name_encoding() =/= latin1,
+ if
+ not UniMode ->
+ throw(need_unicode_mode);
+ true ->
+ ok
+ end,
+ NewHome=filename:join(Priv,Name),
+ file:make_dir(NewHome),
+ {SaveOldName,SaveOldValue} = case os:type() of
+ {win32,nt} ->
+ HomePath=re:replace(filename:nativename(NewHome),"^[a-zA-Z]:","",[{return,list},unicode]),
+ Save = os:getenv("HOMEPATH"),
+ os:putenv("HOMEPATH",HomePath),
+ {"HOMEPATH",Save};
+ {unix,_} ->
+ Save = os:getenv("HOME"),
+ os:putenv("HOME",NewHome),
+ {"HOME",Save};
+ _ ->
+ rm_rf(prim_file,NewHome),
+ throw(unsupported_os)
+ end,
+ try
+ {ok,Node} = test_server:start_node(test_unicode_homedir,slave,[{args,"-setcookie "++atom_to_list(erlang:get_cookie())}]),
+ test_server:stop_node(Node),
+ ok
+ after
+ os:putenv(SaveOldName,SaveOldValue),
+ rm_rf(prim_file,NewHome)
+ end
+ catch
+ throw:need_unicode_mode ->
+ io:format("Sorry, can only run in unicode mode.~n"),
+ {skipped,"VM needs to be started in Unicode filename mode"};
+ throw:unsupported_os ->
+ io:format("Sorry, can only run on Unix/Windows.~n"),
+ {skipped,"Runs only on Unix/Windows"}
+ end.
+
normalize(suite) ->
[];
normalize(doc) ->