2016-04-19 10:25:12 +08:00
|
|
|
// Functions for executing builtin functions.
|
|
|
|
//
|
|
|
|
// How to add a new builtin function:
|
|
|
|
//
|
|
|
|
// 1). Create a function in builtin.c with the following signature:
|
|
|
|
//
|
2020-11-22 21:39:48 +08:00
|
|
|
// <tt>static maybe_t<int> builtin_NAME(parser_t &parser, io_streams_t &streams, wchar_t
|
|
|
|
// **argv)</tt>
|
2016-04-19 10:25:12 +08:00
|
|
|
//
|
|
|
|
// where NAME is the name of the builtin, and args is a zero-terminated list of arguments.
|
|
|
|
//
|
|
|
|
// 2). Add a line like { L"NAME", &builtin_NAME, N_(L"Bla bla bla") }, to the builtin_data_t
|
|
|
|
// variable. The description is used by the completion system. Note that this array is sorted.
|
|
|
|
//
|
2020-02-20 09:00:35 +08:00
|
|
|
// 3). Create a file doc_src/NAME.rst, containing the manual for the builtin in
|
2019-02-25 11:41:51 +08:00
|
|
|
// reStructuredText-format. Check the other builtin manuals for proper syntax.
|
2016-04-19 10:25:12 +08:00
|
|
|
//
|
2020-02-20 09:00:35 +08:00
|
|
|
// 4). Use 'git add doc_src/NAME.txt' to start tracking changes to the documentation file.
|
2016-05-19 06:30:21 +08:00
|
|
|
#include "config.h" // IWYU pragma: keep
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2019-10-14 06:50:48 +08:00
|
|
|
#include "builtin.h"
|
|
|
|
|
2016-04-19 10:25:12 +08:00
|
|
|
#include <unistd.h>
|
2017-02-14 12:37:27 +08:00
|
|
|
|
2015-07-25 23:14:25 +08:00
|
|
|
#include <algorithm>
|
2019-11-19 09:11:16 +08:00
|
|
|
#include <cerrno>
|
|
|
|
#include <cstdlib>
|
2019-10-14 06:50:48 +08:00
|
|
|
#include <cstring>
|
|
|
|
#include <cwchar>
|
2016-06-24 08:24:19 +08:00
|
|
|
#include <memory>
|
2017-04-14 14:13:55 +08:00
|
|
|
#include <string>
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2017-07-08 05:32:41 +08:00
|
|
|
#include "builtin_argparse.h"
|
2017-06-15 09:25:51 +08:00
|
|
|
#include "builtin_bg.h"
|
2017-06-13 08:19:13 +08:00
|
|
|
#include "builtin_bind.h"
|
2017-06-13 09:22:57 +08:00
|
|
|
#include "builtin_block.h"
|
2017-06-14 13:03:27 +08:00
|
|
|
#include "builtin_builtin.h"
|
2017-06-14 12:40:53 +08:00
|
|
|
#include "builtin_cd.h"
|
2017-06-14 12:50:55 +08:00
|
|
|
#include "builtin_command.h"
|
2016-04-20 10:49:15 +08:00
|
|
|
#include "builtin_commandline.h"
|
|
|
|
#include "builtin_complete.h"
|
2017-06-14 13:18:56 +08:00
|
|
|
#include "builtin_contains.h"
|
2017-06-14 09:27:03 +08:00
|
|
|
#include "builtin_disown.h"
|
2017-06-14 08:48:47 +08:00
|
|
|
#include "builtin_echo.h"
|
2017-06-13 09:40:58 +08:00
|
|
|
#include "builtin_emit.h"
|
2019-04-12 19:53:08 +08:00
|
|
|
#include "builtin_eval.h"
|
2017-06-15 12:19:59 +08:00
|
|
|
#include "builtin_exit.h"
|
2017-06-15 04:45:57 +08:00
|
|
|
#include "builtin_fg.h"
|
2017-06-13 10:39:16 +08:00
|
|
|
#include "builtin_functions.h"
|
2017-06-13 12:11:42 +08:00
|
|
|
#include "builtin_history.h"
|
2016-04-20 10:49:15 +08:00
|
|
|
#include "builtin_jobs.h"
|
2017-08-23 10:57:30 +08:00
|
|
|
#include "builtin_math.h"
|
2016-04-20 10:49:15 +08:00
|
|
|
#include "builtin_printf.h"
|
2017-06-15 04:31:05 +08:00
|
|
|
#include "builtin_pwd.h"
|
2017-06-13 13:26:24 +08:00
|
|
|
#include "builtin_random.h"
|
2017-06-13 12:34:24 +08:00
|
|
|
#include "builtin_read.h"
|
2017-06-15 12:19:59 +08:00
|
|
|
#include "builtin_realpath.h"
|
2017-06-15 10:13:45 +08:00
|
|
|
#include "builtin_return.h"
|
2016-04-20 10:49:15 +08:00
|
|
|
#include "builtin_set.h"
|
|
|
|
#include "builtin_set_color.h"
|
2017-06-14 12:20:21 +08:00
|
|
|
#include "builtin_source.h"
|
2017-06-13 12:34:24 +08:00
|
|
|
#include "builtin_status.h"
|
2016-04-20 10:49:15 +08:00
|
|
|
#include "builtin_string.h"
|
|
|
|
#include "builtin_test.h"
|
2020-09-21 23:45:35 +08:00
|
|
|
#include "builtin_type.h"
|
2016-04-20 10:49:15 +08:00
|
|
|
#include "builtin_ulimit.h"
|
2017-10-22 15:10:23 +08:00
|
|
|
#include "builtin_wait.h"
|
2016-04-20 10:49:15 +08:00
|
|
|
#include "common.h"
|
2005-09-20 21:26:39 +08:00
|
|
|
#include "complete.h"
|
2006-11-18 00:24:38 +08:00
|
|
|
#include "exec.h"
|
2016-04-20 10:49:15 +08:00
|
|
|
#include "fallback.h" // IWYU pragma: keep
|
2019-05-28 06:56:53 +08:00
|
|
|
#include "flog.h"
|
2016-04-19 10:25:12 +08:00
|
|
|
#include "intern.h"
|
2016-04-20 10:49:15 +08:00
|
|
|
#include "io.h"
|
2016-04-19 10:25:12 +08:00
|
|
|
#include "parse_constants.h"
|
2006-02-15 03:56:36 +08:00
|
|
|
#include "parse_util.h"
|
2016-04-19 10:25:12 +08:00
|
|
|
#include "parser.h"
|
|
|
|
#include "proc.h"
|
|
|
|
#include "reader.h"
|
|
|
|
#include "wgetopt.h"
|
2016-04-20 10:49:15 +08:00
|
|
|
#include "wutil.h" // IWYU pragma: keep
|
2006-06-21 05:20:16 +08:00
|
|
|
|
2016-08-17 06:30:49 +08:00
|
|
|
/// Counts the number of arguments in the specified null-terminated array
|
2016-04-19 10:25:12 +08:00
|
|
|
int builtin_count_args(const wchar_t *const *argv) {
|
2016-08-17 06:30:49 +08:00
|
|
|
int argc;
|
2019-11-19 10:34:50 +08:00
|
|
|
for (argc = 1; argv[argc] != nullptr;) {
|
2016-09-11 05:55:27 +08:00
|
|
|
argc++;
|
|
|
|
}
|
2016-08-17 06:30:49 +08:00
|
|
|
|
2019-11-19 10:34:50 +08:00
|
|
|
assert(argv[argc] == nullptr);
|
2012-11-19 08:30:30 +08:00
|
|
|
return argc;
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
2016-04-20 09:17:39 +08:00
|
|
|
/// This function works like wperror, but it prints its result into the streams.err string instead
|
|
|
|
/// to stderr. Used by the builtin commands.
|
2021-10-31 18:51:16 +08:00
|
|
|
void builtin_wperror(const wchar_t *program_name, io_streams_t &streams) {
|
2019-03-13 06:07:07 +08:00
|
|
|
char *err = std::strerror(errno);
|
2021-10-31 18:51:16 +08:00
|
|
|
if (program_name != nullptr) {
|
|
|
|
streams.err.append(program_name);
|
2015-09-22 02:24:49 +08:00
|
|
|
streams.err.append(L": ");
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2019-11-19 10:34:50 +08:00
|
|
|
if (err != nullptr) {
|
2012-12-20 05:31:06 +08:00
|
|
|
const wcstring werr = str2wcstring(err);
|
2015-09-22 02:24:49 +08:00
|
|
|
streams.err.append(werr);
|
|
|
|
streams.err.push_back(L'\n');
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
2018-09-29 11:45:56 +08:00
|
|
|
static const wchar_t *const short_options = L"+:h";
|
2019-11-19 10:34:50 +08:00
|
|
|
static const struct woption long_options[] = {{L"help", no_argument, nullptr, 'h'},
|
|
|
|
{nullptr, 0, nullptr, 0}};
|
2017-06-15 09:25:51 +08:00
|
|
|
|
2017-06-17 11:42:33 +08:00
|
|
|
int parse_help_only_cmd_opts(struct help_only_cmd_opts_t &opts, int *optind, int argc,
|
2021-02-14 10:41:09 +08:00
|
|
|
const wchar_t **argv, parser_t &parser, io_streams_t &streams) {
|
|
|
|
const wchar_t *cmd = argv[0];
|
2017-06-15 09:25:51 +08:00
|
|
|
int opt;
|
|
|
|
wgetopter_t w;
|
2019-11-19 10:34:50 +08:00
|
|
|
while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, nullptr)) != -1) {
|
2017-06-15 09:25:51 +08:00
|
|
|
switch (opt) { //!OCLINT(too few branches)
|
|
|
|
case 'h': {
|
2017-06-17 11:42:33 +08:00
|
|
|
opts.print_help = true;
|
2017-06-15 12:19:59 +08:00
|
|
|
break;
|
2017-06-15 09:25:51 +08:00
|
|
|
}
|
2017-06-30 12:49:57 +08:00
|
|
|
case ':': {
|
2017-07-02 05:03:47 +08:00
|
|
|
builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]);
|
2017-06-30 12:49:57 +08:00
|
|
|
return STATUS_INVALID_ARGS;
|
|
|
|
}
|
2017-06-15 09:25:51 +08:00
|
|
|
case '?': {
|
|
|
|
builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]);
|
|
|
|
return STATUS_INVALID_ARGS;
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
DIE("unexpected retval from wgetopt_long");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*optind = w.woptind;
|
|
|
|
return STATUS_CMD_OK;
|
|
|
|
}
|
|
|
|
|
2019-10-20 17:38:17 +08:00
|
|
|
/// Display help/usage information for the specified builtin or function from manpage
|
2016-08-17 06:30:49 +08:00
|
|
|
///
|
|
|
|
/// @param name
|
2019-10-20 17:38:17 +08:00
|
|
|
/// builtin or function name to get up help for
|
2016-08-17 06:30:49 +08:00
|
|
|
///
|
2019-10-20 17:38:17 +08:00
|
|
|
/// Process and print help for the specified builtin or function.
|
2020-03-14 04:59:10 +08:00
|
|
|
void builtin_print_help(parser_t &parser, const io_streams_t &streams, const wchar_t *name,
|
2019-10-20 17:38:17 +08:00
|
|
|
wcstring *error_message) {
|
2016-04-19 10:25:12 +08:00
|
|
|
// This won't ever work if no_exec is set.
|
2019-10-20 17:38:17 +08:00
|
|
|
if (no_exec()) return;
|
|
|
|
const wcstring name_esc = escape_string(name, ESCAPE_ALL);
|
|
|
|
wcstring cmd = format_string(L"__fish_print_help %ls ", name_esc.c_str());
|
|
|
|
io_chain_t ios;
|
|
|
|
if (error_message) {
|
|
|
|
cmd.append(escape_string(*error_message, ESCAPE_ALL));
|
|
|
|
// If it's an error, redirect the output of __fish_print_help to stderr
|
2019-12-20 06:14:23 +08:00
|
|
|
ios.push_back(std::make_shared<io_fd_t>(STDOUT_FILENO, STDERR_FILENO));
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2021-03-16 05:59:37 +08:00
|
|
|
auto res = parser.eval(cmd, ios);
|
2021-09-22 08:45:18 +08:00
|
|
|
if (res.status.normal_exited() && res.status.exit_code() == 2) {
|
2021-03-20 00:20:30 +08:00
|
|
|
streams.err.append_format(BUILTIN_ERR_MISSING_HELP, name_esc.c_str(), name_esc.c_str());
|
2021-03-16 05:59:37 +08:00
|
|
|
}
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
2006-06-21 05:20:16 +08:00
|
|
|
|
2016-04-20 09:17:39 +08:00
|
|
|
/// Perform error reporting for encounter with unknown option.
|
2016-04-21 14:00:54 +08:00
|
|
|
void builtin_unknown_option(parser_t &parser, io_streams_t &streams, const wchar_t *cmd,
|
2021-10-19 23:43:54 +08:00
|
|
|
const wchar_t *opt, bool print_hints) {
|
2015-09-22 02:24:49 +08:00
|
|
|
streams.err.append_format(BUILTIN_ERR_UNKNOWN, cmd, opt);
|
2021-10-19 23:43:54 +08:00
|
|
|
if (print_hints) {
|
|
|
|
builtin_print_error_trailer(parser, streams.err, cmd);
|
|
|
|
}
|
2007-01-21 22:55:27 +08:00
|
|
|
}
|
|
|
|
|
2016-04-20 09:17:39 +08:00
|
|
|
/// Perform error reporting for encounter with missing argument.
|
2016-04-21 14:00:54 +08:00
|
|
|
void builtin_missing_argument(parser_t &parser, io_streams_t &streams, const wchar_t *cmd,
|
2020-01-09 00:33:36 +08:00
|
|
|
const wchar_t *opt, bool print_hints) {
|
|
|
|
if (opt[0] == L'-' && opt[1] != L'-') {
|
2021-11-04 13:51:20 +08:00
|
|
|
// if c in -qc '-qc' is missing the argument, now opt is just 'c'
|
2020-01-09 00:33:36 +08:00
|
|
|
opt += std::wcslen(opt) - 1;
|
2021-11-04 13:51:20 +08:00
|
|
|
// now prepend - to output -c
|
|
|
|
streams.err.append_format(BUILTIN_ERR_MISSING, cmd, wcstring(L"-").append(opt).c_str());
|
2020-01-09 00:33:36 +08:00
|
|
|
}
|
2021-11-04 13:51:20 +08:00
|
|
|
else
|
|
|
|
streams.err.append_format(BUILTIN_ERR_MISSING, cmd, opt);
|
|
|
|
|
2020-01-09 00:33:36 +08:00
|
|
|
if (print_hints) {
|
|
|
|
builtin_print_error_trailer(parser, streams.err, cmd);
|
|
|
|
}
|
2019-03-27 02:13:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Print the backtrace and call for help that we use at the end of error messages.
|
|
|
|
void builtin_print_error_trailer(parser_t &parser, output_stream_t &b, const wchar_t *cmd) {
|
|
|
|
b.append(L"\n");
|
2019-03-27 02:29:44 +08:00
|
|
|
const wcstring stacktrace = parser.current_line();
|
|
|
|
// Don't print two empty lines if we don't have a stacktrace.
|
|
|
|
if (!stacktrace.empty()) {
|
|
|
|
b.append(stacktrace);
|
|
|
|
b.append(L"\n");
|
|
|
|
}
|
2019-03-27 02:13:01 +08:00
|
|
|
b.append_format(_(L"(Type 'help %ls' for related documentation)\n"), cmd);
|
2007-08-02 03:44:50 +08:00
|
|
|
}
|
|
|
|
|
2016-04-20 09:17:39 +08:00
|
|
|
/// A generic bultin that only supports showing a help message. This is only a placeholder that
|
|
|
|
/// prints the help message. Useful for commands that live in the parser.
|
2021-02-14 10:41:09 +08:00
|
|
|
static maybe_t<int> builtin_generic(parser_t &parser, io_streams_t &streams, const wchar_t **argv) {
|
2017-06-15 13:46:12 +08:00
|
|
|
const wchar_t *cmd = argv[0];
|
2016-04-19 10:25:12 +08:00
|
|
|
int argc = builtin_count_args(argv);
|
2017-06-17 11:42:33 +08:00
|
|
|
help_only_cmd_opts_t opts;
|
2017-06-15 13:46:12 +08:00
|
|
|
int optind;
|
2017-06-17 11:42:33 +08:00
|
|
|
int retval = parse_help_only_cmd_opts(opts, &optind, argc, argv, parser, streams);
|
2017-06-15 13:46:12 +08:00
|
|
|
if (retval != STATUS_CMD_OK) return retval;
|
|
|
|
|
|
|
|
if (opts.print_help) {
|
2019-10-20 17:38:17 +08:00
|
|
|
builtin_print_help(parser, streams, cmd);
|
2017-06-15 13:46:12 +08:00
|
|
|
return STATUS_CMD_OK;
|
|
|
|
}
|
2014-01-15 17:40:40 +08:00
|
|
|
|
2016-04-19 10:25:12 +08:00
|
|
|
// Hackish - if we have no arguments other than the command, we are a "naked invocation" and we
|
|
|
|
// just print help.
|
2020-10-27 02:13:34 +08:00
|
|
|
if (argc == 1 || wcscmp(cmd, L"time") == 0) {
|
2019-10-20 17:38:17 +08:00
|
|
|
builtin_print_help(parser, streams, cmd);
|
2017-05-05 12:35:41 +08:00
|
|
|
return STATUS_INVALID_ARGS;
|
2014-01-13 07:10:59 +08:00
|
|
|
}
|
2014-01-15 17:40:40 +08:00
|
|
|
|
2017-05-04 15:18:02 +08:00
|
|
|
return STATUS_CMD_ERROR;
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2012-10-17 17:56:03 +08:00
|
|
|
|
2018-01-04 07:42:12 +08:00
|
|
|
// How many bytes we read() at once.
|
|
|
|
// Since this is just for counting, it can be massive.
|
2019-11-26 09:20:16 +08:00
|
|
|
#define COUNT_CHUNK_SIZE (512 * 256)
|
2016-04-20 09:17:39 +08:00
|
|
|
/// Implementation of the builtin count command, used to count the number of arguments sent to it.
|
2021-02-14 10:41:09 +08:00
|
|
|
static maybe_t<int> builtin_count(parser_t &parser, io_streams_t &streams, const wchar_t **argv) {
|
2016-10-10 05:38:26 +08:00
|
|
|
UNUSED(parser);
|
2018-01-04 07:42:12 +08:00
|
|
|
int argc = 0;
|
|
|
|
|
|
|
|
// Count the newlines coming in via stdin like `wc -l`.
|
|
|
|
if (streams.stdin_is_directly_redirected) {
|
2021-02-11 09:19:08 +08:00
|
|
|
assert(streams.stdin_fd >= 0 &&
|
|
|
|
"Should have a valid fd since stdin is directly redirected");
|
2018-01-04 07:42:12 +08:00
|
|
|
char buf[COUNT_CHUNK_SIZE];
|
|
|
|
while (true) {
|
|
|
|
long n = read_blocked(streams.stdin_fd, buf, COUNT_CHUNK_SIZE);
|
2021-02-11 09:19:08 +08:00
|
|
|
if (n == 0) {
|
|
|
|
break;
|
|
|
|
} else if (n < 0) {
|
|
|
|
wperror(L"read");
|
|
|
|
return STATUS_CMD_ERROR;
|
|
|
|
}
|
2018-01-04 07:42:12 +08:00
|
|
|
for (int i = 0; i < n; i++) {
|
2021-10-26 00:40:43 +08:00
|
|
|
if (buf[i] == '\n') {
|
2018-01-04 07:42:12 +08:00
|
|
|
argc++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Always add the size of argv.
|
|
|
|
// That means if you call `something | count a b c`, you'll get the count of something _plus 3_.
|
|
|
|
argc += builtin_count_args(argv) - 1;
|
|
|
|
streams.out.append_format(L"%d\n", argc);
|
|
|
|
return argc == 0 ? STATUS_CMD_ERROR : STATUS_CMD_OK;
|
2007-08-01 05:23:32 +08:00
|
|
|
}
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2016-04-20 09:17:39 +08:00
|
|
|
/// This function handles both the 'continue' and the 'break' builtins that are used for loop
|
|
|
|
/// control.
|
2020-11-22 21:39:48 +08:00
|
|
|
static maybe_t<int> builtin_break_continue(parser_t &parser, io_streams_t &streams,
|
2021-02-14 10:41:09 +08:00
|
|
|
const wchar_t **argv) {
|
2019-03-13 05:06:01 +08:00
|
|
|
int is_break = (std::wcscmp(argv[0], L"break") == 0);
|
2012-11-19 08:30:30 +08:00
|
|
|
int argc = builtin_count_args(argv);
|
|
|
|
|
2016-04-19 10:25:12 +08:00
|
|
|
if (argc != 1) {
|
2019-10-20 17:38:17 +08:00
|
|
|
wcstring error_message = format_string(BUILTIN_ERR_UNKNOWN, argv[0], argv[1]);
|
|
|
|
builtin_print_help(parser, streams, argv[0], &error_message);
|
2017-05-05 12:35:41 +08:00
|
|
|
return STATUS_INVALID_ARGS;
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
|
|
|
|
2021-10-26 03:22:51 +08:00
|
|
|
// Paranoia: ensure we have a real loop.
|
2021-10-26 03:43:30 +08:00
|
|
|
// This is checked in the AST but we may be invoked dynamically, e.g. just via "eval break".
|
2021-10-26 03:22:51 +08:00
|
|
|
bool has_loop = false;
|
|
|
|
for (const auto &b : parser.blocks()) {
|
|
|
|
if (b.type() == block_type_t::while_block || b.type() == block_type_t::for_block) {
|
|
|
|
has_loop = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (b.is_function_call()) break;
|
|
|
|
}
|
|
|
|
if (!has_loop) {
|
|
|
|
wcstring error_message = format_string(_(L"%ls: Not inside of loop\n"), argv[0]);
|
|
|
|
builtin_print_help(parser, streams, argv[0], &error_message);
|
|
|
|
return STATUS_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
2019-05-19 14:12:34 +08:00
|
|
|
// Mark the status in the libdata.
|
|
|
|
parser.libdata().loop_status = is_break ? loop_status_t::breaks : loop_status_t::continues;
|
2017-05-04 15:18:02 +08:00
|
|
|
return STATUS_CMD_OK;
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
2016-04-20 09:17:39 +08:00
|
|
|
/// Implementation of the builtin breakpoint command, used to launch the interactive debugger.
|
2021-02-14 10:41:09 +08:00
|
|
|
static maybe_t<int> builtin_breakpoint(parser_t &parser, io_streams_t &streams,
|
|
|
|
const wchar_t **argv) {
|
|
|
|
const wchar_t *cmd = argv[0];
|
2019-11-19 10:34:50 +08:00
|
|
|
if (argv[1] != nullptr) {
|
2017-06-20 12:05:34 +08:00
|
|
|
streams.err.append_format(BUILTIN_ERR_ARG_COUNT1, cmd, 0, builtin_count_args(argv) - 1);
|
2017-05-05 12:35:41 +08:00
|
|
|
return STATUS_INVALID_ARGS;
|
2016-10-10 05:38:26 +08:00
|
|
|
}
|
|
|
|
|
2017-06-20 12:05:34 +08:00
|
|
|
// If we're not interactive then we can't enter the debugger. So treat this command as a no-op.
|
2019-05-28 05:52:48 +08:00
|
|
|
if (!parser.is_interactive()) {
|
2017-06-20 12:05:34 +08:00
|
|
|
return STATUS_CMD_ERROR;
|
|
|
|
}
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2017-06-20 12:05:34 +08:00
|
|
|
// Ensure we don't allow creating a breakpoint at an interactive prompt. There may be a simpler
|
|
|
|
// or clearer way to do this but this works.
|
|
|
|
const block_t *block1 = parser.block_at_index(1);
|
2019-12-23 07:37:14 +08:00
|
|
|
if (!block1 || block1->type() == block_type_t::breakpoint) {
|
2017-06-20 12:05:34 +08:00
|
|
|
streams.err.append_format(_(L"%ls: Command not valid at an interactive prompt\n"), cmd);
|
|
|
|
return STATUS_ILLEGAL_CMD;
|
|
|
|
}
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2019-05-20 05:40:06 +08:00
|
|
|
const block_t *bpb = parser.push_block(block_t::breakpoint_block());
|
2019-05-27 09:51:26 +08:00
|
|
|
reader_read(parser, STDIN_FILENO, streams.io_chain ? *streams.io_chain : io_chain_t());
|
2017-01-22 07:35:35 +08:00
|
|
|
parser.pop_block(bpb);
|
2019-05-13 05:00:44 +08:00
|
|
|
return parser.get_last_status();
|
2006-11-11 18:54:00 +08:00
|
|
|
}
|
|
|
|
|
2021-10-31 18:51:16 +08:00
|
|
|
static maybe_t<int> builtin_true(parser_t &parser, io_streams_t &streams, const wchar_t **argv) {
|
2016-10-10 05:38:26 +08:00
|
|
|
UNUSED(parser);
|
|
|
|
UNUSED(streams);
|
2020-04-05 02:34:42 +08:00
|
|
|
UNUSED(argv);
|
2017-05-04 15:18:02 +08:00
|
|
|
return STATUS_CMD_OK;
|
2014-09-30 04:26:28 +08:00
|
|
|
}
|
|
|
|
|
2021-10-31 18:51:16 +08:00
|
|
|
static maybe_t<int> builtin_false(parser_t &parser, io_streams_t &streams, const wchar_t **argv) {
|
2016-10-10 05:38:26 +08:00
|
|
|
UNUSED(parser);
|
|
|
|
UNUSED(streams);
|
2020-04-05 02:34:42 +08:00
|
|
|
UNUSED(argv);
|
2017-05-04 15:18:02 +08:00
|
|
|
return STATUS_CMD_ERROR;
|
2014-09-30 04:26:28 +08:00
|
|
|
}
|
|
|
|
|
2021-10-31 18:51:16 +08:00
|
|
|
static maybe_t<int> builtin_gettext(parser_t &parser, io_streams_t &streams, const wchar_t **argv) {
|
2020-05-30 02:53:44 +08:00
|
|
|
UNUSED(parser);
|
|
|
|
UNUSED(streams);
|
|
|
|
for (int i = 1; i < builtin_count_args(argv); i++) {
|
|
|
|
streams.out.append(_(argv[i]));
|
|
|
|
}
|
|
|
|
return STATUS_CMD_OK;
|
|
|
|
}
|
|
|
|
|
2016-04-19 10:25:12 +08:00
|
|
|
// END OF BUILTIN COMMANDS
|
|
|
|
// Below are functions for handling the builtin commands.
|
2018-09-29 12:22:24 +08:00
|
|
|
// THESE MUST BE SORTED BY NAME! Completion lookup uses binary search.
|
2005-12-15 21:59:02 +08:00
|
|
|
|
2016-04-19 10:25:12 +08:00
|
|
|
// Data about all the builtin commands in fish.
|
|
|
|
// Functions that are bound to builtin_generic are handled directly by the parser.
|
2018-09-29 12:22:24 +08:00
|
|
|
// NOTE: These must be kept in sorted order!
|
2021-08-27 04:40:02 +08:00
|
|
|
static constexpr builtin_data_t builtin_datas[] = {
|
2020-03-28 16:39:12 +08:00
|
|
|
{L".", &builtin_source, N_(L"Evaluate contents of file")},
|
2020-03-28 06:42:44 +08:00
|
|
|
{L":", &builtin_true, N_(L"Return a successful result")},
|
2016-04-19 10:25:12 +08:00
|
|
|
{L"[", &builtin_test, N_(L"Test a condition")},
|
2020-05-30 02:53:44 +08:00
|
|
|
{L"_", &builtin_gettext, N_(L"Translate a string")},
|
2019-11-25 19:03:25 +08:00
|
|
|
{L"and", &builtin_generic, N_(L"Execute command if previous command succeeded")},
|
2017-07-08 05:32:41 +08:00
|
|
|
{L"argparse", &builtin_argparse, N_(L"Parse options in fish script")},
|
2016-04-19 10:25:12 +08:00
|
|
|
{L"begin", &builtin_generic, N_(L"Create a block of code")},
|
|
|
|
{L"bg", &builtin_bg, N_(L"Send job to background")},
|
|
|
|
{L"bind", &builtin_bind, N_(L"Handle fish key bindings")},
|
|
|
|
{L"block", &builtin_block, N_(L"Temporarily block delivery of events")},
|
|
|
|
{L"break", &builtin_break_continue, N_(L"Stop the innermost loop")},
|
2018-09-29 12:22:24 +08:00
|
|
|
{L"breakpoint", &builtin_breakpoint,
|
|
|
|
N_(L"Temporarily halt execution of a script and launch an interactive debug prompt")},
|
2016-04-19 10:25:12 +08:00
|
|
|
{L"builtin", &builtin_builtin, N_(L"Run a builtin command instead of a function")},
|
|
|
|
{L"case", &builtin_generic, N_(L"Conditionally execute a block of commands")},
|
|
|
|
{L"cd", &builtin_cd, N_(L"Change working directory")},
|
|
|
|
{L"command", &builtin_command, N_(L"Run a program instead of a function or builtin")},
|
|
|
|
{L"commandline", &builtin_commandline, N_(L"Set or get the commandline")},
|
|
|
|
{L"complete", &builtin_complete, N_(L"Edit command specific completions")},
|
|
|
|
{L"contains", &builtin_contains, N_(L"Search for a specified string in a list")},
|
2018-09-29 12:22:24 +08:00
|
|
|
{L"continue", &builtin_break_continue,
|
|
|
|
N_(L"Skip the rest of the current lap of the innermost loop")},
|
2016-04-19 10:25:12 +08:00
|
|
|
{L"count", &builtin_count, N_(L"Count the number of arguments")},
|
2017-03-23 08:50:57 +08:00
|
|
|
{L"disown", &builtin_disown, N_(L"Remove job from job list")},
|
2016-04-19 10:25:12 +08:00
|
|
|
{L"echo", &builtin_echo, N_(L"Print arguments")},
|
|
|
|
{L"else", &builtin_generic, N_(L"Evaluate block if condition is false")},
|
|
|
|
{L"emit", &builtin_emit, N_(L"Emit an event")},
|
|
|
|
{L"end", &builtin_generic, N_(L"End a block of commands")},
|
2019-04-12 19:53:08 +08:00
|
|
|
{L"eval", &builtin_eval, N_(L"Evaluate a string as a statement")},
|
2016-04-19 10:25:12 +08:00
|
|
|
{L"exec", &builtin_generic, N_(L"Run command in current process")},
|
|
|
|
{L"exit", &builtin_exit, N_(L"Exit the shell")},
|
|
|
|
{L"false", &builtin_false, N_(L"Return an unsuccessful result")},
|
|
|
|
{L"fg", &builtin_fg, N_(L"Send job to foreground")},
|
|
|
|
{L"for", &builtin_generic, N_(L"Perform a set of commands multiple times")},
|
|
|
|
{L"function", &builtin_generic, N_(L"Define a new function")},
|
|
|
|
{L"functions", &builtin_functions, N_(L"List or remove functions")},
|
|
|
|
{L"history", &builtin_history, N_(L"History of commands executed by user")},
|
|
|
|
{L"if", &builtin_generic, N_(L"Evaluate block if condition is true")},
|
|
|
|
{L"jobs", &builtin_jobs, N_(L"Print currently running jobs")},
|
2017-08-23 10:57:30 +08:00
|
|
|
{L"math", &builtin_math, N_(L"Evaluate math expressions")},
|
2016-04-19 10:25:12 +08:00
|
|
|
{L"not", &builtin_generic, N_(L"Negate exit status of job")},
|
|
|
|
{L"or", &builtin_generic, N_(L"Execute command if previous command failed")},
|
|
|
|
{L"printf", &builtin_printf, N_(L"Prints formatted text")},
|
|
|
|
{L"pwd", &builtin_pwd, N_(L"Print the working directory")},
|
|
|
|
{L"random", &builtin_random, N_(L"Generate random number")},
|
|
|
|
{L"read", &builtin_read, N_(L"Read a line of input into variables")},
|
2016-10-04 08:51:27 +08:00
|
|
|
{L"realpath", &builtin_realpath, N_(L"Convert path to absolute path without symlinks")},
|
2016-04-19 10:25:12 +08:00
|
|
|
{L"return", &builtin_return, N_(L"Stop the currently evaluated function")},
|
|
|
|
{L"set", &builtin_set, N_(L"Handle environment variables")},
|
|
|
|
{L"set_color", &builtin_set_color, N_(L"Set the terminal color")},
|
|
|
|
{L"source", &builtin_source, N_(L"Evaluate contents of file")},
|
|
|
|
{L"status", &builtin_status, N_(L"Return status information about fish")},
|
|
|
|
{L"string", &builtin_string, N_(L"Manipulate strings")},
|
|
|
|
{L"switch", &builtin_generic, N_(L"Conditionally execute a block of commands")},
|
|
|
|
{L"test", &builtin_test, N_(L"Test a condition")},
|
2020-02-24 06:42:31 +08:00
|
|
|
{L"time", &builtin_generic, N_(L"Measure how long a command or block takes")},
|
2016-04-19 10:25:12 +08:00
|
|
|
{L"true", &builtin_true, N_(L"Return a successful result")},
|
2020-09-21 23:45:35 +08:00
|
|
|
{L"type", &builtin_type, N_(L"Check if a thing is a thing")},
|
2016-04-19 10:25:12 +08:00
|
|
|
{L"ulimit", &builtin_ulimit, N_(L"Set or get the shells resource usage limits")},
|
2017-10-22 15:10:23 +08:00
|
|
|
{L"wait", &builtin_wait, N_(L"Wait for background processes completed")},
|
2020-05-30 02:53:44 +08:00
|
|
|
{L"while", &builtin_generic, N_(L"Perform a command multiple times")},
|
|
|
|
};
|
2021-08-27 04:40:02 +08:00
|
|
|
ASSERT_SORTED_BY_NAME(builtin_datas);
|
2012-02-01 11:47:56 +08:00
|
|
|
|
2018-09-29 12:22:24 +08:00
|
|
|
#define BUILTIN_COUNT (sizeof builtin_datas / sizeof *builtin_datas)
|
|
|
|
|
2016-08-17 06:30:49 +08:00
|
|
|
/// Look up a builtin_data_t for a specified builtin
|
|
|
|
///
|
|
|
|
/// @param name
|
|
|
|
/// Name of the builtin
|
|
|
|
///
|
|
|
|
/// @return
|
|
|
|
/// Pointer to a builtin_data_t
|
|
|
|
///
|
2016-04-19 10:25:12 +08:00
|
|
|
static const builtin_data_t *builtin_lookup(const wcstring &name) {
|
2021-08-27 04:40:02 +08:00
|
|
|
return get_by_sorted_name(name.c_str(), builtin_datas);
|
2006-02-05 21:08:40 +08:00
|
|
|
}
|
2006-01-31 03:53:10 +08:00
|
|
|
|
2016-08-17 06:30:49 +08:00
|
|
|
/// Initialize builtin data.
|
2016-04-19 10:25:12 +08:00
|
|
|
void builtin_init() {
|
2021-09-11 02:11:32 +08:00
|
|
|
for (const auto &builtin_data : builtin_datas) {
|
|
|
|
intern_static(builtin_data.name);
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
2016-08-17 06:30:49 +08:00
|
|
|
/// Is there a builtin command with the given name?
|
2016-10-24 04:58:12 +08:00
|
|
|
bool builtin_exists(const wcstring &cmd) { return static_cast<bool>(builtin_lookup(cmd)); }
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2017-07-20 02:44:53 +08:00
|
|
|
/// Is the command a keyword we need to special-case the handling of `-h` and `--help`.
|
2018-10-01 07:57:05 +08:00
|
|
|
static const wchar_t *const help_builtins[] = {L"for", L"while", L"function", L"if",
|
|
|
|
L"end", L"switch", L"case"};
|
2021-02-15 05:15:29 +08:00
|
|
|
static bool cmd_needs_help(const wcstring &cmd) { return contains(help_builtins, cmd); }
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2016-08-17 06:30:49 +08:00
|
|
|
/// Execute a builtin command
|
2021-02-15 05:15:29 +08:00
|
|
|
proc_status_t builtin_run(parser_t &parser, const wcstring_list_t &argv, io_streams_t &streams) {
|
|
|
|
if (argv.empty()) return proc_status_t::from_exit_code(STATUS_INVALID_ARGS);
|
|
|
|
const wcstring &cmdname = argv.front();
|
2012-11-19 08:30:30 +08:00
|
|
|
|
2017-06-19 13:07:48 +08:00
|
|
|
// 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.
|
2021-02-15 05:15:29 +08:00
|
|
|
if (argv.size() == 2 && parse_util_argument_is_help(argv[1]) && cmd_needs_help(cmdname)) {
|
|
|
|
builtin_print_help(parser, streams, cmdname.c_str());
|
2019-02-26 02:05:42 +08:00
|
|
|
return proc_status_t::from_exit_code(STATUS_CMD_OK);
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
|
|
|
|
2021-02-15 05:15:29 +08:00
|
|
|
if (const builtin_data_t *data = builtin_lookup(cmdname)) {
|
2021-04-04 06:36:04 +08:00
|
|
|
// Construct the permutable argv array which the builtin expects, and execute the builtin.
|
2021-02-15 05:15:29 +08:00
|
|
|
null_terminated_array_t<wchar_t> argv_arr(argv);
|
2021-04-04 06:36:04 +08:00
|
|
|
maybe_t<int> builtin_ret = data->func(parser, streams, argv_arr.get());
|
|
|
|
|
|
|
|
// Flush our out and error streams, and check for their errors.
|
|
|
|
int out_ret = streams.out.flush_and_check_error();
|
|
|
|
int err_ret = streams.err.flush_and_check_error();
|
|
|
|
|
|
|
|
// Resolve our status code.
|
|
|
|
// If the builtin itself produced an error, use that error.
|
|
|
|
// Otherwise use any errors from writing to out and writing to err, in that order.
|
|
|
|
int code = builtin_ret ? *builtin_ret : 0;
|
|
|
|
if (code == 0) code = out_ret;
|
|
|
|
if (code == 0) code = err_ret;
|
2021-02-09 05:25:30 +08:00
|
|
|
|
|
|
|
// The exit code is cast to an 8-bit unsigned integer, so saturate to 255. Otherwise,
|
|
|
|
// multiples of 256 are reported as 0.
|
2021-04-04 06:36:04 +08:00
|
|
|
if (code > 255) code = 255;
|
2021-02-09 05:25:30 +08:00
|
|
|
|
2021-04-04 06:36:04 +08:00
|
|
|
// Handle the case of an empty status.
|
|
|
|
if (code == 0 && !builtin_ret.has_value()) {
|
|
|
|
return proc_status_t::empty();
|
|
|
|
}
|
2021-02-09 05:25:30 +08:00
|
|
|
return proc_status_t::from_exit_code(code);
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2016-05-05 06:19:47 +08:00
|
|
|
|
2021-02-15 05:15:29 +08:00
|
|
|
FLOGF(error, UNKNOWN_BUILTIN_ERR_MSG, cmdname.c_str());
|
2019-02-26 02:05:42 +08:00
|
|
|
return proc_status_t::from_exit_code(STATUS_CMD_ERROR);
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
2016-08-17 06:30:49 +08:00
|
|
|
/// Returns a list of all builtin names.
|
2018-02-19 10:33:04 +08:00
|
|
|
wcstring_list_t builtin_get_names() {
|
2012-02-01 11:47:56 +08:00
|
|
|
wcstring_list_t result;
|
2018-09-29 12:22:24 +08:00
|
|
|
result.reserve(BUILTIN_COUNT);
|
2019-11-20 05:46:47 +08:00
|
|
|
for (const auto &builtin_data : builtin_datas) {
|
|
|
|
result.push_back(builtin_data.name);
|
2012-02-01 11:47:56 +08:00
|
|
|
}
|
|
|
|
return result;
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
2016-08-17 06:30:49 +08:00
|
|
|
/// Insert all builtin names into list.
|
2020-01-16 08:13:41 +08:00
|
|
|
void builtin_get_names(completion_list_t *list) {
|
2019-11-19 10:34:50 +08:00
|
|
|
assert(list != nullptr);
|
2018-09-29 12:22:24 +08:00
|
|
|
list->reserve(list->size() + BUILTIN_COUNT);
|
2019-11-20 05:46:47 +08:00
|
|
|
for (const auto &builtin_data : builtin_datas) {
|
|
|
|
append_completion(list, builtin_data.name);
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2012-01-17 00:56:47 +08:00
|
|
|
}
|
|
|
|
|
2016-08-17 06:30:49 +08:00
|
|
|
/// Return a one-line description of the specified builtin.
|
2019-03-15 06:12:14 +08:00
|
|
|
const wchar_t *builtin_get_desc(const wcstring &name) {
|
2019-03-15 12:45:31 +08:00
|
|
|
const wchar_t *result = L"";
|
2012-11-19 08:30:30 +08:00
|
|
|
const builtin_data_t *builtin = builtin_lookup(name);
|
2016-04-19 10:25:12 +08:00
|
|
|
if (builtin) {
|
2018-09-29 12:22:24 +08:00
|
|
|
result = _(builtin->desc);
|
2012-05-18 10:37:46 +08:00
|
|
|
}
|
|
|
|
return result;
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|