eX version 0.4b User's Manual                         File eXMan09.txt
(C) Copyright 1995, 1996 William E. Wilgus III.   All rights reserved.



Data Files
 
I'll admit right now that file system still needs an exterminator.  
It looks like the cause of the worst bUGH(!) is the compiler, but I 
don't know that for sure.  At any rate, the problem is that it will
only allow you to open so many files (17, I think) even though you've
closed (`forget') many of them.  There also seems to have been be an
occasional problem with eX finding file identifiers with the eX
mechanism for FileId, which is used much more often than is apparent, 
but hopefully is gone now.  The file system was scheduled for a re
write anyway... but that's what prototypes are for, no?  Bugh(!) and
all, eX is capable of doing lots of things for you.  

Further, the material in this chapter has been gone over only a few
times (as opposed to the quite a few times for most of the rest of the
manual) and re-written more than once (including this time), so it 
might be rough around the edges.  If you've concluded that working
on the file system isn't my favorite pastime, you're right.  Sorry!


Defining Files

All data files must be defined with the eX word `file' prior to their
use.  A file may be multiply defined; however, additionally defining a
file whose initial definition was any of the sequential output modes
will ultimately cause an error.  Except for random files, file does
not open the file; addressing or referencing the file does that if it
is not already open.    

   file(FileSpecification$, Number#, RandomMode$, BufferDefinition$)

   file(FileSpecification$, Number#, SequentialMode$<<, ModeOption>>)


File Specification
 
FileSpecification$ is an eX file specification and here, alphabetic
case is insignificant.  The following characters are ILLEGAL in
address file specifications given LITERALLY, but are legal in file
specifications given indirectly or as a reference: 

    pound sign               #    ASCII code 35
    dollar sign              $    ASCII code 36
    left and right parens   ( )   ASCII codes 40 and 41
    slash                    /    ASCII code 47
    right bracket            ]    ASCII code 91

File specifications must otherwise conform to the requirements of the 
operating system.  


File Number

FileNumber# may be in the range 0 to the number of files allocated. 
A file number of 0 indicates that eX should use the lowest available
file number, beginning with 1. 

eX tracks how many files are currently open.  If an access is made to
a file that is not currently open and the maximum number of files that
the system will allow to be open concurrently or have been allocated
within eX are open (the default is 15), eX will automatically close a
file to allow opening of the file to be accessed.  When and if the
file that was closed is accessed, eX will automatically re-open the
file (closing another, if necessary) and position the file to where it
was when it was closed.  eX might open it as a different file number,
however. 
 

File Mode

Mode$ is any valid eX file mode in any alphabetic case and only the  
first letter of the mode is required.  The following basic file modes
are provided:    

Append
     Sequential output, beginning at the end of the file, with the 
     data in ASCII representation and seperated by carriage returns.  

Input
     Sequential input commencing at the beginning of the file, with 
     the data in ASCII representation and seperated by carriage
     returns. 

Output
     Same as append, except starts at the beginning of the file and 
     any data that may have existed in the file is lost. 

Random
     Fixed-length record random access. 

 
Sequential file data is in ASCII format and data is delimited with
carriage returns (ASCII code 13); i.e. sequential files are ordinary
`text' files with one variable length record per `line'.  The output
format of numerics may be controlled through the statement form of 
the eX word `form', discussed in the chapter `Terminal'. 


Mode Option

ModeOption$ may be used with sequential modes to specify either of 
the two mode options described below.  If neither option is desired,
simply omit the file definition field.    

With the `delimited' option, specified by either "d" or "D", strings
are delimited with double qoutes `""'.   In the event that a string is
output and includes a carriage return, only the portion of the string
up to the carriage return is output.  Similarly, if a carriage return
is included in a string to be input, only the portion of the string up
to the carriage return will be input.    
 
With the length-encoded option, specified by either "l" or "L",
strings are preceded by an ASCII numeric that specifies the length of
the subsequent string.  This option is provided to enable the input
and output of strings that contain carriage returns.    


Buffer Definition

BufferDefinition$ is used with random files to define a record's 
variables (fields).  A buffer definition is the enumeration of the
buffer variables, with string variables additionally having a stated
length.  As an example,    

            "$one, 10 _two __three .four ..five"

defines a buffer with the string variable $one having a length of 10  
characters followed by the numeric variables _two, __three, .four and
..five.  String data is left justified within the record variable
(field).  Numerics are stored in the internal storage formats
described the chapter `Data Types and Entities'. 
 

`Un-defining' Files

The eX word `forget' is used to `un-define', or close files.  If the
forget file is a random access file, the the current record is written
before closing the file; sequential output file buffers are `flushed'.

All files are similarly closed if eX `crashes' to the operating
system or eX is otherwise exited. 


File Access Methods
 
Both explicit and implicit methods may be used to access files.  
Explicit file operations employ an eX word to specify the operation
and a reference to the object file.  Implicit methods do not use any
eX words, of course: when eX encounters an address to a file (either
the file specification or file number), it accesses the file, taking
its cue for the operation to be performed from either the file mode
(sequential files), or the context of the address (random access
files).  This is perhaps the closest eX comes to context sensitivity,
which isn't particularly close at all.  
             

Explicit File Operations
 
Random Access Files

Explicit random access operations take either of the forms    

     eXword(FileSpecification$<<, RecordNumber#>>)
     eXword(FileNumber#<<, RecordNumber#>>)

where the eX words are the self-explanatory `read' and `write'.  But note
the fact that an explicit random access file operation returns no
data.  If RecordNumber# is omitted or given as 0, the next
(subsequent) record is read or written.  In the absence of a previous
operation to the file, that record is the first.  
 
FileSpecification is a reference, of course, and most of the time
you'll probably give it as a literal.  But the form    

     eXword(FileSpecificationVariable, RecordNumberVariable)

is useful; and 

     eXword(:FileSpecification, RecordNumberVariable)

where :FileSpecification is an implied file operation, can be useful
as well. 


Sequential Access Files

Explicit sequential file operations take a list of language components
to provide values to be output by a write statement, or data entities
to receive values from a read statement (de-facto assignment).  The
formats are

    read(FileSpecification) DataEntity1<<, ... DataEntityN>>  
    write<<;>>(FileSpecification) ValueTerm1<<, ... ValueTermN>>  
    write<<;>>(FileSpecification) ValueTerm1<<; ... ValueTermN>>  

Again FileSpecification is a reference usually given either as a
literal name or number. 

In the read and first write statements shown above, elements within
the argument list are seperated by the usual comma/whitespace 
character combination.  The significance of the use of a comma in that
write statement's argument list is that a carriage return will be
issued to the file after the value(s) produced by the list component
it follows has been written to the file.  The use of a semi-colon in
the second write statement's argument list supresses output of the
carriage return that would otherwise be written to the file after the
value(s) produced by the list component it follows has been written to
the file.  Logically, either a comma or semi-colon/whitespace
combination may be used as the list element seperator for any
individual list element in a write statement argument list. 

The optional semi-colon shown after write in both statements supresses
the carriage return otherwise written to the file after the value(s)
produced by the last element in the argument list.  Note that no
whitespace may appear between write's semi-colon (if any) and the left
parens of the argument; and the use of that semi-colon is independant
of a semi-colon's use in argument list. 
 

Implicit File Operations
 
Basically, addressing a file implies a file operation; and for file
input, this statement is almost sufficient.  For file output, however,
it doesn't go far enough: implied file output is accomplished through
an assignment statement.  I'll begin the details with addressing a
file. 


Addressing Files

Files may be addressed directly through their un-delimited literal
file specification if 

     1. the first character of the term is alphabetic and the second 
        character is a colon, e.g. C:, 
     2. the first character of the term is a period and the second is 
        a backslash, i.e. .\, 
     3. the first two characters of the term are periods and the third
        is a backslash, i.e. ..\, or 
     4. the first character of the term is a colon (which serves as a 
        data entity prefix), i.e. :. 


Some examples might be in order: 

                         c:yourfile
                         .\anydir\anyfile
                         ..\otherdir\otherfile
                         :myfile

To address a file directly via its file number, preface that number
with the combination of a colon (ASCII code 58) and a numeric suffix 
(ASCII code 35): 

                         :#1

A file's number may be determined with the eX function `FileId'. 

There are actually three syntaxes that may be used to indirectly
address a file.  In the first, a semi-colon prefix is followed by
whitespace and then the value producing term that specifies the file,
either the file's number or specification.  BUT, you can't use a data
entity to specify an output or random access file except within an
aggregation.  Perhaps ironically, a file number may in this case be
given as a literal and a file specification may be given as a
delimited literal. 

                    : FileNumber#
                    : FileSpecification$

The second syntax mirrors that of an in-direct variable address, a
semi-colon prefix to a parenthesized argument that at least ultimately
returns either a numeric that is the file number or a string that is
the file specification. 

               :(FileNumberOrSpecification)

(However, :#(FileNumber#) also works.)

The last is simply a data file prefix colon (:) followed immediately 
by a data entity that holds either the file specification or the file
number.  But AGAIN, you can't use a data entity to specify an output
or random access file except within an aggregtion.  (The assignment
would be made to the data entity.)

                    :_FileNumber
                    :__FileNumber
                    :.FileNumber
                    :..FileNumber
                    :$FileSpecification
                    :@#
                    :@$


Implied Sequential Operations

Sequential output is the simplest of the implied file operations, so
I'll start there. 

          :LiteralFileSpecification$<<;>> := value(s)
          :#LiteralFileNumber#<<;>> := value(s)
          : LiteralFileSpecifier<<;>> := value(s)
          : (DataEntityFileSpecifier)<<;>> := value(s)
          :(FileSpecifier)<<;>> := value(s)

If given a numeric, that numeric is written.  If given a string, that
string is written.  If given both a numeric and a string, both are
written (with the string written first).  (Someday you'll be able to
specify which is written first.)  The optional semi-colon supresses
the carriage return that would otherwise be written to the file after
the data is written.  If the file is given both a numeric and a
string, the absence or presence of the semi-colon is applicable to
both.  A sequential output file assignment statement may appear within
a multiple assignment statement.  However, since a sequential output
file cannot provide a pre-assignment value, operated assignments are a
definite no-no!  The reason you can't use a data entity except within
an aggregation or `parens argument' is that the data entity gets
assigned, rather than the file.  

Implied sequential input is just a bit more difficult.  The fact is
that neither eX, the languages it is written in, the operating system
nor the data file know what is in the data file.  But, in order to
correctly input that data, eX must know whether to input a numeric or
a string.  Regrettably, that leaves us with the onus of telling eX
what to expect.  While knowing what to input may not be simple,
telling eX what to input is very simple.  You tell eX to input a
string by simply putting a string suffix $ on the file address: 

                           :FileSpec.Ext$
                           :#_FileNumber$

Although I've only shown two of the forms of addressing a file here,
the suffix may be used with all of them.  Logically, a numeric suffix
# tells eX to input a numeric.  

                         :FileSpecification#

By default (if no suffix is used), eX inputs a numeric.  Although
executionally superfluous, a numeric suffix aids macro documentation. 
A data entity external to an aggregation or `parens argument' can't be
used with input files because any assignment would correctly belong to
the data entity.  In this case any suffix would need to follow the
assignment value.  


Revisiting Random File Addressing

A random access file is a bit more complex than a sequential file.  If
limited to the syntax shown to this point, an address to a random
access file is an address to the current record in its entirety. 
While it is sometimes convenient to do just that, eX provides the
following syntax options for random files: 

     FileSpecifier<<(RecordNumber#)>><</VariableSpecifier>>

FileSpecifier may be either the file specification (name) or its
number.  RecordNumber# is parenthesized and evaluated like a 
(parenthesized) function argument, and in an ancillary sense the left
parens serves as a file specification delimiter---thus the reason it
is illegal in literal eX file specifications.  If the record number is
ommitted, the desired record number is taken to be the current record
number: 

                 FileSpecifier<</VariableSpecifier>>

VariableSpecifier may be either the number of the variable (position) 
in the file buffer or the name of the variable.  

         FileSpecifier<<(RecordNumber#)>>/VariableSpecifier#
         FileSpecifier<<(RecordNumber#)>>/VariableSpecifier$

Note that in this case, VariableSpecifier is a reference and if given 
as a string literal must be delimited.  eX also allows the variable
specifier to be an aggregation.  However, the appropriate data type
filter must be used:

     FileSpecifier/ # (NumericAggregation#)
     FileSpecifier/ $ (Alpha-numericAggregation$)

Finally, a numeric literal or variable that holds the number of the
file variable may be used:    

                     FileSpec/#_NumericVariable

If the variable specifier is ommitted, the desired data is assumed to
be the entire file record (as a string):    

                  FileSpecifier<<(RecordNumber#)>>


In implied random operations, if the record number in the reference is
not the current record, eX writes the current record before reading
the referenced record.  This gives hint to the fact that when eX
encounters a random file address, it treats it just like any other
data entity and returns its value.  Therefore, implied random file
writes are not going to set any speed records.  


Implied Random Operations

All that you need to know about an implied random access file read
(input) has already been stated at various points in this chapter.  
All that remains to be shown is an implied write, and I think
everything needed for that has at least been alluded to.  

An implied random file write statement takes the form of an assignment
statement.  The generalized form is 

                RandomAccessFileEntity := Value

Recalling the full syntax, 

     FileSpecifier<<(RecordNumber#)>><</VariableSpecifier>>

if the variable reference is omitted, the entire record is written.
This is child's play if you're copying records within a file or to and 
from data files with identical record structures.  But if you're building 
the record strings from scratch and the structure includes numerics, you'll 
need to use the data type cast functions (`double', `long', `short', `single') 
described in the chapter `Data Types and Entities' to help build the strings.
Assigning a random access file variable is no different than assigning any 
other data class, however.  Simply address the file and variable, write the
assignment operator, and provide a value of the required data class: 

         :RandFile/_ShortVariable := ShortValue#

(Since the record number was omitted, the assignment was made in the
current record.)  Just like the other data entities, a random access
assignment may be included in a multiple assignment statement, and
operated assignments and/or operated addresses are permitted as well. 
If presented with values in both data classes, the un-required data
class is ignored. 


Implied Operation Summary

A lot of implied operations material has been presented and a summary 
is in order:  

*    Any address to a file causes an implicit access to that file.    

*    In the case of sequential output files, data is written to the
     file through an assignment operation.

*    In the case of sequential input files, data is read from the file
     through its address, and a data type suffix may be required for 
     correct input ($). 

*    In the case of random files, data is either read from the file or
     written to the file, depending on the context of the reference, 
     but every address causes a read, whether the random file is an 
     assignment object or not.  (In this way, operated assignments 
     may be performed on random file variables.) 

 
 
File Functions
 
These function's object file argument requires a reference which may 
be given as either the file specification or file number.  With the
exception of the function `seek', the function argument may take 
the un-parenthesized form. 

               function ObjectFileReference
               function(ObjectFileReference)


eof
     End Of File.  Returns true if at the end of the file, false if 
     not.  For random files, eof returns true only if the last access 
     was unable to read any part of the record structure because the 
     end of the file was reached before the operation was completed. 

lof
     Length Of File.  Returns the size of random mode files in
     records, sequential mode files in bytes.  The file does not need 
     to have been defined to eX, but the size of an un-defined file 
     will always be in bytes.  The `lof' for a sequential file includes 
     the DOS end-of-file marker ctrl Z, or ASCII code 26.  If the file
     was not found, 0 is returned.

FileId
     File Identifier.  Returns the file number for the file named, or 
     file specification of the file number given.

FMode
     File Mode.  Returns the file mode for the file name or number 
     given and the file must be currently defined. 

FPos
     File Position.  Returns the current position in the file.  
     For random files, this is the record number; sequential files,
     the byte number.

seek
               seek(ObjectFileReference, Position#)
     Positions the object file to Position#, which for random access
     files is the record number; and all others the byte number.  `seek'
     returns no data. 


