(********************************************************************) (* *) (* external_file.s7i File implementation type describing OS files *) (* Copyright (C) 1989 - 2014, 2019 - 2021 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 "clib_file.s7i"; include "null_file.s7i"; (** * [[file|File]] implementation type describing OS files. * This type provides access to sequential operating system files. * External files are seekable, therefore they support the * functions [[#length(in_external_file)|length]], * [[#seek(in_external_file,in_integer)|seek]] and * [[#tell(in_external_file)|tell]]. *) const type: external_file is sub null_file struct var clib_file: ext_file is CLIB_NULL_FILE; var string: name is ""; end struct; type_implements_interface(external_file, file); (** * Opens a file with the specified ''path'' and ''mode''. * There are text modes and binary modes: * *Binary modes: * ** "r" Open file for reading. * ** "w" Truncate to zero length or create file for writing. * ** "a" Append; open or create file for writing at end-of-file. * ** "r+" Open file for update (reading and writing). * ** "w+" Truncate to zero length or create file for update. * ** "a+" Append; open or create file for update, writing at end-of-file. * *Text modes: * ** "rt" Open file for reading. * ** "wt" Truncate to zero length or create file for writing. * ** "at" Append; open or create file for writing at end-of-file. * ** "rt+" Open file for update (reading and writing). * ** "wt+" Truncate to zero length or create file for update. * ** "at+" Append; open or create file for update, writing at end-of-file. * Note that this modes differ from the ones used by the C function * fopen(). * @param path Path of the file to be opened. The path must * use the standard path representation. * @param mode Mode of the file to be opened. * @return the file opened, or [[null_file#STD_NULL|STD_NULL]] * if it could not be opened or if ''path'' refers to * a directory. * @exception MEMORY_ERROR Not enough memory to convert the path * to the system path type. * @exception RANGE_ERROR The ''mode'' is not one of the allowed * values or ''path'' does not use the standard path * representation or ''path'' cannot be converted * to the system path type. *) const func file: open (in string: path, in string: mode) is func result var file: newFile is STD_NULL; local var clib_file: open_file is CLIB_NULL_FILE; var external_file: new_file is external_file.value; begin open_file := openClibFile(path, mode); if open_file <> CLIB_NULL_FILE then new_file.ext_file := open_file; new_file.name := path; newFile := toInterface(new_file); end if; end func; const func file: openNullDevice is func result var file: nullDeviceFile is STD_NULL; local var clib_file: open_file is CLIB_NULL_FILE; var external_file: new_file is external_file.value; begin open_file := openNullDeviceClibFile; if open_file <> CLIB_NULL_FILE then new_file.ext_file := open_file; nullDeviceFile := toInterface(new_file); end if; end func; (** * Close an external_file. * @exception FILE_ERROR A system function returns an error. *) const proc: close (inout external_file: aFile) is func begin close(aFile.ext_file); aFile.ext_file := CLIB_NULL_FILE; end func; (** * Forces that all buffered data of ''outFile'' is sent to its destination. * This causes data to be sent to the file system of the OS. *) const proc: flush (in external_file: outFile) is func begin flush(outFile.ext_file); end func; (** * Write a [[string]] to an external_file. * @exception FILE_ERROR A system function returns an error. * @exception RANGE_ERROR The string contains a character that does * not fit into a byte ('\0;' to '\255;' fit into a byte). *) const proc: write (in external_file: outFile, in string: stri) is func begin write(outFile.ext_file, stri); end func; const proc: moveLeft (in external_file: outFile, in string: stri) is func begin write(outFile.ext_file, "\b" mult length(stri)); end func; const proc: erase (in external_file: outFile, in string: stri) is func begin write(outFile.ext_file, " " mult length(stri)); end func; (** * Read a character from an external_file. * @return the character read, or [[char#EOF|EOF]] at the end of the file. *) const func char: getc (in external_file: inFile) is return getc(inFile.ext_file); (** * Read a [[string]] with a maximum length from an external_file. * @return the string read. * @exception RANGE_ERROR The parameter ''maxLength'' is negative. * @exception MEMORY_ERROR Not enough memory to represent the result. * @exception FILE_ERROR A system function returns an error. *) const func string: gets (in external_file: inFile, in integer: maxLength) is return gets(inFile.ext_file, maxLength); (** * Read a [[string]] from ''inFile'' until the ''terminator'' character is found. * If a ''terminator'' is found the string before the ''terminator'' is * returned and the ''terminator'' character is assigned to inFile.bufferChar. * The file position is advanced after the ''terminator'' character. * If no ''terminator'' is found the rest of ''inFile'' is returned and * [[char#EOF|EOF]] is assigned to the inFile.bufferChar. When the function * is left inFile.bufferChar contains either ''terminator'' or [[char#EOF|EOF]]. * @param inFile File from which the string is read. * @param terminator Character which terminates the string. * @return the string read without the ''terminator'' or the rest of the * file if no ''terminator'' is found. * @exception MEMORY_ERROR Not enough memory to represent the result. * @exception FILE_ERROR A system function returns an error. *) const func string: getTerminatedString (inout external_file: inFile, in char: terminator) is return terminated_read(inFile.ext_file, terminator, inFile.bufferChar); (** * Read a word from an external_file. * Before reading the word it skips spaces and tabs. The function * accepts words ending with ' ', '\t', '\n', "\r\n" or [[char#EOF|EOF]]. * The word ending characters are not copied into the string. * That means that the '\r' of a "\r\n" sequence is silently removed. * When the function is left inFile.bufferChar contains ' ', * '\t', '\n' or [[char#EOF|EOF]]. * @return the word read. * @exception MEMORY_ERROR Not enough memory to represent the result. * @exception FILE_ERROR A system function returns an error. *) const func string: getwd (inout external_file: inFile) is return word_read(inFile.ext_file, inFile.bufferChar); (** * Read a line from an external_file. * The function accepts lines ending with '\n', "\r\n" or [[char#EOF|EOF]]. * The line ending characters are not copied into the string. * That means that the '\r' of a "\r\n" sequence is silently removed. * When the function is left inFile.bufferChar contains '\n' or * [[char#EOF|EOF]]. * @return the line read. * @exception MEMORY_ERROR Not enough memory to represent the result. * @exception FILE_ERROR A system function returns an error. *) const func string: getln (inout external_file: inFile) is return line_read(inFile.ext_file, inFile.bufferChar); (** * 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 external_file: inFile) is return eof(inFile.ext_file); (** * 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 external_file: inFile) is return hasNext(inFile.ext_file); (** * Determine if at least one character can be read without blocking. * Blocking means that ''getc'' would wait until a character is * received. Blocking can last for a period of unspecified length. * Regular files do not block. * @return TRUE if ''getc'' would not block, FALSE otherwise. *) const func boolean: inputReady (in external_file: inFile) is return inputReady(inFile.ext_file); (** * Obtain the length of an external_file. * The file length is measured in bytes. * @return the size of the given file. * @exception RANGE_ERROR The file length does not fit into * an integer value. * @exception FILE_ERROR A system function returns an error or the * file length reported by the system is negative. *) const func integer: length (in external_file: aFile) is return length(aFile.ext_file); (** * Truncate ''aFile'' to the given ''length''. * If the file previously was larger than ''length'', the extra data is lost. * If the file previously was shorter, it is extended, and the extended * part is filled with null bytes ('\0;'). * @param aFile File to be truncated. * @param length Requested length of ''aFile'' in bytes. * @exception RANGE_ERROR The requested length is negative or * the length is not representable in the type * used by the system function. * @exception FILE_ERROR A system function returns an error. *) const proc: truncate (in external_file: aFile, in integer: length) is func begin truncate(aFile.ext_file, length); end func; (** * 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, if ''aFile'' is seekable, FALSE otherwise. *) const func boolean: seekable (in external_file: aFile) is return seekable(aFile.ext_file); (** * Set the current file position. * The file position is measured in bytes from the start of the file. * The first byte in the file has the position 1. * @exception RANGE_ERROR The file position is negative or zero or * the file position is not representable in the system * file position type. * @exception FILE_ERROR A system function returns an error. *) const proc: seek (in external_file: aFile, in integer: position) is func begin seek(aFile.ext_file, position); end func; (** * Obtain the current file position. * The file position is measured in bytes from the start of the file. * The first byte in the file has the position 1. * @return the current file position. * @exception RANGE_ERROR The file position does not fit into * an integer value. * @exception FILE_ERROR A system function returns an error or the * file position reported by the system is negative. *) const func integer: tell (in external_file: aFile) is return tell(aFile.ext_file); const func boolean: fileOpenSucceeds (in string: path) is func result var boolean: fileOpenSucceeds is FALSE; local var file: a_file is STD_NULL; begin a_file := open(path, "r"); if a_file <> STD_NULL then close(a_file); fileOpenSucceeds := TRUE; end if; end func;