mirror of
https://github.com/neovim/neovim.git
synced 2025-09-17 16:58:17 +00:00
feat: stdpath('run'), /tmp/nvim.user/ #18993
Problem:
- Since c57f6b28d7
#8519, sockets are created in ~/.local/… but XDG
spec says: "XDG_RUNTIME_DIR: Must be on the local filesystem", which
implies that XDG_STATE_DIR is potentially non-local.
- Not easy to inspect Nvim-created temp files (for debugging etc).
Solution:
- Store sockets in stdpath('run') ($XDG_RUNTIME_DIR).
- Establish "/tmp/nvim.user/" as the tempdir root shared by all Nvims.
- Make ok() actually useful.
- Introduce assert_nolog().
closes #3517
closes #17093
This commit is contained in:
@@ -7543,8 +7543,10 @@ stdpath({what}) *stdpath()* *E6100*
|
|||||||
is stored here.
|
is stored here.
|
||||||
data_dirs List Other data directories.
|
data_dirs List Other data directories.
|
||||||
log String Logs directory (for use by plugins too).
|
log String Logs directory (for use by plugins too).
|
||||||
|
run String Run directory: temporary, local storage
|
||||||
|
for sockets, named pipes, etc.
|
||||||
state String Session state directory: storage for file
|
state String Session state directory: storage for file
|
||||||
drafts, undo, shada, named pipes, ...
|
drafts, undo, shada, etc.
|
||||||
|
|
||||||
Example: >
|
Example: >
|
||||||
:echo stdpath("config")
|
:echo stdpath("config")
|
||||||
|
@@ -1328,8 +1328,9 @@ paths.
|
|||||||
*base-directories* *xdg*
|
*base-directories* *xdg*
|
||||||
The "base" (root) directories conform to the XDG Base Directory Specification.
|
The "base" (root) directories conform to the XDG Base Directory Specification.
|
||||||
https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||||
The $XDG_CONFIG_HOME, $XDG_DATA_HOME and $XDG_STATE_HOME environment variables
|
The $XDG_CONFIG_HOME, $XDG_DATA_HOME, $XDG_RUNTIME_DIR, and $XDG_STATE_HOME
|
||||||
are used if they exist, otherwise default values (listed below) are used.
|
environment variables are used if defined, else default values (listed below)
|
||||||
|
are used.
|
||||||
|
|
||||||
CONFIG DIRECTORY (DEFAULT) ~
|
CONFIG DIRECTORY (DEFAULT) ~
|
||||||
*$XDG_CONFIG_HOME* Nvim: stdpath("config")
|
*$XDG_CONFIG_HOME* Nvim: stdpath("config")
|
||||||
@@ -1341,6 +1342,11 @@ DATA DIRECTORY (DEFAULT) ~
|
|||||||
Unix: ~/.local/share ~/.local/share/nvim
|
Unix: ~/.local/share ~/.local/share/nvim
|
||||||
Windows: ~/AppData/Local ~/AppData/Local/nvim-data
|
Windows: ~/AppData/Local ~/AppData/Local/nvim-data
|
||||||
|
|
||||||
|
RUN DIRECTORY (DEFAULT) ~
|
||||||
|
*$XDG_RUNTIME_DIR* Nvim: stdpath("run")
|
||||||
|
Unix: /tmp/nvim.user/xxx /tmp/nvim.user/xxx
|
||||||
|
Windows: $TMP/nvim.user/xxx $TMP/nvim.user/xxx
|
||||||
|
|
||||||
STATE DIRECTORY (DEFAULT) ~
|
STATE DIRECTORY (DEFAULT) ~
|
||||||
*$XDG_STATE_HOME* Nvim: stdpath("state")
|
*$XDG_STATE_HOME* Nvim: stdpath("state")
|
||||||
Unix: ~/.local/state ~/.local/state/nvim
|
Unix: ~/.local/state ~/.local/state/nvim
|
||||||
|
@@ -1206,7 +1206,7 @@ void ex_diffpatch(exarg_T *eap)
|
|||||||
|| (os_chdir((char *)dirbuf) != 0)) {
|
|| (os_chdir((char *)dirbuf) != 0)) {
|
||||||
dirbuf[0] = NUL;
|
dirbuf[0] = NUL;
|
||||||
} else {
|
} else {
|
||||||
char *tempdir = (char *)vim_gettempdir();
|
char *tempdir = vim_gettempdir();
|
||||||
if (tempdir == NULL) {
|
if (tempdir == NULL) {
|
||||||
tempdir = "/tmp";
|
tempdir = "/tmp";
|
||||||
}
|
}
|
||||||
|
@@ -9719,6 +9719,8 @@ static void f_stdpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
rettv->vval.v_string = get_xdg_home(kXDGStateHome);
|
rettv->vval.v_string = get_xdg_home(kXDGStateHome);
|
||||||
} else if (strequal(p, "log")) {
|
} else if (strequal(p, "log")) {
|
||||||
rettv->vval.v_string = get_xdg_home(kXDGStateHome);
|
rettv->vval.v_string = get_xdg_home(kXDGStateHome);
|
||||||
|
} else if (strequal(p, "run")) {
|
||||||
|
rettv->vval.v_string = stdpaths_get_xdg_var(kXDGRuntimeDir);
|
||||||
} else if (strequal(p, "config_dirs")) {
|
} else if (strequal(p, "config_dirs")) {
|
||||||
get_xdg_var_list(kXDGConfigDirs, rettv);
|
get_xdg_var_list(kXDGConfigDirs, rettv);
|
||||||
} else if (strequal(p, "data_dirs")) {
|
} else if (strequal(p, "data_dirs")) {
|
||||||
|
@@ -5281,45 +5281,80 @@ void forward_slash(char_u *fname)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Name of Vim's own temp dir. Ends in a slash.
|
/// Path to Nvim's own temp dir. Ends in a slash.
|
||||||
static char_u *vim_tempdir = NULL;
|
static char *vim_tempdir = NULL;
|
||||||
|
|
||||||
/// Create a directory for private use by this instance of Neovim.
|
/// Creates a directory for private use by this instance of Nvim, trying each of
|
||||||
/// This is done once, and the same directory is used for all temp files.
|
/// `TEMP_DIR_NAMES` until one succeeds.
|
||||||
|
///
|
||||||
|
/// Only done once, the same directory is used for all temp files.
|
||||||
/// This method avoids security problems because of symlink attacks et al.
|
/// This method avoids security problems because of symlink attacks et al.
|
||||||
/// It's also a bit faster, because we only need to check for an existing
|
/// It's also a bit faster, because we only need to check for an existing
|
||||||
/// file when creating the directory and not for each temp file.
|
/// file when creating the directory and not for each temp file.
|
||||||
static void vim_maketempdir(void)
|
static void vim_mktempdir(void)
|
||||||
{
|
{
|
||||||
static const char *temp_dirs[] = TEMP_DIR_NAMES;
|
static const char *temp_dirs[] = TEMP_DIR_NAMES; // Try each of these until one succeeds.
|
||||||
// Try the entries in `TEMP_DIR_NAMES` to create the temp directory.
|
char tmp[TEMP_FILE_PATH_MAXLEN];
|
||||||
char_u template[TEMP_FILE_PATH_MAXLEN];
|
char path[TEMP_FILE_PATH_MAXLEN];
|
||||||
char_u path[TEMP_FILE_PATH_MAXLEN];
|
char user[40] = { 0 };
|
||||||
|
|
||||||
|
(void)os_get_username(user, sizeof(user));
|
||||||
|
|
||||||
// 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);
|
||||||
for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); i++) {
|
for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); i++) {
|
||||||
// Expand environment variables, leave room for "/nvimXXXXXX/999999999"
|
// Expand environment variables, leave room for "/tmp/nvim.<user>/XXXXXX/999999999".
|
||||||
expand_env((char_u *)temp_dirs[i], template, TEMP_FILE_PATH_MAXLEN - 22);
|
expand_env((char_u *)temp_dirs[i], (char_u *)tmp, TEMP_FILE_PATH_MAXLEN - 64);
|
||||||
if (!os_isdir(template)) { // directory doesn't exist
|
if (!os_isdir((char_u *)tmp)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
add_pathsep((char *)template);
|
// "/tmp/" exists, now try to create "/tmp/nvim.<user>/".
|
||||||
// Concatenate with temporary directory name pattern
|
add_pathsep(tmp);
|
||||||
STRCAT(template, "nvimXXXXXX");
|
xstrlcat(tmp, "nvim.", sizeof(tmp));
|
||||||
|
xstrlcat(tmp, user, sizeof(tmp));
|
||||||
|
(void)os_mkdir(tmp, 0700); // Always create, to avoid a race.
|
||||||
|
bool owned = os_file_owned(tmp);
|
||||||
|
bool isdir = os_isdir((char_u *)tmp);
|
||||||
|
#ifdef UNIX
|
||||||
|
int perm = os_getperm(tmp); // XDG_RUNTIME_DIR must be owned by the user, mode 0700.
|
||||||
|
bool valid = isdir && owned && 0700 == (perm & 0777);
|
||||||
|
#else
|
||||||
|
bool valid = isdir && owned; // TODO(justinmk): Windows ACL?
|
||||||
|
#endif
|
||||||
|
if (valid) {
|
||||||
|
add_pathsep(tmp);
|
||||||
|
} else {
|
||||||
|
if (!owned) {
|
||||||
|
ELOG("tempdir root not owned by current user (%s): %s", user, tmp);
|
||||||
|
} else if (!isdir) {
|
||||||
|
ELOG("tempdir root not a directory: %s", tmp);
|
||||||
|
}
|
||||||
|
#ifdef UNIX
|
||||||
|
if (0700 != (perm & 0777)) {
|
||||||
|
ELOG("tempdir root has invalid permissions (%o): %s", perm, tmp);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// If our "root" tempdir is invalid or fails, proceed without "<user>/".
|
||||||
|
// Else user1 could break user2 by creating "/tmp/nvim.user2/".
|
||||||
|
tmp[strlen(tmp) - strlen(user)] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
if (os_mkdtemp((const char *)template, (char *)path) != 0) {
|
// Now try to create "/tmp/nvim.<user>/XXXXXX".
|
||||||
|
xstrlcat(tmp, "XXXXXX", sizeof(tmp)); // mkdtemp "template", will be replaced with random alphanumeric chars.
|
||||||
|
int r = os_mkdtemp(tmp, path);
|
||||||
|
if (r != 0) {
|
||||||
|
WLOG("tempdir create failed: %s: %s", os_strerror(r), tmp);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vim_settempdir((char *)path)) {
|
if (vim_settempdir(path)) {
|
||||||
// Successfully created and set temporary directory so stop trying.
|
// Successfully created and set temporary directory so stop trying.
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
// Couldn't set `vim_tempdir` to `path` so remove created directory.
|
// Couldn't set `vim_tempdir` to `path` so remove created directory.
|
||||||
os_rmdir((char *)path);
|
os_rmdir(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(void)umask(umask_save);
|
(void)umask(umask_save);
|
||||||
@@ -5415,26 +5450,27 @@ void vim_deltempdir(void)
|
|||||||
{
|
{
|
||||||
if (vim_tempdir != NULL) {
|
if (vim_tempdir != NULL) {
|
||||||
// remove the trailing path separator
|
// remove the trailing path separator
|
||||||
path_tail((char *)vim_tempdir)[-1] = NUL;
|
path_tail(vim_tempdir)[-1] = NUL;
|
||||||
delete_recursive((const char *)vim_tempdir);
|
delete_recursive(vim_tempdir);
|
||||||
XFREE_CLEAR(vim_tempdir);
|
XFREE_CLEAR(vim_tempdir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @return the name of temp directory. This directory would be created on the first
|
/// Gets path to Nvim's own temp dir (ending with slash).
|
||||||
/// call to this function.
|
///
|
||||||
char_u *vim_gettempdir(void)
|
/// Creates the directory on the first call.
|
||||||
|
char *vim_gettempdir(void)
|
||||||
{
|
{
|
||||||
if (vim_tempdir == NULL) {
|
if (vim_tempdir == NULL) {
|
||||||
vim_maketempdir();
|
vim_mktempdir();
|
||||||
}
|
}
|
||||||
|
|
||||||
return vim_tempdir;
|
return vim_tempdir;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set Neovim own temporary directory name to `tempdir`. This directory should
|
/// Sets Nvim's own temporary directory name to `tempdir`. This directory must
|
||||||
/// be already created. Expand this name to a full path and put it in
|
/// already exist. Expands the name to a full path and put it in `vim_tempdir`.
|
||||||
/// `vim_tempdir`. This avoids that using `:cd` would confuse us.
|
/// This avoids that using `:cd` would confuse us.
|
||||||
///
|
///
|
||||||
/// @param tempdir must be no longer than MAXPATHL.
|
/// @param tempdir must be no longer than MAXPATHL.
|
||||||
///
|
///
|
||||||
@@ -5447,7 +5483,7 @@ static bool vim_settempdir(char *tempdir)
|
|||||||
}
|
}
|
||||||
vim_FullName(tempdir, buf, MAXPATHL, false);
|
vim_FullName(tempdir, buf, MAXPATHL, false);
|
||||||
add_pathsep(buf);
|
add_pathsep(buf);
|
||||||
vim_tempdir = (char_u *)xstrdup(buf);
|
vim_tempdir = xstrdup(buf);
|
||||||
xfree(buf);
|
xfree(buf);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -5456,14 +5492,14 @@ static bool vim_settempdir(char *tempdir)
|
|||||||
///
|
///
|
||||||
/// @note The temp file is NOT created.
|
/// @note The temp file is NOT created.
|
||||||
///
|
///
|
||||||
/// @return pointer to the temp file name or NULL if Neovim can't create
|
/// @return pointer to the temp file name or NULL if Nvim can't create
|
||||||
/// temporary directory for its own temporary files.
|
/// temporary directory for its own temporary files.
|
||||||
char_u *vim_tempname(void)
|
char_u *vim_tempname(void)
|
||||||
{
|
{
|
||||||
// Temp filename counter.
|
// Temp filename counter.
|
||||||
static uint64_t temp_count;
|
static uint64_t temp_count;
|
||||||
|
|
||||||
char_u *tempdir = vim_gettempdir();
|
char *tempdir = vim_gettempdir();
|
||||||
if (!tempdir) {
|
if (!tempdir) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@@ -2508,7 +2508,7 @@ bool mch_print_begin(prt_settings_T *psettings)
|
|||||||
*/
|
*/
|
||||||
prt_dsc_start();
|
prt_dsc_start();
|
||||||
prt_dsc_textline("Title", (char *)psettings->jobname);
|
prt_dsc_textline("Title", (char *)psettings->jobname);
|
||||||
if (os_get_user_name(buffer, 256) == FAIL) {
|
if (os_get_username(buffer, 256) == FAIL) {
|
||||||
STRCPY(buffer, "Unknown");
|
STRCPY(buffer, "Unknown");
|
||||||
}
|
}
|
||||||
prt_dsc_textline("For", buffer);
|
prt_dsc_textline("For", buffer);
|
||||||
|
@@ -2016,14 +2016,14 @@ static void source_startup_scripts(const mparm_T *const parmp)
|
|||||||
// do_user_initialization.
|
// do_user_initialization.
|
||||||
#if defined(UNIX)
|
#if defined(UNIX)
|
||||||
// If vimrc file is not owned by user, set 'secure' mode.
|
// If vimrc file is not owned by user, set 'secure' mode.
|
||||||
if (!file_owned(VIMRC_FILE))
|
if (!os_file_owned(VIMRC_FILE)) // NOLINT(readability/braces)
|
||||||
#endif
|
#endif
|
||||||
secure = p_secure;
|
secure = p_secure;
|
||||||
|
|
||||||
if (do_source(VIMRC_FILE, true, DOSO_VIMRC) == FAIL) {
|
if (do_source(VIMRC_FILE, true, DOSO_VIMRC) == FAIL) {
|
||||||
#if defined(UNIX)
|
#if defined(UNIX)
|
||||||
// if ".exrc" is not owned by user set 'secure' mode
|
// if ".exrc" is not owned by user set 'secure' mode
|
||||||
if (!file_owned(EXRC_FILE)) {
|
if (!os_file_owned(EXRC_FILE)) {
|
||||||
secure = p_secure;
|
secure = p_secure;
|
||||||
} else {
|
} else {
|
||||||
secure = 0;
|
secure = 0;
|
||||||
@@ -2068,23 +2068,6 @@ static int execute_env(char *env)
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef UNIX
|
|
||||||
/// Checks if user owns file.
|
|
||||||
/// Use both uv_fs_stat() and uv_fs_lstat() through os_fileinfo() and
|
|
||||||
/// os_fileinfo_link() respectively for extra security.
|
|
||||||
static bool file_owned(const char *fname)
|
|
||||||
{
|
|
||||||
assert(fname != NULL);
|
|
||||||
uid_t uid = getuid();
|
|
||||||
FileInfo file_info;
|
|
||||||
bool file_owned = os_fileinfo(fname, &file_info)
|
|
||||||
&& file_info.stat.st_uid == uid;
|
|
||||||
bool link_owned = os_fileinfo_link(fname, &file_info)
|
|
||||||
&& file_info.stat.st_uid == uid;
|
|
||||||
return file_owned && link_owned;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Prints the following then exits:
|
/// Prints the following then exits:
|
||||||
/// - An error message `errstr`
|
/// - An error message `errstr`
|
||||||
/// - A string `str` if not null
|
/// - A string `str` if not null
|
||||||
|
@@ -311,7 +311,7 @@ int ml_open(buf_T *buf)
|
|||||||
b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
|
b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
|
||||||
b0p->b0_flags = get_fileformat(buf) + 1;
|
b0p->b0_flags = get_fileformat(buf) + 1;
|
||||||
set_b0_fname(b0p, buf);
|
set_b0_fname(b0p, buf);
|
||||||
(void)os_get_user_name((char *)b0p->b0_uname, B0_UNAME_SIZE);
|
(void)os_get_username((char *)b0p->b0_uname, B0_UNAME_SIZE);
|
||||||
b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL;
|
b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL;
|
||||||
os_get_hostname((char *)b0p->b0_hname, B0_HNAME_SIZE);
|
os_get_hostname((char *)b0p->b0_hname, B0_HNAME_SIZE);
|
||||||
b0p->b0_hname[B0_HNAME_SIZE - 1] = NUL;
|
b0p->b0_hname[B0_HNAME_SIZE - 1] = NUL;
|
||||||
@@ -669,7 +669,7 @@ static void set_b0_fname(ZERO_BL *b0p, buf_T *buf)
|
|||||||
B0_FNAME_SIZE_CRYPT, true);
|
B0_FNAME_SIZE_CRYPT, true);
|
||||||
if (b0p->b0_fname[0] == '~') {
|
if (b0p->b0_fname[0] == '~') {
|
||||||
// If there is no user name or it is too long, don't use "~/"
|
// If there is no user name or it is too long, don't use "~/"
|
||||||
int retval = os_get_user_name(uname, B0_UNAME_SIZE);
|
int retval = os_get_username(uname, B0_UNAME_SIZE);
|
||||||
size_t ulen = STRLEN(uname);
|
size_t ulen = STRLEN(uname);
|
||||||
size_t flen = STRLEN(b0p->b0_fname);
|
size_t flen = STRLEN(b0p->b0_fname);
|
||||||
if (retval == FAIL || ulen + flen > B0_FNAME_SIZE_CRYPT - 1) {
|
if (retval == FAIL || ulen + flen > B0_FNAME_SIZE_CRYPT - 1) {
|
||||||
|
@@ -89,7 +89,7 @@ void server_teardown(void)
|
|||||||
///
|
///
|
||||||
/// Named pipe format:
|
/// Named pipe format:
|
||||||
/// - Windows: "\\.\pipe\<name>.<pid>.<counter>"
|
/// - Windows: "\\.\pipe\<name>.<pid>.<counter>"
|
||||||
/// - Other: "~/.local/state/nvim/<name>.<pid>.<counter>"
|
/// - Other: "/tmp/nvim.user/xxx/<name>.<pid>.<counter>"
|
||||||
char *server_address_new(const char *name)
|
char *server_address_new(const char *name)
|
||||||
{
|
{
|
||||||
static uint32_t count = 0;
|
static uint32_t count = 0;
|
||||||
@@ -98,7 +98,7 @@ char *server_address_new(const char *name)
|
|||||||
int r = snprintf(fmt, sizeof(fmt), "\\\\.\\pipe\\%s.%" PRIu64 ".%" PRIu32,
|
int r = snprintf(fmt, sizeof(fmt), "\\\\.\\pipe\\%s.%" PRIu64 ".%" PRIu32,
|
||||||
name ? name : "nvim", os_get_pid(), count++);
|
name ? name : "nvim", os_get_pid(), count++);
|
||||||
#else
|
#else
|
||||||
char *dir = get_xdg_home(kXDGStateHome);
|
char *dir = stdpaths_get_xdg_var(kXDGRuntimeDir);
|
||||||
int r = snprintf(fmt, sizeof(fmt), "%s/%s.%" PRIu64 ".%" PRIu32,
|
int r = snprintf(fmt, sizeof(fmt), "%s/%s.%" PRIu64 ".%" PRIu32,
|
||||||
dir, name ? name : "nvim", os_get_pid(), count++);
|
dir, name ? name : "nvim", os_get_pid(), count++);
|
||||||
xfree(dir);
|
xfree(dir);
|
||||||
|
@@ -663,7 +663,7 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
|
|||||||
// Get the user directory. If this fails the shell is used to expand
|
// Get the user directory. If this fails the shell is used to expand
|
||||||
// ~user, which is slower and may fail on old versions of /bin/sh.
|
// ~user, which is slower and may fail on old versions of /bin/sh.
|
||||||
var = (*dst == NUL) ? NULL
|
var = (*dst == NUL) ? NULL
|
||||||
: (char_u *)os_get_user_directory((char *)dst + 1);
|
: (char_u *)os_get_userdir((char *)dst + 1);
|
||||||
mustfree = true;
|
mustfree = true;
|
||||||
if (var == NULL) {
|
if (var == NULL) {
|
||||||
expand_T xpc;
|
expand_T xpc;
|
||||||
|
@@ -126,7 +126,7 @@ bool os_isrealdir(const char *name)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the given path is a directory or not.
|
/// Check if the given path exists and is a directory.
|
||||||
///
|
///
|
||||||
/// @return `true` if `name` is a directory.
|
/// @return `true` if `name` is a directory.
|
||||||
bool os_isdir(const char_u *name)
|
bool os_isdir(const char_u *name)
|
||||||
@@ -791,6 +791,27 @@ int os_setperm(const char *const name, int perm)
|
|||||||
return (r == kLibuvSuccess ? OK : FAIL);
|
return (r == kLibuvSuccess ? OK : FAIL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef UNIX
|
||||||
|
/// Checks if the current user owns a file.
|
||||||
|
///
|
||||||
|
/// Uses both uv_fs_stat() and uv_fs_lstat() via os_fileinfo() and
|
||||||
|
/// os_fileinfo_link() respectively for extra security.
|
||||||
|
bool os_file_owned(const char *fname)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
uid_t uid = getuid();
|
||||||
|
FileInfo finfo;
|
||||||
|
bool file_owned = os_fileinfo(fname, &finfo) && finfo.stat.st_uid == uid;
|
||||||
|
bool link_owned = os_fileinfo_link(fname, &finfo) && finfo.stat.st_uid == uid;
|
||||||
|
return file_owned && link_owned;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
bool os_file_owned(const char *fname)
|
||||||
|
{
|
||||||
|
return true; // TODO(justinmk): Windows. #8244
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// Changes the owner and group of a file, like chown(2).
|
/// Changes the owner and group of a file, like chown(2).
|
||||||
///
|
///
|
||||||
/// @return 0 on success, or libuv error code on failure.
|
/// @return 0 on success, or libuv error code on failure.
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "nvim/ascii.h"
|
#include "nvim/ascii.h"
|
||||||
|
#include "nvim/fileio.h"
|
||||||
#include "nvim/memory.h"
|
#include "nvim/memory.h"
|
||||||
#include "nvim/os/os.h"
|
#include "nvim/os/os.h"
|
||||||
#include "nvim/os/stdpaths_defs.h"
|
#include "nvim/os/stdpaths_defs.h"
|
||||||
@@ -26,7 +27,7 @@ static const char *const xdg_defaults_env_vars[] = {
|
|||||||
[kXDGDataHome] = "LOCALAPPDATA",
|
[kXDGDataHome] = "LOCALAPPDATA",
|
||||||
[kXDGCacheHome] = "TEMP",
|
[kXDGCacheHome] = "TEMP",
|
||||||
[kXDGStateHome] = "LOCALAPPDATA",
|
[kXDGStateHome] = "LOCALAPPDATA",
|
||||||
[kXDGRuntimeDir] = NULL,
|
[kXDGRuntimeDir] = NULL, // Decided by vim_mktempdir().
|
||||||
[kXDGConfigDirs] = NULL,
|
[kXDGConfigDirs] = NULL,
|
||||||
[kXDGDataDirs] = NULL,
|
[kXDGDataDirs] = NULL,
|
||||||
};
|
};
|
||||||
@@ -41,7 +42,7 @@ static const char *const xdg_defaults[] = {
|
|||||||
[kXDGDataHome] = "~\\AppData\\Local",
|
[kXDGDataHome] = "~\\AppData\\Local",
|
||||||
[kXDGCacheHome] = "~\\AppData\\Local\\Temp",
|
[kXDGCacheHome] = "~\\AppData\\Local\\Temp",
|
||||||
[kXDGStateHome] = "~\\AppData\\Local",
|
[kXDGStateHome] = "~\\AppData\\Local",
|
||||||
[kXDGRuntimeDir] = NULL,
|
[kXDGRuntimeDir] = NULL, // Decided by vim_mktempdir().
|
||||||
[kXDGConfigDirs] = NULL,
|
[kXDGConfigDirs] = NULL,
|
||||||
[kXDGDataDirs] = NULL,
|
[kXDGDataDirs] = NULL,
|
||||||
#else
|
#else
|
||||||
@@ -49,7 +50,7 @@ static const char *const xdg_defaults[] = {
|
|||||||
[kXDGDataHome] = "~/.local/share",
|
[kXDGDataHome] = "~/.local/share",
|
||||||
[kXDGCacheHome] = "~/.cache",
|
[kXDGCacheHome] = "~/.cache",
|
||||||
[kXDGStateHome] = "~/.local/state",
|
[kXDGStateHome] = "~/.local/state",
|
||||||
[kXDGRuntimeDir] = NULL,
|
[kXDGRuntimeDir] = NULL, // Decided by vim_mktempdir().
|
||||||
[kXDGConfigDirs] = "/etc/xdg/",
|
[kXDGConfigDirs] = "/etc/xdg/",
|
||||||
[kXDGDataDirs] = "/usr/local/share/:/usr/share/",
|
[kXDGDataDirs] = "/usr/local/share/:/usr/share/",
|
||||||
#endif
|
#endif
|
||||||
@@ -83,6 +84,11 @@ char *stdpaths_get_xdg_var(const XDGVarType idx)
|
|||||||
ret = xstrdup(env_val);
|
ret = xstrdup(env_val);
|
||||||
} else if (fallback) {
|
} else if (fallback) {
|
||||||
ret = expand_env_save((char *)fallback);
|
ret = expand_env_save((char *)fallback);
|
||||||
|
} else if (idx == kXDGRuntimeDir) {
|
||||||
|
// Special-case: stdpath('run') is defined at startup.
|
||||||
|
ret = vim_gettempdir();
|
||||||
|
size_t len = strlen(ret);
|
||||||
|
ret = xstrndup(ret, len >= 2 ? len - 1 : 0); // Trim trailing slash.
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@@ -112,9 +112,13 @@ int os_get_usernames(garray_T *users)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert user name in s[len].
|
/// Gets the username that owns the current Nvim process.
|
||||||
// Return OK if a name found.
|
///
|
||||||
int os_get_user_name(char *s, size_t len)
|
/// @param s[out] Username.
|
||||||
|
/// @param len Length of `s`.
|
||||||
|
///
|
||||||
|
/// @return OK if a name found.
|
||||||
|
int os_get_username(char *s, size_t len)
|
||||||
{
|
{
|
||||||
#ifdef UNIX
|
#ifdef UNIX
|
||||||
return os_get_uname((uv_uid_t)getuid(), s, len);
|
return os_get_uname((uv_uid_t)getuid(), s, len);
|
||||||
@@ -124,9 +128,13 @@ int os_get_user_name(char *s, size_t len)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert user name for "uid" in s[len].
|
/// Gets the username associated with `uid`.
|
||||||
// Return OK if a name found.
|
///
|
||||||
// If the name is not found, write the uid into s[len] and return FAIL.
|
/// @param uid User id.
|
||||||
|
/// @param s[out] Username, or `uid` on failure.
|
||||||
|
/// @param len Length of `s`.
|
||||||
|
///
|
||||||
|
/// @return OK if a username was found, else FAIL.
|
||||||
int os_get_uname(uv_uid_t uid, char *s, size_t len)
|
int os_get_uname(uv_uid_t uid, char *s, size_t len)
|
||||||
{
|
{
|
||||||
#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID)
|
#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID)
|
||||||
@@ -142,10 +150,10 @@ int os_get_uname(uv_uid_t uid, char *s, size_t len)
|
|||||||
return FAIL; // a number is not a name
|
return FAIL; // a number is not a name
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the user directory for the given username.
|
/// Gets the user directory for the given username, or NULL on failure.
|
||||||
// The caller has to free() the returned string.
|
///
|
||||||
// If the username is not found, NULL is returned.
|
/// Caller must free() the returned string.
|
||||||
char *os_get_user_directory(const char *name)
|
char *os_get_userdir(const char *name)
|
||||||
{
|
{
|
||||||
#if defined(HAVE_GETPWNAM) && defined(HAVE_PWD_H)
|
#if defined(HAVE_GETPWNAM) && defined(HAVE_PWD_H)
|
||||||
if (name == NULL || *name == NUL) {
|
if (name == NULL || *name == NUL) {
|
||||||
|
@@ -170,7 +170,7 @@ describe('server -> client', function()
|
|||||||
if method == "notification" then
|
if method == "notification" then
|
||||||
eq('done!', eval('rpcrequest('..cid..', "nested")'))
|
eq('done!', eval('rpcrequest('..cid..', "nested")'))
|
||||||
elseif method == "nested_done" then
|
elseif method == "nested_done" then
|
||||||
ok(false, 'this should never have been sent')
|
ok(false, 'never sent', 'sent')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@ -1,22 +1,28 @@
|
|||||||
local helpers = require('test.functional.helpers')(after_each)
|
local helpers = require('test.functional.helpers')(after_each)
|
||||||
|
|
||||||
|
local assert_log = helpers.assert_log
|
||||||
|
local assert_nolog = helpers.assert_nolog
|
||||||
local clear = helpers.clear
|
local clear = helpers.clear
|
||||||
local command = helpers.command
|
local command = helpers.command
|
||||||
local eq = helpers.eq
|
local eq = helpers.eq
|
||||||
|
local ok = helpers.ok
|
||||||
local feed = helpers.feed
|
local feed = helpers.feed
|
||||||
local funcs = helpers.funcs
|
local funcs = helpers.funcs
|
||||||
local nvim_prog = helpers.nvim_prog
|
local nvim_prog = helpers.nvim_prog
|
||||||
local request = helpers.request
|
local request = helpers.request
|
||||||
local retry = helpers.retry
|
local retry = helpers.retry
|
||||||
local rmdir = helpers.rmdir
|
local rmdir = helpers.rmdir
|
||||||
|
local matches = helpers.matches
|
||||||
local mkdir = helpers.mkdir
|
local mkdir = helpers.mkdir
|
||||||
local sleep = helpers.sleep
|
local sleep = helpers.sleep
|
||||||
local read_file = helpers.read_file
|
local read_file = helpers.read_file
|
||||||
|
local tmpname = helpers.tmpname
|
||||||
local trim = helpers.trim
|
local trim = helpers.trim
|
||||||
local currentdir = helpers.funcs.getcwd
|
local currentdir = helpers.funcs.getcwd
|
||||||
local iswin = helpers.iswin
|
local iswin = helpers.iswin
|
||||||
local assert_alive = helpers.assert_alive
|
local assert_alive = helpers.assert_alive
|
||||||
local expect_exit = helpers.expect_exit
|
local expect_exit = helpers.expect_exit
|
||||||
|
local write_file = helpers.write_file
|
||||||
|
|
||||||
describe('fileio', function()
|
describe('fileio', function()
|
||||||
before_each(function()
|
before_each(function()
|
||||||
@@ -140,3 +146,69 @@ describe('fileio', function()
|
|||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe('tmpdir', function()
|
||||||
|
local tmproot_pat = [=[.*[/\\]nvim%.[^/\\]+]=]
|
||||||
|
local testlog = 'Xtest_tmpdir_log'
|
||||||
|
local faketmp
|
||||||
|
|
||||||
|
before_each(function()
|
||||||
|
-- Fake /tmp dir so that we can mess it up.
|
||||||
|
faketmp = tmpname()
|
||||||
|
os.remove(faketmp)
|
||||||
|
mkdir(faketmp)
|
||||||
|
end)
|
||||||
|
|
||||||
|
after_each(function()
|
||||||
|
expect_exit(command, ':qall!')
|
||||||
|
os.remove(testlog)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('failure modes', function()
|
||||||
|
clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=faketmp, } })
|
||||||
|
assert_nolog('tempdir is not a directory', testlog)
|
||||||
|
assert_nolog('tempdir has invalid permissions', testlog)
|
||||||
|
|
||||||
|
-- Tempfiles typically look like: "…/nvim.<user>/xxx/0".
|
||||||
|
-- - "…/nvim.<user>/xxx/" is the per-process tmpdir, not shared with other Nvims.
|
||||||
|
-- - "…/nvim.<user>/" is the tmpdir root, shared by all Nvims (normally).
|
||||||
|
local tmproot = (funcs.tempname()):match(tmproot_pat)
|
||||||
|
ok(tmproot:len() > 4, 'tmproot like "nvim.foo"', tmproot)
|
||||||
|
|
||||||
|
-- Test how Nvim handles invalid tmpdir root (by hostile users or accidents).
|
||||||
|
--
|
||||||
|
-- "…/nvim.<user>/" is not a directory:
|
||||||
|
expect_exit(command, ':qall!')
|
||||||
|
rmdir(tmproot)
|
||||||
|
write_file(tmproot, '') -- Not a directory, vim_mktempdir() should skip it.
|
||||||
|
clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=faketmp, } })
|
||||||
|
matches(tmproot_pat, funcs.stdpath('run')) -- Tickle vim_mktempdir().
|
||||||
|
-- Assert that broken tmpdir root was handled.
|
||||||
|
retry(nil, 1000, function()
|
||||||
|
assert_log('tempdir root not a directory', testlog, 100)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- "…/nvim.<user>/" has wrong permissions:
|
||||||
|
if iswin() then
|
||||||
|
return -- TODO(justinmk): need setfperm/getfperm on Windows. #8244
|
||||||
|
end
|
||||||
|
os.remove(testlog)
|
||||||
|
os.remove(tmproot)
|
||||||
|
mkdir(tmproot)
|
||||||
|
funcs.setfperm(tmproot, 'rwxr--r--') -- Invalid permissions, vim_mktempdir() should skip it.
|
||||||
|
clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=faketmp, } })
|
||||||
|
matches(tmproot_pat, funcs.stdpath('run')) -- Tickle vim_mktempdir().
|
||||||
|
-- Assert that broken tmpdir root was handled.
|
||||||
|
retry(nil, 1000, function()
|
||||||
|
assert_log('tempdir root has invalid permissions', testlog, 100)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('too long', function()
|
||||||
|
local bigname = ('%s/%s'):format(faketmp, ('x'):rep(666))
|
||||||
|
mkdir(bigname)
|
||||||
|
clear({ env={ NVIM_LOG_FILE=testlog, TMPDIR=bigname, } })
|
||||||
|
matches(tmproot_pat, funcs.stdpath('run')) -- Tickle vim_mktempdir().
|
||||||
|
local len = (funcs.tempname()):len()
|
||||||
|
ok(len > 4 and len < 256, '4 < len < 256', tostring(len))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
@@ -399,7 +399,8 @@ describe('startup', function()
|
|||||||
eq({'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]])
|
eq({'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]])
|
||||||
|
|
||||||
local rtp = meths.get_option'rtp'
|
local rtp = meths.get_option'rtp'
|
||||||
ok(startswith(rtp, 'test/functional/fixtures/nvim,test/functional/fixtures/pack/*/start/*,test/functional/fixtures/start/*,test/functional/fixtures,test/functional/fixtures/middle,'), 'rtp='..rtp)
|
ok(startswith(rtp, 'test/functional/fixtures/nvim,test/functional/fixtures/pack/*/start/*,test/functional/fixtures/start/*,test/functional/fixtures,test/functional/fixtures/middle,'),
|
||||||
|
'startswith(…)', 'rtp='..rtp)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("handles the correct order with opt packages and after/", function()
|
it("handles the correct order with opt packages and after/", function()
|
||||||
|
@@ -9,6 +9,7 @@ local clear = helpers.clear
|
|||||||
local exc_exec = helpers.exc_exec
|
local exc_exec = helpers.exc_exec
|
||||||
local eval = helpers.eval
|
local eval = helpers.eval
|
||||||
local eq = helpers.eq
|
local eq = helpers.eq
|
||||||
|
local ok = helpers.ok
|
||||||
local funcs = helpers.funcs
|
local funcs = helpers.funcs
|
||||||
local insert = helpers.insert
|
local insert = helpers.insert
|
||||||
local iswin = helpers.iswin
|
local iswin = helpers.iswin
|
||||||
@@ -238,7 +239,7 @@ describe('startup defaults', function()
|
|||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('XDG-based defaults', function()
|
describe('XDG defaults', function()
|
||||||
-- Need separate describe() blocks to not run clear() twice.
|
-- Need separate describe() blocks to not run clear() twice.
|
||||||
-- Do not put before_each() here for the same reasons.
|
-- Do not put before_each() here for the same reasons.
|
||||||
|
|
||||||
@@ -282,6 +283,7 @@ describe('XDG-based defaults', function()
|
|||||||
eq('.', meths.get_option('viewdir'))
|
eq('.', meths.get_option('viewdir'))
|
||||||
eq('.', meths.get_option('directory'))
|
eq('.', meths.get_option('directory'))
|
||||||
eq('.', meths.get_option('undodir'))
|
eq('.', meths.get_option('undodir'))
|
||||||
|
ok((funcs.tempname()):len() > 4)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -306,6 +308,7 @@ describe('XDG-based defaults', function()
|
|||||||
.. env_sep.. root_path .. ('/b'):rep(2048)
|
.. env_sep.. root_path .. ('/b'):rep(2048)
|
||||||
.. (env_sep .. root_path .. '/c'):rep(512)),
|
.. (env_sep .. root_path .. '/c'):rep(512)),
|
||||||
XDG_DATA_HOME=(root_path .. ('/X'):rep(4096)),
|
XDG_DATA_HOME=(root_path .. ('/X'):rep(4096)),
|
||||||
|
XDG_RUNTIME_DIR=(root_path .. ('/X'):rep(4096)),
|
||||||
XDG_STATE_HOME=(root_path .. ('/X'):rep(4096)),
|
XDG_STATE_HOME=(root_path .. ('/X'):rep(4096)),
|
||||||
XDG_DATA_DIRS=(root_path .. ('/A'):rep(2048)
|
XDG_DATA_DIRS=(root_path .. ('/A'):rep(2048)
|
||||||
.. env_sep .. root_path .. ('/B'):rep(2048)
|
.. env_sep .. root_path .. ('/B'):rep(2048)
|
||||||
@@ -376,6 +379,7 @@ describe('XDG-based defaults', function()
|
|||||||
XDG_CONFIG_HOME='$XDG_DATA_HOME',
|
XDG_CONFIG_HOME='$XDG_DATA_HOME',
|
||||||
XDG_CONFIG_DIRS='$XDG_DATA_DIRS',
|
XDG_CONFIG_DIRS='$XDG_DATA_DIRS',
|
||||||
XDG_DATA_HOME='$XDG_CONFIG_HOME',
|
XDG_DATA_HOME='$XDG_CONFIG_HOME',
|
||||||
|
XDG_RUNTIME_DIR='$XDG_RUNTIME_DIR',
|
||||||
XDG_STATE_HOME='$XDG_CONFIG_HOME',
|
XDG_STATE_HOME='$XDG_CONFIG_HOME',
|
||||||
XDG_DATA_DIRS='$XDG_CONFIG_DIRS',
|
XDG_DATA_DIRS='$XDG_CONFIG_DIRS',
|
||||||
}})
|
}})
|
||||||
@@ -438,6 +442,7 @@ describe('XDG-based defaults', function()
|
|||||||
meths.get_option('undodir'):gsub('\\', '/'))
|
meths.get_option('undodir'):gsub('\\', '/'))
|
||||||
eq(('$XDG_CONFIG_HOME/' .. state_dir .. '/view//'),
|
eq(('$XDG_CONFIG_HOME/' .. state_dir .. '/view//'),
|
||||||
meths.get_option('viewdir'):gsub('\\', '/'))
|
meths.get_option('viewdir'):gsub('\\', '/'))
|
||||||
|
eq(nil, (funcs.tempname()):match('XDG_RUNTIME_DIR'))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -519,6 +524,7 @@ describe('stdpath()', function()
|
|||||||
eq(statedir, funcs.fnamemodify(funcs.stdpath('state'), ':t'))
|
eq(statedir, funcs.fnamemodify(funcs.stdpath('state'), ':t'))
|
||||||
eq('table', type(funcs.stdpath('config_dirs')))
|
eq('table', type(funcs.stdpath('config_dirs')))
|
||||||
eq('table', type(funcs.stdpath('data_dirs')))
|
eq('table', type(funcs.stdpath('data_dirs')))
|
||||||
|
eq('string', type(funcs.stdpath('run')))
|
||||||
assert_alive() -- Check for crash. #8393
|
assert_alive() -- Check for crash. #8393
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@@ -48,8 +48,8 @@ describe('api', function()
|
|||||||
{3:-- TERMINAL --} |
|
{3:-- TERMINAL --} |
|
||||||
]])
|
]])
|
||||||
|
|
||||||
ok(socket_session1:request("nvim_ui_attach", 42, 6, {rgb=true}))
|
ok((socket_session1:request("nvim_ui_attach", 42, 6, {rgb=true})))
|
||||||
ok(socket_session2:request("nvim_ui_attach", 25, 30, {rgb=true}))
|
ok((socket_session2:request("nvim_ui_attach", 25, 30, {rgb=true})))
|
||||||
|
|
||||||
socket_session1:notify("nvim_input", "\n[socket 1] this is more than 25 columns")
|
socket_session1:notify("nvim_input", "\n[socket 1] this is more than 25 columns")
|
||||||
socket_session2:notify("nvim_input", "\n[socket 2] input")
|
socket_session2:notify("nvim_input", "\n[socket 2] input")
|
||||||
|
@@ -5,6 +5,7 @@ local iswin = helpers.iswin
|
|||||||
local ok = helpers.ok
|
local ok = helpers.ok
|
||||||
local matches = helpers.matches
|
local matches = helpers.matches
|
||||||
local pcall_err = helpers.pcall_err
|
local pcall_err = helpers.pcall_err
|
||||||
|
local mkdir = helpers.mkdir
|
||||||
|
|
||||||
local function clear_serverlist()
|
local function clear_serverlist()
|
||||||
for _, server in pairs(funcs.serverlist()) do
|
for _, server in pairs(funcs.serverlist()) do
|
||||||
@@ -13,9 +14,19 @@ local function clear_serverlist()
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe('server', function()
|
describe('server', function()
|
||||||
before_each(clear)
|
it('serverstart() stores sockets in $XDG_RUNTIME_DIR', function()
|
||||||
|
local dir = 'Xtest_xdg_run'
|
||||||
|
mkdir(dir)
|
||||||
|
clear({ env={ XDG_RUNTIME_DIR=dir } })
|
||||||
|
matches(dir, funcs.stdpath('run'))
|
||||||
|
if not iswin() then
|
||||||
|
matches(dir, funcs.serverstart())
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
it('serverstart(), serverstop() does not set $NVIM', function()
|
it('serverstart(), serverstop() does not set $NVIM', function()
|
||||||
|
clear()
|
||||||
local s = eval('serverstart()')
|
local s = eval('serverstart()')
|
||||||
assert(s ~= nil and s:len() > 0, "serverstart() returned empty")
|
assert(s ~= nil and s:len() > 0, "serverstart() returned empty")
|
||||||
eq('', eval('$NVIM'))
|
eq('', eval('$NVIM'))
|
||||||
@@ -34,6 +45,7 @@ describe('server', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
it('sets v:servername at startup or if all servers were stopped', function()
|
it('sets v:servername at startup or if all servers were stopped', function()
|
||||||
|
clear()
|
||||||
local initial_server = meths.get_vvar('servername')
|
local initial_server = meths.get_vvar('servername')
|
||||||
assert(initial_server ~= nil and initial_server:len() > 0,
|
assert(initial_server ~= nil and initial_server:len() > 0,
|
||||||
'v:servername was not initialized')
|
'v:servername was not initialized')
|
||||||
@@ -62,11 +74,13 @@ describe('server', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
it('serverstop() returns false for invalid input', function()
|
it('serverstop() returns false for invalid input', function()
|
||||||
|
clear()
|
||||||
eq(0, eval("serverstop('')"))
|
eq(0, eval("serverstop('')"))
|
||||||
eq(0, eval("serverstop('bogus-socket-name')"))
|
eq(0, eval("serverstop('bogus-socket-name')"))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('parses endpoints', function()
|
it('parses endpoints', function()
|
||||||
|
clear()
|
||||||
clear_serverlist()
|
clear_serverlist()
|
||||||
eq({}, funcs.serverlist())
|
eq({}, funcs.serverlist())
|
||||||
|
|
||||||
@@ -111,6 +125,7 @@ describe('server', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
it('serverlist() returns the list of servers', function()
|
it('serverlist() returns the list of servers', function()
|
||||||
|
clear()
|
||||||
-- There should already be at least one server.
|
-- There should already be at least one server.
|
||||||
local n = eval('len(serverlist())')
|
local n = eval('len(serverlist())')
|
||||||
|
|
||||||
|
@@ -96,7 +96,7 @@ describe('timers', function()
|
|||||||
nvim_async("command", "let g:val = 0 | let g:c = getchar()")
|
nvim_async("command", "let g:val = 0 | let g:c = getchar()")
|
||||||
retry(nil, nil, function()
|
retry(nil, nil, function()
|
||||||
local val = eval("g:val")
|
local val = eval("g:val")
|
||||||
ok(val >= 2, "expected >= 2, got: "..tostring(val))
|
ok(val >= 2, '>= 2', tostring(val))
|
||||||
eq(0, eval("getchar(1)"))
|
eq(0, eval("getchar(1)"))
|
||||||
end)
|
end)
|
||||||
feed("c")
|
feed("c")
|
||||||
|
@@ -57,8 +57,16 @@ end
|
|||||||
function module.neq(expected, actual, context)
|
function module.neq(expected, actual, context)
|
||||||
return assert.are_not.same(expected, actual, context)
|
return assert.are_not.same(expected, actual, context)
|
||||||
end
|
end
|
||||||
function module.ok(res, msg)
|
|
||||||
return assert.is_true(res, msg)
|
--- Asserts that `cond` is true, or prints a message.
|
||||||
|
---
|
||||||
|
--- @param cond (boolean) expression to assert
|
||||||
|
--- @param expected (any) description of expected result
|
||||||
|
--- @param actual (any) description of actual result
|
||||||
|
function module.ok(cond, expected, actual)
|
||||||
|
assert((not expected and not actual) or (expected and actual), 'if "expected" is given, "actual" is also required')
|
||||||
|
local msg = expected and ('expected %s, got: %s'):format(expected, tostring(actual)) or nil
|
||||||
|
return assert(cond, msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function epicfail(state, arguments, _)
|
local function epicfail(state, arguments, _)
|
||||||
@@ -77,20 +85,33 @@ function module.matches(pat, actual)
|
|||||||
error(string.format('Pattern does not match.\nPattern:\n%s\nActual:\n%s', pat, actual))
|
error(string.format('Pattern does not match.\nPattern:\n%s\nActual:\n%s', pat, actual))
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Asserts that `pat` matches one or more lines in the tail of $NVIM_LOG_FILE.
|
--- Asserts that `pat` matches (or *not* if inverse=true) any line in the tail of `logfile`.
|
||||||
---
|
---
|
||||||
---@param pat string Lua pattern to search for in the log file
|
---@param pat (string) Lua pattern to match lines in the log file
|
||||||
---@param logfile string Full path to log file (default=$NVIM_LOG_FILE)
|
---@param logfile (string) Full path to log file (default=$NVIM_LOG_FILE)
|
||||||
---@param nrlines number Search up to this many log lines
|
---@param nrlines (number) Search up to this many log lines
|
||||||
function module.assert_log(pat, logfile, nrlines)
|
---@param inverse (boolean) Assert that the pattern does NOT match.
|
||||||
|
function module.assert_log(pat, logfile, nrlines, inverse)
|
||||||
logfile = logfile or os.getenv('NVIM_LOG_FILE') or '.nvimlog'
|
logfile = logfile or os.getenv('NVIM_LOG_FILE') or '.nvimlog'
|
||||||
|
assert(logfile ~= nil, 'no logfile')
|
||||||
nrlines = nrlines or 10
|
nrlines = nrlines or 10
|
||||||
|
inverse = inverse or false
|
||||||
local lines = module.read_file_list(logfile, -nrlines) or {}
|
local lines = module.read_file_list(logfile, -nrlines) or {}
|
||||||
|
local msg = string.format('Pattern %q %sfound in log (last %d lines): %s:\n%s',
|
||||||
|
pat, (inverse and '' or 'not '), nrlines, logfile, ' '..table.concat(lines, '\n '))
|
||||||
for _,line in ipairs(lines) do
|
for _,line in ipairs(lines) do
|
||||||
if line:match(pat) then return end
|
if line:match(pat) then
|
||||||
|
if inverse then error(msg) else return end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
error(string.format('Pattern %q not found in log (last %d lines): %s:\n%s',
|
if not inverse then error(msg) end
|
||||||
pat, nrlines, logfile, ' '..table.concat(lines, '\n ')))
|
end
|
||||||
|
|
||||||
|
--- Asserts that `pat` does NOT matche any line in the tail of `logfile`.
|
||||||
|
---
|
||||||
|
--- @see assert_log
|
||||||
|
function module.assert_nolog(pat, logfile, nrlines)
|
||||||
|
return module.assert_log(pat, logfile, nrlines, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Invokes `fn` and returns the error string (with truncated paths), or raises
|
-- Invokes `fn` and returns the error string (with truncated paths), or raises
|
||||||
@@ -284,6 +305,7 @@ local function tmpdir_is_local(dir)
|
|||||||
return not not (dir and string.find(dir, 'Xtest'))
|
return not not (dir and string.find(dir, 'Xtest'))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Creates a new temporary file for use by tests.
|
||||||
module.tmpname = (function()
|
module.tmpname = (function()
|
||||||
local seq = 0
|
local seq = 0
|
||||||
local tmpdir = tmpdir_get()
|
local tmpdir = tmpdir_get()
|
||||||
|
@@ -266,7 +266,7 @@ describe('env.c', function()
|
|||||||
|
|
||||||
itp('does not crash #3725', function()
|
itp('does not crash #3725', function()
|
||||||
local name_out = ffi.new('char[100]')
|
local name_out = ffi.new('char[100]')
|
||||||
cimp.os_get_user_name(name_out, 100)
|
cimp.os_get_username(name_out, 100)
|
||||||
local curuser = ffi.string(name_out)
|
local curuser = ffi.string(name_out)
|
||||||
|
|
||||||
local src = to_cstr("~"..curuser.."/Vcs/django-rest-framework/rest_framework/renderers.py")
|
local src = to_cstr("~"..curuser.."/Vcs/django-rest-framework/rest_framework/renderers.py")
|
||||||
|
@@ -48,10 +48,10 @@ describe('users function', function()
|
|||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('os_get_user_name', function()
|
describe('os_get_username', function()
|
||||||
itp('should write the username into the buffer and return OK', function()
|
itp('should write the username into the buffer and return OK', function()
|
||||||
local name_out = ffi.new('char[100]')
|
local name_out = ffi.new('char[100]')
|
||||||
eq(OK, users.os_get_user_name(name_out, 100))
|
eq(OK, users.os_get_username(name_out, 100))
|
||||||
eq(current_username, ffi.string(name_out))
|
eq(current_username, ffi.string(name_out))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
@@ -73,18 +73,18 @@ describe('users function', function()
|
|||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('os_get_user_directory', function()
|
describe('os_get_userdir', function()
|
||||||
itp('should return NULL if called with NULL', function()
|
itp('should return NULL if called with NULL', function()
|
||||||
eq(NULL, users.os_get_user_directory(NULL))
|
eq(NULL, users.os_get_userdir(NULL))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
itp('should return $HOME for the current user', function()
|
itp('should return $HOME for the current user', function()
|
||||||
local home = os.getenv('HOME')
|
local home = os.getenv('HOME')
|
||||||
eq(home, ffi.string((users.os_get_user_directory(current_username))))
|
eq(home, ffi.string((users.os_get_userdir(current_username))))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
itp('should return NULL if the user is not found', function()
|
itp('should return NULL if the user is not found', function()
|
||||||
eq(NULL, users.os_get_user_directory('neovim_user_not_found_test'))
|
eq(NULL, users.os_get_userdir('neovim_user_not_found_test'))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
@@ -24,7 +24,7 @@ describe('tempfile related functions', function()
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe('vim_gettempdir', function()
|
describe('vim_gettempdir', function()
|
||||||
itp('returns path to Neovim own temp directory', function()
|
itp('returns path to Nvim own temp directory', function()
|
||||||
local dir = vim_gettempdir()
|
local dir = vim_gettempdir()
|
||||||
assert.True(dir ~= nil and dir:len() > 0)
|
assert.True(dir ~= nil and dir:len() > 0)
|
||||||
-- os_file_is_writable returns 2 for a directory which we have rights
|
-- os_file_is_writable returns 2 for a directory which we have rights
|
||||||
@@ -36,9 +36,7 @@ describe('tempfile related functions', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
itp('returns the same directory on each call', function()
|
itp('returns the same directory on each call', function()
|
||||||
local dir1 = vim_gettempdir()
|
eq(vim_gettempdir(), vim_gettempdir())
|
||||||
local dir2 = vim_gettempdir()
|
|
||||||
eq(dir1, dir2)
|
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -54,12 +52,10 @@ describe('tempfile related functions', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
itp('generate different names on each call', function()
|
itp('generate different names on each call', function()
|
||||||
local fst = vim_tempname()
|
neq(vim_tempname(), vim_tempname())
|
||||||
local snd = vim_tempname()
|
|
||||||
neq(fst, snd)
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
itp('generate file name in Neovim own temp directory', function()
|
itp('generate file name in Nvim own temp directory', function()
|
||||||
local dir = vim_gettempdir()
|
local dir = vim_gettempdir()
|
||||||
local file = vim_tempname()
|
local file = vim_tempname()
|
||||||
eq(string.sub(file, 1, string.len(dir)), dir)
|
eq(string.sub(file, 1, string.len(dir)), dir)
|
||||||
|
Reference in New Issue
Block a user