diff options
-rw-r--r-- | src/nvim/channel.c | 13 | ||||
-rw-r--r-- | src/nvim/eval.c | 38 | ||||
-rw-r--r-- | src/nvim/os/env.c | 152 |
3 files changed, 146 insertions, 57 deletions
diff --git a/src/nvim/channel.c b/src/nvim/channel.c index f8b480adcb..2bb568f025 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -272,15 +272,6 @@ static void close_cb(Stream *stream, void *data) channel_decref(data); } -static inline void free_env(char **env) -{ - if (!env) { return; } - for (char **it = env; *it; it++) { - XFREE_CLEAR(*it); - } - xfree(env); -} - /// Starts a job and returns the associated channel /// @@ -362,7 +353,7 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, if (status) { EMSG3(_(e_jobspawn), os_strerror(status), cmd); xfree(cmd); - free_env(proc->env); + os_free_fullenv(proc->env); if (proc->type == kProcessTypePty) { xfree(chan->stream.pty.term_name); } @@ -371,7 +362,7 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, return NULL; } xfree(cmd); - free_env(proc->env); + os_free_fullenv(proc->env); wstream_init(&proc->in, 0); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 00a0aecf4b..e498253db1 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8716,18 +8716,25 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tv_dict_alloc_ret(rettv); - for (int i = 0; ; i++) { - // TODO(justinmk): use os_copyfullenv from #7202 ? - char *envname = os_getenvname_at_index((size_t)i); - if (envname == NULL) { - break; - } - const char *value = os_getenv(envname); + size_t env_size = os_get_fullenv_size(); + char **env = xmalloc(sizeof(*env) * (env_size + 1)); + env[env_size] = NULL; + + os_copy_fullenv(env, env_size); + + for (size_t i = 0; i < env_size; i++) { + const char * str = env[i]; + const char * const end = strchr(str + (str[0] == '=' ? 1 : 0), + '='); + assert(end != NULL); + ptrdiff_t len = end - str; + assert(len > 0); + const char * value = str + len + 1; tv_dict_add_str(rettv->vval.v_dict, - (char *)envname, STRLEN((char *)envname), - value == NULL ? "" : value); - xfree(envname); + str, len, + value); } + os_free_fullenv(env); } /* @@ -12639,15 +12646,12 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) env = xmalloc((custom_env_size + 1) * sizeof(*env)); env_size = 0; } else { - char **genv = os_getfullenv(); - for (env = genv; *env; env++) { - env_size++; - } + env_size = os_get_fullenv_size(); + env = xmalloc((custom_env_size + env_size + 1) * sizeof(*env)); - for (i = 0; i < env_size; i++) { - env[i] = xstrdup(genv[i]); - } + os_copy_fullenv(env, env_size); + i = env_size; } assert(env); // env must be allocated at this point diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 15153e9bd7..ac442ee2e8 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -184,39 +184,139 @@ int os_unsetenv(const char *name) return r == 0 ? 0 : -1; } -char **os_getfullenv(void) +/// Returns number of variables in the current environment variables block +size_t os_get_fullenv_size(void) { + size_t len = 0; #ifdef _WIN32 - wchar_t *env = GetEnvironmentStringsW(); - if (!env) { + wchar_t *envstrings = GetEnvironmentStringsW(); + wchar_t *p = envstrings; + size_t l; + if (!envstrings) { + return len; + } + // GetEnvironmentStringsW() result has this format: + // var1=value1\0var2=value2\0...varN=valueN\0\0 + while ((l = wcslen(p)) != 0) { + p += l + 1; + len++; + } + + FreeEnvironmentStringsW(envstrings); +#else +# if defined(HAVE__NSGETENVIRON) + char **environ = *_NSGetEnviron(); +# else + extern char **environ; +# endif + + while (environ[len] != NULL) { + len++; + } + +#endif + return len; +} + +void os_free_fullenv(char **env) +{ + if (!env) { return; } + for (char **it = env; *it; it++) { + XFREE_CLEAR(*it); + } + xfree(env); +} + +/// Copies the current environment variables into the given array, `env`. Each +/// array element is of the form "NAME=VALUE". +/// Result must be freed by the caller. +/// +/// @param[out] env array to populate with environment variables +/// @param env_size size of `env`, @see os_fullenv_size +void os_copy_fullenv(char **env, size_t env_size) +{ +#ifdef _WIN32 + wchar_t *envstrings = GetEnvironmentStringsW(); + if (!envstrings) { + return; + } + wchar_t *p = envstrings; + size_t i = 0; + size_t l; + // GetEnvironmentStringsW() result has this format: + // var1=value1\0var2=value2\0...varN=valueN\0\0 + while ((l = wcslen(p)) != 0 && i < env_size) { + char *utf8_str; + int conversion_result = utf16_to_utf8(p, -1, &utf8_str); + if (conversion_result != 0) { + EMSG2("utf16_to_utf8 failed: %d", conversion_result); + break; + } + p += l + 1; + + env[i] = utf8_str; + i++; + } + + FreeEnvironmentStringsW(envstrings); +#else +# if defined(HAVE__NSGETENVIRON) + char **environ = *_NSGetEnviron(); +# else + extern char **environ; +# endif + + size_t i = 0; + while (environ[i] != NULL && i < env_size) { + env[i] = xstrdup(environ[i]); + i++; + } +#endif +} + +/// Copy value of the environment variable at `index` in the current +/// environment variables block. +/// Result must be freed by the caller. +/// +/// @param index nth item in environment variables block +/// @return [allocated] environment variable's value, or NULL +char *os_getenvname_at_index(size_t index) +{ +#ifdef _WIN32 + wchar_t *envstrings = GetEnvironmentStringsW(); + if (!envstrings) { return NULL; } + wchar_t *p = envstrings; char *name = NULL; - size_t current_index = 0; + size_t i = 0; + size_t l; // GetEnvironmentStringsW() result has this format: // var1=value1\0var2=value2\0...varN=valueN\0\0 - for (wchar_t *it = env; *it != L'\0' || *(it + 1) != L'\0'; it++) { - if (index == current_index) { + while ((l = wcslen(p)) != 0 && i <= index) { + if (i == index) { char *utf8_str; - int conversion_result = utf16_to_utf8(it, -1, &utf8_str); + int conversion_result = utf16_to_utf8(p, -1, &utf8_str); if (conversion_result != 0) { EMSG2("utf16_to_utf8 failed: %d", conversion_result); break; } - size_t namesize = 0; - while (utf8_str[namesize] != '=' && utf8_str[namesize] != NUL) { - namesize++; - } - name = (char *)vim_strnsave((char_u *)utf8_str, namesize); + + const char * const end = strchr(utf8_str, '='); + assert(end != NULL); + ptrdiff_t len = end - utf8_str; + assert(len > 0); + name = xstrndup(utf8_str, (size_t)len); xfree(utf8_str); break; } - if (*it == L'\0') { - current_index++; - } + + // Advance past the name and NUL + p += l + 1; + i++; } - FreeEnvironmentStringsW(env); + FreeEnvironmentStringsW(envstrings); return name; #else # if defined(HAVE__NSGETENVIRON) @@ -224,26 +324,20 @@ char **os_getfullenv(void) # else extern char **environ; # endif - return environ; -} -char *os_getenvname_at_index(size_t index) -{ - char **env = os_getfullenv(); // check if index is inside the environ array for (size_t i = 0; i <= index; i++) { - if (env[i] == NULL) { + if (environ[i] == NULL) { return NULL; } } - char *str = env[index]; + char *str = environ[index]; assert(str != NULL); - size_t namesize = 0; - while (str[namesize] != '=' && str[namesize] != NUL) { - namesize++; - } - char *name = (char *)vim_strnsave((char_u *)str, namesize); - return name; + const char * const end = strchr(str, '='); + assert(end != NULL); + ptrdiff_t len = end - str; + assert(len > 0); + return xstrndup(str, (size_t)len); #endif } |