.sh 1 "Input/Output and External Operations"
.lp
The language mechanisms defined in the previous sections are
assumed to exist in any implementation of SR.
In addition, each implementation must provide some means
for communicating with input/output devices.
It is also useful to be able to invoke operations
written in other programming languages.
.pp
Our current implementation is built on top of UNIX:
SR programs run as UNIX processes on one
or more interconnected machines.
In particular, each virtual machine is implemented
by a UNIX process.
Thus, input and output
are supported by a collection of mechanisms
that provide access to the underlying UNIX file system(s).
Mechanisms are also provided to access command
line arguments and to invoke external operations that
are written in C or assembler.
.sh 2 "Files and File Access"
.lp
I/O in the UNIX implementation of SR is supported by an
additional data type, @file@, and several operations on this type.
Each underlying machine is assumed to have its own file system.
File operations invoked by an SR process executing on a specific
machine interact with the file system on that machine.
.pp
A variable of type @file@ contains a descriptor for a UNIX file;
it can be thought of as being a capability for the associated file.
There are 5 pre-defined file literals, all of which are
reserved words.
Three of the literals are used to access the
corresponding UNIX file:
.(b
@stdin@       	the standard input device (usually the keyboard)
@stdout@      	the standard output device (usually the display)
@stderr@      	the standard error device (usually the display)
.)b
The initially created virtual machine (which executes
the main resource) inherits @stdin@, @stdout@,
and @stderr@ from the command that starts execution of
an SR program.
Other virtual machines created by the program inherit
@stdout@ and @stderr@ from the initial virtual machine
but have @stdin@ connected to the file \fB/dev/null\fR.
Except for this implicit duplication of @stdout@ and @stderr@,
all uses of any particular file variable are restricted to
a single virtual machine.
.pp
The other file literals are @null@ and @noop@.
Attempting to access a file whose descriptor has value @null@
results in an error.
Attempting to read from a file whose descriptor has value
@noop@ returns an immediate end of file indication; other operations on such
a descriptor have no effect.
.pp
In addition to the basic type @file@, there are two pre-defined
enumeration types:
.PS
type accessmode = enum(READ, WRITE, READWRITE)
type seektype = enum(ABSOLUTE, RELATIVE, EXTEND)
.PE
These types define enumeration literals used as arguments to the @open@
and @seek@ operations.
Finally, there is one pre-defined integer constant
.PS
const EOF := -1
.PE
which is sometimes the return value on file access operations,
as described below.
.pp
Files are created, opened, flushed, closed, and removed by the
following pre-defined functions.
.PS L
  open(pathname : string(*); mode : accessmode) returns f : file
.PE
.in 5n
If mode is @READ@, open an existing file for reading.
If mode is @WRITE@, create a new file for writing, or truncate
an existing file.
If mode is @READWRITE@, open an existing file for both reading
and writing.\**
.(f
\**Files corresponding to terminals that are to
be read and written should be opened twice:
once for reading from the keyboard, once for writing
to the display.
.)f
In all cases, the read/write pointer starts at the
beginning of the file.
@pathname@ is the absolute or relative pathname of the file to
be opened.
If successful, @open@ returns the appropriate file descriptor;
if unsuccessful, @open@ returns @null@.
For files opened in @READWRITE@ mode, @seek@ must be used when
switching access modes.
.in -5n
.PS L
  flush(f : file)
.PE
.in 5n
Flush the output buffers of file @f@, which should be open,
without closing the file.\**
.(f
\**Terminal output files may sometimes need to be flushed
after a write in order to force a prompt message to appear
on the display.
.)f
Open files are implicitly flushed when closed.
An unsuccessful @flush@ is a fatal error.
.in -5n
.PS L
  close(f : file)
.PE
.in 5n
Close file @f@, which should be open.
Open files are implicitly closed when a program terminates.
An unsuccessful @close@ is a fatal error.
.in -5n
.PS L
  remove(pathname : string(*)) returns s : bool
.PE
.in 5n
Remove file @pathname@ from the file system.
The file should not be open; if it is, @remove@ will not take
effect until the file is closed or the program terminates.
@remove@ returns true if successful;
otherwise it returns false.
.in -5n
.lp
Open files are accessed using several pre-defined operations:
@read@ and @write@ provide a simple, formatted I/O facility;
@get@ and @put@ provide I/O on streams of characters;
@seek@ and @where@ support random access to disk files.
An attempt to access a file that is not open in
the appropriate mode results in a fatal error that
terminates program execution; other errors are also
fatal, except as noted.
.PS L
  read(res v1 : T; ...; res vN : T) returns cnt : int
  read(f : file; res v1 : T; ...; res vN : T) returns cnt : int
.PE
.in 5n
Read N values from @stdin@ (the default) or from file @f@
and store them in variables @v1@, ..., @vN@.
The variables must be of type @int@, @bool@,
@char@, array of @char@, or @string@.
The input values are literals of these types,
but strings are #not enclosed in quotes.
The input values must be separated by whitespace;
thus only strings of visible characters may be input using read.
For a string variable or an array of characters, input characters
are read until the variable or array is filled or a newline
is encountered.
@read@ returns the number of values successfully read,
which might be 0 in the event of an error reading the first value.
@read@ returns @EOF@ if the end of file is reached before any
values are found.
.in -5n
.PS L
  write(e1 : T; ...; eN : T)
  write(f : file; e1 : T; ...; eN : T)
.PE
.in 5n
Write N expressions to stdout (the default) or to file @f@,
converting them to their literal ASCII form.
Each expression must be of type @int@, @bool@, @char@, @ptr@, or @string@.
One blank is inserted between each output value; a newline
is appended after the last output value.
.in -5n
.PS L
  writes(e1 : T; ...; eN : T)
  writes(f : file; e1 : T; ...; eN : T)
.PE
.in 5n
@writes@ is the same as @write@ except no blank or
newline characters are implicitly written.
.in -5n
.PS L
  get(res str : string(*)) returns cnt : int
  get(f : file; res str : string(*)) returns cnt : int
.PE
.in 5n
@get@ reads characters from @stdin@ (the default) or file @f@ and stores
them in @str@.
If the input file contains at least @maxlength(str)@
more characters, that many are read.
Otherwise, all remaining characters are read.
@get@ returns a count of the number of characters that
were read, if there was at least one, and sets
the length of @str@ to that value.
If end-of-file is encountered immediately, no characters
are read and @get@ returns @EOF@.
.ip
The argument to @get@ can also be a character array,
in which case the entire array is filled (unless @EOF@
in encountered).
.in -5n
.PS L
  put(str : string(*))
  put(f : file; str : string(*))
.PE
.in 5n
@put@ writes @length(str)@ characters to @stdout@ (the default) or file @f@.
.ip
The argument to @put@ can also be an array of characters,
in which case the entire array is written.
.in -5n
.PS L
  seek(f : file; t : seektype; offset : int) returns position : int
.PE
.in 5n
Seek in file @f@, which must be open for both reading and writing.
The type of seek is determined by the value of @t@.
If @t@ is @ABSOLUTE@, then set the read/write pointer to @offset@.
If @t@ is @RELATIVE@, then add @offset@ to the read/write pointer.
If @t@ is @EXTEND@, then set the read/write pointer to the end of
the file plus @offset@.
@seek@ returns the new position of the read/write pointer.
.in -5n
.PS L
  where(f : file) returns pos : int
.PE
.in 5n
@where@ returns the current position of
the read/write pointer in file @f@,
which must be open for both reading and writing.
.in -5n
.sh 2 "Argument Access"
.lp
Two operations provide access to the arguments of
the UNIX command that invoked execution of the SR program.
These can be used, for example, to parameterize a program.
.PS L
  numargs() returns cnt : int
.PE
.in 5n
@numargs@ returns the number of user-supplied arguments
on the command line.
.in -5n
.PS L
  getarg(n : int; res arg : T) returns cnt : int
.PE
.in 5n
Read argument @n@ into @arg@, where @n@ is non-negative.
(Argument 0 is the command name, argument 1 is the first
user-supplied argument, etc.)
As for @read@, the type of @arg@ must be @int@,
@bool@, @char@, or @string@ and arguments must
be separated by whitespace.
If successful, @getarg@ returns a positive number.
For @int@, @bool@, and @char@ arguments, 1 is returned;
for @string@ arguments the return value is the smaller
of the number of characters in the
argument or the length the string variable.
@getarg@ returns 0 if the conversion cannot be performed,
and returns @EOF@ if there is no argument @n@.
.in -5n
.lp
The @getarg@ function can be used only in the main virtual machine.
.sh 2 "External Operations"
.lp
External operations provide access to operations that
are implemented in languages other than SR.
Typically these will be C functions.
Thus, the interface between SR and externals
is described below in terms of C.
.pp
An @external@ declaration is similar to an @op@ declaration (Sec. 3.4),
except the keyword @op@ is replaced by the keyword @external@.
An @external@ declaration can appear in a resource specification
or as a component of a resource body.
It may not appear within a @proc@ or block.
.pp
The implementation of an @external@ does not appear
in the SR program.
Instead, the code is compiled separately.
The SR linker %srl (see Appendix 3) accepts `.o' object files
and also `-l' specifications for library searching;
the standard C library is searched automatically.
User-defined @external@s should avoid the use of one- and two-character
uppercase names as well as names beginning with @sr_@ since
such names can collide with those in the SR implementation itself.
.pp
Like an @op@, an @external@ may be invoked by either @call@
or @send@.
Actual parameters are copied and then
passed to the @external@ operation as follows:
.ip "\ \ \ \(bu"
SR expressions of types @bool@, @char@, @int@, and @enum@ are passed
by value as C type @int@.
.ip "\ \ \ \(bu"
SR pointers are cast to C type @(char *)@ and passed by value.
.ip "\ \ \ \(bu"
SR files are cast to C type @(FILE *)@ and passed by value.
.ip "\ \ \ \(bu"
SR strings are guaranteed to be terminated by @'\e0'@;
they are cast to C type @(char *)@ and passed by reference
to the first character of the string.
.ip "\ \ \ \(bu"
All other SR values are passed by reference using
a pointer of C type @(char *)@;
this includes arrays and records.
.lp
Note that several kinds of parameters are passed by value only;
@var@ and @res@ should not be used with these parameters.
.pp
An @external@ can have a return specification
and hence be used to invoke a C function.
The correspondence between the type in
the return specification and the allowed type
of the C function is as follows:
.(b
.ta 1.8i
#SR #return #type  	#C #return #type
.sp .2
none            	anything or nothing
@int@, @char@, @bool@, @enum@	@int@
@ptr@ any type   	@(char *)@ or @(void *)@
@string(n)@      	@(char *)@
anything else    	not supported
.)b
.lp
If a C function returns a pointer to a null-terminated
character string, it may be described in SR
as returning @string(n)@ as long as @n@ is large
enough to accept the largest string ever expected.
(If @n@ turns out to be insufficiently large, the returned
string will be silently truncated.)
If the C function returns a null pointer,
an empty string will be returned to the SR program.
For return values\(emand @var@ or @res@ parameters\(emdeclared
as @string@, the C @strlen()@ function is called implicitly
to set the SR string length after the C function returns.
.PS L
%Examples:
.sp .5
	external fprintf(f : file; s : string(*); v : int)
	external getenv(s : string(*)) returns v : string(250)
	external gethostname(res s : string(*); namelen : int)
	external umask(newmask : int) returns oldmask : int
.PE
