retroforth/interface/filesystem.retro

172 lines
3.5 KiB
Forth
Raw Normal View History

# 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.
~~~
{{
'Files var
:identify
@Files n:zero? 0; drop
#4 io:scan-for dup n:negative?
[ drop 'IO_DEVICE_TYPE_0004_NOT_FOUND s:put nl ]
[ !Files ] choose ;
---reveal---
:file:operation identify @Files 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 ;
~~~
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
'Buffer var
:-eof? (-f) @FID file:tell @Size lt? ;
:preserve (q-) &FID [ &Size &call v:preserve ] v:preserve ;
---reveal---
:file:read-line (f-s)
!FID
[ here dup !Buffer buffer:set
[ @FID file:read dup buffer:add
[ ASCII:CR eq? ] [ ASCII:LF eq? ] [ ASCII:NUL eq? ] tri or or ] until
buffer:get drop ] buffer:preserve
@Buffer ;
: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 ;
~~~