diff options
author | Patrik Nyblom <[email protected]> | 2012-07-26 18:35:46 +0200 |
---|---|---|
committer | Patrik Nyblom <[email protected]> | 2012-08-14 15:04:06 +0200 |
commit | e11bbb37273ebd2408d0c83c62770f9ef023879d (patch) | |
tree | 58ca0f2f6381e171c7c99ba3022a25c137f7b689 /erts | |
parent | 0c9d90f314f364e5b1301ec89d762baabc57c7aa (diff) | |
download | otp-e11bbb37273ebd2408d0c83c62770f9ef023879d.tar.gz otp-e11bbb37273ebd2408d0c83c62770f9ef023879d.tar.bz2 otp-e11bbb37273ebd2408d0c83c62770f9ef023879d.zip |
Make get/putenv and erlexec understand Unicode
Putenv and getenv needs to convert to the proper environment
strings in Unicode depending on platform and user settings for filename
encoding. Also erlexec needs to pass environment strings in an appropriate
way for kernel to pick up. All environment strings on the command
line, as well as home directory, is now passed in UTF8 on windows
and in whatever encoding you have on Unix, kernel tries to convert all
parameters and environments from UTF8 before making strings.
Diffstat (limited to 'erts')
-rw-r--r-- | erts/emulator/beam/break.c | 2 | ||||
-rw-r--r-- | erts/emulator/beam/dist.c | 20 | ||||
-rwxr-xr-x | erts/emulator/beam/erl_bif_info.c | 2 | ||||
-rw-r--r-- | erts/emulator/beam/erl_bif_os.c | 106 | ||||
-rw-r--r-- | erts/emulator/beam/erl_bif_port.c | 16 | ||||
-rw-r--r-- | erts/emulator/beam/erl_init.c | 6 | ||||
-rw-r--r-- | erts/emulator/beam/erl_unicode.c | 104 | ||||
-rwxr-xr-x | erts/emulator/beam/global.h | 8 | ||||
-rw-r--r-- | erts/emulator/beam/io.c | 6 | ||||
-rw-r--r-- | erts/emulator/beam/sys.h | 7 | ||||
-rw-r--r-- | erts/emulator/beam/utils.c | 24 | ||||
-rw-r--r-- | erts/emulator/sys/unix/sys.c | 41 | ||||
-rw-r--r-- | erts/emulator/sys/win32/erl_win_sys.h | 4 | ||||
-rwxr-xr-x | erts/emulator/sys/win32/sys.c | 10 | ||||
-rw-r--r-- | erts/emulator/sys/win32/sys_env.c | 570 | ||||
-rw-r--r-- | erts/etc/common/erlexec.c | 171 | ||||
-rw-r--r-- | erts/preloaded/ebin/init.beam | bin | 48064 -> 48292 bytes | |||
-rw-r--r-- | erts/preloaded/src/init.erl | 6 |
18 files changed, 688 insertions, 415 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 Binary files differindex cc170b86b2..9f2d369af2 100644 --- a/erts/preloaded/ebin/init.beam +++ b/erts/preloaded/ebin/init.beam 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) -> |