From b65a53a2a6d8892bd9bba7de8dea7f55743399f3 Mon Sep 17 00:00:00 2001 From: Fabian Boehm Date: Fri, 14 Apr 2023 18:12:46 +0200 Subject: [PATCH] Rewrite "command" builtin in Rust This is basically a subset of type, so we might as well. To be clear this is `command -s` and friends, if you do `command grep` that's handled as a keyword. One issue here is that we can't get "one path or not" because I don't know how to translate a maybe_t? Do we need to make it a shared_ptr instead? --- CMakeLists.txt | 2 +- fish-rust/src/builtins/command.rs | 99 ++++++++++++++++++++++++++ fish-rust/src/builtins/mod.rs | 1 + fish-rust/src/builtins/shared.rs | 1 + src/builtin.cpp | 6 +- src/builtin.h | 1 + src/builtins/command.cpp | 112 ------------------------------ src/builtins/command.h | 11 --- 8 files changed, 107 insertions(+), 126 deletions(-) create mode 100644 fish-rust/src/builtins/command.rs delete mode 100644 src/builtins/command.cpp delete mode 100644 src/builtins/command.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d4699a7dc..13c3d9531 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,7 +100,7 @@ endif() # List of sources for builtin functions. set(FISH_BUILTIN_SRCS src/builtin.cpp src/builtins/argparse.cpp src/builtins/bind.cpp - src/builtins/builtin.cpp src/builtins/cd.cpp src/builtins/command.cpp + src/builtins/builtin.cpp src/builtins/cd.cpp src/builtins/commandline.cpp src/builtins/complete.cpp src/builtins/disown.cpp src/builtins/eval.cpp src/builtins/fg.cpp diff --git a/fish-rust/src/builtins/command.rs b/fish-rust/src/builtins/command.rs new file mode 100644 index 000000000..dbf65a81f --- /dev/null +++ b/fish-rust/src/builtins/command.rs @@ -0,0 +1,99 @@ +use libc::c_int; + +use crate::builtins::shared::{ + builtin_missing_argument, builtin_print_help, builtin_unknown_option, io_streams_t, + STATUS_CMD_OK, STATUS_CMD_UNKNOWN, STATUS_INVALID_ARGS, +}; +use crate::ffi::parser_t; +use crate::ffi::path_get_paths_ffi; +use crate::wchar::{wstr, WString, L}; +use crate::wchar_ffi::WCharFromFFI; +use crate::wchar_ffi::WCharToFFI; +use crate::wgetopt::{wgetopter_t, wopt, woption, woption_argument_t}; +use crate::wutil::sprintf; + +#[derive(Default)] +struct command_cmd_opts_t { + all: bool, + quiet: bool, + find_path: bool, +} + +pub fn r#command( + parser: &mut parser_t, + streams: &mut io_streams_t, + argv: &mut [&wstr], +) -> Option { + let cmd = argv[0]; + let argc = argv.len(); + let print_hints = false; + let mut opts: command_cmd_opts_t = Default::default(); + + const shortopts: &wstr = L!(":hasqv"); + const longopts: &[woption] = &[ + wopt(L!("help"), woption_argument_t::no_argument, 'h'), + wopt(L!("all"), woption_argument_t::no_argument, 'a'), + wopt(L!("query"), woption_argument_t::no_argument, 'q'), + wopt(L!("quiet"), woption_argument_t::no_argument, 'q'), + wopt(L!("search"), woption_argument_t::no_argument, 's'), + ]; + + let mut w = wgetopter_t::new(shortopts, longopts, argv); + while let Some(c) = w.wgetopt_long() { + match c { + 'a' => opts.all = true, + 'q' => opts.quiet = true, + 's' => opts.find_path = true, + // -s and -v are aliases + 'v' => opts.find_path = true, + 'h' => { + builtin_print_help(parser, streams, cmd); + return STATUS_CMD_OK; + } + ':' => { + builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1], print_hints); + return STATUS_INVALID_ARGS; + } + '?' => { + builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1], print_hints); + return STATUS_INVALID_ARGS; + } + _ => { + panic!("unexpected retval from wgeopter.next()"); + } + } + } + + // Quiet implies find_path. + if !opts.find_path && !opts.all && !opts.quiet { + builtin_print_help(parser, streams, cmd); + return STATUS_INVALID_ARGS; + } + + let mut res = false; + let optind = w.woptind; + for arg in argv.iter().take(argc).skip(optind) { + // TODO: This always gets all paths, and then skips a bunch. + // For the common case, we want to get just the one path. + // Port this over once path.cpp is. + let paths: Vec = path_get_paths_ffi(&arg.to_ffi(), parser).from_ffi(); + + for path in paths.iter() { + res = true; + if opts.quiet { + return STATUS_CMD_OK; + } + + streams.out.append(sprintf!("%ls\n", path)); + if !opts.all { + break; + } + } + } + + if res { + STATUS_CMD_OK + } else { + STATUS_CMD_UNKNOWN + } +} diff --git a/fish-rust/src/builtins/mod.rs b/fish-rust/src/builtins/mod.rs index d032ebfe9..bb2ffa444 100644 --- a/fish-rust/src/builtins/mod.rs +++ b/fish-rust/src/builtins/mod.rs @@ -3,6 +3,7 @@ pub mod abbr; pub mod bg; pub mod block; +pub mod command; pub mod contains; pub mod echo; pub mod emit; diff --git a/fish-rust/src/builtins/shared.rs b/fish-rust/src/builtins/shared.rs index 33da09fbe..ef0c8a5f5 100644 --- a/fish-rust/src/builtins/shared.rs +++ b/fish-rust/src/builtins/shared.rs @@ -148,6 +148,7 @@ pub fn run_builtin( RustBuiltin::Bg => super::bg::bg(parser, streams, args), RustBuiltin::Block => super::block::block(parser, streams, args), RustBuiltin::Contains => super::contains::contains(parser, streams, args), + RustBuiltin::Command => super::command::command(parser, streams, args), RustBuiltin::Echo => super::echo::echo(parser, streams, args), RustBuiltin::Emit => super::emit::emit(parser, streams, args), RustBuiltin::Exit => super::exit::exit(parser, streams, args), diff --git a/src/builtin.cpp b/src/builtin.cpp index 2a89f8be6..db0019652 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -33,7 +33,6 @@ #include "builtins/bind.h" #include "builtins/builtin.h" #include "builtins/cd.h" -#include "builtins/command.h" #include "builtins/commandline.h" #include "builtins/complete.h" #include "builtins/disown.h" @@ -365,7 +364,7 @@ static constexpr builtin_data_t builtin_datas[] = { {L"builtin", &builtin_builtin, N_(L"Run a builtin specifically")}, {L"case", &builtin_generic, N_(L"Block of code to run conditionally")}, {L"cd", &builtin_cd, N_(L"Change working directory")}, - {L"command", &builtin_command, N_(L"Run a command specifically")}, + {L"command", &implemented_in_rust, N_(L"Run a command specifically")}, {L"commandline", &builtin_commandline, N_(L"Set or get the commandline")}, {L"complete", &builtin_complete, N_(L"Edit command specific completions")}, {L"contains", &implemented_in_rust, N_(L"Search for a specified string in a list")}, @@ -535,6 +534,9 @@ static maybe_t try_get_rust_builtin(const wcstring &cmd) { if (cmd == L"contains") { return RustBuiltin::Contains; } + if (cmd == L"command") { + return RustBuiltin::Command; + } if (cmd == L"echo") { return RustBuiltin::Echo; } diff --git a/src/builtin.h b/src/builtin.h index d015126b9..5894fd2c8 100644 --- a/src/builtin.h +++ b/src/builtin.h @@ -113,6 +113,7 @@ enum RustBuiltin : int32_t { Bg, Block, Contains, + Command, Echo, Emit, Exit, diff --git a/src/builtins/command.cpp b/src/builtins/command.cpp deleted file mode 100644 index 91dd8ce1e..000000000 --- a/src/builtins/command.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// Implementation of the command builtin. -#include "config.h" // IWYU pragma: keep - -#include "command.h" - -#include - -#include "../builtin.h" -#include "../common.h" -#include "../env.h" -#include "../fallback.h" // IWYU pragma: keep -#include "../io.h" -#include "../maybe.h" -#include "../parser.h" -#include "../path.h" -#include "../wgetopt.h" -#include "../wutil.h" // IWYU pragma: keep - -struct command_cmd_opts_t { - bool print_help = false; - bool find_path = false; - bool quiet = false; - bool all_paths = false; -}; -static const wchar_t *const short_options = L":ahqsv"; -static const struct woption long_options[] = { - {L"help", no_argument, 'h'}, {L"all", no_argument, 'a'}, {L"quiet", no_argument, 'q'}, - {L"query", no_argument, 'q'}, {L"search", no_argument, 's'}, {}}; - -static int parse_cmd_opts(command_cmd_opts_t &opts, int *optind, int argc, const wchar_t **argv, - parser_t &parser, io_streams_t &streams) { - const 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 'a': { - opts.all_paths = true; - break; - } - case 'h': { - opts.print_help = true; - break; - } - case 'q': { - opts.quiet = true; - break; - } - case 's': // -s and -v are aliases - case 'v': { - opts.find_path = true; - break; - } - case ':': { - builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]); - return STATUS_INVALID_ARGS; - } - 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; -} - -/// Implementation of the builtin 'command'. Actual command running is handled by the parser, this -/// just processes the flags. -maybe_t builtin_command(parser_t &parser, io_streams_t &streams, const 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); - return STATUS_CMD_OK; - } - - // Quiet implies find_path. - if (!opts.find_path && !opts.all_paths && !opts.quiet) { - builtin_print_help(parser, streams, cmd); - 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, parser.vars()); - for (const auto &path : paths) { - if (!opts.quiet) streams.out.append_format(L"%ls\n", path.c_str()); - ++found; - } - } else { // Either find_path explicitly or just quiet. - if (auto path = path_get_path(command_name, parser.vars())) { - if (!opts.quiet) streams.out.append_format(L"%ls\n", path->c_str()); - ++found; - } - } - } - - return found ? STATUS_CMD_OK : STATUS_CMD_UNKNOWN; -} diff --git a/src/builtins/command.h b/src/builtins/command.h deleted file mode 100644 index 833363304..000000000 --- a/src/builtins/command.h +++ /dev/null @@ -1,11 +0,0 @@ -// Prototypes for executing builtin_command function. -#ifndef FISH_BUILTIN_COMMAND_H -#define FISH_BUILTIN_COMMAND_H - -#include "../maybe.h" - -class parser_t; -struct io_streams_t; - -maybe_t builtin_command(parser_t &parser, io_streams_t &streams, const wchar_t **argv); -#endif