diff options
-rw-r--r-- | src/os/fs.c | 88 | ||||
-rw-r--r-- | src/os/os.h | 1 | ||||
-rw-r--r-- | src/os_unix.c | 65 | ||||
-rw-r--r-- | src/os_unix.h | 1 | ||||
-rw-r--r-- | test/unit/os_unix.moon | 38 |
5 files changed, 102 insertions, 91 deletions
diff --git a/src/os/fs.c b/src/os/fs.c index 6ae48ab269..a1e3cd2899 100644 --- a/src/os/fs.c +++ b/src/os/fs.c @@ -15,6 +15,7 @@ #include "os.h" #include "../message.h" +#include "../misc1.h" #include "../misc2.h" int mch_chdir(char *path) { @@ -40,7 +41,7 @@ int mch_dirname(char_u *buf, int len) return OK; } -/* +/* * Get the absolute name of the given relative directory. * * parameter directory: Directory name, relative to current directory. @@ -85,7 +86,7 @@ int mch_full_dir_name(char *directory, char *buffer, int len) return retval; } -/* +/* * Append to_append to path with a slash in between. */ int append_path(char *path, char *to_append, int max_len) @@ -191,3 +192,86 @@ int mch_isdir(char_u *name) return TRUE; } +int is_executable(char_u *name); + +/* + * Return 1 if "name" is an executable file, 0 if not or it doesn't exist. + */ +int is_executable(char_u *name) +{ + uv_fs_t request; + if (0 != uv_fs_stat(uv_default_loop(), &request, (const char*) name, NULL)) { + return FALSE; + } + + if (S_ISREG(request.statbuf.st_mode) && + (S_IEXEC & request.statbuf.st_mode)) { + return TRUE; + } + + return FALSE; +} + +/* + * Return 1 if "name" can be found in $PATH and executed, 0 if not. + * Return -1 if unknown. + */ +int mch_can_exe(char_u *name) +{ + char_u *buf; + char_u *path, *e; + int retval; + + /* If it's an absolute or relative path don't need to use $PATH. */ + if (mch_is_absolute_path(name) || + (name[0] == '.' && (name[1] == '/' || + (name[1] == '.' && name[2] == '/')))) { + return is_executable(name); + } + + path = (char_u *)getenv("PATH"); + /* PATH environment variable does not exist or is empty. */ + if (path == NULL || *path == NUL) { + return -1; + } + + int buf_len = STRLEN(name) + STRLEN(path) + 2; + buf = alloc((unsigned)(buf_len)); + if (buf == NULL) { + return -1; + } + + /* + * Walk through all entries in $PATH to check if "name" exists there and + * is an executable file. + */ + for (;; ) { + e = (char_u *)strchr((char *)path, ':'); + if (e == NULL) { + e = path + STRLEN(path); + } + + if (e - path <= 1) { /* empty entry means current dir */ + STRCPY(buf, "./"); + } else { + vim_strncpy(buf, path, e - path); + add_pathsep(buf); + } + + append_path((char *) buf, (char *) name, buf_len); + + retval = is_executable(buf); + if (retval == OK) { + break; + } + + if (*e != ':') { + break; + } + + path = e + 1; + } + + vim_free(buf); + return retval; +} diff --git a/src/os/os.h b/src/os/os.h index 266b253e86..1d3a0d2c2b 100644 --- a/src/os/os.h +++ b/src/os/os.h @@ -9,6 +9,7 @@ int mch_dirname(char_u *buf, int len); int mch_get_absolute_path(char_u *fname, char_u *buf, int len, int force); int mch_is_absolute_path(char_u *fname); int mch_isdir(char_u *name); +int mch_can_exe(char_u *name); const char *mch_getenv(const char *name); int mch_setenv(const char *name, const char *value, int overwrite); char *mch_getenvname_at_index(size_t index); diff --git a/src/os_unix.c b/src/os_unix.c index 5a01f07c36..3bbe056cc6 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -1319,71 +1319,6 @@ void mch_hide(char_u *name) /* can't hide a file */ } -int executable_file(char_u *name); - -/* - * Return 1 if "name" is an executable file, 0 if not or it doesn't exist. - */ -int executable_file(char_u *name) -{ - struct stat st; - - if (stat((char *)name, &st)) - return 0; - return S_ISREG(st.st_mode) && mch_access((char *)name, X_OK) == 0; -} - -/* - * Return 1 if "name" can be found in $PATH and executed, 0 if not. - * Return -1 if unknown. - */ -int mch_can_exe(char_u *name) -{ - char_u *buf; - char_u *p, *e; - int retval; - - /* If it's an absolute or relative path don't need to use $PATH. */ - if (mch_is_absolute_path(name) || (name[0] == '.' && (name[1] == '/' - || (name[1] == '.' && - name[2] == '/')))) - return executable_file(name); - - p = (char_u *)mch_getenv("PATH"); - if (p == NULL || *p == NUL) - return -1; - buf = alloc((unsigned)(STRLEN(name) + STRLEN(p) + 2)); - if (buf == NULL) - return -1; - - /* - * Walk through all entries in $PATH to check if "name" exists there and - * is an executable file. - */ - for (;; ) { - e = (char_u *)strchr((char *)p, ':'); - if (e == NULL) - e = p + STRLEN(p); - if (e - p <= 1) /* empty entry means current dir */ - STRCPY(buf, "./"); - else { - vim_strncpy(buf, p, e - p); - add_pathsep(buf); - } - STRCAT(buf, name); - retval = executable_file(buf); - if (retval == 1) - break; - - if (*e != ':') - break; - p = e + 1; - } - - vim_free(buf); - return retval; -} - /* * Check what "name" is: * NODE_NORMAL: file or directory (or doesn't exist) diff --git a/src/os_unix.h b/src/os_unix.h index 92222bbca6..606237ee80 100644 --- a/src/os_unix.h +++ b/src/os_unix.h @@ -37,7 +37,6 @@ vim_acl_T mch_get_acl(char_u *fname); void mch_set_acl(char_u *fname, vim_acl_T aclent); void mch_free_acl(vim_acl_T aclent); void mch_hide(char_u *name); -int mch_can_exe(char_u *name); int mch_nodetype(char_u *name); void mch_early_init(void); void mch_free_mem(void); diff --git a/test/unit/os_unix.moon b/test/unit/os_unix.moon index ea054ab020..09505235d7 100644 --- a/test/unit/os_unix.moon +++ b/test/unit/os_unix.moon @@ -7,7 +7,7 @@ enum BOOLEAN { TRUE = 1, FALSE = 0 }; int mch_isdir(char_u * name); -int executable_file(char_u *name); +int is_executable(char_u *name); int mch_can_exe(char_u *name); ]] @@ -22,15 +22,13 @@ describe 'os_unix function', -> -- that executable for several asserts. export absolute_executable = arg[0] - -- Split absolute_executable into a directory and the actual file name and - -- append the directory to $PATH. - export directory, executable = if (string.find absolute_executable, '/') + -- Split absolute_executable into a directory and the actual file name for + -- later usage. + export directory, executable_name = if (string.find absolute_executable, '/') string.match(absolute_executable, '^(.*)/(.*)$') else string.match(absolute_executable, '^(.*)\\(.*)$') - package.path = package.path .. ';' .. directory - teardown -> lfs.rmdir 'unit-test-directory' @@ -57,25 +55,25 @@ describe 'os_unix function', -> it 'returns true if an arbitrary directory is given', -> eq TRUE, (mch_isdir 'unit-test-directory') - describe 'executable_file', -> - executable_file = (name) -> + describe 'is_executable', -> + is_executable = (name) -> name = cstr (string.len name), name - os.executable_file name + os.is_executable name it 'returns false when given a directory', -> - eq FALSE, (executable_file 'unit-test-directory') + eq FALSE, (is_executable 'unit-test-directory') it 'returns false when the given file does not exists', -> - eq FALSE, (executable_file 'does-not-exist.file') + eq FALSE, (is_executable 'does-not-exist.file') it 'returns true when given an executable regular file', -> - eq TRUE, (executable_file absolute_executable) + eq TRUE, (is_executable absolute_executable) it 'returns false when given a regular file without executable bit set', -> -- This is a critical test since Windows does not have any executable - -- bit. Thus executable_file returns TRUE on every regular file on + -- bit. Thus is_executable returns TRUE on every regular file on -- Windows and this test will fail. - eq FALSE, (executable_file 'unit-test-directory/test.file') + eq FALSE, (is_executable 'unit-test-directory/test.file') describe 'mch_can_exe', -> mch_can_exe = (name) -> @@ -83,23 +81,17 @@ describe 'os_unix function', -> os.mch_can_exe name it 'returns false when given a directory', -> - eq FALSE, (mch_can_exe 'unit-test-directory') + eq FALSE, (mch_can_exe './unit-test-directory') it 'returns false when the given file does not exists', -> eq FALSE, (mch_can_exe 'does-not-exist.file') - it 'returns true when given an executable in the current directory', -> - old_dir = lfs.currentdir! - lfs.chdir directory - eq TRUE, (mch_can_exe executable) - lfs.chdir old_dir - it 'returns true when given an executable inside $PATH', -> - eq TRUE, (mch_can_exe executable) + eq TRUE, (mch_can_exe executable_name) it 'returns true when given an executable relative to the current dir', -> old_dir = lfs.currentdir! lfs.chdir directory - relative_executable = './' .. executable + relative_executable = './' .. executable_name eq TRUE, (mch_can_exe relative_executable) lfs.chdir old_dir |