1 ! Copyright (C) 2004, 2008 Slava Pestov, Daniel Ehrenberg.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: io.backend io.files.private io hashtables kernel math
4 memory namespaces sequences strings assocs arrays definitions
5 system combinators splitting sbufs continuations destructors
6 io.encodings io.encodings.binary init accessors math.order ;
9 HOOK: (file-reader) io-backend ( path -- stream )
11 HOOK: (file-writer) io-backend ( path -- stream )
13 HOOK: (file-appender) io-backend ( path -- stream )
15 : <file-reader> ( path encoding -- stream )
16 swap normalize-path (file-reader) swap <decoder> ;
18 : <file-writer> ( path encoding -- stream )
19 swap normalize-path (file-writer) swap <encoder> ;
21 : <file-appender> ( path encoding -- stream )
22 swap normalize-path (file-appender) swap <encoder> ;
24 : file-lines ( path encoding -- seq )
27 : with-file-reader ( path encoding quot -- )
28 >r <file-reader> r> with-input-stream ; inline
30 : file-contents ( path encoding -- str )
31 <file-reader> contents ;
33 : with-file-writer ( path encoding quot -- )
34 >r <file-writer> r> with-output-stream ; inline
36 : set-file-lines ( seq path encoding -- )
37 [ [ print ] each ] with-file-writer ;
39 : set-file-contents ( str path encoding -- )
40 [ write ] with-file-writer ;
42 : with-file-appender ( path encoding quot -- )
43 >r <file-appender> r> with-output-stream ; inline
46 : path-separator? ( ch -- ? ) os windows? "/\\" "/" ? member? ;
48 : path-separator ( -- string ) os windows? "\\" "/" ? ;
50 : right-trim-separators ( str -- newstr )
51 [ path-separator? ] right-trim ;
53 : left-trim-separators ( str -- newstr )
54 [ path-separator? ] left-trim ;
56 : last-path-separator ( path -- n ? )
57 [ length 1- ] keep [ path-separator? ] find-last-from ;
59 HOOK: root-directory? io-backend ( path -- ? )
61 M: object root-directory? ( path -- ? )
62 dup empty? [ drop f ] [ [ path-separator? ] all? ] if ;
64 ERROR: no-parent-directory path ;
66 : parent-directory ( path -- parent )
69 dup last-path-separator [
74 { "" "." ".." } member? [
81 : head-path-separator? ( path1 ? -- ?' )
83 dup empty? [ drop t ] [ first path-separator? ] if
88 : head.? ( path -- ? ) "." ?head head-path-separator? ;
90 : head..? ( path -- ? ) ".." ?head head-path-separator? ;
92 : append-path-empty ( path1 path2 -- path' )
95 rest left-trim-separators append-path-empty
97 { [ dup head..? ] [ drop no-parent-directory ] }
103 : windows-absolute-path? ( path -- path ? )
105 { [ dup "\\\\?\\" head? ] [ t ] }
106 { [ dup length 2 < ] [ f ] }
107 { [ dup second CHAR: : = ] [ t ] }
111 : absolute-path? ( path -- ? )
113 { [ dup empty? ] [ f ] }
114 { [ dup "resource:" head? ] [ t ] }
115 { [ os windows? ] [ windows-absolute-path? ] }
116 { [ dup first path-separator? ] [ t ] }
120 : append-path ( str1 str2 -- str )
122 { [ over empty? ] [ append-path-empty ] }
123 { [ dup empty? ] [ drop ] }
124 { [ dup absolute-path? ] [ nip ] }
125 { [ dup head.? ] [ rest left-trim-separators append-path ] }
127 2 tail left-trim-separators
128 >r parent-directory r> append-path
130 { [ over absolute-path? over first path-separator? and ] [
134 >r right-trim-separators "/" r>
135 left-trim-separators 3append
139 : prepend-path ( str1 str2 -- str )
140 swap append-path ; inline
142 : file-name ( path -- string )
143 dup root-directory? [
144 right-trim-separators
145 dup last-path-separator [ 1+ tail ] [
146 drop "resource:" ?head [ file-name ] when
150 : file-extension ( filename -- extension )
151 "." last-split1 nip ;
154 TUPLE: file-info type size permissions modified ;
156 HOOK: file-info io-backend ( path -- info )
159 HOOK: link-info io-backend ( path -- info )
161 HOOK: make-link io-backend ( target symlink -- )
163 HOOK: read-link io-backend ( symlink -- path )
165 : copy-link ( target symlink -- )
166 >r read-link r> make-link ;
168 SYMBOL: +regular-file+
170 SYMBOL: +symbolic-link+
171 SYMBOL: +character-device+
172 SYMBOL: +block-device+
178 : exists? ( path -- ? ) normalize-path (exists?) ;
180 : directory? ( file-info -- ? ) type>> +directory+ = ;
184 HOOK: cd io-backend ( path -- )
186 HOOK: cwd io-backend ( -- path )
188 M: object cwd ( -- path ) "." ;
192 SYMBOL: current-directory
194 [ cwd current-directory set-global ] "io.files" add-init-hook
196 : resource-path ( path -- newpath )
197 "resource-path" get [ image parent-directory ] unless*
200 : (normalize-path) ( path -- path' )
202 left-trim-separators resource-path
205 current-directory get prepend-path
208 M: object normalize-path ( path -- path' )
211 : set-current-directory ( path -- )
212 (normalize-path) current-directory set ;
214 : with-directory ( path quot -- )
215 >r (normalize-path) current-directory r> with-variable ; inline
217 ! Creating directories
218 HOOK: make-directory io-backend ( path -- )
220 : make-directories ( path -- )
221 normalize-path right-trim-separators {
222 { [ dup "." = ] [ ] }
223 { [ dup root-directory? ] [ ] }
224 { [ dup empty? ] [ ] }
225 { [ dup exists? ] [ ] }
227 dup parent-directory make-directories
233 : fixup-directory ( path seq -- newseq )
236 [ tuck append-path file-info directory? 2array ] [ nip ] if
238 [ first { "." ".." } member? not ] filter ;
240 : directory ( path -- seq )
241 normalize-directory dup (directory) fixup-directory ;
243 : directory* ( path -- seq )
244 dup directory [ first2 >r append-path r> 2array ] with map ;
247 HOOK: touch-file io-backend ( path -- )
250 HOOK: delete-file io-backend ( path -- )
252 HOOK: delete-directory io-backend ( path -- )
254 : delete-tree ( path -- )
255 dup link-info type>> +directory+ = [
257 [ first delete-tree ] each
258 ] with-directory delete-directory
263 : to-directory ( from to -- from to' )
264 over file-name append-path ;
266 ! Moving and renaming files
267 HOOK: move-file io-backend ( from to -- )
269 : move-file-into ( from to -- )
270 to-directory move-file ;
272 : move-files-into ( files to -- )
273 [ move-file-into ] curry each ;
276 HOOK: copy-file io-backend ( from to -- )
279 dup parent-directory make-directories
280 binary <file-writer> [
281 swap binary <file-reader> [
286 : copy-file-into ( from to -- )
287 to-directory copy-file ;
289 : copy-files-into ( files to -- )
290 [ copy-file-into ] curry each ;
292 DEFER: copy-tree-into
294 : copy-tree ( from to -- )
296 over link-info type>>
298 { +symbolic-link+ [ copy-link ] }
300 >r dup directory r> rot [
301 [ >r first r> copy-tree-into ] curry each
307 : copy-tree-into ( from to -- )
308 to-directory copy-tree ;
310 : copy-trees-into ( files to -- )
311 [ copy-tree-into ] curry each ;
315 : temp-directory ( -- path )
316 "temp" resource-path dup make-directories ;
318 : temp-file ( name -- path )
319 temp-directory prepend-path ;
321 ! Pathname presentations
322 TUPLE: pathname string ;
324 C: <pathname> pathname
326 M: pathname <=> [ pathname-string ] compare ;
329 HOOK: home os ( -- dir )
331 M: winnt home "USERPROFILE" os-env ;
333 M: wince home "" resource-path ;
335 M: unix home "HOME" os-env ;