From 1dd700a8d9275439fbc71ac5adeb59914bdbd5cf Mon Sep 17 00:00:00 2001 From: Leonardo Mello Date: Mon, 18 Sep 2023 16:50:47 -0300 Subject: [PATCH 1/3] fix: gf fails on "foo/bar.txt:1:2" on Windows Problem: On Windows, "gf" fails on a filepath that has a line:column suffix. Example: E447: Can't find file "src/app/core/services/identity/identity.service.ts:64:23" Solution: - Remove ":" from 'isfname' on Windows. Colon is not a valid filename character (except for the drive-letter). - Handle drive letters specially in file_name_in_line(). Fixes #25160 --- runtime/doc/news.txt | 3 ++ runtime/doc/options.txt | 2 +- runtime/doc/vim_diff.txt | 2 + src/nvim/options.lua | 4 +- src/nvim/window.c | 10 ++-- test/functional/core/path_spec.lua | 76 ++++++++++++++++++++++++++++++ test/old/testdir/setup.vim | 3 ++ 7 files changed, 92 insertions(+), 8 deletions(-) diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index f0d01f92e7..2bf912dbfd 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -15,6 +15,9 @@ BREAKING CHANGES *news-breaking* The following changes may require adaptations in user config or plugins. +• Windows file path drive letters are now detected even if ":" is not in + 'isfname'. The default 'isfname' no longer includes ":". + • |vim.tbl_islist()| now checks whether a table is actually list-like (i.e., has integer keys without gaps and starting from 1). For the previous behavior (only check for integer keys, allow gaps or not starting with 1), diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index bc207d7755..283c1e3612 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -3453,7 +3453,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'isfname'* *'isf'* 'isfname' 'isf' string (default for Windows: - "@,48-57,/,\,.,-,_,+,,,#,$,%,{,},[,],:,@-@,!,~,=" + "@,48-57,/,\,.,-,_,+,,,#,$,%,{,},[,],@-@,!,~,=" otherwise: "@,48-57,/,.,-,_,+,,,#,$,%,~,=") global The characters specified by this option are included in file names and diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index e9c04443a6..974aa051cf 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -53,6 +53,8 @@ Defaults *nvim-defaults* - 'hlsearch' is enabled - 'include' defaults to "". The C ftplugin sets it to "^\\s*#\\s*include" - 'incsearch' is enabled +- 'isfname' does not include ":" on Windows. Include ":" in 'isfname' to treat + it as part of a filename anywhere in the name (not only the drive letter). - 'joinspaces' is disabled - 'langnoremap' is enabled - 'langremap' is disabled diff --git a/src/nvim/options.lua b/src/nvim/options.lua index a0b9306908..e70fe8614f 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -4298,9 +4298,9 @@ return { defaults = { condition = 'BACKSLASH_IN_FILENAME', if_false = '@,48-57,/,.,-,_,+,,,#,$,%,~,=', - if_true = '@,48-57,/,\\,.,-,_,+,,,#,$,%,{,},[,],:,@-@,!,~,=', + if_true = '@,48-57,/,\\,.,-,_,+,,,#,$,%,{,},[,],@-@,!,~,=', doc = [[for Windows: - "@,48-57,/,\,.,-,_,+,,,#,$,%,{,},[,],:,@-@,!,~,=" + "@,48-57,/,\,.,-,_,+,,,#,$,%,{,},[,],@-@,!,~,=" otherwise: "@,48-57,/,.,-,_,+,,,#,$,%,~,="]], }, deny_duplicates = true, diff --git a/src/nvim/window.c b/src/nvim/window.c index 41199306fa..ef6d3fd4a9 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -6943,11 +6943,12 @@ char *file_name_in_line(char *line, int col, int options, int count, char *rel_f bool is_url = false; // Search backward for first char of the file name. - // Go one char back to ":" before "//" even when ':' is not in 'isfname'. + // Go one char back to ":" before "//", or to the drive letter before ":\" (even if ":" + // is not in 'isfname'). while (ptr > line) { if ((len = (size_t)(utf_head_off(line, ptr - 1))) > 0) { ptr -= len + 1; - } else if (vim_isfilec((uint8_t)ptr[-1]) + } else if (vim_isfilec((uint8_t)ptr[-1]) || path_has_drive_letter(ptr - 2) || ((options & FNAME_HYP) && path_is_url(ptr - 1))) { ptr--; } else { @@ -6957,14 +6958,13 @@ char *file_name_in_line(char *line, int col, int options, int count, char *rel_f // Search forward for the last char of the file name. // Also allow ":/" when ':' is not in 'isfname'. - len = 0; + len = path_has_drive_letter(ptr) ? 2 : 0; while (vim_isfilec((uint8_t)ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ') || ((options & FNAME_HYP) && path_is_url(ptr + len)) || (is_url && vim_strchr(":?&=", (uint8_t)ptr[len]) != NULL)) { // After type:// we also include :, ?, & and = as valid characters, so that // http://google.com:8080?q=this&that=ok works. - if ((ptr[len] >= 'A' && ptr[len] <= 'Z') - || (ptr[len] >= 'a' && ptr[len] <= 'z')) { + if ((ptr[len] >= 'A' && ptr[len] <= 'Z') || (ptr[len] >= 'a' && ptr[len] <= 'z')) { if (in_type && path_is_url(ptr + len + 1)) { is_url = true; } diff --git a/test/functional/core/path_spec.lua b/test/functional/core/path_spec.lua index 6c677d76d1..1c6faf8d60 100644 --- a/test/functional/core/path_spec.lua +++ b/test/functional/core/path_spec.lua @@ -91,4 +91,80 @@ describe('file search', function() feed('gf') eq('filename_with_unicode_ααα', eval('expand("%:t")')) end) + + it('matches Windows drive-letter filepaths (without ":" in &isfname)', function() + local os_win = is_os('win') + + insert([[c:/d:/foo/bar.txt]]) + eq([[c:/d:/foo/bar.txt]], eval('expand("")')) + command('%delete') + + insert([[//share/c:/foo/bar/]]) + eq([[//share/c:/foo/bar/]], eval('expand("")')) + command('%delete') + + insert([[file://c:/foo/bar]]) + eq([[file://c:/foo/bar]], eval('expand("")')) + command('%delete') + + insert([[https://c:/foo/bar]]) + eq([[https://c:/foo/bar]], eval('expand("")')) + command('%delete') + + insert([[\foo\bar]]) + eq(os_win and [[\foo\bar]] or [[bar]], eval('expand("")')) + command('%delete') + + insert([[/foo/bar]]) + eq([[/foo/bar]], eval('expand("")')) + command('%delete') + + insert([[c:\foo\bar]]) + eq(os_win and [[c:\foo\bar]] or [[bar]], eval('expand("")')) + command('%delete') + + insert([[c:/foo/bar]]) + eq([[c:/foo/bar]], eval('expand("")')) + command('%delete') + + insert([[c:foo\bar]]) + eq(os_win and [[foo\bar]] or [[bar]], eval('expand("")')) + command('%delete') + + insert([[c:foo/bar]]) + eq([[foo/bar]], eval('expand("")')) + command('%delete') + + insert([[c:foo]]) + eq([[foo]], eval('expand("")')) + command('%delete') + + -- Examples from: https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats#example-ways-to-refer-to-the-same-file + insert([[c:\temp\test-file.txt]]) + eq(os_win and [[c:\temp\test-file.txt]] or [[test-file.txt]], eval('expand("")')) + command('%delete') + + insert([[\\127.0.0.1\c$\temp\test-file.txt]]) + eq(os_win and [[\\127.0.0.1\c$\temp\test-file.txt]] or [[test-file.txt]], eval('expand("")')) + command('%delete') + + insert([[\\LOCALHOST\c$\temp\test-file.txt]]) + eq(os_win and [[\\LOCALHOST\c$\temp\test-file.txt]] or [[test-file.txt]], eval('expand("")')) + command('%delete') + + insert([[\\.\c:\temp\test-file.txt]]) -- not supported yet + eq(os_win and [[\\.\c]] or [[test-file.txt]], eval('expand("")')) + command('%delete') + + insert([[\\?\c:\temp\test-file.txt]]) -- not supported yet + eq(os_win and [[\c]] or [[test-file.txt]], eval('expand("")')) + command('%delete') + + insert([[\\.\UNC\LOCALHOST\c$\temp\test-file.txt]]) + eq(os_win and [[\\.\UNC\LOCALHOST\c$\temp\test-file.txt]] or [[test-file.txt]], eval('expand("")')) + command('%delete') + + insert([[\\127.0.0.1\c$\temp\test-file.txt]]) + eq(os_win and [[\\127.0.0.1\c$\temp\test-file.txt]] or [[test-file.txt]], eval('expand("")')) + end) end) diff --git a/test/old/testdir/setup.vim b/test/old/testdir/setup.vim index 096ddba40b..b0c2a15a3f 100644 --- a/test/old/testdir/setup.vim +++ b/test/old/testdir/setup.vim @@ -26,6 +26,9 @@ if exists('s:did_load') set sessionoptions+=options set viewoptions+=options set switchbuf= + if has('win32') + set isfname+=: + endif if g:testname !~ 'test_mapping.vim$' " Make "Q" switch to Ex mode. " This does not work for all tests. From 56dc8b9212ef3a1eebe8e32266d4b6735b3c557b Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 6 Oct 2023 13:43:17 +0200 Subject: [PATCH 2/3] fix: heap-buffer-overflow in file_name_in_line heap-buffer-overflow on address 0x6020000079cf at pc 0x563e98bd70b6 bp 0x7ffc52efc430 sp 0x7ffc52efbbf8 = READ of size 1 at 0x6020000079cf thread T0 = 0 0x563e98bd70b5 in strlen (/home/runner/work/neovim/neovim/build/bin/nvim+0xc7b0b5) (BuildId: e40ec79ca4eae53c0155aeb03de88ddd5d29c5d2) = 1 0x563e99da4f45 in path_has_drive_letter /home/runner/work/neovim/neovim/src/nvim/path.c:1754:10 = 2 0x563e9a610d81 in file_name_in_line /home/runner/work/neovim/neovim/src/nvim/window.c:6951:49 = 3 0x563e9a61066f in file_name_at_cursor /home/runner/work/neovim/neovim/src/nvim/window.c:6917:10 = 4 0x563e995a08d6 in eval_vars /home/runner/work/neovim/neovim/src/nvim/ex_docmd.c:6903:16 = 5 0x563e9937c648 in f_expand /home/runner/work/neovim/neovim/src/nvim/eval/funcs.c:1754:20 = 6 0x563e99357819 in call_internal_func /home/runner/work/neovim/neovim/src/nvim/eval/funcs.c:273:3 = 7 0x563e994656de in call_func /home/runner/work/neovim/neovim/src/nvim/eval/userfunc.c:1719:15 = 8 0x563e99461ae6 in get_func_tv /home/runner/work/neovim/neovim/src/nvim/eval/userfunc.c:557:11 = 9 0x563e992c7c5d in eval_func /home/runner/work/neovim/neovim/src/nvim/eval.c:2281:13 = 10 0x563e992bf708 in eval7 /home/runner/work/neovim/neovim/src/nvim/eval.c:3208:15 = 11 0x563e992bbda9 in eval6 /home/runner/work/neovim/neovim/src/nvim/eval.c:2935:7 = 12 0x563e992b9e8d in eval5 /home/runner/work/neovim/neovim/src/nvim/eval.c:2791:7 = 13 0x563e992b87e2 in eval4 /home/runner/work/neovim/neovim/src/nvim/eval.c:2666:7 = 14 0x563e992b758d in eval3 /home/runner/work/neovim/neovim/src/nvim/eval.c:2575:7 = 15 0x563e9926299d in eval2 /home/runner/work/neovim/neovim/src/nvim/eval.c:2497:7 = 16 0x563e99250b0c in eval1 /home/runner/work/neovim/neovim/src/nvim/eval.c:2401:7 = 17 0x563e9924d68a in eval0 /home/runner/work/neovim/neovim/src/nvim/eval.c:2346:9 = 18 0x563e98f17315 in nvim_eval /home/runner/work/neovim/neovim/src/nvim/api/vimscript.c:170:3 = 19 0x563e98e7bb5e in handle_nvim_eval /home/runner/work/neovim/neovim/build/src/nvim/auto/api/private/dispatch_wrappers.generated.h:8953:15 = 20 0x563e99b62f59 in request_event /home/runner/work/neovim/neovim/src/nvim/msgpack_rpc/channel.c:444:19 = 21 0x563e9a1dcdc9 in state_handle_k_event /home/runner/work/neovim/neovim/src/nvim/state.c:115:7 = 22 0x563e99bf0718 in nv_event /home/runner/work/neovim/neovim/src/nvim/normal.c:6623:3 = 23 0x563e99bb4069 in normal_execute /home/runner/work/neovim/neovim/src/nvim/normal.c:1227:3 = 24 0x563e9a1dcb8d in state_enter /home/runner/work/neovim/neovim/src/nvim/state.c:97:26 = 25 0x563e99b9673c in normal_enter /home/runner/work/neovim/neovim/src/nvim/normal.c:516:3 = 26 0x563e98c82e0d in main /home/runner/work/neovim/neovim/src/nvim/main.c:643:3 = 27 0x7fcb81429d8f (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: 229b7dc509053fe4df5e29e8629911f0c3bc66dd) = 28 0x7fcb81429e3f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e3f) (BuildId: 229b7dc509053fe4df5e29e8629911f0c3bc66dd) = 29 0x563e98bc0f74 in _start (/home/runner/work/neovim/neovim/build/bin/nvim+0xc64f74) (BuildId: e40ec79ca4eae53c0155aeb03de88ddd5d29c5d2) = = 0x6020000079cf is located 1 bytes to the left of 9-byte region [0x6020000079d0,0x6020000079d9) = allocated by thread T0 here: = 0 0x563e98c43dbe in __interceptor_malloc (/home/runner/work/neovim/neovim/build/bin/nvim+0xce7dbe) (BuildId: e40ec79ca4eae53c0155aeb03de88ddd5d29c5d2) = 1 0x563e99a7b6f6 in try_malloc /home/runner/work/neovim/neovim/src/nvim/memory.c:89:15 = 2 0x563e99a7b8dc in xmalloc /home/runner/work/neovim/neovim/src/nvim/memory.c:123:15 = 3 0x563e9901ace2 in ins_str /home/runner/work/neovim/neovim/src/nvim/change.c:801:16 = 4 0x563e99208133 in insertchar /home/runner/work/neovim/neovim/src/nvim/edit.c:2172:5 = 5 0x563e9921936c in insert_special /home/runner/work/neovim/neovim/src/nvim/edit.c:1995:5 = 6 0x563e99218e9f in insert_handle_key /home/runner/work/neovim/neovim/src/nvim/edit.c:1173:7 = 7 0x563e991f0f1d in insert_execute /home/runner/work/neovim/neovim/src/nvim/edit.c:671:10 = 8 0x563e9a1dcb8d in state_enter /home/runner/work/neovim/neovim/src/nvim/state.c:97:26 = 9 0x563e991f8c75 in insert_enter /home/runner/work/neovim/neovim/src/nvim/edit.c:338:5 = 10 0x563e991ed4e2 in edit /home/runner/work/neovim/neovim/src/nvim/edit.c:1270:3 = 11 0x563e99bf6007 in invoke_edit /home/runner/work/neovim/neovim/src/nvim/normal.c:6269:7 = 12 0x563e99bcb665 in nv_edit /home/runner/work/neovim/neovim/src/nvim/normal.c:6246:5 = 13 0x563e99bb4069 in normal_execute /home/runner/work/neovim/neovim/src/nvim/normal.c:1227:3 = 14 0x563e9a1dcb8d in state_enter /home/runner/work/neovim/neovim/src/nvim/state.c:97:26 = 15 0x563e99b9673c in normal_enter /home/runner/work/neovim/neovim/src/nvim/normal.c:516:3 = 16 0x563e98c82e0d in main /home/runner/work/neovim/neovim/src/nvim/main.c:643:3 = 17 0x7fcb81429d8f (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: 229b7dc509053fe4df5e29e8629911f0c3bc66dd) --- src/nvim/window.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nvim/window.c b/src/nvim/window.c index ef6d3fd4a9..16bb7f5df7 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -6948,7 +6948,8 @@ char *file_name_in_line(char *line, int col, int options, int count, char *rel_f while (ptr > line) { if ((len = (size_t)(utf_head_off(line, ptr - 1))) > 0) { ptr -= len + 1; - } else if (vim_isfilec((uint8_t)ptr[-1]) || path_has_drive_letter(ptr - 2) + } else if (vim_isfilec((uint8_t)ptr[-1]) + || (len >= 2 && path_has_drive_letter(ptr - 2)) || ((options & FNAME_HYP) && path_is_url(ptr - 1))) { ptr--; } else { From 133e2990efa44cadf1410a452a08843dbd8ca86e Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Fri, 6 Oct 2023 12:59:39 +0200 Subject: [PATCH 3/3] refactor: cleanup --- runtime/doc/news.txt | 5 +- runtime/doc/vim_diff.txt | 4 +- test/functional/core/path_spec.lua | 106 +++++++++-------------------- 3 files changed, 38 insertions(+), 77 deletions(-) diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 2bf912dbfd..b0f3b76587 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -15,9 +15,6 @@ BREAKING CHANGES *news-breaking* The following changes may require adaptations in user config or plugins. -• Windows file path drive letters are now detected even if ":" is not in - 'isfname'. The default 'isfname' no longer includes ":". - • |vim.tbl_islist()| now checks whether a table is actually list-like (i.e., has integer keys without gaps and starting from 1). For the previous behavior (only check for integer keys, allow gaps or not starting with 1), @@ -206,6 +203,8 @@ The following changes to existing APIs or features add new behavior. option, which allows for rendering e.g., diagnostic severities differently. • Defaults: + • On Windows 'isfname' does not include ":". Drive letters are handled + correctly without it. (Use |gF| for filepaths suffixed with ":line:col"). • 'comments' includes "fb:•". • 'shortmess' includes the "C" flag. • Automatic linting of treesitter query files (see |ft-query-plugin|). diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 974aa051cf..4161d3b21e 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -53,8 +53,8 @@ Defaults *nvim-defaults* - 'hlsearch' is enabled - 'include' defaults to "". The C ftplugin sets it to "^\\s*#\\s*include" - 'incsearch' is enabled -- 'isfname' does not include ":" on Windows. Include ":" in 'isfname' to treat - it as part of a filename anywhere in the name (not only the drive letter). +- 'isfname' does not include ":" (on Windows). Drive letters are handled + correctly without it. (Use |gF| for filepaths suffixed with ":line:col"). - 'joinspaces' is disabled - 'langnoremap' is enabled - 'langremap' is disabled diff --git a/test/functional/core/path_spec.lua b/test/functional/core/path_spec.lua index 1c6faf8d60..06fa13dca5 100644 --- a/test/functional/core/path_spec.lua +++ b/test/functional/core/path_spec.lua @@ -81,7 +81,7 @@ describe('expand wildcard', function() end) end) -describe('file search', function() +describe('file search (gf, )', function() before_each(clear) it('find multibyte file name in line #20517', function() @@ -93,78 +93,40 @@ describe('file search', function() end) it('matches Windows drive-letter filepaths (without ":" in &isfname)', function() - local os_win = is_os('win') - - insert([[c:/d:/foo/bar.txt]]) - eq([[c:/d:/foo/bar.txt]], eval('expand("")')) - command('%delete') - - insert([[//share/c:/foo/bar/]]) - eq([[//share/c:/foo/bar/]], eval('expand("")')) - command('%delete') - - insert([[file://c:/foo/bar]]) - eq([[file://c:/foo/bar]], eval('expand("")')) - command('%delete') - - insert([[https://c:/foo/bar]]) - eq([[https://c:/foo/bar]], eval('expand("")')) - command('%delete') - - insert([[\foo\bar]]) - eq(os_win and [[\foo\bar]] or [[bar]], eval('expand("")')) - command('%delete') - - insert([[/foo/bar]]) - eq([[/foo/bar]], eval('expand("")')) - command('%delete') - - insert([[c:\foo\bar]]) - eq(os_win and [[c:\foo\bar]] or [[bar]], eval('expand("")')) - command('%delete') - - insert([[c:/foo/bar]]) - eq([[c:/foo/bar]], eval('expand("")')) - command('%delete') - - insert([[c:foo\bar]]) - eq(os_win and [[foo\bar]] or [[bar]], eval('expand("")')) - command('%delete') - - insert([[c:foo/bar]]) - eq([[foo/bar]], eval('expand("")')) - command('%delete') - - insert([[c:foo]]) - eq([[foo]], eval('expand("")')) - command('%delete') + local iswin = is_os('win') + local function test_cfile(input, expected, expected_win) + expected = (iswin and expected_win or expected) or input + command('%delete') + insert(input) + command('norm! 0') + eq(expected, eval('expand("")')) + end + test_cfile([[c:/d:/foo/bar.txt]]) -- TODO(justinmk): should return "d:/foo/bar.txt" ? + test_cfile([[//share/c:/foo/bar/]]) + test_cfile([[file://c:/foo/bar]]) + test_cfile([[file://c:/foo/bar:42]]) + test_cfile([[file://c:/foo/bar:42:666]]) + test_cfile([[https://c:/foo/bar]]) + test_cfile([[\foo\bar]], [[foo]], [[\foo\bar]]) + test_cfile([[/foo/bar]], [[/foo/bar]]) + test_cfile([[c:\foo\bar]], [[c:]], [[c:\foo\bar]]) + test_cfile([[c:\foo\bar:42:666]], [[c:]], [[c:\foo\bar]]) + test_cfile([[c:/foo/bar]]) + test_cfile([[c:/foo/bar:42]], [[c:/foo/bar]]) + test_cfile([[c:/foo/bar:42:666]], [[c:/foo/bar]]) + test_cfile([[c:foo\bar]], [[c]]) + test_cfile([[c:foo/bar]], [[c]]) + test_cfile([[c:foo]], [[c]]) -- Examples from: https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats#example-ways-to-refer-to-the-same-file - insert([[c:\temp\test-file.txt]]) - eq(os_win and [[c:\temp\test-file.txt]] or [[test-file.txt]], eval('expand("")')) - command('%delete') - - insert([[\\127.0.0.1\c$\temp\test-file.txt]]) - eq(os_win and [[\\127.0.0.1\c$\temp\test-file.txt]] or [[test-file.txt]], eval('expand("")')) - command('%delete') - - insert([[\\LOCALHOST\c$\temp\test-file.txt]]) - eq(os_win and [[\\LOCALHOST\c$\temp\test-file.txt]] or [[test-file.txt]], eval('expand("")')) - command('%delete') - - insert([[\\.\c:\temp\test-file.txt]]) -- not supported yet - eq(os_win and [[\\.\c]] or [[test-file.txt]], eval('expand("")')) - command('%delete') - - insert([[\\?\c:\temp\test-file.txt]]) -- not supported yet - eq(os_win and [[\c]] or [[test-file.txt]], eval('expand("")')) - command('%delete') - - insert([[\\.\UNC\LOCALHOST\c$\temp\test-file.txt]]) - eq(os_win and [[\\.\UNC\LOCALHOST\c$\temp\test-file.txt]] or [[test-file.txt]], eval('expand("")')) - command('%delete') - - insert([[\\127.0.0.1\c$\temp\test-file.txt]]) - eq(os_win and [[\\127.0.0.1\c$\temp\test-file.txt]] or [[test-file.txt]], eval('expand("")')) + test_cfile([[c:\temp\test-file.txt]], [[c:]], [[c:\temp\test-file.txt]]) + test_cfile([[\\127.0.0.1\c$\temp\test-file.txt]], [[127.0.0.1]], [[\\127.0.0.1\c$\temp\test-file.txt]]) + test_cfile([[\\LOCALHOST\c$\temp\test-file.txt]], [[LOCALHOST]], [[\\LOCALHOST\c$\temp\test-file.txt]]) + -- not supported yet + test_cfile([[\\.\c:\temp\test-file.txt]], [[.]], [[\\.\c]]) + -- not supported yet + test_cfile([[\\?\c:\temp\test-file.txt]], [[c:]], [[\\]]) + test_cfile([[\\.\UNC\LOCALHOST\c$\temp\test-file.txt]], [[.]], [[\\.\UNC\LOCALHOST\c$\temp\test-file.txt]]) + test_cfile([[\\127.0.0.1\c$\temp\test-file.txt]], [[127.0.0.1]], [[\\127.0.0.1\c$\temp\test-file.txt]]) end) end)