static int linenoisePrompt(int id, char *buf, size_t buflen, const char *prompt) { size_t plen = strlen(prompt); size_t pos = 0; size_t len = 0; size_t cols = TERM_COLS; int history_index = 0; buf[0] = '\0'; buflen--; /* Make sure there is always space for the nulterm */ /* The latest history entry is always our current buffer, that * initially is just an empty string. */ linenoise_internal_addhistory( id, "", LINENOISE_PUSH_EMPTY ); term_putstr( prompt, plen ); while(1) { int c; c = term_getch( TERM_INPUT_WAIT ); switch(c) { case KC_ENTER: case KC_CTRL_C: case KC_CTRL_Z: history_lengths[ id ] --; free( histories[ id ][history_lengths[ id ]] ); if( c == KC_CTRL_C ) return LINENOISE_CTRL_C; else if( c == KC_CTRL_Z ) return -1; return len; case KC_BACKSPACE: if (pos > 0 && len > 0) { memmove(buf+pos-1,buf+pos,len-pos); pos--; len--; buf[len] = '\0'; refreshLine(prompt,buf,len,pos,cols); } break; case KC_CTRL_T: /* ctrl-t */ // bogdanm: this seems to be rather useless and also a bit buggy, // so it's not enabled /* if (pos > 0 && pos < len) { int aux = buf[pos-1]; buf[pos-1] = buf[pos]; buf[pos] = aux; if (pos != len-1) pos++; refreshLine(prompt,buf,len,pos,cols); }*/ break; case KC_LEFT: /* left arrow */ if (pos > 0) { pos--; refreshLine(prompt,buf,len,pos,cols); } break; case KC_RIGHT: /* right arrow */ if (pos != len) { pos++; refreshLine(prompt,buf,len,pos,cols); } break; case KC_UP: case KC_DOWN: /* up and down arrow: history */ if (history_lengths[ id ] > 1) { /* Update the current history entry before to * overwrite it with tne next one. */ free(histories[ id ][history_lengths[ id ]-1-history_index]); histories[ id ][history_lengths[ id ]-1-history_index] = strdup(buf); /* Show the new entry */ history_index += (c == KC_UP) ? 1 : -1; if (history_index < 0) { history_index = 0; break; } else if (history_index >= history_lengths[ id ]) { history_index = history_lengths[ id ]-1; break; } strncpy(buf,histories[ id ][history_lengths[ id ]-1-history_index],buflen); buf[buflen] = '\0'; len = pos = strlen(buf); refreshLine(prompt,buf,len,pos,cols); } break; case KC_DEL: /* delete */ if (len > 0 && pos < len) { memmove(buf+pos,buf+pos+1,len-pos-1); len--; buf[len] = '\0'; refreshLine(prompt,buf,len,pos,cols); } break; case KC_HOME: /* Ctrl+a, go to the start of the line */ pos = 0; refreshLine(prompt,buf,len,pos,cols); break; case KC_END: /* ctrl+e, go to the end of the line */ pos = len; refreshLine(prompt,buf,len,pos,cols); break; case KC_CTRL_U: /* Ctrl+u, delete the whole line. */ buf[0] = '\0'; pos = len = 0; refreshLine(prompt,buf,len,pos,cols); break; case KC_CTRL_K: /* Ctrl+k, delete from current to end of line. */ buf[pos] = '\0'; len = pos; refreshLine(prompt,buf,len,pos,cols); break; default: if( isprint( c ) && len < buflen ) { if(len == pos) { buf[pos] = c; pos++; len++; buf[len] = '\0'; if (plen+len < cols) { /* Avoid a full update of the line in the * trivial case. */ term_putch( c ); } else { refreshLine(prompt,buf,len,pos,cols); } } else { memmove(buf+pos+1,buf+pos,len-pos); buf[pos] = c; len++; pos++; buf[len] = '\0'; refreshLine(prompt,buf,len,pos,cols); } } break; } } return len; }
int linenoise_addhistory( int id, const char *line ) { return linenoise_internal_addhistory( id, line, LINENOISE_DONT_PUSH_EMPTY ); }
//-------------------------------------------------------------------- int linenoisePrompt(int id, char *buf, int buflen, const char *prompt) { size_t plen = strlen(prompt); size_t pos = 0; size_t len = 0; size_t cols = term_num_cols; int history_index = 0; int c; int ins = 1; buf[0] = '\0'; buflen--; // Make sure there is always space for the nulterm // The latest history entry is always our current buffer, that initially is just an empty string. linenoise_internal_addhistory( id, "", LINENOISE_PUSH_EMPTY ); term_putstr( prompt, plen ); term_curs(0); while(1) { c = term_getch(TERM_INPUT_WAIT); if (c < 0) continue; switch(c) { case KC_ENTER: history_lengths[ id ] --; free( histories[ id ][history_lengths[ id ]] ); return len; break; case KC_CTRL_D: if (len == 0) return LINENOISE_CTRL_D; break; case KC_CTRL_C: len = 0; pos = 0; buf[len] = '\0'; refreshLine(prompt,buf,len,pos,cols); break; case KC_BACKSPACE: if (pos > 0 && len > 0) { memmove(buf+pos-1,buf+pos,len-pos); pos--; len--; buf[len] = '\0'; refreshLine(prompt,buf,len,pos,cols); } break; case KC_INS: ins ^= 1; if (ins) term_curs(0); else term_curs(4); break; case KC_CTRL_T: // bogdanm: this seems to be rather useless and also a bit buggy, // so it's not enabled /* if (pos > 0 && pos < len) { int aux = buf[pos-1]; buf[pos-1] = buf[pos]; buf[pos] = aux; if (pos != len-1) pos++; refreshLine(prompt,buf,len,pos,cols); } */ break; case KC_LEFT: // left arrow if (pos > 0) { pos--; refreshLine(prompt,buf,len,pos,cols); } break; case KC_RIGHT: // right arrow if (pos != len) { pos++; refreshLine(prompt,buf,len,pos,cols); } break; case KC_UP: case KC_DOWN: // up and down arrow: history if (history_lengths[ id ] > 1) { // Update the current history entry before to overwrite it with tne next one. free(histories[ id ][history_lengths[ id ]-1-history_index]); histories[ id ][history_lengths[ id ]-1-history_index] = strdup(buf); // Show the new entry history_index += (c == KC_UP) ? 1 : -1; if (history_index < 0) { history_index = 0; break; } else if (history_index >= history_lengths[ id ]) { history_index = history_lengths[ id ]-1; break; } strncpy(buf, histories[ id ][history_lengths[ id ]-1-history_index], buflen); buf[buflen] = '\0'; if ((strlen(buf) > 7) && (strstr(buf, "return ")) == buf) { memmove(buf+1, buf+7, strlen(buf)-6); buf[0] = '='; } len = pos = strlen(buf); refreshLine(prompt,buf,len,pos,cols); } break; case KC_DEL: // delete if (len > 0 && pos < len) { memmove(buf+pos,buf+pos+1,len-pos-1); len--; buf[len] = '\0'; refreshLine(prompt,buf,len,pos,cols); } break; case KC_HOME: // Ctrl+a, go to the start of the line pos = 0; refreshLine(prompt,buf,len,pos,cols); break; case KC_END: // ctrl+e, go to the end of the line pos = len; refreshLine(prompt,buf,len,pos,cols); break; case KC_CTRL_U: // Ctrl+u, delete the whole line. buf[0] = '\0'; pos = len = 0; refreshLine(prompt,buf,len,pos,cols); break; case KC_CTRL_K: // Ctrl+k, delete from current to end of line. buf[pos] = '\0'; len = pos; refreshLine(prompt,buf,len,pos,cols); break; default: if( isprint( c ) && len < buflen ) { if(len == pos) { buf[pos] = c; pos++; len++; buf[len] = '\0'; if (plen+len < cols) { // Avoid a full update of the line in the trivial case. term_putch( c ); } else { refreshLine(prompt,buf,len,pos,cols); } } else { if (ins) { memmove(buf+pos+1,buf+pos,len-pos); buf[pos] = c; len++; pos++; buf[len] = '\0'; } else { buf[pos] = c; pos++; } refreshLine(prompt,buf,len,pos,cols); } } break; } } return len; }