duskos

dusk os fork
git clone git://git.alexwennerberg.com/duskos
Log | Files | Refs | README | LICENSE

commit 4450bf3d272dd931dcab64ad0364251261e7624c
parent 09e5373e839e928fdad262c792e665c4d330bc70
Author: Virgil Dupras <hsoft@hardcoded.net>
Date:   Thu,  8 Sep 2022 07:59:43 -0400

lib/io: introduce StdIn and StdOut

See doc/io. This makes I/O redirection a bit more straightforward because we
can set handles directly, removing the need for the [f<] hack in sys/file.

Diffstat:
Mfs/app/cos/tools/blkpack.c | 1+
Mfs/doc/io.txt | 35++++++++++++++++++++++++++---------
Mfs/lib/io.fs | 23+++++++++++++++++------
Mfs/sys/file.fs | 18+++---------------
Mfs/tests/cc/ast.fs | 3+--
Mfs/xcomp/bootlo.fs | 8++++----
6 files changed, 52 insertions(+), 36 deletions(-)

diff --git a/fs/app/cos/tools/blkpack.c b/fs/app/cos/tools/blkpack.c @@ -3,6 +3,7 @@ // TODO: string scanning // TODO: add NULL const // TODO: add strlen() (alias to c@) +// TODO: add \n in string literals // TODO: do multiple "return" paths work? // TODO: can we "return" early in a void function? // TODO: signed arithmetics diff --git a/fs/doc/io.txt b/fs/doc/io.txt @@ -7,7 +7,7 @@ needed, all states needed to fulfill the API's specifications. I/O operations are byte based. -When "EOF" is mentioned, it means "end of file". In same case, it's a misnomer, +When "EOF" is mentioned, it means "end of file". In some cases, it's a misnomer, but the idea is the same: if all possible data from the I/O source have been consumed, we're in EOF condition. @@ -17,15 +17,15 @@ The structure of these handle will vary depending on the driver or filesystem, but they all start with the same structure, which is a list of 4b pointers to words, in this order: -readbuf -writebuf -flush +:readbuf +:writebuf +:flush These words have the following API. In all words, "hdl" is a pointer to a IO handle. If the handle has no concept of "flush", it can point to "drop" (as a noop). -readbuf ( n hdl -- a? read-n ) +:readbuf ( n hdl -- a? read-n ) Try to read "n" bytes from file from current position. Unless EOF is reached, at least 1 byte must be read, but otherwise "read-n" can be lower than n, even if that doesn't take the handle to EOF. The idea is to take the handle, at @@ -34,7 +34,7 @@ readbuf ( n hdl -- a? read-n ) After the read operation, advance current position by "read-n" bytes. -writebuf ( a n hdl -- written-n ) +:writebuf ( a n hdl -- written-n ) Try to write "n" bytes from buffer "a" to file from current position, growing the file if needed. The idea is the same as with freadbuf: the filesystem can proceed as is best for its implementation, as long as it writes at least 1 @@ -42,21 +42,38 @@ writebuf ( a n hdl -- written-n ) After the write operation, advance current position by "written-n" bytes. -flush ( hdl -- ) +:flush ( hdl -- ) If the handle's buffer is "dirty" (has been changed compared to the permanent storage it represents), save that data to permanent storage and flag the handle as clean. +In addition to those device-specific words, all IO structures share these +convenience words: + +: write ( a n hdl -- ) + Repeatedly call :writebuf until all requested bytes have been written. + ## Generic API From the base API of IO handlers, lib/io has these generic words: -getc ( hdl -- b ) +getc ( hdl -- c ) Read 1 byte from hdl an return it. Advance position by 1 byte. Return -1 on EOF. -putc ( b hdl -- ) +putc ( c hdl -- ) Write 1 byte to hdl. Advance position by 1 byte. Aborts if unable to write. It also has, for each IO handler word, a helper to directly call it. For example, "hdl flush" simply does "hdl 8 + @ execute". + +## StdIn and StdOut + +lib/io defines StdIn and StdOut values. These point to IO structures and, at +initialization, point to ConsoleIn (an IO structure that reads from in<) and +ConsoleOut (an IO structure that writes to emit). + +You can change those values to redirect I/Os for code that uses these values. + +There is also stdin, which is a shortcut for "StdIn getc" and stdout, for +"StdOut putc". diff --git a/fs/lib/io.fs b/fs/lib/io.fs @@ -1,19 +1,31 @@ \ Input/Output. See doc/io -: getc ( hdl -- c ) 1 swap IO :readbuf if c@ else -1 ( EOF ) then ; - -alias in< stdin ( -- c ) - create _buf( $100 allot here value _)buf +: getc ( hdl -- c ) 1 swap IO :readbuf if c@ else -1 ( EOF ) then ; +: putc ( c hdl -- ) swap _buf( c! _buf( 1 rot IO :writebuf ; + +\ StdIO +: _err abort" Invalid I/O" ; +: _readbuf ( a hdl -- a? read-n ) + drop in< dup 0< if drop 0 else _buf( c! _buf( 1 then ; +create ConsoleIn ' _readbuf , ' _err , ' _err , +ConsoleIn value StdIn +: stdin StdIn getc ; +: _writebuf ( a n hdl -- written-n ) 2drop c@ emit 1 ; +create ConsoleOut ' _err , ' _writebuf , ' drop , +ConsoleOut value StdOut +: stdout StdOut putc ; +: stdio$ ConsoleIn to StdIn ConsoleOut to StdOut ; + \ read stdin for a maximum of STR_MAXSZ-1 characters until LF is encountered, \ then return a string representing that read line. The LF character is not \ included. Aborts on LNSZ overflow. : readline ( -- str ) _buf( 1+ >r begin ( ) \ V1=buf V1 _)buf = if abort" readline overflow" then - in< dup LF = not while 8b to!+ V1 repeat drop + stdin dup LF = not while 8b to!+ V1 repeat drop r> _buf( - 1- ( len ) _buf( c! _buf( ( str ) ; : _iowrite ( a n self -- ) >r begin ( a n ) ?dup while @@ -21,4 +33,3 @@ here value _)buf ( a n written-n ) tuck - ( a written-n new-n ) rot> + swap repeat ( a ) drop rdrop ; current to IO :write - diff --git a/fs/sys/file.fs b/fs/sys/file.fs @@ -112,21 +112,9 @@ Path _curpath structbind Path curpath : f<< word curpath :find# Path :fload ; : ?f<< word curpath :find# dup Path :floaded? if drop else Path :fload then ; -?f<< /lib/scratch.fs -\ We need a private scratchpad here because some cursors can be quite -\ long-lived. If we use the system scratchpad, short-lived data will overwrite -\ our cursors. -create _filespad $200 scratchpad$ -_filespad structbind Scratchpad filespad - -\ This creates a "f<" reader with the file descriptor embedded in it. This -\ allows for a straightforward override of input/output words. -: [f<] ( curfd -- word ) - filespad :[ litn compile getc exit, filespad :] ; - : require word dup curpath :find# Path :floaded? not if stype abort" required" else drop then ; : with-stdin-file ( w str -- ) - to@ stdin >r curpath :find# Path :open dup >r ( w hdl ) - [f<] to stdin execute - r> File :close r> to stdin ; + StdIn >r curpath :find# Path :open dup >r ( w hdl ) + to StdIn execute + r> File :close r> to StdIn ; diff --git a/fs/tests/cc/ast.fs b/fs/tests/cc/ast.fs @@ -2,8 +2,7 @@ ?f<< cc/ast.fs testbegin \ Tests for the C compiler AST -S" tests/cc/test.c" curpath :find# Path :open dup [f<] to stdin parseast -File :close +' parseast S" tests/cc/test.c" with-stdin-file curunit Node firstchild dup Node id AST_FUNCTION #eq ( fnode ) dup Function name S" retconst" s= # diff --git a/fs/xcomp/bootlo.fs b/fs/xcomp/bootlo.fs @@ -265,11 +265,11 @@ struct[ Drive \ I/O API \ Anticipating lib/io struct[ IO - smethod :readbuf - smethod :writebuf - smethod :flush + smethod :readbuf ( n hdl -- a? read-n ) + smethod :writebuf ( a n hdl -- written-n ) + smethod :flush ( hdl -- ) \ forward declarations - alias abort :write + alias abort :write ( a n hdl -- ) ]struct \ File API