# Input/Output

The sys/io 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 "IO handle". This handle 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.

## IO API

Methods:

:readbuf ( n hdl -- a? read-n )
  Try to read "n" bytes from hdl 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 handle to EOF. The idea is to take the handle, 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 handler, that is, :readbuf, :writebuf, :flush
  or :close.

:writebuf ( a n hdl -- written-n )
  Try to write "n" bytes from buffer "a" to hdl 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 ( hdl -- )
  Close hdl and free its resources. When a handle is closed, operations on
  it become noops (read return 0, writes/seeks do nothing). IO has a default
  implementation that is a noop.

Words:

:write ( a n hdl -- 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 hdl -- )
  Call :write and abort if "written-n" is not the same as "n".

:read ( a n hdl -- 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 hdl -- )
  Call :read and abort if "read-n" is not the same as "n".

:getc ( hdl -- c )
  Read 1 byte from hdl 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 hdl -- )
  Write 1 byte to hdl. Advance position by 1 byte. Aborts if unable to write.

:puts ( str hdl -- )
  Write str to hdl.

:readline ( hdl -- str-or-0 )
  read hdl for a maximum of STR_MAXSZ-1 characters until LF or EOF is
  encountered, then return a string representing that read line. The LF
  character is not included. Aborts on LNSZ overflow. The string can be zero in
  length if LF is encountered right away. When EOF is encountered in the middle
  of the string, return the string. When it's encountered at the beginning,
  return 0 to indicate an absence of result.

:spit ( dst hdl -- )
  Until EOF is reached, read "hdl" and write its contents to other IO "dst".

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

:interpret ( hdl -- )
  Run "interpret" with :exec<.

:capture ( w hdl -- )
  Execute "w" with "emit" being overridden to "hdl"'s ":putc". Can be safely
  called from another :capture.

## Console

This unit provides a "console" bind{} which is a IO structure that wraps "in<"
and "emit". This allows the operator to conveniently use IO structures with data
coming from "in<" or going to "emit".
