Merge pull request #12937 from jamessan/term-env
This commit is contained in:
commit
e85c8dff69
|
@ -28,7 +28,7 @@ tasks:
|
|||
gmake deps
|
||||
- build: |
|
||||
cd neovim
|
||||
gmake CMAKE_BUILD_TYPE=Release CMAKE_EXTRA_FLAGS="${CMAKE_EXTRA_FLAGS}" nvim
|
||||
gmake CMAKE_BUILD_TYPE=RelWithDebInfo CMAKE_EXTRA_FLAGS="${CMAKE_EXTRA_FLAGS}" nvim
|
||||
- functionaltest: |
|
||||
cd neovim
|
||||
gmake functionaltest
|
||||
|
|
|
@ -28,16 +28,16 @@ tasks:
|
|||
mkdir neovim/.deps
|
||||
cd neovim/.deps
|
||||
cmake -G Ninja ../third-party/
|
||||
cmake --build . --config Debug
|
||||
cmake --build . --config RelWithDebInfo
|
||||
- build: |
|
||||
mkdir neovim/build
|
||||
cd neovim/build
|
||||
cmake -G Ninja $CMAKE_EXTRA_FLAGS ..
|
||||
cmake --build . --config Debug
|
||||
cmake --build . --config RelWithDebInfo
|
||||
./bin/nvim --version
|
||||
- functionaltest: |
|
||||
cd neovim/build
|
||||
cmake --build . --config Debug --target functionaltest
|
||||
cmake --build . --config RelWithDebInfo --target functionaltest
|
||||
- oldtest: |
|
||||
cd neovim
|
||||
gmake oldtest
|
||||
|
|
|
@ -3270,7 +3270,7 @@ void maketitle(void)
|
|||
case 6: buf_p = strappend(buf_p, " -"); break;
|
||||
case 5:
|
||||
case 7: buf_p = strappend(buf_p, " -+"); break;
|
||||
default: assert(false);
|
||||
default: abort();
|
||||
}
|
||||
|
||||
if (curbuf->b_fname != NULL) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -1747,7 +1747,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len,
|
|||
goto vim_str2nr_dec;
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
} else if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN))
|
||||
|
@ -1788,7 +1788,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len,
|
|||
}
|
||||
|
||||
// Do the string-to-numeric conversion "manually" to avoid sscanf quirks.
|
||||
assert(false); // Should’ve used goto earlier.
|
||||
abort(); // Should’ve used goto earlier.
|
||||
#define PARSE_NUMBER(base, cond, conv) \
|
||||
do { \
|
||||
while (!STRING_ENDED(ptr) && (cond)) { \
|
||||
|
|
|
@ -365,7 +365,7 @@ void eval_init(void)
|
|||
eval_msgpack_type_lists[i] = type_list;
|
||||
if (tv_dict_add(msgpack_types_dict, di) == FAIL) {
|
||||
// There must not be duplicate items in this dictionary by definition.
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
msgpack_types_dict->dv_lock = VAR_FIXED;
|
||||
|
|
|
@ -147,7 +147,7 @@ static inline int json_decoder_pop(ValuesStackItem obj,
|
|||
tv_clear(&key.val);
|
||||
if (tv_dict_add(last_container.container.vval.v_dict, obj_di)
|
||||
== FAIL) {
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
obj_di->di_tv = obj.val;
|
||||
} else {
|
||||
|
@ -480,7 +480,7 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len,
|
|||
break;
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -174,7 +174,7 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack,
|
|||
case kMPConvPartial: {
|
||||
switch (v.data.p.stage) {
|
||||
case kMPConvPartialArgs: {
|
||||
assert(false);
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
case kMPConvPartialSelf: {
|
||||
|
@ -237,7 +237,7 @@ bool encode_vim_list_to_buf(const list_T *const list, size_t *const ret_len,
|
|||
char *const buf = xmalloc(len);
|
||||
size_t read_bytes;
|
||||
if (encode_read_from_list(&lrstate, buf, len, &read_bytes) != OK) {
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
assert(len == read_bytes);
|
||||
*ret_buf = buf;
|
||||
|
|
|
@ -118,7 +118,7 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2,
|
|||
return OK;
|
||||
}
|
||||
case VAR_UNKNOWN: {
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1798,7 +1798,7 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||
|
||||
os_copy_fullenv(env, env_size);
|
||||
|
||||
for (size_t i = 0; i < env_size; i++) {
|
||||
for (ssize_t i = env_size - 1; i >= 0; i--) {
|
||||
const char * str = env[i];
|
||||
const char * const end = strchr(str + (str[0] == '=' ? 1 : 0),
|
||||
'=');
|
||||
|
@ -1806,6 +1806,12 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||
ptrdiff_t len = end - str;
|
||||
assert(len > 0);
|
||||
const char * value = str + len + 1;
|
||||
if (tv_dict_find(rettv->vval.v_dict, str, len) != NULL) {
|
||||
// Since we're traversing from the end of the env block to the front, any
|
||||
// duplicate names encountered should be ignored. This preserves the
|
||||
// semantics of env vars defined later in the env block taking precedence.
|
||||
continue;
|
||||
}
|
||||
tv_dict_add_str(rettv->vval.v_dict,
|
||||
str, len,
|
||||
value);
|
||||
|
@ -3301,7 +3307,7 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||
}
|
||||
break;
|
||||
case kCdScopeInvalid: // We should never get here
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
|
||||
if (from) {
|
||||
|
@ -4354,7 +4360,7 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||
break;
|
||||
case kCdScopeInvalid:
|
||||
// We should never get here
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4875,6 +4881,95 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||
rettv->vval.v_number = 1;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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 (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) {
|
||||
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;
|
||||
}
|
||||
|
||||
// "jobstart()" function
|
||||
static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||
{
|
||||
|
@ -4887,7 +4982,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.
|
||||
|
@ -4911,6 +5006,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||
on_stderr = CALLBACK_READER_INIT;
|
||||
Callback on_exit = CALLBACK_NONE;
|
||||
char *cwd = NULL;
|
||||
dictitem_T *job_env = NULL;
|
||||
if (argvars[1].v_type == VAR_DICT) {
|
||||
job_opts = argvars[1].vval.v_dict;
|
||||
|
||||
|
@ -4936,7 +5032,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,52 +5041,22 @@ 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;
|
||||
job_env = tv_dict_find(job_opts, S_LEN("env"));
|
||||
if (job_env && job_env->di_tv.v_type != VAR_DICT) {
|
||||
EMSG2(_(e_invarg2), "env");
|
||||
shell_free_argv(argv);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) {
|
||||
shell_free_argv(argv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
env = create_environment(job_env, clear_env, pty);
|
||||
|
||||
uint16_t width = 0, height = 0;
|
||||
char *term_name = NULL;
|
||||
|
||||
|
@ -10518,6 +10584,11 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||
Callback on_exit = CALLBACK_NONE;
|
||||
dict_T *job_opts = NULL;
|
||||
const char *cwd = ".";
|
||||
dict_T *env = NULL;
|
||||
const bool pty = true;
|
||||
bool clear_env = false;
|
||||
dictitem_T *job_env = NULL;
|
||||
|
||||
if (argvars[1].v_type == VAR_DICT) {
|
||||
job_opts = argvars[1].vval.v_dict;
|
||||
|
||||
|
@ -10532,17 +10603,31 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||
}
|
||||
}
|
||||
|
||||
job_env = tv_dict_find(job_opts, S_LEN("env"));
|
||||
if (job_env && job_env->di_tv.v_type != VAR_DICT) {
|
||||
EMSG2(_(e_invarg2), "env");
|
||||
shell_free_argv(argv);
|
||||
return;
|
||||
}
|
||||
|
||||
clear_env = tv_dict_get_number(job_opts, "clear_env") != 0;
|
||||
|
||||
if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) {
|
||||
shell_free_argv(argv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
env = create_environment(job_env, clear_env, pty);
|
||||
|
||||
const bool rpc = false;
|
||||
const bool overlapped = false;
|
||||
const bool detach = false;
|
||||
uint16_t term_width = MAX(0, curwin->w_width_inner - win_col_off(curwin));
|
||||
Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit,
|
||||
true, false, false, false, cwd,
|
||||
pty, rpc, overlapped, detach, cwd,
|
||||
term_width, curwin->w_height_inner,
|
||||
xstrdup("xterm-256color"), NULL,
|
||||
xstrdup("xterm-256color"), env,
|
||||
&rettv->vval.v_number);
|
||||
if (rettv->vval.v_number <= 0) {
|
||||
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.
|
||||
|
@ -2494,7 +2521,7 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock)
|
|||
break;
|
||||
}
|
||||
case VAR_UNKNOWN: {
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
#undef CHANGE_LOCK
|
||||
|
@ -2666,7 +2693,7 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic,
|
|||
}
|
||||
}
|
||||
|
||||
assert(false);
|
||||
abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2719,7 +2746,7 @@ bool tv_check_str_or_nr(const typval_T *const tv)
|
|||
return false;
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2764,7 +2791,7 @@ bool tv_check_num(const typval_T *const tv)
|
|||
return false;
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2809,7 +2836,7 @@ bool tv_check_str(const typval_T *const tv)
|
|||
return false;
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
abort();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -78,6 +83,9 @@ int libuv_process_spawn(LibuvProcess *uvproc)
|
|||
int status;
|
||||
if ((status = uv_spawn(&proc->loop->uv, &uvproc->uv, &uvproc->uvopts))) {
|
||||
ELOG("uv_spawn failed: %s", uv_strerror(status));
|
||||
if (uvproc->uvopts.env) {
|
||||
os_free_fullenv(uvproc->uvopts.env);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -97,6 +105,10 @@ static void close_cb(uv_handle_t *handle)
|
|||
if (proc->internal_close_cb) {
|
||||
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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -7591,7 +7591,7 @@ void post_chdir(CdScope scope, bool trigger_dirchanged)
|
|||
curwin->w_localdir = (char_u *)xstrdup(cwd);
|
||||
break;
|
||||
case kCdScopeInvalid:
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
|
||||
shorten_fnames(true);
|
||||
|
|
|
@ -6383,7 +6383,7 @@ int hist_type2char(int type)
|
|||
return '>';
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
return NUL;
|
||||
|
|
|
@ -1595,7 +1595,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, bool changed_window)
|
|||
}
|
||||
case kCdScopeInvalid: {
|
||||
// Should never happen.
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -245,7 +245,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
|
|||
} else {
|
||||
dictitem_T *const di = tv_dict_item_alloc_len(s, len);
|
||||
if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) {
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
kv_push(stack, cur);
|
||||
cur = (TVPopStackItem) { &di->di_tv, false, false, 0 };
|
||||
|
@ -391,7 +391,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
|
|||
break;
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
nlua_pop_typval_table_processing_end:
|
||||
|
@ -1200,7 +1200,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err)
|
|||
break;
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -2623,7 +2623,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
|
|||
}
|
||||
// NOTREACHED
|
||||
case kMTUnknown:
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6092,7 +6092,7 @@ static void set_clipboard(int name, yankreg_T *reg)
|
|||
break;
|
||||
}
|
||||
case kMTUnknown: {
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,10 @@
|
|||
# include <pty.h>
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <crt_externs.h>
|
||||
#endif
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
#include "nvim/lib/klist.h"
|
||||
|
@ -154,28 +158,14 @@ void pty_process_teardown(Loop *loop)
|
|||
static void init_child(PtyProcess *ptyproc)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
#if defined(HAVE__NSGETENVIRON)
|
||||
#define 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 +180,14 @@ 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);
|
||||
|
||||
assert(proc->env);
|
||||
tv_dict_add_str(proc->env, S_LEN("TERM"),
|
||||
ptyproc->term_name ? ptyproc->term_name : "ansi");
|
||||
environ = tv_dict_to_env(proc->env);
|
||||
execvp(prog, proc->argv);
|
||||
ELOG("execvp failed: %s: %s", strerror(errno), prog);
|
||||
|
||||
_exit(122); // 122 is EXEC_FAILED in the Vim source.
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ int pty_process_spawn(PtyProcess *ptyproc)
|
|||
uv_connect_t *out_req = NULL;
|
||||
wchar_t *cmd_line = NULL;
|
||||
wchar_t *cwd = NULL;
|
||||
wchar_t *env = NULL;
|
||||
const char *emsg = NULL;
|
||||
|
||||
assert(proc->err.closed);
|
||||
|
@ -124,13 +125,22 @@ int pty_process_spawn(PtyProcess *ptyproc)
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
if (proc->env != NULL) {
|
||||
status = build_env_block(proc->env, &env);
|
||||
}
|
||||
|
||||
if (status != 0) {
|
||||
emsg = "build_env_block failed";
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (ptyproc->type == kConpty) {
|
||||
if (!os_conpty_spawn(conpty_object,
|
||||
&process_handle,
|
||||
NULL,
|
||||
cmd_line,
|
||||
cwd,
|
||||
NULL)) {
|
||||
env)) {
|
||||
emsg = "os_conpty_spawn failed";
|
||||
status = (int)GetLastError();
|
||||
goto cleanup;
|
||||
|
@ -141,7 +151,7 @@ int pty_process_spawn(PtyProcess *ptyproc)
|
|||
NULL, // Optional application name
|
||||
cmd_line,
|
||||
cwd,
|
||||
NULL, // Optional environment variables
|
||||
env,
|
||||
&err);
|
||||
if (spawncfg == NULL) {
|
||||
emsg = "winpty_spawn_config_new failed";
|
||||
|
@ -213,6 +223,7 @@ cleanup:
|
|||
xfree(in_req);
|
||||
xfree(out_req);
|
||||
xfree(cmd_line);
|
||||
xfree(env);
|
||||
xfree(cwd);
|
||||
return status;
|
||||
}
|
||||
|
@ -454,3 +465,66 @@ int translate_winpty_error(int winpty_errno)
|
|||
default: return UV_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct EnvNode {
|
||||
wchar_t *str;
|
||||
size_t len;
|
||||
QUEUE node;
|
||||
} EnvNode;
|
||||
|
||||
/// Build the environment block to pass to CreateProcessW.
|
||||
///
|
||||
/// @param[in] denv Dict of environment name/value pairs
|
||||
/// @param[out] env Allocated environment block
|
||||
///
|
||||
/// @returns zero on success or error code of MultiByteToWideChar function.
|
||||
static int build_env_block(dict_T *denv, wchar_t **env_block)
|
||||
{
|
||||
const size_t denv_size = (size_t)tv_dict_len(denv);
|
||||
size_t env_block_len = 0;
|
||||
int rc;
|
||||
char **env = tv_dict_to_env(denv);
|
||||
|
||||
QUEUE *q;
|
||||
QUEUE env_q;
|
||||
QUEUE_INIT(&env_q);
|
||||
// Convert env vars to wchar_t and calculate how big the final env block
|
||||
// needs to be
|
||||
for (size_t i = 0; i < denv_size; i++) {
|
||||
EnvNode *env_node = xmalloc(sizeof(*env_node));
|
||||
rc = utf8_to_utf16(env[i], -1, &env_node->str);
|
||||
if (rc != 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
env_node->len = wcslen(env_node->str) + 1;
|
||||
env_block_len += env_node->len;
|
||||
QUEUE_INSERT_TAIL(&env_q, &env_node->node);
|
||||
}
|
||||
|
||||
// Additional '\0' after the final entry
|
||||
env_block_len++;
|
||||
|
||||
*env_block = xmalloc(sizeof(**env_block) * env_block_len);
|
||||
wchar_t *pos = *env_block;
|
||||
|
||||
QUEUE_FOREACH(q, &env_q) {
|
||||
EnvNode *env_node = QUEUE_DATA(q, EnvNode, node);
|
||||
memcpy(pos, env_node->str, env_node->len * sizeof(*pos));
|
||||
pos += env_node->len;
|
||||
}
|
||||
|
||||
*pos = L'\0';
|
||||
|
||||
cleanup:
|
||||
q = QUEUE_HEAD(&env_q);
|
||||
while (q != &env_q) {
|
||||
QUEUE *next = q->next;
|
||||
EnvNode *env_node = QUEUE_DATA(q, EnvNode, node);
|
||||
XFREE_CLEAR(env_node->str);
|
||||
QUEUE_REMOVE(q);
|
||||
xfree(env_node);
|
||||
q = next;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ void os_microdelay(uint64_t us, bool ignoreinput)
|
|||
|
||||
const int rv = uv_cond_timedwait(&delay_cond, &delay_mutex, ns_delta);
|
||||
if (0 != rv && UV_ETIMEDOUT != rv) {
|
||||
assert(false);
|
||||
abort();
|
||||
break;
|
||||
} // Else: Timeout proceeded normally.
|
||||
|
||||
|
|
|
@ -5648,7 +5648,7 @@ static int get_qfline_items(qfline_T *qfp, list_T *list)
|
|||
== FAIL)) {
|
||||
// tv_dict_add* fail only if key already exist, but this is a newly
|
||||
// allocated dictionary which is thus guaranteed to have no existing keys.
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
|
||||
return OK;
|
||||
|
|
|
@ -765,7 +765,7 @@ static ShaDaReadResult sd_reader_skip(ShaDaReadDef *const sd_reader,
|
|||
(uint64_t) offset);
|
||||
return kSDReadStatusNotShaDa;
|
||||
}
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
return kSDReadStatusSuccess;
|
||||
}
|
||||
|
@ -1224,7 +1224,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
|
|||
}
|
||||
case kSDReadStatusFinished: {
|
||||
// Should be handled by the while condition.
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
case kSDReadStatusNotShaDa:
|
||||
case kSDReadStatusReadError: {
|
||||
|
@ -1236,7 +1236,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
|
|||
}
|
||||
switch (cur_entry.type) {
|
||||
case kSDItemMissing: {
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
case kSDItemUnknown: {
|
||||
break;
|
||||
|
@ -1628,7 +1628,7 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
|
|||
((size_t) (!CHECK_DEFAULT(entry, attr)))
|
||||
switch (entry.type) {
|
||||
case kSDItemMissing: {
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
case kSDItemUnknown: {
|
||||
if (spacker->callback(spacker->data, entry.data.unknown_item.contents,
|
||||
|
@ -1850,7 +1850,7 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
|
|||
break;
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2147,7 +2147,7 @@ static inline ShaDaWriteResult shada_read_when_writing(
|
|||
}
|
||||
case kSDReadStatusFinished: {
|
||||
// Should be handled by the while condition.
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
case kSDReadStatusNotShaDa: {
|
||||
ret = kSDWriteReadNotShada;
|
||||
|
@ -2184,7 +2184,7 @@ static inline ShaDaWriteResult shada_read_when_writing(
|
|||
}
|
||||
case kSDItemHeader:
|
||||
case kSDItemBufferList: {
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
case kSDItemUnknown: {
|
||||
ret = shada_pack_entry(packer, entry, 0);
|
||||
|
@ -4044,7 +4044,7 @@ shada_read_next_item_start:
|
|||
}
|
||||
case kSDItemMissing:
|
||||
case kSDItemUnknown: {
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
entry->type = (ShadaEntryType) type_u64;
|
||||
|
|
|
@ -1461,7 +1461,7 @@ find_tags(
|
|||
p_ic = ignorecase_opt(pat, true, true);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
|
||||
help_save = curbuf->b_help;
|
||||
|
|
|
@ -297,7 +297,7 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key)
|
|||
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Release");
|
||||
break;
|
||||
case TERMKEY_MOUSE_UNKNOWN:
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
|
||||
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "><%d,%d>", col, row);
|
||||
|
|
|
@ -157,7 +157,7 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width,
|
|||
#ifndef NDEBUG
|
||||
for (size_t i = 0; i < kv_size(layers); i++) {
|
||||
if (kv_A(layers, i) == grid) {
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -2078,7 +2078,7 @@ viml_pexpr_parse_process_token:
|
|||
case kExprLexMissing:
|
||||
case kExprLexSpacing:
|
||||
case kExprLexEOC: {
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
case kExprLexInvalid: {
|
||||
ERROR_FROM_TOKEN(cur_token);
|
||||
|
@ -3028,7 +3028,7 @@ viml_pexpr_parse_end:
|
|||
// Until trailing "}" it is impossible to distinguish curly braces
|
||||
// identifier and dictionary, so it must not appear in the stack like
|
||||
// this.
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
case kExprNodeInteger:
|
||||
case kExprNodeFloat:
|
||||
|
@ -3042,7 +3042,7 @@ viml_pexpr_parse_end:
|
|||
// These are plain values and not containers, for them it should only
|
||||
// be possible to show up in the topmost stack element, but it was
|
||||
// unconditionally popped at the start.
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
case kExprNodeComma:
|
||||
case kExprNodeColon:
|
||||
|
|
|
@ -31,9 +31,9 @@ describe('jobs', function()
|
|||
nvim('set_var', 'channel', channel)
|
||||
source([[
|
||||
function! Normalize(data) abort
|
||||
" Windows: remove ^M
|
||||
" Windows: remove ^M and term escape sequences
|
||||
return type([]) == type(a:data)
|
||||
\ ? map(a:data, 'substitute(v:val, "\r", "", "g")')
|
||||
\ ? map(a:data, 'substitute(substitute(v:val, "\r", "", "g"), "\x1b\\%(\\]\\d\\+;.\\{-}\x07\\|\\[.\\{-}[\x40-\x7E]\\)", "", "g")')
|
||||
\ : a:data
|
||||
endfunction
|
||||
function! OnEvent(id, data, event) dict
|
||||
|
@ -63,6 +63,7 @@ describe('jobs', function()
|
|||
|
||||
it('append environment #env', function()
|
||||
nvim('command', "let $VAR = 'abc'")
|
||||
nvim('command', "let $TOTO = 'goodbye world'")
|
||||
nvim('command', "let g:job_opts.env = {'TOTO': 'hello world'}")
|
||||
if iswin() then
|
||||
nvim('command', [[call jobstart('echo %TOTO% %VAR%', g:job_opts)]])
|
||||
|
@ -75,8 +76,24 @@ describe('jobs', function()
|
|||
})
|
||||
end)
|
||||
|
||||
it('append environment with pty #env', function()
|
||||
nvim('command', "let $VAR = 'abc'")
|
||||
nvim('command', "let $TOTO = 'goodbye world'")
|
||||
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 $TOTO = 'goodbye world'")
|
||||
nvim('command', "let g:job_opts.env = {'TOTO': 'hello world'}")
|
||||
nvim('command', "let g:job_opts.clear_env = 1")
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len,
|
|||
goto vim_str2nr_dec;
|
||||
}
|
||||
default: {
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
} else if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN))
|
||||
|
@ -102,7 +102,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len,
|
|||
}
|
||||
|
||||
// Do the string-to-numeric conversion "manually" to avoid sscanf quirks.
|
||||
assert(false); // Should’ve used goto earlier.
|
||||
abort(); // Should’ve used goto earlier.
|
||||
#define PARSE_NUMBER(base, cond, conv) \
|
||||
do { \
|
||||
while (!STRING_ENDED(ptr) && (cond)) { \
|
||||
|
|
|
@ -45,7 +45,7 @@ void xfree(void *const p)
|
|||
return;
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
|
||||
void *xrealloc(void *const p, size_t new_size)
|
||||
|
@ -63,7 +63,7 @@ void *xrealloc(void *const p, size_t new_size)
|
|||
return ret;
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
abort();
|
||||
return (void *)(intptr_t)1;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue