diff --git a/include/basic_color_printer.hpp b/include/basic_color_printer.hpp index eebede6..780a088 100644 --- a/include/basic_color_printer.hpp +++ b/include/basic_color_printer.hpp @@ -52,6 +52,7 @@ private: int m_col = 0; //current column number int m_line = 0; //current line number int m_index = -1; //index in lookup_table + const int m_randomness; //change starting color public: constexpr basic_color_printer(const cmd_args& args)noexcept; ~basic_color_printer(void)noexcept = default; diff --git a/include/basic_color_printer.tpp b/include/basic_color_printer.tpp index 6d7fa80..4b521c0 100644 --- a/include/basic_color_printer.tpp +++ b/include/basic_color_printer.tpp @@ -27,10 +27,12 @@ #include //wcwidth #include //wprintf, putwchar +#include //rand template constexpr basic_color_printer::basic_color_printer(const cmd_args& args)noexcept: - color_printer_base>(args){} + color_printer_base>(args), + m_randomness(rand()%color_lookup_len){} template basic_color_printer& basic_color_printer::_print(const wchar_t s)noexcept{ if(s == L'\n'){ @@ -42,7 +44,7 @@ basic_color_printer& basic_color_printer::_print(const wchar_t s)noexcept{ else inc_col(1); } - long new_index = static_cast(this->m_freq*m_col + this->m_spread*m_line) % color_lookup_len; + long new_index = static_cast(this->m_freq*m_col + this->m_spread*m_line + m_randomness) % color_lookup_len; if(new_index < 0) new_index += color_lookup_len; if(new_index != m_index){ diff --git a/include/cmd.hpp b/include/cmd.hpp index b7ef63b..380689f 100644 --- a/include/cmd.hpp +++ b/include/cmd.hpp @@ -42,6 +42,7 @@ struct cmd_args{ std::vector filenames; float freq = 0.2f; float spread = 0.3f; + long seed = 0; unsigned number:2; unsigned ends:1; unsigned squeeze:1; diff --git a/include/color_printer_true.hpp b/include/color_printer_true.hpp index e1f333d..e8155bf 100644 --- a/include/color_printer_true.hpp +++ b/include/color_printer_true.hpp @@ -29,6 +29,7 @@ #include //std::abs #include //std::min, std::max #include //wprintf, putwchar +#include //rand //True color printer. Too many options to enumerate the entire list of possible colors, so we calculate on the fly class color_printer_true : public color_printer_base @@ -48,7 +49,28 @@ private: color curr = color::red; - constexpr color_state(void)noexcept = default; + constexpr color_state(void)noexcept{ + switch(rand()%3){ + case 0: + r = color_max_amount; + g = rand()%(color_max_amount-color_min_amount) + color_min_amount; + b = color_min_amount; + curr = color::green; + break; + case 1: + r = color_min_amount; + g = color_max_amount; + b = rand()%(color_max_amount-color_min_amount) + color_min_amount; + curr = color::blue; + break; + case 2: + r = rand()%(color_max_amount-color_min_amount) + color_min_amount; + g = color_min_amount; + b = color_max_amount; + curr = color::red; + break; + }; + } constexpr color inc(void){ switch(curr){ default: diff --git a/src/cmd.cpp b/src/cmd.cpp index aaf4ec8..8de46cd 100644 --- a/src/cmd.cpp +++ b/src/cmd.cpp @@ -22,6 +22,7 @@ #include //strncmp #include #include //fwprintf, stderr +#include //atol cmd_args::cmd_args(void)noexcept: number(0), ends(0), squeeze(0), tabs(0), nonprinting(0), usage(0), version(0){} @@ -73,6 +74,7 @@ constexpr const char* U_IGNORED_LONG_OPTION = NO_LONG_OPTION; constexpr const char NONPRINTING_LONG_OPTION[] = "show-nonprinting"; constexpr const char USAGE_LONG_OPTION[] = "help"; constexpr const char VERSION_LONG_OPTION[] = "version"; +constexpr const char SEED_LONG_OPTION[] = "seed="; #define NO_SHORT_OPTION "" constexpr const char SHOW_ALL_SHORT_OPTION[] = "A"; @@ -87,6 +89,7 @@ constexpr const char U_IGNORED_SHORT_OPTION[] = "u"; constexpr const char NONPRINTING_SHORT_OPTION[] = "v"; constexpr const char USAGE_SHORT_OPTION[] = "h"; constexpr const char* VERSION_SHORT_OPTION = NO_SHORT_OPTION; +constexpr const char SEED_SHORT_OPTION[] = "S"; #define NO_DESC nullptr constexpr const char EQUIV_DESC_BASE[] = "equivalent to -"; @@ -103,12 +106,31 @@ constexpr const char U_IGNORED_DESC[] = "(ignored)"; constexpr const char NONPRINTING_DESC[] = "should use ^ and M- notation, except for LFD and TAB (not supported as of now)"; constexpr const char USAGE_DESC[] = "display this help and exit"; constexpr const char VERSION_DESC[] = "output version information and exit"; +constexpr const char SEED_DESC[] = "Rainbow seed, 0 = random (default: 0)"; #define SHORT_OPT(x) (x##_SHORT_OPTION)[0] #define OPTION(x) {x##_LONG_OPTION, SHORT_OPT(x), x##_DESC} -#define CHECK_LONG_OPTION(x, arg) (!strncmp(x##_LONG_OPTION, arg, strlen(x##_LONG_OPTION))) +constexpr bool is_integer(const char* s){ + if(*s != '-' && (*s < '0' || *s > '9')){ + return false; + } + for(s = s+1;*s;++s){ + if(*s < '0' || *s > '9'){ + return false; + } + } + return true; +} + +constexpr size_t strlen_pre_eq(const char* str){ + size_t i = 0; + for(;str[i] && str[i] != '=';++i); + return i; +} + +#define CHECK_LONG_OPTION(x, arg) (!strncmp(x##_LONG_OPTION, arg, strlen_pre_eq(x##_LONG_OPTION))) #define IS_SHORT_OPTION(arg) ((arg)[0] == '-' && (arg)[1] != '-') #define IS_LONG_OPTION(arg) ((arg)[0] == '-' && (arg)[1] == '-') //Convert command line options to a cmd_args struct @@ -124,6 +146,7 @@ cmd_args process_cmd_args(int argc, char** argv){ escaped = true; }else if(IS_SHORT_OPTION(argv[i])){ size_t arg_len = strlen(argv[i]); + size_t next_arg = i+1; for(size_t j = 1;j < arg_len;++j){ switch(argv[i][j]){ case SHORT_OPT(SHOW_ALL): @@ -162,20 +185,28 @@ cmd_args process_cmd_args(int argc, char** argv){ case SHORT_OPT(VERSION): ret.version = 1; break; + case SHORT_OPT(SEED): + if(i == (argc-1) || !is_integer(argv[next_arg])){ + fwprintf(stderr, L"'-%s' requires an integer argument\n", SEED_SHORT_OPTION); + ret.usage = 1; + }else{ + ret.seed = atol(argv[next_arg]); + } + ++next_arg; + break; default: //keep parsing after error so we can still properly output requested color style ret.usage = 1; fwprintf(stderr, L"Unrecognized option '-%c'\n", argv[i][j]); }; } + i = next_arg-1; }else if(IS_LONG_OPTION(argv[i])){ const char* arg = argv[i]+2; if(CHECK_LONG_OPTION(SHOW_ENDS, arg)){ ret.ends = 1; }else if(CHECK_LONG_OPTION(NUMBER_NONBLANK, arg)){ ret.number = PRINT_LINE_NONBLANK; - }else if(CHECK_LONG_OPTION(SHOW_ENDS, arg)){ - ret.ends = 1; }else if(CHECK_LONG_OPTION(NUMBER_LINES, arg)){ if(!ret.number) ret.number = PRINT_LINE_ALWAYS; @@ -189,6 +220,14 @@ cmd_args process_cmd_args(int argc, char** argv){ ret.usage = 1; }else if(CHECK_LONG_OPTION(VERSION, arg)){ ret.version = 1; + }else if(CHECK_LONG_OPTION(SEED, arg)){ + constexpr size_t offset = strlen_pre_eq(SEED_LONG_OPTION); + if(arg[offset] != '=' || !is_integer(arg+offset+1)){ + fwprintf(stderr, L"'--%.*s' requires an integer argument\n", offset, SEED_LONG_OPTION); + ret.usage = 1; + }else{ + ret.seed = atol(arg+offset+1); + } }else{ ret.usage = 1; fwprintf(stderr, L"Unrecognized option '%s'\n", argv[i]); @@ -214,6 +253,7 @@ const cmd_options cmd_arguments_list[] = { OPTION(SHOW_TABS), OPTION(U_IGNORED), OPTION(NONPRINTING), + OPTION(SEED), OPTION(USAGE), OPTION(VERSION) }; diff --git a/src/roflcat.cpp b/src/roflcat.cpp index 5041019..9126f6c 100644 --- a/src/roflcat.cpp +++ b/src/roflcat.cpp @@ -31,6 +31,8 @@ #include //isatty #include //strcmp #include //size_t +#include //std::chrono::* +#include //srand constexpr const char* version_string = "roflcat version 0.1a"; @@ -140,12 +142,19 @@ void print_files(cmd_args& args){ } p.reset(); } +auto get_time(void){ + auto time = std::chrono::high_resolution_clock::now(); + auto eptime = time.time_since_epoch(); + auto micros = std::chrono::duration_cast(eptime); + return micros.count(); +} int main(int argc, char** argv){ setlocale(LC_ALL, ""); //change to system locale so wchar_t things work cmd_args args = process_cmd_args(argc, argv); int avail_colors = detect_term_colors(); + srand(args.seed ? args.seed : get_time()); //print_files(args); //if stdout is not a tty, disable color if(!isatty(fileno(stdout))){