1" Vim autoload file for editing compressed files.
2" Maintainer: Bram Moolenaar <Bram@vim.org>
3" Last Change: 2008 Jul 04
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  " Reset 'foldenable', otherwise line numbers get adjusted.
67  if has("folding")
68    let fen_save = &fen
69    setlocal nofen
70  endif
71
72  " when filtering the whole buffer, it will become empty
73  let empty = line("'[") == 1 && line("']") == line("$")
74  let tmp = tempname()
75  let tmpe = tmp . "." . expand("<afile>:e")
76  if exists('*fnameescape')
77    let tmp_esc = fnameescape(tmp)
78    let tmpe_esc = fnameescape(tmpe)
79  else
80    let tmp_esc = escape(tmp, ' ')
81    let tmpe_esc = escape(tmpe, ' ')
82  endif
83  " write the just read lines to a temp file "'[,']w tmp.gz"
84  execute "silent '[,']w " . tmpe_esc
85  " uncompress the temp file: call system("gzip -dn tmp.gz")
86  call system(a:cmd . " " . s:escape(tmpe))
87  if !filereadable(tmp)
88    " uncompress didn't work!  Keep the compressed file then.
89    echoerr "Error: Could not read uncompressed file"
90    let ok = 0
91  else
92    let ok = 1
93    " delete the compressed lines; remember the line number
94    let l = line("'[") - 1
95    if exists(":lockmarks")
96      lockmarks '[,']d _
97    else
98      '[,']d _
99    endif
100    " read in the uncompressed lines "'[-1r tmp"
101    " Use ++edit if the buffer was empty, keep the 'ff' and 'fenc' options.
102    setlocal nobin
103    if exists(":lockmarks")
104      if empty
105	execute "silent lockmarks " . l . "r ++edit " . tmp_esc
106      else
107	execute "silent lockmarks " . l . "r " . tmp_esc
108      endif
109    else
110      execute "silent " . l . "r " . tmp_esc
111    endif
112
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
124  " Restore saved option values.
125  let &pm = pm_save
126  let &cpo = cpo_save
127  let &l:ma = ma_save
128  if has("folding")
129    let &l:fen = fen_save
130  endif
131
132  " When uncompressed the whole buffer, do autocommands
133  if ok && empty
134    if exists('*fnameescape')
135      let fname = fnameescape(expand("%:r"))
136    else
137      let fname = escape(expand("%:r"), " \t\n*?[{`$\\%#'\"|!<")
138    endif
139    if &verbose >= 8
140      execute "doau BufReadPost " . fname
141    else
142      execute "silent! doau BufReadPost " . fname
143    endif
144  endif
145endfun
146
147" After writing compressed file: Compress written file with "cmd"
148fun gzip#write(cmd)
149  " don't do anything if the cmd is not supported
150  if s:check(a:cmd)
151    " Rename the file before compressing it.
152    let nm = resolve(expand("<afile>"))
153    let nmt = s:tempname(nm)
154    if rename(nm, nmt) == 0
155      if exists("b:gzip_comp_arg")
156	call system(a:cmd . " " . b:gzip_comp_arg . " -- " . s:escape(nmt))
157      else
158	call system(a:cmd . " -- " . s:escape(nmt))
159      endif
160      call rename(nmt . "." . expand("<afile>:e"), nm)
161    endif
162  endif
163endfun
164
165" Before appending to compressed file: Uncompress file with "cmd"
166fun gzip#appre(cmd)
167  " don't do anything if the cmd is not supported
168  if s:check(a:cmd)
169    let nm = expand("<afile>")
170
171    " for gzip check current compression level and set b:gzip_comp_arg.
172    silent! unlet b:gzip_comp_arg
173    if a:cmd[0] == 'g'
174      call s:set_compression(readfile(nm, "b", 1)[0])
175    endif
176
177    " Rename to a weird name to avoid the risk of overwriting another file
178    let nmt = expand("<afile>:p:h") . "/X~=@l9q5"
179    let nmte = nmt . "." . expand("<afile>:e")
180    if rename(nm, nmte) == 0
181      if &patchmode != "" && getfsize(nm . &patchmode) == -1
182	" Create patchmode file by creating the decompressed file new
183	call system(a:cmd . " -c -- " . s:escape(nmte) . " > " . s:escape(nmt))
184	call rename(nmte, nm . &patchmode)
185      else
186	call system(a:cmd . " -- " . s:escape(nmte))
187      endif
188      call rename(nmt, nm)
189    endif
190  endif
191endfun
192
193" find a file name for the file to be compressed.  Use "name" without an
194" extension if possible.  Otherwise use a weird name to avoid overwriting an
195" existing file.
196fun s:tempname(name)
197  let fn = fnamemodify(a:name, ":r")
198  if !filereadable(fn) && !isdirectory(fn)
199    return fn
200  endif
201  return fnamemodify(a:name, ":p:h") . "/X~=@l9q5"
202endfun
203
204fun 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 . "'"
210endfun
211
212" vim: set sw=2 :
213