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")