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