
(********************************************************************)
(*                                                                  *)
(*  null_file.s7i  Base implementation type for all files           *)
(*  Copyright (C) 1989 - 2011  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 "file.s7i";


(**
 *  Base implementation type for the [[file]] interface.
 *  A ''null_file'' discards all data written to it. Reads from a
 *  ''null_file'' always yield EOF immediately. A ''null_file'' is
 *  not seekable. The functions [[#length(in_null_file)|length]],
 *  [[#seek(in_null_file,in_integer)|seek]] and [[#tell(in_null_file)|tell]]
 *  raise FILE_ERROR. The functions [[#getc(inout_null_file)|getc]],
 *  [[#getwd(inout_null_file)|getwd]] and [[#getln(inout_null_file)|getln]]
 *  are written such that a derived type can use them. Derived
 *  types of ''null_file'' need to override the functions
 *  [[#write(in_null_file,in_string)|write]], [[#gets(in_null_file,in_integer)|gets]],
 *  [[#eof(in_null_file)|eof]] and [[#hasNext(in_null_file)|hasNext]].
 *  A derived type, which represents a seekable file, needs to
 *  override the functions ''length'', ''seek'' and ''tell''.
 *)
const type: null_file is new struct
    var char: bufferChar is '\n';
    var boolean: io_empty is FALSE;
    var boolean: io_ok is TRUE;
  end struct;

type_implements_interface(null_file, file);

const func boolean: (in null_file param) = (in null_file param)   is action "ENU_EQ";
const func boolean: (in null_file param) <> (in null_file param)  is action "ENU_NE";


(**
 *  Standard null file.
 *  The file STD_NULL is used to initialize [[file]] variables
 *  and as result of [[external_file#open(in_string,in_string)|open]],
 *  when a ''file'' cannot be opened.
 *)
var null_file: STD_NULL is null_file.value;


const file: (attr file) . value is STD_NULL;


(**
 *  Write a string to a null_file.
 *  Derived types of null_file need to override this function.
 *)
const proc: write (in null_file: aFile, in string: stri) is noop;


(**
 *  Read a string with a maximum length from a null_file.
 *  Derived types of null_file need to override this function.
 *  @return the empty string ("").
 *)
const string: gets (in null_file: aFile, in integer: maxLength) is "";


(**
 *  Read a character from a null_file.
 *  This function is based on the gets function. Therefore it is
 *  useful for derived types of null_file. For the null_file itself
 *  it always returns [[char#EOF|EOF]].
 *  @return the character read, or EOF at the end of the file.
 *)
const func char: getc (inout null_file: aFile) is func
  result
    var char: charRead is ' ';
  local
    var string: buffer is "";
  begin
    buffer := gets(aFile, 1);
    if buffer = "" then
      charRead := EOF;
    else
      charRead := buffer[1];
    end if;
  end func;


(**
 *  Read a word from a null_file.
 *  Before reading the word it skips spaces and tabs. The function
 *  accepts words ending with " ", "\t", "\n" or [[char#EOF|EOF]]. The
 *  word ending characters are not copied into the string. When the
 *  function is left inFile.bufferChar contains ' ', '\t', '\n' or EOF.
 *  This function is based on the gets function. Therefore it is
 *  useful for derived types of null_file. For the null_file itself
 *  it always returns "" and assigns EOF to inFile.bufferChar.
 *  @return the word read.
 *)
const func string: getwd (inout null_file: aFile) is func
  result
    var string: stri is "";
  local
    var string: buffer is "";
  begin
    repeat
      buffer := gets(aFile, 1);
    until buffer <> " " and buffer <> "\t";
    while buffer <> " " and buffer <> "\t" and
        buffer <> "\n" and buffer <> "" do
      stri &:= buffer;
      buffer := gets(aFile, 1);
    end while;
    if buffer = "" then
      aFile.bufferChar := EOF;
    else
      aFile.bufferChar := buffer[1];
    end if;
  end func;


(**
 *  Read a line from a null_file.
 *  The function accepts lines ending with "\n" or [[char#EOF|EOF]].
 *  The line ending characters are not copied into the string. When
 *  the function is left inFile.bufferChar contains '\n' or EOF.
 *  This function is based on the gets function. Therefore it is
 *  useful for derived types of null_file. For the null_file itself
 *  it always returns "" and assigns EOF to inFile.bufferChar.
 *  @return the line read.
 *)
const func string: getln (inout null_file: aFile) is func
  result
    var string: stri is "";
  local
    var string: buffer is "";
  begin
    buffer := gets(aFile, 1);
    while buffer <> "\n" and buffer <> "" do
      stri &:= buffer;
      buffer := gets(aFile, 1);
    end while;
    if buffer = "" then
      aFile.bufferChar := EOF;
    else
      aFile.bufferChar := buffer[1];
    end if;
  end func;


const proc: writeln (inout null_file: aFile) is func
  begin
    write(aFile, "\n");
  end func;


(**
 *  Determine the end-of-file indicator.
 *  Derived types of null_file need to override this function.
 *  @return TRUE, since a null_file is always at end-of-file.
 *)
const boolean: eof (in null_file: aFile) is TRUE;


(**
 *  Determine if at least one character can be read successfully.
 *  Derived types of null_file need to override this function.
 *  @return FALSE, since the next ''getc'' will always return EOF.
 *)
const boolean: hasNext (in null_file: aFile) is FALSE;


const func boolean: eoln (in null_file: aFile) is
  return aFile.bufferChar = '\n';


(**
 *  Obtain the length of a file.
 *  A ''null_file'' is not seekable, therefore FILE_ERROR is raised.
 *  Derived types of null_file need to override this function.
 *  @return nothing, because FILE_ERROR is always raised.
 *  @exception FILE_ERROR Is always raised, because a ''null_file'' is
 *             not seekable.
 *)
const func integer: length (in null_file: aFile) is func
  result
    var integer: length is 0;
  begin
    raise FILE_ERROR;
  end func;


(**
 *  Set the current file position.
 *  A ''null_file'' is not seekable, therefore FILE_ERROR is raised.
 *  When a derived type is seekable it needs to override this function.
 *  @exception FILE_ERROR Is always raised, because a ''null_file'' is
 *             not seekable.
 *)
const proc: seek (in null_file: aFile, in integer: pos) is func
  begin
    raise FILE_ERROR;
  end func;


(**
 *  Obtain the current file position.
 *  A ''null_file'' is not seekable, therefore FILE_ERROR is raised.
 *  When a derived type is seekable it needs to override this function.
 *  @return nothing, because FILE_ERROR is always raised.
 *  @exception FILE_ERROR Is always raised, because a ''null_file'' is
 *             not seekable.
 *)
const func integer: tell (in null_file: aFile) is func
  result
    var integer: position is 0;
  begin
    raise FILE_ERROR;
  end func;


const proc: backSpace (in null_file: aFile) is noop;


(**
 *  Close a null_file.
 *  Closing a null_file has no effect.
 *)
const proc: close (in null_file: aFile) is noop;


(**
 *  Forces that all buffered data is sent to its destination.
 *  Flushing a null_file has no effect.
 *)
const proc: flush (in null_file: aFile) is noop;
