mirror of
https://github.com/tio/tio.git
synced 2026-05-01 14:57:59 +02:00
Add Lua REPL mode and multiline edit function in the mode.
Typing "@repl" after Ctrl-t r, enter Lua REPL mode.
Typing "@exit" in REPL mode will return you to normal mode.
Note:
- the determination of continuation lines is not done automatically, and
if the end of a line is \, it is determined to be a continuation
instruction.
- In REPL mode, tio's main loop is blocked. (Ctrl-t q works.)
Example:
>> t = {1,2,3,4,5}
>> for _,v in ipairs(t) do\
>> print(v,"\r")\
>> end
Implementation improvements:
- Add tty_init() and script_interp_init() in order to only once
initialization.
- Fix script function to work without device_fd.
This commit is contained in:
parent
c25a379fbb
commit
266338a926
5 changed files with 155 additions and 37 deletions
|
|
@ -123,6 +123,12 @@ int main(int argc, char *argv[])
|
||||||
socket_configure();
|
socket_configure();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Script interpreter init */
|
||||||
|
script_interp_init();
|
||||||
|
|
||||||
|
/* Initialize tty module once on program start */
|
||||||
|
tty_init();
|
||||||
|
|
||||||
/* Spawn input handling into separate thread */
|
/* Spawn input handling into separate thread */
|
||||||
tty_input_thread_create();
|
tty_input_thread_create();
|
||||||
|
|
||||||
|
|
|
||||||
83
src/script.c
83
src/script.c
|
|
@ -44,7 +44,7 @@
|
||||||
#define MAX_BUFFER_SIZE 2000 // Maximum size of circular buffer
|
#define MAX_BUFFER_SIZE 2000 // Maximum size of circular buffer
|
||||||
#define READ_LINE_SIZE 4096 // read_line buffer length
|
#define READ_LINE_SIZE 4096 // read_line buffer length
|
||||||
|
|
||||||
static int device_fd;
|
static int device_fd = 0;
|
||||||
static lua_State *script_interp = NULL;
|
static lua_State *script_interp = NULL;
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
|
@ -203,6 +203,11 @@ static int line_set(lua_State *L)
|
||||||
int cd = lua_tointeger(L, 5);
|
int cd = lua_tointeger(L, 5);
|
||||||
int ri = lua_tointeger(L, 6);
|
int ri = lua_tointeger(L, 6);
|
||||||
|
|
||||||
|
if (device_fd == 0)
|
||||||
|
{
|
||||||
|
return luaL_error(L, "tty device not ready");
|
||||||
|
}
|
||||||
|
|
||||||
if (dtr != -1)
|
if (dtr != -1)
|
||||||
{
|
{
|
||||||
line_config[0].mask = TIOCM_DTR;
|
line_config[0].mask = TIOCM_DTR;
|
||||||
|
|
@ -252,6 +257,11 @@ static int api_send(lua_State *L)
|
||||||
int protocol = luaL_checkinteger(L, 2);
|
int protocol = luaL_checkinteger(L, 2);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (device_fd == 0)
|
||||||
|
{
|
||||||
|
return luaL_error(L, "tty device not ready");
|
||||||
|
}
|
||||||
|
|
||||||
if (file == NULL)
|
if (file == NULL)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -295,6 +305,11 @@ static int api_write(lua_State *L)
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
int attempts = 100;
|
int attempts = 100;
|
||||||
|
|
||||||
|
if (device_fd == 0)
|
||||||
|
{
|
||||||
|
return luaL_error(L, "tty device not ready");
|
||||||
|
}
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
ret = write(device_fd, string, len);
|
ret = write(device_fd, string, len);
|
||||||
|
|
@ -322,6 +337,11 @@ static int api_twrite(lua_State *L)
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
const char *string = luaL_checklstring(L, 1, &len);
|
const char *string = luaL_checklstring(L, 1, &len);
|
||||||
|
|
||||||
|
if (device_fd == 0)
|
||||||
|
{
|
||||||
|
return luaL_error(L, "tty device not ready");
|
||||||
|
}
|
||||||
|
|
||||||
for (; len > 0; --len, string++)
|
for (; len > 0; --len, string++)
|
||||||
{
|
{
|
||||||
forward_to_tty(device_fd, *string);
|
forward_to_tty(device_fd, *string);
|
||||||
|
|
@ -339,6 +359,11 @@ static int api_read(lua_State *L)
|
||||||
int size = luaL_checkinteger(L, 1);
|
int size = luaL_checkinteger(L, 1);
|
||||||
int timeout = luaL_optinteger(L, 2, -1); // ms, negative value means forever.
|
int timeout = luaL_optinteger(L, 2, -1); // ms, negative value means forever.
|
||||||
|
|
||||||
|
if (device_fd == 0)
|
||||||
|
{
|
||||||
|
return luaL_error(L, "tty device not ready");
|
||||||
|
}
|
||||||
|
|
||||||
luaL_Buffer buffer;
|
luaL_Buffer buffer;
|
||||||
luaL_buffinit(L, &buffer);
|
luaL_buffinit(L, &buffer);
|
||||||
|
|
||||||
|
|
@ -378,6 +403,11 @@ static int api_readline(lua_State *L)
|
||||||
luaL_Buffer b;
|
luaL_Buffer b;
|
||||||
char ch;
|
char ch;
|
||||||
|
|
||||||
|
if (device_fd == 0)
|
||||||
|
{
|
||||||
|
return luaL_error(L, "tty device not ready");
|
||||||
|
}
|
||||||
|
|
||||||
luaL_buffinit(L, &b);
|
luaL_buffinit(L, &b);
|
||||||
luaL_prepbuffer(&b);
|
luaL_prepbuffer(&b);
|
||||||
while (true)
|
while (true)
|
||||||
|
|
@ -581,24 +611,36 @@ static lua_State *script_interp_new(void)
|
||||||
return L;
|
return L;
|
||||||
}
|
}
|
||||||
|
|
||||||
void script_run(int fd, const char *script_filename)
|
void script_device_bind(int fd)
|
||||||
|
{
|
||||||
|
device_fd = fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void script_device_unbind(void)
|
||||||
|
{
|
||||||
|
device_fd = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void script_do_line(const char *script_line)
|
||||||
|
{
|
||||||
|
assert(script_line != NULL);
|
||||||
|
assert(script_interp != NULL);
|
||||||
|
|
||||||
|
script_buffer_run(script_interp, script_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
void script_run(const char *script_filename)
|
||||||
{
|
{
|
||||||
static bool doopt_by_nul = true;
|
static bool doopt_by_nul = true;
|
||||||
device_fd = fd;
|
|
||||||
|
|
||||||
assert(script_filename != NULL);
|
assert(script_filename != NULL);
|
||||||
|
assert(script_interp != NULL);
|
||||||
if (script_interp == NULL)
|
|
||||||
{
|
|
||||||
if (script_interp_new() == NULL)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (script_filename[0] == '\0')
|
if (script_filename[0] == '\0')
|
||||||
{
|
{
|
||||||
if (doopt_by_nul)
|
if (doopt_by_nul)
|
||||||
{
|
{
|
||||||
script_run_as_specified_by_options(fd);
|
script_run_as_specified_by_options();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -611,7 +653,7 @@ void script_run(int fd, const char *script_filename)
|
||||||
}
|
}
|
||||||
else if (strcmp(script_filename, "@doopt") == 0)
|
else if (strcmp(script_filename, "@doopt") == 0)
|
||||||
{
|
{
|
||||||
script_run_as_specified_by_options(fd);
|
script_run_as_specified_by_options();
|
||||||
}
|
}
|
||||||
else if (strcmp(script_filename, "@nuldo=opt") == 0)
|
else if (strcmp(script_filename, "@nuldo=opt") == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -643,15 +685,9 @@ void script_run(int fd, const char *script_filename)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void script_run_as_specified_by_options(int fd)
|
void script_run_as_specified_by_options(void)
|
||||||
{
|
{
|
||||||
device_fd = fd;
|
assert(script_interp != NULL);
|
||||||
|
|
||||||
if (script_interp == NULL)
|
|
||||||
{
|
|
||||||
if (script_interp_new() == NULL)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (option.script_filename != NULL)
|
if (option.script_filename != NULL)
|
||||||
{
|
{
|
||||||
|
|
@ -682,3 +718,12 @@ const char *script_run_state_to_string(script_run_t state)
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void script_interp_init(void)
|
||||||
|
{
|
||||||
|
if (script_interp_new() == NULL)
|
||||||
|
{
|
||||||
|
tio_error_printf("Could not start script interpreter.");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@ typedef enum
|
||||||
SCRIPT_RUN_END,
|
SCRIPT_RUN_END,
|
||||||
} script_run_t;
|
} script_run_t;
|
||||||
|
|
||||||
void script_run(int fd, const char *script_filename);
|
void script_interp_init(void);
|
||||||
void script_run_as_specified_by_options(int fd);
|
void script_device_bind(int fd);
|
||||||
|
void script_device_unbind(void);
|
||||||
|
void script_run(const char *script_filename);
|
||||||
|
void script_run_as_specified_by_options(void);
|
||||||
|
void script_do_line(const char *script_line);
|
||||||
const char *script_run_state_to_string(script_run_t state);
|
const char *script_run_state_to_string(script_run_t state);
|
||||||
|
|
|
||||||
94
src/tty.c
94
src/tty.c
|
|
@ -147,6 +147,8 @@ typedef enum
|
||||||
SUBCOMMAND_MAP,
|
SUBCOMMAND_MAP,
|
||||||
} sub_command_t;
|
} sub_command_t;
|
||||||
|
|
||||||
|
#define MLINE_MAX 4096
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
const char random_array[] =
|
const char random_array[] =
|
||||||
{
|
{
|
||||||
|
|
@ -183,7 +185,7 @@ static char *tty_buffer_write_ptr = tty_buffer;
|
||||||
static pthread_t thread;
|
static pthread_t thread;
|
||||||
static int pipefd[2];
|
static int pipefd[2];
|
||||||
static pthread_mutex_t mutex_input_ready = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t mutex_input_ready = PTHREAD_MUTEX_INITIALIZER;
|
||||||
static char line[PATH_MAX];
|
static char line[PATH_MAX], mline[MLINE_MAX];
|
||||||
static size_t listing_device_name_length_max = 0;
|
static size_t listing_device_name_length_max = 0;
|
||||||
static readline_t *readline_ctx = NULL;
|
static readline_t *readline_ctx = NULL;
|
||||||
static readline_t *subcmd_readline_ctx = NULL;
|
static readline_t *subcmd_readline_ctx = NULL;
|
||||||
|
|
@ -593,7 +595,9 @@ static void tty_line_poke(int fd, int mask, tty_line_mode_t mode, unsigned int d
|
||||||
|
|
||||||
static int tio_subcmd_readln(const char *title_prompt)
|
static int tio_subcmd_readln(const char *title_prompt)
|
||||||
{
|
{
|
||||||
tio_printf_raw("%s\r\n", title_prompt);
|
if (title_prompt && (title_prompt[0] != '\0')) {
|
||||||
|
tio_printf_raw("%s\r\n", title_prompt);
|
||||||
|
}
|
||||||
readline_prompt_for_input(subcmd_readline_ctx);
|
readline_prompt_for_input(subcmd_readline_ctx);
|
||||||
|
|
||||||
/* Read line with line edit and history. */
|
/* Read line with line edit and history. */
|
||||||
|
|
@ -660,6 +664,50 @@ static void mappings_print(void)
|
||||||
// clang-format on
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void handle_script_repl(void)
|
||||||
|
{
|
||||||
|
bool local_echo_bkup = option.local_echo;
|
||||||
|
int line_len;
|
||||||
|
int mline_len = 0;
|
||||||
|
|
||||||
|
option.local_echo = true;
|
||||||
|
tio_printf("Enter Lua REPL mode (@exit to exit)");
|
||||||
|
|
||||||
|
strcpy(mline, "");
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
tio_subcmd_readln("");
|
||||||
|
if (strcmp(line, "@exit") == 0)
|
||||||
|
break;
|
||||||
|
line_len = strlen(line);
|
||||||
|
|
||||||
|
if (line_len > 0)
|
||||||
|
{
|
||||||
|
if (mline_len + line_len + 1 > MLINE_MAX)
|
||||||
|
{
|
||||||
|
tio_printf("Too long lines. The size should be lesser then %d bytes", MLINE_MAX);
|
||||||
|
strcpy(mline, "");
|
||||||
|
mline_len = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcat(&mline[mline_len], line);
|
||||||
|
mline_len += line_len;
|
||||||
|
|
||||||
|
if (mline_len > 0 && mline[mline_len - 1] == '\\')
|
||||||
|
{
|
||||||
|
mline[mline_len - 1] = '\n';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
script_do_line(mline);
|
||||||
|
strcpy(mline, "");
|
||||||
|
mline_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
option.local_echo = local_echo_bkup;
|
||||||
|
}
|
||||||
|
|
||||||
void handle_command_sequence(char input_char, char *output_char, bool *forward)
|
void handle_command_sequence(char input_char, char *output_char, bool *forward)
|
||||||
{
|
{
|
||||||
char unused_char;
|
char unused_char;
|
||||||
|
|
@ -1079,8 +1127,15 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward)
|
||||||
/* Run script */
|
/* Run script */
|
||||||
tio_printf("Run Lua script");
|
tio_printf("Run Lua script");
|
||||||
tio_subcmd_readln("Enter file name or \"!\" lua commands or \"@\" direction to interpreter: ");
|
tio_subcmd_readln("Enter file name or \"!\" lua commands or \"@\" direction to interpreter: ");
|
||||||
clear_line();
|
if (strcmp(line, "@repl") == 0)
|
||||||
script_run(device_fd, line);
|
{
|
||||||
|
handle_script_repl();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clear_line();
|
||||||
|
script_run(line);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KEY_SHIFT_R:
|
case KEY_SHIFT_R:
|
||||||
|
|
@ -2545,6 +2600,21 @@ void forward_to_tty(int fd, char output_char)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tty_init(void)
|
||||||
|
{
|
||||||
|
// Initialize readline like history
|
||||||
|
readline_ctx = readline_create();
|
||||||
|
subcmd_readline_ctx = readline_create();
|
||||||
|
if (readline_ctx == NULL || subcmd_readline_ctx == NULL)
|
||||||
|
{
|
||||||
|
tio_error_printf("Could not allocate readline buffer.");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
readline_set_prompt(readline_ctx, "> ");
|
||||||
|
readline_set_prompt(subcmd_readline_ctx, ">> ");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
int tty_connect(void)
|
int tty_connect(void)
|
||||||
{
|
{
|
||||||
fd_set rdfs; /* Read file descriptor set */
|
fd_set rdfs; /* Read file descriptor set */
|
||||||
|
|
@ -2676,9 +2746,11 @@ int tty_connect(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Manage script activation */
|
/* Manage script activation */
|
||||||
|
script_device_bind(device_fd);
|
||||||
|
|
||||||
if (option.script_run != SCRIPT_RUN_NEVER)
|
if (option.script_run != SCRIPT_RUN_NEVER)
|
||||||
{
|
{
|
||||||
script_run_as_specified_by_options(device_fd);
|
script_run_as_specified_by_options();
|
||||||
|
|
||||||
if (option.script_run == SCRIPT_RUN_ONCE)
|
if (option.script_run == SCRIPT_RUN_ONCE)
|
||||||
{
|
{
|
||||||
|
|
@ -2698,17 +2770,6 @@ int tty_connect(void)
|
||||||
exit(status);
|
exit(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize readline like history
|
|
||||||
readline_ctx = readline_create();
|
|
||||||
subcmd_readline_ctx = readline_create();
|
|
||||||
if (readline_ctx == NULL || subcmd_readline_ctx == NULL)
|
|
||||||
{
|
|
||||||
tio_error_printf("Could not allocate readline buffer.");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
readline_set_prompt(readline_ctx, "> ");
|
|
||||||
readline_set_prompt(subcmd_readline_ctx, ">> ");
|
|
||||||
|
|
||||||
/* Input loop */
|
/* Input loop */
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
|
@ -3035,6 +3096,7 @@ error_setspeed:
|
||||||
error_tcsetattr:
|
error_tcsetattr:
|
||||||
error_tcgetattr:
|
error_tcgetattr:
|
||||||
error_read:
|
error_read:
|
||||||
|
script_device_unbind();
|
||||||
tty_disconnect();
|
tty_disconnect();
|
||||||
error_open:
|
error_open:
|
||||||
return TIO_ERROR;
|
return TIO_ERROR;
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ void stdout_configure(void);
|
||||||
void stdin_configure(void);
|
void stdin_configure(void);
|
||||||
void tty_configure(void);
|
void tty_configure(void);
|
||||||
void tty_reconfigure(void);
|
void tty_reconfigure(void);
|
||||||
|
void tty_init(void);
|
||||||
int tty_connect(void);
|
int tty_connect(void);
|
||||||
void tty_wait_for_device(void);
|
void tty_wait_for_device(void);
|
||||||
void list_serial_devices(void);
|
void list_serial_devices(void);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue