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