Merge pull request #12937 from jamessan/term-env

This commit is contained in:
James McCoy
2021-01-31 13:25:16 -05:00
committed by GitHub
30 changed files with 336 additions and 121 deletions

View File

@@ -28,7 +28,7 @@ tasks:
gmake deps gmake deps
- build: | - build: |
cd neovim 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: | - functionaltest: |
cd neovim cd neovim
gmake functionaltest gmake functionaltest

View File

@@ -28,16 +28,16 @@ tasks:
mkdir neovim/.deps mkdir neovim/.deps
cd neovim/.deps cd neovim/.deps
cmake -G Ninja ../third-party/ cmake -G Ninja ../third-party/
cmake --build . --config Debug cmake --build . --config RelWithDebInfo
- build: | - build: |
mkdir neovim/build mkdir neovim/build
cd neovim/build cd neovim/build
cmake -G Ninja $CMAKE_EXTRA_FLAGS .. cmake -G Ninja $CMAKE_EXTRA_FLAGS ..
cmake --build . --config Debug cmake --build . --config RelWithDebInfo
./bin/nvim --version ./bin/nvim --version
- functionaltest: | - functionaltest: |
cd neovim/build cd neovim/build
cmake --build . --config Debug --target functionaltest cmake --build . --config RelWithDebInfo --target functionaltest
- oldtest: | - oldtest: |
cd neovim cd neovim
gmake oldtest gmake oldtest

View File

@@ -3270,7 +3270,7 @@ void maketitle(void)
case 6: buf_p = strappend(buf_p, " -"); break; case 6: buf_p = strappend(buf_p, " -"); break;
case 5: case 5:
case 7: buf_p = strappend(buf_p, " -+"); break; case 7: buf_p = strappend(buf_p, " -+"); break;
default: assert(false); default: abort();
} }
if (curbuf->b_fname != NULL) { if (curbuf->b_fname != NULL) {

View File

@@ -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) {

View File

@@ -1747,7 +1747,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len,
goto vim_str2nr_dec; goto vim_str2nr_dec;
} }
default: { default: {
assert(false); abort();
} }
} }
} else if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN)) } 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. // Do the string-to-numeric conversion "manually" to avoid sscanf quirks.
assert(false); // Shouldve used goto earlier. abort(); // Shouldve used goto earlier.
#define PARSE_NUMBER(base, cond, conv) \ #define PARSE_NUMBER(base, cond, conv) \
do { \ do { \
while (!STRING_ENDED(ptr) && (cond)) { \ while (!STRING_ENDED(ptr) && (cond)) { \

View File

@@ -365,7 +365,7 @@ void eval_init(void)
eval_msgpack_type_lists[i] = type_list; eval_msgpack_type_lists[i] = type_list;
if (tv_dict_add(msgpack_types_dict, di) == FAIL) { if (tv_dict_add(msgpack_types_dict, di) == FAIL) {
// There must not be duplicate items in this dictionary by definition. // There must not be duplicate items in this dictionary by definition.
assert(false); abort();
} }
} }
msgpack_types_dict->dv_lock = VAR_FIXED; msgpack_types_dict->dv_lock = VAR_FIXED;

View File

@@ -147,7 +147,7 @@ static inline int json_decoder_pop(ValuesStackItem obj,
tv_clear(&key.val); tv_clear(&key.val);
if (tv_dict_add(last_container.container.vval.v_dict, obj_di) if (tv_dict_add(last_container.container.vval.v_dict, obj_di)
== FAIL) { == FAIL) {
assert(false); abort();
} }
obj_di->di_tv = obj.val; obj_di->di_tv = obj.val;
} else { } else {
@@ -480,7 +480,7 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len,
break; break;
} }
default: { default: {
assert(false); abort();
} }
} }
} else { } else {

View File

@@ -174,7 +174,7 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack,
case kMPConvPartial: { case kMPConvPartial: {
switch (v.data.p.stage) { switch (v.data.p.stage) {
case kMPConvPartialArgs: { case kMPConvPartialArgs: {
assert(false); abort();
break; break;
} }
case kMPConvPartialSelf: { 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); char *const buf = xmalloc(len);
size_t read_bytes; size_t read_bytes;
if (encode_read_from_list(&lrstate, buf, len, &read_bytes) != OK) { if (encode_read_from_list(&lrstate, buf, len, &read_bytes) != OK) {
assert(false); abort();
} }
assert(len == read_bytes); assert(len == read_bytes);
*ret_buf = buf; *ret_buf = buf;

View File

@@ -118,7 +118,7 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2,
return OK; return OK;
} }
case VAR_UNKNOWN: { case VAR_UNKNOWN: {
assert(false); abort();
} }
} }
} }

View File

@@ -1798,7 +1798,7 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr)
os_copy_fullenv(env, env_size); 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 * str = env[i];
const char * const end = strchr(str + (str[0] == '=' ? 1 : 0), 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; ptrdiff_t len = end - str;
assert(len > 0); assert(len > 0);
const char * value = str + len + 1; 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, tv_dict_add_str(rettv->vval.v_dict,
str, len, str, len,
value); value);
@@ -3301,7 +3307,7 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} }
break; break;
case kCdScopeInvalid: // We should never get here case kCdScopeInvalid: // We should never get here
assert(false); abort();
} }
if (from) { if (from) {
@@ -4354,7 +4360,7 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
break; break;
case kCdScopeInvalid: case kCdScopeInvalid:
// We should never get here // 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; 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 // "jobstart()" function
static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) 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; 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.
@@ -4911,6 +5006,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;
@@ -4936,7 +5032,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,52 +5041,22 @@ 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"));
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); job_env = tv_dict_find(job_opts, S_LEN("env"));
size_t i = 0; if (job_env && job_env->di_tv.v_type != VAR_DICT) {
size_t env_size = 0; EMSG2(_(e_invarg2), "env");
shell_free_argv(argv);
if (clear_env) { return;
// + 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 (!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);
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;
@@ -10518,6 +10584,11 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
Callback on_exit = CALLBACK_NONE; Callback on_exit = CALLBACK_NONE;
dict_T *job_opts = NULL; dict_T *job_opts = NULL;
const char *cwd = "."; 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) { if (argvars[1].v_type == VAR_DICT) {
job_opts = argvars[1].vval.v_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)) { if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) {
shell_free_argv(argv); shell_free_argv(argv);
return; 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)); 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, 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, term_width, curwin->w_height_inner,
xstrdup("xterm-256color"), NULL, xstrdup("xterm-256color"), env,
&rettv->vval.v_number); &rettv->vval.v_number);
if (rettv->vval.v_number <= 0) { if (rettv->vval.v_number <= 0) {
return; return;

View File

@@ -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.
@@ -2494,7 +2521,7 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock)
break; break;
} }
case VAR_UNKNOWN: { case VAR_UNKNOWN: {
assert(false); abort();
} }
} }
#undef CHANGE_LOCK #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; return false;
} }
@@ -2719,7 +2746,7 @@ bool tv_check_str_or_nr(const typval_T *const tv)
return false; return false;
} }
} }
assert(false); abort();
return false; return false;
} }
@@ -2764,7 +2791,7 @@ bool tv_check_num(const typval_T *const tv)
return false; return false;
} }
} }
assert(false); abort();
return false; return false;
} }
@@ -2809,7 +2836,7 @@ bool tv_check_str(const typval_T *const tv)
return false; return false;
} }
} }
assert(false); abort();
return false; return false;
} }

View File

@@ -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
@@ -78,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;
} }
@@ -97,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)

View File

@@ -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;

View File

@@ -7591,7 +7591,7 @@ void post_chdir(CdScope scope, bool trigger_dirchanged)
curwin->w_localdir = (char_u *)xstrdup(cwd); curwin->w_localdir = (char_u *)xstrdup(cwd);
break; break;
case kCdScopeInvalid: case kCdScopeInvalid:
assert(false); abort();
} }
shorten_fnames(true); shorten_fnames(true);

View File

@@ -6383,7 +6383,7 @@ int hist_type2char(int type)
return '>'; return '>';
} }
default: { default: {
assert(false); abort();
} }
} }
return NUL; return NUL;

View File

@@ -1595,7 +1595,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, bool changed_window)
} }
case kCdScopeInvalid: { case kCdScopeInvalid: {
// Should never happen. // Should never happen.
assert(false); abort();
} }
} }

View File

@@ -245,7 +245,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
} else { } else {
dictitem_T *const di = tv_dict_item_alloc_len(s, len); dictitem_T *const di = tv_dict_item_alloc_len(s, len);
if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) { if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) {
assert(false); abort();
} }
kv_push(stack, cur); kv_push(stack, cur);
cur = (TVPopStackItem) { &di->di_tv, false, false, 0 }; cur = (TVPopStackItem) { &di->di_tv, false, false, 0 };
@@ -391,7 +391,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
break; break;
} }
default: { default: {
assert(false); abort();
} }
} }
nlua_pop_typval_table_processing_end: nlua_pop_typval_table_processing_end:
@@ -1200,7 +1200,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err)
break; break;
} }
default: { default: {
assert(false); abort();
} }
} }
break; break;

View File

@@ -2623,7 +2623,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
} }
// NOTREACHED // NOTREACHED
case kMTUnknown: case kMTUnknown:
assert(false); abort();
} }
} }
@@ -6092,7 +6092,7 @@ static void set_clipboard(int name, yankreg_T *reg)
break; break;
} }
case kMTUnknown: { case kMTUnknown: {
assert(false); abort();
} }
} }

View File

@@ -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"
@@ -154,28 +158,14 @@ void pty_process_teardown(Loop *loop)
static void init_child(PtyProcess *ptyproc) static void init_child(PtyProcess *ptyproc)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
#if defined(HAVE__NSGETENVIRON)
#define 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 +180,14 @@ 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);
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); 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.
} }

View File

@@ -52,6 +52,7 @@ int pty_process_spawn(PtyProcess *ptyproc)
uv_connect_t *out_req = NULL; uv_connect_t *out_req = NULL;
wchar_t *cmd_line = NULL; wchar_t *cmd_line = NULL;
wchar_t *cwd = NULL; wchar_t *cwd = NULL;
wchar_t *env = NULL;
const char *emsg = NULL; const char *emsg = NULL;
assert(proc->err.closed); assert(proc->err.closed);
@@ -124,13 +125,22 @@ int pty_process_spawn(PtyProcess *ptyproc)
goto cleanup; 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 (ptyproc->type == kConpty) {
if (!os_conpty_spawn(conpty_object, if (!os_conpty_spawn(conpty_object,
&process_handle, &process_handle,
NULL, NULL,
cmd_line, cmd_line,
cwd, cwd,
NULL)) { env)) {
emsg = "os_conpty_spawn failed"; emsg = "os_conpty_spawn failed";
status = (int)GetLastError(); status = (int)GetLastError();
goto cleanup; goto cleanup;
@@ -141,7 +151,7 @@ int pty_process_spawn(PtyProcess *ptyproc)
NULL, // Optional application name NULL, // Optional application name
cmd_line, cmd_line,
cwd, cwd,
NULL, // Optional environment variables env,
&err); &err);
if (spawncfg == NULL) { if (spawncfg == NULL) {
emsg = "winpty_spawn_config_new failed"; emsg = "winpty_spawn_config_new failed";
@@ -213,6 +223,7 @@ cleanup:
xfree(in_req); xfree(in_req);
xfree(out_req); xfree(out_req);
xfree(cmd_line); xfree(cmd_line);
xfree(env);
xfree(cwd); xfree(cwd);
return status; return status;
} }
@@ -454,3 +465,66 @@ int translate_winpty_error(int winpty_errno)
default: return UV_UNKNOWN; 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;
}

View File

@@ -97,7 +97,7 @@ void os_microdelay(uint64_t us, bool ignoreinput)
const int rv = uv_cond_timedwait(&delay_cond, &delay_mutex, ns_delta); const int rv = uv_cond_timedwait(&delay_cond, &delay_mutex, ns_delta);
if (0 != rv && UV_ETIMEDOUT != rv) { if (0 != rv && UV_ETIMEDOUT != rv) {
assert(false); abort();
break; break;
} // Else: Timeout proceeded normally. } // Else: Timeout proceeded normally.

View File

@@ -5648,7 +5648,7 @@ static int get_qfline_items(qfline_T *qfp, list_T *list)
== FAIL)) { == FAIL)) {
// tv_dict_add* fail only if key already exist, but this is a newly // 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. // allocated dictionary which is thus guaranteed to have no existing keys.
assert(false); abort();
} }
return OK; return OK;

View File

@@ -765,7 +765,7 @@ static ShaDaReadResult sd_reader_skip(ShaDaReadDef *const sd_reader,
(uint64_t) offset); (uint64_t) offset);
return kSDReadStatusNotShaDa; return kSDReadStatusNotShaDa;
} }
assert(false); abort();
} }
return kSDReadStatusSuccess; return kSDReadStatusSuccess;
} }
@@ -1224,7 +1224,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
} }
case kSDReadStatusFinished: { case kSDReadStatusFinished: {
// Should be handled by the while condition. // Should be handled by the while condition.
assert(false); abort();
} }
case kSDReadStatusNotShaDa: case kSDReadStatusNotShaDa:
case kSDReadStatusReadError: { case kSDReadStatusReadError: {
@@ -1236,7 +1236,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
} }
switch (cur_entry.type) { switch (cur_entry.type) {
case kSDItemMissing: { case kSDItemMissing: {
assert(false); abort();
} }
case kSDItemUnknown: { case kSDItemUnknown: {
break; break;
@@ -1628,7 +1628,7 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
((size_t) (!CHECK_DEFAULT(entry, attr))) ((size_t) (!CHECK_DEFAULT(entry, attr)))
switch (entry.type) { switch (entry.type) {
case kSDItemMissing: { case kSDItemMissing: {
assert(false); abort();
} }
case kSDItemUnknown: { case kSDItemUnknown: {
if (spacker->callback(spacker->data, entry.data.unknown_item.contents, if (spacker->callback(spacker->data, entry.data.unknown_item.contents,
@@ -1850,7 +1850,7 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
break; break;
} }
default: { default: {
assert(false); abort();
} }
} }
} }
@@ -2147,7 +2147,7 @@ static inline ShaDaWriteResult shada_read_when_writing(
} }
case kSDReadStatusFinished: { case kSDReadStatusFinished: {
// Should be handled by the while condition. // Should be handled by the while condition.
assert(false); abort();
} }
case kSDReadStatusNotShaDa: { case kSDReadStatusNotShaDa: {
ret = kSDWriteReadNotShada; ret = kSDWriteReadNotShada;
@@ -2184,7 +2184,7 @@ static inline ShaDaWriteResult shada_read_when_writing(
} }
case kSDItemHeader: case kSDItemHeader:
case kSDItemBufferList: { case kSDItemBufferList: {
assert(false); abort();
} }
case kSDItemUnknown: { case kSDItemUnknown: {
ret = shada_pack_entry(packer, entry, 0); ret = shada_pack_entry(packer, entry, 0);
@@ -4044,7 +4044,7 @@ shada_read_next_item_start:
} }
case kSDItemMissing: case kSDItemMissing:
case kSDItemUnknown: { case kSDItemUnknown: {
assert(false); abort();
} }
} }
entry->type = (ShadaEntryType) type_u64; entry->type = (ShadaEntryType) type_u64;

View File

@@ -1461,7 +1461,7 @@ find_tags(
p_ic = ignorecase_opt(pat, true, true); p_ic = ignorecase_opt(pat, true, true);
break; break;
default: default:
assert(false); abort();
} }
help_save = curbuf->b_help; help_save = curbuf->b_help;

View File

@@ -297,7 +297,7 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key)
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Release"); len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Release");
break; break;
case TERMKEY_MOUSE_UNKNOWN: case TERMKEY_MOUSE_UNKNOWN:
assert(false); abort();
} }
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "><%d,%d>", col, row); len += (size_t)snprintf(buf + len, sizeof(buf) - len, "><%d,%d>", col, row);

View File

@@ -157,7 +157,7 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width,
#ifndef NDEBUG #ifndef NDEBUG
for (size_t i = 0; i < kv_size(layers); i++) { for (size_t i = 0; i < kv_size(layers); i++) {
if (kv_A(layers, i) == grid) { if (kv_A(layers, i) == grid) {
assert(false); abort();
} }
} }
#endif #endif

View File

@@ -2078,7 +2078,7 @@ viml_pexpr_parse_process_token:
case kExprLexMissing: case kExprLexMissing:
case kExprLexSpacing: case kExprLexSpacing:
case kExprLexEOC: { case kExprLexEOC: {
assert(false); abort();
} }
case kExprLexInvalid: { case kExprLexInvalid: {
ERROR_FROM_TOKEN(cur_token); ERROR_FROM_TOKEN(cur_token);
@@ -3028,7 +3028,7 @@ viml_pexpr_parse_end:
// Until trailing "}" it is impossible to distinguish curly braces // Until trailing "}" it is impossible to distinguish curly braces
// identifier and dictionary, so it must not appear in the stack like // identifier and dictionary, so it must not appear in the stack like
// this. // this.
assert(false); abort();
} }
case kExprNodeInteger: case kExprNodeInteger:
case kExprNodeFloat: case kExprNodeFloat:
@@ -3042,7 +3042,7 @@ viml_pexpr_parse_end:
// These are plain values and not containers, for them it should only // 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 // be possible to show up in the topmost stack element, but it was
// unconditionally popped at the start. // unconditionally popped at the start.
assert(false); abort();
} }
case kExprNodeComma: case kExprNodeComma:
case kExprNodeColon: case kExprNodeColon:

View File

@@ -31,9 +31,9 @@ describe('jobs', function()
nvim('set_var', 'channel', channel) nvim('set_var', 'channel', channel)
source([[ source([[
function! Normalize(data) abort function! Normalize(data) abort
" Windows: remove ^M " Windows: remove ^M and term escape sequences
return type([]) == type(a:data) 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 \ : a:data
endfunction endfunction
function! OnEvent(id, data, event) dict function! OnEvent(id, data, event) dict
@@ -63,6 +63,7 @@ describe('jobs', function()
it('append environment #env', function() it('append environment #env', function()
nvim('command', "let $VAR = 'abc'") 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.env = {'TOTO': 'hello world'}")
if iswin() then if iswin() then
nvim('command', [[call jobstart('echo %TOTO% %VAR%', g:job_opts)]]) nvim('command', [[call jobstart('echo %TOTO% %VAR%', g:job_opts)]])
@@ -75,8 +76,24 @@ describe('jobs', function()
}) })
end) 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() it('replace environment #env', function()
nvim('command', "let $VAR = 'abc'") 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.env = {'TOTO': 'hello world'}")
nvim('command', "let g:job_opts.clear_env = 1") nvim('command', "let g:job_opts.clear_env = 1")

View File

@@ -62,7 +62,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len,
goto vim_str2nr_dec; goto vim_str2nr_dec;
} }
default: { default: {
assert(false); abort();
} }
} }
} else if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN)) } 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. // Do the string-to-numeric conversion "manually" to avoid sscanf quirks.
assert(false); // Shouldve used goto earlier. abort(); // Shouldve used goto earlier.
#define PARSE_NUMBER(base, cond, conv) \ #define PARSE_NUMBER(base, cond, conv) \
do { \ do { \
while (!STRING_ENDED(ptr) && (cond)) { \ while (!STRING_ENDED(ptr) && (cond)) { \

View File

@@ -45,7 +45,7 @@ void xfree(void *const p)
return; return;
} }
} }
assert(false); abort();
} }
void *xrealloc(void *const p, size_t new_size) void *xrealloc(void *const p, size_t new_size)
@@ -63,7 +63,7 @@ void *xrealloc(void *const p, size_t new_size)
return ret; return ret;
} }
} }
assert(false); abort();
return (void *)(intptr_t)1; return (void *)(intptr_t)1;
} }