#include "drivenengine.hpp" Channel::Channel(DrivenEngine *de, int chid, int port, const std::string &target, bool stop) { chid_ = chid; port_ = port; closed_ = false; target_ = target; readline_enabled_ = false; readline_lastc_ = 0; desired_prompt_ = ""; stop_driver_ = stop; sb_in_ = std::make_shared(); sb_out_ = std::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. std::string newpart = desired_command_.substr(current_command_.size()); if (newpart != "") { sb_drvout_->write_bytes(newpart); current_command_ = desired_command_; } } void Channel::set_prompt(const std::string &p) { desired_prompt_ = p; } void Channel::feed_readline(int nbytes, const char *bytes) { 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(std::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; } } void Channel::peek_outgoing(int *nbytes, const char **bytes) { if (readline_enabled_) { if (!sb_out_->empty()) { erase_command(); sb_out_->transfer_into(sb_drvout_.get()); } echo_command(); } *nbytes = sb_drvout_->fill(); *bytes = sb_drvout_->data(); } void Channel::sent_outgoing(int nbytes) { sb_drvout_->read_bytes(nbytes); } void Channel::set_readline(bool e) { if (e != readline_enabled_) { readline_enabled_ = e; if (readline_enabled_) { sb_drvout_ = std::make_shared(); } else { sb_out_->transfer_into(sb_drvout_.get()); sb_out_->clear(); sb_drvout_->transfer_into(sb_out_.get()); sb_drvout_ = sb_out_; } desired_command_ = ""; } } 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) { assert(unsigned(chid) < MAX_CHAN); assert(channels_[chid].get() != nullptr); return channels_[chid].get(); } void DrivenEngine::listen_port(int port) { listen_ports_.insert(port); } double DrivenEngine::get_clock() { return clock_; } SharedChannel DrivenEngine::new_outgoing_channel(const std::string &target) { int chid = find_unused_chid(); new_outgoing_.insert(chid); SharedChannel result = std::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; } } } void DrivenEngine::drv_get_listen_ports(std::set &ports) { ports = listen_ports_; } void DrivenEngine::drv_get_new_outgoing(std::set &channels) { channels = std::move(new_outgoing_); new_outgoing_.clear(); } const std::string &DrivenEngine::drv_get_target(int chid) { return get_chid(chid)->target_; } bool DrivenEngine::drv_outgoing_empty(int chid) { int nbytes; const char *bytes; drv_peek_outgoing(chid, &nbytes, &bytes); return (nbytes == 0); } bool DrivenEngine::drv_get_channel_released(int chid) { return channels_[chid].use_count() == 1; } void DrivenEngine::drv_peek_outgoing(int chid, int *nbytes, const char **bytes) { return get_chid(chid)->peek_outgoing(nbytes, bytes); } void DrivenEngine::drv_sent_outgoing(int chid, int nbytes) { return get_chid(chid)->sent_outgoing(nbytes); } void DrivenEngine::drv_recv_incoming(int chid, int nbytes, const char *bytes) { if (nbytes > 0) { Channel *ch = get_chid(chid); if (ch->readline_enabled_) { ch->feed_readline(nbytes, bytes); } else { ch->sb_in_->write_bytes(bytes, nbytes); } } } void DrivenEngine::drv_notify_close(int chid, const std::string &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] = std::make_shared(this, chid, port, "", stop_driver_); accepted_channels_.push_back(channels_[chid]); return chid; } void DrivenEngine::drv_set_lua_source(util::LuaSourcePtr source) { lua_source_ = std::move(source); rescan_lua_source_ = false; } void DrivenEngine::drv_invoke_event_init(int argc, char *argv[]) { event_init(argc, argv); } void DrivenEngine::drv_invoke_event_update(double clock) { clock_ = clock; event_update(); } bool DrivenEngine::drv_get_rescan_lua_source() { return rescan_lua_source_; } bool DrivenEngine::drv_get_stop_driver() { return stop_driver_; } DrivenEngine::DrivenEngine() { next_unused_chid_ = 1; stdio_channel_ = std::make_shared(this, 0, 0, "", false); stdio_channel_->set_readline(true); 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_; }