--Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd.
--All rights reserved.
--
--Redistribution and use in source and binary forms, with or without
--modification, are permitted provided that the following conditions are
--met:
--
--    - Redistributions of source code must retain the above copyright
--      notice, this list of conditions and the following disclaimer.
--
--    - Redistributions in binary form must reproduce the above copyright
--      notice, this list of conditions and the following disclaimer in
--      the documentation and/or other materials provided with the
--      distribution.
--
--    - Neither the name of The Numerical ALgorithms Group Ltd. nor the
--      names of its contributors may be used to endorse or promote products
--      derived from this software without specific prior written permission.
--
--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
--IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
--TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
--PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
--OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
--EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
--PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
--PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
--LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
--NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

)abbrev domain IOMODE IOMode
++ Author: Gabriel Dos Reis
++ Date Created: September 30, 2008
++ Date Last Updated: September 30, 2008
++ Basic Operations: inputIOMode, outputIoMode, bothWayIOMode
++ Description:
++   This domain provides constants to describe directions of
++   IO conduits (file, etc) mode of operations.
IOMode(): Public == Private where
  Public == SetCategory with
    input: %
      ++ \spad{input} indicates that an IO conduit is for input.
    output: %
      ++ \spad{output} indicates that an IO conduit is for output
    bothWays: %
      ++ \spad{bothWays} indicates that an IO conduit is for both input and output.
    closed: %
      ++ \spad{closed} indicates that the IO conduit has been closed.
  Private == add
    input == _$InputIOMode$Foreign(Builtin)
    output == _$OutputIOMode$Foreign(Builtin)
    bothWays == _$BothWaysIOMode$Foreign(Builtin)
    closed == _$ClosedIOMode$Foreign(Builtin)
    x = y == %peq(x,y)$Foreign(Builtin)
    coerce m ==
      m = input => outputForm 'input
      m = output => outputForm 'output
      m = closed => outputForm 'closed
      outputForm 'bothWays
    

)abbrev category FILECAT FileCategory
++ Author: Stephen M. Watt, Victor Miller
++ Date Created: 
++ Date Last Updated: June 4, 1991
++ Basic Operations: 
++ Related Domains: File 
++ Also See:
++ AMS Classifications:
++ Keywords: 
++ Examples:
++ References:
++ Description:
++   This category provides an interface to operate on files in the
++   computer's file system.  The precise method of naming files
++   is determined by the Name parameter.  The type of the contents
++   of the file is determined by S.
 
FileCategory(Name, S): Category == FCdefinition where
    Name:      SetCategory
    S:         SetCategory
    IOMode ==> String  -- Union("input", "output", "closed")
 
    FCdefinition == SetCategory with
        open: Name -> %
          ++ open(s) returns the file s open for input.  

        open: (Name, IOMode) -> %
          ++ open(s,mode) returns a file s open for operation in the 
          ++ indicated mode: "input" or "output".

        reopen!: (%, IOMode) -> %
          ++ reopen!(f,mode) returns a file f reopened for operation in the
          ++ indicated mode: "input" or "output".
          ++ \spad{reopen!(f,"input")} will reopen the file f for input.

        close!: % -> %
          ++ close!(f) returns the file f closed to input and output.
 
        name: % -> Name
          ++ name(f) returns the external name of the file f.

        iomode: % -> IOMode
          ++ iomode(f) returns the status of the file f. The input/output 
          ++ status of f may be "input", "output" or "closed" mode.
 
        read!: % -> S
          ++ read!(f) extracts a value from file f.  The state of f is
          ++ modified so a subsequent call to \spadfun{read!} will return
          ++ the next element.

        write!: (%,S) -> S
          ++ write!(f,s) puts the value s into the file f. 
          ++ The state of f is modified so subsequents call to \spad{write!}
          ++ will append one after another.
 
)abbrev domain FILE File
++ Author: Stephen M. Watt, Victor Miller
++ Date Created: 1984
++ Date Last Updated: June 4, 1991
++ Basic Operations: 
++ Related Domains: 
++ Also See:
++ AMS Classifications:
++ Keywords: 
++ Examples:
++ References:
++ Description:
++   This domain provides a basic model of files to save arbitrary values.
++   The operations provide sequential access to the contents.
 
File(S:SetCategory): FileCategory(FileName, S) with
        readIfCan!: % -> Union(S, "failed")
            ++ readIfCan!(f) returns a value from the file f, if possible.
            ++ If f is not open for reading, or if f is at the end of file
            ++ then \spad{"failed"} is the result.
    == add
        FileState ==> SExpression
        IOMode    ==> String
 
        Rep:=Record(fileName:    FileName,   _
                    fileState:   FileState,  _
                    fileIOmode:  IOMode)
 
        defstream(fn: FileName, mode: IOMode): FileState ==
            mode = "input"  =>
              not readable? fn => error ["File is not readable", fn]
              MAKE_-INSTREAM(fn::String)$Lisp
            mode = "output" =>
              not writable? fn => error ["File is not writable", fn]
              MAKE_-OUTSTREAM(fn::String)$Lisp
            error ["IO mode must be input or output", mode]
 
        f1 = f2 ==
            f1.fileName = f2.fileName
        coerce(f: %): OutputForm ==
            f.fileName::OutputForm
 
        open fname ==
            open(fname, "input")
        open(fname, mode) ==
            fstream := defstream(fname, mode)
            [fname, fstream, mode]
        reopen!(f, mode) ==
            fname := f.fileName
            f.fileState := defstream(fname, mode)
            f.fileIOmode:= mode
            f
        close! f ==
            SHUT(f.fileState)$Lisp
            f.fileIOmode := "closed"
            f
        name f ==
            f.fileName
        iomode f ==
            f.fileIOmode
        read! f ==
            f.fileIOmode ~= "input" =>
                error "File not in read state"
            x: Maybe S := readExpr(f.fileState)$Foreign(Builtin)
            x case nothing => error "End of file"
            x@S
        readIfCan! f ==
            f.fileIOmode ~= "input" =>
                error "File not in read state"
            x: Maybe S := readExpr(f.fileState)$Foreign(Builtin)
            x case nothing => "failed"
            x@S
        write!(f, x) ==
            f.fileIOmode ~= "output" =>
                error "File not in write state"
            z := PRINT_-FULL(x, f.fileState)$Lisp
            %writeNewline(f.fileState)$Foreign(Builtin)
            x

)abbrev domain TEXTFILE TextFile
++ Author: Stephen M. Watt
++ Date Created: 1985
++ Date Last Updated: June 4, 1991
++ Basic Operations: writeLine! readLine! readLineIfCan! readIfCan! endOfFile?
++ Related Constructors:
++ Also See:
++ AMS Classifications:
++ Keywords: 
++ References:
++ Description: 
++   This domain provides an implementation of text files.  Text is stored
++   in these files using the native character set of the computer.

TextFile: Cat == Def where
    StreamName ==> Union(FileName, "console")
 
    Cat == FileCategory(FileName, String) with
        writeLine!: (%, String) -> String
          ++ writeLine!(f,s) writes the contents of the string s 
          ++ and finishes the current line in the file f.
          ++ The value of s is returned.

        writeLine!: % -> String
          ++ writeLine!(f) finishes the current line in the file f.
          ++ An empty string is returned.  The call \spad{writeLine!(f)} is
          ++ equivalent to \spad{writeLine!(f,"")}.

        readLine!: % -> String
          ++ readLine!(f) returns a string of the contents of a line from 
          ++ the file f.

        readLineIfCan!: % -> Union(String, "failed")
          ++ readLineIfCan!(f) returns a string of the contents of a line from
          ++ file f, if possible.  If f is not readable or if it is 
          ++ positioned at the end of file, then \spad{"failed"} is returned.

        readIfCan!: % -> Union(String, "failed")
          ++ readIfCan!(f) returns a string of the contents of a line from
          ++ file f, if possible.  If f is not readable or if it is 
          ++ positioned at the end of file, then \spad{"failed"} is returned.

        endOfFile?: % -> Boolean
          ++ endOfFile?(f) tests whether the file f is positioned after the
          ++ end of all text.  If the file is open for output, then
          ++ this test is always true.
 
    Def == File(String) add
        FileState ==> SExpression
 
        Rep := Record(fileName:   FileName,    _
                      fileState:  FileState,   _
                      fileIOmode: String)
 
        read! f      == readLine! f
        readIfCan! f == readLineIfCan! f
 
        readLine! f ==
            f.fileIOmode ~= "input"  => error "File not in read state"
            s: Maybe String := readLine(f.fileState)$Foreign(Builtin)
            s case String => s
            error "End of file"
        readLineIfCan! f ==
            f.fileIOmode ~= "input"  => error "File not in read state"
            s: Maybe String := readLine(f.fileState)$Foreign(Builtin)
            s case String => s@String
            "failed"
        write!(f, x) ==
            f.fileIOmode ~= "output" => error "File not in write state"
            %writeString(x, f.fileState)$Foreign(Builtin)
            x
        writeLine! f ==
            f.fileIOmode ~= "output" => error "File not in write state"
            %writeNewline(f.fileState)$Foreign(Builtin)
            ""
        writeLine!(f, x) ==
            f.fileIOmode ~= "output" => error "File not in write state"
            %writeLine(x,f.fileState)$Foreign(Builtin)
            x
        endOfFile? f ==
          f.fileIOmode = "output" => false
          (EOFP(f.fileState)$Lisp pretend Boolean) => true
          false

)abbrev domain KAFILE KeyedAccessFile
++ Author: Stephen M. Watt
++ Date Created: 1985
++ Date Last Updated: June 4, 1991
++ Basic Operations: 
++ Related Domains: 
++ Also See:
++ AMS Classifications:
++ Keywords: 
++ Examples:
++ References:
++ Description:
++  This domain allows a random access file to be viewed both as a table
++  and as a file object.
 
 
KeyedAccessFile(Entry): KAFcategory == KAFcapsule where
    Name  ==> FileName
    Key   ==> String
    Entry :   SetCategory
 
    KAFcategory ==
        Join(FileCategory(Name, Record(key: Key, entry: Entry)),
             TableAggregate(Key, Entry)) with
                 finiteAggregate
                 pack!: % -> %
                     ++ pack!(f) reorganizes the file f on disk to recover 
                     ++ unused space.
 
    KAFcapsule == add
 
        CLASS     ==> 131   -- an arbitrary no. greater than 127
        FileState ==> SExpression
        IOMode    ==> String
 
 
        Cons:= Record(car: SExpression, cdr: SExpression)
        Rep := Record(fileName:    Name,     _
                      fileState:   FileState,   _
                      fileIOmode:  IOMode)
 
        defstream(fn: Name, mode: IOMode): FileState ==
            mode = "input"  =>
              not readable? fn => error ["File is not readable", fn]
              RDEFINSTREAM(fn::String)$Lisp
            mode = "output" =>
              not writable? fn => error ["File is not writable", fn]
              RDEFOUTSTREAM(fn::String)$Lisp
            error ["IO mode must be input or output", mode]
 
        ---- From Set ----
        f1 = f2 ==
            f1.fileName = f2.fileName
        coerce(f: %): OutputForm ==
            f.fileName::OutputForm
 
        ---- From FileCategory ----
        open fname ==
            open(fname, "either")
        open(fname, mode) ==
            mode = "either" =>
                exists? fname =>
                    open(fname, "input")
                writable? fname =>
                    reopen!(open(fname, "output"), "input")
                error "File does not exist and cannot be created"
            [fname, defstream(fname, mode), mode]
        reopen!(f, mode) ==
            close! f
            if mode ~= "closed" then
                f.fileState := defstream(f.fileName, mode)
                f.fileIOmode  := mode
            f
        close! f  ==
            if f.fileIOmode ~= "closed" then
                RSHUT(f.fileState)$Lisp
                f.fileIOmode  := "closed"
            f
        read! f ==
            f.fileIOmode ~= "input" => error ["File not in read state",f]
            ks: List Symbol := RKEYIDS(f.fileName)$Lisp
            null ks => error ["Attempt to read empty file", f]
            ix := random()$Integer rem #ks
            k: String := string(ks.ix)
            [k, SPADRREAD(k, f.fileState)$Lisp]
        write!(f, pr) ==
            f.fileIOmode ~= "output" => error ["File not in write state",f]
            SPADRWRITE(pr.key, pr.entry, f.fileState)$Lisp
            pr
        name f ==
            f.fileName
        iomode f ==
            f.fileIOmode
 
        ---- From TableAggregate ----
        empty() ==
            fn := new("", "kaf", "sdata")$Name
            open fn
        keys f ==
            close! f
            l: List SExpression := RKEYIDS(f.fileName)$Lisp
            [PNAME(n)$Lisp for n in l]
        # f ==
            # keys f
        elt(f,k) ==
            reopen!(f, "input")
            SPADRREAD(k, f.fileState)$Lisp
        setelt(f,k,e) ==
            -- Leaves f in a safe, closed state.  For speed use "write".
            reopen!(f, "output")
            try write!(f, [k,e])
            finally close! f
            e
        search(k,f) ==
            not member?(k, keys f) => "failed"   -- can't trap RREAD error
            reopen!(f, "input")
            (SPADRREAD(k, f.fileState)$Lisp)@Entry
        remove!(k:String,f:%)  ==
            result := search(k,f)
            result case "failed" => result
            close! f
            RDROPITEMS(NAMESTRING(f.fileName)$Lisp, LIST(k)$Lisp)$Lisp
            result
        pack! f ==
            close! f
            RPACKFILE(f.fileName)$Lisp
            f

)abbrev domain LIB Library
++ Author: Stephen M. Watt
++ Date Created: 1985
++ Date Last Updated: June 4, 1991
++ Basic Operations: 
++ Related Domains: KeyedAccessFile
++ Also See:
++ AMS Classifications:
++ Keywords: 
++ Examples:
++ References:
++ Description:
++   This domain provides a simple way to save values in files.
Library(): Join(TableAggregate(String, Any),Eltable(Symbol,Any)) with
         library:  FileName -> %
             ++ library(ln) creates a new library file.
         pack!: % -> %
             ++ pack!(f) reorganizes the file f on disk to recover 
             ++ unused space.

         setelt : (%, Symbol, Any) -> Any
             ++ \spad{lib.k := v} saves the value \spad{v} in the library
             ++ \spad{lib}.  It can later be extracted using the key \spad{k}.

    == KeyedAccessFile(Any) add
         library f == per open f
         elt(f:%,v:Symbol) == elt(rep f, string v)
         setelt(f:%, v:Symbol, val:Any) == setelt(rep f, string v, val)

