/// Execute a builtin command proc_status_t builtin_run(parser_t &parser, int job_pgid, wchar_t **argv, io_streams_t &streams) { UNUSED(parser); UNUSED(streams); if (argv == NULL || argv[0] == NULL) return proc_status_t::from_exit_code(STATUS_INVALID_ARGS); // We can be handed a keyword by the parser as if it was a command. This happens when the user // follows the keyword by `-h` or `--help`. Since it isn't really a builtin command we need to // handle displaying help for it here. if (argv[1] && !argv[2] && parse_util_argument_is_help(argv[1]) && cmd_needs_help(argv[0])) { builtin_print_help(parser, streams, argv[0], streams.out); return proc_status_t::from_exit_code(STATUS_CMD_OK); } if (const builtin_data_t *data = builtin_lookup(argv[0])) { // If we are interactive, save the foreground pgroup and restore it after in case the // builtin needs to read from the terminal. See #4540. bool grab_tty = is_interactive_session && isatty(streams.stdin_fd); pid_t pgroup_to_restore = grab_tty ? terminal_acquire_before_builtin(job_pgid) : -1; int ret = data->func(parser, streams, argv); if (pgroup_to_restore >= 0) { tcsetpgrp(STDIN_FILENO, pgroup_to_restore); } return proc_status_t::from_exit_code(ret); } debug(0, UNKNOWN_BUILTIN_ERR_MSG, argv[0]); return proc_status_t::from_exit_code(STATUS_CMD_ERROR); }
// Check if the first argument under the given node is --help static bool first_argument_is_help(const parse_node_tree_t &node_tree, const parse_node_t &node, const wcstring &src) { bool is_help = false; const parse_node_tree_t::parse_node_list_t arg_nodes = node_tree.find_nodes(node, symbol_argument, 1); if (! arg_nodes.empty()) { // Check the first argument only const parse_node_t &arg = *arg_nodes.at(0); const wcstring first_arg_src = arg.get_source(src); is_help = parse_util_argument_is_help(first_arg_src.c_str(), 3); } return is_help; }