Exemplo n.º 1
0
bool wildcard_match(const wcstring &str, const wcstring &wc, bool leading_dots_fail_to_match)
{
    return wildcard_match_internal(str.c_str(), wc.c_str(), leading_dots_fail_to_match, true /* first */);
}
Exemplo n.º 2
0
static bool is_read_only(const wcstring &key)
{
    return env_read_only.find(key.c_str()) != env_read_only.end();
}
Exemplo n.º 3
0
void env_init(const struct config_paths_t *paths /* or NULL */)
{
    /*
      env_read_only variables can not be altered directly by the user
    */

    const wchar_t * const ro_keys[] =
    {
        L"status",
        L"history",
        L"version",
        L"_",
        L"LINES",
        L"COLUMNS",
        L"PWD",
        //L"SHLVL", // will be inserted a bit lower down
        L"FISH_VERSION",
    };
    for (size_t i=0; i < sizeof ro_keys / sizeof *ro_keys; i++)
    {
        env_read_only.insert(ro_keys[i]);
    }

    /*
       Names of all dynamically calculated variables
       */
    env_electric.insert(L"history");
    env_electric.insert(L"status");
    env_electric.insert(L"umask");
    env_electric.insert(L"COLUMNS");
    env_electric.insert(L"LINES");

    top = new env_node_t;
    global_env = top;
    global = &top->env;

    /*
      Now the environemnt variable handling is set up, the next step
      is to insert valid data
    */

    /*
      Import environment variables
    */
    for (char **p = (environ ? environ : __environ); p && *p; p++)
    {
        const wcstring key_and_val = str2wcstring(*p); //like foo=bar
        size_t eql = key_and_val.find(L'=');
        if (eql == wcstring::npos)
        {
            // no equals found
            if (is_read_only(key_and_val) || is_electric(key_and_val)) continue;
            env_set(key_and_val, L"", ENV_EXPORT | ENV_GLOBAL);
        }
        else
        {
            wcstring key = key_and_val.substr(0, eql);
            if (is_read_only(key) || is_electric(key)) continue;
            wcstring val = key_and_val.substr(eql + 1);
            if (variable_is_colon_delimited_array(key))
            {
                std::replace(val.begin(), val.end(), L':', ARRAY_SEP);
            }

            env_set(key, val.c_str(), ENV_EXPORT | ENV_GLOBAL);
        }
    }

    /* Set the given paths in the environment, if we have any */
    if (paths != NULL)
    {
        env_set(FISH_DATADIR_VAR, paths->data.c_str(), ENV_GLOBAL | ENV_EXPORT);
        env_set(FISH_SYSCONFDIR_VAR, paths->sysconf.c_str(), ENV_GLOBAL | ENV_EXPORT);
        env_set(FISH_HELPDIR_VAR, paths->doc.c_str(), ENV_GLOBAL | ENV_EXPORT);
        env_set(FISH_BIN_DIR, paths->bin.c_str(), ENV_GLOBAL | ENV_EXPORT);
    }

    /*
      Set up the PATH variable
    */
    setup_path();

    /*
      Set up the USER variable
    */
    if (env_get_string(L"USER").missing_or_empty())
    {
        const struct passwd *pw = getpwuid(getuid());
        if (pw && pw->pw_name)
        {
            const wcstring uname = str2wcstring(pw->pw_name);
            env_set(L"USER", uname.c_str(), ENV_GLOBAL | ENV_EXPORT);
        }
    }

    /*
      Set up the version variables
    */
    wcstring version = str2wcstring(get_fish_version());
    env_set(L"version", version.c_str(), ENV_GLOBAL);
    env_set(L"FISH_VERSION", version.c_str(), ENV_GLOBAL);

    /*
      Set up SHLVL variable
    */
    const env_var_t shlvl_str = env_get_string(L"SHLVL");
    wcstring nshlvl_str = L"1";
    if (! shlvl_str.missing())
    {
        wchar_t *end;
        long shlvl_i = wcstol(shlvl_str.c_str(), &end, 10);
        while (iswspace(*end)) ++end; /* skip trailing whitespace */
        if (shlvl_i >= 0 && *end == '\0')
        {
            nshlvl_str = to_string<long>(shlvl_i + 1);
        }
    }
    env_set(L"SHLVL", nshlvl_str.c_str(), ENV_GLOBAL | ENV_EXPORT);
    env_read_only.insert(L"SHLVL");

    /* Set up the HOME variable */
    if (env_get_string(L"HOME").missing_or_empty())
    {
        const env_var_t unam = env_get_string(L"USER");
        char *unam_narrow = wcs2str(unam.c_str());
        struct passwd *pw = getpwnam(unam_narrow);
        if (pw->pw_dir != NULL)
        {
            const wcstring dir = str2wcstring(pw->pw_dir);
            env_set(L"HOME", dir.c_str(), ENV_GLOBAL | ENV_EXPORT);
        }
        free(unam_narrow);
    }

    /* Set PWD */
    env_set_pwd();

    /* Set up universal variables. The empty string means to use the deafult path. */
    assert(s_universal_variables == NULL);
    s_universal_variables = new env_universal_t(L"");
    s_universal_variables->load();

    /* Set g_log_forks */
    env_var_t log_forks = env_get_string(L"fish_log_forks");
    g_log_forks = ! log_forks.missing_or_empty() && from_string<bool>(log_forks);

    /* Set g_use_posix_spawn. Default to true. */
    env_var_t use_posix_spawn = env_get_string(L"fish_use_posix_spawn");
    g_use_posix_spawn = (use_posix_spawn.missing_or_empty() ? true : from_string<bool>(use_posix_spawn));

    /* Set fish_bind_mode to "default" */
    env_set(FISH_BIND_MODE_VAR, DEFAULT_BIND_MODE, ENV_GLOBAL);

    /*
      Now that the global scope is fully initialized, add a toplevel local
      scope. This same local scope will persist throughout the lifetime of the
      fish process, and it will ensure that `set -l` commands run at the
      command-line don't affect the global scope.
    */
    env_push(false);
}
Exemplo n.º 4
0
int job_reap(bool allow_interactive) {
    ASSERT_IS_MAIN_THREAD();
    job_t *jnext;
    int found = 0;

    // job_reap may fire an event handler, we do not want to call ourselves recursively (to avoid
    // infinite recursion).
    static bool locked = false;
    if (locked) {
        return 0;
    }
    locked = true;

    // this may be invoked in an exit handler, after the TERM has been torn down
    // don't try to print in that case (#3222)
    const bool interactive = allow_interactive && cur_term != NULL;

    process_mark_finished_children(false);

    // Preserve the exit status.
    const int saved_status = proc_get_last_status();

    job_iterator_t jobs;
    const size_t job_count = jobs.count();
    jnext = jobs.next();
    while (jnext) {
        job_t *j = jnext;
        jnext = jobs.next();

        // If we are reaping only jobs who do not need status messages sent to the console, do not
        // consider reaping jobs that need status messages.
        if ((!job_get_flag(j, JOB_SKIP_NOTIFICATION)) && (!interactive) &&
            (!job_get_flag(j, JOB_FOREGROUND))) {
            continue;
        }

        for (process_t *p = j->first_process; p; p = p->next) {
            int s;
            if (!p->completed) continue;

            if (!p->pid) continue;

            s = p->status;

            proc_fire_event(L"PROCESS_EXIT", EVENT_EXIT, p->pid,
                            (WIFSIGNALED(s) ? -1 : WEXITSTATUS(s)));

            // Ignore signal SIGPIPE.We issue it ourselves to the pipe writer when the pipe reader
            // dies.
            if (!WIFSIGNALED(s) || WTERMSIG(s) == SIGPIPE) {
                continue;
            }

            // Handle signals other than SIGPIPE.
            int proc_is_job = ((p == j->first_process) && (p->next == 0));
            if (proc_is_job) job_set_flag(j, JOB_NOTIFIED, 1);
            if (job_get_flag(j, JOB_SKIP_NOTIFICATION)) {
                continue;
            }

            // Print nothing if we get SIGINT in the foreground process group, to avoid spamming
            // obvious stuff on the console (#1119). If we get SIGINT for the foreground
            // process, assume the user typed ^C and can see it working. It's possible they
            // didn't, and the signal was delivered via pkill, etc., but the SIGINT/SIGTERM
            // distinction is precisely to allow INT to be from a UI
            // and TERM to be programmatic, so this assumption is keeping with the design of
            // signals. If echoctl is on, then the terminal will have written ^C to the console.
            // If off, it won't have. We don't echo ^C either way, so as to respect the user's
            // preference.
            if (WTERMSIG(p->status) != SIGINT || !job_get_flag(j, JOB_FOREGROUND)) {
                if (proc_is_job) {
                    // We want to report the job number, unless it's the only job, in which case
                    // we don't need to.
                    const wcstring job_number_desc =
                        (job_count == 1) ? wcstring() : format_string(L"Job %d, ", j->job_id);
                    fwprintf(stdout, _(L"%ls: %ls\'%ls\' terminated by signal %ls (%ls)"),
                             program_name, job_number_desc.c_str(),
                             truncate_command(j->command()).c_str(), sig2wcs(WTERMSIG(p->status)),
                             signal_get_desc(WTERMSIG(p->status)));
                } else {
                    const wcstring job_number_desc =
                        (job_count == 1) ? wcstring() : format_string(L"from job %d, ", j->job_id);
                    fwprintf(stdout, _(L"%ls: Process %d, \'%ls\' %ls\'%ls\' "
                                       L"terminated by signal %ls (%ls)"),
                             program_name, p->pid, p->argv0(), job_number_desc.c_str(),
                             truncate_command(j->command()).c_str(), sig2wcs(WTERMSIG(p->status)),
                             signal_get_desc(WTERMSIG(p->status)));
                }

                if (cur_term != NULL) {
                    tputs(clr_eol, 1, &writeb);
                } else {
                    fwprintf(stdout, L"\x1b[K");  // no term set up - do clr_eol manually
                }
                fwprintf(stdout, L"\n");
            }
            found = 1;
            p->status = 0;  // clear status so it is not reported more than once
        }

        // If all processes have completed, tell the user the job has completed and delete it from
        // the active job list.
        if (job_is_completed(j)) {
            if (!job_get_flag(j, JOB_FOREGROUND) && !job_get_flag(j, JOB_NOTIFIED) &&
                !job_get_flag(j, JOB_SKIP_NOTIFICATION)) {
                format_job_info(j, _(L"ended"), job_count);
                found = 1;
            }
            proc_fire_event(L"JOB_EXIT", EVENT_EXIT, -j->pgid, 0);
            proc_fire_event(L"JOB_EXIT", EVENT_JOB_ID, j->job_id, 0);

            job_free(j);
        } else if (job_is_stopped(j) && !job_get_flag(j, JOB_NOTIFIED)) {
            // Notify the user about newly stopped jobs.
            if (!job_get_flag(j, JOB_SKIP_NOTIFICATION)) {
                format_job_info(j, _(L"stopped"), job_count);
                found = 1;
            }
            job_set_flag(j, JOB_NOTIFIED, 1);
        }
    }

    if (found) fflush(stdout);

    // Restore the exit status.
    proc_set_last_status(saved_status);

    locked = false;

    return found;
}
Exemplo n.º 5
0
void parse_util_get_parameter_info(const wcstring &cmd, const size_t pos, wchar_t *quote, size_t *offset, int *type)
{
    size_t prev_pos=0;
    wchar_t last_quote = '\0';
    int unfinished;

    tokenizer_t tok(cmd.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS);
    for (; tok_has_next(&tok); tok_next(&tok))
    {
        if (tok_get_pos(&tok) > pos)
            break;

        if (tok_last_type(&tok) == TOK_STRING)
            last_quote = get_quote(tok_last(&tok),
                                   pos - tok_get_pos(&tok));

        if (type != NULL)
            *type = tok_last_type(&tok);

        prev_pos = tok_get_pos(&tok);
    }

    wchar_t *cmd_tmp = wcsdup(cmd.c_str());
    cmd_tmp[pos]=0;
    size_t cmdlen = wcslen(cmd_tmp);
    unfinished = (cmdlen==0);
    if (!unfinished)
    {
        unfinished = (quote != 0);

        if (!unfinished)
        {
            if (wcschr(L" \t\n\r", cmd_tmp[cmdlen-1]) != 0)
            {
                if ((cmdlen == 1) || (cmd_tmp[cmdlen-2] != L'\\'))
                {
                    unfinished=1;
                }
            }
        }
    }

    if (quote)
        *quote = last_quote;

    if (offset != 0)
    {
        if (!unfinished)
        {
            while ((cmd_tmp[prev_pos] != 0) && (wcschr(L";|",cmd_tmp[prev_pos])!= 0))
                prev_pos++;

            *offset = prev_pos;
        }
        else
        {
            *offset = pos;
        }
    }
    free(cmd_tmp);
}
Exemplo n.º 6
0
static screen_layout_t compute_layout(screen_t *s,
                                      size_t screen_width,
                                      const wcstring &left_prompt_str,
                                      const wcstring &right_prompt_str,
                                      const wcstring &commandline,
                                      const wcstring &autosuggestion_str,
                                      const int *indent)
{
    screen_layout_t result = {};

    /* Start by ensuring that the prompts themselves can fit */
    const wchar_t *left_prompt = left_prompt_str.c_str();
    const wchar_t *right_prompt = right_prompt_str.c_str();
    const wchar_t *autosuggestion = autosuggestion_str.c_str();

    prompt_layout_t left_prompt_layout = calc_prompt_layout(left_prompt);
    prompt_layout_t right_prompt_layout = calc_prompt_layout(right_prompt);

    size_t left_prompt_width = left_prompt_layout.last_line_width;
    size_t right_prompt_width = right_prompt_layout.last_line_width;

    if (left_prompt_layout.max_line_width > screen_width)
    {
        /* If we have a multi-line prompt, see if the longest line fits; if not neuter the whole left prompt */
        left_prompt = L"> ";
        left_prompt_width = 2;
    }

    if (left_prompt_width + right_prompt_width >= screen_width)
    {
        /* Nix right_prompt */
        right_prompt = L"";
        right_prompt_width = 0;
    }

    if (left_prompt_width + right_prompt_width >= screen_width)
    {
        /* Still doesn't fit, neuter left_prompt */
        left_prompt = L"> ";
        left_prompt_width = 2;
    }

    /* Now we should definitely fit */
    assert(left_prompt_width + right_prompt_width < screen_width);


    /* Convert commandline to a list of lines and their widths */
    wcstring_list_t command_lines(1);
    std::vector<size_t> line_widths(1);
    for (size_t i=0; i < commandline.size(); i++)
    {
        wchar_t c = commandline.at(i);
        if (c == L'\n')
        {
            /* Make a new line */
            command_lines.push_back(wcstring());
            line_widths.push_back(indent[i]*INDENT_STEP);
        }
        else
        {
            command_lines.back() += c;
            line_widths.back() += fish_wcwidth_min_0(c);
        }
    }
    const size_t first_command_line_width = line_widths.at(0);

    /* If we have more than one line, ensure we have no autosuggestion */
    if (command_lines.size() > 1)
    {
        autosuggestion = L"";
    }

    /* Compute the width of the autosuggestion at all possible truncation offsets */
    std::vector<size_t> autosuggestion_truncated_widths;
    autosuggestion_truncated_widths.reserve(1 + wcslen(autosuggestion));
    size_t autosuggestion_total_width = 0;
    for (size_t i=0; autosuggestion[i] != L'\0'; i++)
    {
        autosuggestion_truncated_widths.push_back(autosuggestion_total_width);
        autosuggestion_total_width += fish_wcwidth_min_0(autosuggestion[i]);
    }

    /* Here are the layouts we try in turn:

    1. Left prompt visible, right prompt visible, command line visible, autosuggestion visible
    2. Left prompt visible, right prompt visible, command line visible, autosuggestion truncated (possibly to zero)
    3. Left prompt visible, right prompt hidden, command line visible, autosuggestion hidden
    4. Newline separator (left prompt visible, right prompt hidden, command line visible, autosuggestion visible)

    A remark about layout #4: if we've pushed the command line to a new line, why can't we draw the right prompt? The issue is resizing: if you resize the window smaller, then the right prompt will wrap to the next line. This means that we can't go back to the line that we were on, and things turn to chaos very quickly.

    */

    bool done = false;

    /* Case 1 */
    if (! done && left_prompt_width + right_prompt_width + first_command_line_width + autosuggestion_total_width < screen_width)
    {
        result.left_prompt = left_prompt;
        result.left_prompt_space = left_prompt_width;
        result.right_prompt = right_prompt;
        result.autosuggestion = autosuggestion;
        done = true;
    }

    /* Case 2. Note that we require strict inequality so that there's always at least one space between the left edge and the rprompt */
    if (! done && left_prompt_width + right_prompt_width + first_command_line_width < screen_width)
    {
        result.left_prompt = left_prompt;
        result.left_prompt_space = left_prompt_width;
        result.right_prompt = right_prompt;

        /* Need at least two characters to show an autosuggestion */
        size_t available_autosuggestion_space = screen_width - (left_prompt_width + right_prompt_width + first_command_line_width);
        if (autosuggestion_total_width > 0 && available_autosuggestion_space > 2)
        {
            size_t truncation_offset = truncation_offset_for_width(autosuggestion_truncated_widths, available_autosuggestion_space - 2);
            result.autosuggestion = wcstring(autosuggestion, truncation_offset);
            result.autosuggestion.push_back(ellipsis_char);
        }
        done = true;
    }

    /* Case 3 */
    if (! done && left_prompt_width + first_command_line_width < screen_width)
    {
        result.left_prompt = left_prompt;
        result.left_prompt_space = left_prompt_width;
        done = true;
    }

    /* Case 4 */
    if (! done)
    {
        result.left_prompt = left_prompt;
        result.left_prompt_space = left_prompt_width;
        // See remark about for why we can't use the right prompt here
        //result.right_prompt = right_prompt;
        result.prompts_get_own_line = true;
        done = true;
    }

    assert(done);
    return result;
}
Exemplo n.º 7
0
static wcstring file_get_desc(const wcstring &filename,
                              int lstat_res,
                              struct stat lbuf,
                              int stat_res,
                              struct stat buf,
                              int err)
{
    const wchar_t *suffix;

    if (!lstat_res)
    {
        if (S_ISLNK(lbuf.st_mode))
        {
            if (!stat_res)
            {
                if (S_ISDIR(buf.st_mode))
                {
                    return COMPLETE_DIRECTORY_SYMLINK_DESC;
                }
                else
                {

                    if ((buf.st_mode & S_IXUSR) ||
                            (buf.st_mode & S_IXGRP) ||
                            (buf.st_mode & S_IXOTH))
                    {

                        if (waccess(filename, X_OK) == 0)
                        {
                            /*
                              Weird group permissions and other such
                              issues make it non-trivial to find out
                              if we can actually execute a file using
                              the result from stat. It is much safer
                              to use the access function, since it
                              tells us exactly what we want to know.
                            */
                            return COMPLETE_EXEC_LINK_DESC;
                        }
                    }
                }

                return COMPLETE_SYMLINK_DESC;

            }
            else
            {
                switch (err)
                {
                    case ENOENT:
                    {
                        return COMPLETE_ROTTEN_SYMLINK_DESC;
                    }

                    case ELOOP:
                    {
                        return COMPLETE_LOOP_SYMLINK_DESC;
                    }
                }
                /*
                  On unknown errors we do nothing. The file will be
                  given the default 'File' description or one based on the suffix.
                */
            }

        }
        else if (S_ISCHR(buf.st_mode))
        {
            return COMPLETE_CHAR_DESC;
        }
        else if (S_ISBLK(buf.st_mode))
        {
            return COMPLETE_BLOCK_DESC;
        }
        else if (S_ISFIFO(buf.st_mode))
        {
            return COMPLETE_FIFO_DESC;
        }
        else if (S_ISSOCK(buf.st_mode))
        {
            return COMPLETE_SOCKET_DESC;
        }
        else if (S_ISDIR(buf.st_mode))
        {
            return COMPLETE_DIRECTORY_DESC;
        }
        else
        {
            if ((buf.st_mode & S_IXUSR) ||
                    (buf.st_mode & S_IXGRP) ||
                    (buf.st_mode & S_IXOTH))
            {

                if (waccess(filename, X_OK) == 0)
                {
                    /*
                      Weird group permissions and other such issues
                      make it non-trivial to find out if we can
                      actually execute a file using the result from
                      stat. It is much safer to use the access
                      function, since it tells us exactly what we want
                      to know.
                    */
                    return COMPLETE_EXEC_DESC;
                }
            }
        }
    }

    suffix = wcsrchr(filename.c_str(), L'.');
    if (suffix != 0 && !wcsrchr(suffix, L'/'))
    {
        return complete_get_desc_suffix(suffix);
    }

    return COMPLETE_FILE_DESC ;
}
Exemplo n.º 8
0
static int is_quotable(const wcstring &str) { return is_quotable(str.c_str()); }
Exemplo n.º 9
0
/// Expand all environment variables in the string *ptr.
///
/// This function is slow, fragile and complicated. There are lots of little corner cases, like
/// $$foo should do a double expansion, $foo$bar should not double expand bar, etc. Also, it's easy
/// to accidentally leak memory on array out of bounds errors an various other situations. All in
/// all, this function should be rewritten, split out into multiple logical units and carefully
/// tested. After that, it can probably be optimized to do fewer memory allocations, fewer string
/// scans and overall just less work. But until that happens, don't edit it unless you know exactly
/// what you are doing, and do proper testing afterwards.
///
/// This function operates on strings backwards, starting at last_idx.
///
/// Note: last_idx is considered to be where it previously finished procesisng. This means it
/// actually starts operating on last_idx-1. As such, to process a string fully, pass string.size()
/// as last_idx instead of string.size()-1.
static bool expand_variables(const wcstring &instr, std::vector<completion_t> *out, size_t last_idx,
                             parse_error_list_t *errors) {
    const size_t insize = instr.size();

    // last_idx may be 1 past the end of the string, but no further.
    assert(last_idx <= insize && "Invalid last_idx");
    if (last_idx == 0) {
        append_completion(out, instr);
        return true;
    }

    // Locate the last VARIABLE_EXPAND or VARIABLE_EXPAND_SINGLE
    bool is_single = false;
    size_t varexp_char_idx = last_idx;
    while (varexp_char_idx--) {
        const wchar_t c = instr.at(varexp_char_idx);
        if (c == VARIABLE_EXPAND || c == VARIABLE_EXPAND_SINGLE) {
            is_single = (c == VARIABLE_EXPAND_SINGLE);
            break;
        }
    }
    if (varexp_char_idx >= instr.size()) {
        // No variable expand char, we're done.
        append_completion(out, instr);
        return true;
    }

    // Get the variable name.
    const size_t var_name_start = varexp_char_idx + 1;
    size_t var_name_stop = var_name_start;
    while (var_name_stop < insize) {
        const wchar_t nc = instr.at(var_name_stop);
        if (nc == VARIABLE_EXPAND_EMPTY) {
            var_name_stop++;
            break;
        }
        if (!valid_var_name_char(nc)) break;
        var_name_stop++;
    }
    assert(var_name_stop >= var_name_start && "Bogus variable name indexes");
    const size_t var_name_len = var_name_stop - var_name_start;

    // It's an error if the name is empty.
    if (var_name_len == 0) {
        if (errors) {
            parse_util_expand_variable_error(instr, 0 /* global_token_pos */, varexp_char_idx,
                                             errors);
        }
        return false;
    }

    // Get the variable name as a string, then try to get the variable from env.
    const wcstring var_name(instr, var_name_start, var_name_len);
    // Do a dirty hack to make sliced history fast (#4650). We expand from either a variable, or a
    // history_t. Note that "history" is read only in env.cpp so it's safe to special-case it in
    // this way (it cannot be shadowed, etc).
    history_t *history = nullptr;
    maybe_t<env_var_t> var{};
    if (var_name == L"history") {
        // We do this only on the main thread, matching env.cpp.
        if (is_main_thread()) {
            history = reader_get_history();
        }
    } else if (var_name != wcstring{VARIABLE_EXPAND_EMPTY}) {
        var = env_get(var_name);
    }

    // Parse out any following slice.
    // Record the end of the variable name and any following slice.
    size_t var_name_and_slice_stop = var_name_stop;
    bool all_values = true;
    const size_t slice_start = var_name_stop;
    // List of indexes, and parallel array of source positions of each index in the variable list.
    std::vector<long> var_idx_list;
    std::vector<size_t> var_pos_list;
    if (slice_start < insize && instr.at(slice_start) == L'[') {
        all_values = false;
        const wchar_t *in = instr.c_str();
        wchar_t *slice_end;
        // If a variable is missing, behave as though we have one value, so that $var[1] always
        // works.
        size_t effective_val_count = 1;
        if (var) {
            effective_val_count = var->as_list().size();
        } else if (history) {
            effective_val_count = history->size();
        }
        size_t bad_pos = parse_slice(in + slice_start, &slice_end, var_idx_list, var_pos_list,
                                     effective_val_count);
        if (bad_pos != 0) {
            append_syntax_error(errors, slice_start + bad_pos, L"Invalid index value");
            return false;
        }
        var_name_and_slice_stop = (slice_end - in);
    }

    if (!var && !history) {
        // Expanding a non-existent variable.
        if (!is_single) {
            // Normal expansions of missing variables successfully expand to nothing.
            return true;
        } else {
            // Expansion to single argument.
            // Replace the variable name and slice with VARIABLE_EXPAND_EMPTY.
            wcstring res(instr, 0, varexp_char_idx);
            if (!res.empty() && res.back() == VARIABLE_EXPAND_SINGLE) {
                res.push_back(VARIABLE_EXPAND_EMPTY);
            }
            res.append(instr, var_name_and_slice_stop, wcstring::npos);
            return expand_variables(res, out, varexp_char_idx, errors);
        }
    }

    // Ok, we have a variable or a history. Let's expand it.
    // Start by respecting the sliced elements.
    assert((var || history) && "Should have variable or history here");
    wcstring_list_t var_item_list;
    if (all_values) {
        if (history) {
            history->get_history(var_item_list);
        } else {
            var->to_list(var_item_list);
        }
    } else {
        // We have to respect the slice.
        if (history) {
            // Ask history to map indexes to item strings.
            // Note this may have missing entries for out-of-bounds.
            auto item_map = history->items_at_indexes(var_idx_list);
            for (long item_index : var_idx_list) {
                auto iter = item_map.find(item_index);
                if (iter != item_map.end()) {
                    var_item_list.push_back(iter->second);
                }
            }
        } else {
            const wcstring_list_t &all_var_items = var->as_list();
            for (long item_index : var_idx_list) {
                // Check that we are within array bounds. If not, skip the element. Note:
                // Negative indices (`echo $foo[-1]`) are already converted to positive ones
                // here, So tmp < 1 means it's definitely not in.
                // Note we are 1-based.
                if (item_index >= 1 && size_t(item_index) <= all_var_items.size()) {
                    var_item_list.push_back(all_var_items.at(item_index - 1));
                }
            }
        }
    }

    if (is_single) {
        wcstring res(instr, 0, varexp_char_idx);
        if (!res.empty()) {
            if (res.back() != VARIABLE_EXPAND_SINGLE) {
                res.push_back(INTERNAL_SEPARATOR);
            } else if (var_item_list.empty() || var_item_list.front().empty()) {
                // First expansion is empty, but we need to recursively expand.
                res.push_back(VARIABLE_EXPAND_EMPTY);
            }
        }

        // Append all entries in var_item_list, separated by spaces.
        // Remove the last space.
        if (!var_item_list.empty()) {
            for (const wcstring &item : var_item_list) {
                res.append(item);
                res.push_back(L' ');
            }
            res.pop_back();
        }
        res.append(instr, var_name_and_slice_stop, wcstring::npos);
        return expand_variables(res, out, varexp_char_idx, errors);
    } else {
        // Normal cartesian-product expansion.
        for (const wcstring &item : var_item_list) {
            if (varexp_char_idx == 0 && var_name_and_slice_stop == insize) {
                append_completion(out, item);
            } else {
                wcstring new_in(instr, 0, varexp_char_idx);
                if (!new_in.empty()) {
                    if (new_in.back() != VARIABLE_EXPAND) {
                        new_in.push_back(INTERNAL_SEPARATOR);
                    } else if (item.empty()) {
                        new_in.push_back(VARIABLE_EXPAND_EMPTY);
                    }
                }
                new_in.append(item);
                new_in.append(instr, var_name_and_slice_stop, wcstring::npos);
                if (!expand_variables(new_in, out, varexp_char_idx, errors)) {
                    return false;
                }
            }
        }
    }
    return true;
}
Exemplo n.º 10
0
/**
    Set the current bind mode
*/
void input_set_bind_mode(const wcstring &bm)
{
    env_set(FISH_BIND_MODE_VAR, bm.c_str(), ENV_GLOBAL);
}
Exemplo n.º 11
0
int main(int argc, char **argv)
{
    int i;
    int is_quoted=0;
    wcstring_list_t comp;
    wcstring prefix;

    int mangle_descriptors = 0;
    int result_fd = -1;
    set_main_thread();
    setup_fork_guards();

    /*
      This initialization is made early, so that the other init code
      can use global_context for memory managment
    */
    program_name = L"fish_pager";


    wsetlocale(LC_ALL, L"");

    /*
      The call signature for fish_pager is a mess. Because we want
      to be able to upgrade fish without breaking running
      instances, we need to support all previous
      modes. Unfortunatly, the two previous ones are a mess. The
      third one is designed to be extensible, so hopefully it will
      be the last.
    */

    if (argc > 1 && argv[1][0] == '-')
    {
        /*
          Third mode
        */

        int completion_fd = -1;
        FILE *completion_file;

        while (1)
        {
            static struct option
                    long_options[] =
            {
                {
                    "result-fd", required_argument, 0, 'r'
                }
                ,
                {
                    "completion-fd", required_argument, 0, 'c'
                }
                ,
                {
                    "prefix", required_argument, 0, 'p'
                }
                ,
                {
                    "is-quoted", no_argument, 0, 'q'
                }
                ,
                {
                    "help", no_argument, 0, 'h'
                }
                ,
                {
                    "version", no_argument, 0, 'v'
                }
                ,
                {
                    0, 0, 0, 0
                }
            }
            ;

            int opt_index = 0;

            int opt = getopt_long(argc,
                                  argv,
                                  GETOPT_STRING,
                                  long_options,
                                  &opt_index);

            if (opt == -1)
                break;

            switch (opt)
            {
                case 0:
                {
                    break;
                }

                case 'r':
                {
                    result_fd = get_fd(optarg);
                    break;
                }

                case 'c':
                {
                    completion_fd = get_fd(optarg);
                    break;
                }

                case 'p':
                {
                    prefix = str2wcstring(optarg);
                    break;
                }

                case 'h':
                {
                    print_help(argv[0], 1);
                    exit(0);
                }

                case 'v':
                {
                    debug(0, L"%ls, version %s\n", program_name, PACKAGE_VERSION);
                    exit(0);
                }

                case 'q':
                {
                    is_quoted = 1;
                }

            }
        }

        if (completion_fd == -1 || result_fd == -1)
        {
            debug(0, _(L"Unspecified file descriptors"));
            exit(1);
        }


        if ((completion_file = fdopen(completion_fd, "r")))
        {
            read_array(completion_file, comp);
            fclose(completion_file);
        }
        else
        {
            debug(0, _(L"Could not read completions"));
            wperror(L"fdopen");
            exit(1);
        }

    }
    else
    {
        /*
          Second or first mode. These suck, but we need to support
          them for backwards compatibility. At least for some
          time.

          Third mode was implemented in January 2007, and previous
          modes should be considered deprecated from that point
          forward. A reasonable time frame for removal of the code
          below has yet to be determined.
        */

        if (argc < 3)
        {
            print_help(argv[0], 1);
            exit(0);
        }
        else
        {
            mangle_descriptors = 1;

            prefix = str2wcstring(argv[2]);
            is_quoted = strcmp("1", argv[1])==0;

            if (argc > 3)
            {
                /*
                  First mode
                */
                for (i=3; i<argc; i++)
                {
                    wcstring wcs = str2wcstring(argv[i]);
                    comp.push_back(wcs);
                }
            }
            else
            {
                /*
                  Second mode
                */
                read_array(stdin, comp);
            }
        }

    }

//    debug( 3, L"prefix is '%ls'", prefix );

    init(mangle_descriptors, result_fd);

    mangle_descriptions(comp);

    if (prefix == L"-")
        join_completions(comp);

    std::vector<comp_t *> completions = mangle_completions(comp, prefix.c_str());

    /**
       Try to print the completions. Start by trying to print the
       list in PAGER_MAX_COLS columns, if the completions won't
       fit, reduce the number of columns by one. Printing a single
       column never fails.
    */
    for (i = PAGER_MAX_COLS; i>0; i--)
    {
        switch (completion_try_print(i, prefix.c_str(), is_quoted, completions))
        {

            case PAGER_RETRY:
                break;

            case PAGER_DONE:
                i=0;
                break;

            case PAGER_RESIZE:
                /*
                  This means we got a resize event, so we start
                  over from the beginning. Since it the screen got
                  bigger, we might be able to fit all completions
                  on-screen.
                */
                i=PAGER_MAX_COLS+1;
                break;

        }
    }

    fwprintf(out_file, L"%ls", out_buff.c_str());
    if (is_ca_mode)
    {
        writembs(exit_ca_mode);
        pager_flush();
    }
    destroy();

}
Exemplo n.º 12
0
int main(int argc, char **argv) {
    int res = 1;
    int my_optind = 0;

    // We can't do this at compile time due to the use of enum symbols.
    assert(EXPAND_SENTINAL >= EXPAND_RESERVED_BASE && EXPAND_SENTINAL <= EXPAND_RESERVED_END);
    assert(ANY_SENTINAL >= WILDCARD_RESERVED_BASE && ANY_SENTINAL <= WILDCARD_RESERVED_END);
    assert(R_SENTINAL >= INPUT_COMMON_BASE && R_SENTINAL <= INPUT_COMMON_END);

    program_name = L"fish";
    set_main_thread();
    setup_fork_guards();

    setlocale(LC_ALL, "");
    fish_setlocale();

    // struct stat tmp;
    // stat("----------FISH_HIT_MAIN----------", &tmp);

    if (!argv[0]) {
        static const char *dummy_argv[2] = {"fish", NULL};
        argv = (char **)dummy_argv;  //!OCLINT(parameter reassignment)
        argc = 1;                    //!OCLINT(parameter reassignment)
    }
    std::vector<std::string> cmds;
    my_optind = fish_parse_opt(argc, argv, &cmds);

    // No-exec is prohibited when in interactive mode.
    if (is_interactive_session && no_exec) {
        debug(1, _(L"Can not use the no-execute mode when running an interactive session"));
        no_exec = 0;
    }

    // Only save (and therefore restore) the fg process group if we are interactive. See issues
    // #197 and #1002.
    if (is_interactive_session) {
        save_term_foreground_process_group();
    }

    const struct config_paths_t paths = determine_config_directory_paths(argv[0]);

    proc_init();
    event_init();
    builtin_init();
    function_init();
    env_init(&paths);
    reader_init();
    history_init();
    // For set_color to support term256 in config.fish (issue #1022).
    update_fish_color_support();
    misc_init();

    parser_t &parser = parser_t::principal_parser();

    const io_chain_t empty_ios;
    if (read_init(paths)) {
        // Stomp the exit status of any initialization commands (issue #635).
        proc_set_last_status(STATUS_BUILTIN_OK);

        // Run the commands specified as arguments, if any.
        if (!cmds.empty()) {
            // Do something nasty to support OpenSUSE assuming we're bash. This may modify cmds.
            if (is_login) {
                fish_xdm_login_hack_hack_hack_hack(&cmds, argc - my_optind, argv + my_optind);
            }
            for (size_t i = 0; i < cmds.size(); i++) {
                const wcstring cmd_wcs = str2wcstring(cmds.at(i));
                res = parser.eval(cmd_wcs, empty_ios, TOP);
            }
            reader_exit(0, 0);
        } else if (my_optind == argc) {
            // Interactive mode
            check_running_fishd();
            res = reader_read(STDIN_FILENO, empty_ios);
        } else {
            char *file = *(argv + (my_optind++));
            int fd = open(file, O_RDONLY);
            if (fd == -1) {
                perror(file);
            } else {
                // OK to not do this atomically since we cannot have gone multithreaded yet.
                set_cloexec(fd);

                if (*(argv + my_optind)) {
                    wcstring sb;
                    char **ptr;
                    int i;
                    for (i = 1, ptr = argv + my_optind; *ptr; i++, ptr++) {
                        if (i != 1) sb.append(ARRAY_SEP_STR);
                        sb.append(str2wcstring(*ptr));
                    }

                    env_set(L"argv", sb.c_str(), 0);
                }

                const wcstring rel_filename = str2wcstring(file);

                reader_push_current_filename(rel_filename.c_str());

                res = reader_read(fd, empty_ios);

                if (res) {
                    debug(1, _(L"Error while reading file %ls\n"), reader_current_filename()
                                                                       ? reader_current_filename()
                                                                       : _(L"Standard input"));
                }
                reader_pop_current_filename();
            }
        }
    }

    int exit_status = res ? STATUS_UNKNOWN_COMMAND : proc_get_last_status();

    proc_fire_event(L"PROCESS_EXIT", EVENT_EXIT, getpid(), exit_status);

    restore_term_mode();
    restore_term_foreground_process_group();

    if (g_profiling_active) {
        parser.emit_profiling(s_profiling_output_filename);
    }

    history_destroy();
    proc_destroy();
    builtin_destroy();
    reader_destroy();
    event_destroy();
    exit_without_destructors(exit_status);
    return EXIT_FAILURE;  // above line should always exit
}
Exemplo n.º 13
0
    bool replace_matches(const wchar_t *arg)
    {
        // A return value of true means all is well (even if no replacements
        // were performed), false indicates an unrecoverable error.
        if (regex.code == 0)
        {
            // pcre2_compile() failed
            return false;
        }

        uint32_t options = opts.all ? PCRE2_SUBSTITUTE_GLOBAL : 0;
        size_t arglen = wcslen(arg);
        PCRE2_SIZE bufsize = (arglen == 0) ? 16 : 2 * arglen;
        wchar_t *output = (wchar_t *)malloc(sizeof(wchar_t) * bufsize);
        if (output == 0)
        {
            DIE_MEM();
        }
        int pcre2_rc = 0;
        for (;;)
        {
            PCRE2_SIZE outlen = bufsize;
            pcre2_rc = pcre2_substitute(
                            regex.code,
                            PCRE2_SPTR(arg),
                            arglen,
                            0,  // start offset
                            options,
                            regex.match,
                            0,  // match context
                            PCRE2_SPTR(replacement.c_str()),
                            PCRE2_ZERO_TERMINATED,
                            (PCRE2_UCHAR *)output,
                            &outlen);

            if (pcre2_rc == PCRE2_ERROR_NOMEMORY)
            {
                if (bufsize < MAX_REPLACE_SIZE)
                {
                    bufsize = std::min(2 * bufsize, MAX_REPLACE_SIZE);
                    output = (wchar_t *)realloc(output, sizeof(wchar_t) * bufsize);
                    if (output == 0)
                    {
                        DIE_MEM();
                    }
                    continue;
                }
                string_error(streams, _(L"%ls: Replacement string too large\n"), argv0);
                free(output);
                return false;
            }
            break;
        }

        bool rc = true;
        if (pcre2_rc < 0)
        {
            string_error(streams, _(L"%ls: Regular expression substitute error: %ls\n"),
                    argv0, pcre2_strerror(pcre2_rc).c_str());
            rc = false;
        }
        else
        {
            if (!opts.quiet)
            {
                streams.out.append(output);
                streams.out.append(L'\n');
            }
            total_replaced += pcre2_rc;
        }

        free(output);
        return rc;
    }
Exemplo n.º 14
0
/**
   This internal helper function does all the real work. By using two
   functions, the internal function can return on various places in
   the code, and the caller can take care of various cleanup work.

     cmd: the command name ('grep')
     really_load: whether to actually parse it as a function, or just check it it exists
     reload: whether to reload it if it's already loaded
     path_list: the set of paths to check

     Result: if really_load is true, returns whether the function was loaded. Otherwise returns whether the function existed.
*/
bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really_load, bool reload, const wcstring_list_t &path_list )
{
    /* Note that we are NOT locked in this function! */
    size_t i;
    bool reloaded = 0;

    /* Try using a cached function. If we really want the function to be loaded, require that it be really loaded. If we're not reloading, allow stale functions. */
    {
        bool allow_stale_functions = ! reload;

        /* Take a lock */
        scoped_lock locker(lock);

        /* Get the function */
        autoload_function_t * func = this->get_node(cmd);

        /* Determine if we can use this cached function */
        bool use_cached;
        if (! func) {
            /* Can't use a function that doesn't exist */
            use_cached = false;
        } else if (really_load && ! func->is_placeholder && ! func->is_loaded) {
            /* Can't use an unloaded function */
            use_cached = false;
        } else if ( ! allow_stale_functions && is_stale(func)) {
            /* Can't use a stale function */
            use_cached = false;
        } else {
            /* I guess we can use it */
            use_cached = true;
        }

        /* If we can use this function, return whether we were able to access it */
        if (use_cached) {
            return func->is_internalized || func->access.accessible;
        }
    }
    /* The source of the script will end up here */
    wcstring script_source;
    bool has_script_source = false;

    /* Whether we found an accessible file */
    bool found_file = false;

    /* Look for built-in scripts via a binary search */
    const builtin_script_t *matching_builtin_script = NULL;
    if (builtin_script_count > 0)
    {
        const builtin_script_t test_script = {cmd.c_str(), NULL};
        const builtin_script_t *array_end = builtin_scripts + builtin_script_count;
        const builtin_script_t *found = std::lower_bound(builtin_scripts, array_end, test_script, script_name_precedes_script_name);
        if (found != array_end && ! wcscmp(found->name, test_script.name))
        {
            /* We found it */
            matching_builtin_script = found;
        }
    }
    if (matching_builtin_script) {
        has_script_source = true;
        script_source = str2wcstring(matching_builtin_script->def);

        /* Make a node representing this function */
        scoped_lock locker(lock);
        autoload_function_t *func = this->get_autoloaded_function_with_creation(cmd, really_load);

        /* This function is internalized */
        func->is_internalized = true;

        /* It's a fiction to say the script is loaded at this point, but we're definitely going to load it down below. */
        if (really_load) func->is_loaded = true;
    }

    if (! has_script_source)
    {
        /* Iterate over path searching for suitable completion files */
        for( i=0; i<path_list.size(); i++ )
        {
            wcstring next = path_list.at(i);
            wcstring path = next + L"/" + cmd + L".fish";

            const file_access_attempt_t access = access_file(path, R_OK);
            if (access.accessible) {
                /* Found it! */
                found_file = true;

                /* Now we're actually going to take the lock. */
                scoped_lock locker(lock);
                autoload_function_t *func = this->get_node(cmd);

                /* Generate the source if we need to load it */
                bool need_to_load_function = really_load && (func == NULL || func->access.mod_time != access.mod_time || ! func->is_loaded);
                if (need_to_load_function) {

                    /* Generate the script source */
                    wcstring esc = escape_string(path, 1);
                    script_source = L". " + esc;
                    has_script_source = true;

                    /* Remove any loaded command because we are going to reload it. Note that this will deadlock if command_removed calls back into us. */
                    if (func && func->is_loaded) {
                        command_removed(cmd);
                        func->is_placeholder = false;
                    }

                    /* Mark that we're reloading it */
                    reloaded = true;
                }

                /* Create the function if we haven't yet. This does not load it. Do not trigger eviction unless we are actually loading, because we don't want to evict off of the main thread. */
                if (! func) {
                    func = get_autoloaded_function_with_creation(cmd, really_load);
                }

                /* It's a fiction to say the script is loaded at this point, but we're definitely going to load it down below. */
                if (need_to_load_function) func->is_loaded = true;

                /* Unconditionally record our access time */
                func->access = access;

                break;
            }
        }

        /*
          If no file or builtin script was found we insert a placeholder function.
          Later we only research if the current time is at least five seconds later.
          This way, the files won't be searched over and over again.
        */
        if( ! found_file && ! has_script_source )
        {
            scoped_lock locker(lock);
            /* Generate a placeholder */
            autoload_function_t *func = this->get_node(cmd);
            if (! func) {
                func = new autoload_function_t(cmd);
                func->is_placeholder = true;
                if (really_load) {
                    this->add_node(func);
                } else {
                    this->add_node_without_eviction(func);
                }
            }
            func->access.last_checked = time(NULL);
        }
    }

    /* If we have a script, either built-in or a file source, then run it */
    if (really_load && has_script_source)
    {
        if( exec_subshell( script_source) == -1 )
        {
            /*
              Do nothing on failiure
            */
        }

    }

    if (really_load) {
        return reloaded;
    } else {
        return found_file || has_script_source;
    }
}
Exemplo n.º 15
0
/// Set the current bind mode.
void input_set_bind_mode(const wcstring &bm) {
    // Only set this if it differs to not execute variable handlers all the time.
    if (input_get_bind_mode() != bm.c_str()) {
        env_set(FISH_BIND_MODE_VAR, bm.c_str(), ENV_GLOBAL);
    }
}
Exemplo n.º 16
0
/// Perform brace expansion.
static expand_error_t expand_braces(const wcstring &instr, expand_flags_t flags,
                                      std::vector<completion_t> *out, parse_error_list_t *errors) {
    bool syntax_error = false;
    int brace_count = 0;

    const wchar_t *brace_begin = NULL, *brace_end = NULL;
    const wchar_t *last_sep = NULL;

    const wchar_t *item_begin;
    size_t length_preceding_braces, length_following_braces, tot_len;

    const wchar_t *const in = instr.c_str();

    // Locate the first non-nested brace pair.
    for (const wchar_t *pos = in; (*pos) && !syntax_error; pos++) {
        switch (*pos) {
            case BRACE_BEGIN: {
                if (brace_count == 0) brace_begin = pos;
                brace_count++;
                break;
            }
            case BRACE_END: {
                brace_count--;
                if (brace_count < 0) {
                    syntax_error = true;
                } else if (brace_count == 0) {
                    brace_end = pos;
                }
                break;
            }
            case BRACE_SEP: {
                if (brace_count == 1) last_sep = pos;
                break;
            }
            default: {
                break;  // we ignore all other characters here
            }
        }
    }

    if (brace_count > 0) {
        if (!(flags & EXPAND_FOR_COMPLETIONS)) {
            syntax_error = true;
        } else {
            // The user hasn't typed an end brace yet; make one up and append it, then expand
            // that.
            wcstring mod;
            if (last_sep) {
                mod.append(in, brace_begin - in + 1);
                mod.append(last_sep + 1);
                mod.push_back(BRACE_END);
            } else {
                mod.append(in);
                mod.push_back(BRACE_END);
            }

            // Note: this code looks very fishy, apparently it has never worked.
            return expand_braces(mod, 1, out, errors);
        }
    }

    // Expand a literal "{}" to itself because it is useless otherwise,
    // and this eases e.g. `find -exec {}`. See #1109.
    if (brace_begin + 1 == brace_end) {
        wcstring newstr = instr;
        newstr.at(brace_begin - in) = L'{';
        newstr.at(brace_end - in) = L'}';
        return expand_braces(newstr, flags, out, errors);
    }

    if (syntax_error) {
        append_syntax_error(errors, SOURCE_LOCATION_UNKNOWN, _(L"Mismatched braces"));
        return EXPAND_ERROR;
    }

    if (brace_begin == NULL) {
        append_completion(out, instr);
        return EXPAND_OK;
    }

    length_preceding_braces = (brace_begin - in);
    length_following_braces = wcslen(brace_end) - 1;
    tot_len = length_preceding_braces + length_following_braces;
    item_begin = brace_begin + 1;
    for (const wchar_t *pos = (brace_begin + 1); true; pos++) {
        if (brace_count == 0 && ((*pos == BRACE_SEP) || (pos == brace_end))) {
            assert(pos >= item_begin);
            size_t item_len = pos - item_begin;
            wcstring item = wcstring(item_begin, item_len);
            item = trim(item, (const wchar_t[]) { BRACE_SPACE, L'\0' });
            for (auto &c : item) {
                if (c == BRACE_SPACE) {
                    c = ' ';
                }
            }

            wcstring whole_item;
            whole_item.reserve(tot_len + item_len + 2);
            whole_item.append(in, length_preceding_braces);
            whole_item.append(item.begin(), item.end());
            whole_item.append(brace_end + 1);
            expand_braces(whole_item, flags, out, errors);

            item_begin = pos + 1;
            if (pos == brace_end) break;
        }
Exemplo n.º 17
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)
        {
            struct stat buff;
            if (wstat(cmd, &buff))
            {
                return false;
            }

            if (S_ISREG(buff.st_mode))
            {
                if (out_path)
                    out_path->assign(cmd);
                return true;
            }
            else
            {
                errno = EACCES;
                return false;
            }
        }
        else
        {
            return false;
        }

    }
    else
    {
        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;
}
Exemplo n.º 18
0
/* Writes our state to the fd. path is provided only for error reporting */
bool env_universal_t::write_to_fd(int fd, const wcstring &path)
{
    ASSERT_IS_LOCKED(lock);
    assert(fd >= 0);
    bool success = true;

    // Stuff we output to fd
    std::string contents;
    
    // Temporary storage
    std::string storage;
    
    // Write the save message. If this fails, we don't bother complaining.
    write_loop(fd, SAVE_MSG, strlen(SAVE_MSG));
    
    var_table_t::const_iterator iter = vars.begin();
    while (iter != vars.end())
    {
        // Append the entry. Note that append_file_entry may fail, but that only affects one variable; soldier on.
        const wcstring &key = iter->first;
        const var_entry_t &entry = iter->second;
        append_file_entry(entry.exportv ? SET_EXPORT : SET, key, entry.val, &contents, &storage);
        
        // Go to next
        ++iter;
        
        // Flush if this is the last iteration or we exceed a page
        if (iter == vars.end() || contents.size() >= 4096)
        {
            if (write_loop(fd, contents.data(), contents.size()) < 0)
            {
                int err = errno;
                report_error(err, L"Unable to write to universal variables file '%ls'", path.c_str());
                success = false;
                break;
            }
            contents.clear();
        }
    }
    
    /* Since we just wrote out this file, it matches our internal state; pretend we read from it */
    this->last_read_file = file_id_for_fd(fd);
    
    /* We don't close the file */
    return success;
}
Exemplo n.º 19
0
bool wildcard_match(const wcstring &str, const wcstring &wc)
{
    return wildcard_match2(str.c_str(), wc.c_str(), true);
}
Exemplo n.º 20
0
bool env_universal_t::open_and_acquire_lock(const wcstring &path, int *out_fd)
{
    /* Attempt to open the file for reading at the given path, atomically acquiring a lock. On BSD, we can use O_EXLOCK. On Linux, we open the file, take a lock, and then compare fstat() to stat(); if they match, it means that the file was not replaced before we acquired the lock.
    
    We pass O_RDONLY with O_CREAT; this creates a potentially empty file. We do this so that we have something to lock on.
    */
    int result_fd = -1;
    bool needs_lock = true;
    int flags = O_RDONLY | O_CREAT;
#ifdef O_EXLOCK
    flags |= O_EXLOCK;
    needs_lock = false;
#endif
    for (;;)
    {
        int fd = wopen_cloexec(path, flags, 0644);
        if (fd < 0)
        {
            int err = errno;
            if (err == EINTR)
            {
                /* Signal; try again */
                continue;
            }
#ifdef O_EXLOCK
            else if (err == EOPNOTSUPP)
            {
                /* Filesystem probably does not support locking. Clear the flag and try again. Note that we try taking the lock via flock anyways. */
                flags &= ~O_EXLOCK;
                needs_lock = true;
                continue;
            }
#endif
            else
            {
                report_error(err, L"Unable to open universal variable file '%ls'", path.c_str());
                break;
            }
        }
        
        /* If we get here, we must have a valid fd */
        assert(fd >= 0);
        
        /* Try taking the lock, if necessary. If we failed, we may be on lockless NFS, etc.; in that case we pretend we succeeded. See the comment in save_to_path for the rationale. */
        if (needs_lock)
        {
            while (flock(fd, LOCK_EX) < 0)
            {
                /* error */
                if (errno != EINTR)
                {
                    int err = errno;
                    report_error(err, L"Unable to lock universal variable file '%ls'", path.c_str());
                    break;
                }
            }
        }
        
        /* Hopefully we got the lock. However, it's possible the file changed out from under us while we were waiting for the lock. Make sure that didn't happen. */
        if (file_id_for_fd(fd) != file_id_for_path(path))
        {
            /* Oops, it changed! Try again */
            close(fd);
            continue;
        }

        
        /* Finally, we have an fd that's valid and hopefully locked. We're done */
        assert(fd >= 0);
        result_fd = fd;
        break;
    }
    
    *out_fd = result_fd;
    
    return result_fd >= 0;
}
Exemplo n.º 21
0
/**
   The real implementation of wildcard expansion is in this
   function. Other functions are just wrappers around this one.

   This function traverses the relevant directory tree looking for
   matches, and recurses when needed to handle wildcrards spanning
   multiple components and recursive wildcards.

   Because this function calls itself recursively with substrings,
   it's important that the parameters be raw pointers instead of wcstring,
   which would be too expensive to construct for all substrings.
 */
static int wildcard_expand_internal(const wchar_t *wc,
                                    const wchar_t *base_dir,
                                    expand_flags_t flags,
                                    std::vector<completion_t> &out,
                                    std::set<wcstring> &completion_set,
                                    std::set<file_id_t> &visited_files
                                   )
{

    /* Points to the end of the current wildcard segment */
    const wchar_t *wc_end;

    /* Variables for traversing a directory */
    DIR *dir;

    /* The result returned */
    int res = 0;

    /* Length of the directory to search in */
    size_t base_len;

    /* Variables for testing for presense of recursive wildcards */
    const wchar_t *wc_recursive;
    bool is_recursive;

    /* Slightly mangled version of base_dir */
    const wchar_t *dir_string;

    //  debug( 3, L"WILDCARD_EXPAND %ls in %ls", wc, base_dir );

    if (is_main_thread() ? reader_interrupted() : reader_thread_job_is_stale())
    {
        return -1;
    }

    if (!wc || !base_dir)
    {
        debug(2, L"Got null string on line %d of file %s", __LINE__, __FILE__);
        return 0;
    }

    if (flags & ACCEPT_INCOMPLETE)
    {
        /*
           Avoid excessive number of returned matches for wc ending with a *
        */
        size_t len = wcslen(wc);
        if (len && (wc[len-1]==ANY_STRING))
        {
            wchar_t * foo = wcsdup(wc);
            foo[len-1]=0;
            int res = wildcard_expand_internal(foo, base_dir, flags, out, completion_set, visited_files);
            free(foo);
            return res;
        }
    }

    /*
      Initialize various variables
    */

    dir_string = base_dir[0]==L'\0'?L".":base_dir;

    if (!(dir = wopendir(dir_string)))
    {
        return 0;
    }

    wc_end = wcschr(wc,L'/');
    base_len = wcslen(base_dir);

    /*
      Test for recursive match string in current segment
    */
    wc_recursive = wcschr(wc, ANY_STRING_RECURSIVE);
    is_recursive = (wc_recursive && (!wc_end || wc_recursive < wc_end));

    /*
      Is this segment of the wildcard the last?
    */
    if (!wc_end)
    {
        /*
          Wildcard segment is the last segment,

          Insert all matching files/directories
        */
        if (wc[0]=='\0')
        {
            /*
              The last wildcard segment is empty. Insert everything if
              completing, the directory itself otherwise.
            */
            if (flags & ACCEPT_INCOMPLETE)
            {
                wcstring next;
                while (wreaddir(dir, next))
                {
                    if (next[0] != L'.')
                    {
                        wcstring long_name = make_path(base_dir, next);

                        if (test_flags(long_name.c_str(), flags))
                        {
                            wildcard_completion_allocate(out,
                                                         long_name,
                                                         next,
                                                         L"",
                                                         flags);
                        }
                    }
                }
            }
            else
            {
                res = 1;
                insert_completion_if_missing(base_dir, out, completion_set);
            }
        }
        else
        {
            /*
              This is the last wildcard segment, and it is not empty. Match files/directories.
            */
            wcstring next;
            while (wreaddir(dir, next))
            {
                const wchar_t * const name = next.c_str();
                if (flags & ACCEPT_INCOMPLETE)
                {

                    const wcstring long_name = make_path(base_dir, next);

                    /*
                      Test for matches before stating file, so as to minimize the number of calls to the much slower stat function
                    */
                    std::vector<completion_t> test;
                    if (wildcard_complete(name,
                                          wc,
                                          L"",
                                          0,
                                          test,
                                          0))
                    {
                        if (test_flags(long_name.c_str(), flags))
                        {
                            wildcard_completion_allocate(out,
                                                         long_name,
                                                         name,
                                                         wc,
                                                         flags);

                        }
                    }
                }
                else
                {
                    if (wildcard_match2(name, wc, true))
                    {
                        const wcstring long_name = make_path(base_dir, next);
                        int skip = 0;

                        if (is_recursive)
                        {
                            /*
                              In recursive mode, we are only
                              interested in adding files -directories
                              will be added in the next pass.
                            */
                            struct stat buf;
                            if (!wstat(long_name, &buf))
                            {
                                skip = S_ISDIR(buf.st_mode);
                            }
                        }
                        if (! skip)
                        {
                            insert_completion_if_missing(long_name, out, completion_set);
                        }
                        res = 1;
                    }
                }
            }
        }
    }

    if (wc_end || is_recursive)
    {
        /*
          Wilcard segment is not the last segment.  Recursively call
          wildcard_expand for all matching subdirectories.
        */

        /*
          wc_str is the part of the wildcarded string from the
          beginning to the first slash
        */
        wchar_t *wc_str;

        /*
          new_dir is a scratch area containing the full path to a
          file/directory we are iterating over
        */
        wchar_t *new_dir;

        /*
          The maximum length of a file element
        */
        long ln=MAX_FILE_LENGTH;
        char * narrow_dir_string = wcs2str(dir_string);

        /*
          In recursive mode, we look through the directory twice. If
          so, this rewind is needed.
        */
        rewinddir(dir);

        if (narrow_dir_string)
        {
            /*
               Find out how long the filename can be in a worst case
               scenario
            */
            ln = pathconf(narrow_dir_string, _PC_NAME_MAX);

            /*
              If not specified, use som large number as fallback
            */
            if (ln < 0)
                ln = MAX_FILE_LENGTH;
            free(narrow_dir_string);
        }
        new_dir= (wchar_t *)malloc(sizeof(wchar_t)*(base_len+ln+2));

        wc_str = wc_end?wcsndup(wc, wc_end-wc):wcsdup(wc);

        if ((!new_dir) || (!wc_str))
        {
            DIE_MEM();
        }

        wcscpy(new_dir, base_dir);

        wcstring next;
        while (wreaddir(dir, next))
        {
            const wchar_t *name = next.c_str();

            /*
              Test if the file/directory name matches the whole
              wildcard element, i.e. regular matching.
            */
            int whole_match = wildcard_match2(name, wc_str, true);
            int partial_match = 0;

            /*
               If we are doing recursive matching, also check if this
               directory matches the part up to the recusrive
               wildcard, if so, then we can search all subdirectories
               for matches.
            */
            if (is_recursive)
            {
                const wchar_t *end = wcschr(wc, ANY_STRING_RECURSIVE);
                wchar_t *wc_sub = wcsndup(wc, end-wc+1);
                partial_match = wildcard_match2(name, wc_sub, true);
                free(wc_sub);
            }

            if (whole_match || partial_match)
            {
                struct stat buf;
                char *dir_str;
                int stat_res;
                int new_res;

                wcscpy(&new_dir[base_len], name);
                dir_str = wcs2str(new_dir);

                if (dir_str)
                {
                    stat_res = stat(dir_str, &buf);
                    free(dir_str);

                    if (!stat_res)
                    {
                        // Insert a "file ID" into visited_files
                        // If the insertion fails, we've already visited this file (i.e. a symlink loop)
                        // If we're not recursive, insert anyways (in case we loop back around in a future recursive segment), but continue on; the idea being that literal path components should still work
                        const file_id_t file_id(buf.st_dev, buf.st_ino);
                        if (S_ISDIR(buf.st_mode) && (visited_files.insert(file_id).second || ! is_recursive))
                        {
                            size_t new_len = wcslen(new_dir);
                            new_dir[new_len] = L'/';
                            new_dir[new_len+1] = L'\0';

                            /*
                              Regular matching
                            */
                            if (whole_match)
                            {
                                const wchar_t *new_wc = L"";
                                if (wc_end)
                                {
                                    new_wc=wc_end+1;
                                    /*
                                      Accept multiple '/' as a single direcotry separator
                                    */
                                    while (*new_wc==L'/')
                                    {
                                        new_wc++;
                                    }
                                }

                                new_res = wildcard_expand_internal(new_wc,
                                                                   new_dir,
                                                                   flags,
                                                                   out,
                                                                   completion_set,
                                                                   visited_files);

                                if (new_res == -1)
                                {
                                    res = -1;
                                    break;
                                }
                                res |= new_res;

                            }

                            /*
                              Recursive matching
                            */
                            if (partial_match)
                            {

                                new_res = wildcard_expand_internal(wcschr(wc, ANY_STRING_RECURSIVE),
                                                                   new_dir,
                                                                   flags | WILDCARD_RECURSIVE,
                                                                   out,
                                                                   completion_set,
                                                                   visited_files);

                                if (new_res == -1)
                                {
                                    res = -1;
                                    break;
                                }
                                res |= new_res;

                            }
                        }
                    }
                }
            }
        }

        free(wc_str);
        free(new_dir);
    }
    closedir(dir);

    return res;
}
Exemplo n.º 22
0
/// Define a function. Calls into `function.cpp` to perform the heavy lifting of defining a
/// function.
int builtin_function(parser_t &parser, io_streams_t &streams, const wcstring_list_t &c_args,
                     const wcstring &contents, int definition_line_offset) {
    // The wgetopt function expects 'function' as the first argument. Make a new wcstring_list with
    // that property. This is needed because this builtin has a different signature than the other
    // builtins.
    wcstring_list_t args = {L"function"};
    args.insert(args.end(), c_args.begin(), c_args.end());

    // Hackish const_cast matches the one in builtin_run.
    const null_terminated_array_t<wchar_t> argv_array(args);
    wchar_t **argv = const_cast<wchar_t **>(argv_array.get());
    wchar_t *cmd = argv[0];
    int argc = builtin_count_args(argv);

    // A valid function name has to be the first argument.
    wcstring function_name;
    int retval = validate_function_name(argc, argv, function_name, cmd, streams);
    if (retval != STATUS_CMD_OK) return retval;
    argv++;
    argc--;

    function_cmd_opts_t opts;
    int optind;
    retval = parse_cmd_opts(opts, &optind, argc, argv, parser, streams);
    if (retval != STATUS_CMD_OK) return retval;

    if (opts.print_help) {
        builtin_print_help(parser, streams, cmd, streams.err);
        return STATUS_CMD_OK;
    }

    if (argc != optind) {
        if (opts.named_arguments.size()) {
            for (int i = optind; i < argc; i++) {
                opts.named_arguments.push_back(argv[i]);
            }
        } else {
            streams.err.append_format(_(L"%ls: Unexpected positional argument '%ls'"), cmd,
                                      argv[optind]);
            return STATUS_INVALID_ARGS;
        }
    }

    // We have what we need to actually define the function.
    function_data_t d;
    d.name = function_name;
    if (!opts.description.empty()) d.description = opts.description;
    // d.description = opts.description;
    d.events.swap(opts.events);
    d.shadow_scope = opts.shadow_scope;
    d.named_arguments.swap(opts.named_arguments);
    d.inherit_vars.swap(opts.inherit_vars);

    for (size_t i = 0; i < d.events.size(); i++) {
        event_t &e = d.events.at(i);
        e.function_name = d.name;
    }

    d.definition = contents.c_str();
    function_add(d, parser, definition_line_offset);

    // Handle wrap targets by creating the appropriate completions.
    for (size_t w = 0; w < opts.wrap_targets.size(); w++) {
        complete_add_wrapper(function_name, opts.wrap_targets.at(w));
    }

    return STATUS_CMD_OK;
}
Exemplo n.º 23
0
void parse_util_token_extent(const wchar_t *buff,
                             size_t cursor_pos,
                             const wchar_t **tok_begin,
                             const wchar_t **tok_end,
                             const wchar_t **prev_begin,
                             const wchar_t **prev_end)
{
    const wchar_t *a = NULL, *b = NULL, *pa = NULL, *pb = NULL;

    CHECK(buff,);

    assert(cursor_pos >= 0);

    const wchar_t *cmdsubst_begin, *cmdsubst_end;
    parse_util_cmdsubst_extent(buff, cursor_pos, &cmdsubst_begin, &cmdsubst_end);

    if (!cmdsubst_end || !cmdsubst_begin)
    {
        return;
    }

    /* pos is equivalent to cursor_pos within the range of the command substitution {begin, end} */
    long offset_within_cmdsubst = cursor_pos - (cmdsubst_begin - buff);

    a = cmdsubst_begin + offset_within_cmdsubst;
    b = a;
    pa = cmdsubst_begin + offset_within_cmdsubst;
    pb = pa;

    assert(cmdsubst_begin >= buff);
    assert(cmdsubst_begin <= (buff+wcslen(buff)));
    assert(cmdsubst_end >= cmdsubst_begin);
    assert(cmdsubst_end <= (buff+wcslen(buff)));

    const wcstring buffcpy = wcstring(cmdsubst_begin, cmdsubst_end-cmdsubst_begin);

    tokenizer_t tok(buffcpy.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS);
    for (; tok_has_next(&tok); tok_next(&tok))
    {
        size_t tok_begin = tok_get_pos(&tok);
        size_t tok_end = tok_begin;

        /*
          Calculate end of token
        */
        if (tok_last_type(&tok) == TOK_STRING)
        {
            tok_end += wcslen(tok_last(&tok));
        }

        /*
          Cursor was before beginning of this token, means that the
          cursor is between two tokens, so we set it to a zero element
          string and break
        */
        if (tok_begin > offset_within_cmdsubst)
        {
            a = b = cmdsubst_begin + offset_within_cmdsubst;
            break;
        }

        /*
          If cursor is inside the token, this is the token we are
          looking for. If so, set a and b and break
        */
        if ((tok_last_type(&tok) == TOK_STRING) && (tok_end >= offset_within_cmdsubst))
        {
            a = cmdsubst_begin + tok_get_pos(&tok);
            b = a + wcslen(tok_last(&tok));
            break;
        }

        /*
          Remember previous string token
        */
        if (tok_last_type(&tok) == TOK_STRING)
        {
            pa = cmdsubst_begin + tok_get_pos(&tok);
            pb = pa + wcslen(tok_last(&tok));
        }
    }

    if (tok_begin)
    {
        *tok_begin = a;
    }

    if (tok_end)
    {
        *tok_end = b;
    }

    if (prev_begin)
    {
        *prev_begin = pa;
    }

    if (prev_end)
    {
        *prev_end = pb;
    }

    assert(pa >= buff);
    assert(pa <= (buff+wcslen(buff)));
    assert(pb >= pa);
    assert(pb <= (buff+wcslen(buff)));

}
Exemplo n.º 24
0
int main(int argc, char **argv)
{
    int res=1;
    int my_optind=0;

    set_main_thread();
    setup_fork_guards();

    wsetlocale(LC_ALL, L"");
    is_interactive_session=1;
    program_name=L"fish";

    //struct stat tmp;
    //stat("----------FISH_HIT_MAIN----------", &tmp);

    std::vector<std::string> cmds;
    my_optind = fish_parse_opt(argc, argv, &cmds);

    /*
      No-exec is prohibited when in interactive mode
    */
    if (is_interactive_session && no_exec)
    {
        debug(1, _(L"Can not use the no-execute mode when running an interactive session"));
        no_exec = 0;
    }

    /* Only save (and therefore restore) the fg process group if we are interactive. See #197, #1002 */
    if (is_interactive_session)
    {
        save_term_foreground_process_group();
    }

    const struct config_paths_t paths = determine_config_directory_paths(argv[0]);

    proc_init();
    event_init();
    wutil_init();
    builtin_init();
    function_init();
    env_init(&paths);
    reader_init();
    history_init();
    /* For setcolor to support term256 in config.fish (#1022) */
    update_fish_color_support();

    parser_t &parser = parser_t::principal_parser();

    if (g_log_forks)
        printf("%d: g_fork_count: %d\n", __LINE__, g_fork_count);

    const io_chain_t empty_ios;
    if (read_init(paths))
    {
        /* Stop the exit status of any initialization commands (#635) */
        proc_set_last_status(STATUS_BUILTIN_OK);

        /* Run the commands specified as arguments, if any */
        if (! cmds.empty())
        {
            /* Do something nasty to support OpenSUSE assuming we're bash. This may modify cmds. */
            if (is_login)
            {
                fish_xdm_login_hack_hack_hack_hack(&cmds, argc - my_optind, argv + my_optind);
            }
            for (size_t i=0; i < cmds.size(); i++)
            {
                const wcstring cmd_wcs = str2wcstring(cmds.at(i));
                res = parser.eval(cmd_wcs, empty_ios, TOP);
            }
            reader_exit(0, 0);
        }
        else
        {
            if (my_optind == argc)
            {
                res = reader_read(STDIN_FILENO, empty_ios);
            }
            else
            {
                char **ptr;
                char *file = *(argv+(my_optind++));
                int i;
                int fd;


                if ((fd = open(file, O_RDONLY)) == -1)
                {
                    wperror(L"open");
                    return 1;
                }

                // OK to not do this atomically since we cannot have gone multithreaded yet
                set_cloexec(fd);

                if (*(argv+my_optind))
                {
                    wcstring sb;
                    for (i=1,ptr = argv+my_optind; *ptr; i++, ptr++)
                    {
                        if (i != 1)
                            sb.append(ARRAY_SEP_STR);
                        sb.append(str2wcstring(*ptr));
                    }

                    env_set(L"argv", sb.c_str(), 0);
                }

                const wcstring rel_filename = str2wcstring(file);
                const wchar_t *abs_filename = wrealpath(rel_filename, NULL);

                if (!abs_filename)
                {
                    abs_filename = wcsdup(rel_filename.c_str());
                }

                reader_push_current_filename(intern(abs_filename));
                free((void *)abs_filename);

                res = reader_read(fd, empty_ios);

                if (res)
                {
                    debug(1,
                          _(L"Error while reading file %ls\n"),
                          reader_current_filename()?reader_current_filename(): _(L"Standard input"));
                }
                reader_pop_current_filename();
            }
        }
    }

    proc_fire_event(L"PROCESS_EXIT", EVENT_EXIT, getpid(), res);

    restore_term_mode();
    restore_term_foreground_process_group();

    if (g_profiling_active)
    {
        parser.emit_profiling(s_profiling_output_filename);
    }

    history_destroy();
    proc_destroy();
    builtin_destroy();
    reader_destroy();
    wutil_destroy();
    event_destroy();

    if (g_log_forks)
        printf("%d: g_fork_count: %d\n", __LINE__, g_fork_count);

    exit_without_destructors(res ? STATUS_UNKNOWN_COMMAND : proc_get_last_status());
    return EXIT_FAILURE; //above line should always exit
}
Exemplo n.º 25
0
/// Convenience variants on fish_wcwswidth().
///
/// See fallback.h for the normal definitions.
int fish_wcswidth(const wcstring &str) { return fish_wcswidth(str.c_str(), str.size()); }
Exemplo n.º 26
0
void env_init(const struct config_paths_t *paths /* or NULL */)
{
    /*
      env_read_only variables can not be altered directly by the user
    */

    const wchar_t * const ro_keys[] =
    {
        L"status",
        L"history",
        L"version",
        L"_",
        L"LINES",
        L"COLUMNS",
        L"PWD",
        L"SHLVL",
        L"FISH_VERSION",
    };
    for (size_t i=0; i < sizeof ro_keys / sizeof *ro_keys; i++)
    {
        env_read_only.insert(ro_keys[i]);
    }

    /*
      HOME and USER should be writeable by root, since this can be a
      convenient way to install software.
    */
    if (getuid() != 0)
    {
        env_read_only.insert(L"HOME");
        env_read_only.insert(L"USER");
    }

    /*
       Names of all dynamically calculated variables
       */
    env_electric.insert(L"history");
    env_electric.insert(L"status");
    env_electric.insert(L"umask");

    top = new env_node_t;
    global_env = top;
    global = &top->env;

    /*
      Now the environemnt variable handling is set up, the next step
      is to insert valid data
    */

    /*
      Import environment variables
    */
    for (char **p = (environ ? environ : __environ); p && *p; p++)
    {
        const wcstring key_and_val = str2wcstring(*p); //like foo=bar
        size_t eql = key_and_val.find(L'=');
        if (eql == wcstring::npos)
        {
            // no equals found
            env_set(key_and_val, L"", ENV_EXPORT);
        }
        else
        {
            wcstring key = key_and_val.substr(0, eql);
            wcstring val = key_and_val.substr(eql + 1);
            if (variable_can_be_array(val))
            {
                std::replace(val.begin(), val.end(), L':', ARRAY_SEP);
            }

            env_set(key, val.c_str(), ENV_EXPORT | ENV_GLOBAL);
        }
    }

    /* Set the given paths in the environment, if we have any */
    if (paths != NULL)
    {
        env_set(FISH_DATADIR_VAR, paths->data.c_str(), ENV_GLOBAL | ENV_EXPORT);
        env_set(FISH_SYSCONFDIR_VAR, paths->sysconf.c_str(), ENV_GLOBAL | ENV_EXPORT);
        env_set(FISH_HELPDIR_VAR, paths->doc.c_str(), ENV_GLOBAL | ENV_EXPORT);
        env_set(FISH_BIN_DIR, paths->bin.c_str(), ENV_GLOBAL | ENV_EXPORT);
    }

    /*
      Set up the PATH variable
    */
    setup_path();

    /*
      Set up the USER variable
    */
    const struct passwd *pw = getpwuid(getuid());
    if (pw && pw->pw_name)
    {
        const wcstring uname = str2wcstring(pw->pw_name);
        env_set(L"USER", uname.c_str(), ENV_GLOBAL | ENV_EXPORT);
    }

    /*
      Set up the version variables
    */
    wcstring version = str2wcstring(get_fish_version());
    env_set(L"version", version.c_str(), ENV_GLOBAL);
    env_set(L"FISH_VERSION", version.c_str(), ENV_GLOBAL);

    /* Set up universal variables. The empty string means to use the deafult path. */
    assert(s_universal_variables == NULL);
    s_universal_variables = new env_universal_t(L"");

    /*
      Set up SHLVL variable
    */
    const env_var_t shlvl_str = env_get_string(L"SHLVL");
    wcstring nshlvl_str = L"1";
    if (! shlvl_str.missing())
    {
        long shlvl_i = wcstol(shlvl_str.c_str(), NULL, 10);
        if (shlvl_i >= 0)
        {
            nshlvl_str = to_string<long>(shlvl_i + 1);
        }
    }
    env_set(L"SHLVL", nshlvl_str.c_str(), ENV_GLOBAL | ENV_EXPORT);

    /* Set correct defaults for e.g. USER and HOME variables */
    env_set_defaults();

    /* Set g_log_forks */
    env_var_t log_forks = env_get_string(L"fish_log_forks");
    g_log_forks = ! log_forks.missing_or_empty() && from_string<bool>(log_forks);

    /* Set g_use_posix_spawn. Default to true. */
    env_var_t use_posix_spawn = env_get_string(L"fish_use_posix_spawn");
    g_use_posix_spawn = (use_posix_spawn.missing_or_empty() ? true : from_string<bool>(use_posix_spawn));

    /* Set fish_bind_mode to "default" */
    env_set(FISH_BIND_MODE_VAR, DEFAULT_BIND_MODE, ENV_GLOBAL);
}
Exemplo n.º 27
0
static bool is_electric(const wcstring &key)
{
    return env_electric.find(key.c_str()) != env_electric.end();
}
Exemplo n.º 28
0
/**
   Test wide/narrow conversion by creating random strings and
   verifying that the original string comes back thorugh double
   conversion.
*/
static void test_convert()
{
    /*  char o[] =
        {
          -17, -128, -121, -68, 0
        }
      ;

      wchar_t *w = str2wcs(o);
      char *n = wcs2str(w);

      int i;

      for( i=0; o[i]; i++ )
      {
        bitprint(o[i]);;
        //wprintf(L"%d ", o[i]);
      }
      wprintf(L"\n");

      for( i=0; w[i]; i++ )
      {
        wbitprint(w[i]);;
        //wprintf(L"%d ", w[i]);
      }
      wprintf(L"\n");

      for( i=0; n[i]; i++ )
      {
        bitprint(n[i]);;
        //wprintf(L"%d ", n[i]);
      }
      wprintf(L"\n");

      return;
    */


    int i;
    std::vector<char> sb;

    say(L"Testing wide/narrow string conversion");

    for (i=0; i<ESCAPE_TEST_COUNT; i++)
    {
        const char *o, *n;

        char c;

        sb.clear();

        while (rand() % ESCAPE_TEST_LENGTH)
        {
            c = rand();
            sb.push_back(c);
        }
        c = 0;
        sb.push_back(c);

        o = &sb.at(0);
        const wcstring w = str2wcstring(o);
        n = wcs2str(w.c_str());

        if (!o || !n)
        {
            err(L"Line %d - Conversion cycle of string %s produced null pointer on %s", __LINE__, o, L"wcs2str");
        }

        if (strcmp(o, n))
        {
            err(L"Line %d - %d: Conversion cycle of string %s produced different string %s", __LINE__, i, o, n);
        }
        free((void *)n);

    }
}
Exemplo n.º 29
0
int main(int argc, char **argv) {
    int res = 1;
    int my_optind = 0;

    program_name = L"fish";
    set_main_thread();
    setup_fork_guards();
    signal_unblock_all();
    setlocale(LC_ALL, "");
    fish_setlocale();

    // struct stat tmp;
    // stat("----------FISH_HIT_MAIN----------", &tmp);

    const char *dummy_argv[2] = {"fish", NULL};
    if (!argv[0]) {
        argv = (char **)dummy_argv;  //!OCLINT(parameter reassignment)
        argc = 1;                    //!OCLINT(parameter reassignment)
    }
    fish_cmd_opts_t opts;
    my_optind = fish_parse_opt(argc, argv, &opts);

    // No-exec is prohibited when in interactive mode.
    if (is_interactive_session && no_exec) {
        debug(1, _(L"Can not use the no-execute mode when running an interactive session"));
        no_exec = 0;
    }

    // Only save (and therefore restore) the fg process group if we are interactive. See issues
    // #197 and #1002.
    if (is_interactive_session) {
        save_term_foreground_process_group();
    }

    auto &globals = env_stack_t::globals();
    const struct config_paths_t paths = determine_config_directory_paths(argv[0]);
    env_init(&paths);
    // Set features early in case other initialization depends on them.
    // Start with the ones set in the environment, then those set on the command line (so the
    // command line takes precedence).
    if (auto features_var = globals.get(L"fish_features")) {
        for (const wcstring &s : features_var->as_list()) {
            mutable_fish_features().set_from_string(s);
        }
    }
    mutable_fish_features().set_from_string(opts.features);
    proc_init();
    builtin_init();
    misc_init();
    reader_init();

    parser_t &parser = parser_t::principal_parser();

    if (read_init(paths)) {
        // Stomp the exit status of any initialization commands (issue #635).
        proc_set_last_statuses(statuses_t::just(STATUS_CMD_OK));

        // Run post-config commands specified as arguments, if any.
        if (!opts.postconfig_cmds.empty()) {
            res = run_command_list(&opts.postconfig_cmds, {});
        }

        if (!opts.batch_cmds.empty()) {
            // Run the commands specified as arguments, if any.
            if (is_login) {
                // Do something nasty to support OpenSUSE assuming we're bash. This may modify cmds.
                fish_xdm_login_hack_hack_hack_hack(&opts.batch_cmds, argc - my_optind,
                                                   argv + my_optind);
            }
            res = run_command_list(&opts.batch_cmds, {});
            reader_set_end_loop(false);
        } else if (my_optind == argc) {
            // Implicitly interactive mode.
            res = reader_read(STDIN_FILENO, {});
        } else {
            char *file = *(argv + (my_optind++));
            int fd = open(file, O_RDONLY);
            if (fd == -1) {
                perror(file);
            } else {
                // OK to not do this atomically since we cannot have gone multithreaded yet.
                set_cloexec(fd);

                wcstring_list_t list;
                for (char **ptr = argv + my_optind; *ptr; ptr++) {
                    list.push_back(str2wcstring(*ptr));
                }
                parser.vars().set(L"argv", ENV_DEFAULT, list);

                const wcstring rel_filename = str2wcstring(file);

                reader_push_current_filename(rel_filename.c_str());

                res = reader_read(fd, {});

                if (res) {
                    debug(1, _(L"Error while reading file %ls\n"),
                          reader_current_filename() ? reader_current_filename()
                                                    : _(L"Standard input"));
                }
                reader_pop_current_filename();
            }
        }
    }

    int exit_status = res ? STATUS_CMD_UNKNOWN : proc_get_last_status();

    // TODO: The generic process-exit event is useless and unused.
    // Remove this in future.
    proc_fire_event(L"PROCESS_EXIT", event_type_t::exit, getpid(), exit_status);

    // Trigger any exit handlers.
    wcstring_list_t event_args = {to_string(exit_status)};
    event_fire_generic(L"fish_exit", &event_args);

    restore_term_mode();
    restore_term_foreground_process_group();

    if (g_profiling_active) {
        parser.emit_profiling(s_profiling_output_filename);
    }

    history_save_all();
    proc_destroy();
    exit_without_destructors(exit_status);
    return EXIT_FAILURE;  // above line should always exit
}
Exemplo n.º 30
0
/**
   Matches the string against the wildcard, and if the wildcard is a
   possible completion of the string, the remainder of the string is
   inserted into the out vector.
*/
static bool wildcard_complete_internal(const wcstring &orig,
                                       const wchar_t *str,
                                       const wchar_t *wc,
                                       bool is_first,
                                       const wchar_t *desc,
                                       wcstring(*desc_func)(const wcstring &),
                                       std::vector<completion_t> &out,
                                       expand_flags_t expand_flags,
                                       complete_flags_t flags)
{
    if (!wc || ! str || orig.empty())
    {
        debug(2, L"Got null string on line %d of file %s", __LINE__, __FILE__);
        return 0;
    }

    bool has_match = false;
    string_fuzzy_match_t fuzzy_match(fuzzy_match_exact);
    const bool at_end_of_wildcard = (*wc == L'\0');
    const wchar_t *completion_string = NULL;

    // Hack hack hack
    // Implement EXPAND_FUZZY_MATCH by short-circuiting everything if there are no remaining wildcards
    if ((expand_flags & EXPAND_FUZZY_MATCH) && ! at_end_of_wildcard && ! wildcard_has(wc, true))
    {
        string_fuzzy_match_t local_fuzzy_match = string_fuzzy_match_string(wc, str);
        if (local_fuzzy_match.type != fuzzy_match_none)
        {
            has_match = true;
            fuzzy_match = local_fuzzy_match;

            /* If we're not a prefix or exact match, then we need to replace the token. Note that in this case we're not going to call ourselves recursively, so these modified flags won't "leak" except into the completion. */
            if (match_type_requires_full_replacement(local_fuzzy_match.type))
            {
                flags |= COMPLETE_REPLACES_TOKEN;
                completion_string = orig.c_str();
            }
            else
            {
                /* Since we are not replacing the token, be careful to only store the part of the string after the wildcard */
                size_t wc_len = wcslen(wc);
                assert(wcslen(str) >= wc_len);
                completion_string = str + wcslen(wc);
            }
        }
    }

    /* Maybe we satisfied the wildcard normally */
    if (! has_match)
    {
        bool file_has_leading_dot = (is_first && str[0] == L'.');
        if (at_end_of_wildcard && ! file_has_leading_dot)
        {
            has_match = true;
            if (flags & COMPLETE_REPLACES_TOKEN)
            {
                completion_string = orig.c_str();
            }
            else
            {
                completion_string = str;
            }
        }
    }

    if (has_match)
    {
        /* Wildcard complete */
        assert(completion_string != NULL);
        wcstring out_completion = completion_string;
        wcstring out_desc = (desc ? desc : L"");

        size_t complete_sep_loc = out_completion.find(PROG_COMPLETE_SEP);
        if (complete_sep_loc != wcstring::npos)
        {
            /* This completion has an embedded description, do not use the generic description */
            out_desc.assign(out_completion, complete_sep_loc + 1, out_completion.size() - complete_sep_loc - 1);
            out_completion.resize(complete_sep_loc);
        }
        else
        {
            if (desc_func && !(expand_flags & EXPAND_NO_DESCRIPTIONS))
            {
                /*
                  A description generating function is specified, call
                  it. If it returns something, use that as the
                  description.
                */
                wcstring func_desc = desc_func(orig);
                if (! func_desc.empty())
                    out_desc = func_desc;
            }

        }

        /* Note: out_completion may be empty if the completion really is empty, e.g. tab-completing 'foo' when a file 'foo' exists. */
        append_completion(out, out_completion, out_desc, flags, fuzzy_match);
        return true;
    }

    if (*wc == ANY_STRING)
    {
        bool res=false;

        /* Ignore hidden file */
        if (is_first && str[0] == L'.')
            return false;

        /* Try all submatches */
        for (size_t i=0; str[i] != L'\0'; i++)
        {
            const size_t before_count = out.size();
            if (wildcard_complete_internal(orig, str + i, wc+1, false, desc, desc_func, out, expand_flags, flags))
            {
                res = true;

                /* #929: if the recursive call gives us a prefix match, just stop. This is sloppy - what we really want to do is say, once we've seen a match of a particular type, ignore all matches of that type further down the string, such that the wildcard produces the "minimal match." */
                bool has_prefix_match = false;
                const size_t after_count = out.size();
                for (size_t j = before_count; j < after_count; j++)
                {
                    if (out[j].match.type <= fuzzy_match_prefix)
                    {
                        has_prefix_match = true;
                        break;
                    }
                }
                if (has_prefix_match)
                    break;
            }
        }
        return res;

    }
    else if (*wc == ANY_CHAR || *wc == *str)
    {
        return wildcard_complete_internal(orig, str+1, wc+1, false, desc, desc_func, out, expand_flags, flags);
    }
    else if (towlower(*wc) == towlower(*str))
    {
        return wildcard_complete_internal(orig, str+1, wc+1, false, desc, desc_func, out, expand_flags, flags | COMPLETE_REPLACES_TOKEN);
    }
    return false;
}