Commit 03bec631bd

Marc Tiehuis <marctiehuis@gmail.com>
2018-04-13 11:27:09
Replace File.exists with File.access
1 parent b946982
Changed files (7)
src-self-hosted/main.zig
@@ -175,7 +175,7 @@ fn cmdBuild(allocator: &Allocator, args: []const []const u8) !void {
     const build_file_abs = try os.path.resolve(allocator, ".", build_file);
     defer allocator.free(build_file_abs);
 
-    const build_file_exists = os.File.exists(allocator, build_file_abs);
+    const build_file_exists = os.File.access(allocator, build_file_abs, os.default_file_mode) catch false;
 
     if (flags.present("init")) {
         if (build_file_exists) {
std/c/index.zig
@@ -28,6 +28,7 @@ pub extern "c" fn unlink(path: &const u8) c_int;
 pub extern "c" fn getcwd(buf: &u8, size: usize) ?&u8;
 pub extern "c" fn waitpid(pid: c_int, stat_loc: &c_int, options: c_int) c_int;
 pub extern "c" fn fork() c_int;
+pub extern "c" fn access(path: &const u8, mode: c_uint) c_int;
 pub extern "c" fn pipe(fds: &c_int) c_int;
 pub extern "c" fn mkdir(path: &const u8, mode: c_uint) c_int;
 pub extern "c" fn symlink(existing: &const u8, new: &const u8) c_int;
std/os/linux/index.zig
@@ -38,6 +38,11 @@ pub const MAP_STACK      = 0x20000;
 pub const MAP_HUGETLB    = 0x40000;
 pub const MAP_FILE       = 0;
 
+pub const F_OK = 0;
+pub const X_OK = 1;
+pub const W_OK = 2;
+pub const R_OK = 4;
+
 pub const WNOHANG    = 1;
 pub const WUNTRACED  = 2;
 pub const WSTOPPED   = 2;
@@ -705,6 +710,10 @@ pub fn pread(fd: i32, buf: &u8, count: usize, offset: usize) usize {
     return syscall4(SYS_pread, usize(fd), @ptrToInt(buf), count, offset);
 }
 
+pub fn access(path: &const u8, mode: u32) usize {
+    return syscall2(SYS_access, @ptrToInt(path), mode);
+}
+
 pub fn pipe(fd: &[2]i32) usize {
     return pipe2(fd, 0);
 }
std/os/windows/index.zig
@@ -78,6 +78,8 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem
 pub extern "kernel32" stdcallcc fn MoveFileExA(lpExistingFileName: LPCSTR, lpNewFileName: LPCSTR,
     dwFlags: DWORD) BOOL;
 
+pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL;
+
 pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: &c_void,
     in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD,
     in_out_lpOverlapped: ?&OVERLAPPED) BOOL;
std/os/darwin.zig
@@ -41,6 +41,11 @@ pub const SA_64REGSET  = 0x0200; /// signal handler with SA_SIGINFO args with 64
 pub const O_LARGEFILE = 0x0000;
 pub const O_PATH = 0x0000;
 
+pub const F_OK = 0;
+pub const X_OK = 1;
+pub const W_OK = 2;
+pub const R_OK = 4;
+
 pub const O_RDONLY   = 0x0000; /// open for reading only
 pub const O_WRONLY   = 0x0001; /// open for writing only
 pub const O_RDWR     = 0x0002; /// open for reading and writing
@@ -209,6 +214,10 @@ pub fn fork() usize {
     return errnoWrap(c.fork());
 }
 
+pub fn access(path: &const u8, mode: u32) usize {
+    return errnoWrap(c.access(path, mode));
+}
+
 pub fn pipe(fds: &[2]i32) usize {
     comptime assert(i32.bit_count == c_int.bit_count);
     return errnoWrap(c.pipe(@ptrCast(&c_int, fds)));
std/os/file.zig
@@ -85,12 +85,45 @@ pub const File = struct {
         };
     }
 
-    pub fn exists(allocator: &mem.Allocator, path: []const u8) bool {
-        if (openRead(allocator, path)) |*file| {
-            file.close();
+    pub fn access(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) !bool {
+        const path_with_null = try std.cstr.addNullByte(allocator, path);
+        defer allocator.free(path_with_null);
+
+        if (is_posix) {
+            // mode is ignored and is always F_OK for now
+            const result = posix.access(path_with_null.ptr, posix.F_OK);
+            const err = posix.getErrno(result);
+            if (err > 0) {
+                return switch (err) {
+                    posix.EACCES => error.PermissionDenied,
+                    posix.EROFS => error.PermissionDenied,
+                    posix.ELOOP => error.PermissionDenied,
+                    posix.ETXTBSY => error.PermissionDenied,
+                    posix.ENOTDIR => error.NotFound,
+                    posix.ENOENT => error.NotFound,
+
+                    posix.ENAMETOOLONG => error.NameTooLong,
+                    posix.EINVAL => error.BadMode,
+                    posix.EFAULT => error.BadPathName,
+                    posix.EIO => error.Io,
+                    posix.ENOMEM => error.SystemResources,
+                    else => os.unexpectedErrorPosix(err),
+                };
+            }
             return true;
-        } else |_| {
-            return false;
+        } else if (is_windows) {
+            if (os.windows.PathFileExists(path_with_null.ptr)) {
+                return true;
+            }
+
+            const err = windows.GetLastError();
+            return switch (err) {
+                windows.ERROR.FILE_NOT_FOUND => error.NotFound,
+                windows.ERROR.ACCESS_DENIED => error.PermissionDenied,
+                else => os.unexpectedErrorWindows(err),
+            };
+        } else {
+            @compileError("TODO implement access for this OS");
         }
     }
 
std/os/test.zig
@@ -23,3 +23,20 @@ test "makePath, put some files in it, deleteTree" {
         assert(err == error.PathNotFound);
     }
 }
+
+test "access file" {
+    if (builtin.os == builtin.Os.windows) {
+        return;
+    }
+
+    try os.makePath(a, "os_test_tmp");
+    if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| {
+        unreachable;
+    } else |err| {
+        assert(err == error.NotFound);
+    }
+
+    try io.writeFile(a, "os_test_tmp/file.txt", "");
+    assert((try os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) == true);
+    try os.deleteTree(a, "os_test_tmp");
+}