)abbrev category FILECAT FileCategory
++ Author: Stephen M. Watt, Victor Miller
++ 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.

        flush : % -> Void
            ++ flush(f) make sure that buffered data in written out to
            ++ the operating system.


)abbrev domain FILE File
++ Author: Stephen M. Watt, Victor Miller
++ Date Created: 1984
++ 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) ==
            if f.fileIOmode = "output" then flush(f)
            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 : None := fri_read(f.fileState)$Lisp
            eof_marker?(x)$Lisp =>
                error "End of file"
            x pretend S
        readIfCan! f ==
            f.fileIOmode ~= "input" =>
                error "File not in read state"
            x : None := fri_read(f.fileState)$Lisp
            eof_marker?(x)$Lisp => "failed"
            x pretend S
        write!(f, x) ==
            f.fileIOmode ~= "output" =>
                error "File not in write state"
            fri_write(x, f.fileState)$Lisp
            x

        flush f ==
            f.fileIOmode ~= "output" => error "File not in write state"
            FORCE_-OUTPUT(f.fileState)$Lisp


)abbrev domain TEXTFILE TextFile
++ Author: Stephen M. Watt
++ Date Created: 1985
++ 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 : None := read_line(f.fileState)$Lisp
            NULL(s)$Lisp => error "End of file"
            s pretend String
        readLineIfCan! f ==
            f.fileIOmode ~= "input"  => error "File not in read state"
            s : None := read_line(f.fileState)$Lisp
            NULL(s)$Lisp => "failed"
            s pretend String
        write!(f, x) ==
            f.fileIOmode ~= "output" => error "File not in write state"
            PRINTEXP(x, f.fileState)$Lisp
            x
        writeLine! f ==
            f.fileIOmode ~= "output" => error "File not in write state"
            TERPRI(f.fileState)$Lisp
            ""
        writeLine!(f, x) ==
            f.fileIOmode ~= "output" => error "File not in write state"
            PRINTEXP(x, f.fileState)$Lisp
            TERPRI(f.fileState)$Lisp
            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
++ 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), finiteAggregate) with
                 pack! : % -> %
                     ++ pack!(f) reorganizes the file f on disk to recover
                     ++ unused space.

    KAFcapsule == add

        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"  =>
                rMkIstream(fn::String)$Lisp
            mode = "output" =>
                rMkOstream(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")
                reopen!(open(fname, "output"), "input")
            [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
            empty?(ks) => error ["Attempt to read empty file", f]
            ix := random(#ks)$Integer
            k : String := PNAME(ks.ix)$Lisp
            [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")
            UNWIND_-PROTECT(write!(f, [k, e]), close! f)$Lisp
            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
            f

)abbrev domain LIB Library
++ Author: Stephen M. Watt
++ Date Created: 1985
++ 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() : TableAggregate(String, Any) with
         library :  FileName -> %
             ++ library(ln) creates a new library file.
         pack! : % -> %
             ++ pack!(f) reorganizes the file f on disk to recover
             ++ unused space.

         elt : (%, Symbol) -> Any
             ++ elt(lib, k) or lib.k  extracts the value corresponding to the key \spad{k}
             ++ from the library \spad{lib}.

         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}.

         close! : % -> %
          ++ close!(f) returns the library f closed to input and output.


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

--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.
