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 <ctype.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/fcntl.h>
#include <unistd.h> #include <unistd.h>
#include "linenoise.h" #include "linenoise.h"
@@ -123,6 +124,7 @@ static linenoiseHintsCallback *hintsCallback = NULL;
static linenoiseFreeHintsCallback *freeHintsCallback = NULL; static linenoiseFreeHintsCallback *freeHintsCallback = NULL;
static int mlmode = 0; /* Multi line mode. Default is single line. */ 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_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
static int history_len = 0; static int history_len = 0;
static char **history = NULL; static char **history = NULL;
@@ -194,6 +196,11 @@ void linenoiseSetMultiLine(int ml) {
mlmode = 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 /* 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 * and return it. On error -1 is returned, on success the position of the
* cursor. */ * cursor. */
@@ -204,6 +211,7 @@ static int getCursorPosition() {
/* Report cursor location */ /* Report cursor location */
fprintf(stdout, "\x1b[6n"); fprintf(stdout, "\x1b[6n");
/* Read the response: ESC [ rows ; cols R */ /* Read the response: ESC [ rows ; cols R */
while (i < sizeof(buf)-1) { while (i < sizeof(buf)-1) {
if (fread(buf+i, 1, 1, stdin) != 1) break; 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; 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) { static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
int count; int count;
@@ -885,18 +925,65 @@ static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
} }
count = linenoiseEdit(buf, buflen, prompt); count = linenoiseEdit(buf, buflen, prompt);
printf("\n"); fputc('\n', stdout);
return count; 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. */ /* The high level function that is the main API of the linenoise library. */
char *linenoise(const char *prompt) { char *linenoise(const char *prompt) {
char buf[LINENOISE_MAX_LINE]; char *buf = calloc(1, LINENOISE_MAX_LINE);
int count; int count = 0;
if (!dumbmode) {
count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt); count = linenoiseRaw(buf, LINENOISE_MAX_LINE, prompt);
if (count == -1) return NULL; } else {
return strdup(buf); 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 /* 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 linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
void linenoiseAddCompletion(linenoiseCompletions *, const char *); void linenoiseAddCompletion(linenoiseCompletions *, const char *);
int linenoiseProbe(void);
char *linenoise(const char *prompt); char *linenoise(const char *prompt);
void linenoiseFree(void *ptr); void linenoiseFree(void *ptr);
int linenoiseHistoryAdd(const char *line); int linenoiseHistoryAdd(const char *line);
@@ -65,6 +66,7 @@ int linenoiseHistoryLoad(const char *filename);
void linenoiseHistoryFree(); void linenoiseHistoryFree();
void linenoiseClearScreen(void); void linenoiseClearScreen(void);
void linenoiseSetMultiLine(int ml); void linenoiseSetMultiLine(int ml);
void linenoiseSetDumbMode(int set);
void linenoisePrintKeyCodes(void); void linenoisePrintKeyCodes(void);
#ifdef __cplusplus #ifdef __cplusplus

View File

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