Input and output ports
Isis software documentation
Input and output ports
This document explains the standard input and output facilities in
Isis. Input from and output to any endpoint is handled using the same
mechanism known as a port, which allows the same procedures to
be used for reading, writing, and other operations, regardless of
where the data is coming from or going to. You can create ports that
connect to disk files, memory buffers, internet sites, multicast
networks, or serial ports, just to name a few.
In Isis, a port is represented as a list of several items, which
includes the port type, internal identity information, and the
procedures used to operate on the port. It will usually not be
necessary to look at anything in this list, and of course, nothing in
the list should ever be modified manually. Use only the port
manipulation routines described below.
Default input and output ports
default-input-port # the default input port (stdin)
default-output-port # the default output port (stdout)
(current-input-port) # return the current input port
(current-output-port) # return the current output port
(set-input-port port) # change the current input port
(set-output-port port) # change the current output port
The default input and output ports are usually your keyboard
and your terminal on your screen. Whatever you type on your keyboard
is available for reading from the default input port, and whatever you
write to the default output port appears on your screen. Technically,
the default input port and output ports refer to the "standard input"
and "standard output" files of the Isis process.
The current input and output ports indicate the places Isis
will read and write data to when no particular port is specified in
the routines described later (for example, the print function
prints messages to the current output port).
current-input-port and current-output-port allow you
to find out what the current input and output ports are. When Isis is
started, the current input and output ports are the same as the
default input and output ports, but you can change them using
set-input-port and set-output-port.
Opening ports
(open-input filename) # open a disk file for input
(open-output filename) # open a disk file for output
(open-update filename) # open a disk file for input and output
(new-string-port name string) # create new port connected to a string
(new-memory-port name size address freeflag) # create new port connected to memory buffer
open-input, open-output, and open-update
open a disk file for reading, writing, or both. A port is returned,
or Null if there was a problem accessing the file.
(set iport (open-input "inputfile.dat"))
(set oport (open-output "outputfile.dat"))
new-string-port creates a port that points to a specified
string (which is copied into a memory buffer), and
new-memory-port creates a port that points directly to a
buffer in memory. A string name is expected for each, to be used for
identification and in error messages. new-memory-port
expects the size of the memory buffer in bytes, the address of the
buffer, and a boolean freeflag indicating whether the buffer
should be automatically freed when the port is closed. Both routines
return the newly created port, valid for reading or writing.
(set size 32768)
(set buf (allocate-memory size))
(set mport (new-memory-port "mybuf" size buf False))
In addition to disk files and memory buffers, you may open ports to
TCP connections, serial ports, multicast networks, unix pipes, and
other devices. Descriptions of these ports appears in other sections
of the documentation.
Closing ports
(close port) # close port and deallocate associated resources
When a port is no longer needed, it should be closed so that resources
associated with the port may be reclaimed.
Reading and writing data
(read-string port) # read string up to a newline character
(read port) # read CODED Isis values
(read-raw port numbytes address) # read raw bytes to memory address
(write-string port val val ...) # write values in human readable format
(write port val val ...) # write CODED Isis values
(write-raw port numbytes address) # write raw bytes from memory address
(read-string) # same as read-string from the current input port
(print val val ...) # same as write-string to current output port
Isis allows you to read and write data on ports in three different
formats: strings, coded Isis values, and raw bytes. There is a
separate pair of read and write functions for each format. If data is
written in one format, it should usually be read back with the
corresponding read function for the same format.
read-string and write-string read and write
human-readable strings. read-string reads characters from
the port up to the next newline character and returns a string
(without the newline in it), or Null if no more data is
available. write-string writes one or more strings,
characters, and other Isis values in a human-readable format, and
returns the total number of bytes written to the port, or
Null if an error occurred. Values are written exactly as
they would be displayed in Isis, except that strings and characters
are written without their quotes, and a newline character must
be given explicitly if it is desired.
(set oport (open-output "test.txt"))
(write-string oport "The number of the day is: " (random) newline)
(write-string oport "Thank you and goodbye." newline)
(close oport)
(set iport (open-input "test.txt"))
(while (!= Null (set aline (read-string iport)))
(print aline newline))
(close iport)
Plain read and write receive and transmit Isis
values in a compact coded form that is not readable by humans.
These routines are most useful for sending and receiving Isis values
over a network or in disk files, where other programs will not have to
access the data. Any type of value except procedures and addresses
may be read or written. read reads one value from the given
port and returns it, or Null if no more data is available.
write writes one or more values to the given port, and
returns the total number of bytes written, or Null if an
error occurred. A description of the protocol used in coding values
can be obtained from Stefan.
(set oport (open-output "test.data"))
(write oport (random) (random) (random))
(close oport)
(set iport (open-input "test.data"))
(print "The three secret numbers are: "
(read iport) (read iport) (read iport) newline)
(close iport)
read-raw and write-raw receive and transmit blocks
of raw data in memory buffers. Each expects the port, an integer
number of bytes, and the address of the memory buffer. Each also
returns the total number of bytes read or written, or Null if
an error occurred. These functions are most useful for transfering
large blocks of raw data, like images or audio, in the most time and
space efficient manner.
(set outbuf (allocate-memory 50)) # allocate a memory buffer
(outbuf c-byte (make-series 0 50 1)) # put something in the buffer
(set oport (open-output "test.raw"))
(write-raw oport 50 outbuf) # write the entire buffer
(close oport)
(set inbuf (allocate-memory 50)) # allocate a second buffer
(set iport (open-input "test.raw"))
(read-raw iport 50 inbuf) # read 50 bytes into it
(close iport)
(display (inbuf [c-byte 50])) # display the contents
When read-string is called with no argument, it reads a
string from the current input port (usually the keyboard). Similarly,
the print function prints values to the current output port
(usually the screen). These functions are most useful as a way of
requesting a line of input from the user and writing messages that the
user will see on the screen. See above for more information about how
to query or change the current input and output ports.
(while True
(begin
(print "Please enter a number: ")
(set numstr (read-string))
(print "The square root of your number is: "
(sqrt (eval numstr)) newline)))
Port position
(tell port) # return the current position in port
(seek port pos) # seek to a particular position in port
tell returns the current read/write position in the port in
bytes relative to the beginning of the file, or Null if it is
not known or if there is an error. seek sets the current
read/write position. It returns True if successful, and
False if not.
Port status
(pending port) # return True if data is waiting to be read at port
(pending port microsecs) # return True if data pending or False if time limit reached
(read-ready port) # same as pending
(read-ready port microsecs) # same as pending
(write-ready port) # return True if data may be written to port
(write-ready port microsecs) # return True if data may be written or False if time limit reached
pending returns True if data is waiting to be read
from the specified port, or False otherwise. If a second
argument is given, the function waits up to the specified number of
microseconds, and returns immediately with True if data is
pending, or False if the time limit was reached.
read-ready is exactly the same as pending.
write-ready performs the converse operation for writing. It
returns True if the port is ready to accept data, or False if
not or if the optional time limit has been reached. This routine is
typically useful to prevent a program from blocking when, in TCP
networking applications for example, a outgoing connection is hung and
momentarily cannot accept more data.
Port queries
(input-port? port) # return True if port supports input
(output-port? port) # return True if port supports output
These routines check if the given port supports input or output. The
result is either True or False.
-> (input-port? (current-input-port))
True
-> (output-port? (current-input-port))
False
Port utilities
(read-text port numchars) # read up to numchars, return string
(read-all-text port) # read entire port, return string
(read-data port numbytes) # read up to numbytes, return [buf len]
(read-all-data port) # read entire port, return [buf len]
(transfer-data inport outport numbytes) # transfer numbytes from inport to outport
(transfer-all-data inport outport) # transfer entire inport to outport
read-text reads up to the specified number of characters from
the given port and returns a string. read-all-text reads as
many characters as possible from the port and returns a string. Both
functions return Null if there is an error.
read-data and read-all-data are similar, except they
read raw data. The return value is a list of two items: the address
of a newly-allocated memory buffer, and an integer indicating the
number of bytes stored in that buffer (which may be smaller than the
number of bytes requested). You are responsible for deallocating the
memory buffer when you no longer need it. If there is an error, the
returned number of bytes will be 0.
transfer-data and transfer-all-data read bytes from one port and write them
to another. The number of bytes actually transferred is returned, or 0 if there was an error.
(set copy-file
(proc (inname outname)
(local (infile outfile)
(begin
(set infile (open-input inname))
(set outfile (open-output outname))
(if (and infile outfile) (transfer-all-data infile outfile))
(close infile)
(close outfile)))))
Port list internals
port-name # string, port name
port-type # string, port type
port-inflag # boolean, input enabled on port?
port-outflag # boolean, output enabled on port?
port-internal-id # internal id of port, or Null if not needed
port-close-proc # procedures for manipulating this port
port-read-proc
port-readline-proc
port-write-proc
port-tell-proc
port-seek-proc
port-read-ready-proc
port-write-ready-proc
port-configure-proc
These constants represent indices in the port descriptor list that you
can use to get certain information about a port. The most useful item
to most users is the probably the name of the port, which you can
obtain by indexing into the port list with port-name.
-> ((current-input-port) port-name)
"Standard Input"
-> ((current-output-port) port-name)
"Standard Output"
Creating new kinds of ports
(new-port name type inflag outflag internal-id
close-proc read-proc readline-proc write-proc
tell-proc seek-proc
read-ready-proc write-ready-proc configure-proc) # create a new port
new-port creates a new port. name and type
should be strings. inflag and outflag should be
booleans that indicate whether the port will support input or output
or both. internal-id can be any value that you want to use
as an internal identifier to the port. The rest of the arguments will
be the procedures that are used to manipulate the port. The
procedures that you design for the port should be defined in the
following way, where id will be the internal-id that you
specify in new-port:
(close id) # close and free resources, return True if successful
(read id n addr) # read n bytes into addr, return num bytes actually read
(readline id n addr stopval) # read n bytes or up to stopval, return num bytes actually read
(write id n addr) # write n bytes from addr, return num bytes actually written
(tell id) # return current read/write position, or Null if N/A
(seek id pos) # set current read/write position, return True if successful
(read-ready id microsecs) # return True if data can be read or False if time limit reached
(write-ready id microsecs) # return True if data can be written or False if time limit reached
(configure id arg arg ...) # perform user-defined operations
For example, below is the definition of new-memory-port,
which uses new-port to create a new port. The procedures for
manipulating the port are defined within the scope of a local
environment that includes bindings needed for the internal operations
of the port. Talk to Stefan for more information on creating new
types of ports.
(set new-memory-port
(proc (name buflen membuf freeflag)
(local (bufpos)
(begin
(set bufpos 0)
(new-port name "MEMORY" [ buflen membuf freeflag ] True True
# close function
(proc (id)
(begin
(if freeflag (free membuf))
True))
# read function
(proc (id size ptr)
(local (readsize)
(begin
(set readsize (min size (- buflen bufpos)))
(if readsize (copy-memory readsize (+ membuf bufpos) ptr))
(set bufpos (+ bufpos readsize))
readsize)))
# readline function
(proc (id size ptr endval)
(local (readsize val going)
(begin
(set readsize 0)
(set going True)
(while (and (< bufpos buflen) going)
(begin
(set val ((+ membuf bufpos) c-byte))
((+ ptr readsize) c-byte val)
(set readsize (+ readsize 1))
(set bufpos (+ bufpos 1))
(if (= val endval) (set going False))))
readsize)))
# write function
(proc (id size ptr)
(local (writesize)
(begin
(set writesize (min size (- buflen bufpos)))
(if writesize (copy-memory writesize ptr (+ membuf bufpos)))
(set bufpos (+ bufpos writesize))
writesize)))
# tell function
(proc (id) bufpos)
# seek function
(proc (id pos) (set bufpos pos))
# read-ready function
(proc (id time) (not (= bufpos buflen)))
# write-ready function
(proc (id time) (= bufpos buflen))
# configure function
(proc args Null)
)))))
Requirements:
Scripts:
| (load "port-utilities.isis")
|