Implement more sophisticated readline-mode
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user