From 025efcb731f4a3af00cf2741177262d1e97cff3e Mon Sep 17 00:00:00 2001 From: r00ster Date: Mon, 6 Jan 2020 09:15:06 +0100 Subject: [PATCH] os: add is_executable, is_writable and is_readable --- vlib/os/os.v | 47 ++++++++++++++++++++++++++++++++++++++++++++-- vlib/os/os_test.v | 48 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 82 insertions(+), 13 deletions(-) diff --git a/vlib/os/os.v b/vlib/os/os.v index bebb2543bf..ba7ae06ff7 100644 --- a/vlib/os/os.v +++ b/vlib/os/os.v @@ -505,16 +505,59 @@ pub fn unsetenv(name string) int { } } +const ( + F_OK = 0 + X_OK = 1 + W_OK = 2 + R_OK = 4 +) + // exists returns true if `path` exists. pub fn exists(path string) bool { $if windows { p := path.replace('/', '\\') - return C._waccess(p.to_wide(), 0) != -1 + return C._waccess(p.to_wide(), F_OK) != -1 } $else { - return C.access(path.str, 0) != -1 + return C.access(path.str, F_OK) != -1 } } +// `is_executable` returns `true` if `path` is executable. +pub fn is_executable(path string) bool { + $if windows { + // NB: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/access-waccess?view=vs-2019 + // i.e. there is no X bit there, the modes can be: + // 00 Existence only + // 02 Write-only + // 04 Read-only + // 06 Read and write + p := os.realpath( path ) + return ( os.exists( p ) && p.ends_with('.exe') ) + } $else { + return C.access(path.str, X_OK) != -1 + } +} + +// `is_writable` returns `true` if `path` is writable. +pub fn is_writable(path string) bool { + $if windows { + p := path.replace('/', '\\') + return C._waccess(p.to_wide(), W_OK) != -1 + } $else { + return C.access(path.str, W_OK) != -1 + } +} + +// `is_readable` returns `true` if `path` is readable. +pub fn is_readable(path string) bool { + $if windows { + p := path.replace('/', '\\') + return C._waccess(p.to_wide(), R_OK) != -1 + } $else { + return C.access(path.str, R_OK) != -1 + } +} + [deprecated] pub fn file_exists(_path string) bool { panic('use os.exists(path) instead of os.file_exists(path)') diff --git a/vlib/os/os_test.v b/vlib/os/os_test.v index 404c8e2789..250f39e1b3 100644 --- a/vlib/os/os_test.v +++ b/vlib/os/os_test.v @@ -107,7 +107,7 @@ fn test_walk() { os.write_file(file1,'test-1') os.walk(folder, walk_callback) - + os.rm(file1) os.rmdir(folder) } @@ -150,17 +150,17 @@ fn test_tmpdir(){ t := os.tmpdir() assert t.len > 0 assert os.is_dir(t) - + tfile := t + os.path_separator + 'tmpfile.txt' - + os.rm(tfile) // just in case - + tfile_content := 'this is a temporary file' os.write_file(tfile, tfile_content) - + tfile_content_read := os.read_file(tfile) or { panic(err) } assert tfile_content_read == tfile_content - + os.rm(tfile) } @@ -175,7 +175,7 @@ fn test_make_symlink_check_is_link_and_remove_symlink() { folder := 'tfolder' symlink := 'tsymlink' - os.rm(symlink) + os.rm(symlink) os.rm(folder) os.mkdir(folder) or { panic(err) } @@ -185,12 +185,12 @@ fn test_make_symlink_check_is_link_and_remove_symlink() { os.system('ln -s $folder $symlink') assert os.is_link(symlink) == true - os.rm(symlink) + os.rm(symlink) os.rm(folder) - + folder_exists := os.is_dir(folder) assert folder_exists == false - + symlink_exists := os.is_link(symlink) assert symlink_exists == false } @@ -234,6 +234,32 @@ fn test_symlink() { os.rm('symlink2') } +fn test_is_executable_writable_readable() { + file_name := os.tmpdir() + os.path_separator + 'rwxfile.exe' + + mut f := os.create(file_name) or { + eprintln('failed to create file $file_name') + return + } + f.close() + + $if !windows { + os.chmod(file_name, 0600) // mark as readable && writable, but NOT executable + assert os.is_writable(file_name) + assert os.is_readable(file_name) + assert !os.is_executable(file_name) + os.chmod(file_name, 0700) // mark as executable too + assert os.is_executable(file_name) + } $else { + assert os.is_writable(file_name) + assert os.is_readable(file_name) + assert os.is_executable(file_name) + } + + // We finally delete the test file. + os.rm(file_name) +} + // this function is called by both test_aaa_setup & test_zzz_cleanup // it ensures that os tests do not polute the filesystem with leftover // files so that they can be run several times in a row. @@ -241,7 +267,7 @@ fn cleanup_leftovers(){ // possible leftovers from test_cp os.rm('cp_example.txt') os.rm('cp_new_example.txt') - + // possible leftovers from test_cp_r os.rm('ex/ex2/ex2.txt') os.rmdir('ex/ex2')