blob: 0eebc5cb316f9bd9ebf78b3a0c6da097b3602325 [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001" Vim plugin for editing compressed files.
2" Maintainer: Bram Moolenaar <Bram@vim.org>
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00003" Last Change: 2005 May 18
Bram Moolenaar071d4272004-06-13 20:20:40 +00004
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
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +000051" 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
Bram Moolenaar071d4272004-06-13 20:20:40 +000073" 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
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +000079
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
Bram Moolenaar071d4272004-06-13 20:20:40 +000086 " 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)
Bram Moolenaard4755bb2004-09-02 19:12:26 +0000103 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
Bram Moolenaar071d4272004-06-13 20:20:40 +0000108 " 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.
Bram Moolenaar2641f772005-03-25 21:58:17 +0000150 let nm = resolve(expand("<afile>"))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000151 let nmt = s:tempname(nm)
152 if rename(nm, nmt) == 0
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +0000153 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
Bram Moolenaar071d4272004-06-13 20:20:40 +0000158 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)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000167 let nm = expand("<afile>")
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +0000168
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
Bram Moolenaar071d4272004-06-13 20:20:40 +0000176 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 :