(********************************************************************)
(*                                                                  *)
(*  osfiles.s7i   Functions to handle operating system files        *)
(*  Copyright (C) 1989 - 2017, 2019 - 2021, 2023  Thomas Mertes     *)
(*                2024  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 "enable_io.s7i";
include "time.s7i";
include "bigint.s7i";
include "filesys.s7i";


const proc: GET_ATIME (in string: filePath, inout integer: year,
    inout integer: month, inout integer: day, inout integer: hour,
    inout integer: minute, inout integer: second, inout integer: micro_second,
    inout integer: timeZone, inout boolean: daylightSavingTime)  is action "CMD_GET_ATIME";

const proc: GET_CTIME (in string: filePath, inout integer: year,
    inout integer: month, inout integer: day, inout integer: hour,
    inout integer: minute, inout integer: second, inout integer: micro_second,
    inout integer: timeZone, inout boolean: daylightSavingTime)  is action "CMD_GET_CTIME";

const proc: GET_MTIME (in string: filePath, inout integer: year,
    inout integer: month, inout integer: day, inout integer: hour,
    inout integer: minute, inout integer: second, inout integer: micro_second,
    inout integer: timeZone, inout boolean: daylightSavingTime)  is action "CMD_GET_MTIME";

const proc: SET_ATIME (in string: filePath, in integer: year, in integer: month,
    in integer: day, in integer: hour, in integer: minute, in integer: second,
    in integer: micro_second, in integer: timeZone)              is action "CMD_SET_ATIME";

const proc: SET_MTIME (in string: filePath, in integer: year, in integer: month,
    in integer: day, in integer: hour, in integer: minute, in integer: second,
    in integer: micro_second, in integer: timeZone)              is action "CMD_SET_MTIME";

const proc: GET_ATIME_OF_SYMLINK (in string: filePath, inout integer: year,
    inout integer: month, inout integer: day, inout integer: hour,
    inout integer: minute, inout integer: second, inout integer: micro_second,
    inout integer: timeZone, inout boolean: daylightSavingTime)  is action "CMD_GET_ATIME_OF_SYMLINK";

const proc: GET_MTIME_OF_SYMLINK (in string: filePath, inout integer: year,
    inout integer: month, inout integer: day, inout integer: hour,
    inout integer: minute, inout integer: second, inout integer: micro_second,
    inout integer: timeZone, inout boolean: daylightSavingTime)  is action "CMD_GET_MTIME_OF_SYMLINK";

const proc: SET_MTIME_OF_SYMLINK (in string: filePath, in integer: year, in integer: month,
    in integer: day, in integer: hour, in integer: minute, in integer: second,
    in integer: micro_second, in integer: timeZone)              is action "CMD_SET_MTIME_OF_SYMLINK";


(**
 *  Determine the filenames in a directory.
 *  The function does follow symbolic links.
 *  The files "." and ".." are excluded from the result.
 *  Note that the function returns only the filenames.
 *  Additional information must be obtained using other calls.
 *  @return a string-array containing the filenames in the directory.
 *  @exception MEMORY_ERROR Not enough memory to convert ''dirPath''
 *             to the system path type or not enough memory to
 *             represent the result ''string array''.
 *  @exception RANGE_ERROR ''dirPath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file described with ''dirPath'' does not
 *             exist, or it is not a directory, or a system function
 *             returns an error.
 *)
const func array string: readDir (in string: dirPath)        is action "CMD_READ_DIR";


(**
 *  Determine the type of a file.
 *  The function does follow symbolic links. If the chain of
 *  symbolic links is too long the function returns ''FILE_SYMLINK''.
 *  If a symbolic link refers to a place where the permission
 *  is denied the function returns ''FILE_SYMLINK''.
 *  A return value of ''FILE_ABSENT'' does not imply that a file
 *  with this name can be created, since missing directories and
 *  invalid file names cause also ''FILE_ABSENT''.
 *  @return the type of the file.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation.
 *  @exception FILE_ERROR The system function returns an error other
 *             than ENOENT, ENOTDIR, ENAMETOOLONG or EACCES.
 *)
const func fileType: fileType (in string: filePath)          is action "CMD_FILETYPE";


(**
 *  Determine the type of a file.
 *  The function does not follow symbolic links. Therefore it may
 *  return ''FILE_SYMLINK''. A return value of ''FILE_ABSENT'' does
 *  not imply that a file with this name can be created, since missing
 *  directories and invalid file names cause also ''FILE_ABSENT''.
 *  @return the type of the file.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation.
 *  @exception FILE_ERROR The system function returns an error other
 *             than ENOENT, ENOTDIR, ENAMETOOLONG or EACCES.
 *)
const func fileType: fileTypeSL (in string: filePath)        is action "CMD_FILETYPE_SL";


(**
 *  Determine the size of a file.
 *  The function follows symbolic links. The file size is measured in bytes.
 *  For directories, fifos and sockets a size of 0 is returned.
 *  @return the size of the file.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception RANGE_ERROR The file size is not representable as integer.
 *  @exception FILE_ERROR It was not possible to determine the file size.
 *)
const func integer: fileSize (in string: filePath)           is action "CMD_FILESIZE";


(**
 *  Determine the size of a file.
 *  The function follows symbolic links. The file size is measured in bytes.
 *  For directories, fifos and sockets a size of 0 is returned.
 *  @return the size of the file.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR It was not possible to determine the file size.
 *)
const func bigInteger: bigFileSize (in string: filePath)     is action "CMD_BIG_FILESIZE";


(**
 *  Determine the file mode (permissions) of a file.
 *  The function follows symbolic links.
 *  @return the file mode.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or a system function returns an error.
 *)
const func fileMode: getFileMode (in string: filePath)       is action "CMD_GET_FILE_MODE";


# The function fileMode() is deprecated. Use getFileMode() instead.
const func fileMode: fileMode (in string: filePath) is
  return getFileMode(filePath);


(**
 *  Change the file mode (permissions) of a file.
 *  The function follows symbolic links.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or a system function returns an error.
 *)
const proc: setFileMode (in string: filePath, in fileMode: mode) is action "CMD_SET_FILE_MODE";


(**
 *  Determine the access time of a file.
 *  The function follows symbolic links.
 *  @return the access time of the file.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or a system function returns an error.
 *)
const func time: getATime (in string: filePath) is func
  result
    var time: accessTime is time.value;
  begin
    GET_ATIME(filePath, accessTime.year, accessTime.month, accessTime.day,
        accessTime.hour, accessTime.minute, accessTime.second,
        accessTime.micro_second, accessTime.timeZone,
        accessTime.daylightSavingTime);
  end func;


(**
 *  Set the access time of a file.
 *  The function follows symbolic links.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception RANGE_ERROR ''aTime'' is invalid or it cannot be
 *             converted to the system file time.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or a system function returns an error.
 *)
const proc: setATime (in string: filePath, in time: aTime) is func
  begin
    SET_ATIME(filePath, aTime.year, aTime.month, aTime.day,
        aTime.hour, aTime.minute, aTime.second,
        aTime.micro_second, aTime.timeZone);
  end func;


(**
 *  Determine the change time of a file.
 *  The function follows symbolic links.
 *  @return the change time of the file.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or a system function returns an error.
 *)
const func time: getCTime (in string: filePath) is func
  result
    var time: changeTime is time.value;
  begin
    GET_CTIME(filePath, changeTime.year, changeTime.month, changeTime.day,
        changeTime.hour, changeTime.minute, changeTime.second,
        changeTime.micro_second, changeTime.timeZone,
        changeTime.daylightSavingTime);
  end func;


(**
 *  Determine the modification time of a file.
 *  The function follows symbolic links.
 *  @return the modification time of the file.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or a system function returns an error.
 *)
const func time: getMTime (in string: filePath) is func
  result
    var time: modificationTime is time.value;
  begin
    GET_MTIME(filePath,
        modificationTime.year, modificationTime.month, modificationTime.day,
        modificationTime.hour, modificationTime.minute, modificationTime.second,
        modificationTime.micro_second, modificationTime.timeZone,
        modificationTime.daylightSavingTime);
  end func;


(**
 *  Set the modification time of a file.
 *  The function follows symbolic links.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception RANGE_ERROR ''mTime'' is invalid or it cannot be
 *             converted to the system file time.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or a system function returns an error.
 *)
const proc: setMTime (in string: filePath, in time: mTime) is func
  begin
    SET_MTIME(filePath, mTime.year, mTime.month, mTime.day,
        mTime.hour, mTime.minute, mTime.second,
        mTime.micro_second, mTime.timeZone);
  end func;


(**
 *  Determine the name of the owner (UID) of a file.
 *  The function follows symbolic links.
 *  @return the name of the file owner.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or a system function returns an error.
 *)
const func string: getOwner (in string: filePath)            is action "CMD_GET_OWNER";


(**
 *  Set the owner of a file.
 *  The function follows symbolic links.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or a system function returns an error.
 *)
const proc: setOwner (in string: filePath, in string: owner) is action "CMD_SET_OWNER";


(**
 *  Determine the name of the group (GID) to which a file belongs.
 *  The function follows symbolic links.
 *  @return the name of the file group.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or a system function returns an error.
 *)
const func string: getGroup (in string: filePath)            is action "CMD_GET_GROUP";


(**
 *  Set the group of a file.
 *  The function follows symbolic links.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or a system function returns an error.
 *)
const proc: setGroup (in string: filePath, in string: group) is action "CMD_SET_GROUP";


(**
 *  Determine the file mode (permissions) of a symbolic link.
 *  The function only works for symbolic links and does not follow the
 *  symbolic link.
 *  @return the file mode.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or it is not a symbolic link, or a system function
 *             returns an error.
 *)
const func fileMode: getFileMode (in string: filePath, SYMLINK) is action "CMD_GET_FILE_MODE_OF_SYMLINK";


(**
 *  Determine the access time of a symbolic link.
 *  The function only works for symbolic links and does not follow the
 *  symbolic link.
 *  @return the access time of the symbolic link.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or it is not a symbolic link, or a system function
 *             returns an error.
 *)
const func time: getATime (in string: filePath, SYMLINK) is func
  result
    var time: accessTime is time.value;
  begin
    GET_ATIME_OF_SYMLINK(filePath,
        accessTime.year, accessTime.month, accessTime.day,
        accessTime.hour, accessTime.minute, accessTime.second,
        accessTime.micro_second, accessTime.timeZone,
        accessTime.daylightSavingTime);
  end func;


(**
 *  Determine the modification time of a symbolic link.
 *  The function only works for symbolic links and does not follow the
 *  symbolic link.
 *  @return the modification time of the symbolic link.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or it is not a symbolic link, or a system function
 *             returns an error.
 *)
const func time: getMTime (in string: filePath, SYMLINK) is func
  result
    var time: modificationTime is time.value;
  begin
    GET_MTIME_OF_SYMLINK(filePath,
        modificationTime.year, modificationTime.month, modificationTime.day,
        modificationTime.hour, modificationTime.minute, modificationTime.second,
        modificationTime.micro_second, modificationTime.timeZone,
        modificationTime.daylightSavingTime);
  end func;


(**
 *  Set the modification time of a symbolic link.
 *  The function only works for symbolic links and does not follow the
 *  symbolic link.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception RANGE_ERROR ''mTime'' is invalid or it cannot be
 *             converted to the system file time.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or it is not a symbolic link, or a system function
 *             returns an error.
 *)
const proc: setMTime (in string: filePath, in time: mTime, SYMLINK) is func
  begin
    SET_MTIME_OF_SYMLINK(filePath, mTime.year, mTime.month, mTime.day,
        mTime.hour, mTime.minute, mTime.second,
        mTime.micro_second, mTime.timeZone);
  end func;


(**
 *  Determine the name of the owner (UID) of a symbolic link.
 *  The function only works for symbolic links and does not follow the
 *  symbolic link.
 *  @return the name of the file owner.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or it is not a symbolic link, or a system function
 *             returns an error.
 *)
const func string: getOwner (in string: filePath, SYMLINK)   is action "CMD_GET_OWNER_OF_SYMLINK";


(**
 *  Set the owner of a symbolic link.
 *  The function only works for symbolic links and does not follow the
 *  symbolic link.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or it is not a symbolic link, or a system function
 *             returns an error.
 *)
const proc: setOwner (in string: filePath, in string: owner, SYMLINK) is action "CMD_SET_OWNER_OF_SYMLINK";


(**
 *  Determine the name of the group (GID) to which a symbolic link belongs.
 *  The function only works for symbolic links and does not follow the
 *  symbolic link.
 *  @return the name of the file group.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or it is not a symbolic link, or a system function
 *             returns an error.
 *)
const func string: getGroup (in string: filePath, SYMLINK)   is action "CMD_GET_GROUP_OF_SYMLINK";


(**
 *  Set the group of a symbolic link.
 *  The function only works for symbolic links and does not follow the
 *  symbolic link.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or it is not a symbolic link, or a system function
 *             returns an error.
 *)
const proc: setGroup (in string: filePath, in string: group, SYMLINK) is action "CMD_SET_GROUP_OF_SYMLINK";


(**
 *  Reads the destination of a symbolic link.
 *  This function reads the link destination stored in the file system.
 *  Symbolic links can be relative or absolute. Relative symbolic links
 *  are relative to their location in the file system and not relative to
 *  the current working directory.
 *   readLink("/aDir/symbolicLink")            returns e.g. "../linkDest"
 *   readLink("/aDir/symbolicLink", ABSOLUTE)  returns e.g. "/aDir/linkDest"
 *  @param filePath Relative or absolute path of a symbolic link.
 *  @return The destination referred by the symbolic link.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type or not enough memory to
 *             represent the result [[string]].
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation, or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or it is not a symbolic link, or a system function
 *             returns an error.
 *)
const func string: readLink (in string: filePath)            is action "CMD_READ_LINK";


(**
 *  Reads the absolute destination path of a symbolic link.
 *  The symbolic links stored in the file system can be relative or
 *  absolute. Relative symbolic links are relative to their location in
 *  the file system and not relative to the current working directory.
 *  For a relative symbolic link ''filePath'' is converted to an
 *  absolute path, and the symbolic link is concatenated to this path.
 *   readLink("/aDir/symbolicLink")            returns e.g. "../linkDest"
 *   readLink("/aDir/symbolicLink", ABSOLUTE)  returns e.g. "/aDir/linkDest"
 *  @param filePath Relative or absolute path of a symbolic link.
 *  @return The absolute destination path referred by the symbolic link.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type or not enough memory to
 *             represent the result [[string]].
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist, or it is not a symbolic link, or a system function
 *             returns an error.
 *)
const func string: readLink (in string: filePath, ABSOLUTE)  is action "CMD_READ_LINK_ABSOLUTE";



(**
 *  Get the final path that functions like getMTime() and open() would use.
 *  If ''filePath'' is not a symbolic link it is returned. For a symbolic link
 *  the function follows the symbolic link chain until the path is not a
 *  symbolic link again. The final path may refer to a non-existing file.
 *  @param filePath Relative or absolute path.
 *  @return The final path after possibly following a symbolic link chain.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath''
 *             to the system path type or not enough memory to
 *             represent the result string.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file described with ''filePath'' does not
 *             exist or a system function returns an error.
 *)
const func string: finalPath (in string: filePath)           is action "CMD_FINAL_PATH";


(**
 *  Create a symbolic link.
 *  The symbolic link ''symlinkPath'' will refer to ''targetPath'' afterwards.
 *  The function does not follow symbolic links.
 *  @param symlinkPath Name of the symbolic link to be created.
 *  @param targetPath String to be contained in the symbolic link.
 *  @exception MEMORY_ERROR Not enough memory to convert ''symlinkPath'' or
 *             ''targetPath'' to the system path type.
 *  @exception RANGE_ERROR ''symlinkPath'' or ''targetPath'' does not use the
 *             standard path representation or one of them cannot be
 *             converted to the system path type.
 *  @exception FILE_ERROR The file ''symlinkPath'' already exists, or a
 *             system function returns an error.
 *)
const proc: makeLink (in string: symlinkPath, in string: targetPath)  is action "CMD_MAKE_LINK";


# The function symlink() is deprecated. Use makeLink() instead.
# Note that makeLink() has the symlinkPath as first parameter.
const proc: symlink (in string: targetPath, in string: symlinkPath) is func
  begin
    makeLink(symlinkPath, targetPath);
  end func;


(**
 *  Remove a file of any type unless it is a directory that is not empty.
 *  The function does not follow symbolic links. An attempt to remove a
 *  directory that is not empty triggers FILE_ERROR.
 *  @param filePath Name of the file to be removed.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath'' to
 *             the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file does not exist or it is a directory
 *             that is not empty or a system function returns an error.
 *)
const proc: removeFile (in string: filePath)                 is action "CMD_REMOVE_FILE";


(**
 *  Remove a file of any type inclusive a directory tree.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath'' to
 *             the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file does not exist or a system function
 *             returns an error.
 *)
const proc: removeTree (in string: filePath)                 is action "CMD_REMOVE_TREE";


(**
 *  Copy a file or directory tree.
 *  Permissions/mode, ownership and timestamps of the destination file
 *  are determined independent of the corresponding source properties.
 *  The destination file gets the permissions/mode defined by umask.
 *  The user executing the program is the owner of the destination file.
 *  The timestamps of the destination file are set to the current time.
 *  Symbolic links in ''sourcePath'' are always followed.
 *  Therefore ''copyFile'' will never create a symbolic link.
 *  Note that ''copyFile'' does not preserve hard links (they are
 *  resolved to distinct files).
 *  @exception MEMORY_ERROR Not enough memory to convert ''sourcePath''
 *             or ''destPath'' to the system path type.
 *  @exception RANGE_ERROR ''sourcePath'' or ''destPath'' does not use
 *             the standard path representation or one of them cannot be
 *             converted to the system path type.
 *  @exception FILE_ERROR Source file does not exist, destination file
 *             already exists or a system function returns an error.
 *)
const proc: copyFile (in string: sourcePath, in string: destPath)  is action "CMD_COPY_FILE";


(**
 *  Clone a file or directory tree.
 *  Permissions/mode, ownership and timestamps of the original are
 *  preserved. Symlinks are not followed. Instead the symlink is
 *  copied. Note that ''cloneFile'' does not preserve hard links (they
 *  are resolved to distinct files).
 *  @exception MEMORY_ERROR Not enough memory to convert
 *             ''sourcePath'' or ''destPath'' to the system path type.
 *  @exception RANGE_ERROR ''sourcePath'' or ''destPath'' does not use
 *             the standard path representation or one of them cannot be
 *             converted to the system path type.
 *  @exception FILE_ERROR Source file does not exist, destination file
 *             already exists or a system function returns an error.
 *)
const proc: cloneFile (in string: sourcePath, in string: destPath) is action "CMD_CLONE_FILE";


(**
 *  Move and rename a file or directory tree.
 *  The function uses the C ''rename()'' function. If ''rename()'' fails
 *  the file (or directory tree) is cloned with ''cloneFile'' (which
 *  preserves permissions/mode, ownership and timestamps) to the new
 *  place and with the new name. If ''cloneFile'' succeeds the original
 *  file is deleted. If ''cloneFile'' fails (no space on device or
 *  other reason) all remains of the failed clone are removed. Note
 *  that ''cloneFile'' works for symbolic links but does not preserve
 *  hard links (they are resolved to distinct files).
 *  @exception MEMORY_ERROR Not enough memory to convert ''sourcePath''
 *             or ''destPath'' to the system path type.
 *  @exception RANGE_ERROR ''sourcePath'' or ''destPath'' does not use
 *             the standard path representation or one of them cannot be
 *             converted to the system path type.
 *  @exception FILE_ERROR Source file does not exist, destination file
 *             already exists or a system function returns an error.
 *)
const proc: moveFile (in string: sourcePath, in string: destPath)  is action "CMD_MOVE";


(**
 *  Create a new directory.
 *  The function does not follow symbolic links.
 *  @param dirPath Name of the directory to be created.
 *  @exception MEMORY_ERROR Not enough memory to convert ''dirPath'' to
 *             the system path type.
 *  @exception RANGE_ERROR ''dirPath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR The file ''dirPath'' already exists, or a
 *             system function returns an error.
 *)
const proc: makeDir (in string: dirPath)                     is action "CMD_MAKE_DIR";


# The function mkdir() is deprecated. Use makeDir() instead.
const proc: mkdir (in string: dirPath) is func
  begin
    makeDir(dirPath);
  end func;


(**
 *  Determine the current working directory of the calling process.
 *  @return The absolute path of the current working directory.
 *  @exception MEMORY_ERROR Not enough memory to represent the
 *             result [[string]].
 *  @exception FILE_ERROR The system function returns an error.
 *)
const func string: getcwd                                    is action "CMD_GETCWD";


(**
 *  Changes the current working directory of the calling process.
 *  @exception MEMORY_ERROR Not enough memory to convert ''dirPath'' to
 *             the system path type.
 *  @exception RANGE_ERROR ''dirPath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR A system function returns an error.
 *)
const proc: chdir (in string: dirPath)                       is action "CMD_CHDIR";


(**
 *  Determine the home directory of the user.
 *  This function should be preferred over the use of an environment
 *  variable such as $HOME. $HOME is not supported under all operating
 *  systems and it is not guaranteed, that it uses the standard path
 *  representation.
 *  @return The absolute path of the home directory.
 *  @exception MEMORY_ERROR Not enough memory to represent the
 *             result [[string]].
 *  @exception FILE_ERROR Not able to determine the home directory.
 *)
const func string: homeDir                                   is action "CMD_HOME_DIR";


(**
 *  Determine the absolute path for a given ''path''.
 *  The examples assume that the current working directory is "/home/myuser".
 *   toAbsPath(getcwd, "aFile")    returns e.g. "/home/myuser/aFile"
 *   toAbsPath(".", "aFile")       returns e.g. "/home/myuser/aFile"
 *   toAbsPath(".", "../aFile")    returns e.g. "/home/aFile"
 *   toAbsPath(".", "..")          returns e.g. "/home"
 *   toAbsPath("/usr", "include")  returns "/usr/include"
 *  @param basePath Path of the base location. The relative ''path'' is
 *         relative to this location.
 *  @param path Absolute or relative path for which the absolute path should
 *         be determined:
 *  @return ''path'' if ''path'' is already absolute, or
 *          concatenate ''basePath'' with ''path''.
 *)
const func string: toAbsPath (in string: basePath, in string: path) is func
  result
    var string: absolutePath is "";
  local
    var integer: dotdotPos is 0;
    var integer: slashPos is 0;
  begin
    if startsWith(path, "/") then
      absolutePath := path;
    elsif basePath = "/" then
      absolutePath := "/" & path;
    elsif startsWith(basePath, "/") then
      absolutePath := basePath & "/" & path;
    else
      absolutePath := getcwd & "/" & basePath & "/" & path;
    end if;
    absolutePath := removeDotFiles(absolutePath);
  end func;


const func string: parentDir (in string: path) is func
  result
    var string: parentDir is "";
  local
    var integer: slashPos is 0;
  begin
    slashPos := rpos(path, '/');
    if slashPos = 0 then
      parentDir := ".";
    elsif slashPos = 1 then
      parentDir := "/";
    else
      parentDir := path[.. pred(slashPos)];
    end if;
  end func;


(**
 *  Create the parent directories of the given ''filePath''.
 *  @exception MEMORY_ERROR Not enough memory to convert ''filePath'' to
 *             the system path type.
 *  @exception RANGE_ERROR ''filePath'' does not use the standard path
 *             representation or it cannot be converted to the system
 *             path type.
 *  @exception FILE_ERROR A system function returns an error, or
 *             a component of ''filePath'' is not a directory.
 *)
const proc: makeParentDirs (in string: filePath) is func
  local
    var integer: slashPos is 0;
    var string: dirPath is "";
  begin
    slashPos := pos(filePath, '/', 2);
    while slashPos <> 0 do
      dirPath := filePath[.. pred(slashPos)];
      if fileTypeSL(dirPath) = FILE_ABSENT then
        makeDir(dirPath);
        slashPos := pos(filePath, '/', succ(slashPos));
      elsif fileTypeSL(dirPath) = FILE_DIR then
        slashPos := pos(filePath, '/', succ(slashPos));
      else
        raise FILE_ERROR;
      end if;
    end while;
  end func;


(**
 *  Convert a DOS/Windows path to the standard path representation.
 *  A path in the standard path representation is left unchanged.
 *   convDosPath("C:/abc")          returns "/c/abc"
 *   convDosPath("C:/abc/")         returns "/c/abc"
 *   convDosPath("C:/abc//def")     returns "/c/abc/def"
 *   convDosPath("C:\\abc")         returns "/c/abc"
 *   convDosPath("C:\\abc\\")       returns "/c/abc"
 *   convDosPath("C:\\abc\\\\def")  returns "/c/abc/def"
 *   convDosPath("d:/abc")          returns "/d/abc"
 *   convDosPath("d://abc")         returns "/d/abc"
 *   convDosPath("d:\\abc")         returns "/d/abc"
 *   convDosPath("d:\\\\abc")       returns "/d/abc"
 *   convDosPath("d:abc")           returns "/d/abc"
 *   convDosPath("dir/file")        returns "dir/file"
 *   convDosPath("dir\\file")       returns "dir/file"
 *   convDosPath("/usr/include")    returns "/usr/include"
 *  @param path Absolute or relative DOS/Windows path.
 *  @return The path in the standard path representation.
 *)
const func string: convDosPath (in string: path) is func
  result
    var string: stdPath is "";
  begin
    # Path delimiter is slash
    stdPath := replace(path, "\\", "/");
    # Convert drive letters to standard path
    if length(stdPath) >= 2 and stdPath[2] = ':' and
        lower(stdPath[1]) >= 'a' and lower(stdPath[1]) <= 'z' then
      stdPath @:= [2] lower(stdPath[1]);
      stdPath @:= [1] '/';
      if length(stdPath) >= 3 and stdPath[3] <> '/' then
        stdPath := stdPath[.. 2] & "/" & stdPath[3 ..];
      end if;
    end if;
    # Remove double slashes
    stdPath := replaceN(stdPath, "//", "/");
    # Remove slash at end of path
    if stdPath <> "/" and endsWith(stdPath, "/") then
      stdPath := stdPath[.. pred(length(stdPath))];
    end if;
  end func;


const func string: toStdPath (in string: path) is func
  result
    var string: stdPath is "";
  begin
    stdPath := path;
    # Remove double slashes
    stdPath := replaceN(stdPath, "//", "/");
    # Remove slash at end of path
    while stdPath <> "/" and endsWith(stdPath, "/") do
      stdPath := stdPath[.. pred(length(stdPath))];
    end while;
  end func;


(**
 *  [[filesys#fileSys|FileSys]] implementation type to access operating system files.
 *)
const type: osFileSys is sub emptyFileSys struct
  end struct;


type_implements_interface(osFileSys, fileSys);


const func fileSys: openOsFileSys is func
  result
    var fileSys: newFileSys is fileSys.value;
  local
    var osFileSys: osFiles is osFileSys.value;
  begin
    newFileSys := toInterface(osFiles);
  end func;


(**
 *  [[filesys#fileSys|File system]] of the operating system files.
 *)
var fileSys: osFiles is openOsFileSys;


const proc: close (inout osFileSys: fileSystem) is noop;

const func array string: readDir (inout osFileSys: fileSystem, in string: dirPath) is
  return readDir(dirPath);

const func fileType: fileType (inout osFileSys: fileSystem, in string: filePath) is
  return fileType(filePath);

const func fileType: fileTypeSL (inout osFileSys: fileSystem, in string: filePath) is
  return fileTypeSL(filePath);

const func integer: fileSize (inout osFileSys: fileSystem, in string: filePath) is
  return fileSize(filePath);

const func bigInteger: bigFileSize (inout osFileSys: fileSystem, in string: filePath) is
  return bigFileSize(filePath);

const func fileMode: getFileMode (inout osFileSys: fileSystem, in string: filePath) is
  return getFileMode(filePath);

const proc: setFileMode (inout osFileSys: fileSystem, in string: filePath,
    in fileMode: mode) is func
  begin
    setFileMode(filePath, mode);
  end func;

const func time: getMTime (inout osFileSys: fileSystem, in string: filePath) is
  return getMTime(filePath);

const proc: setMTime (inout osFileSys: fileSystem, in string: filePath,
    in time: modificationTime) is func
  begin
    setMTime(filePath, modificationTime);
  end func;

const func string: getOwner (inout osFileSys: fileSystem, in string: filePath) is
  return getOwner(filePath);

const proc: setOwner (inout osFileSys: fileSystem, in string: filePath,
    in string: owner) is func
  begin
    setOwner(filePath, owner);
  end func;

const func string: getGroup (inout osFileSys: fileSystem, in string: filePath) is
  return getGroup(filePath);

const proc: setGroup (inout osFileSys: fileSystem, in string: filePath,
    in string: group) is func
  begin
    setGroup(filePath, group);
  end func;

const func fileMode: getFileMode (inout osFileSys: fileSystem, in string: filePath, SYMLINK) is
  return getFileMode(filePath, SYMLINK);

const func time: getMTime (inout osFileSys: fileSystem, in string: filePath, SYMLINK) is
  return getMTime(filePath, SYMLINK);

const proc: setMTime (inout osFileSys: fileSystem, in string: filePath,
    in time: modificationTime, SYMLINK) is func
  begin
    setMTime(filePath, modificationTime, SYMLINK);
  end func;

const func string: getOwner (inout osFileSys: fileSystem, in string: filePath, SYMLINK) is
  return getOwner(filePath, SYMLINK);

const proc: setOwner (inout osFileSys: fileSystem, in string: filePath,
    in string: owner, SYMLINK) is func
  begin
    setOwner(filePath, owner, SYMLINK);
  end func;

const func string: getGroup (inout osFileSys: fileSystem, in string: filePath, SYMLINK) is
  return getGroup(filePath, SYMLINK);

const proc: setGroup (inout osFileSys: fileSystem, in string: filePath,
    in string: group, SYMLINK) is func
  begin
    setGroup(filePath, group, SYMLINK);
  end func;

const func file: open (inout osFileSys: fileSystem, in string: filePath, in string: mode) is
  return open(filePath, mode);

const func string: getFile (inout osFileSys: fileSystem, in string: filePath) is func
  result
    var string: data is "";
  local
    var file: workFile is STD_NULL;
  begin
    workFile := open(filePath, "r");
    if workFile <> STD_NULL then
      data := gets(workFile, length(workFile));
      close(workFile);
    else
      raise FILE_ERROR;
    end if;
  end func;

const proc: putFile (inout osFileSys: fileSystem, in string: filePath, in string: stri) is func
  local
    var file: workFile is STD_NULL;
  begin
    if stri <> "" then
      workFile := open(filePath, "w");
      if workFile <> STD_NULL then
        write(workFile, stri);
        close(workFile);
      end if;
    end if;
  end func;

const func string: readLink (inout osFileSys: fileSystem, in string: filePath) is
  return readLink(filePath);

const proc: makeLink (inout osFileSys: fileSystem, in string: symlinkPath,
    in string: targetPath) is func
  begin
    makeLink(symlinkPath, targetPath);
  end func;

const proc: makeDir (inout osFileSys: fileSystem, in string: dirPath) is func
  begin
    makeDir(dirPath);
  end func;

const func string: getcwd (inout osFileSys: fileSystem) is
  return getcwd;

const proc: chdir (inout osFileSys: fileSystem, in string: dirPath) is func
  begin
    chdir(dirPath);
  end func;