8fbd1cc461
FossilOrigin-Name: 3c9e31d6487b80fa601c94335efe98ee000f688cdf6686522007fea0fb141c05
199 lines
4.4 KiB
Forth
199 lines
4.4 KiB
Forth
# 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
|
|
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
|
|
#1 'file:W const
|
|
#2 'file:A const
|
|
#3 'file:R+ const
|
|
~~~
|
|
|
|
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
|
|
~~~
|