mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	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:
		| @@ -1143,7 +1143,7 @@ static int diff_file(diffio_T *dio) | |||||||
|   char *const cmd = xmalloc(len); |   char *const cmd = xmalloc(len); | ||||||
|  |  | ||||||
|   // We don't want $DIFF_OPTIONS to get in the way. |   // 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"); |     os_unsetenv("DIFF_OPTIONS"); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1556,7 +1556,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) | |||||||
|   const char *p = tv_get_string(&argvars[0]); |   const char *p = tv_get_string(&argvars[0]); | ||||||
|   if (*p == '$') {  // Environment variable. |   if (*p == '$') {  // Environment variable. | ||||||
|     // First try "normal" environment variables (fast). |     // First try "normal" environment variables (fast). | ||||||
|     if (os_env_exists(p + 1)) { |     if (os_env_exists(p + 1, false)) { | ||||||
|       n = true; |       n = true; | ||||||
|     } else { |     } else { | ||||||
|       // Try expanding things like $VIM and ${HOME}. |       // 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]); |       size_t len = strlen(required_env_vars[i]); | ||||||
|       dictitem_T *dv = tv_dict_find(env, required_env_vars[i], (ptrdiff_t)len); |       dictitem_T *dv = tv_dict_find(env, required_env_vars[i], (ptrdiff_t)len); | ||||||
|       if (!dv) { |       if (!dv) { | ||||||
|         const char *env_var = os_getenv(required_env_vars[i]); |         char *env_var = os_getenv(required_env_vars[i]); | ||||||
|         if (env_var) { |         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); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -7810,8 +7810,8 @@ static void ex_checkhealth(exarg_T *eap) | |||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const char *vimruntime_env = os_getenv("VIMRUNTIME"); |   char *vimruntime_env = os_getenv_noalloc("VIMRUNTIME"); | ||||||
|   if (vimruntime_env == NULL) { |   if (!vimruntime_env) { | ||||||
|     emsg(_("E5009: $VIMRUNTIME is empty or unset")); |     emsg(_("E5009: $VIMRUNTIME is empty or unset")); | ||||||
|   } else { |   } else { | ||||||
|     bool rtp_ok = NULL != strstr(p_rtp, vimruntime_env); |     bool rtp_ok = NULL != strstr(p_rtp, vimruntime_env); | ||||||
|   | |||||||
| @@ -3278,7 +3278,7 @@ static void vim_mktempdir(void) | |||||||
|     expand_env((char *)temp_dirs[i], tmp, TEMP_FILE_PATH_MAXLEN - 64); |     expand_env((char *)temp_dirs[i], tmp, TEMP_FILE_PATH_MAXLEN - 64); | ||||||
|     if (!os_isdir(tmp)) { |     if (!os_isdir(tmp)) { | ||||||
|       if (strequal("$TMPDIR", temp_dirs[i])) { |       if (strequal("$TMPDIR", temp_dirs[i])) { | ||||||
|         if (!os_getenv("TMPDIR")) { |         if (!os_env_exists("TMPDIR", true)) { | ||||||
|           DLOG("$TMPDIR is unset"); |           DLOG("$TMPDIR is unset"); | ||||||
|         } else { |         } else { | ||||||
|           WLOG("$TMPDIR tempdir not a directory (or does not exist): \"%s\"", tmp); |           WLOG("$TMPDIR tempdir not a directory (or does not exist): \"%s\"", tmp); | ||||||
|   | |||||||
| @@ -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 ? |   // TODO(justinmk): expose this as v:name ? | ||||||
|   if (regen) { |   if (regen) { | ||||||
|     // Parent servername ($NVIM). |     // 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. |     // Servername. Empty until starting=false. | ||||||
|     const char *serv = path_tail(get_vim_var_str(VV_SEND_SERVER)); |     const char *serv = path_tail(get_vim_var_str(VV_SEND_SERVER)); | ||||||
|     if (parent[0] != NUL) { |     if (parent[0] != NUL) { | ||||||
|   | |||||||
| @@ -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) | void nlua_init(char **argv, int argc, int lua_arg0) | ||||||
| { | { | ||||||
| #ifdef NLUA_TRACK_REFS | #ifdef NLUA_TRACK_REFS | ||||||
|   const char *env = os_getenv("NVIM_LUA_NOTRACK"); |   if (os_env_exists("NVIM_LUA_NOTRACK", true)) { | ||||||
|   if (!env || !*env) { |  | ||||||
|     nlua_track_refs = true; |     nlua_track_refs = true; | ||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
| @@ -1283,7 +1282,7 @@ static int nlua_empty_dict_tostring(lua_State *lstate) | |||||||
| /// @param  lstate  Lua interpreter state. | /// @param  lstate  Lua interpreter state. | ||||||
| static int nlua_getenv(lua_State *lstate) | 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; |   return 1; | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -938,12 +938,11 @@ static void remote_request(mparm_T *params, int remote_args, char *server_addr, | |||||||
|     if (!chan) { |     if (!chan) { | ||||||
|       fprintf(stderr, "Remote ui failed to start: %s\n", connect_error); |       fprintf(stderr, "Remote ui failed to start: %s\n", connect_error); | ||||||
|       os_exit(1); |       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", "Cannot attach UI of :terminal child to its parent. "); | ||||||
|       fprintf(stderr, "%s\n", "(Unset $NVIM to skip this check)"); |       fprintf(stderr, "%s\n", "(Unset $NVIM to skip this check)"); | ||||||
|       os_exit(1); |       os_exit(1); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ui_client_channel_id = chan; |     ui_client_channel_id = chan; | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| @@ -2118,7 +2117,7 @@ static void source_startup_scripts(const mparm_T *const parmp) | |||||||
| static int execute_env(char *env) | static int execute_env(char *env) | ||||||
|   FUNC_ATTR_NONNULL_ALL |   FUNC_ATTR_NONNULL_ALL | ||||||
| { | { | ||||||
|   const char *initstr = os_getenv(env); |   char *initstr = os_getenv(env); | ||||||
|   if (initstr == NULL) { |   if (initstr == NULL) { | ||||||
|     return FAIL; |     return FAIL; | ||||||
|   } |   } | ||||||
| @@ -2132,6 +2131,8 @@ static int execute_env(char *env) | |||||||
|  |  | ||||||
|   estack_pop(); |   estack_pop(); | ||||||
|   current_sctx = save_current_sctx; |   current_sctx = save_current_sctx; | ||||||
|  |  | ||||||
|  |   xfree(initstr); | ||||||
|   return OK; |   return OK; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2412,14 +2412,15 @@ char *enc_locale(void) | |||||||
|   char buf[50]; |   char buf[50]; | ||||||
|  |  | ||||||
|   const char *s; |   const char *s; | ||||||
|  |  | ||||||
| #ifdef HAVE_NL_LANGINFO_CODESET | #ifdef HAVE_NL_LANGINFO_CODESET | ||||||
|   if (!(s = nl_langinfo(CODESET)) || *s == NUL) |   if (!(s = nl_langinfo(CODESET)) || *s == NUL) | ||||||
| #endif | #endif | ||||||
|   { |   { | ||||||
|     if (!(s = setlocale(LC_CTYPE, NULL)) || *s == NUL) { |     if (!(s = setlocale(LC_CTYPE, NULL)) || *s == NUL) { | ||||||
|       if ((s = os_getenv("LC_ALL"))) { |       if ((s = os_getenv_noalloc("LC_ALL"))) { | ||||||
|         if ((s = os_getenv("LC_CTYPE"))) { |         if ((s = os_getenv_noalloc("LC_CTYPE"))) { | ||||||
|           s = os_getenv("LANG"); |           s = os_getenv_noalloc("LANG"); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -788,7 +788,6 @@ void free_all_mem(void) | |||||||
|   free_all_marks(); |   free_all_marks(); | ||||||
|   alist_clear(&global_alist); |   alist_clear(&global_alist); | ||||||
|   free_homedir(); |   free_homedir(); | ||||||
|   free_envmap(); |  | ||||||
|   free_users(); |   free_users(); | ||||||
|   free_search_patterns(); |   free_search_patterns(); | ||||||
|   free_old_sub(); |   free_old_sub(); | ||||||
|   | |||||||
| @@ -41,28 +41,24 @@ bool server_init(const char *listen_addr) | |||||||
|   ga_init(&watchers, sizeof(SocketWatcher *), 1); |   ga_init(&watchers, sizeof(SocketWatcher *), 1); | ||||||
|  |  | ||||||
|   // $NVIM_LISTEN_ADDRESS (deprecated) |   // $NVIM_LISTEN_ADDRESS (deprecated) | ||||||
|   if ((!listen_addr || listen_addr[0] == '\0') && os_env_exists(ENV_LISTEN)) { |   if (!listen_addr || listen_addr[0] == '\0') { | ||||||
|  |     if (os_env_exists(ENV_LISTEN, true)) { | ||||||
|       user_arg = kFalse;  // User-provided env var. |       user_arg = kFalse;  // User-provided env var. | ||||||
|       listen_addr = os_getenv(ENV_LISTEN); |       listen_addr = os_getenv(ENV_LISTEN); | ||||||
|   } |     } else { | ||||||
|  |  | ||||||
|   if (!listen_addr || listen_addr[0] == '\0') { |  | ||||||
|       user_arg = kNone;  // Autogenerated server address. |       user_arg = kNone;  // Autogenerated server address. | ||||||
|       listen_addr = server_address_new(NULL); |       listen_addr = server_address_new(NULL); | ||||||
|  |     } | ||||||
|     must_free = true; |     must_free = true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   int rv = server_start(listen_addr); |   int rv = server_start(listen_addr); | ||||||
|  |  | ||||||
|   // TODO(justinmk): this is for log_spec. Can remove this after nvim_log #7062 is merged. |   // 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"); |     ELOG("test log message"); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (must_free) { |  | ||||||
|     xfree((char *)listen_addr); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (rv == 0 || user_arg == kNone) { |   if (rv == 0 || user_arg == kNone) { | ||||||
|     // The autogenerated servername can fail if the user has a broken $XDG_RUNTIME_DIR. #30282 |     // 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). |     // 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; |   ok = false; | ||||||
|  |  | ||||||
| end: | 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 |     // Unset $NVIM_LISTEN_ADDRESS, it's a liability hereafter. It is "input only", it must not be | ||||||
|     // leaked to child jobs or :terminal. |     // leaked to child jobs or :terminal. | ||||||
|     os_unsetenv(ENV_LISTEN); |     os_unsetenv(ENV_LISTEN); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   if (must_free) { | ||||||
|  |     xfree((char *)listen_addr); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   return ok; |   return ok; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -120,12 +120,12 @@ char *server_address_new(const char *name) | |||||||
|   static uint32_t count = 0; |   static uint32_t count = 0; | ||||||
|   char fmt[ADDRESS_MAX_SIZE]; |   char fmt[ADDRESS_MAX_SIZE]; | ||||||
| #ifdef MSWIN | #ifdef MSWIN | ||||||
|   (void)get_appname(true); |   (void)get_appname(true);  // Writes appname to NameBuf. | ||||||
|   int r = snprintf(fmt, sizeof(fmt), "\\\\.\\pipe\\%s.%" PRIu64 ".%" PRIu32, |   int r = snprintf(fmt, sizeof(fmt), "\\\\.\\pipe\\%s.%" PRIu64 ".%" PRIu32, | ||||||
|                    name ? name : NameBuff, os_get_pid(), count++); |                    name ? name : NameBuff, os_get_pid(), count++); | ||||||
| #else | #else | ||||||
|   char *dir = stdpaths_get_xdg_var(kXDGRuntimeDir); |   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, |   int r = snprintf(fmt, sizeof(fmt), "%s/%s.%" PRIu64 ".%" PRIu32, | ||||||
|                    dir, name ? name : NameBuff, os_get_pid(), count++); |                    dir, name ? name : NameBuff, os_get_pid(), count++); | ||||||
|   xfree(dir); |   xfree(dir); | ||||||
|   | |||||||
| @@ -190,7 +190,7 @@ static void set_init_default_shell(void) | |||||||
| { | { | ||||||
|   // Find default value for 'shell' option. |   // Find default value for 'shell' option. | ||||||
|   // Don't use it if it is empty. |   // Don't use it if it is empty. | ||||||
|   const char *shell = os_getenv("SHELL"); |   char *shell = os_getenv("SHELL"); | ||||||
|   if (shell != NULL) { |   if (shell != NULL) { | ||||||
|     if (vim_strchr(shell, ' ') != NULL) { |     if (vim_strchr(shell, ' ') != NULL) { | ||||||
|       const size_t len = strlen(shell) + 3;  // two quotes and a trailing NUL |       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); |       snprintf(cmd, len, "\"%s\"", shell); | ||||||
|       set_string_default(kOptShell, cmd, true); |       set_string_default(kOptShell, cmd, true); | ||||||
|     } else { |     } 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). |   // abilities (bidi namely). | ||||||
|   // NOTE: mlterm's author is being asked to 'set' a variable |   // NOTE: mlterm's author is being asked to 'set' a variable | ||||||
|   //       instead of an environment variable due to inheritance. |   //       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); |     set_option_value_give_err(kOptTermbidi, BOOLEAN_OPTVAL(true), 0); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -52,13 +52,11 @@ | |||||||
| # include "os/env.c.generated.h" | # include "os/env.c.generated.h" | ||||||
| #endif | #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. | /// Like getenv(), but returns NULL if the variable is empty. | ||||||
|  | /// Result must be freed by the caller. | ||||||
| /// @see os_env_exists | /// @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 |   FUNC_ATTR_NONNULL_ALL | ||||||
| { | { | ||||||
|   char *e = NULL; |   char *e = NULL; | ||||||
| @@ -66,17 +64,6 @@ const char *os_getenv(const char *name) | |||||||
|     return NULL; |     return NULL; | ||||||
|   } |   } | ||||||
|   int r = 0; |   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 | #define INIT_SIZE 64 | ||||||
|   size_t size = INIT_SIZE; |   size_t size = INIT_SIZE; | ||||||
|   char buf[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. |     // except when it does not include the NUL-terminator. | ||||||
|     e = xmemdupz(buf, size); |     e = xmemdupz(buf, size); | ||||||
|   } |   } | ||||||
|   pmap_put(cstr_t)(&envmap, xstrdup(name), e); |  | ||||||
| end: | end: | ||||||
|   if (r != 0 && r != UV_ENOENT && r != UV_UNKNOWN) { |   if (r != 0 && r != UV_ENOENT && r != UV_UNKNOWN) { | ||||||
|     ELOG("uv_os_getenv(%s) failed: %d %s", name, r, uv_err_name(r)); |     ELOG("uv_os_getenv(%s) failed: %d %s", name, r, uv_err_name(r)); | ||||||
| @@ -104,9 +90,43 @@ end: | |||||||
|   return e; |   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 true if environment variable `name` is defined (even if empty). | ||||||
| /// Returns false if not found (UV_ENOENT) or other failure. | /// 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 |   FUNC_ATTR_NONNULL_ALL | ||||||
| { | { | ||||||
|   if (name[0] == NUL) { |   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() |   // Use a tiny buffer because we don't care about the value: if uv_os_getenv() | ||||||
|   // returns UV_ENOBUFS, the env var was found. |   // returns UV_ENOBUFS, the env var was found. | ||||||
|   char buf[1]; |   char buf[2]; | ||||||
|   size_t size = sizeof(buf); |   size_t size = sizeof(buf); | ||||||
|   int r = uv_os_getenv(name, buf, &size); |   int r = uv_os_getenv(name, buf, &size); | ||||||
|   assert(r != UV_EINVAL); |   assert(r != UV_EINVAL); | ||||||
|   if (r != 0 && r != UV_ENOENT && r != UV_ENOBUFS) { |   if (r != 0 && r != UV_ENOENT && r != UV_ENOBUFS) { | ||||||
|     ELOG("uv_os_getenv(%s) failed: %d %s", name, r, uv_err_name(r)); |     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. | /// Sets an environment variable. | ||||||
| @@ -137,7 +157,7 @@ int os_setenv(const char *name, const char *value, int overwrite) | |||||||
|     return -1; |     return -1; | ||||||
|   } |   } | ||||||
| #ifdef MSWIN | #ifdef MSWIN | ||||||
|   if (!overwrite && os_getenv(name) != NULL) { |   if (!overwrite && !os_env_exists(name, true)) { | ||||||
|     return 0; |     return 0; | ||||||
|   } |   } | ||||||
|   if (value[0] == NUL) { |   if (value[0] == NUL) { | ||||||
| @@ -145,7 +165,7 @@ int os_setenv(const char *name, const char *value, int overwrite) | |||||||
|     return os_unsetenv(name); |     return os_unsetenv(name); | ||||||
|   } |   } | ||||||
| #else | #else | ||||||
|   if (!overwrite && os_env_exists(name)) { |   if (!overwrite && os_env_exists(name, false)) { | ||||||
|     return 0; |     return 0; | ||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
| @@ -160,9 +180,6 @@ int os_setenv(const char *name, const char *value, int overwrite) | |||||||
| #endif | #endif | ||||||
|   r = uv_os_setenv(name, value); |   r = uv_os_setenv(name, value); | ||||||
|   assert(r != UV_EINVAL); |   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) { |   if (r != 0) { | ||||||
|     ELOG("uv_os_setenv(%s) failed: %d %s", name, r, uv_err_name(r)); |     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) { |   if (name[0] == NUL) { | ||||||
|     return -1; |     return -1; | ||||||
|   } |   } | ||||||
|   pmap_del2(&envmap, name); |  | ||||||
|   int r = uv_os_unsetenv(name); |   int r = uv_os_unsetenv(name); | ||||||
|   if (r != 0) { |   if (r != 0) { | ||||||
|     ELOG("uv_os_unsetenv(%s) failed: %d %s", name, r, uv_err_name(r)); |     ELOG("uv_os_unsetenv(%s) failed: %d %s", name, r, uv_err_name(r)); | ||||||
| @@ -428,7 +444,8 @@ void init_homedir(void) | |||||||
|   xfree(homedir); |   xfree(homedir); | ||||||
|   homedir = NULL; |   homedir = NULL; | ||||||
|  |  | ||||||
|   const char *var = os_getenv("HOME"); |   char *var = os_getenv("HOME"); | ||||||
|  |   char *tofree = var; | ||||||
|  |  | ||||||
| #ifdef MSWIN | #ifdef MSWIN | ||||||
|   // Typically, $HOME is not defined on Windows, unless the user has |   // 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 |   // platforms, $HOMEDRIVE and $HOMEPATH are automatically defined for | ||||||
|   // each user. Try constructing $HOME from these. |   // each user. Try constructing $HOME from these. | ||||||
|   if (var == NULL) { |   if (var == NULL) { | ||||||
|     const char *homedrive = os_getenv("HOMEDRIVE"); |     char *homedrive = os_getenv("HOMEDRIVE"); | ||||||
|     const char *homepath = os_getenv("HOMEPATH"); |     char *homepath = os_getenv("HOMEPATH"); | ||||||
|     if (homepath == NULL) { |     if (homepath == NULL) { | ||||||
|       homepath = "\\"; |       homepath = xstrdup("\\"); | ||||||
|     } |     } | ||||||
|     if (homedrive != NULL |     if (homedrive != NULL | ||||||
|         && strlen(homedrive) + strlen(homepath) < MAXPATHL) { |         && strlen(homedrive) + strlen(homepath) < MAXPATHL) { | ||||||
| @@ -448,6 +465,8 @@ void init_homedir(void) | |||||||
|         var = os_buf; |         var = os_buf; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |     xfree(homepath); | ||||||
|  |     xfree(homedrive); | ||||||
|   } |   } | ||||||
|   if (var == NULL) { |   if (var == NULL) { | ||||||
|     var = os_uv_homedir(); |     var = os_uv_homedir(); | ||||||
| @@ -461,12 +480,14 @@ void init_homedir(void) | |||||||
|     if (p != NULL) { |     if (p != NULL) { | ||||||
|       vim_snprintf(os_buf, (size_t)(p - var), "%s", var + 1); |       vim_snprintf(os_buf, (size_t)(p - var), "%s", var + 1); | ||||||
|       var = NULL; |       var = NULL; | ||||||
|       const char *exp = os_getenv(os_buf); |       char *exp = os_getenv(os_buf); | ||||||
|       if (exp != NULL && *exp != NUL |       if (exp != NULL) { | ||||||
|           && strlen(exp) + strlen(p) < MAXPATHL) { |         if (*exp != NUL && strlen(exp) + strlen(p) < MAXPATHL) { | ||||||
|           vim_snprintf(os_buf, MAXPATHL, "%s%s", exp, p + 1); |           vim_snprintf(os_buf, MAXPATHL, "%s%s", exp, p + 1); | ||||||
|           var = os_buf; |           var = os_buf; | ||||||
|         } |         } | ||||||
|  |         xfree(exp); | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -498,6 +519,7 @@ void init_homedir(void) | |||||||
|   if (var != NULL) { |   if (var != NULL) { | ||||||
|     homedir = xstrdup(var); |     homedir = xstrdup(var); | ||||||
|   } |   } | ||||||
|  |   xfree(tofree); | ||||||
| } | } | ||||||
|  |  | ||||||
| static char homedir_buf[MAXPATHL]; | static char homedir_buf[MAXPATHL]; | ||||||
| @@ -523,17 +545,6 @@ void free_homedir(void) | |||||||
|   xfree(homedir); |   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 | #endif | ||||||
|  |  | ||||||
| /// Call expand_env() and store the result in an allocated string. | /// Call expand_env() and store the result in an allocated string. | ||||||
| @@ -917,9 +928,9 @@ char *vim_getenv(const char *name) | |||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   const char *kos_env_path = os_getenv(name); |   char *kos_env_path = os_getenv(name); | ||||||
|   if (kos_env_path != NULL) { |   if (kos_env_path != NULL) { | ||||||
|     return xstrdup(kos_env_path); |     return kos_env_path; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool vimruntime = (strcmp(name, "VIMRUNTIME") == 0); |   bool vimruntime = (strcmp(name, "VIMRUNTIME") == 0); | ||||||
| @@ -932,11 +943,13 @@ char *vim_getenv(const char *name) | |||||||
|   char *vim_path = NULL; |   char *vim_path = NULL; | ||||||
|   if (vimruntime |   if (vimruntime | ||||||
|       && *default_vimruntime_dir == NUL) { |       && *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) { |     if (kos_env_path != NULL) { | ||||||
|       vim_path = vim_version_dir(kos_env_path); |       vim_path = vim_version_dir(kos_env_path); | ||||||
|       if (vim_path == NULL) { |       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); |     dirlen = strlen(homedir); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const char *homedir_env = os_getenv("HOME"); |   char *homedir_env = os_getenv("HOME"); | ||||||
| #ifdef MSWIN | #ifdef MSWIN | ||||||
|   if (homedir_env == NULL) { |   if (homedir_env == NULL) { | ||||||
|     homedir_env = os_getenv("USERPROFILE"); |     homedir_env = os_getenv("USERPROFILE"); | ||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
|   char *homedir_env_mod = (char *)homedir_env; |   char *homedir_env_mod = homedir_env; | ||||||
|   bool must_free = false; |   bool must_free = false; | ||||||
|  |  | ||||||
|   if (homedir_env_mod != NULL && *homedir_env_mod == '~') { |   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; |   *dst_p = NUL; | ||||||
|  |  | ||||||
|  |   xfree(homedir_env); | ||||||
|  |  | ||||||
|   if (must_free) { |   if (must_free) { | ||||||
|     xfree(homedir_env_mod); |     xfree(homedir_env_mod); | ||||||
|   } |   } | ||||||
| @@ -1199,9 +1214,10 @@ bool os_setenv_append_path(const char *fname) | |||||||
|   size_t dirlen = (size_t)(tail - fname); |   size_t dirlen = (size_t)(tail - fname); | ||||||
|   assert(tail >= fname && dirlen + 1 < sizeof(os_buf)); |   assert(tail >= fname && dirlen + 1 < sizeof(os_buf)); | ||||||
|   xmemcpyz(os_buf, fname, dirlen); |   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 pathlen = path ? strlen(path) : 0; | ||||||
|   const size_t newlen = pathlen + dirlen + 2; |   const size_t newlen = pathlen + dirlen + 2; | ||||||
|  |   bool retval = false; | ||||||
|   if (newlen < MAX_ENVPATHLEN) { |   if (newlen < MAX_ENVPATHLEN) { | ||||||
|     char *temp = xmalloc(newlen); |     char *temp = xmalloc(newlen); | ||||||
|     if (pathlen == 0) { |     if (pathlen == 0) { | ||||||
| @@ -1215,9 +1231,10 @@ bool os_setenv_append_path(const char *fname) | |||||||
|     xstrlcat(temp, os_buf, newlen); |     xstrlcat(temp, os_buf, newlen); | ||||||
|     os_setenv("PATH", temp, 1); |     os_setenv("PATH", temp, 1); | ||||||
|     xfree(temp); |     xfree(temp); | ||||||
|     return true; |     retval = true; | ||||||
|   } |   } | ||||||
|   return false; |   xfree(path); | ||||||
|  |   return retval; | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Returns true if `sh` looks like it resolves to "cmd.exe". | /// 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; |     return false; | ||||||
|   } |   } | ||||||
|   if (striequal(sh, "$COMSPEC")) { |   if (striequal(sh, "$COMSPEC")) { | ||||||
|     const char *comspec = os_getenv("COMSPEC"); |     char *comspec = os_getenv_noalloc("COMSPEC"); | ||||||
|     return striequal("cmd.exe", path_tail(comspec)); |     return striequal("cmd.exe", path_tail(comspec)); | ||||||
|   } |   } | ||||||
|   if (striequal(sh, "cmd.exe") || striequal(sh, "cmd")) { |   if (striequal(sh, "cmd.exe") || striequal(sh, "cmd")) { | ||||||
|   | |||||||
| @@ -303,7 +303,7 @@ static bool is_executable_ext(const char *name, char **abspath) | |||||||
|   size_t nameext_len = nameext ? strlen(nameext) : 0; |   size_t nameext_len = nameext ? strlen(nameext) : 0; | ||||||
|   xstrlcpy(os_buf, name, sizeof(os_buf)); |   xstrlcpy(os_buf, name, sizeof(os_buf)); | ||||||
|   char *buf_end = xstrchrnul(os_buf, NUL); |   char *buf_end = xstrchrnul(os_buf, NUL); | ||||||
|   const char *pathext = os_getenv("PATHEXT"); |   const char *pathext = os_getenv_noalloc("PATHEXT"); | ||||||
|   if (!pathext) { |   if (!pathext) { | ||||||
|     pathext = ".com;.exe;.bat;.cmd"; |     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) | static bool is_executable_in_path(const char *name, char **abspath) | ||||||
|   FUNC_ATTR_NONNULL_ARG(1) |   FUNC_ATTR_NONNULL_ARG(1) | ||||||
| { | { | ||||||
|   const char *path_env = os_getenv("PATH"); |   char *path_env = os_getenv("PATH"); | ||||||
|   if (path_env == NULL) { |   if (path_env == NULL) { | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| #ifdef MSWIN | #ifdef MSWIN | ||||||
|   char *path = NULL; |   char *path = NULL; | ||||||
|   if (!os_env_exists("NoDefaultCurrentDirectoryInExePath")) { |   if (!os_env_exists("NoDefaultCurrentDirectoryInExePath", false)) { | ||||||
|     // Prepend ".;" to $PATH. |     // Prepend ".;" to $PATH. | ||||||
|     size_t pathlen = strlen(path_env); |     size_t pathlen = strlen(path_env); | ||||||
|     path = xmallocz(pathlen + 2); |     path = xmallocz(pathlen + 2); | ||||||
| @@ -404,6 +404,7 @@ static bool is_executable_in_path(const char *name, char **abspath) | |||||||
| end: | end: | ||||||
|   xfree(buf); |   xfree(buf); | ||||||
|   xfree(path); |   xfree(path); | ||||||
|  |   xfree(path_env); | ||||||
|   return rv; |   return rv; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -70,6 +70,7 @@ char *get_mess_lang(void) | |||||||
| } | } | ||||||
|  |  | ||||||
| /// Get the language used for messages from the environment. | /// 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 | /// 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 | /// 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 | #ifdef LC_MESSAGES | ||||||
|   return get_locale_val(LC_MESSAGES); |   return get_locale_val(LC_MESSAGES); | ||||||
| #else | #else | ||||||
|   char *p = (char *)os_getenv("LC_ALL"); |   char *p = os_getenv_noalloc("LC_ALL"); | ||||||
|   if (p != NULL) { |   if (p != NULL) { | ||||||
|     return p; |     return p; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   p = (char *)os_getenv("LC_MESSAGES"); |   p = os_getenv_noalloc("LC_MESSAGES"); | ||||||
|   if (p != NULL) { |   if (p != NULL) { | ||||||
|     return p; |     return p; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   p = (char *)os_getenv("LANG"); |   p = os_getenv_noalloc("LANG"); | ||||||
|   if (p != NULL && ascii_isdigit(*p)) { |   if (p != NULL && ascii_isdigit(*p)) { | ||||||
|     p = NULL;  // ignore something like "1043" |     p = NULL;  // ignore something like "1043" | ||||||
|   } |   } | ||||||
| @@ -340,7 +341,7 @@ char *get_locales(expand_T *xp, int idx) | |||||||
| void lang_init(void) | void lang_init(void) | ||||||
| { | { | ||||||
| #ifdef __APPLE__ | #ifdef __APPLE__ | ||||||
|   if (os_getenv("LANG") == NULL) { |   if (!os_env_exists("LANG", true)) { | ||||||
|     char buf[50] = { 0 }; |     char buf[50] = { 0 }; | ||||||
|  |  | ||||||
|     // $LANG is not set, either because it was unset or Nvim was started |     // $LANG is not set, either because it was unset or Nvim was started | ||||||
|   | |||||||
| @@ -82,7 +82,7 @@ void os_icon_init(void) | |||||||
|   hOrigIconSmall = (HICON)SendMessage(hWnd, WM_GETICON, (WPARAM)ICON_SMALL, (LPARAM)0); |   hOrigIconSmall = (HICON)SendMessage(hWnd, WM_GETICON, (WPARAM)ICON_SMALL, (LPARAM)0); | ||||||
|   hOrigIcon = (HICON)SendMessage(hWnd, WM_GETICON, (WPARAM)ICON_BIG, (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) { |   if (vimruntime != NULL) { | ||||||
|     snprintf(NameBuff, MAXPATHL, "%s/neovim.ico", vimruntime); |     snprintf(NameBuff, MAXPATHL, "%s/neovim.ico", vimruntime); | ||||||
|     if (!os_path_exists(NameBuff)) { |     if (!os_path_exists(NameBuff)) { | ||||||
| @@ -92,6 +92,7 @@ void os_icon_init(void) | |||||||
|                                  LR_LOADFROMFILE | LR_LOADMAP3DCOLORS); |                                  LR_LOADFROMFILE | LR_LOADMAP3DCOLORS); | ||||||
|       os_icon_set(hVimIcon, hVimIcon); |       os_icon_set(hVimIcon, hVimIcon); | ||||||
|     } |     } | ||||||
|  |     xfree(vimruntime); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -117,7 +118,7 @@ void os_title_reset(void) | |||||||
| /// @param out_fd stdout file descriptor | /// @param out_fd stdout file descriptor | ||||||
| void os_tty_guess_term(const char **term, int out_fd) | 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; |   bool vtp = false; | ||||||
|  |  | ||||||
|   HANDLE handle = (HANDLE)_get_osfhandle(out_fd); |   HANDLE handle = (HANDLE)_get_osfhandle(out_fd); | ||||||
|   | |||||||
| @@ -70,19 +70,19 @@ static const char *const xdg_defaults[] = { | |||||||
| /// @return $NVIM_APPNAME value | /// @return $NVIM_APPNAME value | ||||||
| const char *get_appname(bool namelike) | const char *get_appname(bool namelike) | ||||||
| { | { | ||||||
|   const char *env_val = os_getenv("NVIM_APPNAME"); |   const char *env_val = os_getenv_noalloc("NVIM_APPNAME"); | ||||||
|   if (env_val == NULL || *env_val == NUL) { |  | ||||||
|     env_val = "nvim"; |   if (!env_val) { | ||||||
|  |     xstrlcpy(NameBuff, "nvim", sizeof(NameBuff)); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (namelike) { |   if (namelike) { | ||||||
|     // Appname may be a relative path, replace slashes to make it name-like. |     // 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)); | ||||||
|     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. | /// 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 env = xdg_env_vars[idx]; | ||||||
|   const char *const fallback = xdg_defaults[idx]; |   const char *const fallback = xdg_defaults[idx]; | ||||||
|  |  | ||||||
|   const char *env_val = os_getenv(env); |   char *env_val = os_getenv(env); | ||||||
|  |  | ||||||
| #ifdef MSWIN | #ifdef MSWIN | ||||||
|   if (env_val == NULL && xdg_defaults_env_vars[idx] != NULL) { |   if (env_val == NULL && xdg_defaults_env_vars[idx] != NULL) { | ||||||
|     env_val = os_getenv(xdg_defaults_env_vars[idx]); |     env_val = os_getenv(xdg_defaults_env_vars[idx]); | ||||||
|   } |   } | ||||||
| #else | #else | ||||||
|   if (env_val == NULL && os_env_exists(env)) { |   if (env_val == NULL && os_env_exists(env, false)) { | ||||||
|     env_val = ""; |     env_val = xstrdup(""); | ||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   char *ret = NULL; |   char *ret = NULL; | ||||||
|   if (env_val != NULL) { |   if (env_val != NULL) { | ||||||
|     ret = xstrdup(env_val); |     ret = env_val; | ||||||
|   } else if (fallback) { |   } else if (fallback) { | ||||||
|     ret = expand_env_save((char *)fallback); |     ret = expand_env_save((char *)fallback); | ||||||
|   } else if (idx == kXDGRuntimeDir) { |   } else if (idx == kXDGRuntimeDir) { | ||||||
|   | |||||||
| @@ -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. |   // Call tzset(3) to update the global timezone variables if it has. | ||||||
|   // POSIX standard doesn't require localtime_r() implementations to do that |   // 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. |   // as it does with localtime(), and we don't want to call tzset() every time. | ||||||
|   const char *tz = os_getenv("TZ"); |   const char *tz = os_getenv_noalloc("TZ"); | ||||||
|   if (tz == NULL) { |   if (!tz) { | ||||||
|     tz = ""; |     tz = ""; | ||||||
|   } |   } | ||||||
|   if (strncmp(tz_cache, tz, sizeof(tz_cache) - 1) != 0) { |   if (strncmp(tz_cache, tz, sizeof(tz_cache) - 1) != 0) { | ||||||
|   | |||||||
| @@ -89,7 +89,7 @@ int os_get_usernames(garray_T *users) | |||||||
| #endif | #endif | ||||||
| #ifdef HAVE_PWD_FUNCS | #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, |     // The $USER environment variable may be a valid remote user name (NIS, | ||||||
|     // LDAP) not already listed by getpwent(), as getpwent() only lists |     // LDAP) not already listed by getpwent(), as getpwent() only lists | ||||||
|   | |||||||
| @@ -2392,7 +2392,7 @@ bool path_is_absolute(const char *fname) | |||||||
| void path_guess_exepath(const char *argv0, char *buf, size_t bufsize) | void path_guess_exepath(const char *argv0, char *buf, size_t bufsize) | ||||||
|   FUNC_ATTR_NONNULL_ALL |   FUNC_ATTR_NONNULL_ALL | ||||||
| { | { | ||||||
|   const char *path = os_getenv("PATH"); |   char *path = os_getenv("PATH"); | ||||||
|  |  | ||||||
|   if (path == NULL || path_is_absolute(argv0)) { |   if (path == NULL || path_is_absolute(argv0)) { | ||||||
|     xstrlcpy(buf, argv0, bufsize); |     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. |     // Not found in $PATH, fall back to argv0. | ||||||
|     xstrlcpy(buf, argv0, bufsize); |     xstrlcpy(buf, argv0, bufsize); | ||||||
|   } |   } | ||||||
|  |   xfree(path); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1746,8 +1746,7 @@ char *runtimepath_default(bool clean_arg) | |||||||
|   size_t config_len = 0; |   size_t config_len = 0; | ||||||
|   size_t vimruntime_len = 0; |   size_t vimruntime_len = 0; | ||||||
|   size_t libdir_len = 0; |   size_t libdir_len = 0; | ||||||
|   const char *appname = get_appname(false); |   size_t appname_len = strlen(get_appname(false)); | ||||||
|   size_t appname_len = strlen(appname); |  | ||||||
|   if (data_home != NULL) { |   if (data_home != NULL) { | ||||||
|     data_len = strlen(data_home); |     data_len = strlen(data_home); | ||||||
|     size_t nvim_data_size = appname_len; |     size_t nvim_data_size = appname_len; | ||||||
|   | |||||||
| @@ -139,7 +139,8 @@ void tinput_init(TermInput *input, Loop *loop) | |||||||
|  |  | ||||||
|   input->in_fd = STDIN_FILENO; |   input->in_fd = STDIN_FILENO; | ||||||
|  |  | ||||||
|   const char *term = os_getenv("TERM"); |   const char *term = os_getenv_noalloc("TERM"); | ||||||
|  |  | ||||||
|   if (!term) { |   if (!term) { | ||||||
|     term = "";  // termkey_new_abstract assumes non-null (#2745) |     term = "";  // termkey_new_abstract assumes non-null (#2745) | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -49,7 +49,7 @@ bool terminfo_is_bsd_console(const char *term) | |||||||
| # if defined(__FreeBSD__) | # if defined(__FreeBSD__) | ||||||
|   // FreeBSD console sets TERM=xterm, but it does not support xterm features |   // FreeBSD console sets TERM=xterm, but it does not support xterm features | ||||||
|   // like cursor-shaping. Assume that TERM=xterm is degraded. #8644 |   // 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 | ||||||
| #endif | #endif | ||||||
|   return false; |   return false; | ||||||
|   | |||||||
| @@ -379,7 +379,7 @@ static void terminfo_start(TUIData *tui) | |||||||
|   tui->out_isatty = os_isatty(tui->out_fd); |   tui->out_isatty = os_isatty(tui->out_fd); | ||||||
|   tui->input.tui_data = tui; |   tui->input.tui_data = tui; | ||||||
|  |  | ||||||
|   const char *term = os_getenv("TERM"); |   char *term = os_getenv("TERM"); | ||||||
| #ifdef MSWIN | #ifdef MSWIN | ||||||
|   os_tty_guess_term(&term, tui->out_fd); |   os_tty_guess_term(&term, tui->out_fd); | ||||||
|   os_setenv("TERM", term, 1); |   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 . |   // None of the following work over SSH; see :help TERM . | ||||||
|   const char *colorterm = os_getenv("COLORTERM"); |   char *colorterm = os_getenv("COLORTERM"); | ||||||
|   const char *termprg = os_getenv("TERM_PROGRAM"); |   char *termprg = os_getenv("TERM_PROGRAM"); | ||||||
|   const char *vte_version_env = os_getenv("VTE_VERSION"); |   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; |   int vtev = vte_version_env ? (int)strtol(vte_version_env, NULL, 10) : 0; | ||||||
|   bool iterm_env = termprg && strstr(termprg, "iTerm.app"); |   bool iterm_env = termprg && strstr(termprg, "iTerm.app"); | ||||||
|   bool nsterm = (termprg && strstr(termprg, "Apple_Terminal")) |   bool nsterm = (termprg && strstr(termprg, "Apple_Terminal")) | ||||||
|                 || terminfo_is_term_family(term, "nsterm"); |                 || terminfo_is_term_family(term, "nsterm"); | ||||||
|   bool konsole = terminfo_is_term_family(term, "konsole") |   bool konsole = terminfo_is_term_family(term, "konsole") | ||||||
|                  || os_getenv("KONSOLE_PROFILE_NAME") |                  || os_env_exists("KONSOLE_PROFILE_NAME", true) | ||||||
|                  || os_getenv("KONSOLE_DBUS_SESSION"); |                  || os_env_exists("KONSOLE_DBUS_SESSION", true); | ||||||
|   const char *konsolev_env = os_getenv("KONSOLE_VERSION"); |  | ||||||
|   int konsolev = konsolev_env ? (int)strtol(konsolev_env, NULL, 10) |   int konsolev = konsolev_env ? (int)strtol(konsolev_env, NULL, 10) | ||||||
|                               : (konsole ? 1 : 0); |                               : (konsole ? 1 : 0); | ||||||
|   bool wezterm = strequal(termprg, "WezTerm"); |   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 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 |   // truecolor support must be checked before patching/augmenting terminfo | ||||||
|   tui->rgb = term_has_truecolor(tui, colorterm); |   tui->rgb = term_has_truecolor(tui, colorterm); | ||||||
| @@ -503,6 +505,13 @@ static void terminfo_start(TUIData *tui) | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   flush_buf(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. | /// 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 width = 0; | ||||||
|   int height = 0; |   int height = 0; | ||||||
|  |   char *lines = NULL; | ||||||
|  |   char *columns = NULL; | ||||||
|  |  | ||||||
|   // 1 - try from a system call (ioctl/TIOCGWINSZ on unix) |   // 1 - try from a system call (ioctl/TIOCGWINSZ on unix) | ||||||
|   if (tui->out_isatty |   if (tui->out_isatty | ||||||
| @@ -1782,9 +1793,9 @@ void tui_guess_size(TUIData *tui) | |||||||
|   // 2 - use $LINES/$COLUMNS if available |   // 2 - use $LINES/$COLUMNS if available | ||||||
|   const char *val; |   const char *val; | ||||||
|   int advance; |   int advance; | ||||||
|   if ((val = os_getenv("LINES")) |   if ((val = os_getenv_noalloc("LINES")) | ||||||
|       && sscanf(val, "%d%n", &height, &advance) != EOF && advance |       && 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) { |       && sscanf(val, "%d%n", &width, &advance) != EOF && advance) { | ||||||
|     goto end; |     goto end; | ||||||
|   } |   } | ||||||
| @@ -1804,6 +1815,9 @@ void tui_guess_size(TUIData *tui) | |||||||
|  |  | ||||||
|   // Redraw on SIGWINCH event if size didn't change. #23411 |   // Redraw on SIGWINCH event if size didn't change. #23411 | ||||||
|   ui_client_set_size(width, height); |   ui_client_set_size(width, height); | ||||||
|  |  | ||||||
|  |   xfree(lines); | ||||||
|  |   xfree(columns); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void unibi_goto(TUIData *tui, int row, int col) | 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) |                                 int vte_version, int konsolev, bool iterm_env, bool nsterm) | ||||||
| { | { | ||||||
|   unibi_term *ut = tui->ut; |   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") |   bool xterm = terminfo_is_term_family(term, "xterm") | ||||||
|                // Treat Terminal.app as generic xterm-like, for now. |                // Treat Terminal.app as generic xterm-like, for now. | ||||||
|                || nsterm; |                || 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 teraterm = terminfo_is_term_family(term, "teraterm"); | ||||||
|   bool putty = terminfo_is_term_family(term, "putty"); |   bool putty = terminfo_is_term_family(term, "putty"); | ||||||
|   bool screen = terminfo_is_term_family(term, "screen"); |   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 st = terminfo_is_term_family(term, "st"); | ||||||
|   bool gnome = terminfo_is_term_family(term, "gnome") |   bool gnome = terminfo_is_term_family(term, "gnome") | ||||||
|                || terminfo_is_term_family(term, "vte"); |                || 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"); |                         "\x1b]50;\x07"); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   xfree(xterm_version); | ||||||
| } | } | ||||||
|  |  | ||||||
| /// This adds stuff that is not in standard terminfo as extended unibilium | /// 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) |                              const char *weztermv, bool iterm_env, bool nsterm) | ||||||
| { | { | ||||||
|   unibi_term *ut = tui->ut; |   unibi_term *ut = tui->ut; | ||||||
|  |   char *xterm_version = os_getenv("XTERM_VERSION"); | ||||||
|   bool xterm = terminfo_is_term_family(term, "xterm") |   bool xterm = terminfo_is_term_family(term, "xterm") | ||||||
|                // Treat Terminal.app as generic xterm-like, for now. |                // Treat Terminal.app as generic xterm-like, for now. | ||||||
|                || nsterm; |                || 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 teraterm = terminfo_is_term_family(term, "teraterm"); | ||||||
|   bool putty = terminfo_is_term_family(term, "putty"); |   bool putty = terminfo_is_term_family(term, "putty"); | ||||||
|   bool screen = terminfo_is_term_family(term, "screen"); |   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 st = terminfo_is_term_family(term, "st"); | ||||||
|   bool iterm = terminfo_is_term_family(term, "iterm") |   bool iterm = terminfo_is_term_family(term, "iterm") | ||||||
|                || terminfo_is_term_family(term, "iterm2") |                || 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 . |   // None of the following work over SSH; see :help TERM . | ||||||
|   bool iterm_pretending_xterm = xterm && iterm_env; |   bool iterm_pretending_xterm = xterm && iterm_env; | ||||||
|  |  | ||||||
|   const char *xterm_version = os_getenv("XTERM_VERSION"); |  | ||||||
|   bool true_xterm = xterm && !!xterm_version && !bsdvt; |   bool true_xterm = xterm && !!xterm_version && !bsdvt; | ||||||
|  |  | ||||||
|   // Only define this capability for terminal types that we know understand it. |   // 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 |     // Kitty keyboard protocol | ||||||
|     tui->input.key_encoding = kKeyEncodingXterm; |     tui->input.key_encoding = kKeyEncodingXterm; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   xfree(xterm_version); | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool should_invisible(TUIData *tui) | static bool should_invisible(TUIData *tui) | ||||||
|   | |||||||
| @@ -63,7 +63,7 @@ uint64_t ui_client_start_server(int argc, char **argv) | |||||||
|  |  | ||||||
| #ifdef MSWIN | #ifdef MSWIN | ||||||
|   // TODO(justinmk): detach breaks `tt.setup_child_nvim` tests on Windows? |   // 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 | #else | ||||||
|   bool detach = true; |   bool detach = true; | ||||||
| #endif | #endif | ||||||
| @@ -172,7 +172,7 @@ void ui_client_run(bool remote_ui) | |||||||
|   ui_client_attach(width, height, term, rgb); |   ui_client_attach(width, height, term, rgb); | ||||||
|  |  | ||||||
|   // TODO(justinmk): this is for log_spec. Can remove this after nvim_log #7062 is merged. |   // 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"); |     ELOG("test log message"); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										27
									
								
								test/functional/core/env_spec.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								test/functional/core/env_spec.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | local t = require('test.testutil') | ||||||
|  | local n = require('test.functional.testnvim')() | ||||||
|  |  | ||||||
|  | local eq = t.eq | ||||||
|  | local clear = n.clear | ||||||
|  | local exec_capture = n.exec_capture | ||||||
|  | local command = n.command | ||||||
|  |  | ||||||
|  | describe('vim.uv', function() | ||||||
|  |   before_each(function() | ||||||
|  |     clear() | ||||||
|  |   end) | ||||||
|  |  | ||||||
|  |   -- Subsequential env var assignment consistency | ||||||
|  |   -- see: issue 32550 | ||||||
|  |   it('vim.uv.os_setenv(), vim.uv.os_unsetenv() consistency', function() | ||||||
|  |     eq('', exec_capture('echo $FOO')) | ||||||
|  |     command('lua vim.uv.os_setenv("FOO", "bar")') | ||||||
|  |     eq('bar', exec_capture('echo $FOO')) | ||||||
|  |     command('lua vim.uv.os_setenv("FOO", "fizz")') | ||||||
|  |     eq('fizz', exec_capture('echo $FOO')) | ||||||
|  |     command('lua vim.uv.os_unsetenv("FOO")') | ||||||
|  |     eq('', exec_capture('echo $FOO')) | ||||||
|  |     command('lua vim.uv.os_setenv("FOO", "buzz")') | ||||||
|  |     eq('buzz', exec_capture('echo $FOO')) | ||||||
|  |   end) | ||||||
|  | end) | ||||||
| @@ -13,8 +13,8 @@ local OK = 0 | |||||||
| local cimp = cimport('./src/nvim/os/os.h') | local cimp = cimport('./src/nvim/os/os.h') | ||||||
|  |  | ||||||
| describe('env.c', function() | describe('env.c', function() | ||||||
|   local function os_env_exists(name) |   local function os_env_exists(name, nonempty) | ||||||
|     return cimp.os_env_exists(to_cstr(name)) |     return cimp.os_env_exists(to_cstr(name), nonempty) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   local function os_setenv(name, value, override) |   local function os_setenv(name, value, override) | ||||||
| @@ -34,17 +34,45 @@ describe('env.c', function() | |||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   itp('os_env_exists', function() |   local function os_getenv_noalloc(name) | ||||||
|     eq(false, os_env_exists('')) |     local rval = cimp.os_getenv_noalloc(to_cstr(name)) | ||||||
|     eq(false, os_env_exists('      ')) |     if rval ~= NULL then | ||||||
|     eq(false, os_env_exists('\t')) |       return ffi.string(rval) | ||||||
|     eq(false, os_env_exists('\n')) |     else | ||||||
|     eq(false, os_env_exists('AaあB <= very weird name...')) |       return NULL | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   itp('os_env_exists(..., false)', function() | ||||||
|  |     eq(false, os_env_exists('', false)) | ||||||
|  |     eq(false, os_env_exists('      ', false)) | ||||||
|  |     eq(false, os_env_exists('\t', false)) | ||||||
|  |     eq(false, os_env_exists('\n', false)) | ||||||
|  |     eq(false, os_env_exists('AaあB <= very weird name...', false)) | ||||||
|  |  | ||||||
|     local varname = 'NVIM_UNIT_TEST_os_env_exists' |     local varname = 'NVIM_UNIT_TEST_os_env_exists' | ||||||
|     eq(false, os_env_exists(varname)) |     eq(false, os_env_exists(varname, false)) | ||||||
|     eq(OK, os_setenv(varname, 'foo bar baz ...', 1)) |     eq(OK, os_setenv(varname, 'foo bar baz ...', 1)) | ||||||
|     eq(true, os_env_exists(varname)) |     eq(true, os_env_exists(varname, false)) | ||||||
|  |     eq(OK, os_setenv(varname, 'f', 1)) | ||||||
|  |     eq(true, os_env_exists(varname, true)) | ||||||
|  |   end) | ||||||
|  |  | ||||||
|  |   itp('os_env_exists(..., true)', function() | ||||||
|  |     eq(false, os_env_exists('', true)) | ||||||
|  |     eq(false, os_env_exists('      ', true)) | ||||||
|  |     eq(false, os_env_exists('\t', true)) | ||||||
|  |     eq(false, os_env_exists('\n', true)) | ||||||
|  |     eq(false, os_env_exists('AaあB <= very weird name...', true)) | ||||||
|  |  | ||||||
|  |     local varname = 'NVIM_UNIT_TEST_os_env_defined' | ||||||
|  |     eq(false, os_env_exists(varname, true)) | ||||||
|  |     eq(OK, os_setenv(varname, '', 1)) | ||||||
|  |     eq(false, os_env_exists(varname, true)) | ||||||
|  |     eq(OK, os_setenv(varname, 'foo bar baz ...', 1)) | ||||||
|  |     eq(true, os_env_exists(varname, true)) | ||||||
|  |     eq(OK, os_setenv(varname, 'f', 1)) | ||||||
|  |     eq(true, os_env_exists(varname, true)) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
|   describe('os_setenv', function() |   describe('os_setenv', function() | ||||||
| @@ -130,6 +158,10 @@ describe('env.c', function() | |||||||
|       os_setenv(name, value, 1) |       os_setenv(name, value, 1) | ||||||
|       eq(value, os_getenv(name)) |       eq(value, os_getenv(name)) | ||||||
|  |  | ||||||
|  |       -- Shortest non-empty value | ||||||
|  |       os_setenv(name, 'z', 1) | ||||||
|  |       eq('z', os_getenv(name)) | ||||||
|  |  | ||||||
|       -- Get a big value. |       -- Get a big value. | ||||||
|       local bigval = ('x'):rep(256) |       local bigval = ('x'):rep(256) | ||||||
|       eq(OK, os_setenv(name, bigval, 1)) |       eq(OK, os_setenv(name, bigval, 1)) | ||||||
| @@ -147,6 +179,42 @@ describe('env.c', function() | |||||||
|     end) |     end) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
|  |   describe('os_getenv_noalloc', function() | ||||||
|  |     itp('reads an env var without memory allocation', function() | ||||||
|  |       local name = 'NVIM_UNIT_TEST_GETENV_1N' | ||||||
|  |       local value = 'NVIM_UNIT_TEST_GETENV_1V' | ||||||
|  |       eq(NULL, os_getenv_noalloc(name)) | ||||||
|  |       -- Use os_setenv because Lua doesn't have setenv. | ||||||
|  |       os_setenv(name, value, 1) | ||||||
|  |       eq(value, os_getenv_noalloc(name)) | ||||||
|  |  | ||||||
|  |       -- Shortest non-empty value | ||||||
|  |       os_setenv(name, 'z', 1) | ||||||
|  |       eq('z', os_getenv(name)) | ||||||
|  |  | ||||||
|  |       local bigval = ('x'):rep(256) | ||||||
|  |       eq(OK, os_setenv(name, bigval, 1)) | ||||||
|  |       eq(bigval, os_getenv_noalloc(name)) | ||||||
|  |  | ||||||
|  |       -- Variable size above NameBuff size gets truncated | ||||||
|  |       -- This assumes MAXPATHL is 4096 bytes. | ||||||
|  |       local verybigval = ('y'):rep(4096) | ||||||
|  |       local trunc = string.sub(verybigval, 0, 4095) | ||||||
|  |       eq(OK, os_setenv(name, verybigval, 1)) | ||||||
|  |       eq(trunc, os_getenv_noalloc(name)) | ||||||
|  |  | ||||||
|  |       -- Set non-empty, then set empty. | ||||||
|  |       eq(OK, os_setenv(name, 'non-empty', 1)) | ||||||
|  |       eq('non-empty', os_getenv(name)) | ||||||
|  |       eq(OK, os_setenv(name, '', 1)) | ||||||
|  |       eq(NULL, os_getenv_noalloc(name)) | ||||||
|  |     end) | ||||||
|  |  | ||||||
|  |     itp('returns NULL if the env var is not found', function() | ||||||
|  |       eq(NULL, os_getenv_noalloc('NVIM_UNIT_TEST_GETENV_NOTFOUND')) | ||||||
|  |     end) | ||||||
|  |   end) | ||||||
|  |  | ||||||
|   itp('os_unsetenv', function() |   itp('os_unsetenv', function() | ||||||
|     local name = 'TEST_UNSETENV' |     local name = 'TEST_UNSETENV' | ||||||
|     local value = 'TESTVALUE' |     local value = 'TESTVALUE' | ||||||
| @@ -156,7 +224,7 @@ describe('env.c', function() | |||||||
|     -- Depending on the platform the var might be unset or set as '' |     -- Depending on the platform the var might be unset or set as '' | ||||||
|     assert.True(os_getenv(name) == nil or os_getenv(name) == '') |     assert.True(os_getenv(name) == nil or os_getenv(name) == '') | ||||||
|     if os_getenv(name) == nil then |     if os_getenv(name) == nil then | ||||||
|       eq(false, os_env_exists(name)) |       eq(false, os_env_exists(name, false)) | ||||||
|     end |     end | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Judit Novak
					Judit Novak