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
This commit is contained in:
Fabian Homborg
2020-09-21 17:45:35 +02:00
parent 7934972751
commit ef9c924960
5 changed files with 217 additions and 132 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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")},

203
src/builtin_type.cpp Normal file
View File

@@ -0,0 +1,203 @@
// Implementation of the type builtin.
#include "config.h" // IWYU pragma: keep
#include "builtin_type.h"
#include <unistd.h>
#include <string>
#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<int> 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<highlight_spec_t> 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;
}

11
src/builtin_type.h Normal file
View File

@@ -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<int> builtin_type(parser_t &parser, io_streams_t &streams, wchar_t **argv);
#endif