int prompt_loop(mutex * lock, CommandHistory & history) { int fd = STDIN_FILENO; size_t plen = prompt.size(); int history_index = 0; raw_buffer.clear(); raw_cursor = 0; /* The latest history entry is always our current buffer, that * initially is just an empty string. */ const std::string empty; history.add(empty); if (::write(fd,prompt.c_str(),prompt.size()) == -1) return -1; while(1) { unsigned char c; int isok; unsigned char seq[2], seq2; lock->unlock(); if(!read_char(c)) { lock->lock(); return -2; } lock->lock(); /* Only autocomplete when the callback is set. It returns < 0 when * there was an error reading from fd. Otherwise it will return the * character that should be handled next. */ if (c == 9) { /* if( completionCallback != NULL) { c = completeLine(fd,prompt,buf,buflen,&len,&pos,cols); // Return on errors if (c < 0) return len; // Read next character when 0 if (c == 0) continue; } else { // ignore tab continue; } */ // just ignore tabs continue; } switch(c) { case 13: // enter history.remove(); return raw_buffer.size(); case 3: // ctrl-c errno = EAGAIN; return -1; case 127: // backspace case 8: // ctrl-h if (raw_cursor > 0 && raw_buffer.size() > 0) { raw_buffer.erase(raw_cursor-1,1); raw_cursor--; prompt_refresh(); } break; case 27: // escape sequence lock->unlock(); if(!read_char(seq[0]) || !read_char(seq[1])) { lock->lock(); return -2; } lock->lock(); if(seq[0] == '[') { if (seq[1] == 'D') { left_arrow: if (raw_cursor > 0) { raw_cursor--; prompt_refresh(); } } else if ( seq[1] == 'C') { right_arrow: /* right arrow */ if (raw_cursor != raw_buffer.size()) { raw_cursor++; prompt_refresh(); } } else if (seq[1] == 'A' || seq[1] == 'B') { /* up and down arrow: history */ if (history.size() > 1) { /* Update the current history entry before to * overwrite it with tne next one. */ history[history_index] = raw_buffer; /* Show the new entry */ history_index += (seq[1] == 'A') ? 1 : -1; if (history_index < 0) { history_index = 0; break; } else if (history_index >= history.size()) { history_index = history.size()-1; break; } raw_buffer = history[history_index]; raw_cursor = raw_buffer.size(); prompt_refresh(); } } else if(seq[1] == 'H') { // home raw_cursor = 0; prompt_refresh(); } else if(seq[1] == 'F') { // end raw_cursor = raw_buffer.size(); prompt_refresh(); } else if (seq[1] > '0' && seq[1] < '7') { // extended escape lock->unlock(); if(!read_char(seq2)) { lock->lock(); return -2; } lock->lock(); if (seq[1] == '3' && seq2 == '~' ) { // delete if (raw_buffer.size() > 0 && raw_cursor < raw_buffer.size()) { raw_buffer.erase(raw_cursor,1); prompt_refresh(); } } } } break; default: if (raw_buffer.size() == raw_cursor) { raw_buffer.append(1,c); raw_cursor++; if (plen+raw_buffer.size() < get_columns()) { /* Avoid a full update of the line in the * trivial case. */ if (::write(fd,&c,1) == -1) return -1; } else { prompt_refresh(); } } else { raw_buffer.insert(raw_cursor,1,c); raw_cursor++; prompt_refresh(); } break; case 21: // Ctrl+u, delete the whole line. raw_buffer.clear(); raw_cursor = 0; prompt_refresh(); break; case 11: // Ctrl+k, delete from current to end of line. raw_buffer.erase(raw_cursor); prompt_refresh(); break; case 1: // Ctrl+a, go to the start of the line raw_cursor = 0; prompt_refresh(); break; case 5: // ctrl+e, go to the end of the line raw_cursor = raw_buffer.size(); prompt_refresh(); break; case 12: // ctrl+l, clear screen clear(); prompt_refresh(); } } return raw_buffer.size(); }