From 566123edc6c2f0ae5bffa7083694896a39179660 Mon Sep 17 00:00:00 2001 From: Fabian Boehm Date: Fri, 18 Aug 2023 23:18:52 +0200 Subject: [PATCH] Port builtin count to rust (#9963) * Port builtin count to rust * Explicitly use wstring --- fish-rust/src/builtins/count.rs | 33 +++++++++++++++++++++++++ fish-rust/src/builtins/mod.rs | 1 + fish-rust/src/builtins/shared.rs | 1 + src/builtin.cpp | 41 ++++---------------------------- src/builtin.h | 1 + 5 files changed, 40 insertions(+), 37 deletions(-) create mode 100644 fish-rust/src/builtins/count.rs diff --git a/fish-rust/src/builtins/count.rs b/fish-rust/src/builtins/count.rs new file mode 100644 index 000000000..b01fbb0ed --- /dev/null +++ b/fish-rust/src/builtins/count.rs @@ -0,0 +1,33 @@ +use super::prelude::*; + +// How many bytes we read() at once. +// Since this is just for counting, it can be massive. +const COUNT_CHUNK_SIZE: usize = 512 * 256; + +/// Implementation of the builtin count command, used to count the number of arguments sent to it. +pub fn count( + _parser: &mut parser_t, + streams: &mut io_streams_t, + argv: &mut [&wstr], +) -> Option { + // Always add the size of argv (minus 0, which is "count"). + // That means if you call `something | count a b c`, you'll get the count of something _plus 3_. + let mut numargs = argv.len() - 1; + + // (silly variable for Arguments to do nothing with) + let mut zero = 0; + + // Count the newlines coming in via stdin like `wc -l`. + // This means excluding lines that don't end in a newline! + numargs += Arguments::new(&[] as _, &mut zero, streams, COUNT_CHUNK_SIZE) + // second is "want_newline" - whether the line ended in a newline + .filter(|x| x.1) + .count(); + + streams.out.appendln(numargs.to_wstring()); + + if numargs == 0 { + return STATUS_CMD_ERROR; + } + STATUS_CMD_OK +} diff --git a/fish-rust/src/builtins/mod.rs b/fish-rust/src/builtins/mod.rs index bc103161c..c397e7515 100644 --- a/fish-rust/src/builtins/mod.rs +++ b/fish-rust/src/builtins/mod.rs @@ -8,6 +8,7 @@ pub mod builtin; pub mod cd; pub mod command; pub mod contains; +pub mod count; pub mod echo; pub mod emit; pub mod exit; diff --git a/fish-rust/src/builtins/shared.rs b/fish-rust/src/builtins/shared.rs index b6c86415d..661992fe5 100644 --- a/fish-rust/src/builtins/shared.rs +++ b/fish-rust/src/builtins/shared.rs @@ -240,6 +240,7 @@ pub fn run_builtin( RustBuiltin::Cd => super::cd::cd(parser, streams, args), RustBuiltin::Contains => super::contains::contains(parser, streams, args), RustBuiltin::Command => super::command::command(parser, streams, args), + RustBuiltin::Count => super::count::count(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 274b1b209..7fff068c7 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -210,42 +210,6 @@ static maybe_t implemented_in_rust(parser_t &, io_streams_t &, const wchar_ DIE("builtin is implemented in Rust, this should not be called"); } -// How many bytes we read() at once. -// Since this is just for counting, it can be massive. -#define COUNT_CHUNK_SIZE (512 * 256) -/// Implementation of the builtin count command, used to count the number of arguments sent to it. -static maybe_t builtin_count(parser_t &parser, io_streams_t &streams, const wchar_t **argv) { - UNUSED(parser); - int argc = 0; - - // Count the newlines coming in via stdin like `wc -l`. - if (streams.stdin_is_directly_redirected) { - assert(streams.stdin_fd >= 0 && - "Should have a valid fd since stdin is directly redirected"); - char buf[COUNT_CHUNK_SIZE]; - while (true) { - long n = read_blocked(streams.stdin_fd, buf, COUNT_CHUNK_SIZE); - if (n == 0) { - break; - } else if (n < 0) { - wperror(L"read"); - return STATUS_CMD_ERROR; - } - for (int i = 0; i < n; i++) { - if (buf[i] == '\n') { - 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; -} - /// This function handles both the 'continue' and the 'break' builtins that are used for loop /// control. static maybe_t builtin_break_continue(parser_t &parser, io_streams_t &streams, @@ -359,7 +323,7 @@ static constexpr builtin_data_t builtin_datas[] = { {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")}, {L"continue", &builtin_break_continue, N_(L"Skip over remaining innermost loop")}, - {L"count", &builtin_count, N_(L"Count the number of arguments")}, + {L"count", &implemented_in_rust, N_(L"Count the number of arguments")}, {L"disown", &builtin_disown, N_(L"Remove job from job list")}, {L"echo", &implemented_in_rust, N_(L"Print arguments")}, {L"else", &builtin_generic, N_(L"Evaluate block if condition is false")}, @@ -539,6 +503,9 @@ static maybe_t try_get_rust_builtin(const wcstring &cmd) { if (cmd == L"command") { return RustBuiltin::Command; } + if (cmd == L"count") { + return RustBuiltin::Count; + } if (cmd == L"echo") { return RustBuiltin::Echo; } diff --git a/src/builtin.h b/src/builtin.h index e084a90f5..89eca4a50 100644 --- a/src/builtin.h +++ b/src/builtin.h @@ -120,6 +120,7 @@ enum class RustBuiltin : int32_t { Cd, Contains, Command, + Count, Echo, Emit, Exit,