zip.vim 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. " zip.vim: Handles browsing zipfiles
  2. " AUTOLOAD PORTION
  3. " Date: Nov 08, 2021
  4. " Version: 32
  5. " Maintainer: Charles E Campbell <NcampObell@SdrPchip.AorgM-NOSPAM>
  6. " License: Vim License (see vim's :help license)
  7. " Copyright: Copyright (C) 2005-2019 Charles E. Campbell {{{1
  8. " Permission is hereby granted to use and distribute this code,
  9. " with or without modifications, provided that this copyright
  10. " notice is copied with it. Like anything else that's free,
  11. " zip.vim and zipPlugin.vim are provided *as is* and comes with
  12. " no warranty of any kind, either expressed or implied. By using
  13. " this plugin, you agree that in no event will the copyright
  14. " holder be liable for any damages resulting from the use
  15. " of this software.
  16. "redraw!|call DechoSep()|call inputsave()|call input("Press <cr> to continue")|call inputrestore()
  17. " ---------------------------------------------------------------------
  18. " Load Once: {{{1
  19. if &cp || exists("g:loaded_zip")
  20. finish
  21. endif
  22. let g:loaded_zip= "v32"
  23. if v:version < 702
  24. echohl WarningMsg
  25. echo "***warning*** this version of zip needs vim 7.2 or later"
  26. echohl Normal
  27. finish
  28. endif
  29. let s:keepcpo= &cpo
  30. set cpo&vim
  31. "DechoTabOn
  32. let s:zipfile_escape = ' ?&;\'
  33. let s:ERROR = 2
  34. let s:WARNING = 1
  35. let s:NOTE = 0
  36. " ---------------------------------------------------------------------
  37. " Global Values: {{{1
  38. if !exists("g:zip_shq")
  39. if &shq != ""
  40. let g:zip_shq= &shq
  41. elseif has("unix")
  42. let g:zip_shq= "'"
  43. else
  44. let g:zip_shq= '"'
  45. endif
  46. endif
  47. if !exists("g:zip_zipcmd")
  48. let g:zip_zipcmd= "zip"
  49. endif
  50. if !exists("g:zip_unzipcmd")
  51. let g:zip_unzipcmd= "unzip"
  52. endif
  53. if !exists("g:zip_extractcmd")
  54. let g:zip_extractcmd= g:zip_unzipcmd
  55. endif
  56. " ----------------
  57. " Functions: {{{1
  58. " ----------------
  59. " ---------------------------------------------------------------------
  60. " zip#Browse: {{{2
  61. fun! zip#Browse(zipfile)
  62. " call Dfunc("zip#Browse(zipfile<".a:zipfile.">)")
  63. " sanity check: insure that the zipfile has "PK" as its first two letters
  64. " (zipped files have a leading PK as a "magic cookie")
  65. if !filereadable(a:zipfile) || readfile(a:zipfile, "", 1)[0] !~ '^PK'
  66. exe "noswapfile noautocmd noswapfile e ".fnameescape(a:zipfile)
  67. " call Dret("zip#Browse : not a zipfile<".a:zipfile.">")
  68. return
  69. " else " Decho
  70. " call Decho("zip#Browse: a:zipfile<".a:zipfile."> passed PK test - it's a zip file")
  71. endif
  72. let repkeep= &report
  73. set report=10
  74. " sanity checks
  75. if !exists("*fnameescape")
  76. if &verbose > 1
  77. echoerr "the zip plugin is not available (your vim doesn't support fnameescape())"
  78. endif
  79. return
  80. endif
  81. if !executable(g:zip_unzipcmd)
  82. redraw!
  83. echohl Error | echo "***error*** (zip#Browse) unzip not available on your system"
  84. " call inputsave()|call input("Press <cr> to continue")|call inputrestore()
  85. let &report= repkeep
  86. " call Dret("zip#Browse")
  87. return
  88. endif
  89. if !filereadable(a:zipfile)
  90. if a:zipfile !~# '^\a\+://'
  91. " if it's an url, don't complain, let url-handlers such as vim do its thing
  92. redraw!
  93. echohl Error | echo "***error*** (zip#Browse) File not readable<".a:zipfile.">" | echohl None
  94. " call inputsave()|call input("Press <cr> to continue")|call inputrestore()
  95. endif
  96. let &report= repkeep
  97. " call Dret("zip#Browse : file<".a:zipfile."> not readable")
  98. return
  99. endif
  100. " call Decho("passed sanity checks")
  101. if &ma != 1
  102. set ma
  103. endif
  104. let b:zipfile= a:zipfile
  105. setlocal noswapfile
  106. setlocal buftype=nofile
  107. setlocal bufhidden=hide
  108. setlocal nobuflisted
  109. setlocal nowrap
  110. " Oct 12, 2021: need to re-use Bram's syntax/tar.vim.
  111. " Setting the filetype to zip doesn't do anything (currently),
  112. " but it is perhaps less confusing to curious perusers who do
  113. " a :echo &ft
  114. setf zip
  115. run! syntax/tar.vim
  116. " give header
  117. call append(0, ['" zip.vim version '.g:loaded_zip,
  118. \ '" Browsing zipfile '.a:zipfile,
  119. \ '" Select a file with cursor and press ENTER'])
  120. keepj $
  121. " call Decho("exe silent r! ".g:zip_unzipcmd." -l -- ".s:Escape(a:zipfile,1))
  122. exe "keepj sil! r! ".g:zip_unzipcmd." -Z -1 -- ".s:Escape(a:zipfile,1)
  123. if v:shell_error != 0
  124. redraw!
  125. echohl WarningMsg | echo "***warning*** (zip#Browse) ".fnameescape(a:zipfile)." is not a zip file" | echohl None
  126. " call inputsave()|call input("Press <cr> to continue")|call inputrestore()
  127. keepj sil! %d
  128. let eikeep= &ei
  129. set ei=BufReadCmd,FileReadCmd
  130. exe "keepj r ".fnameescape(a:zipfile)
  131. let &ei= eikeep
  132. keepj 1d
  133. " call Dret("zip#Browse")
  134. return
  135. endif
  136. " Maps associated with zip plugin
  137. setlocal noma nomod ro
  138. noremap <silent> <buffer> <cr> :call <SID>ZipBrowseSelect()<cr>
  139. noremap <silent> <buffer> x :call zip#Extract()<cr>
  140. if &mouse != ""
  141. noremap <silent> <buffer> <leftmouse> <leftmouse>:call <SID>ZipBrowseSelect()<cr>
  142. endif
  143. let &report= repkeep
  144. " call Dret("zip#Browse")
  145. endfun
  146. " ---------------------------------------------------------------------
  147. " ZipBrowseSelect: {{{2
  148. fun! s:ZipBrowseSelect()
  149. " call Dfunc("ZipBrowseSelect() zipfile<".b:zipfile."> curfile<".expand("%").">")
  150. let repkeep= &report
  151. set report=10
  152. let fname= getline(".")
  153. " sanity check
  154. if fname =~ '^"'
  155. let &report= repkeep
  156. " call Dret("ZipBrowseSelect")
  157. return
  158. endif
  159. if fname =~ '/$'
  160. redraw!
  161. echohl Error | echo "***error*** (zip#Browse) Please specify a file, not a directory" | echohl None
  162. " call inputsave()|call input("Press <cr> to continue")|call inputrestore()
  163. let &report= repkeep
  164. " call Dret("ZipBrowseSelect")
  165. return
  166. endif
  167. " call Decho("fname<".fname.">")
  168. " get zipfile to the new-window
  169. let zipfile = b:zipfile
  170. let curfile = expand("%")
  171. " call Decho("zipfile<".zipfile.">")
  172. " call Decho("curfile<".curfile.">")
  173. noswapfile new
  174. if !exists("g:zip_nomax") || g:zip_nomax == 0
  175. wincmd _
  176. endif
  177. let s:zipfile_{winnr()}= curfile
  178. " call Decho("exe e ".fnameescape("zipfile://".zipfile.'::'.fname))
  179. exe "noswapfile e ".fnameescape("zipfile://".zipfile.'::'.fname)
  180. filetype detect
  181. let &report= repkeep
  182. " call Dret("ZipBrowseSelect : s:zipfile_".winnr()."<".s:zipfile_{winnr()}.">")
  183. endfun
  184. " ---------------------------------------------------------------------
  185. " zip#Read: {{{2
  186. fun! zip#Read(fname,mode)
  187. " call Dfunc("zip#Read(fname<".a:fname.">,mode=".a:mode.")")
  188. let repkeep= &report
  189. set report=10
  190. if has("unix")
  191. let zipfile = substitute(a:fname,'zipfile://\(.\{-}\)::[^\\].*$','\1','')
  192. let fname = substitute(a:fname,'zipfile://.\{-}::\([^\\].*\)$','\1','')
  193. else
  194. let zipfile = substitute(a:fname,'^.\{-}zipfile://\(.\{-}\)::[^\\].*$','\1','')
  195. let fname = substitute(a:fname,'^.\{-}zipfile://.\{-}::\([^\\].*\)$','\1','')
  196. let fname = substitute(fname, '[', '[[]', 'g')
  197. endif
  198. " call Decho("zipfile<".zipfile.">")
  199. " call Decho("fname <".fname.">")
  200. " sanity check
  201. if !executable(substitute(g:zip_unzipcmd,'\s\+.*$','',''))
  202. redraw!
  203. echohl Error | echo "***error*** (zip#Read) sorry, your system doesn't appear to have the ".g:zip_unzipcmd." program" | echohl None
  204. " call inputsave()|call input("Press <cr> to continue")|call inputrestore()
  205. let &report= repkeep
  206. " call Dret("zip#Write")
  207. return
  208. endif
  209. " the following code does much the same thing as
  210. " exe "keepj sil! r! ".g:zip_unzipcmd." -p -- ".s:Escape(zipfile,1)." ".s:Escape(fnameescape(fname),1)
  211. " but allows zipfile://... entries in quickfix lists
  212. let temp = tempname()
  213. " call Decho("using temp file<".temp.">")
  214. let fn = expand('%:p')
  215. exe "sil! !".g:zip_unzipcmd." -p -- ".s:Escape(zipfile,1)." ".s:Escape(fnameescape(fname),1).' > '.temp
  216. " call Decho("exe sil! !".g:zip_unzipcmd." -p -- ".s:Escape(zipfile,1)." ".s:Escape(fnameescape(fname),1).' > '.temp)
  217. sil exe 'keepalt file '.temp
  218. sil keepj e!
  219. sil exe 'keepalt file '.fnameescape(fn)
  220. call delete(temp)
  221. filetype detect
  222. " cleanup
  223. " keepj 0d " used to be needed for the ...r! ... method
  224. set nomod
  225. let &report= repkeep
  226. " call Dret("zip#Read")
  227. endfun
  228. " ---------------------------------------------------------------------
  229. " zip#Write: {{{2
  230. fun! zip#Write(fname)
  231. " call Dfunc("zip#Write(fname<".a:fname.">) zipfile_".winnr()."<".s:zipfile_{winnr()}.">")
  232. let repkeep= &report
  233. set report=10
  234. " sanity checks
  235. if !executable(substitute(g:zip_zipcmd,'\s\+.*$','',''))
  236. redraw!
  237. echohl Error | echo "***error*** (zip#Write) sorry, your system doesn't appear to have the ".g:zip_zipcmd." program" | echohl None
  238. " call inputsave()|call input("Press <cr> to continue")|call inputrestore()
  239. let &report= repkeep
  240. " call Dret("zip#Write")
  241. return
  242. endif
  243. if !exists("*mkdir")
  244. redraw!
  245. echohl Error | echo "***error*** (zip#Write) sorry, mkdir() doesn't work on your system" | echohl None
  246. " call inputsave()|call input("Press <cr> to continue")|call inputrestore()
  247. let &report= repkeep
  248. " call Dret("zip#Write")
  249. return
  250. endif
  251. let curdir= getcwd()
  252. let tmpdir= tempname()
  253. " call Decho("orig tempname<".tmpdir.">")
  254. if tmpdir =~ '\.'
  255. let tmpdir= substitute(tmpdir,'\.[^.]*$','','e')
  256. endif
  257. " call Decho("tmpdir<".tmpdir.">")
  258. call mkdir(tmpdir,"p")
  259. " attempt to change to the indicated directory
  260. if s:ChgDir(tmpdir,s:ERROR,"(zip#Write) cannot cd to temporary directory")
  261. let &report= repkeep
  262. " call Dret("zip#Write")
  263. return
  264. endif
  265. " call Decho("current directory now: ".getcwd())
  266. " place temporary files under .../_ZIPVIM_/
  267. if isdirectory("_ZIPVIM_")
  268. call s:Rmdir("_ZIPVIM_")
  269. endif
  270. call mkdir("_ZIPVIM_")
  271. cd _ZIPVIM_
  272. " call Decho("current directory now: ".getcwd())
  273. if has("unix")
  274. let zipfile = substitute(a:fname,'zipfile://\(.\{-}\)::[^\\].*$','\1','')
  275. let fname = substitute(a:fname,'zipfile://.\{-}::\([^\\].*\)$','\1','')
  276. else
  277. let zipfile = substitute(a:fname,'^.\{-}zipfile://\(.\{-}\)::[^\\].*$','\1','')
  278. let fname = substitute(a:fname,'^.\{-}zipfile://.\{-}::\([^\\].*\)$','\1','')
  279. endif
  280. " call Decho("zipfile<".zipfile.">")
  281. " call Decho("fname <".fname.">")
  282. if fname =~ '/'
  283. let dirpath = substitute(fname,'/[^/]\+$','','e')
  284. if has("win32unix") && executable("cygpath")
  285. let dirpath = substitute(system("cygpath ".s:Escape(dirpath,0)),'\n','','e')
  286. endif
  287. " call Decho("mkdir(dirpath<".dirpath.">,p)")
  288. call mkdir(dirpath,"p")
  289. endif
  290. if zipfile !~ '/'
  291. let zipfile= curdir.'/'.zipfile
  292. endif
  293. " call Decho("zipfile<".zipfile."> fname<".fname.">")
  294. exe "w! ".fnameescape(fname)
  295. if has("win32unix") && executable("cygpath")
  296. let zipfile = substitute(system("cygpath ".s:Escape(zipfile,0)),'\n','','e')
  297. endif
  298. if (has("win32") || has("win95") || has("win64") || has("win16")) && &shell !~? 'sh$'
  299. let fname = substitute(fname, '[', '[[]', 'g')
  300. endif
  301. " call Decho(g:zip_zipcmd." -u ".s:Escape(fnamemodify(zipfile,":p"),0)." ".s:Escape(fname,0))
  302. call system(g:zip_zipcmd." -u ".s:Escape(fnamemodify(zipfile,":p"),0)." ".s:Escape(fname,0))
  303. if v:shell_error != 0
  304. redraw!
  305. echohl Error | echo "***error*** (zip#Write) sorry, unable to update ".zipfile." with ".fname | echohl None
  306. " call inputsave()|call input("Press <cr> to continue")|call inputrestore()
  307. elseif s:zipfile_{winnr()} =~ '^\a\+://'
  308. " support writing zipfiles across a network
  309. let netzipfile= s:zipfile_{winnr()}
  310. " call Decho("handle writing <".zipfile."> across network as <".netzipfile.">")
  311. 1split|enew
  312. let binkeep= &binary
  313. let eikeep = &ei
  314. set binary ei=all
  315. exe "noswapfile e! ".fnameescape(zipfile)
  316. call netrw#NetWrite(netzipfile)
  317. let &ei = eikeep
  318. let &binary = binkeep
  319. q!
  320. unlet s:zipfile_{winnr()}
  321. endif
  322. " cleanup and restore current directory
  323. cd ..
  324. call s:Rmdir("_ZIPVIM_")
  325. call s:ChgDir(curdir,s:WARNING,"(zip#Write) unable to return to ".curdir."!")
  326. call s:Rmdir(tmpdir)
  327. setlocal nomod
  328. let &report= repkeep
  329. " call Dret("zip#Write")
  330. endfun
  331. " ---------------------------------------------------------------------
  332. " zip#Extract: extract a file from a zip archive {{{2
  333. fun! zip#Extract()
  334. " call Dfunc("zip#Extract()")
  335. let repkeep= &report
  336. set report=10
  337. let fname= getline(".")
  338. " call Decho("fname<".fname.">")
  339. " sanity check
  340. if fname =~ '^"'
  341. let &report= repkeep
  342. " call Dret("zip#Extract")
  343. return
  344. endif
  345. if fname =~ '/$'
  346. redraw!
  347. echohl Error | echo "***error*** (zip#Extract) Please specify a file, not a directory" | echohl None
  348. let &report= repkeep
  349. " call Dret("zip#Extract")
  350. return
  351. endif
  352. " extract the file mentioned under the cursor
  353. " call Decho("system(".g:zip_extractcmd." ".shellescape(b:zipfile)." ".shellescape(shell).")")
  354. call system(g:zip_extractcmd." ".shellescape(b:zipfile)." ".shellescape(shell))
  355. " call Decho("zipfile<".b:zipfile.">")
  356. if v:shell_error != 0
  357. echohl Error | echo "***error*** ".g:zip_extractcmd." ".b:zipfile." ".fname.": failed!" | echohl NONE
  358. elseif !filereadable(fname)
  359. echohl Error | echo "***error*** attempted to extract ".fname." but it doesn't appear to be present!"
  360. else
  361. echo "***note*** successfully extracted ".fname
  362. endif
  363. " restore option
  364. let &report= repkeep
  365. " call Dret("zip#Extract")
  366. endfun
  367. " ---------------------------------------------------------------------
  368. " s:Escape: {{{2
  369. fun! s:Escape(fname,isfilt)
  370. " call Dfunc("QuoteFileDir(fname<".a:fname."> isfilt=".a:isfilt.")")
  371. if exists("*shellescape")
  372. if a:isfilt
  373. let qnameq= shellescape(a:fname,1)
  374. else
  375. let qnameq= shellescape(a:fname)
  376. endif
  377. else
  378. let qnameq= g:zip_shq.escape(a:fname,g:zip_shq).g:zip_shq
  379. endif
  380. " call Dret("QuoteFileDir <".qnameq.">")
  381. return qnameq
  382. endfun
  383. " ---------------------------------------------------------------------
  384. " ChgDir: {{{2
  385. fun! s:ChgDir(newdir,errlvl,errmsg)
  386. " call Dfunc("ChgDir(newdir<".a:newdir."> errlvl=".a:errlvl." errmsg<".a:errmsg.">)")
  387. try
  388. exe "cd ".fnameescape(a:newdir)
  389. catch /^Vim\%((\a\+)\)\=:E344/
  390. redraw!
  391. if a:errlvl == s:NOTE
  392. echo "***note*** ".a:errmsg
  393. elseif a:errlvl == s:WARNING
  394. echohl WarningMsg | echo "***warning*** ".a:errmsg | echohl NONE
  395. elseif a:errlvl == s:ERROR
  396. echohl Error | echo "***error*** ".a:errmsg | echohl NONE
  397. endif
  398. " call inputsave()|call input("Press <cr> to continue")|call inputrestore()
  399. " call Dret("ChgDir 1")
  400. return 1
  401. endtry
  402. " call Dret("ChgDir 0")
  403. return 0
  404. endfun
  405. " ---------------------------------------------------------------------
  406. " s:Rmdir: {{{2
  407. fun! s:Rmdir(fname)
  408. " call Dfunc("Rmdir(fname<".a:fname.">)")
  409. if (has("win32") || has("win95") || has("win64") || has("win16")) && &shell !~? 'sh$'
  410. call system("rmdir /S/Q ".s:Escape(a:fname,0))
  411. else
  412. call system("/bin/rm -rf ".s:Escape(a:fname,0))
  413. endif
  414. " call Dret("Rmdir")
  415. endfun
  416. " ------------------------------------------------------------------------
  417. " Modelines And Restoration: {{{1
  418. let &cpo= s:keepcpo
  419. unlet s:keepcpo
  420. " vim:ts=8 fdm=marker