--
--	Inmos Libraries
--	Copyright (C) 1988, 1989, 1990, 1991, 1993  Inmos Limited
--      Modifications Copyright (C) 2004  Fred Barnes
--
--	This library is free software; you can redistribute it and/or
--	modify it under the terms of the GNU Lesser General Public
--	License as published by the Free Software Foundation; either
--	version 2 of the License, or (at your option) any later version.
--
--	This library is distributed in the hope that it will be useful,
--	but WITHOUT ANY WARRANTY; without even the implied warranty of
--	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
--	Lesser General Public License for more details.
--
--	You should have received a copy of the GNU Lesser General Public
--	License along with this library; if not, write to the Free Software
--	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
--

--** @module hostsp

--
-- UNIX hostio library interface routines
--
-- $Source: /u3/proj/kroc/develop/inmos/libs/hostsp/RCS/hostunix.occ,v $
--
-- $Id: hostunix.occ,v 1.5 1997/02/04 16:09:06 mdp2 Exp $
--
-- Designed to be included in spserver so as to avoid name clash
-- problems with identically named routines elsewhere
--
-- (C) Copyright 1996 M.D. Poole <M.D.Poole@ukc.ac.uk>
-- University of Kent at Canterbury
-- based on INMOS hostio.lib
--

#COMMENT "Collected and adapted from Host i/o library, V1.00, 10/03/89"
#COMMENT "(c) Copyright INMOS Limited 1989"
#COMMENT "DOS adaptations (c) Copyright MD Poole 1993"
#COMMENT "Unix adaptations: (c) OFA project (MD Poole 1995,1996)"

#INCLUDE "unixerrs.inc"

VAL unix.max.readbuffer.size IS (sp.max.readbuffer.size/4) + 1:
VAL unix.max.getenvname.size IS (sp.max.getenvname.size/4) + 1:
VAL unix.max.openname.size IS (sp.max.openname.size/4) + 1:
VAL unix.max.removename.size IS (sp.max.removename.size/4) + 1:
VAL unix.max.writebuffer.size IS (sp.max.writebuffer.size/4) + 1:
VAL unix.max.systemcommand.size IS (sp.max.systemcommand.size/4) + 1:

#INCLUDE "conv.occ"
#PRAGMA EXTERNAL "PROC C.fclose (INT result, VAL INT32 handle)  = 0 "
#PRAGMA EXTERNAL "PROC C.comdline (INT result, VAL INT all, INT len, [unix.max.readbuffer.size]INT block, VAL INT bytespace) = 0"
#PRAGMA EXTERNAL "PROC C.fflush (INT result, VAL INT32 handle)  = 0 "
#PRAGMA EXTERNAL "PROC C.getenvval (INT result, VAL [unix.max.getenvname.size]INT envname, INT len, [unix.max.readbuffer.size]INT block, VAL INT bytespace) = 0"
#PRAGMA EXTERNAL "PROC C.fgets (INT result, VAL INT32 handle, [unix.max.readbuffer.size]INT buffer, VAL INT bytes.to.read, INT bytes.read)  = 0 "
#PRAGMA EXTERNAL "PROC C.fopen (INT32 handle, VAL[unix.max.openname.size]INT name, VAL[1]INT mode)  = 0 "
#PRAGMA EXTERNAL "PROC C.fremove (INT result, VAL[unix.max.removename.size]INT fname)  = 0 "
#PRAGMA EXTERNAL "PROC C.frename (INT result, VAL[unix.max.openname.size]INT oldname, VAL[unix.max.openname.size]INT newname)  = 0 "
#PRAGMA EXTERNAL "PROC C.fseek (INT result, VAL INT32 handle, VAL INT32 origin, VAL INT32 position)  = 0 "
#PRAGMA EXTERNAL "PROC C.fread (INT result, VAL INT32 handle, [unix.max.readbuffer.size]INT buffer, VAL INT bytes.to.read, INT bytes.read)  = 0 "
#PRAGMA EXTERNAL "PROC C.fwrite (INT result, VAL INT32 handle, VAL [unix.max.writebuffer.size]INT buffer, VAL INT bytes.to.write, INT bytes.written)  = 0 "
#PRAGMA EXTERNAL "PROC C.system (INT result, INT32 status, VAL [unix.max.systemcommand.size]INT block) = 0"
#PRAGMA EXTERNAL "PROC C.ftell (INT result, VAL INT32 handle, INT32 position)  = 0 "
#PRAGMA EXTERNAL "PROC C.timenow (INT32 localtime, INT32 UTCtime) = 0"
#PRAGMA EXTERNAL "PROC C.exitoccam (INT result, VAL INT32 status) = 0"

--{{{  so.close
--* Close a stream.
--
-- Before closing the stream unwritten data is flushed or any unread
-- buffered input is discarded.
--
-- @param fs from server
-- @param ts to server
-- @param streamid the identification number of the open stream to be
--   closed
-- @param result equal to spr.ok if the stream closed properly;
--   otherwise >= spr.operation.failed - refer to hostio.inc or iserver
--   documentation for further details
PROC so.close (CHAN OF SP fs, ts, VAL INT32 streamid, BYTE result)
  --{{{  Call Unix
  INT res:
  SEQ
    C.fclose (res, streamid)
    IF
      res = 0
        result := spr.ok
      res = Unix.INVALID.HANDLE
        result := spr.invalid.streamid
      TRUE
        result := spr.operation.failed
  --}}}  
:
--}}}  so.close
--{{{  so.commandline
--{{{  OC.comdline
PROC OC.comdline (INT result, VAL INT all, INT len, []BYTE block)
  [unix.max.readbuffer.size]INT unixblock:
  SEQ
    C.comdline (result, all, len, unixblock, SIZE block)
    IF
      (result = 0) AND (len > 0)
        chars.to.bytes (unixblock, [block FOR len])
      TRUE
        len := 0
:
--}}}  
--* Return the command line passed to the server on invocation.
--
-- @param fs from server
-- @param ts to server
-- @param all either sp.short.commandline, remove the server's own
--   name, its recognised options and their parameters or
--   sp.whole.commandline, the entire command line
-- @param length the number of bytes returned in string, starting from
--   string[ 0 ], being zero in the case of result >=
--   spr.operation.failed
-- @param string contains the requested command line if result =
--   spr.ok; contains a truncation of the requested command line if
--   result = spr.buffer.overflow; is undefined otherwise.  There is no
--   restriction on the size of string.
-- @param result equal to spr.ok if the command line was successfully
--   returned; otherwise takes on a value indicating what went wrong:
--   spr.buffer.overflow   string is not long enough to contain the
--   command line, but the latter has been truncated to fit
--   >=spr.operation.failed  the operation failed - refer to hostio.inc
--   or iserver documentation for further details
PROC so.commandline (CHAN OF SP fs, ts, VAL BYTE all,
                     INT length, []BYTE string, BYTE result)
  INT iresult:
  SEQ
    OC.comdline (iresult, INT all, length, string)
    IF
      iresult = 0
        result := spr.ok
      TRUE
        result := spr.buffer.overflow
:
--}}}  so.commandline
--{{{  so.flush
--* Flush the specified output stream.
--
-- To flush means to write out any internally buffered data to the
-- stream that it is associated with. The stream remains open.
--
-- @param fs from server
-- @param ts to server
-- @param streamid the identification number of the open stream to be
--   flushed
-- @param result equal to spr.ok stream successfully flushed; otherwise
--   >= spr.operation.failed - see hostio.inc or iserver documentation
--   for further details
PROC so.flush (CHAN OF SP fs, ts, VAL INT32 streamid, BYTE result)
  --{{{  more info
  --           This routine is unnecessary for streamid = spid.stdout ( standard
  --           output ) since this stream is always flushed automatically.
  --}}}  

  --{{{  Call Unix
  INT res:
  SEQ
    C.fflush (res, streamid)
    IF
      res = 0
        result := spr.ok
      res = Unix.INVALID.HANDLE
        result := spr.invalid.streamid
      TRUE
        result := spr.operation.failed
  --}}}  
:
--}}}  so.flush
--{{{  so.getenv
--{{{  OC.getenv
PROC OC.getenv (INT result, VAL []BYTE envname, INT len, []BYTE block)
  [unix.max.getenvname.size]INT unixenvname:
  [unix.max.readbuffer.size]INT unixblock:
  SEQ
    bytes.to.chars (envname, unixenvname)
    C.getenvval (result, unixenvname, len, unixblock, SIZE block)
    IF
      (result = 0) AND (len > 0)
        chars.to.bytes (unixblock, [block FOR len])
      TRUE
        len := 0
:
--}}}  
--* Return the contents of an environment variable from whatever host
-- is being used.
--
-- The largest size the returned contents of an environment variable
-- can be is limited by the functionality of the server, not by this
-- routine.
--
-- @param fs from server
-- @param ts to server
-- @param name a string containing the name of the desired environment
--   variable.  name should not have more than sp.max.getenvname.size
--   characters in it.
-- @param length if result is spr.ok, the number of bytes in value,
--   starting at value[ 0 ]; (SIZE value) if result is
--   spr.buffer.overflow; zero otherwise.
-- @param value the contents of the environment variable, if result is
--   spr.ok; truncated environment variable if result is
--   spr.buffer.overflow; undefined otherwise.
-- @param result spr.ok if the environment string successfully
--   returned; some other value otherwise, indicating the sort of
--   problem: spr.bad.name          the specified name is a null string
--   spr.bad.packet.size   (SIZE name) > sp.max.getenvname.size
--   spr.buffer.overflow   environment string too large for value; it
--   is truncated to fit >=spr.operation.failed  could not read
--   environment string - see hostio.inc or iserver documentation for
--   further details
PROC so.getenv (CHAN OF SP fs, ts, VAL []BYTE name,
                INT length, []BYTE value, BYTE result)
  IF
    (SIZE name) > sp.max.getenvname.size
      SEQ
        result := spr.bad.packet.size
        length := 0
    (SIZE name) = 0
      SEQ
        result := spr.bad.name
        length := 0
    TRUE
      INT iresult:
      SEQ
        OC.getenv (iresult, name, length, value)
        IF
          iresult = 0
            result := spr.ok
          iresult = (-1)
            result := spr.buffer.overflow
          TRUE
            result := spr.operation.failed
:
--}}}  so.getenv
--{{{  so.gets
--{{{  OC.fgets
PROC OC.fgets (INT result, VAL INT32 handle, []BYTE buffer, INT bytes.read)
  [unix.max.readbuffer.size]INT unixbuffer:
  SEQ
    C.fgets (result, handle, unixbuffer, SIZE buffer, bytes.read)
    IF
      (result = 0) AND (bytes.read > 0)
        chars.to.bytes (unixbuffer, [buffer FOR bytes.read])
      TRUE
        bytes.read := 0
:
--}}}  
--* Read a line from the specified input stream.
--
-- Characters are read until a newline sequence is found, the end of
-- the file is reached, or the number of characters is not less than
-- sp.max.readbuffer.size. If a newline sequence is found, it is not
-- included in data.
--
-- @param fs from server
-- @param ts to server
-- @param streamid the identification number of the open stream to be
--   read from
-- @param bytes.read if result is spr.ok this is the number of bytes
--   read; if result is spr.buffer.overflow then this will be (SIZE
--   data) and will be less than the actual number of bytes read from
--   the file; otherwise zero.
-- @param data an array of bytes holding line read if result is spr.ok;
--   or holding a truncation of the line if result is
--   spr.buffer.overflow.  It is undefined otherwise.  If result is
--   spr.ok or spr.buffer.overflow then the bytes will be [data FROM 0
--   FOR bytes.read].
-- @param result equal to spr.ok if line was successfully read;
--   otherwise takes on a value indicating what went wrong:
--   spr.bad.packet.size   data is too large, (SIZE data) >
--   sp.max.readbuffer.size spr.buffer.overflow   data not large enough
--   to hold the line, in which case the line is truncated to fit
--   >=spr.operation.failed  the read failed, either because the end of
--   the file has been reached or some error occurred - see hostio.inc
--   or iserver documentation for further details
PROC so.gets (CHAN OF SP fs, ts, VAL INT32 streamid,
              INT bytes.read, []BYTE data, BYTE result)
  INT res:
  SEQ
    --{{{  Call Unix
    OC.fgets (res, streamid, data, bytes.read)
    IF
      res = 0
        result := spr.ok
      res = Unix.ACCESS.DENIED
        result := spr.bad.stream.use
      res = Unix.INVALID.HANDLE
        result := spr.invalid.streamid
      res = Unix.BUFFER.OVERFLOW
        result := spr.buffer.overflow
      TRUE
        result := spr.operation.failed
    --}}}  
:
--}}}  
--{{{  so.open
--{{{  OC.fopen
PROC OC.fopen (INT32 handle, VAL[]BYTE name, VAL[2]BYTE mode)
  [unix.max.openname.size]INT unixname:
  [1]INT unixmode:
  SEQ
    bytes.to.chars (name, unixname)
    bytes.to.chars (mode, unixmode)
    C.fopen (handle, unixname, unixmode)
:
--}}}  
--* Open a file.
--
-- No path environment variable is used to locate the file; what is in
-- name is all that is used ( cf. so.popen.read )
--
-- @param fs from server
-- @param ts to server
-- @param fname the name of the file to be opened.  The name of the
--   file must fit exactly into name, i.e. there are (SIZE name)
--   characters in the name of the file.  A directory specification may
--   form part of name.
-- @param type either spt.binary, for a file containing raw bytes only
--   or spt.text, for a file stored as text records separated by
--   newlines
-- @param mode one of spm.input            open existing file for
--   reading spm.output           open new file, or truncate existing
--   one, for writing spm.append           open new file, or append to
--   existing one, for writing spm.existing.update  open existing file
--   for update ( reading and writing ), starting at the beginning of
--   the file spm.new.update       open new file, or truncate existing
--   one, for update spm.append.update    open new file, or append to
--   existing one, for update When a file is opened for update then the
--   resulting stream may be used for input and output.
-- @param streamid if result is spr.ok, the identification number of
--   the stream associated with the file that was opened; undefined
--   otherwise
-- @param result spr.ok if the file was successfully opened; otherwise
--   it takes on a value indicating what went wrong:
--   spr.bad.packet.size   name too large: (SIZE name) >
--   sp.max.openname.size spr.bad.name          null file name
--   spr.bad.type          invalid type spr.bad.mode          invalid
--   mode >=spr.operation.failed  the open failed - see hostio.inc or
--   iserver documentation for further details
PROC so.open (CHAN OF SP fs, ts, VAL []BYTE fname, VAL BYTE type, mode,
              INT32 streamid, BYTE result)
  IF
    --{{{  simple error cases
    (SIZE fname) > sp.max.openname.size
      result := spr.bad.packet.size
    (SIZE fname) = 0
      result := spr.bad.name
    (type <> spt.text) AND (type <> spt.binary)
      result := spr.bad.type
    (mode < spm.input) OR (mode > spm.append.update)
      result := spr.bad.mode
    --}}}  
    TRUE
      SEQ
        --{{{  Call Unix
        VAL modeUnix IS [["r ", "w ", "a ", "r+", "w+", "a+"],
                         ["r ", "w ", "a ", "r+", "w+", "a+"]]:
        SEQ
          OC.fopen (streamid, fname, modeUnix [(INT type)-1][(INT mode)-1])
          IF
            streamid <> Unix.NULL
              result := spr.ok
            TRUE
              result := spr.operation.failed
        --}}}  
:
--}}}  so.open
--{{{  so.putblock
--{{{  OC.fwrite
PROC OC.fwrite (INT result, VAL INT32 handle, VAL []BYTE buffer, INT bytes.written)
  [unix.max.writebuffer.size]INT unixbuffer:
  SEQ
    bytes.to.chars (buffer, unixbuffer)
    C.fwrite (result, handle, unixbuffer, SIZE buffer, bytes.written)
:
--}}}  
--* Write a limited number of bytes to a stream.
--
-- @param fs from server
-- @param ts to server
-- @param streamid the identification number of the open stream to
--   write to
-- @param data a string of bytes ALL of which are to be written to the
--   stream
-- @param length.written this gives the number of bytes written. If
--   length.written <> (SIZE data) then an error occurred. 0 <=
--   length.written <= sp.max.writebuffer.size
-- @param result if not equal to spr.bad.packet.size meaning that too
--   many bytes were requested, ie (SIZE data) >
--   sp.max.writebuffer.size, then it is equal to spr.ok
PROC so.putblock (CHAN OF SP fs, ts, VAL INT32 streamid, VAL []BYTE data,
               INT length.written, BYTE result)
  IF
    (SIZE data) > sp.max.writebuffer.size
      SEQ
        result := spr.bad.packet.size
        length.written := 0
    TRUE
      --{{{  Call Unix
      INT res:
      SEQ
        OC.fwrite (res, streamid, data, length.written)
        IF
          res = 0
            result := spr.ok
          res = Unix.ACCESS.DENIED
            result := spr.bad.stream.use
          res = Unix.INVALID.HANDLE
            result := spr.invalid.streamid
          TRUE
            result := spr.operation.failed
      --}}}  
:
--}}}  so.putblock
--{{{  so.remove
--{{{  OC.fremove
PROC OC.fremove (INT result, VAL[]BYTE fname)
  [unix.max.removename.size]INT unixname:
  SEQ
    bytes.to.chars (fname, unixname)
    C.fremove (result, unixname)
:
--}}}  
--* Delete the specified file.
--
-- No path environment variable is used; what is in name is all that is
-- considered.
--
-- @param fs from server
-- @param ts to server
-- @param fname the name of the file to be deleted; the filename must
--   fill all of the bytes of name
-- @param result equals spr.ok if the file was removed; otherwise takes
--   on a value indicating what went wrong: spr.bad.packet.size   name
--   too large, (SIZE name) > sp.max.removename.size spr.bad.name
--      name is null >=spr.operation.failed  the delete failed - see
--   hostio.inc or iserver documentation for further details
PROC so.remove (CHAN OF SP fs, ts, VAL []BYTE fname, BYTE result)
  IF
    (SIZE fname) > sp.max.removename.size
      result := spr.bad.packet.size
    (SIZE fname) = 0
      result := spr.bad.name
    TRUE
      --{{{  Call Unix
      INT res:
      SEQ
        OC.fremove (res, fname)
        IF
          res = 0
            result := spr.ok
          TRUE  -- FILE/PATH.NOT.FOUND, ACCESS.DENIED
            result := spr.operation.failed
      --}}}  
:
--}}}  so.remove
--{{{  so.rename
--{{{  OC.frename
PROC OC.frename (INT result, VAL[]BYTE oldname, VAL[]BYTE newname)
  [unix.max.openname.size]INT unixoldname, unixnewname:
  SEQ
    bytes.to.chars (oldname, unixoldname)
    bytes.to.chars (newname, unixnewname)
    C.frename (result, unixoldname, unixnewname)
:
--}}}  
--* Rename the specified file.
--
-- @param fs from server
-- @param ts to server
-- @param oldfname the present name of the file to be renamed
-- @param newfname the desired name of the file to be renamed
-- @param result equals spr.ok if the renaming was successful;
--   otherwise takes on a value indicating what went wrong:
--   spr.bad.packet.size   the combined lengths of oldname and newname
--   is too large - it must be less than sp.max.renamename.size
--   spr.bad.name          either or both of the filenames are null
--   >=spr.operation.failed  the renaming failed - see hostio.inc or
--   iserver documentation for further details
PROC so.rename (CHAN OF SP fs, ts, VAL []BYTE oldfname, newfname, BYTE result)
  IF
    (((SIZE oldfname) + (SIZE newfname)) > sp.max.renamename.size)
      result := spr.bad.packet.size
    ((SIZE oldfname) = 0) OR ((SIZE newfname) = 0)
      result := spr.bad.name
    TRUE
      --{{{  Call Unix
      INT res:
      SEQ
        OC.frename (res, oldfname, newfname)
        IF
          res = 0
            result := spr.ok
          TRUE  -- FILE/PATH.NOT.FOUND, ACCESS.DENIED, NOT.SAME.DEVICE
            result := spr.operation.failed
      --}}}  
:
--}}}  so.rename
--{{{  so.seek
--* Set the file position for the specified stream.
--
-- @param fs from server
-- @param ts to server
-- @param streamid the identification number of the open stream
--   associated with the file the position of which is to be set
-- @param offset the offset from origin of the new position for reading
--   or writing.  For a binary file the new position will be offset
--   bytes, perhaps negative, from the position defined by origin. For
--   a text file offset must be zero or a value returned by so.tell; in
--   the latter case origin must be spo.start, and offset greater than
--   or equal to zero.
-- @param origin one of spo.start    the start of the file spo.current
--   the current position in the file spo.end      the end of the file
-- @param result equal to spr.ok if the file position was successfully
--   set; otherwise takes on a value indicating what the problem was:
--   spr.bad.origin        invalid origin >=spr.operation.failed  the
--   seek failed - see hostio.inc or iserver documentation for further
--   details
PROC so.seek (CHAN OF SP fs, ts, VAL INT32 streamid, offset, origin, BYTE result)
  IF
    (origin <> spo.start) AND (origin <> spo.current) AND (origin <> spo.end)
      result := spr.bad.origin
    TRUE
      VAL UnixMoveMethod IS origin - 1(INT32):
      INT res:
      SEQ
        C.fseek (res, streamid, UnixMoveMethod, offset)
        IF
          res = 0
            result := spr.ok
          TRUE
            result := spr.operation.failed
:
--}}}  so.seek
--{{{  so.read
--{{{  OC.fread
PROC OC.fread (INT result, VAL INT32 handle, []BYTE buffer, INT bytes.read)
  [unix.max.readbuffer.size]INT unixbuffer:
  SEQ
    C.fread (result, handle, unixbuffer, SIZE buffer, bytes.read)
    IF
      (result = 0) AND (bytes.read > 0)
        chars.to.bytes (unixbuffer, [buffer FOR bytes.read])
      TRUE
        bytes.read := 0
:
--}}}  
--* Read an unlimited number of bytes from a stream.
--
-- @param fs from server
-- @param ts to server
-- @param streamid the identification number of the open stream to be
--   read from
-- @param bytes.read this gives the number of bytes read from the file;
--   these bytes will be [data FROM 0 FOR bytes.read]; if bytes.read <>
--   (SIZE data) then either an error occurred or the end of the file
--   was reached.
-- @param data this contains the bytes read from the file; the number
--   of bytes requested to be read is (SIZE data).
PROC so.read (CHAN OF SP fs, ts, VAL INT32 streamid,
              INT bytes.read, []BYTE data)
  --{{{  more information
  -- Notes:    If more than a packet's worth of data is to be read
  --           from a file then one cannot guarantee that all of the data
  --           will be read from the file before any other process
  --           accesses the same file.
  --}}}  
  --{{{  history
  -- Removed unnecessary use of test (result = spr.ok) from WHILE loop
  -- condition since present implementation of sp.read always returns
  -- spr.ok; V1.1, SRH 14-May-90
  -- Altered one of the WHILE loop conditions from ( length.got <> 0 )
  -- to, effectively, ( length.got = length.to.get ); V1.2, SRH 16-Aug-90
  --}}}  

  BOOL reading :
  SEQ
    bytes.read := 0
    reading := TRUE
    WHILE reading AND (bytes.read < (SIZE data))
      INT length.got, length.to.get :
      SEQ
        IF
          ((SIZE data) - bytes.read) > sp.max.readbuffer.size
            length.to.get := sp.max.readbuffer.size
          TRUE
            length.to.get := ((SIZE data) - bytes.read)

        BYTE result :
        INT res:
        SEQ
          --{{{  Call Unix
          buffer IS [data FROM bytes.read FOR (SIZE data) - bytes.read]:
          OC.fread (res, streamid, buffer, length.got)
          IF
            res = 0
              result := spr.ok
            res = Unix.ACCESS.DENIED
              result := spr.bad.stream.use
            res = Unix.INVALID.HANDLE
              result := spr.invalid.streamid
            TRUE
              result := spr.operation.failed
          --}}}  
          bytes.read := bytes.read + length.got
          reading := (res = 0) AND ( length.got = length.to.get )
:
--}}}  so.read
--{{{  so.write
--* Write an unlimited number of bytes to a stream.
--
-- @param fs from server
-- @param ts to server
-- @param streamid the identification number of the open stream to
--   write to
-- @param data a string of bytes ALL of which are to be written to the
--   file
-- @param length this gives the number of bytes written; if length <>
--   (SIZE data) then an error occurred
PROC so.write (CHAN OF SP fs, ts, VAL INT32 streamid, VAL []BYTE data,
               INT length)
  --{{{  more information
  -- Notes:    If more than a packet's worth of data is to be written
  --           to a file then one cannot guarantee that all of the data
  --           will be written to the file before any other process
  --           accesses the same file.
  --}}}  
  --{{{  history
  -- Removed the unnecessary test ( result = spr.ok ) from the WHILE loop
  -- condition since the present implementation of sp.write always returns
  -- spr.ok; V1.1, SRH 14-May-90
  --}}}  

  BOOL writing :
  SEQ
    writing := TRUE
    length  := 0
    WHILE writing AND (length < (SIZE data))
      INT length.to.write, length.written :
      SEQ
        IF
          ((SIZE data) - length) > sp.max.writebuffer.size
            length.to.write := sp.max.writebuffer.size
          TRUE
            length.to.write := ((SIZE data) - length)

        INT res:
        SEQ
          --{{{  Call Unix
          VAL buffer IS [data FROM length FOR (SIZE data) - length]:
          OC.fwrite (res, streamid, [buffer FOR length.to.write], length.written)
          --}}}  
          writing := (res = 0) AND (length.to.write = length.written)
          length  := length + length.written
:
--}}}  so.write
--{{{  so.system
--{{{  OC.system
PROC OC.system (INT result, INT32 status, VAL []BYTE block)
  [unix.max.systemcommand.size]INT unixsyscmdname:
  SEQ
    bytes.to.chars (block, unixsyscmdname)
    C.system (result, status, unixsyscmdname)
:
--}}}  
--* Execute a command on the host system.
--
-- @param fs from server
-- @param ts to server
-- @param command the command to be executed, which may be null
-- @param status if command is not null and result is spr.ok then this
--   is the return value of the command, which is host dependent
-- @param result equal to spr.ok if a host command processor exists;
--   otherwise: spr.bad.packet.size   command too large, (SIZE command)
--   > sp.max.systemcommand.size >=spr.operation.failed  the operation
--   failed - see hostio.inc or iserver documentation for further
--   details
PROC so.system (CHAN OF SP fs, ts, VAL []BYTE command,
                INT32 status, BYTE result)
  IF
    (SIZE command) > sp.max.systemcommand.size
      result := spr.bad.packet.size
    TRUE
      --{{{  Call Unix
      INT res:
      SEQ
        OC.system (res, status, command)
        IF
          res = 0
            result := spr.ok
          TRUE
            result := spr.operation.failed
      --}}}  
:
--}}}  so.system
--{{{  so.tell
--* Return the current file position for the specified stream.
--
-- @param fs from server
-- @param ts to server
-- @param streamid the identification number of the open stream
--   associated whith the file the reading/writing position of which is
--   desired
-- @param position the current file position
-- @param result equal to spr.ok if the file position determined;
--   otherwise >= spr.operation.failed - refer to hostio.inc or iserver
--   documentation for further details
PROC so.tell (CHAN OF SP fs, ts, VAL INT32 streamid,
              INT32 position, BYTE result)
  INT res:
  SEQ
    C.ftell (res, streamid, position)
    IF
      res = 0
        result := spr.ok
      TRUE
        result := spr.operation.failed
:
--}}}  so.tell
--{{{  so.time
--* Retrieve the local and UTC time from the host system.
--
-- Both times are expressed as the number of seconds that have elapsed
-- since midnight on the 1st of January, 1970 UTC, and are given as
-- unsigned INT32s.
--
-- @param fs from server
-- @param ts to server
-- @param localtime the local time
-- @param UTCtime the Coordinated Universal Time, if available; zero if
--   not available
PROC so.time (CHAN OF SP fs, ts, INT32 localtime, UTCtime)
  SEQ
    C.timenow (localtime, UTCtime)
:
--}}}  so.time
--{{{  so.version
--* Return identification information about the server and the host
-- that it is running on.
--
-- If any of the information ( except result ) has the value zero then
-- that information is not available. The parameter version cannot
-- distinguish between e.g. 1.41 and 1.42.
--
-- @param fs from server
-- @param ts to server
-- @param version on division by 10 this gives the version of the
--   server
-- @param host identifies the host via sph. constants
-- @param os identifies the host environment via spo. constants
-- @param board identifies the interface board via spb. constants
PROC so.version (CHAN OF SP fs, ts,
                 BYTE version, host, os, board)
  SEQ
    version := 0(BYTE)
    host := sph.PC
    os := spo.linux
    board := 0(BYTE)
:
--}}}  so.version
--{{{  so.exit
--* Terminate the server.
--
-- @param fs from server
-- @param ts to server
-- @param status a value which the server passes directly on to the
--   host environment, except in the following two cases: sps.success -
--   a host specific success result is passed on sps.failure - a host
--   specific failure result is passed on
-- @param result equals spr.ok if the operation succeeded; otherwise >=
--   spr.operation.failed - see hostio.inc or iserver documentation for
--   further details
PROC so.exit (CHAN OF SP fs, ts, VAL INT32 status, BYTE result)
  --{{{  Call Unix
  INT res:
  SEQ
    C.exitoccam (res, status)
    IF
      res = 0
        result := spr.ok
      TRUE
        result := spr.operation.failed
  --}}}  
:
--}}}  so.exit
