(********************************************************************)
(*                                                                  *)
(*  line.s7i      Filter file which reads the input linewise        *)
(*  Copyright (C) 1992, 1993, 1994, 2005  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.                       *)
(*                                                                  *)
(********************************************************************)


(**
 *  [[file|File]] implementation type which reads ''baseFile'' linewise.
 *)
const type: lineFile is sub null_file struct
    var file: baseFile is STD_NULL;
    var string: line is "";
  end struct;


(**
 *  Open a lineFile to filter ''aFile'' linewise.
 *  Until a line is finished with '\n' or [[char#EOF|EOF]] the
 *  characters in the line can be corrected with backspace ('\b').
 *  @return the file opened.
 *)
const func file: openLine (in file: aFile) is func
  result
    var file: newFile is STD_NULL;
  local
    var lineFile: new_lineFile is lineFile.value;
  begin
    new_lineFile.baseFile := aFile;
    newFile := toInterface(new_lineFile);
  end func;


(**
 *  Write a string to a ''lineFile''.
 *)
const proc: write (inout lineFile: outFile, in string: stri) is func
  begin
    outFile.line &:= stri;
  end func;


(**
 *  Write end-of-line to a ''lineFile''.
 *)
const proc: writeln (inout lineFile: outFile) is func
  begin
    writeln(outFile.baseFile, outFile.line);
    flush(outFile.baseFile);
    outFile.line := "";
  end func;


const proc: flush (inout lineFile: outFile) is func
  begin
    write(outFile.baseFile, outFile.line);
    flush(outFile.baseFile);
    outFile.line := "";
  end func;


(**
 *  Read a line from a ''lineFile''.
 *  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 the lineFile.bufferChar contains '\n' or
 *  [[char#EOF|EOF]].
 *  @return the line read.
 *)
const func string: getln (inout lineFile: inFile) is func
  result
    var string: stri is "";
  local
    var char: ch is ' ';
  begin
    if inFile.line = "" then
      ch := getc(inFile.baseFile);
      while ch <> '\n' and ch <> EOF do
        if ch = '\b' then
          if stri <> "" then
            stri := stri[ .. pred(length(stri))];
          end if;
        else
          stri &:= str(ch);
        end if;
        ch := getc(inFile.baseFile);
      end while;
      inFile.bufferChar := ch;
    else
      if inFile.line[length(inFile.line)] = '\n' then
        stri := inFile.line[ .. pred(length(inFile.line))];
        inFile.bufferChar := '\n';
      else
        stri := inFile.line;
        inFile.bufferChar := EOF;
      end if;
      inFile.line := "";
    end if;
  end func;


(**
 *  Read a character from a lineFile.
 *  @return the character read, or [[char#EOF|EOF]] at the end of the file.
 *)
const func char: getc (inout lineFile: inFile) is func
  result
    var char: charRead is ' ';
  begin
    if inFile.line = "" then
      inFile.line := getln(inFile) & "\n";
    end if;
    if inFile.line = "\n" and inFile.bufferChar = EOF then
      charRead := EOF;
    else
      charRead := inFile.line[1];
      inFile.line := inFile.line[2 .. ];
    end if;
  end func;


(**
 *  Read a string with a maximum length from a lineFile.
 *  @return the string read.
 *  @exception RANGE_ERROR The parameter ''maxLength'' is negative.
 *)
const func string: gets (inout lineFile: inFile, in integer: maxLength) is func
  result
    var string: striRead is "";
  begin
    if maxLength <= 0 then
      if maxLength <> 0 then
        raise RANGE_ERROR;
      end if;
    else
      while length(inFile.line) < maxLength and inFile.bufferChar <> EOF do
        inFile.line &:= getln(inFile) & "\n";
      end while;
      striRead := inFile.line[ .. maxLength];
      inFile.line := inFile.line[succ(maxLength) .. ];
    end if;
  end func;