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 ascii assocs classes.struct combinators
5 combinators.short-circuit continuations destructors environment
6 io io.backend io.buffers io.files io.files.private
7 io.files.types io.pathnames io.pathnames.private io.ports
8 io.streams.c io.streams.null io.timeouts kernel libc literals
9 locals math math.bitwise namespaces sequences specialized-arrays
10 system threads tr vectors windows windows.errors windows.handles
11 windows.kernel32 windows.shell32 windows.time windows.types
12 windows.winsock splitting ;
13 SPECIALIZED-ARRAY: ushort
18 : CreateFile-flags ( DWORD -- DWORD )
19 flags{ FILE_FLAG_BACKUP_SEMANTICS FILE_FLAG_OVERLAPPED } bitor ;
21 HOOK: open-append os ( path -- win32-file )
23 TUPLE: win32-file < win32-handle ptr ;
25 : <win32-file> ( handle -- win32-file )
26 win32-file new-win32-handle ;
29 [ cancel-operation ] [ call-next-method ] bi ;
38 : default-security-attributes ( -- obj )
39 SECURITY_ATTRIBUTES <struct>
40 SECURITY_ATTRIBUTES heap-size >>nLength ;
43 hFile lpBuffer nNumberOfBytesToRead
44 lpNumberOfBytesRet lpOverlapped ;
46 C: <FileArgs> FileArgs
48 ! Global variable with assoc mapping overlapped to threads
49 SYMBOL: pending-overlapped
51 TUPLE: io-callback port thread ;
53 C: <io-callback> io-callback
55 : <completion-port> ( handle existing -- handle )
56 f 1 CreateIoCompletionPort dup win32-error=0/f ;
58 : <master-completion-port> ( -- handle )
59 INVALID_HANDLE_VALUE f <completion-port> ;
61 SYMBOL: master-completion-port
63 : add-completion ( win32-handle -- win32-handle )
64 dup handle>> master-completion-port get-global <completion-port> drop ;
66 : opened-file ( handle -- win32-file )
67 check-invalid-handle <win32-file> |dispose add-completion ;
70 { [ ERROR_HANDLE_EOF = ] [ ERROR_BROKEN_PIPE = ] } 1|| ;
72 : twiddle-thumbs ( overlapped port -- bytes-transferred )
75 [ self ] dip >c-ptr pending-overlapped get-global set-at
77 { [ dup integer? ] [ ] }
80 [ drop 0 ] [ throw-windows-error ] if
85 :: wait-for-overlapped ( nanos -- bytes-transferred overlapped error? )
86 nanos [ 1,000,000 /i ] [ INFINITE ] if* :> timeout
87 master-completion-port get-global
88 { int void* pointer: OVERLAPPED }
89 [ timeout GetQueuedCompletionStatus zero? ] with-out-parameters
90 :> ( error? bytes key overlapped )
91 bytes overlapped error? ;
93 : resume-callback ( result overlapped -- )
94 >c-ptr pending-overlapped get-global delete-at* drop resume-with ;
96 : handle-overlapped ( nanos -- ? )
99 [ drop GetLastError 1array ] dip resume-callback t
101 ] [ resume-callback t ] if ;
103 M: win32-handle cancel-operation
104 [ handle>> CancelIo win32-error=0/f ] unless-disposed ;
106 M: windows io-multiplex
107 handle-overlapped [ 0 io-multiplex ] when ;
110 <master-completion-port> master-completion-port set-global
111 H{ } clone pending-overlapped set-global ;
113 : (handle>file-size) ( handle -- n/f )
114 0 ulonglong <ref> [ GetFileSizeEx ] keep swap
115 [ drop f ] [ drop ulonglong deref ] if-zero ;
117 ! GetFileSizeEx errors with ERROR_INVALID_FUNCTION if handle is not seekable
118 : handle>file-size ( handle -- n/f )
120 GetLastError ERROR_INVALID_FUNCTION =
121 [ win32-error ] unless f
124 ERROR: seek-before-start n ;
126 : set-seek-ptr ( n handle -- )
127 [ dup 0 < [ seek-before-start ] when ] dip ptr<< ;
129 M: windows tell-handle ptr>> ;
131 M: windows seek-handle
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 ] }
139 M: windows can-seek-handle?
140 handle>> handle>file-size >boolean ;
142 M: windows handle-length
143 handle>> handle>file-size
144 dup { 0 f } member? [ drop f ] when ;
146 : file-error? ( n -- eof? )
149 { [ dup expected-io-error? ] [ drop f ] }
150 { [ dup eof? ] [ drop t ] }
151 [ throw-windows-error ]
155 : wait-for-file ( FileArgs n port -- n )
157 [ 2drop 0 ] [ [ lpOverlapped>> ] dip twiddle-thumbs ] if ;
159 : update-file-ptr ( n port -- )
160 handle>> dup ptr>> [ rot + >>ptr drop ] [ 2drop ] if* ;
162 : (make-overlapped) ( -- overlapped-ext )
163 OVERLAPPED malloc-struct &free ;
165 : make-overlapped ( handle -- overlapped-ext )
166 (make-overlapped) swap
167 ptr>> [ [ 32 bits >>offset ] [ -32 shift >>offset-high ] bi ] when* ;
169 : make-FileArgs ( port handle -- <FileArgs> )
170 [ nip check-disposed handle>> ]
172 [ buffer>> dup buffer-length 0 DWORD <ref> ] dip make-overlapped
175 : setup-write ( <FileArgs> -- hFile lpBuffer nNumberOfBytesToWrite lpNumberOfBytesWritten lpOverlapped )
178 [ lpBuffer>> [ buffer@ ] [ buffer-length ] bi ]
179 [ lpNumberOfBytesRet>> ]
183 : finish-write ( n port -- )
184 [ update-file-ptr ] [ buffer>> buffer-consume ] 2bi ;
187 [ make-FileArgs dup setup-write WriteFile ]
188 [ drop [ wait-for-file ] [ finish-write ] bi ] 2bi f ;
190 : setup-read ( <FileArgs> -- hFile lpBuffer nNumberOfBytesToRead lpNumberOfBytesRead lpOverlapped )
193 [ lpBuffer>> [ buffer-end ] [ buffer-capacity ] bi ]
194 [ lpNumberOfBytesRet>> ]
198 : finish-read ( n port -- )
199 [ update-file-ptr ] [ buffer>> buffer+ ] 2bi ;
202 [ make-FileArgs dup setup-read ReadFile ]
203 [ drop [ wait-for-file ] [ finish-read ] bi ] 2bi f ;
205 M: windows (wait-to-write)
206 [ dup handle>> drain ] with-destructors drop ;
208 M: windows (wait-to-read)
209 [ dup handle>> refill ] with-destructors drop ;
211 : make-fd-set ( socket -- fd_set )
212 fd_set <struct> swap 1array void* >c-array >>fd_array 1 >>fd_count ;
214 : select-sets ( socket event -- read-fds write-fds except-fds )
215 [ make-fd-set ] dip +input+ = [ f f ] [ f swap f ] if ;
217 CONSTANT: select-timeval S{ timeval { sec 0 } { usec 1000 } }
219 M: windows wait-for-fd
220 [ file>> handle>> 1 swap ] dip select-sets select-timeval
223 : console-app? ( -- ? ) GetConsoleWindow >boolean ;
225 M: windows init-stdio
228 [ null-reader null-writer null-writer set-stdio ] if ;
230 : open-file ( path access-mode create-mode flags -- handle )
232 [ share-mode default-security-attributes ] 2dip
233 CreateFile-flags f CreateFileW opened-file
236 : open-r/w ( path -- win32-file )
237 flags{ GENERIC_READ GENERIC_WRITE }
238 OPEN_EXISTING 0 open-file ;
240 : open-read ( path -- win32-file )
241 GENERIC_READ OPEN_EXISTING 0 open-file 0 >>ptr ;
243 : open-write ( path -- win32-file )
244 GENERIC_WRITE CREATE_ALWAYS 0 open-file 0 >>ptr ;
246 : (open-append) ( path -- win32-file )
247 GENERIC_WRITE OPEN_ALWAYS 0 open-file ;
249 : maybe-create-file ( path -- win32-file ? )
250 ! return true if file was just created
251 flags{ GENERIC_READ GENERIC_WRITE }
252 OPEN_ALWAYS 0 open-file
253 GetLastError ERROR_ALREADY_EXISTS = not ;
255 : set-file-pointer ( handle length method -- )
256 [ [ handle>> ] dip d>w/w LONG <ref> ] dip SetFilePointer
257 INVALID_SET_FILE_POINTER = [ "SetFilePointer failed" throw ] when ;
259 M: windows (file-reader)
260 open-read <input-port> ;
262 M: windows (file-writer)
263 open-write <output-port> ;
265 M: windows (file-appender)
266 open-append <output-port> ;
268 SYMBOLS: +read-only+ +hidden+ +system+
269 +archive+ +device+ +normal+ +temporary+
270 +sparse-file+ +reparse-point+ +compressed+ +offline+
271 +not-content-indexed+ +encrypted+ ;
275 : read-only? ( file-info -- ? )
276 attributes>> +read-only+ swap member? ;
278 : set-file-attributes ( path flags -- )
279 SetFileAttributes win32-error=0/f ;
281 : set-file-normal-attribute ( path -- )
282 FILE_ATTRIBUTE_NORMAL set-file-attributes ;
284 : win32-file-attributes ( n -- seq )
286 { +read-only+ $ FILE_ATTRIBUTE_READONLY }
287 { +hidden+ $ FILE_ATTRIBUTE_HIDDEN }
288 { +system+ $ FILE_ATTRIBUTE_SYSTEM }
289 { +directory+ $ FILE_ATTRIBUTE_DIRECTORY }
290 { +archive+ $ FILE_ATTRIBUTE_ARCHIVE }
291 { +device+ $ FILE_ATTRIBUTE_DEVICE }
292 { +normal+ $ FILE_ATTRIBUTE_NORMAL }
293 { +temporary+ $ FILE_ATTRIBUTE_TEMPORARY }
294 { +sparse-file+ $ FILE_ATTRIBUTE_SPARSE_FILE }
295 { +reparse-point+ $ FILE_ATTRIBUTE_REPARSE_POINT }
296 { +compressed+ $ FILE_ATTRIBUTE_COMPRESSED }
297 { +offline+ $ FILE_ATTRIBUTE_OFFLINE }
298 { +not-content-indexed+ $ FILE_ATTRIBUTE_NOT_CONTENT_INDEXED }
299 { +encrypted+ $ FILE_ATTRIBUTE_ENCRYPTED }
300 } [ mask? [ drop f ] unless ] with { } assoc>map sift ;
302 : win32-file-type ( n -- symbol )
303 FILE_ATTRIBUTE_DIRECTORY mask? +directory+ +regular-file+ ? ;
305 : (set-file-times) ( handle timestamp/f timestamp/f timestamp/f -- )
306 [ timestamp>FILETIME ] tri@
307 SetFileTime win32-error=0/f ;
310 MAX_UNICODE_PATH dup ushort <c-array>
311 [ GetCurrentDirectory win32-error=0/f ] keep alien>native-string ;
314 SetCurrentDirectory win32-error=0/f ;
316 CONSTANT: unicode-prefix "\\\\?\\"
318 M: windows root-directory?
320 { [ dup empty? ] [ drop f ] }
321 { [ dup [ path-separator? ] all? ] [ drop t ] }
322 { [ dup trim-tail-separators { [ length 2 = ]
323 [ second CHAR: : = ] } 1&& ] [ drop t ] }
324 { [ dup unicode-prefix head? ]
325 [ trim-tail-separators length unicode-prefix length 2 + = ] }
329 : prepend-unicode-prefix ( string -- string' )
330 dup unicode-prefix head? [
331 unicode-prefix prepend
334 : remove-unicode-prefix ( string -- string' )
335 unicode-prefix ?head drop ;
337 TR: normalize-separators "/" "\\" ;
341 : unc-path? ( string -- ? )
342 [ "//" head? ] [ "\\\\" head? ] bi or ;
346 M: windows canonicalize-path
347 remove-unicode-prefix canonicalize-path* ;
349 M: windows canonicalize-drive
350 dup windows-absolute-path? [ ":" split1 [ >upper ] dip ":" glue ] when ;
352 M: windows canonicalize-path-full canonicalize-path canonicalize-drive >windows-path ;
354 M: windows root-path remove-unicode-prefix root-path* ;
356 M: windows relative-path remove-unicode-prefix relative-path* ;
358 M: windows normalize-path
364 prepend-unicode-prefix
369 : windows-file-size ( path -- size )
370 normalize-path 0 WIN32_FILE_ATTRIBUTE_DATA <struct>
371 [ GetFileAttributesEx win32-error=0/f ] keep
372 [ nFileSizeLow>> ] [ nFileSizeHigh>> ] bi >64bit ;
376 M: windows open-append
377 [ dup windows-file-size ] [ drop 0 ] recover
378 [ (open-append) ] dip >>ptr ;
382 [ "HOMEDRIVE" os-env "HOMEPATH" os-env append-path ]
383 [ "USERPROFILE" os-env ]
387 : STREAM_DATA>out ( WIN32_FIND_STREAM_DATA -- pair/f )
388 [ cStreamName>> alien>native-string ]
389 [ StreamSize>> ] bi 2array ;
391 : file-streams-rest ( streams handle -- streams )
392 WIN32_FIND_STREAM_DATA <struct>
393 [ FindNextStream ] 2keep
395 GetLastError ERROR_HANDLE_EOF = [ win32-error ] unless
398 pick push file-streams-rest
401 : file-streams ( path -- streams )
403 FindStreamInfoStandard
404 WIN32_FIND_STREAM_DATA <struct>
406 [ FindFirstStream ] keepd
407 over INVALID_HANDLE_VALUE = [
410 1vector swap file-streams-rest
413 : alternate-file-streams ( path -- streams )
414 file-streams [ cStreamName>> alien>native-string "::$DATA" = ] reject ;
416 : alternate-file-streams? ( path -- ? )
417 alternate-file-streams empty? not ;