diff --git a/man/tio.1.in b/man/tio.1.in index eafa446..eb61cb3 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -375,17 +375,18 @@ Send ctrl-t character .PP Tio suppots Lua scripting to easily automate interaction with the tty device. -This means that in addition to the Lua API tio makes the following functions -available: +In addition to the Lua API tio makes the following functions available: .TP 6n +.IP "\fBexpect(string)" +Expect string - waits for string to match before continueing. Supports regular expressions. Special characters must be escaped with '\\\\'. +.IP "\fBsend(string)" +Send string. .IP "\fBmodem_send(file, protocol)" Send file using x/y-modem protocol. Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM. -.IP "\fBsend(string)" -Send string. .IP "\fBhigh(line)" Set tty line high. .IP "\fBlow(line)" @@ -660,10 +661,14 @@ Enable RS-485 mode: $ tio --rs-485 --rs-485-config=RTS_ON_SEND=1,RX_DURING_TX /dev/ttyUSB0 .TP -Script manipulation of DTR and RTS lines upon first connect: +Manipulate DTR and RTS lines upon first connect to reset connected microcontroller: $ tio --script "high(DTR); low(RTS); msleep(100); toggle(DTR)" --script-run once /dev/ttyUSB0 +.TP +Automatically log in to connected OS: + +$ tio --script "expect('password:'); send('my_password\\n')" /dev/ttyUSB0 .SH "WEBSITE" .PP diff --git a/src/misc.c b/src/misc.c index 55febe6..06d7512 100644 --- a/src/misc.c +++ b/src/misc.c @@ -20,6 +20,7 @@ */ #include "config.h" +#include #include #include #include @@ -87,3 +88,27 @@ bool fs_dir_exists(const char *path) return true; } + +bool regex_match(const char *string, const char *pattern) +{ + regex_t regex; + int status; + + if (regcomp(®ex, pattern, REG_EXTENDED | REG_NOSUB) != 0) + { + // No match + return false; + } + + status = regexec(®ex, string, (size_t) 0, NULL, 0); + regfree(®ex); + + if (status != 0) + { + // No match + return false; + } + + // Match + return true; +} diff --git a/src/misc.h b/src/misc.h index 65f7f03..ec80d3d 100644 --- a/src/misc.h +++ b/src/misc.h @@ -32,3 +32,4 @@ int ctrl_key_code(unsigned char key); void alert_connect(void); void alert_disconnect(void); bool fs_dir_exists(const char *path); +bool regex_match(const char *string, const char *pattern); diff --git a/src/script.c b/src/script.c index bc7d1d5..bd1d6fe 100644 --- a/src/script.c +++ b/src/script.c @@ -20,6 +20,7 @@ */ #include +#include #include #include #include @@ -34,7 +35,11 @@ #include "tty.h" #include "xymodem.h" +#define MAX_BUFFER_SIZE 2000 // Maximum size of circular buffer + static int serial_fd; +static char circular_buffer[MAX_BUFFER_SIZE]; +static int buffer_size = 0; // lua: sleep(seconds) static int sleep_(lua_State *L) @@ -209,6 +214,95 @@ static int send(lua_State *L) return 1; } +// Function to add a character to the circular buffer +void add_to_buffer(char c) +{ + if (buffer_size < MAX_BUFFER_SIZE) + { + circular_buffer[buffer_size++] = c; + } + else + { + // Shift the buffer to accommodate the new character + memmove(circular_buffer, circular_buffer + 1, MAX_BUFFER_SIZE - 1); + circular_buffer[MAX_BUFFER_SIZE - 1] = c; + } +} + +// Function to match against the circular buffer using regex +bool match_regex(regex_t *regex) +{ + char buffer[MAX_BUFFER_SIZE + 1]; // Temporary buffer for regex matching + memcpy(buffer, circular_buffer, buffer_size); + buffer[buffer_size] = '\0'; // Null-terminate the buffer + + // Match against the regex + int ret = regexec(regex, buffer, 0, NULL, 0); + if (!ret) + { + // Match found + return true; + } + else if (ret == REG_NOMATCH) + { + // No match found, do nothing + } + else + { + // Error occurred during matching + tio_error_print("Regex match failed"); + } + + return false; +} + +// lua: expect(string) +static int expect(lua_State *L) +{ + const char *string = lua_tostring(L, 1); + regex_t regex; + int ret = 0; + char c; + + if (string == NULL) + { + ret = -1; + goto error; + } + + // Compile the regular expression + ret = regcomp(®ex, string, REG_EXTENDED); + if (ret) + { + tio_error_print("Could not compile regex"); + ret = -1; + goto error; + } + + // Main loop to read and match + while (true) + { + ssize_t bytes_read = read(serial_fd, &c, 1); + if (bytes_read > 0) + { + putchar(c); + add_to_buffer(c); + // Match against the entire buffer + if (match_regex(®ex)) + { + break; + } + } + } + + // Cleanup + regfree(®ex); + +error: + lua_pushnumber(L, ret); + return 1; +} + static void script_buffer_run(lua_State *L, const char *script_buffer) { int error; @@ -217,7 +311,7 @@ static void script_buffer_run(lua_State *L, const char *script_buffer) lua_pcall(L, 0, 0, 0); if (error) { - tio_warning_printf("%s\n", lua_tostring(L, -1)); + tio_warning_printf("lua: %s\n", lua_tostring(L, -1)); lua_pop(L, 1); /* pop error message from the stack */ } } @@ -234,6 +328,7 @@ static const struct luaL_Reg tio_lib[] = { "config_apply", config_apply}, { "modem_send", modem_send}, { "send", send}, + { "expect", expect}, {NULL, NULL} }; @@ -276,7 +371,7 @@ void script_file_run(lua_State *L, const char *filename) if (luaL_dofile(L, filename)) { - tio_warning_printf("%s\n", lua_tostring(L, -1)); + tio_warning_printf("lua: %s\n", lua_tostring(L, -1)); lua_pop(L, 1); /* pop error message from the stack */ return; }