-----------------------------------------------------------
--                   Eiffel/S libraries                  --
-----------------------------------------------------------
--               Copyright (C) 1991 - 1993               --
--                           by                          --
--                   SiG Computer GmbH                   --
--                  All rights reserved                  --
-----------------------------------------------------------
-- Release : 1.3 - October 1993                          --
-----------------------------------------------------------
-- Authors : Lambert Strether & Michael Schweitzer       --
-----------------------------------------------------------
  
class   FILE_SYSTEM

-----------------------------------------------------------
creation {ANY}
    make
-----------------------------------------------------------
feature {ANY}
-----------------------------------------------------------

    make is

        do
        end
-----------------------------------------------------------
-- CLUSTERS
-----------------------------------------------------------
-- NOTE: If a path begins with a '/' it is absolute - otherwise 
--       it is relative to `current_cluster'.
-----------------------------------------------------------

    current_cluster : STRING is

        do
            Result := rt_to_string (rt_current_cluster)
        end
-----------------------------------------------------------

    parent_cluster : STRING is 

        do
            Result := ".."

            if not cluster_exists (Result) then
                Result := Void
            end
        end
-----------------------------------------------------------

    add_cluster (path, permissions : STRING) is
                            -- Create new cluster `path'
        require
            path_not_empty : path /= Void and then not path.empty
            valid_perm     : permissions /= Void
            perm_to_modify : has_modperm (path_prefix (path))
        do
            rt_add_cluster (path.to_external, permissions.to_external)
        end
-----------------------------------------------------------

    remove_cluster (path : STRING) is
                            -- Remove cluster `path'
        require
            path_not_empty : path /= Void and then not path.empty
            perm_to_modify : has_modperm (path_prefix (path))
            empty          : cluster_count (path) = 0 and then
                             subcluster_count (path) = 0
        do
            rt_remove_cluster (path.to_external)
        end
-----------------------------------------------------------

    change_cluster (path : STRING) is
                            -- Change current cluster to `path'

        require
            path_not_empty : path /= Void and then not path.empty
            exists         : cluster_exists (path)
        do
            rt_change_cluster (path.to_external)
        end
-----------------------------------------------------------

    cluster_exists (path : STRING) : BOOLEAN is
                            -- Does cluster `path' exist?
        require
            path_not_empty : path /= Void and then not path.empty
            perm_to_list   : has_listperm (path_prefix (path))
        do
            Result := rt_cluster_exists (path.to_external)
        end
-----------------------------------------------------------

    cluster_data (path : STRING) : FSYS_DAT is
                            -- Get all information available about
                            -- cluster `path'.
        require
            path_not_empty : path /= Void and then not path.empty
            perm_to_list   : has_listperm (path_prefix (path))

        local
            pa : STRING
            n  : STRING
        do
            n  := path_suffix (path)
            pa := absolute_path (path)

            !!Result.make (n, pa, Current, false)
        end
-----------------------------------------------------------

    cluster_time (path : STRING) : REAL is
                                -- Time of last modification.
        require
            path_not_empty : path /= Void and then not path.empty
            perm_to_list   : has_listperm (path_prefix (path))
        do
            Result := rt_cluster_time (path.to_external)
        end
-----------------------------------------------------------

    cluster_perm (path : STRING) : STRING is
                                -- All permissions.
        require
            path_not_empty : path /= Void and then not path.empty
            perm_to_list   : has_listperm (path_prefix (path))
        do
            Result := rt_to_string (rt_cluster_perm (path.to_external))
        end
-----------------------------------------------------------

    my_cluster_perm (path : STRING) : STRING is
                                -- My permissions only.
        require
            path_not_empty : path /= Void and then not path.empty
        do
            Result := rt_to_string (rt_my_cluster_perm (path.to_external))
        end
-----------------------------------------------------------

    has_listperm (path : STRING) : BOOLEAN is
                                -- May I list the cluster?
        require
            path_not_empty : path /= Void and then not path.empty
        do
            Result := rt_has_listperm (path.to_external)
        end
-----------------------------------------------------------

    has_modperm (path : STRING) : BOOLEAN is
                                -- May I modify the cluster?
        require
            path_not_empty : path /= Void and then not path.empty
        do
            Result := rt_has_modperm (path.to_external)
        end
-----------------------------------------------------------

    cluster_count (path : STRING) : INTEGER is
                                -- Nr of files in cluster `path'
        require
            path_not_empty : path /= Void and then not path.empty
        do
            Result := name_list (path, true).count
        end
-----------------------------------------------------------

    subcluster_count (path : STRING) : INTEGER is
                                -- Nr of subclusters in cluster `path'
        require
            path_not_empty : path /= Void and then not path.empty
        do
            Result := name_list (path, false).count
        end
-----------------------------------------------------------

    change_cluster_perm (path, new_permissions : STRING) is
                                -- Change the permissions of cluster `path'.
        require
            path_not_empty : path /= Void and then not path.empty
            perm_not_void  : new_permissions /= Void
            perm_to_modify : has_modperm (path_prefix (path))
        do
            rt_change_cluster_perm (path.to_external, 
                                            new_permissions.to_external)
        end
-----------------------------------------------------------

    cluster_list (path : STRING) : SORTED_LIST [FSYS_DAT] is
                                -- Make a list of all subclusters of `path'.
        require
            path_not_empty : path /= Void and then not path.empty
            perm_to_list   : has_listperm (path)
        do
            Result := list_cluster (path, false)
        end
-----------------------------------------------------------
-- FILES
-----------------------------------------------------------
-- NOTE: All pathnames must be understood as concatenations
--       of a cluster path with a filename. If the path begins
--       with '/' it is absolute - otherwise it is relative
--       to `current_cluster'.
-----------------------------------------------------------

    add_file (path, permissions : STRING) is

        require
            path_not_empty : path /= Void and then not path.empty
            valid_perm     : permissions /= Void
            perm_to_modify : has_modperm (path_prefix (path))
            not_in_use     : not in_use (path)
        do
            rt_add_file (path.to_external, permissions.to_external)
        end
-----------------------------------------------------------

    remove_file (path : STRING) is
                                -- Remove file `path'.
        require
            path_not_empty : path /= Void and then not path.empty
            perm_to_modify : has_modperm (path_prefix (path))
            not_in_use     : not in_use (path)
        do
            rt_remove_file (path.to_external)
        end
-----------------------------------------------------------

    rename_file (old_path, new_path : STRING) is
                                -- Rename `old_path' -> `new_path'
        require
            paths_not_empty: old_path /= Void and new_path /= Void and then
                             (not old_path.empty and not new_path.empty)
            not_in_use     : not in_use (old_path) and not in_use (new_path)
            source_exists  : file_exists (old_path)
            target_is_new  : not file_exists (new_path)
        do
            rt_rename_file (old_path.to_external, new_path.to_external)
        end
-----------------------------------------------------------

    copy_file (source_path, dest_path : STRING) is
                                -- Copy the contents of file
                                -- `source_path' onto file
                                -- `dest_path'
        require
            paths_not_void    : source_path /= Void and dest_path /= Void 
                                                 and then
                                (not source_path.empty and not dest_path.empty)
            write_permission  : not file_exists (dest_path) and then
                                    has_modperm (path_prefix (dest_path))
                                or else
                                file_exists (dest_path) and then
                                    has_writeperm (dest_path)

            source_exists     : file_exists (source_path)
            target_not_in_use : not in_use (dest_path)
        do
            rt_copy_file (source_path.to_external, dest_path.to_external)
        end
-----------------------------------------------------------

    same (first, second : STRING) : BOOLEAN is
                                -- Are files `first' and 
                                -- `second' bytewise identical?
        require
            paths_not_void : first /= Void and second /= Void and then
                             (not first.empty and not second.empty)
            first_exists   : file_exists (first)
            second_exists  : file_exists (second)
            read_perms     : has_readperm (first) and has_readperm (second)
        do
            Result := rt_same (first.to_external, second.to_external)
        end
-----------------------------------------------------------

    identify (hook : INTEGER) : STRING is
                                -- return path of file 
                                -- connected with `hook'
        require
            valid_hook : -- `hook' must be a valid file hook
        do
            Result := rt_to_string (rt_identify (hook))
        end
-----------------------------------------------------------

    file_exists (path : STRING) : BOOLEAN is
                                -- Does file `path' exist?
        require
            path_not_empty : path /= Void and then not path.empty
            perm_to_list   : has_listperm (path_prefix (path))
        do
            Result := rt_file_exists (path.to_external)
        end
-----------------------------------------------------------

    file_data (path : STRING) : FSYS_DAT is

        require
            path_not_empty : path /= Void and then not path.empty
            perm_to_list   : has_listperm (path_prefix (path))

        local
            pa : STRING
            n  : STRING
        do
            n  := path_suffix (path)
            pa := absolute_path (path)

            !!Result.make (n, pa, Current, true)
        end
-----------------------------------------------------------

    file_time (path : STRING) : REAL is

        require
            path_not_empty : path /= Void and then not path.empty
            perm_to_list   : has_listperm (path_prefix (path))
        do
            Result := rt_file_time (path.to_external)
        end
-----------------------------------------------------------

    file_perm (path : STRING) : STRING is
                                    -- All permissions.
        require
            path_not_empty : path /= Void and then not path.empty
            perm_to_list   : has_listperm (path_prefix (path))
        do
            Result := rt_to_string (rt_file_perm (path.to_external))
        end
-----------------------------------------------------------

    my_file_perm (path : STRING) : STRING is
                                    -- Which permissions do I have for
                                    -- file `path'.
        require
            path_not_empty : path /= Void and then not path.empty
            perm_to_list   : has_listperm (path_prefix (path))
        do
            Result := rt_to_string (rt_my_file_perm (path.to_external))
        end
-----------------------------------------------------------

    has_readperm (path : STRING) : BOOLEAN is
                                    -- May I read from file `path'?
        require
            path_not_empty : path /= Void and then not path.empty
            perm_to_list   : has_listperm (path_prefix (path))
        do
            Result := rt_has_readperm (path.to_external)
        end
-----------------------------------------------------------

    has_writeperm (path : STRING) : BOOLEAN is
                                    -- May I write to file `path'?
        require
            path_not_empty : path /= Void and then not path.empty
            perm_to_list   : has_listperm (path_prefix (path))
        do
            Result := rt_has_writeperm (path.to_external)
        end
-----------------------------------------------------------

    has_execperm (path : STRING) : BOOLEAN is
                                    -- May I execute file `path'?
        require
            path_not_empty : path /= Void and then not path.empty
            perm_to_list   : has_listperm (path_prefix (path))
        do
            Result := rt_has_execperm (path.to_external)
        end
-----------------------------------------------------------

    file_count (path : STRING) : INTEGER is
                                    -- Nr. of bytes in file `path'.
        require
            path_not_empty : path /= Void and then not path.empty
            perm_to_list   : has_listperm (path_prefix (path))
        do
            Result := rt_file_count (path.to_external)
        end
-----------------------------------------------------------

    change_file_perm (path, new_permissions : STRING) is

        require
            path_not_empty : path /= Void and then not path.empty
            valid_perm     : new_permissions /= Void
            perm_to_modify : has_modperm (path_prefix (path))
        do
           rt_change_file_perm (path.to_external, new_permissions.to_external)
        end
-----------------------------------------------------------

    file_list (path : STRING) : SORTED_LIST [FSYS_DAT] is
                                    -- Make a list of all files in
                                    -- cluster `path'
        require
            path_not_empty : path /= Void and then not path.empty
            perm_to_list   : has_listperm (path_prefix (path))
        do
            Result := list_cluster (path, true)
        end
-----------------------------------------------------------

    access_file (path, permissions : STRING, truncate : BOOLEAN) : INTEGER is
                                    -- Give me a hook to file `path'.
                                    -- Access will be like `permissions'.
                                    -- Truncate file to zero length if
                                    -- `truncate' is true.
        require
            path_not_empty : path /= Void and then not path.empty
            valid_perm     : permissions /= Void
        do
            Result := rt_access_file (path.to_external, 
                                permissions.to_external, truncate)
        end
-----------------------------------------------------------
-----------------------------------------------------------

    temp_file : STRING is 
                                -- Return unique file
        do
            Result := rt_to_string (rt_temp_file)

            if not file_exists (Result) then
                add_file (Result, "rw.rw.rw")
            end
        end
-----------------------------------------------------------
-- PATHS                                                 --
-----------------------------------------------------------

    path_suffix (path : STRING) : STRING is
                                    -- Extract last component of `path'.
        do
            Result := rt_to_string (rt_path_suffix (path.to_external))
        end
-----------------------------------------------------------

    path_prefix (path : STRING) : STRING is
                                   -- Extract all but last component of `path'
        do
            Result := rt_to_string (rt_path_prefix (path.to_external))

            if Result.empty then
                Result := "."
            end
        end
-----------------------------------------------------------

    concat_paths (path1, path2 : STRING) : STRING is
                                    -- Create concatenation of paths.
        do
            Result := rt_to_string (rt_concat_paths (path1.to_external, 
                                            path2.to_external))
        end
-----------------------------------------------------------

    absolute_path (path : STRING) : STRING is
                                    -- Create absolute path from
                                    -- (possibly) relative path.
        do
            Result := rt_to_string (rt_absolute_path (path.to_external))
        end
-----------------------------------------------------------

    same_path (path1, path2 : STRING) : BOOLEAN is
                                    -- Do `path1' and `path2'
                                    -- actually represent the same
                                    -- physical thing (file/cluster)?
        do
            Result := rt_same_path (path1.to_external,
                                        path2.to_external)
        end
-----------------------------------------------------------

    in_use (path : STRING) : BOOLEAN is
                                    -- Is `path' connected to Eiffel file?
        do
            Result := rt_in_use (path.to_external)
        end
-----------------------------------------------------------
feature {NONE}
-----------------------------------------------------------

    list_cluster (path : STRING, files : BOOLEAN) : SORTED_LIST [FSYS_DAT] is

        local
            name  : STRING
            fd    : FSYS_DAT
            nl    : SORTED_LIST [STRING]
            it    : ITERATOR
            cwd   : STRING
        do
            !!Result.make (false)
            nl := name_list (path, files)

            cwd := Clone (current_cluster)
            change_cluster (path)

            from
                it := nl.iterator
            until
                it.finished
            loop
                name := nl.item (it)

                if files then
                    fd := file_data (name)
                else
                    fd := cluster_data (name)
                end

                Result.add (fd)
                it.forth
            end

            change_cluster (cwd)

        rescue
            change_cluster (cwd)        -- Go back to current cluster
        end
-----------------------------------------------------------

    name_list (path : STRING, files : BOOLEAN) : SORTED_LIST [STRING] is

        local
            name  : STRING
            cwd   : STRING
        do
            cwd := Clone (current_cluster)
            change_cluster (path)

            !!Result.make (true)

            rt_open_cluster (".".to_external, files)

            from
                name := rt_to_string (rt_read_cluster)
            until
                name = Void
            loop
                Result.add (name)
                name := rt_to_string (rt_read_cluster)
            end

            rt_close_cluster
            change_cluster (cwd)

        rescue
            change_cluster (cwd)        -- Go back to current cluster
        end
-----------------------------------------------------------

    rt_open_cluster (path : POINTER, files : BOOLEAN) is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_open_cluster"
        end
-----------------------------------------------------------

    rt_close_cluster is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_close_cluster"
        end
-----------------------------------------------------------

    rt_read_cluster : POINTER is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_read_cluster"
        end
-----------------------------------------------------------

    rt_current_cluster : POINTER is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_current_cluster"
        end
-----------------------------------------------------------

    rt_add_cluster (pathp : POINTER, permp : POINTER) is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_add_cluster"
        end
-----------------------------------------------------------

    rt_remove_cluster (pathp : POINTER) is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_remove_cluster"
        end
-----------------------------------------------------------

    rt_change_cluster (pathp : POINTER) is 

        external "C" -- changed by x_c_cwc
        alias    "RTFS_change_cluster"
        end
-----------------------------------------------------------

    rt_cluster_exists (pathp : POINTER) : BOOLEAN is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_cluster_exists"
        end
-----------------------------------------------------------

    rt_cluster_time (pathp : POINTER) : REAL is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_cluster_time"
        end
-----------------------------------------------------------

    rt_cluster_perm (pathp : POINTER) : POINTER is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_cluster_perm"
        end
-----------------------------------------------------------

    rt_my_cluster_perm (pathp : POINTER) : POINTER is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_my_cluster_perm"
        end
-----------------------------------------------------------

    rt_has_listperm (pathp : POINTER) : BOOLEAN is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_has_listperm"
        end
-----------------------------------------------------------

    rt_has_modperm (pathp : POINTER) : BOOLEAN is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_has_modperm"
        end
-----------------------------------------------------------

    rt_change_cluster_perm (pathp, permp : POINTER) is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_change_cluster_perm"
        end
-----------------------------------------------------------
-----------------------------------------------------------

    rt_add_file (pathp : POINTER, permp : POINTER) is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_add_file"
        end
-----------------------------------------------------------

    rt_remove_file (pathp : POINTER) is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_remove_file"
        end
-----------------------------------------------------------

    rt_rename_file (oldp : POINTER, newp : POINTER) is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_rename_file"
        end
-----------------------------------------------------------

    rt_copy_file (sp, dp : POINTER) is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_copy_file"
        end
-----------------------------------------------------------

    rt_same (fp, sp : POINTER) : BOOLEAN is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_same"
        end
-----------------------------------------------------------

    rt_file_exists (pathp : POINTER) : BOOLEAN is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_file_exists"
        end
-----------------------------------------------------------

    rt_file_time (pathp : POINTER) : REAL is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_file_time"
        end
-----------------------------------------------------------

    rt_file_perm (pathp : POINTER) : POINTER is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_file_perm"
        end
-----------------------------------------------------------

    rt_my_file_perm (pathp : POINTER) : POINTER is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_my_file_perm"
        end
-----------------------------------------------------------

    rt_has_readperm (pathp : POINTER) : BOOLEAN is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_has_readperm"
        end
-----------------------------------------------------------

    rt_has_writeperm (pathp : POINTER) : BOOLEAN is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_has_writeperm"
        end
-----------------------------------------------------------

    rt_has_execperm (pathp : POINTER) : BOOLEAN is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_has_execperm"
        end
-----------------------------------------------------------

    rt_file_count (pathp : POINTER) : INTEGER is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_file_count"
        end
-----------------------------------------------------------

    rt_change_file_perm (pathp, permp : POINTER) is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_change_file_perm"
        end
-----------------------------------------------------------

    rt_access_file (pathp, permp : POINTER, trunc : BOOLEAN) : INTEGER is

        external "C" -- changed by x_c_cwc
        alias    "RTFM_open"
        end
-----------------------------------------------------------

    rt_path_suffix (pathp : POINTER) : POINTER is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_path_suffix"
        end
-----------------------------------------------------------

    rt_path_prefix (pathp : POINTER) : POINTER is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_path_prefix"
        end
-----------------------------------------------------------

    rt_concat_paths (p1, p2 : POINTER) : POINTER is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_concat_paths"
        end
-----------------------------------------------------------

    rt_absolute_path (pathp : POINTER) : POINTER is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_absolute_path"
        end
-----------------------------------------------------------

    rt_same_path (p1, p2 : POINTER) : BOOLEAN is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_same_path"
        end
-----------------------------------------------------------

    rt_to_string (p : POINTER) : STRING is

        external "C" -- changed by x_c_cwc
        alias    "RTC2_c_create"
        end
-----------------------------------------------------------

    rt_identify (h : INTEGER) : POINTER is

        external "C" -- changed by x_c_cwc
        alias    "RTFM_identify"
        end
-----------------------------------------------------------

    rt_temp_file : POINTER is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_temp_file"
        end
-----------------------------------------------------------

    rt_in_use (pathp : POINTER) : BOOLEAN is

        external "C" -- changed by x_c_cwc
        alias    "RTFM_in_use"
        end
-----------------------------------------------------------

    rt_stat_start (pathp : POINTER) is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_stat_start"
        end
-----------------------------------------------------------

    rt_stat_end is

        external "C" -- changed by x_c_cwc
        alias    "RTFS_stat_end"
        end
-----------------------------------------------------------

end -- class FILE_SYSTEM

