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();
|
||||
}
|
||||
|
||||
/* Script interpreter init */
|
||||
script_interp_init();
|
||||
|
||||
/* Initialize tty module once on program start */
|
||||
tty_init();
|
||||
|
||||
/* Spawn input handling into separate thread */
|
||||
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 READ_LINE_SIZE 4096 // read_line buffer length
|
||||
|
||||
static int device_fd;
|
||||
static int device_fd = 0;
|
||||
static lua_State *script_interp = NULL;
|
||||
|
||||
// clang-format off
|
||||
|
|
@ -203,6 +203,11 @@ static int line_set(lua_State *L)
|
|||
int cd = lua_tointeger(L, 5);
|
||||
int ri = lua_tointeger(L, 6);
|
||||
|
||||
if (device_fd == 0)
|
||||
{
|
||||
return luaL_error(L, "tty device not ready");
|
||||
}
|
||||
|
||||
if (dtr != -1)
|
||||
{
|
||||
line_config[0].mask = TIOCM_DTR;
|
||||
|
|
@ -252,6 +257,11 @@ static int api_send(lua_State *L)
|
|||
int protocol = luaL_checkinteger(L, 2);
|
||||
int ret;
|
||||
|
||||
if (device_fd == 0)
|
||||
{
|
||||
return luaL_error(L, "tty device not ready");
|
||||
}
|
||||
|
||||
if (file == NULL)
|
||||
{
|
||||
return 0;
|
||||
|
|
@ -295,6 +305,11 @@ static int api_write(lua_State *L)
|
|||
ssize_t ret;
|
||||
int attempts = 100;
|
||||
|
||||
if (device_fd == 0)
|
||||
{
|
||||
return luaL_error(L, "tty device not ready");
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
ret = write(device_fd, string, len);
|
||||
|
|
@ -322,6 +337,11 @@ static int api_twrite(lua_State *L)
|
|||
size_t len = 0;
|
||||
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++)
|
||||
{
|
||||
forward_to_tty(device_fd, *string);
|
||||
|
|
@ -339,6 +359,11 @@ static int api_read(lua_State *L)
|
|||
int size = luaL_checkinteger(L, 1);
|
||||
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_buffinit(L, &buffer);
|
||||
|
||||
|
|
@ -378,6 +403,11 @@ static int api_readline(lua_State *L)
|
|||
luaL_Buffer b;
|
||||
char ch;
|
||||
|
||||
if (device_fd == 0)
|
||||
{
|
||||
return luaL_error(L, "tty device not ready");
|
||||
}
|
||||
|
||||
luaL_buffinit(L, &b);
|
||||
luaL_prepbuffer(&b);
|
||||
while (true)
|
||||
|
|
@ -581,24 +611,36 @@ static lua_State *script_interp_new(void)
|
|||
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;
|
||||
device_fd = fd;
|
||||
|
||||
assert(script_filename != NULL);
|
||||
|
||||
if (script_interp == NULL)
|
||||
{
|
||||
if (script_interp_new() == NULL)
|
||||
return;
|
||||
}
|
||||
assert(script_interp != NULL);
|
||||
|
||||
if (script_filename[0] == '\0')
|
||||
{
|
||||
if (doopt_by_nul)
|
||||
{
|
||||
script_run_as_specified_by_options(fd);
|
||||
script_run_as_specified_by_options();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -611,7 +653,7 @@ void script_run(int fd, const char *script_filename)
|
|||
}
|
||||
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)
|
||||
{
|
||||
|
|
@ -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;
|
||||
|
||||
if (script_interp == NULL)
|
||||
{
|
||||
if (script_interp_new() == NULL)
|
||||
return;
|
||||
}
|
||||
assert(script_interp != NULL);
|
||||
|
||||
if (option.script_filename != NULL)
|
||||
{
|
||||
|
|
@ -682,3 +718,12 @@ const char *script_run_state_to_string(script_run_t state)
|
|||
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_t;
|
||||
|
||||
void script_run(int fd, const char *script_filename);
|
||||
void script_run_as_specified_by_options(int fd);
|
||||
void script_interp_init(void);
|
||||
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);
|
||||
|
|
|
|||
90
src/tty.c
90
src/tty.c
|
|
@ -147,6 +147,8 @@ typedef enum
|
|||
SUBCOMMAND_MAP,
|
||||
} sub_command_t;
|
||||
|
||||
#define MLINE_MAX 4096
|
||||
|
||||
// clang-format off
|
||||
const char random_array[] =
|
||||
{
|
||||
|
|
@ -183,7 +185,7 @@ static char *tty_buffer_write_ptr = tty_buffer;
|
|||
static pthread_t thread;
|
||||
static int pipefd[2];
|
||||
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 readline_t *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)
|
||||
{
|
||||
if (title_prompt && (title_prompt[0] != '\0')) {
|
||||
tio_printf_raw("%s\r\n", title_prompt);
|
||||
}
|
||||
readline_prompt_for_input(subcmd_readline_ctx);
|
||||
|
||||
/* Read line with line edit and history. */
|
||||
|
|
@ -660,6 +664,50 @@ static void mappings_print(void)
|
|||
// 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)
|
||||
{
|
||||
char unused_char;
|
||||
|
|
@ -1079,8 +1127,15 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward)
|
|||
/* Run script */
|
||||
tio_printf("Run Lua script");
|
||||
tio_subcmd_readln("Enter file name or \"!\" lua commands or \"@\" direction to interpreter: ");
|
||||
if (strcmp(line, "@repl") == 0)
|
||||
{
|
||||
handle_script_repl();
|
||||
}
|
||||
else
|
||||
{
|
||||
clear_line();
|
||||
script_run(device_fd, line);
|
||||
script_run(line);
|
||||
}
|
||||
break;
|
||||
|
||||
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)
|
||||
{
|
||||
fd_set rdfs; /* Read file descriptor set */
|
||||
|
|
@ -2676,9 +2746,11 @@ int tty_connect(void)
|
|||
}
|
||||
|
||||
/* Manage script activation */
|
||||
script_device_bind(device_fd);
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
@ -2698,17 +2770,6 @@ int tty_connect(void)
|
|||
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 */
|
||||
while (true)
|
||||
{
|
||||
|
|
@ -3035,6 +3096,7 @@ error_setspeed:
|
|||
error_tcsetattr:
|
||||
error_tcgetattr:
|
||||
error_read:
|
||||
script_device_unbind();
|
||||
tty_disconnect();
|
||||
error_open:
|
||||
return TIO_ERROR;
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ void stdout_configure(void);
|
|||
void stdin_configure(void);
|
||||
void tty_configure(void);
|
||||
void tty_reconfigure(void);
|
||||
void tty_init(void);
|
||||
int tty_connect(void);
|
||||
void tty_wait_for_device(void);
|
||||
void list_serial_devices(void);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue