Example #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();
}
Example #2
0
/// Sets up appropriate signal handlers.
void signal_set_handlers() {
    struct sigaction act;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);

    // Ignore SIGPIPE. We'll detect failed writes and deal with them appropriately. We don't want
    // this signal interrupting other syscalls or terminating us.
    act.sa_handler = SIG_IGN;
    sigaction(SIGPIPE, &act, 0);

    // Whether or not we're interactive we want SIGCHLD to not interrupt restartable syscalls.
    act.sa_flags = SA_SIGINFO;
    act.sa_sigaction = &handle_chld;
    act.sa_flags = SA_SIGINFO | SA_RESTART;
    if (sigaction(SIGCHLD, &act, 0)) {
        wperror(L"sigaction");
        FATAL_EXIT();
    }

    if (shell_is_interactive()) {
        set_interactive_handlers();
    } else {
        set_non_interactive_handlers();
    }
}
Example #3
0
wcstring parser_t::current_line() {
    if (execution_contexts.empty()) {
        return wcstring();
    }
    const parse_execution_context_t *context = execution_contexts.back().get();
    assert(context != NULL);

    int source_offset = context->get_current_source_offset();
    if (source_offset < 0) {
        return wcstring();
    }

    const int lineno = this->get_lineno();
    const wchar_t *file = this->current_filename();

    wcstring prefix;

    // If we are not going to print a stack trace, at least print the line number and filename.
    if (!shell_is_interactive() || is_function()) {
        if (file) {
            append_format(prefix, _(L"%ls (line %d): "), user_presentable_path(file).c_str(),
                          lineno);
        } else if (is_within_fish_initialization) {
            append_format(prefix, L"%ls (line %d): ", _(L"Startup"), lineno);
        } else {
            append_format(prefix, L"%ls (line %d): ", _(L"Standard input"), lineno);
        }
    }

    bool is_interactive = shell_is_interactive();
    bool skip_caret = is_interactive && !is_function();

    // Use an error with empty text.
    assert(source_offset >= 0);
    parse_error_t empty_error = {};
    empty_error.source_start = source_offset;

    wcstring line_info =
        empty_error.describe_with_prefix(context->get_source(), prefix, is_interactive, skip_caret);
    if (!line_info.empty()) {
        line_info.push_back(L'\n');
    }

    line_info.append(this->stack_trace());
    return line_info;
}
Example #4
0
// Check if we are setting a uvar and a global of the same name exists. See
// https://github.com/fish-shell/fish-shell/issues/806
static int check_global_scope_exists(const wchar_t *cmd, set_cmd_opts_t &opts, const wchar_t *dest,
                                     io_streams_t &streams, const environment_t &vars) {
    if (opts.universal) {
        auto global_dest = vars.get(dest, ENV_GLOBAL);
        if (global_dest && shell_is_interactive()) {
            streams.err.append_format(BUILTIN_SET_UVAR_ERR, cmd, dest);
        }
    }

    return STATUS_CMD_OK;
}
Example #5
0
void parser_t::get_backtrace(const wcstring &src, const parse_error_list_t &errors,
                             wcstring *output) const {
    assert(output != NULL);
    if (!errors.empty()) {
        const parse_error_t &err = errors.at(0);

        const bool is_interactive = shell_is_interactive();

        // Determine if we want to try to print a caret to point at the source error. The
        // err.source_start <= src.size() check is due to the nasty way that slices work, which is
        // by rewriting the source.
        size_t which_line = 0;
        bool skip_caret = true;
        if (err.source_start != SOURCE_LOCATION_UNKNOWN && err.source_start <= src.size()) {
            // Determine which line we're on.
            which_line = 1 + std::count(src.begin(), src.begin() + err.source_start, L'\n');

            // Don't include the caret if we're interactive, this is the first line of text, and our
            // source is at its beginning, because then it's obvious.
            skip_caret = (is_interactive && which_line == 1 && err.source_start == 0);
        }

        wcstring prefix;
        const wchar_t *filename = this->current_filename();
        if (filename) {
            if (which_line > 0) {
                prefix = format_string(_(L"%ls (line %lu): "),
                                       user_presentable_path(filename).c_str(), which_line);
            } else {
                prefix = format_string(_(L"%ls: "), user_presentable_path(filename).c_str());
            }
        } else {
            prefix = L"fish: ";
        }

        const wcstring description =
            err.describe_with_prefix(src, prefix, is_interactive, skip_caret);
        if (!description.empty()) {
            output->append(description);
            output->push_back(L'\n');
        }
        output->append(this->stack_trace());
    }
}
Example #6
0
/// The cd builtin. Changes the current directory to the one specified or to $HOME if none is
/// specified. The directory can be relative to any directory in the CDPATH variable.
/// The cd builtin. Changes the current directory to the one specified or to $HOME if none is
/// specified. The directory can be relative to any directory in the CDPATH variable.
int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
    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;
    }

    env_var_t dir_in;
    wcstring dir;

    if (argv[optind]) {
        dir_in = env_var_t(L"", argv[optind]);  // unamed var
    } else {
        auto maybe_dir_in = env_get(L"HOME");
        if (maybe_dir_in.missing_or_empty()) {
            streams.err.append_format(_(L"%ls: Could not find home directory\n"), cmd);
            return STATUS_CMD_ERROR;
        }
        dir_in = std::move(*maybe_dir_in);
    }

    if (!path_get_cdpath(dir_in, &dir)) {
        if (errno == ENOTDIR) {
            streams.err.append_format(_(L"%ls: '%ls' is not a directory\n"), cmd,
                                      dir_in.as_string().c_str());
        } else if (errno == ENOENT) {
            streams.err.append_format(_(L"%ls: The directory '%ls' does not exist\n"), cmd,
                                      dir_in.as_string().c_str());
        } else if (errno == EROTTEN) {
            streams.err.append_format(_(L"%ls: '%ls' is a rotten symlink\n"), cmd,
                                      dir_in.as_string().c_str());
        } else {
            streams.err.append_format(_(L"%ls: Unknown error trying to locate directory '%ls'\n"),
                                      cmd, dir_in.as_string().c_str());
        }

        if (!shell_is_interactive()) streams.err.append(parser.current_line());

        return STATUS_CMD_ERROR;
    }

    if (wchdir(dir) != 0) {
        struct stat buffer;
        int status;

        status = wstat(dir, &buffer);
        if (!status && S_ISDIR(buffer.st_mode)) {
            streams.err.append_format(_(L"%ls: Permission denied: '%ls'\n"), cmd, dir.c_str());

        } else {
            streams.err.append_format(_(L"%ls: '%ls' is not a directory\n"), cmd, dir.c_str());
        }

        if (!shell_is_interactive()) {
            streams.err.append(parser.current_line());
        }

        return STATUS_CMD_ERROR;
    }

    if (!env_set_pwd()) {
        streams.err.append_format(_(L"%ls: Could not set PWD variable\n"), cmd);
        return STATUS_CMD_ERROR;
    }

    return STATUS_CMD_OK;
}
Example #7
0
/// Sets appropriate signal handlers.
void signal_set_handlers() {
    struct sigaction act;

    sigemptyset(&act.sa_mask);
    act.sa_flags = SA_SIGINFO;
    act.sa_sigaction = &default_handler;

    // First reset everything to a use default_handler, a function whose sole action is to fire of
    // an event.
    sigaction(SIGINT, &act, 0);
    sigaction(SIGQUIT, &act, 0);
    sigaction(SIGTSTP, &act, 0);
    sigaction(SIGTTIN, &act, 0);
    sigaction(SIGTTOU, &act, 0);
    sigaction(SIGCHLD, &act, 0);

    // Ignore sigpipe, which we may get from the universal variable notifier.
    sigaction(SIGPIPE, &act, 0);

    if (shell_is_interactive()) {
        // Interactive mode. Ignore interactive signals.  We are a shell, we know what is best for
        // the user.
        act.sa_handler = SIG_IGN;

        sigaction(SIGINT, &act, 0);
        sigaction(SIGQUIT, &act, 0);
        sigaction(SIGTSTP, &act, 0);
        sigaction(SIGTTIN, &act, 0);
        sigaction(SIGTTOU, &act, 0);

        act.sa_sigaction = &handle_int;
        act.sa_flags = SA_SIGINFO;
        if (sigaction(SIGINT, &act, 0)) {
            wperror(L"sigaction");
            FATAL_EXIT();
        }

        act.sa_sigaction = &handle_chld;
        act.sa_flags = SA_SIGINFO;
        if (sigaction(SIGCHLD, &act, 0)) {
            wperror(L"sigaction");
            FATAL_EXIT();
        }

#ifdef SIGWINCH
        act.sa_flags = SA_SIGINFO;
        act.sa_sigaction = &handle_winch;
        if (sigaction(SIGWINCH, &act, 0)) {
            wperror(L"sigaction");
            FATAL_EXIT();
        }
#endif

        act.sa_flags = SA_SIGINFO;
        act.sa_sigaction = &handle_hup;
        if (sigaction(SIGHUP, &act, 0)) {
            wperror(L"sigaction");
            FATAL_EXIT();
        }

        // SIGTERM restores the terminal controlling process before dying.
        act.sa_flags = SA_SIGINFO;
        act.sa_sigaction = &handle_term;
        if (sigaction(SIGTERM, &act, 0)) {
            wperror(L"sigaction");
            FATAL_EXIT();
        }
    } else {
        // Non-interactive. Ignore interrupt, check exit status of processes to determine result
        // instead.
        act.sa_handler = SIG_IGN;
        sigaction(SIGINT, &act, 0);
        sigaction(SIGQUIT, &act, 0);

        act.sa_handler = SIG_DFL;
        act.sa_sigaction = &handle_chld;
        act.sa_flags = SA_SIGINFO;
        if (sigaction(SIGCHLD, &act, 0)) {
            wperror(L"sigaction");
            exit_without_destructors(1);
        }
    }
}