(********************************************************************) (* *) (* subfile.s7i Open part of an existing file as read only file. *) (* Copyright (C) 2019 - 2020 Thomas Mertes *) (* *) (* This file is part of the Seed7 Runtime Library. *) (* *) (* The Seed7 Runtime Library is free software; you can *) (* redistribute it and/or modify it under the terms of the GNU *) (* Lesser General Public License as published by the Free Software *) (* Foundation; either version 2.1 of the License, or (at your *) (* option) any later version. *) (* *) (* The Seed7 Runtime 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 the GNU Lesser General Public License for more *) (* details. *) (* *) (* You should have received a copy of the GNU Lesser General *) (* Public License along with this program; if not, write to the *) (* Free Software Foundation, Inc., 51 Franklin Street, *) (* Fifth Floor, Boston, MA 02110-1301, USA. *) (* *) (********************************************************************) include "stdio.s7i"; (** * [[file|File]] implementation type for a sub segment of a base file. * A sub segment has a starting position and a length. *) const type: subFile is sub null_file struct var file: baseFile is STD_NULL; var integer: startPos is 0; var integer: size is 0; var integer: position is 1; end struct; type_implements_interface(subFile, file); (** * Open a file to read from a sub segment of a base file. * @param baseFile Base file in which the sub segment is situated. * @param startPos Start position of the sub segment. * @param size Size of the sub segment. * @return the file opened. *) const func file: openSubFile (in file: baseFile, in integer: startPos, in integer: size) is func result var file: newFile is STD_NULL; local var subFile: new_subFile is subFile.value; begin if baseFile <> STD_NULL then new_subFile.baseFile := baseFile; new_subFile.startPos := startPos; new_subFile.size := size; newFile := toInterface(new_subFile); end if; end func; (** * Read a character from ''inSubFile''. * @return the character read, or [[char#EOF|EOF]] at the end of the file. *) const func char: getc (inout subFile: inSubFile) is func result var char: charRead is ' '; begin if inSubFile.position <= inSubFile.size then seek(inSubFile.baseFile, pred(inSubFile.startPos + inSubFile.position)); charRead := getc(inSubFile.baseFile); incr(inSubFile.position); else charRead := EOF; end if; end func; (** * Read a [[string]] with maximum length from ''inSubFile''. * @return the string read. * @exception RANGE_ERROR The parameter ''maxLength'' is negative. *) const func string: gets (inout subFile: inSubFile, in integer: maxLength) is func result var string: striRead is ""; begin if maxLength <= 0 then if maxLength <> 0 then raise RANGE_ERROR; end if; elsif inSubFile.position <= inSubFile.size then seek(inSubFile.baseFile, pred(inSubFile.startPos + inSubFile.position)); if maxLength <= succ(inSubFile.size - inSubFile.position) then striRead := gets(inSubFile.baseFile, maxLength); inSubFile.position +:= maxLength; else striRead := gets(inSubFile.baseFile, succ(inSubFile.size - inSubFile.position)); inSubFile.position := succ(inSubFile.size); end if; end if; end func; (** * Determine the end-of-file indicator. * The end-of-file indicator is set if at least one request to read * from the file failed. * @return TRUE if the end-of-file indicator is set, FALSE otherwise. *) const func boolean: eof (in subFile: inSubFile) is return inSubFile.position > inSubFile.size; (** * Determine if at least one character can be read successfully. * This function allows a file to be handled like an iterator. * @return FALSE if ''getc'' would return [[char#EOF|EOF]], * TRUE otherwise. *) const func boolean: hasNext (in subFile: inSubFile) is return inSubFile.position <= inSubFile.size; (** * Obtain the length of a ''aSubFile''. * The file length is measured in characters. * @return the length of a file. *) const func integer: length (in subFile: aSubFile) is return aSubFile.size; (** * Determine if the file ''aFile'' is seekable. * If a file is seekable the functions ''seek'' and ''tell'' * can be used to set and and obtain the current file position. * @return TRUE, since a ''subFile'' is seekable. *) const boolean: seekable (in subFile: aFile) is TRUE; (** * Set the current file position. * The file position is measured in characters from the start of the file. * The first character in the file has the position 1. * @exception RANGE_ERROR The file position is negative or zero. *) const proc: seek (inout subFile: aSubFile, in integer: position) is func begin if position <= 0 then raise RANGE_ERROR; else aSubFile.position := position; end if; end func; (** * Obtain the current file position of ''aSubFile''. * The file position is measured in characters from the start of the file. * The first character in the file has the position 1. * @return the current file position. *) const func integer: tell (in subFile: aSubFile) is return aSubFile.position;