
(********************************************************************)
(*                                                                  *)
(*  utf8.s7i      File implementation type for UTF-8 files          *)
(*  Copyright (C) 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.                       *)
(*                                                                  *)
(********************************************************************)


include "external_file.s7i";


(**
 *  [[file|File]] implementation type for UTF-8 files.
 *  This type supports UTF-8 encoded sequential files of the
 *  operating system. UTF-8 files are seekable, therefore they
 *  support the functions functions ''length'', ''seek'' and ''tell''.
 *)
const type: utf8_file is sub external_file struct
  end struct;


const func char: utf8_getc (ref PRIMITIVE_FILE param) is                          action "UT8_GETC";
const func string: utf8_gets (ref PRIMITIVE_FILE param, ref integer param) is     action "UT8_GETS";
const func string: utf8_word_read (ref PRIMITIVE_FILE param, inout char param) is action "UT8_WORD_READ";
const func string: utf8_line_read (ref PRIMITIVE_FILE param, inout char param) is action "UT8_LINE_READ";
const proc: utf8_write (ref PRIMITIVE_FILE param, in string param) is             action "UT8_WRITE";
const proc: utf8_seek (ref PRIMITIVE_FILE param, in integer param) is             action "UT8_SEEK";


const func utf8_file: malloc (in utf8_file: anUtf8File) is func
  result
    var utf8_file: newFile is utf8_file.value;
  begin
    newFile := anUtf8File;
  end func;


(**
 *  Opens an Unicode file which uses the UTF-8 encoding.
 *  @return the file opened, or STD_NULL if it could not be opened.
 *  @exception RANGE_ERROR Illegal 'mode' or 'fileName' is not in
 *             standard path representation.
 *)
const func file: open_utf8 (in string: fileName, in string: mode) is func
  result
    var file: newFile is STD_NULL;
  local
    var PRIMITIVE_FILE: open_file is PRIMITIVE_NULL_FILE;
    var utf8_file: new_file is utf8_file.value;
  begin
    open_file := PRIMITIVE_FILE_OPEN(fileName, mode);
    if open_file <> PRIMITIVE_NULL_FILE then
      new_file.ext_file := open_file;
      new_file.name := fileName;
      newFile := malloc(new_file);
    end if;
  end func;


(**
 *  Write a string to an UTF-8 file.
 *  @exception FILE_ERROR A system function returns an error.
 *)
const proc: write (in utf8_file: aFile, in string: stri) is func
  begin
    utf8_write(aFile.ext_file, stri);
  end func;


(**
 *  Write the newline character to an UTF-8 file.
 *  @exception FILE_ERROR A system function returns an error.
 *)
const proc: writeln (in utf8_file: aFile) is func
  begin
    utf8_write(aFile.ext_file, "\n");
  end func;


const proc: backSpace (in utf8_file: aFile) is func
  begin
    utf8_write(aFile.ext_file, "\b \b");
  end func;


(**
 *  Read a character from an UTF-8 file.
 *  @return the character read, or EOF at the end of the file.
 *  @exception RANGE_ERROR The file contains an illegal encoding.
 *)
const func char: getc (in utf8_file: aFile) is
  return utf8_getc(aFile.ext_file);


(**
 *  Return a string read with a maximum length from an UTF-8 file.
 *  @return the string read.
 *  @exception RANGE_ERROR The length is negative or the file
 *             contains an illegal encoding.
 *)
const func string: gets (in utf8_file: aFile, in integer: maxLength) is
  return utf8_gets(aFile.ext_file, maxLength);


(**
 *  Read a word from an UTF-8 file.
 *  Before reading the word it skips spaces and tabs. The function
 *  accepts words ending with " ", "\t", "\n", "\r\n" or 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 EOF.
 *  @return the word read.
 *  @exception RANGE_ERROR The file contains an illegal encoding.
 *  @exception MEMORY_ERROR Not enough memory to represent the result.
 *  @exception FILE_ERROR A system function returns an error.
 *)
const func string: getwd (inout utf8_file: aFile) is
  return utf8_word_read(aFile.ext_file, aFile.bufferChar);


(**
 *  Read a line from an UTF-8 file.
 *  The function accepts lines ending with "\n", "\r\n" or 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
 *  EOF.
 *  @return the line read.
 *  @exception RANGE_ERROR The file contains an illegal encoding.
 *  @exception MEMORY_ERROR Not enough memory to represent the result.
 *  @exception FILE_ERROR A system function returns an error.
 *)
const func string: getln (inout utf8_file: aFile) is
  return utf8_line_read(aFile.ext_file, aFile.bufferChar);


(**
 *  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.
 *  When the file position would be in the middle of an UTF-8 encoded
 *  character the position is advanced to the beginning of the next
 *  UTF-8 character.
 *  @exception RANGE_ERROR The file position is negative or zero.
 *  @exception FILE_ERROR A system function returns an error.
 *)
const proc: seek (in utf8_file: aFile, in integer: pos) is func
  begin
    utf8_seek(aFile.ext_file, pos);
  end func;


(* System STD_UTF8_IN, STD_UTF8_OUT and STD_UTF8_ERR files *)


const func utf8_file: INIT_STD_UTF8_FILE (ref PRIMITIVE_FILE: primitive_file,
    in string: fileName) is func
  result
    var utf8_file: aFile is utf8_file.value;
  begin
    aFile.ext_file := primitive_file;
    aFile.name := fileName;
  end func;


(**
 *  UTF-8 version of the standard input file of the operating system.
 *  Reading from ''STD_UTF8_IN'' can be done with e.g.:
 *   read(STD_UTF8_IN, aVariable);
 *  It is also possible to redirect ''STD_UTF8_IN'' to the default
 *  input of ''read'' with:
 *   IN := STD_UTF8_IN;
 *  Afterwards
 *   read(aVariable);
 *  reads from ''STD_UTF8_IN''.
 *)
var utf8_file: STD_UTF8_IN is  INIT_STD_UTF8_FILE(PRIMITIVE_INPUT,  "STD_UTF8_IN");


(**
 *  UTF-8 version of the standard output file of the operating system.
 *  Writing to ''STD_UTF8_OUT'' can be done with e.g.:
 *   write(STD_UTF8_OUT, something);
 *  It is also possible to redirect the default output of ''write''
 *  to ''STD_UTF8_OUT'' with:
 *   OUT := STD_UTF8_OUT;
 *  Afterwards
 *   write(something);
 *  writes to ''STD_UTF8_OUT''.
 *)
var utf8_file: STD_UTF8_OUT is INIT_STD_UTF8_FILE(PRIMITIVE_OUTPUT, "STD_UTF8_OUT");


(**
 *  UTF-8 version of the standard error file of the operating system.
 *)
var utf8_file: STD_UTF8_ERR is INIT_STD_UTF8_FILE(PRIMITIVE_ERROR,  "STD_UTF8_ERR");
