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