xref: /vim-8.2.3635/runtime/plugin/gzip.vim (revision 34cdc3e3)
1" Vim plugin for editing compressed files.
2" Maintainer: Bram Moolenaar <[email protected]>
3" Last Change: 2005 May 18
4
5" Exit quickly when:
6" - this plugin was already loaded
7" - when 'compatible' is set
8" - some autocommands are already taking care of compressed files
9if exists("loaded_gzip") || &cp || exists("#BufReadPre#*.gz")
10  finish
11endif
12let loaded_gzip = 1
13
14augroup gzip
15  " Remove all gzip autocommands
16  au!
17
18  " Enable editing of gzipped files
19  " set binary mode before reading the file
20  " use "gzip -d", gunzip isn't always available
21  autocmd BufReadPre,FileReadPre	*.gz,*.bz2,*.Z setlocal bin
22  autocmd BufReadPost,FileReadPost	*.gz  call s:read("gzip -dn")
23  autocmd BufReadPost,FileReadPost	*.bz2 call s:read("bzip2 -d")
24  autocmd BufReadPost,FileReadPost	*.Z   call s:read("uncompress")
25  autocmd BufWritePost,FileWritePost	*.gz  call s:write("gzip")
26  autocmd BufWritePost,FileWritePost	*.bz2 call s:write("bzip2")
27  autocmd BufWritePost,FileWritePost	*.Z   call s:write("compress -f")
28  autocmd FileAppendPre			*.gz  call s:appre("gzip -dn")
29  autocmd FileAppendPre			*.bz2 call s:appre("bzip2 -d")
30  autocmd FileAppendPre			*.Z   call s:appre("uncompress")
31  autocmd FileAppendPost		*.gz  call s:write("gzip")
32  autocmd FileAppendPost		*.bz2 call s:write("bzip2")
33  autocmd FileAppendPost		*.Z   call s:write("compress -f")
34augroup END
35
36" Function to check that executing "cmd [-f]" works.
37" The result is cached in s:have_"cmd" for speed.
38fun s:check(cmd)
39  let name = substitute(a:cmd, '\(\S*\).*', '\1', '')
40  if !exists("s:have_" . name)
41    let e = executable(name)
42    if e < 0
43      let r = system(name . " --version")
44      let e = (r !~ "not found" && r != "")
45    endif
46    exe "let s:have_" . name . "=" . e
47  endif
48  exe "return s:have_" . name
49endfun
50
51" Set b:gzip_comp_arg to the gzip argument to be used for compression, based on
52" the flags in the compressed file.
53" The only compression methods that can be detected are max speed (-1) and max
54" compression (-9).
55fun s:set_compression(line)
56  " get the Compression Method
57  let l:cm = char2nr(a:line[2])
58  " if it's 8 (DEFLATE), we can check for the compression level
59  if l:cm == 8
60    " get the eXtra FLags
61    let l:xfl = char2nr(a:line[8])
62    " max compression
63    if l:xfl == 2
64      let b:gzip_comp_arg = "-9"
65    " min compression
66    elseif l:xfl == 4
67      let b:gzip_comp_arg = "-1"
68    endif
69  endif
70endfun
71
72
73" After reading compressed file: Uncompress text in buffer with "cmd"
74fun s:read(cmd)
75  " don't do anything if the cmd is not supported
76  if !s:check(a:cmd)
77    return
78  endif
79
80  " for gzip check current compression level and set b:gzip_comp_arg.
81  silent! unlet b:gzip_comp_arg
82  if a:cmd[0] == 'g'
83    call s:set_compression(getline(1))
84  endif
85
86  " make 'patchmode' empty, we don't want a copy of the written file
87  let pm_save = &pm
88  set pm=
89  " remove 'a' and 'A' from 'cpo' to avoid the alternate file changes
90  let cpo_save = &cpo
91  set cpo-=a cpo-=A
92  " set 'modifiable'
93  let ma_save = &ma
94  setlocal ma
95  " when filtering the whole buffer, it will become empty
96  let empty = line("'[") == 1 && line("']") == line("$")
97  let tmp = tempname()
98  let tmpe = tmp . "." . expand("<afile>:e")
99  " write the just read lines to a temp file "'[,']w tmp.gz"
100  execute "silent '[,']w " . tmpe
101  " uncompress the temp file: call system("gzip -dn tmp.gz")
102  call system(a:cmd . " " . tmpe)
103  if !filereadable(tmp)
104    " uncompress didn't work!  Keep the compressed file then.
105    echoerr "Error: Could not read uncompressed file"
106    return
107  endif
108  " delete the compressed lines; remember the line number
109  let l = line("'[") - 1
110  if exists(":lockmarks")
111    lockmarks '[,']d _
112  else
113    '[,']d _
114  endif
115  " read in the uncompressed lines "'[-1r tmp"
116  setlocal nobin
117  if exists(":lockmarks")
118    execute "silent lockmarks " . l . "r " . tmp
119  else
120    execute "silent " . l . "r " . tmp
121  endif
122
123  " if buffer became empty, delete trailing blank line
124  if empty
125    silent $delete _
126    1
127  endif
128  " delete the temp file and the used buffers
129  call delete(tmp)
130  silent! exe "bwipe " . tmp
131  silent! exe "bwipe " . tmpe
132  let &pm = pm_save
133  let &cpo = cpo_save
134  let &l:ma = ma_save
135  " When uncompressed the whole buffer, do autocommands
136  if empty
137    if &verbose >= 8
138      execute "doau BufReadPost " . expand("%:r")
139    else
140      execute "silent! doau BufReadPost " . expand("%:r")
141    endif
142  endif
143endfun
144
145" After writing compressed file: Compress written file with "cmd"
146fun s:write(cmd)
147  " don't do anything if the cmd is not supported
148  if s:check(a:cmd)
149    " Rename the file before compressing it.
150    let nm = resolve(expand("<afile>"))
151    let nmt = s:tempname(nm)
152    if rename(nm, nmt) == 0
153      if exists("b:gzip_comp_arg")
154	call system(a:cmd . " " . b:gzip_comp_arg . " " . nmt)
155      else
156	call system(a:cmd . " " . nmt)
157      endif
158      call rename(nmt . "." . expand("<afile>:e"), nm)
159    endif
160  endif
161endfun
162
163" Before appending to compressed file: Uncompress file with "cmd"
164fun s:appre(cmd)
165  " don't do anything if the cmd is not supported
166  if s:check(a:cmd)
167    let nm = expand("<afile>")
168
169    " for gzip check current compression level and set b:gzip_comp_arg.
170    silent! unlet b:gzip_comp_arg
171    if a:cmd[0] == 'g'
172      call s:set_compression(readfile(nm, "b", 1)[0])
173    endif
174
175    " Rename to a weird name to avoid the risk of overwriting another file
176    let nmt = expand("<afile>:p:h") . "/X~=@l9q5"
177    let nmte = nmt . "." . expand("<afile>:e")
178    if rename(nm, nmte) == 0
179      if &patchmode != "" && getfsize(nm . &patchmode) == -1
180	" Create patchmode file by creating the decompressed file new
181	call system(a:cmd . " -c " . nmte . " > " . nmt)
182	call rename(nmte, nm . &patchmode)
183      else
184	call system(a:cmd . " " . nmte)
185      endif
186      call rename(nmt, nm)
187    endif
188  endif
189endfun
190
191" find a file name for the file to be compressed.  Use "name" without an
192" extension if possible.  Otherwise use a weird name to avoid overwriting an
193" existing file.
194fun s:tempname(name)
195  let fn = fnamemodify(a:name, ":r")
196  if !filereadable(fn) && !isdirectory(fn)
197    return fn
198  endif
199  return fnamemodify(a:name, ":p:h") . "/X~=@l9q5"
200endfun
201
202" vim: set sw=2 :
203