diff --git a/luprex/cpp/drv/driver.cpp b/luprex/cpp/drv/driver.cpp index 9558a587..01e512bb 100644 --- a/luprex/cpp/drv/driver.cpp +++ b/luprex/cpp/drv/driver.cpp @@ -16,6 +16,9 @@ static void if_error_print_and_exit(const std::string_view str) { } } +// DPrints are currently not going through the readline device. +// doing so would not currently be thread-safe. Do I care about +// that? I'm not sure. static void dprint_callback(const char *oneline, size_t size) { fwrite("**", 1, 2, stderr); fwrite(oneline, 1, size, stderr); @@ -91,6 +94,7 @@ class Driver { std::unique_ptr pollvec_; std::unique_ptr chbuf_; ReadlineDevice readline_device_; + std::string console_command_; sslutil::UniqueCTX ssl_server_ctx_; sslutil::UniqueCTX ssl_client_secure_ctx_; @@ -197,34 +201,58 @@ class Driver { } } - void handle_console_output() { + void channel_printbuffer() { if (engw.get_have_prints(&engw)) { uint32_t ndata; const char *data; engw.play_access(&engw, AccessKind::CHANNEL_PRINTS, 0, 0, "", &ndata, &data); if (ndata > 0) { if (ndata > DRV_SHORTSTRING_SIZE) ndata = DRV_SHORTSTRING_SIZE; - std::string_view src(data, ndata); - int consumed; - std::u32string cps = drvutil::utf8_to_utf32(src, &consumed); - readline_device_.print(cps); + readline_device_.printline(std::string_view(data, ndata)); } } } + void add_console_command(std::string_view addition) + { + std::string cmd = console_command_ + std::string(addition); + console_command_.clear(); + uint32_t ndata; + const char *data; + engw.play_access(&engw, AccessKind::VALIDATE_LUA_EXPR, + 0, cmd.size(), cmd.c_str(), &ndata, &data); + std::string_view message(data, ndata); + + // Handle the command. + if (message == "truncated lua") { + console_command_ = cmd; + } else if (message == "white space") { + readline_device_.printline("white space."); + } else if (message == "slash command") { + readline_device_.printline("slash command."); + } else if (message.empty()) { + readline_device_.printline("valid lua"); + } else { + readline_device_.printline(message); + } + + if (console_command_.empty()) { + readline_device_.set_prompt(">"); + } else { + readline_device_.set_prompt(">>"); + } + } + void handle_console_input() { read_console_recently_ = false; - std::u32string prompt = drvutil::utf8_to_utf32(">", nullptr); - readline_device_.set_prompt(prompt); while (true) { std::u32string cps = console_read(); if (cps.size() == 0) break; read_console_recently_ = true; for (char32_t c : cps) { - std::u32string line = readline_device_.putcode(c); + std::string line = readline_device_.putcode(c); if (!line.empty()) { - std::string utf8 = drvutil::utf32_to_utf8(line); - engw.play_recv_incoming(&engw, 0, utf8.size(), utf8.c_str()); + add_console_command(line); } } } @@ -588,7 +616,9 @@ class Driver { int drive(int argc, char *argv[]) { // Set up the console readline device. readline_device_.set_print_callback(console_write); - + readline_device_.set_prompt(">"); + console_command_.clear(); + // Remove the program name from argv. std::string program = argv[0]; argc -= 1; @@ -659,12 +689,11 @@ class Driver { // Main loop. while (!engw.get_stop_driver(&engw)) { handle_lua_source(); - handle_console_output(); handle_new_outgoing_sockets(); handle_socket_input_output(); handle_console_input(); engw.play_update(&engw, drvutil::get_monotonic_clock()); - handle_console_output(); + channel_printbuffer(); } // Cleanup diff --git a/luprex/cpp/drv/readline.cpp b/luprex/cpp/drv/readline.cpp index 630ec70d..d9d487c6 100644 --- a/luprex/cpp/drv/readline.cpp +++ b/luprex/cpp/drv/readline.cpp @@ -2,6 +2,10 @@ #define MAXLINE 512 + +static std::u32string white_(1, ' '); +static std::u32string newline_(1, '\n'); + static std::u32string n_backspaces(int n) { std::u32string result(3 * n, 0); for (int i = 0; i < n; i++) { @@ -24,8 +28,8 @@ void ReadlineDevice::set_print_callback(print_callback cb) { print_cb_ = cb; } -void ReadlineDevice::set_prompt(const std::u32string &prompt) { - desired_prompt_ = prompt; +void ReadlineDevice::set_prompt(std::string_view prompt) { + desired_prompt_ = drvutil::utf8_to_utf32(prompt, nullptr); echo_command(); } @@ -66,46 +70,46 @@ void ReadlineDevice::echo_command() { } } -std::u32string ReadlineDevice::putcode(char32_t c) { +std::string ReadlineDevice::putcode(char32_t c) { if ((c == '\n') && (readline_lastc_ == '\r')) { // Ignore newline immediately after carriage return. // Otherwise, crlf produces two newlines. - return std::u32string(); + return ""; } else if ((c == '\r') || (c == '\n')) { - std::u32string white(1, ' '); - std::u32string newline(1, '\n'); echo_command(); - print_cb_(white + newline); - std::u32string result = desired_command_ + newline; + print_cb_(white_ + newline_); + std::u32string result = desired_command_ + newline_; desired_command_.clear(); current_prompt_.clear(); current_command_.clear(); echo_command(); - return result; + return drvutil::utf32_to_utf8(result); } else if ((c == '\b') || (c == 127)) { int len = desired_command_.size(); if (len > 0) { desired_command_ = desired_command_.substr(0, len-1); } echo_command(); - return std::u32string(); + return ""; } else if ((c >= 32)&&(c <= 0x10FFFF)) { int len = desired_command_.size(); if (len < MAXLINE) { desired_command_ = desired_command_ + c; } echo_command(); - return std::u32string(); + return ""; } readline_lastc_ = c; - return std::u32string(); + return ""; } -void ReadlineDevice::print(const std::u32string &s) { - if (!s.empty()) { - erase_command(); - print_cb_(s); - echo_command(); - } + +void ReadlineDevice::printline(std::string_view s) { + bool missing_newline = ((s.size() == 0) || (s[s.size() - 1] != '\n')); + std::u32string utf32 = drvutil::utf8_to_utf32(s, nullptr); + erase_command(); + print_cb_(utf32); + if (missing_newline) print_cb_(newline_); + echo_command(); } diff --git a/luprex/cpp/drv/readline.hpp b/luprex/cpp/drv/readline.hpp index 78cfe3e7..098b24a3 100644 --- a/luprex/cpp/drv/readline.hpp +++ b/luprex/cpp/drv/readline.hpp @@ -28,15 +28,19 @@ public: void set_print_callback(print_callback cb); // change the prompt. - void set_prompt(const std::u32string &prompt); + void set_prompt(std::string_view prompt); // Use this to print anything on the console. - void print(const std::u32string &cps); + // + // if the string doesn't end in a newline, one will + // be automatically added. + // + void printline(std::string_view cps); // Whenever the user types a character, call 'putcode'. If the code is // newline, this returns the line of text that was entered, including the // newline. Otherwise returns empty string. Backspace is handled here. - std::u32string putcode(char32_t codepoint); + std::string putcode(char32_t codepoint); };