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 io
6 io.backend io.binary io.buffers io.files io.files.private
7 io.files.types io.pathnames io.ports io.streams.c io.streams.null
8 io.timeouts kernel libc literals locals math math.bitwise namespaces
9 sequences specialized-arrays system threads tr windows windows.errors
10 windows.handles windows.kernel32 windows.shell32 windows.time
11 windows.types windows.winsock ;
12 SPECIALIZED-ARRAY: ushort
17 HOOK: CreateFile-flags io-backend ( DWORD -- DWORD )
18 HOOK: open-append os ( path -- win32-file )
20 TUPLE: win32-file < win32-handle ptr ;
22 : <win32-file> ( handle -- win32-file )
23 win32-file new-win32-handle ;
26 [ cancel-operation ] [ call-next-method ] bi ;
35 : default-security-attributes ( -- obj )
36 SECURITY_ATTRIBUTES <struct>
37 SECURITY_ATTRIBUTES heap-size >>nLength ;
40 hFile lpBuffer nNumberOfBytesToRead
41 lpNumberOfBytesRet lpOverlapped ;
43 C: <FileArgs> FileArgs
45 ! Global variable with assoc mapping overlapped to threads
46 SYMBOL: pending-overlapped
48 TUPLE: io-callback port thread ;
50 C: <io-callback> io-callback
52 : <completion-port> ( handle existing -- handle )
53 f 1 CreateIoCompletionPort dup win32-error=0/f ;
55 : <master-completion-port> ( -- handle )
56 INVALID_HANDLE_VALUE f <completion-port> ;
58 SYMBOL: master-completion-port
60 : add-completion ( win32-handle -- win32-handle )
61 dup handle>> master-completion-port get-global <completion-port> drop ;
63 : opened-file ( handle -- win32-file )
64 check-invalid-handle <win32-file> |dispose add-completion ;
67 { [ ERROR_HANDLE_EOF = ] [ ERROR_BROKEN_PIPE = ] } 1|| ;
69 : twiddle-thumbs ( overlapped port -- bytes-transferred )
72 [ self ] dip >c-ptr pending-overlapped get-global set-at
74 { [ dup integer? ] [ ] }
77 [ drop 0 ] [ n>win32-error-string throw ] if
82 :: wait-for-overlapped ( nanos -- bytes-transferred overlapped error? )
83 nanos [ 1,000,000 /i ] [ INFINITE ] if* :> timeout
84 master-completion-port get-global
85 { int void* pointer: OVERLAPPED }
86 [ timeout GetQueuedCompletionStatus zero? ] with-out-parameters
87 :> ( error? bytes key overlapped )
88 bytes overlapped error? ;
90 : resume-callback ( result overlapped -- )
91 >c-ptr pending-overlapped get-global delete-at* drop resume-with ;
93 : handle-overlapped ( nanos -- ? )
96 [ drop GetLastError 1array ] dip resume-callback t
98 ] [ resume-callback t ] if ;
100 M: win32-handle cancel-operation
101 [ handle>> CancelIo win32-error=0/f ] unless-disposed ;
103 M: windows io-multiplex ( nanos -- )
104 handle-overlapped [ 0 io-multiplex ] when ;
106 M: windows init-io ( -- )
107 <master-completion-port> master-completion-port set-global
108 H{ } clone pending-overlapped set-global ;
110 : (handle>file-size) ( handle -- n/f )
111 0 ulonglong <ref> [ GetFileSizeEx ] keep swap
112 [ drop f ] [ drop ulonglong deref ] if-zero ;
114 ! GetFileSizeEx errors with ERROR_INVALID_FUNCTION if handle is not seekable
115 : handle>file-size ( handle -- n/f )
117 GetLastError ERROR_INVALID_FUNCTION =
118 [ f ] [ throw-win32-error ] if
121 ERROR: seek-before-start n ;
123 : set-seek-ptr ( n handle -- )
124 [ dup 0 < [ seek-before-start ] when ] dip ptr<< ;
126 M: windows tell-handle ( handle -- n ) ptr>> ;
128 M: windows seek-handle ( n seek-type handle -- )
130 { seek-absolute [ set-seek-ptr ] }
131 { seek-relative [ [ ptr>> + ] keep set-seek-ptr ] }
132 { seek-end [ [ handle>> handle>file-size + ] keep set-seek-ptr ] }
136 M: windows can-seek-handle? ( handle -- ? )
137 handle>> handle>file-size >boolean ;
139 M: windows handle-length ( handle -- n/f )
140 handle>> handle>file-size
141 dup { 0 f } member? [ drop f ] when ;
143 : file-error? ( n -- eof? )
146 { [ dup expected-io-error? ] [ drop f ] }
147 { [ dup eof? ] [ drop t ] }
148 [ n>win32-error-string throw ]
152 : wait-for-file ( FileArgs n port -- n )
154 [ 2drop 0 ] [ [ lpOverlapped>> ] dip twiddle-thumbs ] if ;
156 : update-file-ptr ( n port -- )
157 handle>> dup ptr>> [ rot + >>ptr drop ] [ 2drop ] if* ;
159 : (make-overlapped) ( -- overlapped-ext )
160 OVERLAPPED malloc-struct &free ;
162 : make-overlapped ( handle -- overlapped-ext )
163 (make-overlapped) swap
164 ptr>> [ [ 32 bits >>offset ] [ -32 shift >>offset-high ] bi ] when* ;
166 : make-FileArgs ( port handle -- <FileArgs> )
167 [ nip check-disposed handle>> ]
169 [ buffer>> dup buffer-length 0 DWORD <ref> ] dip make-overlapped
172 : setup-write ( <FileArgs> -- hFile lpBuffer nNumberOfBytesToWrite lpNumberOfBytesWritten lpOverlapped )
175 [ lpBuffer>> [ buffer@ ] [ buffer-length ] bi ]
176 [ lpNumberOfBytesRet>> ]
180 : finish-write ( n port -- )
181 [ update-file-ptr ] [ buffer>> buffer-consume ] 2bi ;
183 M: object drain ( port handle -- event/f )
184 [ make-FileArgs dup setup-write WriteFile ]
185 [ drop [ wait-for-file ] [ finish-write ] bi ] 2bi f ;
187 : setup-read ( <FileArgs> -- hFile lpBuffer nNumberOfBytesToRead lpNumberOfBytesRead lpOverlapped )
190 [ lpBuffer>> [ buffer-end ] [ buffer-capacity ] bi ]
191 [ lpNumberOfBytesRet>> ]
195 : finish-read ( n port -- )
196 [ update-file-ptr ] [ buffer>> buffer+ ] 2bi ;
198 M: object refill ( port handle -- event/f )
199 [ make-FileArgs dup setup-read ReadFile ]
200 [ drop [ wait-for-file ] [ finish-read ] bi ] 2bi f ;
202 M: windows (wait-to-write) ( port -- )
203 [ dup handle>> drain ] with-destructors drop ;
205 M: windows (wait-to-read) ( port -- )
206 [ dup handle>> refill ] with-destructors drop ;
208 : make-fd-set ( socket -- fd_set )
209 fd_set <struct> swap 1array void* >c-array >>fd_array 1 >>fd_count ;
211 : select-sets ( socket event -- read-fds write-fds except-fds )
212 [ make-fd-set ] dip +input+ = [ f f ] [ f swap f ] if ;
214 CONSTANT: select-timeval S{ timeval { sec 0 } { usec 1000 } }
216 M: windows wait-for-fd ( handle event -- )
217 [ file>> handle>> 1 swap ] dip select-sets select-timeval
220 : console-app? ( -- ? ) GetConsoleWindow >boolean ;
222 M: windows init-stdio
225 [ null-reader null-writer null-writer set-stdio ] if ;
227 : open-file ( path access-mode create-mode flags -- handle )
229 [ share-mode default-security-attributes ] 2dip
230 CreateFile-flags f CreateFile opened-file
233 : open-r/w ( path -- win32-file )
234 flags{ GENERIC_READ GENERIC_WRITE }
235 OPEN_EXISTING 0 open-file ;
237 : open-read ( path -- win32-file )
238 GENERIC_READ OPEN_EXISTING 0 open-file 0 >>ptr ;
240 : open-write ( path -- win32-file )
241 GENERIC_WRITE CREATE_ALWAYS 0 open-file 0 >>ptr ;
243 : (open-append) ( path -- win32-file )
244 GENERIC_WRITE OPEN_ALWAYS 0 open-file ;
246 : open-existing ( path -- win32-file )
247 flags{ GENERIC_READ GENERIC_WRITE }
251 FILE_FLAG_BACKUP_SEMANTICS
252 f CreateFileW dup win32-error=0/f <win32-file> ;
254 : maybe-create-file ( path -- win32-file ? )
255 ! return true if file was just created
256 flags{ GENERIC_READ GENERIC_WRITE }
261 f CreateFileW dup win32-error=0/f <win32-file>
262 GetLastError ERROR_ALREADY_EXISTS = not ;
264 : set-file-pointer ( handle length method -- )
265 [ [ handle>> ] dip d>w/w LONG <ref> ] dip SetFilePointer
266 INVALID_SET_FILE_POINTER = [ "SetFilePointer failed" throw ] when ;
268 M: windows (file-reader) ( path -- stream )
269 open-read <input-port> ;
271 M: windows (file-writer) ( path -- stream )
272 open-write <output-port> ;
274 M: windows (file-appender) ( path -- stream )
275 open-append <output-port> ;
277 SYMBOLS: +read-only+ +hidden+ +system+
278 +archive+ +device+ +normal+ +temporary+
279 +sparse-file+ +reparse-point+ +compressed+ +offline+
280 +not-content-indexed+ +encrypted+ ;
284 : read-only? ( file-info -- ? )
285 attributes>> +read-only+ swap member? ;
287 : set-file-attributes ( path flags -- )
288 SetFileAttributes win32-error=0/f ;
290 : set-file-normal-attribute ( path -- )
291 FILE_ATTRIBUTE_NORMAL set-file-attributes ;
293 : win32-file-attributes ( n -- seq )
295 { +read-only+ FILE_ATTRIBUTE_READONLY }
296 { +hidden+ FILE_ATTRIBUTE_HIDDEN }
297 { +system+ FILE_ATTRIBUTE_SYSTEM }
298 { +directory+ FILE_ATTRIBUTE_DIRECTORY }
299 { +archive+ FILE_ATTRIBUTE_ARCHIVE }
300 { +device+ FILE_ATTRIBUTE_DEVICE }
301 { +normal+ FILE_ATTRIBUTE_NORMAL }
302 { +temporary+ FILE_ATTRIBUTE_TEMPORARY }
303 { +sparse-file+ FILE_ATTRIBUTE_SPARSE_FILE }
304 { +reparse-point+ FILE_ATTRIBUTE_REPARSE_POINT }
305 { +compressed+ FILE_ATTRIBUTE_COMPRESSED }
306 { +offline+ FILE_ATTRIBUTE_OFFLINE }
307 { +not-content-indexed+ FILE_ATTRIBUTE_NOT_CONTENT_INDEXED }
308 { +encrypted+ FILE_ATTRIBUTE_ENCRYPTED }
310 [ execute( -- y ) mask? [ drop f ] unless ] with { } assoc>map sift ;
312 : win32-file-type ( n -- symbol )
313 FILE_ATTRIBUTE_DIRECTORY mask? +directory+ +regular-file+ ? ;
315 : (set-file-times) ( handle timestamp/f timestamp/f timestamp/f -- )
316 [ timestamp>FILETIME ] tri@
317 SetFileTime win32-error=0/f ;
320 MAX_UNICODE_PATH dup ushort <c-array>
321 [ GetCurrentDirectory win32-error=0/f ] keep alien>native-string ;
324 SetCurrentDirectory win32-error=0/f ;
326 CONSTANT: unicode-prefix "\\\\?\\"
328 M: windows root-directory? ( path -- ? )
330 { [ dup empty? ] [ drop f ] }
331 { [ dup [ path-separator? ] all? ] [ drop t ] }
332 { [ dup trim-tail-separators { [ length 2 = ]
333 [ second CHAR: : = ] } 1&& ] [ drop t ] }
334 { [ dup unicode-prefix head? ]
335 [ trim-tail-separators length unicode-prefix length 2 + = ] }
339 : prepend-prefix ( string -- string' )
340 dup unicode-prefix head? [
341 unicode-prefix prepend
344 TR: normalize-separators "/" "\\" ;
348 : unc-path? ( string -- ? )
349 [ "//" head? ] [ "\\\\" head? ] bi or ;
353 M: windows normalize-path ( string -- string' )
362 M: windows CreateFile-flags ( DWORD -- DWORD )
363 FILE_FLAG_OVERLAPPED bitor ;
367 : windows-file-size ( path -- size )
368 normalize-path 0 WIN32_FILE_ATTRIBUTE_DATA <struct>
369 [ GetFileAttributesEx win32-error=0/f ] keep
370 [ nFileSizeLow>> ] [ nFileSizeHigh>> ] bi >64bit ;
374 M: windows open-append
375 [ dup windows-file-size ] [ drop 0 ] recover
376 [ (open-append) ] dip >>ptr ;
380 [ "HOMEDRIVE" os-env "HOMEPATH" os-env append-path ]
381 [ "USERPROFILE" os-env ]