Ejemplo n.º 1
0
// If the given path looks like it's relative to the working directory, then prepend that working
// directory. This operates on unescaped paths only (so a ~ means a literal ~).
wcstring path_apply_working_directory(const wcstring &path, const wcstring &working_directory) {
    if (path.empty() || working_directory.empty()) return path;

    // We're going to make sure that if we want to prepend the wd, that the string has no leading
    // "/".
    bool prepend_wd = path.at(0) != L'/' && path.at(0) != HOME_DIRECTORY;
    if (!prepend_wd) {
        // No need to prepend the wd, so just return the path we were given.
        return path;
    }

    // Remove up to one "./".
    wcstring path_component = path;
    if (string_prefixes_string(L"./", path_component)) {
        path_component.erase(0, 2);
    }

    // Removing leading /s.
    while (string_prefixes_string(L"/", path_component)) {
        path_component.erase(0, 1);
    }

    // Construct and return a new path.
    wcstring new_path = working_directory;
    append_path_component(new_path, path_component);
    return new_path;
}
Ejemplo n.º 2
0
    // Given a start point as an absolute path, for any directory that has exactly one non-hidden
    // entity in it which is itself a directory, return that. The result is a relative path. For
    // example, if start_point is '/usr' we may return 'local/bin/'.
    //
    // The result does not have a leading slash, but does have a trailing slash if non-empty.
    wcstring descend_unique_hierarchy(const wcstring &start_point) {
        assert(!start_point.empty() && start_point.at(0) == L'/');

        wcstring unique_hierarchy;
        wcstring abs_unique_hierarchy = start_point;

        bool stop_descent = false;
        DIR *dir;
        while (!stop_descent && (dir = wopendir(abs_unique_hierarchy))) {
            // We keep track of the single unique_entry entry. If we get more than one, it's not
            // unique and we stop the descent.
            wcstring unique_entry;

            bool child_is_dir;
            wcstring child_entry;
            while (wreaddir_resolving(dir, abs_unique_hierarchy, child_entry, &child_is_dir)) {
                if (child_entry.empty() || child_entry.at(0) == L'.') {
                    continue;  // either hidden, or . and .. entries -- skip them
                } else if (child_is_dir && unique_entry.empty()) {
                    unique_entry = child_entry;  // first candidate
                } else {
                    // We either have two or more candidates, or the child is not a directory. We're
                    // done.
                    stop_descent = true;
                    break;
                }
            }

            // We stop if we got two or more entries; also stop if we got zero or were interrupted
            if (unique_entry.empty() || interrupted()) {
                stop_descent = true;
            }

            if (!stop_descent) {
                // We have an entry in the unique hierarchy!
                append_path_component(unique_hierarchy, unique_entry);
                unique_hierarchy.push_back(L'/');

                append_path_component(abs_unique_hierarchy, unique_entry);
                abs_unique_hierarchy.push_back(L'/');
            }
            closedir(dir);
        }
        return unique_hierarchy;
    }
Ejemplo n.º 3
0
/**
   Get the configuration directory. The resulting string needs to be
   free'd. This is mostly the same code as path_get_config(), but had
   to be rewritten to avoid dragging in additional library
   dependencies.
*/
static wcstring fishd_get_config()
{
    wchar_t *xdg_dir, *home;
    bool done = false;
    wcstring result;

    xdg_dir = fishd_env_get(L"XDG_CONFIG_HOME");
    if (xdg_dir)
    {
        result = xdg_dir;
        append_path_component(result, L"/fish");
        if (!create_directory(result))
        {
            done = true;
        }
        free(xdg_dir);
    }
    else
    {
        home = fishd_env_get(L"HOME");
        if (home)
        {
            result = home;
            append_path_component(result, L"/.config/fish");
            if (!create_directory(result))
            {
                done = 1;
            }
            free(home);
        }
    }

    if (! done)
    {
        /* Bad juju */
        debug(0, _(L"Unable to create a configuration directory for fish. Your personal settings will not be saved. Please set the $XDG_CONFIG_HOME variable to a directory where the current user has write access."));
        result.clear();
    }

    return result;
}
Ejemplo n.º 4
0
static wcstring fishd_get_config()
{
    bool done = false;
    wcstring result;

    env_var_t xdg_dir = env_get_string(L"XDG_CONFIG_HOME", ENV_GLOBAL | ENV_EXPORT);
    if (! xdg_dir.missing_or_empty())
    {
        result = xdg_dir;
        append_path_component(result, L"/fish");
        if (!create_directory(result))
        {
            done = true;
        }
    }
    else
    {
        env_var_t home = env_get_string(L"HOME", ENV_GLOBAL | ENV_EXPORT);
        if (! home.missing_or_empty())
        {
            result = home;
            append_path_component(result, L"/.config/fish");
            if (!create_directory(result))
            {
                done = 1;
            }
        }
    }
    
    if (! done)
    {
        /* Bad juju */
        debug(0, _(L"Unable to create a configuration directory for fish. Your personal settings will not be saved. Please set the $XDG_CONFIG_HOME variable to a directory where the current user has write access."));
        result.clear();
    }
    
    return result;
}
Ejemplo n.º 5
0
 // Helper to resolve using our prefix.
 DIR *open_dir(const wcstring &base_dir) const {
     wcstring path = this->working_directory;
     append_path_component(path, base_dir);
     if (flags & EXPAND_SPECIAL_FOR_CD) {
         // cd operates on logical paths.
         // for example, cd ../<tab> should complete "without resolving symlinks".
         path = normalize_path(path);
     } else {
         // Other commands operate on physical paths.
         if (auto tmp = wrealpath(path)) {
             path = tmp.acquire();
         }
     }
     return wopendir(path);
 }
Ejemplo n.º 6
0
    void try_add_completion_result(const wcstring &filepath, const wcstring &filename,
                                   const wcstring &wildcard, const wcstring &prefix) {
        // This function is only for the completions case.
        assert(this->flags & EXPAND_FOR_COMPLETIONS);

        wcstring abs_path = this->working_directory;
        append_path_component(abs_path, filepath);

        // We must normalize the path to allow 'cd ..' to operate on logical paths.
        if (flags & EXPAND_SPECIAL_FOR_CD) abs_path = normalize_path(abs_path);

        size_t before = this->resolved_completions->size();
        if (wildcard_test_flags_then_complete(abs_path, filename, wildcard.c_str(), this->flags,
                                              this->resolved_completions)) {
            // Hack. We added this completion result based on the last component of the wildcard.
            // Prepend our prefix to each wildcard that replaces its token.
            // Note that prepend_token_prefix is a no-op unless COMPLETE_REPLACES_TOKEN is set
            size_t after = this->resolved_completions->size();
            for (size_t i = before; i < after; i++) {
                completion_t *c = &this->resolved_completions->at(i);
                if (this->has_fuzzy_ancestor && !(c->flags & COMPLETE_REPLACES_TOKEN)) {
                    c->flags |= COMPLETE_REPLACES_TOKEN;
                    c->prepend_token_prefix(wildcard);
                }
                c->prepend_token_prefix(prefix);
            }

            // Implement EXPAND_SPECIAL_FOR_CD_AUTOSUGGEST by descending the deepest unique
            // hierarchy we can, and then appending any components to each new result.
            // Only descend deepest unique for cd autosuggest and not for cd tab completion
            // (issue #4402).
            if (flags & EXPAND_SPECIAL_FOR_CD_AUTOSUGGEST) {
                wcstring unique_hierarchy = this->descend_unique_hierarchy(abs_path);
                if (!unique_hierarchy.empty()) {
                    for (size_t i = before; i < after; i++) {
                        completion_t &c = this->resolved_completions->at(i);
                        c.completion.append(unique_hierarchy);
                    }
                }
            }

            this->did_add = true;
        }
    }
Ejemplo n.º 7
0
/* If the given path looks like it's relative to the working directory, then prepend that working directory. This operates on unescaped paths only (so a ~ means a literal ~) */
wcstring path_apply_working_directory(const wcstring &path, const wcstring &working_directory)
{
    if (path.empty() || working_directory.empty())
        return path;
    
    /* We're going to make sure that if we want to prepend the wd, that the string has no leading / */
    bool prepend_wd;
    switch (path.at(0))
    {
        case L'/':
        case HOME_DIRECTORY:
            prepend_wd = false;
            break;
        default:
            prepend_wd = true;
            break;
    }
    
    if (! prepend_wd)
    {
        /* No need to prepend the wd, so just return the path we were given */
        return path;
    }
    else
    {
        /* Remove up to one ./ */
        wcstring path_component = path;
        if (string_prefixes_string(L"./", path_component))
        {
            path_component.erase(0, 2);
        }
        
        /* Removing leading /s */
        while (string_prefixes_string(L"/", path_component))
        {
            path_component.erase(0, 1);
        }
        
        /* Construct and return a new path */
        wcstring new_path = working_directory;
        append_path_component(new_path, path_component);
        return new_path;
    }
}
Ejemplo n.º 8
0
static bool path_get_path_core(const wcstring &cmd, wcstring *out_path,
                               const env_var_t &bin_path_var) {
    int err = ENOENT;
    debug(3, L"path_get_path( '%ls' )", cmd.c_str());

    // If the command has a slash, it must be a full path.
    if (cmd.find(L'/') != wcstring::npos) {
        if (waccess(cmd, X_OK) != 0) {
            return false;
        }

        struct stat buff;
        if (wstat(cmd, &buff)) {
            return false;
        }
        if (S_ISREG(buff.st_mode)) {
            if (out_path) out_path->assign(cmd);
            return true;
        }
        errno = EACCES;
        return false;
    }

    wcstring bin_path;
    if (!bin_path_var.missing()) {
        bin_path = bin_path_var;
    } else {
        if (contains(PREFIX L"/bin", L"/bin", L"/usr/bin")) {
            bin_path = L"/bin" ARRAY_SEP_STR L"/usr/bin";
        } else {
            bin_path = L"/bin" ARRAY_SEP_STR L"/usr/bin" ARRAY_SEP_STR PREFIX L"/bin";
        }
    }

    wcstring nxt_path;
    wcstokenizer tokenizer(bin_path, ARRAY_SEP_STR);
    while (tokenizer.next(nxt_path)) {
        if (nxt_path.empty()) continue;
        append_path_component(nxt_path, cmd);
        if (waccess(nxt_path, X_OK) == 0) {
            struct stat buff;
            if (wstat(nxt_path, &buff) == -1) {
                if (errno != EACCES) {
                    wperror(L"stat");
                }
                continue;
            }
            if (S_ISREG(buff.st_mode)) {
                if (out_path) out_path->swap(nxt_path);
                return true;
            }
            err = EACCES;
        } else {
            switch (errno) {
                case ENOENT:
                case ENAMETOOLONG:
                case EACCES:
                case ENOTDIR: {
                    break;
                }
                default: {
                    debug(1, MISSING_COMMAND_ERR_MSG, nxt_path.c_str());
                    wperror(L"access");
                }
            }
        }
    }

    errno = err;
    return false;
}
Ejemplo n.º 9
0
bool path_get_cdpath(const wcstring &dir, wcstring *out, const wchar_t *wd,
                     const env_vars_snapshot_t &env_vars) {
    int err = ENOENT;
    if (dir.empty()) return false;

    if (wd) {
        size_t len = wcslen(wd);
        assert(wd[len - 1] == L'/');
    }

    wcstring_list_t paths;
    if (dir.at(0) == L'/') {
        // Absolute path.
        paths.push_back(dir);
    } else if (string_prefixes_string(L"./", dir) || string_prefixes_string(L"../", dir) ||
               dir == L"." || dir == L"..") {
        // Path is relative to the working directory.
        wcstring path;
        if (wd) path.append(wd);
        path.append(dir);
        paths.push_back(path);
    } else {
        // Respect CDPATH.
        env_var_t path = env_vars.get(L"CDPATH");
        if (path.missing_or_empty()) path = L".";  // we'll change this to the wd if we have one

        wcstring nxt_path;
        wcstokenizer tokenizer(path, ARRAY_SEP_STR);
        while (tokenizer.next(nxt_path)) {
            if (nxt_path == L"." && wd != NULL) {
                // nxt_path is just '.', and we have a working directory, so use the wd instead.
                // TODO: if nxt_path starts with ./ we need to replace the . with the wd.
                nxt_path = wd;
            }
            expand_tilde(nxt_path);

            // debug( 2, L"woot %ls\n", expanded_path.c_str() );

            if (nxt_path.empty()) continue;

            wcstring whole_path = nxt_path;
            append_path_component(whole_path, dir);
            paths.push_back(whole_path);
        }
    }

    bool success = false;
    for (wcstring_list_t::const_iterator iter = paths.begin(); iter != paths.end(); ++iter) {
        struct stat buf;
        const wcstring &dir = *iter;
        if (wstat(dir, &buf) == 0) {
            if (S_ISDIR(buf.st_mode)) {
                success = true;
                if (out) out->assign(dir);
                break;
            } else {
                err = ENOTDIR;
            }
        }
    }

    if (!success) errno = err;
    return success;
}
Ejemplo n.º 10
0
bool path_get_cdpath_string(const wcstring &dir_str, wcstring &result, const env_var_t &cdpath)
{
    wchar_t *res = 0;
    int err = ENOENT;
    bool success = false;

    const wchar_t *const dir = dir_str.c_str();
    if (dir[0] == L'/'|| (wcsncmp(dir, L"./", 2)==0))
    {
        struct stat buf;
        if (wstat(dir, &buf) == 0)
        {
            if (S_ISDIR(buf.st_mode))
            {
                result = dir_str;
                success = true;
            }
            else
            {
                err = ENOTDIR;
            }

        }
    }
    else
    {

        wcstring path = L".";

        // Respect CDPATH
        env_var_t cdpath = env_get_string(L"CDPATH");
        if (! cdpath.missing_or_empty())
        {
            path = cdpath.c_str();
        }

        wcstokenizer tokenizer(path, ARRAY_SEP_STR);
        wcstring next_path;
        while (tokenizer.next(next_path))
        {
            expand_tilde(next_path);
            if (next_path.size() == 0) continue;

            wcstring whole_path = next_path;
            append_path_component(whole_path, dir);

            struct stat buf;
            if (wstat(whole_path, &buf) == 0)
            {
                if (S_ISDIR(buf.st_mode))
                {
                    result = whole_path;
                    success = true;
                    break;
                }
                else
                {
                    err = ENOTDIR;
                }
            }
            else
            {
                if (lwstat(whole_path, &buf) == 0)
                {
                    err = EROTTEN;
                }
            }
        }
    }


    if (!success)
    {
        errno = err;
    }

    return res;
}
Ejemplo n.º 11
0
static bool path_get_path_core(const wcstring &cmd, wcstring *out_path,
                               const env_var_t &bin_path_var) {
    int err = ENOENT;
    debug(3, L"path_get_path( '%ls' )", cmd.c_str());

    // If the command has a slash, it must be a full path.
    if (cmd.find(L'/') != wcstring::npos) {
        if (waccess(cmd, X_OK) != 0) {
            return false;
        }

        struct stat buff;
        if (wstat(cmd, &buff)) {
            return false;
        }
        if (S_ISREG(buff.st_mode)) {
            if (out_path) out_path->assign(cmd);
            return true;
        }
        errno = EACCES;
        return false;
    }

    wcstring bin_path;
    if (!bin_path_var.missing()) {
        bin_path = bin_path_var;
    } else {
        // Note that PREFIX is defined in the `Makefile` and is thus defined when this module is
        // compiled. This ensures we always default to "/bin", "/usr/bin" and the bin dir defined
        // for the fish programs. Possibly with a duplicate dir if PREFIX is empty, "/", "/usr" or
        // "/usr/". If the PREFIX duplicates /bin or /usr/bin that is harmless other than a trivial
        // amount of time testing a path we've already tested.
        bin_path = L"/bin" ARRAY_SEP_STR L"/usr/bin" ARRAY_SEP_STR PREFIX L"/bin";
    }

    std::vector<wcstring> pathsv;
    tokenize_variable_array(bin_path, pathsv);
    for (auto next_path : pathsv) {
        if (next_path.empty()) continue;
        append_path_component(next_path, cmd);
        if (waccess(next_path, X_OK) == 0) {
            struct stat buff;
            if (wstat(next_path, &buff) == -1) {
                if (errno != EACCES) {
                    wperror(L"stat");
                }
                continue;
            }
            if (S_ISREG(buff.st_mode)) {
                if (out_path) *out_path = std::move(next_path);
                return true;
            }
            err = EACCES;
        } else {
            switch (errno) {
                case ENOENT:
                case ENAMETOOLONG:
                case EACCES:
                case ENOTDIR: {
                    break;
                }
                default: {
                    debug(1, MISSING_COMMAND_ERR_MSG, next_path.c_str());
                    wperror(L"access");
                    break;
                }
            }
        }
    }

    errno = err;
    return false;
}