Add os_getfullenv_size/os_copyfullenv

This commit is contained in:
James McCoy
2019-06-02 14:36:17 -04:00
parent 19b6237087
commit 6dc1005787
3 changed files with 148 additions and 59 deletions

View File

@@ -272,15 +272,6 @@ static void close_cb(Stream *stream, void *data)
channel_decref(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 /// Starts a job and returns the associated channel
/// ///
@@ -362,7 +353,7 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
if (status) { if (status) {
EMSG3(_(e_jobspawn), os_strerror(status), cmd); EMSG3(_(e_jobspawn), os_strerror(status), cmd);
xfree(cmd); xfree(cmd);
free_env(proc->env); os_free_fullenv(proc->env);
if (proc->type == kProcessTypePty) { if (proc->type == kProcessTypePty) {
xfree(chan->stream.pty.term_name); xfree(chan->stream.pty.term_name);
} }
@@ -371,7 +362,7 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
return NULL; return NULL;
} }
xfree(cmd); xfree(cmd);
free_env(proc->env); os_free_fullenv(proc->env);
wstream_init(&proc->in, 0); wstream_init(&proc->in, 0);

View File

@@ -8716,18 +8716,25 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
tv_dict_alloc_ret(rettv); tv_dict_alloc_ret(rettv);
for (int i = 0; ; i++) { size_t env_size = os_get_fullenv_size();
// TODO(justinmk): use os_copyfullenv from #7202 ? char **env = xmalloc(sizeof(*env) * (env_size + 1));
char *envname = os_getenvname_at_index((size_t)i); env[env_size] = NULL;
if (envname == NULL) {
break; os_copy_fullenv(env, env_size);
}
const char *value = os_getenv(envname); 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, tv_dict_add_str(rettv->vval.v_dict,
(char *)envname, STRLEN((char *)envname), str, len,
value == NULL ? "" : value); value);
xfree(envname);
} }
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 = xmalloc((custom_env_size + 1) * sizeof(*env));
env_size = 0; env_size = 0;
} else { } else {
char **genv = os_getfullenv(); env_size = os_get_fullenv_size();
for (env = genv; *env; env++) {
env_size++;
}
env = xmalloc((custom_env_size + env_size + 1) * sizeof(*env)); env = xmalloc((custom_env_size + env_size + 1) * sizeof(*env));
for (i = 0; i < env_size; i++) { os_copy_fullenv(env, env_size);
env[i] = xstrdup(genv[i]); i = env_size;
}
} }
assert(env); // env must be allocated at this point assert(env); // env must be allocated at this point

View File

@@ -184,39 +184,139 @@ int os_unsetenv(const char *name)
return r == 0 ? 0 : -1; 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 #ifdef _WIN32
wchar_t *env = GetEnvironmentStringsW(); wchar_t *envstrings = GetEnvironmentStringsW();
if (!env) { wchar_t *p = envstrings;
return NULL; size_t l;
if (!envstrings) {
return len;
} }
char *name = NULL;
size_t current_index = 0;
// GetEnvironmentStringsW() result has this format: // GetEnvironmentStringsW() result has this format:
// var1=value1\0var2=value2\0...varN=valueN\0\0 // var1=value1\0var2=value2\0...varN=valueN\0\0
for (wchar_t *it = env; *it != L'\0' || *(it + 1) != L'\0'; it++) { while ((l = wcslen(p)) != 0) {
if (index == current_index) { 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 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 <= index) {
if (i == index) {
char *utf8_str; 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) { if (conversion_result != 0) {
EMSG2("utf16_to_utf8 failed: %d", conversion_result); EMSG2("utf16_to_utf8 failed: %d", conversion_result);
break; break;
} }
size_t namesize = 0;
while (utf8_str[namesize] != '=' && utf8_str[namesize] != NUL) { const char * const end = strchr(utf8_str, '=');
namesize++; assert(end != NULL);
} ptrdiff_t len = end - utf8_str;
name = (char *)vim_strnsave((char_u *)utf8_str, namesize); assert(len > 0);
name = xstrndup(utf8_str, (size_t)len);
xfree(utf8_str); xfree(utf8_str);
break; break;
} }
if (*it == L'\0') {
current_index++; // Advance past the name and NUL
} p += l + 1;
i++;
} }
FreeEnvironmentStringsW(env); FreeEnvironmentStringsW(envstrings);
return name; return name;
#else #else
# if defined(HAVE__NSGETENVIRON) # if defined(HAVE__NSGETENVIRON)
@@ -224,26 +324,20 @@ char **os_getfullenv(void)
# else # else
extern char **environ; extern char **environ;
# endif # endif
return environ;
}
char *os_getenvname_at_index(size_t index)
{
char **env = os_getfullenv();
// check if index is inside the environ array // check if index is inside the environ array
for (size_t i = 0; i <= index; i++) { for (size_t i = 0; i <= index; i++) {
if (env[i] == NULL) { if (environ[i] == NULL) {
return NULL; return NULL;
} }
} }
char *str = env[index]; char *str = environ[index];
assert(str != NULL); assert(str != NULL);
size_t namesize = 0; const char * const end = strchr(str, '=');
while (str[namesize] != '=' && str[namesize] != NUL) { assert(end != NULL);
namesize++; ptrdiff_t len = end - str;
} assert(len > 0);
char *name = (char *)vim_strnsave((char_u *)str, namesize); return xstrndup(str, (size_t)len);
return name;
#endif #endif
} }