From 1c91ec9dfab46fd7e8a94752b880d8a9cd5c650f Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 12 Jun 2017 22:26:24 -0700 Subject: [PATCH] split builtin random into its own module --- Makefile.in | 49 ++++++++--- src/builtin.cpp | 154 +-------------------------------- src/builtin_random.cpp | 191 +++++++++++++++++++++++++++++++++++++++++ src/builtin_random.h | 9 ++ 4 files changed, 238 insertions(+), 165 deletions(-) create mode 100644 src/builtin_random.cpp create mode 100644 src/builtin_random.h diff --git a/Makefile.in b/Makefile.in index e185fa80c..e9de54f84 100644 --- a/Makefile.in +++ b/Makefile.in @@ -101,6 +101,7 @@ HAVE_DOXYGEN=@HAVE_DOXYGEN@ FISH_OBJS := obj/autoload.o obj/builtin.o obj/builtin_bind.o obj/builtin_block.o \ obj/builtin_commandline.o obj/builtin_emit.o obj/builtin_functions.o \ obj/builtin_history.o obj/builtin_status.o obj/builtin_read.o \ + obj/builtin_random.o \ obj/builtin_complete.o obj/builtin_jobs.o obj/builtin_printf.o \ obj/builtin_set.o obj/builtin_set_color.o obj/builtin_string.o \ obj/builtin_test.o obj/builtin_ulimit.o obj/color.o obj/common.o \ @@ -946,17 +947,18 @@ v = $(V$(V)) obj/autoload.o: config.h src/autoload.h src/common.h src/fallback.h obj/autoload.o: src/signal.h src/lru.h src/env.h src/exec.h src/wutil.h obj/builtin.o: config.h src/signal.h src/builtin.h src/common.h -obj/builtin.o: src/fallback.h src/builtin_bind.h src/builtin_block.h src/builtin_functions.h -obj/builtin.o: src/builtin_commandline.h src/builtin_complete.h src/builtin_history.h -obj/builtin.o: src/builtin_emit.h src/builtin_jobs.h src/builtin_printf.h -obj/builtin.o: src/builtin_set.h src/builtin_set_color.h src/builtin_string.h -obj/builtin.o: src/builtin_test.h src/builtin_ulimit.h src/complete.h -obj/builtin.o: src/env.h src/event.h src/exec.h src/expand.h -obj/builtin.o: src/parse_constants.h src/function.h src/highlight.h -obj/builtin.o: src/color.h src/history.h src/wutil.h src/intern.h src/io.h -obj/builtin.o: src/parse_util.h src/tokenizer.h src/parser.h src/parse_tree.h +obj/builtin.o: src/fallback.h src/builtin_bind.h src/builtin_block.h +obj/builtin.o: src/builtin_commandline.h src/builtin_complete.h +obj/builtin.o: src/builtin_emit.h src/builtin_functions.h +obj/builtin.o: src/builtin_history.h src/builtin_jobs.h src/builtin_printf.h +obj/builtin.o: src/builtin_random.h src/builtin_read.h src/builtin_set.h +obj/builtin.o: src/builtin_set_color.h src/builtin_status.h +obj/builtin.o: src/builtin_string.h src/builtin_test.h src/builtin_ulimit.h +obj/builtin.o: src/complete.h src/env.h src/event.h src/exec.h src/function.h +obj/builtin.o: src/intern.h src/io.h src/parse_constants.h src/parse_util.h +obj/builtin.o: src/tokenizer.h src/parser.h src/expand.h src/parse_tree.h obj/builtin.o: src/proc.h src/parser_keywords.h src/path.h src/reader.h -obj/builtin.o: src/wcstringutil.h src/wgetopt.h +obj/builtin.o: src/highlight.h src/color.h src/wgetopt.h src/wutil.h obj/builtin_bind.o: config.h src/builtin.h src/common.h src/fallback.h obj/builtin_bind.o: src/signal.h src/builtin_bind.h src/input.h src/env.h obj/builtin_bind.o: src/io.h src/wgetopt.h src/wutil.h @@ -980,15 +982,33 @@ obj/builtin_complete.o: src/parse_tree.h src/proc.h src/reader.h obj/builtin_complete.o: src/highlight.h src/color.h src/wgetopt.h src/wutil.h obj/builtin_emit.o: config.h src/builtin.h src/common.h src/fallback.h obj/builtin_emit.o: src/signal.h src/builtin_emit.h src/event.h src/io.h -obj/builtin_emit.o: src/parser.h src/expand.h src/parse_constants.h -obj/builtin_emit.o: src/parse_tree.h src/tokenizer.h src/proc.h obj/builtin_emit.o: src/wgetopt.h src/wutil.h +obj/builtin_functions.o: config.h src/builtin.h src/common.h src/fallback.h +obj/builtin_functions.o: src/signal.h src/builtin_functions.h src/env.h +obj/builtin_functions.o: src/event.h src/function.h src/io.h +obj/builtin_functions.o: src/parser_keywords.h src/proc.h src/parse_tree.h +obj/builtin_functions.o: src/parse_constants.h src/tokenizer.h src/wgetopt.h +obj/builtin_functions.o: src/wutil.h +obj/builtin_history.o: config.h src/builtin.h src/common.h src/fallback.h +obj/builtin_history.o: src/signal.h src/builtin_history.h src/history.h +obj/builtin_history.o: src/wutil.h src/io.h src/reader.h src/complete.h +obj/builtin_history.o: src/highlight.h src/color.h src/env.h +obj/builtin_history.o: src/parse_constants.h src/wgetopt.h obj/builtin_jobs.o: config.h src/builtin.h src/common.h src/fallback.h obj/builtin_jobs.o: src/signal.h src/io.h src/proc.h src/parse_tree.h obj/builtin_jobs.o: src/parse_constants.h src/tokenizer.h src/wgetopt.h obj/builtin_jobs.o: src/wutil.h obj/builtin_printf.o: config.h src/builtin.h src/common.h src/fallback.h obj/builtin_printf.o: src/signal.h src/io.h src/wutil.h +obj/builtin_random.o: config.h src/builtin.h src/common.h src/fallback.h +obj/builtin_random.o: src/signal.h src/builtin_random.h src/io.h +obj/builtin_random.o: src/wgetopt.h src/wutil.h +obj/builtin_read.o: config.h src/builtin.h src/common.h src/fallback.h +obj/builtin_read.o: src/signal.h src/builtin_read.h src/complete.h src/env.h +obj/builtin_read.o: src/event.h src/expand.h src/parse_constants.h +obj/builtin_read.o: src/highlight.h src/color.h src/io.h src/proc.h +obj/builtin_read.o: src/parse_tree.h src/tokenizer.h src/reader.h +obj/builtin_read.o: src/wcstringutil.h src/wgetopt.h src/wutil.h obj/builtin_set.o: config.h src/builtin.h src/common.h src/fallback.h obj/builtin_set.o: src/signal.h src/env.h src/expand.h src/parse_constants.h obj/builtin_set.o: src/io.h src/proc.h src/parse_tree.h src/tokenizer.h @@ -996,6 +1016,11 @@ obj/builtin_set.o: src/wgetopt.h src/wutil.h obj/builtin_set_color.o: config.h src/builtin.h src/common.h src/fallback.h obj/builtin_set_color.o: src/signal.h src/color.h src/env.h src/io.h obj/builtin_set_color.o: src/output.h src/wgetopt.h src/wutil.h +obj/builtin_status.o: config.h src/builtin.h src/common.h src/fallback.h +obj/builtin_status.o: src/signal.h src/builtin_status.h src/io.h src/parser.h +obj/builtin_status.o: src/event.h src/expand.h src/parse_constants.h +obj/builtin_status.o: src/parse_tree.h src/tokenizer.h src/proc.h +obj/builtin_status.o: src/wgetopt.h src/wutil.h obj/builtin_string.o: config.h src/builtin.h src/common.h src/fallback.h obj/builtin_string.o: src/signal.h src/io.h src/parse_util.h obj/builtin_string.o: src/parse_constants.h src/tokenizer.h src/wgetopt.h diff --git a/src/builtin.cpp b/src/builtin.cpp index e54dd2b23..d16ac04c8 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -31,7 +30,6 @@ #include #include -#include #include #include @@ -45,6 +43,7 @@ #include "builtin_history.h" #include "builtin_jobs.h" #include "builtin_printf.h" +#include "builtin_random.h" #include "builtin_read.h" #include "builtin_set.h" #include "builtin_set_color.h" @@ -897,157 +896,6 @@ int builtin_function(parser_t &parser, io_streams_t &streams, const wcstring_lis return STATUS_CMD_OK; } -/// The random builtin generates random numbers. -static int builtin_random(parser_t &parser, io_streams_t &streams, wchar_t **argv) { - int argc = builtin_count_args(argv); - - static bool seeded = false; - static std::minstd_rand engine; - if (!seeded) { - // 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); - seeded = true; - } - - static const wchar_t *short_options = L"h"; - static const struct woption long_options[] = {{L"help", no_argument, NULL, 'h'}, - {NULL, 0, NULL, 0}}; - - int opt; - wgetopter_t w; - while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) { - switch (opt) { //!OCLINT(too few branches) - case 'h': { - builtin_print_help(parser, streams, argv[0], streams.out); - return STATUS_CMD_OK; - } - case '?': { - builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); - return STATUS_INVALID_ARGS; - } - default: { - DIE("unexpected retval from wgetopt_long"); - break; - } - } - } - - int arg_count = argc - w.woptind; - long long start, end; - unsigned long long step; - bool choice = false; - if (arg_count >= 1 && !wcscmp(argv[w.woptind], L"choice")) { - if (arg_count == 1) { - streams.err.append_format(L"%ls: nothing to choose from\n", argv[0]); - 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(L"%ls: %ls is not a valid integer\n", argv[0], 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(L"%ls: %ls is not a valid integer\n", argv[0], 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[w.woptind]); - if (parse_error) return STATUS_INVALID_ARGS; - engine.seed(static_cast(seed)); - return STATUS_CMD_OK; - } else if (arg_count == 2) { - start = parse_ll(argv[w.woptind]); - step = 1; - end = parse_ll(argv[w.woptind + 1]); - } else if (arg_count == 3) { - start = parse_ll(argv[w.woptind]); - step = parse_ull(argv[w.woptind + 1]); - end = parse_ll(argv[w.woptind + 2]); - } else { - streams.err.append_format(BUILTIN_ERR_TOO_MANY_ARGUMENTS, argv[0]); - 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", argv[0]); - return STATUS_INVALID_ARGS; - } else if (step == 0) { - streams.err.append_format(L"%ls: STEP must be a positive integer\n", argv[0]); - return STATUS_INVALID_ARGS; - } - } - - // only for negative argument - auto safe_abs = [](long long ll) -> unsigned long long { - return -static_cast(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(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", argv[0]); - return STATUS_INVALID_ARGS; - } - - std::uniform_int_distribution 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(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[w.woptind + result]); - } else { - streams.out.append_format(L"%lld\n", result); - } - return STATUS_CMD_OK; -} - /// The exit builtin. Calls reader_exit to exit and returns the value specified. static int builtin_exit(parser_t &parser, io_streams_t &streams, wchar_t **argv) { int argc = builtin_count_args(argv); diff --git a/src/builtin_random.cpp b/src/builtin_random.cpp new file mode 100644 index 000000000..2f5130fe0 --- /dev/null +++ b/src/builtin_random.cpp @@ -0,0 +1,191 @@ +// Implementation of the random builtin. +#include "config.h" // IWYU pragma: keep + +#include +#include +#include +#include + +#include +#include + +#include "builtin.h" +#include "builtin_random.h" +#include "common.h" +#include "fallback.h" // IWYU pragma: keep +#include "io.h" +#include "wgetopt.h" +#include "wutil.h" // IWYU pragma: keep + +struct random_opts { + bool print_help = false; +}; + +static const wchar_t *short_options = L"h"; +static const struct woption long_options[] = {{L"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0}}; + +static int parse_random_opts(struct random_opts *opts, int *optind, //!OCLINT(high ncss method) + 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, NULL)) != -1) { + switch (opt) { //!OCLINT(too few branches) + case 'h': { + opts->print_help = true; + return STATUS_CMD_OK; + } + case '?': { + builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); + return STATUS_INVALID_ARGS; + } + default: { + DIE("unexpected retval from wgetopt_long"); + break; + } + } + } + + *optind = w.woptind; + return STATUS_CMD_OK; +} + +/// The random builtin generates random numbers. +int builtin_random(parser_t &parser, io_streams_t &streams, wchar_t **argv) { + wchar_t *cmd = argv[0]; + int argc = builtin_count_args(argv); + struct random_opts opts; + + int optind; + int retval = parse_random_opts(&opts, &optind, argc, argv, parser, streams); + if (retval != STATUS_CMD_OK) return retval; + + if (opts.print_help) { + builtin_print_help(parser, streams, cmd, streams.out); + return STATUS_CMD_OK; + } + + static bool seeded = false; + static std::minstd_rand engine; + if (!seeded) { + // 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); + seeded = true; + } + + int arg_count = argc - optind; + long long start, end; + unsigned long long step; + bool choice = false; + if (arg_count >= 1 && !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(L"%ls: %ls is not a valid integer\n", 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(L"%ls: %ls is not a valid integer\n", 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(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(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(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 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(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; +} diff --git a/src/builtin_random.h b/src/builtin_random.h new file mode 100644 index 000000000..878c73f25 --- /dev/null +++ b/src/builtin_random.h @@ -0,0 +1,9 @@ +// Prototypes for executing builtin_random function. +#ifndef FISH_BUILTIN_RANDOM_H +#define FISH_BUILTIN_RANDOM_H + +class parser_t; +struct io_streams_t; + +int builtin_random(parser_t &parser, io_streams_t &streams, wchar_t **argv); +#endif