(********************************************************************) (* *) (* filesys.s7i Interface for file systems (os, tar, zip, ...) *) (* Copyright (C) 2017, 2018, 2020, 2021, 2023, 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 "bigint.s7i"; include "time.s7i"; (** * Type to describe the type of a file. * Possible values are ''FILE_ABSENT'', ''FILE_UNKNOWN'', * ''FILE_REGULAR'', ''FILE_DIR'', ''FILE_CHAR'', ''FILE_BLOCK'', * ''FILE_FIFO'', ''FILE_SYMLINK'' and ''FILE_SOCKET''. The value * ''FILE_ABSENT'' is used to describe a nonexistent file. *) const type: fileType is integer; const fileType: FILE_ABSENT is 0; # A component of path does not exist const fileType: FILE_UNKNOWN is 1; # File exists but has an unknown type const fileType: FILE_REGULAR is 2; const fileType: FILE_DIR is 3; const fileType: FILE_CHAR is 4; const fileType: FILE_BLOCK is 5; const fileType: FILE_FIFO is 6; const fileType: FILE_SYMLINK is 7; const fileType: FILE_SOCKET is 8; const integer: MAX_SYMLINK_CHAIN_LENGTH is 5; (** * Type to describe one file permission. * Possible values are ''EXEC_OTHER'', ''WRITE_OTHER'', ''READ_OTHER'', * ''EXEC_GROUP'', ''WRITE_GROUP'', ''READ_GROUP'', ''EXEC_USER'' and * ''WRITE_USER'' and ''READ_USER''. *) const type: filePermission is new enum EXEC_OTHER, WRITE_OTHER, READ_OTHER, EXEC_GROUP, WRITE_GROUP, READ_GROUP, EXEC_USER, WRITE_USER, READ_USER end enum; (** * Type to describe the mode (all permissions) of a file. * A ''fileMode'' is a set of [[#filePermission|filePermission]]. * A test for a permission is a set mempership test. E.g.: * EXEC_USER in aFileMode *) const type: fileMode is set of filePermission; const func integer: integer (in fileMode: mode) is return integer(bitset(mode)); const func integer: (attr integer) conv (in fileMode: mode) is return integer(bitset(mode)); const func fileMode: fileMode (in integer: int_mode) is return fileMode conv (bitset(int_mode)); const func fileMode: (attr fileMode) conv (in integer: int_mode) is return fileMode conv (bitset(int_mode)); const func string: str (in fileMode: mode) is func result var string: permissions is "rwxrwxrwx"; local var filePermission: aPermission is READ_USER; begin for aPermission range READ_USER downto EXEC_OTHER do if aPermission not in mode then permissions @:= [9 - ord(aPermission)] '-'; end if; end for; end func; enable_output(fileMode); (** * Removes dot files (. and ..) from a path. * removeDotFiles("/home/myuser/aFile/..") returns "/home/myuser" * removeDotFiles("/aDir/aFile/../../home/myuser") returns "/home/myuser" * removeDotFiles("/aDir/aFile/../../..") returns "/" * removeDotFiles("aDir/aFile/../../..") returns ".." * removeDotFiles("aDir/./aFile/.") returns "aDir/aFile" * @param path Absolute or relative path where the dot files should * be removed. * @return an absolute or relative path without dot files. *) const func string: removeDotFiles (in string: path) is func result var string: resultPath is ""; local var integer: dotdotPos is 0; var integer: slashPos is 0; begin resultPath := replace(path, "/./", "/"); dotdotPos := pos(resultPath, "/.."); while dotdotPos <> 0 do slashPos := rpos(resultPath, '/', pred(dotdotPos)); if length(resultPath) = dotdotPos + 2 then if slashPos <= 1 then if startsWith(resultPath, "/") then resultPath := "/"; elsif resultPath = "../.." then resultPath := ".."; else resultPath := "."; end if; else resultPath := resultPath[.. pred(slashPos)]; end if; dotdotPos := 0; elsif resultPath[dotdotPos + 3] = '/' then if slashPos <> 0 then resultPath := resultPath[.. pred(slashPos)] & resultPath[dotdotPos + 3 ..]; else if startsWith(resultPath, "/") then resultPath := resultPath[dotdotPos + 3 ..]; else resultPath := resultPath[dotdotPos + 4 ..]; end if; end if; dotdotPos := pos(resultPath, "/.."); else dotdotPos := pos(resultPath, "/..", succ(dotdotPos)); end if; end while; if endsWith(resultPath, "/.") then if resultPath = "/." then resultPath := "/"; else resultPath := resultPath[.. length(resultPath) - 2]; end if; end if; end func; (** * Destination of the symlink ''symlinkPath'' with the target ''targetPath''. * Symbolic links can be relative or absolute. Relative symbolic links are * relative to their place in the file system and not relative to the * current working directory. * @param symlinkPath Path of the symbolic link. * @param targetPath Relative or absolute target of the symbolic link: * @return an absolute or relative destination of the symbolic link. *) const func string: symlinkDestination (in string: symlinkPath, in string: targetPath) is func result var string: destination is ""; local var integer: slashPos is 0; var string: symlinkDir is ""; begin if startsWith(targetPath, "/") then destination := targetPath; else slashPos := rpos(symlinkPath, '/'); if slashPos > 1 then symlinkDir := symlinkPath[.. pred(slashPos)]; elsif slashPos = 1 then symlinkDir := "/"; else symlinkDir := "."; end if; if symlinkDir = "/" then destination := "/" & targetPath; else destination := symlinkDir & "/" & targetPath; end if; end if; destination := removeDotFiles(destination); end func; (** * Interface type for file systems ([[osfiles#osFileSys|os]], [[ar#arArchive|ar]], [[tar#tarArchive|tar]], [[zip#zipArchive|zip]], [[zip#zipArchive|jar]], [[cpio#cpioArchive|cpio]], [[rpm#rpmArchive|rpm]], [[ftp#ftpConnection|ftp]], ...). *) const type: fileSys is sub object interface; (** * Close a file system. *) const proc: close (inout fileSys: fileSystem) is DYNAMIC; (** * Determine the filenames in a directory of a file system. * Note that the function returns only the file names. * Additional information must be obtained with other calls. * @param fileSystem File system in which the directory is searched for. * @param dirPath Path of a directory in the file system. * @return an array with the file names. *) const func array string: readDir (inout fileSys: fileSystem, in string: dirPath) is DYNAMIC; (** * Paths of files in a directory and its subdirectories inside a file system. * The function returns the file paths relative to the given directory. * @param fileSystem File system in which the directory is searched for. * @param dirPath Path of a directory in the file system. * @return an array with the file paths. *) const func array string: readDir (inout fileSys: fileSystem, in string: dirPath, RECURSIVE) is DYNAMIC; (** * Determine the file names in the top directory of a file system. * @return an array with the file names. *) const func array string: readDir (inout fileSys: fileSystem) is return readDir(fileSystem, "."); (** * Determine all file paths in a file system. * @return an array with the file paths. *) const func array string: readDir (inout fileSys: fileSystem, RECURSIVE) is DYNAMIC; (** * Determine the type of a file. * The function does follow symbolic links. Therefore it never * 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. *) const func fileType: fileType (inout fileSys: fileSystem, in string: filePath) is DYNAMIC; (** * 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. *) const func fileType: fileTypeSL (inout fileSys: fileSystem, in string: filePath) is DYNAMIC; (** * Determine the file size of a file. * The function follows symbolic links. The file size is measured in bytes. * For directories a size of 0 is returned. * @return the size of the file. *) const func integer: fileSize (inout fileSys: fileSystem, in string: filePath) is DYNAMIC; (** * Determine the file size of a file. * The function follows symbolic links. The file size is measured in bytes. * For directories a size of 0 is returned. * @return the size of the file. *) const func bigInteger: bigFileSize (inout fileSys: fileSystem, in string: filePath) is DYNAMIC; (** * Determine the file mode (permissions) of a file. * The function follows symbolic links. * @return the file mode. *) const func fileMode: getFileMode (inout fileSys: fileSystem, in string: filePath) is DYNAMIC; # The function fileMode() is deprecated. Use getFileMode() instead. const func fileMode: fileMode (inout fileSys: fileSystem, in string: filePath) is return getFileMode(fileSystem, filePath); (** * Change the file mode (permissions) of a file. * The function follows symbolic links. * Not all file systems support this function. *) const proc: setFileMode (inout fileSys: fileSystem, in string: filePath, in fileMode: mode) is DYNAMIC; (** * Determine the modification time of a file. * The function follows symbolic links. * @return the modification time of the file. *) const func time: getMTime (inout fileSys: fileSystem, in string: filePath) is DYNAMIC; (** * Set the modification time of a file. * The function follows symbolic links. *) const proc: setMTime (inout fileSys: fileSystem, in string: filePath, in time: modificationTime) is DYNAMIC; (** * Determine the name of the owner (UID) of a file. * The function follows symbolic links. * @return the name of the file owner. *) const func string: getOwner (inout fileSys: fileSystem, in string: filePath) is DYNAMIC; (** * Set the owner of a file. * The function follows symbolic links. *) const proc: setOwner (inout fileSys: fileSystem, in string: filePath, in string: owner) is DYNAMIC; (** * Determine the name of the group (GID) to which a file belongs. * The function follows symbolic links. * @return the name of the file group. *) const func string: getGroup (inout fileSys: fileSystem, in string: filePath) is DYNAMIC; (** * Set the group of a file. * The function follows symbolic links. *) const proc: setGroup (inout fileSys: fileSystem, in string: filePath, in string: group) is DYNAMIC; (** * 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. *) const func fileMode: getFileMode (inout fileSys: fileSystem, in string: filePath, SYMLINK) is DYNAMIC; (** * 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. *) const func time: getMTime (inout fileSys: fileSystem, in string: filePath, SYMLINK) is DYNAMIC; (** * Set the modification time of a symbolic link. * The function only works for symbolic links and does not follow the * symbolic link. *) const proc: setMTime (inout fileSys: fileSystem, in string: filePath, in time: modificationTime, SYMLINK) is DYNAMIC; (** * 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. *) const func string: getOwner (inout fileSys: fileSystem, in string: filePath, SYMLINK) is DYNAMIC; (** * Set the owner of a symbolic link. * The function only works for symbolic links and does not follow the * symbolic link. *) const proc: setOwner (inout fileSys: fileSystem, in string: filePath, in string: owner, SYMLINK) is DYNAMIC; (** * 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. *) const func string: getGroup (inout fileSys: fileSystem, in string: filePath, SYMLINK) is DYNAMIC; (** * Set the group of a symbolic link. * The function only works for symbolic links and does not follow the * symbolic link. *) const proc: setGroup (inout fileSys: fileSystem, in string: filePath, in string: group, SYMLINK) is DYNAMIC; (** * Open a file with ''filePath'' and ''mode'' in the file system. *) const func file: open (inout fileSys: fileSystem, in string: filePath, in string: mode) is DYNAMIC; (** * Get the contents of file ''filePath'' in the file system. * @return the specified file as string. *) const func string: getFile (inout fileSys: fileSystem, in string: filePath) is DYNAMIC; (** * Write ''stri'' to the file ''filePath'' using the file system. * If the file exists already, it is overwritten. * Not all file systems support this function. *) const proc: putFile (inout fileSys: fileSystem, in string: filePath, in string: stri) is DYNAMIC; (** * Reads the destination of a symbolic link. * Not all file systems support this function. * @return The destination referred by the symbolic link. *) const func string: readLink (inout fileSys: fileSystem, in string: filePath) is DYNAMIC; (** * Create a symbolic link. * The symbolic link ''symlinkPath'' will refer to ''targetPath'' afterwards. * The function does not follow symbolic links. * Not all file systems support this function. * @param fileSystem Open file system. * @param symlinkPath Name of the symbolic link to be created. * @param targetPath String to be contained in the symbolic link. *) const proc: makeLink (inout fileSys: fileSystem, in string: symlinkPath, in string: targetPath) is DYNAMIC; (** * Remove a file (except a nonempty directory), from a file system. * The function does not follow symbolic links. * Not all file systems support this function. * @param fileSystem Open file system. * @param filePath Name of the file to be removed. *) const proc: removeFile (inout fileSys: fileSystem, in string: filePath) is DYNAMIC; (** * Remove a file of any type inclusive a directory tree, from a file system. * Not all file systems support this function. *) const proc: removeTree (inout fileSys: fileSystem, in string: filePath) is DYNAMIC; (** * Move and rename a file or directory tree in a file system. * Not all file systems support this function. *) const proc: moveFile (inout fileSys: fileSystem, in string: sourcePath, in string: destPath) is DYNAMIC; (** * Create a directory in the file system. * The function does not follow symbolic links. * Not all file systems support this function. * @param fileSystem Open file system. * @param dirPath Name of the directory to be created. *) const proc: makeDir (inout fileSys: fileSystem, in string: dirPath) is DYNAMIC; # The function mkdir() is deprecated. Use makeDir() instead. const proc: mkdir (inout fileSys: fileSystem, in string: dirPath) is func begin makeDir(fileSystem, dirPath); end func; (** * Deletes an empty directory in the file system. * Not all file systems support this function. *) const proc: rmdir (inout fileSys: fileSystem, in string: dirPath) is DYNAMIC; (** * Determine the current working directory of the file system. * Not all file systems support this function. * @return the current working directory as absolute path. *) const func string: getcwd (inout fileSys: fileSystem) is DYNAMIC; (** * Change the current working directory of the file system. * Not all file systems support this function. *) const proc: chdir (inout fileSys: fileSystem, in string: dirPath) is DYNAMIC; (** * Describes an empty file system. *) const type: emptyFileSys is new struct end struct; type_implements_interface(emptyFileSys, fileSys); (** * Default value of ''fileSys'' (emptyFileSys.value). *) const fileSys: (attr fileSys) . value is emptyFileSys.value; (** * Paths of files in a directory and its subdirectories inside a file system. * The function returns the file paths relative to the given directory. * @param fileSystem File system in which the directory is searched for. * @param dirPath Path of a directory in the file system. * @return an array with the file paths. * @exception RANGE_ERROR ''dirPath'' does not use the standard path * representation. * @exception FILE_ERROR ''dirPath'' is not present in the file system. *) const func array string: readDir (inout emptyFileSys: fileSystem, in var string: dirPath, RECURSIVE) is func result var array string: filePaths is 0 times ""; local var array string: pathsInDir is 0 times ""; var string: path is ""; var string: base is ""; var string: subPath is ""; begin if dirPath <> "/" and endsWith(dirPath, "/") then raise RANGE_ERROR; else pathsInDir := readDir(fileSystem, dirPath); if dirPath <> "/" then dirPath &:= "/"; end if; for path range pathsInDir do filePaths &:= path; if endsWith(path, "/") then base := path; else base := path & "/"; end if; if fileType(fileSystem, dirPath & path) = FILE_DIR then for subPath range readDir(fileSystem, dirPath & path) do filePaths &:= base & subPath; end for; end if; end for; end if; end func; (** * Determine the file size of a file. * The file size is measured in bytes. * For directories a size of 0 is returned. * @return the size of the file. *) const func bigInteger: bigFileSize (inout emptyFileSys: fileSystem, in string: filePath) is return bigInteger(fileSize(fileSystem, filePath));