]> gitweb.factorcode.org Git - factor.git/blob - basis/io/files/windows/windows.factor
a46ef80c4d374bc385acbbd4a64d737b798af15d
[factor.git] / basis / io / files / windows / windows.factor
1 ! Copyright (C) 2008 Doug Coleman.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors alien alien.c-types alien.data alien.strings
4 alien.syntax arrays assocs classes.struct combinators
5 combinators.short-circuit continuations destructors environment
6 fry io io.backend io.binary io.buffers io.encodings.utf16n io.files
7 io.files.private io.files.types io.pathnames io.ports
8 io.streams.c io.streams.null io.timeouts kernel libc literals
9 locals make math math.bitwise namespaces sequences
10 specialized-arrays system threads tr windows windows.errors
11 windows.handles windows.kernel32 windows.shell32 windows.time
12 windows.types windows.winsock ;
13 SPECIALIZED-ARRAY: ushort
14 IN: io.files.windows
15
16 SLOT: file
17
18 HOOK: CreateFile-flags io-backend ( DWORD -- DWORD )
19 HOOK: open-append os ( path -- win32-file )
20
21 TUPLE: win32-file < win32-handle ptr ;
22
23 : <win32-file> ( handle -- win32-file )
24     win32-file new-win32-handle ;
25
26 M: win32-file dispose
27     [ cancel-operation ] [ call-next-method ] bi ;
28
29 CONSTANT: share-mode
30     flags{
31         FILE_SHARE_READ
32         FILE_SHARE_WRITE
33         FILE_SHARE_DELETE
34     }
35
36 : default-security-attributes ( -- obj )
37     SECURITY_ATTRIBUTES <struct>
38     SECURITY_ATTRIBUTES heap-size >>nLength ;
39
40 TUPLE: FileArgs
41     hFile lpBuffer nNumberOfBytesToRead
42     lpNumberOfBytesRet lpOverlapped ;
43
44 C: <FileArgs> FileArgs
45
46 ! Global variable with assoc mapping overlapped to threads
47 SYMBOL: pending-overlapped
48
49 TUPLE: io-callback port thread ;
50
51 C: <io-callback> io-callback
52
53 : <completion-port> ( handle existing -- handle )
54      f 1 CreateIoCompletionPort dup win32-error=0/f ;
55
56 : <master-completion-port> ( -- handle )
57     INVALID_HANDLE_VALUE f <completion-port> ;
58
59 SYMBOL: master-completion-port
60
61 : add-completion ( win32-handle -- win32-handle )
62     dup handle>> master-completion-port get-global <completion-port> drop ;
63
64 : opened-file ( handle -- win32-file )
65     check-invalid-handle <win32-file> |dispose add-completion ;
66
67 : eof? ( error -- ? )
68     { [ ERROR_HANDLE_EOF = ] [ ERROR_BROKEN_PIPE = ] } 1|| ;
69
70 : twiddle-thumbs ( overlapped port -- bytes-transferred )
71     [
72         drop
73         [ self ] dip >c-ptr pending-overlapped get-global set-at
74         "I/O" suspend {
75             { [ dup integer? ] [ ] }
76             { [ dup array? ] [
77                 first dup eof?
78                 [ drop 0 ] [ n>win32-error-string throw ] if
79             ] }
80         } cond
81     ] with-timeout ;
82
83 :: wait-for-overlapped ( nanos -- bytes-transferred overlapped error? )
84     nanos [ 1,000,000 /i ] [ INFINITE ] if* :> timeout
85     master-completion-port get-global
86     { int void* pointer: OVERLAPPED }
87     [ timeout GetQueuedCompletionStatus zero? ] with-out-parameters
88     :> ( error? bytes key overlapped )
89     bytes overlapped error? ;
90
91 : resume-callback ( result overlapped -- )
92     >c-ptr pending-overlapped get-global delete-at* drop resume-with ;
93
94 : handle-overlapped ( nanos -- ? )
95     wait-for-overlapped [
96         [
97             [ drop GetLastError 1array ] dip resume-callback t
98         ] [ drop f ] if*
99     ] [ resume-callback t ] if ;
100
101 M: win32-handle cancel-operation
102     [ handle>> CancelIo win32-error=0/f ] unless-disposed ;
103
104 M: windows io-multiplex ( nanos -- )
105     handle-overlapped [ 0 io-multiplex ] when ;
106
107 M: windows init-io ( -- )
108     <master-completion-port> master-completion-port set-global
109     H{ } clone pending-overlapped set-global ;
110
111 ERROR: invalid-file-size n ;
112
113 : (handle>file-size) ( handle -- n/f )
114     0 ulonglong <ref> [ GetFileSizeEx ] keep swap
115     [ drop f ] [ drop ulonglong deref ] if-zero ;
116
117 ! GetFileSizeEx errors with ERROR_INVALID_FUNCTION if handle is not seekable
118 : handle>file-size ( handle -- n/f )
119     (handle>file-size) [
120         GetLastError ERROR_INVALID_FUNCTION =
121         [ f ] [ throw-win32-error ] if
122     ] unless* ;
123
124 ERROR: seek-before-start n ;
125
126 : set-seek-ptr ( n handle -- )
127     [ dup 0 < [ seek-before-start ] when ] dip ptr<< ;
128
129 M: windows tell-handle ( handle -- n ) ptr>> ;
130
131 M: windows seek-handle ( n seek-type handle -- )
132     swap {
133         { seek-absolute [ set-seek-ptr ] }
134         { seek-relative [ [ ptr>> + ] keep set-seek-ptr ] }
135         { seek-end [ [ handle>> handle>file-size + ] keep set-seek-ptr ] }
136         [ bad-seek-type ]
137     } case ;
138
139 M: windows can-seek-handle? ( handle -- ? )
140     handle>> handle>file-size >boolean ;
141
142 M: windows handle-length ( handle -- n/f )
143     handle>> handle>file-size
144     dup { 0 f } member? [ drop f ] when ;
145
146 : file-error? ( n -- eof? )
147     zero? [
148         GetLastError {
149             { [ dup expected-io-error? ] [ drop f ] }
150             { [ dup eof? ] [ drop t ] }
151             [ n>win32-error-string throw ]
152         } cond
153     ] [ f ] if ;
154
155 : wait-for-file ( FileArgs n port -- n )
156     swap file-error?
157     [ 2drop 0 ] [ [ lpOverlapped>> ] dip twiddle-thumbs ] if ;
158
159 : update-file-ptr ( n port -- )
160     handle>> dup ptr>> [ rot + >>ptr drop ] [ 2drop ] if* ;
161
162 : (make-overlapped) ( -- overlapped-ext )
163     OVERLAPPED malloc-struct &free ;
164
165 : make-overlapped ( handle -- overlapped-ext )
166     (make-overlapped) swap
167     ptr>> [ [ 32 bits >>offset ] [ -32 shift >>offset-high ] bi ] when* ;
168
169 : make-FileArgs ( port handle -- <FileArgs> )
170     [ nip check-disposed handle>> ]
171     [
172         [ buffer>> dup buffer-length 0 DWORD <ref> ] dip make-overlapped
173     ] 2bi <FileArgs> ;
174
175 : setup-write ( <FileArgs> -- hFile lpBuffer nNumberOfBytesToWrite lpNumberOfBytesWritten lpOverlapped )
176     {
177         [ hFile>> ]
178         [ lpBuffer>> [ buffer@ ] [ buffer-length ] bi ]
179         [ lpNumberOfBytesRet>> ]
180         [ lpOverlapped>> ]
181     } cleave ;
182
183 : finish-write ( n port -- )
184     [ update-file-ptr ] [ buffer>> buffer-consume ] 2bi ;
185
186 M: object drain ( port handle -- event/f )
187     [ make-FileArgs dup setup-write WriteFile ]
188     [ drop [ wait-for-file ] [ finish-write ] bi ] 2bi f ;
189
190 : setup-read ( <FileArgs> -- hFile lpBuffer nNumberOfBytesToRead lpNumberOfBytesRead lpOverlapped )
191     {
192         [ hFile>> ]
193         [ lpBuffer>> [ buffer-end ] [ buffer-capacity ] bi ]
194         [ lpNumberOfBytesRet>> ]
195         [ lpOverlapped>> ]
196     } cleave ;
197
198 : finish-read ( n port -- )
199     [ update-file-ptr ] [ buffer>> buffer+ ] 2bi ;
200
201 M: object refill ( port handle -- event/f )
202     [ make-FileArgs dup setup-read ReadFile ]
203     [ drop [ wait-for-file ] [ finish-read ] bi ] 2bi f ;
204
205 M: windows (wait-to-write) ( port -- )
206     [ dup handle>> drain ] with-destructors drop ;
207
208 M: windows (wait-to-read) ( port -- )
209     [ dup handle>> refill ] with-destructors drop ;
210
211 : make-fd-set ( socket -- fd_set )
212     fd_set <struct> swap 1array void* >c-array >>fd_array 1 >>fd_count ;
213
214 : select-sets ( socket event -- read-fds write-fds except-fds )
215     [ make-fd-set ] dip +input+ = [ f f ] [ f swap f ] if ;
216
217 CONSTANT: select-timeval S{ timeval { sec 0 } { usec 1000 } }
218
219 M: windows wait-for-fd ( handle event -- )
220     [ file>> handle>> 1 swap ] dip select-sets select-timeval
221     select drop yield ;
222
223 : console-app? ( -- ? ) GetConsoleWindow >boolean ;
224
225 M: windows init-stdio
226     console-app?
227     [ init-c-stdio ]
228     [ null-reader null-writer null-writer set-stdio ] if ;
229
230 : open-file ( path access-mode create-mode flags -- handle )
231     [
232         [ share-mode default-security-attributes ] 2dip
233         CreateFile-flags f CreateFile opened-file
234     ] with-destructors ;
235
236 : open-r/w ( path -- win32-file )
237     flags{ GENERIC_READ GENERIC_WRITE }
238     OPEN_EXISTING 0 open-file ;
239
240 : open-read ( path -- win32-file )
241     GENERIC_READ OPEN_EXISTING 0 open-file 0 >>ptr ;
242
243 : open-write ( path -- win32-file )
244     GENERIC_WRITE CREATE_ALWAYS 0 open-file 0 >>ptr ;
245
246 : (open-append) ( path -- win32-file )
247     GENERIC_WRITE OPEN_ALWAYS 0 open-file ;
248
249 : open-existing ( path -- win32-file )
250     flags{ GENERIC_READ GENERIC_WRITE }
251     share-mode
252     f
253     OPEN_EXISTING
254     FILE_FLAG_BACKUP_SEMANTICS
255     f CreateFileW dup win32-error=0/f <win32-file> ;
256
257 : maybe-create-file ( path -- win32-file ? )
258     #! return true if file was just created
259     flags{ GENERIC_READ GENERIC_WRITE }
260     share-mode
261     f
262     OPEN_ALWAYS
263     0 CreateFile-flags
264     f CreateFileW dup win32-error=0/f <win32-file>
265     GetLastError ERROR_ALREADY_EXISTS = not ;
266
267 : set-file-pointer ( handle length method -- )
268     [ [ handle>> ] dip d>w/w uint <ref> ] dip SetFilePointer
269     INVALID_SET_FILE_POINTER = [ "SetFilePointer failed" throw ] when ;
270
271 M: windows (file-reader) ( path -- stream )
272     open-read <input-port> ;
273
274 M: windows (file-writer) ( path -- stream )
275     open-write <output-port> ;
276
277 M: windows (file-appender) ( path -- stream )
278     open-append <output-port> ;
279
280 SYMBOLS: +read-only+ +hidden+ +system+
281 +archive+ +device+ +normal+ +temporary+
282 +sparse-file+ +reparse-point+ +compressed+ +offline+
283 +not-content-indexed+ +encrypted+ ;
284
285 SLOT: attributes
286
287 : read-only? ( file-info -- ? )
288     attributes>> +read-only+ swap member? ;
289
290 : set-file-attributes ( path flags -- )
291     SetFileAttributes win32-error=0/f ;
292
293 : set-file-normal-attribute ( path -- )
294     FILE_ATTRIBUTE_NORMAL set-file-attributes ;
295
296 : win32-file-attribute ( n symbol attr -- )
297     rot mask? [ , ] [ drop ] if ;
298
299 : win32-file-attributes ( n -- seq )
300     [
301         {
302             [ +read-only+ FILE_ATTRIBUTE_READONLY win32-file-attribute ]
303             [ +hidden+ FILE_ATTRIBUTE_HIDDEN win32-file-attribute ]
304             [ +system+ FILE_ATTRIBUTE_SYSTEM win32-file-attribute ]
305             [ +directory+ FILE_ATTRIBUTE_DIRECTORY win32-file-attribute ]
306             [ +archive+ FILE_ATTRIBUTE_ARCHIVE win32-file-attribute ]
307             [ +device+ FILE_ATTRIBUTE_DEVICE win32-file-attribute ]
308             [ +normal+ FILE_ATTRIBUTE_NORMAL win32-file-attribute ]
309             [ +temporary+ FILE_ATTRIBUTE_TEMPORARY win32-file-attribute ]
310             [ +sparse-file+ FILE_ATTRIBUTE_SPARSE_FILE win32-file-attribute ]
311             [ +reparse-point+ FILE_ATTRIBUTE_REPARSE_POINT win32-file-attribute ]
312             [ +compressed+ FILE_ATTRIBUTE_COMPRESSED win32-file-attribute ]
313             [ +offline+ FILE_ATTRIBUTE_OFFLINE win32-file-attribute ]
314             [ +not-content-indexed+ FILE_ATTRIBUTE_NOT_CONTENT_INDEXED win32-file-attribute ]
315             [ +encrypted+ FILE_ATTRIBUTE_ENCRYPTED win32-file-attribute ]
316         } cleave
317     ] { } make ;
318
319 : win32-file-type ( n -- symbol )
320     FILE_ATTRIBUTE_DIRECTORY mask? +directory+ +regular-file+ ? ;
321
322 : (set-file-times) ( handle timestamp/f timestamp/f timestamp/f -- )
323     [ timestamp>FILETIME ] tri@
324     SetFileTime win32-error=0/f ;
325
326 M: windows cwd
327     MAX_UNICODE_PATH dup ushort <c-array>
328     [ GetCurrentDirectory win32-error=0/f ] keep alien>native-string ;
329
330 M: windows cd
331     SetCurrentDirectory win32-error=0/f ;
332
333 CONSTANT: unicode-prefix "\\\\?\\"
334
335 M: windows root-directory? ( path -- ? )
336     {
337         { [ dup empty? ] [ drop f ] }
338         { [ dup [ path-separator? ] all? ] [ drop t ] }
339         { [ dup trim-tail-separators { [ length 2 = ]
340           [ second CHAR: : = ] } 1&& ] [ drop t ] }
341         { [ dup unicode-prefix head? ]
342           [ trim-tail-separators length unicode-prefix length 2 + = ] }
343         [ drop f ]
344     } cond ;
345
346 : prepend-prefix ( string -- string' )
347     dup unicode-prefix head? [
348         unicode-prefix prepend
349     ] unless ;
350
351 TR: normalize-separators "/" "\\" ;
352
353 <PRIVATE
354
355 : unc-path? ( string -- ? )
356     [ "//" head? ] [ "\\\\" head? ] bi or ;
357
358 PRIVATE>
359
360 M: windows normalize-path ( string -- string' )
361     dup unc-path? [
362         normalize-separators
363     ] [
364         absolute-path
365         normalize-separators
366         prepend-prefix
367     ] if ;
368
369 M: windows CreateFile-flags ( DWORD -- DWORD )
370     FILE_FLAG_OVERLAPPED bitor ;
371
372 <PRIVATE
373
374 : windows-file-size ( path -- size )
375     normalize-path 0 WIN32_FILE_ATTRIBUTE_DATA <struct>
376     [ GetFileAttributesEx win32-error=0/f ] keep
377     [ nFileSizeLow>> ] [ nFileSizeHigh>> ] bi >64bit ;
378
379 PRIVATE>
380
381 M: windows open-append
382     [ dup windows-file-size ] [ drop 0 ] recover
383     [ (open-append) ] dip >>ptr ;
384
385 M: windows home
386     {
387         [ "HOMEDRIVE" os-env "HOMEPATH" os-env append-path ]
388         [ "USERPROFILE" os-env ]
389         [ my-documents ]
390     } 0|| ;