# file.h | documentation

    #charset "us-ascii"
    #pragma once

    /*
     *   Copyright (c) 2001, 2006 Michael J. Roberts
     *
     *   This file is part of TADS 3.
     *
     *   This header defines the File intrinsic class.
     */

    /* include our base class definition */
    #include "systype.h"

    /*
     *   File methods use the CharacterSet and ByteArray intrinsic
     *   classes, so include their headers to make sure they're available to
     *   File users.
     */
    #include "charset.h"
    #include "bytearr.h"

    /* if we're using File objects, we probably want FileName objects as well */
    #include "filename.h"

    /* ------------------------------------------------------------------------ */
    /*
     *   File access modes.  These are used when calling the file open methods
     *   to specify how the file is to be accessed.
     */

    /*
     *   Read mode - the file is opened for reading (writing is not allowed).
     *   When opened in this mode, the file must exist, or a
     *   FileNotFoundException is thrown from the open method.
     */
    #define FileAccessRead            0x0001

    /*
     *   Write mode - the file is opened for writing (reading is not allowed).
     *   When opened in this mode, if the file doesn't already exist, a new file
     *   is created; if the file does already exist, the existing data in the
     *   file are discarded (i.e., the file is truncated to zero length) on
     *   open.
     */
    #define FileAccessWrite           0x0002

    /*
     *   Read/write mode, keeping existing contents - the file is opened for
     *   both reading and writing.  If the file does not exist, a new file is
     *   created.  If the file does already exist, the existing contents of the
     *   file are kept intact on open.
     */
    #define FileAccessReadWriteKeep   0x0003

    /*
     *   Read/write mode, truncating existing contents - the file is opened for
     *   both reading and writing.  If the file does not exist, a new file is
     *   created.  If the file does already exist, the existing contents of the
     *   file are discarded (i.e., the file is truncated to zero length) on
     *   open.
     */
    #define FileAccessReadWriteTrunc  0x0004

    /* ------------------------------------------------------------------------ */
    /*
     *   File mode constants.  These are returned from getFileMode() to indicate
     *   the mode used to open the file.
     */

    /* text mode */
    #define FileModeText  1

    /* "data" mode */
    #define FileModeData  2

    /* "raw" mode */
    #define FileModeRaw   3

    /* ------------------------------------------------------------------------ */
    /*
     *   Special file identifiers.  These identifiers can be passed to the 'open'
     *   routines in place of the filename string argument.
     *
     *   The actual name and location of a special file is determined by the
     *   interpreter.  Since games use these internal identifiers rather than the
     *   actual system filenames when accessing special files, different
     *   interpreters can adapt to different local conventions without bothering
     *   the game code with the details.  The game code simply refers to the file
     *   it wants using the virtual identifier, and the interpreter takes care of
     *   the rest.
     *
     *   Note that special files generally bypass the interpreter "file safety"
     *   settings.  This is important because it allows the library and games a
     *   degree of controlled access to the file system, even when the file
     *   safety settings wouldn't normally allow similar access for arbitrary
     *   file operations.  Even though this special file access can bypass the
     *   file safety level, it doesn't compromise security, because the
     *   interpreter has exclusive control over the names and locations of the
     *   special files - thus a game can only access the particular files that
     *   the interpreter designates as special, and can't use special files to
     *   access arbitrary file system entities.
     */

    /*
     *   The library defaults file.  This is the special file where the library
     *   stores user-controlled start-up default settings.
     */
    #define LibraryDefaultsFile    0x0001

    /*
     *   Web UI preference settings file.  This is the special file where we
     *   store display style settings for the Web UI.
     */
    #define WebUIPrefsFile         0x0002

    /* ------------------------------------------------------------------------ */
    /*
     *   The File intrinsic class provides access to files in the external file
     *   system.  This lets you create, read, and write files.  The class
     *   supports text files (with translations to and from local character
     *   sets), "data" files (using the special TADS 2 binary file format), and
     *   "raw" files (this mode lets you manipulate files in arbitrary text or
     *   binary formats by giving you direct access to the raw bytes in the
     *   file).
     */
    intrinsic class File 'file/030003': Object
    {
        /*
         *   File has no constructors.  Instead of using 'new', you create a File
         *   object by opening a file through one of the openXxxFile methods:
         *
         *   f = File.openTextFile()
         *
         *   All of the open methods have a 'filename' argument giving the name
         *   of the file to open.  This is usually a string giving a file name in
         *   the local file system.  You can also use a TemporaryFile object in
         *   place of a filename, to open a temporary file.
         *
         *   All of the open methods throw exceptions if the open fails:
         *
         *   FileNotFoundException - indicates that the requested file doesn't
         *   exist.  This is thrown when the access mode requires an existing
         *   file but the named file does not exist.
         *
         *   FileCreationException - indicates that the requested file couldn't
         *   be created.  This is thrown when the access mode requires creating a
         *   new file but the named file cannot be created.
         *
         *   FileOpenException - indicates that the requested file couldn't be
         *   opened.  This is thrown when the access mode allows either an
         *   existing file to be opened or a new file to be created, but neither
         *   could be accomplished.
         *
         *   FileSafetyException - the requested access mode isn't allowed for
         *   the given file due to the current file safety level set by the user.
         *   Users can set the file safety level (through command-line switches
         *   or other preference mechanisms which vary by interpreter) to
         *   restrict the types of file operations that applications are allowed
         *   to perform, in order to protect their systems from malicious
         *   programs.  This exception indicates that the user has set a safety
         *   level that is too restrictive for the requested operation.
         */

        /*
         *   Static creator method: open a text file.  Returns a File object that
         *   can be used to read or write the file.
         *
         *   'filename' is the name of the file to open.  This is a string giving
         *   the name of a file in the local file system.  It can alternatively
         *   be a TemporaryFile object, to open a temporary file.
         *
         *   'access' is the read/write mode, and must be one of FileAccessRead
         *   or FileAccessWrite.
         *
         *   'charset' is a CharacterSet object, or can optionally be a string
         *   naming a character set, in which case a CharacterSet object for the
         *   named character set will automatically be created.  If 'charset' is
         *   omitted, the local system's default character set for file contents
         *   is used.
         *
         *   When a file is opened in text mode for reading, each call to
         *   readFile() reads and returns a line of text from the file.  When a
         *   file is opened in text mode for writing, any existing file is
         *   discarded and replaced with the new data.  Each read and write to a
         *   text file is mapped through the CharacterSet in effect at the time
         *   of the read or write.
         */
        static openTextFile(filename, access, charset?);

        /*
         *   Static creator method: open a file in 'data' mode.  Returns a File
         *   object that can be used to read or write the file.
         *
         *   'filename' is a string giving the file name in the local file
         *   system, or a TemporaryFile object.
         *
         *   'access' indicates the desired read/write access and the disposition
         *   of any existing file; any of the FileAccessXxx modes can be used.
         *
         *   When a file is opened in data mode, you can read and write integers,
         *   strings, and 'true' values to the file, and the values in the file
         *   are marked with their datatype in a private data format.  Because
         *   the file uses a tads-specific format, this mode cannot be used to
         *   read files created by other applications or write files for use by
         *   other applications; however, this storage format is convenient for
         *   storing simple data values because the File object takes care of
         *   converting to and from a portable binary format.
         */
        static openDataFile(filename, access);

        /*
         *   Static creator method: open a file in 'raw' mode.  Returns a File
         *   object that can be used to read or write the file.
         *
         *   'filename' is a string giving the name of the file in the local file
         *   system, or a TemporaryFile object.
         *
         *   'access' indicates the desired read/write access mode and the
         *   disposition of any existing file; any of the FileAccessXxx modes can
         *   be used.
         *
         *   When a file is opened in raw mode, only ByteArray values can be read
         *   and written.  The File object performs no translations of the bytes
         *   read or written.  This mode requires the calling program itself to
         *   perform all data conversions to and from a raw byte format, but the
         *   benefit of this extra work is that this mode can be used to read and
         *   write files in arbitrary data formats, including formats defined by
         *   other applications.
         */
        static openRawFile(filename, access);

        /*
         *   get the CharacterSet object the File is currently using; returns
         *   nil for a non-text file
         */
        getCharacterSet();

        /*
         *   Set the CharacterSet object the File is to use from now on.  This
         *   isn't meaningful except for text files.  'charset' can be a
         *   CharacterSet object, a string giving the name of a character mapping
         *   (in which case a CharacterSet object is automatically created based
         *   on the name), or nil (in which case the local system's default
         *   character set for text files is used).
         */
        setCharacterSet(charset);

        /*
         *   Close the file.  Flushes any buffered information to the underlying
         *   system file and releases any system resources (such as share locks
         *   or system buffers) associated with the file.  After this routine is
         *   called, no further operations on the file can be performed (a
         *   FileClosedException will be thrown if any subsequent operations are
         *   attempted).
         *
         *   If the game is running in web server mode, the file might be on a
         *   remote storage server.  In this case, if the file was opened with
         *   write access, closing it will send the file to the storage server.
         *
         *   Note that this method can throw an error, so you shouldn't consider
         *   updates to the file to be "safe" until this method returns
         *   successfully.  On many systems, writes are buffered in memory, so
         *   closing the file can involve flushing buffers, which can trigger the
         *   same sorts of errors that can happen with ordinary writes (running
         *   out of disk space, physical media defects, etc).  In addition, when
         *   the file is on a remote network storage server, closing a file
         *   opened with write access transmits the file to the storage server,
         *   which can encounter network errors.
         *
         *   You should always explicitly close files when done with them.  This
         *   is especially important when writing to a file, because many systems
         *   buffer written data in memory and don't write changes to the
         *   physical media until the file is closed.  This means that updates
         *   can be lost if the program crashes (or the computer loses power,
         *   etc) while the file is still open.  Closing the file as soon as
         *   you're done with it reduces the chances of this kind of data loss.
         *   It also helps overall system performance to release resources back
         *   to the operating system as soon as you're done with them.
         *
         *   If you *don't* close a file, though, the system will close it
         *   automatically when the File object becomes unreachable and is
         *   deleted by the garbage collector.  It's considered bad form to
         *   depend on this for the reasons above, and it's also problematic
         *   because you won't have any way of finding out if an error should
         *   happen on close.
         */
        closeFile();

        /*
         *   Read from the file.  Returns a data value that depends on the file
         *   mode, as described below, or nil at end of file.
         *
         *   If the file is open in text mode, this reads a line of text from the
         *   file and returns a string with the text of the line read.  A line of
         *   text is a sequence of characters terminated with a line-ending
         *   sequence, which is a carriage return, line feed, CR/LF pair, LF/CR
         *   pair, or a Unicode line terminator character (0x2028) if the file is
         *   being read with one of the Unicode encodings.  If the line read ends
         *   in a line-ending sequence, the returned text will end in a '\n'
         *   character, regardless of which of the possible line-ending sequences
         *   is actually in the file, so the caller need not worry about the
         *   details of the external file's format.  Every line read from the
         *   file will end in a '\n' except possibly the last line - if the file
         *   doesn't end with a line-ending sequence, then the last line read
         *   from the file won't end in a '\n' character.  All bytes read from
         *   the file will be mapped to characters through the CharacterSet
         *   object currently in effect in the file, so the returned string will
         *   always be a standard Unicode string, regardless of the byte encoding
         *   of the file.
         *
         *   If the file is open in 'data' mode, this reads one data element
         *   using the private tads-specific data format.  The result is a value
         *   of one of the types writable with writeFile() in 'data' mode.  In
         *   order to read a 'data' file, the file must have been previously
         *   written in 'data' mode.
         */
        readFile();

        /*
         *   Write to the file.  Writes the given value to the file in a format
         *   that depends on the file mode, as described below.  No return
         *   value; if an error occurs writing the data, this throws a
         *   FileIOException.
         *
         *   If the file is open in text mode, this writes text to the file,
         *   converting the given value to a string if necessary (and throwing
         *   an error if such a conversion isn't possible), and translating the
         *   string to be written to bytes by mapping the string through the
         *   CharacterSet object currently in effect for the file.  Note that no
         *   line-ending characters are automatically added to the output, so if
         *   the caller wishes to write line terminators, it should simply
         *   include a '\n' character at the end of each line.
         *
         *   If the file is open in 'data' mode, this writes the value, which
         *   must be a string, integer, enum, or 'true' value, in a private
         *   tads-specific data format that can later be read using the same
         *   format.  The values are converted to the private binary format,
         *   which is portable across platforms: a file written in 'data' mode
         *   on one machine can be copied (byte-for-byte) to another machine,
         *   even one that uses different hardware and a different operating
         *   system, and read back in 'data' mode on the new machine to yield
         *   the original values written.
         */
        writeFile(val);

        /*
         *   Read bytes from the file into the given ByteArray object.  This can
         *   only be used for a file opened in 'raw' mode.  If 'start' and 'cnt'
         *   are given, they give the starting index in the byte array at which
         *   the bytes read are to be stored, and the number of bytes to read,
         *   respectively; if these are omitted, one byte is read from the file
         *   for each byte in the byte array.
         *
         *   Returns the number of bytes actually read into the byte array,
         *   which will be less than or equal to the number requested.  If the
         *   number read is less than the number requested, it means that the
         *   end of the file was encountered, and only the returned number of
         *   bytes were available.
         */
        readBytes(byteArr, start?, cnt?);

        /*
         *   Write bytes from the given source object into the file.  This can
         *   only be used for a file opened in 'raw' mode.
         *
         *   The source object must be one of the following object types:
         *
         *   File: the contents of the given source file are copied to 'self'.
         *   'start' is the starting seek position in the source file; if
         *   omitted, the current seek position is the default.  'cnt' is the
         *   number of bytes to copy; if omitted, the file is copied from the
         *   given starting position to the end of the file.
         *
         *   ByteArray: the bytes of the byte array are copied to the file.
         *   'start' is the starting index in the byte array; if omitted, the
         *   default is the first byte (index 1).  'cnt' is the number of bytes
         *   to copy; if omitted, bytes are copied from the start position to the
         *   end of the array.
         *
         *   No return value; if an error occurs writing the data, a
         *   FileIOException is thrown.
         */
        writeBytes(source, start?, cnt?);

        /*
         *   Get the current read/write position in the file.  Returns the byte
         *   offset in the file of the next byte to be read or written.  Note
         *   that this value is an offset, so 0 is the offset of the first byte
         *   in the file.
         */
        getPos();

        /*
         *   Set the current read/write position in the file.  'pos' is a byte
         *   offset in the file; 0 is the offset of the first byte.
         *
         *   For files in 'text' and 'data' modes, a caller should NEVER set the
         *   file position to any value other than a value previously returned
         *   by getPos(), because other positions might violate the format
         *   constraints.  For example, if you move the file position to a byte
         *   in the middle of a line-ending sequence in a text file, subsequent
         *   reading from the file might misinterpret the sequence as something
         *   other than a line ending, or as an extra line ending.  If you move
         *   the position in a 'data' file to a byte in the middle of an integer
         *   value, reading from the file would misinterpret as a data type tag
         *   a byte that is part of the integer value instead.  So it is never
         *   meaningful or safe to set an arbitrary byte offset in these file
         *   formats; only values known to be valid by virtue of having been
         *   returned from getPos() can be used here in these modes.
         */
        setPos(pos);

        /*
         *   Set the current read/write position to the end of the file.  This
         *   can be used, for example, to open a 'data' mode file for
         *   read/write/keep access (keeping the contents of an existing file)
         *   and then adding more data after all of the existing data in the
         *   file.
         */
        setPosEnd();

        /*
         *   Static creator method: open a resource in 'text' mode.  This acts
         *   like openTextFile(), but rather than opening an ordinary file, this
         *   method opens a resource.  Resources differ from ordinary files in
         *   two important respects.  First, a resource is named with a
         *   URL-style path rather than a local file system name.  Second, a
         *   resource can be embedded in the program's executable (.t3) file, or
         *   can be embedded in an external resource bundle (.3r0, etc) file.
         *
         *   Resources are read-only, so the access mode is implicitly
         *   FileAccessRead.
         */
        static openTextResource(resname, charset?);

        /*
         *   Static creator method: open a resource in 'raw' mode.  This acts
         *   like openRawFile(), but opens a resource rather than an ordinary
         *   file.
         *
         *   Resources are read-only, so the access mode is implicitly
         *   FileAccessRead.
         */
        static openRawResource(resname);

        /* get the size in bytes of the file */
        getFileSize();

        /*
         *   Get the file mode.  This returns one of the FileModeXxx constants,
         *   indicating the mode used to open the file (text, data, raw).
         */
        getFileMode();

        /*
         *   Extract the file's "root name" from the given filename string.  This
         *   returns a new string giving the portion of the filename excluding
         *   any directory path.  This parses the filename according to the local
         *   file system's syntax rules.  For example, given the filename
         *   'a/b/c.txt', if you're running on a Unix or Linux machine, the
         *   function returns 'c.txt'.
         *
         *   Note that this function doesn't attempt to open the file or check
         *   for its existence or validity; it simply parses the name according
         *   to the local syntax conventions.
         *
         *   (It's recommended that you use the newer FileName.getBaseName() in
         *   place of this function.)
         */
        static getRootName(filename);

        /*
         *   Delete the file with the given name.  This erases the file from
         *   disk.  'filename' is a string giving the name of the file to delete,
         *   or one of the special file identifier values.
         *
         *   The file can only be deleted if the file safety level would allow
         *   you to write to the file; if not, a file safety exception is thrown.
         *
         *   (It's recommended that you use the newer FileName.deleteFile() in
         *   place of this function.)
         */
        static deleteFile(filename);

        /*
         *   Change the file mode.  'mode' is a FileModeXxx value giving the
         *   desired new file mode.
         *
         *   If the mode is FileModeText, 'charset' is the character set mapping
         *   to use for the file; this can be given as a CharacterSet object, or
         *   as a string giving the name of a character set.  If the value is nil
         *   or the argument is omitted, the local system's default character for
         *   file contents is used.  The 'charset' parameter is ignored for other
         *   modes.
         */
        setFileMode(mode, charset?);

        /*
         *   Pack the given data values into bytes according to a format
         *   definition string, and write the packed bytes to the file.  This
         *   function is designed to simplify writing files that use structured
         *   binary formats defined by third parties, such as JPEG or PDF.  The
         *   function translates native TADS data values into selected binary
         *   formats, and writes the resulting bytes to the file, all in a single
         *   operation.
         *
         *   'format' is the format string, and the remaining arguments are the
         *   values to be packed.
         *
         *   Returns the number of bytes written to the file.  (More precisely,
         *   returns the final file position as a byte offset from the starting
         *   file pointer.  If a positioning code like @ or X is used in the
         *   string, it's possible that more bytes were actually written.)
         *
         *   See Byte Packing in the System Manual for details.
         */
        packBytes(format, ...);

        /*
         *   Read bytes and unpack into a data structure, according to the format
         *   description string 'desc'.
         *
         *   'format' is the format string.  The function reads bytes from the
         *   current location in the file and translates them into data values
         *   according to the format string, returning a list of the unpacked
         *   values.
         *
         *   Refer to Byte Packing in the System Manual for details.
         */
        unpackBytes(format);

        /*
         *   Calculate the 256-bit SHA-2 hash of bytes read from the file,
         *   starting at the current seek location and continuing for the given
         *   number of bytes.  If the length is omitted, the whole rest of the
         *   file is hashed.  This has the side effect of reading the given
         *   number of bytes from the file, so it leaves the seek position set to
         *   the next byte after the bytes hashed.
         *
         *   Returns a string of 64 hex digits giving the hash result.
         *
         *   This can only be used on files opened in raw mode with read access.
         */
        sha256(length?);

        /*
         *   Calculate the MD5 digest of bytes read from the file, starting at
         *   the current seek location and continuing for the given number of
         *   bytes.  If the length is omitted, the whole rest of the file is
         *   digested.  This has the side effect of reading the given number of
         *   bytes from the file, so it leaves the seek position set to the next
         *   byte after the bytes digested.
         *
         *   Returns a string of 32 hex digits giving the digest result.
         *
         *   This can only be used on files opened in raw mode with read access.
         */
        digestMD5(length?);
    }

    /* ------------------------------------------------------------------------ */
    /*
     *   The TemporaryFile intrinsic class represents a temporary file name in
     *   the local file system.  Temporary files can be used to store data too
     *   large to conveniently store in memory.
     *
     *   You create a temporary file with 'new TemporaryFile()'.  This
     *   automatically assigns the object a unique filename in the local file
     *   system, typically in a system directory reserved for temporary files.
     *   The local file can then be opened, read, written, and otherwise
     *   manipulated via the File class, just like any other file.  Simply pass
     *   the TemporaryFile object in place of a filename to the File.openXxx
     *   methods.
     *
     *   The underlying file system file will be deleted automatically when the
     *   TemporaryFile object is collected by the garbage collector (or when the
     *   program terminates).  This means that you don't have to worry about
     *   cleaning up the file system space used by the file; it'll be released
     *   automatically when the file is no longer needed.  However, you can call
     *   the deleteFile() method to explicitly release the file when you're done
     *   with it, if you want to ensure that the resource is returned to the
     *   operating system as soon as possible.
     *
     *   TemporaryFile objects are inherently transient - they're only valid for
     *   the current session on the current local system, so they can't be saved
     *   or restored.
     *
     *   Temporary files are exempt from the file safety level settings, because
     *   the inherent restrictions on temporary files provide the same system
     *   protections that the safety level settings provide for ordinary files.
     */
    intrinsic class TemporaryFile 'tempfile/030000': Object
    {
        /*
         *   Get the name of the underlying file system object.  This returns a
         *   string with the local filename.  This is mostly for debugging
         *   purposes or for displaying to the user.  You can't necessarily use
         *   this filename in a call to File.openXxxFile, because the file path
         *   is usually in a system directory reserved for temporary files, and
         *   the file safety level settings often prohibit opening files outside
         *   of the program's own home directory.  To open the temp file, you
         *   should always pass the TemporaryFile object itself in place of the
         *   filename.
         */
        getFilename();

        /*
         *   Delete the underlying file system object.  This deletes the
         *   temporary file and marks the TemporaryFile object as invalid.  After
         *   calling this, you can no longer open the file via the
         *   File.openXxxFile methods.
         *
         *   This method allows you to release the underlying file system
         *   resources as soon as you're done with the temp file.  It's never
         *   necessary to do this.  TADS automatically deletes the underlying
         *   file system resources when the TemporaryFile object is deleted by
         *   the garbage collector (or when the program terminates), so the
         *   operating system file will be deleted eventually whether you call
         *   this method or not.  The point of this method is to let you tell the
         *   system *exactly* when you're done with the file, so that the
         *   resources can be released earlier than if we waited for garbage
         *   collection to take care of it.  This should make little difference
         *   in most situations, but in a program that will run for a long time
         *   and use a lot of temporary files, it might be worthwhile to release
         *   resources manually as soon as possible.
         */
        deleteFile();
    }

    /* ------------------------------------------------------------------------ */
    /*
     *   The filename passed to the File "open" methods, as well as to most
     *   system functions that accept filename arguments, can be a TadsObject
     *   object in lieu of a string.  Such an object must implement the following
     *   methods:
     *
     *   getFilename() - return the actual filename to use, which must be a
     *   string or TemporaryFile object.
     *
     *   closeFile() - optional.  This is called just after the underlying system
     *   file is closed, allowing the program to perform any desired
     *   post-processing on the file.
     */
    export getFilename 'FileSpec.getFilename';
    export closeFile 'FileSpec.closeFile';

Adv3Lite Library Reference Manual
Generated on 15/03/2023 from adv3Lite version 1.6.1