static int recv_req(req_t *req) { /* Receives the request from the client after the greeting has completed. * Returns 0 if the request is read OK, or -1 on error. */ int n; char buf[MAX_SOCK_LINE]; Lex l; int done = 0; int tok; assert(req->sd >= 0); if ((n = read_line(req->sd, buf, sizeof(buf))) < 0) { log_msg(LOG_NOTICE, "Unable to read request from <%s:%d>: %s", req->fqdn, req->port, strerror(errno)); return(-1); } else if (n == 0) { log_msg(LOG_NOTICE, "Connection terminated by <%s:%d>", req->fqdn, req->port); return(-1); } DPRINTF((5, "Received request: %s", buf)); l = lex_create(buf, proto_strs); while (!done) { tok = lex_next(l); switch(tok) { case CONMAN_TOK_CONNECT: req->command = CONMAN_CMD_CONNECT; parse_cmd_opts(l, req); break; case CONMAN_TOK_MONITOR: req->command = CONMAN_CMD_MONITOR; parse_cmd_opts(l, req); break; case CONMAN_TOK_QUERY: req->command = CONMAN_CMD_QUERY; parse_cmd_opts(l, req); break; case LEX_EOF: case LEX_EOL: done = 1; break; default: break; } } lex_destroy(l); return(0); }
/// The math builtin evaluates math expressions. int builtin_math(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wchar_t *cmd = argv[0]; int argc = builtin_count_args(argv); math_cmd_opts_t opts; int optind; // Is this really the right way to handle no expression present? // if (argc == 0) return STATUS_CMD_OK; int retval = parse_cmd_opts(opts, &optind, argc, argv, parser, streams); if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { builtin_print_help(parser, streams, cmd, streams.out); return STATUS_CMD_OK; } wcstring expression; wcstring storage; while (const wchar_t *arg = math_get_arg(&optind, argv, &storage, streams)) { if (!expression.empty()) expression.push_back(L' '); expression.append(arg); } if (expression.empty()) { streams.err.append_format(BUILTIN_ERR_MIN_ARG_COUNT1, L"math", 1, 0); return STATUS_CMD_ERROR; } return evaluate_expression(cmd, parser, streams, opts, expression); }
/// The exit builtin. Calls reader_exit to exit and returns the value specified. int builtin_exit(parser_t &parser, io_streams_t &streams, wchar_t **argv) { const wchar_t *cmd = argv[0]; int argc = builtin_count_args(argv); exit_cmd_opts_t opts; int optind; int retval = parse_cmd_opts(opts, &optind, argc, argv, parser, streams); if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { builtin_print_help(parser, streams, cmd, streams.out); return STATUS_CMD_OK; } if (optind + 1 < argc) { streams.err.append_format(BUILTIN_ERR_TOO_MANY_ARGUMENTS, cmd); builtin_print_help(parser, streams, cmd, streams.err); return STATUS_INVALID_ARGS; } if (optind == argc) { retval = proc_get_last_status(); } else { retval = fish_wcstoi(argv[optind]); if (errno) { streams.err.append_format(_(L"%ls: Argument '%ls' must be an integer\n"), cmd, argv[optind]); builtin_print_help(parser, streams, cmd, streams.err); return STATUS_INVALID_ARGS; } } reader_exit(1, 0); return retval; }
/// Implementation of the builtin contains command, used to check if a specified string is part of /// a list. int builtin_contains(parser_t &parser, io_streams_t &streams, wchar_t **argv) { const wchar_t *cmd = argv[0]; int argc = builtin_count_args(argv); contains_cmd_opts_t opts; int optind; int retval = parse_cmd_opts(opts, &optind, argc, argv, parser, streams); if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { builtin_print_help(parser, streams, cmd, streams.out); return STATUS_CMD_OK; } wchar_t *needle = argv[optind]; if (!needle) { streams.err.append_format(_(L"%ls: Key not specified\n"), cmd); } else { for (int i = optind + 1; i < argc; i++) { if (!std::wcscmp(needle, argv[i])) { if (opts.print_index) streams.out.append_format(L"%d\n", i - optind); return STATUS_CMD_OK; } } } return STATUS_CMD_ERROR; }
/// The builtin builtin, used for giving builtins precedence over functions. Mostly handled by the /// parser. All this code does is some additional operational modes, such as printing a list of all /// builtins, printing help, etc. int builtin_builtin(parser_t &parser, io_streams_t &streams, wchar_t **argv) { const wchar_t *cmd = argv[0]; int argc = builtin_count_args(argv); builtin_cmd_opts_t opts; int optind; int retval = parse_cmd_opts(opts, &optind, argc, argv, parser, streams); if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { builtin_print_help(parser, streams, cmd, streams.out); return STATUS_CMD_OK; } if (opts.query && opts.list_names) { streams.err.append_format(BUILTIN_ERR_COMBO2, cmd, _(L"--query and --names are mutually exclusive")); return STATUS_INVALID_ARGS; } if (opts.query) { wcstring_list_t names = builtin_get_names(); retval = STATUS_CMD_ERROR; for (int i = optind; i < argc; i++) { if (contains(names, argv[i])) { retval = STATUS_CMD_OK; break; } } return retval; } if (opts.list_names) { wcstring_list_t names = builtin_get_names(); std::sort(names.begin(), names.end()); for (size_t i = 0; i < names.size(); i++) { const wchar_t *el = names.at(i).c_str(); streams.out.append(el); streams.out.append(L"\n"); } } return STATUS_CMD_OK; }
int main(int argc, char *argv[]) { int i; /* loop counter */ /* Initialisation */ init_s2(); /* Close all open diagnose streams at exit */ atexit(exit_handler); /* normal exit */ /* Parse command line options */ i = parse_cmd_opts(argc, argv); tp_init(opts.tp_size); #if 0 #define MAX_REQUESTS 5 int data[MAX_REQUESTS]; /* run a loop that generates requests */ for (i = 0; i < MAX_REQUESTS; i++) { data[i] = i; tp_enqueue(&data[i], NULL, NULL); } #else int a = 1, b = 2, c = 3, d = 4; sleep(1); tp_enqueue(&a, NULL, NULL); tp_enqueue(&b, NULL, NULL); tp_enqueue(&c, NULL, NULL); tp_enqueue(&d, NULL, NULL); tp_dequeue(&c); #endif // sleep(10); tp_cleanup(); /* Parse and evaluate S2 language file(s) */ if(0) s2_run(argc, argv, i); return 0; }
int main(int argc, char *argv[]) { int i; uint rval; /* Initialisation */ init_s2(); /* Close all open diagnose streams at exit */ atexit(exit_handler); /* normal exit */ /* Parse command line options */ i = parse_cmd_opts(argc, argv); /* Parse and evaluate S2 language file(s) */ rval = s2_run(argc, argv, i); return (int)rval; }
/// Implementation of the builtin 'command'. Actual command running is handled by the parser, this /// just processes the flags. int builtin_command(parser_t &parser, io_streams_t &streams, wchar_t **argv) { const wchar_t *cmd = argv[0]; int argc = builtin_count_args(argv); command_cmd_opts_t opts; int optind; int retval = parse_cmd_opts(opts, &optind, argc, argv, parser, streams); if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { builtin_print_help(parser, streams, cmd, streams.out); return STATUS_CMD_OK; } if (!opts.find_path && !opts.all_paths) { builtin_print_help(parser, streams, cmd, streams.out); return STATUS_INVALID_ARGS; } int found = 0; for (int idx = optind; argv[idx]; ++idx) { const wchar_t *command_name = argv[idx]; if (opts.all_paths) { wcstring_list_t paths = path_get_paths(command_name); for (auto path : paths) { if (!opts.quiet) streams.out.append_format(L"%ls\n", path.c_str()); ++found; } } else { wcstring path; if (path_get_path(command_name, &path)) { if (!opts.quiet) streams.out.append_format(L"%ls\n", path.c_str()); ++found; } } } return found ? STATUS_CMD_OK : STATUS_CMD_ERROR; }
/// The set builtin creates, updates, and erases (removes, deletes) variables. int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv) { const int incoming_exit_status = proc_get_last_status(); wchar_t *cmd = argv[0]; int argc = builtin_count_args(argv); set_cmd_opts_t opts; int optind; int retval = parse_cmd_opts(opts, &optind, argc, argv, parser, streams); if (retval != STATUS_CMD_OK) return retval; argv += optind; argc -= optind; if (opts.print_help) { builtin_print_help(parser, streams, cmd, streams.out); return STATUS_CMD_OK; } retval = validate_cmd_opts(cmd, opts, argc, parser, streams); if (retval != STATUS_CMD_OK) return retval; if (opts.query) { retval = builtin_set_query(cmd, opts, argc, argv, parser, streams); } else if (opts.erase) { retval = builtin_set_erase(cmd, opts, argc, argv, parser, streams); } else if (opts.list) { // explicit list the vars we know about retval = builtin_set_list(cmd, opts, argc, argv, parser, streams); } else if (opts.show) { retval = builtin_set_show(cmd, opts, argc, argv, parser, streams); } else if (argc == 0) { // implicit list the vars we know about retval = builtin_set_list(cmd, opts, argc, argv, parser, streams); } else { retval = builtin_set_set(cmd, opts, argc, argv, parser, streams); } if (retval == STATUS_CMD_OK && opts.preserve_failure_exit_status) retval = incoming_exit_status; return retval; }
/// The block builtin, used for temporarily blocking events. int builtin_block(parser_t &parser, io_streams_t &streams, wchar_t **argv) { const wchar_t *cmd = argv[0]; int argc = builtin_count_args(argv); block_cmd_opts_t opts; int optind; int retval = parse_cmd_opts(opts, &optind, argc, argv, parser, streams); if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { builtin_print_help(parser, streams, cmd, streams.out); return STATUS_CMD_OK; } if (opts.erase) { if (opts.scope != UNSET) { streams.err.append_format(_(L"%ls: Can not specify scope when removing block\n"), cmd); return STATUS_INVALID_ARGS; } if (parser.global_event_blocks.empty()) { streams.err.append_format(_(L"%ls: No blocks defined\n"), cmd); return STATUS_CMD_ERROR; } parser.global_event_blocks.pop_front(); return STATUS_CMD_OK; } size_t block_idx = 0; block_t *block = parser.block_at_index(block_idx); event_blockage_t eb = {}; eb.typemask = (1 << EVENT_ANY); switch (opts.scope) { case LOCAL: { // If this is the outermost block, then we're global if (block_idx + 1 >= parser.block_count()) { block = NULL; } break; } case GLOBAL: { block = NULL; break; } case UNSET: { while (block != NULL && block->type() != FUNCTION_CALL && block->type() != FUNCTION_CALL_NO_SHADOW) { // Set it in function scope block = parser.block_at_index(++block_idx); } break; } default: { DIE("unexpected scope"); break; } } if (block) { block->event_blocks.push_front(eb); } else { parser.global_event_blocks.push_front(eb); } return STATUS_CMD_OK; }
/// Manipulate history of interactive commands executed by the user. int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wchar_t *cmd = argv[0]; int argc = builtin_count_args(argv); history_cmd_opts_t opts; int optind; int retval = parse_cmd_opts(opts, &optind, argc, argv, parser, streams); if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { builtin_print_help(parser, streams, cmd, streams.out); return STATUS_CMD_OK; } // Use the default history if we have none (which happens if invoked non-interactively, e.g. // from webconfig.py. history_t *history = reader_get_history(); if (!history) history = &history_t::history_with_name(L"fish"); // If a history command hasn't already been specified via a flag check the first word. // Note that this can be simplified after we eliminate allowing subcommands as flags. // See the TODO above regarding the `long_options` array. if (optind < argc) { hist_cmd_t subcmd = str_to_enum(argv[optind], hist_enum_map, hist_enum_map_len); if (subcmd != HIST_UNDEF) { if (!set_hist_cmd(cmd, &opts.hist_cmd, subcmd, streams)) { return STATUS_INVALID_ARGS; } optind++; } } // Every argument that we haven't consumed already is an argument for a subcommand (e.g., a // search term). const wcstring_list_t args(argv + optind, argv + argc); // Establish appropriate defaults. if (opts.hist_cmd == HIST_UNDEF) opts.hist_cmd = HIST_SEARCH; if (!opts.history_search_type_defined) { if (opts.hist_cmd == HIST_SEARCH) opts.search_type = HISTORY_SEARCH_TYPE_CONTAINS; if (opts.hist_cmd == HIST_DELETE) opts.search_type = HISTORY_SEARCH_TYPE_EXACT; } int status = STATUS_CMD_OK; switch (opts.hist_cmd) { case HIST_SEARCH: { if (!history->search(opts.search_type, args, opts.show_time_format, opts.max_items, opts.case_sensitive, opts.null_terminate, streams)) { status = STATUS_CMD_ERROR; } break; } case HIST_DELETE: { // TODO: Move this code to the history module and support the other search types // including case-insensitive matches. At this time we expect the non-exact deletions to // be handled only by the history function's interactive delete feature. if (opts.search_type != HISTORY_SEARCH_TYPE_EXACT) { streams.err.append_format(_(L"builtin history delete only supports --exact\n")); status = STATUS_INVALID_ARGS; break; } if (!opts.case_sensitive) { streams.err.append_format( _(L"builtin history delete only supports --case-sensitive\n")); status = STATUS_INVALID_ARGS; break; } for (wcstring_list_t::const_iterator iter = args.begin(); iter != args.end(); ++iter) { wcstring delete_string = *iter; if (delete_string[0] == '"' && delete_string[delete_string.length() - 1] == '"') { delete_string = delete_string.substr(1, delete_string.length() - 2); } history->remove(delete_string); } break; } case HIST_CLEAR: { CHECK_FOR_UNEXPECTED_HIST_ARGS(opts.hist_cmd) history->clear(); history->save(); break; } case HIST_MERGE: { CHECK_FOR_UNEXPECTED_HIST_ARGS(opts.hist_cmd) history->incorporate_external_changes(); break; } case HIST_SAVE: { CHECK_FOR_UNEXPECTED_HIST_ARGS(opts.hist_cmd) history->save(); break; } case HIST_UNDEF: { DIE("Unexpected HIST_UNDEF seen"); break; } } return status; }
/// The bind builtin, used for setting character sequences. int builtin_bind_t::builtin_bind(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wchar_t *cmd = argv[0]; int argc = builtin_count_args(argv); bind_cmd_opts_t opts; this->opts = &opts; int optind; int retval = parse_cmd_opts(opts, &optind, argc, argv, parser, streams); if (retval != STATUS_CMD_OK) return retval; if (opts.list_modes) { list_modes(streams); return STATUS_CMD_OK; } if (opts.print_help) { builtin_print_help(parser, streams, cmd, streams.out); return STATUS_CMD_OK; } // Default to user mode if (!opts.have_preset && !opts.have_user) opts.user = true; switch (opts.mode) { case BIND_ERASE: { const wchar_t *bind_mode = opts.bind_mode_given ? opts.bind_mode : NULL; // If we get both, we erase both. if (opts.user) { if (erase(&argv[optind], opts.all, bind_mode, opts.use_terminfo, /* user */ true, streams)) { return STATUS_CMD_ERROR; } } if (opts.preset) { if (erase(&argv[optind], opts.all, bind_mode, opts.use_terminfo, /* user */ false, streams)) { return STATUS_CMD_ERROR; } } break; } case BIND_INSERT: { if (insert(optind, argc, argv, streams)) { return STATUS_CMD_ERROR; } break; } case BIND_KEY_NAMES: { key_names(opts.all, streams); break; } case BIND_FUNCTION_NAMES: { function_names(streams); break; } default: { streams.err.append_format(_(L"%ls: Invalid state\n"), cmd); return STATUS_CMD_ERROR; } } return STATUS_CMD_OK; }
/// The status builtin. Gives various status information on fish. int builtin_status(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wchar_t *cmd = argv[0]; int argc = builtin_count_args(argv); status_cmd_opts_t opts; int optind; int retval = parse_cmd_opts(opts, &optind, argc, argv, parser, streams); if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { builtin_print_help(parser, streams, cmd, streams.out); return STATUS_CMD_OK; } // If a status command hasn't already been specified via a flag check the first word. // Note that this can be simplified after we eliminate allowing subcommands as flags. if (optind < argc) { status_cmd_t subcmd = str_to_enum(argv[optind], status_enum_map, status_enum_map_len); if (subcmd != STATUS_UNDEF) { if (!set_status_cmd(cmd, opts, subcmd, streams)) { return STATUS_CMD_ERROR; } optind++; } else { streams.err.append_format(BUILTIN_ERR_INVALID_SUBCMD, cmd, argv[1]); return STATUS_INVALID_ARGS; } } // Every argument that we haven't consumed already is an argument for a subcommand. const wcstring_list_t args(argv + optind, argv + argc); switch (opts.status_cmd) { case STATUS_UNDEF: { CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) if (is_login) { streams.out.append_format(_(L"This is a login shell\n")); } else { streams.out.append_format(_(L"This is not a login shell\n")); } streams.out.append_format( _(L"Job control: %ls\n"), job_control_mode == JOB_CONTROL_INTERACTIVE ? _(L"Only on interactive jobs") : (job_control_mode == JOB_CONTROL_NONE ? _(L"Never") : _(L"Always"))); streams.out.append(parser.stack_trace()); break; } case STATUS_SET_JOB_CONTROL: { if (opts.new_job_control_mode != -1) { // Flag form was used. CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) } else { if (args.size() != 1) { const wchar_t *subcmd_str = enum_to_str(opts.status_cmd, status_enum_map); streams.err.append_format(BUILTIN_ERR_ARG_COUNT2, cmd, subcmd_str, 1, args.size()); return STATUS_INVALID_ARGS; } opts.new_job_control_mode = job_control_str_to_mode(args[0].c_str(), cmd, streams); if (opts.new_job_control_mode == -1) { return STATUS_CMD_ERROR; } } job_control_mode = opts.new_job_control_mode; break; } case STATUS_FEATURES: { print_features(streams); break; } case STATUS_TEST_FEATURE: { if (args.size() != 1) { const wchar_t *subcmd_str = enum_to_str(opts.status_cmd, status_enum_map); streams.err.append_format(BUILTIN_ERR_ARG_COUNT2, cmd, subcmd_str, 1, args.size()); return STATUS_INVALID_ARGS; } const auto *metadata = features_t::metadata_for(args.front().c_str()); if (!metadata) { retval = TEST_FEATURE_NOT_RECOGNIZED; } else { retval = feature_test(metadata->flag) ? TEST_FEATURE_ON : TEST_FEATURE_OFF; } break; } case STATUS_FILENAME: { CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) const wchar_t *fn = parser.current_filename(); if (!fn) fn = _(L"Standard input"); streams.out.append_format(L"%ls\n", fn); break; } case STATUS_FUNCTION: { CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) const wchar_t *fn = parser.get_function_name(opts.level); if (!fn) fn = _(L"Not a function"); streams.out.append_format(L"%ls\n", fn); break; } case STATUS_LINE_NUMBER: { CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) // TBD is how to interpret the level argument when fetching the line number. // See issue #4161. // streams.out.append_format(L"%d\n", parser.get_lineno(opts.level)); streams.out.append_format(L"%d\n", parser.get_lineno()); break; } case STATUS_IS_INTERACTIVE: { CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) retval = !is_interactive_session; break; } case STATUS_IS_COMMAND_SUB: { CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) retval = !is_subshell; break; } case STATUS_IS_BLOCK: { CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) retval = !is_block; break; } case STATUS_IS_BREAKPOINT: { CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) retval = !is_breakpoint; break; } case STATUS_IS_LOGIN: { CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) retval = !is_login; break; } case STATUS_IS_FULL_JOB_CTRL: { CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) retval = job_control_mode != JOB_CONTROL_ALL; break; } case STATUS_IS_INTERACTIVE_JOB_CTRL: { CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) retval = job_control_mode != JOB_CONTROL_INTERACTIVE; break; } case STATUS_IS_NO_JOB_CTRL: { CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) retval = job_control_mode != JOB_CONTROL_NONE; break; } case STATUS_STACK_TRACE: { CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) streams.out.append(parser.stack_trace()); break; } case STATUS_CURRENT_CMD: { CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) // HACK: Go via the deprecated variable to get the command. const auto var = env_get(L"_"); if (!var.missing_or_empty()) { streams.out.append(var->as_string()); streams.out.push_back(L'\n'); } else { streams.out.append(program_name); streams.out.push_back(L'\n'); } break; } case STATUS_FISH_PATH: { CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) streams.out.append(str2wcstring(get_executable_path("fish"))); streams.out.push_back(L'\n'); break; } } return retval; }
/// Define a function. Calls into `function.cpp` to perform the heavy lifting of defining a /// function. int builtin_function(parser_t &parser, io_streams_t &streams, const wcstring_list_t &c_args, const wcstring &contents, int definition_line_offset) { // The wgetopt function expects 'function' as the first argument. Make a new wcstring_list with // that property. This is needed because this builtin has a different signature than the other // builtins. wcstring_list_t args = {L"function"}; args.insert(args.end(), c_args.begin(), c_args.end()); // Hackish const_cast matches the one in builtin_run. const null_terminated_array_t<wchar_t> argv_array(args); wchar_t **argv = const_cast<wchar_t **>(argv_array.get()); wchar_t *cmd = argv[0]; int argc = builtin_count_args(argv); // A valid function name has to be the first argument. wcstring function_name; int retval = validate_function_name(argc, argv, function_name, cmd, streams); if (retval != STATUS_CMD_OK) return retval; argv++; argc--; function_cmd_opts_t opts; int optind; retval = parse_cmd_opts(opts, &optind, argc, argv, parser, streams); if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { builtin_print_help(parser, streams, cmd, streams.err); return STATUS_CMD_OK; } if (argc != optind) { if (opts.named_arguments.size()) { for (int i = optind; i < argc; i++) { opts.named_arguments.push_back(argv[i]); } } else { streams.err.append_format(_(L"%ls: Unexpected positional argument '%ls'"), cmd, argv[optind]); return STATUS_INVALID_ARGS; } } // We have what we need to actually define the function. function_data_t d; d.name = function_name; if (!opts.description.empty()) d.description = opts.description; // d.description = opts.description; d.events.swap(opts.events); d.shadow_scope = opts.shadow_scope; d.named_arguments.swap(opts.named_arguments); d.inherit_vars.swap(opts.inherit_vars); for (size_t i = 0; i < d.events.size(); i++) { event_t &e = d.events.at(i); e.function_name = d.name; } d.definition = contents.c_str(); function_add(d, parser, definition_line_offset); // Handle wrap targets by creating the appropriate completions. for (size_t w = 0; w < opts.wrap_targets.size(); w++) { complete_add_wrapper(function_name, opts.wrap_targets.at(w)); } return STATUS_CMD_OK; }
/// The functions builtin, used for listing and erasing functions. int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **argv) { const wchar_t *cmd = argv[0]; int argc = builtin_count_args(argv); functions_cmd_opts_t opts; int optind; int retval = parse_cmd_opts(opts, &optind, argc, argv, parser, streams); if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { builtin_print_help(parser, streams, cmd, streams.out); return STATUS_CMD_OK; } // Erase, desc, query, copy and list are mutually exclusive. bool describe = opts.description ? true : false; if (describe + opts.erase + opts.list + opts.query + opts.copy > 1) { streams.err.append_format(_(L"%ls: Invalid combination of options\n"), cmd); builtin_print_error_trailer(parser, streams.err, cmd); return STATUS_INVALID_ARGS; } if (opts.erase) { for (int i = optind; i < argc; i++) function_remove(argv[i]); return STATUS_CMD_OK; } if (opts.description) { wchar_t *func; if (argc - optind != 1) { streams.err.append_format(_(L"%ls: Expected exactly one function name\n"), cmd); builtin_print_error_trailer(parser, streams.err, cmd); return STATUS_INVALID_ARGS; } func = argv[optind]; if (!function_exists(func)) { streams.err.append_format(_(L"%ls: Function '%ls' does not exist\n"), cmd, func); builtin_print_error_trailer(parser, streams.err, cmd); return STATUS_CMD_ERROR; } function_set_desc(func, opts.description); return STATUS_CMD_OK; } if (opts.report_metadata) { if (argc - optind != 1) { streams.err.append_format(_(L"%ls: Expected exactly one function name for --details\n"), cmd); return STATUS_INVALID_ARGS; } const wchar_t *funcname = argv[optind]; return report_function_metadata(funcname, opts.verbose, streams, false); } if (opts.handlers) { maybe_t<event_type_t> type_filter; if (opts.handlers_type) { type_filter = event_type_for_name(opts.handlers_type); if (! type_filter) { streams.err.append_format(_(L"%ls: Expected generic | variable | signal | exit | job-id for --handlers-type\n"), cmd); return STATUS_INVALID_ARGS; } } event_print(streams, type_filter); return STATUS_CMD_OK; } // If we query with no argument, just return false. if (opts.query && argc == optind) { return STATUS_CMD_ERROR; } if (opts.list || argc == optind) { wcstring_list_t names = function_get_names(opts.show_hidden); std::sort(names.begin(), names.end()); bool is_screen = !streams.out_is_redirected && isatty(STDOUT_FILENO); if (is_screen) { wcstring buff; for (size_t i = 0; i < names.size(); i++) { buff.append(names.at(i)); buff.append(L", "); } streams.out.append(reformat_for_screen(buff)); } else { for (size_t i = 0; i < names.size(); i++) { streams.out.append(names.at(i).c_str()); streams.out.append(L"\n"); } } return STATUS_CMD_OK; } if (opts.copy) { wcstring current_func; wcstring new_func; if (argc - optind != 2) { streams.err.append_format(_(L"%ls: Expected exactly two names (current function name, " L"and new function name)\n"), cmd); builtin_print_error_trailer(parser, streams.err, cmd); return STATUS_INVALID_ARGS; } current_func = argv[optind]; new_func = argv[optind + 1]; if (!function_exists(current_func)) { streams.err.append_format(_(L"%ls: Function '%ls' does not exist\n"), cmd, current_func.c_str()); builtin_print_error_trailer(parser, streams.err, cmd); return STATUS_CMD_ERROR; } if (!valid_func_name(new_func) || parser_keywords_is_reserved(new_func)) { streams.err.append_format(_(L"%ls: Illegal function name '%ls'\n"), cmd, new_func.c_str()); builtin_print_error_trailer(parser, streams.err, cmd); return STATUS_INVALID_ARGS; } // Keep things simple: don't allow existing names to be copy targets. if (function_exists(new_func)) { streams.err.append_format( _(L"%ls: Function '%ls' already exists. Cannot create copy '%ls'\n"), cmd, new_func.c_str(), current_func.c_str()); builtin_print_error_trailer(parser, streams.err, cmd); return STATUS_CMD_ERROR; } if (function_copy(current_func, new_func)) return STATUS_CMD_OK; return STATUS_CMD_ERROR; } int res = STATUS_CMD_OK; for (int i = optind; i < argc; i++) { if (!function_exists(argv[i])) { res++; } else { if (!opts.query) { if (i != optind) streams.out.append(L"\n"); const wchar_t *funcname = argv[optind]; report_function_metadata(funcname, opts.verbose, streams, true); streams.out.append(functions_def(funcname)); } } } return res; }
/// Define a function. Calls into `function.cpp` to perform the heavy lifting of defining a /// function. int builtin_function(parser_t &parser, io_streams_t &streams, const wcstring_list_t &c_args, const parsed_source_ref_t &source, tnode_t<grammar::job_list> body) { assert(source && "Missing source in builtin_function"); // The wgetopt function expects 'function' as the first argument. Make a new wcstring_list with // that property. This is needed because this builtin has a different signature than the other // builtins. wcstring_list_t args = {L"function"}; args.insert(args.end(), c_args.begin(), c_args.end()); null_terminated_array_t<wchar_t> argv_array(args); wchar_t **argv = argv_array.get(); wchar_t *cmd = argv[0]; int argc = builtin_count_args(argv); // A valid function name has to be the first argument. wcstring function_name; int retval = validate_function_name(argc, argv, function_name, cmd, streams); if (retval != STATUS_CMD_OK) return retval; argv++; argc--; function_cmd_opts_t opts; int optind; retval = parse_cmd_opts(opts, &optind, argc, argv, parser, streams); if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { builtin_print_help(parser, streams, cmd, streams.err); return STATUS_CMD_OK; } if (argc != optind) { if (opts.named_arguments.size()) { for (int i = optind; i < argc; i++) { opts.named_arguments.push_back(argv[i]); } } else { streams.err.append_format(_(L"%ls: Unexpected positional argument '%ls'"), cmd, argv[optind]); return STATUS_INVALID_ARGS; } } // We have what we need to actually define the function. function_data_t d; d.name = function_name; if (!opts.description.empty()) d.description = opts.description; // d.description = opts.description; d.events.swap(opts.events); d.props.shadow_scope = opts.shadow_scope; d.props.named_arguments = std::move(opts.named_arguments); d.inherit_vars = std::move(opts.inherit_vars); for (size_t i = 0; i < d.events.size(); i++) { event_t &e = d.events.at(i); e.function_name = d.name; } d.props.parsed_source = source; d.props.body_node = body; function_add(std::move(d), parser); // Handle wrap targets by creating the appropriate completions. for (const wcstring &wt : opts.wrap_targets) complete_add_wrapper(function_name, wt); return STATUS_CMD_OK; }