Beispiel #1
0
static int parse_cmd_opts(exit_cmd_opts_t &opts, int *optind,  //!OCLINT(high ncss method)
                          int argc, wchar_t **argv, parser_t &parser, io_streams_t &streams) {
    UNUSED(parser);
    UNUSED(streams);
    wchar_t *cmd = argv[0];
    int opt;
    wgetopter_t w;
    while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
        switch (opt) {  //!OCLINT(too few branches)
            case 'h': {
                opts.print_help = true;
                break;
            }
            case ':': {
                builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            case '?': {
                // We would normally invoke builtin_unknown_option() and return an error.
                // But for this command we want to let it try and parse the value as a negative
                // return value.
                *optind = w.woptind - 1;
                return STATUS_CMD_OK;
            }
            default: {
                DIE("unexpected retval from wgetopt_long");
                break;
            }
        }
    }

    *optind = w.woptind;
    return STATUS_CMD_OK;
}
Beispiel #2
0
int parse_help_only_cmd_opts(struct help_only_cmd_opts_t &opts, int *optind, int argc,
                             wchar_t **argv, parser_t &parser, io_streams_t &streams) {
    wchar_t *cmd = argv[0];
    int opt;
    wgetopter_t w;
    while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
        switch (opt) {  //!OCLINT(too few branches)
            case 'h': {
                opts.print_help = true;
                break;
            }
            case ':': {
                builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            case '?': {
                builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            default: {
                DIE("unexpected retval from wgetopt_long");
                break;
            }
        }
    }

    *optind = w.woptind;
    return STATUS_CMD_OK;
}
static int parse_cmd_opts(command_cmd_opts_t &opts, int *optind, int argc, wchar_t **argv,
                          parser_t &parser, io_streams_t &streams) {
    wchar_t *cmd = argv[0];
    int opt;
    wgetopter_t w;
    while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
        switch (opt) {
            case 'a': {
                opts.all_paths = true;
                break;
            }
            case 'h': {
                opts.print_help = true;
                break;
            }
            case 'q': {
                opts.quiet = true;
                break;
            }
            case 's':  // -s and -v are aliases
            case 'v': {
                opts.find_path = true;
                break;
            }
            case ':': {
                builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            case '?': {
                builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            default: {
                DIE("unexpected retval from wgetopt_long");
                break;
            }
        }
    }

    *optind = w.woptind;
    return STATUS_CMD_OK;
}
Beispiel #4
0
static int parse_cmd_opts(math_cmd_opts_t &opts, int *optind,  //!OCLINT(high ncss method)
                          int argc, wchar_t **argv, parser_t &parser, io_streams_t &streams) {
    const wchar_t *cmd = L"math";
    int opt;
    wgetopter_t w;
    while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
        switch (opt) {
            case 's': {
                opts.scale = fish_wcstoi(w.woptarg);
                if (errno || opts.scale < 0 || opts.scale > 15) {
                    streams.err.append_format(_(L"%ls: '%ls' is not a valid scale value\n"), cmd,
                                              w.woptarg);
                    return STATUS_INVALID_ARGS;
                }
                break;
            }
            case 'h': {
                opts.print_help = true;
                break;
            }
            case ':': {
                builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            case '?': {
                // For most commands this is an error. We ignore it because a math expression
                // can begin with a minus sign.
                *optind = w.woptind - 1;
                return STATUS_CMD_OK;
            }
            default: {
                DIE("unexpected retval from wgetopt_long");
                break;
            }
        }
    }

    *optind = w.woptind;
    return STATUS_CMD_OK;
}
/**
   The complete builtin. Used for specifying programmable
   tab-completions. Calls the functions in complete.c for any heavy
   lifting. Defined in builtin_complete.c
*/
static int builtin_complete(parser_t &parser, io_streams_t &streams, wchar_t **argv)
{
    ASSERT_IS_MAIN_THREAD();
    wgetopter_t w;
    bool res=false;
    int argc=0;
    int result_mode=SHARED;
    int remove = 0;
    int authoritative = -1;

    wcstring short_opt;
    wcstring_list_t gnu_opt, old_opt;
    const wchar_t *comp=L"", *desc=L"", *condition=L"";

    bool do_complete = false;
    wcstring do_complete_param;

    wcstring_list_t cmd;
    wcstring_list_t path;
    wcstring_list_t wrap_targets;

    static int recursion_level=0;

    argc = builtin_count_args(argv);

    w.woptind=0;

    while (! res)
    {
        static const struct woption
                long_options[] =
        {
            { L"exclusive", no_argument, 0, 'x' },
            { L"no-files", no_argument, 0, 'f' },
            { L"require-parameter", no_argument, 0, 'r' },
            { L"path", required_argument, 0, 'p' },
            { L"command", required_argument, 0, 'c' },
            { L"short-option", required_argument, 0, 's' },
            { L"long-option", required_argument, 0, 'l' },
            { L"old-option", required_argument, 0, 'o' },
            { L"description", required_argument, 0, 'd' },
            { L"arguments", required_argument, 0, 'a' },
            { L"erase", no_argument, 0, 'e' },
            { L"unauthoritative", no_argument, 0, 'u' },
            { L"authoritative", no_argument, 0, 'A' },
            { L"condition", required_argument, 0, 'n' },
            { L"wraps", required_argument, 0, 'w' },
            { L"do-complete", optional_argument, 0, 'C' },
            { L"help", no_argument, 0, 'h' },
            { 0, 0, 0, 0 }
        };

        int opt_index = 0;

        int opt = w.wgetopt_long(argc,
                                 argv,
                                 L"a:c:p:s:l:o:d:frxeuAn:C::w:h",
                                 long_options,
                                 &opt_index);
        if (opt == -1)
            break;

        switch (opt)
        {
            case 0:
                if (long_options[opt_index].flag != 0)
                    break;
                streams.err.append_format(BUILTIN_ERR_UNKNOWN,
                              argv[0],
                              long_options[opt_index].name);
                builtin_print_help(parser, streams, argv[0], streams.err);


                res = true;
                break;

            case 'x':
                result_mode |= EXCLUSIVE;
                break;

            case 'f':
                result_mode |= NO_FILES;
                break;

            case 'r':
                result_mode |= NO_COMMON;
                break;

            case 'p':
            case 'c':
            {
                wcstring tmp;
                if (unescape_string(w.woptarg, &tmp, UNESCAPE_SPECIAL))
                {
                    if (opt=='p')
                        path.push_back(tmp);
                    else
                        cmd.push_back(tmp);
                }
                else
                {
                    streams.err.append_format(L"%ls: Invalid token '%ls'\n", argv[0], w.woptarg);
                    res = true;
                }
                break;
            }

            case 'd':
                desc = w.woptarg;
                break;

            case 'u':
                authoritative=0;
                break;

            case 'A':
                authoritative=1;
                break;

            case 's':
                short_opt.append(w.woptarg);
                break;

            case 'l':
                gnu_opt.push_back(w.woptarg);
                break;

            case 'o':
                old_opt.push_back(w.woptarg);
                break;

            case 'a':
                comp = w.woptarg;
                break;

            case 'e':
                remove = 1;
                break;

            case 'n':
                condition = w.woptarg;
                break;
                
            case 'w':
                wrap_targets.push_back(w.woptarg);
                break;

            case 'C':
            {
                do_complete = true;
                const wchar_t *arg = w.woptarg ? w.woptarg : reader_get_buffer();
                if (arg == NULL)
                {
                    // This corresponds to using 'complete -C' in non-interactive mode
                    // See #2361
                    builtin_missing_argument(parser, streams, argv[0], argv[w.woptind-1]);
                    return STATUS_BUILTIN_ERROR;
                }
                do_complete_param = arg;
                break;
            }

            case 'h':
                builtin_print_help(parser, streams, argv[0], streams.out);
                return 0;

            case '?':
                builtin_unknown_option(parser, streams, argv[0], argv[w.woptind-1]);
                res = true;
                break;

        }

    }

    if (!res)
    {
        if (condition && wcslen(condition))
        {
            const wcstring condition_string = condition;
            parse_error_list_t errors;
            if (parse_util_detect_errors(condition_string, &errors, false /* do not accept incomplete */))
            {
                streams.err.append_format(L"%ls: Condition '%ls' contained a syntax error",
                              argv[0],
                              condition);
                for (size_t i=0; i < errors.size(); i++)
                {
                    streams.err.append_format(L"\n%s: ", argv[0]);
                    streams.err.append(errors.at(i).describe(condition_string));
                }
                res = true;
            }
        }
    }

    if (!res)
    {
        if (comp && wcslen(comp))
        {
            wcstring prefix;
            if (argv[0])
            {
                prefix.append(argv[0]);
                prefix.append(L": ");
            }

            wcstring err_text;
            if (parser.detect_errors_in_argument_list(comp, &err_text, prefix.c_str()))
            {
                streams.err.append_format(L"%ls: Completion '%ls' contained a syntax error\n",
                              argv[0],
                              comp);
                streams.err.append(err_text);
                streams.err.push_back(L'\n');
                res = true;
            }
        }
    }

    if (!res)
    {
        if (do_complete)
        {
            const wchar_t *token;

            parse_util_token_extent(do_complete_param.c_str(), do_complete_param.size(), &token, 0, 0, 0);
            
            /* Create a scoped transient command line, so that bulitin_commandline will see our argument, not the reader buffer */
            builtin_commandline_scoped_transient_t temp_buffer(do_complete_param);

            if (recursion_level < 1)
            {
                recursion_level++;

                std::vector<completion_t> comp;
                complete(do_complete_param, comp, COMPLETION_REQUEST_DEFAULT);

                for (size_t i=0; i< comp.size() ; i++)
                {
                    const completion_t &next =  comp.at(i);

                    /* Make a fake commandline, and then apply the completion to it.  */
                    const wcstring faux_cmdline = token;
                    size_t tmp_cursor = faux_cmdline.size();
                    wcstring faux_cmdline_with_completion = completion_apply_to_command_line(next.completion, next.flags, faux_cmdline, &tmp_cursor, false);

                    /* completion_apply_to_command_line will append a space unless COMPLETE_NO_SPACE is set. We don't want to set COMPLETE_NO_SPACE because that won't close quotes. What we want is to close the quote, but not append the space. So we just look for the space and clear it. */
                    if (!(next.flags & COMPLETE_NO_SPACE) && string_suffixes_string(L" ", faux_cmdline_with_completion))
                    {
                        faux_cmdline_with_completion.resize(faux_cmdline_with_completion.size() - 1);
                    }

                    /* The input data is meant to be something like you would have on the command line, e.g. includes backslashes. The output should be raw, i.e. unescaped. So we need to unescape the command line. See #1127 */
                    unescape_string_in_place(&faux_cmdline_with_completion, UNESCAPE_DEFAULT);
                    streams.out.append(faux_cmdline_with_completion);

                    /* Append any description */
                    if (! next.description.empty())
                    {
                        streams.out.push_back(L'\t');
                        streams.out.append(next.description);
                    }
                    streams.out.push_back(L'\n');
                }

                recursion_level--;
            }
        }
        else if (w.woptind != argc)
        {
            streams.err.append_format(_(L"%ls: Too many arguments\n"),
                          argv[0]);
            builtin_print_help(parser, streams, argv[0], streams.err);

            res = true;
        }
        else if (cmd.empty() && path.empty())
        {
            /* No arguments specified, meaning we print the definitions of
             * all specified completions to stdout.*/
            streams.out.append(complete_print());
        }
        else
        {
            int flags = COMPLETE_AUTO_SPACE;
        
            if (remove)
            {
                builtin_complete_remove(cmd,
                                        path,
                                        short_opt.c_str(),
                                        gnu_opt,
                                        old_opt);
                
            }
            else
            {
                builtin_complete_add(cmd,
                                     path,
                                     short_opt.c_str(),
                                     gnu_opt,
                                     old_opt,
                                     result_mode,
                                     authoritative,
                                     condition,
                                     comp,
                                     desc,
                                     flags);
            }
            
            // Handle wrap targets (probably empty)
            // We only wrap commands, not paths
            for (size_t w=0; w < wrap_targets.size(); w++)
            {
                const wcstring &wrap_target = wrap_targets.at(w);
                for (size_t i=0; i < cmd.size(); i++)
                {
                    
                    (remove ? complete_remove_wrapper : complete_add_wrapper)(cmd.at(i), wrap_target);
                }
            }
        }
    }

    return res ? 1 : 0;
}
Beispiel #6
0
/// The following function is invoked on the main thread, because the job operation is not thread
/// safe. It waits for child jobs, not for child processes individually.
int builtin_wait(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
    ASSERT_IS_MAIN_THREAD();
    int retval = STATUS_CMD_OK;
    const wchar_t *cmd = argv[0];
    int argc = builtin_count_args(argv);
    bool any_flag = false;  // flag for -n option

    static const wchar_t *const short_options = L":n";
    static const struct woption long_options[] = {{L"any", no_argument, NULL, 'n'},
                                                  {NULL, 0, NULL, 0}};

    int opt;
    wgetopter_t w;
    while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
        switch (opt) {
            case 'n':
                any_flag = true;
                break;
            case ':': {
                builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            case '?': {
                builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            default: {
                DIE("unexpected retval from wgetopt_long");
                break;
            }
        }
    }

    if (w.woptind == argc) {
        // no jobs specified
        retval = wait_for_backgrounds(parser, any_flag);
    } else {
        // jobs specified
        std::vector<job_id_t> waited_job_ids;

        for (int i = w.woptind; i < argc; i++) {
            if (iswnumeric(argv[i])) {
                // argument is pid
                pid_t pid = fish_wcstoi(argv[i]);
                if (errno || pid <= 0) {
                    streams.err.append_format(_(L"%ls: '%ls' is not a valid process id\n"), cmd,
                                              argv[i]);
                    continue;
                }
                if (job_id_t id = get_job_id_from_pid(pid, parser)) {
                    waited_job_ids.push_back(id);
                } else {
                    streams.err.append_format(
                        _(L"%ls: Could not find a job with process id '%d'\n"), cmd, pid);
                }
            } else {
                // argument is process name
                if (!find_job_by_name(argv[i], waited_job_ids, parser)) {
                    streams.err.append_format(
                        _(L"%ls: Could not find child processes with the name '%ls'\n"), cmd,
                        argv[i]);
                }
            }
        }

        if (waited_job_ids.empty()) return STATUS_INVALID_ARGS;

        retval = wait_for_backgrounds_specified(parser, waited_job_ids, any_flag);
    }

    return retval;
}
Beispiel #7
0
static int parse_cmd_opts(set_cmd_opts_t &opts, int *optind,  //!OCLINT(high ncss method)
                          int argc, wchar_t **argv, parser_t &parser, io_streams_t &streams) {
    wchar_t *cmd = argv[0];

    int opt;
    wgetopter_t w;
    while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
        switch (opt) {
            case 'a': {
                opts.append = true;
                break;
            }
            case 'e': {
                opts.erase = true;
                opts.preserve_failure_exit_status = false;
                break;
            }
            case 'g': {
                opts.global = true;
                break;
            }
            case 'h': {
                opts.print_help = true;
                break;
            }
            case 'l': {
                opts.local = true;
                break;
            }
            case 'n': {
                opts.list = true;
                opts.preserve_failure_exit_status = false;
                break;
            }
            case 'p': {
                opts.prepend = true;
                break;
            }
            case 'q': {
                opts.query = true;
                opts.preserve_failure_exit_status = false;
                break;
            }
            case 'x': {
                opts.exportv = true;
                break;
            }
            case 'u': {
                opts.unexport = true;
                break;
            }
            case opt_path: {
                opts.pathvar = true;
                break;
            }
            case opt_unpath: {
                opts.unpathvar = true;
                break;
            }
            case 'U': {
                opts.universal = true;
                break;
            }
            case 'L': {
                opts.shorten_ok = false;
                break;
            }
            case 'S': {
                opts.show = true;
                opts.preserve_failure_exit_status = false;
                break;
            }
            case ':': {
                builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            case '?': {
                builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            default: {
                DIE("unexpected retval from wgetopt_long");
                break;
            }
        }
    }

    *optind = w.woptind;
    return STATUS_CMD_OK;
}
/// The commandline builtin. It is used for specifying a new value for the commandline.
int builtin_commandline(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
    // Pointer to what the commandline builtin considers to be the current contents of the command
    // line buffer.
    const wchar_t *current_buffer = 0;

    // What the commandline builtin considers to be the current cursor position.
    size_t current_cursor_pos = (size_t)(-1);

    wchar_t *cmd = argv[0];
    int buffer_part = 0;
    int cut_at_cursor = 0;

    int argc = builtin_count_args(argv);
    int append_mode = 0;

    int function_mode = 0;
    int selection_mode = 0;

    int tokenize = 0;

    int cursor_mode = 0;
    int line_mode = 0;
    int search_mode = 0;
    int paging_mode = 0;
    const wchar_t *begin = NULL, *end = NULL;

    wcstring transient_commandline;
    if (get_top_transient(&transient_commandline)) {
        current_buffer = transient_commandline.c_str();
        current_cursor_pos = transient_commandline.size();
    } else {
        current_buffer = reader_get_buffer();
        current_cursor_pos = reader_get_cursor_pos();
    }

    if (!current_buffer) {
        if (is_interactive_session) {
            // Prompt change requested while we don't have a prompt, most probably while reading the
            // init files. Just ignore it.
            return STATUS_CMD_ERROR;
        }

        streams.err.append(argv[0]);
        streams.err.append(L": Can not set commandline in non-interactive mode\n");
        builtin_print_help(parser, streams, cmd, streams.err);
        return STATUS_CMD_ERROR;
    }

    static const wchar_t *const short_options = L":abijpctwforhI:CLSsP";
    static const struct woption long_options[] = {{L"append", no_argument, NULL, 'a'},
                                                  {L"insert", no_argument, NULL, 'i'},
                                                  {L"replace", no_argument, NULL, 'r'},
                                                  {L"current-buffer", no_argument, NULL, 'b'},
                                                  {L"current-job", no_argument, NULL, 'j'},
                                                  {L"current-process", no_argument, NULL, 'p'},
                                                  {L"current-selection", no_argument, NULL, 's'},
                                                  {L"current-token", no_argument, NULL, 't'},
                                                  {L"cut-at-cursor", no_argument, NULL, 'c'},
                                                  {L"function", no_argument, NULL, 'f'},
                                                  {L"tokenize", no_argument, NULL, 'o'},
                                                  {L"help", no_argument, NULL, 'h'},
                                                  {L"input", required_argument, NULL, 'I'},
                                                  {L"cursor", no_argument, NULL, 'C'},
                                                  {L"line", no_argument, NULL, 'L'},
                                                  {L"search-mode", no_argument, NULL, 'S'},
                                                  {L"paging-mode", no_argument, NULL, 'P'},
                                                  {NULL, 0, NULL, 0}};

    int opt;
    wgetopter_t w;
    while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
        switch (opt) {
            case L'a': {
                append_mode = APPEND_MODE;
                break;
            }
            case L'b': {
                buffer_part = STRING_MODE;
                break;
            }
            case L'i': {
                append_mode = INSERT_MODE;
                break;
            }
            case L'r': {
                append_mode = REPLACE_MODE;
                break;
            }
            case 'c': {
                cut_at_cursor = 1;
                break;
            }
            case 't': {
                buffer_part = TOKEN_MODE;
                break;
            }
            case 'j': {
                buffer_part = JOB_MODE;
                break;
            }
            case 'p': {
                buffer_part = PROCESS_MODE;
                break;
            }
            case 'f': {
                function_mode = 1;
                break;
            }
            case 'o': {
                tokenize = 1;
                break;
            }
            case 'I': {
                current_buffer = w.woptarg;
                current_cursor_pos = wcslen(w.woptarg);
                break;
            }
            case 'C': {
                cursor_mode = 1;
                break;
            }
            case 'L': {
                line_mode = 1;
                break;
            }
            case 'S': {
                search_mode = 1;
                break;
            }
            case 's': {
                selection_mode = 1;
                break;
            }
            case 'P': {
                paging_mode = 1;
                break;
            }
            case 'h': {
                builtin_print_help(parser, streams, cmd, streams.out);
                return STATUS_CMD_OK;
            }
            case ':': {
                builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            case L'?': {
                builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            default: {
                DIE("unexpected retval from wgetopt_long");
                break;
            }
        }
    }

    if (function_mode) {
        int i;

        // Check for invalid switch combinations.
        if (buffer_part || cut_at_cursor || append_mode || tokenize || cursor_mode || line_mode ||
            search_mode || paging_mode) {
            streams.err.append_format(BUILTIN_ERR_COMBO, argv[0]);
            builtin_print_help(parser, streams, cmd, streams.err);
            return STATUS_INVALID_ARGS;
        }

        if (argc == w.woptind) {
            builtin_missing_argument(parser, streams, cmd, argv[0]);
            return STATUS_INVALID_ARGS;
        }

        for (i = w.woptind; i < argc; i++) {
            wchar_t c = input_function_get_code(argv[i]);
            if (c != INPUT_CODE_NONE) {
                // input_unreadch inserts the specified keypress or readline function at the back of
                // the queue of unused keypresses.
                input_queue_ch(c);
            } else {
                streams.err.append_format(_(L"%ls: Unknown input function '%ls'"), cmd, argv[i]);
                builtin_print_help(parser, streams, cmd, streams.err);
                return STATUS_INVALID_ARGS;
            }
        }

        return STATUS_CMD_OK;
    }

    if (selection_mode) {
        size_t start, len;
        const wchar_t *buffer = reader_get_buffer();
        if (reader_get_selection(&start, &len)) {
            streams.out.append(buffer + start, len);
        }
        return STATUS_CMD_OK;
    }

    // Check for invalid switch combinations.
    if ((search_mode || line_mode || cursor_mode || paging_mode) && (argc - w.woptind > 1)) {
        streams.err.append_format(L"%ls: Too many arguments", argv[0]);
        builtin_print_help(parser, streams, cmd, streams.err);
        return STATUS_INVALID_ARGS;
    }

    if ((buffer_part || tokenize || cut_at_cursor) &&
        (cursor_mode || line_mode || search_mode || paging_mode)) {
        streams.err.append_format(BUILTIN_ERR_COMBO, argv[0]);
        builtin_print_help(parser, streams, cmd, streams.err);
        return STATUS_INVALID_ARGS;
    }

    if ((tokenize || cut_at_cursor) && (argc - w.woptind)) {
        streams.err.append_format(
            BUILTIN_ERR_COMBO2, cmd,
            L"--cut-at-cursor and --tokenize can not be used when setting the commandline");
        builtin_print_help(parser, streams, cmd, streams.err);
        return STATUS_INVALID_ARGS;
    }

    if (append_mode && !(argc - w.woptind)) {
        streams.err.append_format(
            BUILTIN_ERR_COMBO2, cmd,
            L"insertion mode switches can not be used when not in insertion mode");
        builtin_print_help(parser, streams, cmd, streams.err);
        return STATUS_INVALID_ARGS;
    }

    // Set default modes.
    if (!append_mode) {
        append_mode = REPLACE_MODE;
    }

    if (!buffer_part) {
        buffer_part = STRING_MODE;
    }

    if (cursor_mode) {
        if (argc - w.woptind) {
            long new_pos = fish_wcstol(argv[w.woptind]);
            if (errno) {
                streams.err.append_format(BUILTIN_ERR_NOT_NUMBER, cmd, argv[w.woptind]);
                builtin_print_help(parser, streams, cmd, streams.err);
            }

            current_buffer = reader_get_buffer();
            new_pos = maxi(0L, mini(new_pos, (long)wcslen(current_buffer)));
            reader_set_buffer(current_buffer, (size_t)new_pos);
        } else {
            streams.out.append_format(L"%lu\n", (unsigned long)reader_get_cursor_pos());
        }
        return STATUS_CMD_OK;
    }

    if (line_mode) {
        size_t pos = reader_get_cursor_pos();
        const wchar_t *buff = reader_get_buffer();
        streams.out.append_format(L"%lu\n", (unsigned long)parse_util_lineno(buff, pos));
        return STATUS_CMD_OK;
    }

    if (search_mode) {
        return reader_is_in_search_mode() ? 0 : 1;
    }

    if (paging_mode) {
        return reader_has_pager_contents() ? 0 : 1;
    }

    switch (buffer_part) {
        case STRING_MODE: {
            begin = current_buffer;
            end = begin + wcslen(begin);
            break;
        }
        case PROCESS_MODE: {
            parse_util_process_extent(current_buffer, current_cursor_pos, &begin, &end);
            break;
        }
        case JOB_MODE: {
            parse_util_job_extent(current_buffer, current_cursor_pos, &begin, &end);
            break;
        }
        case TOKEN_MODE: {
            parse_util_token_extent(current_buffer, current_cursor_pos, &begin, &end, 0, 0);
            break;
        }
        default: {
            DIE("unexpected buffer_part");
            break;
        }
    }

    int arg_count = argc - w.woptind;
    if (arg_count == 0) {
        write_part(begin, end, cut_at_cursor, tokenize, current_buffer, current_cursor_pos,
                   streams);
    } else if (arg_count == 1) {
        replace_part(begin, end, argv[w.woptind], append_mode, current_buffer, current_cursor_pos);
    } else {
        wcstring sb = argv[w.woptind];
        for (int i = w.woptind + 1; i < argc; i++) {
            sb.push_back(L'\n');
            sb.append(argv[i]);
        }
        replace_part(begin, end, sb.c_str(), append_mode, current_buffer, current_cursor_pos);
    }

    return STATUS_CMD_OK;
}
Beispiel #9
0
int parse_cmd_opts(bind_cmd_opts_t &opts, int *optind,  //!OCLINT(high ncss method)
                   int argc, wchar_t **argv, parser_t &parser, io_streams_t &streams) {
    wchar_t *cmd = argv[0];
    static const wchar_t *const short_options = L":aehkKfM:Lm:s";
    static const struct woption long_options[] = {{L"all", no_argument, NULL, 'a'},
                                                  {L"erase", no_argument, NULL, 'e'},
                                                  {L"function-names", no_argument, NULL, 'f'},
                                                  {L"help", no_argument, NULL, 'h'},
                                                  {L"key", no_argument, NULL, 'k'},
                                                  {L"key-names", no_argument, NULL, 'K'},
                                                  {L"list-modes", no_argument, NULL, 'L'},
                                                  {L"mode", required_argument, NULL, 'M'},
                                                  {L"preset", no_argument, NULL, 'p'},
                                                  {L"sets-mode", required_argument, NULL, 'm'},
                                                  {L"silent", no_argument, NULL, 's'},
                                                  {L"user", no_argument, NULL, 'u'},
                                                  {NULL, 0, NULL, 0}};

    int opt;
    wgetopter_t w;
    while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
        switch (opt) {
            case L'a': {
                opts.all = true;
                break;
            }
            case L'e': {
                opts.mode = BIND_ERASE;
                break;
            }
            case L'f': {
                opts.mode = BIND_FUNCTION_NAMES;
                break;
            }
            case L'h': {
                opts.print_help = true;
                break;
            }
            case L'k': {
                opts.use_terminfo = true;
                break;
            }
            case L'K': {
                opts.mode = BIND_KEY_NAMES;
                break;
            }
            case L'L': {
                opts.list_modes = true;
                return STATUS_CMD_OK;
            }
            case L'M': {
                if (!valid_var_name(w.woptarg)) {
                    streams.err.append_format(BUILTIN_ERR_BIND_MODE, cmd, w.woptarg);
                    return STATUS_INVALID_ARGS;
                }
                opts.bind_mode = w.woptarg;
                opts.bind_mode_given = true;
                break;
            }
            case L'm': {
                if (!valid_var_name(w.woptarg)) {
                    streams.err.append_format(BUILTIN_ERR_BIND_MODE, cmd, w.woptarg);
                    return STATUS_INVALID_ARGS;
                }
                opts.sets_bind_mode = w.woptarg;
                break;
            }
            case L'p': {
                opts.have_preset = true;
                opts.preset = true;
                break;
            }
            case L's': {
                opts.silent = true;
                break;
            }
            case L'u': {
                opts.have_user = true;
                opts.user = true;
                break;
            }
            case ':': {
                builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            case L'?': {
                builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            default: {
                DIE("unexpected retval from wgetopt_long");
                break;
            }
        }
    }

    *optind = w.woptind;
    return STATUS_CMD_OK;
}
Beispiel #10
0
/// The jobs builtin. Used for printing running jobs. Defined in builtin_jobs.c.
int builtin_jobs(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
    wchar_t *cmd = argv[0];
    int argc = builtin_count_args(argv);
    int found = 0;
    int mode = JOBS_DEFAULT;
    int print_last = 0;

    static const wchar_t *const short_options = L":cghlpq";
    static const struct woption long_options[] = {
        {L"command", no_argument, NULL, 'c'},
        {L"group", no_argument, NULL, 'g'},
        {L"help", no_argument, NULL, 'h'},
        {L"last", no_argument, NULL, 'l'},
        {L"pid", no_argument, NULL, 'p'},
        {L"quiet", no_argument, NULL, 'q'},
        {nullptr, 0, NULL, 0}};

    int opt;
    wgetopter_t w;
    while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
        switch (opt) {
            case 'p': {
                mode = JOBS_PRINT_PID;
                break;
            }
            case 'q': {
                mode = JOBS_PRINT_NOTHING;
                break;
            }
            case 'c': {
                mode = JOBS_PRINT_COMMAND;
                break;
            }
            case 'g': {
                mode = JOBS_PRINT_GROUP;
                break;
            }
            case 'l': {
                print_last = 1;
                break;
            }
            case 'h': {
                builtin_print_help(parser, streams, cmd, streams.out);
                return STATUS_CMD_OK;
            }
            case ':': {
                builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            case '?': {
                builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            default: {
                DIE("unexpected retval from wgetopt_long");
                break;
            }
        }
    }

    if (print_last) {
        // Ignore unconstructed jobs, i.e. ourself.
        job_iterator_t jobs;
        const job_t *j;
        while ((j = jobs.next())) {
            if (j->is_constructed() && !j->is_completed()) {
                builtin_jobs_print(j, mode, !streams.out_is_redirected, streams);
                return STATUS_CMD_ERROR;
            }
        }

    } else {
        if (w.woptind < argc) {
            int i;

            for (i = w.woptind; i < argc; i++) {
                const job_t *j = nullptr;

                if (argv[i][0] == L'%') {
                    int jobId = -1;
                    jobId = fish_wcstoi(argv[i] + 1);
                    if (errno || jobId < -1) {
                        streams.err.append_format(_(L"%ls: '%ls' is not a valid job id"), cmd, argv[i]);
                        return STATUS_INVALID_ARGS;
                    }
                    j = job_t::from_job_id(jobId);
                }
                else {
                    int pid = fish_wcstoi(argv[i]);
                    if (errno || pid < 0) {
                        streams.err.append_format(_(L"%ls: '%ls' is not a valid process id\n"), cmd, argv[i]);
                        return STATUS_INVALID_ARGS;
                    }
                    j = job_t::from_pid(pid);
                }

                if (j && !j->is_completed() && j->is_constructed()) {
                    builtin_jobs_print(j, mode, false, streams);
                    found = 1;
                } else {
                    streams.err.append_format(_(L"%ls: No suitable job: %ls\n"), cmd, argv[i]);
                    return STATUS_CMD_ERROR;
                }
            }
        } else {
            job_iterator_t jobs;
            const job_t *j;
            while ((j = jobs.next())) {
                // Ignore unconstructed jobs, i.e. ourself.
                if (j->is_constructed() && !j->is_completed()) {
                    builtin_jobs_print(j, mode, !found && !streams.out_is_redirected, streams);
                    found = 1;
                }
            }
        }
    }

    if (!found) {
        // Do not babble if not interactive.
        if (!streams.out_is_redirected && mode != JOBS_PRINT_NOTHING) {
            streams.out.append_format(_(L"%ls: There are no jobs\n"), argv[0]);
        }
        return STATUS_CMD_ERROR;
    }

    return STATUS_CMD_OK;
}
Beispiel #11
0
static int parse_cmd_opts(status_cmd_opts_t &opts, int *optind,  //!OCLINT(high ncss method)
                          int argc, wchar_t **argv, parser_t &parser, io_streams_t &streams) {
    wchar_t *cmd = argv[0];
    int opt;
    wgetopter_t w;
    while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
        switch (opt) {
            case STATUS_IS_FULL_JOB_CTRL: {
                if (!set_status_cmd(cmd, opts, STATUS_IS_FULL_JOB_CTRL, streams)) {
                    return STATUS_CMD_ERROR;
                }
                break;
            }
            case STATUS_IS_INTERACTIVE_JOB_CTRL: {
                if (!set_status_cmd(cmd, opts, STATUS_IS_INTERACTIVE_JOB_CTRL, streams)) {
                    return STATUS_CMD_ERROR;
                }
                break;
            }
            case STATUS_IS_NO_JOB_CTRL: {
                if (!set_status_cmd(cmd, opts, STATUS_IS_NO_JOB_CTRL, streams)) {
                    return STATUS_CMD_ERROR;
                }
                break;
            }
            case STATUS_FISH_PATH: {
                if (!set_status_cmd(cmd, opts, STATUS_FISH_PATH, streams)) {
                    return STATUS_CMD_ERROR;
                }
                break;
            }
            case 'L': {
                opts.level = fish_wcstoi(w.woptarg);
                if (opts.level < 0 || errno == ERANGE) {
                    streams.err.append_format(_(L"%ls: Invalid level value '%ls'\n"), argv[0],
                                              w.woptarg);
                    return STATUS_INVALID_ARGS;
                } else if (errno) {
                    streams.err.append_format(BUILTIN_ERR_NOT_NUMBER, argv[0], w.woptarg);
                    return STATUS_INVALID_ARGS;
                }
                break;
            }
            case 'c': {
                if (!set_status_cmd(cmd, opts, STATUS_IS_COMMAND_SUB, streams)) {
                    return STATUS_CMD_ERROR;
                }
                break;
            }
            case 'b': {
                if (!set_status_cmd(cmd, opts, STATUS_IS_BLOCK, streams)) {
                    return STATUS_CMD_ERROR;
                }
                break;
            }
            case 'i': {
                if (!set_status_cmd(cmd, opts, STATUS_IS_INTERACTIVE, streams)) {
                    return STATUS_CMD_ERROR;
                }
                break;
            }
            case 'l': {
                if (!set_status_cmd(cmd, opts, STATUS_IS_LOGIN, streams)) {
                    return STATUS_CMD_ERROR;
                }
                break;
            }
            case 'f': {
                if (!set_status_cmd(cmd, opts, STATUS_FILENAME, streams)) {
                    return STATUS_CMD_ERROR;
                }
                break;
            }
            case 'n': {
                if (!set_status_cmd(cmd, opts, STATUS_LINE_NUMBER, streams)) {
                    return STATUS_CMD_ERROR;
                }
                break;
            }
            case 'j': {
                if (!set_status_cmd(cmd, opts, STATUS_SET_JOB_CONTROL, streams)) {
                    return STATUS_CMD_ERROR;
                }
                opts.new_job_control_mode = job_control_str_to_mode(w.woptarg, cmd, streams);
                if (opts.new_job_control_mode == -1) {
                    return STATUS_CMD_ERROR;
                }
                break;
            }
            case 't': {
                if (!set_status_cmd(cmd, opts, STATUS_STACK_TRACE, streams)) {
                    return STATUS_CMD_ERROR;
                }
                break;
            }
            case 'h': {
                opts.print_help = true;
                break;
            }
            case ':': {
                builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            case '?': {
                builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            default: {
                DIE("unexpected retval from wgetopt_long");
                break;
            }
        }
    }

    *optind = w.woptind;
    return STATUS_CMD_OK;
}
/// The complete builtin. Used for specifying programmable tab-completions. Calls the functions in
// complete.cpp for any heavy lifting.
int builtin_complete(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
    ASSERT_IS_MAIN_THREAD();
    static int recursion_level = 0;

    wchar_t *cmd = argv[0];
    int argc = builtin_count_args(argv);
    int result_mode = SHARED;
    int remove = 0;
    wcstring short_opt;
    wcstring_list_t gnu_opt, old_opt;
    const wchar_t *comp = L"", *desc = L"", *condition = L"";
    bool do_complete = false;
    wcstring do_complete_param;
    wcstring_list_t cmd_to_complete;
    wcstring_list_t path;
    wcstring_list_t wrap_targets;
    bool preserve_order = false;

    static const wchar_t *const short_options = L":a:c:p:s:l:o:d:frxeuAn:C::w:hk";
    static const struct woption long_options[] = {{L"exclusive", no_argument, NULL, 'x'},
                                                  {L"no-files", no_argument, NULL, 'f'},
                                                  {L"require-parameter", no_argument, NULL, 'r'},
                                                  {L"path", required_argument, NULL, 'p'},
                                                  {L"command", required_argument, NULL, 'c'},
                                                  {L"short-option", required_argument, NULL, 's'},
                                                  {L"long-option", required_argument, NULL, 'l'},
                                                  {L"old-option", required_argument, NULL, 'o'},
                                                  {L"description", required_argument, NULL, 'd'},
                                                  {L"arguments", required_argument, NULL, 'a'},
                                                  {L"erase", no_argument, NULL, 'e'},
                                                  {L"unauthoritative", no_argument, NULL, 'u'},
                                                  {L"authoritative", no_argument, NULL, 'A'},
                                                  {L"condition", required_argument, NULL, 'n'},
                                                  {L"wraps", required_argument, NULL, 'w'},
                                                  {L"do-complete", optional_argument, NULL, 'C'},
                                                  {L"help", no_argument, NULL, 'h'},
                                                  {L"keep-order", no_argument, NULL, 'k'},
                                                  {NULL, 0, NULL, 0}};

    int opt;
    wgetopter_t w;
    while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
        switch (opt) {
            case 'x': {
                result_mode |= EXCLUSIVE;
                break;
            }
            case 'f': {
                result_mode |= NO_FILES;
                break;
            }
            case 'r': {
                result_mode |= NO_COMMON;
                break;
            }
            case 'k': {
                preserve_order = true;
                break;
            }
            case 'p':
            case 'c': {
                wcstring tmp;
                if (unescape_string(w.woptarg, &tmp, UNESCAPE_SPECIAL)) {
                    if (opt == 'p')
                        path.push_back(tmp);
                    else
                        cmd_to_complete.push_back(tmp);
                } else {
                    streams.err.append_format(_(L"%ls: Invalid token '%ls'\n"), cmd, w.woptarg);
                    return STATUS_INVALID_ARGS;
                }
                break;
            }
            case 'd': {
                desc = w.woptarg;
                break;
            }
            case 'u': {
                // This option was removed in commit 1911298 and is now a no-op.
                break;
            }
            case 'A': {
                // This option was removed in commit 1911298 and is now a no-op.
                break;
            }
            case 's': {
                short_opt.append(w.woptarg);
                if (w.woptarg[0] == '\0') {
                    streams.err.append_format(_(L"%ls: -s requires a non-empty string\n"), cmd);
                    return STATUS_INVALID_ARGS;
                }
                break;
            }
            case 'l': {
                gnu_opt.push_back(w.woptarg);
                if (w.woptarg[0] == '\0') {
                    streams.err.append_format(_(L"%ls: -l requires a non-empty string\n"), cmd);
                    return STATUS_INVALID_ARGS;
                }
                break;
            }
            case 'o': {
                old_opt.push_back(w.woptarg);
                if (w.woptarg[0] == '\0') {
                    streams.err.append_format(_(L"%ls: -o requires a non-empty string\n"), cmd);
                    return STATUS_INVALID_ARGS;
                }
                break;
            }
            case 'a': {
                comp = w.woptarg;
                break;
            }
            case 'e': {
                remove = 1;
                break;
            }
            case 'n': {
                condition = w.woptarg;
                break;
            }
            case 'w': {
                wrap_targets.push_back(w.woptarg);
                break;
            }
            case 'C': {
                do_complete = true;
                const wchar_t *arg = w.woptarg ? w.woptarg : reader_get_buffer();
                if (arg == NULL) {
                    // This corresponds to using 'complete -C' in non-interactive mode.
                    // See #2361.
                    builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]);
                    return STATUS_INVALID_ARGS;
                }
                do_complete_param = arg;
                break;
            }
            case 'h': {
                builtin_print_help(parser, streams, cmd, streams.out);
                return STATUS_CMD_OK;
            }
            case ':': {
                builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            case '?': {
                builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            default: {
                DIE("unexpected retval from wgetopt_long");
                break;
            }
        }
    }

    if (w.woptind != argc) {
        streams.err.append_format(BUILTIN_ERR_TOO_MANY_ARGUMENTS, cmd);
        builtin_print_error_trailer(parser, streams.err, cmd);
        return STATUS_INVALID_ARGS;
    }

    if (condition && std::wcslen(condition)) {
        const wcstring condition_string = condition;
        parse_error_list_t errors;
        if (parse_util_detect_errors(condition_string, &errors,
                                     false /* do not accept incomplete */)) {
            streams.err.append_format(L"%ls: Condition '%ls' contained a syntax error", cmd,
                                      condition);
            for (size_t i = 0; i < errors.size(); i++) {
                streams.err.append_format(L"\n%s: ", cmd);
                streams.err.append(errors.at(i).describe(condition_string));
            }
            return STATUS_CMD_ERROR;
        }
    }

    if (comp && std::wcslen(comp)) {
        wcstring prefix;
        prefix.append(cmd);
        prefix.append(L": ");

        wcstring err_text;
        if (parser.detect_errors_in_argument_list(comp, &err_text, prefix.c_str())) {
            streams.err.append_format(L"%ls: Completion '%ls' contained a syntax error\n", cmd,
                                      comp);
            streams.err.append(err_text);
            streams.err.push_back(L'\n');
            return STATUS_CMD_ERROR;
        }
    }

    if (do_complete) {
        const wchar_t *token;

        parse_util_token_extent(do_complete_param.c_str(), do_complete_param.size(), &token, 0, 0,
                                0);

        // Create a scoped transient command line, so that bulitin_commandline will see our
        // argument, not the reader buffer.
        builtin_commandline_scoped_transient_t temp_buffer(do_complete_param);

        if (recursion_level < 1) {
            recursion_level++;

            std::vector<completion_t> comp;
            complete(do_complete_param, &comp, COMPLETION_REQUEST_DEFAULT | COMPLETION_REQUEST_FUZZY_MATCH, parser.vars());

            for (size_t i = 0; i < comp.size(); i++) {
                const completion_t &next = comp.at(i);

                // Make a fake commandline, and then apply the completion to it.
                const wcstring faux_cmdline = token;
                size_t tmp_cursor = faux_cmdline.size();
                wcstring faux_cmdline_with_completion = completion_apply_to_command_line(
                    next.completion, next.flags, faux_cmdline, &tmp_cursor, false);

                // completion_apply_to_command_line will append a space unless COMPLETE_NO_SPACE
                // is set. We don't want to set COMPLETE_NO_SPACE because that won't close
                // quotes. What we want is to close the quote, but not append the space. So we
                // just look for the space and clear it.
                if (!(next.flags & COMPLETE_NO_SPACE) &&
                    string_suffixes_string(L" ", faux_cmdline_with_completion)) {
                    faux_cmdline_with_completion.resize(faux_cmdline_with_completion.size() - 1);
                }

                // The input data is meant to be something like you would have on the command
                // line, e.g. includes backslashes. The output should be raw, i.e. unescaped. So
                // we need to unescape the command line. See #1127.
                unescape_string_in_place(&faux_cmdline_with_completion, UNESCAPE_DEFAULT);
                streams.out.append(faux_cmdline_with_completion);

                // Append any description.
                if (!next.description.empty()) {
                    streams.out.push_back(L'\t');
                    streams.out.append(next.description);
                }
                streams.out.push_back(L'\n');
            }

            recursion_level--;
        }
    } else if (cmd_to_complete.empty() && path.empty()) {
        // No arguments specified, meaning we print the definitions of all specified completions
        // to stdout.
        streams.out.append(complete_print());
    } else {
        int flags = COMPLETE_AUTO_SPACE;
        if (preserve_order) {
            flags |= COMPLETE_DONT_SORT;
        }

        if (remove) {
            builtin_complete_remove(cmd_to_complete, path, short_opt.c_str(), gnu_opt, old_opt);
        } else {
            builtin_complete_add(cmd_to_complete, path, short_opt.c_str(), gnu_opt, old_opt,
                                 result_mode, condition, comp, desc, flags);
        }

        // Handle wrap targets (probably empty). We only wrap commands, not paths.
        for (size_t w = 0; w < wrap_targets.size(); w++) {
            const wcstring &wrap_target = wrap_targets.at(w);
            for (size_t i = 0; i < cmd_to_complete.size(); i++) {
                (remove ? complete_remove_wrapper : complete_add_wrapper)(cmd_to_complete.at(i),
                                                                          wrap_target);
            }
        }
    }

    return STATUS_CMD_OK;
}
static int parse_cmd_opts(function_cmd_opts_t &opts, int *optind,  //!OCLINT(high ncss method)
                          int argc, wchar_t **argv, parser_t &parser, io_streams_t &streams) {
    const wchar_t *cmd = L"function";
    int opt;
    wgetopter_t w;
    while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
        switch (opt) {
            case 'd': {
                opts.description = w.woptarg;
                break;
            }
            case 's': {
                int sig = wcs2sig(w.woptarg);
                if (sig == -1) {
                    streams.err.append_format(_(L"%ls: Unknown signal '%ls'"), cmd, w.woptarg);
                    return STATUS_INVALID_ARGS;
                }
                opts.events.push_back(event_t::signal_event(sig));
                break;
            }
            case 'v': {
                if (!valid_var_name(w.woptarg)) {
                    streams.err.append_format(BUILTIN_ERR_VARNAME, cmd, w.woptarg);
                    return STATUS_INVALID_ARGS;
                }

                opts.events.push_back(event_t::variable_event(w.woptarg));
                break;
            }
            case 'e': {
                opts.events.push_back(event_t::generic_event(w.woptarg));
                break;
            }
            case 'j':
            case 'p': {
                pid_t pid;
                event_t e(EVENT_ANY);

                if ((opt == 'j') && (wcscasecmp(w.woptarg, L"caller") == 0)) {
                    job_id_t job_id = -1;

                    if (is_subshell) {
                        size_t block_idx = 0;

                        // Find the outermost substitution block.
                        for (block_idx = 0;; block_idx++) {
                            const block_t *b = parser.block_at_index(block_idx);
                            if (b == NULL || b->type() == SUBST) break;
                        }

                        // Go one step beyond that, to get to the caller.
                        const block_t *caller_block = parser.block_at_index(block_idx + 1);
                        if (caller_block != NULL && caller_block->job != NULL) {
                            job_id = caller_block->job->job_id;
                        }
                    }

                    if (job_id == -1) {
                        streams.err.append_format(
                            _(L"%ls: Cannot find calling job for event handler"), cmd);
                        return STATUS_INVALID_ARGS;
                    }
                    e.type = EVENT_JOB_ID;
                    e.param1.job_id = job_id;
                } else {
                    pid = fish_wcstoi(w.woptarg);
                    if (errno || pid < 0) {
                        streams.err.append_format(_(L"%ls: Invalid process id '%ls'"), cmd,
                                                  w.woptarg);
                        return STATUS_INVALID_ARGS;
                    }

                    e.type = EVENT_EXIT;
                    e.param1.pid = (opt == 'j' ? -1 : 1) * abs(pid);
                }
                opts.events.push_back(e);
                break;
            }
            case 'a': {
                opts.named_arguments.push_back(w.woptarg);
                break;
            }
            case 'S': {
                opts.shadow_scope = false;
                break;
            }
            case 'w': {
                opts.wrap_targets.push_back(w.woptarg);
                break;
            }
            case 'V': {
                if (!valid_var_name(w.woptarg)) {
                    streams.err.append_format(BUILTIN_ERR_VARNAME, cmd, w.woptarg);
                    return STATUS_INVALID_ARGS;
                }
                opts.inherit_vars.push_back(w.woptarg);
                break;
            }
            case 'h': {
                opts.print_help = true;
                break;
            }
            case ':': {
                builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            case '?': {
                builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            default: {
                DIE("unexpected retval from wgetopt_long");
                break;
            }
        }
    }

    *optind = w.woptind;
    return STATUS_CMD_OK;
}
static int parse_cmd_opts(functions_cmd_opts_t &opts, int *optind,  //!OCLINT(high ncss method)
                          int argc, wchar_t **argv, parser_t &parser, io_streams_t &streams) {
    wchar_t *cmd = argv[0];
    int opt;
    wgetopter_t w;
    while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
        switch (opt) {
            case 'v': {
                opts.verbose = true;
                break;
            }
            case 'e': {
                opts.erase = true;
                break;
            }
            case 'D': {
                opts.report_metadata = true;
                break;
            }
            case 'd': {
                opts.description = w.woptarg;
                break;
            }
            case 'n': {
                opts.list = true;
                break;
            }
            case 'a': {
                opts.show_hidden = true;
                break;
            }
            case 'h': {
                opts.print_help = true;
                break;
            }
            case 'q': {
                opts.query = true;
                break;
            }
            case 'c': {
                opts.copy = true;
                break;
            }
            case 'H': {
                opts.handlers = true;
                break;
            }
            case 't': {
                opts.handlers_type = w.woptarg;
                opts.handlers = true;
                break;
            }
            case ':': {
                builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            case '?': {
                builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            default: {
                DIE("unexpected retval from wgetopt_long");
                break;
            }
        }
    }

    *optind = w.woptind;
    return STATUS_CMD_OK;
}
/// set_color builtin.
int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
    // By the time this is called we should have initialized the curses subsystem.
    assert(curses_initialized);

    // Hack in missing italics and dim capabilities omitted from MacOS xterm-256color terminfo
    // Helps Terminal.app/iTerm
    #if __APPLE__
    const auto term_prog = parser.vars().get(L"TERM_PROGRAM");
    if (!term_prog.missing_or_empty() && (term_prog->as_string() == L"Apple_Terminal"
        || term_prog->as_string() == L"iTerm.app")) {
        const auto term = parser.vars().get(L"TERM");
        if (!term.missing_or_empty() && (term->as_string() == L"xterm-256color")) {
            enter_italics_mode = sitm_esc;
            exit_italics_mode = ritm_esc;
            enter_dim_mode = dim_esc;
        }
    }
    #endif

    // Variables used for parsing the argument list.
    wchar_t *cmd = argv[0];
    int argc = builtin_count_args(argv);

    // Some code passes variables to set_color that don't exist, like $fish_user_whatever. As a
    // hack, quietly return failure.
    if (argc <= 1) {
        return EXIT_FAILURE;
    }

    const wchar_t *bgcolor = NULL;
    bool bold = false, underline = false, italics = false, dim = false, reverse = false;

    // Parse options to obtain the requested operation and the modifiers.
    int opt;
    wgetopter_t w;
    while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
        switch (opt) {
            case 'b': {
                bgcolor = w.woptarg;
                break;
            }
            case 'h': {
                builtin_print_help(parser, streams, argv[0], streams.out);
                return STATUS_CMD_OK;
            }
            case 'o': {
                bold = true;
                break;
            }
            case 'i': {
                italics = true;
                break;
            }
            case 'd': {
                dim = true;
                break;
            }
            case 'r': {
                reverse = true;
                break;
            }
            case 'u': {
                underline = true;
                break;
            }
            case 'c': {
                print_colors(streams);
                return STATUS_CMD_OK;
            }
            case ':': {
                builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]);
                return STATUS_INVALID_ARGS;
            }
            case '?': {
                return STATUS_INVALID_ARGS;
            }
            default: {
                DIE("unexpected retval from wgetopt_long");
                break;
            }
        }
    }

    // Remaining arguments are foreground color.
    std::vector<rgb_color_t> fgcolors;
    for (; w.woptind < argc; w.woptind++) {
        rgb_color_t fg = rgb_color_t(argv[w.woptind]);
        if (fg.is_none()) {
            streams.err.append_format(_(L"%ls: Unknown color '%ls'\n"), argv[0], argv[w.woptind]);
            return STATUS_INVALID_ARGS;
        }
        fgcolors.push_back(fg);
    }

    if (fgcolors.empty() && bgcolor == NULL && !bold && !underline && !italics && !dim &&
        !reverse) {
        streams.err.append_format(_(L"%ls: Expected an argument\n"), argv[0]);
        return STATUS_INVALID_ARGS;
    }

    // #1323: We may have multiple foreground colors. Choose the best one. If we had no foreground
    // color, we'll get none(); if we have at least one we expect not-none.
    const rgb_color_t fg = best_color(fgcolors, output_get_color_support());
    assert(fgcolors.empty() || !fg.is_none());

    const rgb_color_t bg = rgb_color_t(bgcolor ? bgcolor : L"");
    if (bgcolor && bg.is_none()) {
        streams.err.append_format(_(L"%ls: Unknown color '%ls'\n"), argv[0], bgcolor);
        return STATUS_INVALID_ARGS;
    }

    // Test if we have at least basic support for setting fonts, colors and related bits - otherwise
    // just give up...
    if (cur_term == NULL || !exit_attribute_mode) {
        return STATUS_CMD_ERROR;
    }
    outputter_t outp;

    if (bold && enter_bold_mode) {
        writembs_nofail(outp, tparm((char *)enter_bold_mode));
    }

    if (underline && enter_underline_mode) {
        writembs_nofail(outp, enter_underline_mode);
    }

    if (italics && enter_italics_mode) {
        writembs_nofail(outp, enter_italics_mode);
    }

    if (dim && enter_dim_mode) {
        writembs_nofail(outp, enter_dim_mode);
    }

    if (reverse && enter_reverse_mode) {
        writembs_nofail(outp, enter_reverse_mode);
    } else if (reverse && enter_standout_mode) {
        writembs_nofail(outp, enter_standout_mode);
    }

    if (bgcolor != NULL && bg.is_normal()) {
        writembs_nofail(outp, tparm((char *)exit_attribute_mode));
    }

    if (!fg.is_none()) {
        if (fg.is_normal() || fg.is_reset()) {
            writembs_nofail(outp, tparm((char *)exit_attribute_mode));
        } else {
            if (!outp.write_color(fg, true /* is_fg */)) {
                // We need to do *something* or the lack of any output messes up
                // when the cartesian product here would make "foo" disappear:
                //  $ echo (set_color foo)bar
                outp.set_color(rgb_color_t::reset(), rgb_color_t::none());
            }
        }
    }

    if (bgcolor != NULL && !bg.is_normal() && !bg.is_reset()) {
        outp.write_color(bg, false /* not is_fg */);
    }

    // Output the collected string.
    streams.out.append(str2wcstring(outp.contents()));

    return STATUS_CMD_OK;
}