aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/buffer.c2
-rw-r--r--src/nvim/ex_cmds.c8
-rw-r--r--src/nvim/ex_getln.c31
-rw-r--r--src/nvim/file_search.c59
-rw-r--r--src/nvim/memline.c11
-rw-r--r--src/nvim/os/env.c22
-rw-r--r--src/nvim/path.c3
-rw-r--r--src/nvim/quickfix.c7
-rw-r--r--src/nvim/spell.c22
-rw-r--r--src/nvim/spellfile.c8
-rw-r--r--src/nvim/tag.c23
-rw-r--r--src/nvim/testdir/Makefile1
-rw-r--r--src/nvim/testdir/runtest.vim3
-rw-r--r--src/nvim/testdir/setup.vim11
-rw-r--r--src/nvim/testdir/test_alot.vim1
-rw-r--r--src/nvim/testdir/test_cd.vim13
-rw-r--r--src/nvim/testdir/test_windows_home.vim121
17 files changed, 275 insertions, 71 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 4152c16588..a8d29a3a53 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -4420,7 +4420,7 @@ do_arg_all (
if (i < alist->al_ga.ga_len
&& (AARGLIST(alist)[i].ae_fnum == buf->b_fnum
|| path_full_compare(alist_name(&AARGLIST(alist)[i]),
- buf->b_ffname, TRUE) & kEqualFiles)) {
+ buf->b_ffname, true) & kEqualFiles)) {
int weight = 1;
if (old_curtab == curtab) {
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 3c6a8b1074..176df58fb9 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -4977,7 +4977,8 @@ void fix_help_buffer(void)
while (*p != NUL) {
copy_option_part(&p, NameBuff, MAXPATHL, ",");
rt = (char_u *)vim_getenv("VIMRUNTIME");
- if (path_full_compare(rt, NameBuff, FALSE) != kEqualFiles) {
+ if (rt != NULL
+ && path_full_compare(rt, NameBuff, false) != kEqualFiles) {
int fcount;
char_u **fnames;
FILE *fd;
@@ -5197,8 +5198,9 @@ static void helptags_one(char_u *dir, char_u *ext, char_u *tagfname,
* add the "help-tags" tag.
*/
ga_init(&ga, (int)sizeof(char_u *), 100);
- if (add_help_tags || path_full_compare((char_u *)"$VIMRUNTIME/doc",
- dir, FALSE) == kEqualFiles) {
+ if (add_help_tags
+ || path_full_compare((char_u *)"$VIMRUNTIME/doc",
+ dir, false) == kEqualFiles) {
s = xmalloc(18 + STRLEN(tagfname));
sprintf((char *)s, "help-tags\t%s\t1\n", tagfname);
GA_APPEND(char_u *, &ga, s);
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 775d002e58..fd11acff84 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -5062,38 +5062,33 @@ static void * call_user_expand_func(user_expand_func_T user_expand_func,
*/
static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file)
{
- char_u *retstr;
- char_u *s;
char_u *e;
- char_u keep;
garray_T ga;
- retstr = call_user_expand_func((user_expand_func_T)call_func_retstr, xp,
- num_file, file);
+ char_u *const retstr = call_user_expand_func(
+ (user_expand_func_T)call_func_retstr, xp, num_file, file);
if (retstr == NULL) {
return FAIL;
}
ga_init(&ga, (int)sizeof(char *), 3);
- for (s = retstr; *s != NUL; s = e) {
+ for (char_u *s = retstr; *s != NUL; s = e) {
e = vim_strchr(s, '\n');
if (e == NULL)
e = s + STRLEN(s);
- keep = *e;
- *e = 0;
+ const int keep = *e;
+ *e = NUL;
- if (xp->xp_pattern[0] && vim_regexec(regmatch, s, (colnr_T)0) == 0) {
- *e = keep;
- if (*e != NUL)
- ++e;
- continue;
+ const bool skip = xp->xp_pattern[0]
+ && vim_regexec(regmatch, s, (colnr_T)0) == 0;
+ *e = keep;
+ if (!skip) {
+ GA_APPEND(char_u *, &ga, vim_strnsave(s, (int)(e - s)));
}
- GA_APPEND(char_u *, &ga, vim_strnsave(s, (int)(e - s)));
-
- *e = keep;
- if (*e != NUL)
- ++e;
+ if (*e != NUL) {
+ e++;
+ }
}
xfree(retstr);
*file = ga.ga_data;
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index 5b17b58781..6c1a2f6d7b 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -577,7 +577,7 @@ char_u *vim_findfile(void *search_ctx_arg)
char_u *file_path;
char_u *rest_of_wildcards;
char_u *path_end = NULL;
- ff_stack_T *stackp;
+ ff_stack_T *stackp = NULL;
size_t len;
char_u *p;
char_u *suf;
@@ -683,28 +683,40 @@ char_u *vim_findfile(void *search_ctx_arg)
dirptrs[0] = file_path;
dirptrs[1] = NULL;
- /* if we have a start dir copy it in */
+ // if we have a start dir copy it in
if (!vim_isAbsName(stackp->ffs_fix_path)
&& search_ctx->ffsc_start_dir) {
+ if (STRLEN(search_ctx->ffsc_start_dir) + 1 >= MAXPATHL) {
+ goto fail;
+ }
STRCPY(file_path, search_ctx->ffsc_start_dir);
- add_pathsep((char *)file_path);
+ if (!add_pathsep((char *)file_path)) {
+ goto fail;
+ }
}
- /* append the fix part of the search path */
+ // append the fix part of the search path
+ if (STRLEN(file_path) + STRLEN(stackp->ffs_fix_path) + 1 >= MAXPATHL) {
+ goto fail;
+ }
STRCAT(file_path, stackp->ffs_fix_path);
- add_pathsep((char *)file_path);
+ if (!add_pathsep((char *)file_path)) {
+ goto fail;
+ }
rest_of_wildcards = stackp->ffs_wc_path;
if (*rest_of_wildcards != NUL) {
len = STRLEN(file_path);
if (STRNCMP(rest_of_wildcards, "**", 2) == 0) {
- /* pointer to the restrict byte
- * The restrict byte is not a character!
- */
+ // pointer to the restrict byte
+ // The restrict byte is not a character!
p = rest_of_wildcards + 2;
if (*p > 0) {
(*p)--;
+ if (len + 1 >= MAXPATHL) {
+ goto fail;
+ }
file_path[len++] = '*';
}
@@ -729,8 +741,12 @@ char_u *vim_findfile(void *search_ctx_arg)
* on the stack again for further search.
*/
while (*rest_of_wildcards
- && !vim_ispathsep(*rest_of_wildcards))
+ && !vim_ispathsep(*rest_of_wildcards)) {
+ if (len + 1 >= MAXPATHL) {
+ goto fail;
+ }
file_path[len++] = *rest_of_wildcards++;
+ }
file_path[len] = NUL;
if (vim_ispathsep(*rest_of_wildcards))
@@ -773,10 +789,15 @@ char_u *vim_findfile(void *search_ctx_arg)
&& !os_isdir(stackp->ffs_filearray[i]))
continue; /* not a directory */
- /* prepare the filename to be checked for existence
- * below */
+ // prepare the filename to be checked for existence below
+ if (STRLEN(stackp->ffs_filearray[i]) + 1
+ + STRLEN(search_ctx->ffsc_file_to_search) >= MAXPATHL) {
+ goto fail;
+ }
STRCPY(file_path, stackp->ffs_filearray[i]);
- add_pathsep((char *)file_path);
+ if (!add_pathsep((char *)file_path)) {
+ goto fail;
+ }
STRCAT(file_path, search_ctx->ffsc_file_to_search);
/*
@@ -924,8 +945,14 @@ char_u *vim_findfile(void *search_ctx_arg)
if (*search_ctx->ffsc_start_dir == 0)
break;
+ if (STRLEN(search_ctx->ffsc_start_dir) + 1
+ + STRLEN(search_ctx->ffsc_fix_path) >= MAXPATHL) {
+ goto fail;
+ }
STRCPY(file_path, search_ctx->ffsc_start_dir);
- add_pathsep((char *)file_path);
+ if (!add_pathsep((char *)file_path)) {
+ goto fail;
+ }
STRCAT(file_path, search_ctx->ffsc_fix_path);
/* create a new stack entry */
@@ -936,6 +963,8 @@ char_u *vim_findfile(void *search_ctx_arg)
break;
}
+fail:
+ ff_free_stack_element(stackp);
xfree(file_path);
return NULL;
}
@@ -1194,6 +1223,10 @@ static ff_stack_T *ff_pop(ff_search_ctx_T *search_ctx)
*/
static void ff_free_stack_element(ff_stack_T *stack_ptr)
{
+ if (stack_ptr == NULL) {
+ return;
+ }
+
/* free handles possible NULL pointers */
xfree(stack_ptr->ffs_fix_path);
xfree(stack_ptr->ffs_wc_path);
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index fc9a1e8271..84ceaf0973 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -1365,11 +1365,11 @@ recover_names (
*/
if (curbuf->b_ml.ml_mfp != NULL
&& (p = curbuf->b_ml.ml_mfp->mf_fname) != NULL) {
- for (int i = 0; i < num_files; ++i)
- if (path_full_compare(p, files[i], TRUE) & kEqualFiles) {
- /* Remove the name from files[i]. Move further entries
- * down. When the array becomes empty free it here, since
- * FreeWild() won't be called below. */
+ for (int i = 0; i < num_files; i++) {
+ if (path_full_compare(p, files[i], true) & kEqualFiles) {
+ // Remove the name from files[i]. Move further entries
+ // down. When the array becomes empty free it here, since
+ // FreeWild() won't be called below.
xfree(files[i]);
if (--num_files == 0)
xfree(files);
@@ -1377,6 +1377,7 @@ recover_names (
for (; i < num_files; ++i)
files[i] = files[i + 1];
}
+ }
}
if (nr > 0) {
file_count += num_files;
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index 0df857352b..6997156d4c 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -196,16 +196,19 @@ void init_homedir(void)
const char *homedrive = os_getenv("HOMEDRIVE");
const char *homepath = os_getenv("HOMEPATH");
if (homepath == NULL) {
- homepath = "\\";
+ homepath = "\\";
}
- if (homedrive != NULL && strlen(homedrive) + strlen(homepath) < MAXPATHL) {
+ if (homedrive != NULL
+ && strlen(homedrive) + strlen(homepath) < MAXPATHL) {
snprintf(os_buf, MAXPATHL, "%s%s", homedrive, homepath);
if (os_buf[0] != NUL) {
var = os_buf;
- vim_setenv("HOME", os_buf);
}
}
}
+ if (var == NULL) {
+ var = os_getenv("USERPROFILE");
+ }
#endif
if (var != NULL) {
@@ -608,6 +611,12 @@ char *vim_getenv(const char *name)
return xstrdup(kos_env_path);
}
+#ifdef WIN32
+ if (strcmp(name, "HOME") == 0) {
+ return xstrdup(homedir);
+ }
+#endif
+
bool vimruntime = (strcmp(name, "VIMRUNTIME") == 0);
if (!vimruntime && strcmp(name, "VIM") != 0) {
return NULL;
@@ -758,7 +767,12 @@ size_t home_replace(const buf_T *const buf, const char_u *src,
dirlen = strlen(homedir);
}
- const char *const homedir_env = os_getenv("HOME");
+ const char *homedir_env = os_getenv("HOME");
+#ifdef WIN32
+ if (homedir_env == NULL) {
+ homedir_env = os_getenv("USERPROFILE");
+ }
+#endif
char *homedir_env_mod = (char *)homedir_env;
bool must_free = false;
diff --git a/src/nvim/path.c b/src/nvim/path.c
index d5c636ff08..0b90329686 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -52,7 +52,8 @@
/// @param s2 Second file name.
/// @param checkname When both files don't exist, only compare their names.
/// @return Enum of type FileComparison. @see FileComparison.
-FileComparison path_full_compare(char_u *s1, char_u *s2, int checkname)
+FileComparison path_full_compare(char_u *const s1, char_u *const s2,
+ const bool checkname)
{
assert(s1 && s2);
char_u exp1[MAXPATHL];
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 47760e1e70..664dd3e968 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -3603,15 +3603,16 @@ void ex_vimgrep(exarg_T *eap)
goto theend;
}
- if (s != NULL && *s == NUL) {
- /* Pattern is empty, use last search pattern. */
+ if (s == NULL || *s == NUL) {
+ // Pattern is empty, use last search pattern.
if (last_search_pat() == NULL) {
EMSG(_(e_noprevre));
goto theend;
}
regmatch.regprog = vim_regcomp(last_search_pat(), RE_MAGIC);
- } else
+ } else {
regmatch.regprog = vim_regcomp(s, RE_MAGIC);
+ }
if (regmatch.regprog == NULL)
goto theend;
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index 0714eb3137..cb03257878 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -2043,9 +2043,11 @@ char_u *did_set_spelllang(win_T *wp)
dont_use_region = true;
// Check if we loaded this language before.
- for (slang = first_lang; slang != NULL; slang = slang->sl_next)
- if (path_full_compare(lang, slang->sl_fname, FALSE) == kEqualFiles)
+ for (slang = first_lang; slang != NULL; slang = slang->sl_next) {
+ if (path_full_compare(lang, slang->sl_fname, false) == kEqualFiles) {
break;
+ }
+ }
} else {
filename = false;
if (len > 3 && lang[len - 3] == '_') {
@@ -2085,8 +2087,9 @@ char_u *did_set_spelllang(win_T *wp)
}
// Loop over the languages, there can be several files for "lang".
- for (slang = first_lang; slang != NULL; slang = slang->sl_next)
- if (filename ? path_full_compare(lang, slang->sl_fname, FALSE) == kEqualFiles
+ for (slang = first_lang; slang != NULL; slang = slang->sl_next) {
+ if (filename
+ ? path_full_compare(lang, slang->sl_fname, false) == kEqualFiles
: STRICMP(lang, slang->sl_name) == 0) {
region_mask = REGION_ALL;
if (!filename && region != NULL) {
@@ -2116,6 +2119,7 @@ char_u *did_set_spelllang(win_T *wp)
nobreak = true;
}
}
+ }
}
// round 0: load int_wordlist, if possible.
@@ -2137,17 +2141,21 @@ char_u *did_set_spelllang(win_T *wp)
// If it was already found above then skip it.
for (c = 0; c < ga.ga_len; ++c) {
p = LANGP_ENTRY(ga, c)->lp_slang->sl_fname;
- if (p != NULL && path_full_compare(spf_name, p, FALSE) == kEqualFiles)
+ if (p != NULL
+ && path_full_compare(spf_name, p, false) == kEqualFiles) {
break;
+ }
}
if (c < ga.ga_len)
continue;
}
// Check if it was loaded already.
- for (slang = first_lang; slang != NULL; slang = slang->sl_next)
- if (path_full_compare(spf_name, slang->sl_fname, FALSE) == kEqualFiles)
+ for (slang = first_lang; slang != NULL; slang = slang->sl_next) {
+ if (path_full_compare(spf_name, slang->sl_fname, false) == kEqualFiles) {
break;
+ }
+ }
if (slang == NULL) {
// Not loaded, try loading it now. The language name includes the
// region name, the region is ignored otherwise. for int_wordlist
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index 69fa95107e..6578c7d66c 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -1786,7 +1786,7 @@ spell_reload_one (
bool didit = false;
for (slang = first_lang; slang != NULL; slang = slang->sl_next) {
- if (path_full_compare(fname, slang->sl_fname, FALSE) == kEqualFiles) {
+ if (path_full_compare(fname, slang->sl_fname, false) == kEqualFiles) {
slang_clear(slang);
if (spell_load_file(fname, NULL, slang, false) == NULL)
// reloading failed, clear the language
@@ -4714,9 +4714,11 @@ static void spell_make_sugfile(spellinfo_T *spin, char_u *wfname)
// pointer-linked version of the trie. And it avoids having two versions
// of the code for the soundfolding stuff.
// It might have been done already by spell_reload_one().
- for (slang = first_lang; slang != NULL; slang = slang->sl_next)
- if (path_full_compare(wfname, slang->sl_fname, FALSE) == kEqualFiles)
+ for (slang = first_lang; slang != NULL; slang = slang->sl_next) {
+ if (path_full_compare(wfname, slang->sl_fname, false) == kEqualFiles) {
break;
+ }
+ }
if (slang == NULL) {
spell_message(spin, (char_u *)_("Reading back spell file..."));
slang = spell_load_file(wfname, NULL, NULL, false);
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index beff89d191..c09a13edb1 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -2289,7 +2289,7 @@ static char_u *tag_full_fname(tagptrs_T *tagp)
{
int c = *tagp->fname_end;
*tagp->fname_end = NUL;
- char_u *fullname = expand_tag_fname(tagp->fname, tagp->tag_fname, FALSE);
+ char_u *fullname = expand_tag_fname(tagp->fname, tagp->tag_fname, false);
*tagp->fname_end = c;
return fullname;
@@ -2363,8 +2363,8 @@ jumpto_tag (
* Expand file name, when needed (for environment variables).
* If 'tagrelative' option set, may change file name.
*/
- fname = expand_tag_fname(fname, tagp.tag_fname, TRUE);
- tofree_fname = fname; /* free() it later */
+ fname = expand_tag_fname(fname, tagp.tag_fname, true);
+ tofree_fname = fname; // free() it later
/*
* Check if the file with the tag exists before abandoning the current
@@ -2615,13 +2615,12 @@ erret:
return retval;
}
-/*
- * If "expand" is TRUE, expand wildcards in fname.
- * If 'tagrelative' option set, change fname (name of file containing tag)
- * according to tag_fname (name of tag file containing fname).
- * Returns a pointer to allocated memory.
- */
-static char_u *expand_tag_fname(char_u *fname, char_u *tag_fname, int expand)
+// If "expand" is true, expand wildcards in fname.
+// If 'tagrelative' option set, change fname (name of file containing tag)
+// according to tag_fname (name of tag file containing fname).
+// Returns a pointer to allocated memory.
+static char_u *expand_tag_fname(char_u *fname, char_u *const tag_fname,
+ const bool expand)
{
char_u *p;
char_u *expanded_fname = NULL;
@@ -2676,8 +2675,8 @@ static int test_for_current(char_u *fname, char_u *fname_end, char_u *tag_fname,
c = *fname_end;
*fname_end = NUL;
}
- fullname = expand_tag_fname(fname, tag_fname, TRUE);
- retval = (path_full_compare(fullname, buf_ffname, TRUE) & kEqualFiles);
+ fullname = expand_tag_fname(fname, tag_fname, true);
+ retval = (path_full_compare(fullname, buf_ffname, true) & kEqualFiles);
xfree(fullname);
*fname_end = c;
}
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index 87d7ff5bad..0379235ec0 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -116,6 +116,7 @@ NEW_TESTS ?= \
test_visual.res \
test_winbuf_close.res \
test_window_id.res \
+ test_windows_home.res \
test_wordcount.res \
test_writefile.res \
test_alot_latin.res \
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index f2cd84f556..8014b76af7 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -173,6 +173,9 @@ func FinishTesting()
" Don't write viminfo on exit.
set viminfo=
+ " Clean up files created by setup.vim
+ call delete('XfakeHOME', 'rf')
+
if s:fail == 0
" Success, create the .res file so that make knows it's done.
exe 'split ' . fnamemodify(g:testname, ':r') . '.res'
diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim
index aac9fefef4..f3cc7da70f 100644
--- a/src/nvim/testdir/setup.vim
+++ b/src/nvim/testdir/setup.vim
@@ -1,5 +1,11 @@
" Common preparations for running tests.
+" Only load this once.
+if exists('s:did_load')
+ finish
+endif
+let s:did_load = 1
+
" Align Nvim defaults to Vim.
set sidescroll=0
set directory^=.
@@ -16,7 +22,10 @@ set rtp=$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after
let &packpath = &rtp
" Make sure $HOME does not get read or written.
-let $HOME = '/does/not/exist'
+let $HOME = expand(getcwd() . '/XfakeHOME')
+if !isdirectory($HOME)
+ call mkdir($HOME)
+endif
" Use default shell on Windows to avoid segfault, caused by TUI
if has('win32')
diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim
index 1a70ac152f..d026221dac 100644
--- a/src/nvim/testdir/test_alot.vim
+++ b/src/nvim/testdir/test_alot.vim
@@ -2,6 +2,7 @@
" This makes testing go faster, since Vim doesn't need to restart.
source test_assign.vim
+source test_cd.vim
source test_changedtick.vim
source test_cursor_func.vim
source test_ex_undo.vim
diff --git a/src/nvim/testdir/test_cd.vim b/src/nvim/testdir/test_cd.vim
new file mode 100644
index 0000000000..e573419bd0
--- /dev/null
+++ b/src/nvim/testdir/test_cd.vim
@@ -0,0 +1,13 @@
+" Test for :cd
+
+func Test_cd_large_path()
+ " This used to crash with a heap write overflow.
+ call assert_fails('cd ' . repeat('x', 5000), 'E472:')
+endfunc
+
+func Test_cd_up_and_down()
+ let path = getcwd()
+ cd ..
+ exe 'cd ' . path
+ call assert_equal(path, getcwd())
+endfunc
diff --git a/src/nvim/testdir/test_windows_home.vim b/src/nvim/testdir/test_windows_home.vim
new file mode 100644
index 0000000000..bbcbf96050
--- /dev/null
+++ b/src/nvim/testdir/test_windows_home.vim
@@ -0,0 +1,121 @@
+" Test for $HOME on Windows.
+
+if !has('win32')
+ finish
+endif
+
+let s:env = {}
+
+func s:restore_env()
+ for i in keys(s:env)
+ exe 'let ' . i . '=s:env["' . i . '"]'
+ endfor
+endfunc
+
+func s:save_env(...)
+ for i in a:000
+ exe 'let s:env["' . i . '"]=' . i
+ endfor
+endfunc
+
+func s:unlet_env(...)
+ for i in a:000
+ exe 'let ' . i . '=""'
+ endfor
+endfunc
+
+func CheckHomeIsMissingFromSubprocessEnvironment()
+ silent! let out = system('set')
+ let env = filter(split(out, "\n"), 'v:val=~"^HOME="')
+ call assert_equal(0, len(env))
+endfunc
+
+func CheckHomeIsInSubprocessEnvironment(exp)
+ silent! let out = system('set')
+ let env = filter(split(out, "\n"), 'v:val=~"^HOME="')
+ let home = len(env) == 0 ? "" : substitute(env[0], '[^=]\+=', '', '')
+ call assert_equal(a:exp, home)
+endfunc
+
+func CheckHome(exp, ...)
+ call assert_equal(a:exp, $HOME)
+ call assert_equal(a:exp, expand('~', ':p'))
+ if !a:0
+ call CheckHomeIsMissingFromSubprocessEnvironment()
+ else
+ call CheckHomeIsInSubprocessEnvironment(a:1)
+ endif
+endfunc
+
+func Test_WindowsHome()
+ command! -nargs=* SaveEnv call <SID>save_env(<f-args>)
+ command! -nargs=* RestoreEnv call <SID>restore_env()
+ command! -nargs=* UnletEnv call <SID>unlet_env(<f-args>)
+ set noshellslash
+
+ let save_home = $HOME
+ SaveEnv $USERPROFILE $HOMEDRIVE $HOMEPATH
+ try
+ " Normal behavior: use $HOMEDRIVE and $HOMEPATH, ignore $USERPROFILE
+ let $USERPROFILE = 'unused'
+ let $HOMEDRIVE = 'C:'
+ let $HOMEPATH = '\foobar'
+ let $HOME = '' " Force recomputing "homedir"
+ call CheckHome('C:\foobar')
+
+ " Same, but with $HOMEPATH not set
+ UnletEnv $HOMEPATH
+ let $HOME = '' " Force recomputing "homedir"
+ call CheckHome('C:\')
+
+ " Use $USERPROFILE if $HOMEPATH and $HOMEDRIVE are empty
+ UnletEnv $HOMEDRIVE $HOMEPATH
+ let $USERPROFILE = 'C:\foo'
+ let $HOME = '' " Force recomputing "homedir"
+ call CheckHome('C:\foo')
+
+ " If $HOME is set the others don't matter
+ let $HOME = 'C:\bar'
+ let $USERPROFILE = 'unused'
+ let $HOMEDRIVE = 'unused'
+ let $HOMEPATH = 'unused'
+ call CheckHome('C:\bar', 'C:\bar')
+
+ " If $HOME contains %USERPROFILE% it is expanded
+ let $USERPROFILE = 'C:\foo'
+ let $HOME = '%USERPROFILE%\bar'
+ let $HOMEDRIVE = 'unused'
+ let $HOMEPATH = 'unused'
+ " call CheckHome('C:\foo\bar', '%USERPROFILE%\bar')
+
+ " Invalid $HOME is kept
+ let $USERPROFILE = 'C:\foo'
+ let $HOME = '%USERPROFILE'
+ let $HOMEDRIVE = 'unused'
+ let $HOMEPATH = 'unused'
+ call CheckHome('%USERPROFILE', '%USERPROFILE')
+
+ " %USERPROFILE% not at start of $HOME is not expanded
+ let $USERPROFILE = 'unused'
+ let $HOME = 'C:\%USERPROFILE%'
+ let $HOMEDRIVE = 'unused'
+ let $HOMEPATH = 'unused'
+ call CheckHome('C:\%USERPROFILE%', 'C:\%USERPROFILE%')
+
+ if has('channel')
+ RestoreEnv
+ let $HOME = save_home
+ let env = ''
+ let job = job_start('cmd /c set', {'out_cb': {ch,x->[env,execute('let env=x')]}})
+ sleep 1
+ let env = filter(split(env, "\n"), 'v:val=="HOME"')
+ let home = len(env) == 0 ? "" : env[0]
+ call assert_equal('', home)
+ endif
+ finally
+ RestoreEnv
+ delcommand SaveEnv
+ delcommand RestoreEnv
+ delcommand UnletEnv
+ endtry
+endfunc