From 4e4d9f3fdcb161d6524ec4f10e7e75368604d857 Mon Sep 17 00:00:00 2001 From: RX14 Date: Wed, 29 Nov 2017 18:10:07 +0000 Subject: [PATCH] Implement Crystal::System::FileDescriptor for windows --- src/crystal/system/file_descriptor.cr | 6 +- src/crystal/system/win32/file_descriptor.cr | 98 +++++++++++++++++++++ src/file/stat.cr | 65 ++++++++++---- 3 files changed, 151 insertions(+), 18 deletions(-) create mode 100644 src/crystal/system/win32/file_descriptor.cr diff --git a/src/crystal/system/file_descriptor.cr b/src/crystal/system/file_descriptor.cr index ad9428863bc3..f36e3e79e750 100644 --- a/src/crystal/system/file_descriptor.cr +++ b/src/crystal/system/file_descriptor.cr @@ -1 +1,5 @@ -require "./unix/file_descriptor" +{% if flag?(:win32) %} + require "./win32/file_descriptor" +{% else %} + require "./unix/file_descriptor" +{% end %} diff --git a/src/crystal/system/win32/file_descriptor.cr b/src/crystal/system/win32/file_descriptor.cr new file mode 100644 index 000000000000..3967e5e9f4a2 --- /dev/null +++ b/src/crystal/system/win32/file_descriptor.cr @@ -0,0 +1,98 @@ +require "file/stat" +require "c/io" + +module Crystal::System::FileDescriptor + @fd : LibC::Int + + private def unbuffered_read(slice : Bytes) + bytes_read = LibC._read(@fd, slice, slice.size) + if bytes_read == -1 + raise Errno.new("Error reading file") + end + bytes_read + end + + private def unbuffered_write(slice : Bytes) + loop do + bytes_written = LibC._write(@fd, slice, slice.size) + if bytes_written == -1 + raise Errno.new("Error writing file") + end + + slice += bytes_written + return if slice.size == 0 + end + end + + private def system_blocking? + true + end + + private def system_blocking=(blocking) + raise NotImplementedError.new("Crystal::System::FileDescriptor#system_blocking=") unless blocking + end + + private def system_close_on_exec? + false + end + + private def system_close_on_exec=(close_on_exec) + raise NotImplementedError.new("Crystal::System::FileDescriptor#system_close_on_exec=") if close_on_exec + end + + private def system_stat + if LibC._fstat64(@fd, out stat) != 0 + raise Errno.new("Unable to get stat") + end + ::File::Stat.new(stat) + end + + private def system_seek(offset, whence : IO::Seek) : Nil + seek_value = LibC._lseek(@fd, offset, whence) + + if seek_value == -1 + raise Errno.new "Unable to seek" + end + end + + private def system_pos + pos = LibC._lseek(@fd, 0, IO::Seek::Current) + raise Errno.new "Unable to tell" if pos == -1 + pos + end + + private def system_tty? + LibC._isatty(@fd) != 0 + end + + private def system_reopen(other : IO::FileDescriptor) + {% if LibC.methods.includes? "dup3".id %} + # dup doesn't copy the CLOEXEC flag, so copy it manually using dup3 + flags = other.close_on_exec? ? LibC::O_CLOEXEC : 0 + if LibC.dup3(other.fd, self.fd, flags) == -1 + raise Errno.new("Could not reopen file descriptor") + end + {% else %} + # dup doesn't copy the CLOEXEC flag, copy it manually to the new + if LibC.dup2(other.fd, self.fd) == -1 + raise Errno.new("Could not reopen file descriptor") + end + + if other.close_on_exec? + self.close_on_exec = true + end + {% end %} + end + + private def system_close + err = nil + if LibC._close(@fd) != 0 + case Errno.value + when Errno::EINTR + # ignore + else + raise Errno.new("Error closing file") + end + end + end +end diff --git a/src/file/stat.cr b/src/file/stat.cr index 12f883aff478..cbce74db9b95 100644 --- a/src/file/stat.cr +++ b/src/file/stat.cr @@ -2,34 +2,51 @@ require "c/sys/stat" class File struct Stat - def initialize(filename : String) - if LibC.stat(filename, out @stat) != 0 - raise Errno.new("Unable to get stat for '#{filename}'") - end + def self.new(filename : String) + File.stat(filename) end - def initialize(@stat : LibC::Stat) - end + {% if flag?(:win32) %} + # :nodoc: + def initialize(@stat : LibC::Stat64) + end + {% else %} + # :nodoc: + def initialize(@stat : LibC::Stat) + end + {% end %} def atime {% if flag?(:darwin) %} time @stat.st_atimespec + {% elsif flag?(:win32) %} + time @stat.st_atime {% else %} time @stat.st_atim {% end %} end def blksize - @stat.st_blksize + {% if flag?(:win32) %} + raise NotImplementedError.new("File::Stat#blksize") + {% else %} + @stat.st_blksize + {% end %} end def blocks - @stat.st_blocks + {% if flag?(:win32) %} + raise NotImplementedError.new("File::Stat#blocks") + {% else %} + @stat.st_blocks + {% end %} end def ctime {% if flag?(:darwin) %} time @stat.st_ctimespec + {% elsif flag?(:win32) %} + time @stat.st_ctime {% else %} time @stat.st_ctim {% end %} @@ -59,6 +76,8 @@ class File def mtime {% if flag?(:darwin) %} time @stat.st_mtimespec + {% elsif flag?(:win32) %} + time @stat.st_mtime {% else %} time @stat.st_mtim {% end %} @@ -93,8 +112,11 @@ class File io << ", rdev=0x" rdev.to_s(16, io) io << ", size=" << size - io << ", blksize=" << blksize - io << ", blocks=" << blocks + {% unless flag?(:win32) %} + # These two getters raise NotImplementedError on windows. + io << ", blksize=" << blksize + io << ", blocks=" << blocks + {% end %} io << ", atime=" << atime io << ", mtime=" << mtime io << ", ctime=" << ctime @@ -119,10 +141,13 @@ class File pp.comma pp.text "size=#{size}" pp.comma - pp.text "blksize=#{blksize}" - pp.comma - pp.text "blocks=#{blocks}" - pp.comma + {% unless flag?(:win32) %} + # These two getters raise NotImplementedError on windows. + pp.text "blksize=#{blksize}" + pp.comma + pp.text "blocks=#{blocks}" + pp.comma + {% end %} pp.text "atime=#{atime}" pp.comma pp.text "mtime=#{mtime}" @@ -171,8 +196,14 @@ class File (@stat.st_mode & LibC::S_IFMT) == LibC::S_ISVTX end - private def time(value) - Time.new value, Time::Kind::Utc - end + {% if flag?(:win32) %} + private def time(value) + Time.epoch(value) + end + {% else %} + private def time(value) + Time.new value, Time::Kind::Utc + end + {% end %} end end