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,
|
bool pty, bool rpc, bool overlapped, bool detach,
|
||||||
const char *cwd,
|
const char *cwd,
|
||||||
uint16_t pty_width, uint16_t pty_height,
|
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));
|
assert(cwd == NULL || os_isdir_executable(cwd));
|
||||||
|
|
||||||
@ -358,7 +359,9 @@ 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);
|
||||||
os_free_fullenv(proc->env);
|
if (proc->env) {
|
||||||
|
tv_dict_free(proc->env);
|
||||||
|
}
|
||||||
if (proc->type == kProcessTypePty) {
|
if (proc->type == kProcessTypePty) {
|
||||||
xfree(chan->stream.pty.term_name);
|
xfree(chan->stream.pty.term_name);
|
||||||
}
|
}
|
||||||
@ -367,8 +370,9 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
xfree(cmd);
|
xfree(cmd);
|
||||||
os_free_fullenv(proc->env);
|
if (proc->env) {
|
||||||
|
tv_dict_free(proc->env);
|
||||||
|
}
|
||||||
|
|
||||||
wstream_init(&proc->in, 0);
|
wstream_init(&proc->in, 0);
|
||||||
if (has_out) {
|
if (has_out) {
|
||||||
|
@ -4887,7 +4887,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
|
|
||||||
bool executable = true;
|
bool executable = true;
|
||||||
char **argv = tv_to_argv(&argvars[0], NULL, &executable);
|
char **argv = tv_to_argv(&argvars[0], NULL, &executable);
|
||||||
char **env = NULL;
|
dict_T *env = NULL;
|
||||||
if (!argv) {
|
if (!argv) {
|
||||||
rettv->vval.v_number = executable ? 0 : -1;
|
rettv->vval.v_number = executable ? 0 : -1;
|
||||||
return; // Did error message in tv_to_argv.
|
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
|
#endif
|
||||||
|
|
||||||
char *new_cwd = tv_dict_get_string(job_opts, "cwd", false);
|
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;
|
cwd = new_cwd;
|
||||||
// The new cwd must be a directory.
|
// The new cwd must be a directory.
|
||||||
if (!os_isdir_executable((const char *)cwd)) {
|
if (!os_isdir_executable((const char *)cwd)) {
|
||||||
@ -4945,48 +4945,30 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dictitem_T *job_env = tv_dict_find(job_opts, S_LEN("env"));
|
dictitem_T *job_env = tv_dict_find(job_opts, S_LEN("env"));
|
||||||
if (job_env) {
|
if (job_env && job_env->di_tv.v_type != VAR_DICT) {
|
||||||
if (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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)) {
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
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
|
/// Get a string item from a dictionary
|
||||||
///
|
///
|
||||||
/// @param[in] d Dictionary to get item from.
|
/// @param[in] d Dictionary to get item from.
|
||||||
|
@ -41,7 +41,6 @@ int libuv_process_spawn(LibuvProcess *uvproc)
|
|||||||
#endif
|
#endif
|
||||||
uvproc->uvopts.exit_cb = exit_cb;
|
uvproc->uvopts.exit_cb = exit_cb;
|
||||||
uvproc->uvopts.cwd = proc->cwd;
|
uvproc->uvopts.cwd = proc->cwd;
|
||||||
uvproc->uvopts.env = proc->env;
|
|
||||||
uvproc->uvopts.stdio = uvproc->uvstdio;
|
uvproc->uvopts.stdio = uvproc->uvstdio;
|
||||||
uvproc->uvopts.stdio_count = 3;
|
uvproc->uvopts.stdio_count = 3;
|
||||||
uvproc->uvstdio[0].flags = UV_IGNORE;
|
uvproc->uvstdio[0].flags = UV_IGNORE;
|
||||||
@ -49,6 +48,12 @@ int libuv_process_spawn(LibuvProcess *uvproc)
|
|||||||
uvproc->uvstdio[2].flags = UV_IGNORE;
|
uvproc->uvstdio[2].flags = UV_IGNORE;
|
||||||
uvproc->uv.data = proc;
|
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) {
|
if (!proc->in.closed) {
|
||||||
uvproc->uvstdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
|
uvproc->uvstdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "nvim/event/loop.h"
|
#include "nvim/event/loop.h"
|
||||||
#include "nvim/event/rstream.h"
|
#include "nvim/event/rstream.h"
|
||||||
#include "nvim/event/wstream.h"
|
#include "nvim/event/wstream.h"
|
||||||
|
#include "nvim/eval/typval.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
kProcessTypeUv,
|
kProcessTypeUv,
|
||||||
@ -23,7 +24,7 @@ struct process {
|
|||||||
uint64_t stopped_time; // process_stop() timestamp
|
uint64_t stopped_time; // process_stop() timestamp
|
||||||
const char *cwd;
|
const char *cwd;
|
||||||
char **argv;
|
char **argv;
|
||||||
char **env;
|
dict_T *env;
|
||||||
Stream in, out, err;
|
Stream in, out, err;
|
||||||
process_exit_cb cb;
|
process_exit_cb cb;
|
||||||
internal_process_cb internal_exit_cb, internal_close_cb;
|
internal_process_cb internal_exit_cb, internal_close_cb;
|
||||||
|
@ -20,6 +20,10 @@
|
|||||||
# include <pty.h>
|
# include <pty.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
# include <crt_externs.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <uv.h>
|
#include <uv.h>
|
||||||
|
|
||||||
#include "nvim/lib/klist.h"
|
#include "nvim/lib/klist.h"
|
||||||
@ -151,31 +155,24 @@ 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
|
||||||
{
|
{
|
||||||
|
#if defined(HAVE__NSGETENVIRON)
|
||||||
|
char **environ = *_NSGetEnviron();
|
||||||
|
#else
|
||||||
|
extern char **environ;
|
||||||
|
#endif
|
||||||
// New session/process-group. #6530
|
// New session/process-group. #6530
|
||||||
setsid();
|
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(SIGCHLD, SIG_DFL);
|
||||||
signal(SIGHUP, SIG_DFL);
|
signal(SIGHUP, SIG_DFL);
|
||||||
signal(SIGINT, SIG_DFL);
|
signal(SIGINT, SIG_DFL);
|
||||||
@ -190,9 +187,49 @@ static void init_child(PtyProcess *ptyproc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
char *prog = ptyproc->process.argv[0];
|
char *prog = ptyproc->process.argv[0];
|
||||||
os_setenv("TERM", ptyproc->term_name ? ptyproc->term_name : "ansi", 1);
|
if (proc->env) {
|
||||||
execvp(prog, ptyproc->process.argv);
|
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);
|
ELOG("execvp failed: %s: %s", strerror(errno), prog);
|
||||||
|
|
||||||
_exit(122); // 122 is EXEC_FAILED in the Vim source.
|
_exit(122); // 122 is EXEC_FAILED in the Vim source.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +75,20 @@ describe('jobs', function()
|
|||||||
})
|
})
|
||||||
end)
|
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()
|
it('replace environment #env', function()
|
||||||
nvim('command', "let $VAR = 'abc'")
|
nvim('command', "let $VAR = 'abc'")
|
||||||
nvim('command', "let g:job_opts.env = {'TOTO': 'hello world'}")
|
nvim('command', "let g:job_opts.env = {'TOTO': 'hello world'}")
|
||||||
|
Loading…
Reference in New Issue
Block a user