ETHZ_Logo RAMSES_Logo_Right   RAMSES   RAMSES_Logo_Left Systems Ecology  
Start    search button      Modules:   A-Z   Function   Layer        QuickRefs:   DM   AuxLib   AuxLibE   SciLib   EasyMW   MW   ISIS   RMSLib

DEFINITION MODULE DMFiles;

  (*******************************************************************

    Module  DMFiles     ('Dialog Machine' DM_V3.0)

      Copyright (c) 1986-2006 by Andreas Fischlin and ETH Zurich.

    Purpose   Manages text files and provides routines to read or 
              write sequentially and randomly.

    Remarks   All elementary data types can be read or written.
              Besides the set of common file system routines present
              in many Modula-2 implementations, this module exports
              procedures to access files with the standard MiniFinder
              dialog boxes of the Apple Macintoshª computer.

              This module belongs to the 'Dialog Machine'.


    Programming

      o Design
        Andreas Fischlin          15/02/1986

      o Implementation
        Andreas Fischlin          15/02/1986


    ETH Zurich
    Systems Ecology
    CHN E 35.1
    Universitaetstrasse 16
    8092 Zurich
    SWITZERLAND

    URLs:
        <mailto:RAMSES@env.ethz.ch>
        <http://www.sysecol.ethz.ch>
        <http://www.sysecol.ethz.ch/SimSoftware/RAMSES>


    Last revision of definition:  29/05/2005  AF

  *******************************************************************)


   FROM SYSTEM IMPORT BYTE;


  (*********************************)
  (*#####   File Management   #####*)
  (*********************************)

  TYPE
    Response = (done, filenotfound, volnotfound, cancelled, unknownfile,
                toomanyfiles, diskfull, memfull, alreadyopened, isbusy,
                locked, notdone);
    (* Results of all file operations are returned via the Response
    type; check the result field in the TextFile Record for success
    of operation  (For advanced programers see also
    LastResultCode). The following meaning applies:

      filenotfound   File could not be accessed, e.g. typically because
                     it does not exist physically
      volnotfound    Path information refers to a non-existing volume
                     or directory (platform dependent, since volumes are
                     supported only on the Macintosh and IBM PC platforms;
                     this result is returned on a Unix platform only for a
                     path involving a non existing directory; note, OS X is
                     in this respect considered a Macintosh and not a Unix
                     system)
       cancelled     User cancelled (only possible in an interactive
                     'Dialog Machine')
       unknownfile   Actual parameter (of type TextFile) designates a file
                     which is not known to the 'Dialog Machine'. This
                     value may also be returned if the file name contains
                     illegal characters or is otherwise malformed, the case
                     where no file with such a name could never exist (the
                     latter behavior is of course platform dependent).
       toomanyfiles  Results from an attempt to open a file exceeding the
                     limitations of the underlying operating system or the
                     'Dialog Machine' of how many files can be opened
                     simultaneously
       diskfull      Disk space has been exceeded
       memfull       No more heap space available (RAM limitation exceeded)
       alreadyopened Results from an attempt to open a file which is
                     already open, typically by another process
       isbusy        File is already opened by another process or offers
                     otherwise not even read access
       locked        File is locked or permissions are insufficient for
                     the requested access such as writing or deletion
       notdone       Some error condition encountered which is not covered
                     by any of the above.
    *)

    HiddenFileInfo;

    IOMode = (reading, writing);

    TextFile = RECORD
                 res:        Response;
                 filename:   ARRAY [0..255] OF CHAR;
                 path:       ARRAY [0..63] OF CHAR;
                 curIOMode:  IOMode; (*current input/output mode*)
                 curChar:    CHAR;  (*char at current file position, last
                                    char written or char not yet read;
                                    if EOF = TRUE value of curChar
                                    may not be defined*)
                 fhint: HiddenFileInfo;
               END;


  VAR
    neverOpenedFile: TextFile;  (* read only var! *)
      (*
        May be used for variables of type TextFile to denote that
        the associated file has actually not yet been opened (e.g.
        by a call to one of the following procedures:
        GetExistingFile, CreateNewFile, Lookup, or ReadOnlyLookup.
        It is a recommended programming practice to assign this
        value to all file variables during the initialization phase
        of the "Dialog Machine" program.
      *)






  (*
    Typically one of the following two procedures are used to open
    or create text files.  Their usage is highly recommended.
  *)

  PROCEDURE GetExistingFile(VAR f: TextFile; prompt: ARRAY OF CHAR);
  (*
    Uses the standard Macintosh dialog box (displaying the prompt
    string) to let the user select and open an existing text file.  If
    f.res = done, f is open for subsequent reading (implicit Reset) or
    writing (call Rewrite before writing).  Note, to succeed, the file
    needs not only read, but also write permissions, since routines,
    Rewrite and AlterIOMode can be used for writing with a file opened
    by GetExistingFile.
  *)

  PROCEDURE CreateNewFile(VAR f: TextFile; prompt, defaultName: ARRAY OF CHAR);
  (*
    Uses the standard Macintosh dialog box (displaying the prompt
    string) to let the user enter the name of a text file which
    is to be created . Initially the defaultName is displayed and
    may be accepted by just pushing the OK-button or a new file
    name may be entered. If a file with the same name exists
    already, the user is asked whether he wants to overwrite the
    old file content or not.  If f.res = done then f is open for
    subsequent writing (implicit Rewrite).
  *)




  (*

    o The following procedures support file operations controlled
      solely by the program without user interactions.  If any user
      dialog is involved, the exclusive usage of the two procedures
      GetExistingFile respectively CreateNewFile is  recommended.

    o Files are located via strings containing an optional
      path and a file name specification.

    o Path specifications (MacOS Classic) must follow this syntax
      given in EBNF (start symbol is path):

        path = [VolName] { ':' FolderName | ':' } ':'.
        VolName = String.
        FolderName = String.
        String = char { char }.

        (Remark: all characters (including ' ') are allowed
        except ':', however it is recommended to minimize in
        general the use of special characters in folder and
        file names.

      There are absolute and relative path specifications.  An
      absolute path specification starts with the name of the
      volume, a relative path refers to a so-called current working
      directory as the starting point and must begin with a ':'.
      (See also below the point on path definitions. They can be
      used to define the default search strategy contained in the
      so-called User.Profile).

    o Path specifications (Mac OS X, Unix) must follow this syntax
      given in EBNF (start symbol is path):

        path = ['/'] { FolderName | './' | '../' } ['/'].
        FolderName = String.
        String = char { char }.

        (Remark: all characters (including ' ') are allowed except
        '/', however it is recommended to avoid the use of special
        characters.  In particular don't use '*' or '?'  in folder or
        file names due to operating system idiosynchrasies.  Under
        Unix not only occasionally, but even often do special
        characters cause program failures.

      There are absolute and relative path specifications.  An
      absolute path specification starts with '/', a relative with
      another character.  The folder where the running application
      has been launched is called the current working directory and
      relative paths specify paths relative to that directory.  (See
      also below the point on path definitions.  They can be used to
      define the default search strategy contained in the Unix
      environment variable M2PATH).

    o File name specifications must follow the same syntax as that
      of folders as described in above syntax.

    o Files are located following the so-called default search

      strategy.  It uses path definitions which must be given
      in the text file with the name profileFName
      (User.Profile, MacOS Classic) or config.M2PATH.sh (Mac
      OS X, Unix) or be defined in the environment variable
      M2PATH (Mac OS X, Unix).  The latter file must reside in
      the same folder as the starting application (usually the
      shell or any other linked stand-alone application made
      with the Dialog Machine).  The file profileFName
      (User.Profile) is normally read only at start up time of
      the running Modula-2 application, i.e. the MacMETH,
      RAMSES shell or the produced Dialog Machine program.

    o Path definitions support the default search strategy
      used when locating files.

      Under MacOS Classic they must be given in the file profileFName
      (User.Profile) and have to follow this syntax given in EBNF (start
      symbol is PathDefinition, the syntax of the symbol path is that given
      above):

        PathDefinition = 'PATH' path { ',' path }.

      Ex.:
            PATH
              HD:M2:Work:, :DMLib:, :RAMSESLib:,
              :Work:MyProject:,
              Another Disk:M2:Work:Another Project:,
              ::Utilities:

            Remark to the semantics of path definitions: A
            path definition starting with ':' signifies a relative
            path, i.e. a path which starts at the folder in which the
            file with name profileFName (User.Profile) resides. If
            one uses absolute paths, they must never start with ':' and
            then always indicate a full path starting with the volume
            name.  In the example given below the last path using the
            folder Utilities is preceeded by the construct '::' is
            located relative to the start-up folder in which the file
            with name profileFName (User.Profile) resides at a level
            above (father/mother level).  Never forget to append ':'
            at the end of a path specification.

      Under OS X or Unix the patch specifications follow the Unix
      standards.  please observe to the help information as provided by
      that operating system.

    o Filename arguments using the identifier pathOrFileName in the
      formal paramater list may may or may not contain a path
      before the actual file name.  Regardless whether this
      parameter does contain a path specification or not the file
      is searched according to the default search strategy
      explained below.

    o Upon returning from a successful call to a locating procedure
      f.filename and f.path contain the actual values, in particular
      f.filename is always stripped from any preceeding path and
      f.path contains a path with a path separator at the end,
      unless the path is empty.

    o Path definitions can be changed dynamically during program
      execution but must then be reentered into the system by
      calling routine DMSubLaunch.SetNewPaths.
  *)



  PROCEDURE Lookup(VAR f: TextFile; pathOrFileName: ARRAY OF CHAR; new: BOOLEAN);
  (*
    If new = TRUE an eventually already existing file with the same
    name is overwritten.  If new = FALSE, f's initial IOMode is
    reading, else writing.  Note, to succeed in case of new = FALSE,
    the file needs not only read, but also write permissions, since
    routines, Rewrite and AlterIOMode can be used for writing to f.
    To require only read access, use routine ReadOnlyLookup.
  *)

  PROCEDURE ReadOnlyLookup(VAR f: TextFile; pathOrFileName: ARRAY OF CHAR);
  (*
    Same as Lookup but only reading and no writing to the file
    allowed.  Note, many operations such as Delete, Rename, Rewrite,
    AppendAtEOF, and AlterIOMode to a writing mode do all fail on a
    file opened by this routine.  In contrast to all other file
    opening routines, to succeed this routine does only require read
    permissions.
  *)



  PROCEDURE IsOpen (VAR f: TextFile): BOOLEAN;
  PROCEDURE FileExists(VAR f: TextFile): BOOLEAN;
  (*
    Precondition for procedure FileExists: f may or may not be
    open. f.filename is used as a pathOrFileName input parameter in
    the way described above.  If the file could be located TRUE is
    returned else FALSE and the current status of the file (opened,
    closed) is always left untouched.
  *)

  PROCEDURE FileLevel(VAR f: TextFile): CARDINAL;
  (*
    Returns the level of the sub-program on which the file f has
    been created. If f does not exist DMSystem.startUpLevel-1, i.e.
    0 is returned.
  *)



  (*
    Precondition for all following procedures: f currently open.
  *)
  PROCEDURE Close(VAR f: TextFile);
  PROCEDURE Delete(VAR f: TextFile); (* implicitely closes *)
  PROCEDURE Rename(VAR f: TextFile; newfilename: ARRAY OF CHAR);
  (* newfilename must not contain a path *)
  PROCEDURE FileSize(VAR f: TextFile): LONGINT;



  (*
    text files may only be used sequentially for either reading or
    writing.  Call Reset or Rewrite to change I/O mode.
  *)

  PROCEDURE Reset(VAR f: TextFile);
  (*
    Prepares file f for reading and sets the file position to the
    beginning. Any attempt to read past EOF will result in a
    program halt.
  *)

  PROCEDURE Rewrite(VAR f: TextFile);
  (*
    Prepares file f for writing and sets the file position to the
    beginning (any previously stored file content is lost). EOF
    becomes TRUE.
  *)

  PROCEDURE AppendAtEOF(VAR f: TextFile);
  (*
    Prepares file f for writing and sets the file position at the
    end of the data it currently contains (eventually existing
    file content is preserved otherwise equivalent to a Rewrite).
    EOF becomes TRUE.
  *)




  (*****************************)
  (*#####   IO routines   #####*)
  (*****************************)

  PROCEDURE EOF(VAR f: TextFile): BOOLEAN;
  (*
    Returns whether or not the end of the file has been reached.  This
    routine is essential, since attempts to read past EOF, i.e. while
    EOF returns TRUE, result in a program halt.  While writing,
    typically at the end of a file, EOF is always TRUE. For an empty
    file in the open state, EOF returns TRUE. IMPLEMENTATION
    RESTRICTION: For efficiency reasons this routine assumes as actual
    parameter a valid file in the open state.  Otherwise it will fail,
    typically abort the program (if halt for dereference NIL pointer
    enabled) or return unpredictable results.
  *)

  PROCEDURE Again(VAR f: TextFile);
  (*
    Last read char may be reread again.  NOTE: Again treats the end of
    line (EOL symbol) always like a symbol consisting of a single
    character.  This means that on a machine implementing the end of line
    with a single character (Macintosh, Unix), the result is simple and
    reverses always the reading of the last sequential reading operation.
    In particular this means that the result is the same, regardless
    wether sequential reading was done by ReadByte, ReadChar, or
    ReadChars.  However, on machines like the IBM PC, where 2 characters
    are used to mark an end of line (EOL), the result corresponds only to
    reverting the effect of ReadChar or ReadChars.  You can't revert the
    effect of a single call to ReadByte if ReadByte just read the 2nd
    character of the end of line mark.  Then Again reverts the effect
    of 2 consecutive calls to ReadByte.
  *)


  (*
    the following two routines make input/output without performing
    any interpretations:
  *)

  PROCEDURE ReadByte(VAR f: TextFile; VAR b: BYTE);
  PROCEDURE WriteByte(VAR f: TextFile; b: BYTE);


  CONST EOL = 36C; (* = ASCII RS. This is the char returned by
                   ReadChar if an end of line has been
                   encountered.  Internally the Macintosh uses
                   15C = ASCII CR, Unix uses 12C = ASCII LF, and
                   IBM PC (DOS, Windows) uses two bytes, i.e.
                   15C followed by 10C (ASCII CR LF).  *)

  (*
    the following input/output routines interprete certain characters,
    such as e.g. EOL:
  *)

  PROCEDURE ReadChar(VAR f: TextFile; VAR ch: CHAR);
  (*if an end of line mark is encountered ch=EOL is returned*)

  PROCEDURE SkipGap(VAR f: TextFile);
  (*
    Skip all chars<=" ", i.e. a gap.  Result is that the
    sequential reading stops right before the next char>" "
    (ready for next sequential reading operation) or before an
    EOF. NOTE: A gap consists of any number of white space
    (blanks and horizontal tabulators), plus all non-printing
    characters including any end of line symbols!  If you wish
    to stop the reading process at the EOL, use routine
    SkipGapWithinLn.  Note also, this routine can be called
    safely at EOF.
  *)

  PROCEDURE SkipGapWithinLn(VAR f: TextFile);
  PROCEDURE AtEOL(VAR f: TextFile): BOOLEAN;
  PROCEDURE SkippedUpToToken (VAR f: TextFile): BOOLEAN;
  (*
    SkipGapWithinLn functions similar to SkipGap, but does not
    read beyond EOL. Result is that the sequential reading stops
    right before the next char>" " (ready for next sequential
    reading operation), or before an EOF, or after an EOL
    (AtEOL returns TRUE).  SkipGapWithinLn is useful when reading
    tabulated items arranged in matrix form.  Typical code to
    read a file line per line but otherwise format free:

      WHILE NOT EOF(f) DO
        (* read a line *)
        SkipGapWithinLn(f);
        WHILE NOT (AtEOL(f) AND NOT EOF(f) DO
          ReadChars(f,item); (* deal with item; *) SkipGapWithinLn(f);
        END(*WHILE*);
        (* deal with line *)
      END(*WHILE*);

    Since SkipGapWithinLn stops reading after having encountered an EOL it
    may be useful to know whether another SkipGapWithinLn, e.g. at the
    begin of the next line, is needed before reading of an actual item is
    to be started.  Use SkippedUpToToken to learn about this.  It returns
    TRUE only if the reading has stopped right before the next non-white
    space token (chars>" ").
  *)

  PROCEDURE ReadChars(VAR f: TextFile; VAR string: ARRAY OF CHAR);
  (*
    Read from the current file position till the last char
    before a char<=" " would be encountered (Note: If string is
    too small, reading stops, although next character, which has
    not yet been read, may be actually > " ").
  *)

  PROCEDURE WriteChar(VAR f: TextFile; ch: CHAR);
  PROCEDURE WriteEOL(VAR f: TextFile); (* = Write(EOL) *)
  PROCEDURE WriteChars(VAR f: TextFile; string: ARRAY OF CHAR);
  PROCEDURE WriteVarChars(VAR f: TextFile; VAR string: ARRAY OF CHAR);



  VAR
    legalNum: BOOLEAN;  (*indicates whether read string conforms
                        to legal Modula-2 number syntax*)

  PROCEDURE GetCardinal(VAR f: TextFile; VAR c: CARDINAL);
  PROCEDURE GetLongCard(VAR f: TextFile; VAR c: LONGCARD);
  (*
    Equivalent to statement sequence SkipGap, ReadChars, plus
    interpretation of the read string as a cardinal. legalNum :=
    c = cardinal
  *)

  PROCEDURE PutCardinal(VAR f: TextFile; c: CARDINAL; n: CARDINAL);
  PROCEDURE PutLongCard(VAR f: TextFile; c: LONGCARD; n: CARDINAL);
  (*
    Write cardinal c with at least n chars onto file f, insert
    leading blanks if necessary
  *)

  PROCEDURE GetInteger(VAR f: TextFile; VAR i: INTEGER);
  PROCEDURE GetLongInt(VAR f: TextFile; VAR i: LONGINT);
  (*
    Equivalent to statement sequence SkipGap, ReadChars, plus
    interpretation of the read string as an integer. legalNum :=
    c = integer
  *)

  PROCEDURE PutInteger(VAR f: TextFile; i: INTEGER; n: CARDINAL);
  PROCEDURE PutLongInt(VAR f: TextFile; i: LONGINT; n: CARDINAL);
  (*
    Write integer i with at least n chars onto file f, insert
    leading blanks if necessary
  *)

  PROCEDURE GetReal(VAR f: TextFile; VAR x: REAL);
  PROCEDURE GetLongReal(VAR f: TextFile; VAR x: LONGREAL);
  (*
    Equivalent to statement sequence SkipGap, ReadChars, plus
    interpretation of the read string as a real. legalNum :=  c =
    real
  *)

  PROCEDURE PutReal(VAR f: TextFile; x: REAL; n, dec: CARDINAL);
  PROCEDURE PutLongReal(VAR f: TextFile; x: LONGREAL; n,dec: CARDINAL);
  (*
    Write real x in fixed notation with at least n chars onto
    file f, insert leading blanks if necessary, use dec digits
    after the decimal point
  *)

  PROCEDURE PutRealSci(VAR f: TextFile; x: REAL; n: CARDINAL);
  PROCEDURE PutLongRealSci(VAR f: TextFile; x: LONGREAL; n,dec: CARDINAL);
  (*
    Write real x in scientific notation with at least n chars
    onto file f, insert leading blanks if necessary
  *)



  (*****************************************************************)
  (*#####   Advanced IO routines (supporting random access)   #####*)
  (*****************************************************************)

  PROCEDURE AlterIOMode (VAR f: TextFile; newMode: IOMode);
  (*
    Lets you change the current IOMode to a new setting.  Make
    sure that you use this feature cautiously, since it may
    disturb proper functioning of an algorithm relying on
    sequential files as they are normally used by above,
    safer routines.  Note also that AlterIOMode is not
    possible with files which have been opened as read
    only (see above ReadOnlyLookup).
  *)

  PROCEDURE SetFilePos( VAR f: TextFile; pos: LONGINT );
  (*
    Sets current position in file "f" to "pos". Any reading
    from the file by means of procedures exported from this
    module (e.g. "Read...", "Get...", "SkipGap") will now start
    at character with position "pos"+1 and will increment the
    current position by the number of characters or bytes read.
    The same applies to writing onto the file by means of
    "Write..." or "Put..."-procedures. Remark: a call to
    "Again" decrements the current position by 1.
  *)

  PROCEDURE GetFilePos( VAR f: TextFile; VAR pos: LONGINT );
  (* Returns current position in file "f" *)

  PROCEDURE ReadByteBlock ( VAR f: TextFile; VAR buf: ARRAY OF BYTE;
                            VAR count: LONGINT );
  (*
    Read "count" bytes from file "f" into buffer "buf",
    starting one byte after the current file position.
    Precondition: file has been opened in the IOMode reading.
    "count" returns the number of bytes succesfully read.
  *)

  PROCEDURE WriteByteBlock( VAR f: TextFile; VAR buf: ARRAY OF BYTE;
                            VAR count: LONGINT );
  (*
    Write "count" bytes from buffer "buf" onto file "f",
    starting one byte after the current file position.
    Precondition: file has been opened in the IOMode writing.
    VAR-parameter "buf" for speed-up reasons only. "count"
    returns the number of bytes succesfully written.
  *)





  (**********************************************************************)
  (*#####   Routines only effective in Macintosh implementations   #####*)
  (**********************************************************************)


  PROCEDURE SetFileFilter(ft1,ft2,ft3,ft4: ARRAY OF CHAR);
  PROCEDURE GetFileFilter(VAR ft1,ft2,ft3,ft4: ARRAY OF CHAR);
  (*
    GetExistingFile will display files for selection only which
    match the file types as defined by the last call to SetFileFilter.

    A file type or a file's creator are defined by exactly 4
    characters, e.g. "TEXT" A file filter can contain up to 4
    such file types. The above procedures allow to change the
    default filter used by this module to select the files for
    display by procedure GetExistingFile.  The initial file
    filter is set to just one type, namely the standard type
    "TEXT". This is in accordance with the fact that the
    procedures CreateNewFile and Lookup create by default only
    files of this type.

    To display any kind of files specify all four file types as
    empty strings ( SetFileFilter("","","","") ). NOTE:
    Regardless of the type specified by the filter, this module
    will not behave differently.  For instance, changing the file
    filter won't have any effect on the functions performed by
    this module nor on the file's content.

    GetFileFilter returns the currently used file filters.
    For instance you may wish to save first the old filter
    before overwriting it with your new one.  Once you're
    done with your operations, you may restore the file filter
    to its previous value.

    A typical usage of this feature is to call
    UseAsTypeAndCreator before creating a file by means of this
    module.  This allows to associate certain files with a
    particular Dialog Machine program. E.g. a modelling
    environment storing and loading models of a particular type
    of files. Changing the filter will allow only to selectively
    access such files, in order to hide other kinds of files from
    the user.  Moreover note that on the Macintosh the file
    type may determine the appearance of the file's icon as
    it is displayed by the Finder on the desktop.
  *)

  PROCEDURE HasTypeAndCreator(VAR f: TextFile;
                              VAR filetype,creator: ARRAY OF CHAR);
  (*
    Returns the type and creator of the file f.  (Implementation
    note:  the file f is only specified by the record fields path
    and filename.  The routine functions also regardless wether
    the file is currently open or not.  Except for the field
    f.res, none of the record fields are changed by this routine,
    nor is f's current status (opened/closed) affected.
  *)


  PROCEDURE UseAsTypeAndCreator(filetype,creator: ARRAY OF CHAR);
  PROCEDURE UsedTypeAndCreator(VAR filetype,creator: ARRAY OF CHAR);
  (*
    When a file is created by means of CreateNewFile or Lookup,
    its type and creator will be filetype and creator as set
    by the last call to UseAsTypeAndCreator.  Default is:
    UseAsTypeAndCreator('TEXT','MEDT');  The latter is the creator
    of the MEdit editor (part of RAMSES software available from
    Systems Ecology, Swiss Federal Institute of Technology ETH,
    Zürich, Switzerland
          E-Mail address:  RAMSES@ito.umnw.ethz.ch). UsedTypeAndCreator returns the currently
    used type and creator.  Note:  The resetting to the defaults
    is done automatically upon quitting the environment of this
    module.
  *)



  PROCEDURE LastResultCode(): INTEGER;
  (*
    Returns last result code obtained from a call to an internal
    system routine, which has been involved in a file operation.
    Since Response does only cover some of the more frequently
    encountered errors when operating on files, this routine
    allows to learn more about all possible error causes
    maintained by the Macintosh computer's file system.  For the
    interpretation of result codes you have to refer to Inside
    Macintosh, Volumes I-VI, Addison Wesley Publishing Company,
    Inc., Reading a.o., 1986-1991.
  *)


END DMFiles.

  Contact RAMSES@env.ethz.ch Last updated: 25-Jul-2011 [Top of page]