aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval')
-rw-r--r--src/nvim/eval/decode.c4
-rw-r--r--src/nvim/eval/encode.c4
-rw-r--r--src/nvim/eval/executor.c2
-rw-r--r--src/nvim/eval/funcs.c173
-rw-r--r--src/nvim/eval/typval.c37
5 files changed, 166 insertions, 54 deletions
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c
index 638fef331a..bd4dc87d31 100644
--- a/src/nvim/eval/decode.c
+++ b/src/nvim/eval/decode.c
@@ -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 {
diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c
index 9a9f2e4287..a4d7af7971 100644
--- a/src/nvim/eval/encode.c
+++ b/src/nvim/eval/encode.c
@@ -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;
diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c
index da05ecda43..bbba9d12f2 100644
--- a/src/nvim/eval/executor.c
+++ b/src/nvim/eval/executor.c
@@ -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();
}
}
}
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 8235d74cbb..1a7bbf2d0e 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -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;
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 02d32a4f86..9be487f4fd 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -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;
}