feat: NVIM_APPNAME supports relative paths #25233

Problem:
NVIM_APPNAME does not allow path separators in the name, so relative
paths can't be used:

    NVIM_APPNAME="neovim-configs/first-config" nvim
    NVIM_APPNAME="neovim-configs/second-config" nvim

Solution:
Let NVIM_APPNAME be a relative path. Absolute paths are not supported.

fix #23056
fix #24966
This commit is contained in:
Rory Nesbitt
2023-09-27 18:09:55 +01:00
committed by GitHub
parent bfdec5b0e7
commit a66b0fdfaa
7 changed files with 60 additions and 21 deletions

View File

@ -169,6 +169,8 @@ The following new APIs and features were added.
• |vim.lsp.util.locations_to_items()| sets the `user_data` of each item to the • |vim.lsp.util.locations_to_items()| sets the `user_data` of each item to the
original LSP `Location` or `LocationLink`. original LSP `Location` or `LocationLink`.
• |$NVIM_APPNAME| can be set to a relative path instead of only a name.
============================================================================== ==============================================================================
CHANGED FEATURES *news-changed* CHANGED FEATURES *news-changed*

View File

@ -1366,6 +1366,9 @@ The $XDG_CONFIG_HOME, $XDG_DATA_HOME, $XDG_RUNTIME_DIR, $XDG_STATE_HOME,
$XDG_CACHE_HOME, $XDG_CONFIG_DIRS and $XDG_DATA_DIRS environment variables $XDG_CACHE_HOME, $XDG_CONFIG_DIRS and $XDG_DATA_DIRS environment variables
are used if defined, else default values (listed below) are used. are used if defined, else default values (listed below) are used.
Throughout the help pages these defaults are used as placeholders, e.g.
"~/.config" is understood to mean "$XDG_CONFIG_HOME or ~/.config".
CONFIG DIRECTORY (DEFAULT) ~ CONFIG DIRECTORY (DEFAULT) ~
*$XDG_CONFIG_HOME* Nvim: stdpath("config") *$XDG_CONFIG_HOME* Nvim: stdpath("config")
Unix: ~/.config ~/.config/nvim Unix: ~/.config ~/.config/nvim
@ -1392,10 +1395,12 @@ CACHE DIRECTORY (DEFAULT) ~
Windows: ~/AppData/Local/Temp ~/AppData/Local/Temp/nvim-data Windows: ~/AppData/Local/Temp ~/AppData/Local/Temp/nvim-data
LOG FILE (DEFAULT) ~ LOG FILE (DEFAULT) ~
`$NVIM_LOG_FILE` Nvim: stdpath("log") `$NVIM_LOG_FILE` Nvim: stdpath("log")/log
Unix: ~/.local/state/nvim ~/.local/state/nvim/log Unix: ~/.local/state/nvim ~/.local/state/nvim/log
Windows: ~/AppData/Local/nvim-data ~/AppData/Local/nvim-data/log Windows: ~/AppData/Local/nvim-data ~/AppData/Local/nvim-data/log
Note that stdpath("log") is currently an alias for stdpath("state").
ADDITIONAL CONFIGS DIRECTORY (DEFAULT) ~ ADDITIONAL CONFIGS DIRECTORY (DEFAULT) ~
*$XDG_CONFIG_DIRS* Nvim: stdpath("config_dirs") *$XDG_CONFIG_DIRS* Nvim: stdpath("config_dirs")
Unix: /etc/xdg/ /etc/xdg/nvim Unix: /etc/xdg/ /etc/xdg/nvim
@ -1407,18 +1412,14 @@ ADDITIONAL DATA DIRECTORY (DEFAULT) ~
/usr/share /usr/share/nvim /usr/share /usr/share/nvim
Windows: Not applicable Not applicable Windows: Not applicable Not applicable
Note: Throughout the help pages these defaults are used as placeholders, e.g.
"~/.config" is understood to mean "$XDG_CONFIG_HOME or ~/.config".
Note: The log file directory is controlled by `$XDG_STATE_HOME`.
NVIM_APPNAME *$NVIM_APPNAME* NVIM_APPNAME *$NVIM_APPNAME*
The standard directories can be further configured by the `$NVIM_APPNAME` The standard directories can be further configured by the `$NVIM_APPNAME`
environment variable. This variable controls the sub-directory that Nvim will environment variable. This variable controls the sub-directory that Nvim will
read from (and auto-create) in each of the base directories. For example, read from (and auto-create) in each of the base directories. For example,
setting `$NVIM_APPNAME` to "foo" before starting will cause Nvim to look for setting `$NVIM_APPNAME` to "foo" before starting will cause Nvim to look for
configuration files in `$XDG_CONFIG_HOME/foo` instead of configuration files in `$XDG_CONFIG_HOME/foo` instead of
`$XDG_CONFIG_HOME/nvim`. `$XDG_CONFIG_HOME/nvim`. `$NVIM_APPNAME` must be a name, such as "foo", or a
relative path, such as "foo/bar".
One use-case for $NVIM_APPNAME is to "isolate" Nvim applications. One use-case for $NVIM_APPNAME is to "isolate" Nvim applications.
Alternatively, for true isolation, on Linux you can use cgroups namespaces: > Alternatively, for true isolation, on Linux you can use cgroups namespaces: >

View File

@ -3279,12 +3279,18 @@ static void vim_mktempdir(void)
char tmp[TEMP_FILE_PATH_MAXLEN]; char tmp[TEMP_FILE_PATH_MAXLEN];
char path[TEMP_FILE_PATH_MAXLEN]; char path[TEMP_FILE_PATH_MAXLEN];
char user[40] = { 0 }; char user[40] = { 0 };
char appname[40] = { 0 };
(void)os_get_username(user, sizeof(user)); (void)os_get_username(user, sizeof(user));
// Usernames may contain slashes! #19240 // Usernames may contain slashes! #19240
memchrsub(user, '/', '_', sizeof(user)); memchrsub(user, '/', '_', sizeof(user));
memchrsub(user, '\\', '_', sizeof(user)); memchrsub(user, '\\', '_', sizeof(user));
// Appname may be a relative path, replace slashes to make it name-like.
xstrlcpy(appname, get_appname(), sizeof(appname));
memchrsub(appname, '/', '%', sizeof(appname));
memchrsub(appname, '\\', '%', sizeof(appname));
// Make sure the umask doesn't remove the executable bit. // Make sure the umask doesn't remove the executable bit.
// "repl" has been reported to use "0177". // "repl" has been reported to use "0177".
mode_t umask_save = umask(0077); mode_t umask_save = umask(0077);
@ -3298,7 +3304,6 @@ static void vim_mktempdir(void)
// "/tmp/" exists, now try to create "/tmp/nvim.<user>/". // "/tmp/" exists, now try to create "/tmp/nvim.<user>/".
add_pathsep(tmp); add_pathsep(tmp);
const char *appname = get_appname();
xstrlcat(tmp, appname, sizeof(tmp)); xstrlcat(tmp, appname, sizeof(tmp));
xstrlcat(tmp, ".", sizeof(tmp)); xstrlcat(tmp, ".", sizeof(tmp));
xstrlcat(tmp, user, sizeof(tmp)); xstrlcat(tmp, user, sizeof(tmp));

View File

@ -251,7 +251,7 @@ int main(int argc, char **argv)
argv0 = argv[0]; argv0 = argv[0];
if (!appname_is_valid()) { if (!appname_is_valid()) {
os_errmsg("$NVIM_APPNAME is not a valid file name.\n"); os_errmsg("$NVIM_APPNAME must be a name or relative path.\n");
exit(1); exit(1);
} }

View File

@ -69,15 +69,23 @@ const char *get_appname(void)
return env_val; return env_val;
} }
/// Ensure that APPNAME is valid. In particular, it cannot contain directory separators. /// Ensure that APPNAME is valid. Must be a name or relative path.
bool appname_is_valid(void) bool appname_is_valid(void)
{ {
const char *appname = get_appname(); const char *appname = get_appname();
const size_t appname_len = strlen(appname); if (path_is_absolute(appname)
for (size_t i = 0; i < appname_len; i++) { // TODO(justinmk): on Windows, path_is_absolute says "/" is NOT absolute. Should it?
if (appname[i] == PATHSEP) { || strequal(appname, "/")
return false; || strequal(appname, "\\")
} || strequal(appname, ".")
|| strequal(appname, "..")
#ifdef BACKSLASH_IN_FILENAME
|| strstr(appname, "\\..") != NULL
|| strstr(appname, "..\\") != NULL
#endif
|| strstr(appname, "/..") != NULL
|| strstr(appname, "../") != NULL) {
return false;
} }
return true; return true;
} }

View File

@ -377,4 +377,14 @@ describe('tmpdir', function()
rm_tmpdir() rm_tmpdir()
eq('E5431: tempdir disappeared (3 times)', meths.get_vvar('errmsg')) eq('E5431: tempdir disappeared (3 times)', meths.get_vvar('errmsg'))
end) end)
it('$NVIM_APPNAME relative path', function()
clear({ env={
NVIM_APPNAME='a/b',
NVIM_LOG_FILE=testlog,
TMPDIR=os_tmpdir,
} })
matches([=[.*[/\\]a%%b%.[^/\\]+]=], funcs.tempname())
end)
end) end)

View File

@ -598,8 +598,7 @@ describe('stdpath()', function()
end) end)
it('reacts to $NVIM_APPNAME', function() it('reacts to $NVIM_APPNAME', function()
local appname = "NVIM_APPNAME_TEST____________________________________" .. local appname = 'NVIM_APPNAME_TEST' .. ('_'):rep(106)
"______________________________________________________________________"
clear({env={ NVIM_APPNAME=appname }}) clear({env={ NVIM_APPNAME=appname }})
eq(appname, funcs.fnamemodify(funcs.stdpath('config'), ':t')) eq(appname, funcs.fnamemodify(funcs.stdpath('config'), ':t'))
eq(appname, funcs.fnamemodify(funcs.stdpath('cache'), ':t')) eq(appname, funcs.fnamemodify(funcs.stdpath('cache'), ':t'))
@ -616,10 +615,24 @@ describe('stdpath()', function()
-- Check that Nvim rejects invalid APPNAMEs -- Check that Nvim rejects invalid APPNAMEs
-- Call jobstart() and jobwait() in the same RPC request to reduce flakiness. -- Call jobstart() and jobwait() in the same RPC request to reduce flakiness.
eq(1, exec_lua([[ local function test_appname(testAppname, expected_exitcode)
local child = vim.fn.jobstart({ vim.v.progpath }, { env = { NVIM_APPNAME = 'a/b\\c' } }) local lua_code = string.format([[
return vim.fn.jobwait({ child }, 3000)[1] local child = vim.fn.jobstart({ vim.v.progpath, '--clean', '--headless', '+qall!' }, { env = { NVIM_APPNAME = %q } })
]])) return vim.fn.jobwait({ child }, %d)[1]
]], alter_slashes(testAppname), 3000)
eq(expected_exitcode, exec_lua(lua_code))
end
-- Invalid appnames:
test_appname('a/../b', 1)
test_appname('../a', 1)
test_appname('a/..', 1)
test_appname('..', 1)
test_appname('.', 1)
test_appname('/', 1)
test_appname(is_os('win') and 'C:/a/b' or '/a/b', 1)
-- Valid appnames:
test_appname('a/b', 0)
test_appname('a/b\\c', 0)
end) end)
describe('returns a String', function() describe('returns a String', function()