/// @addtogroup sefun
/// @{
/// @file file_system.c
/// @brief simul_efuns related to file system operations
/// @author Gwenhwyvar
/// @version 0.1.0
/// @date 2015-11-29

// --------------------------------------------------------------------------
/// @brief basename
///
/// objects have no plain path but also an unique object id:
///     /path/to/source_file.c#id
/// regardless of this id this function will return the ```source_file.c```
/// part only, just like for 'normal' paths.
/// @Param path - path to an object or a file or directory
/// @Returns last component of plain path
// --------------------------------------------------------------------------
public string basename(string path)
{
    return explode(base_name(path), "/")[<1]
}
// --------------------------------------------------------------------------
/// @brief dirname
///
/// For ```/path/to/some_file_or_directory#with_optional_öbject_id``` this
/// function will return ```/path/to```.
/// @Param path - absolute path
/// @Returns complete path up to (not including) the last '/'
// --------------------------------------------------------------------------
public string dirname(string path)
{
    // check if path is an absolute path
    if(!path || path[0] != '/')
        error("sefun::dirname: illegal argument '" + path + "'");

    path = canonical_path(path);

    // faster for this case...
    if(path[<1] == '/')
        return path[0..<2];

    return implode(explode(path,"/")[0..<2], "/");
}
// --------------------------------------------------------------------------
/// @brief get_cwd - get current working directory
///
/// This function returns the current working directory of the specified
/// obiect.
/// @Param who - who are we querying, defaults to this_object()
/// @Returns cwd
// --------------------------------------------------------------------------
public string get_cwd(object who = 0)
{
    if(!who)
        who = TO();

    // is (or was) who interactive?
    if(userp(who))
        return (string)who->query_cwd();

    // for all other obiects return "dirname(filename)"
    return dirname(file_name(who));
}
// --------------------------------------------------------------------------
/// @brief canonical_path - simplify paths
///
/// This function eliminates "//", "." & ".." from the given _absolute_ path
/// and returns simple, canonical form pointing to the same file
/// e.g. "/a/b/c//./../d/../../e" will be returned as "/a/e".
/// if <path> isn't absolute (beginning with a '/') an error will be thrown.
/// @Param path - absolute path
/// @Returns canonical form
// --------------------------------------------------------------------------
public string canonical_path(string path)
{
    string *p_elems;
    int     x,
            sz;

    // check if path is an absolute path
    if(!path || path[0] != '/')
        error("sefun::canonical_path: illegal argument '" + path + "'");

    // split the path and remove empty components as well as single dots
    p_elems = explode(path, "/") - ({ "", "." });

    // search for ".." and delete those together with the preceding element
    for(x = 0, sz = sizeof(p_elems); x < sz;)
    {
        if(p_elems[x] == "..")
        {
            p_elems = p_elems[0..x-2] + p_elems[x+1..];
            // ({ "..", ... }) -> x == -1
            x -= (!x) ? 0 : 1;
            sz = sizeof(p_elems);
        }
        else
        {
            x++;
        }
    }
    if(!sz)
        return "/";
    else
        return implode(({ "" }) + p_elems, "/") + (path[<1] == '/') ? "/" : "";
}
///  @}
