forked from test34/can_wizard
Multiplexing: code refactored into calls for each step.
from orig commit 0d66aac
This commit is contained in:
parent
d64bc6bd87
commit
d015138236
1 changed files with 278 additions and 167 deletions
|
@ -281,7 +281,7 @@ static void freeCompletions(linenoiseCompletions *lc) {
|
||||||
free(lc->cvec);
|
free(lc->cvec);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is an helper function for linenoiseEdit() and is called when the
|
/* This is an helper function for linenoiseEdit*() and is called when the
|
||||||
* user types the <tab> key in order to complete the string currently in the
|
* user types the <tab> key in order to complete the string currently in the
|
||||||
* input.
|
* input.
|
||||||
*
|
*
|
||||||
|
@ -706,119 +706,210 @@ void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
|
||||||
refreshLine(l);
|
refreshLine(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function is the core of the line editing capability of linenoise.
|
/* This function is part of the multiplexed API of Linenoise, that is used
|
||||||
* It expects 'fd' to be already in "raw mode" so that every key pressed
|
* in order to implement the blocking variant of the API but can also be
|
||||||
* will be returned ASAP to read().
|
* called by the user directly in an event driven program. It will:
|
||||||
*
|
*
|
||||||
* The resulting string is put into 'buf' when the user type enter, or
|
* 1. Initialize the linenoise state passed by the user.
|
||||||
* when ctrl+d is typed.
|
* 2. Put the terminal in RAW mode.
|
||||||
|
* 3. Show the prompt.
|
||||||
|
* 4. Return control to the user, that will have to call linenoiseEditFeed()
|
||||||
|
* each time there is some data arriving in the standard input.
|
||||||
*
|
*
|
||||||
* The function returns the length of the current buffer. */
|
* The user can also call linenoiseEditHide() and linenoiseEditShow() if it
|
||||||
static int linenoiseEdit(char *buf, size_t buflen, const char *prompt)
|
* is required to show some input arriving asyncronously, without mixing
|
||||||
{
|
* it with the currently edited line.
|
||||||
struct linenoiseState l;
|
*
|
||||||
|
* When linenoiseEditFeed() returns non-NULL, the user finished with the
|
||||||
|
* line editing session (pressed enter CTRL-D/C): in this case the caller
|
||||||
|
* needs to call linenoiseEditStop() to put back the terminal in normal
|
||||||
|
* mode. This will not destroy the buffer, as long as the linenoiseState
|
||||||
|
* is still valid in the context of the caller.
|
||||||
|
*
|
||||||
|
* The function returns 0 on success, or -1 if writing to standard output
|
||||||
|
* fails. If stdin_fd or stdout_fd are set to -1, the default is to use
|
||||||
|
* STDIN_FILENO and STDOUT_FILENO.
|
||||||
|
*/
|
||||||
|
int linenoiseEditStart(struct linenoiseState *l, char *buf, size_t buflen, const char *prompt) {
|
||||||
/* Populate the linenoise state that we pass to functions implementing
|
/* Populate the linenoise state that we pass to functions implementing
|
||||||
* specific editing functionalities. */
|
* specific editing functionalities. */
|
||||||
l.buf = buf;
|
l->buf = buf;
|
||||||
l.buflen = buflen;
|
l->buflen = buflen;
|
||||||
l.prompt = prompt;
|
l->prompt = prompt;
|
||||||
l.plen = strlen(prompt);
|
l->plen = strlen(prompt);
|
||||||
l.oldpos = l.pos = 0;
|
l->oldpos = l->pos = 0;
|
||||||
l.len = 0;
|
l->len = 0;
|
||||||
l.cols = getColumns();
|
l->cols = getColumns();
|
||||||
l.maxrows = 0;
|
l->maxrows = 0;
|
||||||
l.history_index = 0;
|
l->history_index = 0;
|
||||||
|
|
||||||
/* Buffer starts empty. */
|
/* Buffer starts empty. */
|
||||||
l.buf[0] = '\0';
|
l->buf[0] = '\0';
|
||||||
l.buflen--; /* Make sure there is always space for the nulterm */
|
l->buflen--; /* Make sure there is always space for the nulterm */
|
||||||
|
|
||||||
/* The latest history entry is always our current buffer, that
|
/* The latest history entry is always our current buffer, that
|
||||||
* initially is just an empty string. */
|
* initially is just an empty string. */
|
||||||
linenoiseHistoryAdd("");
|
linenoiseHistoryAdd("");
|
||||||
|
|
||||||
int pos1 = getCursorPosition();
|
if (fwrite(prompt,l->plen,1,stdout) == -1) return -1;
|
||||||
if (fwrite(prompt,l.plen,1,stdout) == -1) return -1;
|
return 0;
|
||||||
int pos2 = getCursorPosition();
|
|
||||||
if (pos1 >= 0 && pos2 >= 0) {
|
|
||||||
l.plen = pos2 - pos1;
|
|
||||||
}
|
}
|
||||||
while(1) {
|
//
|
||||||
|
// int pos1 = getCursorPosition();
|
||||||
|
// if (fwrite(prompt,l.plen,1,stdout) == -1) return -1;
|
||||||
|
// int pos2 = getCursorPosition();
|
||||||
|
// if (pos1 >= 0 && pos2 >= 0) {
|
||||||
|
// l.plen = pos2 - pos1;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
/* This function is part of the multiplexed API of linenoise, see the top
|
||||||
|
* comment on linenoiseEditStart() for more information. Call this function
|
||||||
|
* each time there is some data to read from the standard input file
|
||||||
|
* descriptor. In the case of blocking operations, this function can just be
|
||||||
|
* called in a loop, and block.
|
||||||
|
*
|
||||||
|
* The function returns NULL to signal that line editing is still in progress,
|
||||||
|
* that is, the user didn't yet pressed enter / CTRL-D. Otherwise the function
|
||||||
|
* returns the pointer to the buffer and populates '*len' with the current
|
||||||
|
* buffer length. If '*len' is set to -1, some special condition occurred, and
|
||||||
|
* the caller may want to check 'errno':
|
||||||
|
*
|
||||||
|
* EAGAIN if the user pressed Ctrl-C
|
||||||
|
* ENOENT if the user pressed Ctrl-D
|
||||||
|
*/
|
||||||
|
int linenoiseEditStart(struct linenoiseState *l, int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt) {
|
||||||
|
/* Populate the linenoise state that we pass to functions implementing
|
||||||
|
* specific editing functionalities. */
|
||||||
|
l->ifd = stdin_fd != -1 ? stdin_fd : STDIN_FILENO;
|
||||||
|
l->ofd = stdout_fd != -1 ? stdout_fd : STDOUT_FILENO;
|
||||||
|
l->buf = buf;
|
||||||
|
l->buflen = buflen;
|
||||||
|
l->prompt = prompt;
|
||||||
|
l->plen = strlen(prompt);
|
||||||
|
l->oldpos = l->pos = 0;
|
||||||
|
l->len = 0;
|
||||||
|
l->cols = getColumns(stdin_fd, stdout_fd);
|
||||||
|
l->maxrows = 0;
|
||||||
|
l->history_index = 0;
|
||||||
|
|
||||||
|
/* Buffer starts empty. */
|
||||||
|
l->buf[0] = '\0';
|
||||||
|
l->buflen--; /* Make sure there is always space for the nulterm */
|
||||||
|
|
||||||
|
/* Enter raw mode. */
|
||||||
|
if (enableRawMode(l->ifd) == -1) return -1;
|
||||||
|
|
||||||
|
/* The latest history entry is always our current buffer, that
|
||||||
|
* initially is just an empty string. */
|
||||||
|
linenoiseHistoryAdd("");
|
||||||
|
|
||||||
|
if (write(l->ofd,prompt,l->plen) == -1) return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function is part of the multiplexed API of linenoise, see the top
|
||||||
|
* comment on linenoiseEditStart() for more information. Call this function
|
||||||
|
* each time there is some data to read from the standard input file
|
||||||
|
* descriptor. In the case of blocking operations, this function can just be
|
||||||
|
* called in a loop, and block.
|
||||||
|
*
|
||||||
|
* The function returns NULL to signal that line editing is still in progress,
|
||||||
|
* that is, the user didn't yet pressed enter / CTRL-D. Otherwise the function
|
||||||
|
* returns the pointer to the buffer and populates '*len' with the current
|
||||||
|
* buffer length. If '*len' is set to -1, some special condition occurred, and
|
||||||
|
* the caller may want to check 'errno':
|
||||||
|
*
|
||||||
|
* EAGAIN if the user pressed Ctrl-C
|
||||||
|
* ENOENT if the user pressed Ctrl-D
|
||||||
|
*/
|
||||||
|
char *linenoiseEditFeed(struct linenoiseState *l, int *len) {
|
||||||
char c;
|
char c;
|
||||||
int nread;
|
int nread;
|
||||||
char seq[3];
|
char seq[3];
|
||||||
|
|
||||||
nread = fread(&c, 1, 1, stdin);
|
nread = fread(&c, 1, 1, stdin);
|
||||||
if (nread <= 0) return l.len;
|
if (nread <= 0) {
|
||||||
|
if (len) *len = l->len;
|
||||||
|
return l->buf;
|
||||||
|
}
|
||||||
|
|
||||||
/* Only autocomplete when the callback is set. It returns < 0 when
|
/* Only autocomplete when the callback is set. It returns < 0 when
|
||||||
* there was an error reading from fd. Otherwise it will return the
|
* there was an error reading from fd. Otherwise it will return the
|
||||||
* character that should be handled next. */
|
* character that should be handled next. */
|
||||||
if (c == 9 && completionCallback != NULL) {
|
if (c == 9 && completionCallback != NULL) {
|
||||||
int c2 = completeLine(&l);
|
c = completeLine(l);
|
||||||
/* Return on errors */
|
/* Return on errors */
|
||||||
if (c2 < 0) return l.len;
|
if (c < 0) {
|
||||||
|
if (len) *len = -1;
|
||||||
|
return l->buf;
|
||||||
|
}
|
||||||
/* Read next character when 0 */
|
/* Read next character when 0 */
|
||||||
if (c2 == 0) continue;
|
if (c == 0) return NULL;
|
||||||
c = c2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(c) {
|
switch(c) {
|
||||||
case ENTER: /* enter */
|
case ENTER: /* enter */
|
||||||
history_len--;
|
history_len--;
|
||||||
free(history[history_len]);
|
free(history[history_len]);
|
||||||
if (mlmode) linenoiseEditMoveEnd(&l);
|
if (mlmode) linenoiseEditMoveEnd(l);
|
||||||
if (hintsCallback) {
|
if (hintsCallback) {
|
||||||
/* Force a refresh without hints to leave the previous
|
/* Force a refresh without hints to leave the previous
|
||||||
* line as the user typed it after a newline. */
|
* line as the user typed it after a newline. */
|
||||||
linenoiseHintsCallback *hc = hintsCallback;
|
linenoiseHintsCallback *hc = hintsCallback;
|
||||||
hintsCallback = NULL;
|
hintsCallback = NULL;
|
||||||
refreshLine(&l);
|
refreshLine(l);
|
||||||
hintsCallback = hc;
|
hintsCallback = hc;
|
||||||
}
|
}
|
||||||
return (int)l.len;
|
if (len) *len = l->len;
|
||||||
|
return l->buf;
|
||||||
case CTRL_C: /* ctrl-c */
|
case CTRL_C: /* ctrl-c */
|
||||||
errno = EAGAIN;
|
errno = EAGAIN;
|
||||||
return -1;
|
if (len) *len = -1;
|
||||||
|
return l->buf;
|
||||||
case BACKSPACE: /* backspace */
|
case BACKSPACE: /* backspace */
|
||||||
case 8: /* ctrl-h */
|
case 8: /* ctrl-h */
|
||||||
linenoiseEditBackspace(&l);
|
linenoiseEditBackspace(l);
|
||||||
break;
|
break;
|
||||||
case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the
|
case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the
|
||||||
line is empty, act as end-of-file. */
|
line is empty, act as end-of-file. */
|
||||||
if (l.len > 0) {
|
if (l->len > 0) {
|
||||||
linenoiseEditDelete(&l);
|
linenoiseEditDelete(l);
|
||||||
} else {
|
} else {
|
||||||
history_len--;
|
history_len--;
|
||||||
free(history[history_len]);
|
free(history[history_len]);
|
||||||
return -1;
|
if (len) *len = -1;
|
||||||
|
errno = ENOENT;
|
||||||
|
return l->buf;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CTRL_T: /* ctrl-t, swaps current character with previous. */
|
case CTRL_T: /* ctrl-t, swaps current character with previous. */
|
||||||
if (l.pos > 0 && l.pos < l.len) {
|
if (l->pos > 0 && l->pos < l->len) {
|
||||||
int aux = buf[l.pos-1];
|
int aux = l->buf[l->pos-1];
|
||||||
buf[l.pos-1] = buf[l.pos];
|
l->buf[l->pos-1] = l->buf[l->pos];
|
||||||
buf[l.pos] = aux;
|
l->buf[l->pos] = aux;
|
||||||
if (l.pos != l.len-1) l.pos++;
|
if (l->pos != l->len-1) l->pos++;
|
||||||
refreshLine(&l);
|
refreshLine(l);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CTRL_B: /* ctrl-b */
|
case CTRL_B: /* ctrl-b */
|
||||||
linenoiseEditMoveLeft(&l);
|
linenoiseEditMoveLeft(l);
|
||||||
break;
|
break;
|
||||||
case CTRL_F: /* ctrl-f */
|
case CTRL_F: /* ctrl-f */
|
||||||
linenoiseEditMoveRight(&l);
|
linenoiseEditMoveRight(l);
|
||||||
break;
|
break;
|
||||||
case CTRL_P: /* ctrl-p */
|
case CTRL_P: /* ctrl-p */
|
||||||
linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV);
|
linenoiseEditHistoryNext(l, LINENOISE_HISTORY_PREV);
|
||||||
break;
|
break;
|
||||||
case CTRL_N: /* ctrl-n */
|
case CTRL_N: /* ctrl-n */
|
||||||
linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT);
|
linenoiseEditHistoryNext(l, LINENOISE_HISTORY_NEXT);
|
||||||
break;
|
break;
|
||||||
case ESC: /* escape sequence */
|
case ESC: /* escape sequence */
|
||||||
/* Read the next two bytes representing the escape sequence. */
|
/* Read the next two bytes representing the escape sequence.
|
||||||
if (fread(seq, 1, 2, stdin) < 2) break;
|
* chars at different times. */
|
||||||
|
if (fread(seq, 1, 1, stdin) == -1) break;
|
||||||
|
if (fread(seq+1, 1, 1, stdin) == -1) break;
|
||||||
|
// or just if (fread(seq, 1, 2, stdin) < 2) break;
|
||||||
|
|
||||||
/* ESC [ sequences. */
|
/* ESC [ sequences. */
|
||||||
if (seq[0] == '[') {
|
if (seq[0] == '[') {
|
||||||
|
@ -828,29 +919,29 @@ static int linenoiseEdit(char *buf, size_t buflen, const char *prompt)
|
||||||
if (seq[2] == '~') {
|
if (seq[2] == '~') {
|
||||||
switch(seq[1]) {
|
switch(seq[1]) {
|
||||||
case '3': /* Delete key. */
|
case '3': /* Delete key. */
|
||||||
linenoiseEditDelete(&l);
|
linenoiseEditDelete(l);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch(seq[1]) {
|
switch(seq[1]) {
|
||||||
case 'A': /* Up */
|
case 'A': /* Up */
|
||||||
linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV);
|
linenoiseEditHistoryNext(l, LINENOISE_HISTORY_PREV);
|
||||||
break;
|
break;
|
||||||
case 'B': /* Down */
|
case 'B': /* Down */
|
||||||
linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT);
|
linenoiseEditHistoryNext(l, LINENOISE_HISTORY_NEXT);
|
||||||
break;
|
break;
|
||||||
case 'C': /* Right */
|
case 'C': /* Right */
|
||||||
linenoiseEditMoveRight(&l);
|
linenoiseEditMoveRight(l);
|
||||||
break;
|
break;
|
||||||
case 'D': /* Left */
|
case 'D': /* Left */
|
||||||
linenoiseEditMoveLeft(&l);
|
linenoiseEditMoveLeft(l);
|
||||||
break;
|
break;
|
||||||
case 'H': /* Home */
|
case 'H': /* Home */
|
||||||
linenoiseEditMoveHome(&l);
|
linenoiseEditMoveHome(l);
|
||||||
break;
|
break;
|
||||||
case 'F': /* End*/
|
case 'F': /* End*/
|
||||||
linenoiseEditMoveEnd(&l);
|
linenoiseEditMoveEnd(l);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -860,57 +951,76 @@ static int linenoiseEdit(char *buf, size_t buflen, const char *prompt)
|
||||||
else if (seq[0] == 'O') {
|
else if (seq[0] == 'O') {
|
||||||
switch(seq[1]) {
|
switch(seq[1]) {
|
||||||
case 'H': /* Home */
|
case 'H': /* Home */
|
||||||
linenoiseEditMoveHome(&l);
|
linenoiseEditMoveHome(l);
|
||||||
break;
|
break;
|
||||||
case 'F': /* End*/
|
case 'F': /* End*/
|
||||||
linenoiseEditMoveEnd(&l);
|
linenoiseEditMoveEnd(l);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (linenoiseEditInsert(&l,c)) return -1;
|
if (linenoiseEditInsert(l,c)) {
|
||||||
|
if (len) *len = -1;
|
||||||
|
return l->buf;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CTRL_U: /* Ctrl+u, delete the whole line. */
|
case CTRL_U: /* Ctrl+u, delete the whole line. */
|
||||||
buf[0] = '\0';
|
l->buf[0] = '\0';
|
||||||
l.pos = l.len = 0;
|
l->pos = l->len = 0;
|
||||||
refreshLine(&l);
|
refreshLine(l);
|
||||||
break;
|
break;
|
||||||
case CTRL_K: /* Ctrl+k, delete from current to end of line. */
|
case CTRL_K: /* Ctrl+k, delete from current to end of line. */
|
||||||
buf[l.pos] = '\0';
|
l->buf[l->pos] = '\0';
|
||||||
l.len = l.pos;
|
l->len = l->pos;
|
||||||
refreshLine(&l);
|
refreshLine(l);
|
||||||
break;
|
break;
|
||||||
case CTRL_A: /* Ctrl+a, go to the start of the line */
|
case CTRL_A: /* Ctrl+a, go to the start of the line */
|
||||||
linenoiseEditMoveHome(&l);
|
linenoiseEditMoveHome(l);
|
||||||
break;
|
break;
|
||||||
case CTRL_E: /* ctrl+e, go to the end of the line */
|
case CTRL_E: /* ctrl+e, go to the end of the line */
|
||||||
linenoiseEditMoveEnd(&l);
|
linenoiseEditMoveEnd(l);
|
||||||
break;
|
break;
|
||||||
case CTRL_L: /* ctrl+l, clear screen */
|
case CTRL_L: /* ctrl+l, clear screen */
|
||||||
linenoiseClearScreen();
|
linenoiseClearScreen();
|
||||||
refreshLine(&l);
|
refreshLine(l);
|
||||||
break;
|
break;
|
||||||
case CTRL_W: /* ctrl+w, delete previous word */
|
case CTRL_W: /* ctrl+w, delete previous word */
|
||||||
linenoiseEditDeletePrevWord(&l);
|
linenoiseEditDeletePrevWord(l);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
return NULL;
|
||||||
return l.len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
|
/* This is part of the multiplexed linenoise API. See linenoiseEditStart()
|
||||||
int count;
|
* for more information. This function is called when linenoiseEditFeed()
|
||||||
|
* returns something different than NULL. At this point the user input
|
||||||
|
* is in the buffer, and we can restore the terminal in normal mode. */
|
||||||
|
void linenoiseEditStop(struct linenoiseState *l) {
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This just implements a blocking loop for the multiplexed API.
|
||||||
|
* In many applications that are not event-drivern, we can just call
|
||||||
|
* the blocking linenoise API, wait for the user to complete the editing
|
||||||
|
* and return the buffer. */
|
||||||
|
static int linenoiseBlockingEdit(struct linenoiseState *l, int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt)
|
||||||
|
{
|
||||||
|
/* Editing without a buffer is invalid. */
|
||||||
if (buflen == 0) {
|
if (buflen == 0) {
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
count = linenoiseEdit(buf, buflen, prompt);
|
linenoiseEditStart(l,buf,buflen,prompt);
|
||||||
printf("\n");
|
int len;
|
||||||
return count;
|
while(1) {
|
||||||
|
char *res = linenoiseEditFeed(l,&len);
|
||||||
|
if (res != NULL) break;
|
||||||
|
}
|
||||||
|
linenoiseEditStop(l);
|
||||||
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The high level function that is the main API of the linenoise library. */
|
/* The high level function that is the main API of the linenoise library. */
|
||||||
|
@ -918,7 +1028,8 @@ char *linenoise(const char *prompt) {
|
||||||
char buf[LINENOISE_MAX_LINE];
|
char buf[LINENOISE_MAX_LINE];
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);
|
struct linenoiseState l;
|
||||||
|
count = linenoiseBlockingEdit(&l,-1,-1,buf,LINENOISE_MAX_LINE,prompt);
|
||||||
if (count == -1) return NULL;
|
if (count == -1) return NULL;
|
||||||
return strdup(buf);
|
return strdup(buf);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue