Implement more sophisticated readline-mode

This commit is contained in:
2021-11-04 13:40:42 -04:00
parent cf5a2d7462
commit deaf4e4873
7 changed files with 150 additions and 64 deletions

View File

@@ -10,9 +10,8 @@ Channel::Channel(DrivenEngine *de, int chid, int port, const std::string &target
closed_ = false;
target_ = target;
readline_enabled_ = (chid == 0);
readline_len_ = 0;
readline_lastc_ = 0;
echo_len_ = 0;
desired_prompt_ = "] ";
assert(driven_->channels_[chid_] == nullptr);
driven_->channels_[chid_] = this;
}
@@ -24,6 +23,47 @@ Channel::~Channel() {
driven_->channels_[chid_] = nullptr;
}
void Channel::show_command() {
// If the prompt has changed, erase everything and start over.
if (desired_prompt_ != current_prompt_) {
int ccsize = current_prompt_.size() + current_command_.size();
readline_echo_ += util::repeat_string("\b \b", ccsize);
readline_echo_ += 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) {
readline_echo_ += 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 != "") {
readline_echo_ += newpart;
current_command_ = desired_command_;
}
}
void Channel::hide_command() {
int ccsize = current_command_.size() + current_prompt_.size();
if (ccsize > 0) {
readline_echo_ += util::repeat_string("\b \b", ccsize);
current_command_ = "";
current_prompt_ = "";
}
}
void Channel::set_prompt(const std::string &p) {
desired_prompt_ = p;
if (readline_enabled_) show_command();
}
void Channel::feed_readline(int nbytes, const char *bytes) {
for (int i = 0; i < nbytes; i++) {
@@ -32,39 +72,61 @@ void Channel::feed_readline(int nbytes, const char *bytes) {
// Ignore newline immediately after carriage return.
// Otherwise, crlf produces two newlines.
} else if ((c == '\r') || (c == '\n')) {
if ((echo_space() >= 3) && (readline_space() >= 1)) {
echo_buf_[echo_len_++] = ' ';
echo_buf_[echo_len_++] = '\r';
echo_buf_[echo_len_++] = '\n';
readline_buf_[readline_len_++] = '\n';
sb_in_->write_bytes(readline_buf_, readline_len_);
readline_len_ = 0;
}
show_command();
readline_echo_ = readline_echo_ + " \n";
sb_in_->write_bytes(desired_command_);
sb_in_->write_uint8('\n');
desired_command_ = "";
current_prompt_ = "";
current_command_ = "";
} else if (c == '\b') {
if ((readline_len_ >= 1) && (echo_space() >= 3)) {
echo_buf_[echo_len_++] = '\b';
echo_buf_[echo_len_++] = ' ';
echo_buf_[echo_len_++] = '\b';
readline_len_ -= 1;
int len = desired_command_.size();
if (len > 0) {
desired_command_ = desired_command_.substr(0, len-1);
}
} else if (c >= 32) {
// Don't use up the last character in the readline buffer: save
// it for the newline.
if ((readline_space() >= 2) && (echo_space() >= 1)) {
echo_buf_[echo_len_++] = c;
readline_buf_[readline_len_++] = c;
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 (sb_out_->fill() > 0) {
hide_command();
} else if (readline_enabled_) {
show_command();
}
if (readline_echo_.size() > 0) {
*nbytes = readline_echo_.size();
*bytes = readline_echo_.c_str();
} else {
*nbytes = sb_out_->fill();
*bytes = sb_out_->data();
}
}
void Channel::sent_outgoing(int nbytes) {
if (nbytes > 0) {
if (readline_echo_.size() > 0) {
if (nbytes < int(readline_echo_.size())) {
readline_echo_ = readline_echo_.substr(nbytes);
return;
}
nbytes -= readline_echo_.size();
readline_echo_ = "";
}
sb_out_->read_bytes(nbytes);
}
}
void Channel::set_readline(bool e) {
if (e != readline_enabled_) {
readline_enabled_ = e;
readline_len_ = 0;
readline_lastc_ = 0;
echo_len_ = 0;
desired_command_ = "";
}
}
@@ -148,31 +210,11 @@ bool DrivenEngine::drv_outgoing_empty(int chid) {
}
void DrivenEngine::drv_peek_outgoing(int chid, int *nbytes, const char **bytes) {
Channel *ch = get_chid(chid);
if (ch->echo_len_ > 0) {
*nbytes = ch->echo_len_;
*bytes = ch->echo_buf_;
} else {
*nbytes = ch->sb_out_->fill();
*bytes = ch->sb_out_->data();
}
return get_chid(chid)->peek_outgoing(nbytes, bytes);
}
void DrivenEngine::drv_sent_outgoing(int chid, int nbytes) {
Channel *ch = get_chid(chid);
if (nbytes > 0) {
if (ch->echo_len_ > 0) {
if (nbytes >= ch->echo_len_) {
ch->sb_out_->read_bytes(nbytes - ch->echo_len_);
ch->echo_len_ = 0;
} else {
ch->echo_len_ -= nbytes;
memmove(ch->echo_buf_, ch->echo_buf_ + nbytes, ch->echo_len_);
}
} else {
ch->sb_out_->read_bytes(nbytes);
}
}
return get_chid(chid)->sent_outgoing(nbytes);
}
void DrivenEngine::drv_recv_incoming(int chid, int nbytes, const char *bytes) {