#include "wrap-string.hpp" #include "wrap-vector.hpp" #include #include #include #include "drivenengine.hpp" DrivenEngineReg *DrivenEngineReg::All; DrivenEngineReg::DrivenEngineReg(const char *n, DrivenEngineMaker fn) { name = n; maker = fn; next = All; All = this; } void DrivenEngine::print_usage(std::ostream &strm, const char *progname) { strm << "Usage: " << progname << " " << std::endl; for (auto reg = DrivenEngineReg::All; reg != nullptr; reg=reg->next) { strm << " Mode can be: " << reg->name << std::endl; } } UniqueDrivenEngine DrivenEngine::make(const char *kind) { for (auto reg = DrivenEngineReg::All; reg != nullptr; reg=reg->next) { if (strcmp(reg->name, kind) == 0) { return reg->maker(); } } return nullptr; } Channel::Channel(DrivenEngine *de, int chid, int port, const eng::string &target, bool stop) { chid_ = chid; port_ = port; closed_ = false; target_ = target; readline_lastc_ = 0; desired_prompt_ = ""; stop_driver_ = stop; sb_in_ = eng::make_shared(); sb_out_ = eng::make_shared(); sb_drvout_ = sb_out_; } void Channel::erase_command() { int ccsize = current_prompt_.size() + current_command_.size(); if (ccsize > 0) { sb_drvout_->write_bytes(util::repeat_string("\b \b", ccsize)); current_prompt_ = ""; current_command_ = ""; } } void Channel::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(); sb_drvout_->write_bytes(util::repeat_string("\b \b", ccsize)); sb_drvout_->write_bytes(desired_prompt_); current_command_ = ""; current_prompt_ = desired_prompt_; } // Find out how much of the command matches. int match = util::common_prefix_length(current_command_, desired_command_); // Echo backspaces to remove the non-matching part. int remove = current_command_.size() - match; if (remove > 0) { sb_drvout_->write_bytes(util::repeat_string("\b \b", remove)); current_command_ = current_command_.substr(0, match); } // Echo the new part. eng::string newpart = desired_command_.substr(current_command_.size()); if (newpart != "") { sb_drvout_->write_bytes(newpart); current_command_ = desired_command_; } } void Channel::set_prompt(const eng::string &p) { desired_prompt_ = p; } void Channel::feed_readline(std::string_view data) { int nbytes = data.size(); const char *bytes = data.data(); for (int i = 0; i < nbytes; i++) { char c = bytes[i]; if ((c == '\n') && (readline_lastc_ == '\r')) { // Ignore newline immediately after carriage return. // Otherwise, crlf produces two newlines. } else if ((c == '\r') || (c == '\n')) { echo_command(); sb_drvout_->write_bytes(eng::string(" \n")); sb_in_->write_bytes(desired_command_); sb_in_->write_uint8('\n'); desired_command_ = ""; current_prompt_ = ""; current_command_ = ""; } else if ((c == '\b') || (c == 127)) { int len = desired_command_.size(); if (len > 0) { desired_command_ = desired_command_.substr(0, len-1); } } else if (c >= 32) { int len = desired_command_.size(); if (len < READLINE_MAX) { desired_command_ = desired_command_ + c; } } readline_lastc_ = c; } } std::string_view Channel::peek_outgoing() const { return sb_drvout_->view(); } void Channel::pump_readline() { if (sb_drvout_ != sb_out_) { if (!sb_out_->empty()) { erase_command(); sb_out_->transfer_into(sb_drvout_.get()); } echo_command(); } } void Channel::sent_outgoing(int nbytes) { sb_drvout_->read_bytes(nbytes); } int DrivenEngine::find_unused_chid() { // Note: channel ID zero is special, it is never reused. for (int i = 0; i < MAX_CHAN; i++) { int id = next_unused_chid_++; if (next_unused_chid_ == MAX_CHAN) next_unused_chid_ = 1; if (channels_[id] == nullptr) return id; } assert(false); return 0; } Channel *DrivenEngine::get_chid(int chid) const { assert(unsigned(chid) < MAX_CHAN); assert(channels_[chid].get() != nullptr); return channels_[chid].get(); } void DrivenEngine::listen_port(int port) { listen_ports_.push_back(port); } double DrivenEngine::get_clock() { return clock_; } SharedChannel DrivenEngine::new_outgoing_channel(const eng::string &target) { int chid = find_unused_chid(); new_outgoing_.push_back(chid); SharedChannel result = eng::make_shared(this, chid, 0, target, stop_driver_); channels_[chid] = result; return result; } SharedChannel DrivenEngine::new_incoming_channel() { if (accepted_channels_.empty()) { return nullptr; } else { SharedChannel result = std::move(accepted_channels_.back()); accepted_channels_.pop_back(); return result; } } SharedChannel DrivenEngine::get_stdio_channel() { return stdio_channel_; } util::LuaSourcePtr DrivenEngine::get_lua_source() { return std::move(lua_source_); } void DrivenEngine::rescan_lua_source() { rescan_lua_source_ = true; } void DrivenEngine::stop_driver() { stop_driver_ = true; for (int i = 0; i < MAX_CHAN; i++) { if (channels_[i] != nullptr) { channels_[i]->stop_driver_ = true; } } } const eng::vector &DrivenEngine::drv_get_listen_ports() const { return listen_ports_; } const eng::vector &DrivenEngine::drv_get_new_outgoing() const { return new_outgoing_; } void DrivenEngine::drv_clear_new_outgoing() { new_outgoing_.clear(); } const eng::string &DrivenEngine::drv_get_target(int chid) const { return get_chid(chid)->target_; } bool DrivenEngine::drv_outgoing_empty(int chid) const { std::string_view view = drv_peek_outgoing(chid); return (view.size() == 0); } bool DrivenEngine::drv_get_channel_released(int chid) const { return channels_[chid].use_count() == 1; } std::string_view DrivenEngine::drv_peek_outgoing(int chid) const { return get_chid(chid)->peek_outgoing(); } void DrivenEngine::drv_sent_outgoing(int chid, int nbytes) { return get_chid(chid)->sent_outgoing(nbytes); } void DrivenEngine::drv_recv_incoming(int chid, std::string_view data) { if (data.size() > 0) { Channel *ch = get_chid(chid); if (ch->sb_drvout_ != ch->sb_out_) { ch->feed_readline(data); } else { ch->sb_in_->write_bytes(data); } } } void DrivenEngine::drv_notify_close(int chid, std::string_view err) { Channel *ch = get_chid(chid); ch->closed_ = true; ch->error_ = err; channels_[chid].reset(); } int DrivenEngine::drv_notify_accept(int port) { int chid = find_unused_chid(); channels_[chid] = eng::make_shared(this, chid, port, "", stop_driver_); accepted_channels_.push_back(channels_[chid]); return chid; } void DrivenEngine::drv_clear_lua_source() { lua_source_.reset(); rescan_lua_source_ = false; } void DrivenEngine::drv_add_lua_source(std::string_view fn, std::string_view data) { if (lua_source_ == nullptr) { lua_source_.reset(new util::LuaSourceVec); } lua_source_->emplace_back(eng::string(fn), eng::string(data)); } void DrivenEngine::drv_invoke_event_init(int argc, char *argv[]) { event_init(argc, argv); stdio_channel_->pump_readline(); } void DrivenEngine::drv_invoke_event_update(double clock) { clock_ = clock; event_update(); stdio_channel_->pump_readline(); } bool DrivenEngine::drv_get_rescan_lua_source() const { return rescan_lua_source_; } bool DrivenEngine::drv_get_stop_driver() const { return stop_driver_; } DrivenEngine::DrivenEngine() { next_unused_chid_ = 1; stdio_channel_ = eng::make_shared(this, 0, 0, "", false); stdio_channel_->sb_drvout_ = eng::make_shared(); channels_[0] = stdio_channel_; rescan_lua_source_ = true; clock_ = 0.0; stop_driver_ = false; } DrivenEngine::~DrivenEngine() {} static DrivenEngine *engine_; void DrivenEngine::set(DrivenEngine *de) { engine_ = de; } DrivenEngine *DrivenEngine::get() { return engine_; }