# Input/Output

The io/stream subsystem offers a unified API to read and write from and to any
device or filesystem. All words in this API revolve around a structure that we
call the "stream". This stream represent one "place" we read and write to,
and if needed save any state needed to fulfill the API's specification.

I/O operations are byte based.

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.

## API

### Struct

struct Stream
  method readbuf ( n stream -- a? read-n )
  method writebuf ( a n stream -- written-n )
  method close ( stream -- )
endstruct

readbuf ( n stream -- a? read-n )
  Try to read "n" bytes from stream at 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 stream to EOF. The idea is to take the stream, at
  most, to the end of its internal buffer. Return the number of bytes read and,
  if it was nonzero, return an address to the beginning of that buffer.

  After the read operation, advance current position by "read-n" bytes.

  The returned buffer must stay valid as long as a "buffer invalidating"
  operation isn't made on the same stream, that is, readbuf, writebuf, close.

writebuf ( a n stream -- written-n )
  Try to write "n" bytes from buffer "a" to stream at current position, growing
  the file if needed. The idea is the same as with readbuf: the filesystem can
  proceed as is best for its implementation, as long as it writes at least 1
  byte. If no data can be written, return 0.

  After the write operation, advance current position by "written-n" bytes.

close ( stream -- )
  Close stream and free its resources. When a stream is closed, operations on
  it become noops (read return 0, writes/seeks do nothing). The default
  implementation is a noop.

### Words

newstream ( 'readbuf 'writebuf -- stream )
  Create a new stream struct with the specified methods.

write ( a n stream -- written-n )
  Repeatedly call writebuf until at most "n" bytes have been written from
  buffer "a". Yields "written-n", the number of bytes that have actually been
  written.

write# ( a n stream -- )
  Call write and abort if "written-n" is not the same as "n".

read ( a n stream -- read-n )
  Repeatedly call readbuf until at most "n" bytes have been read into buffer
  "a" (which of course must be big enough). Yields "read-n", the number of bytes
  that have actually been read.

read# ( a n stream -- )
  Call read and abort if "read-n" is not the same as "n".

getc ( stream -- c )
  Read 1 byte from stream an return it. Advance position by 1 byte. Return -1 on
  EOF. This can be used to poll an interactive (keyboard, serial link, etc) I/O
  device: -1 is returned as long as there's nothing to return.

putc ( c stream -- )
  Write 1 byte to stream. Advance position by 1 byte. Aborts if unable to write.

puts ( str stream -- )
  Write str to stream.

spitn ( n dst stream -- )
  Until "n" bytes are consumed or until EOF is reached, read "stream" and write
  its contents to other stream "dst".

spit ( dst stream -- )
  Same as "spitn" with n=-1. That is, spit whole stream.

spitclose ( dst stream -- )
  Spit, then close "stream".

spitcloseboth ( dst stream -- )
  Spit, then close "stream" and "dst".

exec< ( w stream -- )
  Call "w" after having changed the input stream so that it spits "stream"'s
  contents. When "w" returns, restore input stream and closes stream.

  Note that the stream is read using "readbuf" with a "-1" argument (read as
  much as possible at once). It's efficient, but it means that "exec<" is only
  suitable for "w" consuming the whole stream.

exec1< ( w stream -- )
  Same as "exec<", but passes "1" to readbuf instead of "-1" (read one character
  at once). This is less efficient, but is suitable for "w" which partially
  reads the stream. Also, unlike "exec<", it doesn't close the stream.

readline< ( -- ?a ?u f )
  Read input stream, accumulating a maximum of STR_MAXSZ characters into the
  string pool [lib/str] until LF or EOF is met or until the buffer is full.

  If at least one character could be read before EOF is met, or if EOF was never
  met, f=1 and "a u" represents the range of the accumulated characters. The
  final LF is not included.

  If the first character that was read was a EOF, f=0 and "a u" are absent.

readline<# ( -- a u )
  Same as readline<, but abort with error instead of yielding f=0.

readline ( stream -- ?a ?u f )
  Calls "readline<" with "stream" as the input. "exec1<" is used for this, which
  means partial reads are ok.

readline# ( stream -- a u )
  Same as readline, but with "readline<#" instead.

interpretstream ( stream -- )
  Run "interpret" with exec<.

capture ( xt stream -- )
  Execute "xt" with RTYPE being overridden to "stream"'s "write#". Can be safely
  called from another capture.

ioerr ( -- )
  Aborts with "I/O error".

## Null stream

This unit provides "nullstream". Reading from this stream always yield EOF and
writing to this stream is always a noop success (written-n same as n).

## Here stream

This unit provides "herestream", a write-only stream that is the equivalent of
writing the contents to "here".
