mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
jobstart now supports env/clear_env
to modify the environment of the launched job.
This commit is contained in:
parent
f3fcaedfad
commit
19b6237087
@ -5451,6 +5451,9 @@ jobstart({cmd}[, {opts}]) *jobstart()*
|
|||||||
|on_exit| : exit event handler (function name or |Funcref|)
|
|on_exit| : exit event handler (function name or |Funcref|)
|
||||||
cwd : Working directory of the job; defaults to
|
cwd : Working directory of the job; defaults to
|
||||||
|current-directory|.
|
|current-directory|.
|
||||||
|
env : A dict of strings to append (or replace see
|
||||||
|
|clear_env|) to the current environment.
|
||||||
|
clear_env: If set, use the exact values passed in |env|
|
||||||
rpc : If set, |msgpack-rpc| will be used to communicate
|
rpc : If set, |msgpack-rpc| will be used to communicate
|
||||||
with the job over stdin and stdout. "on_stdout" is
|
with the job over stdin and stdout. "on_stdout" is
|
||||||
then ignored, but "on_stderr" can still be used.
|
then ignored, but "on_stderr" can still be used.
|
||||||
|
@ -272,11 +272,44 @@ 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
|
||||||
|
///
|
||||||
|
/// @param[in] argv Arguments vector specifying the command to run,
|
||||||
|
/// NULL-terminated
|
||||||
|
/// @param[in] on_stdout Callback to read the job's stdout
|
||||||
|
/// @param[in] on_stderr Callback to read the job's stderr
|
||||||
|
/// @param[in] on_exit Callback to receive the job's exit status
|
||||||
|
/// @param[in] pty True if the job should run attached to a pty
|
||||||
|
/// @param[in] rpc True to communicate with the job using msgpack-rpc,
|
||||||
|
/// `on_stdout` is ignored
|
||||||
|
/// @param[in] detach True if the job should not be killed when nvim exits,
|
||||||
|
/// ignored if `pty` is true
|
||||||
|
/// @param[in] cwd Initial working directory for the job. Nvim's working
|
||||||
|
/// directory if `cwd` is NULL
|
||||||
|
/// @param[in] pty_width Width of the pty, ignored if `pty` is false
|
||||||
|
/// @param[in] pty_height Height of the pty, ignored if `pty` is false
|
||||||
|
/// @param[in] term_name `$TERM` for the pty
|
||||||
|
/// @param[in] env Nvim's configured environment is used if this is NULL,
|
||||||
|
/// otherwise defines all environment variables
|
||||||
|
/// @param[out] status_out 0 for invalid arguments, > 0 for the channel id,
|
||||||
|
/// < 0 if the job can't start
|
||||||
|
///
|
||||||
|
/// @returns [allocated] channel
|
||||||
Channel *channel_job_start(char **argv, CallbackReader on_stdout,
|
Channel *channel_job_start(char **argv, CallbackReader on_stdout,
|
||||||
CallbackReader on_stderr, Callback on_exit,
|
CallbackReader on_stderr, Callback on_exit,
|
||||||
bool pty, bool rpc, bool detach, const char *cwd,
|
bool pty, bool rpc, bool detach, const char *cwd,
|
||||||
uint16_t pty_width, uint16_t pty_height,
|
uint16_t pty_width, uint16_t pty_height,
|
||||||
char *term_name, varnumber_T *status_out)
|
char *term_name, char **env, varnumber_T *status_out)
|
||||||
{
|
{
|
||||||
assert(cwd == NULL || os_isdir_executable(cwd));
|
assert(cwd == NULL || os_isdir_executable(cwd));
|
||||||
|
|
||||||
@ -314,6 +347,7 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
|
|||||||
proc->events = chan->events;
|
proc->events = chan->events;
|
||||||
proc->detach = detach;
|
proc->detach = detach;
|
||||||
proc->cwd = cwd;
|
proc->cwd = cwd;
|
||||||
|
proc->env = env;
|
||||||
|
|
||||||
char *cmd = xstrdup(proc->argv[0]);
|
char *cmd = xstrdup(proc->argv[0]);
|
||||||
bool has_out, has_err;
|
bool has_out, has_err;
|
||||||
@ -328,6 +362,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);
|
||||||
if (proc->type == kProcessTypePty) {
|
if (proc->type == kProcessTypePty) {
|
||||||
xfree(chan->stream.pty.term_name);
|
xfree(chan->stream.pty.term_name);
|
||||||
}
|
}
|
||||||
@ -336,6 +371,8 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
xfree(cmd);
|
xfree(cmd);
|
||||||
|
free_env(proc->env);
|
||||||
|
|
||||||
|
|
||||||
wstream_init(&proc->in, 0);
|
wstream_init(&proc->in, 0);
|
||||||
if (has_out) {
|
if (has_out) {
|
||||||
|
@ -12577,6 +12577,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;
|
||||||
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.
|
||||||
@ -12594,6 +12595,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
bool detach = false;
|
bool detach = false;
|
||||||
bool rpc = false;
|
bool rpc = false;
|
||||||
bool pty = false;
|
bool pty = false;
|
||||||
|
bool clear_env = false;
|
||||||
CallbackReader on_stdout = CALLBACK_READER_INIT,
|
CallbackReader on_stdout = CALLBACK_READER_INIT,
|
||||||
on_stderr = CALLBACK_READER_INIT;
|
on_stderr = CALLBACK_READER_INIT;
|
||||||
Callback on_exit = CALLBACK_NONE;
|
Callback on_exit = CALLBACK_NONE;
|
||||||
@ -12604,6 +12606,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
detach = tv_dict_get_number(job_opts, "detach") != 0;
|
detach = tv_dict_get_number(job_opts, "detach") != 0;
|
||||||
rpc = tv_dict_get_number(job_opts, "rpc") != 0;
|
rpc = tv_dict_get_number(job_opts, "rpc") != 0;
|
||||||
pty = tv_dict_get_number(job_opts, "pty") != 0;
|
pty = tv_dict_get_number(job_opts, "pty") != 0;
|
||||||
|
clear_env = tv_dict_get_number(job_opts, "clear_env") != 0;
|
||||||
if (pty && rpc) {
|
if (pty && rpc) {
|
||||||
EMSG2(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set");
|
EMSG2(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set");
|
||||||
shell_free_argv(argv);
|
shell_free_argv(argv);
|
||||||
@ -12620,6 +12623,47 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
dictitem_T *item;
|
||||||
|
item = tv_dict_find(job_opts, S_LEN("env"));
|
||||||
|
if (item) {
|
||||||
|
size_t custom_env_size = (size_t)tv_dict_len(item->di_tv.vval.v_dict);
|
||||||
|
size_t i = 0;
|
||||||
|
size_t env_size = 0;
|
||||||
|
if (item->di_tv.v_type != VAR_DICT) {
|
||||||
|
EMSG2(_(e_invarg2), "env");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clear_env) {
|
||||||
|
// + 1 for last null entry
|
||||||
|
env = xmalloc((custom_env_size + 1) * sizeof(*env));
|
||||||
|
env_size = 0;
|
||||||
|
} else {
|
||||||
|
char **genv = os_getfullenv();
|
||||||
|
for (env = genv; *env; env++) {
|
||||||
|
env_size++;
|
||||||
|
}
|
||||||
|
env = xmalloc((custom_env_size + env_size + 1) * sizeof(*env));
|
||||||
|
|
||||||
|
for (i = 0; i < env_size; i++) {
|
||||||
|
env[i] = xstrdup(genv[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(env); // env must be allocated at this point
|
||||||
|
|
||||||
|
TV_DICT_ITER(item->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 (!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);
|
||||||
@ -12637,8 +12681,8 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty,
|
Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty,
|
||||||
rpc, detach, cwd, width, height, term_name,
|
rpc, detach, cwd, width, height,
|
||||||
&rettv->vval.v_number);
|
term_name, env, &rettv->vval.v_number);
|
||||||
if (chan) {
|
if (chan) {
|
||||||
channel_create_event(chan, NULL);
|
channel_create_event(chan, NULL);
|
||||||
}
|
}
|
||||||
@ -14933,7 +14977,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
|
|
||||||
Channel *chan = channel_job_start(argv, CALLBACK_READER_INIT,
|
Channel *chan = channel_job_start(argv, CALLBACK_READER_INIT,
|
||||||
CALLBACK_READER_INIT, CALLBACK_NONE,
|
CALLBACK_READER_INIT, CALLBACK_NONE,
|
||||||
false, true, false, NULL, 0, 0, NULL,
|
false, true, false, NULL, 0, 0, NULL, NULL,
|
||||||
&rettv->vval.v_number);
|
&rettv->vval.v_number);
|
||||||
if (chan) {
|
if (chan) {
|
||||||
channel_create_event(chan, NULL);
|
channel_create_event(chan, NULL);
|
||||||
@ -18320,7 +18364,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit,
|
Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit,
|
||||||
true, false, false, cwd,
|
true, false, false, cwd,
|
||||||
term_width, curwin->w_height_inner,
|
term_width, curwin->w_height_inner,
|
||||||
xstrdup("xterm-256color"),
|
xstrdup("xterm-256color"), NULL,
|
||||||
&rettv->vval.v_number);
|
&rettv->vval.v_number);
|
||||||
if (rettv->vval.v_number <= 0) {
|
if (rettv->vval.v_number <= 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -41,7 +41,7 @@ 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 = NULL; // Inherits the parent (nvim) env.
|
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;
|
||||||
|
@ -23,6 +23,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;
|
||||||
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;
|
||||||
|
@ -184,7 +184,7 @@ int os_unsetenv(const char *name)
|
|||||||
return r == 0 ? 0 : -1;
|
return r == 0 ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *os_getenvname_at_index(size_t index)
|
char **os_getfullenv(void)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
wchar_t *env = GetEnvironmentStringsW();
|
wchar_t *env = GetEnvironmentStringsW();
|
||||||
@ -224,13 +224,20 @@ char *os_getenvname_at_index(size_t index)
|
|||||||
# else
|
# else
|
||||||
extern char **environ;
|
extern char **environ;
|
||||||
# endif
|
# endif
|
||||||
// Check if index is inside the environ array and is not the last element.
|
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++) {
|
for (size_t i = 0; i <= index; i++) {
|
||||||
if (environ[i] == NULL) {
|
if (env[i] == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
char *str = environ[index];
|
char *str = env[index];
|
||||||
|
assert(str != NULL);
|
||||||
size_t namesize = 0;
|
size_t namesize = 0;
|
||||||
while (str[namesize] != '=' && str[namesize] != NUL) {
|
while (str[namesize] != '=' && str[namesize] != NUL) {
|
||||||
namesize++;
|
namesize++;
|
||||||
|
Loading…
Reference in New Issue
Block a user