fix(env.c): drop envmap, free os_getenv() result #32683

Problem:
vim.uv.os_setenv gets "stuck" per-key. #32550
Caused by the internal `envmap` cache. #7920

    :echo $FOO  <-- prints nothing
    :lua vim.uv.os_setenv("FOO", "bar")
    :echo $FOO  <-- prints bar (as expected)
    :lua vim.uv.os_setenv("FOO", "fizz")
    :echo $FOO  <-- prints bar, still (not expected. Should be "fizz")
    :lua vim.uv.os_unsetenv("FOO")
    :echo $FOO  <-- prints bar, still (not expected. Should be nothing)
    :lua vim.uv.os_setenv("FOO", "buzz")
    :echo $FOO  <-- prints bar, still (not expected. Should be "buzz")

Solution:
- Remove the `envmap` cache.
  - Callers to `os_getenv` must free the result.
- Update all call-sites.
- Introduce `os_getenv_noalloc`.
- Extend `os_env_exists()` the `nonempty` parameter.
This commit is contained in:
Judit Novak
2025-04-16 12:36:07 +02:00
committed by GitHub
parent 7432781e71
commit fd973c0a4e
26 changed files with 281 additions and 146 deletions

View File

@ -1143,7 +1143,7 @@ static int diff_file(diffio_T *dio)
char *const cmd = xmalloc(len);
// We don't want $DIFF_OPTIONS to get in the way.
if (os_getenv("DIFF_OPTIONS")) {
if (os_env_exists("DIFF_OPTIONS", true)) {
os_unsetenv("DIFF_OPTIONS");
}

View File

@ -1556,7 +1556,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
const char *p = tv_get_string(&argvars[0]);
if (*p == '$') { // Environment variable.
// First try "normal" environment variables (fast).
if (os_env_exists(p + 1)) {
if (os_env_exists(p + 1, false)) {
n = true;
} else {
// Try expanding things like $VIM and ${HOME}.
@ -3881,9 +3881,9 @@ dict_T *create_environment(const dictitem_T *job_env, const bool clear_env, cons
size_t len = strlen(required_env_vars[i]);
dictitem_T *dv = tv_dict_find(env, required_env_vars[i], (ptrdiff_t)len);
if (!dv) {
const char *env_var = os_getenv(required_env_vars[i]);
char *env_var = os_getenv(required_env_vars[i]);
if (env_var) {
tv_dict_add_str(env, required_env_vars[i], len, env_var);
tv_dict_add_allocated_str(env, required_env_vars[i], len, env_var);
}
}
}

View File

@ -7810,8 +7810,8 @@ static void ex_checkhealth(exarg_T *eap)
return;
}
const char *vimruntime_env = os_getenv("VIMRUNTIME");
if (vimruntime_env == NULL) {
char *vimruntime_env = os_getenv_noalloc("VIMRUNTIME");
if (!vimruntime_env) {
emsg(_("E5009: $VIMRUNTIME is empty or unset"));
} else {
bool rtp_ok = NULL != strstr(p_rtp, vimruntime_env);

View File

@ -3278,7 +3278,7 @@ static void vim_mktempdir(void)
expand_env((char *)temp_dirs[i], tmp, TEMP_FILE_PATH_MAXLEN - 64);
if (!os_isdir(tmp)) {
if (strequal("$TMPDIR", temp_dirs[i])) {
if (!os_getenv("TMPDIR")) {
if (!os_env_exists("TMPDIR", true)) {
DLOG("$TMPDIR is unset");
} else {
WLOG("$TMPDIR tempdir not a directory (or does not exist): \"%s\"", tmp);

View File

@ -336,7 +336,7 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, const char *context,
// TODO(justinmk): expose this as v:name ?
if (regen) {
// Parent servername ($NVIM).
const char *parent = path_tail(os_getenv(ENV_NVIM));
const char *parent = path_tail(os_getenv_noalloc(ENV_NVIM));
// Servername. Empty until starting=false.
const char *serv = path_tail(get_vim_var_str(VV_SEND_SERVER));
if (parent[0] != NUL) {

View File

@ -831,8 +831,7 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
void nlua_init(char **argv, int argc, int lua_arg0)
{
#ifdef NLUA_TRACK_REFS
const char *env = os_getenv("NVIM_LUA_NOTRACK");
if (!env || !*env) {
if (os_env_exists("NVIM_LUA_NOTRACK", true)) {
nlua_track_refs = true;
}
#endif
@ -1283,7 +1282,7 @@ static int nlua_empty_dict_tostring(lua_State *lstate)
/// @param lstate Lua interpreter state.
static int nlua_getenv(lua_State *lstate)
{
lua_pushstring(lstate, os_getenv(luaL_checkstring(lstate, 1)));
lua_pushstring(lstate, os_getenv_noalloc(luaL_checkstring(lstate, 1)));
return 1;
}
#endif

View File

@ -938,12 +938,11 @@ static void remote_request(mparm_T *params, int remote_args, char *server_addr,
if (!chan) {
fprintf(stderr, "Remote ui failed to start: %s\n", connect_error);
os_exit(1);
} else if (strequal(server_addr, os_getenv("NVIM"))) {
} else if (strequal(server_addr, os_getenv_noalloc("NVIM"))) {
fprintf(stderr, "%s", "Cannot attach UI of :terminal child to its parent. ");
fprintf(stderr, "%s\n", "(Unset $NVIM to skip this check)");
os_exit(1);
}
ui_client_channel_id = chan;
return;
}
@ -2118,7 +2117,7 @@ static void source_startup_scripts(const mparm_T *const parmp)
static int execute_env(char *env)
FUNC_ATTR_NONNULL_ALL
{
const char *initstr = os_getenv(env);
char *initstr = os_getenv(env);
if (initstr == NULL) {
return FAIL;
}
@ -2132,6 +2131,8 @@ static int execute_env(char *env)
estack_pop();
current_sctx = save_current_sctx;
xfree(initstr);
return OK;
}

View File

@ -2412,14 +2412,15 @@ char *enc_locale(void)
char buf[50];
const char *s;
#ifdef HAVE_NL_LANGINFO_CODESET
if (!(s = nl_langinfo(CODESET)) || *s == NUL)
#endif
{
if (!(s = setlocale(LC_CTYPE, NULL)) || *s == NUL) {
if ((s = os_getenv("LC_ALL"))) {
if ((s = os_getenv("LC_CTYPE"))) {
s = os_getenv("LANG");
if ((s = os_getenv_noalloc("LC_ALL"))) {
if ((s = os_getenv_noalloc("LC_CTYPE"))) {
s = os_getenv_noalloc("LANG");
}
}
}

View File

@ -788,7 +788,6 @@ void free_all_mem(void)
free_all_marks();
alist_clear(&global_alist);
free_homedir();
free_envmap();
free_users();
free_search_patterns();
free_old_sub();

View File

@ -41,28 +41,24 @@ bool server_init(const char *listen_addr)
ga_init(&watchers, sizeof(SocketWatcher *), 1);
// $NVIM_LISTEN_ADDRESS (deprecated)
if ((!listen_addr || listen_addr[0] == '\0') && os_env_exists(ENV_LISTEN)) {
user_arg = kFalse; // User-provided env var.
listen_addr = os_getenv(ENV_LISTEN);
}
if (!listen_addr || listen_addr[0] == '\0') {
user_arg = kNone; // Autogenerated server address.
listen_addr = server_address_new(NULL);
if (os_env_exists(ENV_LISTEN, true)) {
user_arg = kFalse; // User-provided env var.
listen_addr = os_getenv(ENV_LISTEN);
} else {
user_arg = kNone; // Autogenerated server address.
listen_addr = server_address_new(NULL);
}
must_free = true;
}
int rv = server_start(listen_addr);
// TODO(justinmk): this is for log_spec. Can remove this after nvim_log #7062 is merged.
if (os_env_exists("__NVIM_TEST_LOG")) {
if (os_env_exists("__NVIM_TEST_LOG", false)) {
ELOG("test log message");
}
if (must_free) {
xfree((char *)listen_addr);
}
if (rv == 0 || user_arg == kNone) {
// The autogenerated servername can fail if the user has a broken $XDG_RUNTIME_DIR. #30282
// But that is not fatal (startup will continue, logged in $NVIM_LOGFILE, empty v:servername).
@ -78,12 +74,16 @@ bool server_init(const char *listen_addr)
ok = false;
end:
if (os_env_exists(ENV_LISTEN)) {
if (os_env_exists(ENV_LISTEN, false)) {
// Unset $NVIM_LISTEN_ADDRESS, it's a liability hereafter. It is "input only", it must not be
// leaked to child jobs or :terminal.
os_unsetenv(ENV_LISTEN);
}
if (must_free) {
xfree((char *)listen_addr);
}
return ok;
}
@ -120,12 +120,12 @@ char *server_address_new(const char *name)
static uint32_t count = 0;
char fmt[ADDRESS_MAX_SIZE];
#ifdef MSWIN
(void)get_appname(true);
(void)get_appname(true); // Writes appname to NameBuf.
int r = snprintf(fmt, sizeof(fmt), "\\\\.\\pipe\\%s.%" PRIu64 ".%" PRIu32,
name ? name : NameBuff, os_get_pid(), count++);
#else
char *dir = stdpaths_get_xdg_var(kXDGRuntimeDir);
(void)get_appname(true);
(void)get_appname(true); // Writes appname to NameBuf.
int r = snprintf(fmt, sizeof(fmt), "%s/%s.%" PRIu64 ".%" PRIu32,
dir, name ? name : NameBuff, os_get_pid(), count++);
xfree(dir);

View File

@ -190,7 +190,7 @@ static void set_init_default_shell(void)
{
// Find default value for 'shell' option.
// Don't use it if it is empty.
const char *shell = os_getenv("SHELL");
char *shell = os_getenv("SHELL");
if (shell != NULL) {
if (vim_strchr(shell, ' ') != NULL) {
const size_t len = strlen(shell) + 3; // two quotes and a trailing NUL
@ -198,8 +198,9 @@ static void set_init_default_shell(void)
snprintf(cmd, len, "\"%s\"", shell);
set_string_default(kOptShell, cmd, true);
} else {
set_string_default(kOptShell, (char *)shell, false);
set_string_default(kOptShell, shell, false);
}
xfree(shell);
}
}
@ -413,7 +414,7 @@ void set_init_1(bool clean_arg)
// abilities (bidi namely).
// NOTE: mlterm's author is being asked to 'set' a variable
// instead of an environment variable due to inheritance.
if (os_env_exists("MLTERM")) {
if (os_env_exists("MLTERM", false)) {
set_option_value_give_err(kOptTermbidi, BOOLEAN_OPTVAL(true), 0);
}

View File

@ -52,13 +52,11 @@
# include "os/env.c.generated.h"
#endif
// Because `uv_os_getenv` requires allocating, we must manage a map to maintain
// the behavior of `os_getenv`.
static PMap(cstr_t) envmap = MAP_INIT;
/// Like getenv(), but returns NULL if the variable is empty.
/// Result must be freed by the caller.
/// @see os_env_exists
const char *os_getenv(const char *name)
/// @see os_getenv_noalloc
char *os_getenv(const char *name)
FUNC_ATTR_NONNULL_ALL
{
char *e = NULL;
@ -66,17 +64,6 @@ const char *os_getenv(const char *name)
return NULL;
}
int r = 0;
if (map_has(cstr_t, &envmap, name)
&& !!(e = (char *)pmap_get(cstr_t)(&envmap, name))) {
if (e[0] != NUL) {
// Found non-empty cached env var.
// NOTE: This risks incoherence if an in-process library changes the
// environment without going through our os_setenv() wrapper. If
// that turns out to be a problem, we can just remove this codepath.
goto end;
}
pmap_del2(&envmap, name);
}
#define INIT_SIZE 64
size_t size = INIT_SIZE;
char buf[INIT_SIZE];
@ -96,7 +83,6 @@ const char *os_getenv(const char *name)
// except when it does not include the NUL-terminator.
e = xmemdupz(buf, size);
}
pmap_put(cstr_t)(&envmap, xstrdup(name), e);
end:
if (r != 0 && r != UV_ENOENT && r != UV_UNKNOWN) {
ELOG("uv_os_getenv(%s) failed: %d %s", name, r, uv_err_name(r));
@ -104,9 +90,43 @@ end:
return e;
}
/// Like getenv(), but returns a pointer to `NameBuff` instead of allocating, or NULL on failure.
/// Value is truncated if it exceeds sizeof(NameBuff).
/// @see os_env_exists
char *os_getenv_noalloc(const char *name)
FUNC_ATTR_NONNULL_ALL
{
if (name[0] == NUL) {
return NULL;
}
size_t size = sizeof(NameBuff);
int r = uv_os_getenv(name, NameBuff, &size);
if (r == UV_ENOBUFS) {
char *e = xmalloc(size);
r = uv_os_getenv(name, e, &size);
if (r == 0 && size != 0 && e[0] != NUL) {
xmemcpyz(NameBuff, e, sizeof(NameBuff) - 1);
}
xfree(e);
}
if (r != 0 || size == 0 || NameBuff[0] == NUL) {
if (r != 0 && r != UV_ENOENT && r != UV_UNKNOWN) {
ELOG("uv_os_getenv(%s) failed: %d %s", name, r, uv_err_name(r));
}
return NULL;
}
return NameBuff;
}
/// Returns true if environment variable `name` is defined (even if empty).
/// Returns false if not found (UV_ENOENT) or other failure.
bool os_env_exists(const char *name)
///
/// @param name the environment variable in question
/// @param nonempty Require a non-empty value. Treat empty as "does not exist".
/// @return whether the variable exists
bool os_env_exists(const char *name, bool nonempty)
FUNC_ATTR_NONNULL_ALL
{
if (name[0] == NUL) {
@ -114,14 +134,14 @@ bool os_env_exists(const char *name)
}
// Use a tiny buffer because we don't care about the value: if uv_os_getenv()
// returns UV_ENOBUFS, the env var was found.
char buf[1];
char buf[2];
size_t size = sizeof(buf);
int r = uv_os_getenv(name, buf, &size);
assert(r != UV_EINVAL);
if (r != 0 && r != UV_ENOENT && r != UV_ENOBUFS) {
ELOG("uv_os_getenv(%s) failed: %d %s", name, r, uv_err_name(r));
}
return (r == 0 || r == UV_ENOBUFS);
return ((r == 0 && (!nonempty || size > 0)) || r == UV_ENOBUFS);
}
/// Sets an environment variable.
@ -137,7 +157,7 @@ int os_setenv(const char *name, const char *value, int overwrite)
return -1;
}
#ifdef MSWIN
if (!overwrite && os_getenv(name) != NULL) {
if (!overwrite && !os_env_exists(name, true)) {
return 0;
}
if (value[0] == NUL) {
@ -145,7 +165,7 @@ int os_setenv(const char *name, const char *value, int overwrite)
return os_unsetenv(name);
}
#else
if (!overwrite && os_env_exists(name)) {
if (!overwrite && os_env_exists(name, false)) {
return 0;
}
#endif
@ -160,9 +180,6 @@ int os_setenv(const char *name, const char *value, int overwrite)
#endif
r = uv_os_setenv(name, value);
assert(r != UV_EINVAL);
// Destroy the old map item. Do this AFTER uv_os_setenv(), because `value`
// could be a previous os_getenv() result.
pmap_del2(&envmap, name);
if (r != 0) {
ELOG("uv_os_setenv(%s) failed: %d %s", name, r, uv_err_name(r));
}
@ -176,7 +193,6 @@ int os_unsetenv(const char *name)
if (name[0] == NUL) {
return -1;
}
pmap_del2(&envmap, name);
int r = uv_os_unsetenv(name);
if (r != 0) {
ELOG("uv_os_unsetenv(%s) failed: %d %s", name, r, uv_err_name(r));
@ -428,7 +444,8 @@ void init_homedir(void)
xfree(homedir);
homedir = NULL;
const char *var = os_getenv("HOME");
char *var = os_getenv("HOME");
char *tofree = var;
#ifdef MSWIN
// Typically, $HOME is not defined on Windows, unless the user has
@ -436,10 +453,10 @@ void init_homedir(void)
// platforms, $HOMEDRIVE and $HOMEPATH are automatically defined for
// each user. Try constructing $HOME from these.
if (var == NULL) {
const char *homedrive = os_getenv("HOMEDRIVE");
const char *homepath = os_getenv("HOMEPATH");
char *homedrive = os_getenv("HOMEDRIVE");
char *homepath = os_getenv("HOMEPATH");
if (homepath == NULL) {
homepath = "\\";
homepath = xstrdup("\\");
}
if (homedrive != NULL
&& strlen(homedrive) + strlen(homepath) < MAXPATHL) {
@ -448,6 +465,8 @@ void init_homedir(void)
var = os_buf;
}
}
xfree(homepath);
xfree(homedrive);
}
if (var == NULL) {
var = os_uv_homedir();
@ -461,11 +480,13 @@ void init_homedir(void)
if (p != NULL) {
vim_snprintf(os_buf, (size_t)(p - var), "%s", var + 1);
var = NULL;
const char *exp = os_getenv(os_buf);
if (exp != NULL && *exp != NUL
&& strlen(exp) + strlen(p) < MAXPATHL) {
vim_snprintf(os_buf, MAXPATHL, "%s%s", exp, p + 1);
var = os_buf;
char *exp = os_getenv(os_buf);
if (exp != NULL) {
if (*exp != NUL && strlen(exp) + strlen(p) < MAXPATHL) {
vim_snprintf(os_buf, MAXPATHL, "%s%s", exp, p + 1);
var = os_buf;
}
xfree(exp);
}
}
}
@ -498,6 +519,7 @@ void init_homedir(void)
if (var != NULL) {
homedir = xstrdup(var);
}
xfree(tofree);
}
static char homedir_buf[MAXPATHL];
@ -523,17 +545,6 @@ void free_homedir(void)
xfree(homedir);
}
void free_envmap(void)
{
cstr_t name;
ptr_t e;
map_foreach(&envmap, name, e, {
xfree((char *)name);
xfree(e);
});
map_destroy(cstr_t, &envmap);
}
#endif
/// Call expand_env() and store the result in an allocated string.
@ -917,9 +928,9 @@ char *vim_getenv(const char *name)
}
#endif
const char *kos_env_path = os_getenv(name);
char *kos_env_path = os_getenv(name);
if (kos_env_path != NULL) {
return xstrdup(kos_env_path);
return kos_env_path;
}
bool vimruntime = (strcmp(name, "VIMRUNTIME") == 0);
@ -932,11 +943,13 @@ char *vim_getenv(const char *name)
char *vim_path = NULL;
if (vimruntime
&& *default_vimruntime_dir == NUL) {
kos_env_path = os_getenv("VIM");
kos_env_path = os_getenv("VIM"); // kos_env_path was NULL.
if (kos_env_path != NULL) {
vim_path = vim_version_dir(kos_env_path);
if (vim_path == NULL) {
vim_path = xstrdup(kos_env_path);
vim_path = kos_env_path;
} else {
xfree(kos_env_path);
}
}
}
@ -1059,13 +1072,13 @@ size_t home_replace(const buf_T *const buf, const char *src, char *const dst, si
dirlen = strlen(homedir);
}
const char *homedir_env = os_getenv("HOME");
char *homedir_env = os_getenv("HOME");
#ifdef MSWIN
if (homedir_env == NULL) {
homedir_env = os_getenv("USERPROFILE");
}
#endif
char *homedir_env_mod = (char *)homedir_env;
char *homedir_env_mod = homedir_env;
bool must_free = false;
if (homedir_env_mod != NULL && *homedir_env_mod == '~') {
@ -1142,6 +1155,8 @@ size_t home_replace(const buf_T *const buf, const char *src, char *const dst, si
*dst_p = NUL;
xfree(homedir_env);
if (must_free) {
xfree(homedir_env_mod);
}
@ -1199,9 +1214,10 @@ bool os_setenv_append_path(const char *fname)
size_t dirlen = (size_t)(tail - fname);
assert(tail >= fname && dirlen + 1 < sizeof(os_buf));
xmemcpyz(os_buf, fname, dirlen);
const char *path = os_getenv("PATH");
char *path = os_getenv("PATH");
const size_t pathlen = path ? strlen(path) : 0;
const size_t newlen = pathlen + dirlen + 2;
bool retval = false;
if (newlen < MAX_ENVPATHLEN) {
char *temp = xmalloc(newlen);
if (pathlen == 0) {
@ -1215,9 +1231,10 @@ bool os_setenv_append_path(const char *fname)
xstrlcat(temp, os_buf, newlen);
os_setenv("PATH", temp, 1);
xfree(temp);
return true;
retval = true;
}
return false;
xfree(path);
return retval;
}
/// Returns true if `sh` looks like it resolves to "cmd.exe".
@ -1228,7 +1245,7 @@ bool os_shell_is_cmdexe(const char *sh)
return false;
}
if (striequal(sh, "$COMSPEC")) {
const char *comspec = os_getenv("COMSPEC");
char *comspec = os_getenv_noalloc("COMSPEC");
return striequal("cmd.exe", path_tail(comspec));
}
if (striequal(sh, "cmd.exe") || striequal(sh, "cmd")) {

View File

@ -303,7 +303,7 @@ static bool is_executable_ext(const char *name, char **abspath)
size_t nameext_len = nameext ? strlen(nameext) : 0;
xstrlcpy(os_buf, name, sizeof(os_buf));
char *buf_end = xstrchrnul(os_buf, NUL);
const char *pathext = os_getenv("PATHEXT");
const char *pathext = os_getenv_noalloc("PATHEXT");
if (!pathext) {
pathext = ".com;.exe;.bat;.cmd";
}
@ -350,14 +350,14 @@ static bool is_executable_ext(const char *name, char **abspath)
static bool is_executable_in_path(const char *name, char **abspath)
FUNC_ATTR_NONNULL_ARG(1)
{
const char *path_env = os_getenv("PATH");
char *path_env = os_getenv("PATH");
if (path_env == NULL) {
return false;
}
#ifdef MSWIN
char *path = NULL;
if (!os_env_exists("NoDefaultCurrentDirectoryInExePath")) {
if (!os_env_exists("NoDefaultCurrentDirectoryInExePath", false)) {
// Prepend ".;" to $PATH.
size_t pathlen = strlen(path_env);
path = xmallocz(pathlen + 2);
@ -404,6 +404,7 @@ static bool is_executable_in_path(const char *name, char **abspath)
end:
xfree(buf);
xfree(path);
xfree(path_env);
return rv;
}

View File

@ -70,6 +70,7 @@ char *get_mess_lang(void)
}
/// Get the language used for messages from the environment.
/// The function may be using NameBuff.
///
/// This uses LC_MESSAGES when available, which it is for most systems we build for
/// except for windows. Then fallback to get the value from the environment
@ -79,17 +80,17 @@ static char *get_mess_env(void)
#ifdef LC_MESSAGES
return get_locale_val(LC_MESSAGES);
#else
char *p = (char *)os_getenv("LC_ALL");
char *p = os_getenv_noalloc("LC_ALL");
if (p != NULL) {
return p;
}
p = (char *)os_getenv("LC_MESSAGES");
p = os_getenv_noalloc("LC_MESSAGES");
if (p != NULL) {
return p;
}
p = (char *)os_getenv("LANG");
p = os_getenv_noalloc("LANG");
if (p != NULL && ascii_isdigit(*p)) {
p = NULL; // ignore something like "1043"
}
@ -340,7 +341,7 @@ char *get_locales(expand_T *xp, int idx)
void lang_init(void)
{
#ifdef __APPLE__
if (os_getenv("LANG") == NULL) {
if (!os_env_exists("LANG", true)) {
char buf[50] = { 0 };
// $LANG is not set, either because it was unset or Nvim was started

View File

@ -82,7 +82,7 @@ void os_icon_init(void)
hOrigIconSmall = (HICON)SendMessage(hWnd, WM_GETICON, (WPARAM)ICON_SMALL, (LPARAM)0);
hOrigIcon = (HICON)SendMessage(hWnd, WM_GETICON, (WPARAM)ICON_BIG, (LPARAM)0);
const char *vimruntime = os_getenv("VIMRUNTIME");
char *vimruntime = os_getenv("VIMRUNTIME");
if (vimruntime != NULL) {
snprintf(NameBuff, MAXPATHL, "%s/neovim.ico", vimruntime);
if (!os_path_exists(NameBuff)) {
@ -92,6 +92,7 @@ void os_icon_init(void)
LR_LOADFROMFILE | LR_LOADMAP3DCOLORS);
os_icon_set(hVimIcon, hVimIcon);
}
xfree(vimruntime);
}
}
@ -117,7 +118,7 @@ void os_title_reset(void)
/// @param out_fd stdout file descriptor
void os_tty_guess_term(const char **term, int out_fd)
{
bool conemu_ansi = strequal(os_getenv("ConEmuANSI"), "ON");
bool conemu_ansi = strequal(os_getenv_noalloc("ConEmuANSI"), "ON");
bool vtp = false;
HANDLE handle = (HANDLE)_get_osfhandle(out_fd);

View File

@ -70,19 +70,19 @@ static const char *const xdg_defaults[] = {
/// @return $NVIM_APPNAME value
const char *get_appname(bool namelike)
{
const char *env_val = os_getenv("NVIM_APPNAME");
if (env_val == NULL || *env_val == NUL) {
env_val = "nvim";
const char *env_val = os_getenv_noalloc("NVIM_APPNAME");
if (!env_val) {
xstrlcpy(NameBuff, "nvim", sizeof(NameBuff));
}
if (namelike) {
// Appname may be a relative path, replace slashes to make it name-like.
xstrlcpy(NameBuff, env_val, sizeof(NameBuff));
memchrsub(NameBuff, '/', '-', sizeof(NameBuff));
memchrsub(NameBuff, '\\', '-', sizeof(NameBuff));
}
return env_val;
return NameBuff;
}
/// Ensure that APPNAME is valid. Must be a name or relative path.
@ -157,21 +157,21 @@ char *stdpaths_get_xdg_var(const XDGVarType idx)
const char *const env = xdg_env_vars[idx];
const char *const fallback = xdg_defaults[idx];
const char *env_val = os_getenv(env);
char *env_val = os_getenv(env);
#ifdef MSWIN
if (env_val == NULL && xdg_defaults_env_vars[idx] != NULL) {
env_val = os_getenv(xdg_defaults_env_vars[idx]);
}
#else
if (env_val == NULL && os_env_exists(env)) {
env_val = "";
if (env_val == NULL && os_env_exists(env, false)) {
env_val = xstrdup("");
}
#endif
char *ret = NULL;
if (env_val != NULL) {
ret = xstrdup(env_val);
ret = env_val;
} else if (fallback) {
ret = expand_env_save((char *)fallback);
} else if (idx == kXDGRuntimeDir) {

View File

@ -98,8 +98,8 @@ struct tm *os_localtime_r(const time_t *restrict clock,
// Call tzset(3) to update the global timezone variables if it has.
// POSIX standard doesn't require localtime_r() implementations to do that
// as it does with localtime(), and we don't want to call tzset() every time.
const char *tz = os_getenv("TZ");
if (tz == NULL) {
const char *tz = os_getenv_noalloc("TZ");
if (!tz) {
tz = "";
}
if (strncmp(tz_cache, tz, sizeof(tz_cache) - 1) != 0) {

View File

@ -89,7 +89,7 @@ int os_get_usernames(garray_T *users)
#endif
#ifdef HAVE_PWD_FUNCS
{
const char *user_env = os_getenv("USER");
char *user_env = os_getenv_noalloc("USER");
// The $USER environment variable may be a valid remote user name (NIS,
// LDAP) not already listed by getpwent(), as getpwent() only lists

View File

@ -2392,7 +2392,7 @@ bool path_is_absolute(const char *fname)
void path_guess_exepath(const char *argv0, char *buf, size_t bufsize)
FUNC_ATTR_NONNULL_ALL
{
const char *path = os_getenv("PATH");
char *path = os_getenv("PATH");
if (path == NULL || path_is_absolute(argv0)) {
xstrlcpy(buf, argv0, bufsize);
@ -2427,4 +2427,5 @@ void path_guess_exepath(const char *argv0, char *buf, size_t bufsize)
// Not found in $PATH, fall back to argv0.
xstrlcpy(buf, argv0, bufsize);
}
xfree(path);
}

View File

@ -1746,8 +1746,7 @@ char *runtimepath_default(bool clean_arg)
size_t config_len = 0;
size_t vimruntime_len = 0;
size_t libdir_len = 0;
const char *appname = get_appname(false);
size_t appname_len = strlen(appname);
size_t appname_len = strlen(get_appname(false));
if (data_home != NULL) {
data_len = strlen(data_home);
size_t nvim_data_size = appname_len;

View File

@ -139,7 +139,8 @@ void tinput_init(TermInput *input, Loop *loop)
input->in_fd = STDIN_FILENO;
const char *term = os_getenv("TERM");
const char *term = os_getenv_noalloc("TERM");
if (!term) {
term = ""; // termkey_new_abstract assumes non-null (#2745)
}

View File

@ -49,7 +49,7 @@ bool terminfo_is_bsd_console(const char *term)
# if defined(__FreeBSD__)
// FreeBSD console sets TERM=xterm, but it does not support xterm features
// like cursor-shaping. Assume that TERM=xterm is degraded. #8644
return strequal(term, "xterm") && !!os_getenv("XTERM_VERSION");
return strequal(term, "xterm") && os_env_exists("XTERM_VERSION", true);
# endif
#endif
return false;

View File

@ -379,7 +379,7 @@ static void terminfo_start(TUIData *tui)
tui->out_isatty = os_isatty(tui->out_fd);
tui->input.tui_data = tui;
const char *term = os_getenv("TERM");
char *term = os_getenv("TERM");
#ifdef MSWIN
os_tty_guess_term(&term, tui->out_fd);
os_setenv("TERM", term, 1);
@ -401,23 +401,25 @@ static void terminfo_start(TUIData *tui)
}
// None of the following work over SSH; see :help TERM .
const char *colorterm = os_getenv("COLORTERM");
const char *termprg = os_getenv("TERM_PROGRAM");
const char *vte_version_env = os_getenv("VTE_VERSION");
char *colorterm = os_getenv("COLORTERM");
char *termprg = os_getenv("TERM_PROGRAM");
char *vte_version_env = os_getenv("VTE_VERSION");
char *konsolev_env = os_getenv("KONSOLE_VERSION");
char *term_program_version_env = os_getenv("TERM_PROGRAM_VERSION");
int vtev = vte_version_env ? (int)strtol(vte_version_env, NULL, 10) : 0;
bool iterm_env = termprg && strstr(termprg, "iTerm.app");
bool nsterm = (termprg && strstr(termprg, "Apple_Terminal"))
|| terminfo_is_term_family(term, "nsterm");
bool konsole = terminfo_is_term_family(term, "konsole")
|| os_getenv("KONSOLE_PROFILE_NAME")
|| os_getenv("KONSOLE_DBUS_SESSION");
const char *konsolev_env = os_getenv("KONSOLE_VERSION");
|| os_env_exists("KONSOLE_PROFILE_NAME", true)
|| os_env_exists("KONSOLE_DBUS_SESSION", true);
int konsolev = konsolev_env ? (int)strtol(konsolev_env, NULL, 10)
: (konsole ? 1 : 0);
bool wezterm = strequal(termprg, "WezTerm");
const char *weztermv = wezterm ? os_getenv("TERM_PROGRAM_VERSION") : NULL;
const char *weztermv = wezterm ? term_program_version_env : NULL;
bool screen = terminfo_is_term_family(term, "screen");
bool tmux = terminfo_is_term_family(term, "tmux") || !!os_getenv("TMUX");
bool tmux = terminfo_is_term_family(term, "tmux") || os_env_exists("TMUX", true);
// truecolor support must be checked before patching/augmenting terminfo
tui->rgb = term_has_truecolor(tui, colorterm);
@ -503,6 +505,13 @@ static void terminfo_start(TUIData *tui)
}
}
flush_buf(tui);
xfree(term);
xfree(colorterm);
xfree(termprg);
xfree(vte_version_env);
xfree(konsolev_env);
xfree(term_program_version_env);
}
/// Disable the alternate screen and prepare for the TUI to close.
@ -1772,6 +1781,8 @@ void tui_guess_size(TUIData *tui)
{
int width = 0;
int height = 0;
char *lines = NULL;
char *columns = NULL;
// 1 - try from a system call (ioctl/TIOCGWINSZ on unix)
if (tui->out_isatty
@ -1782,9 +1793,9 @@ void tui_guess_size(TUIData *tui)
// 2 - use $LINES/$COLUMNS if available
const char *val;
int advance;
if ((val = os_getenv("LINES"))
if ((val = os_getenv_noalloc("LINES"))
&& sscanf(val, "%d%n", &height, &advance) != EOF && advance
&& (val = os_getenv("COLUMNS"))
&& (val = os_getenv_noalloc("COLUMNS"))
&& sscanf(val, "%d%n", &width, &advance) != EOF && advance) {
goto end;
}
@ -1804,6 +1815,9 @@ void tui_guess_size(TUIData *tui)
// Redraw on SIGWINCH event if size didn't change. #23411
ui_client_set_size(width, height);
xfree(lines);
xfree(columns);
}
static void unibi_goto(TUIData *tui, int row, int col)
@ -1965,7 +1979,7 @@ static void patch_terminfo_bugs(TUIData *tui, const char *term, const char *colo
int vte_version, int konsolev, bool iterm_env, bool nsterm)
{
unibi_term *ut = tui->ut;
const char *xterm_version = os_getenv("XTERM_VERSION");
char *xterm_version = os_getenv("XTERM_VERSION");
bool xterm = terminfo_is_term_family(term, "xterm")
// Treat Terminal.app as generic xterm-like, for now.
|| nsterm;
@ -1977,7 +1991,7 @@ static void patch_terminfo_bugs(TUIData *tui, const char *term, const char *colo
bool teraterm = terminfo_is_term_family(term, "teraterm");
bool putty = terminfo_is_term_family(term, "putty");
bool screen = terminfo_is_term_family(term, "screen");
bool tmux = terminfo_is_term_family(term, "tmux") || !!os_getenv("TMUX");
bool tmux = terminfo_is_term_family(term, "tmux") || os_env_exists("TMUX", true);
bool st = terminfo_is_term_family(term, "st");
bool gnome = terminfo_is_term_family(term, "gnome")
|| terminfo_is_term_family(term, "vte");
@ -2276,6 +2290,8 @@ static void patch_terminfo_bugs(TUIData *tui, const char *term, const char *colo
"\x1b]50;\x07");
}
}
xfree(xterm_version);
}
/// This adds stuff that is not in standard terminfo as extended unibilium
@ -2284,6 +2300,7 @@ static void augment_terminfo(TUIData *tui, const char *term, int vte_version, in
const char *weztermv, bool iterm_env, bool nsterm)
{
unibi_term *ut = tui->ut;
char *xterm_version = os_getenv("XTERM_VERSION");
bool xterm = terminfo_is_term_family(term, "xterm")
// Treat Terminal.app as generic xterm-like, for now.
|| nsterm;
@ -2294,7 +2311,7 @@ static void augment_terminfo(TUIData *tui, const char *term, int vte_version, in
bool teraterm = terminfo_is_term_family(term, "teraterm");
bool putty = terminfo_is_term_family(term, "putty");
bool screen = terminfo_is_term_family(term, "screen");
bool tmux = terminfo_is_term_family(term, "tmux") || !!os_getenv("TMUX");
bool tmux = terminfo_is_term_family(term, "tmux") || os_env_exists("TMUX", true);
bool st = terminfo_is_term_family(term, "st");
bool iterm = terminfo_is_term_family(term, "iterm")
|| terminfo_is_term_family(term, "iterm2")
@ -2305,7 +2322,6 @@ static void augment_terminfo(TUIData *tui, const char *term, int vte_version, in
// None of the following work over SSH; see :help TERM .
bool iterm_pretending_xterm = xterm && iterm_env;
const char *xterm_version = os_getenv("XTERM_VERSION");
bool true_xterm = xterm && !!xterm_version && !bsdvt;
// Only define this capability for terminal types that we know understand it.
@ -2468,6 +2484,8 @@ static void augment_terminfo(TUIData *tui, const char *term, int vte_version, in
// Kitty keyboard protocol
tui->input.key_encoding = kKeyEncodingXterm;
}
xfree(xterm_version);
}
static bool should_invisible(TUIData *tui)

View File

@ -63,7 +63,7 @@ uint64_t ui_client_start_server(int argc, char **argv)
#ifdef MSWIN
// TODO(justinmk): detach breaks `tt.setup_child_nvim` tests on Windows?
bool detach = os_env_exists("__NVIM_DETACH");
bool detach = os_env_exists("__NVIM_DETACH", true);
#else
bool detach = true;
#endif
@ -172,7 +172,7 @@ void ui_client_run(bool remote_ui)
ui_client_attach(width, height, term, rgb);
// TODO(justinmk): this is for log_spec. Can remove this after nvim_log #7062 is merged.
if (os_env_exists("__NVIM_TEST_LOG")) {
if (os_env_exists("__NVIM_TEST_LOG", true)) {
ELOG("test log message");
}