mirror of
https://github.com/neovim/neovim.git
synced 2025-09-13 14:58:18 +00:00
Common handling of required/ignored env vars
When starting a pty job, there are certain env vars that we need to either add or remove. Currently, there are two relevant scenarios. * Removing irrelevant env vars on Unix, mostly related to the terminal hosting nvim since they do not apply to a libvterm-hosted terminal. * Adding required env vars for Windows jobs.
This commit is contained in:
@@ -4875,7 +4875,38 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
rettv->vval.v_number = 1;
|
rettv->vval.v_number = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static dict_T *create_environment(const dictitem_T *job_env, const bool clear_env)
|
static const char *ignored_env_vars[] = {
|
||||||
|
#ifndef WIN32
|
||||||
|
"COLUMNS",
|
||||||
|
"LINES",
|
||||||
|
"TERMCAP",
|
||||||
|
"COLORFGBG",
|
||||||
|
#endif
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
/// According to comments in src/win/process.c of libuv, Windows has a few
|
||||||
|
/// "essential" environment variables.
|
||||||
|
static const char *required_env_vars[] = {
|
||||||
|
#ifdef WIN32
|
||||||
|
"HOMEDRIVE",
|
||||||
|
"HOMEPATH",
|
||||||
|
"LOGONSERVER",
|
||||||
|
"PATH",
|
||||||
|
"SYSTEMDRIVE",
|
||||||
|
"SYSTEMROOT",
|
||||||
|
"TEMP",
|
||||||
|
"USERDOMAIN",
|
||||||
|
"USERNAME",
|
||||||
|
"USERPROFILE",
|
||||||
|
"WINDIR",
|
||||||
|
#endif
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static dict_T *create_environment(const dictitem_T *job_env,
|
||||||
|
const bool clear_env,
|
||||||
|
const bool pty)
|
||||||
{
|
{
|
||||||
dict_T * env = tv_dict_alloc();
|
dict_T * env = tv_dict_alloc();
|
||||||
|
|
||||||
@@ -4884,12 +4915,52 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en
|
|||||||
f_environ(NULL, &temp_env, NULL);
|
f_environ(NULL, &temp_env, NULL);
|
||||||
tv_dict_extend(env, temp_env.vval.v_dict, "force");
|
tv_dict_extend(env, temp_env.vval.v_dict, "force");
|
||||||
tv_dict_free(temp_env.vval.v_dict);
|
tv_dict_free(temp_env.vval.v_dict);
|
||||||
|
|
||||||
|
if (pty) {
|
||||||
|
// These environment variables generally shouldn't be propagated to the
|
||||||
|
// child process. We're removing them here so the user can still decide
|
||||||
|
// they want to explicitly set them.
|
||||||
|
for (size_t i = 0;
|
||||||
|
i < ARRAY_SIZE(ignored_env_vars) && ignored_env_vars[i];
|
||||||
|
i++) {
|
||||||
|
dictitem_T *dv = tv_dict_find(env, ignored_env_vars[i], -1);
|
||||||
|
if (dv) {
|
||||||
|
tv_dict_item_remove(env, dv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifndef WIN32
|
||||||
|
// Set COLORTERM to "truecolor" if termguicolors is set and 256
|
||||||
|
// otherwise, but only if it was set in the parent terminal at all
|
||||||
|
dictitem_T *dv = tv_dict_find(env, S_LEN("COLORTERM"));
|
||||||
|
if (dv) {
|
||||||
|
tv_dict_item_remove(env, dv);
|
||||||
|
tv_dict_add_str(env, S_LEN("COLORTERM"), p_tgc ? "truecolor" : "256");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (job_env) {
|
if (job_env) {
|
||||||
tv_dict_extend(env, job_env->di_tv.vval.v_dict, "force");
|
tv_dict_extend(env, job_env->di_tv.vval.v_dict, "force");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pty) {
|
||||||
|
// Now that the custom environment is configured, we need to ensure certain
|
||||||
|
// environment variables are present.
|
||||||
|
for (size_t i = 0;
|
||||||
|
i < ARRAY_SIZE(required_env_vars) && required_env_vars[i];
|
||||||
|
i++) {
|
||||||
|
size_t len = strlen(required_env_vars[i]);
|
||||||
|
dictitem_T *dv = tv_dict_find(env, required_env_vars[i], len);
|
||||||
|
if (!dv) {
|
||||||
|
const char *env_var = os_getenv(required_env_vars[i]);
|
||||||
|
if (env_var) {
|
||||||
|
tv_dict_add_str(env, required_env_vars[i], len, env_var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4929,6 +5000,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
on_stderr = CALLBACK_READER_INIT;
|
on_stderr = CALLBACK_READER_INIT;
|
||||||
Callback on_exit = CALLBACK_NONE;
|
Callback on_exit = CALLBACK_NONE;
|
||||||
char *cwd = NULL;
|
char *cwd = NULL;
|
||||||
|
dictitem_T *job_env = NULL;
|
||||||
if (argvars[1].v_type == VAR_DICT) {
|
if (argvars[1].v_type == VAR_DICT) {
|
||||||
job_opts = argvars[1].vval.v_dict;
|
job_opts = argvars[1].vval.v_dict;
|
||||||
|
|
||||||
@@ -4964,22 +5036,21 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dictitem_T *job_env = tv_dict_find(job_opts, S_LEN("env"));
|
job_env = tv_dict_find(job_opts, S_LEN("env"));
|
||||||
if (job_env && job_env->di_tv.v_type != VAR_DICT) {
|
if (job_env && job_env->di_tv.v_type != VAR_DICT) {
|
||||||
EMSG2(_(e_invarg2), "env");
|
EMSG2(_(e_invarg2), "env");
|
||||||
shell_free_argv(argv);
|
shell_free_argv(argv);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
env = create_environment(job_env, clear_env);
|
|
||||||
|
|
||||||
if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) {
|
if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) {
|
||||||
shell_free_argv(argv);
|
shell_free_argv(argv);
|
||||||
tv_dict_free(env);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
env = create_environment(job_env, clear_env, pty);
|
||||||
|
|
||||||
uint16_t width = 0, height = 0;
|
uint16_t width = 0, height = 0;
|
||||||
char *term_name = NULL;
|
char *term_name = NULL;
|
||||||
|
|
||||||
@@ -10508,6 +10579,9 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
dict_T *job_opts = NULL;
|
dict_T *job_opts = NULL;
|
||||||
const char *cwd = ".";
|
const char *cwd = ".";
|
||||||
dict_T *env = NULL;
|
dict_T *env = NULL;
|
||||||
|
const bool pty = true;
|
||||||
|
bool clear_env = false;
|
||||||
|
dictitem_T *job_env = NULL;
|
||||||
|
|
||||||
if (argvars[1].v_type == VAR_DICT) {
|
if (argvars[1].v_type == VAR_DICT) {
|
||||||
job_opts = argvars[1].vval.v_dict;
|
job_opts = argvars[1].vval.v_dict;
|
||||||
@@ -10523,16 +10597,14 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dictitem_T *job_env = tv_dict_find(job_opts, S_LEN("env"));
|
job_env = tv_dict_find(job_opts, S_LEN("env"));
|
||||||
if (job_env && job_env->di_tv.v_type != VAR_DICT) {
|
if (job_env && job_env->di_tv.v_type != VAR_DICT) {
|
||||||
EMSG2(_(e_invarg2), "env");
|
EMSG2(_(e_invarg2), "env");
|
||||||
shell_free_argv(argv);
|
shell_free_argv(argv);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool clear_env = tv_dict_get_number(job_opts, "clear_env") != 0;
|
clear_env = tv_dict_get_number(job_opts, "clear_env") != 0;
|
||||||
|
|
||||||
env = create_environment(job_env, clear_env);
|
|
||||||
|
|
||||||
if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) {
|
if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) {
|
||||||
shell_free_argv(argv);
|
shell_free_argv(argv);
|
||||||
@@ -10540,7 +10612,8 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool pty = true;
|
env = create_environment(job_env, clear_env, pty);
|
||||||
|
|
||||||
const bool rpc = false;
|
const bool rpc = false;
|
||||||
const bool overlapped = false;
|
const bool overlapped = false;
|
||||||
const bool detach = false;
|
const bool detach = false;
|
||||||
|
@@ -83,6 +83,9 @@ int libuv_process_spawn(LibuvProcess *uvproc)
|
|||||||
int status;
|
int status;
|
||||||
if ((status = uv_spawn(&proc->loop->uv, &uvproc->uv, &uvproc->uvopts))) {
|
if ((status = uv_spawn(&proc->loop->uv, &uvproc->uv, &uvproc->uvopts))) {
|
||||||
ELOG("uv_spawn failed: %s", uv_strerror(status));
|
ELOG("uv_spawn failed: %s", uv_strerror(status));
|
||||||
|
if (uvproc->uvopts.env) {
|
||||||
|
os_free_fullenv(uvproc->uvopts.env);
|
||||||
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,6 +105,10 @@ static void close_cb(uv_handle_t *handle)
|
|||||||
if (proc->internal_close_cb) {
|
if (proc->internal_close_cb) {
|
||||||
proc->internal_close_cb(proc);
|
proc->internal_close_cb(proc);
|
||||||
}
|
}
|
||||||
|
LibuvProcess *uvproc = (LibuvProcess *)proc;
|
||||||
|
if (uvproc->uvopts.env) {
|
||||||
|
os_free_fullenv(uvproc->uvopts.env);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void exit_cb(uv_process_t *handle, int64_t status, int term_signal)
|
static void exit_cb(uv_process_t *handle, int64_t status, int term_signal)
|
||||||
|
@@ -155,13 +155,6 @@ void pty_process_teardown(Loop *loop)
|
|||||||
uv_signal_stop(&loop->children_watcher);
|
uv_signal_stop(&loop->children_watcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *ignored_env_vars[] = {
|
|
||||||
"COLUMNS",
|
|
||||||
"LINES",
|
|
||||||
"TERMCAP",
|
|
||||||
"COLORFGBG"
|
|
||||||
};
|
|
||||||
|
|
||||||
static void init_child(PtyProcess *ptyproc)
|
static void init_child(PtyProcess *ptyproc)
|
||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
@@ -187,46 +180,11 @@ static void init_child(PtyProcess *ptyproc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
char *prog = ptyproc->process.argv[0];
|
char *prog = ptyproc->process.argv[0];
|
||||||
if (proc->env) {
|
|
||||||
for (size_t i = 0; i < ARRAY_SIZE(ignored_env_vars); i++) {
|
|
||||||
dictitem_T *dv = tv_dict_find(proc->env, ignored_env_vars[i], -1);
|
|
||||||
if (dv) {
|
|
||||||
tv_dict_item_remove(proc->env, dv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tv_dict_add_str(proc->env, S_LEN("TERM"), ptyproc->term_name ? ptyproc->term_name : "ansi");
|
|
||||||
|
|
||||||
// setting COLORTERM to "truecolor" if termguicolors is set and 256
|
assert(proc->env);
|
||||||
// otherwise, but only if it was set in the parent terminal at all
|
tv_dict_add_str(proc->env, S_LEN("TERM"),
|
||||||
dictitem_T *dv = tv_dict_find(proc->env, S_LEN("COLORTERM"));
|
ptyproc->term_name ? ptyproc->term_name : "ansi");
|
||||||
if (dv) {
|
environ = tv_dict_to_env(proc->env);
|
||||||
tv_dict_item_remove(proc->env, dv);
|
|
||||||
tv_dict_add_str(proc->env, S_LEN("COLORTERM"), p_tgc ? "truecolor" : "256");
|
|
||||||
}
|
|
||||||
|
|
||||||
environ = tv_dict_to_env(proc->env);
|
|
||||||
} else {
|
|
||||||
for (size_t i = 0; i < ARRAY_SIZE(ignored_env_vars); i++) {
|
|
||||||
os_unsetenv(ignored_env_vars[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// setting COLORTERM to "truecolor" if termguicolors is set and 256
|
|
||||||
// otherwise, but only if it was set in the parent terminal at all
|
|
||||||
if (os_env_exists("COLORTERM")) {
|
|
||||||
const char *colorterm = os_getenv("COLORTERM");
|
|
||||||
if (colorterm != NULL) {
|
|
||||||
if (p_tgc) {
|
|
||||||
os_setenv("COLORTERM", "truecolor", 1);
|
|
||||||
} else {
|
|
||||||
os_setenv("COLORTERM", "256", 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
os_unsetenv("COLORTERM");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
os_setenv("TERM", ptyproc->term_name ? ptyproc->term_name : "ansi", 1);
|
|
||||||
}
|
|
||||||
execvp(prog, proc->argv);
|
execvp(prog, proc->argv);
|
||||||
ELOG("execvp failed: %s: %s", strerror(errno), prog);
|
ELOG("execvp failed: %s: %s", strerror(errno), prog);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user