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
commit e85c8dff69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 336 additions and 121 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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); // Shouldve used goto earlier.
abort(); // Shouldve used goto earlier.
#define PARSE_NUMBER(base, cond, conv) \
do { \
while (!STRING_ENDED(ptr) && (cond)) { \

View File

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

View File

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

View File

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

View File

@ -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();
}
}
}

View File

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

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);
}
/// 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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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();
}
}

View File

@ -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.
}

View File

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

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);
if (0 != rv && UV_ETIMEDOUT != rv) {
assert(false);
abort();
break;
} // Else: Timeout proceeded normally.

View File

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

View File

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

View File

@ -1461,7 +1461,7 @@ find_tags(
p_ic = ignorecase_opt(pat, true, true);
break;
default:
assert(false);
abort();
}
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");
break;
case TERMKEY_MOUSE_UNKNOWN:
assert(false);
abort();
}
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
for (size_t i = 0; i < kv_size(layers); i++) {
if (kv_A(layers, i) == grid) {
assert(false);
abort();
}
}
#endif

View File

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

View File

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

View File

@ -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); // Shouldve used goto earlier.
abort(); // Shouldve used goto earlier.
#define PARSE_NUMBER(base, cond, conv) \
do { \
while (!STRING_ENDED(ptr) && (cond)) { \

View File

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