5.11. Positioning Within a File

REXX supports two strategies for reading and writing streams:
character-wise, and line-wise, this section describes how a
program can reposition the current positions for each these
strategies. Note that positioning is only allowed for persistent
streams.

For each open file, there is a current read position or a current
write position, depending on whether the file is opened for
reading or writing. If the file is opened for reading and writing
simultaneously, it has both a current read position and a current
write position, and the two are independent and in general
different. A position within a file is the sequence number of the
byte or line that will be read or written in the next such
operation.

Note that REXX starts numbering at one, not zero. Therefore, the
first character and the first line of a stream are both numbered
one.  This differs from several other programming languages, which
starts numbering at zero.

Just after a stream has been opened, the initial values of the
current read position is the first character in the stream, while
the current write position is the end-of-file, i.e. the position
just after the last character in the stream. Then, reading will
return the first character (or line) in the stream, and writing
will append a new character (or line) to the stream.

These initial values for the current read and write positions are
the default values.  Depending on your REXX implementation, other
mechanisms for explicitly opening streams (e.g. through the
STREAM() built-in function) may be provided, and may set other
initial values for these positions. See the implementation-
specific documentation for further information.

When setting the current read position, it must be set to the
position of an existing character in the stream; i.e. a positive
value, not greater than the total number of characters in the
stream. In particular, it is illegal to set the current read
position to the position immediately after the last character in
the stream; although this is legal in many other programming
languages and operating systems, where it is known as “seeking to
the end-of-file”.

When setting the current write position, it too must be set to the
position of an existing character in the stream. In addition, and
unlike the current read position, the current write position may
also be set to the position immediately following the last
character in the stream. This is known as “positioning at the end-
of-file”, and it is the initial value for the current write
position when a stream is opened. Note that you are not allowed to
reposition the current write position further out beyond the end-
of-file—-which would create a “hole” in the stream—-even though
this is allowed in many other languages and operating systems.

Depending on your operating system and REXX interpreter,
repositioning to after the end-of-file may be allowed as an
extension, although it is illegal according to TRL2. You should
avoid this technique if you wish to write portable programs.

REXX only keeps one current read position and one current write
position for each stream. So both line-wise and character-wise
reading as well as positioning of the current read position will
operate on the same current read position, and similarly for the
current write position.

When repositioning line-wise, the current write position is set to
the first character of the line positioned at. However, if
positioning character-wise so that the current read position is in
the middle of a line in the file, a subsequent call to LINEIN()
will read from (and including) the current position until the next
end-of-line marker.  Thus, LINEIN() might under some circumstances
return only the last part of a line. Similarly, if the current
write position has been positioned in the middle of an existing
line by character-wise positioning, and LINEOUT() is called, then
the line written out becomes the last part of the line stored in
the stream.

Note that if you want to reposition the current write position
using a line count, the stream may have to be open for read, too.
This is because the interpreter may have to read the contents of
the stream in order to find where the lines start and end.
Depending on your operating system, this may even apply if you
reposition using character count.

Example: Repositioning in empty files

Since the current read position must be at an existing character
in the stream, it is impossible to reposition in or read from an
empty stream. Consider the following code:

     filename = ‘/tmp/testing’
     call lineout filename,, 1   /* assuming truncation */
     call linein filename, 1, 0

One might believe that this would set the current read and write
positions to the start of the stream. However, assume that the
LINEOUT() call truncates the file, so that it is zero bytes long.
Then, the last call can never be legal, since there is no byte in
the file at which it is possible to position the current read
position.  Therefore, a NOTREADY condition is probably raised.

Example: Relative repositioning

It is rather difficult to reposition a current read or write
position relative to the current position. The only way to do this
within the definition of the standard is to keep a counter which
tells you the current position. That is, if you want to move the
current read position five lines backwards, you must do it like
this:

     filename = ‘/tmp/data’
     linenum = 0 ;
     say linein(filename,10); linenum = 10
     do while random(100)>3
          say linein(filename); linenum = linenum+1
     end
     call linein(filename,linenum-5,0); linenum = linenum-5

Here, the variable linenum is updated for each time the current
read position is altered. This may not seem to difficult, and it
is not in most cases. However, it is nearly impossible to do this
in the general case, since you must keep an account of both line
numbers and character numbers. Setting one may invalidate the
other: consider the situation where you want to reposition the
current read position to the 10th character before the 100th line
in the stream. Except from mixing line-wise and character-wise I/O
(which can have strange effects), this is nearly impossible. When
repositioning character-wise, the line number count is
invalidated, and vice versa.

The “only” proper way of handling this is to allow one or more
(non-standard) STREAM() built-in function operations that returns
the current character and line count of the stream in the
interpreter.

Example: Destroying linecount

This example shows how overwriting text to the middle of a file
can destroy the line count. In the following code, we assume that
the file foobar exists, and contains ten lines which are “first
line”, second line, etc. up to “tenth line”. Then consider the
following code:

     filename = ‘foobar’
     say linein(filename, 5)   /* says ‘fifth line’ */
     say linein(filename)      /* says ‘sixth line’ */
     say linein(filename)      /* says ‘seventh line’ */
     call lineout filename, ‘This is a very long line’, 5
     say linein(filename, 5)   /* says ‘This is a very long line’
     */
     say linein(filename)      /* says ‘venth line’ */
     say linein(filename)      /* says ‘eight line’ */

As you can see from the output of this example, the call to
LINEOUT() inserts a long line and overwrites the fifth and sixth
lines completely, and the seventh line partially. Afterwards, the
sixth line is the remaining part of the old seventh line, and the
new seventh line is the old eighth line, etc.




PREV NEXT