mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Use dict_T to pass env vars to process spawning code
Co-authored-by: Matthieu Coudron <mattator@gmail.com>
This commit is contained in:
parent
55add1c1c8
commit
7f50c69268
@ -304,7 +304,8 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
|
||||
bool pty, bool rpc, bool overlapped, bool detach,
|
||||
const char *cwd,
|
||||
uint16_t pty_width, uint16_t pty_height,
|
||||
char *term_name, char **env, varnumber_T *status_out)
|
||||
char *term_name, dict_T *env,
|
||||
varnumber_T *status_out)
|
||||
{
|
||||
assert(cwd == NULL || os_isdir_executable(cwd));
|
||||
|
||||
@ -358,7 +359,9 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
|
||||
if (status) {
|
||||
EMSG3(_(e_jobspawn), os_strerror(status), cmd);
|
||||
xfree(cmd);
|
||||
os_free_fullenv(proc->env);
|
||||
if (proc->env) {
|
||||
tv_dict_free(proc->env);
|
||||
}
|
||||
if (proc->type == kProcessTypePty) {
|
||||
xfree(chan->stream.pty.term_name);
|
||||
}
|
||||
@ -367,8 +370,9 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
|
||||
return NULL;
|
||||
}
|
||||
xfree(cmd);
|
||||
os_free_fullenv(proc->env);
|
||||
|
||||
if (proc->env) {
|
||||
tv_dict_free(proc->env);
|
||||
}
|
||||
|
||||
wstream_init(&proc->in, 0);
|
||||
if (has_out) {
|
||||
|
@ -4887,7 +4887,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||
|
||||
bool executable = true;
|
||||
char **argv = tv_to_argv(&argvars[0], NULL, &executable);
|
||||
char **env = NULL;
|
||||
dict_T *env = NULL;
|
||||
if (!argv) {
|
||||
rettv->vval.v_number = executable ? 0 : -1;
|
||||
return; // Did error message in tv_to_argv.
|
||||
@ -4936,7 +4936,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||
#endif
|
||||
|
||||
char *new_cwd = tv_dict_get_string(job_opts, "cwd", false);
|
||||
if (new_cwd && strlen(new_cwd) > 0) {
|
||||
if (new_cwd && *new_cwd != NUL) {
|
||||
cwd = new_cwd;
|
||||
// The new cwd must be a directory.
|
||||
if (!os_isdir_executable((const char *)cwd)) {
|
||||
@ -4945,48 +4945,30 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dictitem_T *job_env = tv_dict_find(job_opts, S_LEN("env"));
|
||||
if (job_env) {
|
||||
if (job_env->di_tv.v_type != VAR_DICT) {
|
||||
EMSG2(_(e_invarg2), "env");
|
||||
shell_free_argv(argv);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t custom_env_size = (size_t)tv_dict_len(job_env->di_tv.vval.v_dict);
|
||||
size_t i = 0;
|
||||
size_t env_size = 0;
|
||||
|
||||
if (clear_env) {
|
||||
// + 1 for last null entry
|
||||
env = xmalloc((custom_env_size + 1) * sizeof(*env));
|
||||
env_size = 0;
|
||||
} else {
|
||||
env_size = os_get_fullenv_size();
|
||||
|
||||
env = xmalloc((custom_env_size + env_size + 1) * sizeof(*env));
|
||||
|
||||
os_copy_fullenv(env, env_size);
|
||||
i = env_size;
|
||||
}
|
||||
assert(env); // env must be allocated at this point
|
||||
|
||||
TV_DICT_ITER(job_env->di_tv.vval.v_dict, var, {
|
||||
const char *str = tv_get_string(&var->di_tv);
|
||||
assert(str);
|
||||
size_t len = STRLEN(var->di_key) + strlen(str) + strlen("=") + 1;
|
||||
env[i] = xmalloc(len);
|
||||
snprintf(env[i], len, "%s=%s", (char *)var->di_key, str);
|
||||
i++;
|
||||
});
|
||||
|
||||
// must be null terminated
|
||||
env[env_size + custom_env_size] = NULL;
|
||||
if (job_env && job_env->di_tv.v_type != VAR_DICT) {
|
||||
EMSG2(_(e_invarg2), "env");
|
||||
shell_free_argv(argv);
|
||||
return;
|
||||
}
|
||||
|
||||
env = tv_dict_alloc();
|
||||
|
||||
if (!clear_env) {
|
||||
typval_T temp_env = TV_INITIAL_VALUE;
|
||||
f_environ(NULL, &temp_env, NULL);
|
||||
tv_dict_extend(env, temp_env.vval.v_dict, "force");
|
||||
tv_dict_free(temp_env.vval.v_dict);
|
||||
}
|
||||
|
||||
if (job_env) {
|
||||
tv_dict_extend(env, job_env->di_tv.vval.v_dict, "force");
|
||||
}
|
||||
|
||||
if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) {
|
||||
shell_free_argv(argv);
|
||||
tv_dict_free(env);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1523,6 +1523,33 @@ varnumber_T tv_dict_get_number(const dict_T *const d, const char *const key)
|
||||
return tv_get_number(&di->di_tv);
|
||||
}
|
||||
|
||||
/// Converts a dict to an environment
|
||||
///
|
||||
///
|
||||
char **tv_dict_to_env(dict_T *denv)
|
||||
{
|
||||
size_t env_size = (size_t)tv_dict_len(denv);
|
||||
|
||||
size_t i = 0;
|
||||
char **env = NULL;
|
||||
|
||||
// + 1 for NULL
|
||||
env = xmalloc((env_size + 1) * sizeof(*env));
|
||||
|
||||
TV_DICT_ITER(denv, var, {
|
||||
const char *str = tv_get_string(&var->di_tv);
|
||||
assert(str);
|
||||
size_t len = STRLEN(var->di_key) + strlen(str) + strlen("=") + 1;
|
||||
env[i] = xmalloc(len);
|
||||
snprintf(env[i], len, "%s=%s", (char *)var->di_key, str);
|
||||
i++;
|
||||
});
|
||||
|
||||
// must be null terminated
|
||||
env[env_size] = NULL;
|
||||
return env;
|
||||
}
|
||||
|
||||
/// Get a string item from a dictionary
|
||||
///
|
||||
/// @param[in] d Dictionary to get item from.
|
||||
|
@ -41,7 +41,6 @@ int libuv_process_spawn(LibuvProcess *uvproc)
|
||||
#endif
|
||||
uvproc->uvopts.exit_cb = exit_cb;
|
||||
uvproc->uvopts.cwd = proc->cwd;
|
||||
uvproc->uvopts.env = proc->env;
|
||||
uvproc->uvopts.stdio = uvproc->uvstdio;
|
||||
uvproc->uvopts.stdio_count = 3;
|
||||
uvproc->uvstdio[0].flags = UV_IGNORE;
|
||||
@ -49,6 +48,12 @@ int libuv_process_spawn(LibuvProcess *uvproc)
|
||||
uvproc->uvstdio[2].flags = UV_IGNORE;
|
||||
uvproc->uv.data = proc;
|
||||
|
||||
if (proc->env) {
|
||||
uvproc->uvopts.env = tv_dict_to_env(proc->env);
|
||||
} else {
|
||||
uvproc->uvopts.env = NULL;
|
||||
}
|
||||
|
||||
if (!proc->in.closed) {
|
||||
uvproc->uvstdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
|
||||
#ifdef WIN32
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "nvim/event/loop.h"
|
||||
#include "nvim/event/rstream.h"
|
||||
#include "nvim/event/wstream.h"
|
||||
#include "nvim/eval/typval.h"
|
||||
|
||||
typedef enum {
|
||||
kProcessTypeUv,
|
||||
@ -23,7 +24,7 @@ struct process {
|
||||
uint64_t stopped_time; // process_stop() timestamp
|
||||
const char *cwd;
|
||||
char **argv;
|
||||
char **env;
|
||||
dict_T *env;
|
||||
Stream in, out, err;
|
||||
process_exit_cb cb;
|
||||
internal_process_cb internal_exit_cb, internal_close_cb;
|
||||
|
@ -20,6 +20,10 @@
|
||||
# include <pty.h>
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <crt_externs.h>
|
||||
#endif
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
#include "nvim/lib/klist.h"
|
||||
@ -151,31 +155,24 @@ void pty_process_teardown(Loop *loop)
|
||||
uv_signal_stop(&loop->children_watcher);
|
||||
}
|
||||
|
||||
static const char *ignored_env_vars[] = {
|
||||
"COLUMNS",
|
||||
"LINES",
|
||||
"TERMCAP",
|
||||
"COLORFGBG"
|
||||
};
|
||||
|
||||
static void init_child(PtyProcess *ptyproc)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
#if defined(HAVE__NSGETENVIRON)
|
||||
char **environ = *_NSGetEnviron();
|
||||
#else
|
||||
extern char **environ;
|
||||
#endif
|
||||
// New session/process-group. #6530
|
||||
setsid();
|
||||
|
||||
os_unsetenv("COLUMNS");
|
||||
os_unsetenv("LINES");
|
||||
os_unsetenv("TERMCAP");
|
||||
os_unsetenv("COLORFGBG");
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
signal(SIGHUP, SIG_DFL);
|
||||
signal(SIGINT, SIG_DFL);
|
||||
@ -190,9 +187,49 @@ static void init_child(PtyProcess *ptyproc)
|
||||
}
|
||||
|
||||
char *prog = ptyproc->process.argv[0];
|
||||
os_setenv("TERM", ptyproc->term_name ? ptyproc->term_name : "ansi", 1);
|
||||
execvp(prog, ptyproc->process.argv);
|
||||
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
|
||||
// otherwise, but only if it was set in the parent terminal at all
|
||||
dictitem_T *dv = tv_dict_find(proc->env, S_LEN("COLORTERM"));
|
||||
if (dv) {
|
||||
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);
|
||||
ELOG("execvp failed: %s: %s", strerror(errno), prog);
|
||||
|
||||
_exit(122); // 122 is EXEC_FAILED in the Vim source.
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,20 @@ describe('jobs', function()
|
||||
})
|
||||
end)
|
||||
|
||||
it('append environment with pty #env', function()
|
||||
nvim('command', "let $VAR = 'abc'")
|
||||
nvim('command', "let g:job_opts.pty = v:true")
|
||||
nvim('command', "let g:job_opts.env = {'TOTO': 'hello world'}")
|
||||
if iswin() then
|
||||
nvim('command', [[call jobstart('echo %TOTO% %VAR%', g:job_opts)]])
|
||||
else
|
||||
nvim('command', [[call jobstart('echo $TOTO $VAR', g:job_opts)]])
|
||||
end
|
||||
expect_msg_seq({
|
||||
{'notification', 'stdout', {0, {'hello world abc', ''}}},
|
||||
})
|
||||
end)
|
||||
|
||||
it('replace environment #env', function()
|
||||
nvim('command', "let $VAR = 'abc'")
|
||||
nvim('command', "let g:job_opts.env = {'TOTO': 'hello world'}")
|
||||
|
Loading…
Reference in New Issue
Block a user