:TITLE: File Access 101 
;#
;# RCSID: $Header: /home/cvs/TclTutor3/lesson/Tcl/Tcl24.lsn,v 1.1.1.1 2007/09/15 13:30:24 clif Exp $
;# Copyright (c) 1995 Clif Flynt
;# 9300 Fleming Rd.
;# Dexter, MI  48130
;# clif@cflynt.com
;# See file "NOTICE" for licensing terms.
;#
::CMD:: if {([info exists tcl_platform])} {
  switch $tcl_platform(platform) {
    unix        {
                set Tutor(lsn.codeMod) {
                }
        }
    windows     {
                set Tutor(lsn.codeMod) {
                    {if {[glob -nocomplain C:/temp] != ""} {
                      regsub "/tmp" $line "/temp" line
		    }
		    }
                    {if {[glob -nocomplain C:/windows/temp] != ""} {
                      regsub "/tmp" $line "/windows/temp" line
		    }
		    }
                    {if {[glob -nocomplain C:/winnt/temp] != ""} {
                      regsub "/tmp" $line "/winnt/temp" line
		    }
		    }
                }         
        }
    mac         {
                set Tutor(lsn.codeMod) {
		}
                }
    default     {
                puts "I don't recognize the platform: $tcl_platform(platform)"
                puts "Can't set platform specific parameters"
                }
    } 
  }

:LESSON_TEXT_START_LEVEL 0:
Tcl supports an interface to the file system using the buffered i/o mechanism.
<P>
The simplest methods to access a file are via gets and puts.  When
there is a lot of data to be read, however, it is sometimes more efficient to use the
<CODE>read</CODE> command to load an entire file, and then parse the file
into lines with the <CODE>split</CODE> command.
<DL>
<B><DT>open</B> <I>fileName ?access? ?permission?</I>
<DD>Opens a file and returns a token to be used when accessing the file
via <CODE> gets, puts, close, </CODE>etc.
<UL>
<I><LI>FileName</I> is the name of the file to open.
<I><LI>access</I> is the file access mode
<UL>
<B><LI>r</B>......Open the file for reading.  The file must already exist.
<B><LI>r+</B>...Open the file for reading and writing. The file must already exist.
<B><LI>w</B>.....Open the file for writing.  Create the file if it doesn't
exist, or set the length to zero if it does exist.
<B><LI>w+</B>..Open the file for reading and writing.  Create the file if it doesn't
exist, or set the length to zero if it does exist.
<B><LI>a</B>......Open the file for writing.  The file must already exist.  
Set the current location to the end of the file.
<B><LI>a+</B>...Open the file for writing.  The file does not exist, create it.
Set the current location to the end of the file.
</UL>
<I><LI>permission</I> is an integer to use to set the file access permissions.  The
default is rw-rw-rw- (0666).
</UL>
<B><DT>close</B> <I>fileID</I>
<DD>Closes a file previously opened with <CODE>open</CODE>, and flushes
any remaining output.
<B><DT>gets</B> <I>fileID ?varName?</I>
<DD>Reads a line of input from <I>FileID</I>, and discards the terminating
newline.  
<P>
If there is a <I>varName</I> argument, <CODE>gets</CODE> returns the number of
characters read (or -1 if an EOF occurs), and places the line of input in <I>
varName</I>.  
<P>
If <I>varName</I> is not specified, <CODE>gets</CODE> returns the line of input.
An empty string will be returned if:
<UL>
<LI> There is a blank line in the file.
<LI> The current location is at the end of the file.  (An EOF occurs.)
</UL>
<B><DT>puts ?-nonewline?</B> <I>?fileID? string</I>
<DD>Writes the characters in string to the stream referenced by <I>fileID</I>.
<P>
<I>FileID</I> is one of:
<UL>
<LI>	The value returned by a previous call to <CODE>open</CODE> with write access.
<LI>	stdout
<LI>	stderr
</UL>
<B><DT>read ?-nonewline?</B> <I>fileID</I>
<DD>Reads all the remaining bytes from <I>fileID</I>, and returns that
string.  If <B>-nonewline</B> is set, then the last character will be
discarded if it is a newline.  Any existing end of file condition is
cleared before the <CODE>read</CODE> command is executed.
<B><DT>read</B> <I>fileID numBytes</I>
<DD>Reads up to <I>numBytes</I> from <I>fileID</I>, and returns the input
as a Tcl string.  Any existing end of file condition is
cleared before the <CODE>read</CODE> command is executed.
<B><DT>seek</B> <I>fileID offset ?origin?</I>
<DD>  Change the current position within the file referenced by <I>fileID</I>.
Note that if the file was opened with "a" <I>access</I> that the current 
position can not be set before the end of the file for writing, but can
be set to the beginning of the file for reading.
<UL>
<I><LI>fileID</I> is one of:
<UL>
<LI> a File identifier returned by <CODE>open</CODE>
<LI> stdin
<LI> stdout
<LI> stderr
</UL>
<I><LI> offset</I> is the offset in bytes at which the current position is to
be set.  The position from which the offset is measured defaults to the start
of the file, but can be from the current location, or the end by setting
<I>origin</I> appropriately.
<I><LI>origin</I> is the position to measure <I>offset</I> from.  It defaults
to the start of the file.  <I>Origin</I> must be one of:
<UL>
<LI><B>start</B>.........<I>Offset</I> is measured from the start of the file.
<LI><B>current</B>...<I>Offset</I> is measured from the current position in the file.
<LI><B>end</B>...........<I>Offset</I> is measured from the end of the file.
</UL>
</UL>
<B><DT>tell</B> <I>fileID </I>
<DD>Returns the position of the access pointer in <I>fileID</I> as a decimal
string.
<B><DT>flush</B> <I>fileID</I>
<DD> Flushes any output that has been buffered for <I>fileID</I>.
<B><DT>eof</B> <I>fileID</I>
<DD> returns 1 if an End Of File condition exists, otherwise returns 0.
</DL>
:TEXT_END:

:LESSON_TEXT_START_LEVEL 1:
Tcl supports an interface to the file system using the buffered i/o mechanism.
<P>
The simplest methods to access a file are via gets and puts.  When
there is a lot of data to be read, however, it is sometimes more efficient to use the
<CODE>read</CODE> command to load an entire file, and then parse the file
into lines with the <CODE>split</CODE> command.
<DL>
<B><DT>open</B> <I>fileName ?access? ?permission?</I>
<DD>Opens a file and returns a token to be used when accessing the file
via <CODE> gets, puts, close, </CODE>etc.
<UL>
<I><LI>FileName</I> is the name of the file to open.
<I><LI>access</I> is the file access mode
<UL>
<B><LI>r</B>......Open the file for reading.  The file must already exist.
<B><LI>r+</B>...Open the file for reading and writing. The file must already exist.
<B><LI>w</B>.....Open the file for writing.  Create the file if it doesn't
exist, or set the length to zero if it does exist.
<B><LI>w+</B>..Open the file for reading and writing.  Create the file if it doesn't
exist, or set the length to zero if it does exist.
<B><LI>a</B>......Open the file for writing.  The file must already exist.  
Set the current location to the end of the file.
<B><LI>a+</B>...Open the file for writing.  The file does not exist, create it.
Set the current location to the end of the file.
</UL>
<I><LI>permission</I> is an integer to use to set the file access permissions.  The
default is rw-rw-rw- (0666).
</UL>
<B><DT>close</B> <I>fileID</I>
<DD>Closes a file previously opened with <CODE>open</CODE>, and flushes any 
remaining output.
<B><DT>gets</B> <I>fileID ?varName?</I>
<DD>Reads a line of input from <I>FileID</I>, and discards the terminating
newline.  
<P>
If there is a <I>varName</I> argument, <CODE>gets</CODE> returns the number of
characters read (or -1 if an EOF occurs), and places the line of input in <I>
varName</I>.  
<P>
If <I>varName</I> is not specified, <CODE>gets</CODE> returns the line of input.
An empty string will be returned if:
<UL>
<LI> There is a blank line in the file.
<LI> The current location is at the end of the file.  (An EOF occurs.)
</UL>
<B><DT>puts ?-nonewline?</B> <I>?fileID? string</I>
<DD>Writes the characters in string to the stream referenced by <I>fileID</I>.
<P>
<I>FileID</I> is one of:
<UL>
<LI>	The value returned by a previous call to <CODE>open</CODE> with write access.
<LI>	stdout
<LI>	stderr
</UL>
<B><DT>read ?-nonewline?</B> <I>fileID</I>
<DD>Reads all the remaining bytes from <I>fileID</I>, and returns that
string.  If <B>-nonewline</B> is set, then the last character will be
discarded if it is a newline.  Any existing end of file condition is
cleared before the <CODE>read</CODE> command is executed.
<B><DT>read</B> <I>fileID numBytes</I>
<DD>Reads up to <I>numBytes</I> from <I>fileID</I>, and returns the input
as a Tcl string.  Any existing end of file condition is
cleared before the <CODE>read</CODE> command is executed.
<B><DT>seek</B> <I>fileID offset ?origin?</I>
<DD>  Change the current position within the file referenced by <I>fileID</I>.
Note that if the file was opened with "a" <I>access</I> that the current 
position can not be set before the end of the file for writing, but can
be set to the beginning of the file for reading.
<UL>
<I><LI>fileID</I> is one of:
<UL>
<LI> a File identifier returned by <CODE>open</CODE>
<LI> stdin
<LI> stdout
<LI> stderr
</UL>
<I><LI> offset</I> is the offset in bytes at which the current position is to
be set.  The position from which the offset is measured defaults to the start
of the file, but can be from the current location, or the end by setting
<I>origin</I> appropriately.
<I><LI>origin</I> is the position to measure <I>offset</I> from.  It defaults
to the start of the file.  <I>Origin</I> must be one of:
<UL>
<LI><B>start</B>.........<I>Offset</I> is measured from the start of the file.
<LI><B>current</B>...<I>Offset</I> is measured from the current position in the file.
<LI><B>end</B>...........<I>Offset</I> is measured from the end of the file.
</UL>
</UL>
<B><DT>tell</B> <I>fileID </I>
<DD>Returns the position of the access pointer in <I>fileID</I> as a decimal
string.
<B><DT>flush</B> <I>fileID</I>
<DD> Flushes any output that has been buffered for <I>fileID</I>.
<B><DT>eof</B> <I>fileID</I>
<DD> returns 1 if an End Of File condition exists, otherwise returns 0.
</DL>
<P>
Points to remember about Tcl file access:
<UL>

<LI>The file I/O is buffered.  The output may not be sent out when you
expect it to be sent.  Files will all be closed and flushed when your
program exits normally, but may only be closed (not flushed) if the
program is terminated in an unexpected manner. 

<LI>There are a finite number of open file slots available.  If you expect
the program to run in a manner that will cause it to open several files,
remember to close the files when you are done with them.

<LI>An empty line is indistinguishable from an EOF with the command:
<P><CODE>set string [gets filename]</CODE>.  Use the <CODE>eof</CODE> command to
determine if the file is at the end.

<LI>You can't overwrite any data in a file that was opened with <B>a</B>
<I>access</I>.  You can, however seek to the beginning of the file for
<CODE>gets</CODE> commands.

<LI>Opening a file with the <B>w+</B> access will allow you to overwrite
data, but will delete all existing data in the file.

<LI>Opening a file with the <B>r+</B> access will allow you to overwrite
data, while saving the existing data in the file.

<LI>All data in TCL is saved as ASCII strings.  This means that reading a 
binary file may produce unexpected results.
</UL>

:TEXT_END:

:LESSON_TEXT_START_LEVEL 2:
Tcl supports an interface to the file system using the buffered i/o mechanism.
This mechanism treats the file like a stream of characters that start at
the beginning of the file, and run one after the other to the end.  This
makes a file look the same as a terminal to the program, and a program
can write a line of input to a file with the same <CODE>puts</CODE> command (but with one
new argument) that is used to print text to the screen.  Data can be read
from a file with a <CODE>gets</CODE>, just as it can be read from a keyboard.
<P>
Before a file can be accessed in this manner the program has to
declare which file is to be accessed, and whether it is to be accessed for
reading, writing, or both.  This declaration is made with the <CODE>open</CODE>
command.
Once the file is opened, the program can execute <CODE>gets</CODE> and
<CODE>puts</CODE> calls to read or write lines of data from or to the
file.
<P>
A program can also use a <CODE>seek</CODE> command to position a
marker within the file.  After the marker has been placed, the next <CODE>gets</CODE> or
<CODE>puts</CODE> will occur from that location.
<P>
When a program is finished with a file, it should close the file.  There
are a finite number of open file descriptor slots available for a program,
and if you neglect to close files, you may find your program failing when
it runs out of descriptor slots.
<P>
The file access commands are:
<DL>
<B><DT>open</B> <I>fileName ?access? ?permission?</I>
<DD>Opens a file and returns a token to be used when accessing the file
via <CODE> gets, puts, close, </CODE>etc.
<UL>
<I><LI>FileName</I> is the name of the file to open.
<I><LI>access</I> is the file access mode
<UL>
<B><LI>r</B>......Open the file for reading.  The file must already exist.
<B><LI>r+</B>...Open the file for reading and writing. The file must already exist.
<B><LI>w</B>.....Open the file for writing.  Create the file if it doesn't
exist, or set the length to zero if it does exist.
<B><LI>w+</B>..Open the file for reading and writing.  Create the file if it doesn't
exist, or set the length to zero if it does exist.
<B><LI>a</B>......Open the file for writing.  The file must already exist.  
Set the current location to the end of the file.
<B><LI>a+</B>...Open the file for writing.  The file does not exist, create it.
Set the current location to the end of the file.
</UL>
<I><LI>permission</I> is an integer to use to set the file access permissions.  The
default is rw-rw-rw- (0666).  
<P>The unix file system allows you to set the amount of access that people
are allowed to have to your files.  The permissions are set up as three 
groups of three access modes. 
<P>
The three access modes (permissions) you can set for your files are
<UL>
<LI> Read........Allow the file to be read
<LI> Write.......Allow the file to be written
<LI> Execute..Allow the file to be executed.  
</UL>
<P>
The three groups are
<UL>
<LI>User...... You.
<LI>Group... The other people in your group
<LI>World.... Everyone else
</UL>
<P>
The file permission is set with three octal digits, each octal digit is
one group, and each bit within the digit is one access right.
<PRE>
World-------------------|
                        |
Group --------------|   |
                    |   |
User  ----------|   |   |
               ___ ___ ___
               000 000 000
               R   R   R   ------ Read
                W   W   W  ------ Write
                 X   X   X ------ Execute
               
</PRE>

To restrict the world from reading your files, but allow anyone within
your group read access, while only allowing yourself to write to your files,
you'd use this mask <CODE>0046</CODE>.  The first 0 marks this as an octal
string.  The next 0 sets the access permissions for the world to no read,
no write, and no execute.  The 4 sets the read bit for members of your
group.  The final 6 sets read and write access for the file's owner (you).
<P>
To make a file executable, you set the low order bit.  For instance,
when you write a Tcl program and save it in a file, you will set the read
and execute permissions (probably with the unix <CODE>chmod</CODE> command)
in order to just type the file name and execute the script.
</UL>
<B><DT>close</B> <I>fileID</I>
<DD>Closes a file previously opened with <CODE>open</CODE>, and flushes any 
remaining output.
<B><DT>gets</B> <I>fileID ?varName?</I>
<DD>Reads a line of input from <I>FileID</I>, and discards the terminating
newline.
<P>
<I>Fileid</I> is one of:
<UL>
<LI> a File identifier returned by <CODE>open</CODE>
<LI> stdin
<LI> stdout
<LI> stderr
</UL>
<P>
If there is a <I>varName</I> argument, <CODE>gets</CODE> returns the number of
characters read (or -1 if an EOF occurs), and places the line of input in <I>
varName</I>.  
<P>
If <I>varName</I> is not specified, <CODE>gets</CODE> returns the line of input.
An empty string will be returned if:
<UL>
<LI> There is a blank line in the file.
<LI> The current location is at the end of the file.  (An EOF occurs.)
</UL>
<B><DT>puts ?-nonewline?</B> <I>?fileID? string</I>
<DD>Writes the characters in string to the stream referenced by <I>fileID</I>.
<P>
<I>FileID</I> is one of:
<UL>
<LI>	The value returned by a previous call to <CODE>open</CODE> with write access.
<LI>	stdout
<LI>	stderr
</UL>
<B><DT>read ?-nonewline?</B> <I>fileID</I>
<DD>Reads all the remaining bytes from <I>fileID</I>, and returns that
string.  If <B>-nonewline</B> is set, then the last character will be
discarded if it is a newline.  Any existing end of file condition is
cleared before the <CODE>read</CODE> command is executed.
<B><DT>read</B> <I>fileID numBytes</I>
<DD>Reads up to <I>numBytes</I> from <I>fileID</I>, and returns the input
as a Tcl string.  Any existing end of file condition is
cleared before the <CODE>read</CODE> command is executed.
<B><DT>seek</B> <I>fileID offset ?origin?</I>
<DD>  Change the current position within the file referenced by <I>fileID</I>.
Note that if the file was opened with "a" <I>access</I> that the current 
position can not be set before the end of the file for writing, but can
be set to the beginning of the file for reading.
<UL>
<I><LI>fileID</I> is one of:
<UL>
<LI> a File identifier returned by <CODE>open</CODE>
<LI> stdin
<LI> stdout
<LI> stderr
</UL>
<I><LI> offset</I> is the offset in bytes at which the current position is to
be set.  The position from which the offset is measured defaults to the start
of the file, but can be from the current location, or the end by setting
<I>origin</I> appropriately.
<I><LI>origin</I> is the position to measure <I>offset</I> from.  It defaults
to the start of the file.  <I>Origin</I> must be one of:
<UL>
<LI><B>start</B>.........<I>Offset</I> is measured from the start of the file.
<LI><B>current</B>...<I>Offset</I> is measured from the current position in the file.
<LI><B>end</B>...........<I>Offset</I> is measured from the end of the file.
</UL>
</UL>
<B><DT>tell</B> <I>fileID </I>
<DD>Returns the position of the access pointer in <I>fileID</I> as a decimal
string.
<B><DT>flush</B> <I>fileID</I>
<DD> Flushes any output that has been buffered for <I>fileID</I>.
<B><DT>eof</B> <I>fileID</I>
<DD> returns 1 if an End Of File condition exists, otherwise returns 0.
</DL>
<P>
Points to remember about Tcl file access:
<UL>

<LI>File I/O is buffered.  This means that the operating system keeps
a location in memory to which it copies data when your program writes
it out.  Later, when the computer is not busy, or when a certain amount
of data has been accumulated in the buffer it will be written.  Thus,
the output may not be sent out when you
expect it to be sent. 
<P>
You can force data to be sent from the buffer to the disk with the flush
command.  Files will all be closed and flushed when your
program exits normally, but may only be closed (not flushed) if the
program is terminated in an unexpected manner. 

<LI>There are a finite number of open file slots available.  If you expect
the program to run in a manner that will cause it to open several files,
remember to close the files when you are done with them.

<LI>An empty line is indistinguishable from an EOF with the command:
<P><CODE>set string [gets filename]</CODE>.  Use the <CODE>eof</CODE> command to
determine if the file is at the end.

<LI>You can't overwrite any data in a file that was opened with <B>a</B>
<I>access</I>.  You can, however seek to the beginning of the file for
<CODE>gets</CODE> commands.

<LI>Opening a file with the <B>w+</B> access will allow you to overwrite
data, but will delete all existing data in the file.

<LI>Opening a file with the <B>r+</B> access will allow you to overwrite
data, while saving the existing data in the file, but the file must exist
before you open it.

<LI>All data in TCL is saved as ASCII strings.  This means that reading a 
binary file may produce unexpected results.
</UL>
:TEXT_END:

:CODE_START:

set fileid [open "/tmp/testfile" w+]

seek $fileid 0 start

puts $fileid "This is a test.\nIt is only a test"

seek $fileid 0 start

set chars [gets $fileid line1];
set line2 [gets $fileid];

puts "There are $chars characters in \"$line1\""
puts "The second line in the file is: \"$line2\""

seek $fileid 0 start

set buffer [read $fileid];
puts "\nTotal contents of the file are:\n$buffer"
close $fileid
:TEXT_END:



