Skip to content

Commit eb60796

Browse files
committed
Implement Crystal::System::FileDescriptor for windows
1 parent c6ae527 commit eb60796

File tree

3 files changed

+151
-18
lines changed

3 files changed

+151
-18
lines changed
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1-
require "./unix/file_descriptor"
1+
{% if flag?(:win32) %}
2+
require "./win32/file_descriptor"
3+
{% else %}
4+
require "./unix/file_descriptor"
5+
{% end %}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
require "file/stat"
2+
require "c/io"
3+
4+
module Crystal::System::FileDescriptor
5+
@fd : LibC::Int
6+
7+
private def unbuffered_read(slice : Bytes)
8+
bytes_read = LibC._read(@fd, slice, slice.size)
9+
if bytes_read == -1
10+
raise Errno.new("Error reading file")
11+
end
12+
bytes_read
13+
end
14+
15+
private def unbuffered_write(slice : Bytes)
16+
loop do
17+
bytes_written = LibC._write(@fd, slice, slice.size)
18+
if bytes_written == -1
19+
raise Errno.new("Error writing file")
20+
end
21+
22+
slice += bytes_written
23+
return if slice.size == 0
24+
end
25+
end
26+
27+
private def system_blocking?
28+
true
29+
end
30+
31+
private def system_blocking=(blocking)
32+
raise NotImplementedError.new("Crystal::System::FileDescriptor#system_blocking=") unless blocking
33+
end
34+
35+
private def system_close_on_exec?
36+
false
37+
end
38+
39+
private def system_close_on_exec=(close_on_exec)
40+
raise NotImplementedError.new("Crystal::System::FileDescriptor#system_close_on_exec=") if close_on_exec
41+
end
42+
43+
private def system_stat
44+
if LibC._fstat64(@fd, out stat) != 0
45+
raise Errno.new("Unable to get stat")
46+
end
47+
::File::Stat.new(stat)
48+
end
49+
50+
private def system_seek(offset, whence : IO::Seek) : Nil
51+
seek_value = LibC._lseek(@fd, offset, whence)
52+
53+
if seek_value == -1
54+
raise Errno.new "Unable to seek"
55+
end
56+
end
57+
58+
private def system_pos
59+
pos = LibC._lseek(@fd, 0, IO::Seek::Current)
60+
raise Errno.new "Unable to tell" if pos == -1
61+
pos
62+
end
63+
64+
private def system_tty?
65+
LibC._isatty(@fd) != 0
66+
end
67+
68+
private def system_reopen(other : IO::FileDescriptor)
69+
{% if LibC.methods.includes? "dup3".id %}
70+
# dup doesn't copy the CLOEXEC flag, so copy it manually using dup3
71+
flags = other.close_on_exec? ? LibC::O_CLOEXEC : 0
72+
if LibC.dup3(other.fd, self.fd, flags) == -1
73+
raise Errno.new("Could not reopen file descriptor")
74+
end
75+
{% else %}
76+
# dup doesn't copy the CLOEXEC flag, copy it manually to the new
77+
if LibC.dup2(other.fd, self.fd) == -1
78+
raise Errno.new("Could not reopen file descriptor")
79+
end
80+
81+
if other.close_on_exec?
82+
self.close_on_exec = true
83+
end
84+
{% end %}
85+
end
86+
87+
private def system_close
88+
err = nil
89+
if LibC._close(@fd) != 0
90+
case Errno.value
91+
when Errno::EINTR
92+
# ignore
93+
else
94+
raise Errno.new("Error closing file")
95+
end
96+
end
97+
end
98+
end

src/file/stat.cr

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,51 @@ require "c/sys/stat"
22

33
class File
44
struct Stat
5-
def initialize(filename : String)
6-
if LibC.stat(filename, out @stat) != 0
7-
raise Errno.new("Unable to get stat for '#{filename}'")
8-
end
5+
def self.new(filename : String)
6+
File.stat(filename)
97
end
108

11-
def initialize(@stat : LibC::Stat)
12-
end
9+
{% if flag?(:win32) %}
10+
# :nodoc:
11+
def initialize(@stat : LibC::Stat64)
12+
end
13+
{% else %}
14+
# :nodoc:
15+
def initialize(@stat : LibC::Stat)
16+
end
17+
{% end %}
1318

1419
def atime
1520
{% if flag?(:darwin) %}
1621
time @stat.st_atimespec
22+
{% elsif flag?(:win32) %}
23+
time @stat.st_atime
1724
{% else %}
1825
time @stat.st_atim
1926
{% end %}
2027
end
2128

2229
def blksize
23-
@stat.st_blksize
30+
{% if flag?(:win32) %}
31+
raise NotImplementedError.new("File::Stat#blksize")
32+
{% else %}
33+
@stat.st_blksize
34+
{% end %}
2435
end
2536

2637
def blocks
27-
@stat.st_blocks
38+
{% if flag?(:win32) %}
39+
raise NotImplementedError.new("File::Stat#blocks")
40+
{% else %}
41+
@stat.st_blocks
42+
{% end %}
2843
end
2944

3045
def ctime
3146
{% if flag?(:darwin) %}
3247
time @stat.st_ctimespec
48+
{% elsif flag?(:win32) %}
49+
time @stat.st_ctime
3350
{% else %}
3451
time @stat.st_ctim
3552
{% end %}
@@ -59,6 +76,8 @@ class File
5976
def mtime
6077
{% if flag?(:darwin) %}
6178
time @stat.st_mtimespec
79+
{% elsif flag?(:win32) %}
80+
time @stat.st_mtime
6281
{% else %}
6382
time @stat.st_mtim
6483
{% end %}
@@ -93,8 +112,11 @@ class File
93112
io << ", rdev=0x"
94113
rdev.to_s(16, io)
95114
io << ", size=" << size
96-
io << ", blksize=" << blksize
97-
io << ", blocks=" << blocks
115+
{% unless flag?(:win32) %}
116+
# These two getters raise NotImplementedError on windows.
117+
io << ", blksize=" << blksize
118+
io << ", blocks=" << blocks
119+
{% end %}
98120
io << ", atime=" << atime
99121
io << ", mtime=" << mtime
100122
io << ", ctime=" << ctime
@@ -119,10 +141,13 @@ class File
119141
pp.comma
120142
pp.text "size=#{size}"
121143
pp.comma
122-
pp.text "blksize=#{blksize}"
123-
pp.comma
124-
pp.text "blocks=#{blocks}"
125-
pp.comma
144+
{% unless flag?(:win32) %}
145+
# These two getters raise NotImplementedError on windows.
146+
pp.text "blksize=#{blksize}"
147+
pp.comma
148+
pp.text "blocks=#{blocks}"
149+
pp.comma
150+
{% end %}
126151
pp.text "atime=#{atime}"
127152
pp.comma
128153
pp.text "mtime=#{mtime}"
@@ -171,8 +196,14 @@ class File
171196
(@stat.st_mode & LibC::S_IFMT) == LibC::S_ISVTX
172197
end
173198

174-
private def time(value)
175-
Time.new value, Time::Kind::Utc
176-
end
199+
{% if flag?(:win32) %}
200+
private def time(value)
201+
Time.epoch(value)
202+
end
203+
{% else %}
204+
private def time(value)
205+
Time.new value, Time::Kind::Utc
206+
end
207+
{% end %}
177208
end
178209
end

0 commit comments

Comments
 (0)