aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/os/fs.c38
-rw-r--r--src/os/os.h32
-rw-r--r--test/unit/os/fs.moon105
3 files changed, 172 insertions, 3 deletions
diff --git a/src/os/fs.c b/src/os/fs.c
index c245890873..89eb4c8691 100644
--- a/src/os/fs.c
+++ b/src/os/fs.c
@@ -237,3 +237,41 @@ int os_remove(const char *path)
return result;
}
+bool os_get_file_info(const char *path, FileInfo *file_info)
+{
+ if (os_stat((char_u *)path, &(file_info->stat)) == OK) {
+ return true;
+ }
+ return false;
+}
+
+bool os_get_file_info_link(const char *path, FileInfo *file_info)
+{
+ uv_fs_t request;
+ int result = uv_fs_lstat(uv_default_loop(), &request, path, NULL);
+ file_info->stat = request.statbuf;
+ uv_fs_req_cleanup(&request);
+ if (result == kLibuvSuccess) {
+ return true;
+ }
+ return false;
+}
+
+bool os_get_file_info_fd(int file_descriptor, FileInfo *file_info)
+{
+ uv_fs_t request;
+ int result = uv_fs_fstat(uv_default_loop(), &request, file_descriptor, NULL);
+ file_info->stat = request.statbuf;
+ uv_fs_req_cleanup(&request);
+ if (result == kLibuvSuccess) {
+ return true;
+ }
+ return false;
+}
+
+bool os_file_info_id_equal(FileInfo *file_info_1, FileInfo *file_info_2)
+{
+ return file_info_1->stat.st_ino == file_info_2->stat.st_ino
+ && file_info_1->stat.st_dev == file_info_2->stat.st_dev;
+}
+
diff --git a/src/os/os.h b/src/os/os.h
index 6fa7fbc7ae..b30872f06d 100644
--- a/src/os/os.h
+++ b/src/os/os.h
@@ -111,4 +111,36 @@ char *os_get_user_directory(const char *name);
/// @return OK on success, FAIL if an failure occured.
int os_stat(const char_u *name, uv_stat_t *statbuf);
+/// Struct which encapsulates stat information.
+typedef struct {
+ // TODO(stefan991): make stat private
+ uv_stat_t stat;
+} FileInfo;
+
+/// Get the file information for a given path
+///
+/// @param file_descriptor File descriptor of the file.
+/// @param[out] file_info Pointer to a FileInfo to put the information in.
+/// @return `true` on sucess, `false` for failure.
+bool os_get_file_info(const char *path, FileInfo *file_info);
+
+/// Get the file information for a given path without following links
+///
+/// @param path Path to the file.
+/// @param[out] file_info Pointer to a FileInfo to put the information in.
+/// @return `true` on sucess, `false` for failure.
+bool os_get_file_info_link(const char *path, FileInfo *file_info);
+
+/// Get the file information for a given file descriptor
+///
+/// @param file_descriptor File descriptor of the file.
+/// @param[out] file_info Pointer to a FileInfo to put the information in.
+/// @return `true` on sucess, `false` for failure.
+bool os_get_file_info_fd(int file_descriptor, FileInfo *file_info);
+
+/// Compare the inodes of two FileInfos
+///
+/// @return `true` if the two FileInfos represent the same file.
+bool os_file_info_id_equal(FileInfo *file_info_1, FileInfo *file_info_2);
+
#endif // NEOVIM_OS_OS_H
diff --git a/test/unit/os/fs.moon b/test/unit/os/fs.moon
index c1ab4bd78a..b11e2515bd 100644
--- a/test/unit/os/fs.moon
+++ b/test/unit/os/fs.moon
@@ -14,6 +14,8 @@ describe 'fs function', ->
setup ->
lfs.mkdir 'unit-test-directory'
(io.open 'unit-test-directory/test.file', 'w').close!
+ (io.open 'unit-test-directory/test_2.file', 'w').close!
+ lfs.link 'test.file', 'unit-test-directory/test_link.file', true
-- Since the tests are executed, they are called by an executable. We use
-- that executable for several asserts.
@@ -25,6 +27,8 @@ describe 'fs function', ->
teardown ->
os.remove 'unit-test-directory/test.file'
+ os.remove 'unit-test-directory/test_2.file'
+ os.remove 'unit-test-directory/test_link.file'
lfs.rmdir 'unit-test-directory'
describe 'os_dirname', ->
@@ -213,6 +217,12 @@ describe 'fs function', ->
eq 2, os_file_is_writable 'unit-test-directory'
describe 'file operations', ->
+ setup ->
+ (io.open 'unit-test-directory/test_remove.file', 'w').close!
+
+ teardown ->
+ os.remove 'unit-test-directory/test_remove.file'
+
os_file_exists = (filename) ->
fs.os_file_exists (to_cstr filename)
@@ -261,9 +271,9 @@ describe 'fs function', ->
neq 0, (os_remove 'non-existing-file')
it 'removes the given file and returns 0', ->
- eq true, (os_file_exists 'unit-test-directory/test.file')
- eq 0, (os_remove 'unit-test-directory/test.file')
- eq false, (os_file_exists 'unit-test-directory/test.file')
+ eq true, (os_file_exists 'unit-test-directory/test_remove.file')
+ eq 0, (os_remove 'unit-test-directory/test_remove.file')
+ eq false, (os_file_exists 'unit-test-directory/test_remove.file')
describe 'folder operations', ->
os_mkdir = (path, mode) ->
@@ -293,3 +303,92 @@ describe 'fs function', ->
eq 0, (os_rmdir 'unit-test-directory/new-dir', mode)
eq false, (os_isdir 'unit-test-directory/new-dir')
+ describe 'FileInfo', ->
+
+ file_info_new = () ->
+ file_info = ffi.new 'FileInfo[1]'
+ file_info[0].stat.st_ino = 0
+ file_info[0].stat.st_dev = 0
+ file_info
+
+ is_file_info_filled = (file_info) ->
+ file_info[0].stat.st_ino > 0 and file_info[0].stat.st_dev > 0
+
+ describe 'os_get_file_info', ->
+ it 'returns false if given an non-existing file', ->
+ file_info = file_info_new!
+ assert.is_false (fs.os_get_file_info '/non-existent', file_info)
+
+ it 'returns true if given an existing file and fills file_info', ->
+ file_info = file_info_new!
+ path = 'unit-test-directory/test.file'
+ assert.is_true (fs.os_get_file_info path, file_info)
+ assert.is_true (is_file_info_filled file_info)
+
+ it 'returns the file info of the linked file, not the link', ->
+ file_info = file_info_new!
+ path = 'unit-test-directory/test_link.file'
+ assert.is_true (fs.os_get_file_info path, file_info)
+ assert.is_true (is_file_info_filled file_info)
+ mode = tonumber file_info[0].stat.st_mode
+ eq ffi.C.kS_IFREG, (bit.band mode, ffi.C.kS_IFMT)
+
+ describe 'os_get_file_info_link', ->
+ it 'returns false if given an non-existing file', ->
+ file_info = file_info_new!
+ assert.is_false (fs.os_get_file_info_link '/non-existent', file_info)
+
+ it 'returns true if given an existing file and fills file_info', ->
+ file_info = file_info_new!
+ path = 'unit-test-directory/test.file'
+ assert.is_true (fs.os_get_file_info_link path, file_info)
+ assert.is_true (is_file_info_filled file_info)
+
+ it 'returns the file info of the link, not the linked file', ->
+ file_info = file_info_new!
+ path = 'unit-test-directory/test_link.file'
+ assert.is_true (fs.os_get_file_info_link path, file_info)
+ assert.is_true (is_file_info_filled file_info)
+ mode = tonumber file_info[0].stat.st_mode
+ eq ffi.C.kS_IFLNK, (bit.band mode, ffi.C.kS_IFMT)
+
+ describe 'os_get_file_info_fd', ->
+ it 'returns false if given an invalid file descriptor', ->
+ file_info = file_info_new!
+ assert.is_false (fs.os_get_file_info_fd -1, file_info)
+
+ it 'returns true if given an file descriptor and fills file_info', ->
+ file_info = file_info_new!
+ path = 'unit-test-directory/test.file'
+ fd = ffi.C.open path, 0
+ assert.is_true (fs.os_get_file_info_fd fd, file_info)
+ assert.is_true (is_file_info_filled file_info)
+ ffi.C.close fd
+
+ describe 'os_file_info_id_equal', ->
+ it 'returns false if file infos represent different files', ->
+ file_info_1 = file_info_new!
+ file_info_2 = file_info_new!
+ path_1 = 'unit-test-directory/test.file'
+ path_2 = 'unit-test-directory/test_2.file'
+ assert.is_true (fs.os_get_file_info path_1, file_info_1)
+ assert.is_true (fs.os_get_file_info path_2, file_info_2)
+ assert.is_false (fs.os_file_info_id_equal file_info_1, file_info_2)
+
+ it 'returns true if file infos represent the same file', ->
+ file_info_1 = file_info_new!
+ file_info_2 = file_info_new!
+ path = 'unit-test-directory/test.file'
+ assert.is_true (fs.os_get_file_info path, file_info_1)
+ assert.is_true (fs.os_get_file_info path, file_info_2)
+ assert.is_true (fs.os_file_info_id_equal file_info_1, file_info_2)
+
+ it 'returns true if file infos represent the same file (symlink)', ->
+ file_info_1 = file_info_new!
+ file_info_2 = file_info_new!
+ path_1 = 'unit-test-directory/test.file'
+ path_2 = 'unit-test-directory/test_link.file'
+ assert.is_true (fs.os_get_file_info path_1, file_info_1)
+ assert.is_true (fs.os_get_file_info path_2, file_info_2)
+ assert.is_true (fs.os_file_info_id_equal file_info_1, file_info_2)
+