On server, channel dprint through the readline-device. Also some refactors and quality improvements.
This commit is contained in:
@@ -86,7 +86,7 @@ BASE_CORE := \
|
|||||||
world-difftab world-diffxmit world-pairtab world-testing lpxserver lpxclient \
|
world-difftab world-diffxmit world-pairtab world-testing lpxserver lpxclient \
|
||||||
eng-tests printbuffer serializelua
|
eng-tests printbuffer serializelua
|
||||||
|
|
||||||
BASE_DRV := driver drvutil osdrvutil sslutil readline
|
BASE_DRV := driver drvutil sslutil readline
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "drvutil.hpp"
|
#include "drvutil.hpp"
|
||||||
#include "osdrvutil.hpp"
|
|
||||||
#include "sslutil.hpp"
|
#include "sslutil.hpp"
|
||||||
#include "readline.hpp"
|
#include "readline.hpp"
|
||||||
#include "../core/enginewrapper.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.
|
// 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) {
|
static void call_init_engine_wrapper(const std::filesystem::path &luprexroot, EngineWrapper *w) {
|
||||||
using InitFn = void (*)(EngineWrapper *);
|
using InitFn = void (*)(EngineWrapper *);
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
#define _WIN32_WINNT 0x0600
|
#define _WIN32_WINNT 0x0600
|
||||||
|
|
||||||
#include "drvutil.hpp"
|
#include "drvutil.hpp"
|
||||||
#include "osdrvutil.hpp"
|
|
||||||
#include "sslutil.hpp"
|
#include "sslutil.hpp"
|
||||||
#include "readline.hpp"
|
#include "readline.hpp"
|
||||||
#include "../core/enginewrapper.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) {
|
static void ssl_load_certificate_authorities(SSL_CTX *ctx) {
|
||||||
HCERTSTORE hStore = CertOpenSystemStoreW(0, L"ROOT");
|
HCERTSTORE hStore = CertOpenSystemStoreW(0, L"ROOT");
|
||||||
|
|||||||
@@ -11,6 +11,11 @@
|
|||||||
#define POLLVEC_SIZE (DRV_MAX_CHAN + 1)
|
#define POLLVEC_SIZE (DRV_MAX_CHAN + 1)
|
||||||
#define MAX_BIO_BUFFER (128 * 1024)
|
#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) {
|
static void if_error_print_and_exit(const std::string_view str) {
|
||||||
if (!str.empty()) {
|
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) {
|
inline bool file_exists(const std::filesystem::path &name) {
|
||||||
std::ifstream f(name);
|
std::ifstream f(name);
|
||||||
return f.good();
|
return f.good();
|
||||||
@@ -96,7 +91,6 @@ class Driver {
|
|||||||
bool read_console_recently_;
|
bool read_console_recently_;
|
||||||
std::unique_ptr<struct pollfd[]> pollvec_;
|
std::unique_ptr<struct pollfd[]> pollvec_;
|
||||||
std::unique_ptr<char[]> chbuf_;
|
std::unique_ptr<char[]> chbuf_;
|
||||||
ReadlineDevice readline_device_;
|
|
||||||
std::string console_command_;
|
std::string console_command_;
|
||||||
|
|
||||||
sslutil::UniqueCTX ssl_server_ctx_;
|
sslutil::UniqueCTX ssl_server_ctx_;
|
||||||
@@ -209,7 +203,7 @@ class Driver {
|
|||||||
engw.play_access(&engw, AccessKind::CHANNEL_PRINTS, 0, 0, "", &ndata, &data);
|
engw.play_access(&engw, AccessKind::CHANNEL_PRINTS, 0, 0, "", &ndata, &data);
|
||||||
if (ndata > 0) {
|
if (ndata > 0) {
|
||||||
if (ndata > DRV_SHORTSTRING_SIZE) ndata = DRV_SHORTSTRING_SIZE;
|
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();
|
inject_lua_source();
|
||||||
}
|
}
|
||||||
else {
|
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,
|
engw.play_access(&engw, AccessKind::INVOKE_LUA_EXPR,
|
||||||
0, cmd.size(), cmd.c_str(), nullptr, nullptr);
|
0, cmd.size(), cmd.c_str(), nullptr, nullptr);
|
||||||
} else {
|
} else {
|
||||||
readline_device_.printline(message);
|
readline_device.printline(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (console_command_.empty()) {
|
if (console_command_.empty()) {
|
||||||
readline_device_.set_prompt(">");
|
readline_device.set_prompt(">");
|
||||||
} else {
|
} else {
|
||||||
readline_device_.set_prompt(">>");
|
readline_device.set_prompt(">>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_console_input() {
|
void handle_console_input() {
|
||||||
read_console_recently_ = false;
|
read_console_recently_ = false;
|
||||||
while (true) {
|
while (true) {
|
||||||
std::u32string cps = console_read();
|
std::u32string cps = drvutil::console_read();
|
||||||
if (cps.size() == 0) break;
|
if (cps.size() == 0) break;
|
||||||
read_console_recently_ = true;
|
read_console_recently_ = true;
|
||||||
for (char32_t c : cps) {
|
for (char32_t c : cps) {
|
||||||
std::string line = readline_device_.putcode(c);
|
std::string line = readline_device.putcode(c);
|
||||||
if (!line.empty()) {
|
if (!line.empty()) {
|
||||||
add_console_command(line);
|
add_console_command(line);
|
||||||
}
|
}
|
||||||
@@ -631,8 +625,7 @@ class Driver {
|
|||||||
|
|
||||||
int drive(int argc, char *argv[]) {
|
int drive(int argc, char *argv[]) {
|
||||||
// Set up the console readline device.
|
// Set up the console readline device.
|
||||||
readline_device_.set_print_callback(console_write);
|
readline_device.set_prompt(">");
|
||||||
readline_device_.set_prompt(">");
|
|
||||||
console_command_.clear();
|
console_command_.clear();
|
||||||
|
|
||||||
// Remove the program name from argv.
|
// 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 {
|
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.
|
// Read the lua source from disk into an ostringstream.
|
||||||
//
|
//
|
||||||
// To pass the lua source into the DLL, here is what you do: Construct an
|
// 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;
|
return minlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadlineDevice::set_print_callback(print_callback cb) {
|
|
||||||
print_cb_ = cb;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReadlineDevice::set_prompt(std::string_view prompt) {
|
void ReadlineDevice::set_prompt(std::string_view prompt) {
|
||||||
|
std::scoped_lock lock(mutex_);
|
||||||
desired_prompt_ = drvutil::utf8_to_utf32(prompt, nullptr);
|
desired_prompt_ = drvutil::utf8_to_utf32(prompt, nullptr);
|
||||||
echo_command();
|
echo_command();
|
||||||
}
|
}
|
||||||
@@ -36,7 +33,7 @@ void ReadlineDevice::set_prompt(std::string_view prompt) {
|
|||||||
void ReadlineDevice::erase_command() {
|
void ReadlineDevice::erase_command() {
|
||||||
int ccsize = current_prompt_.size() + current_command_.size();
|
int ccsize = current_prompt_.size() + current_command_.size();
|
||||||
if (ccsize > 0) {
|
if (ccsize > 0) {
|
||||||
print_cb_(n_backspaces(ccsize));
|
drvutil::console_write(n_backspaces(ccsize));
|
||||||
current_prompt_.clear();
|
current_prompt_.clear();
|
||||||
current_command_.clear();
|
current_command_.clear();
|
||||||
}
|
}
|
||||||
@@ -46,8 +43,8 @@ void ReadlineDevice::echo_command() {
|
|||||||
// If the prompt has changed, erase everything and start over.
|
// If the prompt has changed, erase everything and start over.
|
||||||
if (desired_prompt_ != current_prompt_) {
|
if (desired_prompt_ != current_prompt_) {
|
||||||
int ccsize = current_prompt_.size() + current_command_.size();
|
int ccsize = current_prompt_.size() + current_command_.size();
|
||||||
print_cb_(n_backspaces(ccsize));
|
drvutil::console_write(n_backspaces(ccsize));
|
||||||
print_cb_(desired_prompt_);
|
drvutil::console_write(desired_prompt_);
|
||||||
current_command_.clear();
|
current_command_.clear();
|
||||||
current_prompt_ = desired_prompt_;
|
current_prompt_ = desired_prompt_;
|
||||||
}
|
}
|
||||||
@@ -58,31 +55,31 @@ void ReadlineDevice::echo_command() {
|
|||||||
// Echo backspaces to remove the non-matching part.
|
// Echo backspaces to remove the non-matching part.
|
||||||
int remove = current_command_.size() - match;
|
int remove = current_command_.size() - match;
|
||||||
if (remove > 0) {
|
if (remove > 0) {
|
||||||
print_cb_(n_backspaces(remove));
|
drvutil::console_write(n_backspaces(remove));
|
||||||
current_command_ = current_command_.substr(0, match);
|
current_command_ = current_command_.substr(0, match);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Echo the new part.
|
// Echo the new part.
|
||||||
std::u32string newpart = desired_command_.substr(current_command_.size());
|
std::u32string newpart = desired_command_.substr(current_command_.size());
|
||||||
if (!newpart.empty()) {
|
if (!newpart.empty()) {
|
||||||
print_cb_(newpart);
|
drvutil::console_write(newpart);
|
||||||
current_command_ = desired_command_;
|
current_command_ = desired_command_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ReadlineDevice::putcode(char32_t c) {
|
std::string ReadlineDevice::putcode(char32_t c) {
|
||||||
|
std::scoped_lock lock(mutex_);
|
||||||
if ((c == '\n') && (readline_lastc_ == '\r')) {
|
if ((c == '\n') && (readline_lastc_ == '\r')) {
|
||||||
// Ignore newline immediately after carriage return.
|
// Ignore newline immediately after carriage return.
|
||||||
// Otherwise, crlf produces two newlines.
|
// Otherwise, crlf produces two newlines.
|
||||||
return "";
|
return "";
|
||||||
} else if ((c == '\r') || (c == '\n')) {
|
} else if ((c == '\r') || (c == '\n')) {
|
||||||
echo_command();
|
echo_command();
|
||||||
print_cb_(white_ + newline_);
|
drvutil::console_write(white_ + newline_);
|
||||||
std::u32string result = desired_command_ + newline_;
|
std::u32string result = desired_command_ + newline_;
|
||||||
desired_command_.clear();
|
desired_command_.clear();
|
||||||
current_prompt_.clear();
|
current_prompt_.clear();
|
||||||
current_command_.clear();
|
current_command_.clear();
|
||||||
echo_command();
|
|
||||||
return drvutil::utf32_to_utf8(result);
|
return drvutil::utf32_to_utf8(result);
|
||||||
} else if ((c == '\b') || (c == 127)) {
|
} else if ((c == '\b') || (c == 127)) {
|
||||||
int len = desired_command_.size();
|
int len = desired_command_.size();
|
||||||
@@ -105,11 +102,12 @@ std::string ReadlineDevice::putcode(char32_t c) {
|
|||||||
|
|
||||||
|
|
||||||
void ReadlineDevice::printline(std::string_view s) {
|
void ReadlineDevice::printline(std::string_view s) {
|
||||||
|
std::scoped_lock lock(mutex_);
|
||||||
bool missing_newline = ((s.size() == 0) || (s[s.size() - 1] != '\n'));
|
bool missing_newline = ((s.size() == 0) || (s[s.size() - 1] != '\n'));
|
||||||
std::u32string utf32 = drvutil::utf8_to_utf32(s, nullptr);
|
std::u32string utf32 = drvutil::utf8_to_utf32(s, nullptr);
|
||||||
erase_command();
|
erase_command();
|
||||||
print_cb_(utf32);
|
drvutil::console_write(utf32);
|
||||||
if (missing_newline) print_cb_(newline_);
|
if (missing_newline) drvutil::console_write(newline_);
|
||||||
echo_command();
|
echo_command();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <mutex>
|
||||||
#include "drvutil.hpp"
|
#include "drvutil.hpp"
|
||||||
|
|
||||||
|
|
||||||
@@ -12,7 +13,7 @@ public:
|
|||||||
using print_callback = void (*)(const std::u32string &text);
|
using print_callback = void (*)(const std::u32string &text);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
print_callback print_cb_;
|
std::mutex mutex_;
|
||||||
std::u32string desired_command_;
|
std::u32string desired_command_;
|
||||||
std::u32string current_command_;
|
std::u32string current_command_;
|
||||||
std::u32string desired_prompt_;
|
std::u32string desired_prompt_;
|
||||||
@@ -24,9 +25,6 @@ private:
|
|||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// The callback must be set before using the readline device.
|
|
||||||
void set_print_callback(print_callback cb);
|
|
||||||
|
|
||||||
// change the prompt.
|
// change the prompt.
|
||||||
void set_prompt(std::string_view prompt);
|
void set_prompt(std::string_view prompt);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#include "drvutil.hpp"
|
#include "drvutil.hpp"
|
||||||
#include "osdrvutil.hpp"
|
|
||||||
#include "sslutil.hpp"
|
#include "sslutil.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "slash-parser.hpp"
|
#include "slash-parser.hpp"
|
||||||
#endif
|
#endif
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -61,16 +62,18 @@ bool SlashCommandParser::Parse(std::string_view Command, std::string_view ArgTyp
|
|||||||
args_.clear();
|
args_.clear();
|
||||||
for (int i = 0; i < int(ArgTypes.size()); i++)
|
for (int i = 0; i < int(ArgTypes.size()); i++)
|
||||||
{
|
{
|
||||||
|
if (cursor_ == linelen_)
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Not enough arguments for " << Command << " (need " << ArgTypes.size() << ")";
|
||||||
|
error_ = oss.str();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
switch(ArgTypes[i])
|
switch(ArgTypes[i])
|
||||||
{
|
{
|
||||||
case 's':
|
case 's':
|
||||||
{
|
{
|
||||||
std::string word = read_word();
|
std::string word = read_word();
|
||||||
if (word.empty())
|
|
||||||
{
|
|
||||||
error_ = "Expected string arg";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
args_.emplace_back(word);
|
args_.emplace_back(word);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -82,8 +85,11 @@ bool SlashCommandParser::Parse(std::string_view Command, std::string_view ArgTyp
|
|||||||
if ((p < last) && (*p == '+')) p++;
|
if ((p < last) && (*p == '+')) p++;
|
||||||
int64_t result;
|
int64_t result;
|
||||||
auto r = std::from_chars(p, last, result, 10);
|
auto r = std::from_chars(p, last, result, 10);
|
||||||
if ((r.ec != std::errc()) || (r.ptr != last)) {
|
if ((r.ec != std::errc()) || (r.ptr != last))
|
||||||
error_ = "Expected int arg";
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << Command << " expects arg " << i << " to be an int, not '" << word << "'";
|
||||||
|
error_ = oss.str();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
args_.emplace_back(result);
|
args_.emplace_back(result);
|
||||||
@@ -91,15 +97,26 @@ bool SlashCommandParser::Parse(std::string_view Command, std::string_view ArgTyp
|
|||||||
}
|
}
|
||||||
case 'd':
|
case 'd':
|
||||||
{
|
{
|
||||||
error_ = "Double args not implemented yet.";
|
std::string word = read_word();
|
||||||
|
const char *p = word.c_str();
|
||||||
|
const char *last = p + word.size();
|
||||||
|
double result;
|
||||||
|
auto r = std::from_chars(p, last, result);
|
||||||
|
if ((r.ec != std::errc()) || (r.ptr != last))
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << Command << " expects arg " << i << " to be a double, not '" << word << "'";
|
||||||
|
error_ = oss.str();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
args_.emplace_back(result);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
error_ = "Invalid argtypes ";
|
std::ostringstream oss;
|
||||||
error_ += ArgTypes;
|
oss << "Invalid ArgTypes parameter (" << ArgTypes << ") to Parse routine for " << Command;
|
||||||
error_ += " parsing ";
|
error_ = oss.str();
|
||||||
error_ += Command;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ class SlashCommandParser
|
|||||||
|
|
||||||
void skip_white();
|
void skip_white();
|
||||||
std::string read_word();
|
std::string read_word();
|
||||||
|
void not_enough_args(const std::string &cmd);
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Initialize the slash-command parser with the full command line.
|
// Initialize the slash-command parser with the full command line.
|
||||||
|
|||||||
Reference in New Issue
Block a user