mirror of
				https://github.com/vim/vim.git
				synced 2025-10-30 09:47:20 -04:00 
			
		
		
		
	patch 9.1.0686: zip-plugin has problems with special characters
Problem:  zip-plugin has problems with special characters
          (user202729)
Solution: escape '*?[\' on Unix and handle those chars
          a bit differently on MS-Windows, add a test, check
          before overwriting files
runtime(zip): small fixes for zip plugin
This does the following:
- verify the unzip plugin is executable when loading the autoload plugin
- handle extracting file names with '[*?\' in its name correctly by
  escaping those characters for the unzip command (and handle those
  characters a bit differently on MS-Windows, since the quoting is different)
- verify, that the extract plugin is not overwriting a file (could cause
  a hang, because unzip asking for confirmation)
- add a test zip file which contains those special file names
fixes: #15505
closes: #15519
Signed-off-by: Christian Brabandt <cb@256bit.org>
			
			
This commit is contained in:
		
							
								
								
									
										1
									
								
								Filelist
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Filelist
									
									
									
									
									
								
							| @@ -221,6 +221,7 @@ SRC_ALL =	\ | |||||||
| 		src/testdir/samples/*.vim \ | 		src/testdir/samples/*.vim \ | ||||||
| 		src/testdir/samples/test000 \ | 		src/testdir/samples/test000 \ | ||||||
| 		src/testdir/samples/test.zip \ | 		src/testdir/samples/test.zip \ | ||||||
|  | 		src/testdir/samples/testa.zip \ | ||||||
| 		src/testdir/color_ramp.vim \ | 		src/testdir/color_ramp.vim \ | ||||||
| 		src/testdir/silent.wav \ | 		src/testdir/silent.wav \ | ||||||
| 		src/testdir/popupbounce.vim \ | 		src/testdir/popupbounce.vim \ | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| " zip.vim: Handles browsing zipfiles | " zip.vim: Handles browsing zipfiles | ||||||
| " AUTOLOAD PORTION | " AUTOLOAD PORTION | ||||||
| " Date:		Aug 05, 2024 | " Date:		Aug 18, 2024 | ||||||
| " Version:	34 | " Version:	34 | ||||||
| " Maintainer:	This runtime file is looking for a new maintainer. | " Maintainer:	This runtime file is looking for a new maintainer. | ||||||
| " Former Maintainer:	Charles E Campbell | " Former Maintainer:	Charles E Campbell | ||||||
| @@ -12,6 +12,7 @@ | |||||||
| " 2024 Aug 04 by Vim Project: escape '[' in name of file to be extracted | " 2024 Aug 04 by Vim Project: escape '[' in name of file to be extracted | ||||||
| " 2024 Aug 05 by Vim Project: workaround for the FreeBSD's unzip | " 2024 Aug 05 by Vim Project: workaround for the FreeBSD's unzip | ||||||
| " 2024 Aug 05 by Vim Project: clean-up and make it work with shellslash on Windows | " 2024 Aug 05 by Vim Project: clean-up and make it work with shellslash on Windows | ||||||
|  | " 2024 Aug 18 by Vim Project: correctly handle special globbing chars | ||||||
| " License:	Vim License  (see vim's :help license) | " License:	Vim License  (see vim's :help license) | ||||||
| " Copyright:	Copyright (C) 2005-2019 Charles E. Campbell {{{1 | " Copyright:	Copyright (C) 2005-2019 Charles E. Campbell {{{1 | ||||||
| "		Permission is hereby granted to use and distribute this code, | "		Permission is hereby granted to use and distribute this code, | ||||||
| @@ -73,6 +74,11 @@ if v:version < 901 | |||||||
|  call s:Mess('WarningMsg', "***warning*** this version of zip needs vim 9.1 or later") |  call s:Mess('WarningMsg', "***warning*** this version of zip needs vim 9.1 or later") | ||||||
|  finish |  finish | ||||||
| endif | endif | ||||||
|  | " sanity checks | ||||||
|  | if !executable(g:zip_unzipcmd) | ||||||
|  |  call s:Mess('Error', "***error*** (zip#Browse) unzip not available on your system") | ||||||
|  |  finish | ||||||
|  | endif | ||||||
| if !dist#vim#IsSafeExecutable('zip', g:zip_unzipcmd) | if !dist#vim#IsSafeExecutable('zip', g:zip_unzipcmd) | ||||||
|  call s:Mess('Error', "Warning: NOT executing " .. g:zip_unzipcmd .. " from current directory!") |  call s:Mess('Error', "Warning: NOT executing " .. g:zip_unzipcmd .. " from current directory!") | ||||||
|  finish |  finish | ||||||
| @@ -199,7 +205,7 @@ fun! zip#Read(fname,mode) | |||||||
|    let zipfile = substitute(a:fname,'^.\{-}zipfile://\(.\{-}\)::[^\\].*$','\1','') |    let zipfile = substitute(a:fname,'^.\{-}zipfile://\(.\{-}\)::[^\\].*$','\1','') | ||||||
|    let fname   = substitute(a:fname,'^.\{-}zipfile://.\{-}::\([^\\].*\)$','\1','') |    let fname   = substitute(a:fname,'^.\{-}zipfile://.\{-}::\([^\\].*\)$','\1','') | ||||||
|   endif |   endif | ||||||
|   let fname    = substitute(fname, '[', '[[]', 'g') |   let fname    = fname->substitute('[', '[[]', 'g')->escape('?*\\') | ||||||
|   " sanity check |   " sanity check | ||||||
|   if !executable(substitute(g:zip_unzipcmd,'\s\+.*$','','')) |   if !executable(substitute(g:zip_unzipcmd,'\s\+.*$','','')) | ||||||
|    call s:Mess('Error', "***error*** (zip#Read) sorry, your system doesn't appear to have the ".g:zip_unzipcmd." program") |    call s:Mess('Error', "***error*** (zip#Read) sorry, your system doesn't appear to have the ".g:zip_unzipcmd." program") | ||||||
| @@ -331,9 +337,24 @@ fun! zip#Extract() | |||||||
|    call s:Mess('Error', "***error*** (zip#Extract) Please specify a file, not a directory") |    call s:Mess('Error', "***error*** (zip#Extract) Please specify a file, not a directory") | ||||||
|    return |    return | ||||||
|   endif |   endif | ||||||
|  |   if filereadable(fname) | ||||||
|  |    call s:Mess('Error', "***error*** (zip#Extract) <" .. fname .."> already exists in directory, not overwriting!") | ||||||
|  |    return | ||||||
|  |   endif | ||||||
|  |   let target = fname->substitute('\[', '[[]', 'g') | ||||||
|  |   if &shell =~ 'cmd' && (has("win32") || has("win64")) | ||||||
|  |     let target = target | ||||||
|  | 		\ ->substitute('[?*]', '[&]', 'g') | ||||||
|  | 		\ ->substitute('[\\]', '?', 'g') | ||||||
|  | 		\ ->shellescape() | ||||||
|  |     " there cannot be a file name with '\' in its name, unzip replaces it by _ | ||||||
|  |     let fname = fname->substitute('[\\?*]', '_', 'g') | ||||||
|  |   else | ||||||
|  |     let target = target->escape('*?\\')->shellescape() | ||||||
|  |   endif | ||||||
|  |  | ||||||
|   " extract the file mentioned under the cursor |   " extract the file mentioned under the cursor | ||||||
|   call system($"{g:zip_extractcmd} {shellescape(b:zipfile)} {shellescape(fname)}") |   call system($"{g:zip_extractcmd} -o {shellescape(b:zipfile)} {target}") | ||||||
|   if v:shell_error != 0 |   if v:shell_error != 0 | ||||||
|    call s:Mess('Error', "***error*** ".g:zip_extractcmd." ".b:zipfile." ".fname.": failed!") |    call s:Mess('Error', "***error*** ".g:zip_extractcmd." ".b:zipfile." ".fname.": failed!") | ||||||
|   elseif !filereadable(fname) |   elseif !filereadable(fname) | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								src/testdir/samples/testa.zip
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/testdir/samples/testa.zip
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -40,7 +40,8 @@ def Test_zip_basic() | |||||||
|                execute("normal \<CR>")) |                execute("normal \<CR>")) | ||||||
|  |  | ||||||
|   ### Check ENTER on file |   ### Check ENTER on file | ||||||
|   :1|:/^$//file/ |   :1 | ||||||
|  |   search('file.txt') | ||||||
|   exe ":normal \<cr>" |   exe ":normal \<cr>" | ||||||
|   assert_match('zipfile://.*/X.zip::Xzip/file.txt', @%) |   assert_match('zipfile://.*/X.zip::Xzip/file.txt', @%) | ||||||
|   assert_equal('one', getline(1)) |   assert_equal('one', getline(1)) | ||||||
| @@ -65,6 +66,10 @@ def Test_zip_basic() | |||||||
|   :1|:/^$//file/ |   :1|:/^$//file/ | ||||||
|   normal x |   normal x | ||||||
|   assert_true(filereadable("Xzip/file.txt")) |   assert_true(filereadable("Xzip/file.txt")) | ||||||
|  |  | ||||||
|  |   ## Check not overwriting existing file | ||||||
|  |   assert_match('<Xzip/file.txt> .* not overwriting!', execute("normal x")) | ||||||
|  |  | ||||||
|   delete("Xzip", "rf") |   delete("Xzip", "rf") | ||||||
|  |  | ||||||
|   ### Check extracting directory |   ### Check extracting directory | ||||||
| @@ -131,5 +136,102 @@ def Test_zip_basic() | |||||||
|   assert_match('File not readable', execute("e Xnot_exists.zip")) |   assert_match('File not readable', execute("e Xnot_exists.zip")) | ||||||
|  |  | ||||||
|   bw |   bw | ||||||
|  | enddef | ||||||
|  |  | ||||||
|  | def Test_zip_glob_fname() | ||||||
|  |   CheckNotMSWindows | ||||||
|  |   # does not work on Windows, why? | ||||||
|  |  | ||||||
|  |   ### copy sample zip file | ||||||
|  |   if !filecopy("samples/testa.zip", "X.zip") | ||||||
|  |     assert_report("Can't copy samples/testa.zip") | ||||||
|  |     return | ||||||
|  |   endif | ||||||
|  |   defer delete("X.zip") | ||||||
|  |   defer delete('zipglob', 'rf') | ||||||
|  |  | ||||||
|  |   e X.zip | ||||||
|  |  | ||||||
|  |   ### 1) Check extracting strange files | ||||||
|  |   :1 | ||||||
|  |   var fname = 'a[a].txt' | ||||||
|  |   search('\V' .. fname) | ||||||
|  |   normal x | ||||||
|  |   assert_true(filereadable('zipglob/' .. fname)) | ||||||
|  |   delete('zipglob', 'rf') | ||||||
|  |  | ||||||
|  |   :1 | ||||||
|  |   fname = 'a*.txt' | ||||||
|  |   search('\V' .. fname) | ||||||
|  |   normal x | ||||||
|  |   assert_true(filereadable('zipglob/' .. fname)) | ||||||
|  |   delete('zipglob', 'rf') | ||||||
|  |  | ||||||
|  |   :1 | ||||||
|  |   fname = 'a?.txt' | ||||||
|  |   search('\V' .. fname) | ||||||
|  |   normal x | ||||||
|  |   assert_true(filereadable('zipglob/' .. fname)) | ||||||
|  |   delete('zipglob', 'rf') | ||||||
|  |  | ||||||
|  |   :1 | ||||||
|  |   fname = 'a\.txt' | ||||||
|  |   search('\V' .. escape(fname, '\\')) | ||||||
|  |   normal x | ||||||
|  |   assert_true(filereadable('zipglob/' .. fname)) | ||||||
|  |   delete('zipglob', 'rf') | ||||||
|  |  | ||||||
|  |   :1 | ||||||
|  |   fname = 'a\\.txt' | ||||||
|  |   search('\V' .. escape(fname, '\\')) | ||||||
|  |   normal x | ||||||
|  |   assert_true(filereadable('zipglob/' .. fname)) | ||||||
|  |   delete('zipglob', 'rf') | ||||||
|  |  | ||||||
|  |   ### 2) Check entering strange file names | ||||||
|  |   :1 | ||||||
|  |   fname = 'a[a].txt' | ||||||
|  |   search('\V' .. fname) | ||||||
|  |   exe ":normal \<cr>" | ||||||
|  |   assert_match('zipfile://.*/X.zip::zipglob/a\[a\].txt', @%) | ||||||
|  |   assert_equal('a test file with []', getline(1)) | ||||||
|  |   bw | ||||||
|  |  | ||||||
|  |   e X.zip | ||||||
|  |   :1 | ||||||
|  |   fname = 'a*.txt' | ||||||
|  |   search('\V' .. fname) | ||||||
|  |   exe ":normal \<cr>" | ||||||
|  |   assert_match('zipfile://.*/X.zip::zipglob/a\*.txt', @%) | ||||||
|  |   assert_equal('a test file with a*', getline(1)) | ||||||
|  |   bw | ||||||
|  |  | ||||||
|  |   e X.zip | ||||||
|  |   :1 | ||||||
|  |   fname = 'a?.txt' | ||||||
|  |   search('\V' .. fname) | ||||||
|  |   exe ":normal \<cr>" | ||||||
|  |   assert_match('zipfile://.*/X.zip::zipglob/a?.txt', @%) | ||||||
|  |   assert_equal('a test file with a?', getline(1)) | ||||||
|  |   bw | ||||||
|  |  | ||||||
|  |   e X.zip | ||||||
|  |   :1 | ||||||
|  |   fname = 'a\.txt' | ||||||
|  |   search('\V' .. escape(fname, '\\')) | ||||||
|  |   exe ":normal \<cr>" | ||||||
|  |   assert_match('zipfile://.*/X.zip::zipglob/a\\.txt', @%) | ||||||
|  |   assert_equal('a test file with a\', getline(1)) | ||||||
|  |   bw | ||||||
|  |  | ||||||
|  |   e X.zip | ||||||
|  |   :1 | ||||||
|  |   fname = 'a\\.txt' | ||||||
|  |   search('\V' .. escape(fname, '\\')) | ||||||
|  |   exe ":normal \<cr>" | ||||||
|  |   assert_match('zipfile://.*/X.zip::zipglob/a\\\\.txt', @%) | ||||||
|  |   assert_equal('a test file with a double \', getline(1)) | ||||||
|  |   bw | ||||||
|  |  | ||||||
|  |   bw | ||||||
| enddef | enddef | ||||||
|   | |||||||
| @@ -704,6 +704,8 @@ static char *(features[]) = | |||||||
|  |  | ||||||
| static int included_patches[] = | static int included_patches[] = | ||||||
| {   /* Add new patch number below this line */ | {   /* Add new patch number below this line */ | ||||||
|  | /**/ | ||||||
|  |     686, | ||||||
| /**/ | /**/ | ||||||
|     685, |     685, | ||||||
| /**/ | /**/ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user