tar.vim (27073B)
1 " tar.vim: Handles browsing tarfiles - AUTOLOAD PORTION 2 " Date: Mar 01, 2025 3 " Version: 32b (with modifications from the Vim Project) 4 " Maintainer: This runtime file is looking for a new maintainer. 5 " Former Maintainer: Charles E Campbell 6 " License: Vim License (see vim's :help license) 7 " Last Change: 8 " 2024 Jan 08 by Vim Project: fix a few problems (#138331, #12637, #8109) 9 " 2024 Feb 19 by Vim Project: announce adoption 10 " 2024 Nov 11 by Vim Project: support permissions (#7379) 11 " 2025 Feb 06 by Vim Project: add support for lz4 (#16591) 12 " 2025 Feb 28 by Vim Project: add support for bzip3 (#16755) 13 " 2025 Mar 01 by Vim Project: fix syntax error in tar#Read() 14 " 2025 Mar 02 by Vim Project: escape the filename before using :read 15 " 2025 Mar 02 by Vim Project: determine the compression using readblob() 16 " instead of shelling out to file(1) 17 " 2025 Apr 16 by Vim Project: decouple from netrw by adding s:WinPath() 18 " 2025 May 19 by Vim Project: restore working directory after read/write 19 " 2025 Jul 13 by Vim Project: warn with path traversal attacks 20 " 2026 Feb 06 by Vim Project: consider 'nowrapscan' (#19333) 21 " 2026 Feb 07 by Vim Project: make the path traversal detection more robust (#19341) 22 " 23 " Contains many ideas from Michael Toren's <tar.vim> 24 " 25 " Copyright: Copyright (C) 2005-2017 Charles E. Campbell {{{1 26 " Permission is hereby granted to use and distribute this code, 27 " with or without modifications, provided that this copyright 28 " notice is copied with it. Like anything else that's free, 29 " tar.vim and tarPlugin.vim are provided *as is* and comes 30 " with no warranty of any kind, either expressed or implied. 31 " By using this plugin, you agree that in no event will the 32 " copyright holder be liable for any damages resulting from 33 " the use of this software. 34 " --------------------------------------------------------------------- 35 " Load Once: {{{1 36 if &cp || exists("g:loaded_tar") 37 finish 38 endif 39 let g:loaded_tar= "v32b" 40 if !has('nvim-0.12') && v:version < 900 41 echohl WarningMsg 42 echo "***warning*** this version of tar needs vim 9.0" 43 echohl Normal 44 finish 45 endif 46 let s:keepcpo= &cpo 47 set cpo&vim 48 49 " --------------------------------------------------------------------- 50 " Default Settings: {{{1 51 if !exists("g:tar_browseoptions") 52 let g:tar_browseoptions= "tf" 53 endif 54 if !exists("g:tar_readoptions") 55 let g:tar_readoptions= "pxf" 56 endif 57 if !exists("g:tar_cmd") 58 let g:tar_cmd= "tar" 59 endif 60 if !exists("g:tar_writeoptions") 61 let g:tar_writeoptions= "uf" 62 endif 63 if !exists("g:tar_delfile") 64 " Note: not supported on BSD 65 let g:tar_delfile="--delete -f" 66 endif 67 if !exists("g:netrw_cygwin") 68 if has("win32") || has("win95") || has("win64") || has("win16") 69 if &shell =~ '\%(\<bash\>\|\<zsh\>\)\%(\.exe\)\=$' 70 let g:netrw_cygwin= 1 71 else 72 let g:netrw_cygwin= 0 73 endif 74 else 75 let g:netrw_cygwin= 0 76 endif 77 endif 78 if !exists("g:tar_copycmd") 79 if !exists("g:netrw_localcopycmd") 80 if has("win32") || has("win95") || has("win64") || has("win16") 81 if g:netrw_cygwin 82 let g:netrw_localcopycmd= "cp" 83 else 84 let g:netrw_localcopycmd= "copy" 85 endif 86 elseif has("unix") || has("macunix") 87 let g:netrw_localcopycmd= "cp" 88 else 89 let g:netrw_localcopycmd= "" 90 endif 91 endif 92 let g:tar_copycmd= g:netrw_localcopycmd 93 endif 94 if !exists("g:tar_extractcmd") 95 let g:tar_extractcmd= "tar -pxf" 96 endif 97 98 " set up shell quoting character 99 if !exists("g:tar_shq") 100 if exists("+shq") && exists("&shq") && &shq != "" 101 let g:tar_shq= &shq 102 elseif has("win32") || has("win95") || has("win64") || has("win16") 103 if exists("g:netrw_cygwin") && g:netrw_cygwin 104 let g:tar_shq= "'" 105 else 106 let g:tar_shq= '"' 107 endif 108 else 109 let g:tar_shq= "'" 110 endif 111 endif 112 113 let g:tar_secure=' -- ' 114 let g:tar_leading_pat='\m^\%([.]\{,2\}/\)\+' 115 116 " ---------------- 117 " Functions: {{{1 118 " ---------------- 119 120 " --------------------------------------------------------------------- 121 " s:Msg: {{{2 122 fun! s:Msg(func, severity, msg) 123 redraw! 124 if a:severity =~? 'error' 125 echohl Error 126 else 127 echohl WarningMsg 128 endif 129 echo $"***{a:severity}*** ({a:func}) {a:msg}" 130 echohl None 131 endfunc 132 133 " --------------------------------------------------------------------- 134 " tar#Browse: {{{2 135 fun! tar#Browse(tarfile) 136 let repkeep= &report 137 set report=10 138 139 " sanity checks 140 if !executable(g:tar_cmd) 141 call s:Msg('tar#Browse', 'error', $"{g:tar_cmd} not available on your system") 142 let &report= repkeep 143 return 144 endif 145 if !filereadable(a:tarfile) 146 if a:tarfile !~# '^\a\+://' 147 " if it's an url, don't complain, let url-handlers such as vim do its thing 148 call s:Msg('tar#Browse', 'error', $"File not readable<{a:tarfile}>") 149 endif 150 let &report= repkeep 151 return 152 endif 153 if &ma != 1 154 set ma 155 endif 156 let b:tarfile= a:tarfile 157 158 setlocal noswapfile 159 setlocal buftype=nofile 160 setlocal bufhidden=hide 161 setlocal nobuflisted 162 setlocal nowrap 163 set ft=tar 164 165 " give header 166 let lastline= line("$") 167 call setline(lastline+1,'" tar.vim version '.g:loaded_tar) 168 call setline(lastline+2,'" Browsing tarfile '.a:tarfile) 169 call setline(lastline+3,'" Select a file with cursor and press ENTER, "x" to extract a file') 170 keepj $put ='' 171 keepj sil! 0d 172 keepj $ 173 174 let tarfile= a:tarfile 175 if has("win32unix") && executable("cygpath") 176 " assuming cygwin 177 let tarfile=substitute(system("cygpath -u ".shellescape(tarfile,0)),'\n$','','e') 178 endif 179 let curlast= line("$") 180 181 if tarfile =~# '\.\(gz\)$' 182 exe "sil! r! gzip -d -c -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_browseoptions." - " 183 184 elseif tarfile =~# '\.\(tgz\)$' || tarfile =~# '\.\(tbz\)$' || tarfile =~# '\.\(txz\)$' || 185 \ tarfile =~# '\.\(tzst\)$' || tarfile =~# '\.\(tlz4\)$' 186 let header= s:Header(tarfile) 187 188 if header =~? 'bzip2' 189 exe "sil! r! bzip2 -d -c -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_browseoptions." - " 190 elseif header =~? 'bzip3' 191 exe "sil! r! bzip3 -d -c -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_browseoptions." - " 192 elseif header =~? 'xz' 193 exe "sil! r! xz -d -c -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_browseoptions." - " 194 elseif header =~? 'zstd' 195 exe "sil! r! zstd --decompress --stdout -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_browseoptions." - " 196 elseif header =~? 'lz4' 197 exe "sil! r! lz4 --decompress --stdout -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_browseoptions." - " 198 elseif header =~? 'gzip' 199 exe "sil! r! gzip -d -c -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_browseoptions." - " 200 endif 201 202 elseif tarfile =~# '\.lrp' 203 exe "sil! r! cat -- ".shellescape(tarfile,1)."|gzip -d -c -|".g:tar_cmd." -".g:tar_browseoptions." - " 204 elseif tarfile =~# '\.\(bz2\|tbz\|tb2\)$' 205 exe "sil! r! bzip2 -d -c -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_browseoptions." - " 206 elseif tarfile =~# '\.\(bz3\|tb3\)$' 207 exe "sil! r! bzip3 -d -c -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_browseoptions." - " 208 elseif tarfile =~# '\.\(lzma\|tlz\)$' 209 exe "sil! r! lzma -d -c -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_browseoptions." - " 210 elseif tarfile =~# '\.\(xz\|txz\)$' 211 exe "sil! r! xz --decompress --stdout -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_browseoptions." - " 212 elseif tarfile =~# '\.\(zst\|tzst\)$' 213 exe "sil! r! zstd --decompress --stdout -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_browseoptions." - " 214 elseif tarfile =~# '\.\(lz4\|tlz4\)$' 215 exe "sil! r! lz4 --decompress --stdout -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_browseoptions." - " 216 else 217 if tarfile =~ '^\s*-' 218 " A file name starting with a dash is taken as an option. Prepend ./ to avoid that. 219 let tarfile = substitute(tarfile, '-', './-', '') 220 endif 221 exe "sil! r! ".g:tar_cmd." -".g:tar_browseoptions." ".shellescape(tarfile,1) 222 endif 223 if v:shell_error != 0 224 call s:Msg('tar#Browse', 'warning', $"please check your g:tar_browseoptions '<{g:tar_browseoptions}>'") 225 return 226 endif 227 228 " remove tar: Removing leading '/' from member names 229 " Note: the message could be localized 230 if search('\m^g\?tar: ', 'w') > 0 || search(g:tar_leading_pat, 'w') > 0 231 call append(3,'" Note: Path Traversal Attack detected!') 232 let b:leading_slash = 1 233 " remove the message output 234 sil g/^tar: /d 235 endif 236 237 " set up maps supported for tar 238 setlocal noma nomod ro 239 noremap <silent> <buffer> <cr> :call <SID>TarBrowseSelect()<cr> 240 noremap <silent> <buffer> x :call tar#Extract()<cr> 241 if &mouse != "" 242 noremap <silent> <buffer> <leftmouse> <leftmouse>:call <SID>TarBrowseSelect()<cr> 243 endif 244 245 let &report= repkeep 246 endfun 247 248 " --------------------------------------------------------------------- 249 " TarBrowseSelect: {{{2 250 fun! s:TarBrowseSelect() 251 let repkeep= &report 252 set report=10 253 let fname= getline(".") 254 let ls= get(b:, 'leading_slash', 0) 255 256 " sanity check 257 if fname =~ '^"' 258 let &report= repkeep 259 return 260 endif 261 262 " about to make a new window, need to use b:tarfile 263 let tarfile= b:tarfile 264 let curfile= expand("%") 265 if has("win32unix") && executable("cygpath") 266 " assuming cygwin 267 let tarfile=substitute(system("cygpath -u ".shellescape(tarfile,0)),'\n$','','e') 268 endif 269 270 " open a new window (tar#Read will read a file into it) 271 noswapfile new 272 if !exists("g:tar_nomax") || g:tar_nomax == 0 273 wincmd _ 274 endif 275 let s:tblfile_{winnr()}= curfile 276 let b:leading_slash= ls 277 call tar#Read("tarfile:".tarfile.'::'.fname) 278 filetype detect 279 set nomod 280 exe 'com! -buffer -nargs=? -complete=file TarDiff :call tar#Diff(<q-args>,"'.fnameescape(fname).'")' 281 282 let &report= repkeep 283 endfun 284 285 " --------------------------------------------------------------------- 286 " tar#Read: {{{2 287 fun! tar#Read(fname) 288 let repkeep= &report 289 set report=10 290 let tarfile = substitute(a:fname,'tarfile:\(.\{-}\)::.*$','\1','') 291 let fname = substitute(a:fname,'tarfile:.\{-}::\(.*\)$','\1','') 292 " be careful not to execute special crafted files 293 let escape_file = fname->substitute(g:tar_leading_pat, '', '')->fnameescape() 294 295 let curdir= getcwd() 296 let b:curdir= curdir 297 let tmpdir= tempname() 298 let b:tmpdir= tmpdir 299 if tmpdir =~ '\.' 300 let tmpdir= substitute(tmpdir,'\.[^.]*$','','e') 301 endif 302 call mkdir(tmpdir,"p") 303 304 " attempt to change to the indicated directory 305 try 306 exe "lcd ".fnameescape(tmpdir) 307 catch /^Vim\%((\a\+)\)\=:E344/ 308 call s:Msg('tar#Read', 'error', "cannot lcd to temporary directory") 309 let &report= repkeep 310 return 311 endtry 312 313 " place temporary files under .../_ZIPVIM_/ 314 if isdirectory("_ZIPVIM_") 315 call s:Rmdir("_ZIPVIM_") 316 endif 317 call mkdir("_ZIPVIM_") 318 lcd _ZIPVIM_ 319 320 if has("win32unix") && executable("cygpath") 321 " assuming cygwin 322 let tarfile=substitute(system("cygpath -u ".shellescape(tarfile,0)),'\n$','','e') 323 endif 324 325 if fname =~ '\.bz2$' && executable("bzcat") 326 let decmp= "|bzcat" 327 let doro = 1 328 elseif fname =~ '\.bz3$' && executable("bz3cat") 329 let decmp= "|bz3cat" 330 let doro = 1 331 elseif fname =~ '\.t\=gz$' && executable("zcat") 332 let decmp= "|zcat" 333 let doro = 1 334 elseif fname =~ '\.lzma$' && executable("lzcat") 335 let decmp= "|lzcat" 336 let doro = 1 337 elseif fname =~ '\.xz$' && executable("xzcat") 338 let decmp= "|xzcat" 339 let doro = 1 340 elseif fname =~ '\.zst$' && executable("zstdcat") 341 let decmp= "|zstdcat" 342 let doro = 1 343 elseif fname =~ '\.lz4$' && executable("lz4cat") 344 let decmp= "|lz4cat" 345 let doro = 1 346 else 347 let decmp="" 348 let doro = 0 349 if fname =~ '\.bz2$\|\.bz3$\|\.gz$\|\.lzma$\|\.xz$\|\.zip$\|\.Z$' 350 setlocal bin 351 endif 352 endif 353 354 355 if tarfile =~# '\.bz2$' 356 exe "sil! r! bzip2 -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp 357 exe "read ".escape_file 358 elseif tarfile =~# '\.bz3$' 359 exe "sil! r! bzip3 -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp 360 exe "read ".escape_file 361 elseif tarfile =~# '\.\(gz\)$' 362 exe "sil! r! gzip -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp 363 exe "read ".escape_file 364 elseif tarfile =~# '\(\.tgz\|\.tbz\|\.txz\)' 365 let filekind= s:Header(tarfile) 366 if filekind =~? "bzip2" 367 exe "sil! r! bzip2 -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp 368 exe "read ".escape_file 369 elseif filekind =~ "bzip3" 370 exe "sil! r! bzip3 -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp 371 exe "read ".escape_file 372 elseif filekind =~? "xz" 373 exe "sil! r! xz -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp 374 exe "read ".escape_file 375 elseif filekind =~? "zstd" 376 exe "sil! r! zstd --decompress --stdout -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp 377 exe "read ".escape_file 378 elseif filekind =~? "gzip" 379 exe "sil! r! gzip -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp 380 exe "read ".escape_file 381 endif 382 383 elseif tarfile =~# '\.lrp$' 384 exe "sil! r! cat -- ".shellescape(tarfile,1)." | gzip -d -c - | ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp 385 exe "read ".escape_file 386 elseif tarfile =~# '\.lzma$' 387 exe "sil! r! lzma -d -c -- ".shellescape(tarfile,1)."| ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp 388 exe "read ".escape_file 389 elseif tarfile =~# '\.\(xz\|txz\)$' 390 exe "sil! r! xz --decompress --stdout -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp 391 exe "read ".escape_file 392 elseif tarfile =~# '\.\(lz4\|tlz4\)$' 393 exe "sil! r! lz4 --decompress --stdout -- ".shellescape(tarfile,1)." | ".g:tar_cmd." -".g:tar_readoptions." - ".g:tar_secure.shellescape(fname,1).decmp 394 exe "read ".escape_file 395 else 396 if tarfile =~ '^\s*-' 397 " A file name starting with a dash is taken as an option. Prepend ./ to avoid that. 398 let tarfile = substitute(tarfile, '-', './-', '') 399 endif 400 exe "silent r! ".g:tar_cmd." -".g:tar_readoptions.shellescape(tarfile,1)." ".g:tar_secure.shellescape(fname,1).decmp 401 exe "read ".escape_file 402 endif 403 if get(b:, 'leading_slash', 0) 404 sil g/^tar: /d 405 endif 406 407 redraw! 408 409 if v:shell_error != 0 410 lcd .. 411 call s:Rmdir("_ZIPVIM_") 412 exe "lcd ".fnameescape(curdir) 413 call s:Msg('tar#Read', 'error', $"sorry, unable to open or extract {tarfile} with {fname}") 414 endif 415 416 if doro 417 " because the reverse process of compressing changed files back into the tarball is not currently supported 418 setlocal ro 419 endif 420 421 let b:tarfile= a:fname 422 423 " cleanup 424 keepj sil! 0d 425 set nomod 426 427 let &report= repkeep 428 exe "lcd ".fnameescape(curdir) 429 silent exe "file tarfile::". fname->fnameescape() 430 endfun 431 432 " --------------------------------------------------------------------- 433 " tar#Write: {{{2 434 fun! tar#Write(fname) 435 let pwdkeep= getcwd() 436 let repkeep= &report 437 set report=10 438 let curdir= b:curdir 439 let tmpdir= b:tmpdir 440 441 " sanity checks 442 if !executable(g:tar_cmd) 443 redraw! 444 let &report= repkeep 445 return 446 endif 447 let tarfile = substitute(b:tarfile,'tarfile:\(.\{-}\)::.*$','\1','') 448 let fname = substitute(b:tarfile,'tarfile:.\{-}::\(.*\)$','\1','') 449 450 if get(b:, 'leading_slash', 0) 451 call s:Msg('tar#Write', 'error', $"sorry, not attempting to update {tarfile} with {fname}") 452 let &report= repkeep 453 return 454 endif 455 456 if !isdirectory(fnameescape(tmpdir)) 457 call mkdir(fnameescape(tmpdir), 'p') 458 endif 459 exe $"lcd {fnameescape(tmpdir)}" 460 if isdirectory("_ZIPVIM_") 461 call s:Rmdir("_ZIPVIM_") 462 endif 463 call mkdir("_ZIPVIM_") 464 lcd _ZIPVIM_ 465 let dir = fnamemodify(fname, ':p:h') 466 if dir !~# '_ZIPVIM_$' 467 call mkdir(dir) 468 endif 469 470 " handle compressed archives 471 if tarfile =~# '\.bz2' 472 call system("bzip2 -d -- ".shellescape(tarfile,0)) 473 let tarfile = substitute(tarfile,'\.bz2','','e') 474 let compress= "bzip2 -- ".shellescape(tarfile,0) 475 elseif tarfile =~# '\.bz3' 476 call system("bzip3 -d -- ".shellescape(tarfile,0)) 477 let tarfile = substitute(tarfile,'\.bz3','','e') 478 let compress= "bzip3 -- ".shellescape(tarfile,0) 479 elseif tarfile =~# '\.gz' 480 call system("gzip -d -- ".shellescape(tarfile,0)) 481 let tarfile = substitute(tarfile,'\.gz','','e') 482 let compress= "gzip -- ".shellescape(tarfile,0) 483 elseif tarfile =~# '\.tgz' 484 call system("gzip -d -- ".shellescape(tarfile,0)) 485 let tarfile = substitute(tarfile,'\.tgz','.tar','e') 486 let compress= "gzip -- ".shellescape(tarfile,0) 487 let tgz = 1 488 elseif tarfile =~# '\.xz' 489 call system("xz -d -- ".shellescape(tarfile,0)) 490 let tarfile = substitute(tarfile,'\.xz','','e') 491 let compress= "xz -- ".shellescape(tarfile,0) 492 elseif tarfile =~# '\.zst' 493 call system("zstd --decompress --rm -- ".shellescape(tarfile,0)) 494 let tarfile = substitute(tarfile,'\.zst','','e') 495 let compress= "zstd --rm -- ".shellescape(tarfile,0) 496 elseif tarfile =~# '\.lz4' 497 call system("lz4 --decompress --rm -- ".shellescape(tarfile,0)) 498 let tarfile = substitute(tarfile,'\.lz4','','e') 499 let compress= "lz4 --rm -- ".shellescape(tarfile,0) 500 elseif tarfile =~# '\.lzma' 501 call system("lzma -d -- ".shellescape(tarfile,0)) 502 let tarfile = substitute(tarfile,'\.lzma','','e') 503 let compress= "lzma -- ".shellescape(tarfile,0) 504 endif 505 " Note: no support for name.tar.tbz/.txz/.tgz/.tlz4/.tzst 506 507 if v:shell_error != 0 508 call s:Msg('tar#Write', 'error', $"sorry, unable to update {tarfile} with {fname}") 509 else 510 511 if fname =~ '/' 512 let dirpath = substitute(fname,'/[^/]\+$','','e') 513 if has("win32unix") && executable("cygpath") 514 let dirpath = substitute(system("cygpath ".shellescape(dirpath, 0)),'\n','','e') 515 endif 516 call mkdir(dirpath,"p") 517 endif 518 if tarfile !~ '/' 519 let tarfile= curdir.'/'.tarfile 520 endif 521 if tarfile =~ '^\s*-' 522 " A file name starting with a dash may be taken as an option. Prepend ./ to avoid that. 523 let tarfile = substitute(tarfile, '-', './-', '') 524 endif 525 526 " don't overwrite a file forcefully 527 exe "w ".fnameescape(fname) 528 if has("win32unix") && executable("cygpath") 529 let tarfile = substitute(system("cygpath ".shellescape(tarfile,0)),'\n','','e') 530 endif 531 532 " delete old file from tarfile 533 " Note: BSD tar does not support --delete flag 534 call system(g:tar_cmd." ".g:tar_delfile." ".shellescape(tarfile,0).g:tar_secure.shellescape(fname,0)) 535 if v:shell_error != 0 536 call s:Msg('tar#Write', 'error', $"sorry, unable to update {fnameescape(tarfile)} with {fnameescape(fname)} --delete not supported?") 537 else 538 " update tarfile with new file 539 call system(g:tar_cmd." -".g:tar_writeoptions." ".shellescape(tarfile,0).g:tar_secure.shellescape(fname,0)) 540 if v:shell_error != 0 541 call s:Msg('tar#Write', 'error', $"sorry, unable to update {fnameescape(tarfile)} with {fnameescape(fname)}") 542 elseif exists("compress") 543 call system(compress) 544 if exists("tgz") 545 call rename(tarfile.".gz",substitute(tarfile,'\.tar$','.tgz','e')) 546 endif 547 endif 548 endif 549 550 " support writing tarfiles across a network 551 if s:tblfile_{winnr()} =~ '^\a\+://' 552 let tblfile= s:tblfile_{winnr()} 553 1split|noswapfile enew 554 let binkeep= &l:binary 555 let eikeep = &ei 556 set binary ei=all 557 exe "noswapfile e! ".fnameescape(tarfile) 558 call netrw#NetWrite(tblfile) 559 let &ei = eikeep 560 let &l:binary = binkeep 561 q! 562 unlet s:tblfile_{winnr()} 563 endif 564 endif 565 566 " cleanup and restore current directory 567 lcd .. 568 call s:Rmdir("_ZIPVIM_") 569 exe "lcd ".fnameescape(pwdkeep) 570 setlocal nomod 571 572 let &report= repkeep 573 endfun 574 575 " --------------------------------------------------------------------- 576 " tar#Diff: {{{2 577 fun! tar#Diff(userfname,fname) 578 let fname= a:fname 579 if a:userfname != "" 580 let fname= a:userfname 581 endif 582 exe "lcd ".fnameescape(b:tmpdir). '/_ZIPVIM_' 583 if filereadable(fname) 584 " sets current file (from tarball) for diff'ing 585 " splits window vertically 586 " opens original file, sets it for diff'ing 587 " sets up b:tardiff_otherbuf variables so each buffer knows about the other (for closing purposes) 588 diffthis 589 wincmd v 590 exe "noswapfile e ".fnameescape(fname) 591 diffthis 592 else 593 redraw! 594 echo "***warning*** unable to read file<".fname.">" 595 endif 596 endfun 597 598 " --------------------------------------------------------------------- 599 " tar#Extract: extract a file from a (possibly compressed) tar archive {{{2 600 fun! tar#Extract() 601 602 let repkeep= &report 603 set report=10 604 let fname= getline(".") 605 606 " sanity check 607 if fname =~ '^"' 608 let &report= repkeep 609 return 610 endif 611 612 let tarball = expand("%") 613 let tarbase = substitute(tarball,'\..*$','','') 614 615 let extractcmd= s:WinPath(g:tar_extractcmd) 616 if filereadable(tarbase.".tar") 617 call system(extractcmd." ".shellescape(tarbase).".tar ".shellescape(fname)) 618 if v:shell_error != 0 619 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar {fname}: failed!") 620 else 621 echo "***note*** successfully extracted ". fname 622 endif 623 624 elseif filereadable(tarbase.".tgz") 625 let extractcmd= substitute(extractcmd,"-","-z","") 626 call system(extractcmd." ".shellescape(tarbase).".tgz ".shellescape(fname)) 627 if v:shell_error != 0 628 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tgz {fname}: failed!") 629 else 630 echo "***note*** successfully extracted ".fname 631 endif 632 633 elseif filereadable(tarbase.".tar.gz") 634 let extractcmd= substitute(extractcmd,"-","-z","") 635 call system(extractcmd." ".shellescape(tarbase).".tar.gz ".shellescape(fname)) 636 if v:shell_error != 0 637 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.gz {fname}: failed!") 638 else 639 echo "***note*** successfully extracted ".fname 640 endif 641 642 elseif filereadable(tarbase.".tbz") 643 let extractcmd= substitute(extractcmd,"-","-j","") 644 call system(extractcmd." ".shellescape(tarbase).".tbz ".shellescape(fname)) 645 if v:shell_error != 0 646 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tbz {fname}: failed!") 647 else 648 echo "***note*** successfully extracted ".fname 649 endif 650 651 elseif filereadable(tarbase.".tar.bz2") 652 let extractcmd= substitute(extractcmd,"-","-j","") 653 call system(extractcmd." ".shellescape(tarbase).".tar.bz2 ".shellescape(fname)) 654 if v:shell_error != 0 655 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.bz2 {fname}: failed!") 656 else 657 echo "***note*** successfully extracted ".fname 658 endif 659 660 elseif filereadable(tarbase.".tar.bz3") 661 let extractcmd= substitute(extractcmd,"-","-j","") 662 call system(extractcmd." ".shellescape(tarbase).".tar.bz3 ".shellescape(fname)) 663 if v:shell_error != 0 664 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.bz3 {fname}: failed!") 665 else 666 echo "***note*** successfully extracted ".fname 667 endif 668 669 elseif filereadable(tarbase.".txz") 670 let extractcmd= substitute(extractcmd,"-","-J","") 671 call system(extractcmd." ".shellescape(tarbase).".txz ".shellescape(fname)) 672 if v:shell_error != 0 673 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.txz {fname}: failed!") 674 else 675 echo "***note*** successfully extracted ".fname 676 endif 677 678 elseif filereadable(tarbase.".tar.xz") 679 let extractcmd= substitute(extractcmd,"-","-J","") 680 call system(extractcmd." ".shellescape(tarbase).".tar.xz ".shellescape(fname)) 681 if v:shell_error != 0 682 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.xz {fname}: failed!") 683 else 684 echo "***note*** successfully extracted ".fname 685 endif 686 687 elseif filereadable(tarbase.".tzst") 688 let extractcmd= substitute(extractcmd,"-","--zstd","") 689 call system(extractcmd." ".shellescape(tarbase).".tzst ".shellescape(fname)) 690 if v:shell_error != 0 691 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tzst {fname}: failed!") 692 else 693 echo "***note*** successfully extracted ".fname 694 endif 695 696 elseif filereadable(tarbase.".tar.zst") 697 let extractcmd= substitute(extractcmd,"-","--zstd","") 698 call system(extractcmd." ".shellescape(tarbase).".tar.zst ".shellescape(fname)) 699 if v:shell_error != 0 700 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.zst {fname}: failed!") 701 else 702 echo "***note*** successfully extracted ".fname 703 endif 704 705 elseif filereadable(tarbase.".tlz4") 706 let extractcmd= substitute(extractcmd,"-","-I lz4","") 707 call system(extractcmd." ".shellescape(tarbase).".tlz4 ".shellescape(fname)) 708 if v:shell_error != 0 709 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tlz4 {fname}: failed!") 710 else 711 echo "***note*** successfully extracted ".fname 712 endif 713 714 elseif filereadable(tarbase.".tar.lz4") 715 let extractcmd= substitute(extractcmd,"-","-I lz4","") 716 call system(extractcmd." ".shellescape(tarbase).".tar.lz4".shellescape(fname)) 717 if v:shell_error != 0 718 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.lz4 {fname}: failed!") 719 else 720 echo "***note*** successfully extracted ".fname 721 endif 722 endif 723 724 " restore option 725 let &report= repkeep 726 endfun 727 728 " --------------------------------------------------------------------- 729 " s:Rmdir: {{{2 730 fun! s:Rmdir(fname) 731 call delete(a:fname, 'rf') 732 endfun 733 734 " s:FileHeader: {{{2 735 fun! s:Header(fname) 736 let header= readblob(a:fname, 0, 6) 737 " Nvim: see https://github.com/neovim/neovim/pull/34968 738 if header[0:2] == 0z425A68 " bzip2 header 739 return "bzip2" 740 elseif header[0:2] == 0z425A33 " bzip3 header 741 return "bzip3" 742 elseif header == 0zFD377A58.5A00 " xz header 743 return "xz" 744 elseif header[0:3] == 0z28B52FFD " zstd header 745 return "zstd" 746 elseif header[0:3] == 0z04224D18 " lz4 header 747 return "lz4" 748 elseif (header[0:1] == 0z1F9D || 749 \ header[0:1] == 0z1F8B || 750 \ header[0:1] == 0z1F9E || 751 \ header[0:1] == 0z1FA0 || 752 \ header[0:1] == 0z1F1E) 753 return "gzip" 754 endif 755 return "unknown" 756 endfun 757 758 " --------------------------------------------------------------------- 759 " s:WinPath: {{{2 760 fun! s:WinPath(path) 761 if (!g:netrw_cygwin || &shell !~ '\%(\<bash\>\|\<zsh\>\)\%(\.exe\)\=$') && has("win32") 762 " remove cygdrive prefix, if present 763 let path = substitute(a:path, '/cygdrive/\(.\)', '\1:', '') 764 " remove trailing slash (Win95) 765 let path = substitute(path, '\(\\\|/\)$', '', 'g') 766 " remove escaped spaces 767 let path = substitute(path, '\ ', ' ', 'g') 768 " convert slashes to backslashes 769 let path = substitute(path, '/', '\', 'g') 770 else 771 let path = a:path 772 endif 773 774 return path 775 endfun 776 777 " ===================================================================== 778 " Modelines And Restoration: {{{1 779 let &cpo= s:keepcpo 780 unlet s:keepcpo 781 " vim:ts=8 fdm=marker