perm filename NEWIO.F79[206,LSP] blob sn#539768 filedate 1980-10-08 generic text, type C, neo UTF8
C00001 00001
C00002 00002	.s(IO,INPUT AND OUTPUT)
C00004 00003	.ss(basicio, Basic I/O operations.)
C00010 00004	.ss(files,Files)
C00032 00005	.ss(generalio, Basic I/O Operations Revisited.)
C00040 00006	.ss(ioex, I/O Examples.)
C00041 00007	.bb  Elementary examples of file manipulation.
C00045 00008	.bb |The programs $PPOUT, $DSKIN and $$QREAD$.|
C00049 ENDMK

	There are three components to Input and Output in MACLISP.  First
are the basic functions which read and print.
Some of these deal with S-expressions, others with characters.
There are also functions to do conversions between characters and ASCII code.
Second there is a collection of switches which can be turned on or
off to control the source of input and the destinations of output.  Typically
the input source is either the terminal or the currently opened file.
Output can go to the terminal, some subset of the currently opened files, or both.
Finally there are the functions that  open and close files.
.ss(basicio, Basic I/O operations.)

	We begin with a description of the I/O operations 
on the user's terminal.  The same operations can be used for doing "file"
I/O as will be explained later.

.begin indent 0,4
$(READ) reads one S-expression, which is either a list  enclosed  in  matching
parentheses  or  an  atom  delimited  by a special character such as a
space or a parenthesis. (A parenthesis would be saved up and  used  on
the  next call to $READ.)  $READ returns the S-expression which it read,
converting it from the external representation as characters  to  LISP
internal form.  When reading from a file, providing an argument to  $READ 
causes it to return the value of that argument when the end-of-file
is reached  (unless you have specified a fancy eof function).
If no argument is provided, it closes the file and patiently waits for input
from the terminal.

$(READCH)  like read, but reads  in  one  character  and  returns it as a 
character object.

$(TYI) reads in one character and returns  a  number  which  is  the
ASCII code for the character.

$(PRINT_x) prints out the S-expression $x in a form which is readable
by  humans  but  which  could  also  be  read back into LISP if it were
printed to a file rather than to the terminal.  
The expression printed out is preceded by a newline and followed by
a space.  If special characters or strings of characters are used with 
other than their  normal
meanings,  for  example  if  a  parenthesis appears in the pname of an
atom, they are preceded and followed by "|" so that the output  could  be  read
back in.  

$(PRIN1_x) is the same as $(PRINT_x) except that the leading newline
and trailing space are omitted.  $PRIN1 can be used to  print  multiple
items  on  a  line, but spacing between the items must be provided for
explicitly, for example by evaluating $$(TYO 40)$.

$(PRINC_x) is like $(PRIN1_x) except that special characters or
strings of characters are  not surrounded by "|".
This makes the output more
pleasing in certain situations, however it cannot be  read  back  into

$(TERPRI) types out a newline.

$(TYO_n) outputs a character whose ASCII code is given by the number $n. 
$PRINC may also be used to output character objects.


	As an example of how some of these reading and printing functions
can be used here is part of a program that is used as a self-documentation
feature in a larger  system.  When the system is loaded it prints a message
telling the user to type $(BBHELP) for information.  $BBHELP prints out
a message explaining itself then does a $READ.  If the user then
types one of the topic names the appropriate message is printed otherwise
an error message is printed.   The "|"s around each line of the message
tell LISP that this is really a single atom. $PRINC prints it as a
character string omitting the "|" delimeters.  $PRINT would retain the "|"s.
.begin nofill 
.select 6

	    (DEFUN  BBHELP ()
			  ((EQ TOPIC 'EXIT) (RETURN 'Bye))
			  (T (BBERRHELPER TOPIC)))     
		    (GO HELP)))

	    (DEFUN BBMSG ()
		(PRINC '|The BB programs convert a list of atoms having |)
		(PRINC '|EXPR FEXPR or VALUE properties to an external form.|)
		(PRINC '|Type HELP to get a list of BBHELP topics|)
		(PRINC '|Type EXIT to get out of BBHELP|)

	I/O in LISP consists of communication between the LISP environment and
files located in the external world.  LISP refers to these files by using
"file objects," which are special objects within the LISP environment
which serve as representatives of, or symbols for, the files in the
external world.  Because there is a one-to-one correspondence between
files and file objects, it is often convenient to confuse the two and call
them both "file."

	The LISP system includes functions which can manipulate files in
various ways:  A file may be "opened," that is a file object may be
created and associated with a named file in the external world.  A file
may be "closed," that is the association between the file-object and the
external file may be broken and the file-object deleted.  The
file-accessing information contained in a file-object may be examined or
changed; for example, the line length of an output file may be adjusted.
The characters of information in the external file may be read or written.
The attributes of the external file, such as its name, may be changed.

.bb File names and other information.

	In order to "open" a file, the external file and the file object
must be named so that a connection may be established between them.  The
problem of naming file objects is solved trivially by making the rule that
whenever a file object is created its name is decided by the system and
returned as the value of the function that created it.  File objects are
then referred to in the same way as any S-expression.  Note that the name
of a file object does not have a printable form, so that if you want to
manipulate the file object by typing from the terminal (rather than from a
program), you must keep the file object as the value of an atomic symbol.

	The naming of files in the outside world is more difficult because
MACLISP has to operate with several different outside worlds, that is,
under several different operating systems.  Two forms for file names have
been defined, and functions are provided to make the
implementation-dependent translation from one form to the other.  The
forms of a file name are called the namelist and the namestring.

	The namestring is the implementation dependent form.  Namestrings
are represented as LISP character strings, however atomic symbols may also
be used, in which case the pname of the atomic symbol is used as the
character string.  The contents of a namestring is just a sequence of
characters which have meaning to the user and to the function "namelist,"
which converts a namestring to a namelist.  Namestrings should be read in
using  $READSTRING and printed out using $PRINC, so that no
quotes will appear around them.

	A namelist is a list whose car somehow specifies the device and/or
directory which contains the file, and whose ⊗cdr specifies the name of the
file.  The exact way in which the ⊗car specifies the device/directory is
implementation-dependent.  It should not be of concern to programs.  The
⊗cdr of a namelist is a list of names which are the components of the file
name if the operating system uses multi-component file names.  Each name
is represented as an atomic symbol, which is "interned" so that it may be
tested using $EQ.  An additional feature of namelists is the "star
convention," by which a namelist may contain unspecified components, which
are indicated by the atom *.  The *'s are filled when needed using default
values that the system keeps on hand.

	The conversion between namlists and namestrings are carried out
by the programs $NAMELIST and $NAMESTRING as follows:

$NAMELIST  (SUBR 1 arg) converts its argument to a namelist.  
Omitted or * components in the argument produce *'s in the result.

$NAMESTRING  (SUBR 1 arg)  converts its argument from a namelist to a 
namestring.  It is the opposite of namelist.

	The functions $STATUS AND $SSTATUS examine and set the status
of system variables.  Some of these maintain defualts for file I/O.  
They are: 
.begin indent 0,4 preface 0

$$(STATUS UDIR)$ returns the the name of the user's directory.
For example $$(DSK |C.CLT|)$.   $SSTATUS does not apply.

$$(STATUS CRUNIT)$ returns the current (default) device and directory.
Is initialized to $$(STATUS UDIR)$ in LISP at LOTS (not in MACLISP).

$$(SSTATUS CRUNIT dev dir)$ sets the defaults for device and directory
to $dev and $dir respectively.

$$(STATUS CRFILE)$ returns the current (default) file name and extension.
It returns qNIL if none has been set.

$$(SSTATUS CRFILE nam ext)$ sets the defaults for file name and extiosion
to $nam and $ext respectively.


	An example may help clarify the connection between namelists and
namestrings.   A file PS:<C.CS206>GAME.LSP would be referred to by the
namestring $|PS:<C.CS206>GAME.LSP.3| or by the namelist 
$$((PS |C.CS206|) GAME LSP /3)$.   If the first component of the namelist
is omitted then $CRUNIT is used.  In some cases  the file specification may
be omitted enitrely.  The $CRUNIT and $CRFILE are used to make up a 

	Some additional information about file objects has been collected
here.  It is in brief form and will be elaborated in later sections.
There is no way to input file objects to the reader, because they do not
have pnames or anything of that sort, but for convenience in error
messages and debugging the printer will print a file object as a sharp
sign (#), followed by the namestring of the external file to which the
file object is attached.  # is the character which is used to indicate
that an object of unknown type is being printed.  The information
contained within a file object is here described briefly.
.begin indent 0,4 preface 0

$NAMELIST the namelist for the external file  of  which  the
file object is a representative.

$EOFFN a function which is applied when  the  end  of  an
input file is reached.

$ENDPAGEFN a function which is applied  when  the  end  of  a  page  is
reached (on an output file.)

$LINEL the number of characters per  line  on  an  output file.

$Charpos  the horizontal position on the line, where  0  is  the  left margin.

$CHRCT  the number of character positions remaining on the current 
line of an output file,

$PAGEL  the number of lines per page.
$LINENUM  the number of the current line, with 0 being the top of the page

$PAGENUM   the number of the current page, with the first page being 0.

$FILEPOS  the position within  the  file  of  the  character
currently   being   accessed.    (Not  necessarily
meaningful for all kinds of files.)

Note that as a special case $T is considered to be a file object which
represents the terminal.

.bb   Opening and Closing Files

	In order to do file input/output operations files must be opened
and closed.  The LISP primitives $OPEN and $CLOSE carry out these tasks.
.begin indent 0,4 

$OPEN  (LSUBR 0 to 2 args)  $$(OPEN <file> <mode>)$ opens a file and returns a
corresponding file object.  The $<file> defaults to the
current default file.  The $<mode> defaults to $NIL.  
If $<file> is a namelist or namestring, a new file object
is created.  If $<file> is a file object already, it is
closed and re-opened in the specified mode; its former
mode serve as the default for the $<mode>.
The $<mode> can be one of the following: $IN, $OUT, $APPEND 
for input file, output file (creates a new file) and
output file (append to existing file), respectively.

[Remark: $<mode> can also be a list specifing attributes in addition to
"direction".  These default to the right thing for ordinary input/output
in LISP.]

$CLOSE  (SUBR 1 arg)  $$(CLOSE  x)$, where $x is a file, closes $x and returns $T.  
If $x is already closed nothing happens,  otherwise  the  file  system  is
directed to return $x to a quiescent state.

.bb   Specifying the Source or Destination for I/O.

	When an I/O function is called, the source (file) from which it is
to take its input or the destinations (files) on which it is to put its
output may be specified directly as an argument to the function, or they
may be specified by default.  The default input source and output
destinations are specified by several system variables, which may be
setq'ed or lambda-bound by the user.  They are described below.  $INFILE
is the default input source, if the switch $↑Q is $T.  If $↑Q is $NIL the
terminal is used as the default input source.  $OUTFILES is a list of
default output destinations.  Output is sent to all of these files if the
$↑R switch is $T.  Output also goes to the terminal unless the $↑W switch
is $T.  Note that if the value of $INFILE is $T ($OUTFILES is qNIL
or $$(T)$) it means the terminal, any anything else must be a file object.
.begin indent 0,4 preface 0

$INFILE  (VARIABLE)  The value of $INFILE is a file object which is the  
default  input source  if $↑Q is non-NIL.  $INFILE can also be $T which 
specifies that input will be from the terminal even if $↑Q is non-NIL.   
The initial value of $INFILE is $T.  

$↑Q  (SWITCH)  If the value of $↑Q is non-NIL, the default input source 
is the value of the atom $INFILE.  If $↑Q is $NIL, the default input source
is $NIL, i.e. the terminal.

$INSTACK  (VARIABLE)  The value of $INSTACK is a list of pushed-down values  
of $INFILE.  It is managed by the program $INPUSH.  The initial value is qnil.

$OUTFILES  (VARIABLE)  The  value of $OUTFILES is a list of file objects 
which are output destinations if $↑R is not qNIL.  Elements of the list 
$OUTFILES may be either file objects created by $OPEN, or qNIL  meaning
output  to  the  terminal.  Note that output goes to the terminal
anyway if $↑W is qNIL, so it is possible to get  double  characters
this way.

$↑R  (SWITCH)  If  the  value  of $↑R is non-NIL, the default output 
destinations include the files in the list which is the value of the atom

$↑W  (SWITCH)  If the value of $↑W is non-NIL, the default output destinations 
do not  include  the terminal.  (Unless $↑R is on and $T is a member
of the outfiles list.)

$FILEPOS   (LSUBR 1 or 2 args)
$$(FILEPOS x)$, where $x is a file object open for input, returns the
current  character  position  within  the  file as a fixnum.  The
beginning of the file is 0.
$$(FILEPOS x n)$, where $x is a file object open for input and $n is a
non-negative fixnum, resets the character position of the file to
position specified by $n.  It is an error if  this  position  does
not  lie  within  the  file  or  if  the  file  is  not  randomly
accessible.  $N is returned.

.skip 2
.bb   Handling End of File

	Calls to the input functions $READ, $READCH, $READSTRING, and $TYI 
specify an argument called the $eofval.  If this argument is omitted qNIL
is assumed.  If the end of the input file is reached during the execution
of the function, the $eofval argument is used by the following procedure:

	Each file object has an end-of-file handler, its $eoffn.  When an
end of file occurs while input is being taken from this file, the $eoffn
is examined. (Eof on the terminal cannot occur.)  If the $eoffn is qNIL,
then the following default action is taken:  If eofval on the call to
$READ was not supplied, then the input file is closed and $READ continues
taking characters from a new input file popped off the input stack.  If
the input stack is empty, $$(SETQ ↑Q NIL)$ is done and read continues
reading from the terminal.  If an $eofval was supplied on the call to
$READ, then $READ immediately returns it.  The input file is not closed.

	This is not strictly true in the case where the input function is
$READ or $READSTRING and it is in the middle of an object.  In this case,
rather than allowing the object to cross files, a $fail-act error occurs.
The argument passed to the user interrupt service function is the list
$$(READ-EOF)$.  If the interrupt service function returns an atom (such as
qNIL), $READ $errs out; but if it returns a list, $READ goes on reading
from the new input source as if there had not been any end-of-file.

	If the $eoffn for the input file is not qNIL, then it is a
function and it is applied with two arguments.  The first argument is the
file object that eof'ed.  The second argument is the $eofval on the call
to $READ, or, if an $eofval was not supplied, qNIL.  If the $eoffn returns
qNIL, the file is closed and reading continues from the input source
popped off the input stack.  The above prohibition of objects crossing
$eofs applies.  If the $eoffn returns qT, reading continues from whatever
input source was made the current default one by the $eoffn.  If the
$eoffn returns something other than qT or qNIL, then $READ immediately
returns whatever the $eoffn returned, and the file is not closed unless
the $eoffn closes it.
.begin indent 0,4 preface 0

$$(EOFFN x)$, where $x is an input file, gets $$x$'s end-of-file 
function.   The  end-of-file function is called if the end of the
file is reached during input.
$$(EOFFN qNIL)$ gets the default end-of-file function.
$$(EOFFN x f)$ sets $$x$'s end-of-file function to $f.  
$$(EOFFN qNIL f)$ sets the default end-of-file function to $f.  
$f may be qNIL, which means that no end-of-file function is  to  be used.

.ss(generalio, Basic I/O Operations Revisited.)

.begin indent 0,4 preface 0

$(READ) reads an S-expression from the default input source.
$$(READ f)$, where $f is a file or qNIL meaning the terminal, reads an
S-expression from $f.  During the reading, $INFILE and $↑Q are bound
so that evaluation of $$(READ)$ within  a  macro-character  function
will read from the correct input source.
$$(READ  x)$,  where  $x  is  not  a file and not qNIL, passes $x as an
argument to the end-of-file function of the input source  if  the
end  of  the  file is reached.  Usually this means that read will
return $x if there are no more S-expressions in the file.
$$(READ T)$ suppresses the calling of the  end-of-file  function  if
the end of the file is reached.  Instead, read just returns T.
$$(READ  x  f)$  or $$(READ f x)$ specifies the end-of-file value $x and
selects the input source $f.  

$READCH reads in one character and  returns  a  character  object.
The arguments are the same as for read.

$READLINE  reads  in  a  line  of  text,  strips  off  the newline
character or characters at the end, and returns it in the form of
a character string.  The arguments are the same as for $READ.  The
main use for $READLINE is reading in file names typed by the  user
at his terminal in response to a question.

$TYI  inputs one character and returns a fixnum which is the ascii
code for that character.  The arguments are the same as for $READ. 

$$(TYIPEEK)$ is like $(TYI) except that the character is  not  eaten;
it  is  still in the input stream where the next call to an input
operation will find it.  Thus $$(EQ (TYIPEEK) (TYI))$ is $T.   If  the
end  of  the  file is reached, $TYIPEEK returns 3, (the ascii code
for "end of text.") The end of file function is not called.
$$(TYIPEEK n)$, where  $n  is  a  fixnum  <  200  octal,  skips  over
characters of input until one is reached with an ascii code of $n.  
That character is not eaten.
$$(TYIPEEK  n)$,  where $n  is  a  fixnum  >  1000 octal, skips over
characters of input until one is reached whose syntax  bits  from
the readtable, logically anded with (lsh n -9.), are nonzero.
$$(TYIPEEK t)$ skips over characters of input until the beginning of
an  S-expression  is reached.  Splicing macro characters, such as
";" comments, are not considered to begin an object.  If  one  is
encountered,  its associated function is called as usual (so that
the text of the comment  can  be  gobbled  up  or  whatever)  and
$TYIPEEK continues scanning characters.

$$(PRIN1  x)$  outputs $x  to the current output destination(s), in a
form suitable for reading back in.
$$(PRIN1 x  f)$ outputs $x on the file $f, or the  terminal  if  $f  is

$PRINT  is  like $PRIN1  except  that  the output is preceded by a
newline and followed by a space.  This  is  the  output  function
most often used.
$$(PRINT x)$ prints $x to the default output destinations.
$$(PRINT  x  f)$  prints $x to the file $f, or to the terminal if $f is

$PRINC is  like $PRIN1  except  that  special  characters  are  not
slashified and strings are  not quoted.
$$(PRINC x)$ outputs $x to  the current output destination(s).
$$(PRINC x f)$ outputs $x to the file $f, or the terminal if $f is qNIL.

$(TYO  n)$  types  out  the  character whose ascii code is $n on the
current output destination(s).
$$(TYO n f)$ types out the character whoe ascii code  is  $n  on  the
file  $f  or  on  the terminal if $f is qNIL.  $TYO returns its first

$(TERPRI) sends a newline to the current output destination(s).
$$(TERPRI x)$ sends a newline to $x, where $x may be an output file or
qNIL meaning the terminal.

$$(INPUSH x)$, where $x is a file object open for  input  or  qNIL  to
specify  the  terminal,  pushes the current input source onto the
input stack and selects $x as the current input source. 
$$(INPUSH 0)$ just returns $INFILE.  
$$(INPUSH -1)$ pops a new input source off of the input stack:
In the case that $INSTACK is qNIL, i.e. empty.  In this case $INPUSH 
leaves $INSTACK qNIL and  makes $INFILE $T, which means the terminal.
$$(INPUSH -n)$ does $$(INPUSH -1)$ $n times.
$$(INPUSH  1)$  does  $$(INPUSH  (INPUSH  0))$, $$(INPUSH +n)$ does that 
$n times.
The value of $INPUSH is  the  newly  selected  input  source.   If
$INPUSH causes $INFILE to be set to $T, $↑Q is set to qNIL since the
terminal has become the input source.

.ss(ioex, I/O Examples.)

.bb File selection

	$TTYMSG is an example of how the file selection works.
$TTYMSG will write the message given to it
as an argument on the terminal no matter what the value of the I/O switches
are when it is called.  It saves the current switches, disables output to
a file and enables output to the terminal, $$PRINT$s the message then
restores the switches.

.begin verbatim select 6

			(SETQ ↑R CR ↑W CW)))
.bb  Elementary examples of file manipulation.

.begin verbatim select 6

(defun readloop (fileob)   for reading to end-of-file
  (prog (form if)
        (setq if (open fileob 'in))
  loop  (setq form (read if))
        (print form)
        (cond ((null form) (return if)))
        (go loop)))

(setq of (open '(tmp 1) 'out))	  	;;; open file called TMP.1  for output
 #FILE-OUT-|DSK:DSK:<C.CLT>TMP.1.25|-64774  	;;; makes a new version
(print '|hello there| of)		;;; write a messge 
(close of)
(readloop of)				;;; old version is not contained 
|hello there| 				;;; in new version

#FILE-IN-|DSK:<C.CLT>TMP.1|-64774 	;;;NB the file object has been changed!

(setq of (open of 'out))
(close of)
(readloop of)
NIL 					;;; nothing sent, nothing there

(setq of (open '(tmp 1) 'out))		;;; start again
(print '|me again| of)
(close of)
(readloop of)
|me again| 

(setq af (open of 'append))
(print '|whats new| af)
(close af)
(readloop of)		
|me again|
|whats new| 

;;; In append mode the original contents of the file should still be there.
;;; Currently APPEND mode just makes a new version.  In Tenex-20 you can
;;; append the two versions yourself using the monitor command APPEND.
;;; Hopefully this bug will be fixed soon.

;;; In order to get output to a file must give file object to output
;;; program directly or turnon ↑R switch and put the intended file object(s)
;;; on the $OUTFILES list.

(setq of (open of 'out)
((lambda(↑r outfiles)(print '|setting ↑r and outfiles?|))t (list af))   
|setting only ↑r and outfiles?| 
(close af)
(readloop of)
|setting ↑r and outfiles?| 	

.bb |The programs $PPOUT, $DSKIN and $$QREAD$.|

	Here is the code for the programs $PPOUT, $DKSIN, and $QREAD 
described in Section {section LOTS}.


                (PROG (FNS) 
                      (SETQ OUTFILES (LIST OFILE)) ;;;files to write to
                      (SETQ FNS (CDR F))
                 LOOP (COND ((NOT (NULL FNS))
                             (APPLY 'GRINDEF (LIST (CAR FNS)))
                             (SETQ FNS (CDR FNS))
                             (GO LOOP)))
                      (CLOSE OFILE)
                      (APPLY 'CRUNIT OCRU)		 ;;;restore file status
                      (APPLY 'SSTATUS (CONS 'CRFILE OCRF))
                      (SETQ OUTFILES NIL)
                      (RETURN OFILE)))
        (COND ((NOT (GET 'GRINDEF 'FSUBR))   ;;;load grinder if not there
               (APPLY 'LOAD
                      (LIST (GET 'GRINDEF 'AUTOLOAD)))))
        (STATUS CRUNIT)		;;;save current file status
        T					;;;turnoff tty
        T))					;;;turnon dsk

                (PROG (FORM) 
                 LOOP (SETQ FORM (READ IFILE))
                      (COND (FORM (EVAL FORM) (GO LOOP)))
                      (APPLY 'CRUNIT OCRU)
                      (APPLY 'SSTATUS (CONS 'CRFILE OCRF))
	      	      (INPUSH -1)
                      (RETURN IFILE)))

			     (CONS 'CRFILE OCRF))
	      	      (INPUSH -1)
		      (RETURN IFILE)))