Ejemplo n.º 1
0
/// Implementation of the builtin breakpoint command, used to launch the interactive debugger.
static int builtin_breakpoint(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
    wchar_t *cmd = argv[0];
    if (argv[1] != NULL) {
        streams.err.append_format(BUILTIN_ERR_ARG_COUNT1, cmd, 0, builtin_count_args(argv) - 1);
        return STATUS_INVALID_ARGS;
    }

    // If we're not interactive then we can't enter the debugger. So treat this command as a no-op.
    if (!shell_is_interactive()) {
        return STATUS_CMD_ERROR;
    }

    // Ensure we don't allow creating a breakpoint at an interactive prompt. There may be a simpler
    // or clearer way to do this but this works.
    const block_t *block1 = parser.block_at_index(1);
    if (!block1 || block1->type() == BREAKPOINT) {
        streams.err.append_format(_(L"%ls: Command not valid at an interactive prompt\n"), cmd);
        return STATUS_ILLEGAL_CMD;
    }

    const breakpoint_block_t *bpb = parser.push_block<breakpoint_block_t>();
    reader_read(STDIN_FILENO, streams.io_chain ? *streams.io_chain : io_chain_t());
    parser.pop_block(bpb);
    return proc_get_last_status();
}
Ejemplo n.º 2
0
/**
   When fishd isn't started, this function is provided to
   env_universal as a callback, it tries to start up fishd. It's
   implementation is a bit of a hack, since it evaluates a bit of
   shellscript, and it might be used at times when that might not be
   the best idea.
*/
static void start_fishd()
{
    struct passwd *pw = getpwuid(getuid());

    debug(3, L"Spawning new copy of fishd");

    if (!pw)
    {
        debug(0, _(L"Could not get user information"));
        return;
    }

    wcstring cmd = format_string(FISHD_CMD, pw->pw_name);

    /* Prefer the fishd in __fish_bin_dir, if exists */
    const env_var_t bin_dir = env_get_string(L"__fish_bin_dir");
    if (! bin_dir.missing_or_empty())
    {
        wcstring path = bin_dir + L"/fishd";
        if (waccess(path, X_OK) == 0)
        {
            /* The path command just looks like 'fishd', so insert the bin path to make it absolute */
            cmd.insert(0, bin_dir + L"/");
        }
    }
    parser_t &parser = parser_t::principal_parser();
    parser.eval(cmd, io_chain_t(), TOP);
}
Ejemplo n.º 3
0
// Source the file config.fish in the given directory.
static void source_config_in_directory(const wcstring &dir)
{
    // If the config.fish file doesn't exist or isn't readable silently return.
    // Fish versions up thru 2.2.0 would instead try to source the file with
    // stderr redirected to /dev/null to deal with that possibility.
    //
    // This introduces a race condition since the readability of the file can
    // change between this test and the execution of the 'source' command.
    // However, that is not a security problem in this context so we ignore it.
    const wcstring config_pathname = dir + L"/config.fish";
    const wcstring escaped_dir = escape_string(dir, ESCAPE_ALL);
    const wcstring escaped_pathname = escaped_dir + L"/config.fish";
    if (waccess(config_pathname, R_OK) != 0) {
        debug(2, L"not sourcing %ls (not readable or does not exist)",
                escaped_pathname.c_str());
        return;
    }
    debug(2, L"sourcing %ls", escaped_pathname.c_str());

    const wcstring cmd = L"builtin source " + escaped_pathname;
    parser_t &parser = parser_t::principal_parser();
    parser.set_is_within_fish_initialization(true);
    parser.eval(cmd, io_chain_t(), TOP);
    parser.set_is_within_fish_initialization(false);
}
Ejemplo n.º 4
0
/* Source the file config.fish in the given directory */
static void source_config_in_directory(const wcstring &dir)
{
    /* We want to execute a command like 'builtin source dir/config.fish 2>/dev/null' */
    const wcstring escaped_dir = escape_string(dir, ESCAPE_ALL);
    const wcstring cmd = L"builtin source " + escaped_dir + L"/config.fish 2>/dev/null";
    parser_t &parser = parser_t::principal_parser();
    parser.eval(cmd, io_chain_t(), TOP);
}
Ejemplo n.º 5
0
/**
   Perform the specified event. Since almost all event firings will
   not be matched by even a single event handler, we make sure to
   optimize the 'no matches' path. This means that nothing is
   allocated/initialized unless needed.
*/
static void event_fire_internal(const event_t &event)
{

    event_list_t fire;

    /*
      First we free all events that have been removed, but only if this
    		invocation of event_fire_internal is not a recursive call.
    */
    if (is_event <= 1)
        event_free_kills();

    if (events.empty())
        return;

    /*
      Then we iterate over all events, adding events that should be
      fired to a second list. We need to do this in a separate step
      since an event handler might call event_remove or
      event_add_handler, which will change the contents of the \c
      events list.
    */
    for (size_t i=0; i<events.size(); i++)
    {
        event_t *criterion = events.at(i);

        /*
          Check if this event is a match
        */
        if (event_match(*criterion, event))
        {
            fire.push_back(criterion);
        }
    }

    /*
      No matches. Time to return.
    */
    if (fire.empty())
        return;

    if (signal_is_blocked())
    {
        /* Fix for https://github.com/fish-shell/fish-shell/issues/608. Don't run event handlers while signals are blocked. */
        event_t *heap_event = new event_t(event);
        input_common_add_callback(fire_event_callback, heap_event);
        return;
    }

    /*
      Iterate over our list of matching events
    */

    for (size_t i=0; i<fire.size(); i++)
    {
        event_t *criterion = fire.at(i);
        int prev_status;

        /*
          Check if this event has been removed, if so, dont fire it
        */
        if (event_is_killed(*criterion))
            continue;

        /*
          Fire event
        */
        wcstring buffer = criterion->function_name;

        for (size_t j=0; j < event.arguments.size(); j++)
        {
            wcstring arg_esc = escape_string(event.arguments.at(j), 1);
            buffer += L" ";
            buffer += arg_esc;
        }

        // debug( 1, L"Event handler fires command '%ls'", buffer.c_str() );

        /*
          Event handlers are not part of the main flow of code, so
          they are marked as non-interactive
        */
        proc_push_interactive(0);
        prev_status = proc_get_last_status();
        parser_t &parser = parser_t::principal_parser();

        block_t *block = new event_block_t(event);
        parser.push_block(block);
        parser.eval(buffer, io_chain_t(), TOP);
        parser.pop_block();
        proc_pop_interactive();
        proc_set_last_status(prev_status);
    }

    /*
      Free killed events
    */
    if (is_event <= 1)
        event_free_kills();

}
Ejemplo n.º 6
0
/// The  source builtin, sometimes called `.`. Evaluates the contents of a file in the current
/// context.
int builtin_source(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
    ASSERT_IS_MAIN_THREAD();
    const wchar_t *cmd = argv[0];
    int argc = builtin_count_args(argv);
    help_only_cmd_opts_t opts;

    int optind;
    int retval = parse_help_only_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.out);
        return STATUS_CMD_OK;
    }

    int fd;
    struct stat buf;
    const wchar_t *fn, *fn_intern;

    if (argc == optind || wcscmp(argv[optind], L"-") == 0) {
        // Either a bare `source` which means to implicitly read from stdin or an explicit `-`.
        if (argc == optind && !streams.stdin_is_directly_redirected) {
            // Don't implicitly read from the terminal.
            return STATUS_CMD_ERROR;
        }
        fn = L"-";
        fn_intern = fn;
        fd = dup(streams.stdin_fd);
    } else {
        if ((fd = wopen_cloexec(argv[optind], O_RDONLY)) == -1) {
            streams.err.append_format(_(L"%ls: Error encountered while sourcing file '%ls':\n"),
                                      cmd, argv[optind]);
            builtin_wperror(cmd, streams);
            return STATUS_CMD_ERROR;
        }

        if (fstat(fd, &buf) == -1) {
            close(fd);
            streams.err.append_format(_(L"%ls: Error encountered while sourcing file '%ls':\n"),
                                      cmd, argv[optind]);
            builtin_wperror(L"source", streams);
            return STATUS_CMD_ERROR;
        }

        if (!S_ISREG(buf.st_mode)) {
            close(fd);
            streams.err.append_format(_(L"%ls: '%ls' is not a file\n"), cmd, argv[optind]);
            return STATUS_CMD_ERROR;
        }

        fn_intern = intern(argv[optind]);
    }

    const source_block_t *sb = parser.push_block<source_block_t>(fn_intern);
    reader_push_current_filename(fn_intern);

    // This is slightly subtle. If this is a bare `source` with no args then `argv + optind` already
    // points to the end of argv. Otherwise we want to skip the file name to get to the args if any.
    env_set_argv(argv + optind + (argc == optind ? 0 : 1));

    retval = reader_read(fd, streams.io_chain ? *streams.io_chain : io_chain_t());

    parser.pop_block(sb);

    if (retval != STATUS_CMD_OK) {
        streams.err.append_format(_(L"%ls: Error while reading file '%ls'\n"), cmd,
                                  fn_intern == intern_static(L"-") ? L"<stdin>" : fn_intern);
    } else {
        retval = proc_get_last_status();
    }

    // Do not close fd after calling reader_read. reader_read automatically closes it before calling
    // eval.
    reader_pop_current_filename();
    return retval;
}
Ejemplo n.º 7
0
static int exec_subshell_internal(const wcstring &cmd, wcstring_list_t *lst, bool apply_exit_status,
                                  bool is_subcmd) {
    ASSERT_IS_MAIN_THREAD();
    bool prev_subshell = is_subshell;
    const int prev_status = proc_get_last_status();
    bool split_output = false;

    const auto ifs = env_get(L"IFS");
    if (!ifs.missing_or_empty()) {
        split_output = true;
    }

    is_subshell = true;
    int subcommand_status = -1;  // assume the worst

    // IO buffer creation may fail (e.g. if we have too many open files to make a pipe), so this may
    // be null.
    const shared_ptr<io_buffer_t> io_buffer(
        io_buffer_t::create(STDOUT_FILENO, io_chain_t(), is_subcmd ? read_byte_limit : 0));
    if (io_buffer.get() != NULL) {
        parser_t &parser = parser_t::principal_parser();
        if (parser.eval(cmd, io_chain_t(io_buffer), SUBST) == 0) {
            subcommand_status = proc_get_last_status();
        }

        io_buffer->read();
    }

    if (io_buffer->buffer().discarded()) subcommand_status = STATUS_READ_TOO_MUCH;

    // If the caller asked us to preserve the exit status, restore the old status. Otherwise set the
    // status of the subcommand.
    proc_set_last_status(apply_exit_status ? subcommand_status : prev_status);
    is_subshell = prev_subshell;

    if (lst == NULL || io_buffer.get() == NULL) {
        return subcommand_status;
    }
    // Walk over all the elements.
    for (const auto &elem : io_buffer->buffer().elements()) {
        if (elem.is_explicitly_separated()) {
            // Just append this one.
            lst->push_back(str2wcstring(elem.contents));
            continue;
        }

        // Not explicitly separated. We have to split it explicitly.
        assert(!elem.is_explicitly_separated() && "should not be explicitly separated");
        const char *begin = elem.contents.data();
        const char *end = begin + elem.contents.size();
        if (split_output) {
            const char *cursor = begin;
            while (cursor < end) {
                // Look for the next separator.
                const char *stop = (const char *)memchr(cursor, '\n', end - cursor);
                const bool hit_separator = (stop != NULL);
                if (!hit_separator) {
                    // If it's not found, just use the end.
                    stop = end;
                }
                // Stop now points at the first character we do not want to copy.
                lst->push_back(str2wcstring(cursor, stop - cursor));

                // If we hit a separator, skip over it; otherwise we're at the end.
                cursor = stop + (hit_separator ? 1 : 0);
            }
        } else {
            // We're not splitting output, but we still want to trim off a trailing newline.
            if (end != begin && end[-1] == '\n') {
                --end;
            }
            lst->push_back(str2wcstring(begin, end - begin));
        }
    }

    return subcommand_status;
}
Ejemplo n.º 8
0
/**
   Perform the specified event. Since almost all event firings will
   not be matched by even a single event handler, we make sure to
   optimize the 'no matches' path. This means that nothing is
   allocated/initialized unless needed.
*/
static void event_fire_internal(const event_t *event)
{

    size_t i, j;
    event_list_t fire;

    /*
      First we free all events that have been removed
    */
    event_free_kills();

    if (events.empty())
        return;

    /*
      Then we iterate over all events, adding events that should be
      fired to a second list. We need to do this in a separate step
      since an event handler might call event_remove or
      event_add_handler, which will change the contents of the \c
      events list.
    */
    for (i=0; i<events.size(); i++)
    {
        event_t *criterion = events.at(i);

        /*
          Check if this event is a match
        */
        if (event_match(criterion, event))
        {
            fire.push_back(criterion);
        }
    }

    /*
      No matches. Time to return.
    */
    if (fire.empty())
        return;

    /*
      Iterate over our list of matching events
    */

    for (i=0; i<fire.size(); i++)
    {
        event_t *criterion = fire.at(i);
        int prev_status;

        /*
          Check if this event has been removed, if so, dont fire it
        */
        if (event_is_killed(criterion))
            continue;

        /*
          Fire event
        */
        wcstring buffer = criterion->function_name;

        if (event->arguments.get())
        {
            for (j=0; j< event->arguments->size(); j++)
            {
                wcstring arg_esc = escape_string(event->arguments->at(j), 1);
                buffer += L" ";
                buffer += arg_esc;
            }
        }

//    debug( 1, L"Event handler fires command '%ls'", buffer.c_str() );

        /*
          Event handlers are not part of the main flow of code, so
          they are marked as non-interactive
        */
        proc_push_interactive(0);
        prev_status = proc_get_last_status();
        parser_t &parser = parser_t::principal_parser();

        block_t *block = new event_block_t(event);
        parser.push_block(block);
        parser.eval(buffer, io_chain_t(), TOP);
        parser.pop_block();
        proc_pop_interactive();
        proc_set_last_status(prev_status);
    }

    /*
      Free killed events
    */
    event_free_kills();

}