mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-11-25 08:56:43 +08:00
Port random to rust
This commit is contained in:
parent
bc7c29d597
commit
4fd1458d85
|
@ -107,7 +107,7 @@ set(FISH_BUILTIN_SRCS
|
||||||
src/builtins/eval.cpp src/builtins/fg.cpp
|
src/builtins/eval.cpp src/builtins/fg.cpp
|
||||||
src/builtins/function.cpp src/builtins/functions.cpp src/builtins/history.cpp
|
src/builtins/function.cpp src/builtins/functions.cpp src/builtins/history.cpp
|
||||||
src/builtins/jobs.cpp src/builtins/math.cpp src/builtins/printf.cpp src/builtins/path.cpp
|
src/builtins/jobs.cpp src/builtins/math.cpp src/builtins/printf.cpp src/builtins/path.cpp
|
||||||
src/builtins/pwd.cpp src/builtins/random.cpp src/builtins/read.cpp
|
src/builtins/pwd.cpp src/builtins/read.cpp
|
||||||
src/builtins/realpath.cpp src/builtins/set.cpp
|
src/builtins/realpath.cpp src/builtins/set.cpp
|
||||||
src/builtins/set_color.cpp src/builtins/source.cpp src/builtins/status.cpp
|
src/builtins/set_color.cpp src/builtins/source.cpp src/builtins/status.cpp
|
||||||
src/builtins/string.cpp src/builtins/test.cpp src/builtins/type.cpp src/builtins/ulimit.cpp
|
src/builtins/string.cpp src/builtins/test.cpp src/builtins/type.cpp src/builtins/ulimit.cpp
|
||||||
|
|
37
fish-rust/Cargo.lock
generated
37
fish-rust/Cargo.lock
generated
|
@ -358,6 +358,7 @@ dependencies = [
|
||||||
"nix",
|
"nix",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"rand",
|
||||||
"unixstring",
|
"unixstring",
|
||||||
"widestring",
|
"widestring",
|
||||||
"widestring-suffix",
|
"widestring-suffix",
|
||||||
|
@ -658,6 +659,12 @@ version = "0.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prettyplease"
|
name = "prettyplease"
|
||||||
version = "0.1.23"
|
version = "0.1.23"
|
||||||
|
@ -710,6 +717,36 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
|
|
|
@ -15,6 +15,7 @@ libc = "0.2.137"
|
||||||
nix = { version = "0.25.0", default-features = false, features = [] }
|
nix = { version = "0.25.0", default-features = false, features = [] }
|
||||||
num-traits = "0.2.15"
|
num-traits = "0.2.15"
|
||||||
once_cell = "1.17.0"
|
once_cell = "1.17.0"
|
||||||
|
rand = { version = "0.8.5", features = ["small_rng"] }
|
||||||
unixstring = "0.2.7"
|
unixstring = "0.2.7"
|
||||||
widestring = "1.0.2"
|
widestring = "1.0.2"
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ pub mod shared;
|
||||||
|
|
||||||
pub mod echo;
|
pub mod echo;
|
||||||
pub mod emit;
|
pub mod emit;
|
||||||
|
mod exit;
|
||||||
|
pub mod random;
|
||||||
pub mod r#return;
|
pub mod r#return;
|
||||||
pub mod wait;
|
pub mod wait;
|
||||||
mod exit;
|
|
||||||
|
|
188
fish-rust/src/builtins/random.rs
Normal file
188
fish-rust/src/builtins/random.rs
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
use libc::c_int;
|
||||||
|
|
||||||
|
use crate::builtins::shared::{
|
||||||
|
builtin_missing_argument, builtin_print_help, builtin_unknown_option, io_streams_t,
|
||||||
|
STATUS_CMD_OK, STATUS_INVALID_ARGS,
|
||||||
|
};
|
||||||
|
use crate::ffi::parser_t;
|
||||||
|
use crate::wchar::{widestrs, wstr};
|
||||||
|
use crate::wgetopt::{wgetopter_t, wopt, woption, woption_argument_t};
|
||||||
|
use crate::wutil::{self, fish_wcstoi_radix_all, format::printf::sprintf, wgettext_fmt};
|
||||||
|
use num_traits::PrimInt;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use rand::rngs::SmallRng;
|
||||||
|
use rand::{Rng, SeedableRng};
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
static seeded_engine: Lazy<Mutex<SmallRng>> = Lazy::new(|| Mutex::new(SmallRng::from_entropy()));
|
||||||
|
|
||||||
|
#[widestrs]
|
||||||
|
pub fn random(
|
||||||
|
parser: &mut parser_t,
|
||||||
|
streams: &mut io_streams_t,
|
||||||
|
argv: &mut [&wstr],
|
||||||
|
) -> Option<c_int> {
|
||||||
|
let cmd = argv[0];
|
||||||
|
let argc = argv.len();
|
||||||
|
let print_hints = false;
|
||||||
|
|
||||||
|
const shortopts: &wstr = "+:h"L;
|
||||||
|
const longopts: &[woption] = &[wopt("help"L, woption_argument_t::no_argument, 'h')];
|
||||||
|
|
||||||
|
let mut w = wgetopter_t::new(shortopts, longopts, argv);
|
||||||
|
while let Some(c) = w.wgetopt_long() {
|
||||||
|
match c {
|
||||||
|
'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()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut engine = seeded_engine.lock().unwrap();
|
||||||
|
let mut start = 0;
|
||||||
|
let mut end = 32767;
|
||||||
|
let mut step = 1;
|
||||||
|
let arg_count = argc - w.woptind;
|
||||||
|
let i = w.woptind;
|
||||||
|
if arg_count >= 1 && argv[i] == "choice" {
|
||||||
|
if arg_count == 1 {
|
||||||
|
streams
|
||||||
|
.err
|
||||||
|
.append(wgettext_fmt!("%ls: nothing to choose from\n", cmd,));
|
||||||
|
return STATUS_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rand = engine.gen_range(0..arg_count - 1);
|
||||||
|
streams.out.append(sprintf!("%ls\n"L, argv[i + 1 + rand]));
|
||||||
|
return STATUS_CMD_OK;
|
||||||
|
}
|
||||||
|
fn parse<T: PrimInt>(
|
||||||
|
streams: &mut io_streams_t,
|
||||||
|
cmd: &wstr,
|
||||||
|
num: &wstr,
|
||||||
|
) -> Result<T, wutil::Error> {
|
||||||
|
let res = fish_wcstoi_radix_all(num.chars(), None, true);
|
||||||
|
if res.is_err() {
|
||||||
|
streams
|
||||||
|
.err
|
||||||
|
.append(wgettext_fmt!("%ls: %ls: invalid integer\n", cmd, num,));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
match arg_count {
|
||||||
|
0 => {
|
||||||
|
// Keep the defaults
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
// Seed the engine persistently
|
||||||
|
let num = parse::<i64>(streams, cmd, argv[i]);
|
||||||
|
match num {
|
||||||
|
Err(_) => return STATUS_INVALID_ARGS,
|
||||||
|
Ok(x) => *engine = SmallRng::seed_from_u64(x as u64),
|
||||||
|
}
|
||||||
|
return STATUS_CMD_OK;
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
// start is first, end is second
|
||||||
|
match parse::<i64>(streams, cmd, argv[i]) {
|
||||||
|
Err(_) => return STATUS_INVALID_ARGS,
|
||||||
|
Ok(x) => start = x,
|
||||||
|
}
|
||||||
|
|
||||||
|
match parse::<i64>(streams, cmd, argv[i + 1]) {
|
||||||
|
Err(_) => return STATUS_INVALID_ARGS,
|
||||||
|
Ok(x) => end = x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
3 => {
|
||||||
|
// start, step, end
|
||||||
|
match parse::<i64>(streams, cmd, argv[i]) {
|
||||||
|
Err(_) => return STATUS_INVALID_ARGS,
|
||||||
|
Ok(x) => start = x,
|
||||||
|
}
|
||||||
|
|
||||||
|
// start, step, end
|
||||||
|
match parse::<u64>(streams, cmd, argv[i + 1]) {
|
||||||
|
Err(_) => return STATUS_INVALID_ARGS,
|
||||||
|
Ok(0) => {
|
||||||
|
streams
|
||||||
|
.err
|
||||||
|
.append(wgettext_fmt!("%ls: STEP must be a positive integer\n", cmd,));
|
||||||
|
return STATUS_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
Ok(x) => step = x,
|
||||||
|
}
|
||||||
|
|
||||||
|
match parse::<i64>(streams, cmd, argv[i + 2]) {
|
||||||
|
Err(_) => return STATUS_INVALID_ARGS,
|
||||||
|
Ok(x) => end = x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
streams
|
||||||
|
.err
|
||||||
|
.append(wgettext_fmt!("%ls: too many arguments\n", cmd,));
|
||||||
|
return Some(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if end <= start {
|
||||||
|
streams
|
||||||
|
.err
|
||||||
|
.append(wgettext_fmt!("%ls: END must be greater than START\n", cmd,));
|
||||||
|
return STATUS_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Possibilities can be abs(i64::MIN) + i64::MAX,
|
||||||
|
// so we do this as i128
|
||||||
|
let possibilities = (end as i128 - start as i128) / (step as i128);
|
||||||
|
|
||||||
|
if possibilities == 0 {
|
||||||
|
streams.err.append(wgettext_fmt!(
|
||||||
|
"%ls: range contains only one possible value\n",
|
||||||
|
cmd,
|
||||||
|
));
|
||||||
|
return STATUS_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rand = engine.gen_range(0..=possibilities);
|
||||||
|
|
||||||
|
let result = start as i128 + rand as i128 * step as i128;
|
||||||
|
|
||||||
|
// We do our math as i128,
|
||||||
|
// and then we check if it fits in 64 bit - signed or unsigned!
|
||||||
|
match i64::try_from(result) {
|
||||||
|
Ok(x) => {
|
||||||
|
streams.out.append(sprintf!("%d\n"L, x));
|
||||||
|
return STATUS_CMD_OK;
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
match u64::try_from(result) {
|
||||||
|
Ok(x) => {
|
||||||
|
streams.out.append(sprintf!("%d\n"L, x));
|
||||||
|
return STATUS_CMD_OK;
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
streams.err.append(wgettext_fmt!(
|
||||||
|
"%ls: range contains only one possible value\n",
|
||||||
|
cmd,
|
||||||
|
));
|
||||||
|
return STATUS_INVALID_ARGS;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -118,6 +118,7 @@ pub fn run_builtin(
|
||||||
RustBuiltin::Echo => super::echo::echo(parser, streams, args),
|
RustBuiltin::Echo => super::echo::echo(parser, streams, args),
|
||||||
RustBuiltin::Emit => super::emit::emit(parser, streams, args),
|
RustBuiltin::Emit => super::emit::emit(parser, streams, args),
|
||||||
RustBuiltin::Exit => super::exit::exit(parser, streams, args),
|
RustBuiltin::Exit => super::exit::exit(parser, streams, args),
|
||||||
|
RustBuiltin::Random => super::random::random(parser, streams, args),
|
||||||
RustBuiltin::Return => super::r#return::r#return(parser, streams, args),
|
RustBuiltin::Return => super::r#return::r#return(parser, streams, args),
|
||||||
RustBuiltin::Wait => wait::wait(parser, streams, args),
|
RustBuiltin::Wait => wait::wait(parser, streams, args),
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,11 +106,19 @@ where
|
||||||
negative = false;
|
negative = false;
|
||||||
}
|
}
|
||||||
let consumed_all = chars.peek() == None;
|
let consumed_all = chars.peek() == None;
|
||||||
Ok(ParseResult { result, negative, consumed_all })
|
Ok(ParseResult {
|
||||||
|
result,
|
||||||
|
negative,
|
||||||
|
consumed_all,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse some iterator over Chars into some Integer type, optionally with a radix.
|
/// Parse some iterator over Chars into some Integer type, optionally with a radix.
|
||||||
fn fish_wcstoi_impl<Int, Chars>(src: Chars, mradix: Option<u32>, consume_all: bool) -> Result<Int, Error>
|
fn fish_wcstoi_impl<Int, Chars>(
|
||||||
|
src: Chars,
|
||||||
|
mradix: Option<u32>,
|
||||||
|
consume_all: bool,
|
||||||
|
) -> Result<Int, Error>
|
||||||
where
|
where
|
||||||
Chars: Iterator<Item = char>,
|
Chars: Iterator<Item = char>,
|
||||||
Int: PrimInt,
|
Int: PrimInt,
|
||||||
|
@ -120,7 +128,10 @@ where
|
||||||
let signed = Int::min_value() < Int::zero();
|
let signed = Int::min_value() < Int::zero();
|
||||||
|
|
||||||
let ParseResult {
|
let ParseResult {
|
||||||
result, negative, consumed_all, ..
|
result,
|
||||||
|
negative,
|
||||||
|
consumed_all,
|
||||||
|
..
|
||||||
} = fish_parse_radix(src, mradix)?;
|
} = fish_parse_radix(src, mradix)?;
|
||||||
|
|
||||||
if !signed && negative {
|
if !signed && negative {
|
||||||
|
@ -169,7 +180,11 @@ where
|
||||||
fish_wcstoi_impl(src, Some(radix), false)
|
fish_wcstoi_impl(src, Some(radix), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fish_wcstoi_radix_all<Int, Chars>(src: Chars, radix: Option<u32>, consume_all: bool) -> Result<Int, Error>
|
pub fn fish_wcstoi_radix_all<Int, Chars>(
|
||||||
|
src: Chars,
|
||||||
|
radix: Option<u32>,
|
||||||
|
consume_all: bool,
|
||||||
|
) -> Result<Int, Error>
|
||||||
where
|
where
|
||||||
Chars: Iterator<Item = char>,
|
Chars: Iterator<Item = char>,
|
||||||
Int: PrimInt,
|
Int: PrimInt,
|
||||||
|
|
|
@ -50,7 +50,6 @@
|
||||||
#include "builtins/path.h"
|
#include "builtins/path.h"
|
||||||
#include "builtins/printf.h"
|
#include "builtins/printf.h"
|
||||||
#include "builtins/pwd.h"
|
#include "builtins/pwd.h"
|
||||||
#include "builtins/random.h"
|
|
||||||
#include "builtins/read.h"
|
#include "builtins/read.h"
|
||||||
#include "builtins/realpath.h"
|
#include "builtins/realpath.h"
|
||||||
#include "builtins/set.h"
|
#include "builtins/set.h"
|
||||||
|
@ -401,7 +400,7 @@ static constexpr builtin_data_t builtin_datas[] = {
|
||||||
{L"path", &builtin_path, N_(L"Handle paths")},
|
{L"path", &builtin_path, N_(L"Handle paths")},
|
||||||
{L"printf", &builtin_printf, N_(L"Prints formatted text")},
|
{L"printf", &builtin_printf, N_(L"Prints formatted text")},
|
||||||
{L"pwd", &builtin_pwd, N_(L"Print the working directory")},
|
{L"pwd", &builtin_pwd, N_(L"Print the working directory")},
|
||||||
{L"random", &builtin_random, N_(L"Generate random number")},
|
{L"random", &implemented_in_rust, N_(L"Generate random number")},
|
||||||
{L"read", &builtin_read, N_(L"Read a line of input into variables")},
|
{L"read", &builtin_read, N_(L"Read a line of input into variables")},
|
||||||
{L"realpath", &builtin_realpath, N_(L"Show absolute path sans symlinks")},
|
{L"realpath", &builtin_realpath, N_(L"Show absolute path sans symlinks")},
|
||||||
{L"return", &implemented_in_rust, N_(L"Stop the currently evaluated function")},
|
{L"return", &implemented_in_rust, N_(L"Stop the currently evaluated function")},
|
||||||
|
@ -534,6 +533,9 @@ static maybe_t<RustBuiltin> try_get_rust_builtin(const wcstring &cmd) {
|
||||||
if (cmd == L"exit") {
|
if (cmd == L"exit") {
|
||||||
return RustBuiltin::Exit;
|
return RustBuiltin::Exit;
|
||||||
}
|
}
|
||||||
|
if (cmd == L"random") {
|
||||||
|
return RustBuiltin::Random;
|
||||||
|
}
|
||||||
if (cmd == L"wait") {
|
if (cmd == L"wait") {
|
||||||
return RustBuiltin::Wait;
|
return RustBuiltin::Wait;
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,7 +112,8 @@ enum RustBuiltin : int32_t {
|
||||||
Echo,
|
Echo,
|
||||||
Emit,
|
Emit,
|
||||||
Exit,
|
Exit,
|
||||||
Wait,
|
Random,
|
||||||
Return,
|
Return,
|
||||||
|
Wait,
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,160 +0,0 @@
|
||||||
// Implementation of the random builtin.
|
|
||||||
#include "config.h" // IWYU pragma: keep
|
|
||||||
|
|
||||||
#include "random.h"
|
|
||||||
|
|
||||||
#include <cerrno>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cwchar>
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
#include "../builtin.h"
|
|
||||||
#include "../common.h"
|
|
||||||
#include "../fallback.h" // IWYU pragma: keep
|
|
||||||
#include "../io.h"
|
|
||||||
#include "../maybe.h"
|
|
||||||
#include "../wutil.h" // IWYU pragma: keep
|
|
||||||
|
|
||||||
/// \return a random-seeded engine.
|
|
||||||
static std::minstd_rand get_seeded_engine() {
|
|
||||||
std::minstd_rand engine;
|
|
||||||
// seed engine with 2*32 bits of random data
|
|
||||||
// for the 64 bits of internal state of minstd_rand
|
|
||||||
std::random_device rd;
|
|
||||||
std::seed_seq seed{rd(), rd()};
|
|
||||||
engine.seed(seed);
|
|
||||||
return engine;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The random builtin generates random numbers.
|
|
||||||
maybe_t<int> builtin_random(parser_t &parser, io_streams_t &streams, const wchar_t **argv) {
|
|
||||||
const wchar_t *cmd = argv[0];
|
|
||||||
int argc = builtin_count_args(argv);
|
|
||||||
help_only_cmd_opts_t opts;
|
|
||||||
|
|
||||||
int optind;
|
|
||||||
int retval = parse_help_only_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have a single engine which we lazily seed. Lock it here.
|
|
||||||
static owning_lock<std::minstd_rand> s_engine{get_seeded_engine()};
|
|
||||||
auto engine_lock = s_engine.acquire();
|
|
||||||
std::minstd_rand &engine = *engine_lock;
|
|
||||||
|
|
||||||
int arg_count = argc - optind;
|
|
||||||
long long start, end;
|
|
||||||
unsigned long long step;
|
|
||||||
bool choice = false;
|
|
||||||
if (arg_count >= 1 && !std::wcscmp(argv[optind], L"choice")) {
|
|
||||||
if (arg_count == 1) {
|
|
||||||
streams.err.append_format(L"%ls: nothing to choose from\n", cmd);
|
|
||||||
return STATUS_INVALID_ARGS;
|
|
||||||
}
|
|
||||||
choice = true;
|
|
||||||
start = 1;
|
|
||||||
step = 1;
|
|
||||||
end = arg_count - 1;
|
|
||||||
} else {
|
|
||||||
bool parse_error = false;
|
|
||||||
auto parse_ll = [&](const wchar_t *str) {
|
|
||||||
long long ll = fish_wcstoll(str);
|
|
||||||
if (errno) {
|
|
||||||
streams.err.append_format(BUILTIN_ERR_NOT_NUMBER, cmd, str);
|
|
||||||
parse_error = true;
|
|
||||||
}
|
|
||||||
return ll;
|
|
||||||
};
|
|
||||||
auto parse_ull = [&](const wchar_t *str) {
|
|
||||||
unsigned long long ull = fish_wcstoull(str);
|
|
||||||
if (errno) {
|
|
||||||
streams.err.append_format(BUILTIN_ERR_NOT_NUMBER, cmd, str);
|
|
||||||
parse_error = true;
|
|
||||||
}
|
|
||||||
return ull;
|
|
||||||
};
|
|
||||||
if (arg_count == 0) {
|
|
||||||
start = 0;
|
|
||||||
end = 32767;
|
|
||||||
step = 1;
|
|
||||||
} else if (arg_count == 1) {
|
|
||||||
long long seed = parse_ll(argv[optind]);
|
|
||||||
if (parse_error) return STATUS_INVALID_ARGS;
|
|
||||||
engine.seed(static_cast<uint32_t>(seed));
|
|
||||||
return STATUS_CMD_OK;
|
|
||||||
} else if (arg_count == 2) {
|
|
||||||
start = parse_ll(argv[optind]);
|
|
||||||
step = 1;
|
|
||||||
end = parse_ll(argv[optind + 1]);
|
|
||||||
} else if (arg_count == 3) {
|
|
||||||
start = parse_ll(argv[optind]);
|
|
||||||
step = parse_ull(argv[optind + 1]);
|
|
||||||
end = parse_ll(argv[optind + 2]);
|
|
||||||
} else {
|
|
||||||
streams.err.append_format(BUILTIN_ERR_TOO_MANY_ARGUMENTS, cmd);
|
|
||||||
return STATUS_INVALID_ARGS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parse_error) {
|
|
||||||
return STATUS_INVALID_ARGS;
|
|
||||||
} else if (start >= end) {
|
|
||||||
streams.err.append_format(L"%ls: END must be greater than START\n", cmd);
|
|
||||||
return STATUS_INVALID_ARGS;
|
|
||||||
} else if (step == 0) {
|
|
||||||
streams.err.append_format(L"%ls: STEP must be a positive integer\n", cmd);
|
|
||||||
return STATUS_INVALID_ARGS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// only for negative argument
|
|
||||||
auto safe_abs = [](long long ll) -> unsigned long long {
|
|
||||||
return -static_cast<unsigned long long>(ll);
|
|
||||||
};
|
|
||||||
long long real_end;
|
|
||||||
if (start >= 0 || end < 0) {
|
|
||||||
// 0 <= start <= end
|
|
||||||
long long diff = end - start;
|
|
||||||
// 0 <= diff <= LL_MAX
|
|
||||||
real_end = start + static_cast<long long>(diff / step);
|
|
||||||
} else {
|
|
||||||
// start < 0 <= end
|
|
||||||
unsigned long long abs_start = safe_abs(start);
|
|
||||||
unsigned long long diff = (end + abs_start);
|
|
||||||
real_end = diff / step - abs_start;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!choice && start == real_end) {
|
|
||||||
streams.err.append_format(L"%ls: range contains only one possible value\n", cmd);
|
|
||||||
return STATUS_INVALID_ARGS;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uniform_int_distribution<long long> dist(start, real_end);
|
|
||||||
long long random = dist(engine);
|
|
||||||
long long result;
|
|
||||||
if (start >= 0) {
|
|
||||||
// 0 <= start <= random <= end
|
|
||||||
long long diff = random - start;
|
|
||||||
// 0 < step * diff <= end - start <= LL_MAX
|
|
||||||
result = start + static_cast<long long>(diff * step);
|
|
||||||
} else if (random < 0) {
|
|
||||||
// start <= random < 0
|
|
||||||
long long diff = random - start;
|
|
||||||
result = diff * step - safe_abs(start);
|
|
||||||
} else {
|
|
||||||
// start < 0 <= random
|
|
||||||
unsigned long long abs_start = safe_abs(start);
|
|
||||||
unsigned long long diff = (random + abs_start);
|
|
||||||
result = diff * step - abs_start;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (choice) {
|
|
||||||
streams.out.append_format(L"%ls\n", argv[optind + result]);
|
|
||||||
} else {
|
|
||||||
streams.out.append_format(L"%lld\n", result);
|
|
||||||
}
|
|
||||||
return STATUS_CMD_OK;
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
// Prototypes for executing builtin_random function.
|
|
||||||
#ifndef FISH_BUILTIN_RANDOM_H
|
|
||||||
#define FISH_BUILTIN_RANDOM_H
|
|
||||||
|
|
||||||
#include "../maybe.h"
|
|
||||||
|
|
||||||
class parser_t;
|
|
||||||
struct io_streams_t;
|
|
||||||
|
|
||||||
maybe_t<int> builtin_random(parser_t &parser, io_streams_t &streams, const wchar_t **argv);
|
|
||||||
#endif
|
|
|
@ -40,7 +40,7 @@ random choic a b c
|
||||||
#CHECKERR: random: too many arguments
|
#CHECKERR: random: too many arguments
|
||||||
|
|
||||||
function check_boundaries
|
function check_boundaries
|
||||||
if not test $argv[1] -ge $argv[2] -a $argv[1] -le $argv[3]
|
if not test "$argv[1]" -ge "$argv[2]" -a "$argv[1]" -le "$argv[3]"
|
||||||
printf "Unexpected: %s <= %s <= %s not verified\n" $argv[2] $argv[1] $argv[3] >&2
|
printf "Unexpected: %s <= %s <= %s not verified\n" $argv[2] $argv[1] $argv[3] >&2
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue
Block a user