From ef9c924960bac160dc6df1833f4f297abc07df80 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 21 Sep 2020 17:45:35 +0200 Subject: [PATCH] Make type a builtin This is too important to not be one. For one if it couldn't be loaded for any reason it would break a lot of fish scripts. Also this is faster by ~20x. Fixes #7342 --- CMakeLists.txt | 2 +- share/functions/type.fish | 131 ------------------------ src/builtin.cpp | 2 + src/builtin_type.cpp | 203 ++++++++++++++++++++++++++++++++++++++ src/builtin_type.h | 11 +++ 5 files changed, 217 insertions(+), 132 deletions(-) delete mode 100644 share/functions/type.fish create mode 100644 src/builtin_type.cpp create mode 100644 src/builtin_type.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b3beeb8c..a3def5ab7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,7 +107,7 @@ set(FISH_SRCS src/builtin_pwd.cpp src/builtin_random.cpp src/builtin_read.cpp src/builtin_realpath.cpp src/builtin_return.cpp src/builtin_set.cpp src/builtin_set_color.cpp src/builtin_source.cpp src/builtin_status.cpp - src/builtin_string.cpp src/builtin_test.cpp src/builtin_ulimit.cpp + src/builtin_string.cpp src/builtin_test.cpp src/builtin_type.cpp src/builtin_ulimit.cpp src/builtin_wait.cpp src/color.cpp src/common.cpp src/complete.cpp src/env.cpp src/env_dispatch.cpp src/env_universal_common.cpp src/event.cpp src/exec.cpp src/expand.cpp src/fallback.cpp src/fd_monitor.cpp src/fish_version.cpp diff --git a/share/functions/type.fish b/share/functions/type.fish deleted file mode 100644 index 21010e676..000000000 --- a/share/functions/type.fish +++ /dev/null @@ -1,131 +0,0 @@ -function type --description 'Print the type of a command' - # For legacy reasons, no argument simply causes an unsuccessful return. - set -q argv[1] - or return 1 - - # --query is the same thing as --quiet - set -l options h/help a/all s/short f/no-functions t/type p/path P/force-path q/quiet Q-query - argparse -n type -x t,p,P $options -- $argv - or return - - if set -q _flag_help - __fish_print_help type - return 0 - end - - set -l res 1 - set -l mode normal - set -l multi no - set -l selection all - set -l short no - - # Technically all four of these flags are mutually exclusive. However, we allow -q to be used - # with the other three because old versions of this function explicitly allowed it by making - # --quiet have precedence. - if set -q _flag_quiet; or set -q _flag_query - set mode quiet - else if set -q _flag_type - set mode type - else if set -q _flag_path - set mode path - else if set -q _flag_force_path - set mode path - set selection files - end - - set -q _flag_all - and set multi yes - - set -q _flag_short - and set short yes - - set -q _flag_no_functions - and set selection files - - # Check all possible types for the remaining arguments. - for i in $argv - # Found will be set to 1 if a match is found. - set -l found 0 - - if test $selection != files - if functions -q -- $i - set res 0 - set found 1 - switch $mode - case normal - printf (_ '%s is a function') $i - if test $short != yes - echo (_ ' with definition') - functions $i - else - set -l func_path (functions --details $i) - if not contains -- $func_path - stdin - printf (_ ' (defined in %s)') $func_path - end - echo - end - case type - echo (_ 'function') - case path - set -l func_path (functions --details $i) - switch $func_path - case - - case n/a - case stdin - break - case "*" - echo $func_path - end - end - if test $multi != yes - continue - end - end - - if contains -- $i (builtin -n) - set res 0 - set found 1 - switch $mode - case normal - printf (_ '%s is a builtin\n') $i - - case type - echo (_ 'builtin') - end - if test $multi != yes - continue - end - end - end - - set -l paths - if test $multi != yes - set paths (command -s -- $i) - else - set paths (command -sa -- $i) - end - for path in $paths - set res 0 - set found 1 - switch $mode - case normal - printf (_ '%s is %s\n') $i $path - case type - echo (_ 'file') - case path - echo $path - end - if test $multi != yes - continue - end - end - - if test $found = 0 - and test $mode != quiet - and test $mode != path - printf (_ "%s: Could not find '%s'\n") type $i >&2 - end - end - - return $res -end diff --git a/src/builtin.cpp b/src/builtin.cpp index 14641071d..db324cd12 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -61,6 +61,7 @@ #include "builtin_status.h" #include "builtin_string.h" #include "builtin_test.h" +#include "builtin_type.h" #include "builtin_ulimit.h" #include "builtin_wait.h" #include "common.h" @@ -401,6 +402,7 @@ static const builtin_data_t builtin_datas[] = { {L"test", &builtin_test, N_(L"Test a condition")}, {L"time", &builtin_generic, N_(L"Measure how long a command or block takes")}, {L"true", &builtin_true, N_(L"Return a successful result")}, + {L"type", &builtin_type, N_(L"Check if a thing is a thing")}, {L"ulimit", &builtin_ulimit, N_(L"Set or get the shells resource usage limits")}, {L"wait", &builtin_wait, N_(L"Wait for background processes completed")}, {L"while", &builtin_generic, N_(L"Perform a command multiple times")}, diff --git a/src/builtin_type.cpp b/src/builtin_type.cpp new file mode 100644 index 000000000..68aeeaa12 --- /dev/null +++ b/src/builtin_type.cpp @@ -0,0 +1,203 @@ +// Implementation of the type builtin. +#include "config.h" // IWYU pragma: keep + +#include "builtin_type.h" + +#include +#include + +#include "builtin.h" +#include "common.h" +#include "complete.h" +#include "fallback.h" // IWYU pragma: keep +#include "function.h" +#include "highlight.h" +#include "io.h" +#include "parser.h" +#include "path.h" +#include "signal.h" +#include "wcstringutil.h" +#include "wgetopt.h" +#include "wutil.h" // IWYU pragma: keep + +struct type_cmd_opts_t { + bool all = false; + bool short_output = false; + bool no_functions = false; + bool type = false; + bool path = false; + bool force_path = false; + bool print_help = false; + bool query = false; +}; +static const wchar_t *const short_options = L":hasftpPq"; +static const struct woption long_options[] = {{L"help", no_argument, nullptr, 'h'}, + {L"all", no_argument, nullptr, 'a'}, + {L"short", no_argument, nullptr, 's'}, + {L"no-functions", no_argument, nullptr, 'f'}, + {L"type", no_argument, nullptr, 't'}, + {L"path", no_argument, nullptr, 'p'}, + {L"force-path", no_argument, nullptr, 'P'}, + {L"query", no_argument, nullptr, 'q'}, + {nullptr, 0, nullptr, 0}}; + +static int parse_cmd_opts(type_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, nullptr)) != -1) { + switch (opt) { + case 'h': { + opts.print_help = true; + break; + } + case 'a': { + opts.all = true; + break; + } + case 's': { + opts.short_output = true; + break; + } + case 'f': { + opts.no_functions = true; + break; + } + case 't': { + opts.type = true; + break; + } + case 'p': { + opts.path = true; + break; + } + case 'P': { + opts.force_path = true; + break; + } + case 'q': { + opts.query = true; + break; + } + case ':': { + streams.err.append_format(BUILTIN_ERR_MISSING, cmd, argv[w.woptind - 1]); + return STATUS_INVALID_ARGS; + } + case '?': { + streams.err.append_format(BUILTIN_ERR_UNKNOWN, cmd, argv[w.woptind - 1]); + return STATUS_INVALID_ARGS; + } + default: { + DIE("unexpected retval from wgetopt_long"); + } + } + } + + *optind = w.woptind; + return STATUS_CMD_OK; +} + +/// Implementation of the builtin 'type'. +maybe_t builtin_type(parser_t &parser, io_streams_t &streams, wchar_t **argv) { + UNUSED(parser); + const wchar_t *cmd = argv[0]; + int argc = builtin_count_args(argv); + type_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); + return STATUS_CMD_OK; + } + + // Mutually exclusive options + if (opts.query + opts.path + opts.type + opts.force_path > 1) { + streams.err.append_format(BUILTIN_ERR_COMBO, cmd); + return STATUS_INVALID_ARGS; + } + + wcstring_list_t builtins = builtin_get_names(); + bool res = false; + for (int idx = optind; argv[idx]; ++idx) { + int found = 0; + const wchar_t *name = argv[idx]; + // Functions + if (!opts.force_path && !opts.no_functions && function_exists(name, parser)) { + ++found; + res = true; + if (!opts.query && !opts.type) { + streams.out.append_format(_(L"%ls is a function"), name); + auto path = function_get_definition_file(name); + if (opts.path) { + if (path) { + streams.out.append(path); + streams.out.append(L"\n"); + } + } else if (!opts.short_output) { + streams.out.append(_(L" with definition")); + streams.out.append(L"\n"); + // Function path + wcstring def = functions_def(name); + if (path) { + int line_number = function_get_definition_lineno(name); + wcstring comment; + append_format(comment, L"# Defined in %ls @ line %d\n", path, line_number); + def = comment + def; + } + if (!streams.out_is_redirected && isatty(STDOUT_FILENO)) { + std::vector colors; + highlight_shell(def, colors, parser.context()); + streams.out.append(str2wcstring(colorize(def, colors, parser.vars()))); + } else { + streams.out.append(def); + } + } else { + auto path = function_get_definition_file(name); + if (path) { + streams.out.append_format(_(L" (defined in %ls)"), path); + } + streams.out.append(L"\n"); + } + } else if (opts.type) { + streams.out.append(L"function\n"); + } + if (!opts.all) continue; + } + + // Builtins + if (!opts.force_path && contains(builtins, name)) { + ++found; + res = true; + if (!opts.query && !opts.type) { + streams.out.append_format(_(L"%ls is a builtin\n"), name); + } else if (opts.type) { + streams.out.append(_(L"builtin\n")); + } + if (!opts.all) continue; + } + + // Commands + wcstring_list_t paths = path_get_paths(name, parser.vars()); + for (const auto &path : paths) { + ++found; + res = true; + if (!opts.query && !opts.type) { + streams.out.append_format(L"%ls is %ls\n", name, path.c_str()); + } else if (opts.type) { + streams.out.append(_(L"file\n")); + break; + } + if (!opts.all) break; + } + + if (!found && !opts.query && !opts.path) { + streams.err.append_format(_(L"%ls: Could not find '%ls'\n"), L"type", name); + } + } + + return res ? STATUS_CMD_OK : STATUS_CMD_ERROR; +} diff --git a/src/builtin_type.h b/src/builtin_type.h new file mode 100644 index 000000000..99a32ba0a --- /dev/null +++ b/src/builtin_type.h @@ -0,0 +1,11 @@ +// Prototypes for executing builtin_type function. +#ifndef FISH_BUILTIN_TYPE_H +#define FISH_BUILTIN_TYPE_H + +#include "maybe.h" + +class parser_t; +struct io_streams_t; + +maybe_t builtin_type(parser_t &parser, io_streams_t &streams, wchar_t **argv); +#endif