From 241ff93bf4a0e502eb47469044300acbe6d971ac Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 8 May 2024 13:37:05 +0200 Subject: [PATCH 001/146] Man page cleanup --- man/tio.1.in | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 2e5259a..e016154 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -624,8 +624,7 @@ Which is equivalent to: $ tio -b 115200 -c 11 /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 .TP -A configuration profile can also be activated by its pattern which supports -regular expressions: +A configuration profile can also be activated by its pattern which supports regular expressions: .RS .nf @@ -649,8 +648,7 @@ Which becomes equivalent to: $ tio -b 115200 /dev/ttyUSB12 .TP -It is also possible to combine use of configuration profile and command-line -options. For example: +It is also possible to combine use of configuration profile and command-line options. For example: $ tio -l -t usb12 From 6ebd50ab858d20993ee52b5a9ea92f8b80ce6383 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 8 May 2024 13:37:43 +0200 Subject: [PATCH 002/146] Bump version --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 08eb5fd..6ac7be3 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('tio', 'c', - version : '3.2', + version : '3.3', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] From f71ffeabb752b7e68595889ae5758b383187a7e8 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 10 May 2024 14:22:28 +0200 Subject: [PATCH 003/146] Force socket write operation to ignore any signals --- src/socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/socket.c b/src/socket.c index 3500f97..043b657 100644 --- a/src/socket.c +++ b/src/socket.c @@ -263,7 +263,7 @@ void socket_write(char input_char) { if (clientfds[i] != -1) { - if (write(clientfds[i], &input_char, 1) <= 0) + if (send(clientfds[i], &input_char, 1, MSG_NOSIGNAL) <= 0) { tio_error_printf_silent("Failed to write to socket (%s)", strerror(errno)); close(clientfds[i]); From ae9c8edbcaee4ed527635b459005d61756f4fbfd Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 11 May 2024 10:46:34 +0200 Subject: [PATCH 004/146] Clean up includes --- src/alert.c | 7 +------ src/configfile.c | 22 ++++------------------ src/error.c | 9 --------- src/log.c | 9 --------- src/main.c | 2 +- src/misc.c | 14 +------------- src/options.c | 17 +---------------- src/options.h | 3 --- src/print.c | 5 ----- src/print.h | 1 - src/rs485.c | 6 ++---- src/timestamp.c | 2 -- src/xymodem.c | 8 +------- 13 files changed, 11 insertions(+), 94 deletions(-) diff --git a/src/alert.c b/src/alert.c index f9ed3ec..23a728e 100644 --- a/src/alert.c +++ b/src/alert.c @@ -19,15 +19,10 @@ * 02110-1301, USA. */ -#include "alert.h" -#include "config.h" #include -#include #include -#include -#include "error.h" -#include "print.h" #include "options.h" +#include "alert.h" void blink_background(void) { diff --git a/src/configfile.c b/src/configfile.c index cc3013d..3ee878c 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -21,31 +21,17 @@ */ #define _GNU_SOURCE - -#include "config.h" -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include +#include #include +#include +#include #include #include -#include "options.h" #include "configfile.h" -#include "misc.h" -#include "options.h" -#include "error.h" #include "print.h" #include "rs485.h" -#include "timestamp.h" -#include "alert.h" +#include "misc.h" #define CONFIG_GROUP_NAME_DEFAULT "default" diff --git a/src/error.c b/src/error.c index 2973fbb..f318378 100644 --- a/src/error.c +++ b/src/error.c @@ -21,17 +21,8 @@ #define __STDC_WANT_LIB_EXT2__ 1 // To access vasprintf -#include "config.h" #include -#include -#include -#include -#include -#include -#include "options.h" #include "print.h" -#include "error.h" -#include "timestamp.h" static char error[2][1000]; static bool in_session = false; diff --git a/src/log.c b/src/log.c index 9fa0414..4eca7c7 100644 --- a/src/log.c +++ b/src/log.c @@ -21,18 +21,9 @@ #define __STDC_WANT_LIB_EXT2__ 1 // To access vasprintf -#include "config.h" -#include -#include -#include -#include -#include -#include #include #include -#include "options.h" #include "print.h" -#include "error.h" #include "fs.h" #define IS_ESC_CSI_INTERMEDIATE_CHAR(c) ((c >= 0x20) && (c <= 0x3F)) diff --git a/src/main.c b/src/main.c index 304258e..9e6c043 100644 --- a/src/main.c +++ b/src/main.c @@ -19,10 +19,10 @@ * 02110-1301, USA. */ -#include "config.h" #include #include #include +#include "config.h" #include "options.h" #include "configfile.h" #include "tty.h" diff --git a/src/misc.c b/src/misc.c index 9342769..ef08fb3 100644 --- a/src/misc.c +++ b/src/misc.c @@ -20,25 +20,13 @@ */ #define _GNU_SOURCE // For FNM_EXTMATCH -#include "config.h" -#include -#include -#include -#include -#include -#include #include #include -#include -#include -#include #include #include -#include #include -#include "error.h" +#include #include "print.h" -#include "options.h" void delay(long ms) { diff --git a/src/options.c b/src/options.c index 0eefe7e..234e3e2 100644 --- a/src/options.c +++ b/src/options.c @@ -19,29 +19,14 @@ * 02110-1301, USA. */ -#include "config.h" #include #include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include "options.h" -#include "error.h" +#include "config.h" #include "misc.h" #include "print.h" -#include "tty.h" #include "rs485.h" -#include "timestamp.h" -#include "alert.h" #include "log.h" -#include "script.h" #include "configfile.h" #define HEX_N_VALUE_MAX 4096 diff --git a/src/options.h b/src/options.h index ee73b8e..21a5f75 100644 --- a/src/options.h +++ b/src/options.h @@ -23,9 +23,6 @@ #include #include -#include -#include -#include #include "script.h" #include "timestamp.h" #include "alert.h" diff --git a/src/print.c b/src/print.c index 04d17da..bfcccbf 100644 --- a/src/print.c +++ b/src/print.c @@ -19,11 +19,6 @@ * 02110-1301, USA. */ -#include -#include -#include -#include -#include "options.h" #include "print.h" bool print_tainted = false; diff --git a/src/print.h b/src/print.h index 038655c..9baf042 100644 --- a/src/print.h +++ b/src/print.h @@ -23,7 +23,6 @@ #include #include -#include "misc.h" #include "error.h" #include "options.h" #include "timestamp.h" diff --git a/src/rs485.c b/src/rs485.c index 30b7dc8..31ab2ec 100644 --- a/src/rs485.c +++ b/src/rs485.c @@ -19,14 +19,12 @@ * 02110-1301, USA. */ -#include +#include #include -#include #include -#include #include "options.h" #include "print.h" -#include "error.h" +#include "misc.h" #ifdef HAVE_RS485 diff --git a/src/timestamp.c b/src/timestamp.c index 338ff87..a8d165c 100644 --- a/src/timestamp.c +++ b/src/timestamp.c @@ -22,8 +22,6 @@ #include "config.h" #include #include -#include -#include #include #include #include "error.h" diff --git a/src/xymodem.c b/src/xymodem.c index 83ac6dc..4edfb1f 100644 --- a/src/xymodem.c +++ b/src/xymodem.c @@ -7,20 +7,14 @@ * */ -#include -#include -#include -#include -#include #include #include -#include #include #include -#include #include #include "xymodem.h" #include "print.h" +#include "misc.h" #define SOH 0x01 #define STX 0x02 From d2dd9f5a5b8f4b62539de221a12ebdf68626439e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 11 May 2024 16:41:12 +0200 Subject: [PATCH 005/146] Update README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 462859a..e69ff55 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ when used in combination with [tmux](https://tmux.github.io). * Configuration file support * Support for configuration profiles * Activate configuration profiles by name or pattern + * Redirect I/O of shell command to serial device * Redirect I/O to UNIX socket or IPv4/v6 network socket * Useful for scripting or TTY sharing * Pipe input and/or output From c76a4d017263ef2bdcbe6e5f23905e14a550a34e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 12 May 2024 08:57:59 +0200 Subject: [PATCH 006/146] Fix includes --- src/log.c | 1 + src/misc.c | 1 + src/options.c | 1 + 3 files changed, 3 insertions(+) diff --git a/src/log.c b/src/log.c index 4eca7c7..8966457 100644 --- a/src/log.c +++ b/src/log.c @@ -23,6 +23,7 @@ #include #include +#include #include "print.h" #include "fs.h" diff --git a/src/misc.c b/src/misc.c index ef08fb3..67492ce 100644 --- a/src/misc.c +++ b/src/misc.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "print.h" void delay(long ms) diff --git a/src/options.c b/src/options.c index 234e3e2..4989f20 100644 --- a/src/options.c +++ b/src/options.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "config.h" #include "misc.h" #include "print.h" From 747ac62733d4a43dc7ce5211ab89701ecf2c49eb Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 13 May 2024 10:57:27 +0200 Subject: [PATCH 007/146] Fix includes --- src/configfile.c | 1 + src/rs485.c | 1 + 2 files changed, 2 insertions(+) diff --git a/src/configfile.c b/src/configfile.c index 3ee878c..c7b8c36 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include "configfile.h" diff --git a/src/rs485.c b/src/rs485.c index 31ab2ec..3c9280c 100644 --- a/src/rs485.c +++ b/src/rs485.c @@ -20,6 +20,7 @@ */ #include +#include #include #include #include "options.h" From 694524cb6fad007698a352524b5451ca7cdcb56e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 13 May 2024 11:01:24 +0200 Subject: [PATCH 008/146] Run CodeQL on push --- .github/workflows/codeql.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 8164922..3ef873f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -12,8 +12,8 @@ name: "CodeQL" on: - # push: - # branches: [ "main", "master" ] + push: + branches: [ "main", "master" ] schedule: - cron: '0 0 * * *' pull_request: From 3f616a47c81e73c85daab8c2aa7062da48cf91c6 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 13 May 2024 11:04:09 +0200 Subject: [PATCH 009/146] Fix includes --- src/xymodem.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/xymodem.c b/src/xymodem.c index 4edfb1f..7102305 100644 --- a/src/xymodem.c +++ b/src/xymodem.c @@ -7,11 +7,12 @@ * */ -#include #include +#include +#include +#include #include #include -#include #include "xymodem.h" #include "print.h" #include "misc.h" From 5f5a8a9cdd163e4aefb756cb2fac9d2783c7c5b9 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 13 May 2024 14:01:44 +0200 Subject: [PATCH 010/146] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e69ff55..4f59299 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ when used in combination with [tmux](https://tmux.github.io). * Send files via x/y-modem protocol * Search for serial devices * Man page documentation - * Plays nicely with [tmux](https://tmux.github.io) + * Plays nicely with [tmux](https://tmux.github.io) and similar terminal multiplexers ## 3. Usage From 37f8b4fd1b0f9c8b62fa53e750826a15c4b105e6 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 13 May 2024 14:07:14 +0200 Subject: [PATCH 011/146] Update workflow name --- .github/workflows/macos.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 558dbaf..8f54070 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -1,4 +1,4 @@ -name: macOS build +name: MacOS build on: push: From 8f45d6f68851869dd9bdab24cbe695c18e417635 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 14 May 2024 22:20:59 +0200 Subject: [PATCH 012/146] Fix local-echo in configuration file --- src/configfile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/configfile.c b/src/configfile.c index c7b8c36..697fed7 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -183,7 +183,7 @@ static void config_parse_keys(GKeyFile *key_file, char *group) config_get_string(key_file, group, "exclude-drivers", &option.exclude_devices, NULL); config_get_string(key_file, group, "exclude-tids", &option.exclude_devices, NULL); config_get_bool(key_file, group, "no-reconnect", &option.no_reconnect); - config_get_bool(key_file, group, "local-echo", &option.no_reconnect); + config_get_bool(key_file, group, "local-echo", &option.local_echo); config_get_string(key_file, group, "input-mode", &string, "normal", "hex", "line", NULL); if (string != NULL) { From 5b5248929eb3127a8074edcd6fe778eab54a9c42 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 14 May 2024 23:20:38 +0200 Subject: [PATCH 013/146] Force destructive backspace when using local echo Only takes effect in normal output mode. --- src/tty.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tty.c b/src/tty.c index f999a77..7b89d28 100644 --- a/src/tty.c +++ b/src/tty.c @@ -188,6 +188,12 @@ static void optional_local_echo(char c) print(c); + if ((option.output_mode == OUTPUT_MODE_NORMAL) && (c == 127)) + { + // Force destructive backspace + printf("\b \b"); + } + if (option.log) { log_putc(c); From b3aac7b1823935a7cc344d70966ccc570413dee1 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 15 May 2024 00:13:03 +0200 Subject: [PATCH 014/146] Update plain text man page --- man/tio.1.txt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/man/tio.1.txt b/man/tio.1.txt index 372debc..779b102 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -276,6 +276,10 @@ OPTIONS Default value is "always". + --exec + + Execute shell command with I/O redirected to device + -v, --version Display program version. @@ -468,6 +472,8 @@ CONFIGURATION FILE script-run Run script on connect + exec Execute shell command with I/O redirected to device + CONFIGURATION FILE EXAMPLES To change the default configuration simply set options like so: @@ -494,8 +500,7 @@ CONFIGURATION FILE EXAMPLES $ tio -b 115200 -c 11 /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 - A configuration profile can also be activated by its pattern which supports - regular expressions: + A configuration profile can also be activated by its pattern which supports regular expressions: [usb-devices] pattern = ^usb([0-9]*) @@ -510,8 +515,7 @@ CONFIGURATION FILE EXAMPLES $ tio -b 115200 /dev/ttyUSB12 - It is also possible to combine use of configuration profile and command-line - options. For example: + It is also possible to combine use of configuration profile and command-line options. For example: $ tio -l -t usb12 @@ -553,7 +557,7 @@ EXAMPLES send -i $uart "ls -la\n" expect -i $uart "prompt> " - It is also possible to use the expect/send script functionality to e.g. automate logins: + It is also possible to use tio's own simpler expect/send script functionality to e.g. automate logins: $ tio --script 'expect("login: "); send("root\n"); expect("Password: "); send("root\n")' /dev/ttyUSB0 @@ -585,14 +589,10 @@ EXAMPLES $ tio --script "set{DTR=high,RTS=low}; msleep(100); set{RTS=toggle}" --script-run once /dev/ttyUSB0 - Automatically log in to connected OS: - - $ tio --script "expect('password:'); send('my_password\n')" /dev/ttyUSB0 - WEBSITE Visit https://tio.github.io AUTHOR Maintained by Martin Lund . -tio 3.1 2024-05-03 tio(1) +tio 3.3 2024-05-08 tio(1) From dba4690a88b80e520b529b559e395f72ae15695f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 15 May 2024 00:13:34 +0200 Subject: [PATCH 015/146] Update NEWS --- NEWS | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 76a1fd7..30fae48 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,21 @@ -=== tio v3.2 === +=== tio v3.3 === + + + +Changes since tio v3.2: + + * Force destructive backspace when using local echo + + Only takes effect in normal output mode. + + * Fix local-echo in configuration file + + * Clean up includes + + * Force socket write operation to ignore any signals + + * Man page cleanup From 0921796054a12517f2ce215ef9d23892f6828ac2 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 15 May 2024 08:35:16 +0200 Subject: [PATCH 016/146] Update version date --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 6ac7be3..3c88fbd 100644 --- a/meson.build +++ b/meson.build @@ -6,7 +6,7 @@ project('tio', 'c', ) # The tag date of the project_version(), update when the version bumps. -version_date = '2024-05-08' +version_date = '2024-05-15' # Test for dynamic baudrate configuration interface compiler = meson.get_compiler('c') From 6f6038ebcde471a6952cf53a35e6c5fbfb9d4446 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 15 May 2024 08:35:45 +0200 Subject: [PATCH 017/146] Update plain text man page --- man/tio.1.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/tio.1.txt b/man/tio.1.txt index 779b102..4d5c038 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -595,4 +595,4 @@ WEBSITE AUTHOR Maintained by Martin Lund . -tio 3.3 2024-05-08 tio(1) +tio 3.3 2024-05-15 tio(1) From ee3687430b3b1f824431e5e29383111b825860de Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 15 May 2024 09:16:13 +0200 Subject: [PATCH 018/146] Bump version --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 3c88fbd..073c4ff 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('tio', 'c', - version : '3.3', + version : '3.4', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] From ccc01433b700b4da4ffa36525aabd6e41204be18 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 16 May 2024 19:16:17 +0200 Subject: [PATCH 019/146] Include correct header for poll() --- src/fs.c | 2 +- src/misc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fs.c b/src/fs.c index 1e69d53..a0d9edd 100644 --- a/src/fs.c +++ b/src/fs.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include "error.h" #include "print.h" diff --git a/src/misc.c b/src/misc.c index 67492ce..6471e99 100644 --- a/src/misc.c +++ b/src/misc.c @@ -22,7 +22,7 @@ #define _GNU_SOURCE // For FNM_EXTMATCH #include #include -#include +#include #include #include #include From 4014fc4b3e8d1a787ebc06ec1b98f8001fc087f7 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 17 May 2024 11:59:10 +0200 Subject: [PATCH 020/146] Update README --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4f59299..b7197dc 100644 --- a/README.md +++ b/README.md @@ -33,14 +33,15 @@ when used in combination with [tmux](https://tmux.github.io). * Easily connect to serial TTY devices * Sensible defaults (115200 8n1) - * Support for non-standard baud rates - * Support for mark and space parity * Automatic connection management + * Automatic detection of serial ports * Automatic reconnect * Automatically connect to first new appearing serial device * Automatically connect to latest registered serial device * Connect to same port/device combination via unique topology ID (TID) * Useful for reconnecting when serial device has no serial device by ID + * Support for non-standard baud rates + * Support for mark and space parity * X-modem (1K/CRC) and Y-modem file upload * Support for RS-485 mode * List available serial devices From eb9726bbcc78211b05200d171c7d64abbcc960d0 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 22 May 2024 18:54:44 +0200 Subject: [PATCH 021/146] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 9aaace3..6da8aef 100644 --- a/AUTHORS +++ b/AUTHORS @@ -53,5 +53,6 @@ Sebastian Mingjie Shen Brian Davis C +KhazAkar Thanks to everyone who has contributed to this project. From 9315cf6a55e1ad2e52dcbda49249102268a1fc5c Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 27 May 2024 10:10:36 +0200 Subject: [PATCH 022/146] Update README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index b7197dc..90b4c5d 100644 --- a/README.md +++ b/README.md @@ -475,6 +475,12 @@ $ pacman -S tio The latest source releases can be found [here](https://github.com/tio/tio/releases). +Before running the install steps make sure you have glib and lua libraries installed. For example: + +``` +sudo apt install libglib2.0-dev liblua5.2-dev +``` + Install steps: ``` $ meson setup build From 94e40d82f3c31bfa6c457c0fe597d3003907050f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 27 May 2024 12:02:41 +0200 Subject: [PATCH 023/146] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 90b4c5d..ee86ffa 100644 --- a/README.md +++ b/README.md @@ -478,7 +478,7 @@ The latest source releases can be found [here](https://github.com/tio/tio/releas Before running the install steps make sure you have glib and lua libraries installed. For example: ``` -sudo apt install libglib2.0-dev liblua5.2-dev +$ sudo apt install libglib2.0-dev liblua5.2-dev ``` Install steps: From d10e762a7d32f571ebcdac30a1a8c35104a70d98 Mon Sep 17 00:00:00 2001 From: Eliot Alan Foss Date: Tue, 28 May 2024 15:22:29 -0700 Subject: [PATCH 024/146] Added support to receive XMODEM-CRC files from the connected serial port. --- src/tty.c | 20 ++- src/xymodem.c | 388 +++++++++++++++++++++++++++++++++++++++++++++++++- src/xymodem.h | 2 + 3 files changed, 403 insertions(+), 7 deletions(-) diff --git a/src/tty.c b/src/tty.c index 7b89d28..fb6d5a8 100644 --- a/src/tty.c +++ b/src/tty.c @@ -692,7 +692,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) { tio_printf("Sending file '%s' ", line); tio_printf("Press any key to abort transfer"); - tio_printf("%s", xymodem_send(device_fd, line, XMODEM_CRC) < 0 ? "Aborted" : "Done"); + tio_printf("%s", xymodem_send(device_fd, line, XMODEM_1K) < 0 ? "Aborted" : "Done"); } break; @@ -707,6 +707,17 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) } break; + case KEY_2: + tio_printf("Receive file with XMODEM-CRC"); + tio_printf_raw("Enter file name: "); + if (tio_readln()) + { + tio_printf("Ready to receiving file '%s' ", line); + tio_printf("Press any key to abort transfer"); + tio_printf("%s", xymodem_receive(device_fd, line, XMODEM_CRC) < 0 ? "Aborted" : "Done"); + } + break; + default: tio_error_print("Invalid protocol option"); break; @@ -758,7 +769,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf(" ctrl-%c t Toggle line timestamp mode", option.prefix_key); tio_printf(" ctrl-%c U Toggle conversion to uppercase on output", option.prefix_key); tio_printf(" ctrl-%c v Show version", option.prefix_key); - tio_printf(" ctrl-%c x Send file via Xmodem", option.prefix_key); + tio_printf(" ctrl-%c x Send/Receive file via Xmodem", option.prefix_key); tio_printf(" ctrl-%c y Send file via Ymodem", option.prefix_key); tio_printf(" ctrl-%c ctrl-%c Send ctrl-%c character", option.prefix_key, option.prefix_key, option.prefix_key); break; @@ -956,8 +967,9 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) case KEY_X: tio_printf("Please enter which X modem protocol to use:"); - tio_printf(" (0) XMODEM-1K"); - tio_printf(" (1) XMODEM-CRC"); + tio_printf(" (0) XMODEM-1K send"); + tio_printf(" (1) XMODEM-CRC send"); + tio_printf(" (2) XMODEM-CRC receive"); // Process next input character as sub command sub_command = SUBCOMMAND_XMODEM; break; diff --git a/src/xymodem.c b/src/xymodem.c index 7102305..03aca28 100644 --- a/src/xymodem.c +++ b/src/xymodem.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "xymodem.h" #include "print.h" #include "misc.h" @@ -22,10 +23,20 @@ #define ACK 0x06 #define NAK 0x15 #define CAN 0x18 -#define EOT "\004" +#define EOT 0x04 + +#define SOH_STR "\001" +#define ACK_STR "\006" +#define NAK_STR "\025" +#define CAN_STR "\030" +#define EOT_STR "\004" #define OK 0 #define ERR (-1) +#define ERR_FATAL (-2) +#define USER_CAN (-5) + +#define RX_IGNORE 5 #define min(a, b) ((a) < (b) ? (a) : (b)) @@ -161,7 +172,7 @@ static int xmodem_1k(int sio, const void *data, size_t len, int seq) while (seq) { if (key_hit) return ERR; - if (write(sio, EOT, 1) < 0) { + if (write(sio, EOT_STR, 1) < 0) { tio_error_print("Write EOT to serial failed"); return ERR; } @@ -280,7 +291,7 @@ static int xmodem(int sio, const void *data, size_t len) while (1) { if (key_hit) return ERR; - if (write(sio, EOT, 1) < 0) { + if (write(sio, EOT_STR, 1) < 0) { tio_error_print("Write EOT to serial failed"); return ERR; } @@ -301,6 +312,345 @@ static int xmodem(int sio, const void *data, size_t len) return 0; /* not reached */ } +int start_receive(int sio) +{ + int rc; + struct pollfd fds; + fds.events = POLLIN; + fds.fd = sio; + for (int n = 0; n < 20; n++) + { + /* Send the 'C' byte until the sender of the file responds with + something. The start character will be sent once a second for a number of + seconds. If nothing is received in that time then return false to indicate + that the transfer did not start. */ + rc = write(sio, "C", 1); + if (rc < 0) { + if (errno == EWOULDBLOCK) { + usleep(1000); + continue; + } + tio_error_print("Write packet to serial failed"); + return ERR; + } + /* Wait until data is available */ + rc = poll(&fds, 1, 3000); + if (rc < 0) + { + tio_error_print("%s", strerror(errno)); + return rc; + } + else if (rc > 0) + { + if (fds.revents & POLLIN) + { + return rc; + } + } + if (key_hit) + return USER_CAN; + } + return rc; +} + +uint16_t update_CRC(uint16_t crc, char data_char) +{ + uint8_t data = data_char; + crc = crc ^ ((uint16_t)data << 8); + for (int ix = 0; (ix < 8); ix++) + { + if (crc & 0x8000) + { + crc = (crc << 1) ^ 0x1021; + } + else + { + crc <<= 1; + } + } + return crc; +} + +int receive_packet(int sio, struct xpacket packet, int fd) +{ + char rxSeq1, rxSeq2 = 0; + char resp = 0; + uint16_t calcCrc = 0; + uint16_t rxCrc = 0; + int rc; + + struct pollfd fds; + fds.events = POLLIN; + fds.fd = sio; + + /* Read seq bytes*/ + rc = read_poll(sio, &rxSeq1, 1, 3000); + if (rc == 0) { + tio_error_print("Timeout waiting for first seq byte"); + return ERR; + } else if (rc < 0) { + tio_error_print("Error reading first seq byte") + return ERR_FATAL; + } + rc = read_poll(sio, &rxSeq2, 1, 3000); + if (rc == 0) { + tio_error_print("Timeout waiting for second seq byte"); + return ERR; + } else if (rc < 0) { + tio_error_print("Error reading second seq byte") + return ERR_FATAL; + } + if (key_hit) + return USER_CAN; + + /* Read packet Data */ + for (unsigned ix = 0; (ix < sizeof(packet.data)); ix++) + { + rc = read_poll(sio, &resp, 1, 3000); + /* If the read times out or fails then fail this packet. */ + if (rc == 0) + { + tio_error_print("Timeout waiting for next packet char"); + rc = write(sio, CAN_STR, 1); + if (rc < 0) { + tio_error_print("Write cancel packet to serial failed"); + return ERR_FATAL; + } + return ERR; + } else if (rc < 0) { + tio_error_print("Error reading next packet char") + rc = write(sio, CAN_STR, 1); + if (rc < 0) { + tio_error_print("Write cancel packet to serial failed"); + } + return ERR_FATAL; + } + packet.data[ix] = (uint8_t) resp; + calcCrc = update_CRC(calcCrc, resp); + if (key_hit) + return USER_CAN; + } + + /* Read CRC */ + rc = read_poll(sio, &resp, 1, 3000); + if (rc == 0) { + tio_error_print("Timeout waiting for first CRC byte"); + return ERR; + } else if (rc < 0) { + tio_error_print("Error reading first CRC byte") + return ERR_FATAL; + } + + uint8_t uresp = resp; + uint16_t uresp16 = uresp; + rxCrc = uresp16 << 8; + + rc = read_poll(sio, &resp, 1, 3000); + if (rc == 0) { + tio_error_print("Timeout waiting for second CRC byte"); + return ERR; + } else if (rc < 0) { + tio_error_print("Error reading second CRC byte") + return ERR_FATAL; + } + + uresp = resp; + uresp16 = uresp; + rxCrc |= uresp16; + + if (key_hit) + return USER_CAN; + + /* At this point in the code, there should not be anything in the receive buffer + because the sender has just sent a complete packet and is waiting on a response. */ + rc = poll(&fds, 1, 10); + if (rc < 0) + { + tio_error_print("%s", strerror(errno)); + tio_error_print("Poll check error after packet finish"); + rc = write(sio, CAN_STR, 1); + if (rc < 0) { + tio_error_print("Write cancel packet to serial failed"); + } + return ERR_FATAL; + } + else if (rc > 0) + { + if (fds.revents & POLLIN) + { + tio_error_print("RX sync error"); + char dummy = 0; + /* Drain buffer */ + while (read_poll(sio, &dummy, 1, 100) > 0) {} + return ERR; + } + } + + uint8_t tester = 0xff; + uint8_t seq1 = rxSeq1; + uint8_t seq2 = rxSeq2; + + if ((calcCrc == rxCrc) && (seq1 == packet.seq - 1) && ((seq1 ^ seq2) == tester)) + { + /* Resend of previously processed packet. */ + rc = write(sio, ACK_STR, 1); + if (rc < 0) { + tio_error_print("Write acknowlegdement packet to serial failed"); + return ERR_FATAL; + } + return RX_IGNORE; + } + else if ((calcCrc != rxCrc) || (seq1 != packet.seq) || ((seq1 ^ seq2) != tester)) + { + /* Fail if the CRC or sequence number is not correct or if the two received + sequence numbers are not the complement of one another. */ + tio_error_print("Bad CRC or sequence number"); + tio_debug_printf("CRC read: %u", rxCrc); + tio_debug_printf("CRC calculated: %u", calcCrc); + tio_debug_printf("Seq read: %hhu", rxSeq1); + tio_debug_printf("Seq should be: %hhu", packet.seq); + tio_debug_printf("inv seq: %hhu", rxSeq2); + return ERR; + } + else + { + /* The data is good. Process the packet then ACK it to the sender. */ + rc = write(fd, packet.data, sizeof(packet.data)); + if (rc < 0) + { + tio_error_print("Problem writing to file"); + rc = write(sio, CAN_STR, 1); + if (rc < 0) { + tio_error_print("Write cancel packet to serial failed"); + } + return ERR_FATAL; + } + rc = write(sio, ACK_STR, 1); + if (rc < 0) + { + tio_error_print("Write acknowlegdement packet to serial failed"); + return ERR_FATAL; + } + } + + return OK; +} + +int xmodem_receive(int sio, int fd) +{ + struct xpacket packet; + char resp = 0; + int rc; + bool complete = false; + char status; + + /* Drain pending characters from serial line.*/ + while(1) { + if (key_hit) + return -1; + rc = read_poll(sio, &resp, 1, 50); + if (rc == 0) { + if (resp == CAN) return ERR; + break; + } + else if (rc < 0) { + if (rc != USER_CAN) { + tio_error_print("Read sync from serial failed"); + } + return ERR; + } + } + + /* Always work with 128b packets */ + packet.seq = 1; + packet.type = SOH; + + /* Start Receive*/ + rc = start_receive(sio); + if (rc == 0) + { + tio_error_print("Timeout waiting for transfer to start"); + return ERR; + } else if (rc < 0) { + tio_error_print("Error starting XMODEM receive"); + return ERR; + } + + while (!complete) { + /* Poll for 1 new byte for 3 seconds */ + rc = read_poll(sio, &resp, 1, 3000); + if (rc == 0) { + tio_error_print("Timeout waiting for start of next packet"); + return ERR; + } else if (rc < 0) { + tio_error_print("Error reading start of next packet") + return ERR; + } + if (key_hit) + return USER_CAN; + + switch(resp) + { + case SOH: + /* Start of a packet */ + rc = receive_packet(sio, packet, fd); + if (rc == OK) { + packet.seq++; + status = '.'; + } else if (rc == ERR) { + rc = write(sio, NAK_STR, 1); + if (rc < 0) { + tio_error_print("Writing not acknowledge packet to serial failed"); + return ERR; + } + status = 'N'; + } else if (rc == ERR_FATAL) { + tio_error_print("Receive cancelled due to fatal error"); + return ERR; + } else if (rc == USER_CAN) { + rc = write(sio, CAN_STR, 1); + if (rc < 0) { + tio_error_print("Writing cancel to serial failed"); + return ERR; + } + return USER_CAN; + } else if (rc == RX_IGNORE) { + status = ':'; + } + break; + + case EOT: + /* End of Transfer */ + rc = write(sio, ACK_STR, 1); + if (rc < 0) + { + tio_error_print("Write acknowlegdement packet to serial failed"); + return ERR; + } + complete = true; + status = '\0'; + write(STDOUT_FILENO, "|\r\n", 3); + break; + + case CAN: + /* Cancel from sender */ + tio_error_print("Transmission cancelled from sender"); + return ERR; + break; + + default: + tio_error_print("Unexpected character received waiting for next packet"); + return ERR; + break; + } + + + /* Update "progress bar" */ + write(STDOUT_FILENO, &status, 1); + } + return OK; +} + int xymodem_send(int sio, const char *filename, modem_mode_t mode) { size_t len; @@ -355,3 +705,35 @@ int xymodem_send(int sio, const char *filename, modem_mode_t mode) close(fd); return rc; } + +int xymodem_receive(int sio, const char *filename, modem_mode_t mode) +{ + int rc, fd; + + /* Create new file */ + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC); + if (fd < 0) { + tio_error_print("Could not open file"); + return ERR; + } + + /* Do transfer */ + key_hit = 0; + if (mode == XMODEM_1K) { + tio_error_print("Not supported"); + rc = -1; + } + else if (mode == XMODEM_CRC) { + rc = xmodem_receive(sio, fd); + } + else { + tio_error_print("Not supported"); + rc = -1; + } + key_hit = 0xff; + + /* Flush serial and release resources */ + tcflush(sio, TCIOFLUSH); + close(fd); + return rc; +} diff --git a/src/xymodem.h b/src/xymodem.h index ef7f9a3..1b46cd7 100644 --- a/src/xymodem.h +++ b/src/xymodem.h @@ -30,3 +30,5 @@ typedef enum { extern char key_hit; int xymodem_send(int sio, const char *filename, modem_mode_t mode); + +int xymodem_receive(int sio, const char *filename, modem_mode_t mode); From 883acbaa4b2831d53f0c0ca23213ac1d5f135952 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 29 May 2024 01:24:02 +0200 Subject: [PATCH 025/146] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 6da8aef..5c80971 100644 --- a/AUTHORS +++ b/AUTHORS @@ -54,5 +54,6 @@ Mingjie Shen Brian Davis C KhazAkar +Eliot Alan Foss Thanks to everyone who has contributed to this project. From 7e0bd980f2cbff9c4596dacd203122ba5bfd3334 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 29 May 2024 01:39:59 +0200 Subject: [PATCH 026/146] Fix missing open() flags in xymodem_receive() --- src/xymodem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xymodem.c b/src/xymodem.c index 03aca28..1b6c1bc 100644 --- a/src/xymodem.c +++ b/src/xymodem.c @@ -711,7 +711,7 @@ int xymodem_receive(int sio, const char *filename, modem_mode_t mode) int rc, fd; /* Create new file */ - fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC); + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) { tio_error_print("Could not open file"); return ERR; From 133789517afb0afdf08b2e2352022e3ba379ebd1 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 29 May 2024 09:26:08 +0200 Subject: [PATCH 027/146] Add group write permission to xymodem received file --- src/xymodem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xymodem.c b/src/xymodem.c index 1b6c1bc..606093a 100644 --- a/src/xymodem.c +++ b/src/xymodem.c @@ -711,7 +711,7 @@ int xymodem_receive(int sio, const char *filename, modem_mode_t mode) int rc, fd; /* Create new file */ - fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0664); if (fd < 0) { tio_error_print("Could not open file"); return ERR; From bb3636e2d5a474052434866e3a34ab05927ee553 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 30 May 2024 12:17:00 +0200 Subject: [PATCH 028/146] Update TODO --- TODO | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/TODO b/TODO index d1609fa..1cb3926 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,24 @@ + * Porting layer to support native win32 builds. + + Some of the work that needs to be done: + + All posix functions need to be platform independent, go though file by file: + + termios.h + unistd.h has very limited functions + ENV different in config_file_resolve + errno + sys/ioctl.h + sys/poll.h + socket, may need a new thread + Serial, RS485, character mapping + Communication pipe + + Port enumerate, all devices of the same type have the same name (eg. USB + Serial Device for ttyACM) -> which makes regex not meaningful (kind of a + good thing since libtre in Mingw has too much dependencies makes binary too + big) + * Support traditional hex output format such as: 00000000 74 65 73 74 20 74 65 73 74 20 74 65 73 74 20 74 |test test test t| From f148a1413cb3179a2e7f291514cd71df2e7e3bd7 Mon Sep 17 00:00:00 2001 From: Vyacheslav Patkov Date: Sat, 1 Jun 2024 17:31:22 +0400 Subject: [PATCH 029/146] Prompt for Lua script or shell command in interactive session --- README.md | 1 + man/tio.1.in | 2 ++ src/script.c | 9 +++++++-- src/script.h | 2 +- src/tty.c | 19 +++++++++++++++++-- 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ee86ffa..e33cff1 100644 --- a/README.md +++ b/README.md @@ -304,6 +304,7 @@ ctrl-t ? to list the available key commands. [15:02:53.269] ctrl-t p Pulse serial port line [15:02:53.269] ctrl-t q Quit [15:02:53.269] ctrl-t r Run script +[15:02:53.269] ctrl-t R Execute shell command with I/O redirected to device [15:02:53.269] ctrl-t s Show statistics [15:02:53.269] ctrl-t t Toggle line timestamp mode [15:02:53.269] ctrl-t U Toggle conversion to uppercase on output diff --git a/man/tio.1.in b/man/tio.1.in index e016154..16e72d8 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -405,6 +405,8 @@ Pulse serial port line Quit .IP "\fBctrl-t r" Run script +.IP "\fBctrl-t R" +Execute shell command with I/O redirected to device .IP "\fBctrl-t s" Show TX/RX statistics .IP "\fBctrl-t t" diff --git a/src/script.c b/src/script.c index f425a1f..8d68063 100644 --- a/src/script.c +++ b/src/script.c @@ -535,7 +535,7 @@ void script_set_globals(lua_State *L) script_set_global(L, "YMODEM", YMODEM); } -void script_run(int fd) +void script_run(int fd, const char *script_filename) { lua_State *L; @@ -550,7 +550,12 @@ void script_run(int fd) // Initialize globals script_set_globals(L); - if (option.script_filename != NULL) + if (script_filename != NULL) + { + tio_printf("Running script %s", script_filename); + script_file_run(L, script_filename); + } + else if (option.script_filename != NULL) { tio_printf("Running script %s", option.script_filename); script_file_run(L, option.script_filename); diff --git a/src/script.h b/src/script.h index d6df204..58ba1e1 100644 --- a/src/script.h +++ b/src/script.h @@ -29,5 +29,5 @@ typedef enum SCRIPT_RUN_END, } script_run_t; -void script_run(int fd); +void script_run(int fd, const char *script_filename); const char *script_run_state_to_string(script_run_t state); diff --git a/src/tty.c b/src/tty.c index fb6d5a8..df61e7f 100644 --- a/src/tty.c +++ b/src/tty.c @@ -110,6 +110,7 @@ #define KEY_P 0x70 #define KEY_Q 0x71 #define KEY_R 0x72 +#define KEY_SHIFT_R 0x52 #define KEY_S 0x73 #define KEY_T 0x74 #define KEY_U 0x55 @@ -765,6 +766,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf(" ctrl-%c p Pulse serial port line", option.prefix_key); tio_printf(" ctrl-%c q Quit", option.prefix_key); tio_printf(" ctrl-%c r Run script", option.prefix_key); + tio_printf(" ctrl-%c R Execute shell command with I/O redirected to device", option.prefix_key); tio_printf(" ctrl-%c s Show statistics", option.prefix_key); tio_printf(" ctrl-%c t Toggle line timestamp mode", option.prefix_key); tio_printf(" ctrl-%c U Toggle conversion to uppercase on output", option.prefix_key); @@ -922,7 +924,20 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) case KEY_R: /* Run script */ - script_run(device_fd); + tio_printf("Run Lua script") + tio_printf_raw("Enter file name: "); + if (tio_readln()) + script_run(device_fd, line); + else + script_run(device_fd, NULL); + break; + + case KEY_SHIFT_R: + /* Execute shell command */ + tio_printf("Execute shell command with I/O redirected to device"); + tio_printf_raw("Enter command: "); + if (tio_readln()) + execute_shell_command(device_fd, line); break; case KEY_S: @@ -2281,7 +2296,7 @@ int tty_connect(void) /* Manage script activation */ if (option.script_run != SCRIPT_RUN_NEVER) { - script_run(device_fd); + script_run(device_fd, NULL); if (option.script_run == SCRIPT_RUN_ONCE) { From d1d6b45e8e7e397a333e86acc26caf2056410dd6 Mon Sep 17 00:00:00 2001 From: Vyacheslav Patkov Date: Sun, 2 Jun 2024 14:09:09 +0400 Subject: [PATCH 030/146] Use "ctrl-t m" to change mappings interactively --- README.md | 3 +- man/tio.1.in | 4 +- src/tty.c | 108 ++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 92 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index e33cff1..fec55f8 100644 --- a/README.md +++ b/README.md @@ -299,7 +299,7 @@ ctrl-t ? to list the available key commands. [15:02:53.269] ctrl-t i Toggle input mode [15:02:53.269] ctrl-t l Clear screen [15:02:53.269] ctrl-t L Show line states -[15:02:53.269] ctrl-t m Toggle MSB to LSB bit order +[15:02:53.269] ctrl-t m Change mapping of characters on input or output [15:02:53.269] ctrl-t o Toggle output mode [15:02:53.269] ctrl-t p Pulse serial port line [15:02:53.269] ctrl-t q Quit @@ -307,7 +307,6 @@ ctrl-t ? to list the available key commands. [15:02:53.269] ctrl-t R Execute shell command with I/O redirected to device [15:02:53.269] ctrl-t s Show statistics [15:02:53.269] ctrl-t t Toggle line timestamp mode -[15:02:53.269] ctrl-t U Toggle conversion to uppercase on output [15:02:53.269] ctrl-t v Show version [15:02:53.269] ctrl-t x Send file via Xmodem [15:02:53.269] ctrl-t y Send file via Ymodem diff --git a/man/tio.1.in b/man/tio.1.in index 16e72d8..a378095 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -396,7 +396,7 @@ Clear screen .IP "\fBctrl-t L" Show line states (DTR, RTS, CTS, DSR, DCD, RI) .IP "\fBctrl-t m" -Toggle MSB to LSB bit order +Change mapping of characters on input or output .IP "\fBctrl-t o" Toggle output mode .IP "\fBctrl-t p" @@ -411,8 +411,6 @@ Execute shell command with I/O redirected to device Show TX/RX statistics .IP "\fBctrl-t t" Toggle line timestamp mode -.IP "\fBctrl-t U" -Toggle conversion to uppercase on output .IP "\fBctrl-t v" Show version .IP "\fBctrl-t x" diff --git a/src/tty.c b/src/tty.c index df61e7f..1724b1e 100644 --- a/src/tty.c +++ b/src/tty.c @@ -95,7 +95,12 @@ #define KEY_3 0x33 #define KEY_4 0x34 #define KEY_5 0x35 +#define KEY_6 0x36 +#define KEY_7 0x37 +#define KEY_8 0x38 +#define KEY_9 0x39 #define KEY_QUESTION 0x3f +#define KEY_A 0x61 #define KEY_B 0x62 #define KEY_C 0x63 #define KEY_E 0x65 @@ -113,7 +118,6 @@ #define KEY_SHIFT_R 0x52 #define KEY_S 0x73 #define KEY_T 0x74 -#define KEY_U 0x55 #define KEY_V 0x76 #define KEY_X 0x78 #define KEY_Y 0x79 @@ -131,6 +135,7 @@ typedef enum SUBCOMMAND_LINE_TOGGLE, SUBCOMMAND_LINE_PULSE, SUBCOMMAND_XMODEM, + SUBCOMMAND_MAP, } sub_command_t; const char random_array[] = @@ -724,6 +729,62 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) break; } break; + + case SUBCOMMAND_MAP: + switch (input_char) + { + case KEY_0: + tio.c_iflag ^= ICRNL; + map_i_cr_nl = !map_i_cr_nl; + tio_printf("ICRNL is %s", map_i_cr_nl ? "set" : "unset"); + break; + case KEY_1: + tio.c_iflag ^= IGNCR; + map_ign_cr = !map_ign_cr; + tio_printf("IGNCR is %s", map_ign_cr ? "set" : "unset"); + break; + case KEY_2: + map_i_ff_escc = !map_i_ff_escc; + tio_printf("IFFESCC is %s", map_i_ff_escc ? "set" : "unset"); + break; + case KEY_3: + tio.c_iflag ^= INLCR; + map_i_nl_cr = !map_i_nl_cr; + tio_printf("INLCR is %s", map_i_nl_cr ? "set" : "unset"); + break; + case KEY_4: + map_i_nl_crnl = !map_i_nl_crnl; + tio_printf("INLCRNL is %s", map_i_nl_crnl ? "set" : "unset"); + break; + case KEY_5: + map_o_cr_nl = !map_o_cr_nl; + tio_printf("OCRNL is %s", map_o_cr_nl ? "set" : "unset"); + break; + case KEY_6: + map_o_del_bs = !map_o_del_bs; + tio_printf("ODELBS is %s", map_o_del_bs ? "set" : "unset"); + break; + case KEY_7: + map_o_nl_crnl = !map_o_nl_crnl; + tio_printf("ONLCRNL is %s", map_o_nl_crnl ? "set" : "unset"); + break; + case KEY_8: + map_o_ltu = !map_o_ltu; + tio_printf("OLTU is %s", map_o_ltu ? "set" : "unset"); + break; + case KEY_9: + map_o_nulbrk = !map_o_nulbrk; + tio_printf("ONULBRK is %s", map_o_nulbrk ? "set" : "unset"); + break; + case KEY_A: + map_o_msblsb = !map_o_msblsb; + tio_printf("MSB2LSB is %s", map_o_msblsb ? "set" : "unset"); + break; + default: + tio_error_print("Invalid input"); + break; + } + break; } sub_command = SUBCOMMAND_NONE; @@ -761,7 +822,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf(" ctrl-%c i Toggle input mode", option.prefix_key); tio_printf(" ctrl-%c l Clear screen", option.prefix_key); tio_printf(" ctrl-%c L Show line states", option.prefix_key); - tio_printf(" ctrl-%c m Toggle MSB to LSB bit order", option.prefix_key); + tio_printf(" ctrl-%c m Change mapping of characters on input or output", option.prefix_key); tio_printf(" ctrl-%c o Toggle output mode", option.prefix_key); tio_printf(" ctrl-%c p Pulse serial port line", option.prefix_key); tio_printf(" ctrl-%c q Quit", option.prefix_key); @@ -769,7 +830,6 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf(" ctrl-%c R Execute shell command with I/O redirected to device", option.prefix_key); tio_printf(" ctrl-%c s Show statistics", option.prefix_key); tio_printf(" ctrl-%c t Toggle line timestamp mode", option.prefix_key); - tio_printf(" ctrl-%c U Toggle conversion to uppercase on output", option.prefix_key); tio_printf(" ctrl-%c v Show version", option.prefix_key); tio_printf(" ctrl-%c x Send/Receive file via Xmodem", option.prefix_key); tio_printf(" ctrl-%c y Send file via Ymodem", option.prefix_key); @@ -905,17 +965,33 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) break; case KEY_M: - /* Toggle bit order */ - if (!map_o_msblsb) - { - map_o_msblsb = true; - tio_printf("Switched to reverse bit order"); - } - else - { - map_o_msblsb = false; - tio_printf("Switched to normal bit order"); - } + /* Change mapping of characters on input or output */ + tio_printf("Please enter which mapping to set or unset:"); + tio_printf(" (0) ICRNL: %s mapping CR to NL on input (unless IGNCR is set)", + map_i_cr_nl ? "Unset" : "Set"); + tio_printf(" (1) IGNCR: %s ignoring CR on input", + map_ign_cr ? "Unset" : "Set"); + tio_printf(" (2) IFFESCC: %s mapping FF to ESC-c on input", + map_i_ff_escc ? "Unset" : "Set"); + tio_printf(" (3) INLCR: %s mapping NL to CR on input", + map_i_nl_cr ? "Unset" : "Set"); + tio_printf(" (4) INLCRNL: %s mapping NL to CR-NL on input", + map_i_nl_cr ? "Unset" : "Set"); + tio_printf(" (5) OCRNL: %s mapping CR to NL on output", + map_o_cr_nl ? "Unset" : "Set"); + tio_printf(" (6) ODELBS: %s mapping DEL to BS on output", + map_o_del_bs ? "Unset" : "Set"); + tio_printf(" (7) ONLCRNL: %s mapping NL to CR-NL on output", + map_o_nl_crnl ? "Unset" : "Set"); + tio_printf(" (8) OLTU: %s mapping lowercase to uppercase on output", + map_o_ltu ? "Unset" : "Set"); + tio_printf(" (9) ONULBRK: %s mapping NUL to send break signal on output", + map_o_nulbrk ? "Unset" : "Set"); + tio_printf(" (a) MSB2LSB: %s mapping MSB bit order to LSB on output", + map_o_msblsb ? "Unset" : "Set"); + + // Process next input character as sub command + sub_command = SUBCOMMAND_MAP; break; case KEY_Q: @@ -972,10 +1048,6 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) } break; - case KEY_U: - map_o_ltu = !map_o_ltu; - break; - case KEY_V: tio_printf("tio v%s", VERSION); break; From 563c4fa6eaf62ff93549ac9edfe639e30ef53e04 Mon Sep 17 00:00:00 2001 From: Vyacheslav Patkov Date: Sun, 2 Jun 2024 15:17:38 +0400 Subject: [PATCH 031/146] Show current mappings in the configuration printout --- src/options.c | 4 ---- src/tty.c | 27 +++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/options.c b/src/options.c index 4989f20..58ae78e 100644 --- a/src/options.c +++ b/src/options.c @@ -716,10 +716,6 @@ void options_print() tio_printf(" Input mode: %s", option_input_mode_to_string(option.input_mode)); tio_printf(" Output mode: %s", option_output_mode_to_string(option.output_mode)); tio_printf(" Alert: %s", option_alert_state_to_string(option.alert)); - if (option.map[0] != 0) - { - tio_printf(" Map flags: %s", option.map); - } if (option.log) { tio_printf(" Log file: %s", log_get_filename()); diff --git a/src/tty.c b/src/tty.c index 1724b1e..2adef9e 100644 --- a/src/tty.c +++ b/src/tty.c @@ -630,6 +630,32 @@ void tty_output_mode_set(output_mode_t mode) } } +static void mappings_print(void) +{ + if (map_i_cr_nl || map_ign_cr || map_i_ff_escc || map_i_nl_cr || + map_i_nl_crnl || map_o_cr_nl || map_o_del_bs || map_o_nl_crnl || + map_o_ltu || map_o_nulbrk || map_o_msblsb) + { + tio_printf(" Mappings:%s%s%s%s%s%s%s%s%s%s%s", + map_i_cr_nl ? " ICRNL" : "", + map_ign_cr ? " IGNCR" : "", + map_i_ff_escc ? " IFFESCC" : "", + map_i_nl_cr ? " INLCR" : "", + map_i_nl_crnl ? " INLCRNL" : "", + map_o_cr_nl ? " OCRNL" : "", + map_o_del_bs ? " ODELBS" : "", + map_o_nl_crnl ? " ONLCRNL" : "", + map_o_ltu ? " OLTU" : "", + map_o_nulbrk ? " ONULBRK" : "", + map_o_msblsb ? " MSB2LSB" : ""); + } + else + { + tio_printf(" Mappings: none"); + } + +} + void handle_command_sequence(char input_char, char *output_char, bool *forward) { char unused_char; @@ -908,6 +934,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) { rs485_print_config(); } + mappings_print(); break; case KEY_E: From 8014ef68c09835dad29cf66b3ed1de415d41b7de Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 2 Jun 2024 14:26:39 +0200 Subject: [PATCH 032/146] Cleanup parsing of mapping flags --- src/configfile.c | 8 +- src/options.c | 98 +++++++++++++++++++++- src/options.h | 14 +++- src/socket.c | 6 +- src/tty.c | 209 +++++++++++++++-------------------------------- src/tty.h | 3 - 6 files changed, 186 insertions(+), 152 deletions(-) diff --git a/src/configfile.c b/src/configfile.c index 697fed7..10717ac 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -215,7 +215,13 @@ static void config_parse_keys(GKeyFile *key_file, char *group) config_get_string(key_file, group, "log-file", &option.log_filename, NULL); config_get_bool(key_file, group, "log-append", &option.log_append); config_get_bool(key_file, group, "log-strip", &option.log_strip); - config_get_string(key_file, group, "map", &option.map, NULL); + config_get_string(key_file, group, "map", &string, NULL); + if (string != NULL) + { + option_parse_mappings(string); + g_free((void *)string); + string = NULL; + } config_get_string(key_file, group, "color", &string, NULL); if (string != NULL) { diff --git a/src/options.c b/src/options.c index 58ae78e..94a623f 100644 --- a/src/options.c +++ b/src/options.c @@ -85,7 +85,6 @@ struct option_t option = .local_echo = false, .timestamp = TIMESTAMP_NONE, .socket = NULL, - .map = "", .color = 256, // Bold .input_mode = INPUT_MODE_NORMAL, .output_mode = OUTPUT_MODE_NORMAL, @@ -109,6 +108,17 @@ struct option_t option = .hex_n_value = 0, .vt100 = false, .exec = NULL, + .map_i_nl_cr = false, + .map_i_cr_nl = false, + .map_ign_cr = false, + .map_i_ff_escc = false, + .map_i_nl_crnl = false, + .map_o_cr_nl = false, + .map_o_nl_crnl = false, + .map_o_del_bs = false, + .map_o_ltu = false, + .map_o_nulbrk = false, + .map_o_msblsb = false, }; void option_print_help(char *argv[]) @@ -692,6 +702,90 @@ void option_parse_script_run(const char *arg, script_run_t *script_run) } } +void option_parse_mappings(const char *map) +{ + bool token_found = true; + char *token = NULL; + char *buffer; + + if (map == NULL) + { + return; + } + + /* Parse any specified input or output mappings */ + buffer = strdup(map); + while (token_found == true) + { + if (token == NULL) + { + token = strtok(buffer,","); + } + else + { + token = strtok(NULL, ","); + } + + if (token != NULL) + { + if (strcmp(token,"INLCR") == 0) + { + option.map_i_nl_cr = true; + } + else if (strcmp(token,"IGNCR") == 0) + { + option.map_ign_cr = true; + } + else if (strcmp(token,"ICRNL") == 0) + { + option.map_i_cr_nl = true; + } + else if (strcmp(token,"OCRNL") == 0) + { + option.map_o_cr_nl = true; + } + else if (strcmp(token,"ODELBS") == 0) + { + option.map_o_del_bs = true; + } + else if (strcmp(token,"IFFESCC") == 0) + { + option.map_i_ff_escc = true; + } + else if (strcmp(token,"INLCRNL") == 0) + { + option.map_i_nl_crnl = true; + } + else if (strcmp(token, "ONLCRNL") == 0) + { + option.map_o_nl_crnl = true; + } + else if (strcmp(token, "OLTU") == 0) + { + option.map_o_ltu = true; + } + else if (strcmp(token, "ONULBRK") == 0) + { + option.map_o_nulbrk = true; + } + else if (strcmp(token, "MSB2LSB") == 0) + { + option.map_o_msblsb = true; + } + else + { + printf("Error: Unknown mapping flag %s\n", token); + exit(EXIT_FAILURE); + } + } + else + { + token_found = false; + } + } + free(buffer); +} + void options_print() { tio_printf(" Device: %s", device_name); @@ -928,7 +1022,7 @@ void options_parse(int argc, char *argv[]) break; case 'm': - option.map = optarg; + option_parse_mappings(optarg); break; case 'c': diff --git a/src/options.h b/src/options.h index 21a5f75..80e0f3a 100644 --- a/src/options.h +++ b/src/options.h @@ -69,7 +69,6 @@ struct option_t timestamp_t timestamp; char *log_filename; char *log_directory; - char *map; char *socket; int color; input_mode_t input_mode; @@ -94,6 +93,17 @@ struct option_t int hex_n_value; bool vt100; char *exec; + bool map_i_nl_cr; + bool map_i_cr_nl; + bool map_ign_cr; + bool map_i_ff_escc; + bool map_i_nl_crnl; + bool map_o_cr_nl; + bool map_o_nl_crnl; + bool map_o_del_bs; + bool map_o_ltu; + bool map_o_nulbrk; + bool map_o_msblsb; }; extern struct option_t option; @@ -119,3 +129,5 @@ const char *option_auto_connect_state_to_string(auto_connect_t strategy); void option_parse_timestamp(const char *arg, timestamp_t *timestamp); const char* option_timestamp_format_to_string(timestamp_t timestamp); + +void option_parse_mappings(const char *map); diff --git a/src/socket.c b/src/socket.c index 043b657..920596f 100644 --- a/src/socket.c +++ b/src/socket.c @@ -343,20 +343,20 @@ bool socket_handle_input(fd_set *rdfs, char *output_char) } /* If INLCR is set, a received NL character shall be translated into a CR character */ - if (*output_char == '\n' && map_i_nl_cr) + if (*output_char == '\n' && option.map_i_nl_cr) { *output_char = '\r'; } else if (*output_char == '\r') { /* If IGNCR is set, a received CR character shall be ignored (not read). */ - if (map_ign_cr) + if (option.map_ign_cr) { return false; } /* If IGNCR is not set and ICRNL is set, a received CR character shall be translated into an NL character. */ - if (map_i_cr_nl) + if (option.map_i_cr_nl) { *output_char = '\n'; } diff --git a/src/tty.c b/src/tty.c index 2adef9e..d6356bf 100644 --- a/src/tty.c +++ b/src/tty.c @@ -153,9 +153,6 @@ const char random_array[] = }; bool interactive_mode = true; -bool map_i_nl_cr = false; -bool map_i_cr_nl = false; -bool map_ign_cr = false; char key_hit = 0xff; @@ -167,14 +164,6 @@ static bool connected = false; static bool standard_baudrate = true; static void (*print)(char c); static int device_fd; -static bool map_i_ff_escc = false; -static bool map_i_nl_crnl = false; -static bool map_o_cr_nl = false; -static bool map_o_nl_crnl = false; -static bool map_o_del_bs = false; -static bool map_o_ltu = false; -static bool map_o_nulbrk = false; -static bool map_o_msblsb = false; static char hex_chars[2]; static unsigned char hex_char_index = 0; static char tty_buffer[BUFSIZ*2]; @@ -259,7 +248,7 @@ ssize_t tty_write(int fd, const void *buffer, size_t count) ssize_t retval = 0, bytes_written = 0; size_t i; - if (map_o_ltu) + if (option.map_o_ltu) { // Convert lower case to upper case for (i = 0; i Date: Sun, 2 Jun 2024 23:34:33 +0200 Subject: [PATCH 033/146] Make sure ICRNL, IGNCR, INLCR take effect --- src/tty.c | 17 ++++++++++++++--- src/tty.h | 1 + 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/tty.c b/src/tty.c index d6356bf..bb90d12 100644 --- a/src/tty.c +++ b/src/tty.c @@ -749,13 +749,13 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) switch (input_char) { case KEY_0: - tio.c_iflag ^= ICRNL; option.map_i_cr_nl = !option.map_i_cr_nl; + tty_reconfigure(); tio_printf("ICRNL is %s", option.map_i_cr_nl ? "set" : "unset"); break; case KEY_1: - tio.c_iflag ^= IGNCR; option.map_ign_cr = !option.map_ign_cr; + tty_reconfigure(); tio_printf("IGNCR is %s", option.map_ign_cr ? "set" : "unset"); break; case KEY_2: @@ -763,8 +763,8 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf("IFFESCC is %s", option.map_i_ff_escc ? "set" : "unset"); break; case KEY_3: - tio.c_iflag ^= INLCR; option.map_i_nl_cr = !option.map_i_nl_cr; + tty_reconfigure(); tio_printf("INLCR is %s", option.map_i_nl_cr ? "set" : "unset"); break; case KEY_4: @@ -1365,6 +1365,17 @@ void tty_configure(void) } } +void tty_reconfigure(void) +{ + tty_configure(); + + if (connected) + { + /* Activate new port settings */ + tcsetattr(device_fd, TCSANOW, &tio); + } +} + static bool is_serial_device(const char *format, ...) { char filename[PATH_MAX]; diff --git a/src/tty.h b/src/tty.h index a04304d..39c64af 100644 --- a/src/tty.h +++ b/src/tty.h @@ -75,6 +75,7 @@ extern bool interactive_mode; void stdout_configure(void); void stdin_configure(void); void tty_configure(void); +void tty_reconfigure(void); int tty_connect(void); void tty_wait_for_device(void); void list_serial_devices(void); From 134038c1ce3f077caa3d9bc8bf67c2bd4e9365b4 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 9 Jun 2024 13:03:56 +0200 Subject: [PATCH 034/146] Fix line input mode Fix so that ABCD are no longer ignored. --- src/tty.c | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/src/tty.c b/src/tty.c index bb90d12..b60d45a 100644 --- a/src/tty.c +++ b/src/tty.c @@ -2593,43 +2593,6 @@ int tty_connect(void) case INPUT_MODE_LINE: switch (input_char) { - case 27: // Escape - forward = false; - break; - - case '[': - if (previous_char[0] == 27) - { - forward = false; - } - break; - - case 'A': - case 'B': - case 'C': - case 'D': - if ((previous_char[1] == 27) && (previous_char[0] == '[')) - { - // Handle arrow keys - switch (input_char) - { - case 'A': // Up arrow - // Ignore - break; - case 'B': // Down arrow - // Ignore - break; - case 'C': // Right arrow - // Ignore - break; - case 'D': // Left arrow - // Ignore - break; - } - forward = false; - } - break; - case '\b': case 127: // Backspace if (line_index) From bed34a0b9a5014c4a310a751443e18344700235f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 11 Jun 2024 15:18:34 +0200 Subject: [PATCH 035/146] Reuse socket address To avoid having to wait for socket timeout when restarting server. --- src/socket.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/socket.c b/src/socket.c index 920596f..a4de8aa 100644 --- a/src/socket.c +++ b/src/socket.c @@ -124,6 +124,7 @@ void socket_configure(void) struct sockaddr_in6 sockaddr_inet6 = {}; struct sockaddr *sockaddr_p; socklen_t socklen; + int optval; /* Parse socket string */ @@ -225,6 +226,12 @@ void socket_configure(void) exit(EXIT_FAILURE); } + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))) + { + tio_error_printf("Failed to set socket options (%s)", strerror(errno)); + exit(EXIT_FAILURE); + } + /* Bind */ if (bind(sockfd, sockaddr_p, socklen) < 0) { From 6ec2ac19d093ed0f9af3cfe98cb9ceb5f27ac23d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 15 Jun 2024 13:37:22 +0200 Subject: [PATCH 036/146] Add history and editing feature to line input mode Use up and down arrow keys to navigate history. Use left and right arrow keys to move cursor back and forth. We try mimic the behaviour of GNU readline which we can not use because we also need to react to key commands. --- src/print.c | 12 +++ src/print.h | 1 + src/tty.c | 239 ++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 198 insertions(+), 54 deletions(-) diff --git a/src/print.c b/src/print.c index bfcccbf..efc1519 100644 --- a/src/print.c +++ b/src/print.c @@ -75,3 +75,15 @@ void print_tainted_set() { print_tainted = true; } + +void print(const char *format, ...) +{ + va_list args; + + va_start(args, format); + vprintf(format, args); + fflush(stdout); + va_end(args); + + print_tainted = false; +} diff --git a/src/print.h b/src/print.h index 9baf042..d6f8511 100644 --- a/src/print.h +++ b/src/print.h @@ -134,6 +134,7 @@ extern char ansi_format[]; #define tio_debug_printf_raw(format, args...) #endif +void print(const char *format, ...); void print_hex(char c); void print_normal(char c); void print_init_ansi_formatting(void); diff --git a/src/tty.c b/src/tty.c index b60d45a..6805fae 100644 --- a/src/tty.c +++ b/src/tty.c @@ -87,7 +87,8 @@ #define CMSPAR 010000000000 #endif -#define LINE_SIZE_MAX 1000 +#define MAX_LINE_LEN PATH_MAX +#define MAX_HISTORY 1000 #define KEY_0 0x30 #define KEY_1 0x31 @@ -162,7 +163,7 @@ static struct termios tio, tio_old, stdout_new, stdout_old, stdin_new, stdin_old static unsigned long rx_total = 0, tx_total = 0; static bool connected = false; static bool standard_baudrate = true; -static void (*print)(char c); +static void (*printchar)(char c); static int device_fd; static char hex_chars[2]; static unsigned char hex_char_index = 0; @@ -172,7 +173,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[LINE_SIZE_MAX]; +static char line[MAX_LINE_LEN]; static void optional_local_echo(char c) { @@ -181,7 +182,7 @@ static void optional_local_echo(char c) return; } - print(c); + printchar(c); if ((option.output_mode == OUTPUT_MODE_NORMAL) && (c == 127)) { @@ -580,13 +581,13 @@ static int tio_readln(void) char *p = line; /* Read line, accept BS and DEL as rubout characters */ - for (p = line ; p < &line[LINE_SIZE_MAX-1]; ) + for (p = line ; p < &line[MAX_LINE_LEN-1]; ) { if (read(pipefd[0], p, 1) > 0) { if (*p == 0x08 || *p == 0x7f) { - if (p > line ) + if (p > line) { write(STDOUT_FILENO, "\b \b", 3); p--; @@ -607,11 +608,11 @@ void tty_output_mode_set(output_mode_t mode) switch (mode) { case OUTPUT_MODE_NORMAL: - print = print_normal; + printchar = print_normal; break; case OUTPUT_MODE_HEX: - print = print_hex; + printchar = print_hex; break; case OUTPUT_MODE_END: @@ -1191,7 +1192,7 @@ void stdout_configure(void) } /* At start use normal print function */ - print = print_normal; + printchar = print_normal; /* Make sure we restore old stdout settings on exit */ atexit(&stdout_restore); @@ -2156,7 +2157,11 @@ void forward_to_tty(int fd, char output_char) else { /* Send output to tty device */ - optional_local_echo(output_char); + if (option.input_mode != INPUT_MODE_LINE) + { + optional_local_echo(output_char); + } + if ((output_char == 0) && (option.map_o_nulbrk)) { status = tcsendbreak(fd, 0); @@ -2165,6 +2170,7 @@ void forward_to_tty(int fd, char output_char) { status = tty_write(fd, &output_char, 1); } + if (status < 0) { tio_warning_printf("Could not write to tty device"); @@ -2195,19 +2201,32 @@ void forward_to_tty(int fd, char output_char) } } +static void clear_line() +{ + print("\r\033[K"); +} + +static void print_line(const char *string, int cursor_pos) +{ + clear_line(); + print("%s", string); + print("\r"); // Move the cursor back to the beginning + for (int i = 0; i < cursor_pos; ++i) + { + print("\x1b[C"); // Move the cursor right + } +} + int tty_connect(void) { fd_set rdfs; /* Read file descriptor set */ int maxfd; /* Maximum file descriptor used */ char input_char, output_char; char input_buffer[BUFSIZ] = {}; - char line_buffer[BUFSIZ] = {}; static bool first = true; int status; bool do_timestamp = false; char* now = NULL; - unsigned int line_index = 0; - static char previous_char[2] = {}; struct timeval tval_before = {}, tval_now, tval_result; /* Open tty device */ @@ -2351,6 +2370,20 @@ int tty_connect(void) exit(status); } + // Initialize readline like history + char *history[MAX_HISTORY]; + int history_count = 0; + int history_index = 0; + + for (int i = 0; i < MAX_HISTORY; ++i) + { + history[i] = NULL; + } + + int line_length = 0; + int cursor_pos = 0; + int escape = 0; + /* Input loop */ while (true) { @@ -2505,8 +2538,8 @@ int tty_connect(void) /* Map input character */ if ((input_char == '\n') && (option.map_i_nl_crnl) && (!option.map_o_msblsb)) { - print('\r'); - print('\n'); + printchar('\r'); + printchar('\n'); if (option.timestamp) { do_timestamp = true; @@ -2514,13 +2547,13 @@ int tty_connect(void) } else if ((input_char == '\f') && (option.map_i_ff_escc) && (!option.map_o_msblsb)) { - print('\e'); - print('c'); + printchar('\e'); + printchar('c'); } else { /* Print received tty character to stdout */ - print(input_char); + printchar(input_char); } /* Write to log */ @@ -2593,58 +2626,156 @@ int tty_connect(void) case INPUT_MODE_LINE: switch (input_char) { - case '\b': - case 127: // Backspace - if (line_index) - { - printf("\b \b"); // Destructive backspace - line_index--; - } - forward = false; - break; - case '\r': // Carriage return + if (line_length > 0) + { + // Save to history + if (history_count < MAX_HISTORY) + { + history[history_count] = strndup(line, line_length); + history_count++; + } + else + { + free(history[0]); + memmove(&history[0], &history[1], (MAX_HISTORY - 1) * sizeof(char*)); + history[MAX_HISTORY - 1] = strndup(line, line_length); + } + } + + line[line_length] = '\0'; if (option.local_echo == false) { - // Delete line - int i = line_index; - while (i--) - { - printf("\b \b"); // Destructive backspace - } + clear_line(); } else { - // Preserve line, go to next line - putchar('\r'); - putchar('\n'); + print("\r\n"); } - // Write buffered line to tty device - tty_write(device_fd, line_buffer, line_index); + // Write line to tty device + tty_write(device_fd, line, line_length); tty_sync(device_fd); - line_index = 0; + + line_length = 0; + cursor_pos = 0; + history_index = history_count; + escape = 0; + break; + + case 127: // Backspace + if (cursor_pos > 0) + { + memmove(&line[cursor_pos - 1], &line[cursor_pos], line_length - cursor_pos); + line_length--; + cursor_pos--; + line[line_length] = '\0'; + print_line(line, cursor_pos); + } + forward = false; + escape = 0; + break; + + case 27: // Escape + escape = 1; + forward = false; + break; + + case '[': + if (escape == 1) + { + escape = 2; + } + else + { + escape = 0; + } + break; + + case 'A': + if (escape == 2) + { + // Up arrow + if (history_index > 0) + { + history_index--; + strncpy(line, history[history_index], MAX_LINE_LEN-1); + line_length = strlen(line); + cursor_pos = line_length; + print_line(line, cursor_pos); + } + } + forward = false; + escape = 0; + break; + + case 'B': + if (escape == 2) + { + // Down arrow + if (history_index < history_count - 1) + { + history_index++; + strncpy(line, history[history_index], MAX_LINE_LEN-1); + line_length = strlen(line); + cursor_pos = line_length; + print_line(line, cursor_pos); + } + else if (history_index == history_count - 1) + { + history_index++; + line_length = 0; + cursor_pos = 0; + line[line_length] = '\0'; + print_line(line, cursor_pos); + } + } + forward = false; + escape = 0; + break; + + case 'C': + if (escape == 2) + { + // Right arrow + if (cursor_pos < line_length) + { + cursor_pos++; + print("\x1b[C"); + } + } + forward = false; + escape = 0; + break; + + case 'D': + if (escape == 2) + { + // Left arrow + if (cursor_pos > 0) + { + cursor_pos--; + print("\b"); + } + } + forward = false; + escape = 0; break; default: - if (line_index < BUFSIZ) + if (line_length < MAX_LINE_LEN - 1) { - putchar(input_char); - print_tainted_set(); - line_buffer[line_index++] = input_char; + memmove(&line[cursor_pos + 1], &line[cursor_pos], line_length - cursor_pos); + line[cursor_pos] = input_char; + line_length++; + cursor_pos++; + line[line_length] = '\0'; + print_line(line, cursor_pos); } - else - { - tio_error_print("Input exceeds maximum line length. Truncating."); - } - forward = false; + escape = 0; + break; } - // Save 2 latest stdin input characters - previous_char[1] = previous_char[0]; - previous_char[0] = input_char; - break; - default: break; } From be4fc0908f7983707ba6be77c673f9b0b83f4ee6 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 15 Jun 2024 14:59:31 +0200 Subject: [PATCH 037/146] Fix unbounded writes --- src/configfile.c | 3 +-- src/fs.c | 9 +-------- src/xymodem.c | 2 +- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/configfile.c b/src/configfile.c index 10717ac..460e476 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -408,13 +408,12 @@ static char *match_and_replace(const char *str, const char *pattern, char *devic assert(pattern != NULL); assert(device != NULL); - char *string = malloc(strlen(device) + PATH_MAX); + char *string = strndup(device, PATH_MAX); if (string == NULL) { tio_debug_printf("Failure allocating string memory\n"); return NULL; } - strcpy(string, device); /* Find matches of pattern in str. For each match, replace any '%mN' in the * copy of the device string with the corresponding match subexpression and diff --git a/src/fs.c b/src/fs.c index a0d9edd..6a2063c 100644 --- a/src/fs.c +++ b/src/fs.c @@ -150,14 +150,7 @@ char* fs_search_directory(const char *dir_path, const char *dirname) // If it's a directory, check if it's the one we're looking for if (strcmp(entry->d_name, dirname) == 0) { - char* result = malloc(strlen(path) + 1); - if (result == NULL) - { - // Error allocating memory - closedir(dir); - return NULL; - } - strcpy(result, path); + char *result = strndup(path, PATH_MAX); closedir(dir); return result; } diff --git a/src/xymodem.c b/src/xymodem.c index 606093a..6bc3da3 100644 --- a/src/xymodem.c +++ b/src/xymodem.c @@ -688,7 +688,7 @@ int xymodem_send(int sio, const char *filename, modem_mode_t mode) rc = -1; if (strlen(filename) > 977) break; /* hdr block overrun */ - p = stpcpy(hdr, filename) + 1; + p = stpncpy(hdr, filename, 1024) + 1; p += sprintf(p, "%ld %lo %o", len, stat.st_mtime, stat.st_mode); if (xmodem_1k(sio, hdr, p - hdr, 0) < 0) break; /* hdr with metadata */ From 8f77ad5830ab6fa065a6434e63458efe3d4fdf18 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 15 Jun 2024 15:58:08 +0200 Subject: [PATCH 038/146] Clean up script run interaction text --- src/misc.c | 5 +++++ src/misc.h | 1 + src/script.c | 2 +- src/tty.c | 11 ++++++----- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/misc.c b/src/misc.c index 6471e99..50fb339 100644 --- a/src/misc.c +++ b/src/misc.c @@ -247,3 +247,8 @@ int execute_shell_command(int fd, const char *command) return 0; } + +void clear_line() +{ + print("\r\033[K"); +} diff --git a/src/misc.h b/src/misc.h index 9f8a1d4..8ba8285 100644 --- a/src/misc.h +++ b/src/misc.h @@ -35,3 +35,4 @@ int read_poll(int fd, void *data, size_t len, int timeout); double get_current_time(void); bool match_patterns(const char *string, const char *patterns); int execute_shell_command(int fd, const char *command); +void clear_line(); diff --git a/src/script.c b/src/script.c index 8d68063..a92a4b6 100644 --- a/src/script.c +++ b/src/script.c @@ -513,7 +513,7 @@ void script_file_run(lua_State *L, const char *filename) if (luaL_dofile(L, filename)) { - tio_warning_printf("lua: %s\n", lua_tostring(L, -1)); + tio_warning_printf("lua: %s", lua_tostring(L, -1)); lua_pop(L, 1); /* pop error message from the stack */ return; } diff --git a/src/tty.c b/src/tty.c index 6805fae..8dc4ee6 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1020,9 +1020,15 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf("Run Lua script") tio_printf_raw("Enter file name: "); if (tio_readln()) + { + clear_line(); script_run(device_fd, line); + } else + { + clear_line(); script_run(device_fd, NULL); + } break; case KEY_SHIFT_R: @@ -2201,11 +2207,6 @@ void forward_to_tty(int fd, char output_char) } } -static void clear_line() -{ - print("\r\033[K"); -} - static void print_line(const char *string, int cursor_pos) { clear_line(); From 053ae53f19f9ae558dcd0c6b2daaf9a7100451b2 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 15 Jun 2024 16:00:58 +0200 Subject: [PATCH 039/146] Update configuration output --- src/tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tty.c b/src/tty.c index 8dc4ee6..2e2eb84 100644 --- a/src/tty.c +++ b/src/tty.c @@ -918,8 +918,8 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) case KEY_C: tio_printf("Configuration:"); - options_print(); config_file_print(); + options_print(); if (option.rs485) { rs485_print_config(); From 7e9574f98d737b8a0b9d230790b2ee9727721457 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 15 Jun 2024 16:20:44 +0200 Subject: [PATCH 040/146] Update NEWS --- NEWS | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 30fae48..4e140ef 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,52 @@ -=== tio v3.3 === +=== tio v3.4 === + + + +Changes since tio v3.3: + + * Update configuration output + + * Clean up script run interaction text + + * Fix unbounded writes + + * Add history and editing feature to line input mode + + Use up and down arrow keys to navigate history. + + Use left and right arrow keys to move cursor back and forth. + + We try mimic the behaviour of GNU readline which we can not use because + we also need to react to key commands. + + * Reuse socket address + + To avoid having to wait for socket timeout when restarting server. + + * Fix line input mode + + Fix so that ABCD are no longer ignored. + + * Make sure ICRNL, IGNCR, INLCR take effect + + * Include correct header for poll() + + * Add group write permission to xymodem received file + + * Fix missing open() flags in xymodem_receive() + +Vyacheslav Patkov: + + * Show current mappings in the configuration printout + + * Use "ctrl-t m" to change mappings interactively + + * Prompt for Lua script or shell command in interactive session + +Eliot Alan Foss: + + * Added support to receive XMODEM-CRC files from the connected serial port. From 5c7d81b900a4f26086a5a3bbf435b24d61d78921 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 15 Jun 2024 16:21:16 +0200 Subject: [PATCH 041/146] Update version date --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 073c4ff..5547c2e 100644 --- a/meson.build +++ b/meson.build @@ -6,7 +6,7 @@ project('tio', 'c', ) # The tag date of the project_version(), update when the version bumps. -version_date = '2024-05-15' +version_date = '2024-06-15' # Test for dynamic baudrate configuration interface compiler = meson.get_compiler('c') From b756d2e1f1a5e1740c3b5dd58d0239de0ce2b2ab Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 15 Jun 2024 16:46:20 +0200 Subject: [PATCH 042/146] Bump version --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 5547c2e..69ecb2a 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('tio', 'c', - version : '3.4', + version : '3.5', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] From c5afd86b9ae9757ebc465d6066e348bb7164f3c2 Mon Sep 17 00:00:00 2001 From: Jakob Haufe Date: Sat, 15 Jun 2024 17:03:21 +0200 Subject: [PATCH 043/146] Fix typos --- man/tio.1.in | 4 ++-- man/tio.1.txt | 4 ++-- src/tty.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index a378095..c66dae2 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -429,7 +429,7 @@ In addition to the Lua API tio makes the following functions available: .TP 6n .IP "\fBexpect(string, timeout)" -Expect string - waits for string to match or timeout before continueing. +Expect string - waits for string to match or timeout before continuing. Supports regular expressions. Special characters must be escaped with '\\\\'. Timeout is in milliseconds, defaults to 0 meaning it will wait forever. @@ -475,7 +475,7 @@ State is high, low, or toggle. .IP "\fBsleep(seconds)" Sleep for seconds. .IP "\fBmsleep(ms)" -Sleep for miliseconds. +Sleep for milliseconds. .IP "\fBexit(code)" Exit with exit code. diff --git a/man/tio.1.txt b/man/tio.1.txt index 4d5c038..2ec2e56 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -341,7 +341,7 @@ SCRIPT API In addition to the Lua API tio makes the following functions available: expect(string, timeout) - Expect string - waits for string to match or timeout before continueing. Supports regular expressions. Special characters must be escaped with '\\'. Timeout is in milliseconds, defaults to 0 meaning it will wait forever. + Expect string - waits for string to match or timeout before continuing. Supports regular expressions. Special characters must be escaped with '\\'. Timeout is in milliseconds, defaults to 0 meaning it will wait forever. Returns 1 on successful match, 0 on timeout, or -1 on error. @@ -382,7 +382,7 @@ SCRIPT API Sleep for seconds. msleep(ms) - Sleep for miliseconds. + Sleep for milliseconds. exit(code) Exit with exit code. diff --git a/src/tty.c b/src/tty.c index 2e2eb84..5064015 100644 --- a/src/tty.c +++ b/src/tty.c @@ -2520,7 +2520,7 @@ int tty_connect(void) break; default: - tio_error_printf("Unknown outut mode"); + tio_error_printf("Unknown output mode"); exit(EXIT_FAILURE); break; } From b9902bbd7822679e5e0110ecbcdd65e92bfa471d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 27 Jun 2024 18:43:16 +0200 Subject: [PATCH 044/146] Fix tainted print --- src/print.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/print.c b/src/print.c index efc1519..ab88ef9 100644 --- a/src/print.c +++ b/src/print.c @@ -85,5 +85,5 @@ void print(const char *format, ...) fflush(stdout); va_end(args); - print_tainted = false; + print_tainted = true; } From 8c471105fe610caaf7ffbdfc5024e9a24853bef9 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 27 Jun 2024 18:54:13 +0200 Subject: [PATCH 045/146] Fix line input mode ignoring characters ABCD --- src/meson.build | 3 +- src/readline.c | 277 ++++++++++++++++++++++++++++++++++++++++++++++++ src/readline.h | 26 +++++ src/tty.c | 179 ++----------------------------- 4 files changed, 315 insertions(+), 170 deletions(-) create mode 100644 src/readline.c create mode 100644 src/readline.h diff --git a/src/meson.build b/src/meson.build index 3b3790b..2d506de 100644 --- a/src/meson.build +++ b/src/meson.build @@ -20,7 +20,8 @@ tio_sources = [ 'alert.c', 'xymodem.c', 'script.c', - 'fs.c' + 'fs.c', + 'readline.c' ] diff --git a/src/readline.c b/src/readline.c new file mode 100644 index 0000000..d50e63f --- /dev/null +++ b/src/readline.c @@ -0,0 +1,277 @@ +/* + * tio - a serial device I/O tool + * + * Copyright (c) 2014-2024 Martin Lund + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "print.h" +#include "misc.h" + +#define MAX_LINE_LEN PATH_MAX +#define MAX_HISTORY 1000 + +static char line[MAX_LINE_LEN] = {}; +static int history_count = 0; +static int history_index = 0; +static int line_length = 0; +static int cursor_pos = 0; +static int escape = 0; + +static char *history[MAX_HISTORY]; + +static void print_line(const char *string, int cursor_pos) +{ + clear_line(); + print("%s", string); + print("\r"); // Move the cursor back to the beginning + for (int i = 0; i < cursor_pos; ++i) + { + print("\x1b[C"); // Move the cursor right + } +} + +void readline_init(void) +{ + history_count = 0; + history_index = 0; + + for (int i = 0; i < MAX_HISTORY; ++i) + { + history[i] = NULL; + } + + line[0] = 0; + line_length = 0; + cursor_pos = 0; + escape = 0; +} + +char * readline_get(void) +{ + return line; +} + +static void readline_input_char(char input_char) +{ + if (line_length < MAX_LINE_LEN - 1) + { + memmove(&line[cursor_pos + 1], &line[cursor_pos], line_length - cursor_pos); + line[cursor_pos] = input_char; + line_length++; + cursor_pos++; + line[line_length] = '\0'; + print_line(line, cursor_pos); + } + escape = 0; +} + +static void readline_input_cr(void) +{ + if (line_length > 0) + { + // Save to history + if (history_count < MAX_HISTORY) + { + history[history_count] = strndup(line, line_length); + history_count++; + } + else + { + free(history[0]); + memmove(&history[0], &history[1], (MAX_HISTORY - 1) * sizeof(char*)); + history[MAX_HISTORY - 1] = strndup(line, line_length); + } + } + + line[line_length] = '\0'; + if (option.local_echo == false) + { + clear_line(); + } + else + { + print("\r\n"); + } + + line_length = 0; + cursor_pos = 0; + history_index = history_count; + escape = 0; +} + +static void readline_input_bs(void) +{ + if (cursor_pos > 0) + { + memmove(&line[cursor_pos - 1], &line[cursor_pos], line_length - cursor_pos); + line_length--; + cursor_pos--; + line[line_length] = '\0'; + print_line(line, cursor_pos); + } + escape = 0; +} + +static void readline_input_escape(void) +{ + escape = 1; +} + +static void readline_input_left_bracket(void) +{ + if (escape == 1) + { + escape = 2; + } + else + { + escape = 0; + } +} + +static void readline_input_A(void) +{ + if (escape == 2) + { + // Up arrow + if (history_index > 0) + { + history_index--; + strncpy(line, history[history_index], MAX_LINE_LEN-1); + line_length = strlen(line); + cursor_pos = line_length; + print_line(line, cursor_pos); + } + } + else + { + readline_input_char('A'); + } + + escape = 0; +} + +static void readline_input_B(void) +{ + if (escape == 2) + { + // Down arrow + if (history_index < history_count - 1) + { + history_index++; + strncpy(line, history[history_index], MAX_LINE_LEN-1); + line_length = strlen(line); + cursor_pos = line_length; + print_line(line, cursor_pos); + } + else if (history_index == history_count - 1) + { + history_index++; + line_length = 0; + cursor_pos = 0; + line[line_length] = '\0'; + print_line(line, cursor_pos); + } + } + else + { + readline_input_char('B'); + } + + escape = 0; +} + +static void readline_input_C(void) +{ + if (escape == 2) + { + // Right arrow + if (cursor_pos < line_length) + { + cursor_pos++; + print("\x1b[C"); + } + } + else + { + readline_input_char('C'); + } + + escape = 0; +} + +static void readline_input_D(void) +{ + if (escape == 2) + { + // Left arrow + if (cursor_pos > 0) + { + cursor_pos--; + print("\b"); + } + } + else + { + readline_input_char('D'); + } + + escape = 0; +} + +void readline_input(char input_char) +{ + switch (input_char) + { + case '\r': // Carriage return + readline_input_cr(); + break; + + case 127: // Backspace + readline_input_bs(); + break; + + case 27: // Escape + readline_input_escape(); + break; + + case '[': + readline_input_left_bracket(); + break; + + case 'A': + readline_input_A(); + break; + + case 'B': + readline_input_B(); + break; + + case 'C': + readline_input_C(); + break; + + case 'D': + readline_input_D(); + break; + + default: + readline_input_char(input_char); + break; + } +} diff --git a/src/readline.h b/src/readline.h new file mode 100644 index 0000000..46c3f10 --- /dev/null +++ b/src/readline.h @@ -0,0 +1,26 @@ +/* + * tio - a serial device I/O tool + * + * Copyright (c) 2014-2024 Martin Lund + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#pragma once + +void readline_init(void); +void readline_input(char input_char); +char * readline_get(void); diff --git a/src/tty.c b/src/tty.c index 5064015..262ad1d 100644 --- a/src/tty.c +++ b/src/tty.c @@ -64,6 +64,7 @@ #include "script.h" #include "xymodem.h" #include "fs.h" +#include "readline.h" /* tty device listing configuration */ @@ -87,9 +88,6 @@ #define CMSPAR 010000000000 #endif -#define MAX_LINE_LEN PATH_MAX -#define MAX_HISTORY 1000 - #define KEY_0 0x30 #define KEY_1 0x31 #define KEY_2 0x32 @@ -173,7 +171,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[MAX_LINE_LEN]; +static char line[PATH_MAX]; static void optional_local_echo(char c) { @@ -581,7 +579,7 @@ static int tio_readln(void) char *p = line; /* Read line, accept BS and DEL as rubout characters */ - for (p = line ; p < &line[MAX_LINE_LEN-1]; ) + for (p = line ; p < &line[PATH_MAX-1]; ) { if (read(pipefd[0], p, 1) > 0) { @@ -2207,17 +2205,6 @@ void forward_to_tty(int fd, char output_char) } } -static void print_line(const char *string, int cursor_pos) -{ - clear_line(); - print("%s", string); - print("\r"); // Move the cursor back to the beginning - for (int i = 0; i < cursor_pos; ++i) - { - print("\x1b[C"); // Move the cursor right - } -} - int tty_connect(void) { fd_set rdfs; /* Read file descriptor set */ @@ -2372,18 +2359,7 @@ int tty_connect(void) } // Initialize readline like history - char *history[MAX_HISTORY]; - int history_count = 0; - int history_index = 0; - - for (int i = 0; i < MAX_HISTORY; ++i) - { - history[i] = NULL; - } - - int line_length = 0; - int cursor_pos = 0; - int escape = 0; + readline_init(); /* Input loop */ while (true) @@ -2628,152 +2604,17 @@ int tty_connect(void) switch (input_char) { case '\r': // Carriage return - if (line_length > 0) - { - // Save to history - if (history_count < MAX_HISTORY) - { - history[history_count] = strndup(line, line_length); - history_count++; - } - else - { - free(history[0]); - memmove(&history[0], &history[1], (MAX_HISTORY - 1) * sizeof(char*)); - history[MAX_HISTORY - 1] = strndup(line, line_length); - } - } + readline_input(input_char); - line[line_length] = '\0'; - if (option.local_echo == false) - { - clear_line(); - } - else - { - print("\r\n"); - } - - // Write line to tty device - tty_write(device_fd, line, line_length); + // Write current line to tty device + char *line = readline_get(); + tty_write(device_fd, line, strlen(line)); tty_sync(device_fd); - - line_length = 0; - cursor_pos = 0; - history_index = history_count; - escape = 0; - break; - - case 127: // Backspace - if (cursor_pos > 0) - { - memmove(&line[cursor_pos - 1], &line[cursor_pos], line_length - cursor_pos); - line_length--; - cursor_pos--; - line[line_length] = '\0'; - print_line(line, cursor_pos); - } - forward = false; - escape = 0; - break; - - case 27: // Escape - escape = 1; - forward = false; - break; - - case '[': - if (escape == 1) - { - escape = 2; - } - else - { - escape = 0; - } - break; - - case 'A': - if (escape == 2) - { - // Up arrow - if (history_index > 0) - { - history_index--; - strncpy(line, history[history_index], MAX_LINE_LEN-1); - line_length = strlen(line); - cursor_pos = line_length; - print_line(line, cursor_pos); - } - } - forward = false; - escape = 0; - break; - - case 'B': - if (escape == 2) - { - // Down arrow - if (history_index < history_count - 1) - { - history_index++; - strncpy(line, history[history_index], MAX_LINE_LEN-1); - line_length = strlen(line); - cursor_pos = line_length; - print_line(line, cursor_pos); - } - else if (history_index == history_count - 1) - { - history_index++; - line_length = 0; - cursor_pos = 0; - line[line_length] = '\0'; - print_line(line, cursor_pos); - } - } - forward = false; - escape = 0; - break; - - case 'C': - if (escape == 2) - { - // Right arrow - if (cursor_pos < line_length) - { - cursor_pos++; - print("\x1b[C"); - } - } - forward = false; - escape = 0; - break; - - case 'D': - if (escape == 2) - { - // Left arrow - if (cursor_pos > 0) - { - cursor_pos--; - print("\b"); - } - } - forward = false; - escape = 0; break; default: - if (line_length < MAX_LINE_LEN - 1) - { - memmove(&line[cursor_pos + 1], &line[cursor_pos], line_length - cursor_pos); - line[cursor_pos] = input_char; - line_length++; - cursor_pos++; - line[line_length] = '\0'; - print_line(line, cursor_pos); - } - escape = 0; + readline_input(input_char); + forward = false; break; } From 4723cc3f4e3e8c3ca1ff1580cf8767a837afabbb Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 27 Jun 2024 20:09:18 +0200 Subject: [PATCH 046/146] Add OIGNCR mapping flag Ignores CR on output to serial device. --- man/tio.1.in | 6 ++++-- src/bash-completion/tio.in | 2 +- src/options.c | 5 +++++ src/options.h | 1 + src/tty.c | 17 ++++++++++++++--- 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index c66dae2..6f0af8c 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -201,8 +201,8 @@ Strip control characters and escape sequences from log. .TP .BR \-m ", " "\-\-map " \fI -Map (replace, translate) characters on input or output. The following mapping -flags are supported: +Map (replace, translate) characters on input/output to/from the serial device. +The following mapping flags are supported: .RS .TP 12n @@ -226,6 +226,8 @@ Map NL to CR-NL on output Map lowercase characters to uppercase on output .IP "\fBONULBRK" Map nul (zero) to send break signal on output +.IP "\fBOIGNCR" +Ignore CR on output .IP "\fBMSB2LSB" Map MSB bit order to LSB on output .P diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index 3a3e7f6..6607d1f 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -85,7 +85,7 @@ _tio() return 0 ;; -m | --map) - COMPREPLY=( $(compgen -W "ICRNL IGNCR INLCR IFFESCC INLCRNL OCRNL ODELBS ONLCRNL MSB2LSB" -- ${cur}) ) + COMPREPLY=( $(compgen -W "ICRNL IGNCR INLCR IFFESCC INLCRNL OCRNL ODELBS ONLCRNL OLTU ONULBRK OIGNCR MSB2LSB" -- ${cur}) ) return 0 ;; --timestamp-format) diff --git a/src/options.c b/src/options.c index 94a623f..94b940a 100644 --- a/src/options.c +++ b/src/options.c @@ -119,6 +119,7 @@ struct option_t option = .map_o_ltu = false, .map_o_nulbrk = false, .map_o_msblsb = false, + .map_o_ign_cr = false, }; void option_print_help(char *argv[]) @@ -768,6 +769,10 @@ void option_parse_mappings(const char *map) { option.map_o_nulbrk = true; } + else if (strcmp(token, "OIGNCR") == 0) + { + option.map_o_ign_cr = true; + } else if (strcmp(token, "MSB2LSB") == 0) { option.map_o_msblsb = true; diff --git a/src/options.h b/src/options.h index 80e0f3a..20a840b 100644 --- a/src/options.h +++ b/src/options.h @@ -104,6 +104,7 @@ struct option_t bool map_o_ltu; bool map_o_nulbrk; bool map_o_msblsb; + bool map_o_ign_cr; }; extern struct option_t option; diff --git a/src/tty.c b/src/tty.c index 262ad1d..83d6278 100644 --- a/src/tty.c +++ b/src/tty.c @@ -623,9 +623,9 @@ static void mappings_print(void) if (option.map_i_cr_nl || option.map_ign_cr || option.map_i_ff_escc || option.map_i_nl_cr || option.map_i_nl_crnl || option.map_o_cr_nl || option.map_o_del_bs || option.map_o_nl_crnl || option.map_o_ltu || - option.map_o_nulbrk || option.map_o_msblsb) + option.map_o_nulbrk || option.map_o_msblsb || option.map_o_ign_cr) { - tio_printf(" Mappings:%s%s%s%s%s%s%s%s%s%s%s", + tio_printf(" Mappings:%s%s%s%s%s%s%s%s%s%s%s%s", option.map_i_cr_nl ? " ICRNL" : "", option.map_ign_cr ? " IGNCR" : "", option.map_i_ff_escc ? " IFFESCC" : "", @@ -636,6 +636,7 @@ static void mappings_print(void) option.map_o_nl_crnl ? " ONLCRNL" : "", option.map_o_ltu ? " OLTU" : "", option.map_o_nulbrk ? " ONULBRK" : "", + option.map_o_ign_cr ? " OIGNCR" : "", option.map_o_msblsb ? " MSB2LSB" : ""); } else @@ -791,6 +792,10 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf("ONULBRK is %s", option.map_o_nulbrk ? "set" : "unset"); break; case KEY_A: + option.map_o_ign_cr = !option.map_o_ign_cr; + tio_printf("OIGNCR is %s", option.map_o_ign_cr ? "set" : "unset"); + break; + case KEY_B: option.map_o_msblsb = !option.map_o_msblsb; tio_printf("MSB2LSB is %s", option.map_o_msblsb ? "set" : "unset"); break; @@ -1002,7 +1007,9 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) option.map_o_ltu ? "Unset" : "Set"); tio_printf(" (9) ONULBRK: %s mapping NUL to send break signal on output", option.map_o_nulbrk ? "Unset" : "Set"); - tio_printf(" (a) MSB2LSB: %s mapping MSB bit order to LSB on output", + tio_printf(" (a) OIGNCR: %s ignoring CR on output", + option.map_o_ign_cr ? "Unset" : "Set"); + tio_printf(" (b) MSB2LSB: %s mapping MSB bit order to LSB on output", option.map_o_msblsb ? "Unset" : "Set"); // Process next input character as sub command @@ -2133,6 +2140,10 @@ void forward_to_tty(int fd, char output_char) { output_char = '\n'; } + if ((output_char == '\r') && (option.map_o_ign_cr)) + { + return; + } /* Map newline character */ if ((output_char == '\n' || output_char == '\r') && (option.map_o_nl_crnl)) From 9022f51ea5d0ab12319d04ce33917d1039bbc8f0 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 28 Jun 2024 11:51:40 +0200 Subject: [PATCH 047/146] Update NEWS --- NEWS | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 4e140ef..770bef0 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,21 @@ -=== tio v3.4 === +=== tio v3.5 === + + + +Changes since tio v3.4: + + * Add OIGNCR mapping flag + + Ignores CR on output to serial device. + + * Fix line input mode ignoring characters ABCD + + * Fix tainted print + +Jakob Haufe: + + * Fix typos From d34fa1c1ad5c226d2eb082333bad400e86fcc87a Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 29 Jun 2024 12:14:29 +0200 Subject: [PATCH 048/146] Rename mapping flag MSB2LSB to IMSB2LSB This is the correct naming since we are changing the input bit order on input from the serial device. --- man/tio.1.in | 4 ++-- src/bash-completion/tio.in | 2 +- src/options.c | 6 ++--- src/options.h | 2 +- src/tty.c | 46 +++++++++++++++++++------------------- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 6f0af8c..850da9c 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -216,6 +216,8 @@ Map FF to ESC-c on input Map NL to CR on input .IP "\fBINLCRNL" Map NL to CR-NL on input +.IP "\fBIMSB2LSB" +Map MSB bit order to LSB on input .IP "\fBOCRNL" Map CR to NL on output .IP "\fBODELBS" @@ -228,8 +230,6 @@ Map lowercase characters to uppercase on output Map nul (zero) to send break signal on output .IP "\fBOIGNCR" Ignore CR on output -.IP "\fBMSB2LSB" -Map MSB bit order to LSB on output .P If defining more than one flag, the flags must be comma separated. .RE diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index 6607d1f..d71aceb 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -85,7 +85,7 @@ _tio() return 0 ;; -m | --map) - COMPREPLY=( $(compgen -W "ICRNL IGNCR INLCR IFFESCC INLCRNL OCRNL ODELBS ONLCRNL OLTU ONULBRK OIGNCR MSB2LSB" -- ${cur}) ) + COMPREPLY=( $(compgen -W "ICRNL IGNCR INLCR IFFESCC INLCRNL IMSB2LSB OCRNL ODELBS ONLCRNL OLTU ONULBRK OIGNCR" -- ${cur}) ) return 0 ;; --timestamp-format) diff --git a/src/options.c b/src/options.c index 94b940a..6fd2b10 100644 --- a/src/options.c +++ b/src/options.c @@ -118,7 +118,7 @@ struct option_t option = .map_o_del_bs = false, .map_o_ltu = false, .map_o_nulbrk = false, - .map_o_msblsb = false, + .map_i_msb2lsb = false, .map_o_ign_cr = false, }; @@ -773,9 +773,9 @@ void option_parse_mappings(const char *map) { option.map_o_ign_cr = true; } - else if (strcmp(token, "MSB2LSB") == 0) + else if (strcmp(token, "IMSB2LSB") == 0) { - option.map_o_msblsb = true; + option.map_i_msb2lsb = true; } else { diff --git a/src/options.h b/src/options.h index 20a840b..dadf133 100644 --- a/src/options.h +++ b/src/options.h @@ -103,7 +103,7 @@ struct option_t bool map_o_del_bs; bool map_o_ltu; bool map_o_nulbrk; - bool map_o_msblsb; + bool map_i_msb2lsb; bool map_o_ign_cr; }; diff --git a/src/tty.c b/src/tty.c index 83d6278..fdfd0f9 100644 --- a/src/tty.c +++ b/src/tty.c @@ -623,7 +623,7 @@ static void mappings_print(void) if (option.map_i_cr_nl || option.map_ign_cr || option.map_i_ff_escc || option.map_i_nl_cr || option.map_i_nl_crnl || option.map_o_cr_nl || option.map_o_del_bs || option.map_o_nl_crnl || option.map_o_ltu || - option.map_o_nulbrk || option.map_o_msblsb || option.map_o_ign_cr) + option.map_o_nulbrk || option.map_i_msb2lsb || option.map_o_ign_cr) { tio_printf(" Mappings:%s%s%s%s%s%s%s%s%s%s%s%s", option.map_i_cr_nl ? " ICRNL" : "", @@ -631,13 +631,13 @@ static void mappings_print(void) option.map_i_ff_escc ? " IFFESCC" : "", option.map_i_nl_cr ? " INLCR" : "", option.map_i_nl_crnl ? " INLCRNL" : "", + option.map_i_msb2lsb ? " IMSB2LSB" : "", option.map_o_cr_nl ? " OCRNL" : "", option.map_o_del_bs ? " ODELBS" : "", option.map_o_nl_crnl ? " ONLCRNL" : "", option.map_o_ltu ? " OLTU" : "", option.map_o_nulbrk ? " ONULBRK" : "", - option.map_o_ign_cr ? " OIGNCR" : "", - option.map_o_msblsb ? " MSB2LSB" : ""); + option.map_o_ign_cr ? " OIGNCR" : ""); } else { @@ -772,33 +772,33 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf("INLCRNL is %s", option.map_i_nl_crnl ? "set" : "unset"); break; case KEY_5: + option.map_i_msb2lsb = !option.map_i_msb2lsb; + tio_printf("IMSB2LSB is %s", option.map_i_msb2lsb ? "set" : "unset"); + break; + case KEY_6: option.map_o_cr_nl = !option.map_o_cr_nl; tio_printf("OCRNL is %s", option.map_o_cr_nl ? "set" : "unset"); break; - case KEY_6: + case KEY_7: option.map_o_del_bs = !option.map_o_del_bs; tio_printf("ODELBS is %s", option.map_o_del_bs ? "set" : "unset"); break; - case KEY_7: + case KEY_8: option.map_o_nl_crnl = !option.map_o_nl_crnl; tio_printf("ONLCRNL is %s", option.map_o_nl_crnl ? "set" : "unset"); break; - case KEY_8: + case KEY_9: option.map_o_ltu = !option.map_o_ltu; tio_printf("OLTU is %s", option.map_o_ltu ? "set" : "unset"); break; - case KEY_9: + case KEY_A: option.map_o_nulbrk = !option.map_o_nulbrk; tio_printf("ONULBRK is %s", option.map_o_nulbrk ? "set" : "unset"); break; - case KEY_A: + case KEY_B: option.map_o_ign_cr = !option.map_o_ign_cr; tio_printf("OIGNCR is %s", option.map_o_ign_cr ? "set" : "unset"); break; - case KEY_B: - option.map_o_msblsb = !option.map_o_msblsb; - tio_printf("MSB2LSB is %s", option.map_o_msblsb ? "set" : "unset"); - break; default: tio_error_print("Invalid input"); break; @@ -997,20 +997,20 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) option.map_i_nl_cr ? "Unset" : "Set"); tio_printf(" (4) INLCRNL: %s mapping NL to CR-NL on input", option.map_i_nl_cr ? "Unset" : "Set"); - tio_printf(" (5) OCRNL: %s mapping CR to NL on output", + tio_printf(" (5) IMSB2LSB: %s mapping MSB bit order to LSB on input", + option.map_i_msb2lsb ? "Unset" : "Set"); + tio_printf(" (6) OCRNL: %s mapping CR to NL on output", option.map_o_cr_nl ? "Unset" : "Set"); - tio_printf(" (6) ODELBS: %s mapping DEL to BS on output", + tio_printf(" (7) ODELBS: %s mapping DEL to BS on output", option.map_o_del_bs ? "Unset" : "Set"); - tio_printf(" (7) ONLCRNL: %s mapping NL to CR-NL on output", + tio_printf(" (8) ONLCRNL: %s mapping NL to CR-NL on output", option.map_o_nl_crnl ? "Unset" : "Set"); - tio_printf(" (8) OLTU: %s mapping lowercase to uppercase on output", + tio_printf(" (9) OLTU: %s mapping lowercase to uppercase on output", option.map_o_ltu ? "Unset" : "Set"); - tio_printf(" (9) ONULBRK: %s mapping NUL to send break signal on output", + tio_printf(" (a) ONULBRK: %s mapping NUL to send break signal on output", option.map_o_nulbrk ? "Unset" : "Set"); - tio_printf(" (a) OIGNCR: %s ignoring CR on output", + tio_printf(" (b) OIGNCR: %s ignoring CR on output", option.map_o_ign_cr ? "Unset" : "Set"); - tio_printf(" (b) MSB2LSB: %s mapping MSB bit order to LSB on output", - option.map_o_msblsb ? "Unset" : "Set"); // Process next input character as sub command sub_command = SUBCOMMAND_MAP; @@ -2513,7 +2513,7 @@ int tty_connect(void) } /* Convert MSB to LSB bit order */ - if (option.map_o_msblsb) + if (option.map_i_msb2lsb) { char ch = input_char; input_char = 0; @@ -2524,7 +2524,7 @@ int tty_connect(void) } /* Map input character */ - if ((input_char == '\n') && (option.map_i_nl_crnl) && (!option.map_o_msblsb)) + if ((input_char == '\n') && (option.map_i_nl_crnl) && (!option.map_i_msb2lsb)) { printchar('\r'); printchar('\n'); @@ -2533,7 +2533,7 @@ int tty_connect(void) do_timestamp = true; } } - else if ((input_char == '\f') && (option.map_i_ff_escc) && (!option.map_o_msblsb)) + else if ((input_char == '\f') && (option.map_i_ff_escc) && (!option.map_i_msb2lsb)) { printchar('\e'); printchar('c'); From 561376696b8e74c0176071e8c369089f0fc83b57 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 29 Jun 2024 12:17:57 +0200 Subject: [PATCH 049/146] Clarify input and output direction of map flags --- man/tio.1.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 850da9c..371870f 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -201,8 +201,8 @@ Strip control characters and escape sequences from log. .TP .BR \-m ", " "\-\-map " \fI -Map (replace, translate) characters on input/output to/from the serial device. -The following mapping flags are supported: +Map (replace, translate) characters on input to the serial device or output +from the serial device. The following mapping flags are supported: .RS .TP 12n From ef6fa8030e817e9986d5f7c8154f805fb864e30f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 29 Jun 2024 12:19:54 +0200 Subject: [PATCH 050/146] Update version date --- man/tio.1.txt | 14 ++++++++------ meson.build | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/man/tio.1.txt b/man/tio.1.txt index 2ec2e56..e4e7725 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -154,7 +154,7 @@ OPTIONS -m, --map - Map (replace, translate) characters on input or output. The following mapping flags are supported: + Map (replace, translate) characters on input to the serial device or output from the serial device. The following mapping flags are supported: ICRNL Map CR to NL on input (unless IGNCR is set) @@ -166,6 +166,8 @@ OPTIONS INLCRNL Map NL to CR-NL on input + IMSB2LSB Map MSB bit order to LSB on input + OCRNL Map CR to NL on output ODELBS Map DEL to BS on output @@ -176,7 +178,7 @@ OPTIONS ONULBRK Map nul (zero) to send break signal on output - MSB2LSB Map MSB bit order to LSB on output + OIGNCR Ignore CR on output If defining more than one flag, the flags must be comma separated. @@ -311,7 +313,7 @@ KEYS ctrl-t L Show line states (DTR, RTS, CTS, DSR, DCD, RI) - ctrl-t m Toggle MSB to LSB bit order + ctrl-t m Change mapping of characters on input or output ctrl-t o Toggle output mode @@ -321,12 +323,12 @@ KEYS ctrl-t r Run script + ctrl-t R Execute shell command with I/O redirected to device + ctrl-t s Show TX/RX statistics ctrl-t t Toggle line timestamp mode - ctrl-t U Toggle conversion to uppercase on output - ctrl-t v Show version ctrl-t x Send file using the XMODEM-1K or XMODEM-CRC protocol (prompts for file name and protocol) @@ -595,4 +597,4 @@ WEBSITE AUTHOR Maintained by Martin Lund . -tio 3.3 2024-05-15 tio(1) +tio 3.5 2024-06-15 tio(1) diff --git a/meson.build b/meson.build index 69ecb2a..b6c42b3 100644 --- a/meson.build +++ b/meson.build @@ -6,7 +6,7 @@ project('tio', 'c', ) # The tag date of the project_version(), update when the version bumps. -version_date = '2024-06-15' +version_date = '2024-06-29' # Test for dynamic baudrate configuration interface compiler = meson.get_compiler('c') From a3a6b5127f2cdf9d01ed9626febafd9d8644ae5e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 29 Jun 2024 12:22:35 +0200 Subject: [PATCH 051/146] Update NEWS --- NEWS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NEWS b/NEWS index 770bef0..4ae4b9d 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,13 @@ Changes since tio v3.4: + * Clarify input and output direction of map flags + + * Rename mapping flag MSB2LSB to IMSB2LSB + + This is the correct naming since we are changing the input bit order on + input from the serial device. + * Add OIGNCR mapping flag Ignores CR on output to serial device. From 02cac07a77fba9445ca4edba80d9562a2d075a4c Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 29 Jun 2024 12:33:55 +0200 Subject: [PATCH 052/146] Bump version --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index b6c42b3..138c734 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('tio', 'c', - version : '3.5', + version : '3.6', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] From 5f70b75e9053c2651525f0b8dd43c6bbc84ff1a3 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 1 Jul 2024 23:11:58 +0200 Subject: [PATCH 053/146] Fix listing of serial devices on macOS --- src/tty.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/tty.c b/src/tty.c index fdfd0f9..88eecc6 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1407,6 +1407,14 @@ static bool is_serial_device(const char *format, ...) return false; } +#if defined(__APPLE__) + // Make sure device name is on the form /dev/cu.* or /dev/tty.* + if ((strncmp(filename, "/dev/cu.", 8) != 0) && (strncmp(filename, "/dev/tty.", 9) != 0)) + { + return false; + } +#endif + fd = open(filename, O_RDONLY | O_NONBLOCK | O_NOCTTY); if (fd == -1) { From da04c2c444b2ed9ca84763cd27808e430d318576 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 2 Jul 2024 17:41:28 +0200 Subject: [PATCH 054/146] Improve listing of long device names --- src/print.c | 23 +++++++++++++++++++++++ src/print.h | 1 + src/tty.c | 23 ++++++++++++++++++++--- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/print.c b/src/print.c index ab88ef9..06f7e73 100644 --- a/src/print.c +++ b/src/print.c @@ -87,3 +87,26 @@ void print(const char *format, ...) print_tainted = true; } + +void print_padded(char *string, size_t length, char pad_char) +{ + size_t padding = 0; + size_t string_length = 0; + size_t i; + + string_length = strlen(string); + + if (string_length < length) + { + padding += length - string_length; + printf("%s", string); + for (i=0; ipath) > listing_device_name_length_max) + { + listing_device_name_length_max = strlen(device->path); + } } if (g_list_length(device_list) == 0) @@ -1897,8 +1907,14 @@ void list_serial_devices(void) if (g_list_length(device_list) > 0) { - printf("Device TID Uptime [s] Driver Description\n"); - printf("----------------- ---- ------------- ---------------- --------------------------\n"); + if (listing_device_name_length_max < 17) + { + listing_device_name_length_max = 17; + } + print_padded("Device", listing_device_name_length_max, ' '); + printf(" TID Uptime [s] Driver Description\n"); + print_padded("", listing_device_name_length_max, '-'); + printf(" ---- ------------- ---------------- --------------------------\n"); // Iterate through the device list GList *iter; @@ -1907,7 +1923,8 @@ void list_serial_devices(void) device_t *device = (device_t *) iter->data; // Print device information - printf("%-17s %4s %13.3f %-16s %s\n", device->path, device->tid, device->uptime, device->driver, device->description); + print_padded(device->path, listing_device_name_length_max, ' '); + printf(" %4s %13.3f %-16s %s\n", device->tid, device->uptime, device->driver, device->description); } printf("\n"); } From 2c700a90b0c47f64b212e3f3c75d098ab437b88f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 2 Jul 2024 19:11:40 +0200 Subject: [PATCH 055/146] Code cleanup --- src/tty.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/tty.c b/src/tty.c index fa97b23..193c254 100644 --- a/src/tty.c +++ b/src/tty.c @@ -2637,22 +2637,22 @@ int tty_connect(void) break; case INPUT_MODE_LINE: - switch (input_char) + if (input_char == '\r') { - case '\r': // Carriage return - readline_input(input_char); + // Carriage return + readline_input(input_char); - // Write current line to tty device - char *line = readline_get(); - tty_write(device_fd, line, strlen(line)); - tty_sync(device_fd); - break; - - default: - readline_input(input_char); - forward = false; - break; + // Write current line to tty device + char *line = readline_get(); + tty_write(device_fd, line, strlen(line)); + tty_sync(device_fd); } + else + { + readline_input(input_char); + forward = false; + } + break; default: break; From 14ee38a0d955fb287d0812337952e2f6c936a6ee Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 2 Jul 2024 19:13:14 +0200 Subject: [PATCH 056/146] Clean up readline code --- src/readline.c | 159 ++++++++++++++++++++++++------------------------- 1 file changed, 79 insertions(+), 80 deletions(-) diff --git a/src/readline.c b/src/readline.c index d50e63f..8176a0b 100644 --- a/src/readline.c +++ b/src/readline.c @@ -22,17 +22,16 @@ #include "print.h" #include "misc.h" -#define MAX_LINE_LEN PATH_MAX -#define MAX_HISTORY 1000 +#define RL_LINE_LENGTH_MAX PATH_MAX +#define RL_HISTORY_MAX 1000 -static char line[MAX_LINE_LEN] = {}; -static int history_count = 0; -static int history_index = 0; -static int line_length = 0; -static int cursor_pos = 0; -static int escape = 0; - -static char *history[MAX_HISTORY]; +static char rl_line[RL_LINE_LENGTH_MAX] = {}; +static char *rl_history[RL_HISTORY_MAX]; +static int rl_history_count = 0; +static int rl_history_index = 0; +static int rl_line_length = 0; +static int rl_cursor_pos = 0; +static int rl_escape = 0; static void print_line(const char *string, int cursor_pos) { @@ -47,58 +46,58 @@ static void print_line(const char *string, int cursor_pos) void readline_init(void) { - history_count = 0; - history_index = 0; + rl_history_count = 0; + rl_history_index = 0; - for (int i = 0; i < MAX_HISTORY; ++i) + for (int i = 0; i < RL_HISTORY_MAX; ++i) { - history[i] = NULL; + rl_history[i] = NULL; } - line[0] = 0; - line_length = 0; - cursor_pos = 0; - escape = 0; + rl_line[0] = 0; + rl_line_length = 0; + rl_cursor_pos = 0; + rl_escape = 0; } char * readline_get(void) { - return line; + return rl_line; } static void readline_input_char(char input_char) { - if (line_length < MAX_LINE_LEN - 1) + if (rl_line_length < RL_LINE_LENGTH_MAX - 1) { - memmove(&line[cursor_pos + 1], &line[cursor_pos], line_length - cursor_pos); - line[cursor_pos] = input_char; - line_length++; - cursor_pos++; - line[line_length] = '\0'; - print_line(line, cursor_pos); + memmove(&rl_line[rl_cursor_pos + 1], &rl_line[rl_cursor_pos], rl_line_length - rl_cursor_pos); + rl_line[rl_cursor_pos] = input_char; + rl_line_length++; + rl_cursor_pos++; + rl_line[rl_line_length] = '\0'; + print_line(rl_line, rl_cursor_pos); } - escape = 0; + rl_escape = 0; } static void readline_input_cr(void) { - if (line_length > 0) + if (rl_line_length > 0) { // Save to history - if (history_count < MAX_HISTORY) + if (rl_history_count < RL_HISTORY_MAX) { - history[history_count] = strndup(line, line_length); - history_count++; + rl_history[rl_history_count] = strndup(rl_line, rl_line_length); + rl_history_count++; } else { - free(history[0]); - memmove(&history[0], &history[1], (MAX_HISTORY - 1) * sizeof(char*)); - history[MAX_HISTORY - 1] = strndup(line, line_length); + free(rl_history[0]); + memmove(&rl_history[0], &rl_history[1], (RL_HISTORY_MAX - 1) * sizeof(char*)); + rl_history[RL_HISTORY_MAX - 1] = strndup(rl_line, rl_line_length); } } - line[line_length] = '\0'; + rl_line[rl_line_length] = '\0'; if (option.local_echo == false) { clear_line(); @@ -108,54 +107,54 @@ static void readline_input_cr(void) print("\r\n"); } - line_length = 0; - cursor_pos = 0; - history_index = history_count; - escape = 0; + rl_line_length = 0; + rl_cursor_pos = 0; + rl_history_index = rl_history_count; + rl_escape = 0; } static void readline_input_bs(void) { - if (cursor_pos > 0) + if (rl_cursor_pos > 0) { - memmove(&line[cursor_pos - 1], &line[cursor_pos], line_length - cursor_pos); - line_length--; - cursor_pos--; - line[line_length] = '\0'; - print_line(line, cursor_pos); + memmove(&rl_line[rl_cursor_pos - 1], &rl_line[rl_cursor_pos], rl_line_length - rl_cursor_pos); + rl_line_length--; + rl_cursor_pos--; + rl_line[rl_line_length] = '\0'; + print_line(rl_line, rl_cursor_pos); } - escape = 0; + rl_escape = 0; } static void readline_input_escape(void) { - escape = 1; + rl_escape = 1; } static void readline_input_left_bracket(void) { - if (escape == 1) + if (rl_escape == 1) { - escape = 2; + rl_escape = 2; } else { - escape = 0; + rl_escape = 0; } } static void readline_input_A(void) { - if (escape == 2) + if (rl_escape == 2) { // Up arrow - if (history_index > 0) + if (rl_history_index > 0) { - history_index--; - strncpy(line, history[history_index], MAX_LINE_LEN-1); - line_length = strlen(line); - cursor_pos = line_length; - print_line(line, cursor_pos); + rl_history_index--; + strncpy(rl_line, rl_history[rl_history_index], RL_LINE_LENGTH_MAX-1); + rl_line_length = strlen(rl_line); + rl_cursor_pos = rl_line_length; + print_line(rl_line, rl_cursor_pos); } } else @@ -163,29 +162,29 @@ static void readline_input_A(void) readline_input_char('A'); } - escape = 0; + rl_escape = 0; } static void readline_input_B(void) { - if (escape == 2) + if (rl_escape == 2) { // Down arrow - if (history_index < history_count - 1) + if (rl_history_index < rl_history_count - 1) { - history_index++; - strncpy(line, history[history_index], MAX_LINE_LEN-1); - line_length = strlen(line); - cursor_pos = line_length; - print_line(line, cursor_pos); + rl_history_index++; + strncpy(rl_line, rl_history[rl_history_index], RL_LINE_LENGTH_MAX-1); + rl_line_length = strlen(rl_line); + rl_cursor_pos = rl_line_length; + print_line(rl_line, rl_cursor_pos); } - else if (history_index == history_count - 1) + else if (rl_history_index == rl_history_count - 1) { - history_index++; - line_length = 0; - cursor_pos = 0; - line[line_length] = '\0'; - print_line(line, cursor_pos); + rl_history_index++; + rl_line_length = 0; + rl_cursor_pos = 0; + rl_line[rl_line_length] = '\0'; + print_line(rl_line, rl_cursor_pos); } } else @@ -193,17 +192,17 @@ static void readline_input_B(void) readline_input_char('B'); } - escape = 0; + rl_escape = 0; } static void readline_input_C(void) { - if (escape == 2) + if (rl_escape == 2) { // Right arrow - if (cursor_pos < line_length) + if (rl_cursor_pos < rl_line_length) { - cursor_pos++; + rl_cursor_pos++; print("\x1b[C"); } } @@ -212,17 +211,17 @@ static void readline_input_C(void) readline_input_char('C'); } - escape = 0; + rl_escape = 0; } static void readline_input_D(void) { - if (escape == 2) + if (rl_escape == 2) { // Left arrow - if (cursor_pos > 0) + if (rl_cursor_pos > 0) { - cursor_pos--; + rl_cursor_pos--; print("\b"); } } @@ -231,7 +230,7 @@ static void readline_input_D(void) readline_input_char('D'); } - escape = 0; + rl_escape = 0; } void readline_input(char input_char) From ada2db073925a74d45c4311ce362123c809fd44d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 2 Jul 2024 19:20:59 +0200 Subject: [PATCH 057/146] Clean up shadow variable --- src/tty.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tty.c b/src/tty.c index 193c254..24436f7 100644 --- a/src/tty.c +++ b/src/tty.c @@ -2643,8 +2643,8 @@ int tty_connect(void) readline_input(input_char); // Write current line to tty device - char *line = readline_get(); - tty_write(device_fd, line, strlen(line)); + char *rl_line = readline_get(); + tty_write(device_fd, rl_line, strlen(rl_line)); tty_sync(device_fd); } else From 2a1dae6336800603c697b5d4a1dff660e2c8ad74 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 9 Jul 2024 16:44:10 +0200 Subject: [PATCH 058/146] Fix crashy search_reset() on macOS --- src/tty.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tty.c b/src/tty.c index 24436f7..badf82a 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1876,10 +1876,10 @@ GList *tty_search_for_serial_devices(void) // Fill in device information device->path = g_strdup(path); - device->tid = ""; + device->tid = g_strdup(""); device->uptime = uptime; - device->driver = ""; - device->description = ""; + device->driver = g_strdup(""); + device->description = g_strdup(""); // Add device information to device list device_list = g_list_append(device_list, device); From da9f7a65405cb125dc3e0522fb669789f3293181 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 9 Jul 2024 21:06:19 +0200 Subject: [PATCH 059/146] Improve warning upon failing connect Add device path to warning when connect fails. --- src/tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tty.c b/src/tty.c index badf82a..bf9fe0e 100644 --- a/src/tty.c +++ b/src/tty.c @@ -2107,7 +2107,7 @@ void tty_wait_for_device(void) } else if (last_errno != errno) { - tio_warning_printf("Could not open tty device (%s)", strerror(errno)); + tio_warning_printf("Could not open %s (%s)", device_name, strerror(errno)); tio_printf("Waiting for tty device.."); last_errno = errno; } From 53bb2a6ad13b70df013229591103b85a7420d27a Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 9 Jul 2024 22:12:39 +0200 Subject: [PATCH 060/146] Fix uptime on MacOS On MacOS the birth time is apparently not available so we use modification time instead. --- src/fs.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/fs.c b/src/fs.c index 6a2063c..740d9a0 100644 --- a/src/fs.c +++ b/src/fs.c @@ -199,7 +199,6 @@ double fs_get_creation_time(const char *path) double fs_get_creation_time(const char *path) { - // Use stat on macOS to access creation time struct stat st; if (stat(path, &st) != 0) @@ -207,7 +206,7 @@ double fs_get_creation_time(const char *path) return -1; } - return st.st_birthtimespec.tv_sec + st.st_birthtimespec.tv_nsec / 1e9; + return st.st_mtimespec.tv_sec + st.st_mtimespec.tv_nsec / 1e9; } #else From fc0c6f61d2c057ffdc7b7fad4c4795f627ca7138 Mon Sep 17 00:00:00 2001 From: Robert Lipe Date: Tue, 9 Jul 2024 18:23:19 -0500 Subject: [PATCH 061/146] Recompute listing_device_name_length_max for MacOS case, too. --- src/tty.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tty.c b/src/tty.c index bf9fe0e..2fb2e97 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1883,6 +1883,12 @@ GList *tty_search_for_serial_devices(void) // Add device information to device list device_list = g_list_append(device_list, device); + + // Update length of longest device name string + if (strlen(device->path) > listing_device_name_length_max) + { + listing_device_name_length_max = strlen(device->path); + } } if (g_list_length(device_list) == 0) From b5184012c4fe673271de4bde6dcd7ec1d6aee09f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 10 Jul 2024 01:48:13 +0200 Subject: [PATCH 062/146] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 5c80971..8810513 100644 --- a/AUTHORS +++ b/AUTHORS @@ -55,5 +55,6 @@ Brian Davis C KhazAkar Eliot Alan Foss +Robert Lipe Thanks to everyone who has contributed to this project. From c3654486c79d323d6a2485283897df77cbbc4cf4 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 10 Jul 2024 13:11:25 +0200 Subject: [PATCH 063/146] Fix hex output mode when using normal input mode In this combination of modes the input character was not forwarded to the tty device. This fix makes sure it is forwarded. --- src/tty.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/tty.c b/src/tty.c index 2fb2e97..c7f2e73 100644 --- a/src/tty.c +++ b/src/tty.c @@ -2232,11 +2232,17 @@ void forward_to_tty(int fd, char output_char) { handle_hex_prompt(output_char); } - else + else if (option.input_mode == INPUT_MODE_NORMAL) { - if (option.input_mode == INPUT_MODE_NORMAL) + ssize_t status = tty_write(device_fd, &output_char, 1); + if (status < 0) + { + tio_warning_printf("Could not write to tty device"); + } + else { optional_local_echo(output_char); + tx_total++; } } break; From 32d97683f87663ca2607fd36eabdb9157b71208d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 10 Jul 2024 13:27:12 +0200 Subject: [PATCH 064/146] Update TODO --- TODO | 6 ------ 1 file changed, 6 deletions(-) diff --git a/TODO b/TODO index 1cb3926..28f6d7c 100644 --- a/TODO +++ b/TODO @@ -34,12 +34,6 @@ Already supported in hex output mode. - * Advanced line mode - - Current line mode only support backspace editing. Would be nice with arrow - key navigation left/right and insert/overwrite support. Also history browsing - pressing up/down. - * Allow tio to connect to socket After some more consideration I think it makes sense to support connecting to a From 0a95da00f1fd2fa989418a443bb8b6d80ce1d1cf Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 11 Jul 2024 17:00:37 +0200 Subject: [PATCH 065/146] Typo fixes --- NEWS | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/NEWS b/NEWS index 4ae4b9d..9402605 100644 --- a/NEWS +++ b/NEWS @@ -109,7 +109,7 @@ Changes since tio v3.1: * Fix line ending in --list output - * Print location of configuratin file in --list output + * Print location of configuration file in --list output * Fix alignment of profile listing @@ -148,7 +148,7 @@ Changes since tio v3.0: After including the use of glib we might as well replace inih with the glib key file parser. - All configuraiton file parsing has been reworked and also the options + All configuration file parsing has been reworked and also the options parsing has been cleaned up, resulting in better and stricter configuration file and option value checks. @@ -202,9 +202,9 @@ Rui Chen: * fix: add build patch for `FNM_EXTMATCH` - * feat: add macos workflow + * feat: add macOS workflow - * fix: add macos build patch for `fs_get_creation_time` + * fix: add macOS build patch for `fs_get_creation_time` @@ -429,11 +429,11 @@ Changes since tio v2.7: * Add lua modem_send(file,protocol) - * Fix xymodem error print outs + * Fix xymodem error messages * Rework x/y-modem transfer command - Remove ctrl-t X optin and instead introduce submenu to ctrl-t x option + Remove ctrl-t X option and instead introduce submenu to ctrl-t x option for picking which xmodem protocol to use. * Update README @@ -442,22 +442,22 @@ Changes since tio v2.7: * Add independent input and output mode - Replaces -x, --hexadecimal option with --intput-mode and --output-mode + Replaces -x, --hexadecimal option with --input-mode and --output-mode so it is possible to select hex or normal mode for both input and output independently. - To obtain same behaviour as -x, --hexadecimal use the following + To obtain same behavior as -x, --hexadecimal use the following configuration: input-mode = hex output-mode = hex - * Fix file descriptor handling on MacOS + * Fix file descriptor handling on macOS * Add tty line configuration script API On some platforms calling high()/low() to switch line states result in - costly system calls whick makes it impossible to swith two or more tty + costly system calls which makes it impossible to switch two or more tty lines simultaneously. To help solve this timing issue we introduce a tty line state @@ -493,7 +493,7 @@ Changes since tio v2.7: lines. Script is activated automatically on connect or manually via in session key command. - The Lua scripting feature opens up for many posibilities in the future + The Lua scripting feature opens up for many possibilities in the future such as adding expect like functionality to easily and programatically interact with the connected device. @@ -590,7 +590,7 @@ Changes since tio v2.5: Add --log-append option which makes tio append to any existing log file. - This also changes the default behaviour of tio from appending to + This also changes the default behavior of tio from appending to overwriting any existing log file. Now you have to use this new option to make tio append. @@ -924,7 +924,7 @@ Changes since tio v1.46: Victor Oliveira - * add macports install instructions + * add MacPorts install instructions @@ -1424,7 +1424,7 @@ Changes since tio v1.35: * Handle SIGHUP Handle SIGHUP so that the registered exit handlers are called to restore - the terminal back to its orignal state. + the terminal back to its original state. * Add color configuration support @@ -1480,10 +1480,10 @@ Changes since tio v1.34: * Add support for configurable timestamp format - Also changes default timestamp format from ISO8601 to classic 24-hour + Also changes default timestamp format from ISO 8601 to classic 24-hour format as this is assumed to be the format that most users would prefer. - And reintroduces strict but optional ISO8601 format. + And reintroduces strict but optional ISO 8601 format. This feature allows to easily add more timestamp formats in the future. @@ -1604,10 +1604,10 @@ Sylvain LAFRASSE: attila-v: - * Refine timestamps with milliseconds and ISO-8601 format (#129). + * Refine timestamps with milliseconds and ISO 8601 format (#129). * Show milliseconds too in the timestamp (#114) and log file (#124) - * Change timestamp format to ISO-8601. + * Change timestamp format to ISO 8601. Yin Fengwei: @@ -1686,7 +1686,7 @@ Lars Kellogg-Stedman: George Stark: - * dont show line state if ioctl failed + * don't show line state if ioctl failed * add serial lines manual control @@ -1699,9 +1699,9 @@ arichi: Mariusz Midor: - * Newline: handle booth NL and CR + * Newline: handle both NL and CR - Flag ONLCRNL expects code \n after press Enter, but on some systems \r is send instead. + Flag ONLCRNL expects code \n after press Enter, but on some systems \r is sent instead. @@ -2193,7 +2193,7 @@ Changes since tio v1.11: To display the total number of bytes transmitted/received simply perform the 'ctrl-t s' command sequence. - This feature can be useful when eg. trying to detect non-printable + This feature can be useful when e.g. trying to detect non-printable characters. * Further simplification of key handling From 68a64ac55401fe9dd6b78c6f7f9ca62e76dfc155 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sat, 13 Jul 2024 14:02:37 +0200 Subject: [PATCH 066/146] Print correct 'Done' timestamp for X- and Y-modem transfers Closes: #268 Call tio_printf() after completing xymodem_send(). Signed-off-by: Heinrich Schuchardt --- src/script.c | 14 +++++++++----- src/tty.c | 20 ++++++++++++++++---- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/script.c b/src/script.c index a92a4b6..46e6c4e 100644 --- a/src/script.c +++ b/src/script.c @@ -149,6 +149,7 @@ static int modem_send(lua_State *L) { const char *file = lua_tostring(L, 1); int protocol = lua_tointeger(L, 2); + int ret; if (file == NULL) { @@ -159,18 +160,21 @@ static int modem_send(lua_State *L) { case XMODEM_1K: tio_printf("Sending file '%s' using XMODEM-1K", file); - tio_printf("%s", xymodem_send(device_fd, file, XMODEM_1K) < 0 ? "Aborted" : "Done"); + ret = xymodem_send(device_fd, file, XMODEM_1K); + tio_printf("%s", ret < 0 ? "Aborted" : "Done"); break; case XMODEM_CRC: tio_printf("Sending file '%s' using XMODEM-CRC", file); - tio_printf("%s", xymodem_send(device_fd, file, XMODEM_CRC) < 0 ? "Aborted" : "Done"); - break; + ret = xymodem_send(device_fd, file, XMODEM_CRC); + tio_printf("%s", ret < 0 ? "Aborted" : "Done"); + break; case YMODEM: tio_printf("Sending file '%s' using YMODEM", file); - tio_printf("%s", xymodem_send(device_fd, file, YMODEM) < 0 ? "Aborted" : "Done"); - break; + ret = xymodem_send(device_fd, file, YMODEM); + tio_printf("%s", ret < 0 ? "Aborted" : "Done"); + break; } return 0; diff --git a/src/tty.c b/src/tty.c index c7f2e73..bfb475d 100644 --- a/src/tty.c +++ b/src/tty.c @@ -712,9 +712,12 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf_raw("Enter file name: "); if (tio_readln()) { + int ret; + tio_printf("Sending file '%s' ", line); tio_printf("Press any key to abort transfer"); - tio_printf("%s", xymodem_send(device_fd, line, XMODEM_1K) < 0 ? "Aborted" : "Done"); + ret = xymodem_send(device_fd, line, XMODEM_1K); + tio_printf("%s", ret < 0 ? "Aborted" : "Done"); } break; @@ -723,9 +726,12 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf_raw("Enter file name: "); if (tio_readln()) { + int ret; + tio_printf("Sending file '%s' ", line); tio_printf("Press any key to abort transfer"); - tio_printf("%s", xymodem_send(device_fd, line, XMODEM_CRC) < 0 ? "Aborted" : "Done"); + ret = xymodem_send(device_fd, line, XMODEM_CRC); + tio_printf("%s", ret < 0 ? "Aborted" : "Done"); } break; @@ -734,9 +740,12 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf_raw("Enter file name: "); if (tio_readln()) { + int ret; + tio_printf("Ready to receiving file '%s' ", line); tio_printf("Press any key to abort transfer"); - tio_printf("%s", xymodem_receive(device_fd, line, XMODEM_CRC) < 0 ? "Aborted" : "Done"); + ret = xymodem_send(device_fd, line, XMODEM_CRC); + tio_printf("%s", ret < 0 ? "Aborted" : "Done"); } break; @@ -1094,9 +1103,12 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf("Send file with YMODEM"); tio_printf_raw("Enter file name: "); if (tio_readln()) { + int ret; + tio_printf("Sending file '%s' ", line); tio_printf("Press any key to abort transfer"); - tio_printf("%s", xymodem_send(device_fd, line, YMODEM) < 0 ? "Aborted" : "Done"); + ret = xymodem_send(device_fd, line, YMODEM); + tio_printf("%s", ret < 0 ? "Aborted" : "Done"); } break; From 289bbfd39358e4798d9c46134d67252c72847489 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 13 Jul 2024 15:14:24 +0200 Subject: [PATCH 067/146] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 8810513..41be783 100644 --- a/AUTHORS +++ b/AUTHORS @@ -56,5 +56,6 @@ Davis C KhazAkar Eliot Alan Foss Robert Lipe +Heinrich Schuchardt Thanks to everyone who has contributed to this project. From 866b5bcb3079fa912daa10b619254598d2ce6615 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 13 Jul 2024 16:49:19 +0200 Subject: [PATCH 068/146] Mention how to list key commands in help output --- src/options.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/options.c b/src/options.c index 6fd2b10..22a3969 100644 --- a/src/options.c +++ b/src/options.c @@ -172,6 +172,8 @@ void option_print_help(char *argv[]) printf("\n"); printf("Options and profiles may be set via configuration file.\n"); printf("\n"); + printf("In session you can press ctrl-%c ? to list available key commands.\n", option.prefix_key); + printf("\n"); printf("See the man page for more details.\n"); } From f4076258f14e8fa51e62038fcb11d31bb4f93b40 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 13 Jul 2024 17:09:26 +0200 Subject: [PATCH 069/146] Update man page --- man/tio.1.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 371870f..67c1600 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -373,10 +373,10 @@ Display program version. .BR \-h ", " \-\-help Display help. -.SH "KEYS" +.SH "KEY COMMANDS" .PP .TP 16n -In session, all key strokes are forwarded to the serial device except the following key sequence: a prefix key (default: ctrl-t) followed by a command key. These sequences are intercepted as tio commands: +In session, all key strokes are forwarded to the serial device except the following key sequence: a prefix key (default: ctrl-t) followed by a command key. These sequences are intercepted as key commands: .IP "\fBctrl-t ?" List available key commands .IP "\fBctrl-t b" From 9cafcbcab59edb0d77f8dd7eb09cdf19e4f28807 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 13 Jul 2024 17:17:14 +0200 Subject: [PATCH 070/146] Update README --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fec55f8..e434a64 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ Options: -t, --timestamp Enable line timestamp --timestamp-format Set timestamp format (default: 24hour) --timestamp-timeout Set timestamp timeout (default: 200) - -l, --list List available serial devices + -l, --list List available serial devices, TIDs, and profiles -L, --log Enable log to file --log-file Set log filename --log-directory Set log directory path for automatic named logs @@ -142,11 +142,14 @@ Options: --script Run script from string --script-file Run script from file --script-run once|always|never Run script on connect (default: always) + --exec Execute shell command with I/O redirected to device -v, --version Display version -h, --help Display help Options and profiles may be set via configuration file. +In session you can press ctrl-t ? to list available key commands. + See the man page for more details. ``` From e1fe2322545223691d0950d6341bcab26d259ab2 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 13 Jul 2024 18:36:03 +0200 Subject: [PATCH 071/146] Fix shadow variable --- src/tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tty.c b/src/tty.c index bfb475d..52e5942 100644 --- a/src/tty.c +++ b/src/tty.c @@ -2246,7 +2246,7 @@ void forward_to_tty(int fd, char output_char) } else if (option.input_mode == INPUT_MODE_NORMAL) { - ssize_t status = tty_write(device_fd, &output_char, 1); + status = tty_write(device_fd, &output_char, 1); if (status < 0) { tio_warning_printf("Could not write to tty device"); From 14963032c3adee085121ed0f80eda629076b4fb5 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 15 Jul 2024 20:05:28 +0200 Subject: [PATCH 072/146] Update TODO --- TODO | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/TODO b/TODO index 28f6d7c..7557ff3 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,15 @@ + * Add support for nested configuration files + + Add "include" directive to make it possible to include a configuration file + from another. For example: + + [include work/config] + + The feature should support including files that include other files etc. + (multi level nesting). + * Porting layer to support native win32 builds. - + Some of the work that needs to be done: All posix functions need to be platform independent, go though file by file: From 725423c50cc4218104c67656c152c9e04e7bd006 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 18 Jul 2024 21:01:26 +0200 Subject: [PATCH 073/146] Add configuration file include directive To include the contents of another configuration file simply do e.g.: [include raspberrypi.conf] Also, included file can include other files which can include other files etc. This feature is useful for managing many configuration files and sharing configuration files with others. --- README.md | 1 + TODO | 10 ---- man/tio.1.in | 3 + src/configfile.c | 149 ++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 144 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index e434a64..5521df0 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ when used in combination with [tmux](https://tmux.github.io). * Configuration file support * Support for configuration profiles * Activate configuration profiles by name or pattern + * Support for including other configuration files * Redirect I/O of shell command to serial device * Redirect I/O to UNIX socket or IPv4/v6 network socket * Useful for scripting or TTY sharing diff --git a/TODO b/TODO index 7557ff3..962ad71 100644 --- a/TODO +++ b/TODO @@ -1,13 +1,3 @@ - * Add support for nested configuration files - - Add "include" directive to make it possible to include a configuration file - from another. For example: - - [include work/config] - - The feature should support including files that include other files etc. - (multi level nesting). - * Porting layer to support native win32 builds. Some of the work that needs to be done: diff --git a/man/tio.1.in b/man/tio.1.in index 67c1600..114a97d 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -581,6 +581,9 @@ Run script on connect .IP "\fBexec" Execute shell command with I/O redirected to device +.PP +It is possible to include the content of other configuration files using the +include directive like so: "[include ]". .SH "CONFIGURATION FILE EXAMPLES" diff --git a/src/configfile.c b/src/configfile.c index 460e476..53545a4 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -35,9 +36,14 @@ #include "misc.h" #define CONFIG_GROUP_NAME_DEFAULT "default" +#define CONFIG_GROUP_INCLUDE_PREFIX "include " +#define MAX_LINE_LENGTH 1024 struct config_t config = {}; +static void config_file_load(const char *filename, GString *buffer, bool test); +static void config_file_process(const char *filename, GString *buffer, GList **included_files, bool test); + static void config_get_string(GKeyFile *key_file, gchar *group, gchar *key, char **dest, char *allowed_string, ...) { (void)dest; @@ -347,9 +353,11 @@ static int config_file_resolve(void) void config_file_show_profiles(void) { - GKeyFile *keyfile; + GString *config_buffer; GError *error = NULL; + GKeyFile *keyfile; + // Reset configuration memset(&config, 0, sizeof(struct config_t)); // Find config file @@ -359,13 +367,16 @@ void config_file_show_profiles(void) return; } - keyfile = g_key_file_new(); + // Load content of configuration file into buffer + config_buffer = g_string_new(NULL); + config_file_load(config.path, config_buffer, false); - if (!g_key_file_load_from_file(keyfile, config.path, G_KEY_FILE_NONE, &error)) + // Load configuration + keyfile = g_key_file_new(); + if (g_key_file_load_from_data(keyfile, config_buffer->str, config_buffer->len, G_KEY_FILE_NONE, &error) == false) { - tio_error_print("Failure loading file: %s", error->message); g_error_free(error); - return; + goto error_load; } // Get all group names @@ -379,11 +390,20 @@ void config_file_show_profiles(void) { continue; } + + // Skip group with include directive + if (strncmp(group[i], CONFIG_GROUP_INCLUDE_PREFIX, strlen(CONFIG_GROUP_INCLUDE_PREFIX)) == 0) + { + continue; + } + printf("%s ", group[i]); } g_strfreev(group); +error_load: g_key_file_free(keyfile); + g_string_free(config_buffer, TRUE); } static void replace_substring(char *str, const char *substr, const char *replacement) @@ -483,6 +503,95 @@ error: return NULL; } +static void config_file_process_line(const char *line, GString *buffer, GList **included_files, bool test) +{ + if (strncmp(line, "[include ", 9) == 0 && line[strlen(line) - 2] == ']') + { + char include_filename[MAX_LINE_LENGTH]; + + // Construct the format string safely + char format_string[50]; + snprintf(format_string, sizeof(format_string), "[include %%%ds]", MAX_LINE_LENGTH - 1); + + int ret = sscanf(line, format_string, include_filename); + if (ret != 1) + { + return; + } + + // Remove the trailing ']' character + include_filename[strlen(include_filename) - 1] = '\0'; + + if (g_list_find_custom(*included_files, include_filename, (GCompareFunc)strcmp) != NULL) + { + // Already included, avoid recursion + return; + } + + // Add to included files list + *included_files = g_list_append(*included_files, g_strdup(include_filename)); + + // Process the included file + config_file_process(include_filename, buffer, included_files, test); + } + else + { + // Normal line, add to buffer + g_string_append(buffer, line); + } +} + +static void config_file_process(const char *filename, GString *buffer, GList **included_files, bool test) +{ + if (test) + { + // Test that configuration file can be parsed + + GError *error = NULL; + GKeyFile *keyfile = g_key_file_new(); + + if (g_key_file_load_from_file(keyfile, filename, G_KEY_FILE_NONE, &error) == false) + { + tio_error_print("Failure loading file %s: %s", filename, error->message); + g_key_file_free(keyfile); + g_error_free(error); + exit(EXIT_FAILURE); + } + } + + FILE *file = fopen(filename, "r"); + if (file) + { + char line[MAX_LINE_LENGTH]; + while (fgets(line, sizeof(line), file)) + { + config_file_process_line(line, buffer, included_files, test); + } + fclose(file); + } +} + +static void config_file_load(const char *filename, GString *buffer, bool test) +{ + char current_dir[PATH_MAX] = "."; + char *config_file_dir = dirname(strdup(config.path)); + GList *included_files = NULL; + + getcwd(current_dir, PATH_MAX); + + // Change to the directory of the configuration file + chdir(config_file_dir); + + config_file_process(filename, buffer, &included_files, test); + + // Restore current directory + chdir(current_dir); + + // Free memory + g_list_free_full(included_files, g_free); + free(config_file_dir); +} + void config_file_parse(void) { // Find config file @@ -497,12 +606,18 @@ void config_file_parse(void) return; } + GString *config_buffer = g_string_new(NULL); GKeyFile *keyfile = g_key_file_new(); + GList *included_files = NULL; GError *error = NULL; - if (g_key_file_load_from_file(keyfile, config.path, G_KEY_FILE_NONE, &error) == false) + config_file_load(config.path, config_buffer, true); + + if (g_key_file_load_from_data(keyfile, config_buffer->str, config_buffer->len, G_KEY_FILE_NONE, &error) == false) { tio_error_print("Failure loading file %s: %s", config.path, error->message); + g_string_free(config_buffer, TRUE); + g_key_file_free(keyfile); g_error_free(error); exit(EXIT_FAILURE); } @@ -574,7 +689,10 @@ void config_file_parse(void) g_strfreev(group); } + // Cleanup g_key_file_free(keyfile); + g_string_free(config_buffer, TRUE); + g_list_free_full(included_files, g_free); atexit(&config_exit); } @@ -614,10 +732,14 @@ void config_list_targets(void) keyfile = g_key_file_new(); - if (!g_key_file_load_from_file(keyfile, config.path, G_KEY_FILE_NONE, &error)) + GString *config_buffer = g_string_new(NULL); + + config_file_load(config.path, config_buffer, false); + + if (g_key_file_load_from_data(keyfile, config_buffer->str, config_buffer->len, G_KEY_FILE_NONE, &error) == false) { g_error_free(error); - return; + goto cleanup; } // Get all group names @@ -626,7 +748,7 @@ void config_list_targets(void) if (num_groups == 0) { - return; + goto cleanup; } printf("\nConfiguration profiles (%s)\n", config.path); @@ -640,6 +762,13 @@ void config_list_targets(void) { continue; } + + // Skip group with include directive + if (strncmp(group[i], CONFIG_GROUP_INCLUDE_PREFIX, strlen(CONFIG_GROUP_INCLUDE_PREFIX)) == 0) + { + continue; + } + printf("%-19s ", group[i]); if (j++ % 4 == 0) { @@ -652,5 +781,7 @@ void config_list_targets(void) } g_strfreev(group); +cleanup: g_key_file_free(keyfile); + g_string_free(config_buffer, TRUE); } From 2259244eb27f5f1e5bafb89e849b1e5c04e4fe96 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 19 Jul 2024 09:27:23 +0200 Subject: [PATCH 074/146] Update NEWS --- NEWS | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 9402605..6e3a1d1 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,57 @@ -=== tio v3.5 === +=== tio v3.6 (2024-07-19) === + + + +Changes since tio v3.5 (2024-06-29): + + * Add configuration file include directive + + To include the contents of another configuration file simply do e.g.: + + [include raspberrypi.conf] + + Also, included file can include other files which can include other + files etc. + + This feature is useful for managing many configuration files and sharing + configuration files with others. + + * Mention how to list key commands in help output + + * Fix hex output mode when using normal input mode + + In this combination of modes the input character was not forwarded to + the tty device. This fix makes sure it is forwarded. + + * Fix uptime on MacOS + + On MacOS the birth time is apparently not available so we use + modification time instead. + + * Improve warning upon failing connect + + Add device path to warning when connect fails. + + * Fix crashy search_reset() on macOS + + * Clean up shadow variable + + * Clean up readline code + + * Improve listing of long device names + + * Fix listing of serial devices on macOS + +Heinrich Schuchardt: + + * Print correct 'Done' timestamp for X- and Y-modem transfers + + Call tio_printf() after completing xymodem_send(). + +Robert Lipe: + + * Recompute listing_device_name_length_max for MacOS case, too. From 13ad59ac12ac764fa2ca1b5ff110bdb393b9ba1f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 19 Jul 2024 09:37:57 +0200 Subject: [PATCH 075/146] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5521df0..5c79b6f 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ For more usage details please see the man page documentation ### 3.1 Command-line The command-line interface is straightforward as reflected in the output from -'tio --help': +```tio --help```: ``` Usage: tio [] From c4f5269c831de8fdaca3c70a5f60e2b074c375eb Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 19 Jul 2024 09:39:13 +0200 Subject: [PATCH 076/146] Update version date --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 138c734..15ea2d4 100644 --- a/meson.build +++ b/meson.build @@ -6,7 +6,7 @@ project('tio', 'c', ) # The tag date of the project_version(), update when the version bumps. -version_date = '2024-06-29' +version_date = '2024-07-19' # Test for dynamic baudrate configuration interface compiler = meson.get_compiler('c') From 475bc29cc8428d31a23d34dc27744213912cb9b8 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 19 Jul 2024 09:39:41 +0200 Subject: [PATCH 077/146] Update plain text man page --- man/tio.1.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/man/tio.1.txt b/man/tio.1.txt index e4e7725..d0096bb 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -290,8 +290,8 @@ OPTIONS Display help. -KEYS - In session, all key strokes are forwarded to the serial device except the following key sequence: a prefix key (default: ctrl-t) followed by a command key. These sequences are intercepted as tio commands: +KEY COMMANDS + In session, all key strokes are forwarded to the serial device except the following key sequence: a prefix key (default: ctrl-t) followed by a command key. These sequences are intercepted as key commands: ctrl-t ? List available key commands @@ -476,6 +476,8 @@ CONFIGURATION FILE exec Execute shell command with I/O redirected to device + It is possible to include the content of other configuration files using the include directive like so: "[include ]". + CONFIGURATION FILE EXAMPLES To change the default configuration simply set options like so: @@ -597,4 +599,4 @@ WEBSITE AUTHOR Maintained by Martin Lund . -tio 3.5 2024-06-15 tio(1) +tio 3.6 2024-07-19 tio(1) From 6163bc392bd353c2157d3e5a29bf914ada541100 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 20 Jul 2024 08:23:59 +0200 Subject: [PATCH 078/146] Fix socket send call on platforms without MSG_NOSIGNAL To fix build issue encountered on MacOS Catalina but may apply to other platforms. --- src/socket.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/socket.c b/src/socket.c index a4de8aa..e51f937 100644 --- a/src/socket.c +++ b/src/socket.c @@ -226,7 +226,11 @@ void socket_configure(void) exit(EXIT_FAILURE); } +#if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL) + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR | SO_NOSIGPIPE, &optval, sizeof(optval))) +#else if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))) +#endif { tio_error_printf("Failed to set socket options (%s)", strerror(errno)); exit(EXIT_FAILURE); @@ -270,7 +274,12 @@ void socket_write(char input_char) { if (clientfds[i] != -1) { + +#if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL) + if (send(clientfds[i], &input_char, 1, 0) <= 0) +#else if (send(clientfds[i], &input_char, 1, MSG_NOSIGNAL) <= 0) +#endif { tio_error_printf_silent("Failed to write to socket (%s)", strerror(errno)); close(clientfds[i]); From 2f6b3796f2b7d52ab9552cd1be322aaa9314590d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 25 Jul 2024 00:11:38 +0200 Subject: [PATCH 079/146] Bump version --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 15ea2d4..d9f78ec 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('tio', 'c', - version : '3.6', + version : '3.7', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] From ef12ed62df6bc5d7a2571a7a9f31be748af68cf3 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 6 Aug 2024 20:48:32 +0200 Subject: [PATCH 080/146] Remove unnecessary sync in line input mode This caused a problem for some highly timing sensitive modem read-eval-print loops because the input line and line termination characters (cr/nl) would be shifted out on the UART with too big delay inbetween because of two syncs. --- src/tty.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tty.c b/src/tty.c index 52e5942..6b92080 100644 --- a/src/tty.c +++ b/src/tty.c @@ -2669,7 +2669,6 @@ int tty_connect(void) // Write current line to tty device char *rl_line = readline_get(); tty_write(device_fd, rl_line, strlen(rl_line)); - tty_sync(device_fd); } else { From a3b67d3eb609dd8bb9aaaf27d51df509487bf365 Mon Sep 17 00:00:00 2001 From: Tomka Gergely Date: Mon, 19 Aug 2024 18:43:42 +0200 Subject: [PATCH 081/146] The log-directory options is not read from the configuration file. --- src/configfile.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/configfile.c b/src/configfile.c index 53545a4..97fc59f 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -219,6 +219,7 @@ static void config_parse_keys(GKeyFile *key_file, char *group) config_get_integer(key_file, group, "timestamp-timeout", &option.timestamp_timeout, 0, INT_MAX); config_get_bool(key_file, group, "log", &option.log); config_get_string(key_file, group, "log-file", &option.log_filename, NULL); + config_get_string(key_file, group, "log-directory", &option.log_directory, NULL); config_get_bool(key_file, group, "log-append", &option.log_append); config_get_bool(key_file, group, "log-strip", &option.log_strip); config_get_string(key_file, group, "map", &string, NULL); From cdc773100ceffbad5ac4ac6b7ebd4bf356ef12fa Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 19 Aug 2024 20:40:05 +0200 Subject: [PATCH 082/146] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 41be783..709b461 100644 --- a/AUTHORS +++ b/AUTHORS @@ -57,5 +57,6 @@ KhazAkar Eliot Alan Foss Robert Lipe Heinrich Schuchardt +Tomka Gergely Thanks to everyone who has contributed to this project. From f47467271f3aa20cda016d1bf662768d3cde4d3f Mon Sep 17 00:00:00 2001 From: Steve Marple Date: Fri, 23 Aug 2024 12:11:05 +0100 Subject: [PATCH 083/146] Add "epoch" timestamp option Add an option that prints the timestamp as the number of seconds since the Unix epoch. --- man/tio.1.in | 2 ++ man/tio.1.txt | 2 ++ src/options.c | 10 +++++++++- src/timestamp.c | 6 ++++++ src/timestamp.h | 1 + src/tty.c | 3 +++ 6 files changed, 23 insertions(+), 1 deletion(-) diff --git a/man/tio.1.in b/man/tio.1.in index 114a97d..631b938 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -148,6 +148,8 @@ Set timestamp format to any of the following timestamp formats: 24-hour format relative to previous timestamp .IP "\fBiso8601" ISO8601 format ("YYYY-MM-DDThh:mm:ss.sss") +.IP "\fBepoch" +Seconds since Unix epoch (1970-01-01) .PP Default format is \fB24hour\fR .RE diff --git a/man/tio.1.txt b/man/tio.1.txt index d0096bb..99a6e27 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -114,6 +114,8 @@ OPTIONS iso8601 ISO8601 format ("YYYY-MM-DDThh:mm:ss.sss") + epoch Seconds since Unix epoch (1970-01-01) + Default format is 24hour --timestamp-timeout diff --git a/src/options.c b/src/options.c index 22a3969..19f8c8a 100644 --- a/src/options.c +++ b/src/options.c @@ -392,6 +392,10 @@ const char* option_timestamp_format_to_string(timestamp_t timestamp) return "iso8601"; break; + case TIMESTAMP_EPOCH: + return "epoch"; + break; + default: return "unknown"; break; @@ -418,6 +422,10 @@ void option_parse_timestamp(const char *arg, timestamp_t *timestamp) { *timestamp = TIMESTAMP_ISO8601; } + else if (strcmp(arg, "epoch") == 0) + { + *timestamp = TIMESTAMP_EPOCH; + } else { tio_error_print("Invalid timestamp '%s'", arg); @@ -1151,7 +1159,7 @@ void options_parse_final(int argc, char *argv[]) if ( ((strncmp("COM", option.target, 3) == 0) || (strncmp("com", option.target, 3) == 0) ) && (sscanf(option.target + 3, "%hhu", &portnum) == 1) - && (portnum > 0) ) + && (portnum > 0) ) { asprintf(&tty_win, "/dev/ttyS%hhu", portnum - 1); option.target = tty_win; diff --git a/src/timestamp.c b/src/timestamp.c index a8d165c..6a23dc3 100644 --- a/src/timestamp.c +++ b/src/timestamp.c @@ -76,6 +76,12 @@ char *timestamp_current_time(void) tm = localtime(&tv.tv_sec); len = strftime(time_string, sizeof(time_string), "%Y-%m-%dT%H:%M:%S", tm); break; + case TIMESTAMP_EPOCH: + // "1223" (seconds since Unix epoch, 1970-01-01 00:00:00Z) + tv = tv_now; + tm = localtime(&tv.tv_sec); + len = strftime(time_string, sizeof(time_string), "%s", tm); + break; default: return NULL; } diff --git a/src/timestamp.h b/src/timestamp.h index 32d129d..48e0a76 100644 --- a/src/timestamp.h +++ b/src/timestamp.h @@ -28,6 +28,7 @@ typedef enum TIMESTAMP_24HOUR_START, TIMESTAMP_24HOUR_DELTA, TIMESTAMP_ISO8601, + TIMESTAMP_EPOCH, TIMESTAMP_END, } timestamp_t; diff --git a/src/tty.c b/src/tty.c index 6b92080..7dbd286 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1079,6 +1079,9 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) case TIMESTAMP_ISO8601: tio_printf("Switched timestamp mode to iso8601"); break; + case TIMESTAMP_EPOCH: + tio_printf("Switched timestamp mode to epoch"); + break; case TIMESTAMP_END: option.timestamp = TIMESTAMP_NONE; tio_printf("Switched timestamp mode off"); From bb2b4e30b2eb5e750a30e92ca432353cbed5d650 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 24 Aug 2024 12:37:12 +0200 Subject: [PATCH 084/146] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 709b461..8837ec0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -58,5 +58,6 @@ Eliot Alan Foss Robert Lipe Heinrich Schuchardt Tomka Gergely +Steve Marple Thanks to everyone who has contributed to this project. From 2e7da862c8b2b9d8e1ab5c147d6cac340e9fea27 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 24 Aug 2024 13:21:41 +0200 Subject: [PATCH 085/146] Cleanup --- src/timestamp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timestamp.c b/src/timestamp.c index 6a23dc3..e81cda2 100644 --- a/src/timestamp.c +++ b/src/timestamp.c @@ -77,7 +77,7 @@ char *timestamp_current_time(void) len = strftime(time_string, sizeof(time_string), "%Y-%m-%dT%H:%M:%S", tm); break; case TIMESTAMP_EPOCH: - // "1223" (seconds since Unix epoch, 1970-01-01 00:00:00Z) + // "N.sss" (seconds since Unix epoch, 1970-01-01 00:00:00Z) tv = tv_now; tm = localtime(&tv.tv_sec); len = strftime(time_string, sizeof(time_string), "%s", tm); From 27f9b9f2e84cf9489ad7970b9b24c32c233df011 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 31 Aug 2024 09:06:28 +0200 Subject: [PATCH 086/146] Update NEWS --- NEWS | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 6e3a1d1..8b2eae4 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,32 @@ -=== tio v3.6 (2024-07-19) === +=== tio v3.7 (2024-08-31) === + + + +Changes since tio v3.6 (2024-07-19): + + * Remove unnecessary sync in line input mode + + This caused a problem for some highly timing sensitive modem read-eval-print + loops because the input line and line termination characters (cr/nl) would be + shifted out on the UART with too big delay inbetween because of two + syncs. + + * Fix socket send call on platforms without MSG_NOSIGNAL + + To fix build issue encountered on MacOS Catalina but may apply to other + platforms. + +Steve Marple: + + * Add "epoch" timestamp option + + Add an option that prints the timestamp as the number of seconds since + the Unix epoch. + +Tomka Gergely: + + * The log-directory options is not read from the configuration file. From 9f27ce5899b9e5951dc46579eed199ddc7c13e49 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 31 Aug 2024 09:08:33 +0200 Subject: [PATCH 087/146] Update version date --- man/tio.1.txt | 2 +- meson.build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/man/tio.1.txt b/man/tio.1.txt index 99a6e27..7c872f5 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -601,4 +601,4 @@ WEBSITE AUTHOR Maintained by Martin Lund . -tio 3.6 2024-07-19 tio(1) +tio 3.7 2024-08-31 tio(1) diff --git a/meson.build b/meson.build index d9f78ec..b35e649 100644 --- a/meson.build +++ b/meson.build @@ -6,7 +6,7 @@ project('tio', 'c', ) # The tag date of the project_version(), update when the version bumps. -version_date = '2024-07-19' +version_date = '2024-08-31' # Test for dynamic baudrate configuration interface compiler = meson.get_compiler('c') From a22b27074937cfb21ccf6747f5fa63a1299f5f9e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 31 Aug 2024 09:23:19 +0200 Subject: [PATCH 088/146] Bump version --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index b35e649..810f9dd 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('tio', 'c', - version : '3.7', + version : '3.8', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] From 6c4b92270e72299d7260eb26d6fa3c8fdbe68f3d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 7 Sep 2024 09:31:31 +0200 Subject: [PATCH 089/146] Add clang-format spec --- .clang-format | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..682ae09 --- /dev/null +++ b/.clang-format @@ -0,0 +1,5 @@ +BasedOnStyle: llvm +IndentWidth: 4 +AllowShortFunctionsOnASingleLine: None +KeepEmptyLinesAtTheStartOfBlocks: false +BreakBeforeBraces: Allman From 9fec68911769736ab2595fda911a835e5c21633b Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 15 Sep 2024 05:57:31 +0200 Subject: [PATCH 090/146] Fix name declaration conflict with socket send() --- src/script.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/script.c b/src/script.c index 46e6c4e..d9efb09 100644 --- a/src/script.c +++ b/src/script.c @@ -181,7 +181,7 @@ static int modem_send(lua_State *L) } // lua: send(string) -static int send(lua_State *L) +static int send_(lua_State *L) { const char *string = lua_tostring(L, 1); int ret; @@ -455,7 +455,7 @@ static const struct luaL_Reg tio_lib[] = { "msleep", msleep}, { "line_set", line_set}, { "modem_send", modem_send}, - { "send", send}, + { "send", send_}, { "read", read_string}, { "expect", expect}, { "exit", exit_}, From 4034d0ad51fce93d8e35231267303799c5ab389c Mon Sep 17 00:00:00 2001 From: konosubakonoakua Date: Wed, 25 Sep 2024 20:14:27 +0800 Subject: [PATCH 091/146] Update readme.md Update readme.md issue part Update readme.md issue part --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5c79b6f..0ab480d 100644 --- a/README.md +++ b/README.md @@ -500,9 +500,13 @@ Note: The meson install steps may differ depending on your specific system. Getting permission access errors trying to open your serial device? -Add your user to the group which allows serial device access. For example, to add your user to the 'dialout' group do: +Add your user to the group which allows serial device access permanently. For example, to add your user to the 'dialout' group do: +```bash +sudo usermod -a -G dialout ``` -$ sudo usermod -a -G dialout +Switch to the "dialout" group, temporary but immediately for this session. +```bash +newgrp dialout ``` From d494b9d3ac4b4ac1ccf7a99f1d227b8ed7583bfb Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 25 Sep 2024 20:51:40 +0200 Subject: [PATCH 092/146] Update README --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0ab480d..fd93d9e 100644 --- a/README.md +++ b/README.md @@ -232,19 +232,24 @@ Connect automatically to latest registered serial device: $ tio --auto-connect latest ``` -It is also possible to use exclude options to affect which serial devices are +It is possible to use exclude options to affect which serial devices are involved in the automatic connection strategy: ``` $ tio --auto-connect new --exclude-devices "/dev/ttyACM?,/dev/ttyUSB2" ``` -Exclude drivers by pattern: +And to exclude drivers by pattern: ``` $ tio --auto-connect new --exclude-drivers "cdc_acm,ftdi_sio" ``` Note: Pattern matching supports '*' and '?'. Use comma separation to define multiple patterns. +To include drivers by specific pattern simply negate the exclude option: +``` +$ tio --auto-connect new --exclude-drivers !("cp2102") +``` + Log to file with autogenerated filename: ``` $ tio --log /dev/ttyUSB0 From 4fb034858ab22b6aba1bebd969fbf578f92e5465 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 15 Oct 2024 17:22:34 +0200 Subject: [PATCH 093/146] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 8837ec0..4c95c20 100644 --- a/AUTHORS +++ b/AUTHORS @@ -59,5 +59,6 @@ Robert Lipe Heinrich Schuchardt Tomka Gergely Steve Marple +konosubakonoakua Thanks to everyone who has contributed to this project. From 7e314b2cc342966153dca21bfb9367e44586cff0 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 17 Oct 2024 18:53:36 +0200 Subject: [PATCH 094/146] Update TODO --- TODO | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO b/TODO index 962ad71..7dce847 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,5 @@ + * Add mapping feature for printing non-printable characters + * Porting layer to support native win32 builds. Some of the work that needs to be done: From 330e99381e700b6172a921513354f806b78ff13c Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 25 Oct 2024 18:26:59 +0200 Subject: [PATCH 095/146] Fix memory leak in base62_encode() --- src/misc.c | 3 +-- src/misc.h | 2 +- src/tty.c | 3 ++- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/misc.c b/src/misc.c index 50fb339..bd0a429 100644 --- a/src/misc.c +++ b/src/misc.c @@ -121,10 +121,9 @@ unsigned long djb2_hash(const unsigned char *str) } // Function to encode a number to base62 -char *base62_encode(unsigned long num) +void *base62_encode(unsigned long num, char *output) { const char base62_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - char *output = (char *) malloc(5); // 4 characters + null terminator if (output == NULL) { tio_error_print("Memory allocation failed"); diff --git a/src/misc.h b/src/misc.h index 8ba8285..08ae5eb 100644 --- a/src/misc.h +++ b/src/misc.h @@ -30,7 +30,7 @@ void delay(long ms); int ctrl_key_code(unsigned char key); bool regex_match(const char *string, const char *pattern); unsigned long djb2_hash(const unsigned char *str); -char *base62_encode(unsigned long num); +void *base62_encode(unsigned long num, char *output); int read_poll(int fd, void *data, size_t len, int timeout); double get_current_time(void); bool match_patterns(const char *string, const char *patterns); diff --git a/src/tty.c b/src/tty.c index 7dbd286..c4785b9 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1735,7 +1735,8 @@ GList *tty_search_for_serial_devices(void) // Hash remaining string to get unique topology ID unsigned long hash = djb2_hash((const unsigned char *)devices_path); - char *tid = base62_encode(hash); + char tid[5]; + base62_encode(hash, tid); free(devices_path); // Construct the path to the device's driver symlink From ab678e6c882e19cdd0a7d5973348e74ba4099ccb Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 25 Oct 2024 19:35:13 +0200 Subject: [PATCH 096/146] Use version from git --- src/git-version.h.in | 3 +++ src/main.c | 3 ++- src/meson.build | 10 ++++++++-- src/options.c | 3 ++- src/tty.c | 3 ++- 5 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 src/git-version.h.in diff --git a/src/git-version.h.in b/src/git-version.h.in new file mode 100644 index 0000000..a0d2080 --- /dev/null +++ b/src/git-version.h.in @@ -0,0 +1,3 @@ +#pragma once + +#define GIT_VERSION "@GIT_VERSION@" diff --git a/src/main.c b/src/main.c index 9e6c043..a07ba67 100644 --- a/src/main.c +++ b/src/main.c @@ -22,6 +22,7 @@ #include #include #include +#include "git-version.h" #include "config.h" #include "options.h" #include "configfile.h" @@ -101,7 +102,7 @@ int main(int argc, char *argv[]) error_enter_session_mode(); /* Print launch hints */ - tio_printf("tio v%s", VERSION); + tio_printf("tio %s", GIT_VERSION); if (interactive_mode) { tio_printf("Press ctrl-%c q to quit", option.prefix_key); diff --git a/src/meson.build b/src/meson.build index 2d506de..17cc691 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,5 +1,10 @@ +# Generate git version header +git_version_h = vcs_tag(command : ['git', 'describe', '--tags', '--always', '--dirty'], + input : 'git-version.h.in', + output :'git-version.h', + replace_string:'@GIT_VERSION@') + config_h = configuration_data() -config_h.set_quoted('VERSION', meson.project_version()) config_h.set('BAUDRATE_CASES', baudrate_cases) configure_file(output: 'config.h', configuration: config_h) @@ -21,7 +26,8 @@ tio_sources = [ 'xymodem.c', 'script.c', 'fs.c', - 'readline.c' + 'readline.c', + git_version_h ] diff --git a/src/options.c b/src/options.c index 19f8c8a..398279f 100644 --- a/src/options.c +++ b/src/options.c @@ -23,6 +23,7 @@ #include #include #include +#include "git-version.h" #include "config.h" #include "misc.h" #include "print.h" @@ -1085,7 +1086,7 @@ void options_parse(int argc, char *argv[]) break; case 'v': - printf("tio v%s\n", VERSION); + printf("tio %s\n", GIT_VERSION); exit(EXIT_SUCCESS); break; diff --git a/src/tty.c b/src/tty.c index c4785b9..74f6ab3 100644 --- a/src/tty.c +++ b/src/tty.c @@ -22,6 +22,7 @@ #if defined(__linux__) #include #endif +#include "git-version.h" #include "config.h" #include #include @@ -1090,7 +1091,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) break; case KEY_V: - tio_printf("tio v%s", VERSION); + tio_printf("tio %s", GIT_VERSION); break; case KEY_X: From db3f109c7d413dd2f3aa1da713288e3aa28fcc23 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 7 Nov 2024 18:05:32 +0100 Subject: [PATCH 097/146] Zero initialize buffer in read_string() --- src/script.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/script.c b/src/script.c index d9efb09..484c79f 100644 --- a/src/script.c +++ b/src/script.c @@ -255,7 +255,7 @@ static int read_string(lua_State *L) int timeout = lua_tointeger(L, 2); int ret = 0; - char *buffer = malloc(size); + char *buffer = calloc(1, size); if (buffer == NULL) { ret = -1; // Error From afd82f7ac435170e425f0c7e6b3f6856e3135172 Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Fri, 25 Oct 2024 11:22:56 -0500 Subject: [PATCH 098/146] + Add system timestamps to lua read() and new lua read_line() per global options + Add missing timestamp-format epoch + Update send_ to use fsync and tcdrain like normal tty_sync does + Rework read_line to save partial line at timeout + Simplified read_line to reduce cyclomatic complexity + renamed example files read.lua and read_line.lua + moved #define READ_LINE_SIZE to top of file + renamed g_linebuf to linebuf, and moved it into read_line as a static variable --- examples/lua/read.lua | 14 ++++ examples/lua/read_line.lua | 17 +++++ man/tio.1.in | 13 +++- man/tio.1.txt | 9 ++- src/configfile.c | 2 +- src/error.c | 3 +- src/log.c | 3 +- src/options.c | 2 + src/script.c | 127 ++++++++++++++++++++++++++++++++----- src/timestamp.c | 2 - src/timestamp.h | 2 + src/tty.c | 1 + 12 files changed, 171 insertions(+), 24 deletions(-) create mode 100644 examples/lua/read.lua create mode 100644 examples/lua/read_line.lua diff --git a/examples/lua/read.lua b/examples/lua/read.lua new file mode 100644 index 0000000..2ee99db --- /dev/null +++ b/examples/lua/read.lua @@ -0,0 +1,14 @@ +read(1000, 6000) -- initial config +send("\n") +msleep(100) +read(650, 60) -- main menu +send("S") -- S menu +msleep(30) +read(650, 60) +send("t") -- Parallel Value Table +read(650, 60) +while true do + msleep(1000) + send("t") + read(650, 50) -- repeat PVT forever +end diff --git a/examples/lua/read_line.lua b/examples/lua/read_line.lua new file mode 100644 index 0000000..97f157d --- /dev/null +++ b/examples/lua/read_line.lua @@ -0,0 +1,17 @@ +read(1000, 8000) -- read initial config +send("\n") +read(650, 100) -- main menu +send("S") -- S menu +n = 1 +while n > 0 do -- while not empty, read more + n, str = read_line(25) +end +while true do + send("t") -- query PV table + msleep(880) + n = 1 + while n > 0 do -- while not empty, read more + n, str = read_line(60) + msleep(60) + end +end diff --git a/man/tio.1.in b/man/tio.1.in index 631b938..4ad15ed 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -467,7 +467,18 @@ until data is ready to read. Returns number of bytes read on success, 0 on timeout, or -1 on error. -On success, returns read string as second return value. +On success, returns read string as second return value. Also emits a single +timestamp to stdout and log file per options.timestamp and options.log. + +.IP "\fBread_line(timeout)" +Read line from serial device. If timeout is 0 or not provided it will wait +forever until data is ready to read. + +Returns number of bytes read on success, 0 on timeout, or -1 on error. + +On success, returns the string that was read as second return value. Also +emits a single timestamp to stdout and log file per options.timestamp +and options.log. .IP "\fBset{line=state, ...}" Set state of one or multiple tty modem lines. diff --git a/man/tio.1.txt b/man/tio.1.txt index 7c872f5..6debf36 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -373,7 +373,14 @@ SCRIPT API Returns number of bytes read on success, 0 on timeout, or -1 on error. - On success, returns read string as second return value. + On success, returns read string as second return value. Also emits a single timestamp to stdout and log file per options.timestamp and options.log. + + read_line(timeout) + Read line from serial device. If timeout is 0 or not provided it will wait forever until data is ready to read. + + Returns number of bytes read on success, 0 on timeout, or -1 on error. + + On success, returns the string that was read as second return value. Also emits a single timestamp to stdout and log file per options.timestamp and options.log. set{line=state, ...} Set state of one or multiple tty modem lines. diff --git a/src/configfile.c b/src/configfile.c index 97fc59f..07da79a 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -209,7 +209,7 @@ static void config_parse_keys(GKeyFile *key_file, char *group) { option.timestamp = TIMESTAMP_24HOUR; } - config_get_string(key_file, group, "timestamp-format", &string, "24hour", "24hour-start", "24hour-delta", "iso8601", NULL); + config_get_string(key_file, group, "timestamp-format", &string, "24hour", "24hour-start", "24hour-delta", "iso8601", "epoch", NULL); if (string != NULL) { option_parse_timestamp(string, &option.timestamp); diff --git a/src/error.c b/src/error.c index f318378..9f6bac2 100644 --- a/src/error.c +++ b/src/error.c @@ -19,8 +19,7 @@ * 02110-1301, USA. */ -#define __STDC_WANT_LIB_EXT2__ 1 // To access vasprintf - +#define _GNU_SOURCE // To access vasprintf #include #include "print.h" diff --git a/src/log.c b/src/log.c index 8966457..4131617 100644 --- a/src/log.c +++ b/src/log.c @@ -19,8 +19,7 @@ * 02110-1301, USA. */ -#define __STDC_WANT_LIB_EXT2__ 1 // To access vasprintf - +#define _GNU_SOURCE // To access vasprintf #include #include #include diff --git a/src/options.c b/src/options.c index 398279f..04d5be6 100644 --- a/src/options.c +++ b/src/options.c @@ -19,6 +19,8 @@ * 02110-1301, USA. */ +#define _GNU_SOURCE // To access vasprintf + #include #include #include diff --git a/src/script.c b/src/script.c index 484c79f..b565741 100644 --- a/src/script.c +++ b/src/script.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "misc.h" #include "print.h" #include "options.h" @@ -37,8 +38,11 @@ #include "log.h" #include "script.h" #include "fs.h" +#include "timestamp.h" +#include "termios.h" #define MAX_BUFFER_SIZE 2000 // Maximum size of circular buffer +#define READ_LINE_SIZE 4096 // read_line buffer length static int device_fd; static char circular_buffer[MAX_BUFFER_SIZE]; @@ -192,6 +196,8 @@ static int send_(lua_State *L) } ret = write(device_fd, string, strlen(string)); + fsync(device_fd); // flush these characters now + tcdrain(device_fd); //ensure we flushed characters to our device lua_pushnumber(L, ret); @@ -248,10 +254,39 @@ static bool match_regex(regex_t *regex) return false; } -// lua: ret,string = read_string(size, timeout) +// Function to echo a buffer to stdout and to the log +// per the option.timestamp and option.log settings +static void echo_buffer(char buffer[], ssize_t len) +{ + if (option.timestamp) + { + char *pTimeStampNow; + pTimeStampNow = timestamp_current_time(); + if (pTimeStampNow) + { + tio_printf("%s", buffer); //does timestamps for us + if (option.log) + { + log_printf("\n[%s] %s", pTimeStampNow, buffer); + } + } + } else { + for (ssize_t i=0; i 0 ? buffer : ""); free(buffer); } + else + { + lua_pushstring(L, ""); // give empty string to caller + } + return 2; +} + +// lua: ret,string = read_line(timeout) +static int read_line(lua_State *L) +{ + static char linebuf[READ_LINE_SIZE]; + int timeout = lua_tointeger(L, 1); //ms + int ret = 0; + int read_result = 1; //enable reading input from device + char ch; + int bytes_read = 0; + + linebuf[0] = '\0'; + if (timeout == 0) + { + timeout = -1; // Wait forever + } + + // loop reading input until a newline seen or timeout + while (true) + { + read_result = read_poll(device_fd, &ch, 1, timeout); + if (read_result < 0) + { + ret = -1; // Error + linebuf[bytes_read] = '\0'; + goto error_rl; + } + else if (!read_result) + { + // Timeout returns a non-empty linebuf as a 'line' + ret = bytes_read; + linebuf[bytes_read] = '\0'; + break; + } + else // we got a character, so handle it + { + if (ch == '\n') + { + linebuf[bytes_read] = '\0'; + break; + } + else if (bytes_read <= (READ_LINE_SIZE-2)) + { + if (isprint(ch)) // store all printable chars + { + linebuf[bytes_read++] = ch; + } + } + } + } + + if (bytes_read) + { + echo_buffer(linebuf, bytes_read); + } + ret = bytes_read; + +error_rl: + lua_pushnumber(L, ret); + lua_pushstring(L, linebuf); return 2; } @@ -457,6 +553,7 @@ static const struct luaL_Reg tio_lib[] = { "modem_send", modem_send}, { "send", send_}, { "read", read_string}, + { "read_line", read_line}, { "expect", expect}, { "exit", exit_}, { "tty_search", tty_search_}, diff --git a/src/timestamp.c b/src/timestamp.c index e81cda2..b5ec306 100644 --- a/src/timestamp.c +++ b/src/timestamp.c @@ -29,8 +29,6 @@ #include "options.h" #include "timestamp.h" -#define TIME_STRING_SIZE_MAX 24 - char *timestamp_current_time(void) { static char time_string[TIME_STRING_SIZE_MAX]; diff --git a/src/timestamp.h b/src/timestamp.h index 48e0a76..6a10d98 100644 --- a/src/timestamp.h +++ b/src/timestamp.h @@ -32,5 +32,7 @@ typedef enum TIMESTAMP_END, } timestamp_t; +#define TIME_STRING_SIZE_MAX 24 + char *timestamp_current_time(void); diff --git a/src/tty.c b/src/tty.c index 74f6ab3..0021396 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1615,6 +1615,7 @@ const char* get_serial_port_type(const char* port_name) const char* get_serial_port_type(const char* port_name) { + (void)port_name; return ""; } From 4511d74a9e5455c6a764cd491d4a0b7f15c11b2c Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 7 Nov 2024 22:17:26 +0100 Subject: [PATCH 099/146] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 4c95c20..e67ab85 100644 --- a/AUTHORS +++ b/AUTHORS @@ -60,5 +60,6 @@ Heinrich Schuchardt Tomka Gergely Steve Marple konosubakonoakua +Keith Hill Thanks to everyone who has contributed to this project. From c49faa7337bf30f28f51cf27f95e5415d2b477be Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 30 Nov 2024 11:03:40 +0100 Subject: [PATCH 100/146] Clean up lua API Rename modem_send() to send() Rename send to write() --- README.md | 32 ++++++++++++------- examples/lua/automatic-linux-login.lua | 4 +-- examples/lua/read.lua | 8 ++--- examples/lua/read_line.lua | 6 ++-- man/tio.1.in | 43 +++++++++++++------------- src/script.c | 6 ++-- 6 files changed, 55 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index fd93d9e..0f1a0aa 100644 --- a/README.md +++ b/README.md @@ -401,12 +401,30 @@ expect(string, timeout) On successful match it also returns the match string as second return value. -send(string) - Send string. +read(size, timeout) + Read from serial device. If timeout is 0 or not provided it will wait + forever until data is ready to read. + + Returns number of bytes read on success, 0 on timeout, or -1 on error. + + On success, returns read string as second return value. + +read_line(timeout) + Read line from serial device. If timeout is 0 or not provided it will + wait forever until data is ready to read. + + Returns number of bytes read on success, 0 on timeout, or -1 on error. + + On success, returns the string that was read as second return value. + Also emits a single timestamp to stdout and log file per options.timestamp + and options.log. + +write(string) + Write string to serial device. Returns number of bytes written on success or -1 on error. -modem_send(file, protocol) +send(file, protocol) Send file using x/y-modem protocol. Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM. @@ -421,14 +439,6 @@ tty_search() Returns nil if no serial devices are found. -read(size, timeout) - Read from serial device. If timeout is 0 or not provided it will wait - forever until data is ready to read. - - Returns number of bytes read on success, 0 on timeout, or -1 on error. - - On success, returns read string as second return value. - set{line=state, ...} Set state of one or multiple tty modem lines. diff --git a/examples/lua/automatic-linux-login.lua b/examples/lua/automatic-linux-login.lua index a287b40..428caa1 100644 --- a/examples/lua/automatic-linux-login.lua +++ b/examples/lua/automatic-linux-login.lua @@ -18,9 +18,9 @@ if (1 == found) then local hostname = string.match(match_str, "^%w+") local login = logins[hostname] if (nil ~= login) then - send(login.username .. "\n") + write(login.username .. "\n") expect("Password:") - send(login.password .. "\n") + write(login.password .. "\n") else io.write("\r\nDon't know login info for " .. hostname .. "\r\n") end diff --git a/examples/lua/read.lua b/examples/lua/read.lua index 2ee99db..6baa032 100644 --- a/examples/lua/read.lua +++ b/examples/lua/read.lua @@ -1,14 +1,14 @@ read(1000, 6000) -- initial config -send("\n") +write("\n") msleep(100) read(650, 60) -- main menu -send("S") -- S menu +write("S") -- S menu msleep(30) read(650, 60) -send("t") -- Parallel Value Table +write("t") -- Parallel Value Table read(650, 60) while true do msleep(1000) - send("t") + write("t") read(650, 50) -- repeat PVT forever end diff --git a/examples/lua/read_line.lua b/examples/lua/read_line.lua index 97f157d..ef6ec20 100644 --- a/examples/lua/read_line.lua +++ b/examples/lua/read_line.lua @@ -1,13 +1,13 @@ read(1000, 8000) -- read initial config -send("\n") +write("\n") read(650, 100) -- main menu -send("S") -- S menu +write("S") -- S menu n = 1 while n > 0 do -- while not empty, read more n, str = read_line(25) end while true do - send("t") -- query PV table + write("t") -- query PV table msleep(880) n = 1 while n > 0 do -- while not empty, read more diff --git a/man/tio.1.in b/man/tio.1.in index 4ad15ed..9d75c45 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -428,7 +428,8 @@ Send ctrl-t character .PP Tio suppots Lua scripting to easily automate interaction with the tty device. -In addition to the Lua API tio makes the following functions available: +In addition to the standard Lua API tio makes the following functions +available: .TP 6n @@ -441,26 +442,6 @@ Returns 1 on successful match, 0 on timeout, or -1 on error. On successful match it also returns the match string as second return value. -.IP "\fBsend(string)" -Send string. - -Returns number of bytes written on success or -1 on error. - -.IP "\fBmodem_send(file, protocol)" -Send file using x/y-modem protocol. - -Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM. - -.IP "\fBtty_search()" -Search for serial devices. - -Returns a table of number indexed tables, one for each serial device found. -Each of these tables contains the serial device information accessible via the -following string indexed elements "path", "tid", "uptime", "driver", -"description". - -Returns nil if no serial devices are found. - .IP "\fBread(size, timeout)" Read from serial device. If timeout is 0 or not provided it will wait forever until data is ready to read. @@ -480,6 +461,26 @@ On success, returns the string that was read as second return value. Also emits a single timestamp to stdout and log file per options.timestamp and options.log. +.IP "\fBwrite(string)" +Write string to serial device. + +Returns number of bytes written on success or -1 on error. + +.IP "\fBsend(file, protocol)" +Send file using x/y-modem protocol. + +Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM. + +.IP "\fBtty_search()" +Search for serial devices. + +Returns a table of number indexed tables, one for each serial device found. +Each of these tables contains the serial device information accessible via the +following string indexed elements "path", "tid", "uptime", "driver", +"description". + +Returns nil if no serial devices are found. + .IP "\fBset{line=state, ...}" Set state of one or multiple tty modem lines. diff --git a/src/script.c b/src/script.c index b565741..f6023eb 100644 --- a/src/script.c +++ b/src/script.c @@ -185,7 +185,7 @@ static int modem_send(lua_State *L) } // lua: send(string) -static int send_(lua_State *L) +static int write_(lua_State *L) { const char *string = lua_tostring(L, 1); int ret; @@ -550,8 +550,8 @@ static const struct luaL_Reg tio_lib[] = { "sleep", sleep_}, { "msleep", msleep}, { "line_set", line_set}, - { "modem_send", modem_send}, - { "send", send_}, + { "send", modem_send}, + { "write", write_}, { "read", read_string}, { "read_line", read_line}, { "expect", expect}, From b8135ea6395ef6f7f437ac00f3ad205dcca86740 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 30 Nov 2024 11:37:12 +0100 Subject: [PATCH 101/146] Rename git version to simply version --- src/git-version.h.in | 3 --- src/main.c | 4 ++-- src/meson.build | 12 ++++++------ src/options.c | 4 ++-- src/tty.c | 4 ++-- src/version.h.in | 3 +++ 6 files changed, 15 insertions(+), 15 deletions(-) delete mode 100644 src/git-version.h.in create mode 100644 src/version.h.in diff --git a/src/git-version.h.in b/src/git-version.h.in deleted file mode 100644 index a0d2080..0000000 --- a/src/git-version.h.in +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#define GIT_VERSION "@GIT_VERSION@" diff --git a/src/main.c b/src/main.c index a07ba67..1aba51b 100644 --- a/src/main.c +++ b/src/main.c @@ -22,7 +22,7 @@ #include #include #include -#include "git-version.h" +#include "version.h" #include "config.h" #include "options.h" #include "configfile.h" @@ -102,7 +102,7 @@ int main(int argc, char *argv[]) error_enter_session_mode(); /* Print launch hints */ - tio_printf("tio %s", GIT_VERSION); + tio_printf("tio %s", VERSION); if (interactive_mode) { tio_printf("Press ctrl-%c q to quit", option.prefix_key); diff --git a/src/meson.build b/src/meson.build index 17cc691..958d4e9 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,8 +1,8 @@ -# Generate git version header -git_version_h = vcs_tag(command : ['git', 'describe', '--tags', '--always', '--dirty'], - input : 'git-version.h.in', - output :'git-version.h', - replace_string:'@GIT_VERSION@') +# Generate version header +version_h = vcs_tag(command : ['git', 'describe', '--tags', '--always', '--dirty'], + input : 'version.h.in', + output :'version.h', + replace_string:'@VERSION@') config_h = configuration_data() config_h.set('BAUDRATE_CASES', baudrate_cases) @@ -27,7 +27,7 @@ tio_sources = [ 'script.c', 'fs.c', 'readline.c', - git_version_h + version_h ] diff --git a/src/options.c b/src/options.c index 04d5be6..e685690 100644 --- a/src/options.c +++ b/src/options.c @@ -25,7 +25,7 @@ #include #include #include -#include "git-version.h" +#include "version.h" #include "config.h" #include "misc.h" #include "print.h" @@ -1088,7 +1088,7 @@ void options_parse(int argc, char *argv[]) break; case 'v': - printf("tio %s\n", GIT_VERSION); + printf("tio %s\n", VERSION); exit(EXIT_SUCCESS); break; diff --git a/src/tty.c b/src/tty.c index 0021396..ef2f154 100644 --- a/src/tty.c +++ b/src/tty.c @@ -22,7 +22,7 @@ #if defined(__linux__) #include #endif -#include "git-version.h" +#include "version.h" #include "config.h" #include #include @@ -1091,7 +1091,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) break; case KEY_V: - tio_printf("tio %s", GIT_VERSION); + tio_printf("tio %s", VERSION); break; case KEY_X: diff --git a/src/version.h.in b/src/version.h.in new file mode 100644 index 0000000..459952d --- /dev/null +++ b/src/version.h.in @@ -0,0 +1,3 @@ +#pragma once + +#define VERSION "@VERSION@" From 1b2a0ea1305baa9c266c7da057a9539f0c22b88c Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 30 Nov 2024 12:12:09 +0100 Subject: [PATCH 102/146] Update version date --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 810f9dd..f0f2e4d 100644 --- a/meson.build +++ b/meson.build @@ -6,7 +6,7 @@ project('tio', 'c', ) # The tag date of the project_version(), update when the version bumps. -version_date = '2024-08-31' +version_date = '2024-11-30' # Test for dynamic baudrate configuration interface compiler = meson.get_compiler('c') From 01e637cdf4d2d781a87a2fa68e49e7f8fccd0552 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 30 Nov 2024 12:17:47 +0100 Subject: [PATCH 103/146] Update NEWS --- NEWS | 33 ++++++++++++++++++++++++++++++++- README.md | 2 +- man/tio.1.in | 2 +- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 8b2eae4..0a467da 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,36 @@ -=== tio v3.7 (2024-08-31) === +=== tio v3.8 (2024-11-30) === + + +Changes since tio v3.7 (2024-08-31): + + * Rename git version to simply version + + * Clean up lua API + + Rename modem_send() to send() + Rename send to write() + + * Zero initialize buffer in read_string() + + * Use version from git + + * Fix memory leak in base62_encode() + + * Fix name declaration conflict with socket send() + + * Add clang-format spec + +Keith Hill: + + + Add system timestamps to lua read() and new lua read_line() per global options + + Add missing timestamp-format epoch + + Update send_ to use fsync and tcdrain like normal tty_sync does + + Rework read_line to save partial line at timeout + + Simplified read_line to reduce cyclomatic complexity + + renamed example files read.lua and read_line.lua + + moved #define READ_LINE_SIZE to top of file + + renamed g_linebuf to linebuf, and moved it into read_line as a static variable diff --git a/README.md b/README.md index 0f1a0aa..9d639fe 100644 --- a/README.md +++ b/README.md @@ -359,7 +359,7 @@ color = 11 [svf2] device = /dev/ttyUSB0 baudrate = 9600 -script = expect("login: "); send("root\n"); expect("Password: "); send("root\n") +script = expect("login: "); write("root\n"); expect("Password: "); write("root\n") color = 12 [esp32] diff --git a/man/tio.1.in b/man/tio.1.in index 9d75c45..b27f299 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -722,7 +722,7 @@ expect -i $uart "prompt> " .TP It is also possible to use tio's own simpler expect/send script functionality to e.g. automate logins: -$ tio --script 'expect("login: "); send("root\\n"); expect("Password: "); send("root\\n")' /dev/ttyUSB0 +$ tio --script 'expect("login: "); write("root\\n"); expect("Password: "); write("root\\n")' /dev/ttyUSB0 .TP Redirect device I/O to network file socket for remote TTY sharing: From 27f8f2c4e6b554a64ba732421c25f7845b2ea902 Mon Sep 17 00:00:00 2001 From: Jakob Haufe Date: Sun, 1 Dec 2024 14:20:45 +0100 Subject: [PATCH 104/146] Manpage: Fix backslash encoding Literal backslash needs to be written as \e. --- man/tio.1.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/tio.1.in b/man/tio.1.in index b27f299..6cdfb7c 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -435,7 +435,7 @@ available: .IP "\fBexpect(string, timeout)" Expect string - waits for string to match or timeout before continuing. -Supports regular expressions. Special characters must be escaped with '\\\\'. +Supports regular expressions. Special characters must be escaped with '\e\e'. Timeout is in milliseconds, defaults to 0 meaning it will wait forever. Returns 1 on successful match, 0 on timeout, or -1 on error. From 37994b3cc51ae6e7ad3f0617c7d0149c3293572d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 25 Jan 2025 15:09:07 +0100 Subject: [PATCH 105/146] Add character mapping examples --- README.md | 5 +++++ examples/config/config | 5 +++++ man/tio.1.in | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/README.md b/README.md index 9d639fe..129e616 100644 --- a/README.md +++ b/README.md @@ -275,6 +275,11 @@ Redirect I/O to IPv4 network socket on port 4242: $ tio --socket inet:4242 /dev/ttyUSB0 ``` +Map NL to CR-NL on input from device and DEL to BS on output to device: +``` +$ tio --map INLCRNL,ODELBS /dev/ttyUSB0 +``` + Pipe data to the serial device: ``` $ cat data.bin | tio /dev/ttyUSB0 diff --git a/examples/config/config b/examples/config/config index 8cc9017..01e84dd 100644 --- a/examples/config/config +++ b/examples/config/config @@ -71,3 +71,8 @@ device = /dev/ttyUSB0 color = 14 script = set{DTR=high,RTS=low}; msleep(100); set{DTR=low,RTS=high}; msleep(100); set{RTS=low} script-run = always + +[buspirate] +device = /dev/ttyACM0 +map = INLCRNL,ODELBS +color = 15 diff --git a/man/tio.1.in b/man/tio.1.in index 6cdfb7c..19c36da 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -751,6 +751,11 @@ Likewise, to pipe data from file to the serial device: $ cat data.bin | tio /dev/serial/by\-id/usb\-FTDI_TTL232R-3V3_FTGQVXBL\-if00\-port0 +.TP +Map NL to CR-NL on input from device and DEL to BS on output to device: + +$ tio --map INLCRNL,ODELBS /dev/ttyUSB0 + .TP Enable RS-485 mode: From f389f116693afe126c9e604f2860c55072c4a610 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 26 Jan 2025 16:41:24 +0100 Subject: [PATCH 106/146] Update plaintext man page --- man/tio.1.txt | 52 ++++++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/man/tio.1.txt b/man/tio.1.txt index 6debf36..d8a277d 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -1,4 +1,4 @@ -tio(1) User Commands tio(1) +tio(1) User Commands tio(1) NAME tio - a serial device I/O tool @@ -218,7 +218,8 @@ OPTIONS Redirect I/O to socket. - Any input from clients connected to the socket is sent on the serial port as if entered at the terminal where tio is running (except that ctrl-t sequences are not recognized), and any input from the serial port is multiplexed to the terminal and all connected clients. + Any input from clients connected to the socket is sent on the serial port as if entered at the terminal where tio is running (except that ctrl-t sequences are not recognized), and any input from the serial port is multi‐ + plexed to the terminal and all connected clients. Sockets remain open while the serial port is disconnected, and writes will block. @@ -342,7 +343,7 @@ KEY COMMANDS SCRIPT API Tio suppots Lua scripting to easily automate interaction with the tty device. - In addition to the Lua API tio makes the following functions available: + In addition to the standard Lua API tio makes the following functions available: expect(string, timeout) Expect string - waits for string to match or timeout before continuing. Supports regular expressions. Special characters must be escaped with '\\'. Timeout is in milliseconds, defaults to 0 meaning it will wait forever. @@ -351,29 +352,12 @@ SCRIPT API On successful match it also returns the match string as second return value. - send(string) - Send string. - - Returns number of bytes written on success or -1 on error. - - modem_send(file, protocol) - Send file using x/y-modem protocol. - - Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM. - - tty_search() - Search for serial devices. - - Returns a table of number indexed tables, one for each serial device found. Each of these tables contains the serial device information accessible via the following string indexed elements "path", "tid", "uptime", "driver", "description". - - Returns nil if no serial devices are found. - read(size, timeout) Read from serial device. If timeout is 0 or not provided it will wait forever until data is ready to read. Returns number of bytes read on success, 0 on timeout, or -1 on error. - On success, returns read string as second return value. Also emits a single timestamp to stdout and log file per options.timestamp and options.log. + On success, returns read string as second return value. Also emits a single timestamp to stdout and log file per options.timestamp and options.log. read_line(timeout) Read line from serial device. If timeout is 0 or not provided it will wait forever until data is ready to read. @@ -382,6 +366,24 @@ SCRIPT API On success, returns the string that was read as second return value. Also emits a single timestamp to stdout and log file per options.timestamp and options.log. + write(string) + Write string to serial device. + + Returns number of bytes written on success or -1 on error. + + send(file, protocol) + Send file using x/y-modem protocol. + + Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM. + + tty_search() + Search for serial devices. + + Returns a table of number indexed tables, one for each serial device found. Each of these tables contains the serial device information accessible via the following string indexed elements "path", "tid", "uptime", "dri‐ + ver", "description". + + Returns nil if no serial devices are found. + set{line=state, ...} Set state of one or multiple tty modem lines. @@ -572,7 +574,7 @@ EXAMPLES It is also possible to use tio's own simpler expect/send script functionality to e.g. automate logins: - $ tio --script 'expect("login: "); send("root\n"); expect("Password: "); send("root\n")' /dev/ttyUSB0 + $ tio --script 'expect("login: "); write("root\n"); expect("Password: "); write("root\n")' /dev/ttyUSB0 Redirect device I/O to network file socket for remote TTY sharing: @@ -594,6 +596,10 @@ EXAMPLES $ cat data.bin | tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 + Map NL to CR-NL on input from device and DEL to BS on output to device: + + $ tio --map INLCRNL,ODELBS /dev/ttyUSB0 + Enable RS-485 mode: $ tio --rs-485 --rs-485-config=RTS_ON_SEND=1,RX_DURING_TX /dev/ttyUSB0 @@ -608,4 +614,4 @@ WEBSITE AUTHOR Maintained by Martin Lund . -tio 3.7 2024-08-31 tio(1) +tio 3.8 2024-11-30 tio(1) From 8f7bf2fd2cb2cedf5016b4d4df003c50ef7da9d1 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 8 Feb 2025 02:57:48 +0100 Subject: [PATCH 107/146] codeql: Upgrade to upload-artifact@v4 --- .github/workflows/codeql.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 3ef873f..b96d489 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -114,7 +114,7 @@ jobs: - name: Upload CodeQL results as an artifact if: success() || failure() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: codeql-results path: ${{ steps.step1.outputs.sarif-output }} From 6831ad0eae31fc60f53e3ca9cf7bcf2b3ad8354f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 15 Feb 2025 19:50:52 +0100 Subject: [PATCH 108/146] Fix parsing of timestamp options --- src/configfile.c | 21 ++++++++++----------- src/options.c | 5 ++++- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/configfile.c b/src/configfile.c index 07da79a..7b21bc7 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -31,6 +31,7 @@ #include #include #include "configfile.h" +#include "timestamp.h" #include "print.h" #include "rs485.h" #include "misc.h" @@ -148,7 +149,6 @@ static void config_get_bool(GKeyFile *key_file, gchar *group, gchar *key, bool * static void config_parse_keys(GKeyFile *key_file, char *group) { char *string = NULL; - bool boolean = false; config_get_string(key_file, group, "device", &config.device, NULL); config_get_integer(key_file, group, "baudrate", &option.baudrate, 0, INT_MAX); @@ -204,17 +204,16 @@ static void config_parse_keys(GKeyFile *key_file, char *group) g_free((void *)string); string = NULL; } - config_get_bool(key_file, group, "timestamp", &boolean); - if (boolean == true) + config_get_bool(key_file, group, "timestamp", (bool*) &option.timestamp); + if (option.timestamp != TIMESTAMP_NONE) { - option.timestamp = TIMESTAMP_24HOUR; - } - config_get_string(key_file, group, "timestamp-format", &string, "24hour", "24hour-start", "24hour-delta", "iso8601", "epoch", NULL); - if (string != NULL) - { - option_parse_timestamp(string, &option.timestamp); - g_free((void *)string); - string = NULL; + config_get_string(key_file, group, "timestamp-format", &string, "24hour", "24hour-start", "24hour-delta", "iso8601", "epoch", NULL); + if (string != NULL) + { + option_parse_timestamp(string, &option.timestamp); + g_free((void *)string); + string = NULL; + } } config_get_integer(key_file, group, "timestamp-timeout", &option.timestamp_timeout, 0, INT_MAX); config_get_bool(key_file, group, "log", &option.log); diff --git a/src/options.c b/src/options.c index e685690..40bcc23 100644 --- a/src/options.c +++ b/src/options.c @@ -998,7 +998,10 @@ void options_parse(int argc, char *argv[]) break; case 't': - option.timestamp = TIMESTAMP_24HOUR; + if (option.timestamp == TIMESTAMP_NONE) + { + option.timestamp = TIMESTAMP_24HOUR; + } break; case OPT_TIMESTAMP_FORMAT: From 8e155c9276dc155a85b56d7d3bba5d0482d59bc0 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 23 Feb 2025 16:26:28 +0100 Subject: [PATCH 109/146] Update TODO --- TODO | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/TODO b/TODO index 7dce847..dfec023 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,13 @@ + * Add option to send file raw (no modem protocol) + + * Add loopback option + + Send received serial input back to output (for testing etc.) + + * Add loopback support between two serial ports + + Useful for traffic monitoring + * Add mapping feature for printing non-printable characters * Porting layer to support native win32 builds. From 795ef28f79f5a9456848af3ce3e5567bb7563e0f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 25 Feb 2025 16:16:24 +0100 Subject: [PATCH 110/146] Update TODO --- TODO | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO b/TODO index dfec023..234be9b 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,5 @@ + * Support input and input mapping from lua scripts + * Add option to send file raw (no modem protocol) * Add loopback option From 1b60dd1ae768251660e5abad56ea5f71c457db94 Mon Sep 17 00:00:00 2001 From: Lubov66 Date: Mon, 10 Mar 2025 09:47:34 +0200 Subject: [PATCH 111/146] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 129e616..f12b9d7 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # tio - a serial device I/O tool -[![](https://img.shields.io/github/actions/workflow/status/tio/tio/ubuntu.yml?label=GNU%2FLinux)](https://github.com/tio/tio/actions/workflows/ubuntu.yml) +[![](https://img.shields.io/github/actions/workflow/status/tio/tio/ubuntu.yml?label=Ubuntu)](https://github.com/tio/tio/actions/workflows/ubuntu.yml) [![](https://img.shields.io/github/actions/workflow/status/tio/tio/macos.yml?label=MacOS)](https://github.com/tio/tio/actions/workflows/macos.yml) [![](https://img.shields.io/codefactor/grade/github/tio/tio)](https://www.codefactor.io/repository/github/tio/tio) [![](https://img.shields.io/github/v/release/tio/tio?sort=semver)](https://github.com/tio/tio/releases) From d163afc6b164f173e3ee93090d3fe6448bdb1adc Mon Sep 17 00:00:00 2001 From: Lubov66 Date: Mon, 10 Mar 2025 12:15:53 +0200 Subject: [PATCH 112/146] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f12b9d7..f2c2806 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![](https://img.shields.io/github/actions/workflow/status/tio/tio/ubuntu.yml?label=Ubuntu)](https://github.com/tio/tio/actions/workflows/ubuntu.yml) [![](https://img.shields.io/github/actions/workflow/status/tio/tio/macos.yml?label=MacOS)](https://github.com/tio/tio/actions/workflows/macos.yml) +[![](https://github.com/tio/tio/actions/workflows/codeql.yml/badge.svg)](https://github.com/tio/tio/actions/workflows/codeql.yml) [![](https://img.shields.io/codefactor/grade/github/tio/tio)](https://www.codefactor.io/repository/github/tio/tio) [![](https://img.shields.io/github/v/release/tio/tio?sort=semver)](https://github.com/tio/tio/releases) [![](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions) From f5740dbf3150bf292d77a450b9945d7870489192 Mon Sep 17 00:00:00 2001 From: Lubov66 Date: Mon, 10 Mar 2025 16:32:21 +0200 Subject: [PATCH 113/146] docs: edited the license date --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index a793eef..d5fa60d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2022 Martin Lund +Copyright (c) 2014-2025 Martin Lund This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License From 6aca9ffee50d5195d9a4c85da9747f99cd990221 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 10 Mar 2025 16:17:50 +0100 Subject: [PATCH 114/146] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index e67ab85..4d35e4c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -61,5 +61,6 @@ Tomka Gergely Steve Marple konosubakonoakua Keith Hill +Lubov66 Thanks to everyone who has contributed to this project. From 7567e08227ace6992d5c98d1936fd2c119f4b49b Mon Sep 17 00:00:00 2001 From: V Date: Tue, 11 Mar 2025 13:59:45 +0000 Subject: [PATCH 115/146] Disable stdout buffering globally This makes it possible to pipe output to other programs cleanly. --- src/main.c | 4 ++++ src/tty.c | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main.c b/src/main.c index 1aba51b..6676bb3 100644 --- a/src/main.c +++ b/src/main.c @@ -61,6 +61,10 @@ int main(int argc, char *argv[]) /* Configure tty device */ tty_configure(); + /* Disable line buffering in stdout. This is necessary if we + * want things like local echo to work correctly. */ + setvbuf(stdout, NULL, _IONBF, 0); + /* Configure input terminal */ if (isatty(fileno(stdin))) { diff --git a/src/tty.c b/src/tty.c index ef2f154..c72fd67 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1184,10 +1184,6 @@ void stdout_configure(void) { int status; - /* Disable line buffering in stdout. This is necessary if we - * want things like local echo to work correctly. */ - setvbuf(stdout, NULL, _IONBF, 0); - /* Save current stdout settings */ if (tcgetattr(STDOUT_FILENO, &stdout_old) < 0) { From f716d2ccdd461115973c7ca8ab579067152bb41f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 13 Mar 2025 15:44:12 +0100 Subject: [PATCH 116/146] Update TODO --- TODO | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO b/TODO index 234be9b..6036c7b 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,5 @@ + * Add release support for arm and x86 binary tarballs + * Support input and input mapping from lua scripts * Add option to send file raw (no modem protocol) From da4074c9a5d252dabf653a377429cb85d9137775 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 22 Mar 2025 21:35:47 -0500 Subject: [PATCH 117/146] Don't add null characters to the expect buffer They prevent regexec() from seeing the remainder of the buffer. --- src/script.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/script.c b/src/script.c index f6023eb..b471932 100644 --- a/src/script.c +++ b/src/script.c @@ -207,6 +207,11 @@ static int write_(lua_State *L) // Function to add a character to the circular expect buffer static void expect_buffer_add(char c) { + if (!c) + { + return; + } + if (buffer_size < MAX_BUFFER_SIZE) { circular_buffer[buffer_size++] = c; From d33e275ca3ba5c40435fcbf6c6fb9562c073e498 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 23 Mar 2025 07:04:47 +0100 Subject: [PATCH 118/146] Update AUTHORS --- AUTHORS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AUTHORS b/AUTHORS index 4d35e4c..21f2bad 100644 --- a/AUTHORS +++ b/AUTHORS @@ -62,5 +62,7 @@ Steve Marple konosubakonoakua Keith Hill Lubov66 +V +Samuel Holland Thanks to everyone who has contributed to this project. From 16b7aee42f82ead0ae99f36fde905bc7ed200680 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 12 Apr 2025 08:54:50 +0200 Subject: [PATCH 119/146] Update NEWS --- NEWS | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 0a467da..cc83cde 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,39 @@ +=== tio v3.9 (2025-04-12) === + + + +Changes since tio v3.8 (2024-11-30): + + * Fix parsing of timestamp options + + * codeql: Upgrade to upload-artifact@v4 + + * Update plaintext man page + + * Add character mapping examples + +Samuel Holland: + + * Don't add null characters to the expect buffer + + They prevent regexec() from seeing the remainder of the buffer. + +V: + + * Disable stdout buffering globally + + This makes it possible to pipe output to other programs cleanly. + +Lubov66: + + * docs: edited the license date + +Jakob Haufe: + + Manpage: Fix backslash encoding + + Literal backslash needs to be written as \e. -=== tio v3.8 (2024-11-30) === Changes since tio v3.7 (2024-08-31): From ebce2d4ee9733b76173c4e1692c5caa5e9440d3e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 12 Apr 2025 08:55:11 +0200 Subject: [PATCH 120/146] Bump version --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index f0f2e4d..859eec8 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('tio', 'c', - version : '3.8', + version : '3.9', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] From 600c3d7563cf8719622bec7eeff968766f387d7f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 12 Apr 2025 08:55:40 +0200 Subject: [PATCH 121/146] Update version date --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 859eec8..a61dee9 100644 --- a/meson.build +++ b/meson.build @@ -6,7 +6,7 @@ project('tio', 'c', ) # The tag date of the project_version(), update when the version bumps. -version_date = '2024-11-30' +version_date = '2025-04-12' # Test for dynamic baudrate configuration interface compiler = meson.get_compiler('c') From b33045189fc3ee7b1940ba32d623d4fcb13624af Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 12 Apr 2025 08:57:25 +0200 Subject: [PATCH 122/146] Update plain text man page --- man/tio.1.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/tio.1.txt b/man/tio.1.txt index d8a277d..7d78859 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -614,4 +614,4 @@ WEBSITE AUTHOR Maintained by Martin Lund . -tio 3.8 2024-11-30 tio(1) +tio 3.9 2025-04-12 tio(1) From 013aebcc055f09f9cde4e6a05bc6c877b7642607 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 12 Apr 2025 09:02:22 +0200 Subject: [PATCH 123/146] Update NEWS --- NEWS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index cc83cde..52e93cc 100644 --- a/NEWS +++ b/NEWS @@ -30,9 +30,9 @@ Lubov66: Jakob Haufe: - Manpage: Fix backslash encoding + * Manpage: Fix backslash encoding - Literal backslash needs to be written as \e. + Literal backslash needs to be written as \e. From 2e8671897387f25a83e56cdb87a6e97cf7cb80e1 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 13 Apr 2025 08:28:01 +0200 Subject: [PATCH 124/146] Add typos.toml --- .typos.toml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .typos.toml diff --git a/.typos.toml b/.typos.toml new file mode 100644 index 0000000..11f019f --- /dev/null +++ b/.typos.toml @@ -0,0 +1,2 @@ +[default] +extend-ignore-words-re = ["tio"] From f87f470415ad4e257f8ed8d8e2673baf5b9dbecb Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 13 Apr 2025 13:25:25 +0200 Subject: [PATCH 125/146] Fix pattern matching memory corruption --- src/configfile.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/configfile.c b/src/configfile.c index 7b21bc7..6330151 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -428,12 +428,13 @@ static char *match_and_replace(const char *str, const char *pattern, char *devic assert(pattern != NULL); assert(device != NULL); - char *string = strndup(device, PATH_MAX); + char *string = calloc(PATH_MAX, 1); if (string == NULL) { tio_debug_printf("Failure allocating string memory\n"); return NULL; } + strncpy(string, device, PATH_MAX - 1); /* Find matches of pattern in str. For each match, replace any '%mN' in the * copy of the device string with the corresponding match subexpression and From 5c2ced1093c0ec7f058f182e94af47661ea34983 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 13 Apr 2025 13:27:50 +0200 Subject: [PATCH 126/146] Update NEWS --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 52e93cc..5b52e0d 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,8 @@ Changes since tio v3.8 (2024-11-30): * Add character mapping examples + * Fix pattern matching memory corruption + Samuel Holland: * Don't add null characters to the expect buffer From bdfe87e1cbf6e3bfd48324a25ea026fcd3cc47e9 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 13 Apr 2025 13:31:06 +0200 Subject: [PATCH 127/146] Update date --- NEWS | 2 +- man/tio.1.txt | 2 +- meson.build | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 5b52e0d..4289610 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -=== tio v3.9 (2025-04-12) === +=== tio v3.9 (2025-04-13) === diff --git a/man/tio.1.txt b/man/tio.1.txt index 7d78859..bf44f9d 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -614,4 +614,4 @@ WEBSITE AUTHOR Maintained by Martin Lund . -tio 3.9 2025-04-12 tio(1) +tio 3.9 2025-04-13 tio(1) diff --git a/meson.build b/meson.build index a61dee9..05f77ce 100644 --- a/meson.build +++ b/meson.build @@ -6,7 +6,7 @@ project('tio', 'c', ) # The tag date of the project_version(), update when the version bumps. -version_date = '2025-04-12' +version_date = '2025-04-13' # Test for dynamic baudrate configuration interface compiler = meson.get_compiler('c') From d682e98a66161a58b19d40092e90ec5871c3ad91 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 16 Apr 2025 10:47:39 +0200 Subject: [PATCH 128/146] codeql: Build using ubuntu-22.04 --- .github/workflows/codeql.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index b96d489..1667c7e 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -27,7 +27,7 @@ jobs: # - https://gh.io/supported-runners-and-hardware-resources # - https://gh.io/using-larger-runners # Consider using larger runners for possible analysis time improvements. - runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-20.04' }} + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-22.04' }} timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} permissions: actions: read From c736b1e35302c7242e01c16dd77572eecadbe6d1 Mon Sep 17 00:00:00 2001 From: David Ordnung Date: Wed, 23 Apr 2025 01:05:12 +0200 Subject: [PATCH 129/146] Input ICRCRNL mapping to avoid using INLCRNL with ICRNL --- man/tio.1.in | 2 ++ man/tio.1.txt | 2 ++ src/bash-completion/tio.in | 2 +- src/options.c | 5 ++++ src/options.h | 1 + src/tty.c | 53 +++++++++++++++++++++++++------------- 6 files changed, 46 insertions(+), 19 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 19c36da..50f6ae8 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -218,6 +218,8 @@ Map FF to ESC-c on input Map NL to CR on input .IP "\fBINLCRNL" Map NL to CR-NL on input +.IP "\fBICRCRNL" +Map CR to CR-NL on input .IP "\fBIMSB2LSB" Map MSB bit order to LSB on input .IP "\fBOCRNL" diff --git a/man/tio.1.txt b/man/tio.1.txt index bf44f9d..c37a38c 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -168,6 +168,8 @@ OPTIONS INLCRNL Map NL to CR-NL on input + ICRCRNL Map CR to CR-NL on input + IMSB2LSB Map MSB bit order to LSB on input OCRNL Map CR to NL on output diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index d71aceb..daac432 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -85,7 +85,7 @@ _tio() return 0 ;; -m | --map) - COMPREPLY=( $(compgen -W "ICRNL IGNCR INLCR IFFESCC INLCRNL IMSB2LSB OCRNL ODELBS ONLCRNL OLTU ONULBRK OIGNCR" -- ${cur}) ) + COMPREPLY=( $(compgen -W "ICRNL IGNCR INLCR IFFESCC INLCRNL ICRCRNL IMSB2LSB OCRNL ODELBS ONLCRNL OLTU ONULBRK OIGNCR" -- ${cur}) ) return 0 ;; --timestamp-format) diff --git a/src/options.c b/src/options.c index 40bcc23..3bdbfef 100644 --- a/src/options.c +++ b/src/options.c @@ -116,6 +116,7 @@ struct option_t option = .map_ign_cr = false, .map_i_ff_escc = false, .map_i_nl_crnl = false, + .map_i_cr_crnl = false, .map_o_cr_nl = false, .map_o_nl_crnl = false, .map_o_del_bs = false, @@ -770,6 +771,10 @@ void option_parse_mappings(const char *map) { option.map_i_nl_crnl = true; } + else if (strcmp(token,"ICRCRNL") == 0) + { + option.map_i_cr_crnl = true; + } else if (strcmp(token, "ONLCRNL") == 0) { option.map_o_nl_crnl = true; diff --git a/src/options.h b/src/options.h index dadf133..c552217 100644 --- a/src/options.h +++ b/src/options.h @@ -98,6 +98,7 @@ struct option_t bool map_ign_cr; bool map_i_ff_escc; bool map_i_nl_crnl; + bool map_i_cr_crnl; bool map_o_cr_nl; bool map_o_nl_crnl; bool map_o_del_bs; diff --git a/src/tty.c b/src/tty.c index c72fd67..a2c917b 100644 --- a/src/tty.c +++ b/src/tty.c @@ -623,16 +623,18 @@ void tty_output_mode_set(output_mode_t mode) static void mappings_print(void) { if (option.map_i_cr_nl || option.map_ign_cr || option.map_i_ff_escc || - option.map_i_nl_cr || option.map_i_nl_crnl || option.map_o_cr_nl || - option.map_o_del_bs || option.map_o_nl_crnl || option.map_o_ltu || - option.map_o_nulbrk || option.map_i_msb2lsb || option.map_o_ign_cr) + option.map_i_nl_cr || option.map_i_nl_crnl || option.map_i_cr_crnl || + option.map_o_cr_nl || option.map_o_del_bs || option.map_o_nl_crnl || + option.map_o_ltu || option.map_o_nulbrk || option.map_i_msb2lsb || + option.map_o_ign_cr) { - tio_printf(" Mappings:%s%s%s%s%s%s%s%s%s%s%s%s", + tio_printf(" Mappings:%s%s%s%s%s%s%s%s%s%s%s%s%s", option.map_i_cr_nl ? " ICRNL" : "", option.map_ign_cr ? " IGNCR" : "", option.map_i_ff_escc ? " IFFESCC" : "", option.map_i_nl_cr ? " INLCR" : "", option.map_i_nl_crnl ? " INLCRNL" : "", + option.map_i_cr_crnl ? " ICRCRNL" : "", option.map_i_msb2lsb ? " IMSB2LSB" : "", option.map_o_cr_nl ? " OCRNL" : "", option.map_o_del_bs ? " ODELBS" : "", @@ -783,30 +785,34 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf("INLCRNL is %s", option.map_i_nl_crnl ? "set" : "unset"); break; case KEY_5: + option.map_i_cr_crnl = !option.map_i_cr_crnl; + tio_printf("ICRCRNL is %s", option.map_i_cr_crnl ? "set" : "unset"); + break; + case KEY_6: option.map_i_msb2lsb = !option.map_i_msb2lsb; tio_printf("IMSB2LSB is %s", option.map_i_msb2lsb ? "set" : "unset"); break; - case KEY_6: + case KEY_7: option.map_o_cr_nl = !option.map_o_cr_nl; tio_printf("OCRNL is %s", option.map_o_cr_nl ? "set" : "unset"); break; - case KEY_7: + case KEY_8: option.map_o_del_bs = !option.map_o_del_bs; tio_printf("ODELBS is %s", option.map_o_del_bs ? "set" : "unset"); break; - case KEY_8: + case KEY_9: option.map_o_nl_crnl = !option.map_o_nl_crnl; tio_printf("ONLCRNL is %s", option.map_o_nl_crnl ? "set" : "unset"); break; - case KEY_9: + case KEY_A: option.map_o_ltu = !option.map_o_ltu; tio_printf("OLTU is %s", option.map_o_ltu ? "set" : "unset"); break; - case KEY_A: + case KEY_B: option.map_o_nulbrk = !option.map_o_nulbrk; tio_printf("ONULBRK is %s", option.map_o_nulbrk ? "set" : "unset"); break; - case KEY_B: + case KEY_C: option.map_o_ign_cr = !option.map_o_ign_cr; tio_printf("OIGNCR is %s", option.map_o_ign_cr ? "set" : "unset"); break; @@ -1007,20 +1013,22 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf(" (3) INLCR: %s mapping NL to CR on input", option.map_i_nl_cr ? "Unset" : "Set"); tio_printf(" (4) INLCRNL: %s mapping NL to CR-NL on input", - option.map_i_nl_cr ? "Unset" : "Set"); - tio_printf(" (5) IMSB2LSB: %s mapping MSB bit order to LSB on input", + option.map_i_nl_crnl ? "Unset" : "Set"); + tio_printf(" (5) ICRCRNL: %s mapping CR to CR-NL on input", + option.map_i_cr_crnl ? "Unset" : "Set"); + tio_printf(" (6) IMSB2LSB: %s mapping MSB bit order to LSB on input", option.map_i_msb2lsb ? "Unset" : "Set"); - tio_printf(" (6) OCRNL: %s mapping CR to NL on output", + tio_printf(" (7) OCRNL: %s mapping CR to NL on output", option.map_o_cr_nl ? "Unset" : "Set"); - tio_printf(" (7) ODELBS: %s mapping DEL to BS on output", + tio_printf(" (8) ODELBS: %s mapping DEL to BS on output", option.map_o_del_bs ? "Unset" : "Set"); - tio_printf(" (8) ONLCRNL: %s mapping NL to CR-NL on output", + tio_printf(" (9) ONLCRNL: %s mapping NL to CR-NL on output", option.map_o_nl_crnl ? "Unset" : "Set"); - tio_printf(" (9) OLTU: %s mapping lowercase to uppercase on output", + tio_printf(" (a) OLTU: %s mapping lowercase to uppercase on output", option.map_o_ltu ? "Unset" : "Set"); - tio_printf(" (a) ONULBRK: %s mapping NUL to send break signal on output", + tio_printf(" (b) ONULBRK: %s mapping NUL to send break signal on output", option.map_o_nulbrk ? "Unset" : "Set"); - tio_printf(" (b) OIGNCR: %s ignoring CR on output", + tio_printf(" (c) OIGNCR: %s ignoring CR on output", option.map_o_ign_cr ? "Unset" : "Set"); // Process next input character as sub command @@ -2584,6 +2592,15 @@ int tty_connect(void) do_timestamp = true; } } + else if ((input_char == '\r') && (option.map_i_cr_crnl) && (!option.map_i_msb2lsb)) + { + printchar('\r'); + printchar('\n'); + if (option.timestamp) + { + do_timestamp = true; + } + } else if ((input_char == '\f') && (option.map_i_ff_escc) && (!option.map_i_msb2lsb)) { printchar('\e'); From 437881f0ed220e5a44153c75ef1993a04c0c9205 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 23 Apr 2025 08:15:37 +0200 Subject: [PATCH 130/146] Update AUTHORS --- AUTHORS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AUTHORS b/AUTHORS index 21f2bad..3754640 100644 --- a/AUTHORS +++ b/AUTHORS @@ -64,5 +64,7 @@ Keith Hill Lubov66 V Samuel Holland +David Ordnung + Thanks to everyone who has contributed to this project. From 03ef931fb2394f74ce2598fa112aced09cc9342a Mon Sep 17 00:00:00 2001 From: Robert Lipe Date: Thu, 24 Apr 2025 06:49:08 -0500 Subject: [PATCH 131/146] - Implemented getPropertyString(), getDeviceLocation(), tty_search_for_serial_devices() for MacOS - Added error handling and memory management - Improved code readability and consistency - Updated coding style to match project conventions - Added robust error checking for CoreFoundation property retrieval - Implemented more defensive memory allocation and type checking - Switched to using callout device key for more reliable device discovery - Added single-line block bracing consistent with project style - Improved comments and code formatting - Used `kIOCalloutDeviceKey` instead of `kIODialinDeviceKey` for device path retrieval - Enhanced type checking for CoreFoundation objects - Simplified memory management and error handling - Added additional logging and error reporting - Verified functionality on MacOS 10.11 and 10.15. Tested with ESP32-P4 and ESP32-BOX Resolves potential device discovery and memory management issues in the MacOS serial device detection code. --- src/tty.c | 269 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 265 insertions(+), 4 deletions(-) diff --git a/src/tty.c b/src/tty.c index a2c917b..6de4af7 100644 --- a/src/tty.c +++ b/src/tty.c @@ -22,6 +22,15 @@ #if defined(__linux__) #include #endif + +#if defined(__APPLE__) || defined(__MACH__) +#include +#include +#include +#include +#include +#endif + #include "version.h" #include "config.h" #include @@ -623,9 +632,9 @@ void tty_output_mode_set(output_mode_t mode) static void mappings_print(void) { if (option.map_i_cr_nl || option.map_ign_cr || option.map_i_ff_escc || - option.map_i_nl_cr || option.map_i_nl_crnl || option.map_i_cr_crnl || - option.map_o_cr_nl || option.map_o_del_bs || option.map_o_nl_crnl || - option.map_o_ltu || option.map_o_nulbrk || option.map_i_msb2lsb || + option.map_i_nl_cr || option.map_i_nl_crnl || option.map_i_cr_crnl || + option.map_o_cr_nl || option.map_o_del_bs || option.map_o_nl_crnl || + option.map_o_ltu || option.map_o_nulbrk || option.map_i_msb2lsb || option.map_o_ign_cr) { tio_printf(" Mappings:%s%s%s%s%s%s%s%s%s%s%s%s%s", @@ -1841,6 +1850,246 @@ GList *tty_search_for_serial_devices(void) return device_list; } +#elif defined(__APPLE__) || defined(__MACH__) + +char *getPropertyString(io_object_t device, CFStringRef property) +{ + /* Validate inputs */ + if (device == IO_OBJECT_NULL || property == NULL) + { + return NULL; + } + + /* Attempt to get property */ + CFTypeRef valueRef = IORegistryEntryCreateCFProperty( + device, property, kCFAllocatorDefault, 0); + if (!valueRef) + { + return NULL; + } + + /* Ensure it's a CFString */ + if (CFGetTypeID(valueRef) != CFStringGetTypeID()) + { + CFRelease(valueRef); + return NULL; + } + + /* Convert to C string */ + CFIndex length = CFStringGetLength(valueRef); + CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; + char *result = malloc(maxSize); + + if (!result) + { + CFRelease(valueRef); + return NULL; + } + + bool converted = CFStringGetCString( + (CFStringRef)valueRef, + result, + maxSize, + kCFStringEncodingUTF8 + ); + + CFRelease(valueRef); + + if (!converted) + { + free(result); + return NULL; + } + + return result; +} + +char *getDeviceLocation(io_object_t device) +{ + /* Validate device */ + if (device == IO_OBJECT_NULL) + { + return strdup("Invalid Device"); + } + + /* Attempt to get location */ + io_string_t location = {0}; + kern_return_t result = IORegistryEntryGetLocationInPlane( + device, kIOServicePlane, location); + + if (result != KERN_SUCCESS) + { + return strdup("Unknown Location"); + } + + /* Safely copy location */ + size_t len = strnlen(location, sizeof(io_string_t)); + char *trimmed_location = calloc(1, len + 1); + + if (!trimmed_location) + { + return strdup("Memory Error"); + } + + memcpy(trimmed_location, location, len); + return trimmed_location; +} + +// for __APPLE__ +GList *tty_search_for_serial_devices(void) +{ + GList *device_list = NULL; + io_iterator_t iter = IO_OBJECT_NULL; + CFMutableDictionaryRef matchingDict = NULL; + listing_device_name_length_max = 0; + + /* Create matching dictionary for serial devices */ + if (!(matchingDict = IOServiceMatching(kIOSerialBSDServiceValue))) + { + tio_error_print("Failed to create matching dictionary for serial devices"); + return NULL; + } + + /* Get matching services */ + kern_return_t kernResult = IOServiceGetMatchingServices( + kIOMainPortDefault, matchingDict, &iter); + matchingDict = NULL; /* Dictionary ownership transferred */ + + if (kernResult != KERN_SUCCESS) + { + tio_error_print("IOServiceGetMatchingServices failed: %d", kernResult); + return NULL; + } + + /* Defensive check for iterator */ + if (iter == IO_OBJECT_NULL) + { + tio_error_print("Invalid device iterator"); + return NULL; + } + + /* Iterate through serial devices and collect information */ + for (io_object_t device; (device = IOIteratorNext(iter));) + { + char *devicePath = NULL, *locationID = NULL; + char *productName = NULL, *vendorName = NULL; + char tid[5] = {0}; + double uptime = 0.0; + + /* Get device path - key determines if we get tty. or cu. */ + //if (!(devicePath = getPropertyString(device, CFSTR(kIODialinDeviceKey)))) + if (!(devicePath = getPropertyString(device, CFSTR(kIOCalloutDeviceKey)))) + { + IOObjectRelease(device); + continue; /* Skip devices without a path */ + } + + /* Update length of longest device name string */ + listing_device_name_length_max = + strlen(devicePath) > listing_device_name_length_max + ? strlen(devicePath) + : listing_device_name_length_max; + + /* Calculate uptime */ + uptime = get_current_time() - fs_get_creation_time(devicePath); + + /* Find USB device (if applicable) */ + io_object_t usbDevice = IO_OBJECT_NULL; + kern_return_t usbResult = IORegistryEntryGetParentEntry( + device, kIOServicePlane, &usbDevice); + + /* Traverse up the device tree to find a USB device */ + while (usbResult == KERN_SUCCESS && + !IOObjectConformsTo(usbDevice, "IOUSBDevice")) + { + io_object_t oldUsbDevice = usbDevice; + usbResult = IORegistryEntryGetParentEntry( + usbDevice, kIOServicePlane, &usbDevice); + IOObjectRelease(oldUsbDevice); + } + + /* If we found a USB device */ + if (usbResult == KERN_SUCCESS) + { + locationID = getDeviceLocation(usbDevice); + + unsigned long hash2 = djb2_hash((const unsigned char *)(locationID ?: "")); + base62_encode(hash2, tid); + + /* Get product and vendor names */ + productName = getPropertyString(usbDevice, CFSTR("USB Product Name")); + vendorName = getPropertyString(usbDevice, CFSTR("USB Vendor Name")); + + IOObjectRelease(usbDevice); + } + + /* Create device structure */ + device_t *device_info = g_new0(device_t, 1); + if (!device_info) + { + tio_error_print("Memory allocation failed for device_info"); + free(devicePath); + free(locationID); + free(productName); + free(vendorName); + IOObjectRelease(device); + continue; + } + + /* Populate device info */ + *device_info = (device_t) { + .path = devicePath, + .tid = g_strdup(tid), + .uptime = uptime, + .driver = g_strdup(vendorName), + .description = g_strdup(productName ?: vendorName ?: "") + }; + + /* Add to device list */ + device_list = g_list_append(device_list, device_info); + + /* Clean up */ + free(locationID); + free(productName); + free(vendorName); + IOObjectRelease(device); + } + + /* Clean up iterator */ + IOObjectRelease(iter); + + /* Check if device list is empty */ + if (!device_list) + { + tio_error_print("No serial devices found"); + return NULL; + } + + /* Sort device list by uptime */ + device_list = g_list_sort(device_list, compare_uptime); + + /* Print header for device listing */ + print_padded("Device", listing_device_name_length_max, ' '); + printf(" TID Uptime [s] Driver Description\n"); + print_padded("", listing_device_name_length_max, '-'); + printf(" ---- -------------- ---------------- --------------------------\n"); + + /* Print sorted device list */ + for (GList *l = device_list; l; l = l->next) + { + device_t *dev = l->data; + printf("%-*s %-4s %14.3f %-16s %s\n", + (int)listing_device_name_length_max, dev->path, + dev->tid ?: "", + dev->uptime, + dev->driver ?: "", + dev->description ?: ""); + } + printf("\n"); + + return device_list; +} + #else GList *tty_search_for_serial_devices(void) @@ -2121,8 +2370,21 @@ void tty_wait_for_device(void) } else if (status == -1) { +#if defined(__CYGWIN__) + // Happens when port unpluged + if (errno == EACCES) + { + goto error; + } +#elif defined(__APPLE__) + if (errno == EBADF) + { + break; // tty_disconnect() will be naturally triggered by atexit() + } +#else tio_error_printf("select() failed (%s)", strerror(errno)); exit(EXIT_FAILURE); +#endif } } @@ -2761,4 +3023,3 @@ error_read: error_open: return TIO_ERROR; } - From 7516dff8023c9242ef706ffc40d9f5d45113ed3f Mon Sep 17 00:00:00 2001 From: Robert Lipe Date: Thu, 24 Apr 2025 07:12:57 -0500 Subject: [PATCH 132/146] Add missing build piece. --- src/meson.build | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/meson.build b/src/meson.build index 958d4e9..6919e7e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -47,6 +47,12 @@ tio_dep = [ lua_dep ] +if host_machine.system() == 'darwin' + iokit_dep = dependency('appleframeworks', modules: ['IOKit'], required: true) + corefoundation_dep = dependency('appleframeworks', modules: ['CoreFoundation'], required: true) + tio_dep += [iokit_dep, corefoundation_dep] +endif + tio_c_args = ['-Wno-unused-result'] if enable_setspeed2 From 5d915134a3792d8b9c6e40a1030bc0d9836cca0e Mon Sep 17 00:00:00 2001 From: Robert Lipe Date: Tue, 29 Apr 2025 00:00:42 -0500 Subject: [PATCH 133/146] Fix --auto new and --auto latest on MacOS. (redo) Git is being dumb about https://github.com/tio/tio/commit/67c071633dc6d659fae1e529a85f5990531e1669 This PR is identical to that one and will supercede it. Fix --auto new and --auto latest on MacOS. 'device_list' was both a global (eww!) and a local inside tty_search_for_serial_devices(). The local got set and returned, so it looked sane, but the caller used the global instead of the return value of the function it had just called, meaning (global) device_list was NULL while (ignored, local) device_list held a perfectly lovely linked list. Tested: tio --auto new waits for a new device to appare and connects tio --latest will connect to the most recently attached device which, in most worlds, is the most recently enumerated USB device, conveniently skipping all the bluetooth nonsense. If the lone USB device is disconnected, it then connects to one of those, meaning you really do have to restart tio. --- src/tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tty.c b/src/tty.c index 6de4af7..c5e6876 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1938,7 +1938,7 @@ char *getDeviceLocation(io_object_t device) // for __APPLE__ GList *tty_search_for_serial_devices(void) { - GList *device_list = NULL; + search_reset(); io_iterator_t iter = IO_OBJECT_NULL; CFMutableDictionaryRef matchingDict = NULL; listing_device_name_length_max = 0; From f887756a7159967e015e3422e9ab04a82db8945f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 29 Apr 2025 17:42:22 +0200 Subject: [PATCH 134/146] meson: Enable compiler warnings on unused result and global shadows --- src/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meson.build b/src/meson.build index 6919e7e..b44e9a5 100644 --- a/src/meson.build +++ b/src/meson.build @@ -53,7 +53,7 @@ if host_machine.system() == 'darwin' tio_dep += [iokit_dep, corefoundation_dep] endif -tio_c_args = ['-Wno-unused-result'] +tio_c_args = ['-Wshadow'] if enable_setspeed2 tio_c_args += '-DHAVE_TERMIOS2' From 2fb788f817be131c56d7cd44426f6d64dc42ea7c Mon Sep 17 00:00:00 2001 From: Hideaki Tai Date: Tue, 6 May 2025 15:31:45 +0900 Subject: [PATCH 135/146] fix: lua script stops output if it includes null terminate --- src/script.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/script.c b/src/script.c index b471932..1e46a33 100644 --- a/src/script.c +++ b/src/script.c @@ -187,7 +187,9 @@ static int modem_send(lua_State *L) // lua: send(string) static int write_(lua_State *L) { - const char *string = lua_tostring(L, 1); + size_t len = 0; + const char *string = lua_tolstring(L, 1, &len); + int ret; if (string == NULL) @@ -195,7 +197,7 @@ static int write_(lua_State *L) return 0; } - ret = write(device_fd, string, strlen(string)); + ret = write(device_fd, string, len); fsync(device_fd); // flush these characters now tcdrain(device_fd); //ensure we flushed characters to our device From 7e61a34df342c00f05e323cd4dc0602c19fa64d1 Mon Sep 17 00:00:00 2001 From: Maximilian Seesslen Date: Fri, 23 May 2025 16:14:16 +0200 Subject: [PATCH 136/146] Added timestamp format "epoch-usec" This timestamp format will print the seconds since epoch along with subdivision in microseconds. Example: [1748009585.087083] tio v3.9-8-g2fb788f-dirty [1748009585.087156] Press ctrl-t q to quit [1748009585.087683] Connected to /dev/ttyUSB0 --- man/tio.1.in | 2 ++ man/tio.1.txt | 2 ++ src/bash-completion/tio.in | 2 +- src/configfile.c | 2 +- src/options.c | 8 ++++++++ src/timestamp.c | 13 ++++++++++--- src/timestamp.h | 1 + src/tty.c | 3 +++ 8 files changed, 28 insertions(+), 5 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 50f6ae8..a7cf83f 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -150,6 +150,8 @@ Set timestamp format to any of the following timestamp formats: ISO8601 format ("YYYY-MM-DDThh:mm:ss.sss") .IP "\fBepoch" Seconds since Unix epoch (1970-01-01) +.IP "\fBepoch-usec" +Seconds since Unix epoch (1970-01-01) with subdivision in microseconds .PP Default format is \fB24hour\fR .RE diff --git a/man/tio.1.txt b/man/tio.1.txt index c37a38c..3021890 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -116,6 +116,8 @@ OPTIONS epoch Seconds since Unix epoch (1970-01-01) + epoch-usec Seconds since Unix epoch (1970-01-01) with subdivision microseconds + Default format is 24hour --timestamp-timeout diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index daac432..f510b7e 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -89,7 +89,7 @@ _tio() return 0 ;; --timestamp-format) - COMPREPLY=( $(compgen -W "24hour 24hour-start 24hour-delta iso8601" -- ${cur}) ) + COMPREPLY=( $(compgen -W "24hour 24hour-start 24hour-delta iso8601 epoch epoch-usec" -- ${cur}) ) return 0 ;; -c | --color) diff --git a/src/configfile.c b/src/configfile.c index 6330151..ca116bf 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -207,7 +207,7 @@ static void config_parse_keys(GKeyFile *key_file, char *group) config_get_bool(key_file, group, "timestamp", (bool*) &option.timestamp); if (option.timestamp != TIMESTAMP_NONE) { - config_get_string(key_file, group, "timestamp-format", &string, "24hour", "24hour-start", "24hour-delta", "iso8601", "epoch", NULL); + config_get_string(key_file, group, "timestamp-format", &string, "24hour", "24hour-start", "24hour-delta", "iso8601", "epoch", "epoch-usec", NULL); if (string != NULL) { option_parse_timestamp(string, &option.timestamp); diff --git a/src/options.c b/src/options.c index 3bdbfef..6aeff88 100644 --- a/src/options.c +++ b/src/options.c @@ -400,6 +400,10 @@ const char* option_timestamp_format_to_string(timestamp_t timestamp) return "epoch"; break; + case TIMESTAMP_EPOCH_USEC: + return "epoch-usec"; + break; + default: return "unknown"; break; @@ -430,6 +434,10 @@ void option_parse_timestamp(const char *arg, timestamp_t *timestamp) { *timestamp = TIMESTAMP_EPOCH; } + else if (strcmp(arg, "epoch-usec") == 0) + { + *timestamp = TIMESTAMP_EPOCH_USEC; + } else { tio_error_print("Invalid timestamp '%s'", arg); diff --git a/src/timestamp.c b/src/timestamp.c index b5ec306..9273758 100644 --- a/src/timestamp.c +++ b/src/timestamp.c @@ -75,6 +75,7 @@ char *timestamp_current_time(void) len = strftime(time_string, sizeof(time_string), "%Y-%m-%dT%H:%M:%S", tm); break; case TIMESTAMP_EPOCH: + case TIMESTAMP_EPOCH_USEC: // "N.sss" (seconds since Unix epoch, 1970-01-01 00:00:00Z) tv = tv_now; tm = localtime(&tv.tv_sec); @@ -84,12 +85,18 @@ char *timestamp_current_time(void) return NULL; } - // Append milliseconds to all timestamps + // Append millis-/microseconds to all timestamps if (len) { - len = snprintf(time_string + len, TIME_STRING_SIZE_MAX - len, ".%03ld", (long)tv.tv_usec / 1000); + if ( option.timestamp == TIMESTAMP_EPOCH_USEC ) + { + len = snprintf(time_string + len, TIME_STRING_SIZE_MAX - len, ".%06ld", (long)tv.tv_usec); + } + else + { + len = snprintf(time_string + len, TIME_STRING_SIZE_MAX - len, ".%03ld", (long)tv.tv_usec / 1000); + } } - // Save previous time value for next run tv_previous = tv_now; diff --git a/src/timestamp.h b/src/timestamp.h index 6a10d98..0544544 100644 --- a/src/timestamp.h +++ b/src/timestamp.h @@ -29,6 +29,7 @@ typedef enum TIMESTAMP_24HOUR_DELTA, TIMESTAMP_ISO8601, TIMESTAMP_EPOCH, + TIMESTAMP_EPOCH_USEC, TIMESTAMP_END, } timestamp_t; diff --git a/src/tty.c b/src/tty.c index c5e6876..c38d5dc 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1100,6 +1100,9 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) case TIMESTAMP_EPOCH: tio_printf("Switched timestamp mode to epoch"); break; + case TIMESTAMP_EPOCH_USEC: + tio_printf("Switched timestamp mode to epoch with subdivision in microseconds"); + break; case TIMESTAMP_END: option.timestamp = TIMESTAMP_NONE; tio_printf("Switched timestamp mode off"); From 58bf5c500886596d9eda188c7be58ec764eb9a13 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 25 May 2025 19:46:18 +0200 Subject: [PATCH 137/146] Update tio man page --- man/tio.1.in | 4 ++-- man/tio.1.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index a7cf83f..9d5ebee 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -447,8 +447,8 @@ Returns 1 on successful match, 0 on timeout, or -1 on error. On successful match it also returns the match string as second return value. .IP "\fBread(size, timeout)" -Read from serial device. If timeout is 0 or not provided it will wait forever -until data is ready to read. +Read up to size bytes from serial device. If timeout is 0 or not provided it +will wait forever until data is ready to read. Returns number of bytes read on success, 0 on timeout, or -1 on error. diff --git a/man/tio.1.txt b/man/tio.1.txt index 3021890..4363879 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -357,7 +357,7 @@ SCRIPT API On successful match it also returns the match string as second return value. read(size, timeout) - Read from serial device. If timeout is 0 or not provided it will wait forever until data is ready to read. + Read up to size bytes from serial device. If timeout is 0 or not provided it will wait forever until data is ready to read. Returns number of bytes read on success, 0 on timeout, or -1 on error. From a1217af4c631345fe6d11280b4192ef24b125326 Mon Sep 17 00:00:00 2001 From: ii8 Date: Sun, 25 May 2025 19:24:11 +0100 Subject: [PATCH 138/146] Fix string truncation bug in scripting api --- src/script.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/script.c b/src/script.c index 1e46a33..8204563 100644 --- a/src/script.c +++ b/src/script.c @@ -332,7 +332,11 @@ error_rs: lua_pushnumber(L, ret); if (buffer != NULL) { - lua_pushstring(L, ret > 0 ? buffer : ""); + if (ret > 0) { + lua_pushlstring(L, buffer, ret); + } else { + lua_pushstring(L, ""); + } free(buffer); } else @@ -400,7 +404,7 @@ static int read_line(lua_State *L) error_rl: lua_pushnumber(L, ret); - lua_pushstring(L, linebuf); + lua_pushlstring(L, linebuf, ret); return 2; } From 3e0b2d861dd8e8c2da70e7494e346dc1d6de006d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 30 May 2025 17:18:21 +0200 Subject: [PATCH 139/146] Fix Ubuntu workflow --- .github/workflows/ubuntu.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index a72e3d3..90e1a93 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -21,7 +21,8 @@ jobs: - name: Install dependencies run: | - sudo apt-get install -y bash-completion git meson liblua5.2-dev libglib2.0-dev + sudo apt update + sudo apt install -y bash-completion git meson liblua5.2-dev libglib2.0-dev - name: Build run: | From 9d00cd3492915792baf1725baaf79e22521bc563 Mon Sep 17 00:00:00 2001 From: Keith Barratt Date: Thu, 29 May 2025 15:57:11 +0100 Subject: [PATCH 140/146] Fix device description-Linux This commit only effects Linux. The description field of the `device_list`, populated by `tty_search_for_serial_devices()`, was either incorrect or less than ideal for CDC ACM virtual com ports. For instance: (i) Some devices incorrectly have the description field populated by the 'product' property of USB hub they are connected via. (ii) Other devices have description fields populated with the interface, e.g. CDC, when there is a 'product' property available that would give a clearer description. To solve these issues, we first prioritise searching for the 'product' property of the device over the 'interface' property. We also look for the 'product' property in an additional directory. --- src/tty.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/tty.c b/src/tty.c index c38d5dc..22b7eef 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1785,18 +1785,22 @@ GList *tty_search_for_serial_devices(void) creation_time = fs_get_creation_time(path); double uptime = current_time - creation_time; - // Read sysfs files to get best possible description of the driver + // Read sysfs files to get best possible description char description[50] = {}; - length = fs_read_file_stripped(description, sizeof(description), "/sys/class/tty/%s/device/interface", entry->d_name); - if (length == -1) - { - length = fs_read_file_stripped(description, sizeof(description), "/sys/class/tty/%s/device/../interface", entry->d_name); - } + length = fs_read_file_stripped(description, sizeof(description), "/sys/class/tty/%s/device/../product", entry->d_name); if (length == -1) { length = fs_read_file_stripped(description, sizeof(description), "/sys/class/tty/%s/device/../../product", entry->d_name); } if (length == -1) + { + length = fs_read_file_stripped(description, sizeof(description), "/sys/class/tty/%s/device/interface", entry->d_name); + } + if (length == -1) + { + length = fs_read_file_stripped(description, sizeof(description), "/sys/class/tty/%s/device/../interface", entry->d_name); + } + if (length == -1) { snprintf(description, sizeof(description), "%s", get_serial_port_type(path)); } From 8f33cff6ead809fbf08ff19c2cd720e4de7304d5 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 31 May 2025 19:42:08 +0200 Subject: [PATCH 141/146] Disable compiler warning on unused result --- src/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meson.build b/src/meson.build index b44e9a5..05168f7 100644 --- a/src/meson.build +++ b/src/meson.build @@ -53,7 +53,7 @@ if host_machine.system() == 'darwin' tio_dep += [iokit_dep, corefoundation_dep] endif -tio_c_args = ['-Wshadow'] +tio_c_args = ['-Wshadow','-Wno-unused-result'] if enable_setspeed2 tio_c_args += '-DHAVE_TERMIOS2' From 381c0b78236b97a265604801d381587bc204fc5b Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 14 Jun 2025 07:03:17 +0200 Subject: [PATCH 142/146] Update codeql to v3 --- .github/workflows/codeql.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 1667c7e..8a195f4 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -51,7 +51,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -66,7 +66,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). # If this step fails, then you should remove it and run the build manually (see below) #- name: Autobuild - # uses: github/codeql-action/autobuild@v2 + # uses: github/codeql-action/autobuild@v3 # â„šī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -78,7 +78,7 @@ jobs: ./.github/workflows/codeql-buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" upload: false @@ -107,7 +107,7 @@ jobs: output: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif - name: Upload CodeQL results to code scanning - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 with: sarif_file: ${{ steps.step1.outputs.sarif-output }} category: "/language:${{matrix.language}}" From 86f48a2fb689dad56d0e195b4735ef0d4661e8b2 Mon Sep 17 00:00:00 2001 From: ii8 Date: Fri, 13 Jun 2025 15:49:33 +0100 Subject: [PATCH 143/146] Overhaul Lua API Lua API moved into a tio library table and names adjusted to Lua stdlib style. Regex in expect() replaced with Lua patterns so binary data can be handled. New tio.alwaysecho variable allows enabling and disabling echo to console. Read and write functions now manage complex retry and timeout logic internally, giving the user a simple "nil if fail" API like the rest of Lua. exit() was removed, os.exit() already exists in the Lua standard library. --- README.md | 104 ++--- examples/config/config | 2 +- examples/lua/automatic-linux-login.lua | 11 +- examples/lua/control-lines-test.lua | 10 +- examples/lua/read.lua | 24 +- examples/lua/read_line.lua | 28 +- examples/lua/serial-device-search.lua | 2 +- man/tio.1.in | 58 ++- src/script.c | 544 +++++++++---------------- 9 files changed, 318 insertions(+), 465 deletions(-) diff --git a/README.md b/README.md index f2c2806..e3ab066 100644 --- a/README.md +++ b/README.md @@ -288,12 +288,12 @@ $ cat data.bin | tio /dev/ttyUSB0 Manipulate modem lines on connect: ``` -$ tio --script "set{DTR=high,RTS=low}; msleep(100); set{DTR=toggle,RTS=toggle}" /dev/ttyUSB0 +$ tio --script "tio.set{DTR=high,RTS=low}; tio.msleep(100); tio.set{DTR=toggle,RTS=toggle}" /dev/ttyUSB0 ``` Pipe command to serial device and wait for line response within 1 second: ``` -$ echo "*IDN?" | tio /dev/ttyACM0 --script "expect('\r\n', 1000)" --mute +$ echo "*IDN?" | tio /dev/ttyACM0 --script "tio.expect('\r\n', 1000)" --mute KORAD KD3305P V4.2 SN:32475045 ``` @@ -365,12 +365,12 @@ color = 11 [svf2] device = /dev/ttyUSB0 baudrate = 9600 -script = expect("login: "); write("root\n"); expect("Password: "); write("root\n") +script = tio.expect("login: "); tio.write("root\n"); tio.expect("Password: "); tio.write("root\n") color = 12 [esp32] device = /dev/serial/by-id/usb-0403_6014-if00-port0 -script = set{DTR=high,RTS=low}; msleep(100); set{DTR=low,RTS=high}; msleep(100); set{RTS=low} +script = tio.set{DTR=high,RTS=low}; tio.msleep(100); tio.set{DTR=low,RTS=high}; tio.msleep(100); tio.set{RTS=low} script-run = once color = 13 @@ -395,72 +395,80 @@ Another more elaborate configuration file example is available [here](examples/c Tio suppots Lua scripting to easily automate interaction with the tty device. -In addition to the Lua API tio makes the following functions available: +In addition to the standard Lua API tio makes the following functions +and variables available: -``` -expect(string, timeout) - Expect string - waits for string to match or timeout before continueing. - Supports regular expressions. Special characters must be escaped with '\\'. - Timeout is in milliseconds, defaults to 0 meaning it will wait forever. - Returns 1 on successful match, 0 on timeout, or -1 on error. +#### `tio.expect(pattern, timeout)` - On successful match it also returns the match string as second return value. +Waits for the Lua pattern to match or timeout before continuing. +Timeout is in milliseconds, defaults to 0 meaning it will wait forever. -read(size, timeout) - Read from serial device. If timeout is 0 or not provided it will wait - forever until data is ready to read. +Returns the captures from the pattern or `nil` on timeout. - Returns number of bytes read on success, 0 on timeout, or -1 on error. +#### `tio.read(size, timeout)` - On success, returns read string as second return value. +Read up to `size` bytes from serial device. If timeout is 0 or not provided it +will wait forever until data is ready to read. -read_line(timeout) - Read line from serial device. If timeout is 0 or not provided it will - wait forever until data is ready to read. +Returns a string up to `size` bytes long on success and `nil` on timeout. - Returns number of bytes read on success, 0 on timeout, or -1 on error. +#### `tio.readline(timeout)` - On success, returns the string that was read as second return value. - Also emits a single timestamp to stdout and log file per options.timestamp - and options.log. +Read line from serial device. If timeout is 0 or not provided it will wait +forever until data is ready to read. -write(string) - Write string to serial device. +Returns a string on success and `nil` on timeout. On timeout a partially read +line may be returned as a second return value. - Returns number of bytes written on success or -1 on error. +#### `tio.write(string)` -send(file, protocol) - Send file using x/y-modem protocol. +Write string to serial device. - Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM. +Returns the `tio` table. -tty_search() - Search for serial devices. +#### `tio.send(file, protocol)` - Returns a table of number indexed tables, one for each serial device - found. Each of these tables contains the serial device information accessible - via the following string indexed elements "path", "tid", "uptime", "driver", - "description". +Send file using x/y-modem protocol. - Returns nil if no serial devices are found. +Protocol can be any of `XMODEM_1K`, `XMODEM_CRC`, `YMODEM`. -set{line=state, ...} - Set state of one or multiple tty modem lines. +#### `tio.ttysearch()` - Line can be any of DTR, RTS, CTS, DSR, CD, RI +Search for serial devices. - State is high, low, or toggle. +Returns a table of number indexed tables, one for each serial device found. +Each of these tables contains the serial device information accessible via the +following string indexed elements "path", "tid", "uptime", "driver", +"description". -sleep(seconds) - Sleep for seconds. +Returns `nil` if no serial devices are found. -msleep(ms) - Sleep for miliseconds. +#### `tio.set{line=state, ...}` +Set state of one or multiple tty modem lines. + +Line can be any of `DTR`, `RTS`, `CTS`, `DSR`, `CD`, `RI` + +State is `high`, `low`, or `toggle`. + +#### `tio.sleep(seconds)` + +Sleep for seconds. + +#### `tio.msleep(ms)` + +Sleep for milliseconds. + +#### `tio.alwaysecho` + +A boolean value, defaults to `true`. + +If `tio.alwaysecho` is `false`, the result of `tio.read`, `tio.readline` or +`tio.expect` will only be returned from the function and not logged or printed. + +If `tio.alwaysecho` is set to `true`, reading functions also emit a single +timestamp to stdout and log file per `options.timestamp` and `options.log`. -exit(code) - Exit with exit code. -``` ## 4. Installation diff --git a/examples/config/config b/examples/config/config index 01e84dd..7bd141e 100644 --- a/examples/config/config +++ b/examples/config/config @@ -69,7 +69,7 @@ color = 13 [esp32] device = /dev/ttyUSB0 color = 14 -script = set{DTR=high,RTS=low}; msleep(100); set{DTR=low,RTS=high}; msleep(100); set{RTS=low} +script = tio.set{DTR=high,RTS=low}; tio.msleep(100); tio.set{DTR=low,RTS=high}; tio.msleep(100); tio.set{RTS=low} script-run = always [buspirate] diff --git a/examples/lua/automatic-linux-login.lua b/examples/lua/automatic-linux-login.lua index 428caa1..aa7a990 100644 --- a/examples/lua/automatic-linux-login.lua +++ b/examples/lua/automatic-linux-login.lua @@ -13,14 +13,13 @@ local logins = { }, } -local found, match_str = expect("\\w+- login:", 10) -if (1 == found) then - local hostname = string.match(match_str, "^%w+") +local hostname = tio.expect("^(%g+) login:", 10) +if hostname then local login = logins[hostname] if (nil ~= login) then - write(login.username .. "\n") - expect("Password:") - write(login.password .. "\n") + tio.write(login.username .. "\n") + tio.expect("Password:") + tio.write(login.password .. "\n") else io.write("\r\nDon't know login info for " .. hostname .. "\r\n") end diff --git a/examples/lua/control-lines-test.lua b/examples/lua/control-lines-test.lua index 5b54ab4..55d98b5 100644 --- a/examples/lua/control-lines-test.lua +++ b/examples/lua/control-lines-test.lua @@ -1,5 +1,5 @@ -set{DTR=high, RTS=low} -msleep(100) -set{DTR=low, RTS=high} -msleep(100) -set{RTS=toggle} +tio.set{DTR=high, RTS=low} +tio.msleep(100) +tio.set{DTR=low, RTS=high} +tio.msleep(100) +tio.set{RTS=toggle} diff --git a/examples/lua/read.lua b/examples/lua/read.lua index 6baa032..6452816 100644 --- a/examples/lua/read.lua +++ b/examples/lua/read.lua @@ -1,14 +1,14 @@ -read(1000, 6000) -- initial config -write("\n") -msleep(100) -read(650, 60) -- main menu -write("S") -- S menu -msleep(30) -read(650, 60) -write("t") -- Parallel Value Table -read(650, 60) +tio.read(1000, 6000) -- initial config +tio.write("\n") +tio.msleep(100) +tio.read(650, 60) -- main menu +tio.write("S") -- S menu +tio.msleep(30) +tio.read(650, 60) +tio.write("t") -- Parallel Value Table +tio.read(650, 60) while true do - msleep(1000) - write("t") - read(650, 50) -- repeat PVT forever + tio.msleep(1000) + tio.write("t") + tio.read(650, 50) -- repeat PVT forever end diff --git a/examples/lua/read_line.lua b/examples/lua/read_line.lua index ef6ec20..a844b48 100644 --- a/examples/lua/read_line.lua +++ b/examples/lua/read_line.lua @@ -1,17 +1,15 @@ -read(1000, 8000) -- read initial config -write("\n") -read(650, 100) -- main menu -write("S") -- S menu -n = 1 -while n > 0 do -- while not empty, read more - n, str = read_line(25) -end +tio.read(1000, 8000) -- read initial config +tio.write("\n") +tio.read(650, 100) -- main menu +tio.write("S") -- S menu +repeat + str = tio.readline(25) +until str == nil while true do - write("t") -- query PV table - msleep(880) - n = 1 - while n > 0 do -- while not empty, read more - n, str = read_line(60) - msleep(60) - end + tio.write("t") -- query PV table + tio.msleep(880) + repeat + str = tio.readline(60) + tio.msleep(60) + until str == nil end diff --git a/examples/lua/serial-device-search.lua b/examples/lua/serial-device-search.lua index 120d650..77d9dbc 100644 --- a/examples/lua/serial-device-search.lua +++ b/examples/lua/serial-device-search.lua @@ -1,6 +1,6 @@ io.write("Searching... ") -local device = tty_search() +local device = tio.ttysearch() io.write("done\r\n") diff --git a/man/tio.1.in b/man/tio.1.in index 9d5ebee..eb03ec2 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -433,49 +433,40 @@ Send ctrl-t character Tio suppots Lua scripting to easily automate interaction with the tty device. In addition to the standard Lua API tio makes the following functions -available: +and variables available: .TP 6n -.IP "\fBexpect(string, timeout)" -Expect string - waits for string to match or timeout before continuing. -Supports regular expressions. Special characters must be escaped with '\e\e'. +.IP "\fBtio.expect(pattern, timeout)" +Waits for the Lua pattern to match or timeout before continuing. Timeout is in milliseconds, defaults to 0 meaning it will wait forever. -Returns 1 on successful match, 0 on timeout, or -1 on error. +Returns the captures from the pattern or nil on timeout. -On successful match it also returns the match string as second return value. - -.IP "\fBread(size, timeout)" +.IP "\fBtio.read(size, timeout)" Read up to size bytes from serial device. If timeout is 0 or not provided it will wait forever until data is ready to read. -Returns number of bytes read on success, 0 on timeout, or -1 on error. +Returns a string up to size bytes long on success and nil on timeout. -On success, returns read string as second return value. Also emits a single -timestamp to stdout and log file per options.timestamp and options.log. - -.IP "\fBread_line(timeout)" +.IP "\fBtio.readline(timeout)" Read line from serial device. If timeout is 0 or not provided it will wait forever until data is ready to read. -Returns number of bytes read on success, 0 on timeout, or -1 on error. +Returns a string on success and nil on timeout. On timeout a partially read +line may be returned as a second return value. -On success, returns the string that was read as second return value. Also -emits a single timestamp to stdout and log file per options.timestamp -and options.log. - -.IP "\fBwrite(string)" +.IP "\fBtio.write(string)" Write string to serial device. -Returns number of bytes written on success or -1 on error. +Returns the tio table. -.IP "\fBsend(file, protocol)" +.IP "\fBtio.send(file, protocol)" Send file using x/y-modem protocol. Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM. -.IP "\fBtty_search()" +.IP "\fBtio.ttysearch()" Search for serial devices. Returns a table of number indexed tables, one for each serial device found. @@ -485,19 +476,26 @@ following string indexed elements "path", "tid", "uptime", "driver", Returns nil if no serial devices are found. -.IP "\fBset{line=state, ...}" +.IP "\fBtio.set{line=state, ...}" Set state of one or multiple tty modem lines. Line can be any of DTR, RTS, CTS, DSR, CD, RI State is high, low, or toggle. -.IP "\fBsleep(seconds)" +.IP "\fBtio.sleep(seconds)" Sleep for seconds. -.IP "\fBmsleep(ms)" +.IP "\fBtio.msleep(ms)" Sleep for milliseconds. -.IP "\fBexit(code)" -Exit with exit code. + +.IP "\fBtio.alwaysecho" +A boolean value, defaults to true. + +If tio.alwaysecho is false, the result of tio.read, tio.readline or tio.expect +will only be returned from the function and not logged or printed. + +If tio.alwaysecho is set to true, reading functions also emit a single +timestamp to stdout and log file per options.timestamp and options.log. .SH "CONFIGURATION FILE" .PP @@ -726,7 +724,7 @@ expect -i $uart "prompt> " .TP It is also possible to use tio's own simpler expect/send script functionality to e.g. automate logins: -$ tio --script 'expect("login: "); write("root\\n"); expect("Password: "); write("root\\n")' /dev/ttyUSB0 +$ tio --script 'tio.expect("login: "); tio.write("root\\n"); tio.expect("Password: "); tio.write("root\\n")' /dev/ttyUSB0 .TP Redirect device I/O to network file socket for remote TTY sharing: @@ -747,7 +745,7 @@ $ echo "ls -la" | tio /dev/serial/by\-id/usb\-FTDI_TTL232R-3V3_FTGQVXBL\-if00\-p .TP Pipe command to serial device and wait for line response within 1 second: -$ echo "*IDN?" | tio /dev/ttyACM0 --script "expect('\\r\\n', 1000)" --mute +$ echo "*IDN?" | tio /dev/ttyACM0 --script "tio.expect('\\r\\n', 1000)" --mute .TP .TP @@ -768,7 +766,7 @@ $ tio --rs-485 --rs-485-config=RTS_ON_SEND=1,RX_DURING_TX /dev/ttyUSB0 .TP Manipulate DTR and RTS lines upon first connect to reset connected microcontroller: -$ tio --script "set{DTR=high,RTS=low}; msleep(100); set{RTS=toggle}" --script-run once /dev/ttyUSB0 +$ tio --script "tio.set{DTR=high,RTS=low}; tio.msleep(100); tio.set{RTS=toggle}" --script-run once /dev/ttyUSB0 .SH "WEBSITE" .PP diff --git a/src/script.c b/src/script.c index 8204563..b69d55b 100644 --- a/src/script.c +++ b/src/script.c @@ -20,7 +20,6 @@ */ #include -#include #include #include #include @@ -45,23 +44,87 @@ #define READ_LINE_SIZE 4096 // read_line buffer length static int device_fd; -static char circular_buffer[MAX_BUFFER_SIZE]; -static char match_string[MAX_BUFFER_SIZE]; -static int buffer_size = 0; static char script_init[] = -"function set(arg)\n" +"tio.set = function(arg)\n" " local dtr = arg.DTR or -1\n" " local rts = arg.RTS or -1\n" " local cts = arg.CTS or -1\n" " local dsr = arg.DSR or -1\n" " local cd = arg.CD or -1\n" " local ri = arg.RI or -1\n" -" line_set(dtr, rts, cts, dsr, cd, ri)\n" -"end\n"; +" tio.line_set(dtr, rts, cts, dsr, cd, ri)\n" +"end\n" +"tio.expect = function(pattern, timeout)\n" +" local str = ''\n" +" while true do\n" +" local c = tio.read(1, timeout)\n" +" if c then\n" +" str = str .. c\n" +" if string.match(str, pattern) then\n" +" return string.match(str, pattern)\n" +" end\n" +" else\n" +" return nil, str\n" +" end\n" +" end\n" +"end\n" +"tio.alwaysecho = true\n" +"setmetatable(tio, tio)\n"; -// lua: sleep(seconds) -static int sleep_(lua_State *L) +static bool alwaysecho(lua_State *L) +{ + bool b; + + lua_getglobal(L, "tio"); + lua_getfield(L, -1, "alwaysecho"); + b = lua_toboolean(L, -1); + lua_pop(L, 2); + + return b; +} + +static int api_echo(lua_State *L) +{ + size_t len = 0; + const char *str = luaL_checklstring(L, 1, &len); + + if (option.timestamp) + { + char *pTimeStampNow = timestamp_current_time(); + if (pTimeStampNow) + { + tio_printf("%s", str); + if (option.log) + { + log_printf("\n[%s] %s", pTimeStampNow, str); + } + } + } else { + for (size_t i=0; i 0 && --attempts); + + if (len > 0) + return luaL_error(L, "partial write"); - ret = write(device_fd, string, len); fsync(device_fd); // flush these characters now tcdrain(device_fd); //ensure we flushed characters to our device - lua_pushnumber(L, ret); + lua_getglobal(L, "tio"); return 1; } -// Function to add a character to the circular expect buffer -static void expect_buffer_add(char c) +// lua: tio.read(size, timeout) +static int api_read(lua_State *L) { - if (!c) - { - return; - } - - 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 expect buffer using regex -static bool match_regex(regex_t *regex) -{ - char buffer[MAX_BUFFER_SIZE + 1]; // Temporary buffer for regex matching - const char *s = circular_buffer; - regmatch_t pmatch[1]; - regoff_t len; - - memcpy(buffer, circular_buffer, buffer_size); - buffer[buffer_size] = '\0'; // Null-terminate the buffer - - // Match against the regex - int ret = regexec(regex, buffer, 1, pmatch, 0); - if (!ret) - { - // Match found - len = pmatch[0].rm_eo - pmatch[0].rm_so; - memcpy(match_string, s + pmatch[0].rm_so, len); - match_string[len] = '\0'; - - 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; -} - -// Function to echo a buffer to stdout and to the log -// per the option.timestamp and option.log settings -static void echo_buffer(char buffer[], ssize_t len) -{ - if (option.timestamp) - { - char *pTimeStampNow; - pTimeStampNow = timestamp_current_time(); - if (pTimeStampNow) - { - tio_printf("%s", buffer); //does timestamps for us - if (option.log) - { - log_printf("\n[%s] %s", pTimeStampNow, buffer); - } - } - } else { - for (ssize_t i=0; i= 502 + char *p = luaL_prepbuffsize(&buffer, size); +#else + if (size > LUAL_BUFFERSIZE) + return luaL_error(L, "buffer overflow, max size is: %d", LUAL_BUFFERSIZE); + char *p = luaL_prepbuffer(&buffer); +#endif + + ssize_t ret = read_poll(device_fd, p, size, timeout); + if (ret < 0) + return luaL_error(L, "%s", strerror(errno)); + + luaL_addsize(&buffer, ret); + luaL_pushresult(&buffer); + + if (ret == 0) { - ret = -1; // Error - goto error_rs; - } - else if (bytes_read == 0) - { - ret = 0; // Timeout - goto error_rs; + // On timeout return nil instead of an empty string + lua_pop(L, 1); + lua_pushnil(L); } else { - buffer[bytes_read] = (char)0; + maybe_echo(L); } - echo_buffer(&buffer[0], bytes_read); - ret = bytes_read; - -error_rs: - lua_pushnumber(L, ret); - if (buffer != NULL) - { - if (ret > 0) { - lua_pushlstring(L, buffer, ret); - } else { - lua_pushstring(L, ""); - } - free(buffer); - } - else - { - lua_pushstring(L, ""); // give empty string to caller - } - return 2; + return 1; } -// lua: ret,string = read_line(timeout) -static int read_line(lua_State *L) -{ - static char linebuf[READ_LINE_SIZE]; +// lua: string = tio.readline(timeout) +static int api_readline(lua_State *L) { int timeout = lua_tointeger(L, 1); //ms - int ret = 0; - int read_result = 1; //enable reading input from device + luaL_Buffer b; char ch; - int bytes_read = 0; - linebuf[0] = '\0'; if (timeout == 0) { timeout = -1; // Wait forever } - // loop reading input until a newline seen or timeout - while (true) - { - read_result = read_poll(device_fd, &ch, 1, timeout); - if (read_result < 0) - { - ret = -1; // Error - linebuf[bytes_read] = '\0'; - goto error_rl; - } - else if (!read_result) - { - // Timeout returns a non-empty linebuf as a 'line' - ret = bytes_read; - linebuf[bytes_read] = '\0'; - break; - } - else // we got a character, so handle it - { - if (ch == '\n') - { - linebuf[bytes_read] = '\0'; - break; - } - else if (bytes_read <= (READ_LINE_SIZE-2)) - { - if (isprint(ch)) // store all printable chars - { - linebuf[bytes_read++] = ch; - } - } - } - } + luaL_buffinit(L, &b); + luaL_prepbuffer(&b); + while (true) { + int ret = read_poll(device_fd, &ch, 1, timeout); - if (bytes_read) - { - echo_buffer(linebuf, bytes_read); - } - ret = bytes_read; + if (ret < 0) + return luaL_error(L, "%s", strerror(errno)); -error_rl: - lua_pushnumber(L, ret); - lua_pushlstring(L, linebuf, ret); - return 2; + if (ret == 0) + { + luaL_pushresult(&b); + maybe_echo(L); + lua_pushnil(L); + lua_insert(L, -2); + return 2; + } + + if (ch == '\n') + { + luaL_pushresult(&b); + maybe_echo(L); + return 1; + } + + luaL_addchar(&b, ch); + } } -// lua: expect(string, timeout) -static int expect(lua_State *L) -{ - const char *string = lua_tostring(L, 1); - long timeout = lua_tointeger(L, 2); - regex_t regex; - int ret = 0; - char c; - - // Resets buffer to ignore previous `expect` calls - buffer_size = 0; - match_string[0] = '\0'; - - if ((string == NULL) || (timeout < 0)) - { - ret = -1; - goto error; - } - - if (timeout == 0) - { - // Let poll() wait forever - timeout = -1; - } - - // 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_poll(device_fd, &c, 1, timeout); - if (bytes_read > 0) - { - putchar(c); - expect_buffer_add(c); - - if (option.log) - { - log_putc(c); - } - - // Match against the entire buffer - if (match_regex(®ex)) - { - ret = 1; - break; - } - } - else - { - // Timeout or error - break; - } - } - - // Cleanup - regfree(®ex); - -error: - lua_pushnumber(L, ret); - lua_pushstring(L, match_string); - return 2; -} - -// lua: exit(code) -static int exit_(lua_State *L) -{ - long code = lua_tointeger(L, 1); - - exit(code); - - return 0; -} - -// lua: list = tty_search() -static int tty_search_(lua_State *L) +// lua: table = tio.ttysearch() +static int api_ttysearch(lua_State *L) { UNUSED(L); GList *iter; @@ -556,66 +424,7 @@ static void script_buffer_run(lua_State *L, const char *script_buffer) } } -static const struct luaL_Reg tio_lib[] = -{ - { "sleep", sleep_}, - { "msleep", msleep}, - { "line_set", line_set}, - { "send", modem_send}, - { "write", write_}, - { "read", read_string}, - { "read_line", read_line}, - { "expect", expect}, - { "exit", exit_}, - { "tty_search", tty_search_}, - {NULL, NULL} -}; - -#if !defined LUA_VERSION_NUM || LUA_VERSION_NUM==501 -/* -** Adapted from Lua 5.2.0 (for backwards compatibility) -*/ -static void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) -{ - luaL_checkstack(L, nup+1, "too many upvalues"); - for (; l->name != NULL; l++) { /* fill the table with given functions */ - int i; - lua_pushstring(L, l->name); - for (i = 0; i < nup; i++) /* copy upvalues to the top */ - lua_pushvalue(L, -(nup+1)); - lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ - lua_settable(L, -(nup + 3)); - } - lua_pop(L, nup); /* remove upvalues */ -} -#endif - -static void script_load(lua_State *L) -{ - int error; - - error = luaL_loadbuffer(L, script_init, strlen(script_init), "tio") || lua_pcall(L, 0, 0, 0); - if (error) - { - tio_error_print("%s\n", lua_tostring(L, -1)); - lua_pop(L, 1); // Pop error message from the stack - } -} - -int lua_register_tio(lua_State *L) -{ - // Register lxi functions - lua_getglobal(L, "_G"); - luaL_setfuncs(L, tio_lib, 0); - lua_pop(L, 1); - - // Load lua init script - script_load(L); - - return 0; -} - -void script_file_run(lua_State *L, const char *filename) +static void script_file_run(lua_State *L, const char *filename) { if (strlen(filename) == 0) { @@ -631,13 +440,39 @@ void script_file_run(lua_State *L, const char *filename) } } -void script_set_global(lua_State *L, const char *name, long value) +static const struct luaL_Reg tio_lib[] = +{ + { "echo", api_echo}, + { "sleep", api_sleep}, + { "msleep", api_msleep}, + { "line_set", line_set}, + { "send", api_send}, + { "write", api_write}, + { "read", api_read}, + { "readline", api_readline}, + { "ttysearch", api_ttysearch}, + {NULL, NULL} +}; + +static void script_load(lua_State *L) +{ + int error; + + error = luaL_loadbuffer(L, script_init, strlen(script_init), "tio") || lua_pcall(L, 0, 0, 0); + if (error) + { + tio_error_print("%s\n", lua_tostring(L, -1)); + lua_pop(L, 1); // Pop error message from the stack + } +} + +static void script_set_global(lua_State *L, const char *name, long value) { lua_pushnumber(L, value); lua_setglobal(L, name); } -void script_set_globals(lua_State *L) +static void script_set_globals(lua_State *L) { script_set_global(L, "toggle", 2); script_set_global(L, "high", 1); @@ -647,6 +482,14 @@ void script_set_globals(lua_State *L) script_set_global(L, "YMODEM", YMODEM); } +#if LUA_VERSION_NUM >= 502 +static int luaopen_tio(lua_State *L) +{ + luaL_newlib(L, tio_lib); + return 1; +} +#endif + void script_run(int fd, const char *script_filename) { lua_State *L; @@ -656,8 +499,15 @@ void script_run(int fd, const char *script_filename) L = luaL_newstate(); luaL_openlibs(L); - // Bind tio functions - lua_register_tio(L); +#if LUA_VERSION_NUM >= 502 + luaL_requiref(L, "tio", luaopen_tio, 1); +#else + luaL_register(L, "tio", tio_lib); +#endif + lua_pop(L, 1); + + // Load lua init script + script_load(L); // Initialize globals script_set_globals(L); From cce94b9d9280415d34575103b0f7f1d783ad1b0c Mon Sep 17 00:00:00 2001 From: John Barbero Unenge Date: Mon, 16 Jun 2025 14:59:24 +0200 Subject: [PATCH 144/146] Add --complete-profiles to help printout and man pages --- man/tio.1.in | 4 ++++ man/tio.1.txt | 4 ++++ src/bash-completion/tio.in | 1 + src/options.c | 1 + 4 files changed, 10 insertions(+) diff --git a/man/tio.1.in b/man/tio.1.in index eb03ec2..eb0338d 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -371,6 +371,10 @@ Default value is "always". Execute shell command with I/O redirected to device +.TP +.BR "\-\-complete-profiles + +Prints profiles (for shell completion) .TP .BR \-v ", " \-\-version diff --git a/man/tio.1.txt b/man/tio.1.txt index 4363879..eeac82c 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -289,6 +289,10 @@ OPTIONS Execute shell command with I/O redirected to device + --complete-profiles + + Prints profiles (for shell completion) + -v, --version Display program version. diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index f510b7e..b3b61cb 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -46,6 +46,7 @@ _tio() --script-file \ --script-run \ --exec \ + --complete-profiles \ -v --version \ -h --help" diff --git a/src/options.c b/src/options.c index 6aeff88..acff7ce 100644 --- a/src/options.c +++ b/src/options.c @@ -171,6 +171,7 @@ void option_print_help(char *argv[]) printf(" --script-file Run script from file\n"); printf(" --script-run once|always|never Run script on connect (default: always)\n"); printf(" --exec Execute shell command with I/O redirected to device\n"); + printf(" --complete-profiles Prints profiles (for shell completion)\n"); printf(" -v, --version Display version\n"); printf(" -h, --help Display help\n"); printf("\n"); From 3af4c5591e0183ea9871654ea4d62254ac23226d Mon Sep 17 00:00:00 2001 From: aiotter Date: Thu, 7 Aug 2025 23:40:39 +0900 Subject: [PATCH 145/146] Fix redundant output on macOS --- src/tty.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/tty.c b/src/tty.c index 22b7eef..efda859 100644 --- a/src/tty.c +++ b/src/tty.c @@ -2068,32 +2068,12 @@ GList *tty_search_for_serial_devices(void) /* Check if device list is empty */ if (!device_list) { - tio_error_print("No serial devices found"); return NULL; } /* Sort device list by uptime */ device_list = g_list_sort(device_list, compare_uptime); - /* Print header for device listing */ - print_padded("Device", listing_device_name_length_max, ' '); - printf(" TID Uptime [s] Driver Description\n"); - print_padded("", listing_device_name_length_max, '-'); - printf(" ---- -------------- ---------------- --------------------------\n"); - - /* Print sorted device list */ - for (GList *l = device_list; l; l = l->next) - { - device_t *dev = l->data; - printf("%-*s %-4s %14.3f %-16s %s\n", - (int)listing_device_name_length_max, dev->path, - dev->tid ?: "", - dev->uptime, - dev->driver ?: "", - dev->description ?: ""); - } - printf("\n"); - return device_list; } From 6fb3a64ba234cc255f9637ba938cf0c01e132e4a Mon Sep 17 00:00:00 2001 From: Jakob Haufe Date: Thu, 22 Jan 2026 08:33:20 +0100 Subject: [PATCH 146/146] Fix license in meson.build - Make license here match LICENSE - According to meson docs, it should not be an array --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 05f77ce..b95b5c0 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('tio', 'c', version : '3.9', - license : [ 'GPL-2'], + license : 'GPL-2.0-or-later', meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] )