diff options
Diffstat (limited to 'erts/emulator/sys/win32/sys_env.c')
-rw-r--r-- | erts/emulator/sys/win32/sys_env.c | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/erts/emulator/sys/win32/sys_env.c b/erts/emulator/sys/win32/sys_env.c new file mode 100644 index 0000000000..ac4be3f316 --- /dev/null +++ b/erts/emulator/sys/win32/sys_env.c @@ -0,0 +1,261 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2002-2009. 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; + + erts_smp_rwmtx_rlock(&environ_rwmtx); + SetLastError(0); + new_size = GetEnvironmentVariable((LPCTSTR) key, + (LPTSTR) value, + (DWORD) *size); + res = !new_size && GetLastError() == ERROR_ENVVAR_NOT_FOUND ? -1 : 0; + erts_smp_rwmtx_runlock(&environ_rwmtx); + if (res < 0) + return res; + res = new_size > *size ? 1 : 0; + *size = new_size; + 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]; + + if ((tmp = find_arg(c_arg, current)) != NULL) { + if (current[strlen(current)-1] != '=') { + *tmp = current; + } else { + *tmp = c_arg[--i]; + c_arg[i] = NULL; + } + } else if (current[strlen(current)-1] != '=') { + 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; +} |