aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/main.c211
-rw-r--r--src/nvim/memline.c123
-rw-r--r--src/nvim/option.c322
-rw-r--r--src/nvim/options.lua10
-rw-r--r--src/nvim/os/env.c68
-rw-r--r--src/nvim/os/fs.c10
-rw-r--r--src/nvim/os/os.h2
-rw-r--r--src/nvim/os/stdpaths.c108
-rw-r--r--src/nvim/os/stdpaths_defs.h14
-rw-r--r--src/nvim/os/unix_defs.h37
-rw-r--r--src/nvim/os/win_defs.h4
-rw-r--r--src/nvim/path.c59
-rw-r--r--src/nvim/shada.c31
-rw-r--r--src/nvim/testdir/unix.vim3
-rw-r--r--src/nvim/version.c25
15 files changed, 748 insertions, 279 deletions
diff --git a/src/nvim/main.c b/src/nvim/main.c
index d865260295..60a242fae3 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -287,8 +287,8 @@ int main(int argc, char **argv)
* Set the default values for the options that use Rows and Columns.
*/
win_init_size();
- /* Set the 'diff' option now, so that it can be checked for in a .vimrc
- * file. There is no buffer yet though. */
+ // Set the 'diff' option now, so that it can be checked for in a vimrc
+ // file. There is no buffer yet though.
if (params.diff_mode)
diff_win_options(firstwin, FALSE);
@@ -345,7 +345,7 @@ int main(int argc, char **argv)
*/
load_plugins();
- /* Decide about window layout for diff mode after reading vimrc. */
+ // Decide about window layout for diff mode after reading vimrc.
set_window_layout(&params);
/*
@@ -358,10 +358,8 @@ int main(int argc, char **argv)
mch_exit(0);
}
- /*
- * Set a few option defaults after reading .vimrc files:
- * 'title' and 'icon', Unix: 'shellpipe' and 'shellredir'.
- */
+ // Set a few option defaults after reading vimrc files:
+ // 'title' and 'icon', Unix: 'shellpipe' and 'shellredir'.
set_init_3();
TIME_MSG("inits 3");
@@ -1551,8 +1549,8 @@ static void create_windows(mparm_T *parmp)
if (parmp->window_count == 0)
parmp->window_count = GARGCOUNT;
if (parmp->window_count > 1) {
- /* Don't change the windows if there was a command in .vimrc that
- * already split some windows */
+ // Don't change the windows if there was a command in vimrc that
+ // already split some windows
if (parmp->window_layout == 0)
parmp->window_layout = WIN_HOR;
if (parmp->window_layout == WIN_TABS) {
@@ -1574,14 +1572,11 @@ static void create_windows(mparm_T *parmp)
getout(1);
do_modelines(0); /* do modelines */
} else {
- /*
- * Open a buffer for windows that don't have one yet.
- * Commands in the .vimrc might have loaded a file or split the window.
- * Watch out for autocommands that delete a window.
- */
- /*
- * Don't execute Win/Buf Enter/Leave autocommands here
- */
+ // Open a buffer for windows that don't have one yet.
+ // Commands in the vimrc might have loaded a file or split the window.
+ // Watch out for autocommands that delete a window.
+ //
+ // Don't execute Win/Buf Enter/Leave autocommands here
++autocmd_no_enter;
++autocmd_no_leave;
dorewind = TRUE;
@@ -1691,8 +1686,8 @@ static void edit_buffers(mparm_T *parmp)
}
advance = TRUE;
- /* Only open the file if there is no file in this window yet (that can
- * happen when .vimrc contains ":sall"). */
+ // Only open the file if there is no file in this window yet (that can
+ // happen when vimrc contains ":sall").
if (curbuf == firstwin->w_buffer || curbuf->b_ffname == NULL) {
curwin->w_arg_idx = arg_idx;
/* Edit file from arg list, if there is one. When "Quit" selected
@@ -1801,117 +1796,121 @@ static void exe_commands(mparm_T *parmp)
TIME_MSG("executing command arguments");
}
-/*
- * Source startup scripts.
- */
-static void source_startup_scripts(mparm_T *parmp)
+/// Source vimrc or do other user initialization
+///
+/// Does one of the following things, stops after whichever succeeds:
+///
+/// 1. Execution of VIMINIT environment variable.
+/// 2. Sourcing user vimrc file ($XDG_CONFIG_HOME/nvim/init.vim).
+/// 3. Sourcing other vimrc files ($XDG_CONFIG_DIRS[1]/nvim/init.vim, …).
+/// 4. Execution of EXINIT environment variable.
+///
+/// @return True if it is needed to attempt to source exrc file according to
+/// 'exrc' option definition.
+static bool do_user_initialization(void)
+ FUNC_ATTR_WARN_UNUSED_RESULT
{
- int i;
+ bool do_exrc = p_exrc;
+ if (process_env("VIMINIT", true) == OK) {
+ return do_exrc;
+ }
+ char_u *user_vimrc = (char_u *)stdpaths_user_conf_subpath("init.vim");
+ if (do_source(user_vimrc, true, DOSO_VIMRC) != FAIL) {
+ if (do_exrc) {
+ do_exrc = (path_full_compare((char_u *)VIMRC_FILE, user_vimrc, false)
+ != kEqualFiles);
+ }
+ xfree(user_vimrc);
+ return do_exrc;
+ }
+ xfree(user_vimrc);
+ char *const config_dirs = stdpaths_get_xdg_var(kXDGConfigDirs);
+ if (config_dirs != NULL) {
+ const void *iter = NULL;
+ do {
+ const char *dir;
+ size_t dir_len;
+ iter = vim_colon_env_iter(config_dirs, iter, &dir, &dir_len);
+ if (dir == NULL || dir_len == 0) {
+ break;
+ }
+ const char path_tail[] = { 'n', 'v', 'i', 'm', PATHSEP,
+ 'i', 'n', 'i', 't', '.', 'v', 'i', 'm', NUL };
+ char *vimrc = xmalloc(dir_len + sizeof(path_tail) + 1);
+ memmove(vimrc, dir, dir_len);
+ vimrc[dir_len] = PATHSEP;
+ memmove(vimrc + dir_len + 1, path_tail, sizeof(path_tail));
+ if (do_source((char_u *) vimrc, true, DOSO_VIMRC) != FAIL) {
+ if (do_exrc) {
+ do_exrc = (path_full_compare((char_u *)VIMRC_FILE, (char_u *)vimrc,
+ false) != kEqualFiles);
+ }
+ xfree(vimrc);
+ xfree(config_dirs);
+ return do_exrc;
+ }
+ xfree(vimrc);
+ } while (iter != NULL);
+ xfree(config_dirs);
+ }
+ if (process_env("EXINIT", false) == OK) {
+ return do_exrc;
+ }
+ return do_exrc;
+}
- /*
- * If -u argument given, use only the initializations from that file and
- * nothing else.
- */
+/// Source startup scripts
+///
+/// @param[in]
+static void source_startup_scripts(const mparm_T *const parmp)
+ FUNC_ATTR_NONNULL_ALL
+{
+ // If -u argument given, use only the initializations from that file and
+ // nothing else.
if (parmp->use_vimrc != NULL) {
if (strcmp(parmp->use_vimrc, "NONE") == 0
|| strcmp(parmp->use_vimrc, "NORC") == 0) {
if (parmp->use_vimrc[2] == 'N')
- p_lpl = FALSE; // don't load plugins either
+ p_lpl = false; // don't load plugins either
} else {
if (do_source((char_u *)parmp->use_vimrc, FALSE, DOSO_NONE) != OK)
EMSG2(_("E282: Cannot read from \"%s\""), parmp->use_vimrc);
}
} else if (!silent_mode) {
-
- /*
- * Get system wide defaults, if the file name is defined.
- */
#ifdef SYS_VIMRC_FILE
- (void)do_source((char_u *)SYS_VIMRC_FILE, FALSE, DOSO_NONE);
-#endif
-
- /*
- * Try to read initialization commands from the following places:
- * - environment variable VIMINIT
- * - user vimrc file (~/.vimrc)
- * - second user vimrc file ($VIM/.vimrc for Dos)
- * - environment variable EXINIT
- * - user exrc file (~/.exrc)
- * - second user exrc file ($VIM/.exrc for Dos)
- * The first that exists is used, the rest is ignored.
- */
- if (process_env("VIMINIT", true) != OK) {
- if (do_source((char_u *)USR_VIMRC_FILE, TRUE, DOSO_VIMRC) == FAIL
-#ifdef USR_VIMRC_FILE2
- && do_source((char_u *)USR_VIMRC_FILE2, TRUE,
- DOSO_VIMRC) == FAIL
+ // Get system wide defaults, if the file name is defined.
+ (void) do_source((char_u *)SYS_VIMRC_FILE, false, DOSO_NONE);
#endif
-#ifdef USR_VIMRC_FILE3
- && do_source((char_u *)USR_VIMRC_FILE3, TRUE,
- DOSO_VIMRC) == FAIL
-#endif
- && process_env("EXINIT", FALSE) == FAIL
- && do_source((char_u *)USR_EXRC_FILE, FALSE, DOSO_NONE) == FAIL) {
-#ifdef USR_EXRC_FILE2
- (void)do_source((char_u *)USR_EXRC_FILE2, FALSE, DOSO_NONE);
-#endif
- }
- }
- /*
- * Read initialization commands from ".vimrc" or ".exrc" in current
- * directory. This is only done if the 'exrc' option is set.
- * Because of security reasons we disallow shell and write commands
- * now, except for unix if the file is owned by the user or 'secure'
- * option has been reset in environment of global ".exrc" or ".vimrc".
- * Only do this if VIMRC_FILE is not the same as USR_VIMRC_FILE or
- * SYS_VIMRC_FILE.
- */
- if (p_exrc) {
+ if (do_user_initialization()) {
+ // Read initialization commands from ".vimrc" or ".exrc" in current
+ // directory. This is only done if the 'exrc' option is set.
+ // Because of security reasons we disallow shell and write commands
+ // now, except for unix if the file is owned by the user or 'secure'
+ // option has been reset in environment of global "exrc" or "vimrc".
+ // Only do this if VIMRC_FILE is not the same as vimrc file sourced in
+ // do_user_initialization.
#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))
#endif
secure = p_secure;
- i = FAIL;
- if (path_full_compare((char_u *)USR_VIMRC_FILE,
- (char_u *)VIMRC_FILE, FALSE) != kEqualFiles
-#ifdef USR_VIMRC_FILE2
- && path_full_compare((char_u *)USR_VIMRC_FILE2,
- (char_u *)VIMRC_FILE, FALSE) != kEqualFiles
-#endif
-#ifdef USR_VIMRC_FILE3
- && path_full_compare((char_u *)USR_VIMRC_FILE3,
- (char_u *)VIMRC_FILE, FALSE) != kEqualFiles
-#endif
-#ifdef SYS_VIMRC_FILE
- && path_full_compare((char_u *)SYS_VIMRC_FILE,
- (char_u *)VIMRC_FILE, FALSE) != kEqualFiles
-#endif
- )
- i = do_source((char_u *)VIMRC_FILE, TRUE, DOSO_VIMRC);
-
- if (i == FAIL) {
+ if (do_source((char_u *)VIMRC_FILE, true, DOSO_VIMRC) == FAIL) {
#if defined(UNIX)
- /* if ".exrc" is not owned by user set 'secure' mode */
- if (!file_owned(EXRC_FILE))
+ // if ".exrc" is not owned by user set 'secure' mode
+ if (!file_owned(EXRC_FILE)) {
secure = p_secure;
- else
+ } else {
secure = 0;
+ }
#endif
- if ( path_full_compare((char_u *)USR_EXRC_FILE,
- (char_u *)EXRC_FILE, FALSE) != kEqualFiles
-#ifdef USR_EXRC_FILE2
- && path_full_compare((char_u *)USR_EXRC_FILE2,
- (char_u *)EXRC_FILE, FALSE) != kEqualFiles
-#endif
- )
- (void)do_source((char_u *)EXRC_FILE, FALSE, DOSO_NONE);
+ (void)do_source((char_u *)EXRC_FILE, false, DOSO_NONE);
}
}
- if (secure == 2)
- need_wait_return = TRUE;
+ if (secure == 2) {
+ need_wait_return = true;
+ }
secure = 0;
}
did_source_startup_scripts = true;
@@ -2042,7 +2041,7 @@ static void usage(void)
mch_msg(_(" -r, -L List swap files and exit\n"));
mch_msg(_(" -r <file> Recover crashed session\n"));
mch_msg(_(" -u <nvimrc> Use <nvimrc> instead of the default\n"));
- mch_msg(_(" -i <shada> Use <shada> instead of the default " SHADA_FILE "\n")); // NOLINT(whitespace/line_length)
+ mch_msg(_(" -i <shada> Use <shada> instead of the default\n"));
mch_msg(_(" --noplugin Don't load plugin scripts\n"));
mch_msg(_(" -o[N] Open N windows (default: one for each file)\n"));
mch_msg(_(" -O[N] Like -o but split vertically\n"));
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index d90e91be5d..aa3e0e0b1c 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -406,10 +406,12 @@ void ml_setname(buf_T *buf)
* Try all directories in the 'directory' option.
*/
dirp = p_dir;
+ bool found_existing_dir = false;
for (;; ) {
if (*dirp == NUL) /* tried all directories, fail */
break;
- fname = findswapname(buf, &dirp, mfp->mf_fname);
+ fname = (char_u *)findswapname(buf, (char **)&dirp, (char *)mfp->mf_fname,
+ &found_existing_dir);
/* alloc's fname */
if (dirp == NULL) /* out of memory */
break;
@@ -504,13 +506,15 @@ void ml_open_file(buf_T *buf)
* Try all directories in 'directory' option.
*/
dirp = p_dir;
+ bool found_existing_dir = false;
for (;; ) {
if (*dirp == NUL)
break;
- /* There is a small chance that between choosing the swap file name
- * and creating it, another Vim creates the file. In that case the
- * creation will fail and we will use another directory. */
- fname = findswapname(buf, &dirp, NULL); /* allocates fname */
+ // There is a small chance that between choosing the swap file name
+ // and creating it, another Vim creates the file. In that case the
+ // creation will fail and we will use another directory.
+ fname = (char_u *)findswapname(buf, (char **)&dirp, NULL,
+ &found_existing_dir);
if (dirp == NULL)
break; /* out of memory */
if (fname == NULL)
@@ -3222,45 +3226,56 @@ static int do_swapexists(buf_T *buf, char_u *fname)
return 0;
}
-/*
- * Find out what name to use for the swap file for buffer 'buf'.
- *
- * Several names are tried to find one that does not exist
- * Returns the name in allocated memory or NULL.
- * When out of memory "dirp" is set to NULL.
- *
- * Note: If BASENAMELEN is not correct, you will get error messages for
- * not being able to open the swap or undo file
- * Note: May trigger SwapExists autocmd, pointers may change!
- */
-static char_u *
-findswapname (
- buf_T *buf,
- char_u **dirp, /* pointer to list of directories */
- char_u *old_fname /* don't give warning for this file name */
-)
+/// Find out what name to use for the swap file for buffer 'buf'.
+///
+/// Several names are tried to find one that does not exist. Last directory in
+/// option is automatically created.
+///
+/// @note If BASENAMELEN is not correct, you will get error messages for
+/// not being able to open the swap or undo file.
+/// @note May trigger SwapExists autocmd, pointers may change!
+///
+/// @param[in] buf Buffer for which swap file names needs to be found.
+/// @param[in,out] dirp Pointer to a list of directories. When out of memory,
+/// is set to NULL. Is advanced to the next directory in
+/// the list otherwise.
+/// @param[in] old_fname Allowed existing swap file name. Except for this
+/// case, name of the non-existing file is used.
+/// @param[in,out] found_existing_dir If points to true, then new directory
+/// for swap file is not created. At first
+/// findswapname() call this argument must
+/// point to false. This parameter may only
+/// be set to true by this function, it is
+/// never set to false.
+///
+/// @return [allocated] Name of the swap file.
+static char *findswapname(buf_T *buf, char **dirp, char *old_fname,
+ bool *found_existing_dir)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1, 2, 4)
{
- char_u *fname;
- int n;
- char_u *dir_name;
- char_u *buf_fname = buf->b_fname;
+ char *fname;
+ size_t n;
+ char *dir_name;
+ char *buf_fname = (char *) buf->b_fname;
/*
* Isolate a directory name from *dirp and put it in dir_name.
* First allocate some memory to put the directory name in.
*/
- dir_name = xmalloc(STRLEN(*dirp) + 1);
- (void)copy_option_part(dirp, dir_name, 31000, ",");
+ const size_t dir_len = strlen(*dirp);
+ dir_name = xmalloc(dir_len + 1);
+ (void)copy_option_part((char_u **) dirp, (char_u *) dir_name, dir_len, ",");
/*
* we try different names until we find one that does not exist yet
*/
- fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name);
+ fname = (char *)makeswapname((char_u *)buf_fname, buf->b_ffname, buf,
+ (char_u *)dir_name);
for (;; ) {
if (fname == NULL) /* must be out of memory */
break;
- if ((n = (int)STRLEN(fname)) == 0) { /* safety check */
+ if ((n = strlen(fname)) == 0) { /* safety check */
xfree(fname);
fname = NULL;
break;
@@ -3269,7 +3284,7 @@ findswapname (
// Extra security check: When a swap file is a symbolic link, this
// is most likely a symlink attack.
FileInfo file_info;
- bool file_or_link_found = os_fileinfo_link((char *)fname, &file_info);
+ bool file_or_link_found = os_fileinfo_link(fname, &file_info);
if (!file_or_link_found) {
break;
}
@@ -3300,7 +3315,7 @@ findswapname (
* Try to read block 0 from the swap file to get the original
* file name (and inode number).
*/
- fd = os_open((char *)fname, O_RDONLY, 0);
+ fd = os_open(fname, O_RDONLY, 0);
if (fd >= 0) {
if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) {
/*
@@ -3311,7 +3326,7 @@ findswapname (
if (b0.b0_flags & B0_SAME_DIR) {
if (fnamecmp(path_tail(buf->b_ffname),
path_tail(b0.b0_fname)) != 0
- || !same_directory(fname, buf->b_ffname)) {
+ || !same_directory((char_u *) fname, buf->b_ffname)) {
/* Symlinks may point to the same file even
* when the name differs, need to check the
* inode too. */
@@ -3351,12 +3366,12 @@ findswapname (
* user anyway.
*/
if (swap_exists_action != SEA_NONE
- && has_autocmd(EVENT_SWAPEXISTS, buf_fname, buf))
- choice = do_swapexists(buf, fname);
+ && has_autocmd(EVENT_SWAPEXISTS, (char_u *) buf_fname, buf))
+ choice = do_swapexists(buf, (char_u *) fname);
if (choice == 0) {
/* Show info about the existing swap file. */
- attention_message(buf, fname);
+ attention_message(buf, (char_u *) fname);
/* We don't want a 'q' typed at the more-prompt
* interrupt loading a file. */
@@ -3364,20 +3379,21 @@ findswapname (
}
if (swap_exists_action != SEA_NONE && choice == 0) {
- char_u *name;
+ char *name;
- name = xmalloc(STRLEN(fname)
- + STRLEN(_("Swap file \""))
- + STRLEN(_("\" already exists!")) + 5);
+ const size_t fname_len = strlen(fname);
+ name = xmalloc(fname_len
+ + strlen(_("Swap file \""))
+ + strlen(_("\" already exists!")) + 5);
STRCPY(name, _("Swap file \""));
- home_replace(NULL, fname, name + STRLEN(name),
- 1000, TRUE);
+ home_replace(NULL, (char_u *) fname, (char_u *)&name[strlen(name)],
+ fname_len, true);
STRCAT(name, _("\" already exists!"));
choice = do_dialog(VIM_WARNING,
(char_u *)_("VIM - ATTENTION"),
- name == NULL
- ? (char_u *)_("Swap file already exists!")
- : name,
+ (char_u *)(name == NULL
+ ? _("Swap file already exists!")
+ : name),
# if defined(UNIX)
process_still_running
? (char_u *)_(
@@ -3409,7 +3425,7 @@ findswapname (
swap_exists_action = SEA_RECOVER;
break;
case 4:
- os_remove((char *)fname);
+ os_remove(fname);
break;
case 5:
swap_exists_action = SEA_QUIT;
@@ -3421,7 +3437,7 @@ findswapname (
}
/* If the file was deleted this fname can be used. */
- if (!os_file_exists(fname))
+ if (!os_file_exists((char_u *) fname))
break;
} else
{
@@ -3454,6 +3470,19 @@ findswapname (
--fname[n - 1]; /* ".swo", ".swn", etc. */
}
+ if (os_isdir((char_u *) dir_name)) {
+ *found_existing_dir = true;
+ } else if (!*found_existing_dir && **dirp == NUL) {
+ int ret;
+ char *failed_dir;
+ if ((ret = os_mkdir_recurse(dir_name, 0755, &failed_dir)) != 0) {
+ EMSG3(_("E303: Unable to create directory \"%s\" for swap file, "
+ "recovery impossible: %s"),
+ failed_dir, os_strerror(ret));
+ xfree(failed_dir);
+ }
+ }
+
xfree(dir_name);
return fname;
}
diff --git a/src/nvim/option.c b/src/nvim/option.c
index a578f2bb01..a4cfe45f10 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -301,6 +301,243 @@ static char *(p_cot_values[]) = {"menu", "menuone", "longest", "preview",
# include "option.c.generated.h"
#endif
+/// Append string with escaped commas
+static char *strcpy_comma_escaped(char *dest, const char *src, const size_t len)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ size_t shift = 0;
+ for (size_t i = 0; i < len; i++) {
+ if (src[i] == ',') {
+ dest[i + shift++] = '\\';
+ }
+ dest[i + shift] = src[i];
+ }
+ return &dest[len + shift];
+}
+
+/// Compute length of a colon-separated value, doubled and with some suffixes
+///
+/// @param[in] val Colon-separated array value.
+/// @param[in] common_suf_len Length of the common suffix which is appended to
+/// each item in the array, twice.
+/// @param[in] single_suf_len Length of the suffix which is appended to each
+/// item in the array once.
+///
+/// @return Length of the comma-separated string array that contains each item
+/// in the original array twice with suffixes with given length
+/// (common_suf is present after each new item, single_suf is present
+/// after half of the new items) and with commas after each item, commas
+/// inside the values are escaped.
+static inline size_t compute_double_colon_len(const char *const val,
+ const size_t common_suf_len,
+ const size_t single_suf_len)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
+{
+ if (val == NULL && *val) {
+ return 0;
+ }
+ size_t ret = 0;
+ const void *iter = NULL;
+ do {
+ size_t dir_len;
+ const char *dir;
+ iter = vim_colon_env_iter(val, iter, &dir, &dir_len);
+ if (dir != NULL && dir_len > 0) {
+ ret += ((dir_len + memcnt(dir, ',', dir_len) + common_suf_len
+ + !after_pathsep(dir, dir + dir_len)) * 2
+ + single_suf_len);
+ }
+ } while (iter != NULL);
+ return ret;
+}
+
+#define NVIM_SIZE (sizeof("nvim") - 1)
+
+/// Add directories to a comma-separated array from a colon-separated one
+///
+/// Commas are escaped in process. To each item PATHSEP "nvim" is appended in
+/// addition to suf1 and suf2.
+///
+/// @param[in,out] dest Destination comma-separated array.
+/// @param[in] val Source colon-separated array.
+/// @param[in] suf1 If not NULL, suffix appended to destination. Prior to it
+/// directory separator is appended. Suffix must not contain
+/// commas.
+/// @param[in] len1 Length of the suf1.
+/// @param[in] suf2 If not NULL, another suffix appended to destination. Again
+/// with directory separator behind. Suffix must not contain
+/// commas.
+/// @param[in] len2 Length of the suf2.
+/// @param[in] forward If true, iterate over val in forward direction.
+/// Otherwise in reverse.
+///
+/// @return (dest + appended_characters_length)
+static inline char *add_colon_dirs(char *dest, const char *const val,
+ const char *const suf1, const size_t len1,
+ const char *const suf2, const size_t len2,
+ const bool forward)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ARG(1)
+{
+ if (val == NULL && *val) {
+ return dest;
+ }
+ const void *iter = NULL;
+ do {
+ size_t dir_len;
+ const char *dir;
+ iter = (forward ? vim_colon_env_iter : vim_colon_env_iter_rev)(
+ val, iter, &dir, &dir_len);
+ if (dir != NULL && dir_len > 0) {
+ dest = strcpy_comma_escaped(dest, dir, dir_len);
+ if (!after_pathsep(dest - 1, dest)) {
+ *dest++ = PATHSEP;
+ }
+ memmove(dest, "nvim", NVIM_SIZE);
+ dest += NVIM_SIZE;
+ if (suf1 != NULL) {
+ *dest++ = PATHSEP;
+ memmove(dest, suf1, len1);
+ dest += len1;
+ if (suf2 != NULL) {
+ *dest++ = PATHSEP;
+ memmove(dest, suf2, len2);
+ dest += len2;
+ }
+ }
+ *dest++ = ',';
+ }
+ } while (iter != NULL);
+ return dest;
+}
+
+/// Add directory to a comma-separated list of directories
+///
+/// In the added directory comma is escaped.
+///
+/// @param[in,out] dest Destination comma-separated array.
+/// @param[in] dir Directory to append.
+/// @param[in] append_nvim If true, append "nvim" as the very first suffix.
+/// @param[in] suf1 If not NULL, suffix appended to destination. Prior to it
+/// directory separator is appended. Suffix must not contain
+/// commas.
+/// @param[in] len1 Length of the suf1.
+/// @param[in] suf2 If not NULL, another suffix appended to destination. Again
+/// with directory separator behind. Suffix must not contain
+/// commas.
+/// @param[in] len2 Length of the suf2.
+/// @param[in] forward If true, iterate over val in forward direction.
+/// Otherwise in reverse.
+///
+/// @return (dest + appended_characters_length)
+static inline char *add_dir(char *dest, const char *const dir,
+ const size_t dir_len, const bool append_nvim,
+ const char *const suf1, const size_t len1,
+ const char *const suf2, const size_t len2)
+ FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (dir == NULL && dir_len != 0) {
+ return dest;
+ }
+ dest = strcpy_comma_escaped(dest, dir, dir_len);
+ if (append_nvim) {
+ if (!after_pathsep(dest - 1, dest)) {
+ *dest++ = PATHSEP;
+ }
+ memmove(dest, "nvim", NVIM_SIZE);
+ dest += NVIM_SIZE;
+ if (suf1 != NULL) {
+ *dest++ = PATHSEP;
+ memmove(dest, suf1, len1);
+ dest += len1;
+ if (suf2 != NULL) {
+ *dest++ = PATHSEP;
+ memmove(dest, suf2, len2);
+ dest += len2;
+ }
+ }
+ }
+ *dest++ = ',';
+ return dest;
+}
+
+/// Set &runtimepath to default value
+static void set_runtimepath_default(void)
+{
+ size_t rtp_size = 0;
+ char *const data_home = stdpaths_get_xdg_var(kXDGDataHome);
+ char *const config_home = stdpaths_get_xdg_var(kXDGConfigHome);
+ char *const vimruntime = vim_getenv("VIMRUNTIME");
+ char *const data_dirs = stdpaths_get_xdg_var(kXDGDataDirs);
+ char *const config_dirs = stdpaths_get_xdg_var(kXDGConfigDirs);
+#define SITE_SIZE (sizeof("site") - 1)
+#define AFTER_SIZE (sizeof("after") - 1)
+ size_t data_len = 0;
+ size_t config_len = 0;
+ size_t vimruntime_len = 0;
+ if (data_home != NULL) {
+ data_len = strlen(data_home);
+ if (data_len != 0) {
+ rtp_size += ((data_len + memcnt(data_home, ',', data_len)
+ + NVIM_SIZE + 1 + SITE_SIZE + 1
+ + !after_pathsep(data_home, data_home + data_len)) * 2
+ + AFTER_SIZE + 1);
+ }
+ }
+ if (config_home != NULL) {
+ config_len = strlen(config_home);
+ if (config_len != 0) {
+ rtp_size += ((config_len + memcnt(config_home, ',', config_len)
+ + NVIM_SIZE + 1
+ + !after_pathsep(config_home, config_home + config_len)) * 2
+ + AFTER_SIZE + 1);
+ }
+ }
+ if (vimruntime != NULL) {
+ vimruntime_len = strlen(vimruntime);
+ if (vimruntime_len != 0) {
+ rtp_size += vimruntime_len + memcnt(vimruntime, ',', vimruntime_len) + 1;
+ }
+ }
+ rtp_size += compute_double_colon_len(data_dirs, NVIM_SIZE + 1 + SITE_SIZE + 1,
+ AFTER_SIZE + 1);
+ rtp_size += compute_double_colon_len(config_dirs, NVIM_SIZE + 1,
+ AFTER_SIZE + 1);
+ if (rtp_size == 0) {
+ return;
+ }
+ char *const rtp = xmalloc(rtp_size);
+ char *rtp_cur = rtp;
+ rtp_cur = add_dir(rtp_cur, config_home, config_len, true, NULL, 0, NULL, 0);
+ rtp_cur = add_colon_dirs(rtp_cur, config_dirs, NULL, 0, NULL, 0, true);
+ rtp_cur = add_dir(rtp_cur, data_home, data_len, true, "site", SITE_SIZE,
+ NULL, 0);
+ rtp_cur = add_colon_dirs(rtp_cur, data_dirs, "site", SITE_SIZE, NULL, 0,
+ true);
+ rtp_cur = add_dir(rtp_cur, vimruntime, vimruntime_len, false, NULL, 0,
+ NULL, 0);
+ rtp_cur = add_colon_dirs(rtp_cur, data_dirs, "site", SITE_SIZE,
+ "after", AFTER_SIZE, false);
+ rtp_cur = add_dir(rtp_cur, data_home, data_len, true, "site", SITE_SIZE,
+ "after", AFTER_SIZE);
+ rtp_cur = add_colon_dirs(rtp_cur, config_dirs, "after", AFTER_SIZE, NULL, 0,
+ false);
+ rtp_cur = add_dir(rtp_cur, config_home, config_len, true,
+ "after", AFTER_SIZE, NULL, 0);
+ // Strip trailing comma.
+ rtp_cur[-1] = NUL;
+ assert((size_t) (rtp_cur - rtp) == rtp_size);
+#undef SITE_SIZE
+#undef AFTER_SIZE
+ set_string_default("runtimepath", rtp, true);
+ xfree(data_dirs);
+ xfree(config_dirs);
+ xfree(data_home);
+ xfree(config_home);
+ xfree(vimruntime);
+}
+
+#undef NVIM_SIZE
+
/*
* Initialize the options, first part.
*
@@ -308,7 +545,6 @@ static char *(p_cot_values[]) = {"menu", "menuone", "longest", "preview",
*/
void set_init_1(void)
{
- char_u *p;
int opt_idx;
langmap_init();
@@ -320,8 +556,12 @@ void set_init_1(void)
* Find default value for 'shell' option.
* Don't use it if it is empty.
*/
- if ((p = (char_u *)os_getenv("SHELL")) != NULL)
- set_string_default("sh", p);
+ {
+ const char *shell = os_getenv("SHELL");
+ if (shell != NULL) {
+ set_string_default("sh", (char *) shell, false);
+ }
+ }
/*
* Set the default for 'backupskip' to include environment variables for
@@ -339,17 +579,18 @@ void set_init_1(void)
ga_init(&ga, 1, 100);
for (size_t n = 0; n < ARRAY_SIZE(names); ++n) {
bool mustfree = true;
+ char *p;
# ifdef UNIX
if (*names[n] == NUL) {
- p = (char_u *)"/tmp";
+ p = "/tmp";
mustfree = false;
}
else
# endif
- p = (char_u *)vim_getenv(names[n]);
+ p = vim_getenv(names[n]);
if (p != NULL && *p != NUL) {
// First time count the NUL, otherwise count the ','.
- len = (int)STRLEN(p) + 3;
+ len = (int)strlen(p) + 3;
ga_grow(&ga, len);
if (!GA_EMPTY(&ga))
STRCAT(ga.ga_data, ",");
@@ -363,8 +604,7 @@ void set_init_1(void)
}
}
if (ga.ga_data != NULL) {
- set_string_default("bsk", ga.ga_data);
- xfree(ga.ga_data);
+ set_string_default("bsk", ga.ga_data, true);
}
}
@@ -425,17 +665,34 @@ void set_init_1(void)
#if defined(MSWIN) || defined(MAC)
/* Set print encoding on platforms that don't default to latin1 */
- set_string_default("penc",
- (char_u *)"hp-roman8"
- );
+ set_string_default("printencoding", "hp-roman8", false);
#endif
- /* 'printexpr' must be allocated to be able to evaluate it. */
- set_string_default(
- "pexpr",
- (char_u *)
- "system('lpr' . (&printdevice == '' ? '' : ' -P' . &printdevice) . ' ' . v:fname_in) . delete(v:fname_in) + v:shell_error"
- );
+ // 'printexpr' must be allocated to be able to evaluate it.
+ set_string_default("printexpr",
+#ifdef UNIX
+ "system(['lpr'] "
+ "+ (empty(&printdevice)?[]:['-P', &printdevice]) "
+ "+ [v:fname_in])"
+ ". delete(v:fname_in)"
+ "+ v:shell_error",
+#elif defined(MSWIN)
+ "system(['copy', v:fname_in, "
+ "empty(&printdevice)?'LPT1':&printdevice])"
+ ". delete(v:fname_in)",
+#else
+ "",
+#endif
+ false);
+
+ set_string_default("viewdir", stdpaths_user_data_subpath("view", 0), true);
+ set_string_default("backupdir", stdpaths_user_data_subpath("backup", 0),
+ true);
+ set_string_default("directory", stdpaths_user_data_subpath("swap", 2), true);
+ set_string_default("undodir", stdpaths_user_data_subpath("undo", 0), true);
+ // Set default for &runtimepath. All necessary expansions are performed in
+ // this function.
+ set_runtimepath_default();
/*
* Set all the options (except the terminal options) to their default
@@ -478,14 +735,16 @@ void set_init_1(void)
* default.
*/
for (opt_idx = 0; options[opt_idx].fullname; opt_idx++) {
+ char *p;
if ((options[opt_idx].flags & P_GETTEXT)
- && options[opt_idx].var != NULL)
- p = (char_u *)_(*(char **)options[opt_idx].var);
- else
- p = option_expand(opt_idx, NULL);
+ && options[opt_idx].var != NULL) {
+ p = _(*(char **)options[opt_idx].var);
+ } else {
+ p = (char *) option_expand(opt_idx, NULL);
+ }
if (p != NULL) {
- p = vim_strsave(p);
- *(char_u **)options[opt_idx].var = p;
+ p = xstrdup(p);
+ *(char **)options[opt_idx].var = p;
/* VIMEXP
* Defaults for all expanded options are currently the same for Vi
* and Vim. When this changes, add some code here! Also need to
@@ -493,7 +752,7 @@ void set_init_1(void)
*/
if (options[opt_idx].flags & P_DEF_ALLOCED)
xfree(options[opt_idx].def_val[VI_DEFAULT]);
- options[opt_idx].def_val[VI_DEFAULT] = p;
+ options[opt_idx].def_val[VI_DEFAULT] = (char_u *) p;
options[opt_idx].flags |= P_DEF_ALLOCED;
}
}
@@ -522,14 +781,14 @@ void set_init_1(void)
(void)set_chars_option(&p_lcs);
/* enc_locale() will try to find the encoding of the current locale. */
- p = enc_locale();
+ char_u *p = enc_locale();
if (p != NULL) {
char_u *save_enc;
/* Try setting 'encoding' and check if the value is valid.
* If not, go back to the default "utf-8". */
save_enc = p_enc;
- p_enc = p;
+ p_enc = (char_u *) p;
if (STRCMP(p_enc, "gb18030") == 0) {
/* We don't support "gb18030", but "cp936" is a good substitute
* for practical purposes, thus use that. It's not an alias to
@@ -674,7 +933,9 @@ set_options_default (
///
/// @param name The name of the option
/// @param val The value of the option
-void set_string_default(const char *name, const char_u *val)
+/// @param allocated If true, do not copy default as it was already allocated.
+static void set_string_default(const char *name, char *val, bool allocated)
+ FUNC_ATTR_NONNULL_ALL
{
int opt_idx = findoption((char_u *)name);
if (opt_idx >= 0) {
@@ -682,7 +943,10 @@ void set_string_default(const char *name, const char_u *val)
xfree(options[opt_idx].def_val[VI_DEFAULT]);
}
- options[opt_idx].def_val[VI_DEFAULT] = (char_u *) xstrdup((char *) val);
+ options[opt_idx].def_val[VI_DEFAULT] = (char_u *) (
+ allocated
+ ? (char_u *) val
+ : (char_u *) xstrdup(val));
options[opt_idx].flags |= P_DEF_ALLOCED;
}
}
@@ -5948,7 +6212,7 @@ static void paste_option_changed(void)
old_p_paste = p_paste;
}
-/// vimrc_found() - Called when a ".vimrc" or "VIMINIT" has been found.
+/// vimrc_found() - Called when a vimrc or "VIMINIT" has been found.
///
/// Set the values for options that didn't get set yet to the Vim defaults.
/// When "fname" is not NULL, use it to set $"envname" when it wasn't set yet.
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 842b0a7c82..633eabab60 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -176,7 +176,7 @@ return {
vi_def=true,
expand=true,
varname='p_bdir',
- defaults={if_true={vi=macros('DFLT_BDIR')}}
+ defaults={if_true={vi=''}}
},
{
full_name='backupext', abbreviation='bex',
@@ -627,7 +627,7 @@ return {
vi_def=true,
expand=true,
varname='p_dir',
- defaults={if_true={vi=macros('DFLT_DIR')}}
+ defaults={if_true={vi=''}}
},
{
full_name='display', abbreviation='dy',
@@ -1916,7 +1916,7 @@ return {
vi_def=true,
expand=true,
varname='p_rtp',
- defaults={if_true={vi=macros('DFLT_RUNTIMEPATH')}}
+ defaults={if_true={vi=''}}
},
{
full_name='scroll', abbreviation='scr',
@@ -2524,7 +2524,7 @@ return {
vi_def=true,
expand=true,
varname='p_udir',
- defaults={if_true={vi="."}}
+ defaults={if_true={vi=''}}
},
{
full_name='undofile', abbreviation='udf',
@@ -2585,7 +2585,7 @@ return {
vi_def=true,
expand=true,
varname='p_vdir',
- defaults={if_true={vi=macros('DFLT_VDIR')}}
+ defaults={if_true={vi=''}}
},
{
full_name='viewoptions', abbreviation='vop',
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index 7be8a868bd..bf6db97fcf 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -415,6 +415,74 @@ static char *remove_tail(char *p, char *pend, char *name)
return pend;
}
+/// Iterate over colon-separated list
+///
+/// @note Environment variables must not be modified during iteration.
+///
+/// @param[in] val Value of the environment variable to iterate over.
+/// @param[in] iter Pointer used for iteration. Must be NULL on first
+/// iteration.
+/// @param[out] dir Location where pointer to the start of the current
+/// directory name should be saved. May be set to NULL.
+/// @param[out] len Location where current directory length should be saved.
+///
+/// @return Next iter argument value or NULL when iteration should stop.
+const void *vim_colon_env_iter(const char *const val,
+ const void *const iter,
+ const char **const dir,
+ size_t *const len)
+ FUNC_ATTR_NONNULL_ARG(1, 3, 4) FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ const char *varval = (const char *) iter;
+ if (varval == NULL) {
+ varval = val;
+ }
+ *dir = varval;
+ const char *const dirend = strchr(varval, ':');
+ if (dirend == NULL) {
+ *len = strlen(varval);
+ return NULL;
+ } else {
+ *len = (size_t) (dirend - varval);
+ return dirend + 1;
+ }
+}
+
+/// Iterate over colon-separated list in reverse order
+///
+/// @note Environment variables must not be modified during iteration.
+///
+/// @param[in] val Value of the environment variable to iterate over.
+/// @param[in] iter Pointer used for iteration. Must be NULL on first
+/// iteration.
+/// @param[out] dir Location where pointer to the start of the current
+/// directory name should be saved. May be set to NULL.
+/// @param[out] len Location where current directory length should be saved.
+///
+/// @return Next iter argument value or NULL when iteration should stop.
+const void *vim_colon_env_iter_rev(const char *const val,
+ const void *const iter,
+ const char **const dir,
+ size_t *const len)
+ FUNC_ATTR_NONNULL_ARG(1, 3, 4) FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ const char *varend = (const char *) iter;
+ if (varend == NULL) {
+ varend = val + strlen(val) - 1;
+ }
+ const size_t varlen = (size_t) (varend - val) + 1;
+ const char *const colon = xmemrchr(val, ':', varlen);
+ if (colon == NULL) {
+ *len = varlen;
+ *dir = val;
+ return NULL;
+ } else {
+ *dir = colon + 1;
+ *len = (size_t) (varend - colon);
+ return colon - 1;
+ }
+}
+
/// Vim's version of getenv().
/// Special handling of $HOME, $VIM and $VIMRUNTIME, allowing the user to
/// override the vim runtime directory at runtime. Also does ACP to 'enc'
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 522e49950c..05f0f53c63 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -366,11 +366,17 @@ int os_mkdir_recurse(const char *const dir, int32_t mode,
}
while (e != real_end) {
if (e > past_head) {
- *e = '/';
+ *e = PATHSEP;
} else {
*past_head = past_head_save;
}
- e += strlen(e);
+ const size_t component_len = strlen(e);
+ e += component_len;
+ if (e == real_end
+ && memcnt(e - component_len, PATHSEP, component_len) == component_len) {
+ // Path ends with something like "////". Ignore this.
+ break;
+ }
int ret;
if ((ret = os_mkdir(curdir, mode)) != 0) {
*failed_dir = curdir;
diff --git a/src/nvim/os/os.h b/src/nvim/os/os.h
index 69bd1ff4fd..3e89e5a94a 100644
--- a/src/nvim/os/os.h
+++ b/src/nvim/os/os.h
@@ -5,6 +5,7 @@
#include <uv.h>
#include "nvim/os/fs_defs.h"
+#include "nvim/os/stdpaths_defs.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -12,6 +13,7 @@
# include "os/mem.h.generated.h"
# include "os/env.h.generated.h"
# include "os/users.h.generated.h"
+# include "os/stdpaths.h.generated.h"
#endif
#endif // NVIM_OS_OS_H
diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c
new file mode 100644
index 0000000000..ce374828f9
--- /dev/null
+++ b/src/nvim/os/stdpaths.c
@@ -0,0 +1,108 @@
+#include <stdbool.h>
+
+#include "nvim/os/stdpaths_defs.h"
+#include "nvim/os/os.h"
+#include "nvim/path.h"
+#include "nvim/memory.h"
+#include "nvim/ascii.h"
+
+/// Names of the environment variables, mapped to XDGVarType values
+static const char *xdg_env_vars[] = {
+ [kXDGConfigHome] = "XDG_CONFIG_HOME",
+ [kXDGDataHome] = "XDG_DATA_HOME",
+ [kXDGCacheHome] = "XDG_CACHE_HOME",
+ [kXDGRuntimeDir] = "XDG_RUNTIME_DIR",
+ [kXDGConfigDirs] = "XDG_CONFIG_DIRS",
+ [kXDGDataDirs] = "XDG_DATA_DIRS",
+};
+
+/// Defaults for XDGVarType values
+///
+/// Used in case environment variables contain nothing. Need to be expanded.
+static const char *const xdg_defaults[] = {
+#ifdef WIN32
+ // Windows
+ [kXDGConfigHome] = "$LOCALAPPDATA\\nvim\\config",
+ [kXDGDataHome] = "$LOCALAPPDATA\\nvim\\data",
+ [kXDGCacheHome] = "$LOCALAPPDATA\\nvim\\cache",
+ [kXDGRuntimeDir] = "",
+ [kXDGConfigDirs] = NULL,
+ [kXDGDataDirs] = NULL,
+#else
+ // Linux, BSD, CYGWIN, Apple
+ [kXDGConfigHome] = "~/.config",
+ [kXDGDataHome] = "~/.local/share",
+ [kXDGCacheHome] = "~/.cache",
+ [kXDGRuntimeDir] = "",
+ [kXDGConfigDirs] = "/etc/xdg/",
+ [kXDGDataDirs] = "/usr/local/share/:/usr/share/",
+#endif
+};
+
+/// Return XDG variable value
+///
+/// @param[in] idx XDG variable to use.
+///
+/// @return [allocated] variable value.
+char *stdpaths_get_xdg_var(const XDGVarType idx)
+ FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ const char *const env = xdg_env_vars[idx];
+ const char *const fallback = xdg_defaults[idx];
+
+ const char *const env_val = os_getenv(env);
+ char *ret = NULL;
+ if (env_val != NULL) {
+ ret = xstrdup(env_val);
+ } else if (fallback) {
+ ret = (char *) expand_env_save((char_u *)fallback);
+ }
+
+ return ret;
+}
+
+/// Return nvim-specific XDG directory subpath
+///
+/// @param[in] idx XDG directory to use.
+///
+/// @return [allocated] `{xdg_directory}/nvim`
+static char *get_xdg_home(const XDGVarType idx)
+ FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ char *dir = stdpaths_get_xdg_var(idx);
+ if (dir) {
+ dir = concat_fnames_realloc(dir, "nvim", true);
+ }
+ return dir;
+}
+
+/// Return subpath of $XDG_CONFIG_HOME
+///
+/// @param[in] fname New component of the path.
+///
+/// @return [allocated] `$XDG_CONFIG_HOME/nvim/{fname}`
+char *stdpaths_user_conf_subpath(const char *fname)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
+{
+ return concat_fnames_realloc(get_xdg_home(kXDGConfigHome), fname, true);
+}
+
+/// Return subpath of $XDG_DATA_HOME
+///
+/// @param[in] fname New component of the path.
+/// @param[in] trailing_pathseps Amount of trailing path separators to add.
+///
+/// @return [allocated] `$XDG_DATA_HOME/nvim/{fname}`
+char *stdpaths_user_data_subpath(const char *fname,
+ const size_t trailing_pathseps)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
+{
+ char *ret = concat_fnames_realloc(get_xdg_home(kXDGDataHome), fname, true);
+ if (trailing_pathseps) {
+ const size_t len = strlen(ret);
+ ret = xrealloc(ret, len + trailing_pathseps + 1);
+ memset(ret + len, PATHSEP, trailing_pathseps);
+ ret[len + trailing_pathseps] = NUL;
+ }
+ return ret;
+}
diff --git a/src/nvim/os/stdpaths_defs.h b/src/nvim/os/stdpaths_defs.h
new file mode 100644
index 0000000000..1433e0bc62
--- /dev/null
+++ b/src/nvim/os/stdpaths_defs.h
@@ -0,0 +1,14 @@
+#ifndef NVIM_OS_STDPATHS_DEFS_H
+#define NVIM_OS_STDPATHS_DEFS_H
+
+/// List of possible XDG variables
+typedef enum {
+ kXDGConfigHome, ///< XDG_CONFIG_HOME
+ kXDGDataHome, ///< XDG_DATA_HOME
+ kXDGCacheHome, ///< XDG_CACHE_HOME
+ kXDGRuntimeDir, ///< XDG_RUNTIME_DIR
+ kXDGConfigDirs, ///< XDG_CONFIG_DIRS
+ kXDGDataDirs, ///< XDG_DATA_DIRS
+} XDGVarType;
+
+#endif // NVIM_OS_STDPATHS_DEFS_H
diff --git a/src/nvim/os/unix_defs.h b/src/nvim/os/unix_defs.h
index 949973bf40..b1511d4b56 100644
--- a/src/nvim/os/unix_defs.h
+++ b/src/nvim/os/unix_defs.h
@@ -20,7 +20,7 @@
// Unix system-dependent file names
#ifndef SYS_VIMRC_FILE
-# define SYS_VIMRC_FILE "$VIM/nvimrc"
+# define SYS_VIMRC_FILE "$VIM/sysinit.vim"
#endif
#ifndef DFLT_HELPFILE
# define DFLT_HELPFILE "$VIMRUNTIME/doc/help.txt"
@@ -28,46 +28,11 @@
#ifndef SYNTAX_FNAME
# define SYNTAX_FNAME "$VIMRUNTIME/syntax/%s.vim"
#endif
-#ifndef USR_EXRC_FILE
-# define USR_EXRC_FILE "~/.exrc"
-#endif
-#ifndef USR_VIMRC_FILE
-# define USR_VIMRC_FILE "~/.nvimrc"
-#endif
-#ifndef USR_VIMRC_FILE2
-# define USR_VIMRC_FILE2 "~/.nvim/nvimrc"
-#endif
#ifndef EXRC_FILE
# define EXRC_FILE ".exrc"
#endif
#ifndef VIMRC_FILE
# define VIMRC_FILE ".nvimrc"
#endif
-#ifndef SHADA_FILE
-# define SHADA_FILE "~/.nvim/shada/main.shada"
-#endif
-
-// Default for 'backupdir'.
-#ifndef DFLT_BDIR
-# define DFLT_BDIR ".,~/tmp,~/"
-#endif
-
-// Default for 'directory'.
-#ifndef DFLT_DIR
-# define DFLT_DIR ".,~/tmp,/var/tmp,/tmp"
-#endif
-
-// Default for 'viewdir'.
-#ifndef DFLT_VDIR
-# define DFLT_VDIR "~/.nvim/view"
-#endif
-
-#ifdef RUNTIME_GLOBAL
-# define DFLT_RUNTIMEPATH "~/.nvim," RUNTIME_GLOBAL ",$VIMRUNTIME," \
- RUNTIME_GLOBAL "/after,~/.nvim/after"
-#else
-# define DFLT_RUNTIMEPATH \
- "~/.nvim,$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after,~/.nvim/after"
-#endif
#endif // NVIM_OS_UNIX_DEFS_H
diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h
index b7ec50a109..d614582250 100644
--- a/src/nvim/os/win_defs.h
+++ b/src/nvim/os/win_defs.h
@@ -7,13 +7,9 @@
#define TEMP_FILE_PATH_MAXLEN _MAX_PATH
// Defines needed to fix the build on Windows:
-// - USR_EXRC_FILE
-// - USR_VIMRC_FILE
-// - SHADA_FILE
// - DFLT_DIR
// - DFLT_BDIR
// - DFLT_VDIR
-// - DFLT_RUNTIMEPATH
// - EXRC_FILE
// - VIMRC_FILE
// - SYNTAX_FNAME
diff --git a/src/nvim/path.c b/src/nvim/path.c
index a9d1d052d4..eaca85ed40 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -329,6 +329,31 @@ int vim_fnamencmp(char_u *x, char_u *y, size_t len)
#endif
}
+/// Append fname2 to fname1
+///
+/// @param[in] fname1 First fname to append to.
+/// @param[in] len1 Length of fname1.
+/// @param[in] fname2 Secord part of the file name.
+/// @param[in] len2 Length of fname2.
+/// @param[in] sep If true and fname1 does not end with a path separator,
+/// add a path separator before fname2.
+///
+/// @return fname1
+static inline char *do_concat_fnames(char *fname1, const size_t len1,
+ const char *fname2, const size_t len2,
+ const bool sep)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
+{
+ if (sep && *fname1 && !after_pathsep(fname1, fname1 + len1)) {
+ fname1[len1] = PATHSEP;
+ memmove(fname1 + len1 + 1, fname2, len2 + 1);
+ } else {
+ memmove(fname1 + len1, fname2, len2 + 1);
+ }
+
+ return fname1;
+}
+
/// Concatenate file names fname1 and fname2 into allocated memory.
///
/// Only add a '/' or '\\' when 'sep' is true and it is necessary.
@@ -339,17 +364,33 @@ int vim_fnamencmp(char_u *x, char_u *y, size_t len)
/// if necessary
/// @return [allocated] Concatenation of fname1 and fname2.
char *concat_fnames(const char *fname1, const char *fname2, bool sep)
- FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_NONNULL_RET
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
{
- char *dest = xmalloc(strlen(fname1) + strlen(fname2) + 3);
-
- strcpy(dest, fname1);
- if (sep) {
- add_pathsep(dest);
- }
- strcat(dest, fname2);
+ const size_t len1 = strlen(fname1);
+ const size_t len2 = strlen(fname2);
+ char *dest = xmalloc(len1 + len2 + 3);
+ memmove(dest, fname1, len1 + 1);
+ return do_concat_fnames(dest, len1, fname2, len2, sep);
+}
- return dest;
+/// Concatenate file names fname1 and fname2
+///
+/// Like concat_fnames(), but in place of allocating new memory it reallocates
+/// fname1. For this reason fname1 must be allocated with xmalloc, and can no
+/// longer be used after running concat_fnames_realloc.
+///
+/// @param fname1 is the first part of the path or filename
+/// @param fname2 is the second half of the path or filename
+/// @param sep is a flag to indicate a path separator should be added
+/// if necessary
+/// @return [allocated] Concatenation of fname1 and fname2.
+char *concat_fnames_realloc(char *fname1, const char *fname2, bool sep)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
+{
+ const size_t len1 = strlen(fname1);
+ const size_t len2 = strlen(fname2);
+ return do_concat_fnames(xrealloc(fname1, len1 + len2 + 3), len1,
+ fname2, len2, sep);
}
/*
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 93a40fa736..f8643fe655 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -1583,6 +1583,20 @@ shada_read_main_cycle_end:
kh_dealloc(strset, &oldfiles_set);
}
+/// Default shada file location: cached path
+static char *default_shada_file = NULL;
+
+/// Get the default ShaDa file
+static const char *shada_get_default_file(void)
+ FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (default_shada_file == NULL) {
+ char *shada_dir = stdpaths_user_data_subpath("shada", 0);
+ default_shada_file = concat_fnames_realloc(shada_dir, "main.shada", true);
+ }
+ return default_shada_file;
+}
+
/// Get the ShaDa file name to use
///
/// If "file" is given and not empty, use it (has already been expanded by
@@ -1600,22 +1614,7 @@ static char *shada_filename(const char *file)
file = used_shada_file;
} else {
if ((file = find_shada_parameter('n')) == NULL || *file == NUL) {
-#ifdef SHADA_FILE2
- // don't use $HOME when not defined (turned into "c:/"!).
- if (os_getenv((char_u *)"HOME") == NULL) {
- // don't use $VIM when not available.
- expand_env((char_u *)"$VIM", NameBuff, MAXPATHL);
- if (STRCMP("$VIM", NameBuff) != 0) { // $VIM was expanded
- file = SHADA_FILE2;
- } else {
- file = SHADA_FILE;
- }
- } else {
-#endif
- file = SHADA_FILE;
-#ifdef SHADA_FILE2
- }
-#endif
+ file = shada_get_default_file();
}
// XXX It used to be one level lower, so that whatever is in
// `used_shada_file` was expanded. I intentionally moved it here
diff --git a/src/nvim/testdir/unix.vim b/src/nvim/testdir/unix.vim
index aa1f6a92bc..a7daacf8cf 100644
--- a/src/nvim/testdir/unix.vim
+++ b/src/nvim/testdir/unix.vim
@@ -4,3 +4,6 @@ set shell=sh
" Don't depend on system locale, always use utf-8
set encoding=utf-8
+
+" Use safer defaults for various directories
+set backupdir=. directory=. undodir=. viewdir=.
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 961c017bd5..3b80336817 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -1067,31 +1067,6 @@ void list_version(void)
version_msg(SYS_VIMRC_FILE);
version_msg("\"\n");
#endif // ifdef SYS_VIMRC_FILE
-#ifdef USR_VIMRC_FILE
- version_msg(_(" user vimrc file: \""));
- version_msg(USR_VIMRC_FILE);
- version_msg("\"\n");
-#endif // ifdef USR_VIMRC_FILE
-#ifdef USR_VIMRC_FILE2
- version_msg(_(" 2nd user vimrc file: \""));
- version_msg(USR_VIMRC_FILE2);
- version_msg("\"\n");
-#endif // ifdef USR_VIMRC_FILE2
-#ifdef USR_VIMRC_FILE3
- version_msg(_(" 3rd user vimrc file: \""));
- version_msg(USR_VIMRC_FILE3);
- version_msg("\"\n");
-#endif // ifdef USR_VIMRC_FILE3
-#ifdef USR_EXRC_FILE
- version_msg(_(" user exrc file: \""));
- version_msg(USR_EXRC_FILE);
- version_msg("\"\n");
-#endif // ifdef USR_EXRC_FILE
-#ifdef USR_EXRC_FILE2
- version_msg(_(" 2nd user exrc file: \""));
- version_msg(USR_EXRC_FILE2);
- version_msg("\"\n");
-#endif // ifdef USR_EXRC_FILE2
#ifdef HAVE_PATHDEF
if (*default_vim_dir != NUL) {