)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 := VMREAD(f.fileState)$Lisp
            PLACEP(x)$Lisp =>
                error "End of file"
            x pretend S
        readIfCan! f ==
            f.fileIOmode ~= "input" =>
                error "File not in read state"
            x : None := VMREAD(f.fileState)$Lisp
            PLACEP(x)$Lisp => "failed"
            x pretend S
        write!(f, x) ==
            f.fileIOmode ~= "output" =>
                error "File not in write state"
            print_full2(x, f.fileState)$Lisp
            TERPRI(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
            PLACEP(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
            PLACEP(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 BINFILE BinaryFile
++ Author: Barry M. Trager
++ Date Created: 1993
++ Basic Operations: writeByte! readByte! readByteIfCan!
++ Related Constructors:
++ Also See:
++ AMS Classifications:
++ Keywords:
++ References:
++ Description:
++   This domain provides an implementation of binary files. Data is
++   accessed one byte at a time as a small integer.

BinaryFile : Cat == Def where

    Cat == FileCategory(FileName, SingleInteger) with
        readIfCan! : % -> Union(SingleInteger, "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.

--      "#": % -> SingleInteger
--          ++ #(f) returns the length of the file f in bytes.

        position : % -> SingleInteger
            ++ position(f) returns the current byte-position in the file f.

        position! : (%, SingleInteger) -> SingleInteger
            ++ position!(f, i) sets the current byte-position to i.

    Def == File(SingleInteger) add
        FileState ==> SExpression

        Rep := Record(fileName :   FileName,    _
                      fileState :  FileState,   _
                      fileIOmode : String)

--      direc : Symbol := INTERN("DIRECTION","KEYWORD")$Lisp
--      input : Symbol := INTERN("INPUT","KEYWORD")$Lisp
--      output : Symbol := INTERN("OUTPUT","KEYWORD")$Lisp
--      eltype : Symbol := INTERN("ELEMENT-TYPE","KEYWORD")$Lisp
--      bytesize : SExpression := LIST(QUOTE(UNSIGNED$Lisp)$Lisp, 8)$Lisp


        defstream(fn : FileName, mode : String) : FileState ==
            mode = "input"  =>
              not readable? fn => error ["File is not readable", fn]
              BINARY_OPEN_INPUT(fn::String)$Lisp
--            OPEN(fn::String, direc, input, eltype, bytesize)$Lisp
            mode = "output" =>
              not writable? fn => error ["File is not writable", fn]
              BINARY_OPEN_OUTPUT(fn::String)$Lisp
--            OPEN(fn::String, direc, output, eltype, bytesize)$Lisp
            error ["IO mode must be input or output", mode]

        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 ==
            f.fileIOmode = "output" =>
                 BINARY_CLOSE_OUTPUT()$Lisp
                 f
            f.fileIOmode = "input" =>
                  BINARY_CLOSE_INPUT()$Lisp
                  f
            error "file must be in read or write state"

        read! f ==
            f.fileIOmode ~= "input"  => error "File not in read state"
            BINARY_SELECT_INPUT(f.fileState)$Lisp
            BINARY_READBYTE()$Lisp
--          READ_-BYTE(f.fileState)$Lisp
        readIfCan! f ==
            f.fileIOmode ~= "input"  => error "File not in read state"
            BINARY_SELECT_INPUT(f.fileState)$Lisp
            n : SingleInteger := BINARY_READBYTE()$Lisp
            n = -1 => "failed"
            n::Union(SingleInteger,"failed")
--          READ_-BYTE(f.fileState, NIL$Lisp,
--                   "failed"::Union(SingleInteger,"failed"))$Lisp
        write!(f, x) ==
            f.fileIOmode ~= "output" => error "File not in write state"
            x < 0 or x>255 => error "integer cannot be represented as a byte"
            BINARY_PRINBYTE(x)$Lisp
--          WRITE_-BYTE(x, f.fileState)$Lisp
            x

--      # f == FILE_-LENGTH(f.fileState)$Lisp
        position f ==
           f.fileIOmode ~= "input"  => error "file must be in read state"
           FILE_-POSITION(f.fileState)$Lisp
        position!(f, i) ==
           f.fileIOmode ~= "input"  => error "file must be in read state"
           (FILE_-POSITION(f.fileState, i)$Lisp ; i)

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