On server, channel dprint through the readline-device. Also some refactors and quality improvements.
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
|
||||
|
||||
#include "drvutil.hpp"
|
||||
#include "osdrvutil.hpp"
|
||||
#include "sslutil.hpp"
|
||||
#include "readline.hpp"
|
||||
#include "../core/enginewrapper.hpp"
|
||||
@@ -220,21 +219,8 @@ static int socket_poll(struct pollfd *pollvec, int pollcount, int mstimeout, std
|
||||
}
|
||||
|
||||
// Write unicode onto the console.
|
||||
static void console_write(const std::u32string &cps) {
|
||||
std::string utf8 = drvutil::utf32_to_utf8(cps);
|
||||
write(1, utf8.c_str(), utf8.size());
|
||||
}
|
||||
|
||||
static std::u32string console_read() {
|
||||
std::u32string result;
|
||||
char buffer[512];
|
||||
int nread = read(0, buffer, 512);
|
||||
if (nread > 0) {
|
||||
std::string_view s(buffer, nread);
|
||||
result = drvutil::utf8_to_utf32(s, nullptr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static void call_init_engine_wrapper(const std::filesystem::path &luprexroot, EngineWrapper *w) {
|
||||
using InitFn = void (*)(EngineWrapper *);
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#define _WIN32_WINNT 0x0600
|
||||
|
||||
#include "drvutil.hpp"
|
||||
#include "osdrvutil.hpp"
|
||||
#include "sslutil.hpp"
|
||||
#include "readline.hpp"
|
||||
#include "../core/enginewrapper.hpp"
|
||||
@@ -232,52 +231,6 @@ static void init_winsock() {
|
||||
}
|
||||
|
||||
|
||||
static void console_write(const std::u32string &cps) {
|
||||
if (cps.size() == 0) return;
|
||||
// Convert to wstring. Any character not representable as a single wchar_t
|
||||
// is replaced with a box. It's not ideal, but it's pretty good.
|
||||
std::wstring ws(cps.size(), 0);
|
||||
for (int i = 0; i < int(cps.size()); i++) {
|
||||
char32_t c = cps[i];
|
||||
if (drvutil::is_single_wchar_t(c)) ws[i] = (wchar_t)c;
|
||||
else ws[i] = 0x2610;
|
||||
}
|
||||
HANDLE hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
assert(hstdout != INVALID_HANDLE_VALUE);
|
||||
DWORD nwrote;
|
||||
std::wstring_view v(ws);
|
||||
while (v.size() > 0) {
|
||||
int nwrite = v.size();
|
||||
if (nwrite > 10000) nwrite = 10000;
|
||||
assert(WriteConsoleW(hstdout, v.data(), nwrite, &nwrote, nullptr));
|
||||
assert(nwrote > 0);
|
||||
v.remove_prefix(nwrote);
|
||||
}
|
||||
}
|
||||
|
||||
static std::u32string console_read() {
|
||||
HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE);
|
||||
assert(hstdin != INVALID_HANDLE_VALUE);
|
||||
INPUT_RECORD inrecords[512];
|
||||
DWORD nread, nevents;
|
||||
if (GetNumberOfConsoleInputEvents(hstdin, &nevents)) {
|
||||
if (int(nevents) > 0) {
|
||||
if (int(nevents) > 512) nevents = 512;
|
||||
ReadConsoleInputW(hstdin, inrecords, nevents, &nread);
|
||||
std::u32string result(nread, 0);
|
||||
int len = 0;
|
||||
for (int i = 0; i < int(nread); i++) {
|
||||
const INPUT_RECORD &inr = inrecords[i];
|
||||
if (inr.EventType != KEY_EVENT) continue;
|
||||
const KEY_EVENT_RECORD &key = inr.Event.KeyEvent;
|
||||
if (!key.bKeyDown) continue;
|
||||
result[len++] = key.uChar.UnicodeChar;
|
||||
}
|
||||
return result.substr(0, len);
|
||||
}
|
||||
}
|
||||
return std::u32string();
|
||||
}
|
||||
|
||||
static void ssl_load_certificate_authorities(SSL_CTX *ctx) {
|
||||
HCERTSTORE hStore = CertOpenSystemStoreW(0, L"ROOT");
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
#define POLLVEC_SIZE (DRV_MAX_CHAN + 1)
|
||||
#define MAX_BIO_BUFFER (128 * 1024)
|
||||
|
||||
static ReadlineDevice readline_device;
|
||||
|
||||
static void dprint_callback(const char *oneline, size_t size) {
|
||||
readline_device.printline(std::string_view(oneline, size));
|
||||
}
|
||||
|
||||
static void if_error_print_and_exit(const std::string_view str) {
|
||||
if (!str.empty()) {
|
||||
@@ -19,16 +24,6 @@ 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);
|
||||
fwrite("\n", 1, 1, stderr);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
inline bool file_exists(const std::filesystem::path &name) {
|
||||
std::ifstream f(name);
|
||||
return f.good();
|
||||
@@ -96,7 +91,6 @@ class Driver {
|
||||
bool read_console_recently_;
|
||||
std::unique_ptr<struct pollfd[]> pollvec_;
|
||||
std::unique_ptr<char[]> chbuf_;
|
||||
ReadlineDevice readline_device_;
|
||||
std::string console_command_;
|
||||
|
||||
sslutil::UniqueCTX ssl_server_ctx_;
|
||||
@@ -209,7 +203,7 @@ class Driver {
|
||||
engw.play_access(&engw, AccessKind::CHANNEL_PRINTS, 0, 0, "", &ndata, &data);
|
||||
if (ndata > 0) {
|
||||
if (ndata > DRV_SHORTSTRING_SIZE) ndata = DRV_SHORTSTRING_SIZE;
|
||||
readline_device_.printline(std::string_view(data, ndata));
|
||||
readline_device.printline(std::string_view(data, ndata));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -224,7 +218,7 @@ class Driver {
|
||||
inject_lua_source();
|
||||
}
|
||||
else {
|
||||
readline_device_.printline(parser.Error());
|
||||
readline_device.printline(parser.Error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,24 +243,24 @@ class Driver {
|
||||
engw.play_access(&engw, AccessKind::INVOKE_LUA_EXPR,
|
||||
0, cmd.size(), cmd.c_str(), nullptr, nullptr);
|
||||
} else {
|
||||
readline_device_.printline(message);
|
||||
readline_device.printline(message);
|
||||
}
|
||||
|
||||
if (console_command_.empty()) {
|
||||
readline_device_.set_prompt(">");
|
||||
readline_device.set_prompt(">");
|
||||
} else {
|
||||
readline_device_.set_prompt(">>");
|
||||
readline_device.set_prompt(">>");
|
||||
}
|
||||
}
|
||||
|
||||
void handle_console_input() {
|
||||
read_console_recently_ = false;
|
||||
while (true) {
|
||||
std::u32string cps = console_read();
|
||||
std::u32string cps = drvutil::console_read();
|
||||
if (cps.size() == 0) break;
|
||||
read_console_recently_ = true;
|
||||
for (char32_t c : cps) {
|
||||
std::string line = readline_device_.putcode(c);
|
||||
std::string line = readline_device.putcode(c);
|
||||
if (!line.empty()) {
|
||||
add_console_command(line);
|
||||
}
|
||||
@@ -631,8 +625,7 @@ 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(">");
|
||||
readline_device.set_prompt(">");
|
||||
console_command_.clear();
|
||||
|
||||
// Remove the program name from argv.
|
||||
|
||||
83
luprex/cpp/drv/drvutil-linux.cpp
Normal file
83
luprex/cpp/drv/drvutil-linux.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
#include <time.h>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <pthread.h>
|
||||
|
||||
namespace drvutil {
|
||||
|
||||
// strerror has to be the most overcomplicated function imaginable. The simple
|
||||
// version, 'strerror', is not thread-safe, and the improved versions are all
|
||||
// incompatible from OS to OS. Even different versions of linux aren't
|
||||
// compatible. A lot of conditional compilation is needed.
|
||||
|
||||
inline static void strerror_helper(int status, int errnum, char errbuf[256]) {
|
||||
if (status != 0) {
|
||||
snprintf(errbuf, 256, "unknown errno %d", errnum);
|
||||
}
|
||||
}
|
||||
|
||||
inline static void strerror_helper(const char *result, int errnum, char errbuf[256]) {
|
||||
if (result != errbuf) {
|
||||
snprintf(errbuf, 256, "%s", result);
|
||||
}
|
||||
}
|
||||
|
||||
void strerror_safe(int errnum, char errbuf[256]) {
|
||||
auto rval = strerror_r(errnum, errbuf, 256);
|
||||
strerror_helper(rval, errnum, errbuf);
|
||||
}
|
||||
|
||||
std::string strerror_str(int errnum) {
|
||||
char buf[256];
|
||||
strerror_safe(errnum, buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
class MonoClock {
|
||||
private:
|
||||
struct timespec base_;
|
||||
public:
|
||||
MonoClock() {
|
||||
int status = clock_gettime(CLOCK_MONOTONIC, &base_);
|
||||
assert(status == 0);
|
||||
}
|
||||
double get() {
|
||||
struct timespec t;
|
||||
int status = clock_gettime(CLOCK_MONOTONIC, &t);
|
||||
assert(status == 0);
|
||||
double tv_sec = t.tv_sec - base_.tv_sec;
|
||||
double tv_nsec = t.tv_nsec - base_.tv_nsec;
|
||||
return tv_sec + (tv_nsec * 1.0E-9);
|
||||
}
|
||||
};
|
||||
|
||||
static MonoClock monoclock;
|
||||
double get_monotonic_clock() {
|
||||
return monoclock.get();
|
||||
}
|
||||
|
||||
|
||||
void console_write(const std::u32string &cps) {
|
||||
std::string utf8 = drvutil::utf32_to_utf8(cps);
|
||||
write(1, utf8.c_str(), utf8.size());
|
||||
}
|
||||
|
||||
std::u32string console_read() {
|
||||
std::u32string result;
|
||||
char buffer[512];
|
||||
int nread = read(0, buffer, 512);
|
||||
if (nread > 0) {
|
||||
std::string_view s(buffer, nread);
|
||||
result = drvutil::utf8_to_utf32(s, nullptr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace drvutil
|
||||
|
||||
94
luprex/cpp/drv/drvutil-windows.cpp
Normal file
94
luprex/cpp/drv/drvutil-windows.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
|
||||
#include <windows.h>
|
||||
#include <profileapi.h>
|
||||
|
||||
namespace drvutil {
|
||||
|
||||
static void strerror_safe(int errnum, char errbuf[256]) {
|
||||
int status = strerror_s(errbuf, 256, errnum);
|
||||
if (status != 0) {
|
||||
snprintf(errbuf, 256, "unknown errno %d", errnum);
|
||||
}
|
||||
}
|
||||
|
||||
std::string strerror_str(int errnum) {
|
||||
char buf[256];
|
||||
strerror_safe(errnum, buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
class MonoClock {
|
||||
public:
|
||||
double freq_;
|
||||
LONGLONG base_;
|
||||
inline LONGLONG qpc() {
|
||||
LARGE_INTEGER x;
|
||||
BOOL status = QueryPerformanceCounter(&x);
|
||||
assert(status != 0);
|
||||
return x.QuadPart;
|
||||
}
|
||||
MonoClock() {
|
||||
LARGE_INTEGER x;
|
||||
BOOL status = QueryPerformanceFrequency(&x);
|
||||
assert(status != 0);
|
||||
freq_ = 1.0 / double(x.QuadPart);
|
||||
base_ = qpc();
|
||||
}
|
||||
double get() {
|
||||
return (qpc() - base_) * freq_;
|
||||
}
|
||||
};
|
||||
|
||||
static MonoClock monoclock;
|
||||
double get_monotonic_clock() {
|
||||
return monoclock.get();
|
||||
}
|
||||
|
||||
void console_write(const std::u32string &cps) {
|
||||
if (cps.size() == 0) return;
|
||||
// Convert to wstring. Any character not representable as a single wchar_t
|
||||
// is replaced with a box. It's not ideal, but it's pretty good.
|
||||
std::wstring ws(cps.size(), 0);
|
||||
for (int i = 0; i < int(cps.size()); i++) {
|
||||
char32_t c = cps[i];
|
||||
if (drvutil::is_single_wchar_t(c)) ws[i] = (wchar_t)c;
|
||||
else ws[i] = 0x2610;
|
||||
}
|
||||
HANDLE hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
assert(hstdout != INVALID_HANDLE_VALUE);
|
||||
DWORD nwrote;
|
||||
std::wstring_view v(ws);
|
||||
while (v.size() > 0) {
|
||||
int nwrite = v.size();
|
||||
if (nwrite > 10000) nwrite = 10000;
|
||||
assert(WriteConsoleW(hstdout, v.data(), nwrite, &nwrote, nullptr));
|
||||
assert(nwrote > 0);
|
||||
v.remove_prefix(nwrote);
|
||||
}
|
||||
}
|
||||
|
||||
std::u32string console_read() {
|
||||
HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE);
|
||||
assert(hstdin != INVALID_HANDLE_VALUE);
|
||||
INPUT_RECORD inrecords[512];
|
||||
DWORD nread, nevents;
|
||||
if (GetNumberOfConsoleInputEvents(hstdin, &nevents)) {
|
||||
if (int(nevents) > 0) {
|
||||
if (int(nevents) > 512) nevents = 512;
|
||||
ReadConsoleInputW(hstdin, inrecords, nevents, &nread);
|
||||
std::u32string result(nread, 0);
|
||||
int len = 0;
|
||||
for (int i = 0; i < int(nread); i++) {
|
||||
const INPUT_RECORD &inr = inrecords[i];
|
||||
if (inr.EventType != KEY_EVENT) continue;
|
||||
const KEY_EVENT_RECORD &key = inr.Event.KeyEvent;
|
||||
if (!key.bKeyDown) continue;
|
||||
result[len++] = key.uChar.UnicodeChar;
|
||||
}
|
||||
return result.substr(0, len);
|
||||
}
|
||||
}
|
||||
return std::u32string();
|
||||
}
|
||||
|
||||
} // namespace drvutil
|
||||
@@ -354,4 +354,17 @@ std::string package_lua_source(const std::filesystem::path &base, std::ostream *
|
||||
}
|
||||
|
||||
|
||||
} // namespace drv
|
||||
} // namespace drvutil
|
||||
|
||||
|
||||
// Include the system-dependent part of drvutil.
|
||||
|
||||
#if defined(__linux__)
|
||||
#include "drvutil-linux.cpp"
|
||||
#elif defined(_WIN32)
|
||||
#include "drvutil-windows.cpp"
|
||||
#else
|
||||
#error "Only support __linux__ or _WIN32"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -19,6 +19,14 @@
|
||||
|
||||
namespace drvutil {
|
||||
|
||||
// Read/write the console.
|
||||
//
|
||||
// These only work when the program is being run from an actual
|
||||
// text-based console. Otherwise, they may go to the bit-bucket.
|
||||
//
|
||||
std::u32string console_read();
|
||||
void console_write(const std::u32string &cps);
|
||||
|
||||
// Read the lua source from disk into an ostringstream.
|
||||
//
|
||||
// To pass the lua source into the DLL, here is what you do: Construct an
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
#include "osdrvutil.hpp"
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <time.h>
|
||||
#elif defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#include <profileapi.h>
|
||||
#else
|
||||
#error "Only support __linux__ or _WIN32"
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
|
||||
// strerror has to be the most overcomplicated function imaginable. The simple
|
||||
// version, 'strerror', is not thread-safe, and the improved versions are all
|
||||
// incompatible from OS to OS. Even different versions of linux aren't
|
||||
// compatible. A lot of conditional compilation is needed.
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
inline static void strerror_helper(int status, int errnum, char errbuf[256]) {
|
||||
if (status != 0) {
|
||||
snprintf(errbuf, 256, "unknown errno %d", errnum);
|
||||
}
|
||||
}
|
||||
|
||||
inline static void strerror_helper(const char *result, int errnum, char errbuf[256]) {
|
||||
if (result != errbuf) {
|
||||
snprintf(errbuf, 256, "%s", result);
|
||||
}
|
||||
}
|
||||
|
||||
static void strerror_safe(int errnum, char errbuf[256]) {
|
||||
auto rval = strerror_r(errnum, errbuf, 256);
|
||||
strerror_helper(rval, errnum, errbuf);
|
||||
}
|
||||
|
||||
#elif defined(_WIN32)
|
||||
|
||||
static void strerror_safe(int errnum, char errbuf[256]) {
|
||||
int status = strerror_s(errbuf, 256, errnum);
|
||||
if (status != 0) {
|
||||
snprintf(errbuf, 256, "unknown errno %d", errnum);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// The monotonic clock is required to start at zero at initialization time,
|
||||
// advance steadily, and never go backwards. It is okay, however, if it is a
|
||||
// little inaccurate, or if it drifts a little over time.
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
class MonoClock {
|
||||
private:
|
||||
struct timespec base_;
|
||||
public:
|
||||
MonoClock() {
|
||||
int status = clock_gettime(CLOCK_MONOTONIC, &base_);
|
||||
assert(status == 0);
|
||||
}
|
||||
double get() {
|
||||
struct timespec t;
|
||||
int status = clock_gettime(CLOCK_MONOTONIC, &t);
|
||||
assert(status == 0);
|
||||
double tv_sec = t.tv_sec - base_.tv_sec;
|
||||
double tv_nsec = t.tv_nsec - base_.tv_nsec;
|
||||
return tv_sec + (tv_nsec * 1.0E-9);
|
||||
}
|
||||
};
|
||||
|
||||
#elif defined(_WIN32)
|
||||
|
||||
class MonoClock {
|
||||
public:
|
||||
double freq_;
|
||||
LONGLONG base_;
|
||||
inline LONGLONG qpc() {
|
||||
LARGE_INTEGER x;
|
||||
BOOL status = QueryPerformanceCounter(&x);
|
||||
assert(status != 0);
|
||||
return x.QuadPart;
|
||||
}
|
||||
MonoClock() {
|
||||
LARGE_INTEGER x;
|
||||
BOOL status = QueryPerformanceFrequency(&x);
|
||||
assert(status != 0);
|
||||
freq_ = 1.0 / double(x.QuadPart);
|
||||
base_ = qpc();
|
||||
}
|
||||
double get() {
|
||||
return (qpc() - base_) * freq_;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
namespace drvutil {
|
||||
|
||||
static MonoClock monoclock;
|
||||
double get_monotonic_clock() {
|
||||
return monoclock.get();
|
||||
}
|
||||
|
||||
std::string strerror_str(int errnum) {
|
||||
char buf[256];
|
||||
strerror_safe(errnum, buf);
|
||||
return buf;
|
||||
|
||||
}
|
||||
} // namespace drvutil
|
||||
@@ -1,23 +0,0 @@
|
||||
#ifndef OSDRVUTIL_HPP
|
||||
#define OSDRVUTIL_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace drvutil {
|
||||
|
||||
// Get a system error message, in an OS-independent manner.
|
||||
//
|
||||
std::string strerror_str(int errnum);
|
||||
|
||||
// Get the amount of time elapsed since program start.
|
||||
//
|
||||
// This is guaranteed to be monotonically increasing. It is not
|
||||
// guaranteed to be accurate. Error could gradually accumulate over
|
||||
// time.
|
||||
//
|
||||
double get_monotonic_clock();
|
||||
|
||||
} // namespace drvutil
|
||||
|
||||
#endif // OSDRVUTIL_HPP
|
||||
|
||||
@@ -24,11 +24,8 @@ static int common_prefix_length(const std::u32string &a, const std::u32string &b
|
||||
return minlen;
|
||||
}
|
||||
|
||||
void ReadlineDevice::set_print_callback(print_callback cb) {
|
||||
print_cb_ = cb;
|
||||
}
|
||||
|
||||
void ReadlineDevice::set_prompt(std::string_view prompt) {
|
||||
std::scoped_lock lock(mutex_);
|
||||
desired_prompt_ = drvutil::utf8_to_utf32(prompt, nullptr);
|
||||
echo_command();
|
||||
}
|
||||
@@ -36,7 +33,7 @@ void ReadlineDevice::set_prompt(std::string_view prompt) {
|
||||
void ReadlineDevice::erase_command() {
|
||||
int ccsize = current_prompt_.size() + current_command_.size();
|
||||
if (ccsize > 0) {
|
||||
print_cb_(n_backspaces(ccsize));
|
||||
drvutil::console_write(n_backspaces(ccsize));
|
||||
current_prompt_.clear();
|
||||
current_command_.clear();
|
||||
}
|
||||
@@ -46,8 +43,8 @@ void ReadlineDevice::echo_command() {
|
||||
// If the prompt has changed, erase everything and start over.
|
||||
if (desired_prompt_ != current_prompt_) {
|
||||
int ccsize = current_prompt_.size() + current_command_.size();
|
||||
print_cb_(n_backspaces(ccsize));
|
||||
print_cb_(desired_prompt_);
|
||||
drvutil::console_write(n_backspaces(ccsize));
|
||||
drvutil::console_write(desired_prompt_);
|
||||
current_command_.clear();
|
||||
current_prompt_ = desired_prompt_;
|
||||
}
|
||||
@@ -58,31 +55,31 @@ void ReadlineDevice::echo_command() {
|
||||
// Echo backspaces to remove the non-matching part.
|
||||
int remove = current_command_.size() - match;
|
||||
if (remove > 0) {
|
||||
print_cb_(n_backspaces(remove));
|
||||
drvutil::console_write(n_backspaces(remove));
|
||||
current_command_ = current_command_.substr(0, match);
|
||||
}
|
||||
|
||||
// Echo the new part.
|
||||
std::u32string newpart = desired_command_.substr(current_command_.size());
|
||||
if (!newpart.empty()) {
|
||||
print_cb_(newpart);
|
||||
drvutil::console_write(newpart);
|
||||
current_command_ = desired_command_;
|
||||
}
|
||||
}
|
||||
|
||||
std::string ReadlineDevice::putcode(char32_t c) {
|
||||
std::scoped_lock lock(mutex_);
|
||||
if ((c == '\n') && (readline_lastc_ == '\r')) {
|
||||
// Ignore newline immediately after carriage return.
|
||||
// Otherwise, crlf produces two newlines.
|
||||
return "";
|
||||
} else if ((c == '\r') || (c == '\n')) {
|
||||
echo_command();
|
||||
print_cb_(white_ + newline_);
|
||||
drvutil::console_write(white_ + newline_);
|
||||
std::u32string result = desired_command_ + newline_;
|
||||
desired_command_.clear();
|
||||
current_prompt_.clear();
|
||||
current_command_.clear();
|
||||
echo_command();
|
||||
return drvutil::utf32_to_utf8(result);
|
||||
} else if ((c == '\b') || (c == 127)) {
|
||||
int len = desired_command_.size();
|
||||
@@ -105,11 +102,12 @@ std::string ReadlineDevice::putcode(char32_t c) {
|
||||
|
||||
|
||||
void ReadlineDevice::printline(std::string_view s) {
|
||||
std::scoped_lock lock(mutex_);
|
||||
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_);
|
||||
drvutil::console_write(utf32);
|
||||
if (missing_newline) drvutil::console_write(newline_);
|
||||
echo_command();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <mutex>
|
||||
#include "drvutil.hpp"
|
||||
|
||||
|
||||
@@ -12,7 +13,7 @@ public:
|
||||
using print_callback = void (*)(const std::u32string &text);
|
||||
|
||||
private:
|
||||
print_callback print_cb_;
|
||||
std::mutex mutex_;
|
||||
std::u32string desired_command_;
|
||||
std::u32string current_command_;
|
||||
std::u32string desired_prompt_;
|
||||
@@ -24,9 +25,6 @@ private:
|
||||
|
||||
|
||||
public:
|
||||
// The callback must be set before using the readline device.
|
||||
void set_print_callback(print_callback cb);
|
||||
|
||||
// change the prompt.
|
||||
void set_prompt(std::string_view prompt);
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "drvutil.hpp"
|
||||
#include "osdrvutil.hpp"
|
||||
#include "sslutil.hpp"
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
Reference in New Issue
Block a user