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