patch 9.1.0859: several problems with the GLVS plugin

Problem:  several problems with the GLVS plugin
Solution: fix issues, add regression tests, require at least Vim 9.1
          (GuyBrush)

closes: #16036

Signed-off-by: GuyBrush <miguel.barro@live.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/runtime/autoload/getscript.vim b/runtime/autoload/getscript.vim
index dced99c..3906050 100644
--- a/runtime/autoload/getscript.vim
+++ b/runtime/autoload/getscript.vim
@@ -11,6 +11,7 @@
 "   2024 Sep 23 by Vim Project: runtime dir selection fix (#15722)
 "                               autoloading search path fix
 "                               substitution of hardcoded commands with global variables
+"   2024 Nov 12 by Vim Project: fix problems on Windows (#16036)
 "  }}}
 "
 " GetLatestVimScripts: 642 1 :AutoInstall: getscript.vim
@@ -27,9 +28,9 @@
  echoerr "GetLatestVimScripts is not vi-compatible; not loaded (you need to set nocp)"
  finish
 endif
-if v:version < 702
+if v:version < 901
  echohl WarningMsg
- echo "***warning*** this version of GetLatestVimScripts needs vim 7.2"
+ echo "***warning*** this version of GetLatestVimScripts needs vim 9.1"
  echohl Normal
  finish
 endif
@@ -57,6 +58,9 @@
 if !exists("g:GetLatestVimScripts_wget")
  if executable("wget")
   let g:GetLatestVimScripts_wget= "wget"
+ elseif executable("curl.exe")
+  " enforce extension: windows powershell desktop version has a curl alias that hides curl.exe
+  let g:GetLatestVimScripts_wget= "curl.exe"
  elseif executable("curl")
   let g:GetLatestVimScripts_wget= "curl"
  else
@@ -69,7 +73,7 @@
 if !exists("g:GetLatestVimScripts_options")
  if g:GetLatestVimScripts_wget == "wget"
   let g:GetLatestVimScripts_options= "-q -O"
- elseif g:GetLatestVimScripts_wget == "curl"
+ elseif g:GetLatestVimScripts_wget =~ "curl"
   let g:GetLatestVimScripts_options= "-s -o"
  else
   let g:GetLatestVimScripts_options= ""
@@ -121,11 +125,14 @@
  let s:dotvim= s:is_windows ? "vimfiles" : ".vim"
 
  if !exists("g:GetLatestVimScripts_mv")
-  if s:is_windows && &shell !~ '\cbash\|pwsh\|powershell'
+  if &shell =~? '\<pwsh\>\|\<powershell\>'
+   let g:GetLatestVimScripts_mv= "move -Force"
+  elseif s:is_windows && &shell =~? '\<cmd\>'
    " windows (but not cygwin/bash)
-    let g:GetLatestVimScripts_mv= "move"
+   let g:GetLatestVimScripts_mv= "move /Y"
   else
-   " unix
+   " unix or cygwin bash/zsh
+   " 'mv' overrides existing files without asking
     let g:GetLatestVimScripts_mv= "mv"
   endif
  endif
@@ -160,12 +167,6 @@
    return
   endif
 
-  " insure that fnameescape() is available
-  if !exists("*fnameescape")
-   echoerr "GetLatestVimScripts needs fnameescape() (provided by 7.1.299 or later)"
-   return
-  endif
-
   " Find the .../GetLatest subdirectory under the runtimepath
   for datadir in split(&rtp,',') + ['']
    if isdirectory(datadir."/GetLatest")
@@ -377,7 +378,16 @@
   let t_ti= &t_ti
   let t_te= &t_te
   let rs  = &rs
+  let ssl = &ssl
+
   set t_ti= t_te= nors
+  " avoid issues with shellescape() on Windows
+  if s:is_windows && &shell =~? '\<cmd\>'
+    set noshellslash
+  endif
+
+  " restore valures afterwards
+  defer execute("let @a = rega | let &t_ti = t_ti | let &t_te = t_te | let &rs = rs | let &ssl = ssl")
 
  " put current line on top-of-screen and interpret it into
  " a      script identifier  : used to obtain webpage
@@ -394,7 +404,6 @@
   else
    let curline  = getline(".")
    if curline =~ '^\s*#'
-    let @a= rega
 "    call Dret("GetOneScript : skipping a pure comment line")
     return
    endif
@@ -429,7 +438,6 @@
   " plugin author protection from downloading his/her own scripts atop their latest work
   if scriptid == 0 || srcid == 0
    " When looking for :AutoInstall: lines, skip scripts that have   0 0 scriptname
-   let @a= rega
 "   call Dret("GetOneScript : skipping a scriptid==srcid==0 line")
    return
   endif
@@ -497,7 +505,6 @@
 "   call Decho("***warning*** couldn'".'t find "Click on the package..." in description page for <'.aicmmnt.">")
    echomsg "***warning*** couldn'".'t find "Click on the package..." in description page for <'.aicmmnt.">"
 "   call Dret("GetOneScript : srch for /Click on the package/ failed")
-   let @a= rega
    return
   endif
 "  call Decho('found "Click on the package to download"')
@@ -513,7 +520,6 @@
    let s:downerrors = s:downerrors + 1
 "   call Decho("***warning*** couldn'".'t find "src_id=" in description page for <'.aicmmnt.">")
    echomsg "***warning*** couldn'".'t find "src_id=" in description page for <'.aicmmnt.">"
-   let @a= rega
 "  call Dret("GetOneScript : srch for /src_id/ failed")
    return
   endif
@@ -547,11 +553,11 @@
 "   call Decho(".downloading new <".sname.">")
    echomsg ".downloading new <".sname.">"
    if has("win32") || has("win16") || has("win95")
-"    call Decho(".new|exe silent r!".g:GetLatestVimScripts_wget." ".g:GetLatestVimScripts_options." ".shellescape(sname)." ".shellescape(g:GetLatestVimScripts_downloadaddr.latestsrcid)."|q")
-    new|exe "silent r!".g:GetLatestVimScripts_wget." ".g:GetLatestVimScripts_options." ".shellescape(sname)." ".shellescape(g:GetLatestVimScripts_downloadaddr.latestsrcid)|q
+"    call Decho(".new|exe silent r!".g:GetLatestVimScripts_wget." ".g:GetLatestVimScripts_options." ".shellescape(sname)." ".shellescape(g:GetLatestVimScripts_downloadaddr.latestsrcid)."|bw!")
+    new|exe "silent r!".g:GetLatestVimScripts_wget." ".g:GetLatestVimScripts_options." ".shellescape(sname)." ".shellescape(g:GetLatestVimScripts_downloadaddr.latestsrcid)|bw!
    else
-"    call Decho(".exe silent !".g:GetLatestVimScripts_wget." ".g:GetLatestVimScripts_options." ".shellescape(sname)." ".shellescape(g:GetLatestVimScripts_downloadaddr).latestsrcid
-    exe "silent !".g:GetLatestVimScripts_wget." ".g:GetLatestVimScripts_options." ".shellescape(sname)." ".shellescape(g:GetLatestVimScripts_downloadaddr).latestsrcid
+"    call Decho(".exe silent !".g:GetLatestVimScripts_wget." ".g:GetLatestVimScripts_options." ".shellescape(sname)." ".shellescape(g:GetLatestVimScripts_downloadaddr.latestsrcid)
+    exe "silent !".g:GetLatestVimScripts_wget." ".g:GetLatestVimScripts_options." ".shellescape(sname)." ".shellescape(g:GetLatestVimScripts_downloadaddr.latestsrcid)
    endif
 
    " --------------------------------------------------------------------------
@@ -654,8 +660,8 @@
       exe "silent !".g:GetLatestVimScripts_mv." ".shellescape(sname)." ".installdir
      endif
      if tgtdir != "plugin"
-"      call Decho("exe silent !".g:GetLatestVimScripts_mv." plugin/".shellescape(pname)." ".tgtdir)
-      exe "silent !".g:GetLatestVimScripts_mv." plugin/".shellescape(pname)." ".tgtdir
+"      call Decho("exe silent !".g:GetLatestVimScripts_mv." ".shellescape("plugin/".pname)." ".tgtdir)
+      exe "silent !".g:GetLatestVimScripts_mv." ".shellescape("plugin/".pname)." ".tgtdir
      endif
      
      " helptags step
@@ -680,13 +686,7 @@
 "   call Decho("[latestsrcid=".latestsrcid."] <= [srcid=".srcid."], no need to update")
   endif
 
- " restore options
-  let &t_ti = t_ti
-  let &t_te = t_te
-  let &rs   = rs
-  let @a    = rega
 "  call Dredir("BUFFER TEST (GetOneScript)","ls!")
-
 "  call Dret("GetOneScript")
 endfun
 
diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak
index 6a7f4d3..bdf058c 100644
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -173,6 +173,7 @@
 	test_gf \
 	test_glob2regpat \
 	test_global \
+	test_glvs \
 	test_gn \
 	test_goto \
 	test_gui \
@@ -437,6 +438,7 @@
 	test_gettext_make.res \
 	test_getvar.res \
 	test_gf.res \
+	test_glvs.res \
 	test_gn.res \
 	test_goto.res \
 	test_gui.res \
diff --git a/src/testdir/test_glvs.vim b/src/testdir/test_glvs.vim
new file mode 100644
index 0000000..765f8b5
--- /dev/null
+++ b/src/testdir/test_glvs.vim
@@ -0,0 +1,356 @@
+" Tests for GetLatestVimScripts plugin
+
+" vim feature
+set nocp
+set cpo&vim
+
+" constants
+const s:dotvim= has("win32") ? "vimfiles" : ".vim"
+const s:scriptdir = $"{$HOME}/{s:dotvim}/GetLatest"
+const s:vimdir = expand("<script>:h:h:h")
+const s:packages = {
+    \ 'vmb': {
+        \ 'spec': '4979 1 :AutoInstall: AnsiEsc.vim',
+        \ 'files': ['plugin/AnsiEscPlugin.vim', 'autoload/AnsiEsc.vim']
+        \ },
+    \ 'vim.bz2': {
+        \ 'spec': '514 1 :AutoInstall: mrswin.vim',
+        \ 'files': ['plugin/mrswin.vim']
+        \ },
+    \ 'vba.gz': {
+        \ 'spec': '120 1 :AutoInstall: Decho.vim',
+        \ 'package': 'GetLatest/Installed/Decho.vba',
+        \ 'files': ['plugin/Decho.vim', 'syntax/Decho.vim']
+        \ },
+    \ 'tar.xz': {
+        \ 'spec': '5632 1 :AutoInstall: dumpx',
+        \ 'package': 'GetLatest/Installed/dumpx.tar',
+        \ 'files': ['dumpx/plugin/dumpx.vim', 'dumpx/doc/dumpx.txt']
+        \ }
+    \ }
+
+" Before each test recreate the .vim dir structure expected by GLVS and load the plugin
+func SetUp()
+
+    " add the required GetLatest dir (note $HOME is a dummy)
+    call mkdir(s:scriptdir, "p")
+    let &runtimepath = $"{$HOME}/{s:dotvim},{s:vimdir}/runtime"
+
+    " add plugin dir
+    call mkdir($"{$HOME}/{s:dotvim}/plugin")
+
+    " doc file is required for the packages which use :helptags
+    let docdir = $"{$HOME}/{s:dotvim}/doc"
+    call mkdir(docdir, "p")
+    exe $"split {docdir}/tags"
+    w!
+    bwipe!
+
+    " load required plugins, getscript.vim would be loaded manually by the test
+    " (instead of relying on autoload) because set up depends on shell selection
+    runtime plugin/vimballPlugin.vim
+    runtime plugin/getscriptPlugin.vim
+
+    " define tools location
+    if has('win32')
+
+        if executable('git')
+           let git_path = trim(system('powershell -Command "Split-Path (Split-Path (gcm git).Source)"'))
+        endif
+
+        if executable('bunzip2')
+            let g:GetLatestVimScripts_bunzip2= "bunzip2"
+        elseif executable('7z')
+            let g:GetLatestVimScripts_bunzip2= "7z x"
+        elseif exists('git_path')
+            let g:GetLatestVimScripts_bunzip2= $'{git_path}\usr\bin\bunzip2'
+        else
+            call assert_report("Missing tool to decompress .bz2 files")
+        endif
+
+        if executable('gunzip')
+            let g:GetLatestVimScripts_gunzip= "gunzip"
+        elseif executable('7z')
+            let g:GetLatestVimScripts_gunzip="7z e"
+        elseif exists('git_path')
+            let g:GetLatestVimScripts_gunzip= $'{git_path}\usr\bin\gunzip'
+        else
+            call assert_report("Missing tool to decompress .gz files")
+        endif
+
+        if executable('unxz')
+            let g:GetLatestVimScripts_unxz= "unxz"
+        elseif executable('7z')
+            let g:GetLatestVimScripts_unxz="7z x"
+        elseif exists('git_path')
+            let g:GetLatestVimScripts_unxz= $'{git_path}\mingw64\bin\unxz'
+        else
+            call assert_report("Missing tool to decompress .xz files")
+        endif
+
+   endif
+
+endfunc
+
+" After each test remove the contents of the .vim dir and reset the script
+func TearDown()
+    call delete($"{$HOME}/{s:dotvim}", "rf")
+
+    " getscript.vim include guard
+    unlet! g:loaded_getscript g:loaded_getscriptPlugin
+    " remove all globals (shell dependents)
+    let script_globals = keys(g:)
+    call filter(script_globals, 'v:val =~ "GetLatestVimScripts_"')
+    if len(script_globals)
+        call map(script_globals, '"g:" . v:val')
+        exe "unlet " . script_globals->join()
+    endif
+endfunc
+
+" Ancillary functions
+
+func SetShell(shell)
+
+    " select different shells
+    if a:shell == "default"
+        set shell& shellcmdflag& shellxquote& shellpipe& shellredir&
+    elseif a:shell == "powershell" " help dos-powershell
+        " powershell desktop is windows only
+        if !has("win32")
+            throw 'Skipped: powershell desktop is missing'
+        endif
+        set shell=powershell shellcmdflag=-NoProfile\ -Command shellxquote=\"
+        set shellpipe=2>&1\ \|\ Out-File\ -Encoding\ default shellredir=2>&1\ \|\ Out-File\ -Encoding\ default
+    elseif a:shell == "pwsh" " help dos-powershell
+        " powershell core works crossplatform
+        if !executable("pwsh")
+            throw 'Skipped: powershell core is missing'
+        endif
+        set shell=pwsh shellcmdflag=-NoProfile\ -c shellpipe=>%s\ 2>&1 shellredir=>%s\ 2>&1
+        if has("win32")
+            set shellxquote=\"
+        else
+            set shellxquote=
+        endif
+    else
+        call assert_report("Trying to select and unknown shell")
+    endif
+
+    " reload script to force new shell options
+    runtime autoload/getscript.vim
+
+endfunc
+
+func SelectScript(package)
+
+    " add the corresponding file
+    exe $"split {s:scriptdir}/GetLatestVimScripts.dat"
+    let scripts =<< trim END
+        ScriptID SourceID Filename
+        --------------------------
+    END
+    call setline(1, scripts)
+    call append(line('$'), s:packages[a:package]['spec'])
+    w!
+    bwipe!
+
+endfunc
+
+func ValidateInstall(package)
+    " check the package is expected
+    call assert_true(s:packages->has_key(a:package), "This package is unexpected")
+
+    " check if installation work out
+    if s:packages[a:package]->has_key('package')
+        let check = filereadable($"{$HOME}/{s:dotvim}/".s:packages[a:package]['package'])
+        call assert_true(check, "The plugin was not downloaded")
+    endif
+
+    call assert_true(s:packages[a:package]->has_key('files'), "This package lacks validation files")
+    for file in s:packages[a:package]['files']
+        let check = filereadable($"{$HOME}/{s:dotvim}/".file)
+        call assert_true(check, "The plugin was not installed")
+    endfor
+endfunc
+
+" Tests
+"
+func Test_glvs_default_vmb()
+
+    " select different shells
+    call SetShell('default')
+
+    " add the corresponding script
+    call SelectScript('vmb')
+
+    " load the plugins specified
+    GLVS
+
+    call ValidateInstall('vmb')
+
+endfunc
+
+func Test_glvs_pwsh_vmb()
+
+    " select different shells
+    call SetShell('pwsh')
+
+    " add the corresponding script
+    call SelectScript('vmb')
+
+    " load the plugins specified
+    GLVS
+
+    call ValidateInstall('vmb')
+
+endfunc
+
+func Test_glvs_powershell_vmb()
+
+    " select different shells
+    call SetShell('powershell')
+
+    " add the corresponding script
+    call SelectScript('vmb')
+
+    " load the plugins specified
+    GLVS
+
+    call ValidateInstall('vmb')
+
+endfunc
+
+func Test_glvs_default_vim_bz2()
+
+    " select different shells
+    call SetShell('default')
+
+    " add the corresponding script
+    call SelectScript('vim.bz2')
+
+    " load the plugins specified
+    GLVS
+
+    call ValidateInstall('vim.bz2')
+
+endfunc
+
+func Test_glvs_powershell_vim_bz2()
+
+    " select different shells
+    call SetShell('powershell')
+
+    " add the corresponding script
+    call SelectScript('vim.bz2')
+
+    " load the plugins specified
+    GLVS
+
+    call ValidateInstall('vim.bz2')
+
+endfunc
+
+func Test_glvs_pwsh_vim_bz2()
+
+    " select different shells
+    call SetShell('pwsh')
+
+    " add the corresponding script
+    call SelectScript('vim.bz2')
+
+    " load the plugins specified
+    GLVS
+
+    call ValidateInstall('vim.bz2')
+
+endfunc
+
+func Test_glvs_default_vba_gz()
+
+    " select different shells
+    call SetShell('default')
+
+    " add the corresponding script
+    call SelectScript('vba.gz')
+
+    " load the plugins specified
+    GLVS
+
+    call ValidateInstall('vba.gz')
+
+endfunc
+
+func Test_glvs_powershell_vba_gz()
+
+    " select different shells
+    call SetShell('powershell')
+
+    " add the corresponding script
+    call SelectScript('vba.gz')
+
+    " load the plugins specified
+    GLVS
+
+    call ValidateInstall('vba.gz')
+
+endfunc
+
+func Test_glvs_pwsh_vba_gz()
+
+    " select different shells
+    call SetShell('pwsh')
+
+    " add the corresponding script
+    call SelectScript('vba.gz')
+
+    " load the plugins specified
+    GLVS
+
+    call ValidateInstall('vba.gz')
+
+endfunc
+
+func Test_glvs_default_tar_xz()
+
+    " select different shells
+    call SetShell('default')
+
+    " add the corresponding script
+    call SelectScript('tar.xz')
+
+    " load the plugins specified
+    GLVS
+
+    call ValidateInstall('tar.xz')
+
+endfunc
+
+func Test_glvs_powershell_tar_xz()
+
+    " select different shells
+    call SetShell('powershell')
+
+    " add the corresponding script
+    call SelectScript('tar.xz')
+
+    " load the plugins specified
+    GLVS
+
+    call ValidateInstall('tar.xz')
+
+endfunc
+
+func Test_glvs_pwsh_tar_xz()
+
+    " select different shells
+    call SetShell('pwsh')
+
+    " add the corresponding script
+    call SelectScript('tar.xz')
+
+    " load the plugins specified
+    GLVS
+
+    call ValidateInstall('tar.xz')
+
+endfunc
diff --git a/src/version.c b/src/version.c
index bb0de39..8c2533c 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    859,
+/**/
     858,
 /**/
     857,