gzip.vim 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. " Vim autoload file for editing compressed files.
  2. " Maintainer: The Vim Project <https://github.com/vim/vim>
  3. " Last Change: 2024 Nov 25
  4. " Former Maintainer: Bram Moolenaar <Bram@vim.org>
  5. " These functions are used by the gzip plugin.
  6. " Function to check that executing "cmd [-f]" works.
  7. " The result is cached in s:have_"cmd" for speed.
  8. fun s:check(cmd)
  9. let name = substitute(a:cmd, '\(\S*\).*', '\1', '')
  10. if !exists("s:have_" . name)
  11. " safety check, don't execute anything from the current directory
  12. let f = dist#vim#IsSafeExecutable('gzip', name)
  13. if !f
  14. echoerr "Warning: NOT executing " .. name .. " from current directory!"
  15. endif
  16. let e = executable(name)
  17. if e < 0
  18. let r = system(name . " --version")
  19. let e = (r !~ "not found" && r != "")
  20. endif
  21. exe "let s:have_" . name . "=" . (e && f)
  22. endif
  23. exe "return s:have_" . name
  24. endfun
  25. " Set b:gzip_comp_arg to the gzip argument to be used for compression, based on
  26. " the flags in the compressed file.
  27. " The only compression methods that can be detected are max speed (-1) and max
  28. " compression (-9).
  29. fun s:set_compression(line)
  30. " get the Compression Method
  31. let l:cm = char2nr(a:line[2])
  32. " if it's 8 (DEFLATE), we can check for the compression level
  33. if l:cm == 8
  34. " get the eXtra FLags
  35. let l:xfl = char2nr(a:line[8])
  36. " max compression
  37. if l:xfl == 2
  38. let b:gzip_comp_arg = "-9"
  39. " min compression
  40. elseif l:xfl == 4
  41. let b:gzip_comp_arg = "-1"
  42. endif
  43. endif
  44. endfun
  45. " After reading compressed file: Uncompress text in buffer with "cmd"
  46. fun gzip#read(cmd)
  47. " don't do anything if the cmd is not supported
  48. if !s:check(a:cmd)
  49. return
  50. endif
  51. " for gzip check current compression level and set b:gzip_comp_arg.
  52. silent! unlet b:gzip_comp_arg
  53. if a:cmd[0] == 'g'
  54. call s:set_compression(getline(1))
  55. endif
  56. " make 'patchmode' empty, we don't want a copy of the written file
  57. let pm_save = &pm
  58. set pm=
  59. " remove 'a' and 'A' from 'cpo' to avoid the alternate file changes
  60. let cpo_save = &cpo
  61. set cpo-=a cpo-=A
  62. " set 'modifiable'
  63. let ma_save = &ma
  64. setlocal ma
  65. " set 'write'
  66. let write_save = &write
  67. set write
  68. " Reset 'foldenable', otherwise line numbers get adjusted.
  69. if has("folding")
  70. let fen_save = &fen
  71. setlocal nofen
  72. endif
  73. " when filtering the whole buffer, it will become empty
  74. let empty = line("'[") == 1 && line("']") == line("$")
  75. let tmp = tempname()
  76. let tmpe = tmp . "." . expand("<afile>:e")
  77. if exists('*fnameescape')
  78. let tmp_esc = fnameescape(tmp)
  79. let tmpe_esc = fnameescape(tmpe)
  80. else
  81. let tmp_esc = escape(tmp, ' ')
  82. let tmpe_esc = escape(tmpe, ' ')
  83. endif
  84. " write the just read lines to a temp file "'[,']w tmp.gz"
  85. execute "silent '[,']w " . tmpe_esc
  86. " uncompress the temp file: call system("gzip -dn tmp.gz")
  87. call system(a:cmd . " " . s:escape(tmpe))
  88. if !filereadable(tmp)
  89. " uncompress didn't work! Keep the compressed file then.
  90. echoerr "Error: Could not read uncompressed file"
  91. let ok = 0
  92. else
  93. let ok = 1
  94. " delete the compressed lines; remember the line number
  95. let l = line("'[") - 1
  96. if exists(":lockmarks")
  97. lockmarks '[,']d _
  98. else
  99. '[,']d _
  100. endif
  101. " read in the uncompressed lines "'[-1r tmp"
  102. " Use ++edit if the buffer was empty, keep the 'ff' and 'fenc' options.
  103. setlocal nobin
  104. if exists(":lockmarks")
  105. if empty
  106. execute "silent lockmarks " . l . "r ++edit " . tmp_esc
  107. else
  108. execute "silent lockmarks " . l . "r " . tmp_esc
  109. endif
  110. else
  111. execute "silent " . l . "r " . tmp_esc
  112. endif
  113. " if buffer became empty, delete trailing blank line
  114. if empty
  115. silent $delete _
  116. 1
  117. endif
  118. " delete the temp file and the used buffers
  119. call delete(tmp)
  120. silent! exe "bwipe " . tmp_esc
  121. silent! exe "bwipe " . tmpe_esc
  122. endif
  123. " Store the OK flag, so that we can use it when writing.
  124. let b:uncompressOk = ok
  125. " Restore saved option values.
  126. let &pm = pm_save
  127. let &cpo = cpo_save
  128. let &l:ma = ma_save
  129. let &write = write_save
  130. if has("folding")
  131. let &l:fen = fen_save
  132. endif
  133. " When uncompressed the whole buffer, do autocommands
  134. if ok && empty
  135. if exists('*fnameescape')
  136. let fname = fnameescape(expand("%:r"))
  137. else
  138. let fname = escape(expand("%:r"), " \t\n*?[{`$\\%#'\"|!<")
  139. endif
  140. if filereadable(undofile(expand("%")))
  141. exe "sil rundo " . fnameescape(undofile(expand("%")))
  142. endif
  143. if &verbose >= 8
  144. execute "doau BufReadPost " . fname
  145. else
  146. execute "silent! doau BufReadPost " . fname
  147. endif
  148. endif
  149. endfun
  150. " After writing compressed file: Compress written file with "cmd"
  151. fun gzip#write(cmd)
  152. if exists('b:uncompressOk') && !b:uncompressOk
  153. echomsg "Not compressing file because uncompress failed; reset b:uncompressOk to compress anyway"
  154. " don't do anything if the cmd is not supported
  155. elseif s:check(a:cmd)
  156. " Rename the file before compressing it.
  157. let nm = resolve(expand("<afile>"))
  158. let nmt = s:tempname(nm)
  159. if rename(nm, nmt) == 0
  160. if exists("b:gzip_comp_arg")
  161. call system(a:cmd . " " . b:gzip_comp_arg . " -- " . s:escape(nmt))
  162. else
  163. call system(a:cmd . " -- " . s:escape(nmt))
  164. endif
  165. call rename(nmt . "." . expand("<afile>:e"), nm)
  166. endif
  167. endif
  168. endfun
  169. " Before appending to compressed file: Uncompress file with "cmd"
  170. fun gzip#appre(cmd)
  171. " don't do anything if the cmd is not supported
  172. if s:check(a:cmd)
  173. let nm = expand("<afile>")
  174. " for gzip check current compression level and set b:gzip_comp_arg.
  175. silent! unlet b:gzip_comp_arg
  176. if a:cmd[0] == 'g'
  177. call s:set_compression(readfile(nm, "b", 1)[0])
  178. endif
  179. " Rename to a weird name to avoid the risk of overwriting another file
  180. let nmt = expand("<afile>:p:h") . "/X~=@l9q5"
  181. let nmte = nmt . "." . expand("<afile>:e")
  182. if rename(nm, nmte) == 0
  183. if &patchmode != "" && getfsize(nm . &patchmode) == -1
  184. " Create patchmode file by creating the decompressed file new
  185. call system(a:cmd . " -c -- " . s:escape(nmte) . " > " . s:escape(nmt))
  186. call rename(nmte, nm . &patchmode)
  187. else
  188. call system(a:cmd . " -- " . s:escape(nmte))
  189. endif
  190. call rename(nmt, nm)
  191. endif
  192. endif
  193. endfun
  194. " find a file name for the file to be compressed. Use "name" without an
  195. " extension if possible. Otherwise use a weird name to avoid overwriting an
  196. " existing file.
  197. fun s:tempname(name)
  198. let fn = fnamemodify(a:name, ":r")
  199. if !filereadable(fn) && !isdirectory(fn)
  200. return fn
  201. endif
  202. return fnamemodify(a:name, ":p:h") . "/X~=@l9q5"
  203. endfun
  204. fun s:escape(name)
  205. " shellescape() was added by patch 7.0.111
  206. if exists("*shellescape")
  207. return shellescape(a:name)
  208. endif
  209. return "'" . a:name . "'"
  210. endfun
  211. " vim: set sw=2 :