console: add support for terminal probing and dumb terminal mode

This commit is contained in:
Ivan Grokhotkov 2017-08-16 14:59:28 +08:00
parent 1e4587a09f
commit 10f0543080
3 changed files with 137 additions and 31 deletions

View file

@ -112,6 +112,7 @@
#include <ctype.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include "linenoise.h"
@ -123,6 +124,7 @@ static linenoiseHintsCallback *hintsCallback = NULL;
static linenoiseFreeHintsCallback *freeHintsCallback = NULL;
static int mlmode = 0; /* Multi line mode. Default is single line. */
static int dumbmode = 0; /* Dumb mode where line editing is disabled. Off by default */
static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
static int history_len = 0;
static char **history = NULL;
@ -194,6 +196,11 @@ void linenoiseSetMultiLine(int ml) {
mlmode = ml;
}
/* Set if terminal does not recognize escape sequences */
void linenoiseSetDumbMode(int set) {
dumbmode = set;
}
/* Use the ESC [6n escape sequence to query the horizontal cursor position
* and return it. On error -1 is returned, on success the position of the
* cursor. */
@ -204,6 +211,7 @@ static int getCursorPosition() {
/* Report cursor location */
fprintf(stdout, "\x1b[6n");
/* Read the response: ESC [ rows ; cols R */
while (i < sizeof(buf)-1) {
if (fread(buf+i, 1, 1, stdin) != 1) break;
@ -875,6 +883,38 @@ static int linenoiseEdit(char *buf, size_t buflen, const char *prompt)
return l.len;
}
int linenoiseProbe() {
/* Switch to non-blocking mode */
int flags = fcntl(STDIN_FILENO, F_GETFL);
flags |= O_NONBLOCK;
int res = fcntl(STDIN_FILENO, F_SETFL, flags);
if (res != 0) {
return -1;
}
/* Device status request */
fprintf(stdout, "\x1b[5n");
/* Try to read response */
int timeout_ms = 200;
size_t read_bytes = 0;
while (timeout_ms > 0 && read_bytes < 4) { // response is ESC[0n or ESC[3n
usleep(1000);
char c;
int cb = fread(&c, 1, 1, stdin);
read_bytes += cb;
timeout_ms--;
}
/* Restore old mode */
flags &= ~O_NONBLOCK;
res = fcntl(STDIN_FILENO, F_SETFL, flags);
if (res != 0) {
return -1;
}
if (read_bytes < 4) {
return -2;
}
return 0;
}
static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
int count;
@ -885,18 +925,65 @@ static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
}
count = linenoiseEdit(buf, buflen, prompt);
printf("\n");
fputc('\n', stdout);
return count;
}
static int linenoiseDumb(char* buf, size_t buflen, const char* prompt) {
/* dumb terminal, fall back to fgets */
fputs(prompt, stdout);
int count = 0;
while (count < buflen) {
int c = fgetc(stdin);
if (c == '\n') {
break;
} else if (c >= 0x1c && c <= 0x1f){
continue; /* consume arrow keys */
} else if (c == BACKSPACE || c == 0x8) {
if (count > 0) {
buf[count - 1] = 0;
count --;
}
fputs("\x08 ", stdout); /* Windows CMD: erase symbol under cursor */
} else {
buf[count] = c;
++count;
}
fputc(c, stdout); /* echo */
}
fputc('\n', stdout);
return count;
}
static void sanitize(char* src) {
char* dst = src;
for (int c = *src; c != 0; src++, c = *src) {
if (isprint(c)) {
*dst = c;
++dst;
}
}
*dst = 0;
}
/* The high level function that is the main API of the linenoise library. */
char *linenoise(const char *prompt) {
char buf[LINENOISE_MAX_LINE];
int count;
count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);
if (count == -1) return NULL;
return strdup(buf);
char *buf = calloc(1, LINENOISE_MAX_LINE);
int count = 0;
if (!dumbmode) {
count = linenoiseRaw(buf, LINENOISE_MAX_LINE, prompt);
} else {
count = linenoiseDumb(buf, LINENOISE_MAX_LINE, prompt);
}
if (count > 0) {
sanitize(buf);
count = strlen(buf);
}
if (count <= 0) {
free(buf);
return NULL;
}
return buf;
}
/* This is just a wrapper the user may want to call in order to make sure

View file

@ -56,6 +56,7 @@ void linenoiseSetHintsCallback(linenoiseHintsCallback *);
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
int linenoiseProbe(void);
char *linenoise(const char *prompt);
void linenoiseFree(void *ptr);
int linenoiseHistoryAdd(const char *line);
@ -65,6 +66,7 @@ int linenoiseHistoryLoad(const char *filename);
void linenoiseHistoryFree();
void linenoiseClearScreen(void);
void linenoiseSetMultiLine(int ml);
void linenoiseSetDumbMode(int set);
void linenoisePrintKeyCodes(void);
#ifdef __cplusplus

View file

@ -105,41 +105,58 @@ void app_main()
register_system();
register_wifi();
/* Prompt to be printed before each line.
* This can be customized, made dynamic, etc.
*/
const char* prompt = LOG_COLOR_I "esp32> " LOG_RESET_COLOR;
printf("\n"
"This is an example of ESP-IDF console component.\n"
"Type 'help' to get the list of commands.\n"
"Use UP/DOWN arrows to navigate through command history.\n"
"Press TAB when typing command name to auto-complete.\n");
/* Prompt to be printed before each line.
* This can be customized, made dynamic, etc.
*/
const char* prompt = LOG_COLOR_I "[esp32]> " LOG_RESET_COLOR;
/* Figure out if the terminal supports escape sequences */
int probe_status = linenoiseProbe();
if (probe_status) { /* zero indicates success */
printf("\n"
"Your terminal application does not support escape sequences.\n"
"Line editing and history features are disabled.\n"
"On Windows, try using Putty instead.\n");
linenoiseSetDumbMode(1);
#if CONFIG_LOG_COLORS
/* Since the terminal doesn't support escape sequences,
* don't use color codes in the prompt.
*/
prompt = "esp32> ";
#endif //CONFIG_LOG_COLORS
}
/* Main loop */
char *line;
/* Get a line using linenoise.
* The line is returned when ENTER is pressed.
*/
while((line = linenoise(prompt)) != NULL) {
if (strlen(line) > 0) { /* Ignore empty lines */
/* Add the command to the history */
linenoiseHistoryAdd(line);
while(true) {
/* Get a line using linenoise.
* The line is returned when ENTER is pressed.
*/
char* line = linenoise(prompt);
if (line == NULL) { /* Ignore empty lines */
continue;
}
/* Add the command to the history */
linenoiseHistoryAdd(line);
#if CONFIG_STORE_HISTORY
/* Save command history to filesystem */
linenoiseHistorySave(HISTORY_PATH);
/* Save command history to filesystem */
linenoiseHistorySave(HISTORY_PATH);
#endif
/* Try to run the command */
int ret;
esp_err_t err = esp_console_run(line, &ret);
if (err == ESP_ERR_NOT_FOUND) {
printf("Unrecognized command\n");
} else if (err == ESP_OK && ret != ESP_OK) {
printf("Command returned non-zero error code: 0x%x\n", ret);
} else if (err != ESP_OK) {
printf("Internal error: 0x%x\n", err);
}
/* Try to run the command */
int ret;
esp_err_t err = esp_console_run(line, &ret);
if (err == ESP_ERR_NOT_FOUND) {
printf("Unrecognized command\n");
} else if (err == ESP_OK && ret != ESP_OK) {
printf("Command returned non-zero error code: 0x%x\n", ret);
} else if (err != ESP_OK) {
printf("Internal error: 0x%x\n", err);
}
/* linenoise allocates line buffer on the heap, so need to free it */
linenoiseFree(line);