# File I/O The file device (device type 4) adds support for generic file I/O that is similar to the C standard library on Unix. On a Unix host these are very thin wrappers over fopen(), fclose(), etc. For non Unix hosts, implementing these may take much more work, and it may be preferable to design a file I/O device that models the host expectations. ~~~ :file:operation (:-n) DEVICE:FILES io:scan-for dup n:negative? [ drop 'Error:_files_device_not_found s:put nl ] if; io:invoke ; ~~~ First up, constants for the file modes. | # | Used For | | -- | ---------------------------- | | R | Mode for READING | | W | Mode for WRITING | | A | Mode for APPENDING | | R+ | Mode for READING and WRITING | ~~~ #0 'file:R const (:-n) #1 'file:W const (:-n) #2 'file:A const (:-n) #3 'file:R+ const (:-n) ~~~ For opening a file, provide the file name and mode. This will return a number identifying the file handle. ~~~ :file:open (:sm-h) #0 file:operation ; ~~~ Given a file handle, close the file. ~~~ :file:close (:h-) #1 file:operation ; ~~~ Given a file handle, read a character. ~~~ :file:read (:h-c) #2 file:operation ; ~~~ Write a character to an open file. ~~~ :file:write (:ch-) #3 file:operation ; ~~~ Return the current pointer within a file. ~~~ :file:tell (:h-n) #4 file:operation ; ~~~ Move the file pointer to the specified location. ~~~ :file:seek (:nh-) #5 file:operation ; ~~~ Return the size of the opened file. ~~~ :file:size (:h-n) #6 file:operation ; ~~~ Given a file name, delete the file. ~~~ :file:delete (:s-) #7 file:operation ; ~~~ Flush pending writes to disk. ~~~ :file:flush (:f-) #8 file:operation ; ~~~ ~~~ :file:read/bytes (:pnf-) #9 file:operation ; :file:write/bytes (:pnf-) #10 file:operation ; :file:read/c (:h-c) #11 file:operation ; :file:write/c (:ch-c) #12 file:operation ; ~~~ Given a file name, return `TRUE` if it exists or `FALSE` otherwise. ~~~ :file:exists? (:s-f) file:R file:open dup n:-zero? [ file:close TRUE ] [ drop FALSE ] choose ; ~~~ ~~~ :file:open-for-reading (:s-nn) file:R file:open dup file:size swap ; :file:open-for-append (:s-nn) file:A file:open dup file:size swap ; :file:open-for-writing (:s-n) file:W file:open ; ~~~ With that out of the way, we can begin building higher level functionality. The first of these reads a line from the file. This is read to `here`; move it somewhere safe if you need to keep it around. The second goes with it. The `for-each-line` word will invoke a combinator once for each line in a file. This makes some things trivial. E.g., a simple 'cat' implementation could be as simple as: 'filename [ s:put nl ] file:for-each-line ~~~ {{ 'FID var 'Size var 'Action var :-eof? (-f) @FID file:tell @Size lt? ; :preserve (q-) &FID [ &Size &call v:preserve ] v:preserve ; ---reveal--- :file:read-line (:f-s) here swap #13 file:operation here ; :file:for-each-line (:sq-) [ !Action file:open-for-reading !FID !Size [ @FID file:read-line @Action call -eof? ] while @FID file:close ] preserve ; }} ~~~ `file:slurp` reads a file into a buffer. ~~~ {{ 'FID var ---reveal--- :file:slurp (:as-) [ swap buffer:set file:open-for-reading !FID [ @FID file:read buffer:add ] times @FID file:close ] buffer:preserve ; }} ~~~ `file:spew` writes a string to a file. ~~~ :file:spew (:ss-) file:open-for-writing swap [ over file:write ] s:for-each file:close ; ~~~ ## d:source ~~~ 'interface/filesystem.retro s:dedup dup 'file:spew d:set-source dup 'file:slurp d:set-source dup 'file:for-each-line d:set-source dup 'file:read-line d:set-source dup 'file:open-for-writing d:set-source dup 'file:open-for-append d:set-source dup 'file:open-for-reading d:set-source dup 'file:exists? d:set-source dup 'file:flush d:set-source dup 'file:delete d:set-source dup 'file:size d:set-source dup 'file:seek d:set-source dup 'file:tell d:set-source dup 'file:write d:set-source dup 'file:read d:set-source dup 'file:close d:set-source dup 'file:open d:set-source dup 'file:R+ d:set-source dup 'file:A d:set-source dup 'file:W d:set-source dup 'file:R d:set-source dup 'file:operation d:set-source dup 'file:write/bytes d:set-source dup 'file:read/bytes d:set-source dup 'file:write/c d:set-source dup 'file:read/c d:set-source drop ~~~