diff options
-rw-r--r-- | README.md | 6 | ||||
-rw-r--r-- | autoload/shebang_create_exec.vim | 87 | ||||
-rw-r--r-- | doc/shebang_create_exec.txt | 12 | ||||
-rw-r--r-- | plugin/shebang_create_exec.vim | 6 |
4 files changed, 89 insertions, 22 deletions
@@ -2,9 +2,9 @@ shebang\_create\_exec.vim ========================= This plugin sets up a `BufWritePre` hook to check whether a buffer contains a -Unix shebang like `#!/bin/sh` as the first line, and is being saved to a new -file; if so, it attempts to make the file executable with `chmod +x` after a -successful save. +Unix shebang with an absolute pathname like `#!/bin/sh` as the first line, and +is being saved to a new file. If so, it attempts to make the file executable +using the safest method available to Vim. License ------- diff --git a/autoload/shebang_create_exec.vim b/autoload/shebang_create_exec.vim index b01cf58..1ce68e3 100644 --- a/autoload/shebang_create_exec.vim +++ b/autoload/shebang_create_exec.vim @@ -1,14 +1,83 @@ -" If the buffer starts with a shebang and the file being saved to doesn't -" exist yet, set up a hook to make it executable after the write is done -function! shebang_create_exec#Check(filename) abort - if stridx(getline(1), '#!') == 0 && !filereadable(a:filename) - autocmd shebang_create_exec BufWritePost <buffer> - \ call shebang_create_exec#Chmod(expand('<afile>:p')) +" Pattern to detect real-looking shebang to an absolute path +let s:shebang = '^#!\s*/[^/]\+' + +" If the buffer looks shebanged, and the file being saved to doesn't exist +" yet, set up a hook to make it executable after the write is done done +function! shebang_create_exec#(filename) abort + + " If the first line isn't a shebang, or the file exists, do nothing + if getline(1) !~# s:shebang || filereadable(a:filename) + return endif + + " Set a buffer variable to the target filename + let b:shebang_create_exec_filename = a:filename + + " Set up a hook to run and clean up after the write + autocmd shebang_create_exec BufWritePost <buffer> + \ call s:Run(expand('<afile>:p')) + endfunction -" Make the file executable and clear away the hook that called us -function! shebang_create_exec#Chmod(filename) abort +" Clear away the hook that called us and make the file executable +function! s:Run(filename) abort + + " Clear away the hook that called us autocmd! shebang_create_exec BufWritePost <buffer> - call system('chmod +x '.shellescape(a:filename)) + + " Check that the save filename was set by BufWritePre + if !exists('b:shebang_create_exec_filename') + return + endif + + " Check that it matches the file we just saved, and if so, make that file + " executable + if a:filename ==# b:shebang_create_exec_filename + call s:MakeExecutable(b:shebang_create_exec_filename) + endif + + " Clear away the save filename, even if we didn't change any permissions + unlet b:shebang_create_exec_filename + +endfunction + +" Make a given filename executable +function! s:MakeExecutable(filename) abort + + " Get filename into local variable + let filename = a:filename + + " How we do this depends on whether we have native file permissions + " functions (Vim >=8.0) + if exists('*setfperm') + + " We have setfperm(), so we can make the file executable without a fork to + " chmod(1), which should be quick and safe + let cperm = getfperm(filename) + + " Replace every third character of the permissions string with an 'x' + let nperm + \ = strpart(cperm, 0, 2).'x' + \ . strpart(cperm, 3, 2).'x' + \ . strpart(cperm, 6, 2).'x' + + " If our new permissions string differs from the current one, apply it to + " the file + if nperm !=# cperm + call setfperm(filename, nperm) + endif + + else + + " We'll need to fork to chmod(1); escape the filename safely, using + " shellescape() if we've got it + let l:filename_escaped = exists('*shellescape') + \ ? shellescape(filename) + \ : '''' . escape(filename, '''') . '''' + + " Try to make the file executable with a fork to chmod(1) + call system('chmod +x '.filename_escaped) + + endif + endfunction diff --git a/doc/shebang_create_exec.txt b/doc/shebang_create_exec.txt index d5995d4..c43d204 100644 --- a/doc/shebang_create_exec.txt +++ b/doc/shebang_create_exec.txt @@ -1,17 +1,15 @@ -*shebang_create_exec.txt* For Vim version 7.1 Last change: 2018 July 17 +*shebang_create_exec.txt* For Vim version 7.0 Last change: 2018 May 28 DESCRIPTION *shebang_create_exec* This plugin sets up a |BufWritePre| hook to check whether a buffer contains a -Unix shebang like `#!/bin/sh` as the first line, and is being saved to a new -file; if so, it attempts to make the file executable with `chmod +x` after a -successful save. +Unix shebang with an absolute pathname like `#!/bin/sh` as the first line, and +is being saved to a new file. If so, it attempts to make the file executable +using the safest method available to Vim. REQUIREMENTS *shebang_create_exec-requirements* -This plugin only loads if 'compatible' is not set, and requires the |+autocmd| -and |+unix| features. It also requires the |shellescape()| function that was -added in |version-7.0| patch 111. +This plugin only loads if 'compatible' is not set, and requires |+unix|. AUTHOR *shebang_create_exec-author* diff --git a/plugin/shebang_create_exec.vim b/plugin/shebang_create_exec.vim index 1af3fe9..c93628a 100644 --- a/plugin/shebang_create_exec.vim +++ b/plugin/shebang_create_exec.vim @@ -5,10 +5,10 @@ " Author: Tom Ryder <tom@sanctum.geek.nz> " License: Same as Vim itself " -if exists('loaded_shebang_create_exec') || &compatible +if exists('loaded_shebang_create_exec') || &compatible || v:version < 700 finish endif -if !has('autocmd') || !has('unix') || !exists('*shellescape') +if !has('unix') finish endif let loaded_shebang_create_exec = 1 @@ -17,5 +17,5 @@ let loaded_shebang_create_exec = 1 augroup shebang_create_exec autocmd! autocmd BufWritePre * - \ call shebang_create_exec#Check(expand('<afile>:p')) + \ call shebang_create_exec#(expand('<afile>:p')) augroup END |