diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2015-10-25 22:38:23 -0400 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2015-10-25 22:38:23 -0400 |
commit | 1ca5646bb52ec5c23b28f45bb7bc5d25cffad9b0 (patch) | |
tree | 7495d3f50b897e74fc4597d061d427a4e9b1ae36 /src | |
parent | de4cb766ca381c09fd3f938136c1932ebf008f63 (diff) | |
parent | 42047acb4f07c689936b051864c6b4448b1b6aa1 (diff) | |
download | rneovim-1ca5646bb52ec5c23b28f45bb7bc5d25cffad9b0.tar.gz rneovim-1ca5646bb52ec5c23b28f45bb7bc5d25cffad9b0.tar.bz2 rneovim-1ca5646bb52ec5c23b28f45bb7bc5d25cffad9b0.zip |
Merge pull request #3470 from ZyX-I/pr-3198
XDG base directory specification support
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/main.c | 211 | ||||
-rw-r--r-- | src/nvim/memline.c | 123 | ||||
-rw-r--r-- | src/nvim/option.c | 322 | ||||
-rw-r--r-- | src/nvim/options.lua | 10 | ||||
-rw-r--r-- | src/nvim/os/env.c | 68 | ||||
-rw-r--r-- | src/nvim/os/fs.c | 10 | ||||
-rw-r--r-- | src/nvim/os/os.h | 2 | ||||
-rw-r--r-- | src/nvim/os/stdpaths.c | 108 | ||||
-rw-r--r-- | src/nvim/os/stdpaths_defs.h | 14 | ||||
-rw-r--r-- | src/nvim/os/unix_defs.h | 37 | ||||
-rw-r--r-- | src/nvim/os/win_defs.h | 4 | ||||
-rw-r--r-- | src/nvim/path.c | 59 | ||||
-rw-r--r-- | src/nvim/shada.c | 31 | ||||
-rw-r--r-- | src/nvim/testdir/unix.vim | 3 | ||||
-rw-r--r-- | src/nvim/version.c | 25 |
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(¶ms); /* @@ -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) { |