textfile.sa
Generated by gen_html_sa_files from ICSI. Contact gomes@icsi.berkeley.edu for details
-------------------------> GNU Sather - sourcefile <-------------------------
-- Copyright (C) 2000 by K Hopper, University of Waikato, New Zealand --
-- This file is part of the GNU Sather library. It is free software; you may --
-- redistribute and/or modify it under the terms of the GNU Library General --
-- Public License (LGPL) as published by the Free Software Foundation; --
-- either version 2 of the license, or (at your option) any later version. --
-- This library is distributed in the hope that it will be useful, but --
-- WITHOUT ANY WARRANTY without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. See Doc/LGPL for more details. --
-- The license text is also available from: Free Software Foundation, Inc., --
-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --
--------------> Please email comments to <bug-sather@gnu.org> <--------------
class TEXT_FILE < $FILES,$OSTREAM
class TEXT_FILE < $FILES,$OSTREAM is
-- This class specifies the text file specific operations on file
-- objects. It treats a file as a simple stream for which no positioning
-- operations are provided. See also the $FILE_CURSORS class.
-- Version 1.3 Nov 2000. Copright K Hopper, U of Waikato
-- Development History
-- -------------------
-- Date Who By Detail
-- ---- ------ ------
-- 5 Apr 96 kh Original
-- 5 Apr 97 kh Modified for INT to CARD
-- 26 Oct 98 kh Revised, added pre/post conditions
-- 17 Nov 00 kh Added size to give chars not octets
include FILE{TEXT_FILE}
size -> private octet_size ;
size(
lib : LIBCHARS
) : CARD
pre ~void(self)
and ~void(fyle)
post true -- or an exception has been raised.
is
-- This routine returns the current size of the file as the number of
-- CHARs contained in the given culture encoding. If the contents have been
-- written and not flushed then the value returned may not reflect the
-- actual contents of the file when flushed and closed. If the attempt to
-- obtain the size is not accepted by the underlying file system then an
-- exception is raised for external handling.
return octet_size / lib.my_size
end ;
size : CARD
pre ~void(self)
and ~void(fyle)
post true -- or an exception has been raised.
is
-- This routine returns the current size of the file as the number of
-- CHARs contained using the default culture encoding. See also the above
-- definition.
return size(LIBCHARS::default)
end ;
cursor(
buff_size : CARD,
lib : LIBCHARS
) : TEXT_FILE_CURSOR
pre ~void(fyle)
and ((buff_size % 4) = 0)
post ~void(result)
is
-- This routine provides for buffered I/O using the file cursor
-- returned. This permits the use of large files where reading all of
-- file contents at once would be impractical.
return TEXT_FILE_CURSOR::create(fyle,buff_size,lib,readable,writable)
end ;
cursor(
buff_size : CARD
) : TEXT_FILE_CURSOR
pre ~void(fyle)
and ((buff_size % 4) = 0)
post ~void(result)
is
-- This routine provides for buffered I/O using the file cursor
-- returned. This permits the use of large files where reading all of
-- file contents at once would be impractical.
return cursor(buff_size,LIBCHARS::default)
end ;
plus(
item : $STR
)
pre ~void(self)
and ~void(fyle)
and writable
post (size >= initial(size))
is
-- This routine appends the given item to the current channel at its
-- present position. Note the special handling of the void string at the
-- beginning!
if void(item) then -- nothing to do!
return
end ;
dummy : BOOL ;
count : CARD ;
loc_lib : LIBCHARS ;
loc_octs : BINSTR ;
typecase item
when CHAR then
count := 1 ;
loc_octs := item.binstr ;
loc_lib := LIBCHARS::default ;
loc_octs := loc_octs.tail(loc_lib.my_size)
when FSTR then
count := item.size ;
loc_octs := item.binstr ;
loc_lib := item.index_lib
when STR then
count := item.size ;
loc_octs := item.binstr ;
loc_lib := item.index_lib
else
SYS_ERROR::create.error(self,SYS_EXCEPT::Bad_Type,my_name)
end ;
dummy := FILE_SYS::file_write(loc_octs,loc_lib.my_size,inout count,fyle)
end ;
plus(
item : $STR
) : SAME
pre ~void(self)
and ~void(fyle)
and writable
post (size >= initial(size))
is
-- This routine appends the given item to the current channel. It
-- returns self.
plus(item) ;
return self
end ;
fstr(
lib : LIBCHARS
) : FSTR
pre ~void(self)
and ~void(fyle)
and readable
post void(result)
or (result.loc <= size)
is
-- This routine returns a string buffer containing the entire
-- contents of the file. Note that some operating systems carry out
-- conversions on input and the number of characters contained may,
-- therefore, not be the same as reported by the filing system.
--
file_size : CARD ;
if ~FILE_SYS::size(fyle,out file_size) then
return void
end ;
if file_size = 0 then -- Nothing to read!
return FSTR::create(1,lib) -- an empty buffer!
end ;
res : FSTR := FSTR::create(file_size,lib) ;
buffer_cnt : CARD := file_size ; -- in octets!!
if ~FILE_SYS::seek(fyle,0.int,FILE_LOCS::Beginning) then
return void
end ;
if ~FILE_SYS::file_read(res,1,inout buffer_cnt,fyle) then
return void -- probably not readable!
end ;
res.loc := buffer_cnt ;
return res
end ;
fstr : FSTR
pre ~void(self)
and ~void(fyle)
and readable
post void(result)
or (result.loc <= size)
is
-- This routine returns a string buffer containing the entire
-- contents of the file. Note that some operating systems carry out
-- conversions on input and the number of characters contained may,
-- therefore, not be the same as reported by the filing system.
return fstr(LIBCHARS::default)
end ;
frunes(
lib : LIBCHARS
) : FRUNES
pre ~void(self)
and ~void(fyle)
and readable
post (result.loc = size)
is
-- This routine returns a text buffer containing the entire contents of
-- the file if no error was detected (otherwise an empty text string). This
-- contents is organised as a sequence of runes each of which in general have
-- more encodings than one. The string is organised into runes on the
-- assumption that it uses the given repertoire and encoding.
file_size : CARD ;
if ~FILE_SYS::size(fyle,out file_size) then
return void
end ;
if file_size = 0 then -- Nothing to read!
return FRUNES::create(1,lib) -- an empty buffer!
end ;
res : FRUNES := FRUNES::create(file_size,lib) ;
buffer_cnt : CARD := file_size ; -- in octets!!
if ~FILE_SYS::seek(fyle,0.int,FILE_LOCS::Beginning) then
return void
end ;
if ~FILE_SYS::file_read(res,1,inout buffer_cnt,fyle) then
return void -- probably not readable!
end ;
res.loc := buffer_cnt ;
res.buffer_scan ;
return res
end ;
frunes : FRUNES
pre ~void(self)
and ~void(fyle)
and readable
post (result.loc = size)
is
-- This routine returns a text buffer containing the entire contents of
-- the file if no error was detected (otherwise an empty text string). This
-- contents is organised as a sequence of runes each of which in general have
-- more encodings than one. The string is organised into runes on the
-- assumption that it is in the program environment repertoire and encoding.
return frunes(LIBCHARS::default)
end ;
end ; -- TEXT_FILE
class TEXT_FILE_CURSOR < $FILE_CURSORS{CHAR,STR}
class TEXT_FILE_CURSOR < $FILE_CURSORS{CHAR,STR} is
-- This class implements the detailed buffered I/O operations for
-- a text file. Note that the majority of these make use of the underlying
-- STR_CURSOR operations.
-- Version 1.1 Apr 97. Copright K Hopper, U of Waikato
-- Development History
-- -------------------
-- Date Who By Detail
-- ---- ------ ------
-- 5 Apr 96 kh Original
-- 5 Apr 97 kh Modified for portability
include FILE_CURSOR{TEXT_FILE_CURSOR, FSTR} ;
create(
handle : REFERENCE,
buff_size : CARD,
lib : LIBCHARS,
rd,
wr : BOOL
) : SAME
pre ~void(handle)
and (buff_size % 4 = 0) -- to avoid character code across boundary!
post ~void(result)
is
-- THIS ROUTINE IS NOT FOR DIRECT USE -- IT IS ONLY FOR USE BY AN
-- OBJECT OF THE TEXT_FILE CLASS!!!
--
-- This routine establishes a new buffer for the specified file. It
-- does NOT fill the buffer until an actual input/output operation is
-- required!
me : SAME := new ;
me.fyle := handle ;
me.buffer := FSTR::create(buff_size,lib) ;
me.position := 0 ;
me.buff_position := 0 ;
me.buff_length := buff_size ;
me.buff_valid := false ;
me.buff_altered := false ;
me.index := 0 ;
me.writable := wr ;
me.readable := rd ;
return me
end ;
getch : CHAR
pre ~void(self)
and readable
post ((result.code = CHAR_CODE::null)
and at_end)
or ~(result.code = CHAR_CODE::null)
is
-- This procedure returns a single character from the current line of
-- text (providing that there is one), moving the cursor forward by one.
loop -- needed to use next_ch!
return next_ch!
end
end ;
retract : SAME
pre ~void(self)
and (position >= buffer.index_lib.my_size)
post ((result.position + result.buffer.index_lib.my_size) =
initial(self.position))
is
-- This routine moves the cursor backwards one character position. This
-- is done by using backup.
back_up ;
return self
end ;
get_upto(
ch : CHAR
) : STR
pre ~void(self)
and readable
post result.index_lib = buffer.index_lib
is
-- This routine returns the string up to the next occurrence of
-- ch -- or the end of the current line, whichever occurs first.
res : FSTR := FSTR::create(buffer.index_lib) ;
loc_line : STR := STR::create(buffer.index_lib).line_mark ;
loop
loc_ch : CHAR := next_ch! ;
if (loc_ch.code = CHAR_CODE::null) then
return res.str
elsif (loc_ch = ch)
or loc_line.contains(loc_ch) then
dummy : SAME := retract ;
return res.str
else
res := res + loc_ch
end
end ;
return res.str
end ;
get_line : STR
pre ~void(self)
and readable
post buffer.index_lib = result.index_lib
is
-- This routine returns all of the characters remaining on the current
-- text line, advancing the cursor by as many places. It does NOT
-- include any end of line mark!
loc_mark : STR := STR::create(buffer.index_lib).line_mark ;
res : FSTR := FSTR::create(buffer.index_lib) ;
loc_ch : CHAR ;
loop
if at_end then
return res.str
end ;
loc_ch := next_ch! ;
if loc_mark.contains(loc_ch) then
back_up ;
return res.str
else
res := res + loc_ch
end
end
end ;
skip_to(
ch : CHAR
)
pre ~void(self)
and readable
post true
is
-- This routine skips up to the next occurrence of ch wherever it may
-- occur in the remainder of the file -- or to the end of file, whichever
-- occurs first.
loop
if at_end then
break!
elsif ch = next_ch! then
dummy : SAME := retract ;
break!
end
end
end ;
skip_over(
ch : CHAR
)
pre ~void(self)
and readable
post true
is
-- This routine skips up to the position one character beyond the next
-- occurrence of ch wherever it may occur in the remainder of the file --
-- or to the end of file, whichever occurs first.
loop
if at_end then
break!
elsif ch = next_ch! then
break!
end
end
end ;
skip_line
pre ~void(self)
and readable
post true
is
-- This routine advances the cursor past the next end of line mark,
-- or to the end of file -- whichever occurs first.
loc_mark : STR := STR::create(buffer.index_lib).line_mark ;
first_ch : CHAR ;
loop
if at_end then
return
end ;
first_ch := next_ch! ;
until!(loc_mark.contains(first_ch)) ;
end ;
loop
if at_end then
return
end ;
second_ch : CHAR := next_ch! ;
if first_ch = second_ch
or ~loc_mark.contains(second_ch) then
back_up
end ;
return
end
end ;
plus(
item : CHAR
)
pre ~void(self)
and writable
and ((buff_contents % buffer.index_lib.my_size) = 0)
post (buffer[(index - 1) / buffer.index_lib.my_size] = item)
is
-- This routine appends item at the current position of the cursor.
ch_size : CARD := buffer.index_lib.my_size ;
if position < buff_position then
emptybuff ;
fillbuff
else
index := position - buff_position
end ;
if index = buff_contents then
emptybuff ;
if ~at_end then -- refill to over-write!
fillbuff
end
end ;
ch_index : CARD := index / ch_size ;
buffer[ch_index] := item ;
buff_altered := true ; -- needs writing after this!
index := index + ch_size ;
position := position + ch_size
end ;
plus(
item : CHAR
) : SAME
pre ~void(self)
and writable
post (buffer[(index - 1) / buffer.index_lib.my_size] = item)
is
-- This routine appends the given character to the file, returning
-- the cursor.
plus(item) ;
return self
end ;
plus(
str : STR
)
pre ~void(self)
and (str.size > 0)
and writable
and (str.index_lib = buffer.index_lib)
post (position = (initial(position) +
(str.size * str.index_lib.my_size)))
is
-- This routine appends str as a sequence of characters, using the
-- routine above, to the file, starting at the current position of the cursor.
todo : CARD := str.size ; -- characters - NOT octets
cnt : CARD := 0 ;
ch_index : CARD ; -- character index
ch_size : CARD := buffer.index_lib.my_size ; -- octets!
loop
if position >= buff_position then
index := position - buff_position ;
buff_valid := (index < buff_length)
else -- buffer needs filling!
buff_valid := false
end ;
if ~buff_valid then -- simple! going forward!
emptybuff ;
if ~at_end then
fillbuff
else
buff_position := position ;
index := 0
end
end ;
todo := todo - cnt ;
if todo = 0 then
break!
elsif todo > cnt then
cnt := todo.min((buff_length - index) / ch_size)
end ;
loop
cnt.times! ;
ch_index := (index / ch_size).up! ;
buffer.aset(ch_index,str.elt!)
end ;
buff_altered := true ;
position := position + (cnt * ch_size)
end
end ;
plus(
str : STR
) : SAME
pre ~void(self)
and (str.size > 0)
and writable
and (str.index_lib = buffer.index_lib)
post (position = (initial(position) +
(str.size * str.index_lib.my_size)))
is
-- This routine appends str to the file, returning the cursor.
plus(str) ;
return self
end ;
skip(
cnt : CARD
) : SAME
pre ~void(self)
and (cnt > 0)
post (position > initial(position))
is
-- This routine skips forward the given number of string items. It is
-- NOT merely a renaming of forward above.
loop
cnt.times! ;
dummy : CHAR := next_ch!
end ;
return self
end ;
private back_up
pre ~void(self)
and (position >= buffer.index_lib.my_size)
post ((position + buffer.index_lib.my_size) = initial(position))
is
-- This routine backs up one character code length.
ch_size : CARD := buffer.index_lib.my_size ;
position := position - ch_size
end ;
private next_ch! : CHAR
pre ~void(self)
post ~(result.code = CHAR_CODE::null) -- or has quit!
is
-- This iter 'hides' all of the buffer filling and emptying needed
-- for file reading. Note that the attribute position is always considered
-- to be the file position which may or may not be in the buffer on
-- entry. Note that there is no readable pre-condition since all of the
-- routines which may call this make a readability test!
ch_size : CARD := buffer.index_lib.my_size ;
loop
if position = size then
quit
elsif position >= buff_position then
index := position - buff_position ;
buff_valid := (index < buff_contents)
else -- buffer needs filling!
buff_valid := false
end ;
if ~buff_valid then -- simple! going forward!
fillbuff ;
if buff_contents = 0 then -- reached the end
quit
end
end ;
position := position + ch_size ; -- after the yield
yield buffer[index / ch_size] ;
end
end ;
end ; -- TEXT_FILE_CURSOR