From 0eb59b09cf6d91ab46c151435b3d259522134b98 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 23 Jul 2022 14:52:58 +0200 Subject: [PATCH 001/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a79bc15..f0539ad 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ when used in combination with [tmux](https://tmux.github.io). * Easily connect to serial TTY devices * Automatic connect and reconnect - * Support for arbitrary baud rates + * Support for non-standard baud rates * List available serial devices by ID * Show RX/TX statistics * Toggle serial lines From b5273b2b2363f78a754850b1b10d5039436f7180 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 23 Jul 2022 20:05:51 +0200 Subject: [PATCH 002/506] Bump version --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 6f11ea4..2d87d85 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('tio', 'c', - version : '1.47', + version : '1.48', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] From 463415d63b383b1e4420b5fcadfa91b64a6d7bb4 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 24 Jul 2022 20:57:34 +0200 Subject: [PATCH 003/506] Remove duplicate show config entry of DTR pulse duration --- src/options.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/options.c b/src/options.c index 4964674..b5e6f71 100644 --- a/src/options.c +++ b/src/options.c @@ -237,7 +237,6 @@ void options_print() tio_printf(" Timestamp: %s", timestamp_state_to_string(option.timestamp)); tio_printf(" Output delay: %d", option.output_delay); tio_printf(" Output line delay: %d", option.output_line_delay); - tio_printf(" DTR pulse duration: %d", option.dtr_pulse_duration); tio_printf(" Auto connect: %s", option.no_autoconnect ? "disabled" : "enabled"); tio_printf(" Pulse duration: DTR=%d RTS=%d CTS=%d DSR=%d DCD=%d RI=%d", option.dtr_pulse_duration, option.rts_pulse_duration, From 4c9f28203d37cc132793f0336836a176132c9a28 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 25 Jul 2022 10:58:46 +0200 Subject: [PATCH 004/506] Update README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f0539ad..8eb5038 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ when used in combination with [tmux](https://tmux.github.io). * Color support * Remapping of prefix key * Man page documentation + * Plays nicely with tmux ## 3. Usage From 31459f851bc297f70355c5387e5abb84fe4a277d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 25 Jul 2022 11:01:31 +0200 Subject: [PATCH 005/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8eb5038..5a5029e 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ when used in combination with [tmux](https://tmux.github.io). * Color support * Remapping of prefix key * Man page documentation - * Plays nicely with tmux + * Plays nicely with [tmux](https://tmux.github.io) ## 3. Usage From dd785f9a3006fb27429919a0da2b5e4c6fcdbecf Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 25 Jul 2022 11:04:05 +0200 Subject: [PATCH 006/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5a5029e..07f5943 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ when used in combination with [tmux](https://tmux.github.io). ## 2. Features - * Easily connect to serial TTY devices + * Easily connect to serial TTY devices with sensible defaults * Automatic connect and reconnect * Support for non-standard baud rates * List available serial devices by ID From 1fb0cad7b9ad3ef33d6f5d35a48e3687c0e248bb Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 25 Jul 2022 11:06:08 +0200 Subject: [PATCH 007/506] Update README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 07f5943..5d30b93 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,9 @@ when used in combination with [tmux](https://tmux.github.io). ## 2. Features - * Easily connect to serial TTY devices with sensible defaults + * Easily connect to serial TTY devices * Automatic connect and reconnect + * Sensible defaults * Support for non-standard baud rates * List available serial devices by ID * Show RX/TX statistics From 70f69899fc46780857f94c61f8dd58a0a2f661e1 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 25 Jul 2022 12:48:20 +0200 Subject: [PATCH 008/506] Cleanup --- src/print.c | 23 +++++++++++++++++++++++ src/print.h | 9 +++++++++ src/tty.c | 21 ++++++++++++++++++++- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/print.c b/src/print.c index 359c078..cf130f0 100644 --- a/src/print.c +++ b/src/print.c @@ -20,7 +20,9 @@ */ #include +#include #include +#include #include "options.h" #include "print.h" @@ -50,3 +52,24 @@ void print_init_ansi_formatting() sprintf(ansi_format, "\e[1;38;5;%dm", option.color); } } + +void tio_printf_array(const char *array) +{ + int i = 0, j = 0; + + tio_printf(""); + + while (array[i]) + { + if (array[i] == '\n') + { + const char *line = &array[j]; + char *line_copy = strndup(line, i-j); + tio_printf_raw("%s\r", line_copy); + free(line_copy); + j = i; + } + i++; + } + tio_printf(""); +} diff --git a/src/print.h b/src/print.h index c212a7c..24a02b9 100644 --- a/src/print.h +++ b/src/print.h @@ -74,6 +74,14 @@ extern char ansi_format[]; print_tainted = false; \ } +#define tio_printf_raw(format, args...) \ +{ \ + if (print_tainted) \ + putchar('\n'); \ + ansi_printf_raw("[%s] " format, current_time(), ## args); \ + print_tainted = false; \ +} + #ifdef DEBUG #define tio_debug_printf(format, args...) \ fprintf (stdout, "[debug] " format, ## args) @@ -87,3 +95,4 @@ extern char ansi_format[]; void print_hex(char c); void print_normal(char c); void print_init_ansi_formatting(void); +void tio_printf_array(const char *array); diff --git a/src/tty.c b/src/tty.c index 43cfb91..e653c6e 100644 --- a/src/tty.c +++ b/src/tty.c @@ -74,13 +74,14 @@ #define KEY_G 0x67 #define KEY_H 0x68 #define KEY_L 0x6C +#define KEY_SHIFT_L 0x4C #define KEY_P 0x70 #define KEY_Q 0x71 #define KEY_S 0x73 #define KEY_T 0x74 #define KEY_U 0x55 #define KEY_V 0x76 -#define KEY_SHIFT_L 0x4C +#define KEY_Z 0x7a #define NORMAL 0 #define HEX 1 @@ -92,6 +93,20 @@ enum line_mode_t LINE_PULSE }; +const char random_array[] = +{ +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x20, 0x28, 0x0A, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x29, 0x20, 0x29, 0x0A, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, +0x2E, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7C, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x7C, 0x5D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x5C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2F, 0x0A, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x60, 0x2D, 0x2D, 0x2D, 0x2D, 0x27, 0x0A, 0x0A, 0x54, +0x69, 0x6D, 0x65, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x61, 0x20, 0x63, 0x6F, 0x66, +0x66, 0x65, 0x65, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6B, 0x21, 0x0A, 0x20, 0x0A, +0x00 +}; + bool interactive_mode = true; static struct termios tio, tio_old, stdout_new, stdout_old, stdin_new, stdin_old; @@ -517,6 +532,10 @@ void handle_command_sequence(char input_char, char previous_char, char *output_c tio_printf("tio v%s", VERSION); break; + case KEY_Z: + tio_printf_array(random_array); + break; + default: /* Ignore unknown ctrl-t escaped keys */ break; From 4952c6ca13e22605384841fe5a4d235f5536bb9e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 25 Jul 2022 20:29:06 +0200 Subject: [PATCH 009/506] Update TODO --- TODO | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/TODO b/TODO index 7801868..12f1d9c 100644 --- a/TODO +++ b/TODO @@ -17,3 +17,20 @@ tio /dev/ttyUSB0 --socket unix:/tmp/tio-socket-0,split-io Will result in output stream being hosted via /tmp/tio-socket-0 and input stream hosted via /tmp/tio-socket-0_input + + * Websocket support + + Extend the socket feature to redirect serial I/O to websocket or e.g. port 1234 like so + + $ tio --socket ws:1234 + + Use libwesockets to implement feature. + + * RS-485 support + + Many modern RS-485 devices such as the ones from FTDI already operate in + RS-485 mode by default and will work with tio out of the box. However, there + are still some RS-232/485 devices which need to be switched from e.g. RS-232 + to RS-485 mode to operate accordingly on the physical level. + + To support RS-485 on such serial devices the idea is to add a --rs-485 option. From 459834516837680593fe230e75fc98e0ba89895d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 25 Jul 2022 20:36:38 +0200 Subject: [PATCH 010/506] Update TODO --- TODO | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/TODO b/TODO index 12f1d9c..cc02ccc 100644 --- a/TODO +++ b/TODO @@ -1,26 +1,30 @@ * Split I/O feature - Allow to split input and output so that it is possible to manage these independently. + Allow to split input and output so that it is possible to manage these + independently. The general idea is to redirect the output stream on the socket port number specified but then redirect the input stream on the same port number + 1. Example: - tio /dev/ttyUSB0 --socket inet:4444,split-io + $ tio /dev/ttyUSB0 --socket inet:4444,split-io - Will result in output stream being hosted on port 4444 and input stream hosted on port 4445. + Will result in output stream being hosted on port 4444 and input stream + hosted on port 4445. For file sockets something similar can be arranged: tio /dev/ttyUSB0 --socket unix:/tmp/tio-socket-0,split-io - Will result in output stream being hosted via /tmp/tio-socket-0 and input stream hosted via /tmp/tio-socket-0_input + Will result in output stream being hosted via /tmp/tio-socket-0 and input + stream hosted via /tmp/tio-socket-0_input * Websocket support - Extend the socket feature to redirect serial I/O to websocket or e.g. port 1234 like so + Extend the socket feature to redirect serial I/O to websocket on e.g. port + 1234 like so: $ tio --socket ws:1234 @@ -30,7 +34,7 @@ Many modern RS-485 devices such as the ones from FTDI already operate in RS-485 mode by default and will work with tio out of the box. However, there - are still some RS-232/485 devices which need to be switched from e.g. RS-232 - to RS-485 mode to operate accordingly on the physical level. + are still some RS-232/485 devices which need to be switched from e.g. RS-232 to + RS-485 mode to operate accordingly on the physical level. To support RS-485 on such serial devices the idea is to add a --rs-485 option. From 99c7aa85ed0434f5e314ae3f5f5c2e070ad7c5a8 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 25 Jul 2022 20:38:04 +0200 Subject: [PATCH 011/506] Update TODO --- TODO | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/TODO b/TODO index cc02ccc..c0496e3 100644 --- a/TODO +++ b/TODO @@ -16,7 +16,7 @@ For file sockets something similar can be arranged: - tio /dev/ttyUSB0 --socket unix:/tmp/tio-socket-0,split-io + $ tio /dev/ttyUSB0 --socket unix:/tmp/tio-socket-0,split-io Will result in output stream being hosted via /tmp/tio-socket-0 and input stream hosted via /tmp/tio-socket-0_input @@ -37,4 +37,5 @@ are still some RS-232/485 devices which need to be switched from e.g. RS-232 to RS-485 mode to operate accordingly on the physical level. - To support RS-485 on such serial devices the idea is to add a --rs-485 option. + To enable RS-485 mode on such serial devices the idea is to add a --rs-485 + option. From 040743662452a7cac9789d9ca8f34f583c85f44d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 27 Jul 2022 15:06:32 +0200 Subject: [PATCH 012/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5d30b93..e8d3c58 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ when used in combination with [tmux](https://tmux.github.io). * Autogeneration of log filename * Configuration file support * Activate sub-configurations by name or pattern - * Redirect I/O to file or network socket for scripting or TTY sharing + * Redirect I/O to file or IPv4/v6 network socket for scripting or TTY sharing * Pipe input and/or output * Bash completion * Color support From a9b7284ba2b8c4dbafe8ce0409963d028d546c19 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 28 Jul 2022 21:21:04 +0200 Subject: [PATCH 013/506] Revert "Enable log feature when using --log-filename" This reverts commit c3116b2b565d1a202f74a62f664d1d83c7f6ac84. On second thought, this is less appropriate when using the configuration file. We may want to define the log filename in config file but not necessarily enable log feature. --- src/options.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/options.c b/src/options.c index b5e6f71..7d52028 100644 --- a/src/options.c +++ b/src/options.c @@ -371,7 +371,6 @@ void options_parse(int argc, char *argv[]) break; case OPT_LOG_FILE: - option.log = true; option.log_filename = optarg; break; From 15bef05179033377906d5a9c37485f66a9de07df Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 28 Jul 2022 21:32:00 +0200 Subject: [PATCH 014/506] Only print version on '--version' --- src/options.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/options.c b/src/options.c index 7d52028..ebbdc17 100644 --- a/src/options.c +++ b/src/options.c @@ -422,11 +422,6 @@ void options_parse(int argc, char *argv[]) case 'v': printf("tio v%s\n", VERSION); - printf("Copyright (c) 2014-2022 Martin Lund\n"); - printf("\n"); - printf("License GPLv2+: GNU GPL version 2 or later .\n"); - printf("This is free software: you are free to change and redistribute it.\n"); - printf("There is NO WARRANTY, to the extent permitted by law.\n"); exit(EXIT_SUCCESS); break; From 75aa066bf82122007013d4883ce2636369c43a88 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 6 Aug 2022 16:54:40 +0200 Subject: [PATCH 015/506] Fix potential sscanf() overflow --- src/options.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/options.c b/src/options.c index ebbdc17..4bd2540 100644 --- a/src/options.c +++ b/src/options.c @@ -187,7 +187,7 @@ void line_pulse_duration_option_parse(const char *arg) if (token != NULL) { - char keyname[10]; + char keyname[11]; unsigned int value; sscanf(token, "%10[^=]=%d", keyname, &value); From 72eb6822670a23a29849c08e5d8d6a321e69957a Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 6 Aug 2022 17:12:16 +0200 Subject: [PATCH 016/506] Cleanup hex mode code --- src/options.c | 1 + src/tty.c | 16 +++++----------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/options.c b/src/options.c index 4bd2540..df3c2bc 100644 --- a/src/options.c +++ b/src/options.c @@ -244,6 +244,7 @@ void options_print() option.dsr_pulse_duration, option.dcd_pulse_duration, option.ri_pulse_duration); + tio_printf(" Hexadecimal mode: %s", option.hex_mode ? "enabled" : "disabled"); if (option.map[0] != 0) tio_printf(" Map flags: %s", option.map); if (option.log) diff --git a/src/tty.c b/src/tty.c index e653c6e..d04679e 100644 --- a/src/tty.c +++ b/src/tty.c @@ -83,9 +83,6 @@ #define KEY_V 0x76 #define KEY_Z 0x7a -#define NORMAL 0 -#define HEX 1 - enum line_mode_t { LINE_OFF, @@ -112,7 +109,6 @@ bool interactive_mode = true; 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 print_mode = NORMAL; static bool standard_baudrate = true; static void (*print)(char c); static int fd; @@ -469,16 +465,16 @@ void handle_command_sequence(char input_char, char previous_char, char *output_c case KEY_H: /* Toggle hexadecimal printing mode */ - if (print_mode == NORMAL) + if (!option.hex_mode) { print = print_hex; - print_mode = HEX; + option.hex_mode = true; tio_printf("Switched to hexadecimal mode"); } else { print = print_normal; - print_mode = NORMAL; + option.hex_mode = false; tio_printf("Switched to normal mode"); } break; @@ -988,7 +984,7 @@ void forward_to_tty(int fd, char output_char) } else { - if (print_mode == HEX) + if (option.hex_mode) { output_hex(output_char); } @@ -1060,12 +1056,10 @@ int tty_connect(void) if (option.hex_mode) { print = print_hex; - print_mode = HEX; } else { print = print_normal; - print_mode = NORMAL; } /* Save current port settings */ @@ -1228,7 +1222,7 @@ int tty_connect(void) /* Save previous key */ previous_char = input_char; - if ((print_mode == HEX) && (forward)) + if ((option.hex_mode) && (forward)) { if (!is_valid_hex(input_char)) { From b3a0917bc9fe94ad1b8b5e473515c996e4dae854 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 6 Aug 2022 17:12:49 +0200 Subject: [PATCH 017/506] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1eef64a..40120d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /build /subprojects/libinih *.swp +.cache From f82d2353d4b5b0be0aba4b2c8cf0a77fc05be2be Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 8 Aug 2022 15:08:34 +0200 Subject: [PATCH 018/506] Update README --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index e8d3c58..7ae7bfc 100644 --- a/README.md +++ b/README.md @@ -227,6 +227,13 @@ If you have [MacPorts](https://www.macports.org) installed: $ sudo port install tio ``` +### 4.4 Installation using MSYS2 (Windows) + +If you have [MSYS2](https://www.msys2.org) installed: +``` + $ pacman -S tio +``` + ### 4.5 Installation from source The latest source releases can be found [here](https://github.com/tio/tio/releases). From 614517bfc4c831bb37a3c3b495c635ec41e5e47b Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 13 Aug 2022 22:50:22 +0200 Subject: [PATCH 019/506] Update README --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 7ae7bfc..c7763ea 100644 --- a/README.md +++ b/README.md @@ -247,8 +247,7 @@ Install steps: See meson\_options.txt for tio specific build options. -Note: It is recommended to only try to install from source if you are familiar -with how to build stuff using meson. +Note: The meson install steps may differ depending on your specific system. ## 5. Contributing From a75e04b883b6c73c942e16ef7e10b72fb146d4c8 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 15 Aug 2022 11:00:47 +0200 Subject: [PATCH 020/506] Update man page --- man/tio.1.in | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 48623b2..38ce30a 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -60,13 +60,19 @@ valur pair format in the duration field: = Each key represents a serial line. The following keys are available: .RS -.TP 12n -.IP "\fBDTR - Data Terminal Ready" -.IP "\fBRTS - Request To Send" -.IP "\fBCTS - Clear To Send" -.IP "\fBDSR - Data Set Ready" -.IP "\fBDCD - Data Carrier Detect" -.IP "\fBRI - Ring Indicator" +.TP 8n +.IP "\fBDTR +Data Terminal Ready" +.IP "\fBRTS +Request To Send" +.IP "\fBCTS +Clear To Send" +.IP "\fBDSR +Data Set Ready" +.IP "\fBDCD +Data Carrier Detect" +.IP "\fBRI +Ring Indicator" .P If defining more than one key value pair, the pairs must be comma separated. @@ -288,7 +294,7 @@ Any options set via command-line will override options set in the configuration .PP The following configuration file options are available: -.TP 21n +.TP 25n .IP "\fBpattern" Pattern matching user input. This pattern can be an extended regular expression with a single group. .IP "\fBtty" From e837fd0303ecc925741f9f0eabf8bc58e7fcd492 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 13 Aug 2022 00:04:00 +0200 Subject: [PATCH 021/506] Add line response feature Add a simple line response feature to make it possible to send e.g. a command string to your serial device and easily receive and parse a line response. This is a convenience feature for simple request/response interaction based on lines. For more advanced interaction the socket feature should be used instead. The line response feature is detailed via the following options: -r, --response-wait Wait for line response then quit. A line is considered any string ending with either CR or NL character. If no line is received tio will quit after response timeout. Any tio text is automatically muted when piping a string to tio while in response mode to make it easy to parse the response. --response-timeout Set timeout [ms] of line response (default: 100). Example: Sending a string (SCPI command) to a test instrument (Korad PSU) and print line response: $ echo "*IDN?" | tio /dev/ttyACM0 --response-wait KORAD KD3305P V4.2 SN:32477045 --- README.md | 3 +++ man/tio.1.in | 28 +++++++++++++++++++- src/bash-completion/tio.in | 10 +++++++ src/configfile.c | 16 ++++++++++- src/main.c | 6 +++++ src/options.c | 18 ++++++++++++- src/options.h | 3 +++ src/print.h | 12 +++++++++ src/tty.c | 54 +++++++++++++++++++++++++++++++++++--- 9 files changed, 143 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c7763ea..5032175 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ when used in combination with [tmux](https://tmux.github.io). * Activate sub-configurations by name or pattern * Redirect I/O to file or IPv4/v6 network socket for scripting or TTY sharing * Pipe input and/or output + * Support for simple line request/response handling * Bash completion * Color support * Remapping of prefix key @@ -87,6 +88,8 @@ The command-line interface is straightforward as reflected in the output from -c, --color 0..255|bold|none|list Colorize tio text (default: bold) -S, --socket Redirect I/O to file or network socket -x, --hexadecimal Enable hexadecimal mode + -r, --response-wait Wait for line response then quit + --response-timeout Response timeout (default: 100) -v, --version Display version -h, --help Display help diff --git a/man/tio.1.in b/man/tio.1.in index 38ce30a..2102af7 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -214,6 +214,21 @@ If port is 0 or no port is provided default port 3333 is used. At present there is a hardcoded limit of 16 clients connected at one time. .RE +.TP +.BR \-r ", " \-\-response-wait + +Wait for line response then quit. A line is considered any string ending with +either CR or NL character. If no line is received tio will quit after response +timeout. + +Any tio text is automatically muted when piping a string to tio while in +response mode to make it easy to parse the response. + +.TP +.BR " \-\-response\-timeout " \fI + +Set timeout [ms] of line response (default: 100). + .TP .BR \-v ", " \-\-version @@ -339,6 +354,10 @@ Enable hexadecimal mode Set socket to redirect I/O to .IP "\fBprefix-ctrl-key" Set prefix ctrl key (a..z, default: t) +.IP "\fBresponse-wait" +Enable wait for line response +.IP "\fBresponse-timeout" +Set line response timeout .SH "CONFIGURATION FILE EXAMPLES" @@ -475,7 +494,14 @@ $ nc -N 10.0.0.42 4444 Pipe command to the serial device: $ echo "ls -la" | tio /dev/serial/by\-id/usb\-FTDI_TTL232R-3V3_FTGQVXBL\-if00\-port0 - + +.TP +Pipe command to the serial device and wait for line response (string ending with CR or NL): + +$ echo "*IDN?" | tio /dev/ttyACM0 --response-wait +.TP +In this mode, only the response will be printed. + .TP Likewise, to pipe data from file to the serial device: diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index 3d87cb6..67b936c 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -29,6 +29,8 @@ _tio() -L --list-devices \ -c --color \ -S --socket \ + -r --response-wait \ + --response-timeout \ -x --hexadecimal \ -v --version \ -h --help" @@ -112,6 +114,14 @@ _tio() COMPREPLY=( $(compgen -W "unix: inet: inet6:" -- ${cur}) ) return 0 ;; + -r | --response-wait) + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + ;; + --response-timeout) + COMPREPLY=( $(compgen -W "1 10 100" -- ${cur}) ) + return 0 + ;; -x | --hexadecimal) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 diff --git a/src/configfile.c b/src/configfile.c index b8a1649..1a31ae3 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -249,7 +249,21 @@ static int data_handler(void *user, const char *section, const char *name, option.prefix_key = value[0]; } } - + else if (!strcmp(name, "response-wait")) + { + if (!strcmp(value, "enable")) + { + option.response_wait = true; + } + else if (!strcmp(value, "disable")) + { + option.response_wait = false; + } + } + else if (!strcmp(name, "response-timeout")) + { + option.response_timeout = atoi(value); + } } return 0; } diff --git a/src/main.c b/src/main.c index 7f31645..02f8a47 100644 --- a/src/main.c +++ b/src/main.c @@ -63,6 +63,12 @@ int main(int argc, char *argv[]) { // Enter non interactive mode interactive_mode = false; + + // Mute tio text in response mode + if (option.response_wait) + { + option.mute = true; + } } /* Configure output terminal */ diff --git a/src/options.c b/src/options.c index df3c2bc..49e99d2 100644 --- a/src/options.c +++ b/src/options.c @@ -43,6 +43,7 @@ enum opt_t OPT_LOG_FILE, OPT_LOG_STRIP, OPT_LINE_PULSE_DURATION, + OPT_RESPONSE_TIMEOUT, }; /* Default options */ @@ -74,6 +75,9 @@ struct option_t option = .hex_mode = false, .prefix_code = 20, // ctrl-t .prefix_key = 't', + .response_wait = false, + .response_timeout = 100, + .mute = false, }; void print_help(char *argv[]) @@ -103,6 +107,8 @@ void print_help(char *argv[]) printf(" -c, --color 0..255|bold|none|list Colorize tio text (default: bold)\n"); printf(" -S, --socket Redirect I/O to file or network socket\n"); printf(" -x, --hexadecimal Enable hexadecimal mode\n"); + printf(" -r, --response-wait Wait for line response then quit\n"); + printf(" --response-timeout Response timeout (default: 100)\n"); printf(" -v, --version Display version\n"); printf(" -h, --help Display help\n"); printf("\n"); @@ -287,6 +293,8 @@ void options_parse(int argc, char *argv[]) {"map", required_argument, 0, 'm' }, {"color", required_argument, 0, 'c' }, {"hexadecimal", no_argument, 0, 'x' }, + {"response-wait", no_argument, 0, 'r' }, + {"response-timeout", required_argument, 0, OPT_RESPONSE_TIMEOUT }, {"version", no_argument, 0, 'v' }, {"help", no_argument, 0, 'h' }, {0, 0, 0, 0 } @@ -296,7 +304,7 @@ void options_parse(int argc, char *argv[]) int option_index = 0; /* Parse argument using getopt_long */ - c = getopt_long(argc, argv, "b:d:f:s:p:o:O:netLlS:m:c:xvh", long_options, &option_index); + c = getopt_long(argc, argv, "b:d:f:s:p:o:O:netLlS:m:c:xrvh", long_options, &option_index); /* Detect the end of the options */ if (c == -1) @@ -421,6 +429,14 @@ void options_parse(int argc, char *argv[]) option.hex_mode = true; break; + case 'r': + option.response_wait = true; + break; + + case OPT_RESPONSE_TIMEOUT: + option.response_timeout = string_to_long(optarg); + break; + case 'v': printf("tio v%s\n", VERSION); exit(EXIT_SUCCESS); diff --git a/src/options.h b/src/options.h index ed8a4fa..7650047 100644 --- a/src/options.h +++ b/src/options.h @@ -67,6 +67,9 @@ struct option_t bool hex_mode; unsigned char prefix_code; unsigned char prefix_key; + bool response_wait; + int response_timeout; + bool mute; }; extern struct option_t option; diff --git a/src/print.h b/src/print.h index 24a02b9..c2aba7c 100644 --- a/src/print.h +++ b/src/print.h @@ -33,53 +33,65 @@ extern char ansi_format[]; #define ansi_printf(format, args...) \ { \ + if (!option.mute) { \ if (option.color < 0) \ fprintf (stdout, "\r" format "\r\n", ## args); \ else \ fprintf (stdout, "\r%s" format ANSI_RESET "\r\n", ansi_format, ## args); \ + } \ } #define ansi_error_printf(format, args...) \ { \ + if (!option.mute) { \ if (option.color < 0) \ fprintf (stderr, "\r" format "\r\n", ## args); \ else \ fprintf (stderr, "\r%s" format ANSI_RESET "\r\n", ansi_format, ## args); \ fflush(stderr); \ + } \ } #define ansi_printf_raw(format, args...) \ { \ + if (!option.mute) { \ if (option.color < 0) \ fprintf (stdout, format, ## args); \ else \ fprintf (stdout, "%s" format ANSI_RESET, ansi_format, ## args); \ + } \ } #define tio_warning_printf(format, args...) \ { \ + if (!option.mute) { \ if (print_tainted) \ putchar('\n'); \ if (option.color < 0) \ fprintf (stdout, "\r[%s] Warning: " format "\r\n", current_time(), ## args); \ else \ ansi_printf("[%s] Warning: " format, current_time(), ## args); \ + } \ } #define tio_printf(format, args...) \ { \ + if (!option.mute) { \ if (print_tainted) \ putchar('\n'); \ ansi_printf("[%s] " format, current_time(), ## args); \ print_tainted = false; \ + } \ } #define tio_printf_raw(format, args...) \ { \ + if (!option.mute) { \ if (print_tainted) \ putchar('\n'); \ ansi_printf_raw("[%s] " format, current_time(), ## args); \ print_tainted = false; \ + } \ } #ifdef DEBUG diff --git a/src/tty.c b/src/tty.c index d04679e..a1e4233 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1015,6 +1015,9 @@ int tty_connect(void) int status; bool next_timestamp = false; char* now = NULL; + struct timeval tv; + struct timeval *tv_p = &tv; + bool ignore_stdin = false; /* Open tty device */ fd = open(option.tty_device, O_RDWR | O_NOCTTY | O_NONBLOCK); @@ -1108,12 +1111,28 @@ int tty_connect(void) { FD_ZERO(&rdfs); FD_SET(fd, &rdfs); - FD_SET(STDIN_FILENO, &rdfs); + if (!ignore_stdin) + { + FD_SET(STDIN_FILENO, &rdfs); + } maxfd = MAX(fd, STDIN_FILENO); maxfd = MAX(maxfd, socket_add_fds(&rdfs, true)); + /* Manage timeout */ + if ((option.response_wait) && (option.response_timeout != 0)) + { + // Set response timeout + tv_p->tv_sec = 0; + tv_p->tv_usec = option.response_timeout * 1000; + } + else + { + // No timeout + tv_p = NULL; + } + /* Block until input becomes available */ - status = select(maxfd + 1, &rdfs, NULL, NULL, NULL); + status = select(maxfd + 1, &rdfs, NULL, NULL, tv_p); if (status > 0) { bool forward = false; @@ -1181,6 +1200,15 @@ int tty_connect(void) { next_timestamp = true; } + + if (option.response_wait) + { + if ((input_char == '\r') || (input_char == '\n')) + { + tty_sync(fd); + exit(EXIT_SUCCESS); + } + } } } else if (FD_ISSET(STDIN_FILENO, &rdfs)) @@ -1195,8 +1223,21 @@ int tty_connect(void) else if (bytes_read == 0) { /* Reached EOF (when piping to stdin) */ - tty_sync(fd); - exit(EXIT_SUCCESS); + if (option.response_wait) + { + /* Stdin pipe closed but not blocking so stop listening + * to stdin in response mode. + * + * Note: select() really indicates not if data is ready + * but if file descriptor is non-blocking for I/O + * operation. */ + ignore_stdin = true; + } + else + { + tty_sync(fd); + exit(EXIT_SUCCESS); + } } /* Process input byte by byte */ @@ -1257,6 +1298,11 @@ int tty_connect(void) tio_error_printf("select() failed (%s)", strerror(errno)); exit(EXIT_FAILURE); } + else + { + // Timeout (only happens in response wait mode) + exit(EXIT_FAILURE); + } } return TIO_SUCCESS; From 34e9d028a13e95027b4eea3fd8132b40f8271d15 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 15 Aug 2022 20:05:40 +0200 Subject: [PATCH 022/506] Bump version to v2.0 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 2d87d85..eba9fdc 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('tio', 'c', - version : '1.48', + version : '2.0', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] From 1219c6b8c06e0c551e72771cf031efd6eefd9d90 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 16 Aug 2022 05:12:39 +0200 Subject: [PATCH 023/506] Update README --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5032175..f002f2a 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,18 @@ $ tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 Using serial devices by ID ensures that tio automatically reconnects to the correct serial device if it is disconnected and then reconnected. + +Inject data to the serial device: +``` +$ cat data.bin | tio /dev/ttyUSB0 +``` + +Send command to serial device and wait for line response: +``` +$ echo "*IDN?" | tio /dev/ttyACM0 --response-wait +KORAD KD3305P V4.2 SN:32475045 +``` + ### 3.2 Key commands Various in session key commands are supported. When tio is started, press @@ -207,7 +219,7 @@ Packages for various GNU/Linux distributions are available. Please consult your package manager tool to find and install tio. If you would like to see tio included in your favorite distribution, please -reach out to their package maintainers team. +reach out to its package maintainers team. ### 4.2 Installation using snap (Linux) From db765bc3719eef67b14a93cc8d8aed80fbc8614b Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Wed, 17 Aug 2022 16:39:12 -0700 Subject: [PATCH 024/506] Ignore SIGPIPE signals If the remote end of a socket is closed between when an input character is received from the serial port and when it is written to the socket, tio will receive a SIGPIPE signal when writing the character to the socket, which will terminate the program. To prevent this, ignore the signal, which will cause write(2) to return -EPIPE, causing tio to close the socket. --- src/signals.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/signals.c b/src/signals.c index 02ccbbe..6829b44 100644 --- a/src/signals.c +++ b/src/signals.c @@ -47,4 +47,5 @@ void signal_handlers_install(void) { signal(SIGHUP, signal_handler); signal(SIGINT, signal_handler); + signal(SIGPIPE, SIG_IGN); } From d2aafc24cc7bfdddfa125fd989861e1476eddf8c Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 19 Aug 2022 15:47:19 +0200 Subject: [PATCH 025/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f002f2a..ef24cbd 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ when used in combination with [tmux](https://tmux.github.io). * Autogeneration of log filename * Configuration file support * Activate sub-configurations by name or pattern - * Redirect I/O to file or IPv4/v6 network socket for scripting or TTY sharing + * Redirect I/O to UNIX socket or IPv4/v6 network socket for scripting or TTY sharing * Pipe input and/or output * Support for simple line request/response handling * Bash completion From 0dd703e5a2f8c4467ed400f87078d0c8da9d837a Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 23 Aug 2022 12:54:20 +0200 Subject: [PATCH 026/506] Remove MacPorts instructions Remove instructions for MacPorts because the port has no maintainer and the port build definition is broken (missing dependency on libinih etc.). It is recommended to use brew instead. --- README.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/README.md b/README.md index ef24cbd..70a858c 100644 --- a/README.md +++ b/README.md @@ -235,13 +235,6 @@ If you have [brew](http://brew.sh) installed: $ brew install tio ``` -### 4.4 Installation using MacPorts (MacOS) - -If you have [MacPorts](https://www.macports.org) installed: -``` - $ sudo port install tio -``` - ### 4.4 Installation using MSYS2 (Windows) If you have [MSYS2](https://www.msys2.org) installed: From be7f77e214df319d46248830267550a9ac608e7b Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 23 Aug 2022 13:13:40 +0200 Subject: [PATCH 027/506] Update NEWS --- NEWS | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index e7305cc..f87839e 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,66 @@ -=== tio v1.47 === +=== tio v2.0 === + + + +Changes since tio v1.47: + + * Add line response feature + + Add a simple line response feature to make it possible to send e.g. a + command string to your serial device and easily receive and parse a line + response. + + This is a convenience feature for simple request/response interaction + based on lines. For more advanced interaction the socket feature should + be used instead. + + The line response feature is detailed via the following options: + + -r, --response-wait + + Wait for line response then quit. A line is considered any string ending + with either CR or NL character. If no line is received tio will quit + after response timeout. + + Any tio text is automatically muted when piping a string to tio while in + response mode to make it easy to parse the response. + + --response-timeout + + Set timeout [ms] of line response (default: 100). + + Example: + + Sending a string (SCPI command) to a test instrument (Korad PSU) and + print line response: + + $ echo "*IDN?" | tio /dev/ttyACM0 --response-wait + KORAD KD3305P V4.2 SN:32477045 + + * Fix potential sscanf() overflow + + * Only print version on '--version' + + * Remove duplicate show config entry of DTR pulse duration + + * Remove MacPorts instructions + + Remove instructions for MacPorts because the port has no maintainer and + the port build definition is broken (missing dependency on libinih etc.). + + It is recommended to use brew instead. + +Peter Collingbourne: + + * Ignore SIGPIPE signals + + If the remote end of a socket is closed between when an input character + is received from the serial port and when it is written to the socket, + tio will receive a SIGPIPE signal when writing the character to the + socket, which will terminate the program. To prevent this, ignore the + signal, which will cause write(2) to return -EPIPE, causing tio to close + the socket. From fe6827bc7e2615f4428a8618c32cb7916aa9f3fe Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 30 Aug 2022 01:11:27 +0200 Subject: [PATCH 028/506] Clean up man page --- man/tio.1.in | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 2102af7..16120db 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -55,24 +55,24 @@ Set output delay [ms] inserted between each sent line (default: 0). .BR " \-\-line\-pulse\-duration " \fI Set the pulse duration [ms] of each serial port line using the following key -valur pair format in the duration field: = +value pair format in the duration field: = Each key represents a serial line. The following keys are available: .RS .TP 8n -.IP "\fBDTR -Data Terminal Ready" -.IP "\fBRTS -Request To Send" -.IP "\fBCTS -Clear To Send" -.IP "\fBDSR -Data Set Ready" -.IP "\fBDCD -Data Carrier Detect" -.IP "\fBRI -Ring Indicator" +.IP \fBDTR +Data Terminal Ready +.IP \fBRTS +Request To Send +.IP \fBCTS +Clear To Send +.IP \fBDSR +Data Set Ready +.IP \fBDCD +Data Carrier Detect +.IP \fBRI +Ring Indicator .P If defining more than one key value pair, the pairs must be comma separated. From a58d406a3cee15b750a83ca00e7601bbf04ea03d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 8 Sep 2022 09:32:51 +0200 Subject: [PATCH 029/506] Update list description --- man/tio.1.in | 2 +- src/options.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 16120db..9d81c2d 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -124,7 +124,7 @@ Default format is \fB24hour\fR .TP .BR \-L ", " \-\-list\-devices -List available serial devices. +List available serial devices by ID. .TP .BR \-l ", " \-\-log diff --git a/src/options.c b/src/options.c index 49e99d2..e11af12 100644 --- a/src/options.c +++ b/src/options.c @@ -99,7 +99,7 @@ void print_help(char *argv[]) printf(" -e, --local-echo Enable local echo\n"); printf(" -t, --timestamp Enable line timestamp\n"); printf(" --timestamp-format Set timestamp format (default: 24hour)\n"); - printf(" -L, --list-devices List available serial devices\n"); + printf(" -L, --list-devices List available serial devices by ID\n"); printf(" -l, --log Enable log to file\n"); printf(" --log-file Set log filename\n"); printf(" --log-strip Strip control characters and escape sequences\n"); From ee46686fb664539d4892af9642ad2ae6f14b4bb0 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 10 Sep 2022 15:20:42 +0200 Subject: [PATCH 030/506] Add experimental RS-485 support Many modern RS-485 serial devices such as the ones from FTDI already operate in RS-485 mode by default and will work with tio out of the box. However, there are some RS-232/485 devices which need to be switched from e.g. RS-232 to RS-485 mode to operate accordingly on the physical level. This commit implements the switching mechanism and interface required to enable RS-485 mode. It only works on Linux and with serial devices which use device drivers that support the Linux RS-485 control interface. The RS-485 feature is detailed via the following options: --rs-485 Enable RS-485 mode --rs-485-config Set RS-485 configuration Set the RS-485 configuration using the following key or key value pair format in the configuration field: RTS_ON_SEND=value Set logical level (0 or 1) for RTS pin when sending RTS_AFTER_SEND=value Set logical level (0 or 1) for RTS pin after sending RTS_DELAY_BEFORE_SEND=value Set RTS delay (ms) before sending RTS_DELAY_AFTER_SEND=value Set RTS delay (ms) after sending RX_DURING_TX Receive data even while sending data If defining more than one key or key value pair, they must be comma separated. Example use: $ tio /dev/ttyUSB0 --rs-485 --rs-r485-config=RTS_DELAY_AFTER_SEND=50,RX_DURING_TX --- TODO | 10 -- example/tiorc | 6 ++ man/tio.1.in | 36 +++++++ meson.build | 7 ++ src/bash-completion/tio.in | 10 ++ src/configfile.c | 17 ++++ src/meson.build | 7 +- src/options.c | 19 ++++ src/options.h | 5 + src/print.h | 1 + src/rs485.c | 200 +++++++++++++++++++++++++++++++++++++ src/rs485.h | 27 +++++ src/tty.c | 17 ++++ 13 files changed, 351 insertions(+), 11 deletions(-) create mode 100644 src/rs485.c create mode 100644 src/rs485.h diff --git a/TODO b/TODO index c0496e3..14d8025 100644 --- a/TODO +++ b/TODO @@ -29,13 +29,3 @@ $ tio --socket ws:1234 Use libwesockets to implement feature. - - * RS-485 support - - Many modern RS-485 devices such as the ones from FTDI already operate in - RS-485 mode by default and will work with tio out of the box. However, there - are still some RS-232/485 devices which need to be switched from e.g. RS-232 to - RS-485 mode to operate accordingly on the physical level. - - To enable RS-485 mode on such serial devices the idea is to add a --rs-485 - option. diff --git a/example/tiorc b/example/tiorc index a1340e3..e7f0f18 100644 --- a/example/tiorc +++ b/example/tiorc @@ -44,3 +44,9 @@ color = 11 pattern = usb([0-9]*) tty = /dev/ttyUSB%s color = 12 + +[rs-485-device] +tty = /dev/ttyUSB0 +rs-485 = enable +rs-485-config = RTS_ON_SEND=1,RTS_AFTER_SEND=1,RTS_DELAY_BEFORE_SEND=60,RTS_DELAY_AFTER_SEND=80,RX_DURING_TX +color = 13 diff --git a/man/tio.1.in b/man/tio.1.in index 9d81c2d..36b0b1c 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -229,6 +229,33 @@ response mode to make it easy to parse the response. Set timeout [ms] of line response (default: 100). +.TP +.BR " \-\-rs\-485" + +Enable RS-485 mode. + +.TP +.BR " \-\-rs\-485\-config " \fI + +Set the RS-485 configuration using the following key or key value pair format in +the configuration field: + +.RS +.TP 30n +.IP \fBRTS_ON_SEND=value +Set logical level (0 or 1) for RTS pin when sending +.IP \fBRTS_AFTER_SEND=value +Set logical level (0 or 1) for RTS pin after sending +.IP \fBRTS_DELAY_BEFORE_SEND=value +Set RTS delay (ms) before sending +.IP \fBRTS_DELAY_AFTER_SEND=value +Set RTS delay (ms) after sending +.IP \fBRX_DURING_TX +Receive data even while sending data +.P +If defining more than one key or key value pair, they must be comma separated. +.RE + .TP .BR \-v ", " \-\-version @@ -358,6 +385,10 @@ Set prefix ctrl key (a..z, default: t) Enable wait for line response .IP "\fBresponse-timeout" Set line response timeout +.IP "\fBrs-485" +Enable RS-485 mode +.IP "\fBrs-485-config" +Set RS-485 configuration .SH "CONFIGURATION FILE EXAMPLES" @@ -507,6 +538,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 +Enable RS-485 mode: + +$ tio --rs-485 --rs-485-config=RTS_ON_SEND=1,RX_DURING_TX /dev/ttyUSB0 + .SH "WEBSITE" .PP Visit https://tio.github.io diff --git a/meson.build b/meson.build index eba9fdc..d1eedcc 100644 --- a/meson.build +++ b/meson.build @@ -71,5 +71,12 @@ foreach rate : test_baudrates endif endforeach +# Test for RS-485 support on Linux +if host_machine.system() == 'linux' + if compiler.check_header('linux/serial.h') + enable_rs485 = compiler.has_header_symbol('sys/ioctl.h', 'TIOCSRS485') + endif +endif + subdir('src') subdir('man') diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index 67b936c..cea90f1 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -31,6 +31,8 @@ _tio() -S --socket \ -r --response-wait \ --response-timeout \ + --rs-485 \ + --rs-485-config \ -x --hexadecimal \ -v --version \ -h --help" @@ -122,6 +124,14 @@ _tio() COMPREPLY=( $(compgen -W "1 10 100" -- ${cur}) ) return 0 ;; + --rs-485) + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + ;; + --rs-485-config) + COMPREPLY=( $(compgen -W "RTS_ON_SEND RTS_AFTER_SEND RTS_DELAY_BEFORE_SEND RTS_DELAY_AFTER_SEND RX_DURING_TX" -- ${cur}) ) + return 0 + ;; -x | --hexadecimal) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 diff --git a/src/configfile.c b/src/configfile.c index 1a31ae3..e081a70 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -42,6 +42,7 @@ #include "options.h" #include "error.h" #include "print.h" +#include "rs485.h" static struct config_t *c; @@ -264,6 +265,22 @@ static int data_handler(void *user, const char *section, const char *name, { option.response_timeout = atoi(value); } + else if (!strcmp(name, "rs-485")) + { + if (!strcmp(value, "enable")) + { + option.rs485 = true; + } + else if (!strcmp(value, "disable")) + { + option.rs485 = false; + } + } + else if (!strcmp(name, "rs-485-config")) + { + rs485_parse_config(value); + } + } return 0; } diff --git a/src/meson.build b/src/meson.build index 464f4f3..061d070 100644 --- a/src/meson.build +++ b/src/meson.build @@ -14,7 +14,8 @@ tio_sources = [ 'configfile.c', 'signals.c', 'socket.c', - 'setspeed.c' + 'setspeed.c', + 'rs485.c' ] tio_dep = dependency('inih', required: true, @@ -31,6 +32,10 @@ if enable_iossiospeed tio_c_args += '-DHAVE_IOSSIOSPEED' endif +if enable_rs485 + tio_c_args += '-DHAVE_RS485' +endif + executable('tio', tio_sources, c_args: tio_c_args, diff --git a/src/options.c b/src/options.c index e11af12..b6099ee 100644 --- a/src/options.c +++ b/src/options.c @@ -35,6 +35,7 @@ #include "misc.h" #include "print.h" #include "tty.h" +#include "rs485.h" enum opt_t { @@ -44,6 +45,8 @@ enum opt_t OPT_LOG_STRIP, OPT_LINE_PULSE_DURATION, OPT_RESPONSE_TIMEOUT, + OPT_RS485, + OPT_RS485_CONFIG, }; /* Default options */ @@ -78,6 +81,10 @@ struct option_t option = .response_wait = false, .response_timeout = 100, .mute = false, + .rs485 = false, + .rs485_config_flags = 0, + .rs485_delay_rts_before_send = -1, + .rs485_delay_rts_after_send = -1, }; void print_help(char *argv[]) @@ -109,6 +116,8 @@ void print_help(char *argv[]) printf(" -x, --hexadecimal Enable hexadecimal mode\n"); printf(" -r, --response-wait Wait for line response then quit\n"); printf(" --response-timeout Response timeout (default: 100)\n"); + printf(" --rs-485 Enable RS-485 mode\n"); + printf(" --rs-485-config Set RS-485 configuration\n"); printf(" -v, --version Display version\n"); printf(" -h, --help Display help\n"); printf("\n"); @@ -295,6 +304,8 @@ void options_parse(int argc, char *argv[]) {"hexadecimal", no_argument, 0, 'x' }, {"response-wait", no_argument, 0, 'r' }, {"response-timeout", required_argument, 0, OPT_RESPONSE_TIMEOUT }, + {"rs-485", no_argument, 0, OPT_RS485 }, + {"rs-485-config", required_argument, 0, OPT_RS485_CONFIG }, {"version", no_argument, 0, 'v' }, {"help", no_argument, 0, 'h' }, {0, 0, 0, 0 } @@ -437,6 +448,14 @@ void options_parse(int argc, char *argv[]) option.response_timeout = string_to_long(optarg); break; + case OPT_RS485: + option.rs485 = true; + break; + + case OPT_RS485_CONFIG: + rs485_parse_config(optarg); + break; + case 'v': printf("tio v%s\n", VERSION); exit(EXIT_SUCCESS); diff --git a/src/options.h b/src/options.h index 7650047..b5e1bca 100644 --- a/src/options.h +++ b/src/options.h @@ -21,6 +21,7 @@ #pragma once +#include #include #include #include @@ -70,6 +71,10 @@ struct option_t bool response_wait; int response_timeout; bool mute; + bool rs485; + uint32_t rs485_config_flags; + int32_t rs485_delay_rts_before_send; + int32_t rs485_delay_rts_after_send; }; extern struct option_t option; diff --git a/src/print.h b/src/print.h index c2aba7c..84eef2b 100644 --- a/src/print.h +++ b/src/print.h @@ -21,6 +21,7 @@ #pragma once +#include #include #include "misc.h" #include "error.h" diff --git a/src/rs485.c b/src/rs485.c new file mode 100644 index 0000000..e364b13 --- /dev/null +++ b/src/rs485.c @@ -0,0 +1,200 @@ +/* + * tio - a simple serial device I/O tool + * + * Copyright (c) 2022 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 +#include +#include +#include +#include +#include "options.h" +#include "print.h" +#include "error.h" + +#ifdef HAVE_RS485 + +#include + +static struct serial_rs485 rs485_config_saved; +static struct serial_rs485 rs485_config; +static bool rs485_config_written = false; + +void rs485_parse_config(const char *arg) +{ + bool token_found = true; + char *token = NULL; + char *buffer = strdup(arg); + + while (token_found == true) + { + if (token == NULL) + { + token = strtok(buffer,","); + } + else + { + token = strtok(NULL, ","); + } + + if (token != NULL) + { + char keyname[31]; + unsigned int value; + sscanf(token, "%30[^=]=%d", keyname, &value); + + if (!strcmp(keyname, "RTS_ON_SEND")) + { + if (value) + { + + /* Set logical level for RTS pin equal to 1 when sending */ + option.rs485_config_flags |= SER_RS485_RTS_ON_SEND; + } + else + { + /* Set logical level for RTS pin equal to 0 when sending */ + option.rs485_config_flags &= ~(SER_RS485_RTS_ON_SEND); + } + } + else if (!strcmp(keyname, "RTS_AFTER_SEND")) + { + if (value) + { + /* Set logical level for RTS pin equal to 1 after sending */ + option.rs485_config_flags |= SER_RS485_RTS_AFTER_SEND; + } + else + { + /* Set logical level for RTS pin equal to 0 after sending */ + option.rs485_config_flags &= ~(SER_RS485_RTS_AFTER_SEND); + } + } + else if (!strcmp(keyname, "RTS_DELAY_BEFORE_SEND")) + { + /* Set RTS delay before send */ + option.rs485_delay_rts_before_send = value; + } + else if (!strcmp(keyname, "RTS_DELAY_AFTER_SEND")) + { + /* Set RTS delay after send */ + option.rs485_delay_rts_after_send = value; + } + else if (!strcmp(keyname, "RX_DURING_TX")) + { + /* Receive data even while sending data */ + option.rs485_config_flags |= SER_RS485_RX_DURING_TX; + } + } + else + { + token_found = false; + } + } + free(buffer); +} + +void rs485_print_config(void) +{ + tio_printf(" RS-485 Configuration:"); + tio_printf(" RTS_ON_SEND: %s", (rs485_config.flags & SER_RS485_RTS_ON_SEND) ? "high" : "low"); + tio_printf(" RTS_AFTER_SEND: %s", (rs485_config.flags & SER_RS485_RTS_AFTER_SEND) ? "high" : "low"); + tio_printf(" RTS_DELAY_BEFORE_SEND = %d", rs485_config.delay_rts_before_send); + tio_printf(" RTS_DELAY_AFTER_SEND = %d", rs485_config.delay_rts_after_send); + tio_printf(" RX_DURING_TX: %s", (rs485_config.flags & SER_RS485_RX_DURING_TX) ? "enabled" : "disabled"); +} + +int rs485_mode_enable(int fd) +{ + /* Save existing RS-485 configuration */ + ioctl (fd, TIOCGRS485, &rs485_config_saved); + + /* Prepare new RS-485 configuration */ + rs485_config.flags = SER_RS485_ENABLED; + rs485_config.flags |= option.rs485_config_flags; + + if (option.rs485_delay_rts_before_send > 0) + { + rs485_config.delay_rts_before_send = option.rs485_delay_rts_before_send; + } + else + { + rs485_config.delay_rts_before_send = rs485_config_saved.delay_rts_before_send; + } + + if (option.rs485_delay_rts_after_send > 0) + { + rs485_config.delay_rts_after_send = option.rs485_delay_rts_after_send; + } + else + { + rs485_config.delay_rts_after_send = rs485_config_saved.delay_rts_after_send; + } + + /* Write new RS-485 configuration */ + if (ioctl(fd, TIOCSRS485, &rs485_config) < 0) + { + tio_warning_printf("RS-485 mode is not supported by your device (%s)", strerror(errno)); + return -1; + } + + rs485_config_written = true; + + return 0; +} + +void rs485_mode_restore(int fd) +{ + if (rs485_config_written) + { + /* Write saved RS-485 configuration */ + if (ioctl(fd, TIOCSRS485, &rs485_config_saved) < 0) + { + tio_warning_printf("TIOCGRS485 ioctl failed (%s)", strerror(errno)); + } + } +} + +#else + +void rs485_parse_config(const char *arg) +{ + UNUSED(arg); + return; +} + +void rs485_print_config(void) +{ + return; +} + +int rs485_mode_enable(int fd) +{ + UNUSED(fd); + tio_error_printf("RS485 mode is not supported on your system"); + exit(EXIT_FAILURE); +} + +void rs485_mode_restore(int fd) +{ + UNUSED(fd); + return; +} + +#endif diff --git a/src/rs485.h b/src/rs485.h new file mode 100644 index 0000000..b7a4882 --- /dev/null +++ b/src/rs485.h @@ -0,0 +1,27 @@ +/* + * tio - a simple serial device I/O tool + * + * Copyright (c) 2022 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 rs485_parse_config(const char *arg); +int rs485_mode_enable(int fd); +void rs485_mode_restore(int fd); +void rs485_print_config(void); diff --git a/src/tty.c b/src/tty.c index a1e4233..8064ac1 100644 --- a/src/tty.c +++ b/src/tty.c @@ -50,6 +50,7 @@ #include "error.h" #include "socket.h" #include "setspeed.h" +#include "rs485.h" #ifdef __APPLE__ #define PATH_SERIAL_DEVICES "/dev/" @@ -456,6 +457,10 @@ void handle_command_sequence(char input_char, char previous_char, char *output_c tio_printf("Configuration:"); config_file_print(); options_print(); + if (option.rs485) + { + rs485_print_config(); + } break; case KEY_E: @@ -947,6 +952,12 @@ void tty_restore(void) { tcsetattr(fd, TCSANOW, &tio_old); + if (option.rs485) + { + /* Restore original RS-485 mode */ + rs485_mode_restore(fd); + } + if (connected) { tty_disconnect(); @@ -1081,6 +1092,12 @@ int tty_connect(void) } #endif + /* Manage RS-485 mode */ + if (option.rs485) + { + rs485_mode_enable(fd); + } + /* Make sure we restore tty settings on exit */ if (first) { From 8afbc1d813e248641fd5aaddf5c359d5eaecfb14 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 10 Sep 2022 18:03:08 +0200 Subject: [PATCH 031/506] Update README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 70a858c..6f3765b 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ when used in combination with [tmux](https://tmux.github.io). * Automatic connect and reconnect * Sensible defaults * Support for non-standard baud rates + * Support for RS-485 mode * List available serial devices by ID * Show RX/TX statistics * Toggle serial lines @@ -90,6 +91,8 @@ The command-line interface is straightforward as reflected in the output from -x, --hexadecimal Enable hexadecimal mode -r, --response-wait Wait for line response then quit --response-timeout Response timeout (default: 100) + --rs-485 Enable RS-485 mode + --rs-485-config Set RS-485 configuration -v, --version Display version -h, --help Display help From 3c8d7fa78670d072fa5dfa4144c08194064f8fce Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 10 Sep 2022 18:08:05 +0200 Subject: [PATCH 032/506] Update NEWS --- NEWS | 55 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/NEWS b/NEWS index f87839e..848c3b6 100644 --- a/NEWS +++ b/NEWS @@ -5,36 +5,69 @@ Changes since tio v1.47: + * Add experimental RS-485 support + + Many modern RS-485 serial devices such as the ones from FTDI already + operate in RS-485 mode by default and will work with tio out of the box. + However, there are some RS-232/485 devices which need to be switched + from e.g. RS-232 to RS-485 mode to operate accordingly on the physical + level. + + This commit implements the switching mechanism and interface required to + enable RS-485 mode. It only works on Linux and with serial devices which + use device drivers that support the Linux RS-485 control interface. + + The RS-485 feature is detailed via the following options: + + --rs-485 Enable RS-485 mode + --rs-485-config Set RS-485 configuration + + Set the RS-485 configuration using the following key or key value pair + format in the configuration field: + + RTS_ON_SEND=value Set logical level (0 or 1) for RTS pin when sending + RTS_AFTER_SEND=value Set logical level (0 or 1) for RTS pin after sending + RTS_DELAY_BEFORE_SEND=value Set RTS delay (ms) before sending + RTS_DELAY_AFTER_SEND=value Set RTS delay (ms) after sending + RX_DURING_TX Receive data even while sending data + + If defining more than one key or key value pair, they must be comma + separated. + + Example use: + + $ tio /dev/ttyUSB0 --rs-485 --rs-r485-config=RTS_DELAY_AFTER_SEND=50,RX_DURING_TX + * Add line response feature Add a simple line response feature to make it possible to send e.g. a command string to your serial device and easily receive and parse a line response. - + This is a convenience feature for simple request/response interaction based on lines. For more advanced interaction the socket feature should be used instead. - + The line response feature is detailed via the following options: - + -r, --response-wait - + Wait for line response then quit. A line is considered any string ending with either CR or NL character. If no line is received tio will quit after response timeout. - + Any tio text is automatically muted when piping a string to tio while in response mode to make it easy to parse the response. - + --response-timeout - + Set timeout [ms] of line response (default: 100). - + Example: - + Sending a string (SCPI command) to a test instrument (Korad PSU) and print line response: - + $ echo "*IDN?" | tio /dev/ttyACM0 --response-wait KORAD KD3305P V4.2 SN:32477045 @@ -761,7 +794,7 @@ Changes since tio v1.32: * Rename option -i to -L * Shorten timestamp - + * Shorten timestamp description We do not need the date part of the timestamp. It simply takes up too From 68fc1596542b13980a4ce4f1154683189cd2b463 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 10 Sep 2022 18:59:27 +0200 Subject: [PATCH 033/506] Fix listing of serial devices in MSYS2/Cygwin We generally try to list serial devices by ID but in the MSYS2/Cygwin environment serial devices are not available by ID and so we simply list the basic serial device paths instead. --- src/tty.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/tty.c b/src/tty.c index 8064ac1..4cbee06 100644 --- a/src/tty.c +++ b/src/tty.c @@ -54,8 +54,13 @@ #ifdef __APPLE__ #define PATH_SERIAL_DEVICES "/dev/" +#define PREFIX_TTY_DEVICES "tty." +#elifdef __CYGWIN__ +#define PATH_SERIAL_DEVICES "/dev/serial/by-id/" +#define PREFIX_TTY_DEVICES "ttyS" #else #define PATH_SERIAL_DEVICES "/dev/serial/by-id/" +#define PREFIX_TTY_DEVICES "" #endif #ifndef CMSPAR @@ -1343,11 +1348,10 @@ void list_serial_devices(void) { if ((strcmp(dir->d_name, ".")) && (strcmp(dir->d_name, ".."))) { -#ifdef __APPLE__ -#define TTY_DEVICES_PREFIX "tty." - if (!strncmp(dir->d_name, TTY_DEVICES_PREFIX, sizeof(TTY_DEVICES_PREFIX) - 1)) -#endif - printf("%s%s\n", PATH_SERIAL_DEVICES, dir->d_name); + if (!strncmp(dir->d_name, PREFIX_TTY_DEVICES, sizeof(PREFIX_TTY_DEVICES) - 1)) + { + printf("%s%s\n", PATH_SERIAL_DEVICES, dir->d_name); + } } } closedir(d); From dbc9a8e82d9d6141692eb2e8b2bc0b4097e8bcca Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 10 Sep 2022 22:27:31 +0200 Subject: [PATCH 034/506] Add visual or audible alert support on connect/disconnect The feature is detailed via the following option: --alert none|bell|blink Set alert action on connect/disconnect. It will sound the bell once or blink once on successful connect. Likewise it will sound the bell twice or blink twice on disconnect. Default value is "none" for no alert. --- example/tiorc | 1 + man/tio.1.in | 12 ++++ src/alert.c | 110 +++++++++++++++++++++++++++++++++++++ src/alert.h | 33 +++++++++++ src/bash-completion/tio.in | 11 +++- src/configfile.c | 6 +- src/meson.build | 3 +- src/misc.c | 1 + src/misc.h | 2 + src/options.c | 12 +++- src/options.h | 3 + src/tty.c | 8 +++ 12 files changed, 195 insertions(+), 7 deletions(-) create mode 100644 src/alert.c create mode 100644 src/alert.h diff --git a/example/tiorc b/example/tiorc index e7f0f18..da6be99 100644 --- a/example/tiorc +++ b/example/tiorc @@ -30,6 +30,7 @@ color = 9 baudrate = 115200 tty = /dev/serial/by-id/usb-Silicon_Labs_CP2105_Dual_USB_to_UART_Bridge_Controller_01093176-if01-port0 line-pulse-duration = DTR=200,RTS=300,RI=50 +alert = bell color = 10 [tincan] diff --git a/man/tio.1.in b/man/tio.1.in index 36b0b1c..3cf3d16 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -256,6 +256,16 @@ Receive data even while sending data If defining more than one key or key value pair, they must be comma separated. .RE +.TP +.BR "\-\-alert none|bell|blink" + +Set alert action on connect/disconnect. + +It will sound the bell once or blink once on successful connect. Likewise it +will sound the bell twice or blink twice on disconnect. + +Default value is "none". + .TP .BR \-v ", " \-\-version @@ -389,6 +399,8 @@ Set line response timeout Enable RS-485 mode .IP "\fBrs-485-config" Set RS-485 configuration +.IP "\fBalert" +Set alert action on connect/disconnect .SH "CONFIGURATION FILE EXAMPLES" diff --git a/src/alert.c b/src/alert.c new file mode 100644 index 0000000..4cb0132 --- /dev/null +++ b/src/alert.c @@ -0,0 +1,110 @@ +/* + * tio - a simple serial terminal I/O tool + * + * Copyright (c) 2014-2022 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 "config.h" +#include +#include +#include +#include +#include "error.h" +#include "print.h" +#include "options.h" + +enum alert_t alert_option_parse(const char *arg) +{ + enum alert_t alert = option.alert; // Default + + if (arg != NULL) + { + if (strcmp(arg, "none") == 0) + { + return ALERT_NONE; + } + else if (strcmp(arg, "bell") == 0) + { + return ALERT_BELL; + } + else if (strcmp(arg, "blink") == 0) + { + return ALERT_BLINK; + } + } + + return alert; +} + +void blink_background(void) +{ + // Turn on reverse video + printf("\e[?5h"); + fflush(stdout); + + usleep(200*1000); + + // Turn on normal video + printf("\e[?5l"); + fflush(stdout); +} + +void sound_bell(void) +{ + // Audio bell + printf("\a"); + fflush(stdout); +} + +void alert_connect(void) +{ + switch (option.alert) + { + case ALERT_NONE: + break; + case ALERT_BELL: + sound_bell(); + break; + case ALERT_BLINK: + blink_background(); + break; + default: + break; + } +} + +void alert_disconnect(void) +{ + switch (option.alert) + { + case ALERT_NONE: + break; + case ALERT_BELL: + sound_bell(); + usleep(200*1000); + sound_bell(); + break; + case ALERT_BLINK: + blink_background(); + usleep(200*1000); + blink_background(); + break; + default: + break; + } +} diff --git a/src/alert.h b/src/alert.h new file mode 100644 index 0000000..3fe8679 --- /dev/null +++ b/src/alert.h @@ -0,0 +1,33 @@ +/* + * tio - a simple serial terminal I/O tool + * + * Copyright (c) 2014-2022 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 + +enum alert_t +{ + ALERT_NONE, + ALERT_BELL, + ALERT_BLINK, + ALERT_END, +}; + +void alert_connect(void); +void alert_disconnect(void); diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index cea90f1..ca115e6 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -29,11 +29,12 @@ _tio() -L --list-devices \ -c --color \ -S --socket \ + -x --hexadecimal \ -r --response-wait \ --response-timeout \ --rs-485 \ --rs-485-config \ - -x --hexadecimal \ + --alert \ -v --version \ -h --help" @@ -116,6 +117,10 @@ _tio() COMPREPLY=( $(compgen -W "unix: inet: inet6:" -- ${cur}) ) return 0 ;; + -x | --hexadecimal) + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + ;; -r | --response-wait) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 @@ -132,8 +137,8 @@ _tio() COMPREPLY=( $(compgen -W "RTS_ON_SEND RTS_AFTER_SEND RTS_DELAY_BEFORE_SEND RTS_DELAY_AFTER_SEND RX_DURING_TX" -- ${cur}) ) return 0 ;; - -x | --hexadecimal) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + --alert) + COMPREPLY=( $(compgen -W "none bell blink" -- ${cur}) ) return 0 ;; -v | --version) diff --git a/src/configfile.c b/src/configfile.c index e081a70..5375e73 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -280,8 +280,12 @@ static int data_handler(void *user, const char *section, const char *name, { rs485_parse_config(value); } - + else if (!strcmp(name, "alert")) + { + option.alert = alert_option_parse(value); + } } + return 0; } diff --git a/src/meson.build b/src/meson.build index 061d070..f5ee7a3 100644 --- a/src/meson.build +++ b/src/meson.build @@ -15,7 +15,8 @@ tio_sources = [ 'signals.c', 'socket.c', 'setspeed.c', - 'rs485.c' + 'rs485.c', + 'alert.c' ] tio_dep = dependency('inih', required: true, diff --git a/src/misc.c b/src/misc.c index c519c46..22be237 100644 --- a/src/misc.c +++ b/src/misc.c @@ -22,6 +22,7 @@ #include "config.h" #include #include +#include #include #include #include diff --git a/src/misc.h b/src/misc.h index 23208a6..6fe5368 100644 --- a/src/misc.h +++ b/src/misc.h @@ -27,3 +27,5 @@ char * current_time(void); void delay(long ms); long string_to_long(char *string); int ctrl_key_code(unsigned char key); +void alert_connect(void); +void alert_disconnect(void); diff --git a/src/options.c b/src/options.c index b6099ee..633cca4 100644 --- a/src/options.c +++ b/src/options.c @@ -36,6 +36,7 @@ #include "print.h" #include "tty.h" #include "rs485.h" +#include "alert.h" enum opt_t { @@ -47,6 +48,7 @@ enum opt_t OPT_RESPONSE_TIMEOUT, OPT_RS485, OPT_RS485_CONFIG, + OPT_ALERT, }; /* Default options */ @@ -85,6 +87,7 @@ struct option_t option = .rs485_config_flags = 0, .rs485_delay_rts_before_send = -1, .rs485_delay_rts_after_send = -1, + .alert = ALERT_NONE, }; void print_help(char *argv[]) @@ -118,6 +121,7 @@ void print_help(char *argv[]) printf(" --response-timeout Response timeout (default: 100)\n"); printf(" --rs-485 Enable RS-485 mode\n"); printf(" --rs-485-config Set RS-485 configuration\n"); + printf(" --alert none|bell|blink Alert on connect/disconnect (default: none)\n"); printf(" -v, --version Display version\n"); printf(" -h, --help Display help\n"); printf("\n"); @@ -159,7 +163,7 @@ const char* timestamp_state_to_string(enum timestamp_t timestamp) enum timestamp_t timestamp_option_parse(const char *arg) { enum timestamp_t timestamp = TIMESTAMP_24HOUR; // Default - + if (arg != NULL) { if (strcmp(arg, "24hour") == 0) @@ -237,7 +241,6 @@ void line_pulse_duration_option_parse(const char *arg) } } free(buffer); - } void options_print() @@ -306,6 +309,7 @@ void options_parse(int argc, char *argv[]) {"response-timeout", required_argument, 0, OPT_RESPONSE_TIMEOUT }, {"rs-485", no_argument, 0, OPT_RS485 }, {"rs-485-config", required_argument, 0, OPT_RS485_CONFIG }, + {"alert", required_argument, 0, OPT_ALERT }, {"version", no_argument, 0, 'v' }, {"help", no_argument, 0, 'h' }, {0, 0, 0, 0 } @@ -456,6 +460,10 @@ void options_parse(int argc, char *argv[]) rs485_parse_config(optarg); break; + case OPT_ALERT: + option.alert = alert_option_parse(optarg); + break; + case 'v': printf("tio v%s\n", VERSION); exit(EXIT_SUCCESS); diff --git a/src/options.h b/src/options.h index b5e1bca..8ce6ec0 100644 --- a/src/options.h +++ b/src/options.h @@ -26,6 +26,7 @@ #include #include #include +#include "alert.h" enum timestamp_t { @@ -75,6 +76,7 @@ struct option_t uint32_t rs485_config_flags; int32_t rs485_delay_rts_before_send; int32_t rs485_delay_rts_after_send; + enum alert_t alert; }; extern struct option_t option; @@ -84,3 +86,4 @@ void options_parse(int argc, char *argv[]); void options_parse_final(int argc, char *argv[]); void line_pulse_duration_option_parse(const char *arg); +enum alert_t alert_option_parse(const char *arg); diff --git a/src/tty.c b/src/tty.c index 4cbee06..004e52a 100644 --- a/src/tty.c +++ b/src/tty.c @@ -51,6 +51,8 @@ #include "socket.h" #include "setspeed.h" #include "rs485.h" +#include "alert.h" +#include "misc.h" #ifdef __APPLE__ #define PATH_SERIAL_DEVICES "/dev/" @@ -950,6 +952,9 @@ void tty_disconnect(void) flock(fd, LOCK_UN); close(fd); connected = false; + + /* Fire alert action */ + alert_disconnect(); } } @@ -1066,6 +1071,9 @@ int tty_connect(void) connected = true; print_tainted = false; + /* Fire alert action */ + alert_connect(); + if (option.timestamp) { next_timestamp = true; From 989874ffb85a3d760161e2fea9171d2d0dc9304f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 10 Sep 2022 22:39:36 +0200 Subject: [PATCH 035/506] Cleanup --- man/tio.1.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 3cf3d16..52005a9 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -131,8 +131,7 @@ List available serial devices by ID. Enable log to file. -If no filename is provided the filename will be -automatically generated. +If no filename is provided the filename will be automatically generated. .TP .BR " \-\-log-file \fI From 9cad01791b1481b20e27a9433a9052ac10398f5a Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 10 Sep 2022 22:45:55 +0200 Subject: [PATCH 036/506] Update NEWS --- NEWS | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/NEWS b/NEWS index 848c3b6..3dffbca 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,19 @@ Changes since tio v1.47: + * Add visual or audible alert support on connect/disconnect + + The feature is detailed via the following option: + + --alert none|bell|blink + + Set alert action on connect/disconnect. + + It will sound the bell once or blink once on successful connect. + Likewise it will sound the bell twice or blink twice on disconnect. + + Default value is "none" for no alert. + * Add experimental RS-485 support Many modern RS-485 serial devices such as the ones from FTDI already From 88588100e4964650f599339029151c5af048c7cb Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 10 Sep 2022 22:46:58 +0200 Subject: [PATCH 037/506] Update plain text man page --- man/tio.1.txt | 157 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 102 insertions(+), 55 deletions(-) diff --git a/man/tio.1.txt b/man/tio.1.txt index dc1072a..35a30b4 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 simple serial device I/O tool @@ -7,8 +7,7 @@ SYNOPSIS tio [] DESCRIPTION - tio is a simple serial device tool which features a straightforward command-line and configuration file interface to easily con‐ - nect to serial TTY devices for basic I/O operations. + tio is a simple serial device tool which features a straightforward command-line and configuration file interface to easily connect to serial TTY devices for basic I/O operations. OPTIONS -b, --baudrate @@ -31,8 +30,7 @@ OPTIONS Set parity (default: none). - Note: With mark parity the parity bit is always 0. With space parity the parity bit is always 1. Not all platforms support - mark and space parity. + Note: With mark parity the parity bit is always 0. With space parity the parity bit is always 1. Not all platforms support mark and space parity. -o, --output-delay @@ -44,22 +42,21 @@ OPTIONS --line-pulse-duration - Set the pulse duration [ms] of each serial port line using the following key valur pair format in the duration field: - = + Set the pulse duration [ms] of each serial port line using the following key value pair format in the duration field: = Each key represents a serial line. The following keys are available: - DTR - Data Terminal Ready + DTR Data Terminal Ready - RTS - Request To Send + RTS Request To Send - CTS - Clear To Send + CTS Clear To Send - DSR - Data Set Ready + DSR Data Set Ready - DCD - Data Carrier Detect + DCD Data Carrier Detect - RI - Ring Indicator + RI Ring Indicator If defining more than one key value pair, the pairs must be comma separated. @@ -69,12 +66,10 @@ OPTIONS Disable automatic connect. - By default tio automatically connects to the provided device if present. If the device is not present, it will wait for it - to appear and then connect. If the connection is lost (eg. device disconnects), it will wait for the device to reappear and - then reconnect. + By default tio automatically connects to the provided device if present. If the device is not present, it will wait for it to appear and then connect. If the connection is lost + (eg. device disconnects), it will wait for the device to reappear and then reconnect. - However, if the --no-autoconnect option is provided, tio will exit if the device is not present or an established connec‐ - tion is lost. + However, if the --no-autoconnect option is provided, tio will exit if the device is not present or an established connection is lost. -e, --local-echo @@ -100,7 +95,7 @@ OPTIONS -L, --list-devices - List available serial devices. + List available serial devices by ID. -l, --log @@ -144,8 +139,7 @@ OPTIONS -c, --color 0..255|bold|none|list - Colorize tio text using ANSI color code value ranging from 0 to 255 or use "none" for no color or use "bold" to apply bold - formatting to existing system color. + Colorize tio text using ANSI color code value ranging from 0 to 255 or use "none" for no color or use "bold" to apply bold formatting to existing system color. Use "list" to print a list of available ANSI color codes. @@ -153,9 +147,8 @@ OPTIONS -S, --socket - Redirect I/O to socket. Any input from clients connected to the socket is sent on the serial port as if entered at the ter‐ - minal 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. + 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. Sockets remain open while the serial port is disconnected, and writes will block. @@ -171,6 +164,44 @@ OPTIONS At present there is a hardcoded limit of 16 clients connected at one time. + -r, --response-wait + + Wait for line response then quit. A line is considered any string ending with either CR or NL character. If no line is received tio will quit after response timeout. + + Any tio text is automatically muted when piping a string to tio while in response mode to make it easy to parse the response. + + --response-timeout + + Set timeout [ms] of line response (default: 100). + + --rs-485 + + Enable RS-485 mode. + + --rs-485-config + + Set the RS-485 configuration using the following key or key value pair format in the configuration field: + + RTS_ON_SEND=value Set logical level (0 or 1) for RTS pin when sending + + RTS_AFTER_SEND=value Set logical level (0 or 1) for RTS pin after sending + + RTS_DELAY_BEFORE_SEND=value Set RTS delay (ms) before sending + + RTS_DELAY_AFTER_SEND=value Set RTS delay (ms) after sending + + RX_DURING_TX Receive data even while sending data + + If defining more than one key or key value pair, they must be comma separated. + + --alert none|bell|blink + + Set alert action on connect/disconnect. + + It will sound the bell once or blink once on successful connect. Likewise it will sound the bell twice or blink twice on disconnect. + + Default value is "none". + -v, --version Display program version. @@ -180,8 +211,7 @@ OPTIONS Display help. KEYS - In session, the following key sequences, a prefix key (default: ctrl-t) followed by a command key, are intercepted as tio com‐ - mands: + In session, the following key sequences, a prefix key (default: ctrl-t) followed by a command key, are intercepted as tio commands: ctrl-t ? List available key commands @@ -214,12 +244,10 @@ KEYS HEXADECIMAL MODE In hexadecimal mode each incoming byte is printed out as a hexadecimal value. - Bytes can be sent in this mode by typing the two-character hexadecimal representation of the value, e.g.: to send 0xA you must - type 0a or 0A. + Bytes can be sent in this mode by typing the two-character hexadecimal representation of the value, e.g.: to send 0xA you must type 0a or 0A. CONFIGURATION FILE - Options can be set via configuration file using the INI format. tio uses the configuration file first found in the following loca‐ - tions in the order listed: + Options can be set via configuration file using the INI format. tio uses the configuration file first found in the following locations in the order listed: $XDG_CONFIG_HOME/tio/tiorc @@ -237,49 +265,59 @@ CONFIGURATION FILE The following configuration file options are available: - pattern Pattern matching user input. This pattern can be an extended regular expression with a single group. + pattern Pattern matching user input. This pattern can be an extended regular expression with a single group. - tty tty device to open. If it contains a "%s" it is substituted with the first group match. + tty tty device to open. If it contains a "%s" it is substituted with the first group match. - baudrate Set baud rate + baudrate Set baud rate - databits Set data bits + databits Set data bits - flow Set flow control + flow Set flow control - stopbits Set stop bits + stopbits Set stop bits - parity Set parity + parity Set parity - output-delay Set output character delay + output-delay Set output character delay - output-line-delay Set output line delay + output-line-delay Set output line delay - line-pulse-duration Set line pulse duration + line-pulse-duration Set line pulse duration - no-autoconnect Disable automatic connect + no-autoconnect Disable automatic connect - log Enable log to file + log Enable log to file - log-file Set log filename + log-file Set log filename - log-strip Enable strip of control and escape sequences from log + log-strip Enable strip of control and escape sequences from log - local-echo Enable local echo + local-echo Enable local echo - timestamp Enable line timestamp + timestamp Enable line timestamp - timestamp-format Set timestamp format + timestamp-format Set timestamp format - map Map characters on input or output + map Map characters on input or output - color Colorize tio text using ANSI color code ranging from 0 to 255 + color Colorize tio text using ANSI color code ranging from 0 to 255 - hexadecimal Enable hexadecimal mode + hexadecimal Enable hexadecimal mode - socket Set socket to redirect I/O to + socket Set socket to redirect I/O to - prefix-ctrl-key Set prefix ctrl key (a..z, default: t) + prefix-ctrl-key Set prefix ctrl key (a..z, default: t) + + response-wait Enable wait for line response + + response-timeout Set line response timeout + + rs-485 Enable RS-485 mode + + rs-485-config Set RS-485 configuration + + alert Set alert action on connect/disconnect CONFIGURATION FILE EXAMPLES To change the default configuration simply set options like so: @@ -339,8 +377,7 @@ EXAMPLES $ tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 - Using serial devices by ID ensures that tio automatically reconnects to the correct serial device if it is disconnected and then - reconnected. + Using serial devices by ID ensures that tio automatically reconnects to the correct serial device if it is disconnected and then reconnected. Redirect serial device I/O to Unix file socket for scripting: @@ -377,14 +414,24 @@ EXAMPLES $ echo "ls -la" | tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 + Pipe command to the serial device and wait for line response (string ending with CR or NL): + + $ echo "*IDN?" | tio /dev/ttyACM0 --response-wait + + In this mode, only the response will be printed. + 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 + Enable RS-485 mode: + + $ tio --rs-485 --rs-485-config=RTS_ON_SEND=1,RX_DURING_TX /dev/ttyUSB0 + WEBSITE Visit https://tio.github.io AUTHOR Created by Martin Lund . -tio 1.46 2022-07-15 tio(1) +tio 2.0 2022-07-23 tio(1) From 6224fe292e96728fb6edd0c158bf821b8f93b90e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 10 Sep 2022 22:53:27 +0200 Subject: [PATCH 038/506] Update example --- example/tiorc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/example/tiorc b/example/tiorc index da6be99..c555df1 100644 --- a/example/tiorc +++ b/example/tiorc @@ -16,7 +16,11 @@ output-line-delay = 0 no-autoconnect = disable hexadecimal = disable timestamp = disable +log = disable +log-strip = disable color = bold +rs-485 = disable +alert = none # Sub-configuraions From a85cfcf3ef9f8abe05d90f47bc9601dad88a55fb Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 10 Sep 2022 23:01:28 +0200 Subject: [PATCH 039/506] Update README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6f3765b..2fc5ad0 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ when used in combination with [tmux](https://tmux.github.io). * Support for simple line request/response handling * Bash completion * Color support + * Visual or audible alert on connect/disconnect * Remapping of prefix key * Man page documentation * Plays nicely with [tmux](https://tmux.github.io) @@ -93,6 +94,7 @@ The command-line interface is straightforward as reflected in the output from --response-timeout Response timeout (default: 100) --rs-485 Enable RS-485 mode --rs-485-config Set RS-485 configuration + --alert none|bell|blink Alert on connect/disconnect (default: none) -v, --version Display version -h, --help Display help From ba2d49d2f7cf35bd60a46bdbfd4f717aeb86e859 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 10 Sep 2022 23:29:23 +0200 Subject: [PATCH 040/506] Consolidate timestamp implementation in one file --- src/configfile.c | 2 + src/error.c | 3 +- src/meson.build | 1 + src/misc.c | 64 -------------------- src/options.c | 58 +----------------- src/options.h | 13 +--- src/print.h | 9 +-- src/timestamp.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++ src/timestamp.h | 36 +++++++++++ src/tty.c | 3 +- 10 files changed, 202 insertions(+), 139 deletions(-) create mode 100644 src/timestamp.c create mode 100644 src/timestamp.h diff --git a/src/configfile.c b/src/configfile.c index 5375e73..864380e 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -43,6 +43,8 @@ #include "error.h" #include "print.h" #include "rs485.h" +#include "timestamp.h" +#include "alert.h" static struct config_t *c; diff --git a/src/error.c b/src/error.c index 4e0142f..43da8dd 100644 --- a/src/error.c +++ b/src/error.c @@ -31,6 +31,7 @@ #include "options.h" #include "print.h" #include "error.h" +#include "timestamp.h" static char error[2][1000]; static bool in_session = false; @@ -54,7 +55,7 @@ void error_printf_(const char *format, ...) { putchar('\n'); } - ansi_error_printf("[%s] %s", current_time(), line); + ansi_error_printf("[%s] %s", timestamp_current_time(), line); } else { diff --git a/src/meson.build b/src/meson.build index f5ee7a3..0077733 100644 --- a/src/meson.build +++ b/src/meson.build @@ -16,6 +16,7 @@ tio_sources = [ 'socket.c', 'setspeed.c', 'rs485.c', + 'timestamp.c', 'alert.c' ] diff --git a/src/misc.c b/src/misc.c index 22be237..1983950 100644 --- a/src/misc.c +++ b/src/misc.c @@ -25,75 +25,11 @@ #include #include #include -#include #include #include "error.h" #include "print.h" #include "options.h" -#define TIME_STRING_SIZE_MAX 24 - -char *current_time(void) -{ - static char time_string[TIME_STRING_SIZE_MAX]; - static struct timeval tv, tv_now, tv_start, tv_previous; - static bool first = true; - struct tm *tm; - size_t len; - - // Get current time value - gettimeofday(&tv_now, NULL); - - if (first) - { - tv_start = tv_now; - first = false; - } - - // Add formatted timestap - switch (option.timestamp) - { - case TIMESTAMP_NONE: - case TIMESTAMP_24HOUR: - // "hh:mm:ss.sss" (24 hour format) - tv = tv_now; - tm = localtime(&tv.tv_sec); - len = strftime(time_string, sizeof(time_string), "%H:%M:%S", tm); - break; - case TIMESTAMP_24HOUR_START: - // "hh:mm:ss.sss" (24 hour format relative to start time) - timersub(&tv_now, &tv_start, &tv); - tm = gmtime(&tv.tv_sec); - len = strftime(time_string, sizeof(time_string), "%H:%M:%S", tm); - break; - case TIMESTAMP_24HOUR_DELTA: - // "hh:mm:ss.sss" (24 hour format relative to previous time stamp) - timersub(&tv_now, &tv_previous, &tv); - tm = gmtime(&tv.tv_sec); - len = strftime(time_string, sizeof(time_string), "%H:%M:%S", tm); - break; - case TIMESTAMP_ISO8601: - // "YYYY-MM-DDThh:mm:ss.sss" (ISO-8601) - tv = tv_now; - tm = localtime(&tv.tv_sec); - len = strftime(time_string, sizeof(time_string), "%Y-%m-%dT%H:%M:%S", tm); - break; - default: - return NULL; - } - - // Append milliseconds to all timestamps - if (len) - { - 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; - - return (len < TIME_STRING_SIZE_MAX) ? time_string : NULL; -} - void delay(long ms) { struct timespec ts; diff --git a/src/options.c b/src/options.c index 633cca4..52fd00d 100644 --- a/src/options.c +++ b/src/options.c @@ -36,6 +36,7 @@ #include "print.h" #include "tty.h" #include "rs485.h" +#include "timestamp.h" #include "alert.h" enum opt_t @@ -130,63 +131,6 @@ void print_help(char *argv[]) printf("See the man page for more details.\n"); } -const char* timestamp_state_to_string(enum timestamp_t timestamp) -{ - switch (timestamp) - { - case TIMESTAMP_NONE: - return "disabled"; - break; - - case TIMESTAMP_24HOUR: - return "24hour"; - break; - - case TIMESTAMP_24HOUR_START: - return "24hour-start"; - break; - - case TIMESTAMP_24HOUR_DELTA: - return "24hour-delta"; - break; - - case TIMESTAMP_ISO8601: - return "iso8601"; - break; - - default: - return "unknown"; - break; - } -} - -enum timestamp_t timestamp_option_parse(const char *arg) -{ - enum timestamp_t timestamp = TIMESTAMP_24HOUR; // Default - - if (arg != NULL) - { - if (strcmp(arg, "24hour") == 0) - { - return TIMESTAMP_24HOUR; - } - else if (strcmp(arg, "24hour-start") == 0) - { - return TIMESTAMP_24HOUR_START; - } - else if (strcmp(arg, "24hour-delta") == 0) - { - return TIMESTAMP_24HOUR_DELTA; - } - else if (strcmp(arg, "iso8601") == 0) - { - return TIMESTAMP_ISO8601; - } - } - - return timestamp; -} - void line_pulse_duration_option_parse(const char *arg) { bool token_found = true; diff --git a/src/options.h b/src/options.h index 8ce6ec0..f7a28e3 100644 --- a/src/options.h +++ b/src/options.h @@ -26,20 +26,9 @@ #include #include #include +#include "timestamp.h" #include "alert.h" -enum timestamp_t -{ - TIMESTAMP_NONE, - TIMESTAMP_24HOUR, - TIMESTAMP_24HOUR_START, - TIMESTAMP_24HOUR_DELTA, - TIMESTAMP_ISO8601, - TIMESTAMP_END, -}; - -enum timestamp_t timestamp_option_parse(const char *arg); - /* Options */ struct option_t { diff --git a/src/print.h b/src/print.h index 84eef2b..e3f6193 100644 --- a/src/print.h +++ b/src/print.h @@ -26,6 +26,7 @@ #include "misc.h" #include "error.h" #include "options.h" +#include "timestamp.h" extern bool print_tainted; extern char ansi_format[]; @@ -69,9 +70,9 @@ extern char ansi_format[]; if (print_tainted) \ putchar('\n'); \ if (option.color < 0) \ - fprintf (stdout, "\r[%s] Warning: " format "\r\n", current_time(), ## args); \ + fprintf (stdout, "\r[%s] Warning: " format "\r\n", timestamp_current_time(), ## args); \ else \ - ansi_printf("[%s] Warning: " format, current_time(), ## args); \ + ansi_printf("[%s] Warning: " format, timestamp_current_time(), ## args); \ } \ } @@ -80,7 +81,7 @@ extern char ansi_format[]; if (!option.mute) { \ if (print_tainted) \ putchar('\n'); \ - ansi_printf("[%s] " format, current_time(), ## args); \ + ansi_printf("[%s] " format, timestamp_current_time(), ## args); \ print_tainted = false; \ } \ } @@ -90,7 +91,7 @@ extern char ansi_format[]; if (!option.mute) { \ if (print_tainted) \ putchar('\n'); \ - ansi_printf_raw("[%s] " format, current_time(), ## args); \ + ansi_printf_raw("[%s] " format, timestamp_current_time(), ## args); \ print_tainted = false; \ } \ } diff --git a/src/timestamp.c b/src/timestamp.c new file mode 100644 index 0000000..b8a2198 --- /dev/null +++ b/src/timestamp.c @@ -0,0 +1,152 @@ +/* + * tio - a simple serial terminal I/O tool + * + * Copyright (c) 2014-2022 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 "config.h" +#include +#include +#include +#include +#include +#include +#include "error.h" +#include "print.h" +#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]; + static struct timeval tv, tv_now, tv_start, tv_previous; + static bool first = true; + struct tm *tm; + size_t len; + + // Get current time value + gettimeofday(&tv_now, NULL); + + if (first) + { + tv_start = tv_now; + first = false; + } + + // Add formatted timestap + switch (option.timestamp) + { + case TIMESTAMP_NONE: + case TIMESTAMP_24HOUR: + // "hh:mm:ss.sss" (24 hour format) + tv = tv_now; + tm = localtime(&tv.tv_sec); + len = strftime(time_string, sizeof(time_string), "%H:%M:%S", tm); + break; + case TIMESTAMP_24HOUR_START: + // "hh:mm:ss.sss" (24 hour format relative to start time) + timersub(&tv_now, &tv_start, &tv); + tm = gmtime(&tv.tv_sec); + len = strftime(time_string, sizeof(time_string), "%H:%M:%S", tm); + break; + case TIMESTAMP_24HOUR_DELTA: + // "hh:mm:ss.sss" (24 hour format relative to previous time stamp) + timersub(&tv_now, &tv_previous, &tv); + tm = gmtime(&tv.tv_sec); + len = strftime(time_string, sizeof(time_string), "%H:%M:%S", tm); + break; + case TIMESTAMP_ISO8601: + // "YYYY-MM-DDThh:mm:ss.sss" (ISO-8601) + tv = tv_now; + tm = localtime(&tv.tv_sec); + len = strftime(time_string, sizeof(time_string), "%Y-%m-%dT%H:%M:%S", tm); + break; + default: + return NULL; + } + + // Append milliseconds to all timestamps + if (len) + { + 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; + + return (len < TIME_STRING_SIZE_MAX) ? time_string : NULL; +} + +const char* timestamp_state_to_string(enum timestamp_t timestamp) +{ + switch (timestamp) + { + case TIMESTAMP_NONE: + return "disabled"; + break; + + case TIMESTAMP_24HOUR: + return "24hour"; + break; + + case TIMESTAMP_24HOUR_START: + return "24hour-start"; + break; + + case TIMESTAMP_24HOUR_DELTA: + return "24hour-delta"; + break; + + case TIMESTAMP_ISO8601: + return "iso8601"; + break; + + default: + return "unknown"; + break; + } +} + +enum timestamp_t timestamp_option_parse(const char *arg) +{ + enum timestamp_t timestamp = TIMESTAMP_24HOUR; // Default + + if (arg != NULL) + { + if (strcmp(arg, "24hour") == 0) + { + return TIMESTAMP_24HOUR; + } + else if (strcmp(arg, "24hour-start") == 0) + { + return TIMESTAMP_24HOUR_START; + } + else if (strcmp(arg, "24hour-delta") == 0) + { + return TIMESTAMP_24HOUR_DELTA; + } + else if (strcmp(arg, "iso8601") == 0) + { + return TIMESTAMP_ISO8601; + } + } + + return timestamp; +} diff --git a/src/timestamp.h b/src/timestamp.h new file mode 100644 index 0000000..ee23ce6 --- /dev/null +++ b/src/timestamp.h @@ -0,0 +1,36 @@ +/* + * tio - a simple serial terminal I/O tool + * + * Copyright (c) 2014-2022 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 + +enum timestamp_t +{ + TIMESTAMP_NONE, + TIMESTAMP_24HOUR, + TIMESTAMP_24HOUR_START, + TIMESTAMP_24HOUR_DELTA, + TIMESTAMP_ISO8601, + TIMESTAMP_END, +}; + +char *timestamp_current_time(void); +const char* timestamp_state_to_string(enum timestamp_t timestamp); +enum timestamp_t timestamp_option_parse(const char *arg); diff --git a/src/tty.c b/src/tty.c index 004e52a..d2cc5a9 100644 --- a/src/tty.c +++ b/src/tty.c @@ -52,6 +52,7 @@ #include "setspeed.h" #include "rs485.h" #include "alert.h" +#include "timestamp.h" #include "misc.h" #ifdef __APPLE__ @@ -1188,7 +1189,7 @@ int tty_connect(void) /* Print timestamp on new line if enabled */ if (next_timestamp && input_char != '\n' && input_char != '\r') { - now = current_time(); + now = timestamp_current_time(); if (now) { ansi_printf_raw("[%s] ", now); From 1dc3a3891d3f653fd8b2e2e8cc30a6df9816b727 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 10 Sep 2022 23:31:02 +0200 Subject: [PATCH 041/506] Cleanup --- src/alert.h | 1 + src/options.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/alert.h b/src/alert.h index 3fe8679..cb26eff 100644 --- a/src/alert.h +++ b/src/alert.h @@ -29,5 +29,6 @@ enum alert_t ALERT_END, }; +enum alert_t alert_option_parse(const char *arg); void alert_connect(void); void alert_disconnect(void); diff --git a/src/options.h b/src/options.h index f7a28e3..22ec236 100644 --- a/src/options.h +++ b/src/options.h @@ -75,4 +75,3 @@ void options_parse(int argc, char *argv[]); void options_parse_final(int argc, char *argv[]); void line_pulse_duration_option_parse(const char *arg); -enum alert_t alert_option_parse(const char *arg); From 0afcef807bd584b2cf60c6bb23decf4e86864752 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 11 Sep 2022 00:57:05 +0200 Subject: [PATCH 042/506] Cleanup --- README.md | 2 +- src/options.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2fc5ad0..6f7916e 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ The command-line interface is straightforward as reflected in the output from --response-timeout Response timeout (default: 100) --rs-485 Enable RS-485 mode --rs-485-config Set RS-485 configuration - --alert none|bell|blink Alert on connect/disconnect (default: none) + --alert bell|blink|none Alert on connect/disconnect (default: none) -v, --version Display version -h, --help Display help diff --git a/src/options.c b/src/options.c index 52fd00d..9d4b2e3 100644 --- a/src/options.c +++ b/src/options.c @@ -122,7 +122,7 @@ void print_help(char *argv[]) printf(" --response-timeout Response timeout (default: 100)\n"); printf(" --rs-485 Enable RS-485 mode\n"); printf(" --rs-485-config Set RS-485 configuration\n"); - printf(" --alert none|bell|blink Alert on connect/disconnect (default: none)\n"); + printf(" --alert bell|blink|none Alert on connect/disconnect (default: none)\n"); printf(" -v, --version Display version\n"); printf(" -h, --help Display help\n"); printf("\n"); From 8188bb5b7699360c7893c6deaae8a338daeeac1b Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 11 Sep 2022 11:20:23 +0200 Subject: [PATCH 043/506] Handle stale unix socket file Delete existing unix socket file if it is tested to be stale, meaning no one is listening on it. --- src/socket.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/socket.c b/src/socket.c index c21904f..16d9621 100644 --- a/src/socket.c +++ b/src/socket.c @@ -78,6 +78,44 @@ static void socket_exit(void) } } +static bool socket_stale(const char *path) +{ + struct sockaddr_un addr; + bool stale = false; + int sockfd; + + /* Test if socket file exists */ + if (access(path, F_OK) == 0) + { + /* Create test socket */ + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sockfd < 0) + { + tio_warning_printf("Failure opening socket (%s)", strerror(errno)); + return false; + } + + /* Prepare address */ + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); + + /* Perform connect to test if socket is active */ + if (connect(sockfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) == -1) + { + if (errno == ECONNREFUSED) + { + // No one is listening on socket file + stale = true; + } + } + + /* Cleanup */ + close(sockfd); + } + + return stale; +} + void socket_configure(void) { struct sockaddr_un sockaddr_unix = {}; @@ -136,7 +174,7 @@ void socket_configure(void) tio_error_printf("%s: Invalid socket scheme, must be prefixed with 'unix:', 'inet:', or 'inet6:'", option.socket); exit(EXIT_FAILURE); } - + /* Configure socket */ switch (socket_family) @@ -146,6 +184,14 @@ void socket_configure(void) strncpy(sockaddr_unix.sun_path, socket_filename(), sizeof(sockaddr_unix.sun_path) - 1); sockaddr_p = (struct sockaddr *) &sockaddr_unix; socklen = sizeof(sockaddr_unix); + + /* Test for stale unix socket file */ + if (socket_stale(socket_filename())) + { + tio_printf("Cleaning up old socket file"); + unlink(socket_filename()); + } + break; case AF_INET: From 75c5261fe510b2513438921a2c6883fe2fb133bb Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 11 Sep 2022 11:43:56 +0200 Subject: [PATCH 044/506] Update NEWS --- NEWS | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NEWS b/NEWS index 3dffbca..544f8a1 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,11 @@ Changes since tio v1.47: + * Handle stale unix socket file + + Delete existing unix socket file if it is tested to be stale, meaning no + one is listening on it. + * Add visual or audible alert support on connect/disconnect The feature is detailed via the following option: From f8c9d6091a3ada0df6b74c04e66792d56b3fd8c9 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 11 Sep 2022 11:46:24 +0200 Subject: [PATCH 045/506] Update version date --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index d1eedcc..bebe86b 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 = '2022-07-23' +version_date = '2022-09-11' # Test for dynamic baudrate configuration interface compiler = meson.get_compiler('c') From 278750428c82b40161ff58a570c2966811835da4 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 11 Sep 2022 11:47:04 +0200 Subject: [PATCH 046/506] Update plain text man page --- man/tio.1.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/man/tio.1.txt b/man/tio.1.txt index 35a30b4..032256e 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 simple serial device I/O tool @@ -66,7 +66,7 @@ OPTIONS Disable automatic connect. - By default tio automatically connects to the provided device if present. If the device is not present, it will wait for it to appear and then connect. If the connection is lost + By default tio automatically connects to the provided device if present. If the device is not present, it will wait for it to appear and then connect. If the connection is lost (eg. device disconnects), it will wait for the device to reappear and then reconnect. However, if the --no-autoconnect option is provided, tio will exit if the device is not present or an established connection is lost. @@ -147,7 +147,7 @@ OPTIONS -S, --socket - 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 + 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. Sockets remain open while the serial port is disconnected, and writes will block. @@ -434,4 +434,4 @@ WEBSITE AUTHOR Created by Martin Lund . -tio 2.0 2022-07-23 tio(1) +tio 2.0 2022-09-11 tio(1) From 16479c8d863922d579ef42b4e7f089ae3ac336d9 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 11 Sep 2022 11:54:27 +0200 Subject: [PATCH 047/506] Cleanup --- README.md | 2 +- man/tio.1.in | 10 ++++++---- src/options.c | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6f7916e..505b75a 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ The command-line interface is straightforward as reflected in the output from --log-strip Strip control characters and escape sequences -m, --map Map characters -c, --color 0..255|bold|none|list Colorize tio text (default: bold) - -S, --socket Redirect I/O to file or network socket + -S, --socket Redirect I/O to socket -x, --hexadecimal Enable hexadecimal mode -r, --response-wait Wait for line response then quit --response-timeout Response timeout (default: 100) diff --git a/man/tio.1.in b/man/tio.1.in index 52005a9..de87ea3 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -190,10 +190,12 @@ Default value is "bold". .TP .BR \-S ", " "\-\-socket \fI\fR\fB -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 \fBctrl-t\fR sequences are not recognized), and any input from the serial -port is multiplexed to the terminal and all connected clients. +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 \fBctrl-t\fR sequences +are not recognized), and any input from the serial port is multiplexed to the +terminal and all connected clients. Sockets remain open while the serial port is disconnected, and writes will block. diff --git a/src/options.c b/src/options.c index 9d4b2e3..d21ed87 100644 --- a/src/options.c +++ b/src/options.c @@ -116,7 +116,7 @@ void print_help(char *argv[]) printf(" --log-strip Strip control characters and escape sequences\n"); printf(" -m, --map Map characters\n"); printf(" -c, --color 0..255|bold|none|list Colorize tio text (default: bold)\n"); - printf(" -S, --socket Redirect I/O to file or network socket\n"); + printf(" -S, --socket Redirect I/O to socket\n"); printf(" -x, --hexadecimal Enable hexadecimal mode\n"); printf(" -r, --response-wait Wait for line response then quit\n"); printf(" --response-timeout Response timeout (default: 100)\n"); From 61b204aa3d65a8c7abd61b99ad268b138f869835 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 11 Sep 2022 11:58:18 +0200 Subject: [PATCH 048/506] Update plain text man page --- man/tio.1.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/man/tio.1.txt b/man/tio.1.txt index 032256e..0399b63 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -147,8 +147,10 @@ OPTIONS -S, --socket - 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. + 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. Sockets remain open while the serial port is disconnected, and writes will block. From 137e6111f653720651db37fa34421cbab5ad6770 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 11 Sep 2022 11:59:42 +0200 Subject: [PATCH 049/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 505b75a..8861b68 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ when used in combination with [tmux](https://tmux.github.io). ## 3. Usage For more usage details please see the man page documentation -[here](man/tio.1.txt). +[here](https://raw.githubusercontent.com/tio/tio/master/man/tio.1.txt). ### 3.1 Command-line From 47827f094bb5683e11aa1ca436433c236928505c Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 11 Sep 2022 12:51:48 +0200 Subject: [PATCH 050/506] Fix compilation error on systems without RS-485 support --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index bebe86b..0291474 100644 --- a/meson.build +++ b/meson.build @@ -72,6 +72,7 @@ foreach rate : test_baudrates endforeach # Test for RS-485 support on Linux +enable_rs485 = false if host_machine.system() == 'linux' if compiler.check_header('linux/serial.h') enable_rs485 = compiler.has_header_symbol('sys/ioctl.h', 'TIOCSRS485') From 6618642acf28fec6d3e70ed75b50d4ce138ea08a Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 11 Sep 2022 13:14:01 +0200 Subject: [PATCH 051/506] Fix tty path on MSYS/CYGWIN platforms --- src/tty.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tty.c b/src/tty.c index d2cc5a9..4165017 100644 --- a/src/tty.c +++ b/src/tty.c @@ -55,11 +55,11 @@ #include "timestamp.h" #include "misc.h" -#ifdef __APPLE__ +#if defined(__APPLE__) #define PATH_SERIAL_DEVICES "/dev/" #define PREFIX_TTY_DEVICES "tty." -#elifdef __CYGWIN__ -#define PATH_SERIAL_DEVICES "/dev/serial/by-id/" +#elif defined(__CYGWIN__) +#define PATH_SERIAL_DEVICES "/dev/" #define PREFIX_TTY_DEVICES "ttyS" #else #define PATH_SERIAL_DEVICES "/dev/serial/by-id/" From d0926b25ba6261ec3c01adbad3098d4346e3d0ed Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 11 Sep 2022 14:00:45 +0200 Subject: [PATCH 052/506] Bump version --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 0291474..ddd18a8 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('tio', 'c', - version : '2.0', + version : '2.1', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] From 81607424f859411e39efa9b2b137ecd2a6792c65 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 11 Sep 2022 20:13:21 +0200 Subject: [PATCH 053/506] Update README --- README.md | 76 +++++++++++++++++++++++++++---------------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 8861b68..3f678d3 100644 --- a/README.md +++ b/README.md @@ -65,42 +65,42 @@ For more usage details please see the man page documentation The command-line interface is straightforward as reflected in the output from 'tio --help': ``` - Usage: tio [] + Usage: tio [] - Connect to tty device directly or via sub-configuration. + Connect to tty device directly or via sub-configuration. - Options: - -b, --baudrate Baud rate (default: 115200) - -d, --databits 5|6|7|8 Data bits (default: 8) - -f, --flow hard|soft|none Flow control (default: none) - -s, --stopbits 1|2 Stop bits (default: 1) - -p, --parity odd|even|none|mark|space Parity (default: none) - -o, --output-delay Output character delay (default: 0) - -O, --output-line-delay Output line delay (default: 0) - --line-pulse-duration Set line pulse duration - -n, --no-autoconnect Disable automatic connect - -e, --local-echo Enable local echo - -t, --timestamp Enable line timestamp - --timestamp-format Set timestamp format (default: 24hour) - -L, --list-devices List available serial devices - -l, --log Enable log to file - --log-file Set log filename - --log-strip Strip control characters and escape sequences - -m, --map Map characters - -c, --color 0..255|bold|none|list Colorize tio text (default: bold) - -S, --socket Redirect I/O to socket - -x, --hexadecimal Enable hexadecimal mode - -r, --response-wait Wait for line response then quit - --response-timeout Response timeout (default: 100) - --rs-485 Enable RS-485 mode - --rs-485-config Set RS-485 configuration - --alert bell|blink|none Alert on connect/disconnect (default: none) - -v, --version Display version - -h, --help Display help + Options: + -b, --baudrate Baud rate (default: 115200) + -d, --databits 5|6|7|8 Data bits (default: 8) + -f, --flow hard|soft|none Flow control (default: none) + -s, --stopbits 1|2 Stop bits (default: 1) + -p, --parity odd|even|none|mark|space Parity (default: none) + -o, --output-delay Output character delay (default: 0) + -O, --output-line-delay Output line delay (default: 0) + --line-pulse-duration Set line pulse duration + -n, --no-autoconnect Disable automatic connect + -e, --local-echo Enable local echo + -t, --timestamp Enable line timestamp + --timestamp-format Set timestamp format (default: 24hour) + -L, --list-devices List available serial devices + -l, --log Enable log to file + --log-file Set log filename + --log-strip Strip control characters and escape sequences + -m, --map Map characters + -c, --color 0..255|bold|none|list Colorize tio text (default: bold) + -S, --socket Redirect I/O to socket + -x, --hexadecimal Enable hexadecimal mode + -r, --response-wait Wait for line response then quit + --response-timeout Response timeout (default: 100) + --rs-485 Enable RS-485 mode + --rs-485-config Set RS-485 configuration + --alert bell|blink|none Alert on connect/disconnect (default: none) + -v, --version Display version + -h, --help Display help - Options and sub-configurations may be set via configuration file. + Options and sub-configurations may be set via configuration file. - See the man page for more details. + See the man page for more details. ``` @@ -230,21 +230,21 @@ reach out to its package maintainers team. Install latest stable version: ``` - $ snap install tio +$ snap install tio ``` ### 4.3 Installation using brew (MacOS, Linux) If you have [brew](http://brew.sh) installed: ``` - $ brew install tio +$ brew install tio ``` ### 4.4 Installation using MSYS2 (Windows) If you have [MSYS2](https://www.msys2.org) installed: ``` - $ pacman -S tio +$ pacman -S tio ``` ### 4.5 Installation from source @@ -253,9 +253,9 @@ The latest source releases can be found [here](https://github.com/tio/tio/releas Install steps: ``` - $ meson build - $ meson compile -C build - $ meson install -C build +$ meson build +$ meson compile -C build +$ meson install -C build ``` See meson\_options.txt for tio specific build options. From ab885713b954446a7fc6997fa551f193386181e5 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 11 Sep 2022 22:08:06 +0200 Subject: [PATCH 054/506] Add support for sending prefix character to serial device Do so by inputting prefix key twice, e.g. input ctrl-t ctrl-t to send ctrl-t character to serial device. --- src/tty.c | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/src/tty.c b/src/tty.c index 4165017..a3f674c 100644 --- a/src/tty.c +++ b/src/tty.c @@ -402,20 +402,21 @@ void handle_command_sequence(char input_char, char previous_char, char *output_c { case KEY_QUESTION: tio_printf("Key commands:"); - tio_printf(" ctrl-%c ? List available key commands", option.prefix_key); - tio_printf(" ctrl-%c b Send break", option.prefix_key); - tio_printf(" ctrl-%c c Show configuration", option.prefix_key); - tio_printf(" ctrl-%c e Toggle local echo mode", option.prefix_key); - tio_printf(" ctrl-%c g Toggle serial port line", option.prefix_key); - tio_printf(" ctrl-%c h Toggle hexadecimal 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 p Pulse serial port line", option.prefix_key); - tio_printf(" ctrl-%c q Quit", 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 ? List available key commands", option.prefix_key); + tio_printf(" ctrl-%c b Send break", option.prefix_key); + tio_printf(" ctrl-%c c Show configuration", option.prefix_key); + tio_printf(" ctrl-%c e Toggle local echo mode", option.prefix_key); + tio_printf(" ctrl-%c g Toggle serial port line", option.prefix_key); + tio_printf(" ctrl-%c h Toggle hexadecimal 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 p Pulse serial port line", option.prefix_key); + tio_printf(" ctrl-%c q Quit", 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 ctrl-%c Send ctrl-%c character", option.prefix_key, option.prefix_key, option.prefix_key); break; case KEY_SHIFT_L: @@ -546,6 +547,23 @@ void handle_command_sequence(char input_char, char previous_char, char *output_c break; default: + /* Handle double prefix key input case */ + if (input_char == option.prefix_code) + { + static int count = 0; + if (count++ == 1) + { + // Do not forward prefix characters excessively + count = 0; + break; + } + + /* Forward prefix character to tty */ + *output_char = option.prefix_code; + *forward = true; + break; + } + /* Ignore unknown ctrl-t escaped keys */ break; } From bf3974c2b6027c860d1eda72ee36cc4b2d0a5979 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 11 Sep 2022 22:31:20 +0200 Subject: [PATCH 055/506] Update README --- README.md | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 3f678d3..a33ba9d 100644 --- a/README.md +++ b/README.md @@ -150,20 +150,21 @@ ctrl-t ? to list the available key commands. ``` [20:19:12.040] Key commands: -[20:19:12.040] ctrl-t ? List available key commands -[20:19:12.040] ctrl-t b Send break -[20:19:12.040] ctrl-t c Show configuration -[20:19:12.040] ctrl-t e Toggle local echo mode -[20:19:12.040] ctrl-t g Toggle serial port line -[20:19:12.040] ctrl-t h Toggle hexadecimal mode -[20:19:12.040] ctrl-t l Clear screen -[20:19:12.040] ctrl-t L Show line states -[20:19:12.040] ctrl-t p Pulse serial port line -[20:19:12.040] ctrl-t q Quit -[20:19:12.041] ctrl-t s Show statistics -[20:19:12.041] ctrl-t T Toggle line timestamp mode -[20:19:12.041] ctrl-t U Toggle conversion to uppercase -[20:19:12.041] ctrl-t v Show version +[20:19:12.040] ctrl-t ? List available key commands +[20:19:12.040] ctrl-t b Send break +[20:19:12.040] ctrl-t c Show configuration +[20:19:12.040] ctrl-t e Toggle local echo mode +[20:19:12.040] ctrl-t g Toggle serial port line +[20:19:12.040] ctrl-t h Toggle hexadecimal mode +[20:19:12.040] ctrl-t l Clear screen +[20:19:12.040] ctrl-t L Show line states +[20:19:12.040] ctrl-t p Pulse serial port line +[20:19:12.040] ctrl-t q Quit +[20:19:12.041] ctrl-t s Show statistics +[20:19:12.041] ctrl-t t Toggle line timestamp mode +[20:19:12.041] ctrl-t U Toggle conversion to uppercase +[20:19:12.041] ctrl-t v Show version +[20:19:12.041] ctrl-t ctrl-t Send ctrl-t character ``` If needed, the prefix key (ctrl-t) can be remapped via configuration file. From eb95a656b00cee054528a8fa20c728c68432e85b Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 13 Sep 2022 16:15:58 +0200 Subject: [PATCH 056/506] Update README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a33ba9d..09fa98d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # tio - a simple serial device I/O tool +[![CircleCI](https://img.shields.io/circleci/build/gh/tio/tio?token=da7e7fd0d0ee99b9f986f8877dcdbe28f73d9e06)] + [![CircleCI](https://circleci.com/gh/tio/tio/tree/master.svg?style=shield)](https://circleci.com/gh/tio/tio/tree/master) [![tio](https://snapcraft.io/tio/badge.svg)](https://snapcraft.io/tio) [![Packaging status](https://repology.org/badge/tiny-repos/tio.svg)](https://repology.org/project/tio/versions) From 76a51a4af38752337face0b5f7c239e3958b8f72 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 13 Sep 2022 16:19:15 +0200 Subject: [PATCH 057/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 09fa98d..3830b8d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # tio - a simple serial device I/O tool -[![CircleCI](https://img.shields.io/circleci/build/gh/tio/tio?token=da7e7fd0d0ee99b9f986f8877dcdbe28f73d9e06)] +[![CircleCI](https://img.shields.io/circleci/build/gh/tio/tio?token=da7e7fd0d0ee99b9f986f8877dcdbe28f73d9e06)](https://circleci.com/gh/tio/tio/tree/master) [![CircleCI](https://circleci.com/gh/tio/tio/tree/master.svg?style=shield)](https://circleci.com/gh/tio/tio/tree/master) [![tio](https://snapcraft.io/tio/badge.svg)](https://snapcraft.io/tio) From 529692805c8a2823ebee1ba7886a1aad24e9fd90 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 13 Sep 2022 16:22:57 +0200 Subject: [PATCH 058/506] Update README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3830b8d..ac91ad9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # tio - a simple serial device I/O tool [![CircleCI](https://img.shields.io/circleci/build/gh/tio/tio?token=da7e7fd0d0ee99b9f986f8877dcdbe28f73d9e06)](https://circleci.com/gh/tio/tio/tree/master) +[![version](https://img.shields.io/github/v/release/tio/tio?color=green&label=version&sort=semver)] + [![CircleCI](https://circleci.com/gh/tio/tio/tree/master.svg?style=shield)](https://circleci.com/gh/tio/tio/tree/master) [![tio](https://snapcraft.io/tio/badge.svg)](https://snapcraft.io/tio) From f43ef9b5842a9764df2dbd4741004fde242b2865 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 13 Sep 2022 16:30:49 +0200 Subject: [PATCH 059/506] Update README --- README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ac91ad9..a3e758b 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,8 @@ # tio - a simple serial device I/O tool [![CircleCI](https://img.shields.io/circleci/build/gh/tio/tio?token=da7e7fd0d0ee99b9f986f8877dcdbe28f73d9e06)](https://circleci.com/gh/tio/tio/tree/master) -[![version](https://img.shields.io/github/v/release/tio/tio?color=green&label=version&sort=semver)] - - -[![CircleCI](https://circleci.com/gh/tio/tio/tree/master.svg?style=shield)](https://circleci.com/gh/tio/tio/tree/master) -[![tio](https://snapcraft.io/tio/badge.svg)](https://snapcraft.io/tio) -[![Packaging status](https://repology.org/badge/tiny-repos/tio.svg)](https://repology.org/project/tio/versions) +[![Version](https://img.shields.io/github/v/release/tio/tio?color=green&label=version&sort=semver)](https://github.com/tio/tio/releases) +[![Packaging status](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions) ## 1. Introduction From 97796395aaa6d58759a37104f470ea72c0e496cf Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 13 Sep 2022 16:32:25 +0200 Subject: [PATCH 060/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a3e758b..bed0cd1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # tio - a simple serial device I/O tool [![CircleCI](https://img.shields.io/circleci/build/gh/tio/tio?token=da7e7fd0d0ee99b9f986f8877dcdbe28f73d9e06)](https://circleci.com/gh/tio/tio/tree/master) -[![Version](https://img.shields.io/github/v/release/tio/tio?color=green&label=version&sort=semver)](https://github.com/tio/tio/releases) +[![Version](https://img.shields.io/github/v/release/tio/tio?label=version&sort=semver)](https://github.com/tio/tio/releases) [![Packaging status](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions) ## 1. Introduction From 0f31f17f5df6b1598c89abc7210856e2831b0bf8 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 13 Sep 2022 16:43:10 +0200 Subject: [PATCH 061/506] Update README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index bed0cd1..8b128a0 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ [![CircleCI](https://img.shields.io/circleci/build/gh/tio/tio?token=da7e7fd0d0ee99b9f986f8877dcdbe28f73d9e06)](https://circleci.com/gh/tio/tio/tree/master) [![Version](https://img.shields.io/github/v/release/tio/tio?label=version&sort=semver)](https://github.com/tio/tio/releases) [![Packaging status](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions) +![Code Lines](https://img.shields.io/tokei/lines/github/tio/tio) +![Contributors](https://img.shields.io/github/contributors/tio/tio) ## 1. Introduction From 7d597d315be2a29d14b526420470c103021cb598 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 13 Sep 2022 16:54:36 +0200 Subject: [PATCH 062/506] Update README --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 8b128a0..df3d29c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ [![Version](https://img.shields.io/github/v/release/tio/tio?label=version&sort=semver)](https://github.com/tio/tio/releases) [![Packaging status](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions) ![Code Lines](https://img.shields.io/tokei/lines/github/tio/tio) -![Contributors](https://img.shields.io/github/contributors/tio/tio) ## 1. Introduction From e7dcd5d034e0eb8bab518d6031ab672cf6e7ee38 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 13 Sep 2022 16:56:08 +0200 Subject: [PATCH 063/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index df3d29c..e08b8d0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # tio - a simple serial device I/O tool [![CircleCI](https://img.shields.io/circleci/build/gh/tio/tio?token=da7e7fd0d0ee99b9f986f8877dcdbe28f73d9e06)](https://circleci.com/gh/tio/tio/tree/master) -[![Version](https://img.shields.io/github/v/release/tio/tio?label=version&sort=semver)](https://github.com/tio/tio/releases) +[![Release](https://img.shields.io/github/v/release/tio/tio?sort=semver)](https://github.com/tio/tio/releases) [![Packaging status](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions) ![Code Lines](https://img.shields.io/tokei/lines/github/tio/tio) From 0131a9a051b104886cac40e8ca649339a3c61e98 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 13 Sep 2022 16:57:15 +0200 Subject: [PATCH 064/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e08b8d0..f461dad 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ [![CircleCI](https://img.shields.io/circleci/build/gh/tio/tio?token=da7e7fd0d0ee99b9f986f8877dcdbe28f73d9e06)](https://circleci.com/gh/tio/tio/tree/master) [![Release](https://img.shields.io/github/v/release/tio/tio?sort=semver)](https://github.com/tio/tio/releases) -[![Packaging status](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions) ![Code Lines](https://img.shields.io/tokei/lines/github/tio/tio) +[![Packaging status](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions) ## 1. Introduction From 3fab077a7a8a5b252cd45996c9846ee28170ed45 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 13 Sep 2022 16:58:28 +0200 Subject: [PATCH 065/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f461dad..cccd32c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![CircleCI](https://img.shields.io/circleci/build/gh/tio/tio?token=da7e7fd0d0ee99b9f986f8877dcdbe28f73d9e06)](https://circleci.com/gh/tio/tio/tree/master) [![Release](https://img.shields.io/github/v/release/tio/tio?sort=semver)](https://github.com/tio/tio/releases) -![Code Lines](https://img.shields.io/tokei/lines/github/tio/tio) +[![Code Lines](https://img.shields.io/tokei/lines/github/tio/tio)]() [![Packaging status](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions) ## 1. Introduction From 1f19ba1302c7f56b78dc9e8b0583a9dd7c078db1 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 13 Sep 2022 16:59:34 +0200 Subject: [PATCH 066/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cccd32c..7fbc6e1 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ [![CircleCI](https://img.shields.io/circleci/build/gh/tio/tio?token=da7e7fd0d0ee99b9f986f8877dcdbe28f73d9e06)](https://circleci.com/gh/tio/tio/tree/master) [![Release](https://img.shields.io/github/v/release/tio/tio?sort=semver)](https://github.com/tio/tio/releases) -[![Code Lines](https://img.shields.io/tokei/lines/github/tio/tio)]() [![Packaging status](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions) +[![Code Lines](https://img.shields.io/tokei/lines/github/tio/tio)]() ## 1. Introduction From 3e728a223c38b822c2a785b94189705d05a9c95d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 13 Sep 2022 17:01:09 +0200 Subject: [PATCH 067/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7fbc6e1..e08b8d0 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![CircleCI](https://img.shields.io/circleci/build/gh/tio/tio?token=da7e7fd0d0ee99b9f986f8877dcdbe28f73d9e06)](https://circleci.com/gh/tio/tio/tree/master) [![Release](https://img.shields.io/github/v/release/tio/tio?sort=semver)](https://github.com/tio/tio/releases) [![Packaging status](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions) -[![Code Lines](https://img.shields.io/tokei/lines/github/tio/tio)]() +![Code Lines](https://img.shields.io/tokei/lines/github/tio/tio) ## 1. Introduction From 167988bf27c41038f616e1c631b6a2f800a9bd20 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 13 Sep 2022 17:03:59 +0200 Subject: [PATCH 068/506] Update README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index e08b8d0..9e5794a 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,11 @@ # tio - a simple serial device I/O tool [![CircleCI](https://img.shields.io/circleci/build/gh/tio/tio?token=da7e7fd0d0ee99b9f986f8877dcdbe28f73d9e06)](https://circleci.com/gh/tio/tio/tree/master) + [![Release](https://img.shields.io/github/v/release/tio/tio?sort=semver)](https://github.com/tio/tio/releases) + [![Packaging status](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions) + ![Code Lines](https://img.shields.io/tokei/lines/github/tio/tio) ## 1. Introduction From 33f287e8cfc160c200661475d00e778abf5719f2 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 13 Sep 2022 17:05:05 +0200 Subject: [PATCH 069/506] Update README --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 9e5794a..cccd32c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,10 @@ # tio - a simple serial device I/O tool [![CircleCI](https://img.shields.io/circleci/build/gh/tio/tio?token=da7e7fd0d0ee99b9f986f8877dcdbe28f73d9e06)](https://circleci.com/gh/tio/tio/tree/master) - [![Release](https://img.shields.io/github/v/release/tio/tio?sort=semver)](https://github.com/tio/tio/releases) - +[![Code Lines](https://img.shields.io/tokei/lines/github/tio/tio)]() [![Packaging status](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions) -![Code Lines](https://img.shields.io/tokei/lines/github/tio/tio) - ## 1. Introduction tio is a simple serial device tool which features a straightforward From 52c89ca071c3f8441b50b6edeeb8141d3ba29fe0 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 13 Sep 2022 17:12:28 +0200 Subject: [PATCH 070/506] Update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cccd32c..e739ed6 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ [![CircleCI](https://img.shields.io/circleci/build/gh/tio/tio?token=da7e7fd0d0ee99b9f986f8877dcdbe28f73d9e06)](https://circleci.com/gh/tio/tio/tree/master) [![Release](https://img.shields.io/github/v/release/tio/tio?sort=semver)](https://github.com/tio/tio/releases) -[![Code Lines](https://img.shields.io/tokei/lines/github/tio/tio)]() -[![Packaging status](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions) +[![Code Lines](https://img.shields.io/tokei/lines/github/tio/tio)](https://github.com/tio/tio) +[![Packaging Status](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions) ## 1. Introduction From 512bc410f315ddfa40588fefc47732eb6dcfaf35 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 14 Sep 2022 19:53:27 +0200 Subject: [PATCH 071/506] Update README --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e739ed6..37fcb4f 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # tio - a simple serial device I/O tool -[![CircleCI](https://img.shields.io/circleci/build/gh/tio/tio?token=da7e7fd0d0ee99b9f986f8877dcdbe28f73d9e06)](https://circleci.com/gh/tio/tio/tree/master) -[![Release](https://img.shields.io/github/v/release/tio/tio?sort=semver)](https://github.com/tio/tio/releases) -[![Code Lines](https://img.shields.io/tokei/lines/github/tio/tio)](https://github.com/tio/tio) -[![Packaging Status](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions) +[![](https://img.shields.io/circleci/build/gh/tio/tio?token=da7e7fd0d0ee99b9f986f8877dcdbe28f73d9e06)](https://circleci.com/gh/tio/tio/tree/master) +[![](https://img.shields.io/github/v/release/tio/tio?sort=semver)](https://github.com/tio/tio/releases) +[![](https://img.shields.io/tokei/lines/github/tio/tio)](https://github.com/tio/tio) +[![](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions) ## 1. Introduction From 6c168bcc77eaa886f2fcc3b40729984d1f3130f8 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 16 Sep 2022 13:25:18 +0200 Subject: [PATCH 072/506] Update README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 37fcb4f..44a5d31 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,12 @@ $ tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 Using serial devices by ID ensures that tio automatically reconnects to the correct serial device if it is disconnected and then reconnected. +List available serial devices by ID: +``` +$ tio --list-devices +``` +Note: One can also use tio shell completion on /dev which will automatically +list all available serial tty devices. Inject data to the serial device: ``` From d06623dd7ab73f3157b9d52249611d0a41c29ab1 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 16 Sep 2022 13:35:23 +0200 Subject: [PATCH 073/506] Update README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 44a5d31..cd34f2d 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ established connection is lost. tio features full bash autocompletion. +#### 3.1.1 Examples Typical use is without options: ``` @@ -139,6 +140,11 @@ $ tio --list-devices Note: One can also use tio shell completion on /dev which will automatically list all available serial tty devices. +Log to file with autogenerated filename: +``` +$ tio /dev/ttyUSB0 --log +``` + Inject data to the serial device: ``` $ cat data.bin | tio /dev/ttyUSB0 From 3298ba0f1b0dad0f890aa951f0c16188c65df4c3 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 16 Sep 2022 13:43:51 +0200 Subject: [PATCH 074/506] Update README --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cd34f2d..5effb79 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,17 @@ list all available serial tty devices. Log to file with autogenerated filename: ``` -$ tio /dev/ttyUSB0 --log +$ tio --log /dev/ttyUSB0 +``` + +Enable ISO8601 timestamps per line: +``` +$ tio --timestamp --timestamp-format iso8601 /dev/ttyUSB0 +``` + +Redirect I/O to IPv4 network socket on port 4242: +``` +$ tio --socket inet:4242 /dev/ttyUSB0 ``` Inject data to the serial device: From fb89b79b3097ba9ded357cdecfb96670143e1a8f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 16 Sep 2022 13:47:29 +0200 Subject: [PATCH 075/506] Update man page --- man/tio.1.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/man/tio.1.in b/man/tio.1.in index de87ea3..ac03318 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -307,6 +307,8 @@ Toggle line timestamp mode Toggle conversion to uppercase on output .IP "\fBctrl-t v" Show version +.IP "\fBctrl-t ctrl-t" +Send ctrl-t character .SH "HEXADECIMAL MODE" .PP From f2535e4ea00c3f4c102f3bf73024e60cf37aa2ba Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 17 Sep 2022 22:57:55 +0200 Subject: [PATCH 076/506] Update README --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5effb79..943d244 100644 --- a/README.md +++ b/README.md @@ -32,12 +32,13 @@ when used in combination with [tmux](https://tmux.github.io). * Sensible defaults * Support for non-standard baud rates * Support for RS-485 mode + * Support for mark and space parity * List available serial devices by ID * Show RX/TX statistics * Toggle serial lines * Pulse serial lines with configurable pulse duration * Local echo support - * Map characters (nl, cr-nl, bs, lowercase to uppercase, etc.) + * Remapping of characters (nl, cr-nl, bs, lowercase to uppercase, etc.) * Line timestamps * Support for delayed output per character * Support for delayed output per line @@ -50,7 +51,7 @@ when used in combination with [tmux](https://tmux.github.io). * Pipe input and/or output * Support for simple line request/response handling * Bash completion - * Color support + * Configurable text color * Visual or audible alert on connect/disconnect * Remapping of prefix key * Man page documentation From d70af20454b72152d9a728faf4ec3603b65d78ab Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 18 Sep 2022 12:30:55 +0200 Subject: [PATCH 077/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 943d244..9b54d64 100644 --- a/README.md +++ b/README.md @@ -239,7 +239,7 @@ Or by pattern match: $ tio usb12 ``` -Another configuration file example is available [here](example/tiorc). +Another more elaborate configuration file example is available [here](example/tiorc). ## 4. Installation From e54a13827b4ba808cefa2ce6025651cb16d485c6 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 18 Sep 2022 12:35:49 +0200 Subject: [PATCH 078/506] Update example tiorc --- example/tiorc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/example/tiorc b/example/tiorc index c555df1..525a982 100644 --- a/example/tiorc +++ b/example/tiorc @@ -18,8 +18,10 @@ hexadecimal = disable timestamp = disable log = disable log-strip = disable +local-echo = disable color = bold rs-485 = disable +response-wait = disable alert = none # Sub-configuraions From ce3101a3805cd39ae1c2b7395c8fd0753e4b92fb Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 18 Sep 2022 19:20:12 +0200 Subject: [PATCH 079/506] Clean up indentation --- src/configfile.h | 20 +++++------ src/error.c | 72 +++++++++++++++++++-------------------- src/print.c | 50 +++++++++++++-------------- src/print.h | 88 ++++++++++++++++++++++++++---------------------- 4 files changed, 118 insertions(+), 112 deletions(-) diff --git a/src/configfile.h b/src/configfile.h index 8fbea07..6d0cc30 100644 --- a/src/configfile.h +++ b/src/configfile.h @@ -24,18 +24,18 @@ struct config_t { - const char *user; + const char *user; - char *path; - char *section_name; - char *match; + char *path; + char *section_name; + char *match; - char *tty; - char *flow; - char *parity; - char *log_filename; - char *socket; - char *map; + char *tty; + char *flow; + char *parity; + char *log_filename; + char *socket; + char *map; }; void config_file_print(void); diff --git a/src/error.c b/src/error.c index 43da8dd..dfe32aa 100644 --- a/src/error.c +++ b/src/error.c @@ -38,64 +38,64 @@ static bool in_session = false; void error_enter_session_mode(void) { - in_session = true; + in_session = true; } void error_printf_(const char *format, ...) { - va_list args; - char *line; + va_list args; + char *line; - va_start(args, format); - vasprintf(&line, format, args); + va_start(args, format); + vasprintf(&line, format, args); - if (in_session) - { - if (print_tainted) + if (in_session) { - putchar('\n'); + if (print_tainted) + { + putchar('\n'); + } + ansi_error_printf("[%s] %s", timestamp_current_time(), line); + } + else + { + fprintf(stderr, "%s\n", line); } - ansi_error_printf("[%s] %s", timestamp_current_time(), line); - } - else - { - fprintf(stderr, "%s\n", line); - } - va_end(args); + va_end(args); - print_tainted = false; - free(line); + print_tainted = false; + free(line); } void tio_error_printf(const char *format, ...) { - va_list args; + va_list args; - va_start(args, format); - vsnprintf(error[0], 1000, format, args); - va_end(args); + va_start(args, format); + vsnprintf(error[0], 1000, format, args); + va_end(args); } void tio_error_printf_silent(const char *format, ...) { - va_list args; + va_list args; - va_start(args, format); - vsnprintf(error[1], 1000, format, args); - va_end(args); + va_start(args, format); + vsnprintf(error[1], 1000, format, args); + va_end(args); } void error_exit(void) { - if (error[0][0] != 0) - { - /* Print error */ - error_printf_("Error: %s", error[0]); - } - else if ((error[1][0] != 0) && (option.no_autoconnect)) - { - /* Print silent error */ - error_printf_("Error: %s", error[1]); - } + if (error[0][0] != 0) + { + /* Print error */ + error_printf_("Error: %s", error[0]); + } + else if ((error[1][0] != 0) && (option.no_autoconnect)) + { + /* Print silent error */ + error_printf_("Error: %s", error[1]); + } } diff --git a/src/print.c b/src/print.c index cf130f0..b5ad48c 100644 --- a/src/print.c +++ b/src/print.c @@ -31,45 +31,45 @@ char ansi_format[30]; void print_hex(char c) { - printf("%02x ", (unsigned char) c); + printf("%02x ", (unsigned char) c); } void print_normal(char c) { - putchar(c); + putchar(c); } void print_init_ansi_formatting() { - if (option.color == 256) - { - // Set bold text with no color changes - sprintf(ansi_format, "\e[1m"); - } - else - { - // Set bold text with user defined ANSI color - sprintf(ansi_format, "\e[1;38;5;%dm", option.color); - } + if (option.color == 256) + { + // Set bold text with no color changes + sprintf(ansi_format, "\e[1m"); + } + else + { + // Set bold text with user defined ANSI color + sprintf(ansi_format, "\e[1;38;5;%dm", option.color); + } } void tio_printf_array(const char *array) { - int i = 0, j = 0; + int i = 0, j = 0; - tio_printf(""); + tio_printf(""); - while (array[i]) - { - if (array[i] == '\n') + while (array[i]) { - const char *line = &array[j]; - char *line_copy = strndup(line, i-j); - tio_printf_raw("%s\r", line_copy); - free(line_copy); - j = i; + if (array[i] == '\n') + { + const char *line = &array[j]; + char *line_copy = strndup(line, i-j); + tio_printf_raw("%s\r", line_copy); + free(line_copy); + j = i; + } + i++; } - i++; - } - tio_printf(""); + tio_printf(""); } diff --git a/src/print.h b/src/print.h index e3f6193..c870f17 100644 --- a/src/print.h +++ b/src/print.h @@ -35,72 +35,78 @@ extern char ansi_format[]; #define ansi_printf(format, args...) \ { \ - if (!option.mute) { \ - if (option.color < 0) \ - fprintf (stdout, "\r" format "\r\n", ## args); \ - else \ - fprintf (stdout, "\r%s" format ANSI_RESET "\r\n", ansi_format, ## args); \ - } \ + if (!option.mute) \ + { \ + if (option.color < 0) \ + fprintf (stdout, "\r" format "\r\n", ## args); \ + else \ + fprintf (stdout, "\r%s" format ANSI_RESET "\r\n", ansi_format, ## args); \ + } \ } #define ansi_error_printf(format, args...) \ { \ - if (!option.mute) { \ - if (option.color < 0) \ - fprintf (stderr, "\r" format "\r\n", ## args); \ - else \ - fprintf (stderr, "\r%s" format ANSI_RESET "\r\n", ansi_format, ## args); \ - fflush(stderr); \ - } \ + if (!option.mute) \ + { \ + if (option.color < 0) \ + fprintf (stderr, "\r" format "\r\n", ## args); \ + else \ + fprintf (stderr, "\r%s" format ANSI_RESET "\r\n", ansi_format, ## args); \ + fflush(stderr); \ + } \ } #define ansi_printf_raw(format, args...) \ { \ - if (!option.mute) { \ - if (option.color < 0) \ - fprintf (stdout, format, ## args); \ - else \ - fprintf (stdout, "%s" format ANSI_RESET, ansi_format, ## args); \ - } \ + if (!option.mute) \ + { \ + if (option.color < 0) \ + fprintf (stdout, format, ## args); \ + else \ + fprintf (stdout, "%s" format ANSI_RESET, ansi_format, ## args); \ + } \ } #define tio_warning_printf(format, args...) \ { \ - if (!option.mute) { \ - if (print_tainted) \ - putchar('\n'); \ - if (option.color < 0) \ - fprintf (stdout, "\r[%s] Warning: " format "\r\n", timestamp_current_time(), ## args); \ - else \ - ansi_printf("[%s] Warning: " format, timestamp_current_time(), ## args); \ - } \ + if (!option.mute) \ + { \ + if (print_tainted) \ + putchar('\n'); \ + if (option.color < 0) \ + fprintf (stdout, "\r[%s] Warning: " format "\r\n", timestamp_current_time(), ## args); \ + else \ + ansi_printf("[%s] Warning: " format, timestamp_current_time(), ## args); \ + } \ } #define tio_printf(format, args...) \ { \ - if (!option.mute) { \ - if (print_tainted) \ - putchar('\n'); \ - ansi_printf("[%s] " format, timestamp_current_time(), ## args); \ - print_tainted = false; \ - } \ + if (!option.mute) \ + { \ + if (print_tainted) \ + putchar('\n'); \ + ansi_printf("[%s] " format, timestamp_current_time(), ## args); \ + print_tainted = false; \ + } \ } #define tio_printf_raw(format, args...) \ { \ - if (!option.mute) { \ - if (print_tainted) \ - putchar('\n'); \ - ansi_printf_raw("[%s] " format, timestamp_current_time(), ## args); \ - print_tainted = false; \ - } \ + if (!option.mute) \ + { \ + if (print_tainted) \ + putchar('\n'); \ + ansi_printf_raw("[%s] " format, timestamp_current_time(), ## args); \ + print_tainted = false; \ + } \ } #ifdef DEBUG #define tio_debug_printf(format, args...) \ - fprintf (stdout, "[debug] " format, ## args) + fprintf(stdout, "[debug] " format, ## args) #define tio_debug_printf_raw(format, args...) \ - fprintf (stdout, "" format, ## args) + fprintf(stdout, "" format, ## args) #else #define tio_debug_printf(format, args...) #define tio_debug_printf_raw(format, args...) From 5b8f33bd78d89c17dee0e4fb89965e2880a3c594 Mon Sep 17 00:00:00 2001 From: Attila Veghelyi Date: Thu, 29 Sep 2022 15:42:44 +0200 Subject: [PATCH 080/506] Add bit reverse order feature --- src/tty.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/tty.c b/src/tty.c index a3f674c..d5fb7af 100644 --- a/src/tty.c +++ b/src/tty.c @@ -84,6 +84,7 @@ #define KEY_H 0x68 #define KEY_L 0x6C #define KEY_SHIFT_L 0x4C +#define KEY_M 0x6D #define KEY_P 0x70 #define KEY_Q 0x71 #define KEY_S 0x73 @@ -126,6 +127,7 @@ 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_msblsb = false; static char hex_chars[2]; static unsigned char hex_char_index = 0; static char tty_buffer[BUFSIZ*2]; @@ -410,6 +412,7 @@ void handle_command_sequence(char input_char, char previous_char, char *output_c tio_printf(" ctrl-%c h Toggle hexadecimal 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 p Pulse serial port line", option.prefix_key); tio_printf(" ctrl-%c q Quit", option.prefix_key); tio_printf(" ctrl-%c s Show statistics", option.prefix_key); @@ -498,6 +501,20 @@ void handle_command_sequence(char input_char, char previous_char, char *output_c printf("\033c"); 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"); + } + break; + case KEY_Q: /* Exit upon ctrl-t q sequence */ exit(EXIT_SUCCESS); @@ -860,6 +877,10 @@ void tty_configure(void) { map_o_ltu = true; } + else if (strcmp(token, "MSB2LSB") == 0) + { + map_o_msblsb = true; + } else { printf("Error: Unknown mapping flag %s\n", token); @@ -1219,8 +1240,19 @@ int tty_connect(void) } } + /* Convert MSB to LSB bit order */ + if (map_o_msblsb) + { + char ch = input_char; + input_char = 0; + for (int j = 0; j < 8; ++j) + { + input_char |= ((1 << j) & ch) ? (1 << (7 - j)) : 0; + } + } + /* Map input character */ - if ((input_char == '\n') && (map_i_nl_crnl)) + if ((input_char == '\n') && (map_i_nl_crnl) && (!map_o_msblsb)) { print('\r'); print('\n'); From 49249692686c01f5eb16a0c8b747107a6fa5c927 Mon Sep 17 00:00:00 2001 From: Attila Veghelyi Date: Thu, 29 Sep 2022 17:33:35 +0200 Subject: [PATCH 081/506] Complete bit reorder feature for release --- man/tio.1.in | 2 ++ man/tio.1.txt | 2 ++ src/bash-completion/tio.in | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/man/tio.1.in b/man/tio.1.in index ac03318..936ab5c 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -167,6 +167,8 @@ Map DEL to BS on output Map NL to CR-NL on output .IP "\fBOLTU" Map lowercase characters to uppercase 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/man/tio.1.txt b/man/tio.1.txt index 0399b63..3e03611 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -131,6 +131,8 @@ OPTIONS OLTU Map lowercase characters to uppercase on output + MSB2LSB Map MSB bit order to LSB on output + If defining more than one flag, the flags must be comma separated. -x, --hexadecimal diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index ca115e6..7e110f6 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -94,7 +94,7 @@ _tio() return 0 ;; -m | --map) - COMPREPLY=( $(compgen -W "ICRNL IGNCR INLCR INLCRNL OCRNL ODELBS ONLCRNL" -- ${cur}) ) + COMPREPLY=( $(compgen -W "ICRNL IGNCR INLCR INLCRNL OCRNL ODELBS ONLCRNL MSB2LSB" -- ${cur}) ) return 0 ;; -t | --timestamp) From 0b61aaf451f57cf79eb840b6bc93ea21182cbec8 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 29 Sep 2022 20:16:47 +0200 Subject: [PATCH 082/506] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 762ce18..8f5cada 100644 --- a/AUTHORS +++ b/AUTHORS @@ -40,5 +40,6 @@ Robert Snell Rui Chen Ralph Siemsen Victor Oliveira +Attila Veghelyi Thanks to everyone who has contributed to this project. From 5f14924c964c731ba1e24bbb479b88c3d6cce747 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 29 Sep 2022 20:18:32 +0200 Subject: [PATCH 083/506] Improve input mechanism in hex mode Print the 2 character hex code that you input in hex mode but then delete it before sending. This way it is easier to keep track of what you are inputting. It basically mimics the ctrl-shift-u input mechanism that is used to input unicode. --- src/tty.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tty.c b/src/tty.c index d5fb7af..0fd2579 100644 --- a/src/tty.c +++ b/src/tty.c @@ -259,8 +259,14 @@ static void output_hex(char c) { hex_chars[hex_char_index++] = c; + printf("%c", c); + if (hex_char_index == 2) { + usleep(100*1000); + printf("\b \b"); + printf("\b \b"); + unsigned char hex_value = char_to_nibble(hex_chars[0]) << 4 | (char_to_nibble(hex_chars[1]) & 0x0F); hex_char_index = 0; From a65b5b0e3a9320f24228674c651fe55dbf837260 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 29 Sep 2022 20:50:12 +0200 Subject: [PATCH 084/506] Do not print timestamps in hex mode --- src/tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tty.c b/src/tty.c index 0fd2579..70d6208 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1232,7 +1232,7 @@ int tty_connect(void) input_char = input_buffer[i]; /* Print timestamp on new line if enabled */ - if (next_timestamp && input_char != '\n' && input_char != '\r') + if ((next_timestamp && input_char != '\n' && input_char != '\r') && !option.hex_mode) { now = timestamp_current_time(); if (now) From c8aeba7ef885361c3e74551910922cbc69646868 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 11 Oct 2022 12:12:37 +0200 Subject: [PATCH 085/506] Fix output line delay Apply output line delay on lines ending with \n. On most systems lines ends with \n or \r\n. --- src/tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tty.c b/src/tty.c index 70d6208..75c7242 100644 --- a/src/tty.c +++ b/src/tty.c @@ -223,7 +223,7 @@ ssize_t tty_write(int fd, const void *buffer, size_t count) } bytes_written += retval; - if (option.output_line_delay && *(unsigned char*)buffer == '\r') + if (option.output_line_delay && *(unsigned char*)buffer == '\n') { delay(option.output_line_delay); } From 0850b1938b3a9f95f024a66b51415897fd1ca463 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 15 Oct 2022 00:56:36 +0200 Subject: [PATCH 086/506] Update NEWS --- NEWS | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 544f8a1..4e99f00 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,36 @@ -=== tio v2.0 === +=== tio v2.1 === + + +Changes since tio v2.0: + + * Fix output line delay + + Apply output line delay on lines ending with \n. + + On most systems lines ends with \n or \r\n. + + * Do not print timestamps in hex mode + + * Improve input mechanism in hex mode + + Print the 2 character hex code that you input in hex mode but then + delete it before sending. This way it is easier to keep track of what + you are inputting. It basically mimics the ctrl-shift-u input mechanism + that is used to input unicode. + + * Add support for sending prefix character to serial device + + Do so by inputting prefix key twice, e.g. input ctrl-t ctrl-t to send + ctrl-t character to serial device. + + * Clean up indentation + + * Update example tiorc + +Attila Veghelyi: + + * Add bit reverse order feature From ca5af47e7b47b56b61f3d493d5f294733eeea1ec Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 15 Oct 2022 00:57:35 +0200 Subject: [PATCH 087/506] Update tag date --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index ddd18a8..a89cc4f 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 = '2022-09-11' +version_date = '2022-10-15' # Test for dynamic baudrate configuration interface compiler = meson.get_compiler('c') From 14fc77ffc13a4c60a98f0bb7e0f431e9ed7cf1fd Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 15 Oct 2022 00:58:47 +0200 Subject: [PATCH 088/506] Update plain text man page --- man/tio.1.txt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/man/tio.1.txt b/man/tio.1.txt index 3e03611..2ed7b1c 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 simple serial device I/O tool @@ -66,7 +66,7 @@ OPTIONS Disable automatic connect. - By default tio automatically connects to the provided device if present. If the device is not present, it will wait for it to appear and then connect. If the connection is lost + By default tio automatically connects to the provided device if present. If the device is not present, it will wait for it to appear and then connect. If the connection is lost (eg. device disconnects), it will wait for the device to reappear and then reconnect. However, if the --no-autoconnect option is provided, tio will exit if the device is not present or an established connection is lost. @@ -151,8 +151,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 multiplexed to the terminal and all connected clients. Sockets remain open while the serial port is disconnected, and writes will block. @@ -245,6 +245,8 @@ KEYS ctrl-t v Show version + ctrl-t ctrl-t Send ctrl-t character + HEXADECIMAL MODE In hexadecimal mode each incoming byte is printed out as a hexadecimal value. @@ -438,4 +440,4 @@ WEBSITE AUTHOR Created by Martin Lund . -tio 2.0 2022-09-11 tio(1) +tio 2.1 2022-10-15 tio(1) From bf7b0326496179d64136fe47d79a44e6b15b520d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 15 Oct 2022 23:15:01 +0200 Subject: [PATCH 089/506] Bump version --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index a89cc4f..09252bc 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('tio', 'c', - version : '2.1', + version : '2.2', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] From a97b2c00cd3f92925d28d1d444910145ad3ed540 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 16 Oct 2022 23:01:08 +0200 Subject: [PATCH 090/506] Add shell completion of sub-configuration names Does not work with sub configuration names that contains one or more white spaces. --- src/bash-completion/tio.in | 4 ++- src/configfile.c | 40 +++++++++++++++++++++- src/configfile.h | 1 + src/main.c | 6 ++++ src/options.c | 68 ++++++++++++++++++++++---------------- src/options.h | 1 + 6 files changed, 90 insertions(+), 30 deletions(-) diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index 7e110f6..82cb649 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -159,12 +159,14 @@ _tio() ;; esac + sub_configs="`tio --complete-sub-configs`" + if [ -d /dev/serial/by-id ]; then ttys=$(printf '%s\n' /dev/tty* /dev/serial/by-id/*) else ttys=$(printf '%s\n' /dev/tty*) fi - COMPREPLY=( $(compgen -W "${ttys}" -- ${cur}) ) + COMPREPLY=( $(compgen -W "${ttys} ${sub_configs}" -- ${cur}) ) return 0 } diff --git a/src/configfile.c b/src/configfile.c index 864380e..b9ebf2f 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -342,6 +342,24 @@ static int section_name_search_handler(void *user, const char *section, const ch return 0; } +static int section_name_print_handler(void *user, const char *section, const char *varname, + const char *varval) +{ + UNUSED(user); + UNUSED(varname); + UNUSED(varval); + + static char *section_previous = ""; + + if (strcmp(section, section_previous) != 0) + { + printf("%s ", section); + section_previous = strdup(section); + } + + return 0; +} + static int resolve_config_file(void) { asprintf(&c->path, "%s/tio/tiorc", getenv("XDG_CONFIG_HOME")); @@ -373,6 +391,26 @@ static int resolve_config_file(void) return -EINVAL; } +void config_file_show_sub_configurations(void) +{ + c = malloc(sizeof(struct config_t)); + if (!c) + { + tio_error_printf("Insufficient memory allocation"); + exit(EXIT_FAILURE); + } + memset(c, 0, sizeof(struct config_t)); + + // Find config file + if (resolve_config_file() != 0) + { + // None found - stop parsing + return; + } + + ini_parse(c->path, section_name_print_handler, NULL); +} + void config_file_parse(void) { int ret; @@ -382,7 +420,7 @@ void config_file_parse(void) { tio_error_printf("Insufficient memory allocation"); exit(EXIT_FAILURE); - } + } memset(c, 0, sizeof(struct config_t)); // Find config file diff --git a/src/configfile.h b/src/configfile.h index 6d0cc30..1f6f8b7 100644 --- a/src/configfile.h +++ b/src/configfile.h @@ -41,3 +41,4 @@ struct config_t void config_file_print(void); void config_file_parse(void); void config_exit(void); +void config_file_show_sub_configurations(void); diff --git a/src/main.c b/src/main.c index 02f8a47..d2b1c0d 100644 --- a/src/main.c +++ b/src/main.c @@ -45,6 +45,12 @@ int main(int argc, char *argv[]) /* Parse command-line options (1st pass) */ options_parse(argc, argv); + if (option.complete_sub_configs) + { + config_file_show_sub_configurations(); + return status; + } + /* Parse configuration file */ config_file_parse(); diff --git a/src/options.c b/src/options.c index d21ed87..35608f7 100644 --- a/src/options.c +++ b/src/options.c @@ -50,6 +50,7 @@ enum opt_t OPT_RS485, OPT_RS485_CONFIG, OPT_ALERT, + OPT_COMPLETE_SUB_CONFIGS, }; /* Default options */ @@ -89,6 +90,7 @@ struct option_t option = .rs485_delay_rts_before_send = -1, .rs485_delay_rts_after_send = -1, .alert = ALERT_NONE, + .complete_sub_configs = false, }; void print_help(char *argv[]) @@ -229,34 +231,35 @@ void options_parse(int argc, char *argv[]) { static struct option long_options[] = { - {"baudrate", required_argument, 0, 'b' }, - {"databits", required_argument, 0, 'd' }, - {"flow", required_argument, 0, 'f' }, - {"stopbits", required_argument, 0, 's' }, - {"parity", required_argument, 0, 'p' }, - {"output-delay", required_argument, 0, 'o' }, - {"output-line-delay" , required_argument, 0, 'O' }, - {"line-pulse-duration", required_argument, 0, OPT_LINE_PULSE_DURATION}, - {"no-autoconnect", no_argument, 0, 'n' }, - {"local-echo", no_argument, 0, 'e' }, - {"timestamp", no_argument, 0, 't' }, - {"timestamp-format", required_argument, 0, OPT_TIMESTAMP_FORMAT }, - {"list-devices", no_argument, 0, 'L' }, - {"log", no_argument, 0, 'l' }, - {"log-file", required_argument, 0, OPT_LOG_FILE }, - {"log-strip", no_argument, 0, OPT_LOG_STRIP }, - {"socket", required_argument, 0, 'S' }, - {"map", required_argument, 0, 'm' }, - {"color", required_argument, 0, 'c' }, - {"hexadecimal", no_argument, 0, 'x' }, - {"response-wait", no_argument, 0, 'r' }, - {"response-timeout", required_argument, 0, OPT_RESPONSE_TIMEOUT }, - {"rs-485", no_argument, 0, OPT_RS485 }, - {"rs-485-config", required_argument, 0, OPT_RS485_CONFIG }, - {"alert", required_argument, 0, OPT_ALERT }, - {"version", no_argument, 0, 'v' }, - {"help", no_argument, 0, 'h' }, - {0, 0, 0, 0 } + {"baudrate", required_argument, 0, 'b' }, + {"databits", required_argument, 0, 'd' }, + {"flow", required_argument, 0, 'f' }, + {"stopbits", required_argument, 0, 's' }, + {"parity", required_argument, 0, 'p' }, + {"output-delay", required_argument, 0, 'o' }, + {"output-line-delay" , required_argument, 0, 'O' }, + {"line-pulse-duration", required_argument, 0, OPT_LINE_PULSE_DURATION }, + {"no-autoconnect", no_argument, 0, 'n' }, + {"local-echo", no_argument, 0, 'e' }, + {"timestamp", no_argument, 0, 't' }, + {"timestamp-format", required_argument, 0, OPT_TIMESTAMP_FORMAT }, + {"list-devices", no_argument, 0, 'L' }, + {"log", no_argument, 0, 'l' }, + {"log-file", required_argument, 0, OPT_LOG_FILE }, + {"log-strip", no_argument, 0, OPT_LOG_STRIP }, + {"socket", required_argument, 0, 'S' }, + {"map", required_argument, 0, 'm' }, + {"color", required_argument, 0, 'c' }, + {"hexadecimal", no_argument, 0, 'x' }, + {"response-wait", no_argument, 0, 'r' }, + {"response-timeout", required_argument, 0, OPT_RESPONSE_TIMEOUT }, + {"rs-485", no_argument, 0, OPT_RS485 }, + {"rs-485-config", required_argument, 0, OPT_RS485_CONFIG }, + {"alert", required_argument, 0, OPT_ALERT }, + {"version", no_argument, 0, 'v' }, + {"help", no_argument, 0, 'h' }, + {"complete-sub-configs", no_argument, 0, OPT_COMPLETE_SUB_CONFIGS}, + {0, 0, 0, 0 } }; /* getopt_long stores the option index here */ @@ -418,6 +421,10 @@ void options_parse(int argc, char *argv[]) exit(EXIT_SUCCESS); break; + case OPT_COMPLETE_SUB_CONFIGS: + option.complete_sub_configs = true; + break; + case '?': /* getopt_long already printed an error message */ exit(EXIT_FAILURE); @@ -434,6 +441,11 @@ void options_parse(int argc, char *argv[]) else if (optind < argc) option.tty_device = argv[optind++]; + if (option.complete_sub_configs) + { + return; + } + if (strlen(option.tty_device) == 0) { tio_error_printf("Missing tty device or sub-configuration name"); diff --git a/src/options.h b/src/options.h index 22ec236..8ae556d 100644 --- a/src/options.h +++ b/src/options.h @@ -66,6 +66,7 @@ struct option_t int32_t rs485_delay_rts_before_send; int32_t rs485_delay_rts_after_send; enum alert_t alert; + bool complete_sub_configs; }; extern struct option_t option; From ad9b93dc522e0e59b8faf9af557ed8b9ef031eeb Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 16 Oct 2022 23:16:27 +0200 Subject: [PATCH 091/506] Simplify configfile implementation --- src/configfile.c | 142 ++++++++++++++++++++++++----------------------- src/configfile.h | 16 ------ 2 files changed, 72 insertions(+), 86 deletions(-) diff --git a/src/configfile.c b/src/configfile.c index b9ebf2f..e749e4b 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -46,7 +46,23 @@ #include "timestamp.h" #include "alert.h" -static struct config_t *c; +struct config_t +{ + const char *user; + + char *path; + char *section_name; + char *match; + + char *tty; + char *flow; + char *parity; + char *log_filename; + char *socket; + char *map; +}; + +static struct config_t c; static int get_match(const char *input, const char *pattern, char **match) { @@ -93,13 +109,13 @@ static int data_handler(void *user, const char *section, const char *name, UNUSED(user); // If section matches current section being parsed - if (!strcmp(section, c->section_name)) + if (!strcmp(section, c.section_name)) { // Set configuration parameter if found if (!strcmp(name, "tty")) { - asprintf(&c->tty, value, c->match); - option.tty_device = c->tty; + asprintf(&c.tty, value, c.match); + option.tty_device = c.tty; } else if (!strcmp(name, "baudrate")) { @@ -111,8 +127,8 @@ static int data_handler(void *user, const char *section, const char *name, } else if (!strcmp(name, "flow")) { - asprintf(&c->flow, "%s", value); - option.flow = c->flow; + asprintf(&c.flow, "%s", value); + option.flow = c.flow; } else if (!strcmp(name, "stopbits")) { @@ -120,8 +136,8 @@ static int data_handler(void *user, const char *section, const char *name, } else if (!strcmp(name, "parity")) { - asprintf(&c->parity, "%s", value); - option.parity = c->parity; + asprintf(&c.parity, "%s", value); + option.parity = c.parity; } else if (!strcmp(name, "output-delay")) { @@ -159,8 +175,8 @@ static int data_handler(void *user, const char *section, const char *name, } else if (!strcmp(name, "log-file")) { - asprintf(&c->log_filename, "%s", value); - option.log_filename = c->log_filename; + asprintf(&c.log_filename, "%s", value); + option.log_filename = c.log_filename; } else if (!strcmp(name, "log-strip")) { @@ -212,8 +228,8 @@ static int data_handler(void *user, const char *section, const char *name, } else if (!strcmp(name, "map")) { - asprintf(&c->map, "%s", value); - option.map = c->map; + asprintf(&c.map, "%s", value); + option.map = c.map; } else if (!strcmp(name, "color")) { @@ -241,8 +257,8 @@ static int data_handler(void *user, const char *section, const char *name, } else if (!strcmp(name, "socket")) { - asprintf(&c->socket, "%s", value); - option.socket = c->socket; + asprintf(&c.socket, "%s", value); + option.socket = c.socket; } else if (!strcmp(name, "prefix-ctrl-key")) { @@ -306,15 +322,15 @@ static int section_pattern_search_handler(void *user, const char *section, const if (strcmp(varname, "pattern")) return 0; - if (!strcmp(varval, c->user)) + if (!strcmp(varval, c.user)) { /* pattern matches as plain text */ - asprintf(&c->section_name, "%s", section); + asprintf(&c.section_name, "%s", section); } - else if (get_match(c->user, varval, &c->match) > 0) + else if (get_match(c.user, varval, &c.match) > 0) { /* pattern matches as regex */ - asprintf(&c->section_name, "%s", section); + asprintf(&c.section_name, "%s", section); } return 0; @@ -333,10 +349,10 @@ static int section_name_search_handler(void *user, const char *section, const ch UNUSED(varname); UNUSED(varval); - if (!strcmp(section, c->user)) + if (!strcmp(section, c.user)) { /* section name matches as plain text */ - asprintf(&c->section_name, "%s", section); + asprintf(&c.section_name, "%s", section); } return 0; @@ -362,44 +378,38 @@ static int section_name_print_handler(void *user, const char *section, const cha static int resolve_config_file(void) { - asprintf(&c->path, "%s/tio/tiorc", getenv("XDG_CONFIG_HOME")); - if (!access(c->path, F_OK)) + asprintf(&c.path, "%s/tio/tiorc", getenv("XDG_CONFIG_HOME")); + if (!access(c.path, F_OK)) { return 0; } - free(c->path); + free(c.path); - asprintf(&c->path, "%s/.config/tio/tiorc", getenv("HOME")); - if (!access(c->path, F_OK)) + asprintf(&c.path, "%s/.config/tio/tiorc", getenv("HOME")); + if (!access(c.path, F_OK)) { return 0; } - free(c->path); + free(c.path); - asprintf(&c->path, "%s/.tiorc", getenv("HOME")); - if (!access(c->path, F_OK)) + asprintf(&c.path, "%s/.tiorc", getenv("HOME")); + if (!access(c.path, F_OK)) { return 0; } - free(c->path); + free(c.path); - c->path = NULL; + c.path = NULL; return -EINVAL; } void config_file_show_sub_configurations(void) { - c = malloc(sizeof(struct config_t)); - if (!c) - { - tio_error_printf("Insufficient memory allocation"); - exit(EXIT_FAILURE); - } - memset(c, 0, sizeof(struct config_t)); + memset(&c, 0, sizeof(struct config_t)); // Find config file if (resolve_config_file() != 0) @@ -408,20 +418,14 @@ void config_file_show_sub_configurations(void) return; } - ini_parse(c->path, section_name_print_handler, NULL); + ini_parse(c.path, section_name_print_handler, NULL); } void config_file_parse(void) { int ret; - c = malloc(sizeof(struct config_t)); - if (!c) - { - tio_error_printf("Insufficient memory allocation"); - exit(EXIT_FAILURE); - } - memset(c, 0, sizeof(struct config_t)); + memset(&c, 0, sizeof(struct config_t)); // Find config file if (resolve_config_file() != 0) @@ -431,30 +435,30 @@ void config_file_parse(void) } // Set user input which may be tty device or sub config - c->user = option.tty_device; + c.user = option.tty_device; - if (!c->user) + if (!c.user) { return; } // Parse default (unnamed) settings - asprintf(&c->section_name, "%s", ""); - ret = ini_parse(c->path, data_handler, NULL); + asprintf(&c.section_name, "%s", ""); + ret = ini_parse(c.path, data_handler, NULL); if (ret < 0) { tio_error_printf("Unable to parse configuration file (%d)", ret); exit(EXIT_FAILURE); } - free(c->section_name); - c->section_name = NULL; + free(c.section_name); + c.section_name = NULL; // Find matching section - ret = ini_parse(c->path, section_pattern_search_handler, NULL); - if (!c->section_name) + ret = ini_parse(c.path, section_pattern_search_handler, NULL); + if (!c.section_name) { - ret = ini_parse(c->path, section_name_search_handler, NULL); - if (!c->section_name) + ret = ini_parse(c.path, section_name_search_handler, NULL); + if (!c.section_name) { tio_debug_printf("Unable to match user input to configuration section (%d)", ret); return; @@ -462,7 +466,7 @@ void config_file_parse(void) } // Parse settings of found section (sub config) - ret = ini_parse(c->path, data_handler, NULL); + ret = ini_parse(c.path, data_handler, NULL); if (ret < 0) { tio_error_printf("Unable to parse configuration file (%d)", ret); @@ -474,27 +478,25 @@ void config_file_parse(void) void config_exit(void) { - free(c->tty); - free(c->flow); - free(c->parity); - free(c->log_filename); - free(c->map); + free(c.tty); + free(c.flow); + free(c.parity); + free(c.log_filename); + free(c.map); - free(c->match); - free(c->section_name); - free(c->path); - - free(c); + free(c.match); + free(c.section_name); + free(c.path); } void config_file_print(void) { - if (c->path != NULL) + if (c.path != NULL) { - tio_printf(" Path: %s", c->path); - if (c->section_name != NULL) + tio_printf(" Path: %s", c.path); + if (c.section_name != NULL) { - tio_printf(" Active sub-configuration: %s", c->section_name); + tio_printf(" Active sub-configuration: %s", c.section_name); } } } diff --git a/src/configfile.h b/src/configfile.h index 1f6f8b7..74e1b6e 100644 --- a/src/configfile.h +++ b/src/configfile.h @@ -22,22 +22,6 @@ #pragma once -struct config_t -{ - const char *user; - - char *path; - char *section_name; - char *match; - - char *tty; - char *flow; - char *parity; - char *log_filename; - char *socket; - char *map; -}; - void config_file_print(void); void config_file_parse(void); void config_exit(void); From c2aa3489fdacb701a96690cb9ce7ec60d7b16ddd Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 17 Oct 2022 01:12:35 +0200 Subject: [PATCH 092/506] Fix error message --- src/options.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/options.c b/src/options.c index 35608f7..274e1eb 100644 --- a/src/options.c +++ b/src/options.c @@ -457,7 +457,9 @@ void options_parse(int argc, char *argv[]) { fprintf(stderr, "Error: Unknown argument "); while (optind < argc) - printf("%s ", argv[optind++]); + { + fprintf(stderr, "%s ", argv[optind++]); + } fprintf(stderr, "\n"); exit(EXIT_FAILURE); } From 4768db53119a8c10a033ec526f4a9d2967000fc6 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 17 Oct 2022 13:31:07 +0200 Subject: [PATCH 093/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b54d64..1c87aa0 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ when used in combination with [tmux](https://tmux.github.io). * Redirect I/O to UNIX socket or IPv4/v6 network socket for scripting or TTY sharing * Pipe input and/or output * Support for simple line request/response handling - * Bash completion + * Bash completion on options, serial device names, and sub-configuration names * Configurable text color * Visual or audible alert on connect/disconnect * Remapping of prefix key From b00b0e872f7f0213bdc34cc589addbd82061817f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 17 Oct 2022 13:55:00 +0200 Subject: [PATCH 094/506] Beautify help --- src/options.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/options.c b/src/options.c index 274e1eb..7b61fd8 100644 --- a/src/options.c +++ b/src/options.c @@ -95,7 +95,9 @@ struct option_t option = void print_help(char *argv[]) { - printf("Usage: %s [] \n", argv[0]); + UNUSED(argv); + + printf("Usage: tio [] \n"); printf("\n"); printf("Connect to tty device directly or via sub-configuration.\n"); printf("\n"); From a40097f837b423b64d2a3747c67129fe4290fb34 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 18 Oct 2022 15:28:59 +0200 Subject: [PATCH 095/506] Update man page --- man/tio.1.txt | 11 +++++------ meson.build | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/man/tio.1.txt b/man/tio.1.txt index 2ed7b1c..1874c65 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 simple serial device I/O tool @@ -66,8 +66,7 @@ OPTIONS Disable automatic connect. - By default tio automatically connects to the provided device if present. If the device is not present, it will wait for it to appear and then connect. If the connection is lost - (eg. device disconnects), it will wait for the device to reappear and then reconnect. + By default tio automatically connects to the provided device if present. If the device is not present, it will wait for it to appear and then connect. If the connection is lost (eg. device disconnects), it will wait for the device to reappear and then reconnect. However, if the --no-autoconnect option is provided, tio will exit if the device is not present or an established connection is lost. @@ -151,8 +150,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 multiplexed to the terminal and all connected + clients. Sockets remain open while the serial port is disconnected, and writes will block. @@ -440,4 +439,4 @@ WEBSITE AUTHOR Created by Martin Lund . -tio 2.1 2022-10-15 tio(1) +tio 2.2 2022-10-18 tio(1) diff --git a/meson.build b/meson.build index 09252bc..9a91e85 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 = '2022-10-15' +version_date = '2022-10-18' # Test for dynamic baudrate configuration interface compiler = meson.get_compiler('c') From eaab692d4d6be1ef41c0f6950977cf9054520cb7 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 18 Oct 2022 15:31:28 +0200 Subject: [PATCH 096/506] Update NEWS --- NEWS | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 4e99f00..1a1cc4d 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,21 @@ -=== tio v2.1 === +=== tio v2.2 === + + + +Changes since tio v2.1: + + * Add shell completion of sub-configuration names + + Does not work with sub configuration names that contains one or more + white spaces. + + * Beautify help + + * Fix error message + + * Simplify configfile implementation + Changes since tio v2.0: From 134fa87a5c4d388f0c46b1182a4de6f91236924c Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 18 Oct 2022 16:13:35 +0200 Subject: [PATCH 097/506] Bump version --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 9a91e85..bf265e2 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('tio', 'c', - version : '2.2', + version : '2.3', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] From 6f9e41152b605d1705e1a44893cd1464c28963a0 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 19 Oct 2022 22:18:25 +0200 Subject: [PATCH 098/506] Update show config --- src/options.c | 2 +- src/tty.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/options.c b/src/options.c index 7b61fd8..9daac17 100644 --- a/src/options.c +++ b/src/options.c @@ -193,7 +193,7 @@ void line_pulse_duration_option_parse(const char *arg) void options_print() { - tio_printf(" TTY device: %s", option.tty_device); + tio_printf(" Device: %s", option.tty_device); tio_printf(" Baudrate: %u", option.baudrate); tio_printf(" Databits: %d", option.databits); tio_printf(" Flow: %s", option.flow); diff --git a/src/tty.c b/src/tty.c index 75c7242..8c7bfa7 100644 --- a/src/tty.c +++ b/src/tty.c @@ -473,8 +473,8 @@ void handle_command_sequence(char input_char, char previous_char, char *output_c case KEY_C: tio_printf("Configuration:"); - config_file_print(); options_print(); + config_file_print(); if (option.rs485) { rs485_print_config(); From 48a2298e81ac6d81861f1d8b937fd186563428fb Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 19 Oct 2022 22:29:33 +0200 Subject: [PATCH 099/506] Rename config variable 'tty' to 'device' --- man/tio.1.in | 16 ++++++++-------- src/configfile.c | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 936ab5c..01349d8 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -340,7 +340,7 @@ activated from the command-line when starting tio. .PP \fBtio\fR will try to match the user input to a sub-configuration by name or by -pattern to get the tty and other options. +pattern to get the TTY device and other options. .PP Options without any label change the default options. @@ -354,8 +354,8 @@ The following configuration file options are available: .TP 25n .IP "\fBpattern" Pattern matching user input. This pattern can be an extended regular expression with a single group. -.IP "\fBtty" -tty device to open. If it contains a "%s" it is substituted with the first group match. +.IP "\fBdevice" +TTY device to open. If it contains a "%s" it is substituted with the first group match. .IP "\fBbaudrate" Set baud rate .IP "\fBdatabits" @@ -433,7 +433,7 @@ Named sub-configurations can be added via labels: .nf .eo [rpi3] -tty = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 +device = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 baudrate = 115200 color = 11 .ec @@ -458,7 +458,7 @@ A sub-configuration can also be activated by its pattern which supports regular .eo [usb device] pattern = usb([0-9]*) -tty = /dev/ttyUSB%s +device = /dev/ttyUSB%s baudrate = 115200 .ec .fi @@ -489,7 +489,7 @@ Which corresponds to the commonly used default options: $ tio \-b 115200 \-d 8 \-f none \-s 1 \-p none /dev/ttyUSB0 .TP -It is recommended to connect serial tty devices by ID: +It is recommended to connect serial TTY devices by ID: $ tio /dev/serial/by\-id/usb\-FTDI_TTL232R-3V3_FTGQVXBL\-if00\-port0 .PP @@ -528,13 +528,13 @@ expect -i $uart "prompt> " .RE .TP -Redirect device I/O to network file socket for remote tty sharing: +Redirect device I/O to network file socket for remote TTY sharing: $ tio --socket inet:4444 /dev/ttyUSB0 .TP -Then, use netcat to connect to the shared tty session over network (assuming tio is hosted on IP 10.0.0.42): +Then, use netcat to connect to the shared TTY session over network (assuming tio is hosted on IP 10.0.0.42): $ nc -N 10.0.0.42 4444 diff --git a/src/configfile.c b/src/configfile.c index e749e4b..d849685 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -112,7 +112,7 @@ static int data_handler(void *user, const char *section, const char *name, if (!strcmp(section, c.section_name)) { // Set configuration parameter if found - if (!strcmp(name, "tty")) + if (!strcmp(name, "device")) { asprintf(&c.tty, value, c.match); option.tty_device = c.tty; From fba56318f8ab903837d5d973864e2b4bb624615d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 19 Oct 2022 22:35:34 +0200 Subject: [PATCH 100/506] Update show config --- src/configfile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/configfile.c b/src/configfile.c index d849685..b44b9f3 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -493,7 +493,7 @@ void config_file_print(void) { if (c.path != NULL) { - tio_printf(" Path: %s", c.path); + tio_printf(" Active configuration file: %s", c.path); if (c.section_name != NULL) { tio_printf(" Active sub-configuration: %s", c.section_name); From 2cba4b863fce08e63c210f9882348d6c989ade6e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 19 Oct 2022 22:44:10 +0200 Subject: [PATCH 101/506] Update text --- README.md | 8 ++++---- src/options.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1c87aa0..9b571d2 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ The command-line interface is straightforward as reflected in the output from ``` Usage: tio [] - Connect to tty device directly or via sub-configuration. + Connect to TTY device directly or via sub-configuration. Options: -b, --baudrate Baud rate (default: 115200) @@ -127,7 +127,7 @@ Which corresponds to the commonly used default options: $ tio -b 115200 -d 8 -f none -s 1 -p none /dev/ttyUSB0 ``` -It is recommended to connect serial tty devices by ID: +It is recommended to connect serial TTY devices by ID: ``` $ tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 ``` @@ -139,7 +139,7 @@ List available serial devices by ID: $ tio --list-devices ``` Note: One can also use tio shell completion on /dev which will automatically -list all available serial tty devices. +list all available serial TTY devices. Log to file with autogenerated filename: ``` @@ -203,7 +203,7 @@ following locations in the order listed: The configuration file supports sub-configurations using named sections which can be activated via the command-line by name or pattern. A sub-configuration -specifies which tty to connect to and other options. +specifies which TTY device to connect to and other options. Example configuration file: diff --git a/src/options.c b/src/options.c index 9daac17..f02da9e 100644 --- a/src/options.c +++ b/src/options.c @@ -99,7 +99,7 @@ void print_help(char *argv[]) printf("Usage: tio [] \n"); printf("\n"); - printf("Connect to tty device directly or via sub-configuration.\n"); + printf("Connect to TTY device directly or via sub-configuration.\n"); printf("\n"); printf("Options:\n"); printf(" -b, --baudrate Baud rate (default: 115200)\n"); From 784201ea2edfab1832daa84b5d810aae4aade503 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 19 Oct 2022 23:56:31 +0200 Subject: [PATCH 102/506] Deprecate tty config keyword but keep it around for now --- src/configfile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/configfile.c b/src/configfile.c index b44b9f3..96e7ea5 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -112,7 +112,7 @@ static int data_handler(void *user, const char *section, const char *name, if (!strcmp(section, c.section_name)) { // Set configuration parameter if found - if (!strcmp(name, "device")) + if (!strcmp(name, "device") || !strcmp(name, "tty")) { asprintf(&c.tty, value, c.match); option.tty_device = c.tty; From c80f833a06eea51ccdeaaa50255ec3462ab0eca8 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 22 Oct 2022 08:59:34 +0200 Subject: [PATCH 103/506] Add mute feature This will make tio go fully silent and not print anything. --- src/bash-completion/tio.in | 5 +++++ src/configfile.c | 11 +++++++++++ src/options.c | 7 +++++++ 3 files changed, 23 insertions(+) diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index 82cb649..9602c4f 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -35,6 +35,7 @@ _tio() --rs-485 \ --rs-485-config \ --alert \ + --mute \ -v --version \ -h --help" @@ -141,6 +142,10 @@ _tio() COMPREPLY=( $(compgen -W "none bell blink" -- ${cur}) ) return 0 ;; + --mute) + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + ;; -v | --version) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 diff --git a/src/configfile.c b/src/configfile.c index 96e7ea5..8e29a26 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -302,6 +302,17 @@ static int data_handler(void *user, const char *section, const char *name, { option.alert = alert_option_parse(value); } + else if (!strcmp(name, "mute")) + { + if (!strcmp(value, "enable")) + { + option.mute = true; + } + else if (!strcmp(value, "disable")) + { + option.mute = false; + } + } } return 0; diff --git a/src/options.c b/src/options.c index f02da9e..cac6222 100644 --- a/src/options.c +++ b/src/options.c @@ -51,6 +51,7 @@ enum opt_t OPT_RS485_CONFIG, OPT_ALERT, OPT_COMPLETE_SUB_CONFIGS, + OPT_MUTE, }; /* Default options */ @@ -127,6 +128,7 @@ void print_help(char *argv[]) printf(" --rs-485 Enable RS-485 mode\n"); printf(" --rs-485-config Set RS-485 configuration\n"); printf(" --alert bell|blink|none Alert on connect/disconnect (default: none)\n"); + printf(" --mute Mute tio\n"); printf(" -v, --version Display version\n"); printf(" -h, --help Display help\n"); printf("\n"); @@ -258,6 +260,7 @@ void options_parse(int argc, char *argv[]) {"rs-485", no_argument, 0, OPT_RS485 }, {"rs-485-config", required_argument, 0, OPT_RS485_CONFIG }, {"alert", required_argument, 0, OPT_ALERT }, + {"mute", no_argument, 0, OPT_MUTE }, {"version", no_argument, 0, 'v' }, {"help", no_argument, 0, 'h' }, {"complete-sub-configs", no_argument, 0, OPT_COMPLETE_SUB_CONFIGS}, @@ -413,6 +416,10 @@ void options_parse(int argc, char *argv[]) option.alert = alert_option_parse(optarg); break; + case OPT_MUTE: + option.mute = true; + break; + case 'v': printf("tio v%s\n", VERSION); exit(EXIT_SUCCESS); From 374c2ad113ff3b1e376294f916d8be06203ad6d3 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 23 Oct 2022 22:29:29 +0200 Subject: [PATCH 104/506] Update example configuration file --- example/tiorc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/tiorc b/example/tiorc index 525a982..747388a 100644 --- a/example/tiorc +++ b/example/tiorc @@ -47,7 +47,7 @@ log-file = tincan.log log-strip = enable color = 11 -[usb devices] +[usb] pattern = usb([0-9]*) tty = /dev/ttyUSB%s color = 12 From 73c1129be788612a3d977ca442cb06167bf20225 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 29 Oct 2022 16:50:22 +0200 Subject: [PATCH 105/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b571d2..a9d3ce4 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ [![](https://img.shields.io/circleci/build/gh/tio/tio?token=da7e7fd0d0ee99b9f986f8877dcdbe28f73d9e06)](https://circleci.com/gh/tio/tio/tree/master) [![](https://img.shields.io/github/v/release/tio/tio?sort=semver)](https://github.com/tio/tio/releases) -[![](https://img.shields.io/tokei/lines/github/tio/tio)](https://github.com/tio/tio) [![](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions) +[![](https://img.shields.io/tokei/lines/github/tio/tio)](https://github.com/tio/tio) ## 1. Introduction From 7c32e5a21ed3f2df59f8f3177afa6b8fd0036b77 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 31 Oct 2022 14:42:14 +0100 Subject: [PATCH 106/506] Update TODO --- TODO | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/TODO b/TODO index 14d8025..ce25173 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,33 @@ + * Allow tio to connect to socket + + After some more consideration I think it makes sense to support connecting to a + socket as that will make tio be able to both serve a serial port via a socket + and connect to it - it will be an end to end solution. In short we will be able + to do the following: + + Host serial port on socket (existing feature): + $ tio --socket unix:/tmp/tio-socket-0 /dev/ttyUSB0 + + Connect to same socket (new feature): + $ tio unix:/tmp/tio-socket-0 + + Besides a bit of refactoring the following required changes spring to mind: + + * Socket mode and type of socket should be activated via device name prefix. For example: + * UNIX socket: tio unix: + * TCPv4 socket: tio inet:: + * TCPv6 socket: tio inet6:: + * If no port number defined default to 3333 + * Mapping flags INLCR, IGNCR, ICRNL needs implementation for socket mode + * Error messages should just say "device" instead of "tty device" etc. + * Remove other tty'isms (tty_write() should be device_write() etc.) + * In session key commands that do not work in socket mode should either not be listed or print an error messages if used. + * All non-tty features should continue work (auto-connect etc.) + * Shell completion script update + * Man page update + + * Split I/O feature Allow to split input and output so that it is possible to manage these From c6beed8265202f3c935ff33c29a4ce281d008329 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 1 Nov 2022 10:48:54 +0100 Subject: [PATCH 107/506] Update example tiorc --- example/tiorc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/example/tiorc b/example/tiorc index 747388a..78a197b 100644 --- a/example/tiorc +++ b/example/tiorc @@ -28,20 +28,20 @@ alert = none [rpi3] baudrate = 115200 -tty = /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A6009HU3-if00-port0 +device = /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A6009HU3-if00-port0 socket = unix:/tmp/tio-socket-0 color = 9 [am64-evm] baudrate = 115200 -tty = /dev/serial/by-id/usb-Silicon_Labs_CP2105_Dual_USB_to_UART_Bridge_Controller_01093176-if01-port0 +device = /dev/serial/by-id/usb-Silicon_Labs_CP2105_Dual_USB_to_UART_Bridge_Controller_01093176-if01-port0 line-pulse-duration = DTR=200,RTS=300,RI=50 alert = bell color = 10 [tincan] baudrate = 9600 -tty = /dev/serial/by-id/usb-TinCanTools_Flyswatter2_FS20000-if00-port0 +device = /dev/serial/by-id/usb-TinCanTools_Flyswatter2_FS20000-if00-port0 log = enable log-file = tincan.log log-strip = enable @@ -49,11 +49,11 @@ color = 11 [usb] pattern = usb([0-9]*) -tty = /dev/ttyUSB%s +device = /dev/ttyUSB%s color = 12 [rs-485-device] -tty = /dev/ttyUSB0 +device = /dev/ttyUSB0 rs-485 = enable rs-485-config = RTS_ON_SEND=1,RTS_AFTER_SEND=1,RTS_DELAY_BEFORE_SEND=60,RTS_DELAY_AFTER_SEND=80,RX_DURING_TX color = 13 From d3ac8e7b1fa0fc79951e5b3b229ab6f380a393f7 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 1 Nov 2022 10:49:02 +0100 Subject: [PATCH 108/506] Update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a9d3ce4..0033c64 100644 --- a/README.md +++ b/README.md @@ -216,7 +216,7 @@ stopbits = 1 color = 10 [rpi3] -tty = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 +device = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 baudrate = 115200 no-autoconnect = enable log = enable @@ -226,7 +226,7 @@ color = 12 [usb devices] pattern = usb([0-9]*) -tty = /dev/ttyUSB%s +device = /dev/ttyUSB%s color = 13 ``` From 845e683fc9e2d2da686fe0c8e4b19b1245e9904b Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 1 Nov 2022 10:55:41 +0100 Subject: [PATCH 109/506] Update NEWS --- NEWS | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 1a1cc4d..a5fff49 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,21 @@ -=== tio v2.2 === +=== tio v2.3 === + + + +Changes since tio v2.2: + + * Add mute feature + + This will make tio go fully silent and not print anything. + + * Rename config variable 'tty' to 'device' + + * Deprecate tty config keyword but keep it around for now + + * Update show config + + * Update example tiorc From 6c19cd07506627ab0d6437284a6fa2215f76ca78 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 1 Nov 2022 11:10:49 +0100 Subject: [PATCH 110/506] Bump version --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index bf265e2..f29e42d 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('tio', 'c', - version : '2.3', + version : '2.4', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] From 1aaecef419f24b2861246e5bf3fc617c0f75aadb Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 1 Nov 2022 11:11:54 +0100 Subject: [PATCH 111/506] Update plain text man page --- man/tio.1.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/man/tio.1.txt b/man/tio.1.txt index 1874c65..3cd3e52 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -262,7 +262,7 @@ CONFIGURATION FILE Labels can be used to group settings into named sub-configurations which can be activated from the command-line when starting tio. - tio will try to match the user input to a sub-configuration by name or by pattern to get the tty and other options. + tio will try to match the user input to a sub-configuration by name or by pattern to get the TTY device and other options. Options without any label change the default options. @@ -272,7 +272,7 @@ CONFIGURATION FILE pattern Pattern matching user input. This pattern can be an extended regular expression with a single group. - tty tty device to open. If it contains a "%s" it is substituted with the first group match. + device TTY device to open. If it contains a "%s" it is substituted with the first group match. baudrate Set baud rate @@ -338,7 +338,7 @@ CONFIGURATION FILE EXAMPLES Named sub-configurations can be added via labels: [rpi3] - tty = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 + device = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 baudrate = 115200 color = 11 @@ -354,7 +354,7 @@ CONFIGURATION FILE EXAMPLES [usb device] pattern = usb([0-9]*) - tty = /dev/ttyUSB%s + device = /dev/ttyUSB%s baudrate = 115200 Activate the sub-configuration by pattern match: @@ -378,7 +378,7 @@ EXAMPLES $ tio -b 115200 -d 8 -f none -s 1 -p none /dev/ttyUSB0 - It is recommended to connect serial tty devices by ID: + It is recommended to connect serial TTY devices by ID: $ tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 @@ -407,11 +407,11 @@ EXAMPLES send -i $uart "ls -la\n" expect -i $uart "prompt> " - Redirect device I/O to network file socket for remote tty sharing: + Redirect device I/O to network file socket for remote TTY sharing: $ tio --socket inet:4444 /dev/ttyUSB0 - Then, use netcat to connect to the shared tty session over network (assuming tio is hosted on IP 10.0.0.42): + Then, use netcat to connect to the shared TTY session over network (assuming tio is hosted on IP 10.0.0.42): $ nc -N 10.0.0.42 4444 @@ -439,4 +439,4 @@ WEBSITE AUTHOR Created by Martin Lund . -tio 2.2 2022-10-18 tio(1) +tio 2.3 2022-10-18 tio(1) From a4f0d4da538070fabba1dd66e49790129d9ef414 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 21 Nov 2022 17:16:14 +0100 Subject: [PATCH 112/506] Add socket input comment --- src/tty.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tty.c b/src/tty.c index 8c7bfa7..b27c73f 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1370,6 +1370,7 @@ int tty_connect(void) } else { + /* Input from socket ready */ forward = socket_handle_input(&rdfs, &output_char); if (forward) From 419fbdc3fafc8e47d5ffd8e32130a5d01f523111 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 23 Nov 2022 17:24:51 +0100 Subject: [PATCH 113/506] Add key command to toggle log on/off Add key command 'ctrl-t f' which will toggle log on/off. If no log filename has been specified via the 'log-filename' option then tio will automatically generate a new log filename every time the log feature is toggled on. Meaning, when toggled multiple times, multiple log files will be generated. However, if a log filename has been specified, tio will only write and append to that same file. --- README.md | 1 + man/tio.1.in | 2 ++ src/log.c | 57 +++++++++++++++++++++++++++++---------------------- src/log.h | 3 ++- src/options.c | 3 ++- src/tty.c | 18 ++++++++++++++++ 6 files changed, 58 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 0033c64..c58d4b4 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,7 @@ ctrl-t ? to list the available key commands. [20:19:12.040] ctrl-t b Send break [20:19:12.040] ctrl-t c Show configuration [20:19:12.040] ctrl-t e Toggle local echo mode +[20:19:12.040] ctrl-t f Toggle log to file [20:19:12.040] ctrl-t g Toggle serial port line [20:19:12.040] ctrl-t h Toggle hexadecimal mode [20:19:12.040] ctrl-t l Clear screen diff --git a/man/tio.1.in b/man/tio.1.in index 01349d8..87cd9ef 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -289,6 +289,8 @@ Send serial break (triggers SysRq on Linux, etc.) Show configuration (baudrate, databits, etc.) .IP "\fBctrl-t e" Toggle local echo mode +.IP "\fBctrl-t f" +Toggle log to file .IP "\fBctrl-t g" Toggle serial port line .IP "\fBctrl-t h" diff --git a/src/log.c b/src/log.c index aa86847..d0cf543 100644 --- a/src/log.c +++ b/src/log.c @@ -38,9 +38,9 @@ #define IS_ESC_END_CHAR(c) ((c >= 0x30) && (c <= 0x7E)) #define IS_CTRL_CHAR(c) ((c >= 0x00) && (c <= 0x1F)) -static FILE *fp; -static bool log_error = false; +static FILE *fp = NULL; static char file_buffer[BUFSIZ]; +static const char *log_filename = NULL; static char *date_time(void) { @@ -56,7 +56,7 @@ static char *date_time(void) return date_time_string; } -void log_open(const char *filename) +int log_open(const char *filename) { static char automatic_filename[400]; @@ -65,19 +65,22 @@ void log_open(const char *filename) // Generate filename if none provided ("tio_DEVICE_YYYY-MM-DDTHH:MM:SS.log") sprintf(automatic_filename, "tio_%s_%s.log", basename((char *)option.tty_device), date_time()); filename = automatic_filename; - option.log_filename = automatic_filename; } + log_filename = filename; + // Open log file in append write mode fp = fopen(filename, "a+"); if (fp == NULL) { - log_error = true; - exit(EXIT_FAILURE); + tio_warning_printf("Could not open log file %s (%s)", filename, strerror(errno)); + return -1; } // Enable line buffering setvbuf(fp, file_buffer, _IOLBF, BUFSIZ); + + return 0; } bool log_strip(char c) @@ -141,6 +144,11 @@ bool log_strip(char c) void log_printf(const char *format, ...) { + if (fp == NULL) + { + return; + } + char *line; va_list(args); @@ -155,20 +163,22 @@ void log_printf(const char *format, ...) void log_putc(char c) { - if (fp != NULL) + if (fp == NULL) { - if (option.log_strip) - { - if (!log_strip(c)) - { - fputc(c, fp); - } - } - else + return; + } + + if (option.log_strip) + { + if (!log_strip(c)) { fputc(c, fp); } } + else + { + fputc(c, fp); + } } void log_close(void) @@ -176,6 +186,8 @@ void log_close(void) if (fp != NULL) { fclose(fp); + fp = NULL; + log_filename = NULL; } } @@ -183,15 +195,12 @@ void log_exit(void) { if (option.log) { + tio_printf("Saved log to file %s", log_filename); log_close(); } - - if (log_error) - { - tio_error_printf("Could not open log file %s (%s)", option.log_filename, strerror(errno)); - } - else if (option.log) - { - tio_printf("Saved log to file %s", option.log_filename); - } +} + +const char *log_get_filename(void) +{ + return log_filename; } diff --git a/src/log.h b/src/log.h index d2a2188..b24114e 100644 --- a/src/log.h +++ b/src/log.h @@ -21,8 +21,9 @@ #pragma once -void log_open(const char *filename); +int log_open(const char *filename); void log_printf(const char *format, ...); void log_putc(char c); void log_close(void); void log_exit(void); +const char * log_get_filename(void); diff --git a/src/options.c b/src/options.c index cac6222..eb75144 100644 --- a/src/options.c +++ b/src/options.c @@ -38,6 +38,7 @@ #include "rs485.h" #include "timestamp.h" #include "alert.h" +#include "log.h" enum opt_t { @@ -216,7 +217,7 @@ void options_print() if (option.map[0] != 0) tio_printf(" Map flags: %s", option.map); if (option.log) - tio_printf(" Log file: %s", option.log_filename); + tio_printf(" Log file: %s", log_get_filename()); if (option.socket) tio_printf(" Socket: %s", option.socket); } diff --git a/src/tty.c b/src/tty.c index b27c73f..7329303 100644 --- a/src/tty.c +++ b/src/tty.c @@ -80,6 +80,7 @@ #define KEY_B 0x62 #define KEY_C 0x63 #define KEY_E 0x65 +#define KEY_F 0x66 #define KEY_G 0x67 #define KEY_H 0x68 #define KEY_L 0x6C @@ -414,6 +415,7 @@ void handle_command_sequence(char input_char, char previous_char, char *output_c tio_printf(" ctrl-%c b Send break", option.prefix_key); tio_printf(" ctrl-%c c Show configuration", option.prefix_key); tio_printf(" ctrl-%c e Toggle local echo mode", option.prefix_key); + tio_printf(" ctrl-%c f Toggle log to file", option.prefix_key); tio_printf(" ctrl-%c g Toggle serial port line", option.prefix_key); tio_printf(" ctrl-%c h Toggle hexadecimal mode", option.prefix_key); tio_printf(" ctrl-%c l Clear screen", option.prefix_key); @@ -443,6 +445,22 @@ void handle_command_sequence(char input_char, char previous_char, char *output_c tio_printf(" RI : %s", (state & TIOCM_RI) ? "HIGH" : "LOW"); break; + case KEY_F: + if (option.log) + { + log_close(); + option.log = false; + } + else + { + if (log_open(option.log_filename) == 0) + { + option.log = true; + } + } + tio_printf("Switched log to file %s", option.log ? "on" : "off"); + break; + case KEY_G: tio_printf("Please enter which serial line number to toggle:"); tio_printf(" DTR (0)"); From 12f20c84e3f834750e5f5b356d8bfcf1ac2cc135 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 28 Nov 2022 14:30:48 +0100 Subject: [PATCH 114/506] Fix so that is it possible to quit tio in tio etc. Fix regression so that it is possible to send the prefix key code to the remote tio session without local tio session reacting to same key code (quitting etc.). --- src/tty.c | 44 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/src/tty.c b/src/tty.c index 7329303..328be11 100644 --- a/src/tty.c +++ b/src/tty.c @@ -349,12 +349,13 @@ static void toggle_line(const char *line_name, int mask, enum line_mode_t line_m } } -void handle_command_sequence(char input_char, char previous_char, char *output_char, bool *forward) +void handle_command_sequence(char input_char, char *output_char, bool *forward) { char unused_char; bool unused_bool; int state; static enum line_mode_t line_mode = LINE_OFF; + static char previous_char = 0; /* Ignore unused arguments */ if (output_char == NULL) @@ -407,6 +408,16 @@ void handle_command_sequence(char input_char, char previous_char, char *output_c /* Do not forward input char to output by default */ *forward = false; + /* Handle special double prefix key input case */ + if (input_char == option.prefix_code) + { + /* Forward prefix character to tty */ + *output_char = option.prefix_code; + *forward = true; + previous_char = 0; + return; + } + switch (input_char) { case KEY_QUESTION: @@ -588,27 +599,12 @@ void handle_command_sequence(char input_char, char previous_char, char *output_c break; default: - /* Handle double prefix key input case */ - if (input_char == option.prefix_code) - { - static int count = 0; - if (count++ == 1) - { - // Do not forward prefix characters excessively - count = 0; - break; - } - - /* Forward prefix character to tty */ - *output_char = option.prefix_code; - *forward = true; - break; - } - /* Ignore unknown ctrl-t escaped keys */ break; } } + + previous_char = input_char; } void stdin_restore(void) @@ -925,7 +921,7 @@ void tty_wait_for_device(void) int status; int maxfd; struct timeval tv; - static char input_char, previous_char = 0; + static char input_char; static bool first = true; static int last_errno = 0; @@ -971,9 +967,7 @@ void tty_wait_for_device(void) } /* Handle commands */ - handle_command_sequence(input_char, previous_char, NULL, NULL); - - previous_char = input_char; + handle_command_sequence(input_char, NULL, NULL); } socket_handle_input(&rdfs, NULL); } @@ -1095,7 +1089,6 @@ int tty_connect(void) int maxfd; /* Maximum file descriptor used */ char input_char, output_char; char input_buffer[BUFSIZ]; - static char previous_char = 0; static bool first = true; int status; bool next_timestamp = false; @@ -1363,10 +1356,7 @@ int tty_connect(void) } /* Handle commands */ - handle_command_sequence(input_char, previous_char, &output_char, &forward); - - /* Save previous key */ - previous_char = input_char; + handle_command_sequence(input_char, &output_char, &forward); if ((option.hex_mode) && (forward)) { From 93e6efc00153e625e4ceb8a51e01b324247d312a Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 24 Nov 2022 22:52:28 +0100 Subject: [PATCH 115/506] Add threaded input handling To make tio more responsive to quit and I/O flush key command when main I/O thread is blocked on output. --- man/tio.1.in | 2 + src/main.c | 6 +++ src/meson.build | 5 ++- src/tty.c | 117 ++++++++++++++++++++++++++++++++++++++++++++---- src/tty.h | 2 + 5 files changed, 123 insertions(+), 9 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 87cd9ef..3f29c6b 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -291,6 +291,8 @@ Show configuration (baudrate, databits, etc.) Toggle local echo mode .IP "\fBctrl-t f" Toggle log to file +.IP "\fBctrl-t F" +Flush data I/O channels (discard data written but not transmitted and data received but not read) .IP "\fBctrl-t g" Toggle serial port line .IP "\fBctrl-t h" diff --git a/src/main.c b/src/main.c index d2b1c0d..ddf8848 100644 --- a/src/main.c +++ b/src/main.c @@ -120,6 +120,12 @@ int main(int argc, char *argv[]) socket_configure(); } + /* Spawn input handling into separate thread */ + tty_input_thread_create(); + + /* Wait for input to be ready */ + tty_input_thread_wait_ready(); + /* Connect to tty device */ if (option.no_autoconnect) { diff --git a/src/meson.build b/src/meson.build index 0077733..cec9429 100644 --- a/src/meson.build +++ b/src/meson.build @@ -20,9 +20,12 @@ tio_sources = [ 'alert.c' ] -tio_dep = dependency('inih', required: true, +tio_dep = [ + dependency('threads', required: true), + dependency('inih', required: true, fallback : ['libinih', 'inih_dep'], default_options: ['default_library=static', 'distro_install=false']) +] tio_c_args = ['-Wno-unused-result'] diff --git a/src/tty.c b/src/tty.c index 328be11..83116b9 100644 --- a/src/tty.c +++ b/src/tty.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "config.h" #include "configfile.h" #include "tty.h" @@ -81,6 +82,7 @@ #define KEY_C 0x63 #define KEY_E 0x65 #define KEY_F 0x66 +#define KEY_SHIFT_F 0x46 #define KEY_G 0x67 #define KEY_H 0x68 #define KEY_L 0x6C @@ -134,6 +136,9 @@ static unsigned char hex_char_index = 0; static char tty_buffer[BUFSIZ*2]; static size_t tty_buffer_count = 0; 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 void optional_local_echo(char c) { @@ -256,6 +261,99 @@ ssize_t tty_write(int fd, const void *buffer, size_t count) return bytes_written; } +void *tty_stdin_input_thread(void *arg) +{ + UNUSED(arg); + char input_buffer[BUFSIZ]; + ssize_t byte_count; + ssize_t bytes_written; + + // Create FIFO pipe + if (pipe(pipefd) == -1) + { + tio_error_printf("Failed to create pipe"); + exit(EXIT_FAILURE); + } + + // Signal that input pipe is ready + pthread_mutex_unlock(&mutex_input_ready); + + // Input loop for stdin + while (1) + { + /* Input from stdin ready */ + byte_count = read(STDIN_FILENO, input_buffer, BUFSIZ); + if (byte_count < 0) + { + tio_warning_printf("Could not read from stdin (%s)", strerror(errno)); + } + else if (byte_count == 0) + { + // Close write end to signal EOF in read end + close(pipefd[1]); + pthread_exit(0); + } + + if (interactive_mode) + { + static char previous_char = 0; + char input_char; + + // Process quit and flush key command + for (int i = 0; i 0) { - if (FD_ISSET(STDIN_FILENO, &rdfs)) + if (FD_ISSET(pipefd[0], &rdfs)) { /* Input from stdin ready */ /* Read one character */ - status = read(STDIN_FILENO, &input_char, 1); + status = read(pipefd[0], &input_char, 1); if (status <= 0) { tio_error_printf("Could not read from stdin"); @@ -1200,9 +1301,9 @@ int tty_connect(void) FD_SET(fd, &rdfs); if (!ignore_stdin) { - FD_SET(STDIN_FILENO, &rdfs); + FD_SET(pipefd[0], &rdfs); } - maxfd = MAX(fd, STDIN_FILENO); + maxfd = MAX(fd, pipefd[0]); maxfd = MAX(maxfd, socket_add_fds(&rdfs, true)); /* Manage timeout */ @@ -1309,10 +1410,10 @@ int tty_connect(void) } } } - else if (FD_ISSET(STDIN_FILENO, &rdfs)) + else if (FD_ISSET(pipefd[0], &rdfs)) { /* Input from stdin ready */ - ssize_t bytes_read = read(STDIN_FILENO, input_buffer, BUFSIZ); + ssize_t bytes_read = read(pipefd[0], input_buffer, BUFSIZ); if (bytes_read < 0) { tio_error_printf_silent("Could not read from stdin (%s)", strerror(errno)); diff --git a/src/tty.h b/src/tty.h index 14d1779..c4b6797 100644 --- a/src/tty.h +++ b/src/tty.h @@ -31,3 +31,5 @@ void tty_configure(void); int tty_connect(void); void tty_wait_for_device(void); void list_serial_devices(void); +void tty_input_thread_create(void); +void tty_input_thread_wait_ready(void); From f4dc46d0024b634c4a4d62eb128d068b89c8c8b7 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 2 Dec 2022 04:12:35 +0100 Subject: [PATCH 116/506] Update NEWS --- NEWS | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index a5fff49..1de122c 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,32 @@ -=== tio v2.3 === +=== tio v2.4 === + + + +Changes since tio v2.3: + + * Add threaded input handling + + To make tio more responsive to quit and I/O flush key command when main I/O + thread is blocked on output. + + * Fix so that is it possible to quit tio in tio etc. + + Fix regression so that it is possible to send the prefix key code to the + remote tio session without local tio session reacting to same key code + (quitting etc.). + + * Add key command to toggle log on/off + + Add key command 'ctrl-t f' which will toggle log on/off. + + If no log filename has been specified via the 'log-filename' option then + tio will automatically generate a new log filename every time the log + feature is toggled on. Meaning, when toggled multiple times, multiple + log files will be generated. + + However, if a log filename has been specified, tio will only write and + append to that same file. From cd2479796124586f245c25291f310c27ea4b585f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 2 Dec 2022 12:45:45 +0100 Subject: [PATCH 117/506] Update flush command --- man/tio.1.in | 2 +- src/tty.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/man/tio.1.in b/man/tio.1.in index 3f29c6b..1ec1355 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -292,7 +292,7 @@ Toggle local echo mode .IP "\fBctrl-t f" Toggle log to file .IP "\fBctrl-t F" -Flush data I/O channels (discard data written but not transmitted and data received but not read) +Flush data I/O buffers (discard data written but not transmitted and data received but not read) .IP "\fBctrl-t g" Toggle serial port line .IP "\fBctrl-t h" diff --git a/src/tty.c b/src/tty.c index 83116b9..faad20c 100644 --- a/src/tty.c +++ b/src/tty.c @@ -525,6 +525,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf(" ctrl-%c c Show configuration", option.prefix_key); tio_printf(" ctrl-%c e Toggle local echo mode", option.prefix_key); tio_printf(" ctrl-%c f Toggle log to file", option.prefix_key); + tio_printf(" ctrl-%c F Flush data I/O buffers", option.prefix_key); tio_printf(" ctrl-%c g Toggle serial port line", option.prefix_key); tio_printf(" ctrl-%c h Toggle hexadecimal mode", option.prefix_key); tio_printf(" ctrl-%c l Clear screen", option.prefix_key); From a21809b0906046df4cde54b5499dc3de06129c42 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 2 Dec 2022 12:59:51 +0100 Subject: [PATCH 118/506] Update plain text man page --- man/tio.1.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/man/tio.1.txt b/man/tio.1.txt index 3cd3e52..b21a477 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -224,6 +224,10 @@ KEYS ctrl-t e Toggle local echo mode + ctrl-t f Toggle log to file + + ctrl-t F Flush data I/O buffers (discard data written but not transmitted and data received but not read) + ctrl-t g Toggle serial port line ctrl-t h Toggle hexadecimal mode @@ -439,4 +443,4 @@ WEBSITE AUTHOR Created by Martin Lund . -tio 2.3 2022-10-18 tio(1) +tio 2.4 2022-10-18 tio(1) From 6b084a53b84dd7b369a6486f59f74138f6d7221a Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 2 Dec 2022 13:05:00 +0100 Subject: [PATCH 119/506] Update version tag 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 b21a477..aa26dcc 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -443,4 +443,4 @@ WEBSITE AUTHOR Created by Martin Lund . -tio 2.4 2022-10-18 tio(1) +tio 2.4 2022-11-02 tio(1) diff --git a/meson.build b/meson.build index f29e42d..c394f8c 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 = '2022-10-18' +version_date = '2022-11-02' # Test for dynamic baudrate configuration interface compiler = meson.get_compiler('c') From e2e974958e2129d874850820d40cf42c3938447e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 2 Dec 2022 13:42:53 +0100 Subject: [PATCH 120/506] Bump version --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index c394f8c..885efa8 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('tio', 'c', - version : '2.4', + version : '2.5', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] From 2915d26a19fa16bfda859b0051720b6427bdd1e5 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 5 Dec 2022 16:27:53 +0100 Subject: [PATCH 121/506] Fix double prefix key regression --- src/tty.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/tty.c b/src/tty.c index faad20c..f5ffc35 100644 --- a/src/tty.c +++ b/src/tty.c @@ -306,10 +306,16 @@ void *tty_stdin_input_thread(void *arg) if (previous_char == option.prefix_code) { + if (input_char == option.prefix_code) + { + previous_char = 0; + continue; + } + switch (input_char) { case KEY_Q: - exit(EXIT_FAILURE); + exit(EXIT_SUCCESS); break; case KEY_SHIFT_F: tio_printf("Flushed data I/O channels") From 42739c0817d87e930a72eb4506ad4a723206ac17 Mon Sep 17 00:00:00 2001 From: Vyacheslav Patkov Date: Tue, 6 Dec 2022 20:27:14 +0400 Subject: [PATCH 122/506] Better error checking in config file, rename the file Accept "true", "enable", "on", "yes", "1" as true values, their counterparts as false ones. Check integer values for errors and range. Warn about ignored (e.g. misspelled) options. Check getenv() return value for NULL. Rename "tiorc" to "config", as it's a static INI file, not an executable "run commands". --- man/tio.1.in | 6 +- src/configfile.c | 167 ++++++++++++++++++++--------------------------- 2 files changed, 72 insertions(+), 101 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 1ec1355..701b852 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -332,11 +332,9 @@ the configuration file first found in the following locations in the order listed: .PP -.I $XDG_CONFIG_HOME/tio/tiorc +.I $XDG_CONFIG_HOME/tio/config .PP -.I $HOME/.config/tio/tiorc -.PP -.I $HOME/.tiorc +.I $HOME/.config/tio/config .PP Labels can be used to group settings into named sub-configurations which can be diff --git a/src/configfile.c b/src/configfile.c index 8e29a26..c3ff4cf 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -98,6 +98,40 @@ static int get_match(const char *input, const char *pattern, char **match) return len; } +static bool read_boolean(const char *value, const char *name) +{ + const char *true_values[] = { "true", "enable", "on", "yes", "1", NULL }; + const char *false_values[] = { "false", "disable", "off", "no", "0", NULL }; + + for (int i = 0; true_values[i] != NULL; i++) + if (strcmp(value, true_values[i]) == 0) + return true; + + for (int i = 0; false_values[i] != NULL; i++) + if (strcmp(value, false_values[i]) == 0) + return false; + + tio_error_printf("Invalid value '%s' for option '%s' in configuration file", + value, name); + exit(EXIT_FAILURE); +} + +static long read_integer(const char *value, const char *name, long min_value, long max_value) +{ + errno = 0; + char *endptr; + long result = strtol(value, &endptr, 10); + + if (errno || endptr == value || *endptr != '\0' || result < min_value || result > max_value) + { + tio_error_printf("Invalid value '%s' for option '%s' in configuration file", + value, name); + exit(EXIT_FAILURE); + } + + return result; +} + /** * data_handler() - walk config file to load parameters matching user input * @@ -119,11 +153,11 @@ static int data_handler(void *user, const char *section, const char *name, } else if (!strcmp(name, "baudrate")) { - option.baudrate = string_to_long((char *)value); + option.baudrate = read_integer(value, name, 0, LONG_MAX); } else if (!strcmp(name, "databits")) { - option.databits = atoi(value); + option.databits = read_integer(value, name, 5, 8); } else if (!strcmp(name, "flow")) { @@ -132,7 +166,7 @@ static int data_handler(void *user, const char *section, const char *name, } else if (!strcmp(name, "stopbits")) { - option.stopbits = atoi(value); + option.stopbits = read_integer(value, name, 1, 2); } else if (!strcmp(name, "parity")) { @@ -141,11 +175,11 @@ static int data_handler(void *user, const char *section, const char *name, } else if (!strcmp(name, "output-delay")) { - option.output_delay = atoi(value); + option.output_delay = read_integer(value, name, 0, LONG_MAX); } else if (!strcmp(name, "output-line-delay")) { - option.output_line_delay = atoi(value); + option.output_line_delay = read_integer(value, name, 0, LONG_MAX); } else if (!strcmp(name, "line-pulse-duration")) { @@ -153,25 +187,11 @@ static int data_handler(void *user, const char *section, const char *name, } else if (!strcmp(name, "no-autoconnect")) { - if (!strcmp(value, "enable")) - { - option.no_autoconnect = true; - } - else if (!strcmp(value, "disable")) - { - option.no_autoconnect = false; - } + option.no_autoconnect = read_boolean(value, name); } else if (!strcmp(name, "log")) { - if (!strcmp(value, "enable")) - { - option.log = true; - } - else if (!strcmp(value, "disable")) - { - option.log = false; - } + option.log = read_boolean(value, name); } else if (!strcmp(name, "log-file")) { @@ -180,47 +200,20 @@ static int data_handler(void *user, const char *section, const char *name, } else if (!strcmp(name, "log-strip")) { - if (!strcmp(value, "enable")) - { - option.log_strip = true; - } - else if (!strcmp(value, "disable")) - { - option.log_strip = false; - } + option.log_strip = read_boolean(value, name); } else if (!strcmp(name, "local-echo")) { - if (!strcmp(value, "enable")) - { - option.local_echo = true; - } - else if (!strcmp(value, "disable")) - { - option.local_echo = false; - } + option.local_echo = read_boolean(value, name); } else if (!strcmp(name, "hexadecimal")) { - if (!strcmp(value, "enable")) - { - option.hex_mode = true; - } - else if (!strcmp(value, "disable")) - { - option.hex_mode = false; - } + option.hex_mode = read_boolean(value, name); } else if (!strcmp(name, "timestamp")) { - if (!strcmp(value, "enable")) - { - option.timestamp = TIMESTAMP_24HOUR; - } - else if (!strcmp(value, "disable")) - { - option.timestamp = TIMESTAMP_NONE; - } + option.timestamp = read_boolean(value, name) ? + TIMESTAMP_24HOUR : TIMESTAMP_NONE; } else if (!strcmp(name, "timestamp-format")) { @@ -270,29 +263,15 @@ static int data_handler(void *user, const char *section, const char *name, } else if (!strcmp(name, "response-wait")) { - if (!strcmp(value, "enable")) - { - option.response_wait = true; - } - else if (!strcmp(value, "disable")) - { - option.response_wait = false; - } + option.response_wait = read_boolean(value, name); } else if (!strcmp(name, "response-timeout")) { - option.response_timeout = atoi(value); + option.response_timeout = read_integer(value, name, 0, LONG_MAX); } else if (!strcmp(name, "rs-485")) { - if (!strcmp(value, "enable")) - { - option.rs485 = true; - } - else if (!strcmp(value, "disable")) - { - option.rs485 = false; - } + option.rs485 = read_boolean(value, name); } else if (!strcmp(name, "rs-485-config")) { @@ -304,14 +283,11 @@ static int data_handler(void *user, const char *section, const char *name, } else if (!strcmp(name, "mute")) { - if (!strcmp(value, "enable")) - { - option.mute = true; - } - else if (!strcmp(value, "disable")) - { - option.mute = false; - } + option.mute = read_boolean(value, name); + } + else + { + tio_warning_printf("Unknown option '%s' in configuration file, ignored", name); } } @@ -389,32 +365,29 @@ static int section_name_print_handler(void *user, const char *section, const cha static int resolve_config_file(void) { - asprintf(&c.path, "%s/tio/tiorc", getenv("XDG_CONFIG_HOME")); - if (!access(c.path, F_OK)) + char *xdg = getenv("XDG_CONFIG_HOME"); + if (xdg) { - return 0; + asprintf(&c.path, "%s/tio/config", xdg); + if (access(c.path, F_OK) == 0) + { + return 0; + } + free(c.path); } - free(c.path); - - asprintf(&c.path, "%s/.config/tio/tiorc", getenv("HOME")); - if (!access(c.path, F_OK)) + char *home = getenv("HOME"); + if (home) { - return 0; + asprintf(&c.path, "%s/.config/tio/config", home); + if (access(c.path, F_OK) == 0) + { + return 0; + } + free(c.path); } - free(c.path); - - asprintf(&c.path, "%s/.tiorc", getenv("HOME")); - if (!access(c.path, F_OK)) - { - return 0; - } - - free(c.path); - c.path = NULL; - return -EINVAL; } From 6a955b08e1e0ac75572c115c2eefe0dbb6d727ea Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 6 Dec 2022 18:48:19 +0100 Subject: [PATCH 123/506] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 8f5cada..2941ec9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -41,5 +41,6 @@ Rui Chen Ralph Siemsen Victor Oliveira Attila Veghelyi +Vyacheslav Patkov Thanks to everyone who has contributed to this project. From 4e9d29c88ddfc26db3f9b52067d54f96c3cdbeed Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 7 Dec 2022 04:07:43 +0100 Subject: [PATCH 124/506] Add support for $HOME/.tioconfig Replaces what used to be $HOME/.tiorc --- man/tio.1.in | 2 ++ src/configfile.c | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/man/tio.1.in b/man/tio.1.in index 701b852..52523ee 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -335,6 +335,8 @@ listed: .I $XDG_CONFIG_HOME/tio/config .PP .I $HOME/.config/tio/config +.PP +.I $HOME/.tioconfig .PP Labels can be used to group settings into named sub-configurations which can be diff --git a/src/configfile.c b/src/configfile.c index c3ff4cf..d3dbd16 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -385,6 +385,13 @@ static int resolve_config_file(void) return 0; } free(c.path); + + asprintf(&c.path, "%s/.tioconfig", home); + if (access(c.path, F_OK) == 0) + { + return 0; + } + free(c.path); } c.path = NULL; From ea7f43e60caac621aa2b01edd176999d6d99676e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 17 Dec 2022 16:58:24 +0100 Subject: [PATCH 125/506] Update configuration file documentation Rename .tiorc to .tioconfig, tiorc to config, etc. --- README.md | 8 ++++---- example/{tiorc => config} | 7 ++++++- 2 files changed, 10 insertions(+), 5 deletions(-) rename example/{tiorc => config} (88%) diff --git a/README.md b/README.md index c58d4b4..7625e85 100644 --- a/README.md +++ b/README.md @@ -198,9 +198,9 @@ If needed, the prefix key (ctrl-t) can be remapped via configuration file. Options can be set via the configuration file first found in any of the following locations in the order listed: - - $XDG_CONFIG_HOME/tio/tiorc - - $HOME/.config/tio/tiorc - - $HOME/.tiorc + - $XDG_CONFIG_HOME/tio/config + - $HOME/.config/tio/config + - $HOME/.tioconfig The configuration file supports sub-configurations using named sections which can be activated via the command-line by name or pattern. A sub-configuration @@ -240,7 +240,7 @@ Or by pattern match: $ tio usb12 ``` -Another more elaborate configuration file example is available [here](example/tiorc). +Another more elaborate configuration file example is available [here](example/config). ## 4. Installation diff --git a/example/tiorc b/example/config similarity index 88% rename from example/tiorc rename to example/config index 78a197b..05f96c9 100644 --- a/example/tiorc +++ b/example/config @@ -2,7 +2,12 @@ # tio - https://tio.github.io # ############################### -# Example configuration file +# Example tio configuration file +# +# Place file in any of the following locations: +# $XDG_CONFIG_HOME/tio/config +# $HOME/.config/tio/config +# $HOME/.tioconfig # Defaults baudrate = 115200 From dfe5f827f90fe1190d6c0aff3b2894433c0115a6 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 17 Dec 2022 17:10:20 +0100 Subject: [PATCH 126/506] Update NEWS --- NEWS | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 1de122c..cb608e0 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,32 @@ -=== tio v2.4 === +=== tio v2.5 === + + + +Changes since tio v2.4: + + * Update configuration file documentation + + Rename .tiorc to .tioconfig, tiorc to config, etc. + + * Add support for $HOME/.tioconfig + + Replaces what used to be $HOME/.tiorc + + * Fix double prefix key regression + +Vyacheslav Patkov: + + * Better error checking in config file, rename the file + + Accept "true", "enable", "on", "yes", "1" as true values, their + counterparts as false ones. Check integer values for errors and range. + Warn about ignored (e.g. misspelled) options. + + Check getenv() return value for NULL. + + Rename "tiorc" to "config", as it's a static INI file, not an executable + "run commands". From a2bbab068428275febeea13906654b9e58400c95 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 17 Dec 2022 17:11:22 +0100 Subject: [PATCH 127/506] Update plain text man page --- man/tio.1.txt | 17 +++++++++-------- meson.build | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/man/tio.1.txt b/man/tio.1.txt index aa26dcc..c66269f 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 simple serial device I/O tool @@ -66,7 +66,8 @@ OPTIONS Disable automatic connect. - By default tio automatically connects to the provided device if present. If the device is not present, it will wait for it to appear and then connect. If the connection is lost (eg. device disconnects), it will wait for the device to reappear and then reconnect. + By default tio automatically connects to the provided device if present. If the device is not present, it will wait for it to appear and then connect. If the connection is lost (eg. device disconnects), it will wait for the device to + reappear and then reconnect. However, if the --no-autoconnect option is provided, tio will exit if the device is not present or an established connection is lost. @@ -150,8 +151,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 multiplexed to the + terminal and all connected clients. Sockets remain open while the serial port is disconnected, and writes will block. @@ -258,11 +259,11 @@ HEXADECIMAL MODE CONFIGURATION FILE Options can be set via configuration file using the INI format. tio uses the configuration file first found in the following locations in the order listed: - $XDG_CONFIG_HOME/tio/tiorc + $XDG_CONFIG_HOME/tio/config - $HOME/.config/tio/tiorc + $HOME/.config/tio/config - $HOME/.tiorc + $HOME/.tioconfig Labels can be used to group settings into named sub-configurations which can be activated from the command-line when starting tio. @@ -443,4 +444,4 @@ WEBSITE AUTHOR Created by Martin Lund . -tio 2.4 2022-11-02 tio(1) +tio 2.5 2022-11-02 tio(1) diff --git a/meson.build b/meson.build index 885efa8..3fb3a22 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 = '2022-11-02' +version_date = '2022-12-17' # Test for dynamic baudrate configuration interface compiler = meson.get_compiler('c') From 76eb17e4d49816f61568d0386a3fb3ba32cbb9db Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 17 Dec 2022 17:28:28 +0100 Subject: [PATCH 128/506] Bump version --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 3fb3a22..1ed012e 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('tio', 'c', - version : '2.5', + version : '2.6', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] From 27b8c1b82a95988fbaa5a1c4d2e7ec1b58aafd7b Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 25 Dec 2022 16:02:47 +0100 Subject: [PATCH 129/506] Update TODO --- TODO | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/TODO b/TODO index ce25173..46f580f 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,14 @@ + * Support for running external command + + Add key command e.g. 'ctrl-t r' which prompts user to run external command. + The command will be run in a process which stdin/stdout is redirected to the + serial port. + + This is the first step towards maybe also adding automatic support for + x/y/zmodem data transfer protocols by calling external programs such as + rb/sb, rx/sx, rz/sz, etc. + * Allow tio to connect to socket After some more consideration I think it makes sense to support connecting to a From 4a1c213409a7e514fd731e6a53a27d4d5e29a109 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 5 Jan 2023 08:20:44 +0100 Subject: [PATCH 130/506] 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 52523ee..317e1cb 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -502,12 +502,12 @@ correct serial device if it is disconnected and then reconnected. .TP Redirect serial device I/O to Unix file socket for scripting: -$ tio -S unix:/tmp/tmux-socket0 /dev/ttyUSB0 +$ tio -S unix:/tmp/tio-socket0 /dev/ttyUSB0 .TP Then, to issue a command via the file socket simply do: -$ echo "ls -la" | nc -UN /tmp/tmux-socket0 > /dev/null +$ echo "ls -la" | nc -UN /tmp/tio-socket0 > /dev/null .TP Or use the expect command to script an interaction: From 4782faf47faf3d989d78db5441dc4a2e9d83029a Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 5 Jan 2023 08:23:09 +0100 Subject: [PATCH 131/506] Update plain text man page --- man/tio.1.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/man/tio.1.txt b/man/tio.1.txt index c66269f..8f64e0a 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -391,11 +391,11 @@ EXAMPLES Redirect serial device I/O to Unix file socket for scripting: - $ tio -S unix:/tmp/tmux-socket0 /dev/ttyUSB0 + $ tio -S unix:/tmp/tio-socket0 /dev/ttyUSB0 Then, to issue a command via the file socket simply do: - $ echo "ls -la" | nc -UN /tmp/tmux-socket0 > /dev/null + $ echo "ls -la" | nc -UN /tmp/tio-socket0 > /dev/null Or use the expect command to script an interaction: @@ -444,4 +444,4 @@ WEBSITE AUTHOR Created by Martin Lund . -tio 2.5 2022-11-02 tio(1) +tio 2.6 2022-12-17 tio(1) From ae604d12e2c95c25bb5ebfd927d82441a4b9dd86 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 8 Jan 2023 13:58:48 +0100 Subject: [PATCH 132/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7625e85..da58153 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ when used in combination with [tmux](https://tmux.github.io). * Easily connect to serial TTY devices * Automatic connect and reconnect - * Sensible defaults + * Sensible defaults (115200 8n1) * Support for non-standard baud rates * Support for RS-485 mode * Support for mark and space parity From b6672c170400c63dd98e5cd9d758b80538b6796a Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 8 Jan 2023 22:34:13 +0100 Subject: [PATCH 133/506] Update TODO --- TODO | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/TODO b/TODO index 46f580f..5a0bbf1 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,24 @@ + * Support for interaction using simple autoresponse strings + + Add support for simple autoresponse strings in the configuration file. For + example: + + autoresponse = expect:'localhost login: ', send:'root\n', + expect:'Password: ', send:'abcd1234\n' + + When expect line is matched tio will respond by writing the send string. + + When parsing the autoresponse variable make sure matching expect/send pairs + else provide warning. + + Maybe support regex matching in expect string to make feature more powerful. + + This is mostly a convenience feature. For more powerful scripted interaction + users can continue use the socket feature in combination with the expect tool + as described in the man page. + + Maybe provide a mechanism to disable autoresponse feature. Maybe by defining + maximum match count and/or in session key command to toggle feature. * Support for running external command From c6f319ecb07d50776af02be307d04c42052f625f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 12 Jan 2023 00:08:27 +0100 Subject: [PATCH 134/506] Mention which keystrokes are forwarded --- 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 317e1cb..46bf144 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -280,7 +280,7 @@ Display help. .SH "KEYS" .PP .TP 16n -In session, the following key sequences, a prefix key (default: ctrl-t) followed by a command key, 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 tio commands: .IP "\fBctrl-t ?" List available key commands .IP "\fBctrl-t b" From b533312d45364756ea594805403e6fee348e35fd Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 27 Jan 2023 21:14:57 +0100 Subject: [PATCH 135/506] Update README --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index da58153..1dbabc8 100644 --- a/README.md +++ b/README.md @@ -288,6 +288,15 @@ See meson\_options.txt for tio specific build options. Note: The meson install steps may differ depending on your specific system. +### 4.6 Known issues + +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: +``` +$ sudo usermod -a -G dialout +``` + ## 5. Contributing From 35c9b40982d477d00d9bf09395d96e0047926ba5 Mon Sep 17 00:00:00 2001 From: Bill Hass Date: Sun, 29 Jan 2023 19:34:09 -0500 Subject: [PATCH 136/506] Update README with details on snap confinement --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1dbabc8..53db874 100644 --- a/README.md +++ b/README.md @@ -256,9 +256,12 @@ reach out to its package maintainers team. Install latest stable version: ``` -$ snap install tio +$ snap install tio --classic ``` +Note: Classic confinement is currently required due to limitations of the snapcraft framework. +See [Issue #187](https://github.com/tio/tio/issues/187) for discussion. + ### 4.3 Installation using brew (MacOS, Linux) If you have [brew](http://brew.sh) installed: From 725faa5455f6ef942df2f9b604811f28495454fe Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 30 Jan 2023 16:14:37 +0100 Subject: [PATCH 137/506] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 2941ec9..87234dc 100644 --- a/AUTHORS +++ b/AUTHORS @@ -42,5 +42,6 @@ Ralph Siemsen Victor Oliveira Attila Veghelyi Vyacheslav Patkov +Bill Hass Thanks to everyone who has contributed to this project. From f6905b4213eaf1056c666c9eaf909d0788e9f68d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 28 Feb 2023 17:56:46 +0100 Subject: [PATCH 138/506] Update tty device listing configuration Cleanup and add FreeBSD tty device listing support. --- src/tty.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/tty.c b/src/tty.c index f5ffc35..6f1911c 100644 --- a/src/tty.c +++ b/src/tty.c @@ -56,15 +56,23 @@ #include "timestamp.h" #include "misc.h" -#if defined(__APPLE__) +/* tty device listing configuration */ + +#if defined(__linux__) +#define PATH_SERIAL_DEVICES "/dev/serial/by-id/" +#define PREFIX_TTY_DEVICES "" +#elif defined(__FreeBSD__) +#define PATH_SERIAL_DEVICES "/dev/" +#define PREFIX_TTY_DEVICES "cua" +#elif defined(__APPLE__) #define PATH_SERIAL_DEVICES "/dev/" #define PREFIX_TTY_DEVICES "tty." #elif defined(__CYGWIN__) #define PATH_SERIAL_DEVICES "/dev/" #define PREFIX_TTY_DEVICES "ttyS" #else -#define PATH_SERIAL_DEVICES "/dev/serial/by-id/" -#define PREFIX_TTY_DEVICES "" +#define PATH_SERIAL_DEVICES "/dev/" +#define PREFIX_TTY_DEVICES "tty" #endif #ifndef CMSPAR From 0cdc3ce045a600cd8ed86cca587bfbcf700b7b23 Mon Sep 17 00:00:00 2001 From: Peter van Dijk Date: Fri, 10 Mar 2023 23:58:52 +0000 Subject: [PATCH 139/506] use right /dev/ path on Haiku --- src/tty.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tty.c b/src/tty.c index 6f1911c..92bcb7e 100644 --- a/src/tty.c +++ b/src/tty.c @@ -70,6 +70,9 @@ #elif defined(__CYGWIN__) #define PATH_SERIAL_DEVICES "/dev/" #define PREFIX_TTY_DEVICES "ttyS" +#elif defined(__HAIKU__) +#define PATH_SERIAL_DEVICES "/dev/ports/" +#define PREFIX_TTY_DEVICES "" #else #define PATH_SERIAL_DEVICES "/dev/" #define PREFIX_TTY_DEVICES "tty" From 309a2abfd44b9aeb1672882690803e655d186210 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 11 Mar 2023 02:15:56 +0100 Subject: [PATCH 140/506] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 87234dc..2a11fba 100644 --- a/AUTHORS +++ b/AUTHORS @@ -43,5 +43,6 @@ Victor Oliveira Attila Veghelyi Vyacheslav Patkov Bill Hass +Peter van Dijk Thanks to everyone who has contributed to this project. From c16e0047579d9b3d022ab50f48c8bc7e579c4634 Mon Sep 17 00:00:00 2001 From: Peter van Dijk Date: Mon, 20 Mar 2023 19:59:18 +0000 Subject: [PATCH 141/506] avoid "warning: unused parameter" on setspeed stub --- src/setspeed.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/setspeed.c b/src/setspeed.c index 745bd4b..012d97b 100644 --- a/src/setspeed.c +++ b/src/setspeed.c @@ -33,6 +33,7 @@ #include #endif +#include "misc.h" #ifdef HAVE_TERMIOS2 int setspeed(int fd, int baudrate) @@ -62,6 +63,9 @@ int setspeed(int fd, int baudrate) #else int setspeed(int fd, int baudrate) { + UNUSED(fd); + UNUSED(baudrate); + errno = EINVAL; return -1; } From 4f509594d88a129422ce38e7f99f6fe7c970513a Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 31 Mar 2023 13:13:33 +0200 Subject: [PATCH 142/506] Update README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 53db874..695f61f 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ when used in combination with [tmux](https://tmux.github.io). * Visual or audible alert on connect/disconnect * Remapping of prefix key * Man page documentation + * Binary size less than 80kB * Plays nicely with [tmux](https://tmux.github.io) ## 3. Usage From a6f1b8d5466a6bc5f236d2af691c92a96c4720ac Mon Sep 17 00:00:00 2001 From: Josh Soref <2119212+jsoref@users.noreply.github.com> Date: Mon, 3 Apr 2023 17:39:10 -0400 Subject: [PATCH 143/506] spelling: adds Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index cb608e0..cc96ed9 100644 --- a/NEWS +++ b/NEWS @@ -1018,7 +1018,7 @@ Alban Bedel: * Fix out of tree builds Out of tree builds are currently broken because $(top_srcdir)src/include - is not in the search path. In tree builds are working because autconf add + is not in the search path. In tree builds are working because autconf adds $(top_builddir)/src/include to the search path for the generated config.h. As $(top_builddir) and $(top_srcdir) are identical during in tree builds the search path still end up beeing somehow correct. From fcf760f0d72be8db6b02993b557e48d0ffc93b95 Mon Sep 17 00:00:00 2001 From: Josh Soref <2119212+jsoref@users.noreply.github.com> Date: Mon, 3 Apr 2023 17:40:08 -0400 Subject: [PATCH 144/506] spelling: arguments Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index cc96ed9..68ff63e 100644 --- a/NEWS +++ b/NEWS @@ -653,7 +653,7 @@ Changes since tio v1.38: inherently inconsistent with how you define required arguments. To avoid confusion we decide to avoid this inconsistency by replacing - optional options with additional options with required argmuments. + optional options with additional options with required arguments. * Replace '1' with 'enable' in config files From cee560dd8bed55cac06781057781336189bdb645 Mon Sep 17 00:00:00 2001 From: Josh Soref <2119212+jsoref@users.noreply.github.com> Date: Mon, 3 Apr 2023 17:40:09 -0400 Subject: [PATCH 145/506] spelling: autoconf Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 68ff63e..9ce3fd4 100644 --- a/NEWS +++ b/NEWS @@ -1018,7 +1018,7 @@ Alban Bedel: * Fix out of tree builds Out of tree builds are currently broken because $(top_srcdir)src/include - is not in the search path. In tree builds are working because autconf adds + is not in the search path. In tree builds are working because autoconf adds $(top_builddir)/src/include to the search path for the generated config.h. As $(top_builddir) and $(top_srcdir) are identical during in tree builds the search path still end up beeing somehow correct. From 36e4c672a7a2eb41e5b6ef489cf040e03c5c7c48 Mon Sep 17 00:00:00 2001 From: Josh Soref <2119212+jsoref@users.noreply.github.com> Date: Mon, 3 Apr 2023 17:40:09 -0400 Subject: [PATCH 146/506] spelling: being Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 9ce3fd4..5c85eef 100644 --- a/NEWS +++ b/NEWS @@ -1021,7 +1021,7 @@ Alban Bedel: is not in the search path. In tree builds are working because autoconf adds $(top_builddir)/src/include to the search path for the generated config.h. As $(top_builddir) and $(top_srcdir) are identical during in tree builds - the search path still end up beeing somehow correct. + the search path still end up being somehow correct. To fix this add -I$(srcdir)/include to the CPPFLAGS in Makefile.am. From 2f3583ebdecce7e94a8b6befb3f15a4916975e56 Mon Sep 17 00:00:00 2001 From: Josh Soref <2119212+jsoref@users.noreply.github.com> Date: Mon, 3 Apr 2023 17:40:10 -0400 Subject: [PATCH 147/506] spelling: configurations Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> --- example/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/config b/example/config index 05f96c9..1b66d07 100644 --- a/example/config +++ b/example/config @@ -29,7 +29,7 @@ rs-485 = disable response-wait = disable alert = none -# Sub-configuraions +# Sub-configurations [rpi3] baudrate = 115200 From b540f16734d54027ce9de44bfb11ad59028a118f Mon Sep 17 00:00:00 2001 From: Josh Soref <2119212+jsoref@users.noreply.github.com> Date: Mon, 3 Apr 2023 17:40:10 -0400 Subject: [PATCH 148/506] spelling: interactive Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 5c85eef..8162647 100644 --- a/NEWS +++ b/NEWS @@ -524,7 +524,7 @@ Changes since tio v1.39: pipe data to tio which then forwards the data to the connected serial device. - Non ineractive means that tio does not react to interactive key commands + Non interactive means that tio does not react to interactive key commands in the incoming stream. This allows users to pipe binary data directly to the connected serial device. From cbb8ad8362548e2253671c4967d999dac3ffa8ba Mon Sep 17 00:00:00 2001 From: Josh Soref <2119212+jsoref@users.noreply.github.com> Date: Mon, 3 Apr 2023 17:40:11 -0400 Subject: [PATCH 149/506] spelling: timestamp Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> --- src/timestamp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timestamp.c b/src/timestamp.c index b8a2198..91374dd 100644 --- a/src/timestamp.c +++ b/src/timestamp.c @@ -50,7 +50,7 @@ char *timestamp_current_time(void) first = false; } - // Add formatted timestap + // Add formatted timestamp switch (option.timestamp) { case TIMESTAMP_NONE: From f5c8d5233c6bf6351e9f1166f4129f763cd45e10 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 8 Apr 2023 20:02:32 +0200 Subject: [PATCH 150/506] Add tio icon to README --- README.md | 3 +++ images/tio-icon.png | Bin 0 -> 13675 bytes 2 files changed, 3 insertions(+) create mode 100644 images/tio-icon.png diff --git a/README.md b/README.md index 695f61f..0c9a0f7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ + +[![tio](images/tio-icon.png)]() + # tio - a simple serial device I/O tool [![](https://img.shields.io/circleci/build/gh/tio/tio?token=da7e7fd0d0ee99b9f986f8877dcdbe28f73d9e06)](https://circleci.com/gh/tio/tio/tree/master) diff --git a/images/tio-icon.png b/images/tio-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d95d449a2a63cb0314f2581ab6297ab69373acd5 GIT binary patch literal 13675 zcmeHsWmp`~^6uj9ZUL6y?h@SH-ED!z-Q7b77J?Jp-7UB~1PSg=@L<6%$@v|-_kTZ} z=ehUWnP+Eqrn~B`s<*3qdurb+D@vgt5+VWs02CQ%an;vv^WP^te%5E2Riz=82r z*LGDk@gQ?_cCY~3g2-IG96@9tPp}05;JH+h26iXnO$vK4!)b>e>vd-NqV}$M_tpdB zEoju;LeoR0Q5u#2egnlrc@dsm!!E7td5uuVrLu~Ed)_ZR%AN!m8dd!lwmO71UO4rb(E z6BknWxm*j`# zyNbqTnZo3nPcejFttMPfwsZ9=+qQ4~GVIy<51N?yy@mXm2kBa@{1a^Oa{WKP>j=fw zELQK!*My9CvSd0nY+4;v&? z(?j%CwMW)v4o$QLBpyp%$p#+FPs-NR8J^#Ui!!{I4ztSJYk#@ScUYcQ)$JMTTyd*( zpBMSo4s5EfiScyHs7?%}#KuJ9fdYeXMp7%7B&)_yS_it`xk_A)vC;C5(wdpG*zeCc zWv%#uPMf^gYo4<%_c|>nXxv^tju8}`>h1ZDFFVvWio;%6{BX}{5orjzdB@~obPqQ! z7|V2=^Fj0Fe1QS$0*HHc>~pR>YLxeMuQD2YruI>Lq`%z7YoykIICTA6C2LE-d5T{q z$1zAZ_>HTe{;t=|)3vz&i&e+g)M@GSX33$BYHQ9cmP?kKq}-;E?({>vn-$J^8nA~j zOT#az=r&8vEw-_h)GG_Ep#H(OE;=`_&SysRWICX5w`7v2@xiMH|J0FKPL=L3Hg?ZH z@`5(v9rLZeo_?u^>$J2wmubI5koM5%zFRE+;TP76FJ1{rYYx2Y{W@`xQ>iqbNALP+?c3qN)2`9dgwkUD@AHFk9i(I&0VyqRN)r^?apx) zvABv>ndr;&;l`Ge+G7B@0hmYh<7NIuxX zIOOGB!gIlOu(D?C=zsjVTri)z+;{CR9Ns7x>W1o(@oJaXiBi8>?h z*!MN!t1+BCYc`YiXiMyjx!f{y#)t@AVgfmGw1pZIkQZDArm}K#N`Tr5Rm<=%$qF0Z zg0%7)_rTk@wl{E+klO6mu!kYs zHDPT>olg>m$#W-jd$%Ww|N8D4p?)<0YklizsS;1S0<4%_f|urmbUmOCCj+`DX3>Ns zMSk+ZiS^P@QuoAlD<)ygqq)MhD`L>1DaH(m!(@Y5Zj)go+KH^OQ+5xaem}} z+ZBr}QrTxxoJ3vnUa0oUi^-OV$imG8Y#gGns4E%7PwbHtn=n}WoZfdS8?+SPsnJt2 z7EWYji%#uopcDv}u?Ww`D9^x@Br=sR`wKav-g(Cgsc6WLlI0`IJ`WBb^7qcx$QL0x zp{d(!_GmmU%rsK2rTW*_V$B8HBO@u3e?_bG$2YUQQijEhowm%AU9wU&v1vx_ffh1G zIs8B(UT=z0rU*@h=w&{VeKodwgCi_xQ2p2p<%@9<6VmwJ8M`g_VhT;03XV|ua2Ru= z%;e)+!Z_l~-KGWdccnt5^&@swh$3@yBx=R-F-tODYVmKw7T-+`chAhK1Trim`+VVksE z5Gt8TZCISh6NTkj%1fd}vCFj0PRvng=#&x$!_z8q-KPTLAv9%fmA6=>JsE7YH~@2- z7)m9d%N#{c%f802=qJ8J8UxtFq) zH}CHb6tfD9SjI`tQ|KE{Y(N48PL5L>h!Oy-`-Tna$QRWo!dE!jEw~V}6Tumf{5=x0 zQCdc#%qo)X-4KxhmEpL&(&Y1R;9=nf)Cw+CzIEW5HQ6Pkk{w0shYhKym3KCyo?Qpy zT}CTSiqoZUjBnqPh1krpG7pQ0&Iym;+8t0=yP?}-lk_89Eg_Vv3Wsr;Ecjv)?`uQb zP9eInaTDH@&)ud@$Xj8vOK27&cdF3CHwsNYVmOThNE#VH&i%SWUve!dXP7ycR|h9GccNy?WJ>_huSSWIyiR%}d^ zj_D`n*`h+9J$9L-vt#&8kNXM|7W#z<2UHw(H+PX=DvUUs6wbi5F}t~tbR;pX73OMZ zL0okto5ac7s1kj^@%8MfsR@ZUf_7&5Dbz7d#S9AN#MjMe2L;7tQ!7liz5VTd@bg!E z93x3Piu`GxfnBQ{;uBJuopgxgK|v87@UYdmJXA!;N0e z!TA}-JM=nR%zJLHuWnznfgmtkB;S}FOe|ZBd7zKZ5m`Izppl&N{Zy>Kl^vOPuvfeK zn9SLx>J5J3H6ESpHQGoo6uLxBsw%7q0M9yG(*=_P;Jkp0eZDpEH^pY9Hl75!5nDe}f4lXm?AfQ0}j`FqN^^B3J8 zbW0NQq@$Z`9Y#-b!LvWwdvSTP{7y7sHklw3%cyYMgY<=W>H9jA5!f!!Q8)ttTtB&a z*dv5XHaIR4PKwvcLb8_ZSo4*F#u^scrf&VV40>VO0*{ldwPEwm?1ENmaurn|eqsVS zHRMjV^$rQ*%43`5<_7p2`QAy^^G?0*7zh`0MsQitMpivsHZ64TsIDo<5Phu2ZF+|0 zJ>O%MRw$*Sz$HU&OTh?-T34xsnT(4^am|c9ZCUoY6kGSu$DfOK5Uw{iXQF-!xk|z! zU+xI)>7tz&is4qf%iiqw6$@Awhzer9gD)Q_B%iyGWwXqNE17b)6S7t&WV_BisV->< zd;Fnjif|eu9OkYNsggMpUj~4sOespLAC?rkEL&1x(v}N?ra>AF+C_N>AH1n6?J7!* z4b?ZD_LijgTRE&Th7a=a4+^_Q#4-h_Inuf?K$pa7EwxX+!71#Uu2itm=2F6Y`rC2%x))s;A z-jV{*uU`TIrwrj@NqQrt^#=_on%EIDekmQ#~RhWVhhH4*NJiB`8dvylwm5c1K`113&{ zEuBX(9}`Amrz-~{Iu2CS{s<9hYXwJgXsHn2aWtK~t{fta^9;-3@VdGpuXnk~*OGrB zVeAMYAAlo}v&0bDq5vAzY`+kr%Jrwj-jEP@W$Xt120k~RbR)HHrrob= z>v}~Qng__ckNSBa6=$K?Y{FP-L{w^9%5{q|G4KYuq|o2pY1@cr4|Bg&65$JkQwSB; zdiHHv(u5)TG=v1CZLQhj;u+uV&A|<2jVlR}_ekzHr=zmT>3YIK^mEuCp`v<5#QxwF zR?6{##?tRs48-Ta6R2P0>0r%v>mB}EGC%`8PZ3w#!7WwtL{HR>tV+7~Qxr3mtJS9{ z)|27sF|m*`;(_nH@|O=fG;y8HE4+(C6rEV^fq`3OE$O!P?d1+>46;jtHB8ZWlQ`$Y zofzRcHy5yiO4_`+*PBxN)}ssJ;HX0PTw-Ubd>Io z(*+b-rPm{@czgje@RP;{9+j|;FjF&Y$Z|NgC)kiMzffN{WmZy4SeHGV`3d!9L=7Ra z#a$mOhLu%_w;On~QF=m!1`TuS(I`l*SqPOb$VB%8?=qF&GOm6@&>GGslnU1=x-l9_ zF~R+kZMb~GO?3hqacWwv=c_CX=1Aa2x*lYSr<-i`f>Y!@6qdr7uEl2n4<{%p#YTzV zo6zv>@d$r`>gz!_-KYy=ghHUlSnEbq7|w}CS4z=78Ev88BYZ>9>ro$~oI=VLEDxgzC=kCExj3{Q6}b zbh;3sg?uOO3$wv%`|L>U|FkIwsW0Dg=&?@fx&E2k==R68M&$X*(aUn%L#JRGDO&XC z&zD8{HOuq>Z-W0V*tej~L8APyXotkaNkCPQQtk2Ow|GWjfG1z(E(FLDmB zw5mQa%=h3B(hnMNc$Nkv`YZ7xm{Ek4;Eci-6DyVUWWEArpgEU&KqxE=Xw8`xZQfqR9N`!7n5)>Ruz_# z-4g7+-xfUmFx>_3MX?OtTmPNbtqY?4+)>EM=#67rIbM$Z zJ1jo$o=Onx;&sSN_nCb~PhWE~wjv;tIp&HLIn+H!a@?sSmZZoD5;`l0zJ0f9Ht104 zn++qQJM>RGD7^2C3X4!o9)aD|Vq73C#BbP$FdI+N9kg&i*C7SU3?0V(G6VqxM|5sb z#Ke#k5+jCJULGDk4liH8H@8q|NX`K;$mbESg=<3aYk^u@L7vad!H&tq+`$yYw4a_VDmv@?d9jaJFP-<>lpNW?^Gy zV`F@kV07`acQx^3w0EKS4e>V&agd9dGuY7;>|js!8`H$p!Oc|w2z>1)`@4U3jtUC@ zfVX$~i-lJ{m_1D#nOT`wnC8!)V6EV{U53W5&h8X7Mi&O3vWdtTeIxSFe6U znZH8un(*>)fh^1!%{VR07&%Nqyo{zMoGgqMATA4DZZ>WnGYif?Q08WQk`B&xCa=Q@ zwllE=F+18@{^|HlIG>2Ji~x|0iRGUy%C;u17Ox5dKzXpeo990})WLQjHCL10Y_f83 zaIo^c9_*|vti0Up{}j>$IlH`O;%`h=7A7{1KRv&Ph3_?(S7J?m=jkiJpZ3>S_{5w+ zCaw<7>JARJ0>Ixck^R>ElipzNlmY~=9{Z~T$y&e2N3>Ju+lhuUVf{l@votur3!<^HE zk=GQ&!pOtTZq8xK%MM~;=lXYa7Y7Sh4-;pQh~;afuhG0F(4T0?X#P-1`|r{oR-oTJ zvAoW0me&urItvRQ3lAR)J3R|49}5eR`LBSPf6wZ_N6gRse{jP8N8n$Ufmgl1wY@Gc zud5aFKbEV%IQvcG|HtF6vH1UJ;T8J7M*bsy|4Y|@>H3ct_>YADn_d5<>px=PKN9|L zcKtu23-O;XJRtkmU69A?>r7aCo&D>p5Q3w$jtc;Qg!TJ`0HkH$zc#|T$|y*{?Lorg z!9x)(v-G_-5x7cdyNWs3{oW-5{%qbr=42jVS1YpLn>Vc>R6GCx^i}qA|%g{O!>FOMlfap7(>zMQ5Zf!Pp zDu9d*`bh<0HuSPM3Y`&63Ne2Z16a)(hi@CnDl_0dP$TeW@@k^pqw0CV=j1!@{+iDt zA>Qil#fr<>;-o{>bw$H>K^O4D&B+0!_-x(SX9RX5k&0`=*jB~%XY~<4IU*aNEEu_~ zlnknxPP!sXRBNxUGOPT%Cv2LAi1n&j5>Z|F)1bm}**4VpADN;5O~pv$@Z+bqq0KF( zo9Pe?BPQ5uY|bv(jg`hKm%4PmuXlDBl=a?U?b;V&N4(viEHuEd9i+b25o`( zU$o?&bYJVgz2qYr1JP5HbH5=6@*eck&3hkcRon`8p!Fg+eCE{B&m0p%T$dALn1dgK zT=AA|u`u9l`N<$XV&q-F6D5M~EB?{b@45_{!MU^YQ`}XrxOlGc9xB8bE~jaAM07UL zs=V>Dn7iF1rHk?B9&CJRk8fL#QZ16w!Ehmo_`p$~_g!PKNRaS-fDi<13~hMzEE^vj z>jmoWWf@h3!~tTSdIDv>@&$>zq(r~<49miyYMQ)X_}Q6=G9X2Sa?vSooC`aECOLX; z95x@lED|QZDGzLIZ9(A%ccR2ccc&*a#zO)QmQR;|=>;^q@XQkQd>|F_+Bqn%FlJ1^|3RWyiT1tp z_KO72X1_k_Lpd`8ac3+fYUp;&RQh@jg-5@OCuQ0$6wFRMkPCvtMZ~xqJ0+)q0UslX z11R9(LI$B>bMuhSKUf12+e2s0XB;>_*>cFOPuWCIGN67Rf|yiKWY|#5inv89B8ySu zB!d_=qO^nntx8bW_?$@)6^XFX6-Zpg&Ypg4jDE55xG(s))f`pW#MgAGJ7}MaQH|S| z2$V9H1jKrpheM#De^Rq_?uI8~8pW^+)T-$FBt74EXJ@J^Ngx{sDUAVeg^$Y7;RuEy zi)!o}`v#%cX2+HFaA)JMkIcoj?-~@zqZv%2=zbL11;MJ#u-a|ChM;DZCyZT;FA*0y z)NesUFLntPl4#f~SyfzyN(A?r4gZq(ivePuBiG*x8aKDs9 zEN@sO?MSt5k}?cPNerGRGo_+|Wl!y|i_bvwg=Q8Jgd9Dr>tsnP7KSbuN>w2+ho-5X zfM0U_OcHS=sAz&9&lE_8l%oop+@;sD%c)OGO#S9NvW91Tw{dMB;%d0P+%R$}?B}3J z0_7-VLB)OTH9c5_;`#R3$2fh-z#o+;WZ{|?{82R8RmAdS5+d3s!Rl0jsVR?dQIz6m z-Va5j7(s@WFj|=MhKpq;yl07bgu$r2`1Kr7c(eYF)lbi?UZmV>SJY*%ucyY6>CMBM zb-JBwZ!~sPH<>~jln?UVC;`REN=vVzmI#EhIDi&CyV3qWVzS*|t^{~Zx&KA*`8I%e z&<*oL_Qb)$I=|EYnt_zgL<{z%Ikendm>`+_ddvx_kYi5ipvn;hI!=nmoWx128}m*LscDcvJyEc6Y-)fnMc z0stUxxN1(a@?uE9Wot-h3wS2?28Y!0{(i?~7Ci4xKv34_53vk&3`5 zk9*m5VuS8{?@=5s%VY%^Y@{ypE?d#LUu+FEPUmMgoyXH9!x#3_!^*QeCq!%aYkr+K zL&aQrDgIejo37A4-UDQhspWzJ)FtEF^PXJmN^mV#ma26u8FrZG$?}#{;&O;>Dev6oYatRLuqTGPk>#s#JJ6 zf~xWZg-=*sB)a}?_s3c7#-{Y+$W#^KXDzS zboH11xZs>^xMfVgmH$4ImU(ma;NFbVKSJ2m^@Xf7rDIZK;4lIC_WknzL5jq$|Pbo-#=;Ry3* zLH%j4Ybf-Ym?jr1N5nxIFVVx8(y-()Ymy4t#3e;s_uvT)c&=ANTq@l8xM}5GX+mq6 z&AR`M=%*F7bi{n@icFq>owBl$SR$gd4!dAmYqf_kk);|adejpVSKU3+m}6NnPqm0e zN=NM*9prg(P1Je77?2;*{Oz4N(KHb{cAVXpg*L{1uU*yS&%I^UQXk#z7{~*S?x*{A z71?YX_LEGSU=qjWy91qbLf9og*#&0ukM2Zibe$9|kZKIsuo%jOhB?(_$_Q;jBb*KI zfvvnv*|X*drJ#FNEEtj}_) zp-FlsCE(CaoW5snH})VBgK;qp=#vwD3B<5qY6zGc_*6n5tU@Oe_7p289I0;GWU_Tk zu41-n`M!!&>C`Z5ulYDPulfaw4599u$nxxZ%ia0nE(f{r1d*XZGEGi*5nwGVj}n<7 zS5sP>y}uEUlfC`HWiLUsWyX+H|Bv&C#Rm?c5I8@sh3zes0)i41< zUX$q}W8`9U&5w=XzOp_o)df9!oi<#es5I8O_thLTW9f-Y9DGULZxanxHfuZbmTgMo zTRHu}D3EG+r7FWec{{3GR?cUO#Tl}OHhvV!h{`@tXqkTt!HA|D|Fs&REP8I8Y4|mf zXlCgA;AadOI-I<+wZGDtHHYN493$@%(D#~I=5%46-JT5%{WLY&`Tj`Sv)NtlnweU3 zaiRx;IWetu3ig^`qcC#|%P%O>2r+!Yp{&#CE0|<2Q^q;Xx`k8ISw~D-w zJ9~zgZ-u!V%x_|`*0^+PJ2e_UEKgo&LNrV`_83ci8)#kzwRn#m)l5*85$|(Qo+JfwR~Mzl0s#p)>iR^$Zi|Bu;)Z2-u)IPwBOd; z?;PX>_>WGk6#AI>-}luiv}J81rt|`eErhbqD{tSQ-o-{{Lxj0l>Q>;8DQ1_|Tu2Gk zzr={`@9!;-H)M+qJPx;<5rf|WmmfycZ|tvQPdz0GNk*xKYrRiCc6RJ)+NEDYr0ykm z2neyym~0djF?BrNoOrChF^ii!^|oDaYs$EvXTP$vSeh+9Y4zD!YdZb9Ke3s?b&&ev zf8Q@hg&Syd`)lZQo2dpfzJZrr?54>~7CFV}?HKD#ysxuONjtvly zasya#+r;`Eox<7M>>T4#zu>p3ZDdL}bv`->J^rA^6W%~o68QSSyvCiW1je|y)VFDP z{Y6<|;aHOX6hNl`d|Gz)GNV>!Gk<1Xk*ZKRU=M~s(~HYR7)-%Sn%TYjV8oZP%-qcN zzE=0A8j&deFh~px@t}qt1qH34?HNgf)s%P8^D^Okp_Z)MLKd7VNp+mwh~}`QOFCR2 z15rQ8{KngNPhSo`w@jB@Vft-g*s=gvc+Tl(NKuDR?rH!u$!X%l;{4C$@^Ff@Dgpn? zDYXxCN6ZW)zs`p(J6bK+=qHE@(b|Xfm;|bak}X-X721;VvhIqon2Ggtb?0f&;x84h z`8`U=_B_)#wZ%gAzjxeq;0fNpM@q(ZBb|=ya6P&V=(xSx@2t*8kmqXH;qGKxhmHu; z80VEZ4_{>p)n-psqcDr6AKVu@mttOdN*Z2XXX$X6T!_&lN^hDY`nct5nenzov&Z<; zAnc-gi~lbp|GQ6T3Qs+~%l;h?GozOC$LYBkQX1YOG?eilY{>`>=`yOnINf*cr%|UI zn&DfzCKVJKKMR5}gu7s^@HlAktW{@ap$Vac2UU@9uIaZ~)s7vDP*xL2^k6wd%lGy- z>+DW9-c0ER-*&;T%H4KWOGi%+%y2zOUe02(t7ejMkMbvqn?J)2Mvn^8C0kpl_r|Pb zC=Q$PZYG81W4}$8C#XeUtZtE05dmKHnFOn6^fThiKCiHtGpR)o*Y9o8eB6i|7j#Il z;geNrGZ0>QA2yDvLJ`qSTEnV=ip{C~ZW56x$K*?pqh6UK6Tk)`FZ3FSg9OV+5$DmL zhEozH2LYK2vVDX1N#e&lAUsDmNAHBaDKzHv(kcp+myqyssh+5N2_owL8QlYS4lC98 zF08t*@7dtlvDzI+>G0CfIyVk_45^{wQ1&T33toxEVkDN$`_o8^&jg=5+fz_`uUdWE zy5V-$vozwG6>Led_3=1_cW+mC7O)h+i=L>+S>EuLdl9&ICJ}Nyi+g#(i@qfZ_CfZ8 zx5Uhy4r|Dw11CmwvK)HbJV;V1&nn_)-GglSBLX3B`hor2!Hq6{9@pU@8vI*4)cDnu zhorbWa)$YK$1IN!ob#Agho{Y8QBChBCyOBe7NSi((dzuC4}+xv6QxW39say;C^>8j zVmr+_akY?jREy6wA+A5qR?&n-&$ao2wFCyIZg2UZ<8spa-L9Cw+qP-X%afiYOCs`? z+q4o2cj$B7mg%LbFs)B9(#m>*)^Sz!_e#O Date: Fri, 14 Apr 2023 11:18:27 -0700 Subject: [PATCH 151/506] Support input mapping modes for sockets --- src/socket.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/src/socket.c b/src/socket.c index 16d9621..89a10e3 100644 --- a/src/socket.c +++ b/src/socket.c @@ -41,6 +41,9 @@ static int sockfd; static int clientfds[MAX_SOCKET_CLIENTS]; static int socket_family = AF_UNSPEC; static int port_number = SOCKET_PORT_DEFAULT; +static bool map_i_nl_cr = false; +static bool map_i_cr_nl = false; +static bool map_ign_cr = false; static const char *socket_filename(void) { @@ -123,6 +126,9 @@ void socket_configure(void) struct sockaddr_in6 sockaddr_inet6 = {}; struct sockaddr *sockaddr_p; socklen_t socklen; + bool token_found = true; + char *token = NULL; + char *buffer; /* Parse socket string */ @@ -175,6 +181,41 @@ void socket_configure(void) exit(EXIT_FAILURE); } + /* Configure any specified input mappings */ + buffer = strdup(option.map); + while (token_found == true) + { + if (token == NULL) + { + token = strtok(buffer,","); + } + else + { + token = strtok(NULL, ","); + } + + if (token != NULL) + { + if (strcmp(token,"INLCR") == 0) + { + map_i_nl_cr = true; + } + else if (strcmp(token,"IGNCR") == 0) + { + map_ign_cr = true; + } + else if (strcmp(token,"ICRNL") == 0) + { + map_i_cr_nl = true; + } + } + else + { + token_found = false; + } + } + free(buffer); + /* Configure socket */ switch (socket_family) @@ -340,11 +381,26 @@ bool socket_handle_input(fd_set *rdfs, char *output_char) clientfds[i] = -1; continue; } - /* match the behavior of a terminal in raw mode */ - if (*output_char == '\n') + + /* If INLCR is set, a received NL character shall be translated into a CR character */ + if (*output_char == '\n' && 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) + { + 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) + { + *output_char = '\n'; + } + } return true; } } From 5651c1c5d7f8303a9131232970f67f25815e62a4 Mon Sep 17 00:00:00 2001 From: Braden Young Date: Fri, 14 Apr 2023 13:08:48 -0700 Subject: [PATCH 152/506] Configure socket mapping flags from tty parsing logic. Remove duplicate parsing logic in socket --- src/socket.c | 44 +++----------------------------------------- src/socket.h | 4 ++++ src/tty.c | 3 +++ 3 files changed, 10 insertions(+), 41 deletions(-) diff --git a/src/socket.c b/src/socket.c index 89a10e3..1d7d15d 100644 --- a/src/socket.c +++ b/src/socket.c @@ -41,9 +41,9 @@ static int sockfd; static int clientfds[MAX_SOCKET_CLIENTS]; static int socket_family = AF_UNSPEC; static int port_number = SOCKET_PORT_DEFAULT; -static bool map_i_nl_cr = false; -static bool map_i_cr_nl = false; -static bool map_ign_cr = false; +bool map_i_nl_cr = false; +bool map_i_cr_nl = false; +bool map_ign_cr = false; static const char *socket_filename(void) { @@ -126,9 +126,6 @@ void socket_configure(void) struct sockaddr_in6 sockaddr_inet6 = {}; struct sockaddr *sockaddr_p; socklen_t socklen; - bool token_found = true; - char *token = NULL; - char *buffer; /* Parse socket string */ @@ -181,41 +178,6 @@ void socket_configure(void) exit(EXIT_FAILURE); } - /* Configure any specified input mappings */ - buffer = strdup(option.map); - while (token_found == true) - { - if (token == NULL) - { - token = strtok(buffer,","); - } - else - { - token = strtok(NULL, ","); - } - - if (token != NULL) - { - if (strcmp(token,"INLCR") == 0) - { - map_i_nl_cr = true; - } - else if (strcmp(token,"IGNCR") == 0) - { - map_ign_cr = true; - } - else if (strcmp(token,"ICRNL") == 0) - { - map_i_cr_nl = true; - } - } - else - { - token_found = false; - } - } - free(buffer); - /* Configure socket */ switch (socket_family) diff --git a/src/socket.h b/src/socket.h index 2caffaf..53c343e 100644 --- a/src/socket.h +++ b/src/socket.h @@ -25,6 +25,10 @@ #include #include +extern bool map_i_nl_cr; +extern bool map_i_cr_nl; +extern bool map_ign_cr; + void socket_configure(void); void socket_write(char input_char); int socket_add_fds(fd_set *fds, bool connected); diff --git a/src/tty.c b/src/tty.c index 92bcb7e..9c4c8c1 100644 --- a/src/tty.c +++ b/src/tty.c @@ -987,14 +987,17 @@ void tty_configure(void) if (strcmp(token,"INLCR") == 0) { tio.c_iflag |= INLCR; + map_i_nl_cr = true; } else if (strcmp(token,"IGNCR") == 0) { tio.c_iflag |= IGNCR; + map_ign_cr = true; } else if (strcmp(token,"ICRNL") == 0) { tio.c_iflag |= ICRNL; + map_i_cr_nl = true; } else if (strcmp(token,"OCRNL") == 0) { From afc9e3be5b787787e99966a6d21485ce0930ceba Mon Sep 17 00:00:00 2001 From: Braden Young Date: Fri, 14 Apr 2023 15:00:03 -0700 Subject: [PATCH 153/506] Move map variables to tty to keep them all in one spot --- src/socket.c | 4 +--- src/socket.h | 4 ---- src/tty.c | 3 +++ src/tty.h | 3 +++ 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/socket.c b/src/socket.c index 1d7d15d..23b1249 100644 --- a/src/socket.c +++ b/src/socket.c @@ -33,6 +33,7 @@ #include "socket.h" #include "options.h" #include "print.h" +#include "tty.h" #define MAX_SOCKET_CLIENTS 16 #define SOCKET_PORT_DEFAULT 3333 @@ -41,9 +42,6 @@ static int sockfd; static int clientfds[MAX_SOCKET_CLIENTS]; static int socket_family = AF_UNSPEC; static int port_number = SOCKET_PORT_DEFAULT; -bool map_i_nl_cr = false; -bool map_i_cr_nl = false; -bool map_ign_cr = false; static const char *socket_filename(void) { diff --git a/src/socket.h b/src/socket.h index 53c343e..2caffaf 100644 --- a/src/socket.h +++ b/src/socket.h @@ -25,10 +25,6 @@ #include #include -extern bool map_i_nl_cr; -extern bool map_i_cr_nl; -extern bool map_ign_cr; - void socket_configure(void); void socket_write(char input_char); int socket_add_fds(fd_set *fds, bool connected); diff --git a/src/tty.c b/src/tty.c index 9c4c8c1..54ea980 100644 --- a/src/tty.c +++ b/src/tty.c @@ -129,6 +129,9 @@ 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; static struct termios tio, tio_old, stdout_new, stdout_old, stdin_new, stdin_old; static unsigned long rx_total = 0, tx_total = 0; diff --git a/src/tty.h b/src/tty.h index c4b6797..add8bf1 100644 --- a/src/tty.h +++ b/src/tty.h @@ -24,6 +24,9 @@ #include extern bool interactive_mode; +extern bool map_i_nl_cr; +extern bool map_i_cr_nl; +extern bool map_ign_cr; void stdout_configure(void); void stdin_configure(void); From fd1003533c8f39fbcfe1d0188a3d942e7f5f3016 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 15 Apr 2023 00:13:26 +0200 Subject: [PATCH 154/506] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 2a11fba..481dbe8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -44,5 +44,6 @@ Attila Veghelyi Vyacheslav Patkov Bill Hass Peter van Dijk +Braden Young Thanks to everyone who has contributed to this project. From b5ca54c56e2f1648b0f9fd86cf8a524580738e10 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 15 Apr 2023 00:17:27 +0200 Subject: [PATCH 155/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c9a0f7..6a6db74 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # tio - a simple serial device I/O tool -[![](https://img.shields.io/circleci/build/gh/tio/tio?token=da7e7fd0d0ee99b9f986f8877dcdbe28f73d9e06)](https://circleci.com/gh/tio/tio/tree/master) +[![](https://img.shields.io/circleci/build/github/tio/tio)](https://circleci.com/github/tio/tio/tree/master) [![](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) [![](https://img.shields.io/tokei/lines/github/tio/tio)](https://github.com/tio/tio) From 8fe5dde4b83ed7c0859eb6eeb410ab38db9abb22 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 20 Apr 2023 17:28:39 +0200 Subject: [PATCH 156/506] Fix line termination for response wait feature The response wait feature waited for a line response, a string terminated with either CR or NL. However, some devices may send a CR and then their line content and then NL. This means tio will quit before receiving and printing the line response. To solve this we simply ignore the CR character and only consider lines terminated with a NL character. This should work for all devices as lines are AFAIK always terminated with either CRNL or a NL. --- man/tio.1.in | 4 ++-- src/tty.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 46bf144..83cf6b1 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -220,8 +220,8 @@ At present there is a hardcoded limit of 16 clients connected at one time. .TP .BR \-r ", " \-\-response-wait -Wait for line response then quit. A line is considered any string ending with -either CR or NL character. If no line is received tio will quit after response +Wait for line response then quit. A line is considered any string terminated +with a NL character. If no line is received tio will quit after response timeout. Any tio text is automatically muted when piping a string to tio while in diff --git a/src/tty.c b/src/tty.c index 54ea980..186fa77 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1426,7 +1426,7 @@ int tty_connect(void) if (option.response_wait) { - if ((input_char == '\r') || (input_char == '\n')) + if (input_char == '\n') { tty_sync(fd); exit(EXIT_SUCCESS); From ab905d8e0c202fcc0c3394181c7ccbf6b2d3bc9c Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 22 Apr 2023 20:41:27 +0200 Subject: [PATCH 157/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6a6db74..c935cf9 100644 --- a/README.md +++ b/README.md @@ -286,7 +286,7 @@ The latest source releases can be found [here](https://github.com/tio/tio/releas Install steps: ``` -$ meson build +$ meson setup build $ meson compile -C build $ meson install -C build ``` From 3bedd85e7cc584dbb1baf60f38ed43e0ad11bb89 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 27 Apr 2023 10:13:57 +0200 Subject: [PATCH 158/506] Update man page --- man/tio.1.in | 5 ++++- man/tio.1.txt | 18 +++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 83cf6b1..36118e5 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -131,7 +131,10 @@ List available serial devices by ID. Enable log to file. -If no filename is provided the filename will be automatically generated. +The filename will be automatically generated using the following format +tio_DEVICE_YYYY-MM-DDTHH:MM:SS.log. + +The filename can be manually set using the \-\-log-file option. .TP .BR " \-\-log-file \fI diff --git a/man/tio.1.txt b/man/tio.1.txt index 8f64e0a..2aa04e6 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 simple serial device I/O tool @@ -66,8 +66,7 @@ OPTIONS Disable automatic connect. - By default tio automatically connects to the provided device if present. If the device is not present, it will wait for it to appear and then connect. If the connection is lost (eg. device disconnects), it will wait for the device to - reappear and then reconnect. + By default tio automatically connects to the provided device if present. If the device is not present, it will wait for it to appear and then connect. If the connection is lost (eg. device disconnects), it will wait for the device to reappear and then reconnect. However, if the --no-autoconnect option is provided, tio will exit if the device is not present or an established connection is lost. @@ -101,7 +100,9 @@ OPTIONS Enable log to file. - If no filename is provided the filename will be automatically generated. + The filename will be automatically generated using the following format tio_DEVICE_YYYY-MM-DDTHH:MM:SS.log. + + The filename can be manually set using the --log-file option. --log-file @@ -151,8 +152,7 @@ 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 multiplexed to the terminal and all connected clients. Sockets remain open while the serial port is disconnected, and writes will block. @@ -170,7 +170,7 @@ OPTIONS -r, --response-wait - Wait for line response then quit. A line is considered any string ending with either CR or NL character. If no line is received tio will quit after response timeout. + Wait for line response then quit. A line is considered any string terminated with a NL character. If no line is received tio will quit after response timeout. Any tio text is automatically muted when piping a string to tio while in response mode to make it easy to parse the response. @@ -215,7 +215,7 @@ OPTIONS Display help. KEYS - In session, the following key sequences, a prefix key (default: ctrl-t) followed by a command key, 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 tio commands: ctrl-t ? List available key commands @@ -444,4 +444,4 @@ WEBSITE AUTHOR Created by Martin Lund . -tio 2.6 2022-12-17 tio(1) +tio 2.6 2022-12-17 tio(1) From 148a3c1da1dd837f2403ddf1456fc0deaf21fe69 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 28 Apr 2023 20:50:31 +0200 Subject: [PATCH 159/506] Add --log-append option Add --log-append option which makes tio append to any existing log file. This also changes the default behaviour of tio from appending to overwriting any existing log file. Now you have to use this new option to make tio append. --- example/config | 1 + man/tio.1.in | 5 +++++ man/tio.1.txt | 4 ++++ src/configfile.c | 4 ++++ src/log.c | 13 +++++++++++-- src/options.c | 2 ++ src/options.h | 1 + 7 files changed, 28 insertions(+), 2 deletions(-) diff --git a/example/config b/example/config index 1b66d07..c208fba 100644 --- a/example/config +++ b/example/config @@ -22,6 +22,7 @@ no-autoconnect = disable hexadecimal = disable timestamp = disable log = disable +log-append = disable log-strip = disable local-echo = disable color = bold diff --git a/man/tio.1.in b/man/tio.1.in index 36118e5..ba2a9ad 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -141,6 +141,11 @@ The filename can be manually set using the \-\-log-file option. Set log filename. +.TP +.BR " \-\-log-append + +Append to log file. + .TP .BR " \-\-log-strip diff --git a/man/tio.1.txt b/man/tio.1.txt index 2aa04e6..252ca60 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -108,6 +108,10 @@ OPTIONS Set log filename. + --log-append + + Append to log file. + --log-strip Strip control characters and escape sequences from log. diff --git a/src/configfile.c b/src/configfile.c index d3dbd16..6715059 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -198,6 +198,10 @@ static int data_handler(void *user, const char *section, const char *name, asprintf(&c.log_filename, "%s", value); option.log_filename = c.log_filename; } + else if (!strcmp(name, "log-append")) + { + option.log_append = read_boolean(value, name); + } else if (!strcmp(name, "log-strip")) { option.log_strip = read_boolean(value, name); diff --git a/src/log.c b/src/log.c index d0cf543..de3be86 100644 --- a/src/log.c +++ b/src/log.c @@ -69,8 +69,17 @@ int log_open(const char *filename) log_filename = filename; - // Open log file in append write mode - fp = fopen(filename, "a+"); + // Open log file + if (option.log_append) + { + // Appends to existing log file + fp = fopen(filename, "a+"); + } + else + { + // Truncates existing log file + fp = fopen(filename, "w+"); + } if (fp == NULL) { tio_warning_printf("Could not open log file %s (%s)", filename, strerror(errno)); diff --git a/src/options.c b/src/options.c index eb75144..6217cc4 100644 --- a/src/options.c +++ b/src/options.c @@ -74,6 +74,7 @@ struct option_t option = .ri_pulse_duration = 100, .no_autoconnect = false, .log = false, + .log_append = false, .log_filename = NULL, .log_strip = false, .local_echo = false, @@ -119,6 +120,7 @@ void print_help(char *argv[]) printf(" -L, --list-devices List available serial devices by ID\n"); printf(" -l, --log Enable log to file\n"); printf(" --log-file Set log filename\n"); + printf(" --log-append Append to log file\n"); printf(" --log-strip Strip control characters and escape sequences\n"); printf(" -m, --map Map characters\n"); printf(" -c, --color 0..255|bold|none|list Colorize tio text (default: bold)\n"); diff --git a/src/options.h b/src/options.h index 8ae556d..b18d8ff 100644 --- a/src/options.h +++ b/src/options.h @@ -48,6 +48,7 @@ struct option_t unsigned int ri_pulse_duration; bool no_autoconnect; bool log; + bool log_append; bool log_strip; bool local_echo; enum timestamp_t timestamp; From d3bd5d8e172a7b17603cfc19bae994530e584c35 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 9 Jun 2023 20:13:39 +0200 Subject: [PATCH 160/506] Remove warning when using pattern option --- src/configfile.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/configfile.c b/src/configfile.c index 6715059..387063b 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -289,6 +289,10 @@ static int data_handler(void *user, const char *section, const char *name, { option.mute = read_boolean(value, name); } + else if (!strcmp(name, "pattern")) + { + // Do nothing + } else { tio_warning_printf("Unknown option '%s' in configuration file, ignored", name); From 88ef473362c5a1ff0dcd389fd5b1d4d6bf202472 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 10 Jun 2023 13:02:32 +0200 Subject: [PATCH 161/506] Update NEWS --- NEWS | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 8162647..7d67de2 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,60 @@ -=== tio v2.5 === +=== tio v2.6 === + + + +Changes since tio v2.5: + + * Remove warning when using pattern option + + * Add --log-append option + + Add --log-append option which makes tio append to any existing log file. + + This also changes the default behaviour of tio from appending to + overwriting any existing log file. Now you have to use this new option + to make tio append. + + * Update man page + + * Update README + + * Fix line termination for response wait feature + + The response wait feature waited for a line response, a string + terminated with either CR or NL. However, some devices may send a CR and + then their line content and then NL. This means tio will quit before + receiving and printing the line response. To solve this we simply ignore + the CR character and only consider lines terminated with a NL character. + + This should work for all devices as lines are AFAIK always terminated + with either CRNL or a NL. + + * Update tty device listing configuration + + Cleanup and add FreeBSD tty device listing support. + +Braden Young: + + * Move map variables to tty to keep them all in one spot + + * Configure socket mapping flags from tty parsing logic. Remove duplicate parsing logic in socket + + * Support input mapping modes for sockets + +Josh Soref: + + * Various spelling fixes + +Peter van Dijk: + + * avoid "warning: unused parameter" on setspeed stub + + * use right /dev/ path on Haiku + +Bill Hass: + + * Update README with details on snap confinement From d461751a7140491e4c284c78df860e2cc2c6c288 Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Thu, 13 Jul 2023 20:49:53 -0400 Subject: [PATCH 162/506] fix: support --log-append in cli options fixes: #199 --- src/options.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/options.c b/src/options.c index 6217cc4..9c0908c 100644 --- a/src/options.c +++ b/src/options.c @@ -46,6 +46,7 @@ enum opt_t OPT_TIMESTAMP_FORMAT, OPT_LOG_FILE, OPT_LOG_STRIP, + OPT_LOG_APPEND, OPT_LINE_PULSE_DURATION, OPT_RESPONSE_TIMEOUT, OPT_RS485, @@ -254,6 +255,7 @@ void options_parse(int argc, char *argv[]) {"log", no_argument, 0, 'l' }, {"log-file", required_argument, 0, OPT_LOG_FILE }, {"log-strip", no_argument, 0, OPT_LOG_STRIP }, + {"log-append", no_argument, 0, OPT_LOG_APPEND }, {"socket", required_argument, 0, 'S' }, {"map", required_argument, 0, 'm' }, {"color", required_argument, 0, 'c' }, @@ -357,6 +359,10 @@ void options_parse(int argc, char *argv[]) option.log_strip = true; break; + case OPT_LOG_APPEND: + option.log_append = true; + break; + case 'S': option.socket = optarg; break; From 5656381cc3e984a4307fa9fe5970ceeac3e5948d Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Thu, 13 Jul 2023 20:58:01 -0400 Subject: [PATCH 163/506] chore: update readme, bash completion, man page --- README.md | 1 + man/tio.1.in | 2 ++ man/tio.1.txt | 2 ++ src/bash-completion/tio.in | 5 +++++ 4 files changed, 10 insertions(+) diff --git a/README.md b/README.md index c935cf9..0d119d5 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,7 @@ The command-line interface is straightforward as reflected in the output from -L, --list-devices List available serial devices -l, --log Enable log to file --log-file Set log filename + --log-append Append to log file --log-strip Strip control characters and escape sequences -m, --map Map characters -c, --color 0..255|bold|none|list Colorize tio text (default: bold) diff --git a/man/tio.1.in b/man/tio.1.in index ba2a9ad..a5f209e 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -390,6 +390,8 @@ Disable automatic connect Enable log to file .IP "\fBlog-file" Set log filename +.IP "\fBlog-append" +Append to log file .IP "\fBlog-strip" Enable strip of control and escape sequences from log .IP "\fBlocal-echo" diff --git a/man/tio.1.txt b/man/tio.1.txt index 252ca60..47318ac 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -305,6 +305,8 @@ CONFIGURATION FILE log-file Set log filename + log-append Append to log file + log-strip Enable strip of control and escape sequences from log local-echo Enable local echo diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index 9602c4f..abad324 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -22,6 +22,7 @@ _tio() -e --local-echo \ -l --log \ --log-file \ + --log-append \ --log-strip \ -m --map \ -t --timestamp \ @@ -90,6 +91,10 @@ _tio() COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 ;; + --log-append) + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + ;; --log-strip) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 From df5379bac5f8af8ce9d64f262565a21ee46ee53c Mon Sep 17 00:00:00 2001 From: Wes Koerber Date: Thu, 13 Jul 2023 20:58:20 -0400 Subject: [PATCH 164/506] chore: reorder log-strip and log-append reorder to maintain consistency with documentation --- src/options.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/options.c b/src/options.c index 9c0908c..e34c8d1 100644 --- a/src/options.c +++ b/src/options.c @@ -254,8 +254,8 @@ void options_parse(int argc, char *argv[]) {"list-devices", no_argument, 0, 'L' }, {"log", no_argument, 0, 'l' }, {"log-file", required_argument, 0, OPT_LOG_FILE }, - {"log-strip", no_argument, 0, OPT_LOG_STRIP }, {"log-append", no_argument, 0, OPT_LOG_APPEND }, + {"log-strip", no_argument, 0, OPT_LOG_STRIP }, {"socket", required_argument, 0, 'S' }, {"map", required_argument, 0, 'm' }, {"color", required_argument, 0, 'c' }, From 98052936b023f30f9772cc5f799dc6317d13b59f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 18 Aug 2023 14:20:14 +0200 Subject: [PATCH 165/506] Update TODO --- TODO | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/TODO b/TODO index 5a0bbf1..8bd7f3f 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,9 @@ - * Support for interaction using simple autoresponse strings +* Line mode feature + + Only send line when pressing enter. Maybe even add readline support so one + can use all the readline editing feature before sending the line. + +* Support for interaction using simple autoresponse strings Add support for simple autoresponse strings in the configuration file. For example: From 59cd3a1379bc01bd2446e529fb632511a3322ca3 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 20 Aug 2023 23:48:43 +0200 Subject: [PATCH 166/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0d119d5..7f57ab7 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![](https://img.shields.io/circleci/build/github/tio/tio)](https://circleci.com/github/tio/tio/tree/master) [![](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) -[![](https://img.shields.io/tokei/lines/github/tio/tio)](https://github.com/tio/tio) + ## 1. Introduction From 3c7c865e590f6d92f883a78c90fdaabe59225d3e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 7 Sep 2023 00:55:52 +0200 Subject: [PATCH 167/506] Update man page --- man/tio.1.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/man/tio.1.in b/man/tio.1.in index a5f209e..6b75422 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -309,6 +309,8 @@ Toggle hexadecimal mode 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 .IP "\fBctrl-t p" Pulse serial port line .IP "\fBctrl-t q" From bdca5a27ec017751c8b2ee7882f5632911f13015 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Sun, 10 Sep 2023 13:32:01 +0200 Subject: [PATCH 168/506] CYGWIN: Add support for "COM*" naming. --- src/options.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/options.c b/src/options.c index e34c8d1..4f08e6d 100644 --- a/src/options.c +++ b/src/options.c @@ -492,6 +492,19 @@ void options_parse_final(int argc, char *argv[]) optind = 1; // Reset option index to restart scanning of argv options_parse(argc, argv); +#ifdef __CYGWIN__ + unsigned char portnum; + char *tty_win; + if ( ((strncmp("COM", tty_device, 3) == 0) + || (strncmp("com", tty_device, 3) == 0) ) + && (sscanf(tty_device + 3, "%hhu", &portnum) == 1) + && (portnum > 0) ) + { + asprintf(&tty_win, "/dev/ttyS%hhu", portnum - 1); + tty_device = tty_win; + } +#endif + /* Restore tty device */ option.tty_device = tty_device; } From 8134cd3486f939c8520294e952f07a473e1048b6 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 10 Sep 2023 13:57:12 +0200 Subject: [PATCH 169/506] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 481dbe8..fe8ecd0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -45,5 +45,6 @@ Vyacheslav Patkov Bill Hass Peter van Dijk Braden Young +HiFiPhile Thanks to everyone who has contributed to this project. From 5c441f22c2e41d0db44ce9a81d748043feda15b8 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Wed, 13 Sep 2023 22:45:11 +0200 Subject: [PATCH 170/506] Ignore EINTR error. --- src/tty.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tty.c b/src/tty.c index 186fa77..5308c67 100644 --- a/src/tty.c +++ b/src/tty.c @@ -299,6 +299,11 @@ void *tty_stdin_input_thread(void *arg) byte_count = read(STDIN_FILENO, input_buffer, BUFSIZ); if (byte_count < 0) { + /* No error actually occurred */ + if (errno == EINTR) + { + continue; + } tio_warning_printf("Could not read from stdin (%s)", strerror(errno)); } else if (byte_count == 0) From d9dc1ff69849f8c299bd1d7b4477aacdc2c890cc Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Thu, 14 Sep 2023 00:06:46 +0200 Subject: [PATCH 171/506] tty_stdin_input_thread(): write to pipe only if byte_count > 0. --- src/tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tty.c b/src/tty.c index 5308c67..d6a38ed 100644 --- a/src/tty.c +++ b/src/tty.c @@ -349,7 +349,7 @@ void *tty_stdin_input_thread(void *arg) } // Write all bytes read to pipe - while (byte_count) + while (byte_count > 0) { bytes_written = write(pipefd[1], input_buffer, byte_count); if (bytes_written < 0) From e6ffbd9058f4a7930f3fdd0f1f8cca0df11c9e5e Mon Sep 17 00:00:00 2001 From: pnrhub <55432384+pnrhub@users.noreply.github.com> Date: Sat, 16 Sep 2023 12:17:38 +0200 Subject: [PATCH 172/506] Add xmodem and ymodem file send support (#208) * Add xmodem and ymodem file send support --------- Co-authored-by: pnr --- README.md | 2 + man/tio.1.in | 4 + src/meson.build | 1 + src/misc.h | 3 + src/tty.c | 53 +++++++++++- src/xymodem.c | 223 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 src/xymodem.c diff --git a/README.md b/README.md index 7f57ab7..dc70429 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,8 @@ ctrl-t ? to list the available key commands. [20:19:12.041] ctrl-t t Toggle line timestamp mode [20:19:12.041] ctrl-t U Toggle conversion to uppercase [20:19:12.041] ctrl-t v Show version +[20:19:12.041] ctrl-t x Send file using the XMODEM protocol +[20:19:12.041] ctrl-t y Send file using the YMODEM protocol [20:19:12.041] ctrl-t ctrl-t Send ctrl-t character ``` diff --git a/man/tio.1.in b/man/tio.1.in index 6b75422..b9cdf1a 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -323,6 +323,10 @@ Toggle line timestamp mode Toggle conversion to uppercase on output .IP "\fBctrl-t v" Show version +.IP "\fBctrl-t x" +Send a file using the XMODEM protocol (prompts for file name) +.IP "\fBctrl-t y" +Send a file using the YMODEM protocol (prompts for file name) .IP "\fBctrl-t ctrl-t" Send ctrl-t character diff --git a/src/meson.build b/src/meson.build index cec9429..73af4c7 100644 --- a/src/meson.build +++ b/src/meson.build @@ -18,6 +18,7 @@ tio_sources = [ 'rs485.c', 'timestamp.c', 'alert.c' + 'xymodem.c' ] tio_dep = [ diff --git a/src/misc.h b/src/misc.h index 6fe5368..d9a8e2b 100644 --- a/src/misc.h +++ b/src/misc.h @@ -29,3 +29,6 @@ long string_to_long(char *string); int ctrl_key_code(unsigned char key); void alert_connect(void); void alert_disconnect(void); + +extern char key_hit; +int xymodem_send(int sio, const char *filename, char mode); diff --git a/src/tty.c b/src/tty.c index d6a38ed..0f57d75 100644 --- a/src/tty.c +++ b/src/tty.c @@ -105,6 +105,8 @@ #define KEY_T 0x74 #define KEY_U 0x55 #define KEY_V 0x76 +#define KEY_X 0x78 +#define KEY_Y 0x79 #define KEY_Z 0x7a enum line_mode_t @@ -133,6 +135,8 @@ bool map_i_nl_cr = false; bool map_i_cr_nl = false; bool map_ign_cr = false; +char key_hit = 0xff; + 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; @@ -321,6 +325,14 @@ void *tty_stdin_input_thread(void *arg) // Process quit and flush key command for (int i = 0; i 0) { + if (*p == 0x08 || *p == 0x7f) { + if (p > line ) { + write(STDOUT_FILENO, "\b \b", 3); + p--; + } + continue; + } + write(STDOUT_FILENO, p, 1); + if (*p == '\r') break; + p++; + } + } + *p = 0; + return (p - line); +} + void handle_command_sequence(char input_char, char *output_char, bool *forward) { char unused_char; @@ -562,7 +600,9 @@ 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 ctrl-%c Send ctrl-%c character", option.prefix_key, option.prefix_key, option.prefix_key); + tio_printf(" ctrl-%c x Send file via Xmodem-1K", 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; case KEY_SHIFT_L: @@ -721,6 +761,17 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf("tio v%s", VERSION); break; + case KEY_X: + case KEY_Y: + tio_printf("Send file with %cMODEM", toupper(input_char)); + tio_printf_raw("Enter file name: "); + if (tio_readln()) { + tio_printf("Sending file '%s'", line); + tio_printf("Press any key to abort transfer"); + tio_printf("%s", xymodem_send(fd, line, input_char) < 0 ? "Aborted" : "Done"); + } + break; + case KEY_Z: tio_printf_array(random_array); break; diff --git a/src/xymodem.c b/src/xymodem.c new file mode 100644 index 0000000..1dbeddc --- /dev/null +++ b/src/xymodem.c @@ -0,0 +1,223 @@ +/* + * Minimalistic implementation of the xmodem-1k and ymodem sender protocol. + * https://en.wikipedia.org/wiki/XMODEM + * https://en.wikipedia.org/wiki/YMODEM + * + * SPDX-License-Identifier: GPL-2.0-or-later OR MIT-0 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "misc.h" + +#define STX 0x02 +#define ACK 0x06 +#define NAK 0x15 +#define CAN 0x18 +#define EOT "\004" + +#define OK 0 +#define ERR (-1) + +#define min(a, b) ((a) < (b) ? (a) : (b)) + +struct xpacket { + uint8_t type; + uint8_t seq; + uint8_t nseq; + uint8_t data[1024]; + uint8_t crc_hi; + uint8_t crc_lo; +} __attribute__((packed)); + +/* See https://en.wikipedia.org/wiki/Computation_of_cyclic_redundancy_checks */ +static uint16_t crc16(const uint8_t *data, uint16_t size) +{ + uint16_t crc, s; + + for (crc = 0; size > 0; size--) { + s = *data++ ^ (crc >> 8); + s ^= (s >> 4); + crc = (crc << 8) ^ s ^ (s << 5) ^ (s << 12); + } + return crc; +} + +static int xmodem(int sio, const void *data, size_t len, int seq) +{ + struct xpacket packet; + const uint8_t *buf = data; + char resp = 0; + int rc, crc; + + /* Drain pending characters from serial line. Insist on the + * last drained character being 'C'. + */ + while(1) { + if (key_hit) + return -1; + if (read(sio, &resp, 1) < 0) { + if (errno == EWOULDBLOCK) { + if (resp == 'C') break; + if (resp == CAN) return ERR; + usleep(50000); + continue; + } + perror("Read sync from serial failed"); + return ERR; + } + } + + /* Always work with 1K packets */ + packet.seq = seq; + packet.type = STX; + + while (len) { + size_t sz, z = 0; + char *from, status; + + /* Build next packet, pad with 0 to full seq */ + z = min(len, sizeof(packet.data)); + memcpy(packet.data, buf, z); + memset(packet.data + z, 0, sizeof(packet.data) - z); + crc = crc16(packet.data, sizeof(packet.data)); + packet.crc_hi = crc >> 8; + packet.crc_lo = crc; + packet.nseq = 0xff - packet.seq; + + /* Send packet */ + from = (char *) &packet; + sz = sizeof(packet); + while (sz) { + if (key_hit) + return ERR; + if ((rc = write(sio, from, sz)) < 0 ) { + if (errno == EWOULDBLOCK) { + usleep(1000); + continue; + } + perror("Write packet to serial failed"); + return ERR; + } + from += rc; + sz -= rc; + } + + /* 'lrzsz' does not ACK ymodem's fin packet */ + if (seq == 0 && packet.data[0] == 0) resp = ACK; + + /* Read receiver response, timeout 1 s */ + for(int n=0; n < 20; n++) { + if (key_hit) + return ERR; + if (read(sio, &resp, 1) < 0) { + if (errno == EWOULDBLOCK) { + usleep(50000); + continue; + } + perror("Read ack/nak from serial failed"); + return ERR; + } + break; + } + + /* Update "progress bar" */ + switch (resp) { + case NAK: status = 'N'; break; + case ACK: status = '.'; break; + case 'C': status = 'C'; break; + case CAN: status = '!'; return ERR; + default: status = '?'; + } + write(STDOUT_FILENO, &status, 1); + + /* Move to next block after ACK */ + if (resp == ACK) { + packet.seq++; + len -= z; + buf += z; + } + } + + /* Send EOT at 1 Hz until ACK or CAN received */ + while (seq) { + if (key_hit) + return ERR; + if (write(sio, EOT, 1) < 0) { + perror("Write EOT to serial failed"); + return ERR; + } + write(STDOUT_FILENO, "|", 1); + usleep(1000000); /* 1 s timeout*/ + if (read(sio, &resp, 1) < 0) { + if (errno == EWOULDBLOCK) continue; + perror("Read from serial failed"); + return ERR; + } + if (resp == ACK || resp == CAN) { + write(STDOUT_FILENO, "\r\n", 2); + return (resp == ACK) ? OK : ERR; + } + } + return 0; /* not reached */ +} + +int xymodem_send(int sio, const char *filename, char mode) +{ + size_t len; + int rc, fd; + struct stat stat; + const uint8_t *buf; + + /* Open file, map into memory */ + fd = open(filename, O_RDONLY); + if (fd < 0) { + perror("Could not open file"); + return ERR; + } + fstat(fd, &stat); + len = stat.st_size; + buf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); + if (!buf) { + close(fd); + perror("Could not mmap file"); + return ERR; + } + + /* Do transfer */ + key_hit = 0; + if (mode == 'x') { + rc = xmodem(sio, buf, len, 1); + } + else { + /* Ymodem: hdr + file + fin */ + while(1) { + char hdr[128], *p; + p = stpcpy(hdr, filename) + 1; + p += sprintf(p, "%ld %lo %o", len, stat.st_mtime, stat.st_mode); + + rc = -1; + if (xmodem(sio, hdr, p - hdr, 0) < 0) break; /* hdr with metadata */ + if (xmodem(sio, buf, len, 1) < 0) break; /* xmodem file */ + if (xmodem(sio, "", 1, 0) < 0) break; /* empty hdr = fin */ + rc = 0; break; + } + } + key_hit = 0xff; + + /* Flush serial and release resources */ + tcflush(sio, TCIOFLUSH); + munmap((void *)buf, len); + close(fd); + return rc; +} From c9c5f03c10b90b71c767f19a21305a86d745bfc9 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 16 Sep 2023 12:20:34 +0200 Subject: [PATCH 173/506] Fix meson source listing --- src/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meson.build b/src/meson.build index 73af4c7..2b4a0fe 100644 --- a/src/meson.build +++ b/src/meson.build @@ -17,7 +17,7 @@ tio_sources = [ 'setspeed.c', 'rs485.c', 'timestamp.c', - 'alert.c' + 'alert.c', 'xymodem.c' ] From 07864a0e78c53ed466f8a687af0653de276417a9 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 16 Sep 2023 12:25:35 +0200 Subject: [PATCH 174/506] Increase line buffer size Just to make sure we accept very long filenames. --- src/tty.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/tty.c b/src/tty.c index 0f57d75..9a3dccd 100644 --- a/src/tty.c +++ b/src/tty.c @@ -157,6 +157,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[BUFSIZ]; static void optional_local_echo(char c) { @@ -484,18 +485,19 @@ static void toggle_line(const char *line_name, int mask, enum line_mode_t line_m } } -#define MAX_LINE 100 -static char line[MAX_LINE]; - static int tio_readln(void) { char *p = line; - + /* Read line, accept BS and DEL as rubout characters */ - for (p = line ; p < &line[MAX_LINE-1]; ) { - if (read(pipefd[0], p, 1) > 0) { - if (*p == 0x08 || *p == 0x7f) { - if (p > line ) { + for (p = line ; p < &line[BUFSIZ-1]; ) + { + if (read(pipefd[0], p, 1) > 0) + { + if (*p == 0x08 || *p == 0x7f) + { + if (p > line ) + { write(STDOUT_FILENO, "\b \b", 3); p--; } From cf6e8b963b8813607df88822c80774f029a190c1 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 16 Sep 2023 12:27:34 +0200 Subject: [PATCH 175/506] Clean up whitespaces --- src/xymodem.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/xymodem.c b/src/xymodem.c index 1dbeddc..3827776 100644 --- a/src/xymodem.c +++ b/src/xymodem.c @@ -4,7 +4,7 @@ * https://en.wikipedia.org/wiki/YMODEM * * SPDX-License-Identifier: GPL-2.0-or-later OR MIT-0 - * + * */ #include @@ -59,7 +59,7 @@ static int xmodem(int sio, const void *data, size_t len, int seq) const uint8_t *buf = data; char resp = 0; int rc, crc; - + /* Drain pending characters from serial line. Insist on the * last drained character being 'C'. */ @@ -112,7 +112,7 @@ static int xmodem(int sio, const void *data, size_t len, int seq) from += rc; sz -= rc; } - + /* 'lrzsz' does not ACK ymodem's fin packet */ if (seq == 0 && packet.data[0] == 0) resp = ACK; @@ -130,7 +130,7 @@ static int xmodem(int sio, const void *data, size_t len, int seq) } break; } - + /* Update "progress bar" */ switch (resp) { case NAK: status = 'N'; break; @@ -178,7 +178,7 @@ int xymodem_send(int sio, const char *filename, char mode) int rc, fd; struct stat stat; const uint8_t *buf; - + /* Open file, map into memory */ fd = open(filename, O_RDONLY); if (fd < 0) { @@ -214,7 +214,7 @@ int xymodem_send(int sio, const char *filename, char mode) } } key_hit = 0xff; - + /* Flush serial and release resources */ tcflush(sio, TCIOFLUSH); munmap((void *)buf, len); From 63dced047f1ba16109805bd80cec842a6b996ba6 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 16 Sep 2023 12:30:52 +0200 Subject: [PATCH 176/506] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index fe8ecd0..f545ad2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -46,5 +46,6 @@ Bill Hass Peter van Dijk Braden Young HiFiPhile +Paul Ruizendaal Thanks to everyone who has contributed to this project. From ed0386d2c4d2f4eb97ff3828f4a748fdba3ddd2c Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 16 Sep 2023 13:14:52 +0200 Subject: [PATCH 177/506] Re-adjust max line size So it stays within maximum size handled by xmodem. --- src/tty.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tty.c b/src/tty.c index 9a3dccd..12df23e 100644 --- a/src/tty.c +++ b/src/tty.c @@ -82,6 +82,8 @@ #define CMSPAR 010000000000 #endif +#define LINE_SIZE_MAX 1000 + #define KEY_0 0x30 #define KEY_1 0x31 #define KEY_2 0x32 @@ -157,7 +159,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[BUFSIZ]; +static char line[LINE_SIZE_MAX]; static void optional_local_echo(char c) { @@ -490,7 +492,7 @@ static int tio_readln(void) char *p = line; /* Read line, accept BS and DEL as rubout characters */ - for (p = line ; p < &line[BUFSIZ-1]; ) + for (p = line ; p < &line[LINE_SIZE_MAX-1]; ) { if (read(pipefd[0], p, 1) > 0) { From 93e49ab5a23b40bd27e60a1028592ac23e4433f1 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 16 Sep 2023 14:13:25 +0200 Subject: [PATCH 178/506] Make quit hint more explicit To minimize confusion for new users. --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index ddf8848..97264cf 100644 --- a/src/main.c +++ b/src/main.c @@ -107,7 +107,7 @@ int main(int argc, char *argv[]) tio_printf("tio v%s", VERSION); if (interactive_mode) { - tio_printf("Press ctrl-%c q to quit", option.prefix_key); + tio_printf("Press then to quit", option.prefix_key); } else { tio_printf("Non-interactive mode enabled"); From a42f3f78d196b26183a979a9241ee5290a78942d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 16 Sep 2023 14:50:41 +0200 Subject: [PATCH 179/506] Overwrite old stale letters on xmodem filename input When entering a file name, eg. 'test' it whould output the following with 2 stale letters of the former input string: [14:41:58.987] Send file with XMODEM [14:42:08.015] Sending file 'test'st [14:42:08.015] Press any key to abort transfer To avoid this we simply overwrite the 2 stale letters with whitespaces. --- src/tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tty.c b/src/tty.c index 12df23e..027e795 100644 --- a/src/tty.c +++ b/src/tty.c @@ -770,7 +770,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf("Send file with %cMODEM", toupper(input_char)); tio_printf_raw("Enter file name: "); if (tio_readln()) { - tio_printf("Sending file '%s'", line); + tio_printf("Sending file '%s' ", line); tio_printf("Press any key to abort transfer"); tio_printf("%s", xymodem_send(fd, line, input_char) < 0 ? "Aborted" : "Done"); } From 838c1108766aac4d3285844043e0518197b86d6d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 16 Sep 2023 15:03:38 +0200 Subject: [PATCH 180/506] Increase header size for ymodem --- src/xymodem.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/xymodem.c b/src/xymodem.c index 3827776..53ec37d 100644 --- a/src/xymodem.c +++ b/src/xymodem.c @@ -202,11 +202,13 @@ int xymodem_send(int sio, const char *filename, char mode) else { /* Ymodem: hdr + file + fin */ while(1) { - char hdr[128], *p; + char hdr[1024], *p; + + rc = -1; + if (strlen(filename) > 977) break; /* hdr block overrun */ p = stpcpy(hdr, filename) + 1; p += sprintf(p, "%ld %lo %o", len, stat.st_mtime, stat.st_mode); - rc = -1; if (xmodem(sio, hdr, p - hdr, 0) < 0) break; /* hdr with metadata */ if (xmodem(sio, buf, len, 1) < 0) break; /* xmodem file */ if (xmodem(sio, "", 1, 0) < 0) break; /* empty hdr = fin */ From c93922fd36ed1a010c76fa03c7076f082bb3378b Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 16 Sep 2023 16:10:30 +0200 Subject: [PATCH 181/506] Update README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dc70429..b0758f7 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ when used in combination with [tmux](https://tmux.github.io). * Sensible defaults (115200 8n1) * Support for non-standard baud rates * Support for RS-485 mode + * X-modem (1K) and Y-modem file upload * Support for mark and space parity * List available serial devices by ID * Show RX/TX statistics From 02b60e9fb32ebc94f335f9adb110c3ce36aea44e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 19 Sep 2023 21:21:59 +0200 Subject: [PATCH 182/506] Revert "Make quit hint more explicit" This reverts commit 93e49ab5a23b40bd27e60a1028592ac23e4433f1. --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 97264cf..ddf8848 100644 --- a/src/main.c +++ b/src/main.c @@ -107,7 +107,7 @@ int main(int argc, char *argv[]) tio_printf("tio v%s", VERSION); if (interactive_mode) { - tio_printf("Press then to quit", option.prefix_key); + tio_printf("Press ctrl-%c q to quit", option.prefix_key); } else { tio_printf("Non-interactive mode enabled"); From 3d7d9c85b5bb59b64f44f44bc5a8919a40d8604d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 19 Sep 2023 21:37:05 +0200 Subject: [PATCH 183/506] Bump version --- meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 1ed012e..5295d0d 100644 --- a/meson.build +++ b/meson.build @@ -1,12 +1,12 @@ project('tio', 'c', - version : '2.6', + version : '2.7', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] ) # The tag date of the project_version(), update when the version bumps. -version_date = '2022-12-17' +version_date = '2023-09-19' # Test for dynamic baudrate configuration interface compiler = meson.get_compiler('c') From 4307e8176010708876e280d528c6ccc27a5d9c95 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 19 Sep 2023 21:47:55 +0200 Subject: [PATCH 184/506] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index f545ad2..c9684ea 100644 --- a/AUTHORS +++ b/AUTHORS @@ -45,6 +45,7 @@ Vyacheslav Patkov Bill Hass Peter van Dijk Braden Young +Wes Koerber HiFiPhile Paul Ruizendaal From 1c32555c2a4f26b60f94757656825fc6684d6892 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 19 Sep 2023 21:48:58 +0200 Subject: [PATCH 185/506] Update NEWS --- NEWS | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 7d67de2..28a3d31 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,31 @@ -=== tio v2.6 === +=== tio v2.7 === + + + +Changes since tio v2.6: + +Paul Ruizendaal: + + * Add xmodem and ymodem file send support + +HiFiPhile: + + * tty_stdin_input_thread(): write to pipe only if byte_count > 0. + + * Ignore EINTR error. + + * CYGWIN: Add support for "COM*" naming. + +Wes Koerber: + + * chore: reorder log-strip and log-append + + reorder to maintain consistency with documentation + + * chore: update readme, bash completion, man page + + * fix: support --log-append in cli options From 1777206de73aeba7d14e1f2297a0ca96fc791327 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 21 Sep 2023 08:47:34 +0200 Subject: [PATCH 186/506] Update plain text man page --- man/tio.1.txt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/man/tio.1.txt b/man/tio.1.txt index 47318ac..6db7fa4 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 simple serial device I/O tool @@ -241,6 +241,8 @@ KEYS ctrl-t L Show line states (DTR, RTS, CTS, DSR, DCD, RI) + ctrl-t m Toggle MSB to LSB bit order + ctrl-t p Pulse serial port line ctrl-t q Quit @@ -253,6 +255,10 @@ KEYS ctrl-t v Show version + ctrl-t x Send a file using the XMODEM protocol (prompts for file name) + + ctrl-t y Send a file using the YMODEM protocol (prompts for file name) + ctrl-t ctrl-t Send ctrl-t character HEXADECIMAL MODE @@ -279,7 +285,8 @@ CONFIGURATION FILE The following configuration file options are available: - pattern Pattern matching user input. This pattern can be an extended regular expression with a single group. + pattern + Pattern matching user input. This pattern can be an extended regular expression with a single group. device TTY device to open. If it contains a "%s" it is substituted with the first group match. @@ -450,4 +457,4 @@ WEBSITE AUTHOR Created by Martin Lund . -tio 2.6 2022-12-17 tio(1) +tio 2.7 2023-09-19 tio(1) From 72399c4fe67be153bd3afa83cb41fe0e35b6d1e2 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Fri, 22 Sep 2023 11:53:37 +0200 Subject: [PATCH 187/506] CYGWIN: Fix port auto connection. (#211) --- src/tty.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/tty.c b/src/tty.c index 027e795..cdab4b9 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1576,6 +1576,13 @@ int tty_connect(void) } else if (status == -1) { +#if defined(__CYGWIN__) + // Happens when port unpluged + if (errno == EACCES) + { + goto error_read; + } +#endif tio_error_printf("select() failed (%s)", strerror(errno)); exit(EXIT_FAILURE); } From 7193e6f0c90aaed23ec6c52b8d8bb1ad6a02192c Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 22 Sep 2023 16:57:44 +0200 Subject: [PATCH 188/506] Bump version --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 5295d0d..3b807ef 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('tio', 'c', - version : '2.7', + version : '2.8', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] From 14f598e11e14cefaae8c767a32fe2565b8b4578a Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 28 Sep 2023 12:52:01 +0200 Subject: [PATCH 189/506] Update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b0758f7..456aaab 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,9 @@ when used in combination with [tmux](https://tmux.github.io). * Automatic connect and reconnect * Sensible defaults (115200 8n1) * Support for non-standard baud rates - * Support for RS-485 mode - * X-modem (1K) and Y-modem file upload * Support for mark and space parity + * X-modem (1K) and Y-modem file upload + * Support for RS-485 mode * List available serial devices by ID * Show RX/TX statistics * Toggle serial lines From ed66c72ca13c58639f92819426edfe9f74df73b4 Mon Sep 17 00:00:00 2001 From: Jakob Haufe Date: Fri, 8 Dec 2023 10:43:37 +0100 Subject: [PATCH 190/506] Fix troff warning (#216) .eo/.ec sections seemingly need explicit empty lines using .sp Otherwise, troff complains: troff::535: warning: expected numeric expression, got '\' troff::538: warning: expected numeric expression, got '\' troff::541: warning: expected numeric expression, got '\' --- man/tio.1.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index b9cdf1a..776340c 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -532,13 +532,13 @@ Or use the expect command to script an interaction: .nf .eo #!/usr/bin/expect -f - +.sp set timeout -1 log_user 0 - +.sp spawn nc -UN /tmp/tio-socket0 set uart $spawn_id - +.sp send -i $uart "date\n" expect -i $uart "prompt> " send -i $uart "ls -la\n" From bfefd04b5567ba2b5903fea99f35f4efdb1ba4f3 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 10 Dec 2023 21:43:11 +0100 Subject: [PATCH 191/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 456aaab..cd0e16c 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ when used in combination with [tmux](https://tmux.github.io). * Visual or audible alert on connect/disconnect * Remapping of prefix key * Man page documentation - * Binary size less than 80kB + * Binary size less than 90kB * Plays nicely with [tmux](https://tmux.github.io) ## 3. Usage From e572255fd2cac3b0c3bbade9551b201b28c39ca6 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 11 Jan 2024 20:36:17 +0100 Subject: [PATCH 192/506] Fix file descriptor handling on MacOS --- src/tty.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/tty.c b/src/tty.c index cdab4b9..49024cd 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1582,9 +1582,15 @@ int tty_connect(void) { goto error_read; } -#endif +#elif defined(__APPLE__) + if (errno == EBADF) + { + goto error_read; + } +#else tio_error_printf("select() failed (%s)", strerror(errno)); exit(EXIT_FAILURE); +#endif } else { From 4269ec835dff1d6abc807f27909b74e04851e1ad Mon Sep 17 00:00:00 2001 From: Sylvain LAFRASSE Date: Fri, 12 Jan 2024 12:11:24 +0100 Subject: [PATCH 193/506] Fix double call of tty_disconnect() on macOS/Darwin. --- src/tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tty.c b/src/tty.c index 49024cd..a153f0c 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1585,7 +1585,7 @@ int tty_connect(void) #elif defined(__APPLE__) if (errno == EBADF) { - goto error_read; + break; // tty_disconnect() will be naturally triggered by atexit() } #else tio_error_printf("select() failed (%s)", strerror(errno)); From 553b67e4064ef03d72612e2382f1018e124b3c4a Mon Sep 17 00:00:00 2001 From: Brian <89487381+b4yuan@users.noreply.github.com> Date: Tue, 6 Feb 2024 18:31:35 -0500 Subject: [PATCH 194/506] Add CodeQL Workflow for Code Security Analysis (#220) * Add CodeQL Workflow for Code Security Analysis Add CodeQL Workflow for Code Security Analysis This pull request introduces a CodeQL workflow to enhance the security analysis of our repository. CodeQL is a powerful static analysis tool that helps identify and mitigate security vulnerabilities in our codebase. By integrating this workflow into our GitHub Actions, we can proactively identify and address potential issues before they become security threats. We added a new CodeQL workflow file (.github/workflows/codeql.yml) that - Runs on every push and pull request to the main branch. - Excludes queries with a high false positive rate or low-severity findings. - Does not display results for third-party code, focusing only on our own codebase. Testing: To validate the functionality of this workflow, we have run several test scans on the codebase and reviewed the results. The workflow successfully compiles the project, identifies issues, and provides actionable insights while reducing noise by excluding certain queries and third-party code. Deployment: Once this pull request is merged, the CodeQL workflow will be active and automatically run on every push and pull request to the main branch. To view the results of these code scans, please follow these steps: 1. Under the repository name, click on the Security tab. 2. In the left sidebar, click Code scanning alerts. Additional Information: - You can further customize the workflow to adapt to your specific needs by modifying the workflow file. - For more information on CodeQL and how to interpret its results, refer to the GitHub documentation and the CodeQL documentation. Signed-off-by: Brian * Add CodeQL Workflow for Code Security Analysis Add CodeQL Workflow for Code Security Analysis This pull request introduces a CodeQL workflow to enhance the security analysis of our repository. CodeQL is a powerful static analysis tool that helps identify and mitigate security vulnerabilities in our codebase. By integrating this workflow into our GitHub Actions, we can proactively identify and address potential issues before they become security threats. We added a new CodeQL workflow file (.github/workflows/codeql.yml) that - Runs on every pull request (functionality to run on every push to main branches is included as a comment for convenience). - Runs daily. - Excludes queries with a high false positive rate or low-severity findings. - Does not display results for git submodules, focusing only on our own codebase. Testing: To validate the functionality of this workflow, we have run several test scans on the codebase and reviewed the results. The workflow successfully compiles the project, identifies issues, and provides actionable insights while reducing noise by excluding certain queries and third-party code. Deployment: Once this pull request is merged, the CodeQL workflow will be active and automatically run on every push and pull request to the main branch. To view the results of these code scans, please follow these steps: 1. Under the repository name, click on the Security tab. 2. In the left sidebar, click Code scanning alerts. Additional Information: - You can further customize the workflow to adapt to your specific needs by modifying the workflow file. - For more information on CodeQL and how to interpret its results, refer to the GitHub documentation and the CodeQL documentation (https://codeql.github.com/ and https://codeql.github.com/docs/). Signed-off-by: Brian * Add CodeQL Workflow for Code Security Analysis Add CodeQL Workflow for Code Security Analysis This pull request introduces a CodeQL workflow to enhance the security analysis of our repository. CodeQL is a powerful static analysis tool that helps identify and mitigate security vulnerabilities in our codebase. By integrating this workflow into our GitHub Actions, we can proactively identify and address potential issues before they become security threats. We added a new CodeQL workflow file (.github/workflows/codeql.yml) that - Runs on every pull request (functionality to run on every push to main branches is included as a comment for convenience). - Runs daily. - Excludes queries with a high false positive rate or low-severity findings. - Does not display results for git submodules, focusing only on our own codebase. Testing: To validate the functionality of this workflow, we have run several test scans on the codebase and reviewed the results. The workflow successfully compiles the project, identifies issues, and provides actionable insights while reducing noise by excluding certain queries and third-party code. Deployment: Once this pull request is merged, the CodeQL workflow will be active and automatically run on every push and pull request to the main branch. To view the results of these code scans, please follow these steps: 1. Under the repository name, click on the Security tab. 2. In the left sidebar, click Code scanning alerts. Additional Information: - You can further customize the workflow to adapt to your specific needs by modifying the workflow file. - For more information on CodeQL and how to interpret its results, refer to the GitHub documentation and the CodeQL documentation (https://codeql.github.com/ and https://codeql.github.com/docs/). Signed-off-by: Brian * Add CodeQL Workflow for Code Security Analysis Add CodeQL Workflow for Code Security Analysis This pull request introduces a CodeQL workflow to enhance the security analysis of our repository. CodeQL is a powerful static analysis tool that helps identify and mitigate security vulnerabilities in our codebase. By integrating this workflow into our GitHub Actions, we can proactively identify and address potential issues before they become security threats. We added a new CodeQL workflow file (.github/workflows/codeql.yml) that - Runs on every pull request (functionality to run on every push to main branches is included as a comment for convenience). - Runs daily. - Excludes queries with a high false positive rate or low-severity findings. - Does not display results for git submodules, focusing only on our own codebase. Testing: To validate the functionality of this workflow, we have run several test scans on the codebase and reviewed the results. The workflow successfully compiles the project, identifies issues, and provides actionable insights while reducing noise by excluding certain queries and third-party code. Deployment: Once this pull request is merged, the CodeQL workflow will be active and automatically run on every push and pull request to the main branch. To view the results of these code scans, please follow these steps: 1. Under the repository name, click on the Security tab. 2. In the left sidebar, click Code scanning alerts. Additional Information: - You can further customize the workflow to adapt to your specific needs by modifying the workflow file. - For more information on CodeQL and how to interpret its results, refer to the GitHub documentation and the CodeQL documentation (https://codeql.github.com/ and https://codeql.github.com/docs/). Signed-off-by: Brian * Update codeql-buildscript.sh --------- Signed-off-by: Brian --- .github/workflows/codeql-buildscript.sh | 6 ++ .github/workflows/codeql.yml | 126 ++++++++++++++++++++++++ .github/workflows/fail_on_error.py | 34 +++++++ 3 files changed, 166 insertions(+) create mode 100644 .github/workflows/codeql-buildscript.sh create mode 100644 .github/workflows/codeql.yml create mode 100755 .github/workflows/fail_on_error.py diff --git a/.github/workflows/codeql-buildscript.sh b/.github/workflows/codeql-buildscript.sh new file mode 100644 index 0000000..c75b03d --- /dev/null +++ b/.github/workflows/codeql-buildscript.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +pip3 install meson -U +pip3 install ninja -U +meson setup build +meson compile -C build diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..8164922 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,126 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + # push: + # branches: [ "main", "master" ] + schedule: + - cron: '0 0 * * *' + pull_request: + branches: '*' + +jobs: + analyze: + name: Analyze + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - 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' }} + timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'cpp' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ] + # Use only 'java' to analyze code written in Java, Kotlin or both + # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + with: + submodules: recursive + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + queries: security-and-quality + + + # 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 + + # â„šī¸ 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 + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + - run: | + ./.github/workflows/codeql-buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" + upload: false + id: step1 + + # Filter out rules with low severity or high false positve rate + # Also filter out warnings in third-party code + - name: Filter out unwanted errors and warnings + uses: advanced-security/filter-sarif@v1 + with: + patterns: | + -**:cpp/path-injection + -**:cpp/world-writable-file-creation + -**:cpp/poorly-documented-function + -**:cpp/potentially-dangerous-function + -**:cpp/use-of-goto + -**:cpp/integer-multiplication-cast-to-long + -**:cpp/comparison-with-wider-type + -**:cpp/leap-year/* + -**:cpp/ambiguously-signed-bit-field + -**:cpp/suspicious-pointer-scaling + -**:cpp/suspicious-pointer-scaling-void + -**:cpp/unsigned-comparison-zero + -**/cmake*/Modules/** + input: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif + output: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif + + - name: Upload CodeQL results to code scanning + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: ${{ steps.step1.outputs.sarif-output }} + category: "/language:${{matrix.language}}" + + - name: Upload CodeQL results as an artifact + if: success() || failure() + uses: actions/upload-artifact@v3 + with: + name: codeql-results + path: ${{ steps.step1.outputs.sarif-output }} + retention-days: 5 + + - name: Fail if an error is found + run: | + ./.github/workflows/fail_on_error.py \ + ${{ steps.step1.outputs.sarif-output }}/cpp.sarif diff --git a/.github/workflows/fail_on_error.py b/.github/workflows/fail_on_error.py new file mode 100755 index 0000000..2979174 --- /dev/null +++ b/.github/workflows/fail_on_error.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +import json +import sys + +# Return whether SARIF file contains error-level results +def codeql_sarif_contain_error(filename): + with open(filename, 'r') as f: + s = json.load(f) + + for run in s.get('runs', []): + rules_metadata = run['tool']['driver']['rules'] + if not rules_metadata: + rules_metadata = run['tool']['extensions'][0]['rules'] + + for res in run.get('results', []): + if 'ruleIndex' in res: + rule_index = res['ruleIndex'] + elif 'rule' in res and 'index' in res['rule']: + rule_index = res['rule']['index'] + else: + continue + try: + rule_level = rules_metadata[rule_index]['defaultConfiguration']['level'] + except IndexError as e: + print(e, rule_index, len(rules_metadata)) + else: + if rule_level == 'error': + return True + return False + +if __name__ == "__main__": + if codeql_sarif_contain_error(sys.argv[1]): + sys.exit(1) From 58c9489b9268b249a5f6456a798f362697ecf54f Mon Sep 17 00:00:00 2001 From: Fredrik Svedberg Date: Thu, 8 Feb 2024 15:37:20 +0100 Subject: [PATCH 195/506] Add map FF to ESC-c on input Added map of form feed to ESC-c on input for terminals that do not clear screen on ^L but do on ESC-c. --- man/tio.1.in | 2 ++ man/tio.1.txt | 2 ++ src/bash-completion/tio.in | 2 +- src/tty.c | 10 ++++++++++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/man/tio.1.in b/man/tio.1.in index 776340c..0576d0d 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -163,6 +163,8 @@ flags are supported: Map CR to NL on input (unless IGNCR is set) .IP "\fBIGNCR" Ignore CR on input +.IP "\fBIFFESCC" +Map FF to ESC-c on input .IP "\fBINLCR" Map NL to CR on input .IP "\fBINLCRNL" diff --git a/man/tio.1.txt b/man/tio.1.txt index 6db7fa4..3c1daa7 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -124,6 +124,8 @@ OPTIONS IGNCR Ignore CR on input + IFFESCC Map FF to ESC-c on input + INLCR Map NL to CR on input INLCRNL Map NL to CR-NL on input diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index abad324..908f7a3 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -100,7 +100,7 @@ _tio() return 0 ;; -m | --map) - COMPREPLY=( $(compgen -W "ICRNL IGNCR INLCR INLCRNL OCRNL ODELBS ONLCRNL MSB2LSB" -- ${cur}) ) + COMPREPLY=( $(compgen -W "ICRNL IGNCR INLCR IFFESCC INLCRNL OCRNL ODELBS ONLCRNL MSB2LSB" -- ${cur}) ) return 0 ;; -t | --timestamp) diff --git a/src/tty.c b/src/tty.c index a153f0c..e620f8f 100644 --- a/src/tty.c +++ b/src/tty.c @@ -145,6 +145,7 @@ static bool connected = false; static bool standard_baudrate = true; static void (*print)(char c); static int 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; @@ -1070,6 +1071,10 @@ void tty_configure(void) { map_o_del_bs = true; } + else if (strcmp(token,"IFFESCC") == 0) + { + map_i_ff_escc = true; + } else if (strcmp(token,"INLCRNL") == 0) { map_i_nl_crnl = true; @@ -1463,6 +1468,11 @@ int tty_connect(void) next_timestamp = true; } } + else if ((input_char == '\f') && (map_i_ff_escc) && (!map_o_msblsb)) + { + print('\e'); + print('c'); + } else { /* Print received tty character to stdout */ From 67640d4a003453760b8a42aefcb97c18becfda64 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 12 Feb 2024 22:25:07 +0100 Subject: [PATCH 196/506] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index c9684ea..23a3796 100644 --- a/AUTHORS +++ b/AUTHORS @@ -48,5 +48,6 @@ Braden Young Wes Koerber HiFiPhile Paul Ruizendaal +Fredrik Svedberg Thanks to everyone who has contributed to this project. From 6c520090c6b2145632875b5ab4337724096f3329 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 13 Feb 2024 13:49:19 +0100 Subject: [PATCH 197/506] Add meson man pages install option Defaults to installing man pages. --- meson.build | 6 +++++- meson_options.txt | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 3b807ef..39f2fc1 100644 --- a/meson.build +++ b/meson.build @@ -80,4 +80,8 @@ if host_machine.system() == 'linux' endif subdir('src') -subdir('man') + +install_man_pages = get_option('install_man_pages') +if install_man_pages + subdir('man') +endif diff --git a/meson_options.txt b/meson_options.txt index ddb4225..51b7ca0 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,3 +1,6 @@ option('bashcompletiondir', type : 'string', description : 'Directory for bash completion scripts ["no" disables]') +option('install_man_pages', + type : 'boolean', value: true, + description : 'Install man pages') From 593f9495f401724968e975fdffa9ca72f2c4757a Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 19 Feb 2024 16:46:20 +0100 Subject: [PATCH 198/506] Add support for disabling prefix key handling To disable prefix key input handing simply set prefix-ctrl-key to 'none'. Based on original patch from Sebastian Krahmer. --- man/tio.1.in | 2 +- src/configfile.c | 6 +++++- src/options.c | 1 + src/options.h | 1 + src/tty.c | 6 +++--- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 0576d0d..97ef873 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -417,7 +417,7 @@ Enable hexadecimal mode .IP "\fBsocket" Set socket to redirect I/O to .IP "\fBprefix-ctrl-key" -Set prefix ctrl key (a..z, default: t) +Set prefix ctrl key (a..z or 'none', default: t) .IP "\fBresponse-wait" Enable wait for line response .IP "\fBresponse-timeout" diff --git a/src/configfile.c b/src/configfile.c index 387063b..ef97f31 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -259,7 +259,11 @@ static int data_handler(void *user, const char *section, const char *name, } else if (!strcmp(name, "prefix-ctrl-key")) { - if (ctrl_key_code(value[0]) > 0) + if (!strcmp(value, "none")) + { + option.prefix_enabled = false; + } + else if (ctrl_key_code(value[0]) > 0) { option.prefix_code = ctrl_key_code(value[0]); option.prefix_key = value[0]; diff --git a/src/options.c b/src/options.c index 4f08e6d..8cfede9 100644 --- a/src/options.c +++ b/src/options.c @@ -86,6 +86,7 @@ struct option_t option = .hex_mode = false, .prefix_code = 20, // ctrl-t .prefix_key = 't', + .prefix_enabled = true, .response_wait = false, .response_timeout = 100, .mute = false, diff --git a/src/options.h b/src/options.h index b18d8ff..f1f2f73 100644 --- a/src/options.h +++ b/src/options.h @@ -59,6 +59,7 @@ struct option_t bool hex_mode; unsigned char prefix_code; unsigned char prefix_key; + bool prefix_enabled; bool response_wait; int response_timeout; bool mute; diff --git a/src/tty.c b/src/tty.c index e620f8f..abf79e9 100644 --- a/src/tty.c +++ b/src/tty.c @@ -339,7 +339,7 @@ void *tty_stdin_input_thread(void *arg) input_char = input_buffer[i]; - if (previous_char == option.prefix_code) + if (option.prefix_enabled && previous_char == option.prefix_code) { if (input_char == option.prefix_code) { @@ -569,7 +569,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) } /* Handle escape key commands */ - if (previous_char == option.prefix_code) + if (option.prefix_enabled && previous_char == option.prefix_code) { /* Do not forward input char to output by default */ *forward = false; @@ -1545,7 +1545,7 @@ int tty_connect(void) if (interactive_mode) { /* Do not forward prefix key */ - if (input_char == option.prefix_code) + if (option.prefix_enabled && input_char == option.prefix_code) { forward = false; } From 91459c2490411f40f554286ea35430568bbac3fc Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 19 Feb 2024 16:51:24 +0100 Subject: [PATCH 199/506] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 23a3796..3ea95ee 100644 --- a/AUTHORS +++ b/AUTHORS @@ -49,5 +49,6 @@ Wes Koerber HiFiPhile Paul Ruizendaal Fredrik Svedberg +Sebastian Thanks to everyone who has contributed to this project. From d45c9b1a227e118fb7a17276bbb3f1f0ae1b8902 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 1 Mar 2024 12:25:35 +0100 Subject: [PATCH 200/506] Update TODO --- TODO | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/TODO b/TODO index 8bd7f3f..9fc3626 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,31 @@ +* Add additional hex mode formats + + - 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| + 00000010 65 73 74 64 66 0a 61 0a 66 61 0a 66 0a 61 73 66 |estdf.a.fa.f.asf| + 00000020 64 61 64 73 66 61 73 66 64 61 73 64 66 61 64 73 |dadsfasfdasdfads| + 00000030 66 0a 61 73 64 66 61 64 73 66 61 73 64 66 61 73 |f.asdfadsfasdfas| + 00000040 64 66 0a 61 73 64 66 61 64 73 66 61 73 64 66 61 |df.asdfadsfasdfa| + 00000050 73 64 66 66 64 61 73 64 66 0a 0a 31 32 33 31 0a |sdffdasdf..1231.| + 00000060 65 32 31 64 73 77 65 64 0a 0a |e21dswed..| + 0000006a + + With configurable width output (4, 8, 16 bytes etc.). E.g. config file: + + hex-mode-width = 16 + + With and without timestamp support for each broken up line. + + * Add support for activity based time stamping in both normal and hex-mode. + + Will print a new timestamp if there is no input activity within the time + defined in the configuration file, e.g.: + + timestamp-timeout = 200 + + Time is in ms. + * Line mode feature Only send line when pressing enter. Maybe even add readline support so one From ed4ac0c797cd25b0b5d31a7654349e928b3eac58 Mon Sep 17 00:00:00 2001 From: Jakob Haufe Date: Wed, 6 Mar 2024 13:27:08 +0100 Subject: [PATCH 201/506] Support NO_COLOR env variable as per no-color.org --- src/options.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/options.c b/src/options.c index 8cfede9..6fd673d 100644 --- a/src/options.c +++ b/src/options.c @@ -236,6 +236,11 @@ void options_parse(int argc, char *argv[]) exit(EXIT_SUCCESS); } + // Support no-color.org informal spec + char *no_color = getenv("NO_COLOR"); + if(no_color != NULL && no_color[0] != '\0') + option.color = -1; + while (1) { static struct option long_options[] = From 6720da3b887043ee7ddf6c2abe304e2f898071f9 Mon Sep 17 00:00:00 2001 From: Mingjie Shen Date: Sat, 23 Mar 2024 22:26:43 -0400 Subject: [PATCH 202/506] Check return values of sscanf() Failing to check that a call to 'sscanf' actually writes to an output variable can lead to unexpected behavior at reading time. --- src/options.c | 6 +++++- src/rs485.c | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/options.c b/src/options.c index 6fd673d..88e2379 100644 --- a/src/options.c +++ b/src/options.c @@ -163,7 +163,11 @@ void line_pulse_duration_option_parse(const char *arg) { char keyname[11]; unsigned int value; - sscanf(token, "%10[^=]=%d", keyname, &value); + + if (sscanf(token, "%10[^=]=%d", keyname, &value) != 2) + { + token_found = false; + } if (!strcmp(keyname, "DTR")) { diff --git a/src/rs485.c b/src/rs485.c index e364b13..ee84b60 100644 --- a/src/rs485.c +++ b/src/rs485.c @@ -57,7 +57,11 @@ void rs485_parse_config(const char *arg) { char keyname[31]; unsigned int value; - sscanf(token, "%30[^=]=%d", keyname, &value); + + if (sscanf(token, "%30[^=]=%d", keyname, &value) != 2) + { + token_found = false; + } if (!strcmp(keyname, "RTS_ON_SEND")) { From 10255d52d034306ecf116ad849ceb0fc03342a49 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 24 Mar 2024 11:21:42 +0100 Subject: [PATCH 203/506] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 3ea95ee..84417f1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -50,5 +50,6 @@ HiFiPhile Paul Ruizendaal Fredrik Svedberg Sebastian +Mingjie Shen Thanks to everyone who has contributed to this project. From 6fee8514f425e4148577bc55878fbb28e41b7679 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 30 Mar 2024 16:31:45 +0100 Subject: [PATCH 204/506] Invert line states to reflect true electrical level --- src/tty.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/tty.c b/src/tty.c index abf79e9..e8ba150 100644 --- a/src/tty.c +++ b/src/tty.c @@ -440,12 +440,12 @@ static void toggle_line(const char *line_name, int mask, enum line_mode_t line_m if (state & mask) { state &= ~mask; - tio_printf("Setting %s to LOW", line_name); + tio_printf("Setting %s to HIGH", line_name); } else { state |= mask; - tio_printf("Setting %s to HIGH", line_name); + tio_printf("Setting %s to LOW", line_name); } if (ioctl(fd, TIOCMSET, &state) < 0) tio_warning_printf("Could not set line state (%s)", strerror(errno)); @@ -617,12 +617,12 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) break; } tio_printf("Line states:"); - tio_printf(" DTR: %s", (state & TIOCM_DTR) ? "HIGH" : "LOW"); - tio_printf(" RTS: %s", (state & TIOCM_RTS) ? "HIGH" : "LOW"); - tio_printf(" CTS: %s", (state & TIOCM_CTS) ? "HIGH" : "LOW"); - tio_printf(" DSR: %s", (state & TIOCM_DSR) ? "HIGH" : "LOW"); - tio_printf(" DCD: %s", (state & TIOCM_CD) ? "HIGH" : "LOW"); - tio_printf(" RI : %s", (state & TIOCM_RI) ? "HIGH" : "LOW"); + tio_printf(" DTR: %s", (state & TIOCM_DTR) ? "LOW" : "HIGH"); + tio_printf(" RTS: %s", (state & TIOCM_RTS) ? "LOW" : "HIGH"); + tio_printf(" CTS: %s", (state & TIOCM_CTS) ? "LOW" : "HIGH"); + tio_printf(" DSR: %s", (state & TIOCM_DSR) ? "LOW" : "HIGH"); + tio_printf(" DCD: %s", (state & TIOCM_CD) ? "LOW" : "HIGH"); + tio_printf(" RI : %s", (state & TIOCM_RI) ? "LOW" : "HIGH"); break; case KEY_F: From 0becfa3274c80cb8d18fed0325c776eb4bfdb1ad Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 1 Apr 2024 15:37:25 +0200 Subject: [PATCH 205/506] Add Lua scripting feature Add support for running Lua scripts that can manipulate the tty control 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 such as adding expect like functionality to easily and programatically interact with the connected device. --- example/config | 8 + example/control-lines-test.lua | 7 + man/tio.1.in | 59 ++++++- src/bash-completion/tio.in | 15 ++ src/configfile.c | 17 ++ src/meson.build | 17 +- src/options.c | 46 ++++++ src/options.h | 5 + src/script.c | 280 +++++++++++++++++++++++++++++++++ src/script.h | 32 ++++ src/tty.c | 171 ++++++++++++-------- src/tty.h | 5 + 12 files changed, 590 insertions(+), 72 deletions(-) create mode 100644 example/control-lines-test.lua create mode 100644 src/script.c create mode 100644 src/script.h diff --git a/example/config b/example/config index c208fba..2c01505 100644 --- a/example/config +++ b/example/config @@ -29,6 +29,7 @@ color = bold rs-485 = disable response-wait = disable alert = none +script-run = always # Sub-configurations @@ -63,3 +64,10 @@ device = /dev/ttyUSB0 rs-485 = enable rs-485-config = RTS_ON_SEND=1,RTS_AFTER_SEND=1,RTS_DELAY_BEFORE_SEND=60,RTS_DELAY_AFTER_SEND=80,RX_DURING_TX color = 13 + +[esp32] +device = /dev/ttyUSB0 +color = 14 +script = high(DTR); low(RTS); msleep(100); low(DTR); high(RTS); msleep(100); low(RTS) +script-run = always + diff --git a/example/control-lines-test.lua b/example/control-lines-test.lua new file mode 100644 index 0000000..9f0d493 --- /dev/null +++ b/example/control-lines-test.lua @@ -0,0 +1,7 @@ +high(DTR) +low(RTS) +msleep(100) +low(DTR) +high(RTS) +msleep(100) +low(RTS) diff --git a/man/tio.1.in b/man/tio.1.in index 97ef873..41138de 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -103,7 +103,7 @@ Enable local echo. Enable line timestamp. .TP -.BR " \-\-timestamp-format \fI +.BR " \-\-timestamp\-format \fI Set timestamp format to any of the following timestamp formats: .RS @@ -137,12 +137,12 @@ tio_DEVICE_YYYY-MM-DDTHH:MM:SS.log. The filename can be manually set using the \-\-log-file option. .TP -.BR " \-\-log-file \fI +.BR " \-\-log\-file \fI Set log filename. .TP -.BR " \-\-log-append +.BR " \-\-log\-append Append to log file. @@ -228,7 +228,7 @@ At present there is a hardcoded limit of 16 clients connected at one time. .RE .TP -.BR \-r ", " \-\-response-wait +.BR \-r ", " \-\-response\-wait Wait for line response then quit. A line is considered any string terminated with a NL character. If no line is received tio will quit after response @@ -279,6 +279,23 @@ will sound the bell twice or blink twice on disconnect. Default value is "none". +.TP +.BR "\-\-script \fI + +Run script from string. + +.TP +.BR "\-\-script\-file \fI + +Run script from file with filename. + +.TP +.BR "\-\-script\-run once|always|never" + +Run script on connect once, always, or never. + +Default value is "always". + .TP .BR \-v ", " \-\-version @@ -317,6 +334,8 @@ Toggle MSB to LSB bit order Pulse serial port line .IP "\fBctrl-t q" Quit +.IP "\fBctrl-t r" +Run script .IP "\fBctrl-t s" Show TX/RX statistics .IP "\fBctrl-t t" @@ -341,6 +360,26 @@ Bytes can be sent in this mode by typing the \fBtwo-character hexadecimal\fR representation of the value, e.g.: to send \fI0xA\fR you must type \fI0a\fR or \fI0A\fR. +.SH "SCRIPT API" +.PP +Tio suppots Lua scripting for manipulating the tty device. In addition to the +Lua API tio makes the following functions available: + +.TP 6n +.IP "\fBhigh(line)" +Set tty line high. +.IP "\fBlow(line)" +Set tty line low. +.IP "\fBtoggle(line)" +Toggle the tty line. +.IP "\fBsleep(seconds)" +Sleep for seconds. +.IP "\fBmsleep(ms)" +Sleep for miliseconds. + +.TP 0n +Note: Line can be any of DTR, RTS, CTS, DSR, CD, RI + .SH "CONFIGURATION FILE" .PP Options can be set via configuration file using the INI format. \fBtio\fR uses @@ -428,6 +467,12 @@ Enable RS-485 mode Set RS-485 configuration .IP "\fBalert" Set alert action on connect/disconnect +.IP "\fBscript" +Run script from string +.IP "\fBscript-file" +Run script from file +.IP "\fBscript-run" +Run script on connect. .SH "CONFIGURATION FILE EXAMPLES" @@ -582,6 +627,12 @@ Enable RS-485 mode: $ tio --rs-485 --rs-485-config=RTS_ON_SEND=1,RX_DURING_TX /dev/ttyUSB0 +.TP +Script manipulation of DTR and RTS lines upon first connect: + +$ tio --script "high(DTR); low(RTS); msleep(100); toggle(DTR)" --script-run once /dev/ttyUSB0 + + .SH "WEBSITE" .PP Visit https://tio.github.io diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index 908f7a3..6466db1 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -37,6 +37,9 @@ _tio() --rs-485-config \ --alert \ --mute \ + --script \ + --script-file \ + --script-run \ -v --version \ -h --help" @@ -151,6 +154,18 @@ _tio() COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 ;; + --script) + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + ;; + --script-file) + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + ;; + --script-run) + COMPREPLY=( $(compgen -W "once always never" -- ${cur}) ) + return 0 + ;; -v | --version) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 diff --git a/src/configfile.c b/src/configfile.c index ef97f31..2e50e05 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -60,6 +60,9 @@ struct config_t char *log_filename; char *socket; char *map; + char *script; + char *script_filename; + bool script_run; }; static struct config_t c; @@ -297,6 +300,20 @@ static int data_handler(void *user, const char *section, const char *name, { // Do nothing } + else if (!strcmp(name, "script")) + { + asprintf(&c.script, "%s", value); + option.script = c.script; + } + else if (!strcmp(name, "script-file")) + { + asprintf(&c.script_filename, "%s", value); + option.script_filename = c.script_filename; + } + else if (!strcmp(name, "script-run")) + { + option.script_run = script_run_option_parse(value); + } else { tio_warning_printf("Unknown option '%s' in configuration file, ignored", name); diff --git a/src/meson.build b/src/meson.build index 2b4a0fe..dddddd5 100644 --- a/src/meson.build +++ b/src/meson.build @@ -18,14 +18,27 @@ tio_sources = [ 'rs485.c', 'timestamp.c', 'alert.c', - 'xymodem.c' + 'xymodem.c', + 'script.c' ] + +foreach name: ['lua-5.4', 'lua-5.3', 'lua-5.2', 'lua-5.1', 'lua'] + lua_dep = dependency(name, version: '>=5.1', required: false) + if lua_dep.found() + break + endif +endforeach +if not lua_dep.found() + error('Lua could not be found!') +endif + tio_dep = [ dependency('threads', required: true), dependency('inih', required: true, fallback : ['libinih', 'inih_dep'], - default_options: ['default_library=static', 'distro_install=false']) + default_options: ['default_library=static', 'distro_install=false']), + lua_dep ] tio_c_args = ['-Wno-unused-result'] diff --git a/src/options.c b/src/options.c index 88e2379..1f70122 100644 --- a/src/options.c +++ b/src/options.c @@ -39,6 +39,7 @@ #include "timestamp.h" #include "alert.h" #include "log.h" +#include "script.h" enum opt_t { @@ -54,6 +55,9 @@ enum opt_t OPT_ALERT, OPT_COMPLETE_SUB_CONFIGS, OPT_MUTE, + OPT_SCRIPT, + OPT_SCRIPT_FILE, + OPT_SCRIPT_RUN, }; /* Default options */ @@ -96,6 +100,9 @@ struct option_t option = .rs485_delay_rts_after_send = -1, .alert = ALERT_NONE, .complete_sub_configs = false, + .script = NULL, + .script_filename = NULL, + .script_run = SCRIPT_RUN_ALWAYS, }; void print_help(char *argv[]) @@ -134,6 +141,9 @@ void print_help(char *argv[]) printf(" --rs-485-config Set RS-485 configuration\n"); printf(" --alert bell|blink|none Alert on connect/disconnect (default: none)\n"); printf(" --mute Mute tio\n"); + printf(" --script Run script from string\n"); + printf(" --script-file Run script from file\n"); + printf(" --script-run once|always|never Run script on connect (default: always)\n"); printf(" -v, --version Display version\n"); printf(" -h, --help Display help\n"); printf("\n"); @@ -202,6 +212,27 @@ void line_pulse_duration_option_parse(const char *arg) free(buffer); } +enum script_run_t script_run_option_parse(const char *arg) +{ + if (strcmp("once", arg) == 0) + { + return SCRIPT_RUN_ONCE; + } + else if (strcmp("always", arg) == 0) + { + return SCRIPT_RUN_ALWAYS; + } + else if (strcmp("never", arg) == 0) + { + return SCRIPT_RUN_NEVER; + } + else + { + tio_error_printf("Invalid script run option"); + exit(EXIT_FAILURE); + } +} + void options_print() { tio_printf(" Device: %s", option.tty_device); @@ -276,6 +307,9 @@ void options_parse(int argc, char *argv[]) {"rs-485-config", required_argument, 0, OPT_RS485_CONFIG }, {"alert", required_argument, 0, OPT_ALERT }, {"mute", no_argument, 0, OPT_MUTE }, + {"script", required_argument, 0, OPT_SCRIPT }, + {"script-file", required_argument, 0, OPT_SCRIPT_FILE }, + {"script-run", required_argument, 0, OPT_SCRIPT_RUN }, {"version", no_argument, 0, 'v' }, {"help", no_argument, 0, 'h' }, {"complete-sub-configs", no_argument, 0, OPT_COMPLETE_SUB_CONFIGS}, @@ -439,6 +473,18 @@ void options_parse(int argc, char *argv[]) option.mute = true; break; + case OPT_SCRIPT: + option.script = optarg; + break; + + case OPT_SCRIPT_FILE: + option.script_filename = optarg; + break; + + case OPT_SCRIPT_RUN: + option.script_run = script_run_option_parse(optarg); + break; + case 'v': printf("tio v%s\n", VERSION); exit(EXIT_SUCCESS); diff --git a/src/options.h b/src/options.h index f1f2f73..e909b98 100644 --- a/src/options.h +++ b/src/options.h @@ -26,6 +26,7 @@ #include #include #include +#include "script.h" #include "timestamp.h" #include "alert.h" @@ -69,6 +70,9 @@ struct option_t int32_t rs485_delay_rts_after_send; enum alert_t alert; bool complete_sub_configs; + const char *script; + const char *script_filename; + enum script_run_t script_run; }; extern struct option_t option; @@ -78,3 +82,4 @@ void options_parse(int argc, char *argv[]); void options_parse_final(int argc, char *argv[]); void line_pulse_duration_option_parse(const char *arg); +enum script_run_t script_run_option_parse(const char *arg); diff --git a/src/script.c b/src/script.c new file mode 100644 index 0000000..c736a50 --- /dev/null +++ b/src/script.c @@ -0,0 +1,280 @@ +/* + * tio - a simple serial terminal 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 +#include +#include +#include +#include +#include +#include +#include +#include "print.h" +#include "options.h" +#include "tty.h" + +static int serial_fd; + +// lua: sleep(seconds) +static int sleep_(lua_State *L) +{ + long seconds = lua_tointeger(L, 1); + + if (seconds < 0) + { + return 0; + } + + tio_printf("Sleeping %ld seconds", seconds); + + sleep(seconds); + + return 0; +} + +// lua: msleep(miliseconds) +static int msleep(lua_State *L) +{ + long mseconds = lua_tointeger(L, 1); + long useconds = mseconds * 1000; + + if (useconds < 0) + { + return 0; + } + + tio_printf("Sleeping %ld ms", mseconds); + usleep(useconds); + + return 0; +} + +static void script_line_set(int line, bool value) +{ + switch (line) + { + case TIOCM_DTR: + tty_line_set(serial_fd, "DTR", line, value); + break; + case TIOCM_RTS: + tty_line_set(serial_fd, "RTS", line, value); + break; + case TIOCM_CTS: + tty_line_set(serial_fd, "CTS", line, value); + break; + case TIOCM_DSR: + tty_line_set(serial_fd, "DSR", line, value); + break; + case TIOCM_CD: + tty_line_set(serial_fd, "CD", line, value); + break; + case TIOCM_RI: + tty_line_set(serial_fd, "RI", line, value); + break; + default: + break; + } +} + +static void script_line_toggle(int line) +{ + switch (line) + { + case TIOCM_DTR: + tty_line_toggle(serial_fd, "DTR", line); + break; + case TIOCM_RTS: + tty_line_toggle(serial_fd, "RTS", line); + break; + case TIOCM_CTS: + tty_line_toggle(serial_fd, "CTS", line); + break; + case TIOCM_DSR: + tty_line_toggle(serial_fd, "DSR", line); + break; + case TIOCM_CD: + tty_line_toggle(serial_fd, "CD", line); + break; + case TIOCM_RI: + tty_line_toggle(serial_fd, "RI", line); + break; + default: + break; + } +} + +// lua: high(line) +static int high(lua_State *L) +{ + long line = lua_tointeger(L, 1); + + if (line < 0) + { + return 0; + } + + script_line_set(line, LINE_HIGH); + + return 0; +} + +// lua: low(line) +static int low(lua_State *L) +{ + long line = lua_tointeger(L, 1); + + if (line < 0) + { + return 0; + } + + script_line_set(line, LINE_LOW); + + return 0; +} + +// lua: toggle(line) +static int toggle(lua_State *L) +{ + long line = lua_tointeger(L, 1); + + if (line < 0) + { + return 0; + } + + script_line_toggle(line); + + return 0; +} + +static void script_buffer_run(lua_State *L, const char *script_buffer) +{ + int error; + + error = luaL_loadbuffer(L, script_buffer, strlen(script_buffer), "tio") || + lua_pcall(L, 0, 0, 0); + if (error) + { + tio_warning_printf("%s\n", lua_tostring(L, -1)); + lua_pop(L, 1); /* pop error message from the stack */ + } +} + +static const struct luaL_Reg tio_lib[] = +{ + { "sleep", sleep_}, + { "msleep", msleep}, + { "high", high}, + { "low", low}, + { "toggle", toggle}, + {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 + +int lua_register_tio(lua_State *L) +{ + // Register lxi functions + lua_getglobal(L, "_G"); + luaL_setfuncs(L, tio_lib, 0); + lua_pop(L, 1); + + return 0; +} + +void script_file_run(lua_State *L, const char *filename) +{ + if (strlen(filename) == 0) + { + tio_warning_printf("Missing script filename\n"); + return; + } + + if (luaL_dofile(L, filename)) + { + tio_warning_printf("%s\n", lua_tostring(L, -1)); + lua_pop(L, 1); /* pop error message from the stack */ + return; + } +} + +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) +{ + script_set_global(L, "DTR", TIOCM_DTR); + script_set_global(L, "RTS", TIOCM_RTS); + script_set_global(L, "CTS", TIOCM_CTS); + script_set_global(L, "DSR", TIOCM_DSR); + script_set_global(L, "CD", TIOCM_CD); + script_set_global(L, "RI", TIOCM_RI); +} + +void script_run(int fd) +{ + lua_State *L; + + serial_fd = fd; + + L = luaL_newstate(); + luaL_openlibs(L); + + // Bind tio functions + lua_register_tio(L); + + // Initialize globals + script_set_globals(L); + + if (option.script_filename != NULL) + { + tio_printf("Running script %s", option.script_filename); + script_file_run(L, option.script_filename); + } + else if (option.script != NULL) + { + tio_printf("Running script"); + script_buffer_run(L, option.script); + } + + lua_close(L); +} diff --git a/src/script.h b/src/script.h new file mode 100644 index 0000000..0ea0faf --- /dev/null +++ b/src/script.h @@ -0,0 +1,32 @@ +/* + * tio - a simple serial terminal 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 + +enum script_run_t +{ + SCRIPT_RUN_ONCE, + SCRIPT_RUN_ALWAYS, + SCRIPT_RUN_NEVER, + SCRIPT_RUN_END, +}; + +void script_run(int fd); diff --git a/src/tty.c b/src/tty.c index e8ba150..6921d04 100644 --- a/src/tty.c +++ b/src/tty.c @@ -55,6 +55,7 @@ #include "alert.h" #include "timestamp.h" #include "misc.h" +#include "script.h" /* tty device listing configuration */ @@ -103,6 +104,7 @@ #define KEY_M 0x6D #define KEY_P 0x70 #define KEY_Q 0x71 +#define KEY_R 0x72 #define KEY_S 0x73 #define KEY_T 0x74 #define KEY_U 0x55 @@ -111,12 +113,12 @@ #define KEY_Y 0x79 #define KEY_Z 0x7a -enum line_mode_t +typedef enum { LINE_OFF, LINE_TOGGLE, LINE_PULSE -}; +} tty_line_mode_t; const char random_array[] = { @@ -424,67 +426,87 @@ static void output_hex(char c) } } -static void toggle_line(const char *line_name, int mask, enum line_mode_t line_mode) +void tty_line_set(int fd, const char *name, int mask, bool value) { int state; - if (line_mode == LINE_TOGGLE) + if (ioctl(fd, TIOCMGET, &state) < 0) { - // Toggle line - if (ioctl(fd, TIOCMGET, &state) < 0) - { - tio_warning_printf("Could not get line state (%s)", strerror(errno)); - } - else - { - if (state & mask) - { - state &= ~mask; - tio_printf("Setting %s to HIGH", line_name); - } - else - { - state |= mask; - tio_printf("Setting %s to LOW", line_name); - } - if (ioctl(fd, TIOCMSET, &state) < 0) - tio_warning_printf("Could not set line state (%s)", strerror(errno)); - } - } else if (line_mode == LINE_PULSE) + tio_warning_printf("Could not get line state (%s)", strerror(errno)); + return; + } + + if (value) { - int duration = 0; - // Pulse line - toggle_line(line_name, mask, LINE_TOGGLE); - switch (mask) - { - case TIOCM_DTR: - duration = option.dtr_pulse_duration; - break; - case TIOCM_RTS: - duration = option.rts_pulse_duration; - break; - case TIOCM_CTS: - duration = option.cts_pulse_duration; - break; - case TIOCM_DSR: - duration = option.dsr_pulse_duration; - break; - case TIOCM_CD: - duration = option.dcd_pulse_duration; - break; - case TIOCM_RI: - duration = option.ri_pulse_duration; - break; - default: - duration = 0; - break; - } - if (duration > 0) - { - tio_printf("Waiting %d ms", duration); - delay(duration); - } - toggle_line(line_name, mask, LINE_TOGGLE); + state &= ~mask; + tio_printf("Setting %s to HIGH", name); + } + else + { + state |= mask; + tio_printf("Setting %s to LOW", name); + } + + if (ioctl(fd, TIOCMSET, &state) < 0) + { + tio_warning_printf("Could not set line state (%s)", strerror(errno)); + } +} + +void tty_line_toggle(int fd, const char *line_name, int mask) +{ + int state; + + if (ioctl(fd, TIOCMGET, &state) < 0) + { + tio_warning_printf("Could not get line state (%s)", strerror(errno)); + return; + } + + if (state & mask) + { + state &= ~mask; + tio_printf("Setting %s to HIGH", line_name); + } + else + { + state |= mask; + tio_printf("Setting %s to LOW", line_name); + } + + if (ioctl(fd, TIOCMSET, &state) < 0) + { + tio_warning_printf("Could not set line state (%s)", strerror(errno)); + } +} + +static void tty_line_pulse(int fd, const char *line_name, int mask, unsigned int duration) +{ + tty_line_toggle(fd, line_name, mask); + + if (duration > 0) + { + tio_printf("Waiting %d ms", duration); + delay(duration); + } + + tty_line_toggle(fd, line_name, mask); +} + +static void tty_line_poke(int fd, const char *name, int mask, tty_line_mode_t mode, unsigned int duration) +{ + switch (mode) + { + case LINE_TOGGLE: + tty_line_toggle(fd, name, mask); + break; + + case LINE_PULSE: + tty_line_pulse(fd, name, mask, duration); + break; + + case LINE_OFF: + break; } } @@ -520,7 +542,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) char unused_char; bool unused_bool; int state; - static enum line_mode_t line_mode = LINE_OFF; + static tty_line_mode_t line_mode = LINE_OFF; static char previous_char = 0; /* Ignore unused arguments */ @@ -534,29 +556,29 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) forward = &unused_bool; } + // Handle tty line toggle and pulse action if (line_mode) { - // Handle line toggle number action *forward = false; switch (input_char) { case KEY_0: - toggle_line("DTR", TIOCM_DTR, line_mode); + tty_line_poke(fd, "DTR", TIOCM_DTR, line_mode, option.dtr_pulse_duration); break; case KEY_1: - toggle_line("RTS", TIOCM_RTS, line_mode); + tty_line_poke(fd, "RTS", TIOCM_RTS, line_mode, option.rts_pulse_duration); break; case KEY_2: - toggle_line("CTS", TIOCM_CTS, line_mode); + tty_line_poke(fd, "CTS", TIOCM_CTS, line_mode, option.cts_pulse_duration); break; case KEY_3: - toggle_line("DSR", TIOCM_DSR, line_mode); + tty_line_poke(fd, "DSR", TIOCM_DSR, line_mode, option.dsr_pulse_duration); break; case KEY_4: - toggle_line("DCD", TIOCM_CD, line_mode); + tty_line_poke(fd, "DCD", TIOCM_CD, line_mode, option.dcd_pulse_duration); break; case KEY_5: - toggle_line("RI", TIOCM_RI, line_mode); + tty_line_poke(fd, "RI", TIOCM_RI, line_mode, option.ri_pulse_duration); break; default: tio_warning_printf("Invalid line number"); @@ -601,6 +623,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf(" ctrl-%c m Toggle MSB to LSB bit order", option.prefix_key); 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 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); @@ -726,6 +749,11 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) /* Exit upon ctrl-t q sequence */ exit(EXIT_SUCCESS); + case KEY_R: + /* Run script */ + script_run(fd); + break; + case KEY_S: /* Show tx/rx statistics upon ctrl-t s sequence */ tio_printf("Statistics:"); @@ -1383,6 +1411,17 @@ int tty_connect(void) } } + /* Manage script activation */ + if (option.script_run != SCRIPT_RUN_NEVER) + { + script_run(fd); + + if (option.script_run == SCRIPT_RUN_ONCE) + { + option.script_run = SCRIPT_RUN_NEVER; + } + } + /* Input loop */ while (true) { diff --git a/src/tty.h b/src/tty.h index add8bf1..4c667ef 100644 --- a/src/tty.h +++ b/src/tty.h @@ -23,6 +23,9 @@ #include +#define LINE_HIGH true +#define LINE_LOW false + extern bool interactive_mode; extern bool map_i_nl_cr; extern bool map_i_cr_nl; @@ -36,3 +39,5 @@ void tty_wait_for_device(void); void list_serial_devices(void); void tty_input_thread_create(void); void tty_input_thread_wait_ready(void); +void tty_line_set(int fd, const char *name, int mask, bool value); +void tty_line_toggle(int fd, const char *name, int mask); From 00f57c99921740e4e3dbafa4636e4f366c3a5858 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 1 Apr 2024 15:55:03 +0200 Subject: [PATCH 206/506] Update circleCI --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 418ee82..8baeeb0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -15,7 +15,7 @@ jobs: steps: - checkout - run: sudo apt-get -qq update - - run: sudo apt-get install -y bash-completion git meson libinih-dev + - run: sudo apt-get install -y bash-completion git meson libinih-dev liblua5.2-dev - run: git clone https://github.com/tio/tio.git - run: cd tio && meson build --prefix $HOME/test/tio && ninja -C build install From 83f826349bbce456f471311595479aa58e0101a9 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 2 Apr 2024 03:52:54 +0200 Subject: [PATCH 207/506] Update codeql config --- .github/workflows/codeql-buildscript.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/codeql-buildscript.sh b/.github/workflows/codeql-buildscript.sh index c75b03d..4b9e11d 100644 --- a/.github/workflows/codeql-buildscript.sh +++ b/.github/workflows/codeql-buildscript.sh @@ -2,5 +2,6 @@ pip3 install meson -U pip3 install ninja -U +sudo apt-get install -y liblua5.2-dev meson setup build meson compile -C build From 70913fe120f1f8456647e00e921fc90af050d513 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 4 Apr 2024 12:31:39 +0200 Subject: [PATCH 208/506] Add --log-directory option For specifying directory path in which to save automatically named log files. --- man/tio.1.in | 9 ++++++++- src/bash-completion/tio.in | 5 +++++ src/log.c | 29 +++++++++++++++++++++++------ src/misc.c | 17 +++++++++++++++++ src/misc.h | 3 +++ src/options.c | 8 ++++++++ src/options.h | 1 + 7 files changed, 65 insertions(+), 7 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 41138de..501a260 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -131,7 +131,7 @@ List available serial devices by ID. Enable log to file. -The filename will be automatically generated using the following format +The log file will be automatically named using the following format tio_DEVICE_YYYY-MM-DDTHH:MM:SS.log. The filename can be manually set using the \-\-log-file option. @@ -141,6 +141,11 @@ The filename can be manually set using the \-\-log-file option. Set log filename. +.TP +.BR " \-\-log\-directory \fI + +Set log directory path in which to save automatically named log files. + .TP .BR " \-\-log\-append @@ -437,6 +442,8 @@ Disable automatic connect Enable log to file .IP "\fBlog-file" Set log filename +.IP "\fBlog-directory" +Set log directory path in which to save automatically named log files. .IP "\fBlog-append" Append to log file .IP "\fBlog-strip" diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index 6466db1..9bf1629 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -22,6 +22,7 @@ _tio() -e --local-echo \ -l --log \ --log-file \ + --log-directory \ --log-append \ --log-strip \ -m --map \ @@ -94,6 +95,10 @@ _tio() COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 ;; + --log-directory) + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + ;; --log-append) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 diff --git a/src/log.c b/src/log.c index de3be86..f940d44 100644 --- a/src/log.c +++ b/src/log.c @@ -33,6 +33,7 @@ #include "options.h" #include "print.h" #include "error.h" +#include "misc.h" #define IS_ESC_CSI_INTERMEDIATE_CHAR(c) ((c >= 0x20) && (c <= 0x3F)) #define IS_ESC_END_CHAR(c) ((c >= 0x30) && (c <= 0x7E)) @@ -58,13 +59,29 @@ static char *date_time(void) int log_open(const char *filename) { - static char automatic_filename[400]; + char *automatic_filename; + char *dir_plus_automatic_filename; if (filename == NULL) { // Generate filename if none provided ("tio_DEVICE_YYYY-MM-DDTHH:MM:SS.log") - sprintf(automatic_filename, "tio_%s_%s.log", basename((char *)option.tty_device), date_time()); - filename = automatic_filename; + asprintf(&automatic_filename, "tio_%s_%s.log", basename((char *)option.tty_device), date_time()); + + if (option.log_directory != NULL) + { + if (fs_dir_exists(option.log_directory) == false) + { + tio_error_printf("Log directory not found"); + exit(EXIT_FAILURE); + } + + asprintf(&dir_plus_automatic_filename, "%s/%s", option.log_directory, automatic_filename); + filename = dir_plus_automatic_filename; + } + else + { + filename = automatic_filename; + } } log_filename = filename; @@ -72,12 +89,12 @@ int log_open(const char *filename) // Open log file if (option.log_append) { - // Appends to existing log file + // Append to existing log file fp = fopen(filename, "a+"); } else { - // Truncates existing log file + // Truncate existing log file fp = fopen(filename, "w+"); } if (fp == NULL) @@ -202,7 +219,7 @@ void log_close(void) void log_exit(void) { - if (option.log) + if ((option.log) && (log_filename != NULL)) { tio_printf("Saved log to file %s", log_filename); log_close(); diff --git a/src/misc.c b/src/misc.c index 1983950..6770a28 100644 --- a/src/misc.c +++ b/src/misc.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include "error.h" @@ -70,3 +71,19 @@ int ctrl_key_code(unsigned char key) return -1; } + +bool fs_dir_exists(const char *path) +{ + struct stat st; + + if (stat(path, &st) != 0) + { + return false; + } + else if (!S_ISDIR(st.st_mode)) + { + return false; + } + + return true; +} diff --git a/src/misc.h b/src/misc.h index d9a8e2b..e949db7 100644 --- a/src/misc.h +++ b/src/misc.h @@ -21,6 +21,8 @@ #pragma once +#include + #define UNUSED(expr) do { (void)(expr); } while (0) char * current_time(void); @@ -29,6 +31,7 @@ long string_to_long(char *string); int ctrl_key_code(unsigned char key); void alert_connect(void); void alert_disconnect(void); +bool fs_dir_exists(const char *path); extern char key_hit; int xymodem_send(int sio, const char *filename, char mode); diff --git a/src/options.c b/src/options.c index 1f70122..510259e 100644 --- a/src/options.c +++ b/src/options.c @@ -46,6 +46,7 @@ enum opt_t OPT_NONE, OPT_TIMESTAMP_FORMAT, OPT_LOG_FILE, + OPT_LOG_DIRECTORY, OPT_LOG_STRIP, OPT_LOG_APPEND, OPT_LINE_PULSE_DURATION, @@ -81,6 +82,7 @@ struct option_t option = .log = false, .log_append = false, .log_filename = NULL, + .log_directory = NULL, .log_strip = false, .local_echo = false, .timestamp = TIMESTAMP_NONE, @@ -129,6 +131,7 @@ void print_help(char *argv[]) printf(" -L, --list-devices List available serial devices by ID\n"); printf(" -l, --log Enable log to file\n"); printf(" --log-file Set log filename\n"); + printf(" --log-directory Set log directory path (for automatic named logs)\n"); printf(" --log-append Append to log file\n"); printf(" --log-strip Strip control characters and escape sequences\n"); printf(" -m, --map Map characters\n"); @@ -295,6 +298,7 @@ void options_parse(int argc, char *argv[]) {"list-devices", no_argument, 0, 'L' }, {"log", no_argument, 0, 'l' }, {"log-file", required_argument, 0, OPT_LOG_FILE }, + {"log-directory", required_argument, 0, OPT_LOG_DIRECTORY }, {"log-append", no_argument, 0, OPT_LOG_APPEND }, {"log-strip", no_argument, 0, OPT_LOG_STRIP }, {"socket", required_argument, 0, 'S' }, @@ -399,6 +403,10 @@ void options_parse(int argc, char *argv[]) option.log_filename = optarg; break; + case OPT_LOG_DIRECTORY: + option.log_directory = optarg; + break; + case OPT_LOG_STRIP: option.log_strip = true; break; diff --git a/src/options.h b/src/options.h index e909b98..1cc9a60 100644 --- a/src/options.h +++ b/src/options.h @@ -54,6 +54,7 @@ struct option_t bool local_echo; enum timestamp_t timestamp; const char *log_filename; + const char *log_directory; const char *map; const char *socket; int color; From 4369d5b66f89fb26520f3ed20c9d7ee250d10259 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 5 Apr 2024 13:46:12 +0200 Subject: [PATCH 209/506] Add ONULBRK mapping flag Add ONULBRK mapping to map nul (zero) to send break signal on output. This is useful if one needs to e.g. send the break signal to the tty device when connected via socket. --- man/tio.1.in | 2 ++ src/tty.c | 14 +++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/man/tio.1.in b/man/tio.1.in index 501a260..d36c56a 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -182,6 +182,8 @@ Map DEL to BS on output Map NL to CR-NL on output .IP "\fBOLTU" Map lowercase characters to uppercase on output +.IP "\fBONULBRK" +Map nul (zero) to send break signal on output .IP "\fBMSB2LSB" Map MSB bit order to LSB on output .P diff --git a/src/tty.c b/src/tty.c index 6921d04..38bf1ef 100644 --- a/src/tty.c +++ b/src/tty.c @@ -153,6 +153,7 @@ 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; @@ -1115,6 +1116,10 @@ void tty_configure(void) { map_o_ltu = true; } + else if (strcmp(token, "ONULBRK") == 0) + { + map_o_nulbrk = true; + } else if (strcmp(token, "MSB2LSB") == 0) { map_o_msblsb = true; @@ -1289,7 +1294,14 @@ void forward_to_tty(int fd, char output_char) { /* Send output to tty device */ optional_local_echo(output_char); - status = tty_write(fd, &output_char, 1); + if ((output_char == 0) && (map_o_nulbrk)) + { + status = tcsendbreak(fd, 0); + } + else + { + status = tty_write(fd, &output_char, 1); + } if (status < 0) { tio_warning_printf("Could not write to tty device"); From 3e5019110729be65e4c8d83e543c456c3786c02d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 6 Apr 2024 09:17:01 +0200 Subject: [PATCH 210/506] 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 lines simultaneously. To help solve this timing issue we introduce a tty line state configuration API which can be used instead of using high()/low(). Using config_low(line) and config_high(line) one can set up a new line state configuration for multiple lines and then use config_apply() to finally apply the configuration. This will result in only one system call to instruct the serial port drive to switch all the configured line states which should help ensure that the lines are switched simultaneously. Example: script = config_high(DTR); config_low(RTS); config_apply() --- man/tio.1.in | 8 ++++ src/script.c | 104 +++++++++++++++++++----------------------- src/tty.c | 126 +++++++++++++++++++++++++++++++++++++++++++-------- src/tty.h | 6 ++- 4 files changed, 167 insertions(+), 77 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index d36c56a..3757f1b 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -383,6 +383,14 @@ Toggle the tty line. Sleep for seconds. .IP "\fBmsleep(ms)" Sleep for miliseconds. +.IP "\fBconfig_high(line)" +Set tty line state configuration to high. +.IP "\fBconfig_low(line)" +Set tty line state configuration to low. +.IP "\fBapply_config()" +Apply tty line state configuration. Using the line state configuration API +instead of high()/low() will help to make the lines physically switch as +simultaneously as possible. This may solve timing issues on some platforms. .TP 0n Note: Line can be any of DTR, RTS, CTS, DSR, CD, RI diff --git a/src/script.c b/src/script.c index c736a50..c06e93e 100644 --- a/src/script.c +++ b/src/script.c @@ -27,6 +27,7 @@ #include #include #include +#include "misc.h" #include "print.h" #include "options.h" #include "tty.h" @@ -67,60 +68,6 @@ static int msleep(lua_State *L) return 0; } -static void script_line_set(int line, bool value) -{ - switch (line) - { - case TIOCM_DTR: - tty_line_set(serial_fd, "DTR", line, value); - break; - case TIOCM_RTS: - tty_line_set(serial_fd, "RTS", line, value); - break; - case TIOCM_CTS: - tty_line_set(serial_fd, "CTS", line, value); - break; - case TIOCM_DSR: - tty_line_set(serial_fd, "DSR", line, value); - break; - case TIOCM_CD: - tty_line_set(serial_fd, "CD", line, value); - break; - case TIOCM_RI: - tty_line_set(serial_fd, "RI", line, value); - break; - default: - break; - } -} - -static void script_line_toggle(int line) -{ - switch (line) - { - case TIOCM_DTR: - tty_line_toggle(serial_fd, "DTR", line); - break; - case TIOCM_RTS: - tty_line_toggle(serial_fd, "RTS", line); - break; - case TIOCM_CTS: - tty_line_toggle(serial_fd, "CTS", line); - break; - case TIOCM_DSR: - tty_line_toggle(serial_fd, "DSR", line); - break; - case TIOCM_CD: - tty_line_toggle(serial_fd, "CD", line); - break; - case TIOCM_RI: - tty_line_toggle(serial_fd, "RI", line); - break; - default: - break; - } -} - // lua: high(line) static int high(lua_State *L) { @@ -131,7 +78,7 @@ static int high(lua_State *L) return 0; } - script_line_set(line, LINE_HIGH); + tty_line_set(serial_fd, line, LINE_HIGH); return 0; } @@ -146,7 +93,7 @@ static int low(lua_State *L) return 0; } - script_line_set(line, LINE_LOW); + tty_line_set(serial_fd, line, LINE_LOW); return 0; } @@ -161,7 +108,47 @@ static int toggle(lua_State *L) return 0; } - script_line_toggle(line); + tty_line_toggle(serial_fd, line); + + return 0; +} + +// lua: config_high(line) +static int config_high(lua_State *L) +{ + long line = lua_tointeger(L, 1); + + if (line < 0) + { + return 0; + } + + tty_line_config(line, true); + + return 0; +} + +// lua: config_low(line) +static int config_low(lua_State *L) +{ + long line = lua_tointeger(L, 1); + + if (line < 0) + { + return 0; + } + + tty_line_config(line, false); + + return 0; +} + +// lua: config_apply(line) +static int config_apply(lua_State *L) +{ + UNUSED(L); + + tty_line_config_apply(); return 0; } @@ -186,6 +173,9 @@ static const struct luaL_Reg tio_lib[] = { "high", high}, { "low", low}, { "toggle", toggle}, + { "config_high", config_high}, + { "config_low", config_low}, + { "config_apply", config_apply}, {NULL, NULL} }; diff --git a/src/tty.c b/src/tty.c index 38bf1ef..af36ae1 100644 --- a/src/tty.c +++ b/src/tty.c @@ -120,6 +120,13 @@ typedef enum LINE_PULSE } tty_line_mode_t; +typedef struct +{ + int mask; + bool value; + bool reserved; +} tty_line_config_t; + const char random_array[] = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x20, 0x28, 0x0A, 0x20, @@ -164,6 +171,7 @@ 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 tty_line_config_t line_config[6] = { }; static void optional_local_echo(char c) { @@ -427,7 +435,89 @@ static void output_hex(char c) } } -void tty_line_set(int fd, const char *name, int mask, bool value) +static const char *tty_line_name(int mask) +{ + switch (mask) + { + case TIOCM_DTR: + return "DTR"; + case TIOCM_RTS: + return "RTS"; + case TIOCM_CTS: + return "CTS"; + case TIOCM_DSR: + return "DSR"; + case TIOCM_CD: + return "CD"; + case TIOCM_RI: + return "RI"; + default: + return NULL; + } +} + +void tty_line_config(int mask, bool value) +{ + int i = 0; + + for (i=0; i<6; i++) + { + if ((line_config[i].mask == mask) || (line_config[i].reserved == false)) + { + line_config[i].mask = mask; + line_config[i].value = value; + line_config[i].reserved = true; + break; + } + } +} + +void tty_line_config_apply(void) +{ + int i = 0; + static int state; + + if (ioctl(fd, TIOCMGET, &state) < 0) + { + tio_warning_printf("Could not get line state (%s)", strerror(errno)); + return; + } + + for (i=0; i<6; i++) + { + if (line_config[i].reserved) + { + if (line_config[i].value) + { + // High + state &= ~line_config[i].mask; + tio_printf("Setting %s to HIGH", tty_line_name(line_config[i].mask)); + } + else + { + // Low + state |= line_config[i].mask; + tio_printf("Setting %s to LOW", tty_line_name(line_config[i].mask)); + } + + line_config[i].reserved = true; + } + } + + if (ioctl(fd, TIOCMSET, &state) < 0) + { + tio_warning_printf("Could not set line state configuration (%s)", strerror(errno)); + } + + // Reset configuration + for (i=0; i<6; i++) + { + line_config[i].reserved = false; + line_config[i].mask = -1; + } +} + +void tty_line_set(int fd, int mask, bool value) { int state; @@ -440,12 +530,12 @@ void tty_line_set(int fd, const char *name, int mask, bool value) if (value) { state &= ~mask; - tio_printf("Setting %s to HIGH", name); + tio_printf("Setting %s to HIGH", tty_line_name(mask)); } else { state |= mask; - tio_printf("Setting %s to LOW", name); + tio_printf("Setting %s to LOW", tty_line_name(mask)); } if (ioctl(fd, TIOCMSET, &state) < 0) @@ -454,7 +544,7 @@ void tty_line_set(int fd, const char *name, int mask, bool value) } } -void tty_line_toggle(int fd, const char *line_name, int mask) +void tty_line_toggle(int fd, int mask) { int state; @@ -467,12 +557,12 @@ void tty_line_toggle(int fd, const char *line_name, int mask) if (state & mask) { state &= ~mask; - tio_printf("Setting %s to HIGH", line_name); + tio_printf("Setting %s to HIGH", tty_line_name(mask)); } else { state |= mask; - tio_printf("Setting %s to LOW", line_name); + tio_printf("Setting %s to LOW", tty_line_name(mask)); } if (ioctl(fd, TIOCMSET, &state) < 0) @@ -481,9 +571,9 @@ void tty_line_toggle(int fd, const char *line_name, int mask) } } -static void tty_line_pulse(int fd, const char *line_name, int mask, unsigned int duration) +static void tty_line_pulse(int fd, int mask, unsigned int duration) { - tty_line_toggle(fd, line_name, mask); + tty_line_toggle(fd, mask); if (duration > 0) { @@ -491,19 +581,19 @@ static void tty_line_pulse(int fd, const char *line_name, int mask, unsigned int delay(duration); } - tty_line_toggle(fd, line_name, mask); + tty_line_toggle(fd, mask); } -static void tty_line_poke(int fd, const char *name, int mask, tty_line_mode_t mode, unsigned int duration) +static void tty_line_poke(int fd, int mask, tty_line_mode_t mode, unsigned int duration) { switch (mode) { case LINE_TOGGLE: - tty_line_toggle(fd, name, mask); + tty_line_toggle(fd, mask); break; case LINE_PULSE: - tty_line_pulse(fd, name, mask, duration); + tty_line_pulse(fd, mask, duration); break; case LINE_OFF: @@ -564,22 +654,22 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) switch (input_char) { case KEY_0: - tty_line_poke(fd, "DTR", TIOCM_DTR, line_mode, option.dtr_pulse_duration); + tty_line_poke(fd, TIOCM_DTR, line_mode, option.dtr_pulse_duration); break; case KEY_1: - tty_line_poke(fd, "RTS", TIOCM_RTS, line_mode, option.rts_pulse_duration); + tty_line_poke(fd, TIOCM_RTS, line_mode, option.rts_pulse_duration); break; case KEY_2: - tty_line_poke(fd, "CTS", TIOCM_CTS, line_mode, option.cts_pulse_duration); + tty_line_poke(fd, TIOCM_CTS, line_mode, option.cts_pulse_duration); break; case KEY_3: - tty_line_poke(fd, "DSR", TIOCM_DSR, line_mode, option.dsr_pulse_duration); + tty_line_poke(fd, TIOCM_DSR, line_mode, option.dsr_pulse_duration); break; case KEY_4: - tty_line_poke(fd, "DCD", TIOCM_CD, line_mode, option.dcd_pulse_duration); + tty_line_poke(fd, TIOCM_CD, line_mode, option.dcd_pulse_duration); break; case KEY_5: - tty_line_poke(fd, "RI", TIOCM_RI, line_mode, option.ri_pulse_duration); + tty_line_poke(fd, TIOCM_RI, line_mode, option.ri_pulse_duration); break; default: tio_warning_printf("Invalid line number"); diff --git a/src/tty.h b/src/tty.h index 4c667ef..eb4b3bb 100644 --- a/src/tty.h +++ b/src/tty.h @@ -39,5 +39,7 @@ void tty_wait_for_device(void); void list_serial_devices(void); void tty_input_thread_create(void); void tty_input_thread_wait_ready(void); -void tty_line_set(int fd, const char *name, int mask, bool value); -void tty_line_toggle(int fd, const char *name, int mask); +void tty_line_set(int fd, int mask, bool value); +void tty_line_toggle(int fd, int mask); +void tty_line_config(int mask, bool value); +void tty_line_config_apply(void); From 2ee1f5c224cf104fb46bfe5a00d2acc525dcf211 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 7 Apr 2024 12:40:06 +0200 Subject: [PATCH 211/506] Update README --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cd0e16c..986c64c 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,11 @@ when used in combination with [tmux](https://tmux.github.io). * Configurable text color * Visual or audible alert on connect/disconnect * Remapping of prefix key + * Support NO_COLOR env variable as per no-color.org * Man page documentation - * Binary size less than 90kB + * Lua scripting support + * Manipulate port control lines at connect/reconnect (useful for microcontroller reset/boot etc.) + * Automate interaction with tty device (TBD) * Plays nicely with [tmux](https://tmux.github.io) ## 3. Usage From eea46a20053d4df6cd8ddb476c46ea9e5fef8a9b Mon Sep 17 00:00:00 2001 From: Mengsk Date: Wed, 3 Apr 2024 15:25:57 +0200 Subject: [PATCH 212/506] Add Xmodem-CRC support. --- src/tty.c | 5 +- src/xymodem.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 147 insertions(+), 9 deletions(-) diff --git a/src/tty.c b/src/tty.c index af36ae1..7281ebb 100644 --- a/src/tty.c +++ b/src/tty.c @@ -110,6 +110,7 @@ #define KEY_U 0x55 #define KEY_V 0x76 #define KEY_X 0x78 +#define KEY_SHIFT_X 0x58 #define KEY_Y 0x79 #define KEY_Z 0x7a @@ -720,6 +721,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) 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-1K", option.prefix_key); + tio_printf(" ctrl-%c X Send file via Xmodem-CRC", 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; @@ -887,7 +889,8 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) case KEY_X: case KEY_Y: - tio_printf("Send file with %cMODEM", toupper(input_char)); + case KEY_SHIFT_X: + tio_printf("Send file with %s", input_char == KEY_X ? "XMODEM-1K" : (input_char == KEY_Y ? "YMODEM" : "XMODEM-CRC")); tio_printf_raw("Enter file name: "); if (tio_readln()) { tio_printf("Sending file '%s' ", line); diff --git a/src/xymodem.c b/src/xymodem.c index 53ec37d..42cd791 100644 --- a/src/xymodem.c +++ b/src/xymodem.c @@ -20,6 +20,7 @@ #include #include "misc.h" +#define SOH 0x01 #define STX 0x02 #define ACK 0x06 #define NAK 0x15 @@ -31,7 +32,7 @@ #define min(a, b) ((a) < (b) ? (a) : (b)) -struct xpacket { +struct xpacket_1k { uint8_t type; uint8_t seq; uint8_t nseq; @@ -40,6 +41,15 @@ struct xpacket { uint8_t crc_lo; } __attribute__((packed)); +struct xpacket { + uint8_t type; + uint8_t seq; + uint8_t nseq; + uint8_t data[128]; + uint8_t crc_hi; + uint8_t crc_lo; +} __attribute__((packed)); + /* See https://en.wikipedia.org/wiki/Computation_of_cyclic_redundancy_checks */ static uint16_t crc16(const uint8_t *data, uint16_t size) { @@ -53,9 +63,9 @@ static uint16_t crc16(const uint8_t *data, uint16_t size) return crc; } -static int xmodem(int sio, const void *data, size_t len, int seq) +static int xmodem_1k(int sio, const void *data, size_t len, int seq) { - struct xpacket packet; + struct xpacket_1k packet; const uint8_t *buf = data; char resp = 0; int rc, crc; @@ -80,7 +90,7 @@ static int xmodem(int sio, const void *data, size_t len, int seq) /* Always work with 1K packets */ packet.seq = seq; - packet.type = STX; + packet.type = STX; while (len) { size_t sz, z = 0; @@ -113,6 +123,9 @@ static int xmodem(int sio, const void *data, size_t len, int seq) sz -= rc; } + /* Clear response */ + resp = 0; + /* 'lrzsz' does not ACK ymodem's fin packet */ if (seq == 0 && packet.data[0] == 0) resp = ACK; @@ -172,6 +185,125 @@ static int xmodem(int sio, const void *data, size_t len, int seq) return 0; /* not reached */ } +static int xmodem(int sio, const void *data, size_t len) +{ + struct xpacket packet; + const uint8_t *buf = data; + char resp = 0; + int rc, crc; + + /* Drain pending characters from serial line. Insist on the + * last drained character being 'C'. + */ + while(1) { + if (key_hit) + return -1; + if (read(sio, &resp, 1) < 0) { + if (errno == EWOULDBLOCK) { + if (resp == 'C') break; + if (resp == CAN) return ERR; + usleep(50000); + continue; + } + perror("Read sync from serial failed"); + return ERR; + } + } + + /* Always work with 128b packets */ + packet.seq = 1; + packet.type = SOH; + + while (len) { + size_t sz, z = 0; + char *from, status; + + /* Build next packet, pad with 0 to full seq */ + z = min(len, sizeof(packet.data)); + memcpy(packet.data, buf, z); + memset(packet.data + z, 0, sizeof(packet.data) - z); + crc = crc16(packet.data, sizeof(packet.data)); + packet.crc_hi = crc >> 8; + packet.crc_lo = crc; + packet.nseq = 0xff - packet.seq; + + /* Send packet */ + from = (char *) &packet; + sz = sizeof(packet); + while (sz) { + if (key_hit) + return ERR; + if ((rc = write(sio, from, sz)) < 0 ) { + if (errno == EWOULDBLOCK) { + usleep(1000); + continue; + } + perror("Write packet to serial failed"); + return ERR; + } + from += rc; + sz -= rc; + } + + /* Clear response */ + resp = 0; + + /* Read receiver response, timeout 1 s */ + for(int n=0; n < 20; n++) { + if (key_hit) + return ERR; + if (read(sio, &resp, 1) < 0) { + if (errno == EWOULDBLOCK) { + usleep(50000); + continue; + } + perror("Read ack/nak from serial failed"); + return ERR; + } + break; + } + + /* Update "progress bar" */ + switch (resp) { + case NAK: status = 'N'; break; + case ACK: status = '.'; break; + case 'C': status = 'C'; break; + case CAN: status = '!'; return ERR; + default: status = '?'; + } + write(STDOUT_FILENO, &status, 1); + + /* Move to next block after ACK */ + if (resp == ACK) { + packet.seq++; + len -= z; + buf += z; + } + } + + /* Send EOT at 1 Hz until ACK or CAN received */ + while (1) { + if (key_hit) + return ERR; + if (write(sio, EOT, 1) < 0) { + perror("Write EOT to serial failed"); + return ERR; + } + write(STDOUT_FILENO, "|", 1); + usleep(1000000); /* 1 s timeout*/ + if (read(sio, &resp, 1) < 0) { + if (errno == EWOULDBLOCK) continue; + perror("Read from serial failed"); + return ERR; + } + if (resp == ACK || resp == CAN) { + write(STDOUT_FILENO, "\r\n", 2); + return (resp == ACK) ? OK : ERR; + } + } + return 0; /* not reached */ +} + int xymodem_send(int sio, const char *filename, char mode) { size_t len; @@ -197,7 +329,10 @@ int xymodem_send(int sio, const char *filename, char mode) /* Do transfer */ key_hit = 0; if (mode == 'x') { - rc = xmodem(sio, buf, len, 1); + rc = xmodem_1k(sio, buf, len, 1); + } + else if (mode == 'X') { + rc = xmodem(sio, buf, len); } else { /* Ymodem: hdr + file + fin */ @@ -209,9 +344,9 @@ int xymodem_send(int sio, const char *filename, char mode) p = stpcpy(hdr, filename) + 1; p += sprintf(p, "%ld %lo %o", len, stat.st_mtime, stat.st_mode); - if (xmodem(sio, hdr, p - hdr, 0) < 0) break; /* hdr with metadata */ - if (xmodem(sio, buf, len, 1) < 0) break; /* xmodem file */ - if (xmodem(sio, "", 1, 0) < 0) break; /* empty hdr = fin */ + if (xmodem_1k(sio, hdr, p - hdr, 0) < 0) break; /* hdr with metadata */ + if (xmodem_1k(sio, buf, len, 1) < 0) break; /* xmodem file */ + if (xmodem_1k(sio, "", 1, 0) < 0) break; /* empty hdr = fin */ rc = 0; break; } } From 901e00bba36b74c6adfa3375de65d18d8f576861 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Wed, 3 Apr 2024 20:46:44 +0200 Subject: [PATCH 213/506] Poll on serial port read instead of delay. --- src/xymodem.c | 86 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/src/xymodem.c b/src/xymodem.c index 42cd791..0e7fb41 100644 --- a/src/xymodem.c +++ b/src/xymodem.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "misc.h" @@ -63,6 +64,27 @@ static uint16_t crc16(const uint8_t *data, uint16_t size) return crc; } +static int serial_poll(int sio, void *data, size_t len, int timeout) +{ + struct pollfd fds[1]; + fds[0].fd = sio; + fds[0].events = POLLIN ; + int ret = 0; + + /* Wait data available */ + ret = poll(fds, 1, timeout); + if (ret < 0) { + return ret; + } + else if (ret > 0) { + if(fds[0].revents & POLLIN) { + return read(sio, data, len); + } + } + /* Timeout */ + return ret; +} + static int xmodem_1k(int sio, const void *data, size_t len, int seq) { struct xpacket_1k packet; @@ -76,13 +98,13 @@ static int xmodem_1k(int sio, const void *data, size_t len, int seq) while(1) { if (key_hit) return -1; - if (read(sio, &resp, 1) < 0) { - if (errno == EWOULDBLOCK) { - if (resp == 'C') break; - if (resp == CAN) return ERR; - usleep(50000); - continue; - } + rc = serial_poll(sio, &resp, 1, 50); + if (rc == 0) { + if (resp == 'C') break; + if (resp == CAN) return ERR; + continue; + } + else if (rc < 0) { perror("Read sync from serial failed"); return ERR; } @@ -133,15 +155,13 @@ static int xmodem_1k(int sio, const void *data, size_t len, int seq) for(int n=0; n < 20; n++) { if (key_hit) return ERR; - if (read(sio, &resp, 1) < 0) { - if (errno == EWOULDBLOCK) { - usleep(50000); - continue; - } + rc = serial_poll(sio, &resp, 1, 50); + if (rc < 0) { perror("Read ack/nak from serial failed"); return ERR; + } else if(rc > 0) { + break; } - break; } /* Update "progress bar" */ @@ -171,11 +191,13 @@ static int xmodem_1k(int sio, const void *data, size_t len, int seq) return ERR; } write(STDOUT_FILENO, "|", 1); - usleep(1000000); /* 1 s timeout*/ - if (read(sio, &resp, 1) < 0) { - if (errno == EWOULDBLOCK) continue; + /* 1s timeout */ + rc = serial_poll(sio, &resp, 1, 1000); + if (rc < 0) { perror("Read from serial failed"); return ERR; + } else if(rc == 0) { + continue; } if (resp == ACK || resp == CAN) { write(STDOUT_FILENO, "\r\n", 2); @@ -198,13 +220,13 @@ static int xmodem(int sio, const void *data, size_t len) while(1) { if (key_hit) return -1; - if (read(sio, &resp, 1) < 0) { - if (errno == EWOULDBLOCK) { - if (resp == 'C') break; - if (resp == CAN) return ERR; - usleep(50000); - continue; - } + rc = serial_poll(sio, &resp, 1, 50); + if (rc == 0) { + if (resp == 'C') break; + if (resp == CAN) return ERR; + continue; + } + else if (rc < 0) { perror("Read sync from serial failed"); return ERR; } @@ -252,15 +274,13 @@ static int xmodem(int sio, const void *data, size_t len) for(int n=0; n < 20; n++) { if (key_hit) return ERR; - if (read(sio, &resp, 1) < 0) { - if (errno == EWOULDBLOCK) { - usleep(50000); - continue; - } + rc = serial_poll(sio, &resp, 1, 50); + if (rc < 0) { perror("Read ack/nak from serial failed"); return ERR; + } else if(rc > 0) { + break; } - break; } /* Update "progress bar" */ @@ -290,11 +310,13 @@ static int xmodem(int sio, const void *data, size_t len) return ERR; } write(STDOUT_FILENO, "|", 1); - usleep(1000000); /* 1 s timeout*/ - if (read(sio, &resp, 1) < 0) { - if (errno == EWOULDBLOCK) continue; + /* 1s timeout */ + rc = serial_poll(sio, &resp, 1, 1000); + if (rc < 0) { perror("Read from serial failed"); return ERR; + } else if(rc == 0) { + continue; } if (resp == ACK || resp == CAN) { write(STDOUT_FILENO, "\r\n", 2); From fd6a246908e13d25fe84ab6c6745e56ba7af5fb0 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Sat, 6 Apr 2024 14:07:04 +0200 Subject: [PATCH 214/506] Add manpage. --- man/tio.1.in | 4 +++- man/tio.1.txt | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 3757f1b..78cb511 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -352,7 +352,9 @@ Toggle conversion to uppercase on output .IP "\fBctrl-t v" Show version .IP "\fBctrl-t x" -Send a file using the XMODEM protocol (prompts for file name) +Send a file using the XMODEM-1K protocol (prompts for file name) +.IP "\fBctrl-t X" +Send a file using the XMODEM-CRC protocol (prompts for file name) .IP "\fBctrl-t y" Send a file using the YMODEM protocol (prompts for file name) .IP "\fBctrl-t ctrl-t" diff --git a/man/tio.1.txt b/man/tio.1.txt index 3c1daa7..d85deaa 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -257,7 +257,9 @@ KEYS ctrl-t v Show version - ctrl-t x Send a file using the XMODEM protocol (prompts for file name) + ctrl-t x Send a file using the XMODEM-1K protocol (prompts for file name) + + ctrl-t X Send a file using the XMODEM-CRC protocol (prompts for file name) ctrl-t y Send a file using the YMODEM protocol (prompts for file name) From 2fff4d36d023e90d30bdf3dc416be39495ffab44 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 10 Apr 2024 14:39:42 +0200 Subject: [PATCH 215/506] Add independent input and output mode Replaces -x, --hexadecimal option with --intput-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 configuration: input-mode = hex output-mode = hex --- README.md | 5 +- man/tio.1.in | 36 +++++---- src/bash-completion/tio.in | 11 ++- src/configfile.c | 8 +- src/options.c | 86 ++++++++++++++++++-- src/options.h | 20 ++++- src/print.c | 5 ++ src/print.h | 1 + src/tty.c | 160 ++++++++++++++++++++++++++----------- 9 files changed, 254 insertions(+), 78 deletions(-) diff --git a/README.md b/README.md index 986c64c..a135737 100644 --- a/README.md +++ b/README.md @@ -46,9 +46,8 @@ when used in combination with [tmux](https://tmux.github.io). * Line timestamps * Support for delayed output per character * Support for delayed output per line - * Hexadecimal mode - * Log to file - * Autogeneration of log filename + * Switchable independent input and output mode (normal vs hex) + * Log to file with automatic or manual naming of log file * Configuration file support * Activate sub-configurations by name or pattern * Redirect I/O to UNIX socket or IPv4/v6 network socket for scripting or TTY sharing diff --git a/man/tio.1.in b/man/tio.1.in index 78cb511..3782681 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -191,9 +191,20 @@ If defining more than one flag, the flags must be comma separated. .RE .TP -.BR \-x ", " \-\-hexadecimal +.BR " \-\-input\-mode " \fInormal|hex -Enable hexadecimal mode. +Set input mode. In hex input mode bytes can be sent by typing the +\fBtwo-character hexadecimal\fR representation of the value, e.g.: to send +\fI0xA\fR you must type \fI0a\fR or \fI0A\fR. + +Default value is "normal". + +.TP +.BR " \-\-output\-mode " \fInormal|hex + +Set output mode. In hex mode each incoming byte is printed out as a hex value. + +Default value is "normal". .TP .BR \-c ", " "\-\-color " \fI0..255|bold|none|list @@ -329,14 +340,16 @@ Toggle log to file Flush data I/O buffers (discard data written but not transmitted and data received but not read) .IP "\fBctrl-t g" Toggle serial port line -.IP "\fBctrl-t h" -Toggle hexadecimal mode +.IP "\fBctrl-t i" +Toggle input mode .IP "\fBctrl-t l" 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 +.IP "\fBctrl-t o" +Toggle output mode .IP "\fBctrl-t p" Pulse serial port line .IP "\fBctrl-t q" @@ -360,15 +373,6 @@ Send a file using the YMODEM protocol (prompts for file name) .IP "\fBctrl-t ctrl-t" Send ctrl-t character -.SH "HEXADECIMAL MODE" -.PP -In hexadecimal mode each incoming byte is printed out as a hexadecimal value. - -.PP -Bytes can be sent in this mode by typing the \fBtwo-character hexadecimal\fR -representation of the value, e.g.: to send \fI0xA\fR you must type \fI0a\fR or -\fI0A\fR. - .SH "SCRIPT API" .PP Tio suppots Lua scripting for manipulating the tty device. In addition to the @@ -470,8 +474,10 @@ Set timestamp format Map characters on input or output .IP "\fBcolor" Colorize tio text using ANSI color code ranging from 0 to 255 -.IP "\fBhexadecimal" -Enable hexadecimal mode +.IP "\fBinput-mode" +Set input mode. +.IP "\fBoutput-mode" +Set output mode. .IP "\fBsocket" Set socket to redirect I/O to .IP "\fBprefix-ctrl-key" diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index 9bf1629..c94c0cb 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -31,7 +31,8 @@ _tio() -L --list-devices \ -c --color \ -S --socket \ - -x --hexadecimal \ + --input-mode \ + --output-mode \ -r --response-wait \ --response-timeout \ --rs-485 \ @@ -131,8 +132,12 @@ _tio() COMPREPLY=( $(compgen -W "unix: inet: inet6:" -- ${cur}) ) return 0 ;; - -x | --hexadecimal) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + --input-mode) + COMPREPLY=( $(compgen -W "normal hex" -- ${cur}) ) + return 0 + ;; + --output-mode) + COMPREPLY=( $(compgen -W "normal hex" -- ${cur}) ) return 0 ;; -r | --response-wait) diff --git a/src/configfile.c b/src/configfile.c index 2e50e05..f454115 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -213,9 +213,13 @@ static int data_handler(void *user, const char *section, const char *name, { option.local_echo = read_boolean(value, name); } - else if (!strcmp(name, "hexadecimal")) + else if (!strcmp(name, "input-mode")) { - option.hex_mode = read_boolean(value, name); + option.input_mode = input_mode_option_parse(value); + } + else if (!strcmp(name, "output-mode")) + { + option.output_mode = output_mode_option_parse(value); } else if (!strcmp(name, "timestamp")) { diff --git a/src/options.c b/src/options.c index 510259e..9455886 100644 --- a/src/options.c +++ b/src/options.c @@ -59,6 +59,8 @@ enum opt_t OPT_SCRIPT, OPT_SCRIPT_FILE, OPT_SCRIPT_RUN, + OPT_INPUT_MODE, + OPT_OUTPUT_MODE, }; /* Default options */ @@ -89,7 +91,8 @@ struct option_t option = .socket = NULL, .map = "", .color = 256, // Bold - .hex_mode = false, + .input_mode = INPUT_MODE_NORMAL, + .output_mode = OUTPUT_MODE_NORMAL, .prefix_code = 20, // ctrl-t .prefix_key = 't', .prefix_enabled = true, @@ -137,7 +140,8 @@ void print_help(char *argv[]) printf(" -m, --map Map characters\n"); printf(" -c, --color 0..255|bold|none|list Colorize tio text (default: bold)\n"); printf(" -S, --socket Redirect I/O to socket\n"); - printf(" -x, --hexadecimal Enable hexadecimal mode\n"); + printf(" --input-mode normal|hex Select input mode (default: normal)\n"); + printf(" --output-mode normal|hex Select output mode (default: normal)\n"); printf(" -r, --response-wait Wait for line response then quit\n"); printf(" --response-timeout Response timeout (default: 100)\n"); printf(" --rs-485 Enable RS-485 mode\n"); @@ -215,6 +219,70 @@ void line_pulse_duration_option_parse(const char *arg) free(buffer); } +input_mode_t input_mode_option_parse(const char *arg) +{ + if (strcmp("normal", arg) == 0) + { + return INPUT_MODE_NORMAL; + } + else if (strcmp("hex", arg) == 0) + { + return INPUT_MODE_HEX; + } + else + { + tio_error_printf("Invalid input mode option"); + exit(EXIT_FAILURE); + } +} + +output_mode_t output_mode_option_parse(const char *arg) +{ + if (strcmp("normal", arg) == 0) + { + return OUTPUT_MODE_NORMAL; + } + else if (strcmp("hex", arg) == 0) + { + return OUTPUT_MODE_HEX; + } + else + { + tio_error_printf("Invalid output mode option"); + exit(EXIT_FAILURE); + } +} + +const char *input_mode_by_string(input_mode_t mode) +{ + switch (mode) + { + case INPUT_MODE_NORMAL: + return "normal"; + case INPUT_MODE_HEX: + return "hex"; + case INPUT_MODE_END: + break; + } + + return NULL; +} + +const char *output_mode_by_string(output_mode_t mode) +{ + switch (mode) + { + case OUTPUT_MODE_NORMAL: + return "normal"; + case OUTPUT_MODE_HEX: + return "hex"; + case OUTPUT_MODE_END: + break; + } + + return NULL; +} + enum script_run_t script_run_option_parse(const char *arg) { if (strcmp("once", arg) == 0) @@ -255,7 +323,8 @@ void options_print() option.dsr_pulse_duration, option.dcd_pulse_duration, option.ri_pulse_duration); - tio_printf(" Hexadecimal mode: %s", option.hex_mode ? "enabled" : "disabled"); + tio_printf(" Input mode: %s", input_mode_by_string(option.input_mode)); + tio_printf(" Output mode: %s", output_mode_by_string(option.output_mode)); if (option.map[0] != 0) tio_printf(" Map flags: %s", option.map); if (option.log) @@ -304,7 +373,8 @@ void options_parse(int argc, char *argv[]) {"socket", required_argument, 0, 'S' }, {"map", required_argument, 0, 'm' }, {"color", required_argument, 0, 'c' }, - {"hexadecimal", no_argument, 0, 'x' }, + {"input-mode", required_argument, 0, OPT_INPUT_MODE }, + {"output-mode", required_argument, 0, OPT_OUTPUT_MODE }, {"response-wait", no_argument, 0, 'r' }, {"response-timeout", required_argument, 0, OPT_RESPONSE_TIMEOUT }, {"rs-485", no_argument, 0, OPT_RS485 }, @@ -453,8 +523,12 @@ void options_parse(int argc, char *argv[]) } break; - case 'x': - option.hex_mode = true; + case OPT_INPUT_MODE: + option.input_mode = input_mode_option_parse(optarg); + break; + + case OPT_OUTPUT_MODE: + option.output_mode = output_mode_option_parse(optarg); break; case 'r': diff --git a/src/options.h b/src/options.h index 1cc9a60..4e1c17f 100644 --- a/src/options.h +++ b/src/options.h @@ -30,6 +30,20 @@ #include "timestamp.h" #include "alert.h" +typedef enum +{ + INPUT_MODE_NORMAL, + INPUT_MODE_HEX, + INPUT_MODE_END, +} input_mode_t; + +typedef enum +{ + OUTPUT_MODE_NORMAL, + OUTPUT_MODE_HEX, + OUTPUT_MODE_END, +} output_mode_t; + /* Options */ struct option_t { @@ -58,7 +72,8 @@ struct option_t const char *map; const char *socket; int color; - bool hex_mode; + input_mode_t input_mode; + output_mode_t output_mode; unsigned char prefix_code; unsigned char prefix_key; bool prefix_enabled; @@ -84,3 +99,6 @@ void options_parse_final(int argc, char *argv[]); void line_pulse_duration_option_parse(const char *arg); enum script_run_t script_run_option_parse(const char *arg); + +input_mode_t input_mode_option_parse(const char *arg); +output_mode_t output_mode_option_parse(const char *arg); diff --git a/src/print.c b/src/print.c index b5ad48c..6e743b8 100644 --- a/src/print.c +++ b/src/print.c @@ -73,3 +73,8 @@ void tio_printf_array(const char *array) } tio_printf(""); } + +void print_tainted_set() +{ + print_tainted = true; +} diff --git a/src/print.h b/src/print.h index c870f17..e646167 100644 --- a/src/print.h +++ b/src/print.h @@ -116,3 +116,4 @@ void print_hex(char c); void print_normal(char c); void print_init_ansi_formatting(void); void tio_printf_array(const char *array); +void print_tainted_set(void); diff --git a/src/tty.c b/src/tty.c index 7281ebb..5cad8c2 100644 --- a/src/tty.c +++ b/src/tty.c @@ -98,10 +98,11 @@ #define KEY_F 0x66 #define KEY_SHIFT_F 0x46 #define KEY_G 0x67 -#define KEY_H 0x68 +#define KEY_I 0x69 #define KEY_L 0x6C #define KEY_SHIFT_L 0x4C #define KEY_M 0x6D +#define KEY_O 0x6F #define KEY_P 0x70 #define KEY_Q 0x71 #define KEY_R 0x72 @@ -180,11 +181,15 @@ static void optional_local_echo(char c) { return; } + print(c); + if (option.log) { log_putc(c); } + + print_tainted_set(); } inline static bool is_valid_hex(char c) @@ -407,23 +412,29 @@ void tty_input_thread_wait_ready(void) pthread_mutex_lock(&mutex_input_ready); } -static void output_hex(char c) +static void handle_hex_prompt(char c) { hex_chars[hex_char_index++] = c; printf("%c", c); + print_tainted_set(); if (hex_char_index == 2) { usleep(100*1000); - printf("\b \b"); - printf("\b \b"); + if (option.local_echo == false) + { + printf("\b \b"); + printf("\b \b"); + } + else + { + printf(" "); + } unsigned char hex_value = char_to_nibble(hex_chars[0]) << 4 | (char_to_nibble(hex_chars[1]) & 0x0F); hex_char_index = 0; - optional_local_echo(hex_value); - ssize_t status = tty_write(fd, &hex_value, 1); if (status < 0) { @@ -629,6 +640,23 @@ static int tio_readln(void) return (p - line); } +void tty_output_mode_set(output_mode_t mode) +{ + switch (mode) + { + case OUTPUT_MODE_NORMAL: + print = print_normal; + break; + + case OUTPUT_MODE_HEX: + print = print_hex; + break; + + case OUTPUT_MODE_END: + break; + } +} + void handle_command_sequence(char input_char, char *output_char, bool *forward) { char unused_char; @@ -709,10 +737,11 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf(" ctrl-%c f Toggle log to file", option.prefix_key); tio_printf(" ctrl-%c F Flush data I/O buffers", option.prefix_key); tio_printf(" ctrl-%c g Toggle serial port line", option.prefix_key); - tio_printf(" ctrl-%c h Toggle hexadecimal mode", option.prefix_key); + 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 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); tio_printf(" ctrl-%c r Run script", option.prefix_key); @@ -803,19 +832,42 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf("Switched local echo %s", option.local_echo ? "on" : "off"); break; - case KEY_H: - /* Toggle hexadecimal printing mode */ - if (!option.hex_mode) + case KEY_I: + option.input_mode += 1; + switch (option.input_mode) { - print = print_hex; - option.hex_mode = true; - tio_printf("Switched to hexadecimal mode"); + case INPUT_MODE_NORMAL: + break; + + case INPUT_MODE_HEX: + option.input_mode = INPUT_MODE_HEX; + tio_printf("Switched to hex input mode"); + break; + + case INPUT_MODE_END: + option.input_mode = INPUT_MODE_NORMAL; + tio_printf("Switched to normal input mode"); + break; } - else + break; + + case KEY_O: + option.output_mode += 1; + switch (option.output_mode) { - print = print_normal; - option.hex_mode = false; - tio_printf("Switched to normal mode"); + case OUTPUT_MODE_NORMAL: + break; + + case OUTPUT_MODE_HEX: + tty_output_mode_set(OUTPUT_MODE_HEX); + tio_printf("Switched to hex output mode"); + break; + + case OUTPUT_MODE_END: + option.output_mode = OUTPUT_MODE_NORMAL; + tty_output_mode_set(OUTPUT_MODE_NORMAL); + tio_printf("Switched to normal output mode"); + break; } break; @@ -1379,29 +1431,48 @@ void forward_to_tty(int fd, char output_char) } else { - if (option.hex_mode) + switch (option.output_mode) { - output_hex(output_char); - } - else - { - /* Send output to tty device */ - optional_local_echo(output_char); - if ((output_char == 0) && (map_o_nulbrk)) - { - status = tcsendbreak(fd, 0); - } - else - { - status = tty_write(fd, &output_char, 1); - } - if (status < 0) - { - tio_warning_printf("Could not write to tty device"); - } + case OUTPUT_MODE_NORMAL: + if (option.input_mode == INPUT_MODE_HEX) + { + handle_hex_prompt(output_char); + } + else + { + /* Send output to tty device */ + optional_local_echo(output_char); + if ((output_char == 0) && (map_o_nulbrk)) + { + status = tcsendbreak(fd, 0); + } + else + { + status = tty_write(fd, &output_char, 1); + } + if (status < 0) + { + tio_warning_printf("Could not write to tty device"); + } - /* Update transmit statistics */ - tx_total++; + /* Update transmit statistics */ + tx_total++; + } + break; + + case OUTPUT_MODE_HEX: + if (option.input_mode == INPUT_MODE_HEX) + { + handle_hex_prompt(output_char); + } + else + { + optional_local_echo(output_char); + } + break; + + case OUTPUT_MODE_END: + break; } } } @@ -1460,14 +1531,7 @@ int tty_connect(void) } /* Manage print output mode */ - if (option.hex_mode) - { - print = print_hex; - } - else - { - print = print_normal; - } + tty_output_mode_set(option.output_mode); /* Save current port settings */ if (tcgetattr(fd, &tio_old) < 0) @@ -1577,7 +1641,7 @@ int tty_connect(void) input_char = input_buffer[i]; /* Print timestamp on new line if enabled */ - if ((next_timestamp && input_char != '\n' && input_char != '\r') && !option.hex_mode) + if ((next_timestamp && input_char != '\n' && input_char != '\r') && (option.output_mode == OUTPUT_MODE_NORMAL)) { now = timestamp_current_time(); if (now) @@ -1697,7 +1761,7 @@ int tty_connect(void) /* Handle commands */ handle_command_sequence(input_char, &output_char, &forward); - if ((option.hex_mode) && (forward)) + if ((option.input_mode == INPUT_MODE_HEX) && (forward)) { if (!is_valid_hex(input_char)) { From 2b6a79b9f0f1312edc3d9055fb9445b1be63129b Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 10 Apr 2024 15:04:47 +0200 Subject: [PATCH 216/506] Cleanup options --- src/options.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/options.c b/src/options.c index 9455886..f50e65f 100644 --- a/src/options.c +++ b/src/options.c @@ -129,19 +129,19 @@ void print_help(char *argv[]) printf(" --line-pulse-duration Set line pulse duration\n"); printf(" -n, --no-autoconnect Disable automatic connect\n"); printf(" -e, --local-echo Enable local echo\n"); + printf(" --input-mode normal|hex Select input mode (default: normal)\n"); + printf(" --output-mode normal|hex Select output mode (default: normal)\n"); printf(" -t, --timestamp Enable line timestamp\n"); printf(" --timestamp-format Set timestamp format (default: 24hour)\n"); printf(" -L, --list-devices List available serial devices by ID\n"); printf(" -l, --log Enable log to file\n"); printf(" --log-file Set log filename\n"); - printf(" --log-directory Set log directory path (for automatic named logs)\n"); + printf(" --log-directory Set log directory path for automatic named logs\n"); printf(" --log-append Append to log file\n"); printf(" --log-strip Strip control characters and escape sequences\n"); printf(" -m, --map Map characters\n"); printf(" -c, --color 0..255|bold|none|list Colorize tio text (default: bold)\n"); printf(" -S, --socket Redirect I/O to socket\n"); - printf(" --input-mode normal|hex Select input mode (default: normal)\n"); - printf(" --output-mode normal|hex Select output mode (default: normal)\n"); printf(" -r, --response-wait Wait for line response then quit\n"); printf(" --response-timeout Response timeout (default: 100)\n"); printf(" --rs-485 Enable RS-485 mode\n"); From a208c9908a20cb817e3c104e3df2eb0f650ba5dd Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 10 Apr 2024 15:05:29 +0200 Subject: [PATCH 217/506] Update README --- README.md | 118 +++++++++++++++++++++++++++++------------------------- 1 file changed, 64 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index a135737..0e91e84 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ To make a simpler serial device tool for talking with serial TTY devices with less focus on classic terminal/modem features and more focus on the needs of embedded developers and hackers. -tio was originally created to replace +tio was originally created as an alternative to [screen](https://www.gnu.org/software/screen) for connecting to serial devices when used in combination with [tmux](https://tmux.github.io). @@ -74,44 +74,49 @@ For more usage details please see the man page documentation The command-line interface is straightforward as reflected in the output from 'tio --help': ``` - Usage: tio [] +Usage: tio [] - Connect to TTY device directly or via sub-configuration. +Connect to TTY device directly or via sub-configuration. - Options: - -b, --baudrate Baud rate (default: 115200) - -d, --databits 5|6|7|8 Data bits (default: 8) - -f, --flow hard|soft|none Flow control (default: none) - -s, --stopbits 1|2 Stop bits (default: 1) - -p, --parity odd|even|none|mark|space Parity (default: none) - -o, --output-delay Output character delay (default: 0) - -O, --output-line-delay Output line delay (default: 0) - --line-pulse-duration Set line pulse duration - -n, --no-autoconnect Disable automatic connect - -e, --local-echo Enable local echo - -t, --timestamp Enable line timestamp - --timestamp-format Set timestamp format (default: 24hour) - -L, --list-devices List available serial devices - -l, --log Enable log to file - --log-file Set log filename - --log-append Append to log file - --log-strip Strip control characters and escape sequences - -m, --map Map characters - -c, --color 0..255|bold|none|list Colorize tio text (default: bold) - -S, --socket Redirect I/O to socket - -x, --hexadecimal Enable hexadecimal mode - -r, --response-wait Wait for line response then quit - --response-timeout Response timeout (default: 100) - --rs-485 Enable RS-485 mode - --rs-485-config Set RS-485 configuration - --alert bell|blink|none Alert on connect/disconnect (default: none) - -v, --version Display version - -h, --help Display help +Options: + -b, --baudrate Baud rate (default: 115200) + -d, --databits 5|6|7|8 Data bits (default: 8) + -f, --flow hard|soft|none Flow control (default: none) + -s, --stopbits 1|2 Stop bits (default: 1) + -p, --parity odd|even|none|mark|space Parity (default: none) + -o, --output-delay Output character delay (default: 0) + -O, --output-line-delay Output line delay (default: 0) + --line-pulse-duration Set line pulse duration + -n, --no-autoconnect Disable automatic connect + -e, --local-echo Enable local echo + --input-mode normal|hex Select input mode (default: normal) + --output-mode normal|hex Select output mode (default: normal) + -t, --timestamp Enable line timestamp + --timestamp-format Set timestamp format (default: 24hour) + -L, --list-devices List available serial devices by ID + -l, --log Enable log to file + --log-file Set log filename + --log-directory Set log directory path for automatic named logs + --log-append Append to log file + --log-strip Strip control characters and escape sequences + -m, --map Map characters + -c, --color 0..255|bold|none|list Colorize tio text (default: bold) + -S, --socket Redirect I/O to socket + -r, --response-wait Wait for line response then quit + --response-timeout Response timeout (default: 100) + --rs-485 Enable RS-485 mode + --rs-485-config Set RS-485 configuration + --alert bell|blink|none Alert on connect/disconnect (default: none) + --mute Mute tio + --script Run script from string + --script-file Run script from file + --script-run once|always|never Run script on connect (default: always) + -v, --version Display version + -h, --help Display help - Options and sub-configurations may be set via configuration file. - - See the man page for more details. +Options and sub-configurations may be set via configuration file. +See the man page for more details. ``` By default tio automatically connects to the provided TTY device if present. @@ -181,25 +186,30 @@ Various in session key commands are supported. When tio is started, press ctrl-t ? to list the available key commands. ``` -[20:19:12.040] Key commands: -[20:19:12.040] ctrl-t ? List available key commands -[20:19:12.040] ctrl-t b Send break -[20:19:12.040] ctrl-t c Show configuration -[20:19:12.040] ctrl-t e Toggle local echo mode -[20:19:12.040] ctrl-t f Toggle log to file -[20:19:12.040] ctrl-t g Toggle serial port line -[20:19:12.040] ctrl-t h Toggle hexadecimal mode -[20:19:12.040] ctrl-t l Clear screen -[20:19:12.040] ctrl-t L Show line states -[20:19:12.040] ctrl-t p Pulse serial port line -[20:19:12.040] ctrl-t q Quit -[20:19:12.041] ctrl-t s Show statistics -[20:19:12.041] ctrl-t t Toggle line timestamp mode -[20:19:12.041] ctrl-t U Toggle conversion to uppercase -[20:19:12.041] ctrl-t v Show version -[20:19:12.041] ctrl-t x Send file using the XMODEM protocol -[20:19:12.041] ctrl-t y Send file using the YMODEM protocol -[20:19:12.041] ctrl-t ctrl-t Send ctrl-t character +[15:02:53.269] Key commands: +[15:02:53.269] ctrl-t ? List available key commands +[15:02:53.269] ctrl-t b Send break +[15:02:53.269] ctrl-t c Show configuration +[15:02:53.269] ctrl-t e Toggle local echo mode +[15:02:53.269] ctrl-t f Toggle log to file +[15:02:53.269] ctrl-t F Flush data I/O buffers +[15:02:53.269] ctrl-t g Toggle serial port line +[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 o Toggle output mode +[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 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-1K +[15:02:53.269] ctrl-t X Send file via Xmodem-CRC +[15:02:53.269] ctrl-t y Send file via Ymodem +[15:02:53.269] ctrl-t ctrl-t Send ctrl-t character ``` If needed, the prefix key (ctrl-t) can be remapped via configuration file. From d8fb1ab0cae4056cc46f4eaafd0b62b11f9403c4 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 10 Apr 2024 19:56:14 +0200 Subject: [PATCH 218/506] Rework x/y-modem transfer command Remove ctrl-t X optin and instead introduce submenu to ctrl-t x option for picking which xmodem protocol to use. --- src/misc.h | 3 -- src/tty.c | 145 +++++++++++++++++++++++++++++++++----------------- src/xymodem.c | 8 +-- src/xymodem.h | 30 +++++++++++ 4 files changed, 131 insertions(+), 55 deletions(-) create mode 100644 src/xymodem.h diff --git a/src/misc.h b/src/misc.h index e949db7..b05142c 100644 --- a/src/misc.h +++ b/src/misc.h @@ -32,6 +32,3 @@ int ctrl_key_code(unsigned char key); void alert_connect(void); void alert_disconnect(void); bool fs_dir_exists(const char *path); - -extern char key_hit; -int xymodem_send(int sio, const char *filename, char mode); diff --git a/src/tty.c b/src/tty.c index 5cad8c2..2aea470 100644 --- a/src/tty.c +++ b/src/tty.c @@ -56,6 +56,7 @@ #include "timestamp.h" #include "misc.h" #include "script.h" +#include "xymodem.h" /* tty device listing configuration */ @@ -111,17 +112,23 @@ #define KEY_U 0x55 #define KEY_V 0x76 #define KEY_X 0x78 -#define KEY_SHIFT_X 0x58 #define KEY_Y 0x79 #define KEY_Z 0x7a typedef enum { - LINE_OFF, LINE_TOGGLE, LINE_PULSE } tty_line_mode_t; +typedef enum +{ + SUBCOMMAND_NONE, + SUBCOMMAND_LINE_TOGGLE, + SUBCOMMAND_LINE_PULSE, + SUBCOMMAND_XMODEM, +} sub_command_t; + typedef struct { int mask; @@ -607,9 +614,6 @@ static void tty_line_poke(int fd, int mask, tty_line_mode_t mode, unsigned int d case LINE_PULSE: tty_line_pulse(fd, mask, duration); break; - - case LINE_OFF: - break; } } @@ -662,7 +666,8 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) char unused_char; bool unused_bool; int state; - static tty_line_mode_t line_mode = LINE_OFF; + static tty_line_mode_t line_mode; + static sub_command_t sub_command = SUBCOMMAND_NONE; static char previous_char = 0; /* Ignore unused arguments */ @@ -676,37 +681,73 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) forward = &unused_bool; } - // Handle tty line toggle and pulse action - if (line_mode) + // Handle sub commands + if (sub_command) { *forward = false; - switch (input_char) + + switch (sub_command) { - case KEY_0: - tty_line_poke(fd, TIOCM_DTR, line_mode, option.dtr_pulse_duration); + case SUBCOMMAND_NONE: break; - case KEY_1: - tty_line_poke(fd, TIOCM_RTS, line_mode, option.rts_pulse_duration); + + case SUBCOMMAND_LINE_TOGGLE: + case SUBCOMMAND_LINE_PULSE: + switch (input_char) + { + case KEY_0: + tty_line_poke(fd, TIOCM_DTR, line_mode, option.dtr_pulse_duration); + break; + case KEY_1: + tty_line_poke(fd, TIOCM_RTS, line_mode, option.rts_pulse_duration); + break; + case KEY_2: + tty_line_poke(fd, TIOCM_CTS, line_mode, option.cts_pulse_duration); + break; + case KEY_3: + tty_line_poke(fd, TIOCM_DSR, line_mode, option.dsr_pulse_duration); + break; + case KEY_4: + tty_line_poke(fd, TIOCM_CD, line_mode, option.dcd_pulse_duration); + break; + case KEY_5: + tty_line_poke(fd, TIOCM_RI, line_mode, option.ri_pulse_duration); + break; + default: + tio_warning_printf("Invalid line number"); + break; + } break; - case KEY_2: - tty_line_poke(fd, TIOCM_CTS, line_mode, option.cts_pulse_duration); - break; - case KEY_3: - tty_line_poke(fd, TIOCM_DSR, line_mode, option.dsr_pulse_duration); - break; - case KEY_4: - tty_line_poke(fd, TIOCM_CD, line_mode, option.dcd_pulse_duration); - break; - case KEY_5: - tty_line_poke(fd, TIOCM_RI, line_mode, option.ri_pulse_duration); - break; - default: - tio_warning_printf("Invalid line number"); + + case SUBCOMMAND_XMODEM: + switch (input_char) + { + case KEY_0: + tio_printf("Send file with XMODEM-1K"); + tio_printf_raw("Enter file name: "); + if (tio_readln()) + { + tio_printf("Sending file '%s' ", line); + tio_printf("Press any key to abort transfer"); + tio_printf("%s", xymodem_send(fd, line, XMODEM_CRC) < 0 ? "Aborted" : "Done"); + } + break; + + case KEY_1: + tio_printf("Send file with XMODEM-CRC"); + tio_printf_raw("Enter file name: "); + if (tio_readln()) + { + tio_printf("Sending file '%s' ", line); + tio_printf("Press any key to abort transfer"); + tio_printf("%s", xymodem_send(fd, line, XMODEM_CRC) < 0 ? "Aborted" : "Done"); + } + break; + } break; } - line_mode = LINE_OFF; - + sub_command = SUBCOMMAND_NONE; return; } @@ -726,6 +767,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) return; } + // Handle commands switch (input_char) { case KEY_QUESTION: @@ -749,8 +791,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-1K", option.prefix_key); - tio_printf(" ctrl-%c X Send file via Xmodem-CRC", option.prefix_key); + tio_printf(" ctrl-%c x Send 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; @@ -791,26 +832,28 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) case KEY_G: tio_printf("Please enter which serial line number to toggle:"); - tio_printf(" DTR (0)"); - tio_printf(" RTS (1)"); - tio_printf(" CTS (2)"); - tio_printf(" DSR (3)"); - tio_printf(" DCD (4)"); - tio_printf(" RI (5)"); - // Process next input character as part of the line toggle step + tio_printf("(0) DTR"); + tio_printf("(1) RTS"); + tio_printf("(2) CTS"); + tio_printf("(3) DSR"); + tio_printf("(4) DCD"); + tio_printf("(5) RI"); line_mode = LINE_TOGGLE; + // Process next input character as sub command + sub_command = SUBCOMMAND_LINE_TOGGLE; break; case KEY_P: tio_printf("Please enter which serial line number to pulse:"); - tio_printf(" DTR (0)"); - tio_printf(" RTS (1)"); - tio_printf(" CTS (2)"); - tio_printf(" DSR (3)"); - tio_printf(" DCD (4)"); - tio_printf(" RI (5)"); - // Process next input character as part of the line pulse step + tio_printf("(0) DTR"); + tio_printf("(1) RTS"); + tio_printf("(2) CTS"); + tio_printf("(3) DSR"); + tio_printf("(4) DCD"); + tio_printf("(5) RI"); line_mode = LINE_PULSE; + // Process next input character as sub command + sub_command = SUBCOMMAND_LINE_PULSE; break; case KEY_B: @@ -940,14 +983,20 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) break; case KEY_X: + tio_printf("Please enter which X modem protocol to use:"); + tio_printf(" (0) XMODEM-1K"); + tio_printf(" (1) XMODEM-CRC"); + // Process next input character as sub command + sub_command = SUBCOMMAND_XMODEM; + break; + case KEY_Y: - case KEY_SHIFT_X: - tio_printf("Send file with %s", input_char == KEY_X ? "XMODEM-1K" : (input_char == KEY_Y ? "YMODEM" : "XMODEM-CRC")); + tio_printf("Send file with YMODEM"); tio_printf_raw("Enter file name: "); if (tio_readln()) { tio_printf("Sending file '%s' ", line); tio_printf("Press any key to abort transfer"); - tio_printf("%s", xymodem_send(fd, line, input_char) < 0 ? "Aborted" : "Done"); + tio_printf("%s", xymodem_send(fd, line, YMODEM) < 0 ? "Aborted" : "Done"); } break; diff --git a/src/xymodem.c b/src/xymodem.c index 0e7fb41..b25b382 100644 --- a/src/xymodem.c +++ b/src/xymodem.c @@ -19,7 +19,7 @@ #include #include #include -#include "misc.h" +#include "xymodem.h" #define SOH 0x01 #define STX 0x02 @@ -326,7 +326,7 @@ static int xmodem(int sio, const void *data, size_t len) return 0; /* not reached */ } -int xymodem_send(int sio, const char *filename, char mode) +int xymodem_send(int sio, const char *filename, modem_mode_t mode) { size_t len; int rc, fd; @@ -350,10 +350,10 @@ int xymodem_send(int sio, const char *filename, char mode) /* Do transfer */ key_hit = 0; - if (mode == 'x') { + if (mode == XMODEM_1K) { rc = xmodem_1k(sio, buf, len, 1); } - else if (mode == 'X') { + else if (mode == XMODEM_CRC) { rc = xmodem(sio, buf, len); } else { diff --git a/src/xymodem.h b/src/xymodem.h new file mode 100644 index 0000000..8e8232c --- /dev/null +++ b/src/xymodem.h @@ -0,0 +1,30 @@ +/* + * tio - a simple serial terminal 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. + */ + +typedef enum { + XMODEM_1K, + XMODEM_CRC, + YMODEM, +} modem_mode_t; + +extern char key_hit; + +int xymodem_send(int sio, const char *filename, modem_mode_t mode); From 78f96bd32cafd93bedabaa2400b726d67a675164 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 10 Apr 2024 20:04:28 +0200 Subject: [PATCH 219/506] Fix xymodem error print outs --- src/print.h | 13 +++++++++++++ src/xymodem.c | 25 +++++++++++++------------ 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/print.h b/src/print.h index e646167..0bcf03a 100644 --- a/src/print.h +++ b/src/print.h @@ -80,6 +80,19 @@ extern char ansi_format[]; } \ } +#define tio_error_print(format, args...) \ +{ \ + if (!option.mute) \ + { \ + if (print_tainted) \ + putchar('\n'); \ + if (option.color < 0) \ + fprintf (stdout, "\r[%s] Error: " format "\r\n", timestamp_current_time(), ## args); \ + else \ + ansi_printf("[%s] Error: " format, timestamp_current_time(), ## args); \ + } \ +} + #define tio_printf(format, args...) \ { \ if (!option.mute) \ diff --git a/src/xymodem.c b/src/xymodem.c index b25b382..df83a25 100644 --- a/src/xymodem.c +++ b/src/xymodem.c @@ -20,6 +20,7 @@ #include #include #include "xymodem.h" +#include "print.h" #define SOH 0x01 #define STX 0x02 @@ -105,7 +106,7 @@ static int xmodem_1k(int sio, const void *data, size_t len, int seq) continue; } else if (rc < 0) { - perror("Read sync from serial failed"); + tio_error_print("Read sync from serial failed"); return ERR; } } @@ -138,7 +139,7 @@ static int xmodem_1k(int sio, const void *data, size_t len, int seq) usleep(1000); continue; } - perror("Write packet to serial failed"); + tio_error_print("Write packet to serial failed"); return ERR; } from += rc; @@ -157,7 +158,7 @@ static int xmodem_1k(int sio, const void *data, size_t len, int seq) return ERR; rc = serial_poll(sio, &resp, 1, 50); if (rc < 0) { - perror("Read ack/nak from serial failed"); + tio_error_print("Read ack/nak from serial failed"); return ERR; } else if(rc > 0) { break; @@ -187,14 +188,14 @@ static int xmodem_1k(int sio, const void *data, size_t len, int seq) if (key_hit) return ERR; if (write(sio, EOT, 1) < 0) { - perror("Write EOT to serial failed"); + tio_error_print("Write EOT to serial failed"); return ERR; } write(STDOUT_FILENO, "|", 1); /* 1s timeout */ rc = serial_poll(sio, &resp, 1, 1000); if (rc < 0) { - perror("Read from serial failed"); + tio_error_print("Read from serial failed"); return ERR; } else if(rc == 0) { continue; @@ -227,7 +228,7 @@ static int xmodem(int sio, const void *data, size_t len) continue; } else if (rc < 0) { - perror("Read sync from serial failed"); + tio_error_print("Read sync from serial failed"); return ERR; } } @@ -260,7 +261,7 @@ static int xmodem(int sio, const void *data, size_t len) usleep(1000); continue; } - perror("Write packet to serial failed"); + tio_error_print("Write packet to serial failed"); return ERR; } from += rc; @@ -276,7 +277,7 @@ static int xmodem(int sio, const void *data, size_t len) return ERR; rc = serial_poll(sio, &resp, 1, 50); if (rc < 0) { - perror("Read ack/nak from serial failed"); + tio_error_print("Read ack/nak from serial failed"); return ERR; } else if(rc > 0) { break; @@ -306,14 +307,14 @@ static int xmodem(int sio, const void *data, size_t len) if (key_hit) return ERR; if (write(sio, EOT, 1) < 0) { - perror("Write EOT to serial failed"); + tio_error_print("Write EOT to serial failed"); return ERR; } write(STDOUT_FILENO, "|", 1); /* 1s timeout */ rc = serial_poll(sio, &resp, 1, 1000); if (rc < 0) { - perror("Read from serial failed"); + tio_error_print("Read from serial failed"); return ERR; } else if(rc == 0) { continue; @@ -336,7 +337,7 @@ int xymodem_send(int sio, const char *filename, modem_mode_t mode) /* Open file, map into memory */ fd = open(filename, O_RDONLY); if (fd < 0) { - perror("Could not open file"); + tio_error_print("Could not open file"); return ERR; } fstat(fd, &stat); @@ -344,7 +345,7 @@ int xymodem_send(int sio, const char *filename, modem_mode_t mode) buf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); if (!buf) { close(fd); - perror("Could not mmap file"); + tio_error_print("Could not mmap file"); return ERR; } From 00c8124a0a9ff4b2b50097d9159177013d9dfc70 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 11 Apr 2024 21:20:50 +0200 Subject: [PATCH 220/506] Add lua modem_send(file,protocol) --- man/tio.1.in | 18 ++++++++++++------ src/script.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 3782681..1442975 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -365,20 +365,26 @@ Toggle conversion to uppercase on output .IP "\fBctrl-t v" Show version .IP "\fBctrl-t x" -Send a file using the XMODEM-1K protocol (prompts for file name) -.IP "\fBctrl-t X" -Send a file using the XMODEM-CRC protocol (prompts for file name) +Send file using the XMODEM-1K or XMODEM-CRC protocol (prompts for file name and protocol) .IP "\fBctrl-t y" -Send a file using the YMODEM protocol (prompts for file name) +Send file using the YMODEM protocol (prompts for file name) .IP "\fBctrl-t ctrl-t" Send ctrl-t character .SH "SCRIPT API" .PP -Tio suppots Lua scripting for manipulating the tty device. In addition to the -Lua API tio makes the following functions available: +Tio suppots Lua scripting to easily automate interaction with the tty device. + +This means that in addition to the Lua API tio makes the following functions +available: .TP 6n + +.IP "\fBmodem_send(file, protocol)" +Send file using x/y-modem protocol. + +Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM. + .IP "\fBhigh(line)" Set tty line high. .IP "\fBlow(line)" diff --git a/src/script.c b/src/script.c index c06e93e..e017205 100644 --- a/src/script.c +++ b/src/script.c @@ -31,6 +31,7 @@ #include "print.h" #include "options.h" #include "tty.h" +#include "xymodem.h" static int serial_fd; @@ -153,6 +154,38 @@ static int config_apply(lua_State *L) return 0; } +// lua: modem_send(file, protocol) +static int modem_send(lua_State *L) +{ + const char *file = lua_tostring(L, 1); + int protocol = lua_tointeger(L, 2); + + if (file == NULL) + { + return 0; + } + + switch (protocol) + { + case XMODEM_1K: + tio_printf("Sending file '%s' using XMODEM-1K", file); + tio_printf("%s", xymodem_send(serial_fd, file, XMODEM_1K) < 0 ? "Aborted" : "Done"); + break; + + case XMODEM_CRC: + tio_printf("Sending file '%s' using XMODEM-CRC", file); + tio_printf("%s", xymodem_send(serial_fd, file, XMODEM_CRC) < 0 ? "Aborted" : "Done"); + break; + + case YMODEM: + tio_printf("Sending file '%s' using YMODEM", file); + tio_printf("%s", xymodem_send(serial_fd, file, YMODEM) < 0 ? "Aborted" : "Done"); + break; + } + + return 0; +} + static void script_buffer_run(lua_State *L, const char *script_buffer) { int error; @@ -176,6 +209,7 @@ static const struct luaL_Reg tio_lib[] = { "config_high", config_high}, { "config_low", config_low}, { "config_apply", config_apply}, + { "modem_send", modem_send}, {NULL, NULL} }; @@ -238,6 +272,9 @@ void script_set_globals(lua_State *L) script_set_global(L, "DSR", TIOCM_DSR); script_set_global(L, "CD", TIOCM_CD); script_set_global(L, "RI", TIOCM_RI); + script_set_global(L, "XMODEM_CRC", XMODEM_CRC); + script_set_global(L, "XMODEM_1K", XMODEM_1K); + script_set_global(L, "YMODEM", YMODEM); } void script_run(int fd) From 418a43d96e949fa0f51696120b8e4bf7906484fa Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 12 Apr 2024 00:08:45 +0200 Subject: [PATCH 221/506] Add lua send(string) --- man/tio.1.in | 3 ++- src/script.c | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/man/tio.1.in b/man/tio.1.in index 1442975..bec35c0 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -384,7 +384,8 @@ available: Send file using x/y-modem protocol. Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM. - +.IP "\fBsend(string)" +Send string. .IP "\fBhigh(line)" Set tty line high. .IP "\fBlow(line)" diff --git a/src/script.c b/src/script.c index e017205..063c583 100644 --- a/src/script.c +++ b/src/script.c @@ -19,6 +19,7 @@ * 02110-1301, USA. */ +#include #include #include #include @@ -186,6 +187,28 @@ static int modem_send(lua_State *L) return 0; } +// lua: send(string) +static int send(lua_State *L) +{ + const char *string = lua_tostring(L, 1); + int ret; + + if (string == NULL) + { + return 0; + } + + ret = write(serial_fd, string, strlen(string)); + if (ret < 0) + { + tio_error_print("%s\n", strerror(errno)); + } + + lua_pushnumber(L, ret); + + return 1; +} + static void script_buffer_run(lua_State *L, const char *script_buffer) { int error; @@ -210,6 +233,7 @@ static const struct luaL_Reg tio_lib[] = { "config_low", config_low}, { "config_apply", config_apply}, { "modem_send", modem_send}, + { "send", send}, {NULL, NULL} }; From 5eb649278a6310c8c4645c3bc1d70160ad24ee67 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 12 Apr 2024 00:23:04 +0200 Subject: [PATCH 222/506] Clean up man page --- man/tio.1.in | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index bec35c0..14b0b34 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -21,19 +21,19 @@ devices for basic I/O operations. Set baud rate [bps] (default: 115200). .TP -.BR \-d ", " "\-\-databits 5" | 6 | 7 | 8 +.BR \-d ", " "\-\-databits " 5 | 6 | 7 | 8 Set data bits (default: 8). .TP -.BR \-f ", " "\-\-flow hard" | soft | none +.BR \-f ", " "\-\-flow " hard | soft | none Set flow control (default: none). .TP -.BR \-s ", " "\-\-stopbits 1" | 2 +.BR \-s ", " "\-\-stopbits " 1 | 2 Set stop bits (default: 1). .TP -.BR \-p ", " "\-\-parity odd" | even | none | mark | space +.BR \-p ", " "\-\-parity " odd | even | none | mark | space Set parity (default: none). @@ -191,23 +191,23 @@ If defining more than one flag, the flags must be comma separated. .RE .TP -.BR " \-\-input\-mode " \fInormal|hex +.BR " \-\-input\-mode " normal|hex Set input mode. In hex input mode bytes can be sent by typing the -\fBtwo-character hexadecimal\fR representation of the value, e.g.: to send -\fI0xA\fR you must type \fI0a\fR or \fI0A\fR. +\fBtwo-character hexadecimal\fR representation of the 1 byte value, e.g.: to +send \fI0xA\fR you must type \fI0a\fR or \fI0A\fR. Default value is "normal". .TP -.BR " \-\-output\-mode " \fInormal|hex +.BR " \-\-output\-mode " normal|hex -Set output mode. In hex mode each incoming byte is printed out as a hex value. +Set output mode. In hex mode each incoming byte is printed out as a 1 byte hex value. Default value is "normal". .TP -.BR \-c ", " "\-\-color " \fI0..255|bold|none|list +.BR \-c ", " "\-\-color " 0..255|bold|none|list Colorize tio text using ANSI color code value ranging from 0 to 255 or use "none" for no color or use "bold" to apply bold formatting to existing system From e028544cd04fc1541d84c8da98f85f64c6e1259e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 12 Apr 2024 00:23:52 +0200 Subject: [PATCH 223/506] Update plain text man page --- man/tio.1.txt | 109 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 93 insertions(+), 16 deletions(-) diff --git a/man/tio.1.txt b/man/tio.1.txt index d85deaa..f2c3af8 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -100,7 +100,7 @@ OPTIONS Enable log to file. - The filename will be automatically generated using the following format tio_DEVICE_YYYY-MM-DDTHH:MM:SS.log. + The log file will be automatically named using the following format tio_DEVICE_YYYY-MM-DDTHH:MM:SS.log. The filename can be manually set using the --log-file option. @@ -108,6 +108,10 @@ OPTIONS Set log filename. + --log-directory + + Set log directory path in which to save automatically named log files. + --log-append Append to log file. @@ -138,13 +142,23 @@ OPTIONS OLTU Map lowercase characters to uppercase on output + ONULBRK Map nul (zero) to send break signal on output + MSB2LSB Map MSB bit order to LSB on output If defining more than one flag, the flags must be comma separated. - -x, --hexadecimal + --input-mode normal|hex - Enable hexadecimal mode. + Set input mode. In hex input mode bytes can be sent by typing the two-character hexadecimal representation of the 1 byte value, e.g.: to send 0xA you must type 0a or 0A. + + Default value is "normal". + + --output-mode normal|hex + + Set output mode. In hex mode each incoming byte is printed out as a 1 byte hex value. + + Default value is "normal". -c, --color 0..255|bold|none|list @@ -212,6 +226,20 @@ OPTIONS Default value is "none". + --script + + Run script from string. + + --script-file + + Run script from file with filename. + + --script-run once|always|never + + Run script on connect once, always, or never. + + Default value is "always". + -v, --version Display program version. @@ -237,7 +265,7 @@ KEYS ctrl-t g Toggle serial port line - ctrl-t h Toggle hexadecimal mode + ctrl-t i Toggle input mode ctrl-t l Clear screen @@ -245,10 +273,14 @@ KEYS ctrl-t m Toggle MSB to LSB bit order + ctrl-t o Toggle output mode + ctrl-t p Pulse serial port line ctrl-t q Quit + ctrl-t r Run script + ctrl-t s Show TX/RX statistics ctrl-t t Toggle line timestamp mode @@ -257,18 +289,50 @@ KEYS ctrl-t v Show version - ctrl-t x Send a file using the XMODEM-1K protocol (prompts for file name) + ctrl-t x Send file using the XMODEM-1K or XMODEM-CRC protocol (prompts for file name and protocol) - ctrl-t X Send a file using the XMODEM-CRC protocol (prompts for file name) - - ctrl-t y Send a file using the YMODEM protocol (prompts for file name) + ctrl-t y Send file using the YMODEM protocol (prompts for file name) ctrl-t ctrl-t Send ctrl-t character -HEXADECIMAL MODE - In hexadecimal mode each incoming byte is printed out as a hexadecimal value. +SCRIPT API + Tio suppots Lua scripting to easily automate interaction with the tty device. - Bytes can be sent in this mode by typing the two-character hexadecimal representation of the value, e.g.: to send 0xA you must type 0a or 0A. + This means that in addition to the Lua API tio makes the following functions available: + + modem_send(file, protocol) + Send file using x/y-modem protocol. + + Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM. + + send(string) + Send string. + + high(line) + Set tty line high. + + low(line) + Set tty line low. + + toggle(line) + Toggle the tty line. + + sleep(seconds) + Sleep for seconds. + + msleep(ms) + Sleep for miliseconds. + + config_high(line) + Set tty line state configuration to high. + + config_low(line) + Set tty line state configuration to low. + + apply_config() + Apply tty line state configuration. Using the line state configuration API instead of high()/low() will help to make the lines physically switch as simultaneously as possible. This may solve timing issues on some platforms. + + Note: Line can be any of DTR, RTS, CTS, DSR, CD, RI CONFIGURATION FILE Options can be set via configuration file using the INI format. tio uses the configuration file first found in the following locations in the order listed: @@ -289,8 +353,7 @@ CONFIGURATION FILE The following configuration file options are available: - pattern - Pattern matching user input. This pattern can be an extended regular expression with a single group. + pattern Pattern matching user input. This pattern can be an extended regular expression with a single group. device TTY device to open. If it contains a "%s" it is substituted with the first group match. @@ -316,6 +379,8 @@ CONFIGURATION FILE log-file Set log filename + log-directory Set log directory path in which to save automatically named log files. + log-append Append to log file log-strip Enable strip of control and escape sequences from log @@ -330,11 +395,13 @@ CONFIGURATION FILE color Colorize tio text using ANSI color code ranging from 0 to 255 - hexadecimal Enable hexadecimal mode + input-mode Set input mode. + + output-mode Set output mode. socket Set socket to redirect I/O to - prefix-ctrl-key Set prefix ctrl key (a..z, default: t) + prefix-ctrl-key Set prefix ctrl key (a..z or 'none', default: t) response-wait Enable wait for line response @@ -346,6 +413,12 @@ CONFIGURATION FILE alert Set alert action on connect/disconnect + script Run script from string + + script-file Run script from file + + script-run Run script on connect. + CONFIGURATION FILE EXAMPLES To change the default configuration simply set options like so: @@ -455,10 +528,14 @@ EXAMPLES $ tio --rs-485 --rs-485-config=RTS_ON_SEND=1,RX_DURING_TX /dev/ttyUSB0 + Script manipulation of DTR and RTS lines upon first connect: + + $ tio --script "high(DTR); low(RTS); msleep(100); toggle(DTR)" --script-run once /dev/ttyUSB0 + WEBSITE Visit https://tio.github.io AUTHOR Created by Martin Lund . -tio 2.7 2023-09-19 tio(1) +tio 2.8 2023-09-19 tio(1) From 0afae5d3ee9283c3761047669158f1ad20bcd9c5 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 12 Apr 2024 13:03:51 +0200 Subject: [PATCH 224/506] Update text --- README.md | 10 +++++----- man/tio.1.in | 8 ++++---- src/alert.c | 2 +- src/alert.h | 2 +- src/configfile.c | 2 +- src/configfile.h | 2 +- src/error.c | 2 +- src/error.h | 2 +- src/log.c | 2 +- src/log.h | 2 +- src/main.c | 2 +- src/misc.c | 2 +- src/misc.h | 2 +- src/options.c | 2 +- src/options.h | 2 +- src/print.c | 2 +- src/print.h | 2 +- src/rs485.c | 2 +- src/rs485.h | 2 +- src/script.c | 2 +- src/script.h | 2 +- src/setspeed.c | 2 +- src/setspeed.h | 2 +- src/signals.c | 2 +- src/signals.h | 2 +- src/socket.c | 2 +- src/socket.h | 2 +- src/timestamp.c | 2 +- src/timestamp.h | 2 +- src/tty.c | 2 +- src/tty.h | 2 +- src/xymodem.h | 2 +- 32 files changed, 39 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 0e91e84..b09d0a9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![tio](images/tio-icon.png)]() -# tio - a simple serial device I/O tool +# tio - a serial device I/O tool [![](https://img.shields.io/circleci/build/github/tio/tio)](https://circleci.com/github/tio/tio/tree/master) [![](https://img.shields.io/github/v/release/tio/tio?sort=semver)](https://github.com/tio/tio/releases) @@ -10,9 +10,9 @@ ## 1. Introduction -tio is a simple serial device tool which features a straightforward -command-line and configuration file interface to easily connect to serial TTY -devices for basic I/O operations. +tio is a serial device tool which features a straightforward command-line and +configuration file interface to easily connect to serial TTY devices for basic +I/O operations.

@@ -24,7 +24,7 @@ To make a simpler serial device tool for talking with serial TTY devices with less focus on classic terminal/modem features and more focus on the needs of embedded developers and hackers. -tio was originally created as an alternative to +tio was originally created as an alternative to [screen](https://www.gnu.org/software/screen) for connecting to serial devices when used in combination with [tmux](https://tmux.github.io). diff --git a/man/tio.1.in b/man/tio.1.in index 14b0b34..eafa446 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -1,7 +1,7 @@ .TH "tio" "1" "@version_date@" "tio @version@" "User Commands" .SH "NAME" -tio \- a simple serial device I/O tool +tio \- a serial device I/O tool .SH "SYNOPSIS" .PP @@ -10,9 +10,9 @@ tio \- a simple serial device I/O tool .SH "DESCRIPTION" .PP -\fBtio\fR is a simple serial device tool which features a straightforward -command-line and configuration file interface to easily connect to serial TTY -devices for basic I/O operations. +\fBtio\fR is a serial device tool which features a straightforward command-line +and configuration file interface to easily connect to serial TTY devices for +basic I/O operations. .SH "OPTIONS" diff --git a/src/alert.c b/src/alert.c index 4cb0132..cc6abdd 100644 --- a/src/alert.c +++ b/src/alert.c @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2022 Martin Lund * diff --git a/src/alert.h b/src/alert.h index cb26eff..10932ea 100644 --- a/src/alert.h +++ b/src/alert.h @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2022 Martin Lund * diff --git a/src/configfile.c b/src/configfile.c index f454115..193f7fe 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2020-2022 Liam Beguin * Copyright (c) 2022 Martin Lund diff --git a/src/configfile.h b/src/configfile.h index 74e1b6e..7748149 100644 --- a/src/configfile.h +++ b/src/configfile.h @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2020 Liam Beguin * Copyright (c) 2022 Martin Lund diff --git a/src/error.c b/src/error.c index dfe32aa..8049bc6 100644 --- a/src/error.c +++ b/src/error.c @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2022 Martin Lund * diff --git a/src/error.h b/src/error.h index 109d002..ed81ba3 100644 --- a/src/error.h +++ b/src/error.h @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2022 Martin Lund * diff --git a/src/log.c b/src/log.c index f940d44..0962d26 100644 --- a/src/log.c +++ b/src/log.c @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2022 Martin Lund * diff --git a/src/log.h b/src/log.h index b24114e..d95d9f3 100644 --- a/src/log.h +++ b/src/log.h @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2022 Martin Lund * diff --git a/src/main.c b/src/main.c index ddf8848..4b6d91e 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2022 Martin Lund * diff --git a/src/misc.c b/src/misc.c index 6770a28..55febe6 100644 --- a/src/misc.c +++ b/src/misc.c @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2022 Martin Lund * diff --git a/src/misc.h b/src/misc.h index b05142c..65f7f03 100644 --- a/src/misc.h +++ b/src/misc.h @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2022 Martin Lund * diff --git a/src/options.c b/src/options.c index f50e65f..44fc47f 100644 --- a/src/options.c +++ b/src/options.c @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2022 Martin Lund * diff --git a/src/options.h b/src/options.h index 4e1c17f..b5b5c7b 100644 --- a/src/options.h +++ b/src/options.h @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2022 Martin Lund * diff --git a/src/print.c b/src/print.c index 6e743b8..1c891aa 100644 --- a/src/print.c +++ b/src/print.c @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2022 Martin Lund * diff --git a/src/print.h b/src/print.h index 0bcf03a..f20d173 100644 --- a/src/print.h +++ b/src/print.h @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2022 Martin Lund * diff --git a/src/rs485.c b/src/rs485.c index ee84b60..2cb1067 100644 --- a/src/rs485.c +++ b/src/rs485.c @@ -1,5 +1,5 @@ /* - * tio - a simple serial device I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2022 Martin Lund * diff --git a/src/rs485.h b/src/rs485.h index b7a4882..ed075ab 100644 --- a/src/rs485.h +++ b/src/rs485.h @@ -1,5 +1,5 @@ /* - * tio - a simple serial device I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2022 Martin Lund * diff --git a/src/script.c b/src/script.c index 063c583..bc7d1d5 100644 --- a/src/script.c +++ b/src/script.c @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2024 Martin Lund * diff --git a/src/script.h b/src/script.h index 0ea0faf..a2778e7 100644 --- a/src/script.h +++ b/src/script.h @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2024 Martin Lund * diff --git a/src/setspeed.c b/src/setspeed.c index 012d97b..8ed54c4 100644 --- a/src/setspeed.c +++ b/src/setspeed.c @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2017-2022 Martin Lund * diff --git a/src/setspeed.h b/src/setspeed.h index c201f6e..feef305 100644 --- a/src/setspeed.h +++ b/src/setspeed.h @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2022 Martin Lund * diff --git a/src/signals.c b/src/signals.c index 6829b44..95a6062 100644 --- a/src/signals.c +++ b/src/signals.c @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2022 Martin Lund * diff --git a/src/signals.h b/src/signals.h index 8d8d70f..68c4e76 100644 --- a/src/signals.h +++ b/src/signals.h @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2022 Martin Lund * diff --git a/src/socket.c b/src/socket.c index 23b1249..595efd9 100644 --- a/src/socket.c +++ b/src/socket.c @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2022 Martin Lund * Copyright (c) 2022 Google LLC diff --git a/src/socket.h b/src/socket.h index 2caffaf..523f6c4 100644 --- a/src/socket.h +++ b/src/socket.h @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2022 Martin Lund * Copyright (c) 2022 Google LLC diff --git a/src/timestamp.c b/src/timestamp.c index 91374dd..aae80c3 100644 --- a/src/timestamp.c +++ b/src/timestamp.c @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2022 Martin Lund * diff --git a/src/timestamp.h b/src/timestamp.h index ee23ce6..572be3c 100644 --- a/src/timestamp.h +++ b/src/timestamp.h @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2022 Martin Lund * diff --git a/src/tty.c b/src/tty.c index 2aea470..f282cbe 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2022 Martin Lund * diff --git a/src/tty.h b/src/tty.h index eb4b3bb..633e51c 100644 --- a/src/tty.h +++ b/src/tty.h @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2022 Martin Lund * diff --git a/src/xymodem.h b/src/xymodem.h index 8e8232c..cd837e7 100644 --- a/src/xymodem.h +++ b/src/xymodem.h @@ -1,5 +1,5 @@ /* - * tio - a simple serial terminal I/O tool + * tio - a serial device I/O tool * * Copyright (c) 2014-2024 Martin Lund * From fc54df1f227d2d14136cac2c54299eae66f1f08e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 12 Apr 2024 18:20:27 +0200 Subject: [PATCH 225/506] Add lua expect(string) Add simple expect functionality. The expect(string) function will wait for input from the tty device and only return when there is a string match. Regular expressions are supported. Example: script = expect('password:'); send('my_password\n') --- man/tio.1.in | 15 +++++--- src/misc.c | 25 +++++++++++++ src/misc.h | 1 + src/script.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 133 insertions(+), 7 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index eafa446..eb61cb3 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -375,17 +375,18 @@ Send ctrl-t character .PP Tio suppots Lua scripting to easily automate interaction with the tty device. -This means that in addition to the Lua API tio makes the following functions -available: +In addition to the Lua API tio makes the following functions available: .TP 6n +.IP "\fBexpect(string)" +Expect string - waits for string to match before continueing. Supports regular expressions. Special characters must be escaped with '\\\\'. +.IP "\fBsend(string)" +Send string. .IP "\fBmodem_send(file, protocol)" Send file using x/y-modem protocol. Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM. -.IP "\fBsend(string)" -Send string. .IP "\fBhigh(line)" Set tty line high. .IP "\fBlow(line)" @@ -660,10 +661,14 @@ Enable RS-485 mode: $ tio --rs-485 --rs-485-config=RTS_ON_SEND=1,RX_DURING_TX /dev/ttyUSB0 .TP -Script manipulation of DTR and RTS lines upon first connect: +Manipulate DTR and RTS lines upon first connect to reset connected microcontroller: $ tio --script "high(DTR); low(RTS); msleep(100); toggle(DTR)" --script-run once /dev/ttyUSB0 +.TP +Automatically log in to connected OS: + +$ tio --script "expect('password:'); send('my_password\\n')" /dev/ttyUSB0 .SH "WEBSITE" .PP diff --git a/src/misc.c b/src/misc.c index 55febe6..06d7512 100644 --- a/src/misc.c +++ b/src/misc.c @@ -20,6 +20,7 @@ */ #include "config.h" +#include #include #include #include @@ -87,3 +88,27 @@ bool fs_dir_exists(const char *path) return true; } + +bool regex_match(const char *string, const char *pattern) +{ + regex_t regex; + int status; + + if (regcomp(®ex, pattern, REG_EXTENDED | REG_NOSUB) != 0) + { + // No match + return false; + } + + status = regexec(®ex, string, (size_t) 0, NULL, 0); + regfree(®ex); + + if (status != 0) + { + // No match + return false; + } + + // Match + return true; +} diff --git a/src/misc.h b/src/misc.h index 65f7f03..ec80d3d 100644 --- a/src/misc.h +++ b/src/misc.h @@ -32,3 +32,4 @@ int ctrl_key_code(unsigned char key); void alert_connect(void); void alert_disconnect(void); bool fs_dir_exists(const char *path); +bool regex_match(const char *string, const char *pattern); diff --git a/src/script.c b/src/script.c index bc7d1d5..bd1d6fe 100644 --- a/src/script.c +++ b/src/script.c @@ -20,6 +20,7 @@ */ #include +#include #include #include #include @@ -34,7 +35,11 @@ #include "tty.h" #include "xymodem.h" +#define MAX_BUFFER_SIZE 2000 // Maximum size of circular buffer + static int serial_fd; +static char circular_buffer[MAX_BUFFER_SIZE]; +static int buffer_size = 0; // lua: sleep(seconds) static int sleep_(lua_State *L) @@ -209,6 +214,95 @@ static int send(lua_State *L) return 1; } +// Function to add a character to the circular buffer +void add_to_buffer(char c) +{ + if (buffer_size < MAX_BUFFER_SIZE) + { + circular_buffer[buffer_size++] = c; + } + else + { + // Shift the buffer to accommodate the new character + memmove(circular_buffer, circular_buffer + 1, MAX_BUFFER_SIZE - 1); + circular_buffer[MAX_BUFFER_SIZE - 1] = c; + } +} + +// Function to match against the circular buffer using regex +bool match_regex(regex_t *regex) +{ + char buffer[MAX_BUFFER_SIZE + 1]; // Temporary buffer for regex matching + memcpy(buffer, circular_buffer, buffer_size); + buffer[buffer_size] = '\0'; // Null-terminate the buffer + + // Match against the regex + int ret = regexec(regex, buffer, 0, NULL, 0); + if (!ret) + { + // Match found + return true; + } + else if (ret == REG_NOMATCH) + { + // No match found, do nothing + } + else + { + // Error occurred during matching + tio_error_print("Regex match failed"); + } + + return false; +} + +// lua: expect(string) +static int expect(lua_State *L) +{ + const char *string = lua_tostring(L, 1); + regex_t regex; + int ret = 0; + char c; + + if (string == NULL) + { + ret = -1; + goto error; + } + + // Compile the regular expression + ret = regcomp(®ex, string, REG_EXTENDED); + if (ret) + { + tio_error_print("Could not compile regex"); + ret = -1; + goto error; + } + + // Main loop to read and match + while (true) + { + ssize_t bytes_read = read(serial_fd, &c, 1); + if (bytes_read > 0) + { + putchar(c); + add_to_buffer(c); + // Match against the entire buffer + if (match_regex(®ex)) + { + break; + } + } + } + + // Cleanup + regfree(®ex); + +error: + lua_pushnumber(L, ret); + return 1; +} + static void script_buffer_run(lua_State *L, const char *script_buffer) { int error; @@ -217,7 +311,7 @@ static void script_buffer_run(lua_State *L, const char *script_buffer) lua_pcall(L, 0, 0, 0); if (error) { - tio_warning_printf("%s\n", lua_tostring(L, -1)); + tio_warning_printf("lua: %s\n", lua_tostring(L, -1)); lua_pop(L, 1); /* pop error message from the stack */ } } @@ -234,6 +328,7 @@ static const struct luaL_Reg tio_lib[] = { "config_apply", config_apply}, { "modem_send", modem_send}, { "send", send}, + { "expect", expect}, {NULL, NULL} }; @@ -276,7 +371,7 @@ void script_file_run(lua_State *L, const char *filename) if (luaL_dofile(L, filename)) { - tio_warning_printf("%s\n", lua_tostring(L, -1)); + tio_warning_printf("lua: %s\n", lua_tostring(L, -1)); lua_pop(L, 1); /* pop error message from the stack */ return; } From 6c75ec553d0aacfe9acb37a19d529d230cad9c23 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 12 Apr 2024 18:34:45 +0200 Subject: [PATCH 226/506] Update README --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b09d0a9..d14105b 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ when used in combination with [tmux](https://tmux.github.io). * Sensible defaults (115200 8n1) * Support for non-standard baud rates * Support for mark and space parity - * X-modem (1K) and Y-modem file upload + * X-modem (1K/CRC) and Y-modem file upload * Support for RS-485 mode * List available serial devices by ID * Show RX/TX statistics @@ -59,9 +59,10 @@ when used in combination with [tmux](https://tmux.github.io). * Remapping of prefix key * Support NO_COLOR env variable as per no-color.org * Man page documentation - * Lua scripting support + * Lua scripting support for automating interaction with serial device * Manipulate port control lines at connect/reconnect (useful for microcontroller reset/boot etc.) - * Automate interaction with tty device (TBD) + * Simple expect/send like functionality with support for regular expressions + * Send files via x/y-modem protocol * Plays nicely with [tmux](https://tmux.github.io) ## 3. Usage From 7915c1a445b495b5b5de4ed6e5ee5d777cdd3d27 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 12 Apr 2024 18:51:17 +0200 Subject: [PATCH 227/506] Update README --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index d14105b..22dc26b 100644 --- a/README.md +++ b/README.md @@ -207,8 +207,7 @@ ctrl-t ? to list the available key commands. [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-1K -[15:02:53.269] ctrl-t X Send file via Xmodem-CRC +[15:02:53.269] ctrl-t x Send file via Xmodem [15:02:53.269] ctrl-t y Send file via Ymodem [15:02:53.269] ctrl-t ctrl-t Send ctrl-t character ``` From fca76a017da6b342589d3d93cde0c09a7f306edf Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 12 Apr 2024 21:15:36 +0200 Subject: [PATCH 228/506] Fix text alignment --- src/tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tty.c b/src/tty.c index f282cbe..af79f3f 100644 --- a/src/tty.c +++ b/src/tty.c @@ -793,7 +793,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) 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 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); + tio_printf(" ctrl-%c ctrl-%c Send ctrl-%c character", option.prefix_key, option.prefix_key, option.prefix_key); break; case KEY_SHIFT_L: From 2db87ede53941c56148ca05ace84721348a65530 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 12 Apr 2024 22:03:03 +0200 Subject: [PATCH 229/506] Update README --- README.md | 69 +++++++++++++++++++++++++++++++++++++++++++++++---- man/tio.1.txt | 21 ++++++++++------ 2 files changed, 78 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 22dc26b..daa5299 100644 --- a/README.md +++ b/README.md @@ -59,9 +59,9 @@ when used in combination with [tmux](https://tmux.github.io). * Remapping of prefix key * Support NO_COLOR env variable as per no-color.org * Man page documentation - * Lua scripting support for automating interaction with serial device - * Manipulate port control lines at connect/reconnect (useful for microcontroller reset/boot etc.) + * Lua scripting support for automating interaction with serial device at connect * Simple expect/send like functionality with support for regular expressions + * Manipulate port control lines (useful for microcontroller reset/boot etc.) * Send files via x/y-modem protocol * Plays nicely with [tmux](https://tmux.github.io) @@ -209,12 +209,63 @@ ctrl-t ? to list the available key commands. [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 -[15:02:53.269] ctrl-t ctrl-t Send ctrl-t character +[15:02:53.269] ctrl-t ctrl-t Send ctrl-t character ``` If needed, the prefix key (ctrl-t) can be remapped via configuration file. -### 3.3 Configuration file +### 3.3 Lua 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: + +``` + expect(string) + Expect string - waits for string to match before continueing. + + Supports regular expressions. Special characters must be escaped with '\\'. + + send(string) + Send string. + + modem_send(file, protocol) + Send file using x/y-modem protocol. + + Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM. + + high(line) + Set tty line high. + + low(line) + Set tty line low. + + toggle(line) + Toggle the tty line. + + sleep(seconds) + Sleep for seconds. + + msleep(ms) + Sleep for miliseconds. + + config_high(line) + Set tty line state configuration to high. + + config_low(line) + Set tty line state configuration to low. + + apply_config() + Apply tty line state configuration. + + Using the line state configuration API instead of high()/low() will + help to make the lines physically switch as simultaneously as possible. + This may solve timing issues on some platforms. + + Note: Line can be any of DTR, RTS, CTS, DSR, CD, RI +``` + +### 3.4 Configuration file Options can be set via the configuration file first found in any of the following locations in the order listed: @@ -226,6 +277,8 @@ The configuration file supports sub-configurations using named sections which ca be activated via the command-line by name or pattern. A sub-configuration specifies which TTY device to connect to and other options. +### 3.4.1 Examples + Example configuration file: ``` @@ -245,10 +298,16 @@ log-file = rpi3.log line-pulse-duration = DTR=200,RTS=150 color = 12 +[esp32] +device = /dev/serial/by-id/usb-0403_6014-if00-port0 +script = high(DTR); low(RTS); msleep(100); low(DTR); high(RTS); msleep(100); low(RTS) +script-run = always +color = 13 + [usb devices] pattern = usb([0-9]*) device = /dev/ttyUSB%s -color = 13 +color = 14 ``` To use a specific sub-configuration by name simply start tio like so: diff --git a/man/tio.1.txt b/man/tio.1.txt index f2c3af8..fa3cc0e 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -1,13 +1,13 @@ tio(1) User Commands tio(1) NAME - tio - a simple serial device I/O tool + tio - a serial device I/O tool SYNOPSIS tio [] DESCRIPTION - tio is a simple serial device tool which features a straightforward command-line and configuration file interface to easily connect to serial TTY devices for basic I/O operations. + tio is a serial device tool which features a straightforward command-line and configuration file interface to easily connect to serial TTY devices for basic I/O operations. OPTIONS -b, --baudrate @@ -298,16 +298,19 @@ KEYS SCRIPT API Tio suppots Lua scripting to easily automate interaction with the tty device. - This means that in addition to the Lua API tio makes the following functions available: + In addition to the Lua API tio makes the following functions available: + + expect(string) + Expect string - waits for string to match before continueing. Supports regular expressions. Special characters must be escaped with '\\'. + + send(string) + Send string. modem_send(file, protocol) Send file using x/y-modem protocol. Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM. - send(string) - Send string. - high(line) Set tty line high. @@ -528,10 +531,14 @@ EXAMPLES $ tio --rs-485 --rs-485-config=RTS_ON_SEND=1,RX_DURING_TX /dev/ttyUSB0 - Script manipulation of DTR and RTS lines upon first connect: + Manipulate DTR and RTS lines upon first connect to reset connected microcontroller: $ tio --script "high(DTR); low(RTS); msleep(100); toggle(DTR)" --script-run once /dev/ttyUSB0 + Automatically log in to connected OS: + + $ tio --script "expect('password:'); send('my_password\n')" /dev/ttyUSB0 + WEBSITE Visit https://tio.github.io From fba73f98dbf143cbeb2d893d3e33e7b1cbeb108a Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 13 Apr 2024 00:11:44 +0200 Subject: [PATCH 230/506] Update README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index daa5299..8a69268 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,8 @@ when used in combination with [tmux](https://tmux.github.io). * Remapping of prefix key * Support NO_COLOR env variable as per no-color.org * Man page documentation - * Lua scripting support for automating interaction with serial device at connect + * Lua scripting support for automation + * Run script at connect once/always/never * Simple expect/send like functionality with support for regular expressions * Manipulate port control lines (useful for microcontroller reset/boot etc.) * Send files via x/y-modem protocol From 3ad090caf7ecd317427316bce44a3e46399e9225 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 13 Apr 2024 00:56:41 +0200 Subject: [PATCH 231/506] Update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8a69268..32a826d 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ when used in combination with [tmux](https://tmux.github.io). * Support NO_COLOR env variable as per no-color.org * Man page documentation * Lua scripting support for automation - * Run script at connect once/always/never + * Run script manually or automatically at connect once/always/never * Simple expect/send like functionality with support for regular expressions * Manipulate port control lines (useful for microcontroller reset/boot etc.) * Send files via x/y-modem protocol @@ -302,7 +302,7 @@ color = 12 [esp32] device = /dev/serial/by-id/usb-0403_6014-if00-port0 script = high(DTR); low(RTS); msleep(100); low(DTR); high(RTS); msleep(100); low(RTS) -script-run = always +script-run = once color = 13 [usb devices] From 51300cc4f0380cb551ef31e945d329e4cca03a1a Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 13 Apr 2024 15:30:14 +0200 Subject: [PATCH 232/506] Add timeout feature to expect() --- README.md | 6 ++++-- man/tio.1.in | 6 ++++-- src/misc.c | 29 +++++++++++++++++++++++++++++ src/misc.h | 2 ++ src/script.c | 18 +++++++++++++++--- src/xymodem.c | 33 ++++++--------------------------- 6 files changed, 60 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 32a826d..dadeb46 100644 --- a/README.md +++ b/README.md @@ -222,11 +222,13 @@ Tio suppots Lua scripting to easily automate interaction with the tty device. In addition to the Lua API tio makes the following functions available: ``` - expect(string) - Expect string - waits for string to match before continueing. + 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. + send(string) Send string. diff --git a/man/tio.1.in b/man/tio.1.in index eb61cb3..1bc32c8 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -379,8 +379,10 @@ In addition to the Lua API tio makes the following functions available: .TP 6n -.IP "\fBexpect(string)" -Expect string - waits for string to match before continueing. Supports regular expressions. Special characters must be escaped with '\\\\'. +.IP "\fBexpect(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. .IP "\fBsend(string)" Send string. .IP "\fBmodem_send(file, protocol)" diff --git a/src/misc.c b/src/misc.c index 06d7512..cf57724 100644 --- a/src/misc.c +++ b/src/misc.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "error.h" #include "print.h" #include "options.h" @@ -112,3 +113,31 @@ bool regex_match(const char *string, const char *pattern) // Match return true; } + +int read_poll(int fd, void *data, size_t len, int timeout) +{ + struct pollfd fds; + int ret = 0; + + fds.events = POLLIN; + fds.fd = fd; + + /* Wait data available */ + ret = poll(&fds, 1, timeout); + if (ret < 0) + { + tio_error_print("%s", strerror(errno)); + return ret; + } + else if (ret > 0) + { + if (fds.revents & POLLIN) + { + // Read ready data + return read(fd, data, len); + } + } + + /* Timeout */ + return ret; +} diff --git a/src/misc.h b/src/misc.h index ec80d3d..c669cbd 100644 --- a/src/misc.h +++ b/src/misc.h @@ -22,6 +22,7 @@ #pragma once #include +#include #define UNUSED(expr) do { (void)(expr); } while (0) @@ -33,3 +34,4 @@ void alert_connect(void); void alert_disconnect(void); bool fs_dir_exists(const char *path); bool regex_match(const char *string, const char *pattern); +int read_poll(int fd, void *data, size_t len, int timeout); diff --git a/src/script.c b/src/script.c index bd1d6fe..7874574 100644 --- a/src/script.c +++ b/src/script.c @@ -256,20 +256,27 @@ bool match_regex(regex_t *regex) return false; } -// lua: expect(string) +// 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; - if (string == NULL) + 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) @@ -282,7 +289,7 @@ static int expect(lua_State *L) // Main loop to read and match while (true) { - ssize_t bytes_read = read(serial_fd, &c, 1); + ssize_t bytes_read = read_poll(serial_fd, &c, 1, timeout); if (bytes_read > 0) { putchar(c); @@ -293,6 +300,11 @@ static int expect(lua_State *L) break; } } + else + { + // Timeout or error + break; + } } // Cleanup diff --git a/src/xymodem.c b/src/xymodem.c index df83a25..83ac6dc 100644 --- a/src/xymodem.c +++ b/src/xymodem.c @@ -65,27 +65,6 @@ static uint16_t crc16(const uint8_t *data, uint16_t size) return crc; } -static int serial_poll(int sio, void *data, size_t len, int timeout) -{ - struct pollfd fds[1]; - fds[0].fd = sio; - fds[0].events = POLLIN ; - int ret = 0; - - /* Wait data available */ - ret = poll(fds, 1, timeout); - if (ret < 0) { - return ret; - } - else if (ret > 0) { - if(fds[0].revents & POLLIN) { - return read(sio, data, len); - } - } - /* Timeout */ - return ret; -} - static int xmodem_1k(int sio, const void *data, size_t len, int seq) { struct xpacket_1k packet; @@ -99,7 +78,7 @@ static int xmodem_1k(int sio, const void *data, size_t len, int seq) while(1) { if (key_hit) return -1; - rc = serial_poll(sio, &resp, 1, 50); + rc = read_poll(sio, &resp, 1, 50); if (rc == 0) { if (resp == 'C') break; if (resp == CAN) return ERR; @@ -156,7 +135,7 @@ static int xmodem_1k(int sio, const void *data, size_t len, int seq) for(int n=0; n < 20; n++) { if (key_hit) return ERR; - rc = serial_poll(sio, &resp, 1, 50); + rc = read_poll(sio, &resp, 1, 50); if (rc < 0) { tio_error_print("Read ack/nak from serial failed"); return ERR; @@ -193,7 +172,7 @@ static int xmodem_1k(int sio, const void *data, size_t len, int seq) } write(STDOUT_FILENO, "|", 1); /* 1s timeout */ - rc = serial_poll(sio, &resp, 1, 1000); + rc = read_poll(sio, &resp, 1, 1000); if (rc < 0) { tio_error_print("Read from serial failed"); return ERR; @@ -221,7 +200,7 @@ static int xmodem(int sio, const void *data, size_t len) while(1) { if (key_hit) return -1; - rc = serial_poll(sio, &resp, 1, 50); + rc = read_poll(sio, &resp, 1, 50); if (rc == 0) { if (resp == 'C') break; if (resp == CAN) return ERR; @@ -275,7 +254,7 @@ static int xmodem(int sio, const void *data, size_t len) for(int n=0; n < 20; n++) { if (key_hit) return ERR; - rc = serial_poll(sio, &resp, 1, 50); + rc = read_poll(sio, &resp, 1, 50); if (rc < 0) { tio_error_print("Read ack/nak from serial failed"); return ERR; @@ -312,7 +291,7 @@ static int xmodem(int sio, const void *data, size_t len) } write(STDOUT_FILENO, "|", 1); /* 1s timeout */ - rc = serial_poll(sio, &resp, 1, 1000); + rc = read_poll(sio, &resp, 1, 1000); if (rc < 0) { tio_error_print("Read from serial failed"); return ERR; From c5dac4fd33838b27ff225bde950224cc4bbdb211 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 13 Apr 2024 15:44:09 +0200 Subject: [PATCH 233/506] pdate README --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dadeb46..0558d32 100644 --- a/README.md +++ b/README.md @@ -250,7 +250,7 @@ In addition to the Lua API tio makes the following functions available: Sleep for seconds. msleep(ms) - Sleep for miliseconds. + Sleep for milliseconds. config_high(line) Set tty line state configuration to high. @@ -299,6 +299,11 @@ no-autoconnect = enable log = enable log-file = rpi3.log line-pulse-duration = DTR=200,RTS=150 +color = 11 + +[svf2] +device = /dev/ttyUSB0 +script = expect("login: "); send("root\n"); expect("Password: "); send("root\n") color = 12 [esp32] From e1e3e298bf8e6fd3bb3a8620c363a077d6019a7e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 13 Apr 2024 16:44:07 +0200 Subject: [PATCH 234/506] Add lua exit(code) --- README.md | 3 +++ man/tio.1.in | 2 ++ src/script.c | 11 +++++++++++ 3 files changed, 16 insertions(+) diff --git a/README.md b/README.md index 0558d32..fc6d3c1 100644 --- a/README.md +++ b/README.md @@ -237,6 +237,9 @@ In addition to the Lua API tio makes the following functions available: Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM. + exit(code) + Exit with code. + high(line) Set tty line high. diff --git a/man/tio.1.in b/man/tio.1.in index 1bc32c8..45ee188 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -389,6 +389,8 @@ Send string. Send file using x/y-modem protocol. Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM. +.IP "\fBexit(code)" +Exit with exit code. .IP "\fBhigh(line)" Set tty line high. .IP "\fBlow(line)" diff --git a/src/script.c b/src/script.c index 7874574..d8c4487 100644 --- a/src/script.c +++ b/src/script.c @@ -315,6 +315,16 @@ error: return 1; } +// lua: exit(code) +static int exit_(lua_State *L) +{ + long code = lua_tointeger(L, 1); + + exit(code); + + return 0; +} + static void script_buffer_run(lua_State *L, const char *script_buffer) { int error; @@ -341,6 +351,7 @@ static const struct luaL_Reg tio_lib[] = { "modem_send", modem_send}, { "send", send}, { "expect", expect}, + { "exit", exit_}, {NULL, NULL} }; From 97537853a89c6d2b39aa79058b14b523afa03adb Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 13 Apr 2024 23:16:31 +0200 Subject: [PATCH 235/506] Remove options --response-wait, --response-timeout Remove options and rework input handling so it is possible to do the same thing but via script which is much more flexible. These options were always a bit of a hardcoded solution. With the new script expect feature we can wait for any type of response. For example, pipe command to serial device and wait for line response within 1 second: $ echo "*IDN?" | tio /dev/ttyACM0 --script "expect('\r\n', 1000)" --mute --- README.md | 9 ++-- example/config | 1 - man/tio.1.in | 24 +---------- src/bash-completion/tio.in | 10 ----- src/configfile.c | 8 ---- src/main.c | 6 --- src/options.c | 15 ------- src/options.h | 2 - src/tty.c | 87 ++++++++++++++++++-------------------- 9 files changed, 46 insertions(+), 116 deletions(-) diff --git a/README.md b/README.md index fc6d3c1..ac04486 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,6 @@ when used in combination with [tmux](https://tmux.github.io). * Activate sub-configurations by name or pattern * Redirect I/O to UNIX socket or IPv4/v6 network socket for scripting or TTY sharing * Pipe input and/or output - * Support for simple line request/response handling * Bash completion on options, serial device names, and sub-configuration names * Configurable text color * Visual or audible alert on connect/disconnect @@ -104,8 +103,6 @@ Options: -m, --map Map characters -c, --color 0..255|bold|none|list Colorize tio text (default: bold) -S, --socket Redirect I/O to socket - -r, --response-wait Wait for line response then quit - --response-timeout Response timeout (default: 100) --rs-485 Enable RS-485 mode --rs-485-config Set RS-485 configuration --alert bell|blink|none Alert on connect/disconnect (default: none) @@ -171,14 +168,14 @@ Redirect I/O to IPv4 network socket on port 4242: $ tio --socket inet:4242 /dev/ttyUSB0 ``` -Inject data to the serial device: +Pipe data to the serial device: ``` $ cat data.bin | tio /dev/ttyUSB0 ``` -Send command to serial device and wait for line response: +Pipe command to serial device and wait for line response within 1 second: ``` -$ echo "*IDN?" | tio /dev/ttyACM0 --response-wait +$ echo "*IDN?" | tio /dev/ttyACM0 --script "expect('\r\n', 1000)" --mute KORAD KD3305P V4.2 SN:32475045 ``` diff --git a/example/config b/example/config index 2c01505..5f72f2a 100644 --- a/example/config +++ b/example/config @@ -27,7 +27,6 @@ log-strip = disable local-echo = disable color = bold rs-485 = disable -response-wait = disable alert = none script-run = always diff --git a/man/tio.1.in b/man/tio.1.in index 45ee188..18d4cbf 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -245,21 +245,6 @@ If port is 0 or no port is provided default port 3333 is used. At present there is a hardcoded limit of 16 clients connected at one time. .RE -.TP -.BR \-r ", " \-\-response\-wait - -Wait for line response then quit. A line is considered any string terminated -with a NL character. If no line is received tio will quit after response -timeout. - -Any tio text is automatically muted when piping a string to tio while in -response mode to make it easy to parse the response. - -.TP -.BR " \-\-response\-timeout " \fI - -Set timeout [ms] of line response (default: 100). - .TP .BR " \-\-rs\-485" @@ -494,10 +479,6 @@ Set output mode. Set socket to redirect I/O to .IP "\fBprefix-ctrl-key" Set prefix ctrl key (a..z or 'none', default: t) -.IP "\fBresponse-wait" -Enable wait for line response -.IP "\fBresponse-timeout" -Set line response timeout .IP "\fBrs-485" Enable RS-485 mode .IP "\fBrs-485-config" @@ -648,11 +629,10 @@ Pipe command to the serial device: $ echo "ls -la" | tio /dev/serial/by\-id/usb\-FTDI_TTL232R-3V3_FTGQVXBL\-if00\-port0 .TP -Pipe command to the serial device and wait for line response (string ending with CR or NL): +Pipe command to serial device and wait for line response within 1 second: -$ echo "*IDN?" | tio /dev/ttyACM0 --response-wait +$ echo "*IDN?" | tio /dev/ttyACM0 --script "expect('\\r\\n', 1000)" --mute .TP -In this mode, only the response will be printed. .TP Likewise, to pipe data from file to the serial device: diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index c94c0cb..10bf6ef 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -33,8 +33,6 @@ _tio() -S --socket \ --input-mode \ --output-mode \ - -r --response-wait \ - --response-timeout \ --rs-485 \ --rs-485-config \ --alert \ @@ -140,14 +138,6 @@ _tio() COMPREPLY=( $(compgen -W "normal hex" -- ${cur}) ) return 0 ;; - -r | --response-wait) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - ;; - --response-timeout) - COMPREPLY=( $(compgen -W "1 10 100" -- ${cur}) ) - return 0 - ;; --rs-485) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 diff --git a/src/configfile.c b/src/configfile.c index 193f7fe..b1ef72c 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -276,14 +276,6 @@ static int data_handler(void *user, const char *section, const char *name, option.prefix_key = value[0]; } } - else if (!strcmp(name, "response-wait")) - { - option.response_wait = read_boolean(value, name); - } - else if (!strcmp(name, "response-timeout")) - { - option.response_timeout = read_integer(value, name, 0, LONG_MAX); - } else if (!strcmp(name, "rs-485")) { option.rs485 = read_boolean(value, name); diff --git a/src/main.c b/src/main.c index 4b6d91e..eb9aa60 100644 --- a/src/main.c +++ b/src/main.c @@ -69,12 +69,6 @@ int main(int argc, char *argv[]) { // Enter non interactive mode interactive_mode = false; - - // Mute tio text in response mode - if (option.response_wait) - { - option.mute = true; - } } /* Configure output terminal */ diff --git a/src/options.c b/src/options.c index 44fc47f..427e4e8 100644 --- a/src/options.c +++ b/src/options.c @@ -50,7 +50,6 @@ enum opt_t OPT_LOG_STRIP, OPT_LOG_APPEND, OPT_LINE_PULSE_DURATION, - OPT_RESPONSE_TIMEOUT, OPT_RS485, OPT_RS485_CONFIG, OPT_ALERT, @@ -96,8 +95,6 @@ struct option_t option = .prefix_code = 20, // ctrl-t .prefix_key = 't', .prefix_enabled = true, - .response_wait = false, - .response_timeout = 100, .mute = false, .rs485 = false, .rs485_config_flags = 0, @@ -142,8 +139,6 @@ void print_help(char *argv[]) printf(" -m, --map Map characters\n"); printf(" -c, --color 0..255|bold|none|list Colorize tio text (default: bold)\n"); printf(" -S, --socket Redirect I/O to socket\n"); - printf(" -r, --response-wait Wait for line response then quit\n"); - printf(" --response-timeout Response timeout (default: 100)\n"); printf(" --rs-485 Enable RS-485 mode\n"); printf(" --rs-485-config Set RS-485 configuration\n"); printf(" --alert bell|blink|none Alert on connect/disconnect (default: none)\n"); @@ -375,8 +370,6 @@ void options_parse(int argc, char *argv[]) {"color", required_argument, 0, 'c' }, {"input-mode", required_argument, 0, OPT_INPUT_MODE }, {"output-mode", required_argument, 0, OPT_OUTPUT_MODE }, - {"response-wait", no_argument, 0, 'r' }, - {"response-timeout", required_argument, 0, OPT_RESPONSE_TIMEOUT }, {"rs-485", no_argument, 0, OPT_RS485 }, {"rs-485-config", required_argument, 0, OPT_RS485_CONFIG }, {"alert", required_argument, 0, OPT_ALERT }, @@ -531,14 +524,6 @@ void options_parse(int argc, char *argv[]) option.output_mode = output_mode_option_parse(optarg); break; - case 'r': - option.response_wait = true; - break; - - case OPT_RESPONSE_TIMEOUT: - option.response_timeout = string_to_long(optarg); - break; - case OPT_RS485: option.rs485 = true; break; diff --git a/src/options.h b/src/options.h index b5b5c7b..7a6b627 100644 --- a/src/options.h +++ b/src/options.h @@ -77,8 +77,6 @@ struct option_t unsigned char prefix_code; unsigned char prefix_key; bool prefix_enabled; - bool response_wait; - int response_timeout; bool mute; bool rs485; uint32_t rs485_config_flags; diff --git a/src/tty.c b/src/tty.c index af79f3f..b85cbb7 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1536,9 +1536,6 @@ int tty_connect(void) int status; bool next_timestamp = false; char* now = NULL; - struct timeval tv; - struct timeval *tv_p = &tv; - bool ignore_stdin = false; /* Open tty device */ fd = open(option.tty_device, O_RDWR | O_NOCTTY | O_NONBLOCK); @@ -1629,6 +1626,35 @@ int tty_connect(void) } } + /* If stdin is a pipe forward all input to tty device */ + if (interactive_mode == false) + { + while (true) + { + int ret = read(pipefd[0], &input_char, 1); + if (ret < 0) + { + tio_error_printf("Could not read from pipe (%s)", strerror(errno)); + exit(EXIT_FAILURE); + } + else if (ret > 0) + { + // Forward to tty device + ret = write(fd, &input_char, 1); + if (ret < 0) + { + tio_error_printf("Could not write to serial device (%s)", strerror(errno)); + exit(EXIT_FAILURE); + } + } + else + { + // EOF - finished forwarding + break; + } + } + } + /* Manage script activation */ if (option.script_run != SCRIPT_RUN_NEVER) { @@ -1640,33 +1666,24 @@ int tty_connect(void) } } + // Exit if piped input + if (interactive_mode == false) + { + exit(EXIT_SUCCESS); + } + /* Input loop */ while (true) { FD_ZERO(&rdfs); FD_SET(fd, &rdfs); - if (!ignore_stdin) - { - FD_SET(pipefd[0], &rdfs); - } + FD_SET(pipefd[0], &rdfs); + maxfd = MAX(fd, pipefd[0]); maxfd = MAX(maxfd, socket_add_fds(&rdfs, true)); - /* Manage timeout */ - if ((option.response_wait) && (option.response_timeout != 0)) - { - // Set response timeout - tv_p->tv_sec = 0; - tv_p->tv_usec = option.response_timeout * 1000; - } - else - { - // No timeout - tv_p = NULL; - } - /* Block until input becomes available */ - status = select(maxfd + 1, &rdfs, NULL, NULL, tv_p); + status = select(maxfd + 1, &rdfs, NULL, NULL, NULL); if (status > 0) { bool forward = false; @@ -1750,15 +1767,6 @@ int tty_connect(void) { next_timestamp = true; } - - if (option.response_wait) - { - if (input_char == '\n') - { - tty_sync(fd); - exit(EXIT_SUCCESS); - } - } } } else if (FD_ISSET(pipefd[0], &rdfs)) @@ -1772,22 +1780,9 @@ int tty_connect(void) } else if (bytes_read == 0) { - /* Reached EOF (when piping to stdin) */ - if (option.response_wait) - { - /* Stdin pipe closed but not blocking so stop listening - * to stdin in response mode. - * - * Note: select() really indicates not if data is ready - * but if file descriptor is non-blocking for I/O - * operation. */ - ignore_stdin = true; - } - else - { - tty_sync(fd); - exit(EXIT_SUCCESS); - } + /* Reached EOF (when piping to stdin, never reached) */ + tty_sync(fd); + exit(EXIT_SUCCESS); } /* Process input byte by byte */ From 7dbb8063113a3323a747f107478009864a004ebd Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 14 Apr 2024 01:08:37 +0200 Subject: [PATCH 236/506] Upgrade inih subproject --- subprojects/libinih.wrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subprojects/libinih.wrap b/subprojects/libinih.wrap index 8a7447c..fc5f690 100644 --- a/subprojects/libinih.wrap +++ b/subprojects/libinih.wrap @@ -1,4 +1,4 @@ [wrap-git] directory=libinih url=https://github.com/benhoyt/inih.git -revision=r56 +revision=r58 From a1a4dc464273026446f607846c1da87c88347558 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 14 Apr 2024 01:42:43 +0200 Subject: [PATCH 237/506] Update plain text man page --- man/tio.1.txt | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/man/tio.1.txt b/man/tio.1.txt index fa3cc0e..1da130f 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -188,16 +188,6 @@ OPTIONS At present there is a hardcoded limit of 16 clients connected at one time. - -r, --response-wait - - Wait for line response then quit. A line is considered any string terminated with a NL character. If no line is received tio will quit after response timeout. - - Any tio text is automatically muted when piping a string to tio while in response mode to make it easy to parse the response. - - --response-timeout - - Set timeout [ms] of line response (default: 100). - --rs-485 Enable RS-485 mode. @@ -300,8 +290,8 @@ SCRIPT API In addition to the Lua API tio makes the following functions available: - expect(string) - Expect string - waits for string to match before continueing. Supports regular expressions. Special characters must be escaped with '\\'. + 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. send(string) Send string. @@ -311,6 +301,9 @@ SCRIPT API Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM. + exit(code) + Exit with exit code. + high(line) Set tty line high. @@ -406,10 +399,6 @@ CONFIGURATION FILE prefix-ctrl-key Set prefix ctrl key (a..z or 'none', default: t) - response-wait Enable wait for line response - - response-timeout Set line response timeout - rs-485 Enable RS-485 mode rs-485-config Set RS-485 configuration @@ -517,11 +506,9 @@ EXAMPLES $ echo "ls -la" | tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 - Pipe command to the serial device and wait for line response (string ending with CR or NL): + Pipe command to serial device and wait for line response within 1 second: - $ echo "*IDN?" | tio /dev/ttyACM0 --response-wait - - In this mode, only the response will be printed. + $ echo "*IDN?" | tio /dev/ttyACM0 --script "expect('\r\n', 1000)" --mute Likewise, to pipe data from file to the serial device: From fa41771e651e5ce86b84bdee7437d9d643ab6c3c Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 14 Apr 2024 01:50:50 +0200 Subject: [PATCH 238/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ac04486..f6e7bab 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ $ tio /dev/ttyUSB0 Which corresponds to the commonly used default options: ``` -$ tio -b 115200 -d 8 -f none -s 1 -p none /dev/ttyUSB0 +$ tio --baudrate 115200 --databits 8 --flow none --stopbits 1 --parity none /dev/ttyUSB0 ``` It is recommended to connect serial TTY devices by ID: From 10eedd4ad2f8b8da3e35611457d09900d8bc33f1 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 14 Apr 2024 02:32:25 +0200 Subject: [PATCH 239/506] Update README --- README.md | 8 +++----- man/tio.1.in | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f6e7bab..76a7ef6 100644 --- a/README.md +++ b/README.md @@ -390,10 +390,8 @@ $ sudo usermod -a -G dialout ## 5. Contributing -tio is open source. If you want to help out with the project please feel free -to join in. - -All contributions (bug reports, code, doc, ideas, etc.) are welcome. +This is an open source project - all contributions (bug reports, code, doc, +ideas, etc.) are welcome. Please use the github issue tracker and pull request features. @@ -420,6 +418,6 @@ tio is GPLv2+. See LICENSE file for more details. ## 9. Authors -Created by Martin Lund \ +Maintained by Martin Lund \ See the AUTHORS file for full list of contributors. diff --git a/man/tio.1.in b/man/tio.1.in index 18d4cbf..875a881 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -660,4 +660,4 @@ Visit https://tio.github.io .SH "AUTHOR" .PP -Created by Martin Lund . +Maintained by Martin Lund . From ae461dc29694bff0426f32462ef1e46f052d656a Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 14 Apr 2024 10:23:43 +0200 Subject: [PATCH 240/506] Add missing header guard --- src/xymodem.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/xymodem.h b/src/xymodem.h index cd837e7..ef7f9a3 100644 --- a/src/xymodem.h +++ b/src/xymodem.h @@ -19,6 +19,8 @@ * 02110-1301, USA. */ +#pragma once + typedef enum { XMODEM_1K, XMODEM_CRC, From c4878a90d7d39acaef9393d9fcf0f26aa178d7de Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 14 Apr 2024 10:42:22 +0200 Subject: [PATCH 241/506] Clean up file descriptor name shadowing --- src/script.c | 20 ++++++------- src/tty.c | 82 ++++++++++++++++++++++++++-------------------------- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/script.c b/src/script.c index d8c4487..5a8fd41 100644 --- a/src/script.c +++ b/src/script.c @@ -37,7 +37,7 @@ #define MAX_BUFFER_SIZE 2000 // Maximum size of circular buffer -static int serial_fd; +static int device_fd; static char circular_buffer[MAX_BUFFER_SIZE]; static int buffer_size = 0; @@ -85,7 +85,7 @@ static int high(lua_State *L) return 0; } - tty_line_set(serial_fd, line, LINE_HIGH); + tty_line_set(device_fd, line, LINE_HIGH); return 0; } @@ -100,7 +100,7 @@ static int low(lua_State *L) return 0; } - tty_line_set(serial_fd, line, LINE_LOW); + tty_line_set(device_fd, line, LINE_LOW); return 0; } @@ -115,7 +115,7 @@ static int toggle(lua_State *L) return 0; } - tty_line_toggle(serial_fd, line); + tty_line_toggle(device_fd, line); return 0; } @@ -175,17 +175,17 @@ static int modem_send(lua_State *L) { case XMODEM_1K: tio_printf("Sending file '%s' using XMODEM-1K", file); - tio_printf("%s", xymodem_send(serial_fd, file, XMODEM_1K) < 0 ? "Aborted" : "Done"); + tio_printf("%s", xymodem_send(device_fd, file, XMODEM_1K) < 0 ? "Aborted" : "Done"); break; case XMODEM_CRC: tio_printf("Sending file '%s' using XMODEM-CRC", file); - tio_printf("%s", xymodem_send(serial_fd, file, XMODEM_CRC) < 0 ? "Aborted" : "Done"); + tio_printf("%s", xymodem_send(device_fd, file, XMODEM_CRC) < 0 ? "Aborted" : "Done"); break; case YMODEM: tio_printf("Sending file '%s' using YMODEM", file); - tio_printf("%s", xymodem_send(serial_fd, file, YMODEM) < 0 ? "Aborted" : "Done"); + tio_printf("%s", xymodem_send(device_fd, file, YMODEM) < 0 ? "Aborted" : "Done"); break; } @@ -203,7 +203,7 @@ static int send(lua_State *L) return 0; } - ret = write(serial_fd, string, strlen(string)); + ret = write(device_fd, string, strlen(string)); if (ret < 0) { tio_error_print("%s\n", strerror(errno)); @@ -289,7 +289,7 @@ static int expect(lua_State *L) // Main loop to read and match while (true) { - ssize_t bytes_read = read_poll(serial_fd, &c, 1, timeout); + ssize_t bytes_read = read_poll(device_fd, &c, 1, timeout); if (bytes_read > 0) { putchar(c); @@ -423,7 +423,7 @@ void script_run(int fd) { lua_State *L; - serial_fd = fd; + device_fd = fd; L = luaL_newstate(); luaL_openlibs(L); diff --git a/src/tty.c b/src/tty.c index b85cbb7..84c5b91 100644 --- a/src/tty.c +++ b/src/tty.c @@ -162,7 +162,7 @@ static unsigned long rx_total = 0, tx_total = 0; static bool connected = false; static bool standard_baudrate = true; static void (*print)(char c); -static int fd; +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; @@ -378,7 +378,7 @@ void *tty_stdin_input_thread(void *arg) break; case KEY_SHIFT_F: tio_printf("Flushed data I/O channels") - tcflush(fd, TCIOFLUSH); + tcflush(device_fd, TCIOFLUSH); break; default: break; @@ -442,7 +442,7 @@ static void handle_hex_prompt(char c) unsigned char hex_value = char_to_nibble(hex_chars[0]) << 4 | (char_to_nibble(hex_chars[1]) & 0x0F); hex_char_index = 0; - ssize_t status = tty_write(fd, &hex_value, 1); + ssize_t status = tty_write(device_fd, &hex_value, 1); if (status < 0) { tio_warning_printf("Could not write to tty device"); @@ -496,7 +496,7 @@ void tty_line_config_apply(void) int i = 0; static int state; - if (ioctl(fd, TIOCMGET, &state) < 0) + if (ioctl(device_fd, TIOCMGET, &state) < 0) { tio_warning_printf("Could not get line state (%s)", strerror(errno)); return; @@ -523,7 +523,7 @@ void tty_line_config_apply(void) } } - if (ioctl(fd, TIOCMSET, &state) < 0) + if (ioctl(device_fd, TIOCMSET, &state) < 0) { tio_warning_printf("Could not set line state configuration (%s)", strerror(errno)); } @@ -696,22 +696,22 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) switch (input_char) { case KEY_0: - tty_line_poke(fd, TIOCM_DTR, line_mode, option.dtr_pulse_duration); + tty_line_poke(device_fd, TIOCM_DTR, line_mode, option.dtr_pulse_duration); break; case KEY_1: - tty_line_poke(fd, TIOCM_RTS, line_mode, option.rts_pulse_duration); + tty_line_poke(device_fd, TIOCM_RTS, line_mode, option.rts_pulse_duration); break; case KEY_2: - tty_line_poke(fd, TIOCM_CTS, line_mode, option.cts_pulse_duration); + tty_line_poke(device_fd, TIOCM_CTS, line_mode, option.cts_pulse_duration); break; case KEY_3: - tty_line_poke(fd, TIOCM_DSR, line_mode, option.dsr_pulse_duration); + tty_line_poke(device_fd, TIOCM_DSR, line_mode, option.dsr_pulse_duration); break; case KEY_4: - tty_line_poke(fd, TIOCM_CD, line_mode, option.dcd_pulse_duration); + tty_line_poke(device_fd, TIOCM_CD, line_mode, option.dcd_pulse_duration); break; case KEY_5: - tty_line_poke(fd, TIOCM_RI, line_mode, option.ri_pulse_duration); + tty_line_poke(device_fd, TIOCM_RI, line_mode, option.ri_pulse_duration); break; default: tio_warning_printf("Invalid line number"); @@ -729,7 +729,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(fd, line, XMODEM_CRC) < 0 ? "Aborted" : "Done"); + tio_printf("%s", xymodem_send(device_fd, line, XMODEM_CRC) < 0 ? "Aborted" : "Done"); } break; @@ -740,7 +740,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(fd, line, XMODEM_CRC) < 0 ? "Aborted" : "Done"); + tio_printf("%s", xymodem_send(device_fd, line, XMODEM_CRC) < 0 ? "Aborted" : "Done"); } break; } @@ -797,7 +797,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) break; case KEY_SHIFT_L: - if (ioctl(fd, TIOCMGET, &state) < 0) + if (ioctl(device_fd, TIOCMGET, &state) < 0) { tio_warning_printf("Could not get line state (%s)", strerror(errno)); break; @@ -857,7 +857,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) break; case KEY_B: - tcsendbreak(fd, 0); + tcsendbreak(device_fd, 0); break; case KEY_C: @@ -939,7 +939,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) case KEY_R: /* Run script */ - script_run(fd); + script_run(device_fd); break; case KEY_S: @@ -996,7 +996,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) if (tio_readln()) { tio_printf("Sending file '%s' ", line); tio_printf("Press any key to abort transfer"); - tio_printf("%s", xymodem_send(fd, line, YMODEM) < 0 ? "Aborted" : "Done"); + tio_printf("%s", xymodem_send(device_fd, line, YMODEM) < 0 ? "Aborted" : "Done"); } break; @@ -1424,8 +1424,8 @@ void tty_disconnect(void) if (connected) { tio_printf("Disconnected"); - flock(fd, LOCK_UN); - close(fd); + flock(device_fd, LOCK_UN); + close(device_fd); connected = false; /* Fire alert action */ @@ -1435,12 +1435,12 @@ void tty_disconnect(void) void tty_restore(void) { - tcsetattr(fd, TCSANOW, &tio_old); + tcsetattr(device_fd, TCSANOW, &tio_old); if (option.rs485) { /* Restore original RS-485 mode */ - rs485_mode_restore(fd); + rs485_mode_restore(device_fd); } if (connected) @@ -1538,22 +1538,22 @@ int tty_connect(void) char* now = NULL; /* Open tty device */ - fd = open(option.tty_device, O_RDWR | O_NOCTTY | O_NONBLOCK); - if (fd < 0) + device_fd = open(option.tty_device, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (device_fd < 0) { tio_error_printf_silent("Could not open tty device (%s)", strerror(errno)); goto error_open; } /* Make sure device is of tty type */ - if (!isatty(fd)) + if (!isatty(device_fd)) { tio_error_printf("Not a tty device"); exit(EXIT_FAILURE);; } /* Lock device file */ - status = flock(fd, LOCK_EX | LOCK_NB); + status = flock(device_fd, LOCK_EX | LOCK_NB); if ((status == -1) && (errno == EWOULDBLOCK)) { tio_error_printf("Device file is locked by another process"); @@ -1561,7 +1561,7 @@ int tty_connect(void) } /* Flush stale I/O data (if any) */ - tcflush(fd, TCIOFLUSH); + tcflush(device_fd, TCIOFLUSH); /* Print connect status */ tio_printf("Connected"); @@ -1580,7 +1580,7 @@ int tty_connect(void) tty_output_mode_set(option.output_mode); /* Save current port settings */ - if (tcgetattr(fd, &tio_old) < 0) + if (tcgetattr(device_fd, &tio_old) < 0) { tio_error_printf_silent("Could not get port settings (%s)", strerror(errno)); goto error_tcgetattr; @@ -1598,7 +1598,7 @@ int tty_connect(void) /* Manage RS-485 mode */ if (option.rs485) { - rs485_mode_enable(fd); + rs485_mode_enable(device_fd); } /* Make sure we restore tty settings on exit */ @@ -1609,7 +1609,7 @@ int tty_connect(void) } /* Activate new port settings */ - status = tcsetattr(fd, TCSANOW, &tio); + status = tcsetattr(device_fd, TCSANOW, &tio); if (status == -1) { tio_error_printf_silent("Could not apply port settings (%s)", strerror(errno)); @@ -1619,7 +1619,7 @@ int tty_connect(void) /* Set arbitrary baudrate (only works on supported platforms) */ if (!standard_baudrate) { - if (setspeed(fd, option.baudrate) != 0) + if (setspeed(device_fd, option.baudrate) != 0) { tio_error_printf_silent("Could not set baudrate speed (%s)", strerror(errno)); goto error_setspeed; @@ -1640,7 +1640,7 @@ int tty_connect(void) else if (ret > 0) { // Forward to tty device - ret = write(fd, &input_char, 1); + ret = write(device_fd, &input_char, 1); if (ret < 0) { tio_error_printf("Could not write to serial device (%s)", strerror(errno)); @@ -1658,7 +1658,7 @@ int tty_connect(void) /* Manage script activation */ if (option.script_run != SCRIPT_RUN_NEVER) { - script_run(fd); + script_run(device_fd); if (option.script_run == SCRIPT_RUN_ONCE) { @@ -1676,10 +1676,10 @@ int tty_connect(void) while (true) { FD_ZERO(&rdfs); - FD_SET(fd, &rdfs); + FD_SET(device_fd, &rdfs); FD_SET(pipefd[0], &rdfs); - maxfd = MAX(fd, pipefd[0]); + maxfd = MAX(device_fd, pipefd[0]); maxfd = MAX(maxfd, socket_add_fds(&rdfs, true)); /* Block until input becomes available */ @@ -1687,10 +1687,10 @@ int tty_connect(void) if (status > 0) { bool forward = false; - if (FD_ISSET(fd, &rdfs)) + if (FD_ISSET(device_fd, &rdfs)) { /* Input from tty device ready */ - ssize_t bytes_read = read(fd, input_buffer, BUFSIZ); + ssize_t bytes_read = read(device_fd, input_buffer, BUFSIZ); if (bytes_read <= 0) { /* Error reading - device is likely unplugged */ @@ -1781,7 +1781,7 @@ int tty_connect(void) else if (bytes_read == 0) { /* Reached EOF (when piping to stdin, never reached) */ - tty_sync(fd); + tty_sync(device_fd); exit(EXIT_SUCCESS); } @@ -1817,11 +1817,11 @@ int tty_connect(void) if (forward) { - forward_to_tty(fd, output_char); + forward_to_tty(device_fd, output_char); } } - tty_sync(fd); + tty_sync(device_fd); } else { @@ -1830,10 +1830,10 @@ int tty_connect(void) if (forward) { - forward_to_tty(fd, output_char); + forward_to_tty(device_fd, output_char); } - tty_sync(fd); + tty_sync(device_fd); } } else if (status == -1) From 9744fcafcf576c5eee75bc7bfdfd90d643ca40a3 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 14 Apr 2024 11:21:26 +0200 Subject: [PATCH 242/506] Rework rs485_parse_config() Introduce proper sscanf() checks. --- src/rs485.c | 95 ++++++++++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/src/rs485.c b/src/rs485.c index 2cb1067..c1b06db 100644 --- a/src/rs485.c +++ b/src/rs485.c @@ -57,54 +57,61 @@ void rs485_parse_config(const char *arg) { char keyname[31]; unsigned int value; + int match_count; - if (sscanf(token, "%30[^=]=%d", keyname, &value) != 2) + match_count = sscanf(token, "%30[^=]=%d", keyname, &value); + + if (match_count == 2) + { + if (!strcmp(keyname, "RTS_ON_SEND")) + { + if (value) + { + /* Set logical level for RTS pin equal to 1 when sending */ + option.rs485_config_flags |= SER_RS485_RTS_ON_SEND; + } + else + { + /* Set logical level for RTS pin equal to 0 when sending */ + option.rs485_config_flags &= ~(SER_RS485_RTS_ON_SEND); + } + } + else if (!strcmp(keyname, "RTS_AFTER_SEND")) + { + if (value) + { + /* Set logical level for RTS pin equal to 1 after sending */ + option.rs485_config_flags |= SER_RS485_RTS_AFTER_SEND; + } + else + { + /* Set logical level for RTS pin equal to 0 after sending */ + option.rs485_config_flags &= ~(SER_RS485_RTS_AFTER_SEND); + } + } + else if (!strcmp(keyname, "RTS_DELAY_BEFORE_SEND")) + { + /* Set RTS delay before send */ + option.rs485_delay_rts_before_send = value; + } + else if (!strcmp(keyname, "RTS_DELAY_AFTER_SEND")) + { + /* Set RTS delay after send */ + option.rs485_delay_rts_after_send = value; + } + } + else if (match_count == 1) + { + if (!strcmp(keyname, "RX_DURING_TX")) + { + /* Receive data even while sending data */ + option.rs485_config_flags |= SER_RS485_RX_DURING_TX; + } + } + else { token_found = false; } - - if (!strcmp(keyname, "RTS_ON_SEND")) - { - if (value) - { - - /* Set logical level for RTS pin equal to 1 when sending */ - option.rs485_config_flags |= SER_RS485_RTS_ON_SEND; - } - else - { - /* Set logical level for RTS pin equal to 0 when sending */ - option.rs485_config_flags &= ~(SER_RS485_RTS_ON_SEND); - } - } - else if (!strcmp(keyname, "RTS_AFTER_SEND")) - { - if (value) - { - /* Set logical level for RTS pin equal to 1 after sending */ - option.rs485_config_flags |= SER_RS485_RTS_AFTER_SEND; - } - else - { - /* Set logical level for RTS pin equal to 0 after sending */ - option.rs485_config_flags &= ~(SER_RS485_RTS_AFTER_SEND); - } - } - else if (!strcmp(keyname, "RTS_DELAY_BEFORE_SEND")) - { - /* Set RTS delay before send */ - option.rs485_delay_rts_before_send = value; - } - else if (!strcmp(keyname, "RTS_DELAY_AFTER_SEND")) - { - /* Set RTS delay after send */ - option.rs485_delay_rts_after_send = value; - } - else if (!strcmp(keyname, "RX_DURING_TX")) - { - /* Receive data even while sending data */ - option.rs485_config_flags |= SER_RS485_RX_DURING_TX; - } } else { From 76a7a56e85ad56f2e688ca595fbecdaadf71442e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 14 Apr 2024 11:43:19 +0200 Subject: [PATCH 243/506] Rework line_pulse_duration_option_parse() Introduce proper sscanf() checks. --- src/options.c | 54 ++++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/src/options.c b/src/options.c index 427e4e8..feef128 100644 --- a/src/options.c +++ b/src/options.c @@ -176,35 +176,37 @@ void line_pulse_duration_option_parse(const char *arg) char keyname[11]; unsigned int value; - if (sscanf(token, "%10[^=]=%d", keyname, &value) != 2) + if (sscanf(token, "%10[^=]=%d", keyname, &value) == 2) + { + if (!strcmp(keyname, "DTR")) + { + option.dtr_pulse_duration = value; + } + else if (!strcmp(keyname, "RTS")) + { + option.rts_pulse_duration = value; + } + else if (!strcmp(keyname, "CTS")) + { + option.cts_pulse_duration = value; + } + else if (!strcmp(keyname, "DSR")) + { + option.dsr_pulse_duration = value; + } + else if (!strcmp(keyname, "DCD")) + { + option.dcd_pulse_duration = value; + } + else if (!strcmp(keyname, "RI")) + { + option.ri_pulse_duration = value; + } + } + else { token_found = false; } - - if (!strcmp(keyname, "DTR")) - { - option.dtr_pulse_duration = value; - } - else if (!strcmp(keyname, "RTS")) - { - option.rts_pulse_duration = value; - } - else if (!strcmp(keyname, "CTS")) - { - option.cts_pulse_duration = value; - } - else if (!strcmp(keyname, "DSR")) - { - option.dsr_pulse_duration = value; - } - else if (!strcmp(keyname, "DCD")) - { - option.dcd_pulse_duration = value; - } - else if (!strcmp(keyname, "RI")) - { - option.ri_pulse_duration = value; - } } else { From de0a7c547fa13491a70df787084ee15c30253cf3 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 14 Apr 2024 12:52:28 +0200 Subject: [PATCH 244/506] Rework resolve_config_file() --- src/configfile.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/configfile.c b/src/configfile.c index b1ef72c..b123071 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -393,30 +393,36 @@ static int resolve_config_file(void) char *xdg = getenv("XDG_CONFIG_HOME"); if (xdg) { - asprintf(&c.path, "%s/tio/config", xdg); - if (access(c.path, F_OK) == 0) + if (asprintf(&c.path, "%s/tio/config", xdg) != -1) { - return 0; + if (access(c.path, F_OK) == 0) + { + return 0; + } + free(c.path); } - free(c.path); } char *home = getenv("HOME"); if (home) { - asprintf(&c.path, "%s/.config/tio/config", home); - if (access(c.path, F_OK) == 0) + if (asprintf(&c.path, "%s/.config/tio/config", home) != -1) { - return 0; + if (access(c.path, F_OK) == 0) + { + return 0; + } + free(c.path); } - free(c.path); - asprintf(&c.path, "%s/.tioconfig", home); - if (access(c.path, F_OK) == 0) + if (asprintf(&c.path, "%s/.tioconfig", home) != -1) { - return 0; + if (access(c.path, F_OK) == 0) + { + return 0; + } + free(c.path); } - free(c.path); } c.path = NULL; From e8114ca0a447e87ffcdecd07a3f569acf30d2535 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 14 Apr 2024 18:04:31 +0200 Subject: [PATCH 245/506] Update version date --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 39f2fc1..183044d 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 = '2023-09-19' +version_date = '2024-04-14' # Test for dynamic baudrate configuration interface compiler = meson.get_compiler('c') From 00f3ea9b7fca7bf51945ea91c9f1e663ea479a1c Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 14 Apr 2024 18:04:51 +0200 Subject: [PATCH 246/506] Update NEWS --- NEWS | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 178 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 28a3d31..e62ce4e 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,182 @@ -=== tio v2.7 === +=== tio v2.8 === + + + +Changes since tio v2.7: + + * Rework resolve_config_file() + + * Rework line_pulse_duration_option_parse() + + Introduce proper sscanf() checks. + + * Rework rs485_parse_config() + + Introduce proper sscanf() checks. + + * Clean up file descriptor name shadowing + + * Add missing header guard + + * Upgrade inih subproject + + * Remove options --response-wait, --response-timeout + + Remove options and rework input handling so it is possible to do the + same thing but via script which is much more flexible. + + These options were always a bit of a hardcoded solution. With the new + script expect feature we can wait for any type of response. + + For example, pipe command to serial device and wait for line response within 1 second: + + $ echo "*IDN?" | tio /dev/ttyACM0 --script "expect('\r\n', 1000)" --mute + + * Add lua exit(code) + + * Add timeout feature to expect() + + * Add lua expect(string) + + Add simple expect functionality. + + The expect(string) function will wait for input from the tty device and + only return when there is a string match. Regular expressions are + supported. + + Example: + + script = expect('password:'); send('my_password\n') + + * Add lua send(string) + + * Add lua modem_send(file,protocol) + + * Fix xymodem error print outs + + * Rework x/y-modem transfer command + + Remove ctrl-t X optin and instead introduce submenu to ctrl-t x option + for picking which xmodem protocol to use. + + * Update README + + * Cleanup options + + * Add independent input and output mode + + Replaces -x, --hexadecimal option with --intput-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 + configuration: + + input-mode = hex + output-mode = hex + + * 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 + lines simultaneously. + + To help solve this timing issue we introduce a tty line state + configuration API which can be used instead of using + high()/low(). + + Using config_low(line) and config_high(line) one can set up a new line + state configuration for multiple lines and then use config_apply() to + finally apply the configuration. This will result in only one system + call to instruct the serial port drive to switch all the configured line + states which should help ensure that the lines are switched + simultaneously. + + Example: + + script = config_high(DTR); config_low(RTS); config_apply() + + * Add ONULBRK mapping flag + + Add ONULBRK mapping to map nul (zero) to send break signal on output. + + This is useful if one needs to e.g. send the break signal to the tty + device when connected via socket. + + * Add --log-directory option + + For specifying directory path in which to save automatically named log + files. + + * Add Lua scripting feature + + Add support for running Lua scripts that can manipulate the tty control + 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 + such as adding expect like functionality to easily and programatically + interact with the connected device. + + * Invert line states to reflect true electrical level + + * Add support for disabling prefix key handling + + To disable prefix key input handing simply set prefix-ctrl-key to + 'none'. + + Based on original patch from Sebastian Krahmer. + + * Add meson man pages install option + + Defaults to installing man pages. + +HiFiPhile: + + * Poll on serial port read instead of delay. + + * Add Xmodem-CRC support. + + * CYGWIN: Fix port auto connection. + +Mingjie Shen: + + * Check return values of sscanf() + + Failing to check that a call to 'sscanf' actually writes to an output + variable can lead to unexpected behavior at reading time. + +Jakob Haufe: + + * Support NO_COLOR env variable as per no-color.org + + * Fix troff warning + + .eo/.ec sections seemingly need explicit empty lines using .sp + + Otherwise, troff complains: + + troff::535: warning: expected numeric expression, got '\' + troff::538: warning: expected numeric expression, got '\' + troff::541: warning: expected numeric expression, got '\' + +Fredrik Svedberg: + + * Add map FF to ESC-c on input + + Added map of form feed to ESC-c on input for terminals that + do not clear screen on ^L but do on ESC-c. + +Brian: + + * Add CodeQL Workflow for Code Security Analysis + +Sylvain LAFRASSE: + + * Fix double call of tty_disconnect() on macOS/Darwin. From d3993da6d4ec59a9e280ee9584d1557915cc4282 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 14 Apr 2024 18:05:01 +0200 Subject: [PATCH 247/506] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 84417f1..e6f22c9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -51,5 +51,6 @@ Paul Ruizendaal Fredrik Svedberg Sebastian Mingjie Shen +Brian Thanks to everyone who has contributed to this project. From 5c45150f5841bc00209da57bca21d216bb4f249f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 14 Apr 2024 18:43:58 +0200 Subject: [PATCH 248/506] Bump version --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 183044d..792711e 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('tio', 'c', - version : '2.8', + version : '2.9', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] From d60363a64c4c91057a535b0fac2e4991ebbbb5c0 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 15 Apr 2024 11:21:36 +0200 Subject: [PATCH 249/506] Cleanup global variable name shadowing --- src/socket.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/socket.c b/src/socket.c index 595efd9..3500f97 100644 --- a/src/socket.c +++ b/src/socket.c @@ -52,23 +52,23 @@ static const char *socket_filename(void) static int socket_inet_port(void) { /* skip 'inet:' */ - int port_number = atoi(option.socket + 5); - if (port_number == 0) + int port = atoi(option.socket + 5); + if (port == 0) { - port_number = SOCKET_PORT_DEFAULT; + port = SOCKET_PORT_DEFAULT; } - return port_number; + return port; } static int socket_inet6_port(void) { /* skip 'inet6:' */ - int port_number = atoi(option.socket + 6); - if (port_number == 0) + int port = atoi(option.socket + 6); + if (port == 0) { - port_number = SOCKET_PORT_DEFAULT; + port = SOCKET_PORT_DEFAULT; } - return port_number; + return port; } static void socket_exit(void) @@ -83,14 +83,14 @@ static bool socket_stale(const char *path) { struct sockaddr_un addr; bool stale = false; - int sockfd; + int sfd; /* Test if socket file exists */ if (access(path, F_OK) == 0) { /* Create test socket */ - sockfd = socket(AF_UNIX, SOCK_STREAM, 0); - if (sockfd < 0) + sfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sfd < 0) { tio_warning_printf("Failure opening socket (%s)", strerror(errno)); return false; From 4801816357e93ce5b0ca68b713395e40dfa2d2c5 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 16 Apr 2024 17:42:34 +0200 Subject: [PATCH 250/506] Introduce basic line input mode --- README.md | 2 +- TODO | 7 ++- man/tio.1.in | 15 +++-- src/bash-completion/tio.in | 2 +- src/options.c | 8 ++- src/options.h | 1 + src/print.c | 2 + src/tty.c | 115 ++++++++++++++++++++++++++++++++++--- 8 files changed, 135 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 76a7ef6..f084d0e 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ Options: --line-pulse-duration Set line pulse duration -n, --no-autoconnect Disable automatic connect -e, --local-echo Enable local echo - --input-mode normal|hex Select input mode (default: normal) + --input-mode normal|hex|line Select input mode (default: normal) --output-mode normal|hex Select output mode (default: normal) -t, --timestamp Enable line timestamp --timestamp-format Set timestamp format (default: 24hour) diff --git a/TODO b/TODO index 9fc3626..e00f76b 100644 --- a/TODO +++ b/TODO @@ -26,10 +26,11 @@ Time is in ms. -* Line mode feature +* Advanced line mode - Only send line when pressing enter. Maybe even add readline support so one - can use all the readline editing feature before sending the line. + 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. * Support for interaction using simple autoresponse strings diff --git a/man/tio.1.in b/man/tio.1.in index 875a881..17871f1 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -191,11 +191,18 @@ If defining more than one flag, the flags must be comma separated. .RE .TP -.BR " \-\-input\-mode " normal|hex +.BR " \-\-input\-mode " normal|hex|line -Set input mode. In hex input mode bytes can be sent by typing the -\fBtwo-character hexadecimal\fR representation of the 1 byte value, e.g.: to -send \fI0xA\fR you must type \fI0a\fR or \fI0A\fR. +Set input mode. + +In normal mode input characters are sent immediately as they are typed. + +In hex input mode bytes can be sent by typing the \fBtwo-character +hexadecimal\fR representation of the 1 byte value, e.g.: to send \fI0xA\fR you +must type \fI0a\fR or \fI0A\fR. + +In line input mode input characters are sent when you press enter. The only +editing feature supported in this mode is backspace. Default value is "normal". diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index 10bf6ef..efe9617 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -131,7 +131,7 @@ _tio() return 0 ;; --input-mode) - COMPREPLY=( $(compgen -W "normal hex" -- ${cur}) ) + COMPREPLY=( $(compgen -W "normal hex line" -- ${cur}) ) return 0 ;; --output-mode) diff --git a/src/options.c b/src/options.c index feef128..d231cc7 100644 --- a/src/options.c +++ b/src/options.c @@ -126,7 +126,7 @@ void print_help(char *argv[]) printf(" --line-pulse-duration Set line pulse duration\n"); printf(" -n, --no-autoconnect Disable automatic connect\n"); printf(" -e, --local-echo Enable local echo\n"); - printf(" --input-mode normal|hex Select input mode (default: normal)\n"); + printf(" --input-mode normal|hex|line Select input mode (default: normal)\n"); printf(" --output-mode normal|hex Select output mode (default: normal)\n"); printf(" -t, --timestamp Enable line timestamp\n"); printf(" --timestamp-format Set timestamp format (default: 24hour)\n"); @@ -226,6 +226,10 @@ input_mode_t input_mode_option_parse(const char *arg) { return INPUT_MODE_HEX; } + else if (strcmp("line", arg) == 0) + { + return INPUT_MODE_LINE; + } else { tio_error_printf("Invalid input mode option"); @@ -258,6 +262,8 @@ const char *input_mode_by_string(input_mode_t mode) return "normal"; case INPUT_MODE_HEX: return "hex"; + case INPUT_MODE_LINE: + return "line"; case INPUT_MODE_END: break; } diff --git a/src/options.h b/src/options.h index 7a6b627..aff99fc 100644 --- a/src/options.h +++ b/src/options.h @@ -34,6 +34,7 @@ typedef enum { INPUT_MODE_NORMAL, INPUT_MODE_HEX, + INPUT_MODE_LINE, INPUT_MODE_END, } input_mode_t; diff --git a/src/print.c b/src/print.c index 1c891aa..04d17da 100644 --- a/src/print.c +++ b/src/print.c @@ -31,11 +31,13 @@ char ansi_format[30]; void print_hex(char c) { + print_tainted = true; printf("%02x ", (unsigned char) c); } void print_normal(char c) { + print_tainted = true; putchar(c); } diff --git a/src/tty.c b/src/tty.c index 84c5b91..d43acf3 100644 --- a/src/tty.c +++ b/src/tty.c @@ -195,8 +195,6 @@ static void optional_local_echo(char c) { log_putc(c); } - - print_tainted_set(); } inline static bool is_valid_hex(char c) @@ -887,6 +885,11 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf("Switched to hex input mode"); break; + case INPUT_MODE_LINE: + option.input_mode = INPUT_MODE_LINE; + tio_printf("Switched to line input mode"); + break; + case INPUT_MODE_END: option.input_mode = INPUT_MODE_NORMAL; tio_printf("Switched to normal input mode"); @@ -1531,11 +1534,14 @@ 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 input_buffer[BUFSIZ] = {}; + char line_buffer[BUFSIZ] = {}; static bool first = true; int status; bool next_timestamp = false; char* now = NULL; + unsigned int line_index = 0; + static char previous_char[2] = {}; /* Open tty device */ device_fd = open(option.tty_device, O_RDWR | O_NOCTTY | O_NONBLOCK); @@ -1805,12 +1811,107 @@ int tty_connect(void) /* Handle commands */ handle_command_sequence(input_char, &output_char, &forward); - if ((option.input_mode == INPUT_MODE_HEX) && (forward)) + if (forward) { - if (!is_valid_hex(input_char)) + switch (option.input_mode) { - tio_warning_printf("Invalid hex character: '%d' (0x%02x)", input_char, input_char); - forward = false; + case INPUT_MODE_HEX: + if (!is_valid_hex(input_char)) + { + tio_warning_printf("Invalid hex character: '%d' (0x%02x)", input_char, input_char); + forward = false; + } + break; + + 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) + { + if ((option.output_mode == OUTPUT_MODE_HEX) && (option.local_echo)) + { + printf("\b\b\b \b\b\b"); // Destructive backspace + } + else + { + printf("\b \b"); // Destructive backspace + } + line_index--; + } + forward = false; + break; + + case 13: // Carriage return + // Write buffered line to tty device + tty_write(device_fd, line_buffer, line_index); + tty_write(device_fd, "\r", 1); + optional_local_echo('\r'); + tty_sync(device_fd); + putchar('\r'); + putchar('\n'); + line_index = 0; + forward = false; + break; + + default: + if (line_index < BUFSIZ) + { + line_buffer[line_index++] = input_char; + } + else + { + tio_error_print("Input exceeds maximum line length. Truncating."); + forward = false; + } + } + + // Save 2 latest stdin input characters + previous_char[1] = previous_char[0]; + previous_char[0] = input_char; + + break; + + default: + break; } } } From 1e20948d83a82b31a058557f6158d0dbd1a2e655 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 16 Apr 2024 19:38:36 +0200 Subject: [PATCH 251/506] Fix line input mode Do not forward input characters to tty device before a line is input via carriage return. --- src/tty.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tty.c b/src/tty.c index d43acf3..32a6b20 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1895,13 +1895,14 @@ int tty_connect(void) default: if (line_index < BUFSIZ) { + optional_local_echo(input_char); line_buffer[line_index++] = input_char; } else { tio_error_print("Input exceeds maximum line length. Truncating."); - forward = false; } + forward = false; } // Save 2 latest stdin input characters From a6055332135337c81e61e0697da4916b323372f3 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 17 Apr 2024 16:16:01 +0200 Subject: [PATCH 252/506] Fix local echo in line mode --- src/tty.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/tty.c b/src/tty.c index 32a6b20..02eefa6 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1867,35 +1867,40 @@ int tty_connect(void) case 127: // Backspace if (line_index) { - if ((option.output_mode == OUTPUT_MODE_HEX) && (option.local_echo)) - { - printf("\b\b\b \b\b\b"); // Destructive backspace - } - else - { - printf("\b \b"); // Destructive backspace - } + printf("\b \b"); // Destructive backspace line_index--; } forward = false; break; - case 13: // Carriage return + case '\r': // Carriage return + if (option.local_echo == false) + { + // Delete line + int i = line_index; + while (i--) + { + printf("\b \b"); // Destructive backspace + } + } + else + { + // Preserve line, go to next line + putchar('\r'); + putchar('\n'); + } + // Write buffered line to tty device tty_write(device_fd, line_buffer, line_index); - tty_write(device_fd, "\r", 1); - optional_local_echo('\r'); tty_sync(device_fd); - putchar('\r'); - putchar('\n'); line_index = 0; - forward = false; break; default: if (line_index < BUFSIZ) { - optional_local_echo(input_char); + putchar(input_char); + print_tainted_set(); line_buffer[line_index++] = input_char; } else @@ -1908,7 +1913,6 @@ int tty_connect(void) // Save 2 latest stdin input characters previous_char[1] = previous_char[0]; previous_char[0] = input_char; - break; default: From 98653566a854d66bef2f1f83b638bf57ff11be43 Mon Sep 17 00:00:00 2001 From: Davis C Date: Tue, 16 Apr 2024 20:22:13 -0500 Subject: [PATCH 253/506] Return 1 when `expect` matches --- src/script.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/script.c b/src/script.c index 5a8fd41..6b99ec2 100644 --- a/src/script.c +++ b/src/script.c @@ -297,6 +297,7 @@ static int expect(lua_State *L) // Match against the entire buffer if (match_regex(®ex)) { + ret = 1; break; } } From 794c5202f4175a43b57da0c8cfde6c5be6591ee1 Mon Sep 17 00:00:00 2001 From: Davis C Date: Wed, 17 Apr 2024 09:21:20 -0500 Subject: [PATCH 254/506] Added `reset_buffer()` --- README.md | 3 +++ man/tio.1.in | 2 ++ src/script.c | 9 +++++++++ 3 files changed, 14 insertions(+) diff --git a/README.md b/README.md index f084d0e..bd38050 100644 --- a/README.md +++ b/README.md @@ -265,6 +265,9 @@ In addition to the Lua API tio makes the following functions available: help to make the lines physically switch as simultaneously as possible. This may solve timing issues on some platforms. + reset_buffer() + Flush the internal ring buffer. + Note: Line can be any of DTR, RTS, CTS, DSR, CD, RI ``` diff --git a/man/tio.1.in b/man/tio.1.in index 17871f1..ead3967 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -401,6 +401,8 @@ Set tty line state configuration to low. Apply tty line state configuration. Using the line state configuration API instead of high()/low() will help to make the lines physically switch as simultaneously as possible. This may solve timing issues on some platforms. +.IP "\fBreset_buffer()" +Flush the internal ring buffer. .TP 0n Note: Line can be any of DTR, RTS, CTS, DSR, CD, RI diff --git a/src/script.c b/src/script.c index 6b99ec2..410085a 100644 --- a/src/script.c +++ b/src/script.c @@ -256,6 +256,14 @@ bool match_regex(regex_t *regex) return false; } +// lua: reset_buffer() +static int reset_buffer(lua_State *L) +{ + (void)L; + buffer_size = 0; + return 1; +} + // lua: expect(string, timeout) static int expect(lua_State *L) { @@ -352,6 +360,7 @@ static const struct luaL_Reg tio_lib[] = { "modem_send", modem_send}, { "send", send}, { "expect", expect}, + { "reset_buffer", reset_buffer}, { "exit", exit_}, {NULL, NULL} }; From 1cefb7b6bcf9cc637f1f39d02642a59c07579639 Mon Sep 17 00:00:00 2001 From: Davis C Date: Wed, 17 Apr 2024 11:04:51 -0500 Subject: [PATCH 255/506] Revert "Added `reset_buffer()`" This reverts commit ee56d1280d022a84870603378f0512ac3cabeba2. --- README.md | 3 --- man/tio.1.in | 2 -- src/script.c | 9 --------- 3 files changed, 14 deletions(-) diff --git a/README.md b/README.md index bd38050..f084d0e 100644 --- a/README.md +++ b/README.md @@ -265,9 +265,6 @@ In addition to the Lua API tio makes the following functions available: help to make the lines physically switch as simultaneously as possible. This may solve timing issues on some platforms. - reset_buffer() - Flush the internal ring buffer. - Note: Line can be any of DTR, RTS, CTS, DSR, CD, RI ``` diff --git a/man/tio.1.in b/man/tio.1.in index ead3967..17871f1 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -401,8 +401,6 @@ Set tty line state configuration to low. Apply tty line state configuration. Using the line state configuration API instead of high()/low() will help to make the lines physically switch as simultaneously as possible. This may solve timing issues on some platforms. -.IP "\fBreset_buffer()" -Flush the internal ring buffer. .TP 0n Note: Line can be any of DTR, RTS, CTS, DSR, CD, RI diff --git a/src/script.c b/src/script.c index 410085a..6b99ec2 100644 --- a/src/script.c +++ b/src/script.c @@ -256,14 +256,6 @@ bool match_regex(regex_t *regex) return false; } -// lua: reset_buffer() -static int reset_buffer(lua_State *L) -{ - (void)L; - buffer_size = 0; - return 1; -} - // lua: expect(string, timeout) static int expect(lua_State *L) { @@ -360,7 +352,6 @@ static const struct luaL_Reg tio_lib[] = { "modem_send", modem_send}, { "send", send}, { "expect", expect}, - { "reset_buffer", reset_buffer}, { "exit", exit_}, {NULL, NULL} }; From d0e95c5fba517f2468dc7bed5ce90063d22e3fed Mon Sep 17 00:00:00 2001 From: Davis C Date: Wed, 17 Apr 2024 11:06:41 -0500 Subject: [PATCH 256/506] Reset buffer size at start of expect --- src/script.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/script.c b/src/script.c index 6b99ec2..41a415b 100644 --- a/src/script.c +++ b/src/script.c @@ -265,6 +265,9 @@ static int expect(lua_State *L) int ret = 0; char c; + // Resets buffer to ignore previous `expect` calls + buffer_size = 0; + if ((string == NULL) || (timeout < 0)) { ret = -1; From f8924182d324dc5d7039966934be90937fbb99ca Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 17 Apr 2024 18:15:51 +0200 Subject: [PATCH 257/506] Update TODO --- TODO | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/TODO b/TODO index e00f76b..59ea160 100644 --- a/TODO +++ b/TODO @@ -32,28 +32,6 @@ key navigation left/right and insert/overwrite support. Also history browsing pressing up/down. -* Support for interaction using simple autoresponse strings - - Add support for simple autoresponse strings in the configuration file. For - example: - - autoresponse = expect:'localhost login: ', send:'root\n', - expect:'Password: ', send:'abcd1234\n' - - When expect line is matched tio will respond by writing the send string. - - When parsing the autoresponse variable make sure matching expect/send pairs - else provide warning. - - Maybe support regex matching in expect string to make feature more powerful. - - This is mostly a convenience feature. For more powerful scripted interaction - users can continue use the socket feature in combination with the expect tool - as described in the man page. - - Maybe provide a mechanism to disable autoresponse feature. Maybe by defining - maximum match count and/or in session key command to toggle feature. - * Support for running external command Add key command e.g. 'ctrl-t r' which prompts user to run external command. From a1987b61b425889a22486d94da39d87b28570099 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 17 Apr 2024 18:24:33 +0200 Subject: [PATCH 258/506] Organize examples directory --- README.md | 2 +- {example => examples/config}/config | 0 {example => examples/lua}/control-lines-test.lua | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename {example => examples/config}/config (100%) rename {example => examples/lua}/control-lines-test.lua (100%) diff --git a/README.md b/README.md index f084d0e..8d502cb 100644 --- a/README.md +++ b/README.md @@ -327,7 +327,7 @@ Or by pattern match: $ tio usb12 ``` -Another more elaborate configuration file example is available [here](example/config). +Another more elaborate configuration file example is available [here](examples/config/config). ## 4. Installation diff --git a/example/config b/examples/config/config similarity index 100% rename from example/config rename to examples/config/config diff --git a/example/control-lines-test.lua b/examples/lua/control-lines-test.lua similarity index 100% rename from example/control-lines-test.lua rename to examples/lua/control-lines-test.lua From f4c4387e05ebc305fd70c24d280710056ad2ae36 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 17 Apr 2024 18:27:46 +0200 Subject: [PATCH 259/506] Add automatic login script example --- examples/lua/automatic-login.lua | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 examples/lua/automatic-login.lua diff --git a/examples/lua/automatic-login.lua b/examples/lua/automatic-login.lua new file mode 100644 index 0000000..1787da1 --- /dev/null +++ b/examples/lua/automatic-login.lua @@ -0,0 +1,28 @@ +local logins = { + { + serialnumber = "foo", + username = "foouser", + password = "foopass", + }, + { + serialnumber = "bar", + username = "baruser", + password = "barpass", + }, + { + serialnumber = "baz", + username = "bazuser", + password = "bazpass", + }, +} + +for _, login in ipairs(logins) do + send("\n") + local found = expect(login.serialnumber .. ".*login:", 10) + if (1 == found) then + send(login.username .. "\n") + expect("Password:") + send(login.password .. "\n") + break + end +end From 48c9e8a9a9c1fb796828a405870e1dcb81d88ea7 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 17 Apr 2024 18:30:53 +0200 Subject: [PATCH 260/506] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index e6f22c9..9aaace3 100644 --- a/AUTHORS +++ b/AUTHORS @@ -52,5 +52,6 @@ Fredrik Svedberg Sebastian Mingjie Shen Brian +Davis C Thanks to everyone who has contributed to this project. From 3cc2d90fda1a5dc1d8edaad2425849b83fd8372b Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 17 Apr 2024 23:38:18 +0200 Subject: [PATCH 261/506] Extend lua expect() to also return matched string --- man/tio.1.in | 5 +++++ src/script.c | 15 +++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 17871f1..5cf55d7 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -375,6 +375,11 @@ In addition to the Lua API tio makes the following functions available: 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 invalid regular expression. + +On successful match it also returns the match string as second return value. + .IP "\fBsend(string)" Send string. .IP "\fBmodem_send(file, protocol)" diff --git a/src/script.c b/src/script.c index 41a415b..243ae0c 100644 --- a/src/script.c +++ b/src/script.c @@ -39,6 +39,7 @@ static int device_fd; static char circular_buffer[MAX_BUFFER_SIZE]; +static char match_string[MAX_BUFFER_SIZE]; static int buffer_size = 0; // lua: sleep(seconds) @@ -233,14 +234,22 @@ void add_to_buffer(char c) 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, 0, NULL, 0); + 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) @@ -267,6 +276,7 @@ static int expect(lua_State *L) // Resets buffer to ignore previous `expect` calls buffer_size = 0; + match_string[0] = '\0'; if ((string == NULL) || (timeout < 0)) { @@ -316,7 +326,8 @@ static int expect(lua_State *L) error: lua_pushnumber(L, ret); - return 1; + lua_pushstring(L, match_string); + return 2; } // lua: exit(code) From f1144ca5cc7cb3edd7eb7d88eabff20303376eb6 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 18 Apr 2024 13:29:06 +0200 Subject: [PATCH 262/506] Cleanup --- src/tty.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/tty.c b/src/tty.c index 02eefa6..b486f29 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1695,7 +1695,10 @@ int tty_connect(void) bool forward = false; if (FD_ISSET(device_fd, &rdfs)) { + /*******************************/ /* Input from tty device ready */ + /*******************************/ + ssize_t bytes_read = read(device_fd, input_buffer, BUFSIZ); if (bytes_read <= 0) { @@ -1777,7 +1780,10 @@ int tty_connect(void) } else if (FD_ISSET(pipefd[0], &rdfs)) { + /**************************/ /* Input from stdin ready */ + /**************************/ + ssize_t bytes_read = read(pipefd[0], input_buffer, BUFSIZ); if (bytes_read < 0) { @@ -1931,7 +1937,10 @@ int tty_connect(void) } else { + /***************************/ /* Input from socket ready */ + /***************************/ + forward = socket_handle_input(&rdfs, &output_char); if (forward) From c440da2ea8ed448860a2c113c2391c4ce53d0881 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 18 Apr 2024 13:35:09 +0200 Subject: [PATCH 263/506] Improve switched messages --- src/tty.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/tty.c b/src/tty.c index b486f29..e43c4e2 100644 --- a/src/tty.c +++ b/src/tty.c @@ -882,17 +882,17 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) case INPUT_MODE_HEX: option.input_mode = INPUT_MODE_HEX; - tio_printf("Switched to hex input mode"); + tio_printf("Switched input mode to hex"); break; case INPUT_MODE_LINE: option.input_mode = INPUT_MODE_LINE; - tio_printf("Switched to line input mode"); + tio_printf("Switched input mode to line"); break; case INPUT_MODE_END: option.input_mode = INPUT_MODE_NORMAL; - tio_printf("Switched to normal input mode"); + tio_printf("Switched input mode to normal"); break; } break; @@ -906,13 +906,13 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) case OUTPUT_MODE_HEX: tty_output_mode_set(OUTPUT_MODE_HEX); - tio_printf("Switched to hex output mode"); + tio_printf("Switched output mode to hex"); break; case OUTPUT_MODE_END: option.output_mode = OUTPUT_MODE_NORMAL; tty_output_mode_set(OUTPUT_MODE_NORMAL); - tio_printf("Switched to normal output mode"); + tio_printf("Switched output mode to normal"); break; } break; @@ -959,20 +959,20 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) case TIMESTAMP_NONE: break; case TIMESTAMP_24HOUR: - tio_printf("Switched to 24hour timestamp mode"); + tio_printf("Switched timestamp mode to 24hour"); break; case TIMESTAMP_24HOUR_START: - tio_printf("Switched to 24hour-start timestamp mode"); + tio_printf("Switched timestamp mode to 24hour-start"); break; case TIMESTAMP_24HOUR_DELTA: - tio_printf("Switched to 24hour-delta timestamp mode"); + tio_printf("Switched timestamp mode to 24hour-delta"); break; case TIMESTAMP_ISO8601: - tio_printf("Switched to iso8601 timestamp mode"); + tio_printf("Switched timestamp mode to iso8601"); break; case TIMESTAMP_END: option.timestamp = TIMESTAMP_NONE; - tio_printf("Switched timestamp off"); + tio_printf("Switched timestamp mode to off"); break; } break; From a8e0d2693d8e0016d09e90012a23de30999294be Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 18 Apr 2024 13:39:55 +0200 Subject: [PATCH 264/506] Do not echo CR in line input and hex output mode --- src/tty.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tty.c b/src/tty.c index e43c4e2..e4e348a 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1519,7 +1519,10 @@ void forward_to_tty(int fd, char output_char) } else { - optional_local_echo(output_char); + if (option.input_mode == INPUT_MODE_NORMAL) + { + optional_local_echo(output_char); + } } break; From 6fff4939e4e48ba0b3af60cda7d2b901d3fed569 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 18 Apr 2024 15:45:50 +0200 Subject: [PATCH 265/506] Add timeout based timestamps in hex output mode This change reintroduces timestamping in hex output mode but based on timeout instead of new lines which made no sense. This means that timestamps will only be printed when timeout time has elapsed with no output activity from serial device. Adds option --timestamp-timeout for setting the timeout value in milliseconds. Defaults to 200 ms. --- README.md | 4 +++- TODO | 17 +++++--------- man/tio.1.in | 12 ++++++++++ src/bash-completion/tio.in | 5 +++++ src/configfile.c | 4 ++++ src/options.c | 8 +++++++ src/options.h | 1 + src/tty.c | 45 +++++++++++++++++++++++++++++++------- 8 files changed, 76 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 8d502cb..6127a1a 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,8 @@ when used in combination with [tmux](https://tmux.github.io). * Pulse serial lines with configurable pulse duration * Local echo support * Remapping of characters (nl, cr-nl, bs, lowercase to uppercase, etc.) - * Line timestamps + * Per line timestamps in normal output mode + * Timeout based timestamps in hex output mode * Support for delayed output per character * Support for delayed output per line * Switchable independent input and output mode (normal vs hex) @@ -94,6 +95,7 @@ Options: --output-mode normal|hex Select output mode (default: normal) -t, --timestamp Enable line timestamp --timestamp-format Set timestamp format (default: 24hour) + --timestamp-timeout Set timestamp timeout (default: 200) -L, --list-devices List available serial devices by ID -l, --log Enable log to file --log-file Set log filename diff --git a/TODO b/TODO index 59ea160..99a1b7b 100644 --- a/TODO +++ b/TODO @@ -17,20 +17,15 @@ With and without timestamp support for each broken up line. - * Add support for activity based time stamping in both normal and hex-mode. + * Add support for activity based time stamping in normal output mode - Will print a new timestamp if there is no input activity within the time - defined in the configuration file, e.g.: + Already supported in hex output mode. - timestamp-timeout = 200 + * Advanced line mode - Time is in ms. - -* 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. + 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. * Support for running external command diff --git a/man/tio.1.in b/man/tio.1.in index 5cf55d7..5b15b8c 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -121,6 +121,16 @@ ISO8601 format ("YYYY-MM-DDThh:mm:ss.sss") Default format is \fB24hour\fR .RE +.TP +.BR " \-\-timestamp\-timeout \fI + +Set timestamp timeout value in milliseconds. + +This value only takes effect in hex output mode where timestamps are only +printed after elapsed timeout time of no output activity from tty device. + +Default value is 200. + .TP .BR \-L ", " \-\-list\-devices @@ -479,6 +489,8 @@ Enable local echo Enable line timestamp .IP "\fBtimestamp-format" Set timestamp format +.IP "\fBtimestamp-timeout" +Set timestamp timeout .IP "\fBmap" Map characters on input or output .IP "\fBcolor" diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index efe9617..6513b1a 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -28,6 +28,7 @@ _tio() -m --map \ -t --timestamp \ --timestamp-format \ + --timestamp-timeout \ -L --list-devices \ -c --color \ -S --socket \ @@ -118,6 +119,10 @@ _tio() COMPREPLY=( $(compgen -W "24hour 24hour-start 24hour-delta iso8601" -- ${cur}) ) return 0 ;; + --timestamp-timeout) + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + ;; -L | --list-devices) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 diff --git a/src/configfile.c b/src/configfile.c index b123071..62c0edd 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -230,6 +230,10 @@ static int data_handler(void *user, const char *section, const char *name, { option.timestamp = timestamp_option_parse(value); } + else if (!strcmp(name, "timestamp-timeout")) + { + option.timestamp_timeout = read_integer(value, name, 0, LONG_MAX); + } else if (!strcmp(name, "map")) { asprintf(&c.map, "%s", value); diff --git a/src/options.c b/src/options.c index d231cc7..5c49c1d 100644 --- a/src/options.c +++ b/src/options.c @@ -45,6 +45,7 @@ enum opt_t { OPT_NONE, OPT_TIMESTAMP_FORMAT, + OPT_TIMESTAMP_TIMEOUT, OPT_LOG_FILE, OPT_LOG_DIRECTORY, OPT_LOG_STRIP, @@ -105,6 +106,7 @@ struct option_t option = .script = NULL, .script_filename = NULL, .script_run = SCRIPT_RUN_ALWAYS, + .timestamp_timeout = 200, }; void print_help(char *argv[]) @@ -130,6 +132,7 @@ void print_help(char *argv[]) printf(" --output-mode normal|hex Select output mode (default: normal)\n"); printf(" -t, --timestamp Enable line timestamp\n"); printf(" --timestamp-format Set timestamp format (default: 24hour)\n"); + printf(" --timestamp-timeout Set timestamp timeout (default: 200)\n"); printf(" -L, --list-devices List available serial devices by ID\n"); printf(" -l, --log Enable log to file\n"); printf(" --log-file Set log filename\n"); @@ -367,6 +370,7 @@ void options_parse(int argc, char *argv[]) {"local-echo", no_argument, 0, 'e' }, {"timestamp", no_argument, 0, 't' }, {"timestamp-format", required_argument, 0, OPT_TIMESTAMP_FORMAT }, + {"timestamp-timeout", required_argument, 0, OPT_TIMESTAMP_TIMEOUT }, {"list-devices", no_argument, 0, 'L' }, {"log", no_argument, 0, 'l' }, {"log-file", required_argument, 0, OPT_LOG_FILE }, @@ -461,6 +465,10 @@ void options_parse(int argc, char *argv[]) option.timestamp = timestamp_option_parse(optarg); break; + case OPT_TIMESTAMP_TIMEOUT: + option.timestamp_timeout = string_to_long(optarg); + break; + case 'L': list_serial_devices(); exit(EXIT_SUCCESS); diff --git a/src/options.h b/src/options.h index aff99fc..dca5d13 100644 --- a/src/options.h +++ b/src/options.h @@ -88,6 +88,7 @@ struct option_t const char *script; const char *script_filename; enum script_run_t script_run; + unsigned int timestamp_timeout; }; extern struct option_t option; diff --git a/src/tty.c b/src/tty.c index e4e348a..197a431 100644 --- a/src/tty.c +++ b/src/tty.c @@ -22,6 +22,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -972,7 +973,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) break; case TIMESTAMP_END: option.timestamp = TIMESTAMP_NONE; - tio_printf("Switched timestamp mode to off"); + tio_printf("Switched timestamp mode off"); break; } break; @@ -1545,6 +1546,7 @@ int tty_connect(void) char* now = NULL; unsigned int line_index = 0; static char previous_char[2] = {}; + struct timeval tval_before = {}, tval_now, tval_result; /* Open tty device */ device_fd = open(option.tty_device, O_RDWR | O_NOCTTY | O_NONBLOCK); @@ -1713,23 +1715,50 @@ int tty_connect(void) /* Update receive statistics */ rx_total += bytes_read; + // Manage timeout based timestamping in hex mode + if (option.output_mode == OUTPUT_MODE_HEX) + { + if (option.timestamp != TIMESTAMP_NONE) + { + gettimeofday(&tval_now, NULL); + timersub(&tval_now, &tval_before, &tval_result); + if ((tval_result.tv_sec * 1000 + tval_result.tv_usec / 1000) > option.timestamp_timeout) + { + now = timestamp_current_time(); + if (now) + { + ansi_printf_raw("\r\n[%s] ", now); + if (option.log) + { + log_printf("\r\n[%s] ", now); + } + next_timestamp = false; + } + } + tval_before = tval_now; + } + } + /* Process input byte by byte */ for (int i=0; i Date: Thu, 18 Apr 2024 16:23:41 +0200 Subject: [PATCH 266/506] Update README --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 6127a1a..63e2bba 100644 --- a/README.md +++ b/README.md @@ -43,11 +43,13 @@ when used in combination with [tmux](https://tmux.github.io). * Pulse serial lines with configurable pulse duration * Local echo support * Remapping of characters (nl, cr-nl, bs, lowercase to uppercase, etc.) - * Per line timestamps in normal output mode - * Timeout based timestamps in hex output mode - * Support for delayed output per character - * Support for delayed output per line * Switchable independent input and output mode (normal vs hex) + * Timestamp support + * Per line in normal output mode + * Output timeout timestamps in hex output mode + * Support for delayed output + * Per character + * Per line * Log to file with automatic or manual naming of log file * Configuration file support * Activate sub-configurations by name or pattern @@ -58,12 +60,12 @@ when used in combination with [tmux](https://tmux.github.io). * Visual or audible alert on connect/disconnect * Remapping of prefix key * Support NO_COLOR env variable as per no-color.org - * Man page documentation * Lua scripting support for automation * Run script manually or automatically at connect once/always/never * Simple expect/send like functionality with support for regular expressions * Manipulate port control lines (useful for microcontroller reset/boot etc.) * Send files via x/y-modem protocol + * Man page documentation * Plays nicely with [tmux](https://tmux.github.io) ## 3. Usage From b0e9fa02e86e55c9004f6114556df54802a781dc Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 18 Apr 2024 18:55:08 +0200 Subject: [PATCH 267/506] Fix log output in hex output mode --- src/log.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/log.c b/src/log.c index 0962d26..4f09331 100644 --- a/src/log.c +++ b/src/log.c @@ -194,16 +194,23 @@ void log_putc(char c) return; } - if (option.log_strip) + if (option.output_mode == OUTPUT_MODE_HEX) { - if (!log_strip(c)) - { - fputc(c, fp); - } + fprintf(fp, "%02x ", (unsigned char) c); } else { - fputc(c, fp); + if (option.log_strip) + { + if (log_strip(c) == false) + { + fputc(c, fp); + } + } + else + { + fputc(c, fp); + } } } From 29546bb13abc771a5c68a2dbf48d4140097b3623 Mon Sep 17 00:00:00 2001 From: Davis C Date: Thu, 18 Apr 2024 15:01:55 -0500 Subject: [PATCH 268/506] Updated login example with new expect logic --- examples/lua/automatic-login.lua | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/examples/lua/automatic-login.lua b/examples/lua/automatic-login.lua index 1787da1..c951694 100644 --- a/examples/lua/automatic-login.lua +++ b/examples/lua/automatic-login.lua @@ -1,28 +1,30 @@ local logins = { - { - serialnumber = "foo", + ["foo"] = { username = "foouser", password = "foopass", }, - { - serialnumber = "bar", + ["bar"] = { username = "baruser", password = "barpass", }, - { - serialnumber = "baz", + ["baz"] = { username = "bazuser", password = "bazpass", }, } -for _, login in ipairs(logins) do - send("\n") - local found = expect(login.serialnumber .. ".*login:", 10) - if (1 == found) then +send("\n") +local found, match_str = expect("\\w+- login:", 10) +if (1 == found) then + local model = string.match(match_str, "^%w+") + local login = logins[model] + if (nil ~= login) then send(login.username .. "\n") expect("Password:") send(login.password .. "\n") - break + else + print("\r\nDon't know login info for " .. model .. "\r\n") end +else + print("\r\nDidn't find a login prompt\r\n") end From 96fafc5fb4f9cac808910272dcade06b548fcd78 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 19 Apr 2024 14:44:54 +0200 Subject: [PATCH 269/506] Generalize automatic login example for Linux --- .../lua/{automatic-login.lua => automatic-linux-login.lua} | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) rename examples/lua/{automatic-login.lua => automatic-linux-login.lua} (77%) diff --git a/examples/lua/automatic-login.lua b/examples/lua/automatic-linux-login.lua similarity index 77% rename from examples/lua/automatic-login.lua rename to examples/lua/automatic-linux-login.lua index c951694..b700f78 100644 --- a/examples/lua/automatic-login.lua +++ b/examples/lua/automatic-linux-login.lua @@ -13,17 +13,16 @@ local logins = { }, } -send("\n") local found, match_str = expect("\\w+- login:", 10) if (1 == found) then - local model = string.match(match_str, "^%w+") - local login = logins[model] + local hostname = string.match(match_str, "^%w+") + local login = logins[hostname] if (nil ~= login) then send(login.username .. "\n") expect("Password:") send(login.password .. "\n") else - print("\r\nDon't know login info for " .. model .. "\r\n") + print("\r\nDon't know login info for " .. hostname .. "\r\n") end else print("\r\nDidn't find a login prompt\r\n") From 0105346a11674e94f3b3d21cce2c8a15b4fe1322 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 19 Apr 2024 16:56:30 +0200 Subject: [PATCH 270/506] Cleanup --- src/script.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/script.c b/src/script.c index 243ae0c..c19fe95 100644 --- a/src/script.c +++ b/src/script.c @@ -215,8 +215,8 @@ static int send(lua_State *L) return 1; } -// Function to add a character to the circular buffer -void add_to_buffer(char c) +// Function to add a character to the circular expect buffer +static void expect_buffer_add(char c) { if (buffer_size < MAX_BUFFER_SIZE) { @@ -230,8 +230,8 @@ void add_to_buffer(char c) } } -// Function to match against the circular buffer using regex -bool match_regex(regex_t *regex) +// 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; @@ -306,7 +306,7 @@ static int expect(lua_State *L) if (bytes_read > 0) { putchar(c); - add_to_buffer(c); + expect_buffer_add(c); // Match against the entire buffer if (match_regex(®ex)) { From f1c5394570bfdb0d136606e1126be69dc182b473 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 19 Apr 2024 16:57:27 +0200 Subject: [PATCH 271/506] Don't forget to log output in lua expect() --- src/script.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/script.c b/src/script.c index c19fe95..9490057 100644 --- a/src/script.c +++ b/src/script.c @@ -34,6 +34,7 @@ #include "options.h" #include "tty.h" #include "xymodem.h" +#include "log.h" #define MAX_BUFFER_SIZE 2000 // Maximum size of circular buffer @@ -307,6 +308,12 @@ static int expect(lua_State *L) { putchar(c); expect_buffer_add(c); + + if (option.log) + { + log_putc(c); + } + // Match against the entire buffer if (match_regex(®ex)) { From 3ca6a66d9b6275e0d2423cdbab1f9ada150165d6 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 19 Apr 2024 17:10:36 +0200 Subject: [PATCH 272/506] Add lua read_string() function --- src/script.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/script.c b/src/script.c index 9490057..da2a9a1 100644 --- a/src/script.c +++ b/src/script.c @@ -266,6 +266,59 @@ static bool match_regex(regex_t *regex) return false; } +// lua: ret,string = read_string(size, timeout) +static int read_string(lua_State *L) +{ + int size = lua_tointeger(L, 1); + int timeout = lua_tointeger(L, 2); + int ret = 0; + + char *buffer = malloc(size); + if (buffer == NULL) + { + ret = -3; // Read buffer allocation failed + goto error; + } + + if (timeout == 0) + { + timeout = -1; // Wait forever + } + + ssize_t bytes_read = read_poll(device_fd, buffer, size, timeout); + if (bytes_read < 0) + { + ret = -1; // Read error + goto error; + } + else if (bytes_read == 0) + { + ret = -2; // Timeout + goto error; + } + + for (ssize_t i=0; i Date: Fri, 19 Apr 2024 20:11:52 +0200 Subject: [PATCH 273/506] Update description of mute option --- README.md | 2 +- man/tio.1.in | 7 +++++++ src/options.c | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 63e2bba..b20351e 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ Options: --rs-485 Enable RS-485 mode --rs-485-config Set RS-485 configuration --alert bell|blink|none Alert on connect/disconnect (default: none) - --mute Mute tio + --mute Mute tio messages --script Run script from string --script-file Run script from file --script-run once|always|never Run script on connect (default: always) diff --git a/man/tio.1.in b/man/tio.1.in index 5b15b8c..70d9053 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -299,6 +299,11 @@ will sound the bell twice or blink twice on disconnect. Default value is "none". +.TP +.BR "\-\-mute" + +Mute tio messages. + .TP .BR "\-\-script \fI @@ -509,6 +514,8 @@ Enable RS-485 mode Set RS-485 configuration .IP "\fBalert" Set alert action on connect/disconnect +.IP "\fBmute" +Mute tio messages .IP "\fBscript" Run script from string .IP "\fBscript-file" diff --git a/src/options.c b/src/options.c index 5c49c1d..500ede1 100644 --- a/src/options.c +++ b/src/options.c @@ -145,7 +145,7 @@ void print_help(char *argv[]) printf(" --rs-485 Enable RS-485 mode\n"); printf(" --rs-485-config Set RS-485 configuration\n"); printf(" --alert bell|blink|none Alert on connect/disconnect (default: none)\n"); - printf(" --mute Mute tio\n"); + printf(" --mute Mute tio messages\n"); printf(" --script Run script from string\n"); printf(" --script-file Run script from file\n"); printf(" --script-run once|always|never Run script on connect (default: always)\n"); From 51bfa68bdd7c28331a910137f7d1cd6a1aa4356c Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 20 Apr 2024 14:03:38 +0200 Subject: [PATCH 274/506] Text cleanup --- src/tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tty.c b/src/tty.c index 197a431..3eb7ae6 100644 --- a/src/tty.c +++ b/src/tty.c @@ -376,7 +376,7 @@ void *tty_stdin_input_thread(void *arg) exit(EXIT_SUCCESS); break; case KEY_SHIFT_F: - tio_printf("Flushed data I/O channels") + tio_printf("Flushed data I/O buffers") tcflush(device_fd, TCIOFLUSH); break; default: From b05f38abd013174465a499b38f7e0a2acc634b5b Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 20 Apr 2024 14:51:45 +0200 Subject: [PATCH 275/506] Add missing options to show configuration --- src/alert.c | 20 ++++++++++++++++++-- src/alert.h | 7 ++++--- src/options.c | 21 ++++++++++++++++++++- src/options.h | 6 +++--- src/script.c | 16 ++++++++++++++++ src/script.h | 5 +++-- 6 files changed, 64 insertions(+), 11 deletions(-) diff --git a/src/alert.c b/src/alert.c index cc6abdd..e2f90a4 100644 --- a/src/alert.c +++ b/src/alert.c @@ -19,6 +19,7 @@ * 02110-1301, USA. */ +#include "alert.h" #include "config.h" #include #include @@ -28,9 +29,9 @@ #include "print.h" #include "options.h" -enum alert_t alert_option_parse(const char *arg) +alert_t alert_option_parse(const char *arg) { - enum alert_t alert = option.alert; // Default + alert_t alert = option.alert; // Default if (arg != NULL) { @@ -108,3 +109,18 @@ void alert_disconnect(void) break; } } + +const char *alert_state_to_string(alert_t state) +{ + switch (state) + { + case ALERT_NONE: + return "none"; + case ALERT_BELL: + return "bell"; + case ALERT_BLINK: + return "blink"; + default: + return "Unknown"; + } +} diff --git a/src/alert.h b/src/alert.h index 10932ea..d7b66f5 100644 --- a/src/alert.h +++ b/src/alert.h @@ -21,14 +21,15 @@ #pragma once -enum alert_t +typedef enum { ALERT_NONE, ALERT_BELL, ALERT_BLINK, ALERT_END, -}; +} alert_t; -enum alert_t alert_option_parse(const char *arg); +alert_t alert_option_parse(const char *arg); void alert_connect(void); void alert_disconnect(void); +const char *alert_state_to_string(alert_t state); diff --git a/src/options.c b/src/options.c index 500ede1..79556aa 100644 --- a/src/options.c +++ b/src/options.c @@ -289,7 +289,7 @@ const char *output_mode_by_string(output_mode_t mode) return NULL; } -enum script_run_t script_run_option_parse(const char *arg) +script_run_t script_run_option_parse(const char *arg) { if (strcmp("once", arg) == 0) { @@ -320,6 +320,7 @@ void options_print() tio_printf(" Parity: %s", option.parity); tio_printf(" Local echo: %s", option.local_echo ? "enabled" : "disabled"); tio_printf(" Timestamp: %s", timestamp_state_to_string(option.timestamp)); + tio_printf(" Timestamp timeout: %u", option.timestamp_timeout); tio_printf(" Output delay: %d", option.output_delay); tio_printf(" Output line delay: %d", option.output_line_delay); tio_printf(" Auto connect: %s", option.no_autoconnect ? "disabled" : "enabled"); @@ -331,12 +332,30 @@ void options_print() option.ri_pulse_duration); tio_printf(" Input mode: %s", input_mode_by_string(option.input_mode)); tio_printf(" Output mode: %s", output_mode_by_string(option.output_mode)); + tio_printf(" Alert: %s", 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()); + if (option.log_directory != NULL) + { + tio_printf(" Log file directory: %s", option.log_directory); + } + tio_printf(" Log append: %s", option.log_append ? "enabled" : "disabled"); + tio_printf(" Log strip: %s", option.log_strip ? "enabled" : "disabled"); + } if (option.socket) + { tio_printf(" Socket: %s", option.socket); + } + if (option.script_filename != NULL) + { + tio_printf(" Script file: %s", option.script_filename); + tio_printf(" Script run: %s", script_run_state_to_string(option.script_run)); + } } void options_parse(int argc, char *argv[]) diff --git a/src/options.h b/src/options.h index dca5d13..6f872a5 100644 --- a/src/options.h +++ b/src/options.h @@ -83,11 +83,11 @@ struct option_t uint32_t rs485_config_flags; int32_t rs485_delay_rts_before_send; int32_t rs485_delay_rts_after_send; - enum alert_t alert; + alert_t alert; bool complete_sub_configs; const char *script; const char *script_filename; - enum script_run_t script_run; + script_run_t script_run; unsigned int timestamp_timeout; }; @@ -98,7 +98,7 @@ void options_parse(int argc, char *argv[]); void options_parse_final(int argc, char *argv[]); void line_pulse_duration_option_parse(const char *arg); -enum script_run_t script_run_option_parse(const char *arg); +script_run_t script_run_option_parse(const char *arg); input_mode_t input_mode_option_parse(const char *arg); output_mode_t output_mode_option_parse(const char *arg); diff --git a/src/script.c b/src/script.c index da2a9a1..8dd91dd 100644 --- a/src/script.c +++ b/src/script.c @@ -35,6 +35,7 @@ #include "tty.h" #include "xymodem.h" #include "log.h" +#include "script.h" #define MAX_BUFFER_SIZE 2000 // Maximum size of circular buffer @@ -523,3 +524,18 @@ void script_run(int fd) lua_close(L); } + +const char *script_run_state_to_string(script_run_t state) +{ + switch (state) + { + case SCRIPT_RUN_ONCE: + return "once"; + case SCRIPT_RUN_ALWAYS: + return "always"; + case SCRIPT_RUN_NEVER: + return "never"; + default: + return "Unknown"; + } +} diff --git a/src/script.h b/src/script.h index a2778e7..d6df204 100644 --- a/src/script.h +++ b/src/script.h @@ -21,12 +21,13 @@ #pragma once -enum script_run_t +typedef enum { SCRIPT_RUN_ONCE, SCRIPT_RUN_ALWAYS, SCRIPT_RUN_NEVER, SCRIPT_RUN_END, -}; +} script_run_t; void script_run(int fd); +const char *script_run_state_to_string(script_run_t state); From ae76f8f58d076f4159ae7bd748a1665e3659f34e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 20 Apr 2024 15:02:43 +0200 Subject: [PATCH 276/506] Clean up timestamp enum definition --- src/options.h | 2 +- src/timestamp.c | 6 +++--- src/timestamp.h | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/options.h b/src/options.h index 6f872a5..699c875 100644 --- a/src/options.h +++ b/src/options.h @@ -67,7 +67,7 @@ struct option_t bool log_append; bool log_strip; bool local_echo; - enum timestamp_t timestamp; + timestamp_t timestamp; const char *log_filename; const char *log_directory; const char *map; diff --git a/src/timestamp.c b/src/timestamp.c index aae80c3..19ea70e 100644 --- a/src/timestamp.c +++ b/src/timestamp.c @@ -94,7 +94,7 @@ char *timestamp_current_time(void) return (len < TIME_STRING_SIZE_MAX) ? time_string : NULL; } -const char* timestamp_state_to_string(enum timestamp_t timestamp) +const char* timestamp_state_to_string(timestamp_t timestamp) { switch (timestamp) { @@ -124,9 +124,9 @@ const char* timestamp_state_to_string(enum timestamp_t timestamp) } } -enum timestamp_t timestamp_option_parse(const char *arg) +timestamp_t timestamp_option_parse(const char *arg) { - enum timestamp_t timestamp = TIMESTAMP_24HOUR; // Default + timestamp_t timestamp = TIMESTAMP_24HOUR; // Default if (arg != NULL) { diff --git a/src/timestamp.h b/src/timestamp.h index 572be3c..e36660a 100644 --- a/src/timestamp.h +++ b/src/timestamp.h @@ -21,7 +21,7 @@ #pragma once -enum timestamp_t +typedef enum { TIMESTAMP_NONE, TIMESTAMP_24HOUR, @@ -29,8 +29,8 @@ enum timestamp_t TIMESTAMP_24HOUR_DELTA, TIMESTAMP_ISO8601, TIMESTAMP_END, -}; +} timestamp_t; char *timestamp_current_time(void); -const char* timestamp_state_to_string(enum timestamp_t timestamp); -enum timestamp_t timestamp_option_parse(const char *arg); +const char* timestamp_state_to_string(timestamp_t timestamp); +timestamp_t timestamp_option_parse(const char *arg); From d19ba1c4920e135b77a093401d752e1a80e0cee7 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 26 Apr 2024 20:34:17 +0200 Subject: [PATCH 277/506] Add new ways to manage serial devices * Rename --list-devices to --list * Rename --no-autoconnect to --no-reconnect * Switch -l and -L options * -l now lists available serial devices * -L enables log to file * Add option --auto-connect * Supported strategies: * "new" - Waits to connect first new appearing serial device * "latest" - Connects to latest registered serial device * "direct" - Connect directly to specified serial device (default) * Add options to exclude serial devices from auto connect strategy by pattern * Supported exclude options: * --exclude-devices Example: '--exclude-devices "/dev/ttyUSB2,/dev/ttyS?"' * --exclude-drivers Example: '--exclude-drivers "cdc_acm"' * --exclude-tids Example: '--exclude-tids "yW07,bCC2"' * Patterns support '*' and '?' * Connect to same port/device combination via unique topology ID (TID) * Topology ID is a 4 digit base62 encoded hash of a device topology string coming from the Linux kernel. This means that whenever you plug in the same e.g. USB serial port device to the same USB hub port connected via the exact same hub topology all the way to your computer, you will get the same unique TID. * Useful for stable reconnections when serial device has no serial device by ID * For now, only tested on Linux. * Reworked and improved listing of serial devices to show serial devices: * By device * Including TID, uptime, driver, and description. * Sorted by uptime (newest device listed last) * By unique topology ID * By ID * By path * Add script interface 'list = tty_search()' for searching for serial devices. --- .circleci/config.yml | 2 +- .github/workflows/codeql-buildscript.sh | 2 +- README.md | 153 ++++++- examples/config/config | 6 +- examples/lua/serial-device-search.lua | 13 + man/tio.1.in | 78 +++- man/tio.1.txt | 87 +++- src/bash-completion/tio.in | 16 +- src/configfile.c | 38 +- src/error.c | 2 +- src/fs.c | 205 +++++++++ src/fs.h | 31 ++ src/log.c | 18 +- src/main.c | 5 +- src/meson.build | 4 +- src/misc.c | 133 +++++- src/misc.h | 6 +- src/options.c | 141 ++++-- src/options.h | 11 +- src/script.c | 75 +++- src/tty.c | 569 +++++++++++++++++++++++- src/tty.h | 23 + 22 files changed, 1468 insertions(+), 150 deletions(-) create mode 100644 examples/lua/serial-device-search.lua create mode 100644 src/fs.c create mode 100644 src/fs.h diff --git a/.circleci/config.yml b/.circleci/config.yml index 8baeeb0..43e399a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -15,7 +15,7 @@ jobs: steps: - checkout - run: sudo apt-get -qq update - - run: sudo apt-get install -y bash-completion git meson libinih-dev liblua5.2-dev + - run: sudo apt-get install -y bash-completion git meson libinih-dev liblua5.2-dev libglib2.0-dev - run: git clone https://github.com/tio/tio.git - run: cd tio && meson build --prefix $HOME/test/tio && ninja -C build install diff --git a/.github/workflows/codeql-buildscript.sh b/.github/workflows/codeql-buildscript.sh index 4b9e11d..720bd6b 100644 --- a/.github/workflows/codeql-buildscript.sh +++ b/.github/workflows/codeql-buildscript.sh @@ -2,6 +2,6 @@ pip3 install meson -U pip3 install ninja -U -sudo apt-get install -y liblua5.2-dev +sudo apt-get install -y liblua5.2-dev libglib2.0-dev meson setup build meson compile -C build diff --git a/README.md b/README.md index b20351e..13179d2 100644 --- a/README.md +++ b/README.md @@ -31,40 +31,61 @@ when used in combination with [tmux](https://tmux.github.io). ## 2. Features * Easily connect to serial TTY devices - * Automatic connect and reconnect * Sensible defaults (115200 8n1) * Support for non-standard baud rates * Support for mark and space parity + * Automatic connection management + * 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 * X-modem (1K/CRC) and Y-modem file upload * Support for RS-485 mode - * List available serial devices by ID + * List available serial devices + * By device + * Including topology ID, uptime, driver, description + * Sorted by uptime (newest device listed last) + * By ID + * By path * Show RX/TX statistics * Toggle serial lines * Pulse serial lines with configurable pulse duration * Local echo support * Remapping of characters (nl, cr-nl, bs, lowercase to uppercase, etc.) - * Switchable independent input and output mode (normal vs hex) + * Switchable independent input and output + * Normal mode + * Hex mode + * Line mode (input only) * Timestamp support * Per line in normal output mode * Output timeout timestamps in hex output mode * Support for delayed output * Per character * Per line - * Log to file with automatic or manual naming of log file + * Log to file + * Automatic naming of log file (default) + * Configurable directory for saving automatic named log files + * Manual naming of log file + * Overwrite (default) or append to log file + * Strip control characters and escape sequences * Configuration file support - * Activate sub-configurations by name or pattern - * Redirect I/O to UNIX socket or IPv4/v6 network socket for scripting or TTY sharing + * Support for sub-configurations + * Activate sub-configurations by name or pattern + * Redirect I/O to UNIX socket or IPv4/v6 network socket + * Useful for scripting or TTY sharing * Pipe input and/or output * Bash completion on options, serial device names, and sub-configuration names - * Configurable text color + * Configurable tio message text color + * Supports NO_COLOR env variable as per no-color.org * Visual or audible alert on connect/disconnect * Remapping of prefix key - * Support NO_COLOR env variable as per no-color.org * Lua scripting support for automation - * Run script manually or automatically at connect once/always/never + * Run script manually or automatically at connect (once/always/never) * Simple expect/send like functionality with support for regular expressions * Manipulate port control lines (useful for microcontroller reset/boot etc.) * Send files via x/y-modem protocol + * Search for serial devices * Man page documentation * Plays nicely with [tmux](https://tmux.github.io) @@ -78,9 +99,9 @@ For more usage details please see the man page documentation The command-line interface is straightforward as reflected in the output from 'tio --help': ``` -Usage: tio [] +Usage: tio [] -Connect to TTY device directly or via sub-configuration. +Connect to TTY device directly or via sub-configuration or topology ID. Options: -b, --baudrate Baud rate (default: 115200) @@ -91,15 +112,19 @@ Options: -o, --output-delay Output character delay (default: 0) -O, --output-line-delay Output line delay (default: 0) --line-pulse-duration Set line pulse duration - -n, --no-autoconnect Disable automatic connect + -a, --auto-connect new|latest|direct Automatic connect strategy (default: direct) + --exclude-devices Exclude devices by pattern + --exclude-drivers Exclude drivers by pattern + --exclude-tids Exclude topology IDs by pattern + -n, --no-reconnect Do not reconnect -e, --local-echo Enable local echo --input-mode normal|hex|line Select input mode (default: normal) --output-mode normal|hex Select output mode (default: normal) -t, --timestamp Enable line timestamp --timestamp-format Set timestamp format (default: 24hour) --timestamp-timeout Set timestamp timeout (default: 200) - -L, --list-devices List available serial devices by ID - -l, --log Enable log to file + -l, --list List available serial devices + -L, --log Enable log to file --log-file Set log filename --log-directory Set log directory path for automatic named logs --log-append Append to log file @@ -125,7 +150,7 @@ See the man page for more details. By default tio automatically connects to the provided TTY device if present. If the device is not present, it will wait for it to appear and then connect. If the connection is lost (eg. device is unplugged), it will wait for the -device to reappear and then reconnect. However, if the `--no-autoconnect` +device to reappear and then reconnect. However, if the `--no-reconnect` option is provided, tio will exit if the device is not present or an established connection is lost. @@ -150,18 +175,23 @@ $ tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 Using serial devices by ID ensures that tio automatically reconnects to the correct serial device if it is disconnected and then reconnected. -List available serial devices by ID: +List available serial devices: ``` -$ tio --list-devices +$ tio --list ``` -Note: One can also use tio shell completion on /dev which will automatically -list all available serial TTY devices. +Note: One can also use tio bash shell completion on /dev which will +automatically list all available serial TTY devices by ID. Log to file with autogenerated filename: ``` $ tio --log /dev/ttyUSB0 ``` +Log to file with filename: +``` +$ tio --log --log-file my-log.txt +``` + Enable ISO8601 timestamps per line: ``` $ tio --timestamp --timestamp-format iso8601 /dev/ttyUSB0 @@ -183,6 +213,80 @@ $ echo "*IDN?" | tio /dev/ttyACM0 --script "expect('\r\n', 1000)" --mute KORAD KD3305P V4.2 SN:32475045 ``` +### 3.1.2 Different ways to connect to serial devices + +Using tio there are up to 4 recommended ways to connect to a specific serial +device: + + * Connect by ID + * Example: ```tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTCHUV56-if00-port0``` + * Connect by topology ID + * Example: ```tio bCC2``` + * Connect to enumerated device in /dev + * Example: ```tio /dev/ttyUSB4``` + * Connect by path + * Example: ```tio /dev/serial/by-path/pci-0000:00:14.0-usb-0:8.1.3.1.4:1.0-port0``` + +Which serial device to connect becomes more clear from tio's serial device +listing which provides more information about each serial device. For example: +``` +$ tio --list +Device TID Uptime [s] Driver Description +----------------- ---- ------------- ---------------- -------------------------- +/dev/ttyS4 8xSh 32532.317 port 16550A UART +/dev/ttyS5 HJhB 32530.578 port 16550A UART +/dev/ttyUSB3 yW07 32464.194 ftdi_sio TTL232RG-VREG3V3 +/dev/ttyUSB4 bCC2 26066.573 ftdi_sio TTL232R-3V3 +/dev/ttyUSB0 g5q4 136.717 ftdi_sio Flyswatter2 +/dev/ttyUSB1 h5q4 136.715 ftdi_sio Flyswatter2 +/dev/ttyACM0 EOEs 10.449 cdc_acm ST-Link VCP Ctrl + +By-id +-------------------------------------------------------------------------------- +/dev/serial/by-id/usb-FTDI_TTL232RG-VREG3V3_FT1NC2D0-if00-port0 +/dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTCHUV56-if00-port0 +/dev/serial/by-id/usb-TinCanTools_Flyswatter2_FS20000-if00-port0 +/dev/serial/by-id/usb-TinCanTools_Flyswatter2_FS20000-if01-port0 +/dev/serial/by-id/usb-STMicroelectronics_STLINK-V3_004900343438510234313939-if02 + +By-path +-------------------------------------------------------------------------------- +/dev/serial/by-path/pci-0000:00:14.0-usb-0:8.1.3.2.2:1.0-port0 +/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:8.1.3.2.2:1.0-port0 +/dev/serial/by-path/pci-0000:00:14.0-usb-0:8.1.3.1.4:1.0-port0 +/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:8.1.3.1.4:1.0-port0 +/dev/serial/by-path/pci-0000:00:14.0-usb-0:6.3:1.0-port0 +/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:6.3:1.0-port0 +/dev/serial/by-path/pci-0000:00:14.0-usb-0:6.3:1.1-port0 +/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:6.3:1.1-port0 +/dev/serial/by-path/pci-0000:00:14.0-usb-0:6.4:1.2 +/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:6.4:1.2 +``` + +Note: The topology ID (TID) is a special hash of the full topology path of the +connected device. This means that every time e.g. a USB serial device is +plugged into to the exact same USB hub chain it will get the exact same TID. +This helps solve the problem of reconnecting to serical devices which do not +provide a unique device by ID. + +Additonally tio offers two convenient ways of connecting to serial devices: + + * Connect automatically to first new appearing serial device + * ```tio --auto-connect new``` + * Connect automatically to latest registered serial device + * ```tio --auto-connect latest``` + +It is also possible to use excludes to affect strategy decisions: + + * Exclude devices by pattern + * Example: ```tio --auto-connect new --exclude-devices "/dev/ttyACM?,/dev/ttyUSB2"``` + * Exclude drivers by pattern + * Example: ```tio --auto-connect new --exclude-drivers "cdc_acm,ftdi_sio"``` + * Exclude topology IDs by pattern + * Example: ```tio --auto-connect new --exclude-tids "EOEs"``` + +Note: Pattern matching supports '*' and '?'. Use comma separation to define multiple patterns. + ### 3.2 Key commands Various in session key commands are supported. When tio is started, press @@ -237,6 +341,15 @@ In addition to the Lua API tio makes the following functions available: 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. exit(code) Exit with code. @@ -299,7 +412,7 @@ color = 10 [rpi3] device = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 baudrate = 115200 -no-autoconnect = enable +no-reconnect = enable log = enable log-file = rpi3.log line-pulse-duration = DTR=200,RTS=150 diff --git a/examples/config/config b/examples/config/config index 5f72f2a..200ec79 100644 --- a/examples/config/config +++ b/examples/config/config @@ -18,8 +18,10 @@ parity = none prefix-ctrl-key = t output-delay = 0 output-line-delay = 0 -no-autoconnect = disable -hexadecimal = disable +auto-connect = direct +no-reconnect = disable +input-mode = normal +output-mode = normal timestamp = disable log = disable log-append = disable diff --git a/examples/lua/serial-device-search.lua b/examples/lua/serial-device-search.lua new file mode 100644 index 0000000..120d650 --- /dev/null +++ b/examples/lua/serial-device-search.lua @@ -0,0 +1,13 @@ +io.write("Searching... ") + +local device = tty_search() + +io.write("done\r\n") + +for i in ipairs(device) do + io.write("\r\n" .. device[i]["path"] .. "\r\n") + io.write(" tid = " .. device[i]["tid"] .. "\r\n") + io.write(" uptime = " .. device[i]["uptime"] .. "\r\n") + io.write(" driver = " .. device[i]["driver"] .. "\r\n") + io.write(" description = " .. device[i]["description"] .. "\r\n") +end diff --git a/man/tio.1.in b/man/tio.1.in index 70d9053..861ccf0 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -80,17 +80,48 @@ The default pulse duration for each line is 100 ms. .RE .TP -.BR \-n ", " \-\-no\-autoconnect +.BR "\-a, \-\-auto\-connect new|latest|direct" -Disable automatic connect. +Automatically connect to serial device according to one of the following +strategies: -By default tio automatically connects to the provided device if present. If the -device is not present, it will wait for it to appear and then connect. If the -connection is lost (eg. device disconnects), it will wait for the device to -reappear and then reconnect. +.RS +.TP 10n +.IP "\fBnew" +Automatically connect to first new appearing serial device. +.IP "\fBlatest" +Automatically connect to latest registered serial device. +.IP "\fBdirect" +Connect directly to specified TTY device. +.P +All the listed strategies automatically reconnects according to strategy if +device is not available or connection is lost. +.P +Default value is "direct". +.RE -However, if the \fB\-\-no\-autoconnect\fR option is provided, tio will exit if -the device is not present or an established connection is lost. +.TP +.BR " \-\-exclude\-devices \fI" + +Exclude devices by pattern ('*' and '?' supported). + +.TP +.BR " \-\-exclude\-drivers \fI" + +Exclude drivers by pattern ('*' and '?' supported). + +.TP +.BR " \-\-exclude\-tids \fI" + +Exclude topology IDs by pattern ('*' and '?' supported). + +.TP +.BR \-n ", " \-\-no\-reconnect + +Do not reconnect. + +This means that tio will exit if it fails to connect to device or an +established connection is lost. .TP .BR \-e ", " "\-\-local\-echo @@ -103,7 +134,7 @@ Enable local echo. Enable line timestamp. .TP -.BR " \-\-timestamp\-format \fI +.BR " \-\-timestamp\-format \fI" Set timestamp format to any of the following timestamp formats: .RS @@ -122,7 +153,7 @@ Default format is \fB24hour\fR .RE .TP -.BR " \-\-timestamp\-timeout \fI +.BR " \-\-timestamp\-timeout \fI" Set timestamp timeout value in milliseconds. @@ -132,12 +163,12 @@ printed after elapsed timeout time of no output activity from tty device. Default value is 200. .TP -.BR \-L ", " \-\-list\-devices +.BR \-l ", " \-\-list -List available serial devices by ID. +List available serial devices. .TP -.BR \-l ", " \-\-log +.BR \-L ", " \-\-log Enable log to file. @@ -401,6 +432,17 @@ Send string. 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 "\fBexit(code)" Exit with exit code. .IP "\fBhigh(line)" @@ -476,8 +518,8 @@ Set output character delay Set output line delay .IP "\fBline-pulse-duration" Set line pulse duration -.IP "\fBno-autoconnect" -Disable automatic connect +.IP "\fBno-reconnect" +Do not reconnect .IP "\fBlog" Enable log to file .IP "\fBlog-file" @@ -501,9 +543,9 @@ Map characters on input or output .IP "\fBcolor" Colorize tio text using ANSI color code ranging from 0 to 255 .IP "\fBinput-mode" -Set input mode. +Set input mode .IP "\fBoutput-mode" -Set output mode. +Set output mode .IP "\fBsocket" Set socket to redirect I/O to .IP "\fBprefix-ctrl-key" @@ -521,7 +563,7 @@ Run script from string .IP "\fBscript-file" Run script from file .IP "\fBscript-run" -Run script on connect. +Run script on connect .SH "CONFIGURATION FILE EXAMPLES" diff --git a/man/tio.1.txt b/man/tio.1.txt index 1da130f..a6851db 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -62,13 +62,37 @@ OPTIONS The default pulse duration for each line is 100 ms. - -n, --no-autoconnect + -a, --auto-connect new|latest|direct - Disable automatic connect. + Automatically connect to serial device according to one of the following strategies: - By default tio automatically connects to the provided device if present. If the device is not present, it will wait for it to appear and then connect. If the connection is lost (eg. device disconnects), it will wait for the device to reappear and then reconnect. + new Automatically connect to first new appearing serial device. - However, if the --no-autoconnect option is provided, tio will exit if the device is not present or an established connection is lost. + latest Automatically connect to latest registered serial device. + + direct Connect directly to specified TTY device. + + All the listed strategies automatically reconnects according to strategy if device is not available or connection is lost. + + Default value is "direct". + + --exclude-devices + + Exclude devices by pattern ('*' and '?' supported). + + --exclude-drivers + + Exclude drivers by pattern ('*' and '?' supported). + + --exclude-tids + + Exclude topology IDs by pattern ('*' and '?' supported). + + -n, --no-reconnect + + Do not reconnect. + + This means that tio will exit if it fails to connect to device or an established connection is lost. -e, --local-echo @@ -92,11 +116,19 @@ OPTIONS Default format is 24hour - -L, --list-devices + --timestamp-timeout - List available serial devices by ID. + Set timestamp timeout value in milliseconds. - -l, --log + This value only takes effect in hex output mode where timestamps are only printed after elapsed timeout time of no output activity from tty device. + + Default value is 200. + + -l, --list + + List available serial devices. + + -L, --log Enable log to file. @@ -148,9 +180,15 @@ OPTIONS If defining more than one flag, the flags must be comma separated. - --input-mode normal|hex + --input-mode normal|hex|line - Set input mode. In hex input mode bytes can be sent by typing the two-character hexadecimal representation of the 1 byte value, e.g.: to send 0xA you must type 0a or 0A. + Set input mode. + + In normal mode input characters are sent immediately as they are typed. + + In hex input mode bytes can be sent by typing the two-character hexadecimal representation of the 1 byte value, e.g.: to send 0xA you must type 0a or 0A. + + In line input mode input characters are sent when you press enter. The only editing feature supported in this mode is backspace. Default value is "normal". @@ -216,6 +254,10 @@ OPTIONS Default value is "none". + --mute + + Mute tio messages. + --script Run script from string. @@ -293,6 +335,10 @@ SCRIPT API 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 invalid regular expression. + + On successful match it also returns the match string as second return value. + send(string) Send string. @@ -301,6 +347,13 @@ SCRIPT API 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. + exit(code) Exit with exit code. @@ -369,7 +422,7 @@ CONFIGURATION FILE line-pulse-duration Set line pulse duration - no-autoconnect Disable automatic connect + no-reconnect Do not reconnect log Enable log to file @@ -387,13 +440,15 @@ CONFIGURATION FILE timestamp-format Set timestamp format + timestamp-timeout Set timestamp timeout + map Map characters on input or output color Colorize tio text using ANSI color code ranging from 0 to 255 - input-mode Set input mode. + input-mode Set input mode - output-mode Set output mode. + output-mode Set output mode socket Set socket to redirect I/O to @@ -405,11 +460,13 @@ CONFIGURATION FILE alert Set alert action on connect/disconnect + mute Mute tio messages + script Run script from string script-file Run script from file - script-run Run script on connect. + script-run Run script on connect CONFIGURATION FILE EXAMPLES To change the default configuration simply set options like so: @@ -530,6 +587,6 @@ WEBSITE Visit https://tio.github.io AUTHOR - Created by Martin Lund . + Maintained by Martin Lund . -tio 2.8 2023-09-19 tio(1) +tio 2.9 2024-04-14 tio(1) diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index 6513b1a..4eb902a 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -18,7 +18,11 @@ _tio() -o --output-delay \ -o --output-line-delay \ --line-pulse-duration \ - -n --no-autoconnect \ + -a --auto-connect \ + --exclude-devices \ + --exclude-drivers \ + --exclude-tids \ + -n --no-reconnect \ -e --local-echo \ -l --log \ --log-file \ @@ -29,7 +33,7 @@ _tio() -t --timestamp \ --timestamp-format \ --timestamp-timeout \ - -L --list-devices \ + -L --list \ -c --color \ -S --socket \ --input-mode \ @@ -79,7 +83,11 @@ _tio() COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 ;; - -n | --no-autoconnect) + -a | --auto-connect) + COMPREPLY=( $(compgen -W "new latest none" -- ${cur}) ) + return 0 + ;; + -n | --no-reconnect) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 ;; @@ -123,7 +131,7 @@ _tio() COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 ;; - -L | --list-devices) + -L | --list) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 ;; diff --git a/src/configfile.c b/src/configfile.c index 62c0edd..00e4fb4 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -54,7 +54,7 @@ struct config_t char *section_name; char *match; - char *tty; + char *target; char *flow; char *parity; char *log_filename; @@ -63,6 +63,9 @@ struct config_t char *script; char *script_filename; bool script_run; + char *exclude_devices; + char *exclude_drivers; + char *exclude_tids; }; static struct config_t c; @@ -151,8 +154,8 @@ static int data_handler(void *user, const char *section, const char *name, // Set configuration parameter if found if (!strcmp(name, "device") || !strcmp(name, "tty")) { - asprintf(&c.tty, value, c.match); - option.tty_device = c.tty; + asprintf(&c.target, value, c.match); + option.target = c.target; } else if (!strcmp(name, "baudrate")) { @@ -188,9 +191,13 @@ static int data_handler(void *user, const char *section, const char *name, { line_pulse_duration_option_parse(value); } - else if (!strcmp(name, "no-autoconnect")) + else if (!strcmp(name, "no-reconnect")) { - option.no_autoconnect = read_boolean(value, name); + option.no_reconnect = read_boolean(value, name); + } + else if (!strcmp(name, "auto-connect")) + { + option.auto_connect = auto_connect_option_parse(value); } else if (!strcmp(name, "log")) { @@ -314,6 +321,21 @@ static int data_handler(void *user, const char *section, const char *name, { option.script_run = script_run_option_parse(value); } + else if (!strcmp(name, "exclude-devices")) + { + c.exclude_devices = strdup(value); + option.exclude_devices = c.exclude_devices; + } + else if (!strcmp(name, "exclude-drivers")) + { + c.exclude_drivers = strdup(value); + option.exclude_drivers = c.exclude_drivers; + } + else if (!strcmp(name, "exclude-tids")) + { + c.exclude_tids = strdup(value); + option.exclude_tids = c.exclude_tids; + } else { tio_warning_printf("Unknown option '%s' in configuration file, ignored", name); @@ -460,8 +482,8 @@ void config_file_parse(void) return; } - // Set user input which may be tty device or sub config - c.user = option.tty_device; + // Set user input which may be tty device or sub config or tid + c.user = option.target; if (!c.user) { @@ -504,7 +526,7 @@ void config_file_parse(void) void config_exit(void) { - free(c.tty); + free(c.target); free(c.flow); free(c.parity); free(c.log_filename); diff --git a/src/error.c b/src/error.c index 8049bc6..9cff900 100644 --- a/src/error.c +++ b/src/error.c @@ -93,7 +93,7 @@ void error_exit(void) /* Print error */ error_printf_("Error: %s", error[0]); } - else if ((error[1][0] != 0) && (option.no_autoconnect)) + else if ((error[1][0] != 0) && (option.no_reconnect)) { /* Print silent error */ error_printf_("Error: %s", error[1]); diff --git a/src/fs.c b/src/fs.c new file mode 100644 index 0000000..fa989d4 --- /dev/null +++ b/src/fs.c @@ -0,0 +1,205 @@ +/* + * 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. + */ + +#define _GNU_SOURCE // For statx() +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "error.h" +#include "print.h" +#include "options.h" + +bool fs_dir_exists(const char *path) +{ + struct stat st; + + if (stat(path, &st) != 0) + { + return false; + } + else if (!S_ISDIR(st.st_mode)) + { + return false; + } + + return true; +} + +// Function to read the content of a file but stripped of newline +ssize_t fs_read_file_stripped(char *buf, size_t bufsiz, const char *format, ...) +{ + char filename[PATH_MAX]; + int bytes_printed = 0; + va_list args; + + va_start(args, format); + bytes_printed = vsnprintf(filename, sizeof(filename), format, args); + va_end(args); + + if (bytes_printed < 0) + { + return -1; + } + + FILE *file = fopen(filename, "r"); + if (!file) + { + return -1; + } + ssize_t length = fread(buf, 1, bufsiz - 1, file); + if (length == -1) + { + fclose(file); + return -1; + } + + // Strip any newline + buf[strcspn(buf, "\n")] = 0; + buf[length] = '\0'; // Make sure to null-terminate the string + + fclose(file); + + return length; +} + +bool fs_file_exists(const char *format, ...) +{ + char filename[PATH_MAX]; + int bytes_printed = 0; + struct stat st; + va_list args; + + va_start(args, format); + bytes_printed = vsnprintf(filename, sizeof(filename), format, args); + va_end(args); + + if (bytes_printed < 0) + { + return false; + } + + return stat(filename, &st) == 0; +} + +char* fs_search_directory(const char *dir_path, const char *dirname) +{ + struct dirent *entry; + char path[PATH_MAX]; + struct stat st; + DIR *dir; + + if ((dir = opendir(dir_path)) == NULL) + { + // Error opening directory + return NULL; + } + + while ((entry = readdir(dir)) != NULL) + { + snprintf(path, PATH_MAX, "%s/%s", dir_path, entry->d_name); + + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + { + continue; + } + + if (lstat(path, &st) == -1) + { + // Error getting directory status + closedir(dir); + return NULL; + } + + if (S_ISLNK(st.st_mode)) + { + // Skip symbolic links + continue; + } + + if (S_ISDIR(st.st_mode)) + { + // 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); + closedir(dir); + return result; + } + else + { + // Recursively search within directories + char* result = fs_search_directory(path, dirname); + if (result != NULL) + { + closedir(dir); + return result; + } + } + } + } + + closedir(dir); + return NULL; +} + +// Function to return creation time of file +double fs_get_creation_time(const char *path) +{ + struct statx stx; + + int fd = open(path, O_RDONLY); + if (fd == -1) + { + // Error + return -1; + } + + int ret = statx(fd, "", AT_EMPTY_PATH, STATX_ALL, &stx); + if (ret == -1) + { + // Error + close(fd); + return -1; + } + + // Close the file + close(fd); + + return stx.stx_btime.tv_sec + stx.stx_btime.tv_nsec / 1e9; +} diff --git a/src/fs.h b/src/fs.h new file mode 100644 index 0000000..fa78f97 --- /dev/null +++ b/src/fs.h @@ -0,0 +1,31 @@ +/* + * 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 + +#include +#include + +bool fs_dir_exists(const char *path); +bool fs_file_exists(const char *format, ...); +char* fs_search_directory(const char *dir_path, const char *dirname); +ssize_t fs_read_file_stripped(char *buf, size_t bufsiz, const char *format, ...); +double fs_get_creation_time(const char *path); diff --git a/src/log.c b/src/log.c index 4f09331..0a2df34 100644 --- a/src/log.c +++ b/src/log.c @@ -33,7 +33,7 @@ #include "options.h" #include "print.h" #include "error.h" -#include "misc.h" +#include "fs.h" #define IS_ESC_CSI_INTERMEDIATE_CHAR(c) ((c >= 0x20) && (c <= 0x3F)) #define IS_ESC_END_CHAR(c) ((c >= 0x30) && (c <= 0x7E)) @@ -64,8 +64,20 @@ int log_open(const char *filename) if (filename == NULL) { - // Generate filename if none provided ("tio_DEVICE_YYYY-MM-DDTHH:MM:SS.log") - asprintf(&automatic_filename, "tio_%s_%s.log", basename((char *)option.tty_device), date_time()); + // Generate filename if none provided + if (option.auto_connect == AUTO_CONNECT_DIRECT) + { + // File name format ("tio_TARGET_YYYY-MM-DDTHH:MM:SS.log") + asprintf(&automatic_filename, "tio_%s_%s.log", basename((char *)option.target), date_time()); + } + else + { + // If using 'new' or 'latest' autoconnect strategy we simply use strategy + // name to name autogenerated log name as device names may vary + asprintf(&automatic_filename, "tio_%s_%s.log", + auto_connect_state_to_string(option.auto_connect), + date_time()); + } if (option.log_directory != NULL) { diff --git a/src/main.c b/src/main.c index eb9aa60..79b9a90 100644 --- a/src/main.c +++ b/src/main.c @@ -121,8 +121,9 @@ int main(int argc, char *argv[]) tty_input_thread_wait_ready(); /* Connect to tty device */ - if (option.no_autoconnect) + if (option.no_reconnect) { + tty_search(); status = tty_connect(); } else @@ -131,7 +132,7 @@ int main(int argc, char *argv[]) while (true) { tty_wait_for_device(); - status = tty_connect(); + tty_connect(); } } diff --git a/src/meson.build b/src/meson.build index dddddd5..297777e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -19,7 +19,8 @@ tio_sources = [ 'timestamp.c', 'alert.c', 'xymodem.c', - 'script.c' + 'script.c', + 'fs.c' ] @@ -35,6 +36,7 @@ endif tio_dep = [ dependency('threads', required: true), + dependency('glib-2.0', required: true), dependency('inih', required: true, fallback : ['libinih', 'inih_dep'], default_options: ['default_library=static', 'distro_install=false']), diff --git a/src/misc.c b/src/misc.c index cf57724..61e19a8 100644 --- a/src/misc.c +++ b/src/misc.c @@ -20,7 +20,10 @@ */ #include "config.h" +#include +#include #include +#include #include #include #include @@ -29,6 +32,7 @@ #include #include #include +#include #include "error.h" #include "print.h" #include "options.h" @@ -74,22 +78,6 @@ int ctrl_key_code(unsigned char key) return -1; } -bool fs_dir_exists(const char *path) -{ - struct stat st; - - if (stat(path, &st) != 0) - { - return false; - } - else if (!S_ISDIR(st.st_mode)) - { - return false; - } - - return true; -} - bool regex_match(const char *string, const char *pattern) { regex_t regex; @@ -141,3 +129,116 @@ int read_poll(int fd, void *data, size_t len, int timeout) /* Timeout */ return ret; } + +// Function to calculate djb2 hash of string +unsigned long djb2_hash(const unsigned char *str) +{ + unsigned long hash = 5381; + int c; + + while ((c = *str++)) + { + hash = ((hash << 5) + hash) + c; // hash * 33 + c + } + + return hash; +} + +// Function to encode a number to base62 +char *base62_encode(unsigned long num) +{ + const char base62_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + char *output = (char *) malloc(5); // 4 characters + null terminator + if (output == NULL) + { + tio_error_print("Memory allocation failed"); + exit(EXIT_FAILURE); + } + + for (int i = 0; i < 4; ++i) + { + output[i] = base62_chars[num % 62]; + num /= 62; + } + output[4] = '\0'; + + return output; +} + +// Function to return current time +double get_current_time(void) +{ + struct timespec current_time_ts; + + if (clock_gettime(CLOCK_REALTIME, ¤t_time_ts) == -1) + { + // Error + return -1; + } + + return current_time_ts.tv_sec + current_time_ts.tv_nsec / 1e9; +} + +// Function to match string with comma separated patterns which supports '*' and '?' +static bool is_match(const char *str, const char *pattern) +{ + // If both string and pattern reach end, they match + if (*str == '\0' && *pattern == '\0') + { + return true; + } + + // If pattern reaches end but string has characters left, no match + if (*pattern == '\0') + { + return false; + } + + // If current characters match or pattern has '?', move to the next character in both + if (*str == *pattern || *pattern == '?') + { + return is_match(str + 1, pattern + 1); + } + + // If current pattern character is '*', check for matches by moving string or pattern + if (*pattern == '*') + { + // '*' matches zero or more characters, so try all possibilities + // Move pattern to the next character and check if remaining pattern matches remaining string + // Move string to the next character and check if current pattern matches remaining string + return is_match(str, pattern + 1) || is_match(str + 1, pattern); + } + + // No match + return false; +} + +bool match_any_pattern(const char *str, const char *patterns) +{ + if ((str == NULL) || (patterns == NULL)) + { + return false; + } + + char *patterns_copy = strdup(patterns); + if (patterns_copy == NULL) + { + tio_error_print("Memory allocation failed"); + exit(EXIT_FAILURE); + } + + char *token = strtok(patterns_copy, ","); + while (token != NULL) + { + if (is_match(str, token)) + { + free(patterns_copy); + return true; + } + token = strtok(NULL, ","); + } + + free(patterns_copy); + + return false; +} diff --git a/src/misc.h b/src/misc.h index c669cbd..5eb049f 100644 --- a/src/misc.h +++ b/src/misc.h @@ -26,12 +26,14 @@ #define UNUSED(expr) do { (void)(expr); } while (0) -char * current_time(void); void delay(long ms); long string_to_long(char *string); int ctrl_key_code(unsigned char key); void alert_connect(void); void alert_disconnect(void); -bool fs_dir_exists(const char *path); bool regex_match(const char *string, const char *pattern); +unsigned long djb2_hash(const unsigned char *str); +char *base62_encode(unsigned long num); int read_poll(int fd, void *data, size_t len, int timeout); +double get_current_time(void); +bool match_any_pattern(const char *str, const char *patterns); diff --git a/src/options.c b/src/options.c index 79556aa..e95d129 100644 --- a/src/options.c +++ b/src/options.c @@ -61,12 +61,15 @@ enum opt_t OPT_SCRIPT_RUN, OPT_INPUT_MODE, OPT_OUTPUT_MODE, + OPT_EXCLUDE_DEVICES, + OPT_EXCLUDE_DRIVERS, + OPT_EXCLUDE_TIDS, }; /* Default options */ struct option_t option = { - .tty_device = "", + .target = "", .baudrate = 115200, .databits = 8, .flow = "none", @@ -80,7 +83,8 @@ struct option_t option = .dsr_pulse_duration = 100, .dcd_pulse_duration = 100, .ri_pulse_duration = 100, - .no_autoconnect = false, + .no_reconnect = false, + .auto_connect = AUTO_CONNECT_DIRECT, .log = false, .log_append = false, .log_filename = NULL, @@ -107,15 +111,18 @@ struct option_t option = .script_filename = NULL, .script_run = SCRIPT_RUN_ALWAYS, .timestamp_timeout = 200, + .exclude_devices = NULL, + .exclude_drivers = NULL, + .exclude_tids = NULL, }; void print_help(char *argv[]) { UNUSED(argv); - printf("Usage: tio [] \n"); + printf("Usage: tio [] \n"); printf("\n"); - printf("Connect to TTY device directly or via sub-configuration.\n"); + printf("Connect to TTY device directly or via sub-configuration or topology ID.\n"); printf("\n"); printf("Options:\n"); printf(" -b, --baudrate Baud rate (default: 115200)\n"); @@ -126,15 +133,19 @@ void print_help(char *argv[]) printf(" -o, --output-delay Output character delay (default: 0)\n"); printf(" -O, --output-line-delay Output line delay (default: 0)\n"); printf(" --line-pulse-duration Set line pulse duration\n"); - printf(" -n, --no-autoconnect Disable automatic connect\n"); + printf(" -a, --auto-connect new|latest|direct Automatic connect strategy (default: direct)\n"); + printf(" --exclude-devices Exclude devices by pattern\n"); + printf(" --exclude-drivers Exclude drivers by pattern\n"); + printf(" --exclude-tids Exclude topology IDs by pattern\n"); + printf(" -n, --no-reconnect Do not reconnect\n"); printf(" -e, --local-echo Enable local echo\n"); printf(" --input-mode normal|hex|line Select input mode (default: normal)\n"); printf(" --output-mode normal|hex Select output mode (default: normal)\n"); printf(" -t, --timestamp Enable line timestamp\n"); printf(" --timestamp-format Set timestamp format (default: 24hour)\n"); printf(" --timestamp-timeout Set timestamp timeout (default: 200)\n"); - printf(" -L, --list-devices List available serial devices by ID\n"); - printf(" -l, --log Enable log to file\n"); + printf(" -l, --list List available serial devices\n"); + printf(" -L, --log Enable log to file\n"); printf(" --log-file Set log filename\n"); printf(" --log-directory Set log directory path for automatic named logs\n"); printf(" --log-append Append to log file\n"); @@ -157,6 +168,44 @@ void print_help(char *argv[]) printf("See the man page for more details.\n"); } +const char *auto_connect_state_to_string(auto_connect_t strategy) +{ + switch (strategy) + { + case AUTO_CONNECT_DIRECT: + return "direct"; + case AUTO_CONNECT_NEW: + return "new"; + case AUTO_CONNECT_LATEST: + return "latest"; + default: + return "Unknown"; + } +} + +auto_connect_t auto_connect_option_parse(const char *arg) +{ + auto_connect_t auto_connect = option.auto_connect; // Default + + if (arg != NULL) + { + if (strcmp(arg, "direct") == 0) + { + return AUTO_CONNECT_DIRECT; + } + else if (strcmp(arg, "new") == 0) + { + return AUTO_CONNECT_NEW; + } + else if (strcmp(arg, "latest") == 0) + { + return AUTO_CONNECT_LATEST; + } + } + + return auto_connect; +} + void line_pulse_duration_option_parse(const char *arg) { bool token_found = true; @@ -312,7 +361,7 @@ script_run_t script_run_option_parse(const char *arg) void options_print() { - tio_printf(" Device: %s", option.tty_device); + tio_printf(" Device: %s", device_name); tio_printf(" Baudrate: %u", option.baudrate); tio_printf(" Databits: %d", option.databits); tio_printf(" Flow: %s", option.flow); @@ -323,7 +372,8 @@ void options_print() tio_printf(" Timestamp timeout: %u", option.timestamp_timeout); tio_printf(" Output delay: %d", option.output_delay); tio_printf(" Output line delay: %d", option.output_line_delay); - tio_printf(" Auto connect: %s", option.no_autoconnect ? "disabled" : "enabled"); + tio_printf(" Automatic connect strategy: %s", auto_connect_state_to_string(option.auto_connect)); + tio_printf(" Automatic reconnect: %s", option.no_reconnect ? "disabled" : "enabled"); tio_printf(" Pulse duration: DTR=%d RTS=%d CTS=%d DSR=%d DCD=%d RI=%d", option.dtr_pulse_duration, option.rts_pulse_duration, option.cts_pulse_duration, @@ -385,13 +435,17 @@ void options_parse(int argc, char *argv[]) {"output-delay", required_argument, 0, 'o' }, {"output-line-delay" , required_argument, 0, 'O' }, {"line-pulse-duration", required_argument, 0, OPT_LINE_PULSE_DURATION }, - {"no-autoconnect", no_argument, 0, 'n' }, + {"auto-connect", required_argument, 0, 'a' }, + {"exclude-devices", required_argument, 0, OPT_EXCLUDE_DEVICES }, + {"exclude-drivers", required_argument, 0, OPT_EXCLUDE_DRIVERS }, + {"exclude-tids", required_argument, 0, OPT_EXCLUDE_TIDS }, + {"no-reconnect", no_argument, 0, 'n' }, {"local-echo", no_argument, 0, 'e' }, {"timestamp", no_argument, 0, 't' }, {"timestamp-format", required_argument, 0, OPT_TIMESTAMP_FORMAT }, {"timestamp-timeout", required_argument, 0, OPT_TIMESTAMP_TIMEOUT }, - {"list-devices", no_argument, 0, 'L' }, - {"log", no_argument, 0, 'l' }, + {"list", no_argument, 0, 'l' }, + {"log", no_argument, 0, 'L' }, {"log-file", required_argument, 0, OPT_LOG_FILE }, {"log-directory", required_argument, 0, OPT_LOG_DIRECTORY }, {"log-append", no_argument, 0, OPT_LOG_APPEND }, @@ -418,7 +472,7 @@ void options_parse(int argc, char *argv[]) int option_index = 0; /* Parse argument using getopt_long */ - c = getopt_long(argc, argv, "b:d:f:s:p:o:O:netLlS:m:c:xrvh", long_options, &option_index); + c = getopt_long(argc, argv, "b:d:f:s:p:o:O:a:netLlS:m:c:xrvh", long_options, &option_index); /* Detect the end of the options */ if (c == -1) @@ -468,8 +522,24 @@ void options_parse(int argc, char *argv[]) line_pulse_duration_option_parse(optarg); break; + case 'a': + option.auto_connect = auto_connect_option_parse(optarg); + break; + + case OPT_EXCLUDE_DEVICES: + option.exclude_devices = optarg; + break; + + case OPT_EXCLUDE_DRIVERS: + option.exclude_drivers = optarg; + break; + + case OPT_EXCLUDE_TIDS: + option.exclude_tids = optarg; + break; + case 'n': - option.no_autoconnect = true; + option.no_reconnect = true; break; case 'e': @@ -489,12 +559,12 @@ void options_parse(int argc, char *argv[]) break; case 'L': - list_serial_devices(); - exit(EXIT_SUCCESS); + option.log = true; break; case 'l': - option.log = true; + list_serial_devices(); + exit(EXIT_SUCCESS); break; case OPT_LOG_FILE: @@ -611,24 +681,33 @@ void options_parse(int argc, char *argv[]) } } - /* Assume first non-option is the tty device name */ - if (strcmp(option.tty_device, "")) + /* Assume first non-option is the target (tty device, sub-config, tid) */ + if (strcmp(option.target, "")) + { optind++; + } else if (optind < argc) - option.tty_device = argv[optind++]; + { + option.target = argv[optind++]; + } if (option.complete_sub_configs) { return; } - if (strlen(option.tty_device) == 0) + if (option.auto_connect != AUTO_CONNECT_DIRECT) { - tio_error_printf("Missing tty device or sub-configuration name"); + return; + } + + if (strlen(option.target) == 0) + { + tio_error_printf("Missing tty device, sub-configuration or topology ID"); exit(EXIT_FAILURE); } - /* Print any remaining command line arguments (unknown options) */ + /* Print any remaining command line arguments as unknown */ if (optind < argc) { fprintf(stderr, "Error: Unknown argument "); @@ -643,8 +722,8 @@ void options_parse(int argc, char *argv[]) void options_parse_final(int argc, char *argv[]) { - /* Preserve tty device which may have been set by configuration file */ - const char *tty_device = option.tty_device; + /* Preserve target which may have been set by configuration file */ + const char *target = option.target; /* Do 2nd pass to override settings set by configuration file */ optind = 1; // Reset option index to restart scanning of argv @@ -653,16 +732,16 @@ void options_parse_final(int argc, char *argv[]) #ifdef __CYGWIN__ unsigned char portnum; char *tty_win; - if ( ((strncmp("COM", tty_device, 3) == 0) - || (strncmp("com", tty_device, 3) == 0) ) - && (sscanf(tty_device + 3, "%hhu", &portnum) == 1) + if ( ((strncmp("COM", target, 3) == 0) + || (strncmp("com", target, 3) == 0) ) + && (sscanf(target + 3, "%hhu", &portnum) == 1) && (portnum > 0) ) { asprintf(&tty_win, "/dev/ttyS%hhu", portnum - 1); - tty_device = tty_win; + target = tty_win; } #endif - /* Restore tty device */ - option.tty_device = tty_device; + /* Restore target */ + option.target = target; } diff --git a/src/options.h b/src/options.h index 699c875..2522bee 100644 --- a/src/options.h +++ b/src/options.h @@ -29,6 +29,7 @@ #include "script.h" #include "timestamp.h" #include "alert.h" +#include "tty.h" typedef enum { @@ -48,7 +49,7 @@ typedef enum /* Options */ struct option_t { - const char *tty_device; + const char *target; unsigned int baudrate; int databits; char *flow; @@ -62,7 +63,8 @@ struct option_t unsigned int dsr_pulse_duration; unsigned int dcd_pulse_duration; unsigned int ri_pulse_duration; - bool no_autoconnect; + bool no_reconnect; + auto_connect_t auto_connect; bool log; bool log_append; bool log_strip; @@ -89,6 +91,9 @@ struct option_t const char *script_filename; script_run_t script_run; unsigned int timestamp_timeout; + const char *exclude_devices; + const char *exclude_drivers; + const char *exclude_tids; }; extern struct option_t option; @@ -102,3 +107,5 @@ script_run_t script_run_option_parse(const char *arg); input_mode_t input_mode_option_parse(const char *arg); output_mode_t output_mode_option_parse(const char *arg); +auto_connect_t auto_connect_option_parse(const char *arg); +const char *auto_connect_state_to_string(auto_connect_t strategy); diff --git a/src/script.c b/src/script.c index 8dd91dd..f998d40 100644 --- a/src/script.c +++ b/src/script.c @@ -36,6 +36,7 @@ #include "xymodem.h" #include "log.h" #include "script.h" +#include "fs.h" #define MAX_BUFFER_SIZE 2000 // Maximum size of circular buffer @@ -44,6 +45,9 @@ static char circular_buffer[MAX_BUFFER_SIZE]; static char match_string[MAX_BUFFER_SIZE]; static int buffer_size = 0; +static char init_script[] = +"\n"; + // lua: sleep(seconds) static int sleep_(lua_State *L) { @@ -401,6 +405,60 @@ static int exit_(lua_State *L) return 0; } +// lua: list = tty_search() +static int tty_search_(lua_State *L) +{ + UNUSED(L); + GList *iter; + int i = 1; + + GList *device_list = tty_search_for_serial_devices(); + + if (device_list == NULL) + { + return 0; + } + + // Create a new table + lua_newtable(L); + + // Iterate through found devices + for (iter = device_list; iter != NULL; iter = g_list_next(iter)) + { + device_t *device = (device_t *) iter->data; + + // Create a new sub-table for each serial device + lua_newtable(L); + + // Add elements to the table + lua_pushstring(L, "path"); + lua_pushstring(L, device->path); + lua_settable(L, -3); + + lua_pushstring(L, "tid"); + lua_pushstring(L, device->tid); + lua_settable(L, -3); + + lua_pushstring(L, "uptime"); + lua_pushnumber(L, device->uptime); + lua_settable(L, -3); + + lua_pushstring(L, "driver"); + lua_pushstring(L, device->driver); + lua_settable(L, -3); + + lua_pushstring(L, "description"); + lua_pushstring(L, device->description); + lua_settable(L, -3); + + // Set the sub-table as a row in the main table + lua_rawseti(L, -2, i++); + } + + // Return table + return 1; +} + static void script_buffer_run(lua_State *L, const char *script_buffer) { int error; @@ -410,7 +468,7 @@ static void script_buffer_run(lua_State *L, const char *script_buffer) if (error) { tio_warning_printf("lua: %s\n", lua_tostring(L, -1)); - lua_pop(L, 1); /* pop error message from the stack */ + lua_pop(L, 1); /* Pop error message from the stack */ } } @@ -429,6 +487,7 @@ static const struct luaL_Reg tio_lib[] = { "read", read_string}, { "expect", expect}, { "exit", exit_}, + { "tty_search", tty_search_}, {NULL, NULL} }; @@ -451,6 +510,18 @@ static void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) } #endif +static void load_init_script(lua_State *L) +{ + int error; + + error = luaL_loadbuffer(L, init_script, strlen(init_script), "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 @@ -458,6 +529,8 @@ int lua_register_tio(lua_State *L) luaL_setfuncs(L, tio_lib, 0); lua_pop(L, 1); + load_init_script(L); + return 0; } diff --git a/src/tty.c b/src/tty.c index 3eb7ae6..eed3f81 100644 --- a/src/tty.c +++ b/src/tty.c @@ -19,7 +19,11 @@ * 02110-1301, USA. */ +#if defined(__linux__) +#include +#endif #include "config.h" +#include #include #include #include @@ -42,6 +46,7 @@ #include #include #include +#include #include "config.h" #include "configfile.h" #include "tty.h" @@ -58,11 +63,13 @@ #include "misc.h" #include "script.h" #include "xymodem.h" +#include "fs.h" /* tty device listing configuration */ #if defined(__linux__) #define PATH_SERIAL_DEVICES "/dev/serial/by-id/" +#define PATH_SERIAL_DEVICES_BY_PATH "/dev/serial/by-path/" #define PREFIX_TTY_DEVICES "" #elif defined(__FreeBSD__) #define PATH_SERIAL_DEVICES "/dev/" @@ -158,6 +165,8 @@ bool map_ign_cr = false; char key_hit = 0xff; +const char* device_name; +GList *device_list = NULL; 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; @@ -1336,6 +1345,539 @@ void tty_configure(void) free(buffer); } +static bool is_serial_device(const char *format, ...) +{ + char filename[PATH_MAX]; + struct winsize ws; + int bytes_printed; + int status = true; + struct stat st; + va_list args; + int fd = -1; + + va_start(args, format); + bytes_printed = vsnprintf(filename, sizeof(filename), format, args); + va_end(args); + + if (bytes_printed < 0) + { + return false; + } + + if (stat(filename, &st) != 0) + { + return false; + } + + // Make sure it is a character device + if ((st.st_mode & S_IFMT) != S_IFCHR) + { + return false; + } + + fd = open(filename, O_RDONLY | O_NONBLOCK | O_NOCTTY); + if (fd == -1) + { + return false; + } + + // Make sure it is a tty + status = isatty(fd); + if (status == 0) + { + goto error; + } + + // Serial devices do not have rows and columns + status = ioctl(fd, TIOCGWINSZ, &ws); + if (status == 0) + { + status = true; + if (ws.ws_row && ws.ws_col) + { + status = false; + goto error; + } + } + +error: + close(fd); + return status; +} + +static void list_serial_devices_by_id(void) +{ + DIR *d = opendir(PATH_SERIAL_DEVICES); + if (d) + { + struct dirent *dir; + + printf("By-id\n"); + printf("--------------------------------------------------------------------------------\n"); + + while ((dir = readdir(d)) != NULL) + { + if ((strcmp(dir->d_name, ".")) && (strcmp(dir->d_name, ".."))) + { + if (!strncmp(dir->d_name, PREFIX_TTY_DEVICES, sizeof(PREFIX_TTY_DEVICES) - 1)) + { + if (is_serial_device("%s%s", PATH_SERIAL_DEVICES, dir->d_name)) + { + printf("%s%s\n", PATH_SERIAL_DEVICES, dir->d_name); + } + } + } + } + closedir(d); + } +} + +static void list_serial_devices_by_path(void) +{ +#ifdef PATH_SERIAL_DEVICES_BY_PATH + + DIR *d = opendir(PATH_SERIAL_DEVICES_BY_PATH); + if (d) + { + struct dirent *dir; + + printf("\nBy-path\n"); + printf("--------------------------------------------------------------------------------\n"); + + while ((dir = readdir(d)) != NULL) + { + if ((strcmp(dir->d_name, ".")) && (strcmp(dir->d_name, ".."))) + { + if (!strncmp(dir->d_name, "", sizeof("") - 1)) + { + if (is_serial_device("%s%s", PATH_SERIAL_DEVICES_BY_PATH, dir->d_name)) + { + printf("%s%s\n", PATH_SERIAL_DEVICES_BY_PATH, dir->d_name); + } + } + } + } + closedir(d); + } +#endif +} + +static gint compare_uptime(gconstpointer a, gconstpointer b) +{ + device_t *device_a = (device_t *) a; + device_t *device_b = (device_t *) b; + + // Make sure we end up with device with smallest uptime last in list + if (device_a->uptime > device_b->uptime) + return -1; + else if (device_a->uptime < device_b->uptime) + return 1; + else + return 0; +} + +#if defined(__linux__) + +// Function to get serial port type as a string +const char* get_serial_port_type(const char* port_name) +{ + int fd; + static struct serial_struct serial_info; + + // Open the serial port + fd = open(port_name, O_RDWR); + if (fd == -1) + { + return ""; + } + + // Get serial port information + if (ioctl(fd, TIOCGSERIAL, &serial_info) == -1) + { + close(fd); + return ""; + } + + // Close the serial port + close(fd); + + // Return the serial port type as a string + switch (serial_info.type) + { + case PORT_UNKNOWN: + return "Unknown"; + + case PORT_8250: + return "8250 UART"; + + case PORT_16450: + return "16450 UART"; + + case PORT_16550: + return "16550 UART"; + + case PORT_16550A: + return "16550A UART"; + + case PORT_16650: + return "16650 UART"; + + case PORT_16650V2: + return "16650V2 UART"; + + case PORT_16750: + return "16750 UART"; + + case PORT_STARTECH: + return "Startech UART"; + + case PORT_16850: + return "16850 UART"; + + case PORT_16C950: + return "16C950 UART"; + + case PORT_16654: + return "16654 UART"; + + case PORT_RSA: + return "RSA UART"; + + default: + return ""; + } +} + +#else + +const char* get_serial_port_type(const char* port_name) +{ + return ""; +} + +#endif + +static void search_reset(void) +{ + GList *iter; + + if (g_list_length(device_list) == 0) + { + return; + } + + // Free data of all list elements + for (iter = device_list; iter != NULL; iter = g_list_next(iter)) + { + device_t *device = (device_t *) iter->data; + g_free(device->tid); + g_free(device->path); + g_free(device->driver); + g_free(device->description); + } + + // Free all list elements + g_list_free_full(device_list, g_free); + + // Indicate an empty list + device_list = NULL; +} + +GList *tty_search_for_serial_devices(void) +{ + DIR *dir; + char path[PATH_MAX] = {}; + char device_path[PATH_MAX] = {}; + char driver_path[PATH_MAX] = {}; + double current_time, creation_time; + ssize_t length; + + search_reset(); + + // Open the sysfs directory for the tty subsystem + dir = opendir("/sys/class/tty"); + if (!dir) + { + // Error + return NULL; + } + + current_time = get_current_time(); + + // Iterate through each device in the subsystem directory + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) + { + // Skip . and .. entries + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + { + continue; + } + + // Skip non serial devices + if (is_serial_device("/dev/%s", entry->d_name) == false) + { + continue; + } + + // Construct the path to the device's device symlink + snprintf(path, sizeof(path), "/sys/class/tty/%s/device", entry->d_name); + + // Read the device symlink to get the device path + // Example symlinks: + // /sys/class/tty/ttyUSB0/device -> ../../../ttyUSB0 + // /sys/class/tty/ttyACM0/device -> ../../../3-6.4:1.2 + length = readlink(path, device_path, sizeof(device_path) - 1); + if (length == -1) + { + continue; + } + + // Null-terminate the string + device_path[length] = '\0'; + + // Extract last part of device path (string after last '/') + // Example resulting device_name: + // "ttyUSB0" + // "3-6.4:1.2" + char *device_name = strrchr(device_path, '/'); + device_name++; // Move past the '/' + + // Find that part in /sys/devices and return first result string + // Example devices_path: + // "/sys/devices/pci0000:00/0000:00:14.0/usb3/3-6/3-6.3/3-6.3:1.0/ttyUSB0" + // "/sys/devices/pci0000:00/0000:00:14.0/usb3/3-6/3-6.4/3-6.4:1.2" + char *devices_path = fs_search_directory("/sys/devices", device_name); + if (devices_path == NULL) + { + continue; + } + + // Remove last part if it contains device short name (e.g ttyUSB0) + // Example resulting devices_path: + // "/sys/devices/pci0000:00/0000:00:14.0/usb3/3-6/3-6.3/3-6.3:1.0" + // "/sys/devices/pci0000:00/0000:00:14.0/usb3/3-6/3-6.4/3-6.4:1.2" + char *last_part = strrchr(devices_path, '/'); + last_part++; + if (strcmp(last_part, entry->d_name) == 0) + { + // Remove last part (string after last '/') + char *slash = strrchr(devices_path, '/'); + int index = (int) (slash - devices_path); + devices_path[index] = '\0'; + } + + // Hash remaining string to get unique topology ID + unsigned long hash = djb2_hash((const unsigned char *)devices_path); + char *tid = base62_encode(hash); + free(devices_path); + + // Construct the path to the device's driver symlink + snprintf(path, sizeof(path), "/sys/class/tty/%s/device/driver", entry->d_name); + + // Read the symlink to get the driver's path + length = readlink(path, driver_path, sizeof(driver_path) - 1); + if (length == -1) + { + continue; + } + + // Null-terminate the string + driver_path[length] = '\0'; + + // Extract the driver name from the path + char *driver = strrchr(driver_path, '/'); + if (driver == NULL) + { + continue; + } + driver++; // Move past the last '/' + + // Construct the path to the TTY device file + snprintf(path, sizeof(path), "/dev/%s", entry->d_name); + + // Calculate uptime + creation_time = fs_get_creation_time(path); + double uptime = current_time - creation_time; + + // Read sysfs files to get best possible description of the driver + 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); + } + if (length == -1) + { + length = fs_read_file_stripped(description, sizeof(description), "/sys/class/tty/%s/device/../../product", entry->d_name); + } + if (length == -1) + { + snprintf(description, sizeof(description), "%s", get_serial_port_type(path)); + } + + // Do not add devices excluded by exclude patterns + if (match_any_pattern(path, option.exclude_devices)) + { + continue; + } + if (match_any_pattern(driver, option.exclude_drivers)) + { + continue; + } + if (match_any_pattern(tid, option.exclude_tids)) + { + continue; + } + + // Allocate new device item for device list + device_t *device = g_new0(device_t, 1); + if (device == NULL) + { + continue; + } + + // Fill in device information + device->path = g_strdup(path); + device->tid = g_strdup(tid); + device->uptime = uptime; + device->driver = g_strdup(driver); + device->description = g_strdup(description); + + // Add device information to device list + device_list = g_list_append(device_list, device); + } + + if (g_list_length(device_list) == 0) + { + // Return NULL if no serial devices found + return NULL; + } + + // Sort device list device with respect to uptime + device_list = g_list_sort(device_list, compare_uptime); + + closedir(dir); + + return device_list; +} + +void list_serial_devices(void) +{ + tty_search_for_serial_devices(); + + if (g_list_length(device_list) > 0) + { + printf("Device TID Uptime [s] Driver Description\n"); + printf("----------------- ---- ------------- ---------------- --------------------------\n"); + + // Iterate through the device list + GList *iter; + for (iter = device_list; iter != NULL; iter = g_list_next(iter)) + { + 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); + } + printf("\n"); + } + + list_serial_devices_by_id(); + list_serial_devices_by_path(); +} + +void tty_search(void) +{ + GList *iter; + device_t *device = NULL; + double uptime_minimum = 0; + bool no_new = true; + + switch (option.auto_connect) + { + case AUTO_CONNECT_NEW: + tty_search_for_serial_devices(); + + // Save smallest uptime + if (g_list_length(device_list) > 0) + { + // Get latest registered device (smallest uptime) + GList *last = g_list_last(device_list); + device = last->data; + uptime_minimum = device->uptime; + } + + tio_printf("Waiting for tty device.."); + + while (no_new) + { + tty_search_for_serial_devices(); + + // Iterate through the device list generated by search + for (iter = device_list; iter != NULL; iter = g_list_next(iter)) + { + device = (device_t *) iter->data; + + // Find first new device + if (device->uptime < uptime_minimum) + { + // Match found -> update device + device_name = device->path; + no_new = false; + break; + } + } + if (no_new) + { + usleep(500*1000); // Sleep 0.5 s + } + } + return; + + case AUTO_CONNECT_LATEST: + tty_search_for_serial_devices(); + if (g_list_length(device_list) > 0) + { + // Get latest registered device (smallest uptime) + GList *last = g_list_last(device_list); + device = last->data; + device_name = device->path; + } + return; + + case AUTO_CONNECT_DIRECT: + if (strlen(option.target) == TOPOLOGY_ID_SIZE) + { + // Potential topology ID detected -> trigger device search + tty_search_for_serial_devices(); + + // Iterate through the device list generated by search + for (iter = device_list; iter != NULL; iter = g_list_next(iter)) + { + device = (device_t *) iter->data; + + if (strcmp(device->tid, option.target) == 0) + { + // Topology ID match found -> use corresponding device name + device_name = device->path; + + return; + } + } + } + + // Fallback to using tty device provided via cmdline target + device_name = option.target; + break; + + default: + // Should never be reached + tio_printf("Unknown connection strategy"); + exit(EXIT_FAILURE); + } +} + void tty_wait_for_device(void) { fd_set rdfs; @@ -1349,6 +1891,8 @@ void tty_wait_for_device(void) /* Loop until device pops up */ while (true) { + tty_search(); + if (interactive_mode) { /* In interactive mode, while waiting for tty device, we need to @@ -1400,7 +1944,7 @@ void tty_wait_for_device(void) } /* Test for accessible device file */ - status = access(option.tty_device, R_OK); + status = access(device_name, R_OK); if (status == 0) { last_errno = 0; @@ -1549,7 +2093,7 @@ int tty_connect(void) struct timeval tval_before = {}, tval_now, tval_result; /* Open tty device */ - device_fd = open(option.tty_device, O_RDWR | O_NOCTTY | O_NONBLOCK); + device_fd = open(device_name, O_RDWR | O_NOCTTY | O_NONBLOCK); if (device_fd < 0) { tio_error_printf_silent("Could not open tty device (%s)", strerror(errno)); @@ -1575,7 +2119,7 @@ int tty_connect(void) tcflush(device_fd, TCIOFLUSH); /* Print connect status */ - tio_printf("Connected"); + tio_printf("Connected to %s", device_name); connected = true; print_tainted = false; @@ -2019,22 +2563,3 @@ error_open: return TIO_ERROR; } -void list_serial_devices(void) -{ - DIR *d = opendir(PATH_SERIAL_DEVICES); - if (d) - { - struct dirent *dir; - while ((dir = readdir(d)) != NULL) - { - if ((strcmp(dir->d_name, ".")) && (strcmp(dir->d_name, ".."))) - { - if (!strncmp(dir->d_name, PREFIX_TTY_DEVICES, sizeof(PREFIX_TTY_DEVICES) - 1)) - { - printf("%s%s\n", PATH_SERIAL_DEVICES, dir->d_name); - } - } - } - closedir(d); - } -} diff --git a/src/tty.h b/src/tty.h index 633e51c..140f8dc 100644 --- a/src/tty.h +++ b/src/tty.h @@ -22,10 +22,31 @@ #pragma once #include +#include #define LINE_HIGH true #define LINE_LOW false +#define TOPOLOGY_ID_SIZE 4 + +typedef enum +{ + AUTO_CONNECT_DIRECT, + AUTO_CONNECT_NEW, + AUTO_CONNECT_LATEST, + AUTO_CONNECT_END, +} auto_connect_t; + +typedef struct +{ + char *tid; + double uptime; + char *path; + char *driver; + char *description; +} device_t; + +extern const char *device_name; extern bool interactive_mode; extern bool map_i_nl_cr; extern bool map_i_cr_nl; @@ -43,3 +64,5 @@ void tty_line_set(int fd, int mask, bool value); void tty_line_toggle(int fd, int mask); void tty_line_config(int mask, bool value); void tty_line_config_apply(void); +void tty_search(void); +GList *tty_search_for_serial_devices(void); From b4741de50ceedda6ba6cf072dba7d16eacc07e8a Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 26 Apr 2024 22:28:27 +0200 Subject: [PATCH 278/506] Bump version --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 792711e..79c8a67 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('tio', 'c', - version : '2.9', + version : '3.0', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] From 5bbdf3b9f84e5f01f69afa8fb3154cae7dc2274e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 26 Apr 2024 22:33:01 +0200 Subject: [PATCH 279/506] Update README --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 13179d2..d627f2c 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ I/O operations. ### 1.1 Motivation -To make a simpler serial device tool for talking with serial TTY devices with +To make a simpler serial device tool for working with serial TTY devices with less focus on classic terminal/modem features and more focus on the needs of embedded developers and hackers. @@ -77,7 +77,7 @@ when used in combination with [tmux](https://tmux.github.io). * Pipe input and/or output * Bash completion on options, serial device names, and sub-configuration names * Configurable tio message text color - * Supports NO_COLOR env variable as per no-color.org + * Supports NO_COLOR env variable as per [no-color.org](https://no-color.org) * Visual or audible alert on connect/disconnect * Remapping of prefix key * Lua scripting support for automation @@ -276,7 +276,8 @@ Additonally tio offers two convenient ways of connecting to serial devices: * Connect automatically to latest registered serial device * ```tio --auto-connect latest``` -It is also possible to use excludes to affect strategy decisions: +It is also possible to use excludes to affect which serial devices are involved +in the strategy decisions: * Exclude devices by pattern * Example: ```tio --auto-connect new --exclude-devices "/dev/ttyACM?,/dev/ttyUSB2"``` @@ -341,6 +342,7 @@ In addition to the Lua API tio makes the following functions available: Send file using x/y-modem protocol. Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM. + tty_search() Search for serial devices. From 01f3c391f09905d74a936ceac1ea4507d6572ccd Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 26 Apr 2024 22:54:25 +0200 Subject: [PATCH 280/506] Update README --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index d627f2c..4584f64 100644 --- a/README.md +++ b/README.md @@ -175,6 +175,15 @@ $ tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 Using serial devices by ID ensures that tio automatically reconnects to the correct serial device if it is disconnected and then reconnected. +If no serial device by ID is available it is also possible to connect via +topology ID (TID): +``` +$ tio bCC2 +``` +The TID is unique and will stay the same as long as your USB serial port device +plugs into the same USB topology (same ports, same hubs, etc.). This way tio +will successfully reconnect to the same device when reconnected. + List available serial devices: ``` $ tio --list From d8fbd607d483cb6a66031b3619b890520efb1595 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 26 Apr 2024 22:58:24 +0200 Subject: [PATCH 281/506] Update README --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4584f64..2c6cb67 100644 --- a/README.md +++ b/README.md @@ -272,11 +272,11 @@ By-path /dev/serial/by-path/pci-0000:00:14.0-usbv2-0:6.4:1.2 ``` -Note: The topology ID (TID) is a special hash of the full topology path of the -connected device. This means that every time e.g. a USB serial device is -plugged into to the exact same USB hub chain it will get the exact same TID. -This helps solve the problem of reconnecting to serical devices which do not -provide a unique device by ID. +Note: The topology ID (TID) is a unique hash of the full topology path of the +connected device. This means that every time e.g. a USB serial port device is +plugged into to the exact same USB topology (same ports, same hubs, etc.) it +will get the exact same TID. This helps solve the problem of reconnecting to +serical devices which do not provide a unique device by ID. Additonally tio offers two convenient ways of connecting to serial devices: From 6e779a05205074e892bbe62a05bdb72718a70896 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 27 Apr 2024 00:48:35 +0200 Subject: [PATCH 282/506] Use lua io.write() instead of print() io.write() gives better output control as print() is hardcoded to always print a newline. --- examples/lua/automatic-linux-login.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/lua/automatic-linux-login.lua b/examples/lua/automatic-linux-login.lua index b700f78..a287b40 100644 --- a/examples/lua/automatic-linux-login.lua +++ b/examples/lua/automatic-linux-login.lua @@ -22,8 +22,8 @@ if (1 == found) then expect("Password:") send(login.password .. "\n") else - print("\r\nDon't know login info for " .. hostname .. "\r\n") + io.write("\r\nDon't know login info for " .. hostname .. "\r\n") end else - print("\r\nDidn't find a login prompt\r\n") + io.write("\r\nDidn't find a login prompt\r\n") end From c61d56935b2980a5b19c391ec8c2a8747bca30bd Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 27 Apr 2024 01:31:35 +0200 Subject: [PATCH 283/506] Fix excludes pattern matching --- src/misc.c | 60 +++++++++++++----------------------------------------- src/misc.h | 2 +- src/tty.c | 6 +++--- 3 files changed, 18 insertions(+), 50 deletions(-) diff --git a/src/misc.c b/src/misc.c index 61e19a8..1d1acea 100644 --- a/src/misc.c +++ b/src/misc.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "error.h" #include "print.h" #include "options.h" @@ -179,66 +180,33 @@ double get_current_time(void) return current_time_ts.tv_sec + current_time_ts.tv_nsec / 1e9; } -// Function to match string with comma separated patterns which supports '*' and '?' -static bool is_match(const char *str, const char *pattern) +bool match_patterns(const char *string, const char *patterns) { - // If both string and pattern reach end, they match - if (*str == '\0' && *pattern == '\0') - { - return true; - } + char *pattern; + char *patterns_copy; - // If pattern reaches end but string has characters left, no match - if (*pattern == '\0') + if ((string == NULL) || (patterns == NULL)) { return false; } - // If current characters match or pattern has '?', move to the next character in both - if (*str == *pattern || *pattern == '?') - { - return is_match(str + 1, pattern + 1); - } + patterns_copy = strdup(patterns); - // If current pattern character is '*', check for matches by moving string or pattern - if (*pattern == '*') + // Tokenize the patterns string using strtok + pattern = strtok(patterns_copy, ","); + while (pattern != NULL) { - // '*' matches zero or more characters, so try all possibilities - // Move pattern to the next character and check if remaining pattern matches remaining string - // Move string to the next character and check if current pattern matches remaining string - return is_match(str, pattern + 1) || is_match(str + 1, pattern); - } - - // No match - return false; -} - -bool match_any_pattern(const char *str, const char *patterns) -{ - if ((str == NULL) || (patterns == NULL)) - { - return false; - } - - char *patterns_copy = strdup(patterns); - if (patterns_copy == NULL) - { - tio_error_print("Memory allocation failed"); - exit(EXIT_FAILURE); - } - - char *token = strtok(patterns_copy, ","); - while (token != NULL) - { - if (is_match(str, token)) + // Check if the string matches the current pattern + if (fnmatch(pattern, string, 0) == 0) { free(patterns_copy); return true; } - token = strtok(NULL, ","); + + // Move to the next pattern + pattern = strtok(NULL, ","); } free(patterns_copy); - return false; } diff --git a/src/misc.h b/src/misc.h index 5eb049f..25be622 100644 --- a/src/misc.h +++ b/src/misc.h @@ -36,4 +36,4 @@ unsigned long djb2_hash(const unsigned char *str); char *base62_encode(unsigned long num); int read_poll(int fd, void *data, size_t len, int timeout); double get_current_time(void); -bool match_any_pattern(const char *str, const char *patterns); +bool match_patterns(const char *string, const char *patterns); diff --git a/src/tty.c b/src/tty.c index eed3f81..cebe7f5 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1717,15 +1717,15 @@ GList *tty_search_for_serial_devices(void) } // Do not add devices excluded by exclude patterns - if (match_any_pattern(path, option.exclude_devices)) + if (match_patterns(path, option.exclude_devices)) { continue; } - if (match_any_pattern(driver, option.exclude_drivers)) + if (match_patterns(driver, option.exclude_drivers)) { continue; } - if (match_any_pattern(tid, option.exclude_tids)) + if (match_patterns(tid, option.exclude_tids)) { continue; } From 41b8e4f99cdfb46a99aae300d576dc43ce52d983 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 27 Apr 2024 02:03:12 +0200 Subject: [PATCH 284/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2c6cb67..ac72a3a 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,7 @@ $ tio bCC2 ``` The TID is unique and will stay the same as long as your USB serial port device plugs into the same USB topology (same ports, same hubs, etc.). This way tio -will successfully reconnect to the same device when reconnected. +will successfully reconnect to the same device each time. List available serial devices: ``` From 1b77ed783bc2ddb97057963950ba967b250be760 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 27 Apr 2024 02:25:41 +0200 Subject: [PATCH 285/506] Update README --- README.md | 60 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index ac72a3a..9725b86 100644 --- a/README.md +++ b/README.md @@ -227,14 +227,25 @@ KORAD KD3305P V4.2 SN:32475045 Using tio there are up to 4 recommended ways to connect to a specific serial device: - * Connect by ID - * Example: ```tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTCHUV56-if00-port0``` - * Connect by topology ID - * Example: ```tio bCC2``` - * Connect to enumerated device in /dev - * Example: ```tio /dev/ttyUSB4``` - * Connect by path - * Example: ```tio /dev/serial/by-path/pci-0000:00:14.0-usb-0:8.1.3.1.4:1.0-port0``` +Connect by ID (preferred method): +``` +$ tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTCHUV56-if00-port0 +``` + +Connect by topology ID: +``` +$ tio bCC2 +``` + +Connect to enumerated device in /dev: +``` +$ tio /dev/ttyUSB4 +``` + +Connect by path: +``` +$ tio /dev/serial/by-path/pci-0000:00:14.0-usb-0:8.1.3.1.4:1.0-port0 +``` Which serial device to connect becomes more clear from tio's serial device listing which provides more information about each serial device. For example: @@ -280,20 +291,33 @@ serical devices which do not provide a unique device by ID. Additonally tio offers two convenient ways of connecting to serial devices: - * Connect automatically to first new appearing serial device - * ```tio --auto-connect new``` - * Connect automatically to latest registered serial device - * ```tio --auto-connect latest``` +(1) Connect automatically to first new appearing serial device: +``` +$ tio --auto-connect new +``` + +(2) Connect automatically to latest registered serial device: +``` +$ tio --auto-connect latest +``` It is also possible to use excludes to affect which serial devices are involved in the strategy decisions: - * Exclude devices by pattern - * Example: ```tio --auto-connect new --exclude-devices "/dev/ttyACM?,/dev/ttyUSB2"``` - * Exclude drivers by pattern - * Example: ```tio --auto-connect new --exclude-drivers "cdc_acm,ftdi_sio"``` - * Exclude topology IDs by pattern - * Example: ```tio --auto-connect new --exclude-tids "EOEs"``` +Exclude devices by pattern: +``` +$ tio --auto-connect new --exclude-devices "/dev/ttyACM?,/dev/ttyUSB2" +``` + +Exclude drivers by pattern: +``` +$ tio --auto-connect new --exclude-drivers "cdc_acm,ftdi_sio" +``` + +Exclude topology IDs by pattern: +``` +$ tio --auto-connect new --exclude-tids "EOEs" +``` Note: Pattern matching supports '*' and '?'. Use comma separation to define multiple patterns. From 232cbee6974e2fb83168074c86fdac0f1f12c646 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 27 Apr 2024 09:13:29 +0200 Subject: [PATCH 286/506] Rename sub-config to profile Because better naming. --- README.md | 20 ++++++++++---------- examples/config/config | 2 +- man/tio.1.in | 20 +++++++++++--------- src/bash-completion/tio.in | 4 ++-- src/configfile.c | 8 ++++---- src/configfile.h | 2 +- src/main.c | 4 ++-- src/options.c | 22 +++++++++++----------- src/options.h | 2 +- 9 files changed, 43 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 9725b86..a196fd1 100644 --- a/README.md +++ b/README.md @@ -70,12 +70,12 @@ when used in combination with [tmux](https://tmux.github.io). * Overwrite (default) or append to log file * Strip control characters and escape sequences * Configuration file support - * Support for sub-configurations - * Activate sub-configurations by name or pattern + * Support for configuration profiles + * Activate configuration profiles by name or pattern * Redirect I/O to UNIX socket or IPv4/v6 network socket * Useful for scripting or TTY sharing * Pipe input and/or output - * Bash completion on options, serial device names, and sub-configuration names + * Bash completion on options, serial device names, and profile names * Configurable tio message text color * Supports NO_COLOR env variable as per [no-color.org](https://no-color.org) * Visual or audible alert on connect/disconnect @@ -99,9 +99,9 @@ For more usage details please see the man page documentation The command-line interface is straightforward as reflected in the output from 'tio --help': ``` -Usage: tio [] +Usage: tio [] -Connect to TTY device directly or via sub-configuration or topology ID. +Connect to TTY device directly or via configuration profile or topology ID. Options: -b, --baudrate Baud rate (default: 115200) @@ -142,7 +142,7 @@ Options: -v, --version Display version -h, --help Display help -Options and sub-configurations may be set via configuration file. +Options and profiles may be set via configuration file. See the man page for more details. ``` @@ -428,9 +428,9 @@ following locations in the order listed: - $HOME/.config/tio/config - $HOME/.tioconfig -The configuration file supports sub-configurations using named sections which can -be activated via the command-line by name or pattern. A sub-configuration -specifies which TTY device to connect to and other options. +The configuration file supports profiles using named sections which can be +activated via the command-line by name or pattern. A profile specifies which +TTY device to connect to and other options. ### 3.4.1 Examples @@ -470,7 +470,7 @@ device = /dev/ttyUSB%s color = 14 ``` -To use a specific sub-configuration by name simply start tio like so: +To use a specific profile by name simply start tio like so: ``` $ tio rpi3 ``` diff --git a/examples/config/config b/examples/config/config index 200ec79..4e0de75 100644 --- a/examples/config/config +++ b/examples/config/config @@ -32,7 +32,7 @@ rs-485 = disable alert = none script-run = always -# Sub-configurations +# Configuration profiles [rpi3] baudrate = 115200 diff --git a/man/tio.1.in b/man/tio.1.in index 861ccf0..4507d2f 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -6,7 +6,7 @@ tio \- a serial device I/O tool .SH "SYNOPSIS" .PP .B tio -.RI "[" "] " "" +.RI "[" "] " "" .SH "DESCRIPTION" .PP @@ -481,11 +481,11 @@ listed: .I $HOME/.tioconfig .PP -Labels can be used to group settings into named sub-configurations which can be -activated from the command-line when starting tio. +Labels can be used to group settings into named configuration profiles which +can be activated from the command-line when starting tio. .PP -\fBtio\fR will try to match the user input to a sub-configuration by name or by +\fBtio\fR will try to match the user input to a configuration profile by name or by pattern to get the TTY device and other options. .PP @@ -585,7 +585,7 @@ line-pulse-duration = DTR=200,RTS=400 .RE .TP -Named sub-configurations can be added via labels: +Named configuration profiles can be added via labels: .RS .nf @@ -599,7 +599,7 @@ color = 11 .RE .TP -Activate the sub-configuration by name: +Activate the configuration profile by name: $ tio rpi3 @@ -609,7 +609,8 @@ Which is equivalent to: $ tio -b 115200 -c 11 /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 .TP -A sub-configuration 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 @@ -623,7 +624,7 @@ baudrate = 115200 .RE .TP -Activate the sub-configuration by pattern match: +Activate the configuration profile by pattern match: $ tio usb12 @@ -633,7 +634,8 @@ Which is equivalent to: $ tio -b 115200 /dev/ttyUSB12 .TP -It is also possible to combine use of sub-configuration 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 diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index 4eb902a..75a207c 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -197,14 +197,14 @@ _tio() ;; esac - sub_configs="`tio --complete-sub-configs`" + profiles="`tio --complete-profiles`" if [ -d /dev/serial/by-id ]; then ttys=$(printf '%s\n' /dev/tty* /dev/serial/by-id/*) else ttys=$(printf '%s\n' /dev/tty*) fi - COMPREPLY=( $(compgen -W "${ttys} ${sub_configs}" -- ${cur}) ) + COMPREPLY=( $(compgen -W "${ttys} ${profiles}" -- ${cur}) ) return 0 } diff --git a/src/configfile.c b/src/configfile.c index 00e4fb4..0357b53 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -455,7 +455,7 @@ static int resolve_config_file(void) return -EINVAL; } -void config_file_show_sub_configurations(void) +void config_file_show_profiles(void) { memset(&c, 0, sizeof(struct config_t)); @@ -482,7 +482,7 @@ void config_file_parse(void) return; } - // Set user input which may be tty device or sub config or tid + // Set user input which may be tty device or profile or tid c.user = option.target; if (!c.user) @@ -513,7 +513,7 @@ void config_file_parse(void) } } - // Parse settings of found section (sub config) + // Parse settings of found section (profile) ret = ini_parse(c.path, data_handler, NULL); if (ret < 0) { @@ -544,7 +544,7 @@ void config_file_print(void) tio_printf(" Active configuration file: %s", c.path); if (c.section_name != NULL) { - tio_printf(" Active sub-configuration: %s", c.section_name); + tio_printf(" Active configuration profile: %s", c.section_name); } } } diff --git a/src/configfile.h b/src/configfile.h index 7748149..554df3c 100644 --- a/src/configfile.h +++ b/src/configfile.h @@ -25,4 +25,4 @@ void config_file_print(void); void config_file_parse(void); void config_exit(void); -void config_file_show_sub_configurations(void); +void config_file_show_profiles(void); diff --git a/src/main.c b/src/main.c index 79b9a90..269995e 100644 --- a/src/main.c +++ b/src/main.c @@ -45,9 +45,9 @@ int main(int argc, char *argv[]) /* Parse command-line options (1st pass) */ options_parse(argc, argv); - if (option.complete_sub_configs) + if (option.complete_profiles) { - config_file_show_sub_configurations(); + config_file_show_profiles(); return status; } diff --git a/src/options.c b/src/options.c index e95d129..73c5bc8 100644 --- a/src/options.c +++ b/src/options.c @@ -54,7 +54,7 @@ enum opt_t OPT_RS485, OPT_RS485_CONFIG, OPT_ALERT, - OPT_COMPLETE_SUB_CONFIGS, + OPT_COMPLETE_PROFILES, OPT_MUTE, OPT_SCRIPT, OPT_SCRIPT_FILE, @@ -106,7 +106,7 @@ struct option_t option = .rs485_delay_rts_before_send = -1, .rs485_delay_rts_after_send = -1, .alert = ALERT_NONE, - .complete_sub_configs = false, + .complete_profiles = false, .script = NULL, .script_filename = NULL, .script_run = SCRIPT_RUN_ALWAYS, @@ -120,9 +120,9 @@ void print_help(char *argv[]) { UNUSED(argv); - printf("Usage: tio [] \n"); + printf("Usage: tio [] \n"); printf("\n"); - printf("Connect to TTY device directly or via sub-configuration or topology ID.\n"); + printf("Connect to TTY device directly or via configuration profile or topology ID.\n"); printf("\n"); printf("Options:\n"); printf(" -b, --baudrate Baud rate (default: 115200)\n"); @@ -163,7 +163,7 @@ void print_help(char *argv[]) printf(" -v, --version Display version\n"); printf(" -h, --help Display help\n"); printf("\n"); - printf("Options and sub-configurations may be set via configuration file.\n"); + printf("Options and profiles may be set via configuration file.\n"); printf("\n"); printf("See the man page for more details.\n"); } @@ -464,7 +464,7 @@ void options_parse(int argc, char *argv[]) {"script-run", required_argument, 0, OPT_SCRIPT_RUN }, {"version", no_argument, 0, 'v' }, {"help", no_argument, 0, 'h' }, - {"complete-sub-configs", no_argument, 0, OPT_COMPLETE_SUB_CONFIGS}, + {"complete-profiles", no_argument, 0, OPT_COMPLETE_PROFILES }, {0, 0, 0, 0 } }; @@ -667,8 +667,8 @@ void options_parse(int argc, char *argv[]) exit(EXIT_SUCCESS); break; - case OPT_COMPLETE_SUB_CONFIGS: - option.complete_sub_configs = true; + case OPT_COMPLETE_PROFILES: + option.complete_profiles = true; break; case '?': @@ -681,7 +681,7 @@ void options_parse(int argc, char *argv[]) } } - /* Assume first non-option is the target (tty device, sub-config, tid) */ + /* Assume first non-option is the target (tty device, profile, tid) */ if (strcmp(option.target, "")) { optind++; @@ -691,7 +691,7 @@ void options_parse(int argc, char *argv[]) option.target = argv[optind++]; } - if (option.complete_sub_configs) + if (option.complete_profiles) { return; } @@ -703,7 +703,7 @@ void options_parse(int argc, char *argv[]) if (strlen(option.target) == 0) { - tio_error_printf("Missing tty device, sub-configuration or topology ID"); + tio_error_printf("Missing tty device, profile or topology ID"); exit(EXIT_FAILURE); } diff --git a/src/options.h b/src/options.h index 2522bee..48048cb 100644 --- a/src/options.h +++ b/src/options.h @@ -86,7 +86,7 @@ struct option_t int32_t rs485_delay_rts_before_send; int32_t rs485_delay_rts_after_send; alert_t alert; - bool complete_sub_configs; + bool complete_profiles; const char *script; const char *script_filename; script_run_t script_run; From 4113a072c2cfeed70f94629d23c24164524df888 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 27 Apr 2024 14:41:08 +0200 Subject: [PATCH 287/506] Make sure to reset tainted state --- src/print.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/print.h b/src/print.h index f20d173..dd65393 100644 --- a/src/print.h +++ b/src/print.h @@ -77,6 +77,7 @@ extern char ansi_format[]; fprintf (stdout, "\r[%s] Warning: " format "\r\n", timestamp_current_time(), ## args); \ else \ ansi_printf("[%s] Warning: " format, timestamp_current_time(), ## args); \ + print_tainted = false; \ } \ } @@ -90,6 +91,7 @@ extern char ansi_format[]; fprintf (stdout, "\r[%s] Error: " format "\r\n", timestamp_current_time(), ## args); \ else \ ansi_printf("[%s] Error: " format, timestamp_current_time(), ## args); \ + print_tainted = false; \ } \ } From 42ff23420419e3839fa064a7490c9216d94896a7 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 27 Apr 2024 15:22:41 +0200 Subject: [PATCH 288/506] Add hexN output mode Adds support for hexN mode where N is a number in the range 1 to 4096 which defines how many hex values will be printed before a line break. In short, it defines the width of the hex output. In this mode, if timestamps are enabled they will be added to each hex line. --- README.md | 2 +- man/tio.1.in | 9 +++-- src/options.c | 66 +++++++++++++++++++++++++++++++++-- src/options.h | 1 + src/tty.c | 95 ++++++++++++++++++++++++++++++++++++++++++--------- 5 files changed, 151 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index a196fd1..445897d 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ Options: -n, --no-reconnect Do not reconnect -e, --local-echo Enable local echo --input-mode normal|hex|line Select input mode (default: normal) - --output-mode normal|hex Select output mode (default: normal) + --output-mode normal|hex|hexN Select output mode (default: normal, N <= 4096) -t, --timestamp Enable line timestamp --timestamp-format Set timestamp format (default: 24hour) --timestamp-timeout Set timestamp timeout (default: 200) diff --git a/man/tio.1.in b/man/tio.1.in index 4507d2f..75d05bb 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -248,9 +248,14 @@ editing feature supported in this mode is backspace. Default value is "normal". .TP -.BR " \-\-output\-mode " normal|hex +.BR " \-\-output\-mode " normal|hex|hexN -Set output mode. In hex mode each incoming byte is printed out as a 1 byte hex value. +Set output mode. + +In hex mode each incoming byte is printed out as a 1 byte hex value. + +In hexN mode, N is a number less than or equal to 4096 which defines how many +hex values will be printed before a line break. Default value is "normal". diff --git a/src/options.c b/src/options.c index 73c5bc8..f8a77da 100644 --- a/src/options.c +++ b/src/options.c @@ -20,6 +20,7 @@ */ #include "config.h" +#include #include #include #include @@ -41,6 +42,8 @@ #include "log.h" #include "script.h" +#define HEX_N_VALUE_MAX 4096 + enum opt_t { OPT_NONE, @@ -114,6 +117,7 @@ struct option_t option = .exclude_devices = NULL, .exclude_drivers = NULL, .exclude_tids = NULL, + .hex_n_value = 0, }; void print_help(char *argv[]) @@ -140,7 +144,7 @@ void print_help(char *argv[]) printf(" -n, --no-reconnect Do not reconnect\n"); printf(" -e, --local-echo Enable local echo\n"); printf(" --input-mode normal|hex|line Select input mode (default: normal)\n"); - printf(" --output-mode normal|hex Select output mode (default: normal)\n"); + printf(" --output-mode normal|hex|hexN Select output mode (default: normal, N <= %d)\n", HEX_N_VALUE_MAX); printf(" -t, --timestamp Enable line timestamp\n"); printf(" --timestamp-format Set timestamp format (default: 24hour)\n"); printf(" --timestamp-timeout Set timestamp timeout (default: 200)\n"); @@ -268,6 +272,61 @@ void line_pulse_duration_option_parse(const char *arg) free(buffer); } +// Function to parse the input string +int parse_hexN_string(const char *input_string) +{ + regmatch_t match[2]; // One for entire match, one for the optional N + int n_value = 0; + regex_t regex; + int ret; + + // Compile the regular expression to match "hex" and optionally capture N + ret = regcomp(®ex, "^hex([0-9]+)?$", REG_EXTENDED); + if (ret) + { + tio_error_printf("Could not compile regex\n"); + exit(EXIT_FAILURE); + } + + // Execute the regular expression + ret = regexec(®ex, input_string, 2, match, 0); + if (!ret) + { + // If there is a match, extract the N value if present + if (match[1].rm_so != -1) + { + char n_value_str[32]; // Assume max 32 digits for the numerical value + strncpy(n_value_str, input_string + match[1].rm_so, match[1].rm_eo - match[1].rm_so); + n_value_str[match[1].rm_eo - match[1].rm_so] = '\0'; // Null-terminate the string + n_value = atoi(n_value_str); + + if ((n_value > HEX_N_VALUE_MAX) || (n_value == 0)) + { + n_value = -1; + } + } + else + { + n_value = 0; + } + } + else if (ret == REG_NOMATCH) + { + n_value = -1; + } + else + { + char msgbuf[100]; + regerror(ret, ®ex, msgbuf, sizeof(msgbuf)); + tio_error_printf("Regex match failed: %s\n", msgbuf); + exit(EXIT_FAILURE); + } + + regfree(®ex); + + return n_value; +} + input_mode_t input_mode_option_parse(const char *arg) { if (strcmp("normal", arg) == 0) @@ -291,12 +350,15 @@ input_mode_t input_mode_option_parse(const char *arg) output_mode_t output_mode_option_parse(const char *arg) { + int n = 0; + if (strcmp("normal", arg) == 0) { return OUTPUT_MODE_NORMAL; } - else if (strcmp("hex", arg) == 0) + else if ((n = parse_hexN_string(arg)) != -1) { + option.hex_n_value = n; return OUTPUT_MODE_HEX; } else diff --git a/src/options.h b/src/options.h index 48048cb..d5a054b 100644 --- a/src/options.h +++ b/src/options.h @@ -94,6 +94,7 @@ struct option_t const char *exclude_devices; const char *exclude_drivers; const char *exclude_tids; + int hex_n_value; }; extern struct option_t option; diff --git a/src/tty.c b/src/tty.c index cebe7f5..6be7581 100644 --- a/src/tty.c +++ b/src/tty.c @@ -2086,7 +2086,7 @@ int tty_connect(void) char line_buffer[BUFSIZ] = {}; static bool first = true; int status; - bool next_timestamp = false; + bool do_timestamp = false; char* now = NULL; unsigned int line_index = 0; static char previous_char[2] = {}; @@ -2128,7 +2128,7 @@ int tty_connect(void) if (option.timestamp) { - next_timestamp = true; + do_timestamp = true; } /* Manage print output mode */ @@ -2260,7 +2260,7 @@ int tty_connect(void) rx_total += bytes_read; // Manage timeout based timestamping in hex mode - if (option.output_mode == OUTPUT_MODE_HEX) + if ((option.output_mode == OUTPUT_MODE_HEX) && (option.hex_n_value == 0)) { if (option.timestamp != TIMESTAMP_NONE) { @@ -2276,7 +2276,7 @@ int tty_connect(void) { log_printf("\r\n[%s] ", now); } - next_timestamp = false; + do_timestamp = false; } } tval_before = tval_now; @@ -2286,24 +2286,85 @@ int tty_connect(void) /* Process input byte by byte */ for (int i=0; i 0) + { + static bool first = true; + if ((count % option.hex_n_value) == 0) + { + if (option.timestamp != TIMESTAMP_NONE) + { + now = timestamp_current_time(); + if (first) + { + ansi_printf_raw("[%s] ", now); + if (option.log) + { + log_printf("[%s] ", now); + } + first = false; + } + else + { + ansi_printf_raw("\r\n[%s] ", now); + if (option.log) + { + log_printf("\n[%s] ", now); + } + } + } + else + { + if (first) + { + // Do nothing + } + else + { + putchar('\r'); + putchar('\n'); + + if (option.log) + { + log_putc('\n'); + } + first = false; + } + } + } + } + count++; + break; + + default: + tio_error_printf("Unknown outut mode"); + exit(EXIT_FAILURE); + break; } /* Convert MSB to LSB bit order */ @@ -2324,7 +2385,7 @@ int tty_connect(void) print('\n'); if (option.timestamp) { - next_timestamp = true; + do_timestamp = true; } } else if ((input_char == '\f') && (map_i_ff_escc) && (!map_o_msblsb)) @@ -2350,7 +2411,7 @@ int tty_connect(void) if (input_char == '\n' && option.timestamp) { - next_timestamp = true; + do_timestamp = true; } } } From 76de9b890e1e93287b6e12c612ec7b2b6a610d4b Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 27 Apr 2024 15:31:43 +0200 Subject: [PATCH 289/506] Update README --- README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 445897d..3daa7fe 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ when used in combination with [tmux](https://tmux.github.io). * Remapping of characters (nl, cr-nl, bs, lowercase to uppercase, etc.) * Switchable independent input and output * Normal mode - * Hex mode + * Hex mode (output supports variable width) * Line mode (input only) * Timestamp support * Per line in normal output mode @@ -175,7 +175,7 @@ $ tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 Using serial devices by ID ensures that tio automatically reconnects to the correct serial device if it is disconnected and then reconnected. -If no serial device by ID is available it is also possible to connect via +If no serial device by ID is available it is recommended to connect via topology ID (TID): ``` $ tio bCC2 @@ -191,6 +191,11 @@ $ tio --list Note: One can also use tio bash shell completion on /dev which will automatically list all available serial TTY devices by ID. +Automatically connect to first new appearing serial device: +``` +$ tio --auto-connect new +``` + Log to file with autogenerated filename: ``` $ tio --log /dev/ttyUSB0 @@ -206,6 +211,11 @@ Enable ISO8601 timestamps per line: $ tio --timestamp --timestamp-format iso8601 /dev/ttyUSB0 ``` +Output to hex with width 16: +``` +$ tio --output-mode hex16 /dev/ttyUSB0 +``` + Redirect I/O to IPv4 network socket on port 4242: ``` $ tio --socket inet:4242 /dev/ttyUSB0 From 588ac3e8ac4694e6c2baed56cfa1b2af9eda564a Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 27 Apr 2024 15:45:34 +0200 Subject: [PATCH 290/506] Update TODO --- TODO | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/TODO b/TODO index 99a1b7b..bf36c62 100644 --- a/TODO +++ b/TODO @@ -1,21 +1,13 @@ -* Add additional hex mode formats + * Support traditional hex output format such as: - - 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| - 00000010 65 73 74 64 66 0a 61 0a 66 61 0a 66 0a 61 73 66 |estdf.a.fa.f.asf| - 00000020 64 61 64 73 66 61 73 66 64 61 73 64 66 61 64 73 |dadsfasfdasdfads| - 00000030 66 0a 61 73 64 66 61 64 73 66 61 73 64 66 61 73 |f.asdfadsfasdfas| - 00000040 64 66 0a 61 73 64 66 61 64 73 66 61 73 64 66 61 |df.asdfadsfasdfa| - 00000050 73 64 66 66 64 61 73 64 66 0a 0a 31 32 33 31 0a |sdffdasdf..1231.| - 00000060 65 32 31 64 73 77 65 64 0a 0a |e21dswed..| - 0000006a - - With configurable width output (4, 8, 16 bytes etc.). E.g. config file: - - hex-mode-width = 16 - - With and without timestamp support for each broken up line. + 00000000 74 65 73 74 20 74 65 73 74 20 74 65 73 74 20 74 |test test test t| + 00000010 65 73 74 64 66 0a 61 0a 66 61 0a 66 0a 61 73 66 |estdf.a.fa.f.asf| + 00000020 64 61 64 73 66 61 73 66 64 61 73 64 66 61 64 73 |dadsfasfdasdfads| + 00000030 66 0a 61 73 64 66 61 64 73 66 61 73 64 66 61 73 |f.asdfadsfasdfas| + 00000040 64 66 0a 61 73 64 66 61 64 73 66 61 73 64 66 61 |df.asdfadsfasdfa| + 00000050 73 64 66 66 64 61 73 64 66 0a 0a 31 32 33 31 0a |sdffdasdf..1231.| + 00000060 65 32 31 64 73 77 65 64 0a 0a |e21dswed..| + 0000006a * Add support for activity based time stamping in normal output mode From cd160250a67206190f2c9b0a9197959b01972e43 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 27 Apr 2024 17:13:06 +0200 Subject: [PATCH 291/506] Cleanup --- src/options.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/options.c b/src/options.c index f8a77da..a460ab7 100644 --- a/src/options.c +++ b/src/options.c @@ -482,8 +482,10 @@ void options_parse(int argc, char *argv[]) // Support no-color.org informal spec char *no_color = getenv("NO_COLOR"); - if(no_color != NULL && no_color[0] != '\0') - option.color = -1; + if (no_color != NULL && no_color[0] != '\0') + { + option.color = -1; + } while (1) { From f5f62ee02dbfc43c18262517f3611ccb91147288 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 27 Apr 2024 17:32:45 +0200 Subject: [PATCH 292/506] Disable DEC Special Graphics at exit if vt100 If a vt100 terminal receives the Shift In character '\016' it will enable the 7 bit DEC Special Graphics character set used for line drawing. For most users this can happen due to line noise from the tty device and will likely mess up your terminal even after tio exits. To better handle this we want to make sure that tio disables this mode by sending the Shift Out character '\017' at exit. This mechanism will only activate if environment variable TERM assumes value "vt100". --- src/options.c | 8 ++++++++ src/options.h | 1 + src/tty.c | 8 ++++++++ 3 files changed, 17 insertions(+) diff --git a/src/options.c b/src/options.c index a460ab7..f538f42 100644 --- a/src/options.c +++ b/src/options.c @@ -118,6 +118,7 @@ struct option_t option = .exclude_drivers = NULL, .exclude_tids = NULL, .hex_n_value = 0, + .vt100 = false, }; void print_help(char *argv[]) @@ -487,6 +488,13 @@ void options_parse(int argc, char *argv[]) option.color = -1; } + // Check for vt100 terminal + char *term = getenv("TERM"); + if ((term != NULL) && (!strcmp(term, "vt100"))) + { + option.vt100 = true; + } + while (1) { static struct option long_options[] = diff --git a/src/options.h b/src/options.h index d5a054b..016978d 100644 --- a/src/options.h +++ b/src/options.h @@ -95,6 +95,7 @@ struct option_t const char *exclude_drivers; const char *exclude_tids; int hex_n_value; + bool vt100; }; extern struct option_t option; diff --git a/src/tty.c b/src/tty.c index 6be7581..ccbb44d 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1067,6 +1067,14 @@ void stdin_configure(void) void stdout_restore(void) { tcsetattr(STDOUT_FILENO, TCSANOW, &stdout_old); + + // If terminal is vt100 + if (option.vt100) + { + // Disable DEC Special Graphics character set just in case it was randomly + // enabled by noise from serial device. + putchar('\017'); + } } void stdout_configure(void) From fe2973522ec8494d33eb5b02c230b82523a6f837 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 27 Apr 2024 19:11:37 +0200 Subject: [PATCH 293/506] Fix shadow variable --- src/tty.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tty.c b/src/tty.c index ccbb44d..2767a9a 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1648,14 +1648,14 @@ GList *tty_search_for_serial_devices(void) // Example resulting device_name: // "ttyUSB0" // "3-6.4:1.2" - char *device_name = strrchr(device_path, '/'); - device_name++; // Move past the '/' + char *last_part = strrchr(device_path, '/'); + last_part++; // Move past the '/' // Find that part in /sys/devices and return first result string // Example devices_path: // "/sys/devices/pci0000:00/0000:00:14.0/usb3/3-6/3-6.3/3-6.3:1.0/ttyUSB0" // "/sys/devices/pci0000:00/0000:00:14.0/usb3/3-6/3-6.4/3-6.4:1.2" - char *devices_path = fs_search_directory("/sys/devices", device_name); + char *devices_path = fs_search_directory("/sys/devices", last_part); if (devices_path == NULL) { continue; @@ -1665,7 +1665,7 @@ GList *tty_search_for_serial_devices(void) // Example resulting devices_path: // "/sys/devices/pci0000:00/0000:00:14.0/usb3/3-6/3-6.3/3-6.3:1.0" // "/sys/devices/pci0000:00/0000:00:14.0/usb3/3-6/3-6.4/3-6.4:1.2" - char *last_part = strrchr(devices_path, '/'); + last_part = strrchr(devices_path, '/'); last_part++; if (strcmp(last_part, entry->d_name) == 0) { From ed70a587ec434cc0a8d84f44fcf83aed02a83ffa Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 27 Apr 2024 19:30:24 +0200 Subject: [PATCH 294/506] Update NEWS --- NEWS | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 166 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index e62ce4e..fa355bc 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,170 @@ -=== tio v2.8 === +=== tio v3.0 === + + + +Changes since tio v2.8: + + * Disable DEC Special Graphics at exit if vt100 + + If a vt100 terminal receives the Shift In character '\016' it will + enable the 7 bit DEC Special Graphics character set used for line drawing. + + For most users this can happen due to line noise from the tty device and + will likely mess up your terminal even after tio exits. + + To better handle this we want to make sure that tio disables this mode + by sending the Shift Out character '\017' at exit. + + This mechanism will only activate if environment variable TERM assumes + value "vt100". + + * Add hexN output mode + + Adds support for hexN mode where N is a number in the range 1 to 4096 + which defines how many hex values will be printed before a line break. + + In short, it defines the width of the hex output. + + In this mode, if timestamps are enabled they will be added to each hex + line. + + * Make sure to reset tainted state + + * Rename sub-config to profile + + Because better naming. + + * Use lua io.write() instead of print() + + io.write() gives better output control as print() is hardcoded to always + print a newline. + + * Add new ways to manage serial devices + + * Rename --list-devices to --list + + * Rename --no-autoconnect to --no-reconnect + + * Switch -l and -L options + + * -l now lists available serial devices + + * -L enables log to file + + * Add option --auto-connect + + * Supported strategies: + + * "new" - Waits to connect first new appearing serial device + + * "latest" - Connects to latest registered serial device + + * "direct" - Connect directly to specified serial device (default) + + * Add options to exclude serial devices from auto connect strategy by + pattern + + * Supported exclude options: + + * --exclude-devices + + Example: '--exclude-devices "/dev/ttyUSB2,/dev/ttyS?"' + + * --exclude-drivers + + Example: '--exclude-drivers "cdc_acm"' + + * --exclude-tids + + Example: '--exclude-tids "yW07,bCC2"' + + * Patterns support '*' and '?' + + * Connect to same port/device combination via unique topology ID (TID) + + * Topology ID is a 4 digit base62 encoded hash of a device topology + string coming from the Linux kernel. This means that whenever you + plug in the same e.g. USB serial port device to the same USB hub + port connected via the exact same hub topology all the way to your + computer, you will get the same unique TID. + + * Useful for stable reconnections when serial device has no serial + device by ID + + * For now, only tested on Linux. + + * Reworked and improved listing of serial devices to show serial devices: + + * By device + + * Including TID, uptime, driver, and description. + + * Sorted by uptime (newest device listed last) + + * By unique topology ID + + * By ID + + * By path + + * Add script interface 'list = tty_search()' for searching for serial + devices. + + * Clean up timestamp enum definition + + * Add missing options to show configuration + + * Update description of mute option + + * Add lua read_string() function + + * Don't forget to log output in lua expect() + + * Generalize automatic login example for Linux + + * Fix log output in hex output mode + + * Add timeout based timestamps in hex output mode + + This change reintroduces timestamping in hex output mode but based on + timeout instead of new lines which made no sense. This means that + timestamps will only be printed when timeout time has elapsed with no + output activity from serial device. + + Adds option --timestamp-timeout for setting the timeout value in + milliseconds. + + Defaults to 200 ms. + + * Do not echo CR in line input and hex output mode + + * Improve switched messages + + * Extend lua expect() to also return matched string + + * Add automatic login script example + + * Organize examples directory + + * Fix local echo in line mode + + * Fix line input mode + + Do not forward input characters to tty device before a line is input via + carriage return. + + * Introduce basic line input mode + + * Cleanup global variable name shadowing + +Davis C: + + * Updated login example with new expect logic + + * Reset buffer size at start of expect + + * Return 1 when `expect` matches From 9bc93991e7957fdaf14d2f994c83a8bea34bb12d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 27 Apr 2024 23:51:01 +0200 Subject: [PATCH 295/506] Update NEWS --- NEWS | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/NEWS b/NEWS index fa355bc..a58bc67 100644 --- a/NEWS +++ b/NEWS @@ -29,8 +29,6 @@ Changes since tio v2.8: In this mode, if timestamps are enabled they will be added to each hex line. - * Make sure to reset tainted state - * Rename sub-config to profile Because better naming. @@ -137,8 +135,6 @@ Changes since tio v2.8: Defaults to 200 ms. - * Do not echo CR in line input and hex output mode - * Improve switched messages * Extend lua expect() to also return matched string @@ -147,13 +143,6 @@ Changes since tio v2.8: * Organize examples directory - * Fix local echo in line mode - - * Fix line input mode - - Do not forward input characters to tty device before a line is input via - carriage return. - * Introduce basic line input mode * Cleanup global variable name shadowing From 33eae0c30d49bbe480056d0076dadc068633de63 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 28 Apr 2024 14:46:41 +0200 Subject: [PATCH 296/506] Update script API --- README.md | 84 ++++++++++++++++++++++++++++------------------------ man/tio.1.in | 11 ++++++- src/script.c | 10 ++----- 3 files changed, 59 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 3daa7fe..c182872 100644 --- a/README.md +++ b/README.md @@ -371,63 +371,71 @@ Tio suppots Lua scripting to easily automate interaction with the tty device. 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. +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. - Supports regular expressions. Special characters must be escaped with '\\'. + Returns 1 on successful match, 0 on timeout, or -1 on error. - Timeout is in milliseconds, defaults to 0 meaning it will wait forever. + On successful match it also returns the match string as second return value. - send(string) - Send string. +send(string) + Send string. - modem_send(file, protocol) - Send file using x/y-modem protocol. + Returns number of bytes written on success or -1 on error. - Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM. +modem_send(file, protocol) + Send file using x/y-modem protocol. - tty_search() - Search for serial devices. + Protocol can be any of XMODEM_1K, XMODEM_CRC, YMODEM. - 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". +tty_search() + Search for serial devices. - Returns nil if no serial devices are found. + 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". - exit(code) - Exit with code. + Returns nil if no serial devices are found. - high(line) - Set tty line high. +read(size, timeout) + Read from serial device. If timeout is 0 or not provided it will wait + forever until data is ready to read. - low(line) - Set tty line low. + Returns number of bytes read on success, 0 on timeout, or -1 on error. - toggle(line) - Toggle the tty line. +exit(code) + Exit with exit code. - sleep(seconds) - Sleep for seconds. +high(line) + Set tty line high. - msleep(ms) - Sleep for milliseconds. +low(line) + Set tty line low. - config_high(line) - Set tty line state configuration to high. +toggle(line) + Toggle the tty line. - config_low(line) - Set tty line state configuration to low. +sleep(seconds) + Sleep for seconds. - apply_config() - Apply tty line state configuration. +msleep(ms) + Sleep for miliseconds. - Using the line state configuration API instead of high()/low() will - help to make the lines physically switch as simultaneously as possible. - This may solve timing issues on some platforms. +config_high(line) + Set tty line state configuration to high. - Note: Line can be any of DTR, RTS, CTS, DSR, CD, RI +config_low(line) + Set tty line state configuration to low. + +apply_config() + Apply tty line state configuration. Using the line state configuration + API instead of high()/low() will help to make the lines physically switch as + simultaneously as possible. This may solve timing issues on some platforms. + +Note: Line can be any of DTR, RTS, CTS, DSR, CD, RI ``` ### 3.4 Configuration file diff --git a/man/tio.1.in b/man/tio.1.in index 75d05bb..87d5180 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -427,12 +427,15 @@ 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 invalid regular expression. +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. @@ -448,6 +451,12 @@ following string indexed elements "path", "tid", "uptime", "driver", 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. + +Returns number of bytes read on success, 0 on timeout, or -1 on error. + .IP "\fBexit(code)" Exit with exit code. .IP "\fBhigh(line)" diff --git a/src/script.c b/src/script.c index f998d40..571c58c 100644 --- a/src/script.c +++ b/src/script.c @@ -211,10 +211,6 @@ static int send(lua_State *L) } ret = write(device_fd, string, strlen(string)); - if (ret < 0) - { - tio_error_print("%s\n", strerror(errno)); - } lua_pushnumber(L, ret); @@ -281,7 +277,7 @@ static int read_string(lua_State *L) char *buffer = malloc(size); if (buffer == NULL) { - ret = -3; // Read buffer allocation failed + ret = -1; // Error goto error; } @@ -293,12 +289,12 @@ static int read_string(lua_State *L) ssize_t bytes_read = read_poll(device_fd, buffer, size, timeout); if (bytes_read < 0) { - ret = -1; // Read error + ret = -1; // Error goto error; } else if (bytes_read == 0) { - ret = -2; // Timeout + ret = 0; // Timeout goto error; } From 84dd4c36850590736f84c1469543c24682ac600e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 28 Apr 2024 14:47:07 +0200 Subject: [PATCH 297/506] Update README --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c182872..96712c6 100644 --- a/README.md +++ b/README.md @@ -299,20 +299,20 @@ plugged into to the exact same USB topology (same ports, same hubs, etc.) it will get the exact same TID. This helps solve the problem of reconnecting to serical devices which do not provide a unique device by ID. -Additonally tio offers two convenient ways of connecting to serial devices: +Additonally tio offers two convenient ways of connecting to serial devices. -(1) Connect automatically to first new appearing serial device: +Connect automatically to first new appearing serial device: ``` $ tio --auto-connect new ``` -(2) Connect automatically to latest registered serial device: +Connect automatically to latest registered serial device: ``` $ tio --auto-connect latest ``` -It is also possible to use excludes to affect which serial devices are involved -in the strategy decisions: +It is also possible to use exclude options to affect which serial devices are +involved in the strategy decisions: Exclude devices by pattern: ``` From 273afb73f450b4bede7ed92bc16195051d1c6b6e Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 28 Apr 2024 17:20:41 +0200 Subject: [PATCH 298/506] Update README --- README.md | 179 ++++++++++++++++++------------------------------------ 1 file changed, 58 insertions(+), 121 deletions(-) diff --git a/README.md b/README.md index 96712c6..2356298 100644 --- a/README.md +++ b/README.md @@ -147,14 +147,12 @@ Options and profiles may be set via configuration file. See the man page for more details. ``` -By default tio automatically connects to the provided TTY device if present. -If the device is not present, it will wait for it to appear and then connect. -If the connection is lost (eg. device is unplugged), it will wait for the -device to reappear and then reconnect. However, if the `--no-reconnect` -option is provided, tio will exit if the device is not present or an -established connection is lost. - -tio features full bash autocompletion. +By default tio automatically connects to the provided TTY device. If the device +is not present, tio will wait for it to appear and then connect. If the +connection is lost (e.g. device is unplugged), it will wait for the device to +reappear and then reconnect. However, if the `--no-reconnect` option is +provided, tio will exit if the device is not present or an established +connection is lost. #### 3.1.1 Examples @@ -168,12 +166,39 @@ Which corresponds to the commonly used default options: $ tio --baudrate 115200 --databits 8 --flow none --stopbits 1 --parity none /dev/ttyUSB0 ``` +List available serial devices: +``` +$ tio --list +Device TID Uptime [s] Driver Description +----------------- ---- ------------- ---------------- -------------------------- +/dev/ttyS4 BaaB 19526.576 port 16550A UART +/dev/ttyS5 eV0Z 19525.845 port 16550A UART +/dev/ttyUSB1 bCC2 1023.274 ftdi_sio TTL232R-3V3 +/dev/ttyUSB0 SPpw 978.527 ftdi_sio TTL232RG-VREG3V3 +/dev/ttyACM0 i5q4 2.079 cdc_acm ST-Link VCP Ctrl + +By-id +-------------------------------------------------------------------------------- +/dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTCHUV56-if00-port0 +/dev/serial/by-id/usb-FTDI_TTL232RG-VREG3V3_FT1NELUB-if00-port0 +/dev/serial/by-id/usb-STMicroelectronics_STLINK-V3_004900343438510234313939-if02 + +By-path +-------------------------------------------------------------------------------- +/dev/serial/by-path/pci-0000:00:14.0-usb-0:8.1.3.1.4:1.0-port0 +/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:8.1.3.1.4:1.0-port0 +/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:6.4:1.0-port0 +/dev/serial/by-path/pci-0000:00:14.0-usb-0:6.4:1.0-port0 +/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:6.3:1.2 +/dev/serial/by-path/pci-0000:00:14.0-usb-0:6.3:1.2 +``` + It is recommended to connect serial TTY devices by ID: ``` -$ tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 +$ tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTCHUV56-if00-port0 ``` -Using serial devices by ID ensures that tio automatically reconnects to the -correct serial device if it is disconnected and then reconnected. +Using serial devices by ID helps ensure that tio automatically reconnects to +the same serial device when reattached, even when it enumerates differently. If no serial device by ID is available it is recommended to connect via topology ID (TID): @@ -181,27 +206,38 @@ topology ID (TID): $ tio bCC2 ``` The TID is unique and will stay the same as long as your USB serial port device -plugs into the same USB topology (same ports, same hubs, etc.). This way tio -will successfully reconnect to the same device each time. +plugs into the same USB topology (same ports, same hubs, etc.). This way it is +possible for tio to successfully reconnect to the same device. -List available serial devices: -``` -$ tio --list -``` -Note: One can also use tio bash shell completion on /dev which will -automatically list all available serial TTY devices by ID. - -Automatically connect to first new appearing serial device: +Connect automatically to first new appearing serial device: ``` $ tio --auto-connect new ``` +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 +involved in the automatic connection strategy: +``` +$ tio --auto-connect new --exclude-devices "/dev/ttyACM?,/dev/ttyUSB2" +``` + +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. + Log to file with autogenerated filename: ``` $ tio --log /dev/ttyUSB0 ``` -Log to file with filename: +Log to file with specific filename: ``` $ tio --log --log-file my-log.txt ``` @@ -232,105 +268,6 @@ $ echo "*IDN?" | tio /dev/ttyACM0 --script "expect('\r\n', 1000)" --mute KORAD KD3305P V4.2 SN:32475045 ``` -### 3.1.2 Different ways to connect to serial devices - -Using tio there are up to 4 recommended ways to connect to a specific serial -device: - -Connect by ID (preferred method): -``` -$ tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTCHUV56-if00-port0 -``` - -Connect by topology ID: -``` -$ tio bCC2 -``` - -Connect to enumerated device in /dev: -``` -$ tio /dev/ttyUSB4 -``` - -Connect by path: -``` -$ tio /dev/serial/by-path/pci-0000:00:14.0-usb-0:8.1.3.1.4:1.0-port0 -``` - -Which serial device to connect becomes more clear from tio's serial device -listing which provides more information about each serial device. For example: -``` -$ tio --list -Device TID Uptime [s] Driver Description ------------------ ---- ------------- ---------------- -------------------------- -/dev/ttyS4 8xSh 32532.317 port 16550A UART -/dev/ttyS5 HJhB 32530.578 port 16550A UART -/dev/ttyUSB3 yW07 32464.194 ftdi_sio TTL232RG-VREG3V3 -/dev/ttyUSB4 bCC2 26066.573 ftdi_sio TTL232R-3V3 -/dev/ttyUSB0 g5q4 136.717 ftdi_sio Flyswatter2 -/dev/ttyUSB1 h5q4 136.715 ftdi_sio Flyswatter2 -/dev/ttyACM0 EOEs 10.449 cdc_acm ST-Link VCP Ctrl - -By-id --------------------------------------------------------------------------------- -/dev/serial/by-id/usb-FTDI_TTL232RG-VREG3V3_FT1NC2D0-if00-port0 -/dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTCHUV56-if00-port0 -/dev/serial/by-id/usb-TinCanTools_Flyswatter2_FS20000-if00-port0 -/dev/serial/by-id/usb-TinCanTools_Flyswatter2_FS20000-if01-port0 -/dev/serial/by-id/usb-STMicroelectronics_STLINK-V3_004900343438510234313939-if02 - -By-path --------------------------------------------------------------------------------- -/dev/serial/by-path/pci-0000:00:14.0-usb-0:8.1.3.2.2:1.0-port0 -/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:8.1.3.2.2:1.0-port0 -/dev/serial/by-path/pci-0000:00:14.0-usb-0:8.1.3.1.4:1.0-port0 -/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:8.1.3.1.4:1.0-port0 -/dev/serial/by-path/pci-0000:00:14.0-usb-0:6.3:1.0-port0 -/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:6.3:1.0-port0 -/dev/serial/by-path/pci-0000:00:14.0-usb-0:6.3:1.1-port0 -/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:6.3:1.1-port0 -/dev/serial/by-path/pci-0000:00:14.0-usb-0:6.4:1.2 -/dev/serial/by-path/pci-0000:00:14.0-usbv2-0:6.4:1.2 -``` - -Note: The topology ID (TID) is a unique hash of the full topology path of the -connected device. This means that every time e.g. a USB serial port device is -plugged into to the exact same USB topology (same ports, same hubs, etc.) it -will get the exact same TID. This helps solve the problem of reconnecting to -serical devices which do not provide a unique device by ID. - -Additonally tio offers two convenient ways of connecting to serial devices. - -Connect automatically to first new appearing serial device: -``` -$ tio --auto-connect new -``` - -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 -involved in the strategy decisions: - -Exclude devices by pattern: -``` -$ tio --auto-connect new --exclude-devices "/dev/ttyACM?,/dev/ttyUSB2" -``` - -Exclude drivers by pattern: -``` -$ tio --auto-connect new --exclude-drivers "cdc_acm,ftdi_sio" -``` - -Exclude topology IDs by pattern: -``` -$ tio --auto-connect new --exclude-tids "EOEs" -``` - -Note: Pattern matching supports '*' and '?'. Use comma separation to define multiple patterns. - ### 3.2 Key commands Various in session key commands are supported. When tio is started, press From c453728ab59186948e4a80a313c177782c6ad2cb Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 28 Apr 2024 17:26:10 +0200 Subject: [PATCH 299/506] Update README --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2356298..850eac2 100644 --- a/README.md +++ b/README.md @@ -197,17 +197,17 @@ It is recommended to connect serial TTY devices by ID: ``` $ tio /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTCHUV56-if00-port0 ``` -Using serial devices by ID helps ensure that tio automatically reconnects to -the same serial device when reattached, even when it enumerates differently. +Note: Using serial devices by ID helps ensure that tio automatically reconnects +to the same serial device when reattached, even when it enumerates differently. If no serial device by ID is available it is recommended to connect via topology ID (TID): ``` $ tio bCC2 ``` -The TID is unique and will stay the same as long as your USB serial port device -plugs into the same USB topology (same ports, same hubs, etc.). This way it is -possible for tio to successfully reconnect to the same device. +Note: The TID is unique and will stay the same as long as your USB serial port +device plugs into the same USB topology (same ports, same hubs, etc.). This way +it is possible for tio to successfully reconnect to the same device. Connect automatically to first new appearing serial device: ``` From 6895c05321217eb5a55a99fb51791484cd595d3f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 28 Apr 2024 18:23:08 +0200 Subject: [PATCH 300/506] Update README --- README.md | 2 +- src/options.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 850eac2..dd83628 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ Options: -n, --no-reconnect Do not reconnect -e, --local-echo Enable local echo --input-mode normal|hex|line Select input mode (default: normal) - --output-mode normal|hex|hexN Select output mode (default: normal, N <= 4096) + --output-mode normal|hex|hexN Select output mode (default: normal) -t, --timestamp Enable line timestamp --timestamp-format Set timestamp format (default: 24hour) --timestamp-timeout Set timestamp timeout (default: 200) diff --git a/src/options.c b/src/options.c index f538f42..65082b6 100644 --- a/src/options.c +++ b/src/options.c @@ -145,7 +145,7 @@ void print_help(char *argv[]) printf(" -n, --no-reconnect Do not reconnect\n"); printf(" -e, --local-echo Enable local echo\n"); printf(" --input-mode normal|hex|line Select input mode (default: normal)\n"); - printf(" --output-mode normal|hex|hexN Select output mode (default: normal, N <= %d)\n", HEX_N_VALUE_MAX); + printf(" --output-mode normal|hex|hexN Select output mode (default: normal)\n"); printf(" -t, --timestamp Enable line timestamp\n"); printf(" --timestamp-format Set timestamp format (default: 24hour)\n"); printf(" --timestamp-timeout Set timestamp timeout (default: 200)\n"); From eef0a151942f03455228d9ec8f4c4792ab55e0fb Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 29 Apr 2024 01:49:44 +0200 Subject: [PATCH 301/506] Unshadow variable --- src/tty.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tty.c b/src/tty.c index 2767a9a..a28ba34 100644 --- a/src/tty.c +++ b/src/tty.c @@ -2322,20 +2322,20 @@ int tty_connect(void) // Support hexN mode if (option.hex_n_value > 0) { - static bool first = true; + static bool first_ = true; if ((count % option.hex_n_value) == 0) { if (option.timestamp != TIMESTAMP_NONE) { now = timestamp_current_time(); - if (first) + if (first_) { ansi_printf_raw("[%s] ", now); if (option.log) { log_printf("[%s] ", now); } - first = false; + first_ = false; } else { @@ -2348,7 +2348,7 @@ int tty_connect(void) } else { - if (first) + if (first_) { // Do nothing } @@ -2361,7 +2361,7 @@ int tty_connect(void) { log_putc('\n'); } - first = false; + first_ = false; } } } From 6d77201ba09ad7b000c82002e69fc9219db6bab4 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 29 Apr 2024 14:45:36 +0200 Subject: [PATCH 302/506] Simplify lua line manipulation API Collapses lua high(), low(), toggle(), config_high(), config_low(), config_apply() into one simple function: set{=, ...} Line can be any of DTR, RTS, CTS, DSR, CD, RI. State is high, low, or toggle. Example: script = set{DTR=high, RTS=low}; msleep(100); set{DTR=low, RTS=high}; msleep(100); set{RTS=low} Notice the use of {} instad of () when calling the set function. This is required to pass parameters by name in lua. --- README.md | 27 ++--- examples/lua/control-lines-test.lua | 8 +- man/tio.1.in | 28 ++--- src/script.c | 156 +++++++++++----------------- src/tty.c | 92 ++++------------ src/tty.h | 12 ++- 6 files changed, 112 insertions(+), 211 deletions(-) diff --git a/README.md b/README.md index dd83628..0981663 100644 --- a/README.md +++ b/README.md @@ -343,17 +343,12 @@ read(size, timeout) Returns number of bytes read on success, 0 on timeout, or -1 on error. -exit(code) - Exit with exit code. +set{line=state, ...} + Set state of one or multiple tty modem lines. -high(line) - Set tty line high. + Line can be any of DTR, RTS, CTS, DSR, CD, RI -low(line) - Set tty line low. - -toggle(line) - Toggle the tty line. + State is high, low, or toggle. sleep(seconds) Sleep for seconds. @@ -361,18 +356,8 @@ sleep(seconds) msleep(ms) Sleep for miliseconds. -config_high(line) - Set tty line state configuration to high. - -config_low(line) - Set tty line state configuration to low. - -apply_config() - Apply tty line state configuration. Using the line state configuration - API instead of high()/low() will help to make the lines physically switch as - simultaneously as possible. This may solve timing issues on some platforms. - -Note: Line can be any of DTR, RTS, CTS, DSR, CD, RI +exit(code) + Exit with exit code. ``` ### 3.4 Configuration file diff --git a/examples/lua/control-lines-test.lua b/examples/lua/control-lines-test.lua index 9f0d493..5b54ab4 100644 --- a/examples/lua/control-lines-test.lua +++ b/examples/lua/control-lines-test.lua @@ -1,7 +1,5 @@ -high(DTR) -low(RTS) +set{DTR=high, RTS=low} msleep(100) -low(DTR) -high(RTS) +set{DTR=low, RTS=high} msleep(100) -low(RTS) +set{RTS=toggle} diff --git a/man/tio.1.in b/man/tio.1.in index 87d5180..4b76cfa 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -457,29 +457,19 @@ until data is ready to read. Returns number of bytes read on success, 0 on timeout, or -1 on error. -.IP "\fBexit(code)" -Exit with exit code. -.IP "\fBhigh(line)" -Set tty line high. -.IP "\fBlow(line)" -Set tty line low. -.IP "\fBtoggle(line)" -Toggle the tty line. +.IP "\fBset{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)" Sleep for seconds. .IP "\fBmsleep(ms)" Sleep for miliseconds. -.IP "\fBconfig_high(line)" -Set tty line state configuration to high. -.IP "\fBconfig_low(line)" -Set tty line state configuration to low. -.IP "\fBapply_config()" -Apply tty line state configuration. Using the line state configuration API -instead of high()/low() will help to make the lines physically switch as -simultaneously as possible. This may solve timing issues on some platforms. - -.TP 0n -Note: Line can be any of DTR, RTS, CTS, DSR, CD, RI +.IP "\fBexit(code)" +Exit with exit code. .SH "CONFIGURATION FILE" .PP diff --git a/src/script.c b/src/script.c index 571c58c..f425a1f 100644 --- a/src/script.c +++ b/src/script.c @@ -45,8 +45,16 @@ static char circular_buffer[MAX_BUFFER_SIZE]; static char match_string[MAX_BUFFER_SIZE]; static int buffer_size = 0; -static char init_script[] = -"\n"; +static char script_init[] = +"function set(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"; // lua: sleep(seconds) static int sleep_(lua_State *L) @@ -82,87 +90,56 @@ static int msleep(lua_State *L) return 0; } -// lua: high(line) -static int high(lua_State *L) +// lua: line_set(dtr,rts,cts,dsr,cd,ri) +static int line_set(lua_State *L) { - long line = lua_tointeger(L, 1); + tty_line_config_t line_config[6] = { }; - if (line < 0) + int dtr = lua_tointeger(L, 1); + int rts = lua_tointeger(L, 2); + int cts = lua_tointeger(L, 3); + int dsr = lua_tointeger(L, 4); + int cd = lua_tointeger(L, 5); + int ri = lua_tointeger(L, 6); + + if (dtr != -1) { - return 0; + line_config[0].mask = TIOCM_DTR; + line_config[0].value = dtr; + line_config[0].reserved = true; + } + if (rts != -1) + { + line_config[1].mask = TIOCM_RTS; + line_config[1].value = rts; + line_config[1].reserved = true; + } + if (cts != -1) + { + line_config[2].mask = TIOCM_CTS; + line_config[2].value = cts; + line_config[2].reserved = true; + } + if (dsr != -1) + { + line_config[3].mask = TIOCM_DSR; + line_config[3].value = dsr; + line_config[3].reserved = true; + } + if (cd != -1) + { + line_config[4].mask = TIOCM_CD; + line_config[4].value = cd; + line_config[4].reserved = true; + } + if (ri != -1) + { + line_config[5].mask = TIOCM_RI; + line_config[5].value = ri; + line_config[5].reserved = true; } - tty_line_set(device_fd, line, LINE_HIGH); - - return 0; -} - -// lua: low(line) -static int low(lua_State *L) -{ - long line = lua_tointeger(L, 1); - - if (line < 0) - { - return 0; - } - - tty_line_set(device_fd, line, LINE_LOW); - - return 0; -} - -// lua: toggle(line) -static int toggle(lua_State *L) -{ - long line = lua_tointeger(L, 1); - - if (line < 0) - { - return 0; - } - - tty_line_toggle(device_fd, line); - - return 0; -} - -// lua: config_high(line) -static int config_high(lua_State *L) -{ - long line = lua_tointeger(L, 1); - - if (line < 0) - { - return 0; - } - - tty_line_config(line, true); - - return 0; -} - -// lua: config_low(line) -static int config_low(lua_State *L) -{ - long line = lua_tointeger(L, 1); - - if (line < 0) - { - return 0; - } - - tty_line_config(line, false); - - return 0; -} - -// lua: config_apply(line) -static int config_apply(lua_State *L) -{ - UNUSED(L); - - tty_line_config_apply(); + tty_line_set(device_fd, line_config); return 0; } @@ -472,12 +449,7 @@ static const struct luaL_Reg tio_lib[] = { { "sleep", sleep_}, { "msleep", msleep}, - { "high", high}, - { "low", low}, - { "toggle", toggle}, - { "config_high", config_high}, - { "config_low", config_low}, - { "config_apply", config_apply}, + { "line_set", line_set}, { "modem_send", modem_send}, { "send", send}, { "read", read_string}, @@ -506,11 +478,11 @@ static void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) } #endif -static void load_init_script(lua_State *L) +static void script_load(lua_State *L) { int error; - error = luaL_loadbuffer(L, init_script, strlen(init_script), "tio") || lua_pcall(L, 0, 0, 0); + 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)); @@ -525,7 +497,8 @@ int lua_register_tio(lua_State *L) luaL_setfuncs(L, tio_lib, 0); lua_pop(L, 1); - load_init_script(L); + // Load lua init script + script_load(L); return 0; } @@ -554,12 +527,9 @@ void script_set_global(lua_State *L, const char *name, long value) void script_set_globals(lua_State *L) { - script_set_global(L, "DTR", TIOCM_DTR); - script_set_global(L, "RTS", TIOCM_RTS); - script_set_global(L, "CTS", TIOCM_CTS); - script_set_global(L, "DSR", TIOCM_DSR); - script_set_global(L, "CD", TIOCM_CD); - script_set_global(L, "RI", TIOCM_RI); + script_set_global(L, "toggle", 2); + script_set_global(L, "high", 1); + script_set_global(L, "low", 0); script_set_global(L, "XMODEM_CRC", XMODEM_CRC); script_set_global(L, "XMODEM_1K", XMODEM_1K); script_set_global(L, "YMODEM", YMODEM); diff --git a/src/tty.c b/src/tty.c index a28ba34..c8f7d7b 100644 --- a/src/tty.c +++ b/src/tty.c @@ -137,13 +137,6 @@ typedef enum SUBCOMMAND_XMODEM, } sub_command_t; -typedef struct -{ - int mask; - bool value; - bool reserved; -} tty_line_config_t; - const char random_array[] = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x20, 0x28, 0x0A, 0x20, @@ -190,7 +183,6 @@ 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 tty_line_config_t line_config[6] = { }; static void optional_local_echo(char c) { @@ -483,28 +475,12 @@ static const char *tty_line_name(int mask) } } -void tty_line_config(int mask, bool value) +void tty_line_set(int fd, tty_line_config_t line_config[]) { - int i = 0; - - for (i=0; i<6; i++) - { - if ((line_config[i].mask == mask) || (line_config[i].reserved == false)) - { - line_config[i].mask = mask; - line_config[i].value = value; - line_config[i].reserved = true; - break; - } - } -} - -void tty_line_config_apply(void) -{ - int i = 0; static int state; + int i = 0; - if (ioctl(device_fd, TIOCMGET, &state) < 0) + if (ioctl(fd, TIOCMGET, &state) < 0) { tio_warning_printf("Could not get line state (%s)", strerror(errno)); return; @@ -514,57 +490,35 @@ void tty_line_config_apply(void) { if (line_config[i].reserved) { - if (line_config[i].value) - { - // High - state &= ~line_config[i].mask; - tio_printf("Setting %s to HIGH", tty_line_name(line_config[i].mask)); - } - else + if (line_config[i].value == 0) { // Low state |= line_config[i].mask; tio_printf("Setting %s to LOW", tty_line_name(line_config[i].mask)); } + else if (line_config[i].value == 1) + { + // High + state &= ~line_config[i].mask; + tio_printf("Setting %s to HIGH", tty_line_name(line_config[i].mask)); + } + else if (line_config[i].value == 2) + { + // Toggle + state ^= line_config[i].mask; - line_config[i].reserved = true; + if (state & line_config[i].mask) + { + tio_printf("Setting %s to LOW", tty_line_name(line_config[i].mask)); + } + else + { + tio_printf("Setting %s to HIGH", tty_line_name(line_config[i].mask)); + } + } } } - if (ioctl(device_fd, TIOCMSET, &state) < 0) - { - tio_warning_printf("Could not set line state configuration (%s)", strerror(errno)); - } - - // Reset configuration - for (i=0; i<6; i++) - { - line_config[i].reserved = false; - line_config[i].mask = -1; - } -} - -void tty_line_set(int fd, int mask, bool value) -{ - int state; - - if (ioctl(fd, TIOCMGET, &state) < 0) - { - tio_warning_printf("Could not get line state (%s)", strerror(errno)); - return; - } - - if (value) - { - state &= ~mask; - tio_printf("Setting %s to HIGH", tty_line_name(mask)); - } - else - { - state |= mask; - tio_printf("Setting %s to LOW", tty_line_name(mask)); - } - if (ioctl(fd, TIOCMSET, &state) < 0) { tio_warning_printf("Could not set line state (%s)", strerror(errno)); diff --git a/src/tty.h b/src/tty.h index 140f8dc..b142034 100644 --- a/src/tty.h +++ b/src/tty.h @@ -46,6 +46,13 @@ typedef struct char *description; } device_t; +typedef struct +{ + int mask; + int value; + bool reserved; +} tty_line_config_t; + extern const char *device_name; extern bool interactive_mode; extern bool map_i_nl_cr; @@ -60,9 +67,6 @@ void tty_wait_for_device(void); void list_serial_devices(void); void tty_input_thread_create(void); void tty_input_thread_wait_ready(void); -void tty_line_set(int fd, int mask, bool value); -void tty_line_toggle(int fd, int mask); -void tty_line_config(int mask, bool value); -void tty_line_config_apply(void); +void tty_line_set(int fd, tty_line_config_t line_config[]); void tty_search(void); GList *tty_search_for_serial_devices(void); From 08404e700f74de1e0746f4520d03f16b4510b89b Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 29 Apr 2024 15:48:00 +0200 Subject: [PATCH 303/506] Update NEWS --- NEWS | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/NEWS b/NEWS index a58bc67..8a4739e 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,23 @@ Changes since tio v2.8: + * Simplify lua line manipulation API + + Collapses lua high(), low(), toggle(), config_high(), config_low(), + config_apply() into one simple function: + + set{=, ...} + + Line can be any of DTR, RTS, CTS, DSR, CD, RI. + + State is high, low, or toggle. + + Example: + script = set{DTR=high, RTS=low}; msleep(100); set{DTR=low, RTS=high}; msleep(100); set{RTS=low} + + Notice the use of {} instad of () when calling the set function. This is + required to pass parameters by name in lua. + * Disable DEC Special Graphics at exit if vt100 If a vt100 terminal receives the Shift In character '\016' it will From e2960c3f824c1b46df945b857ca8f41b777230cb Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 29 Apr 2024 15:58:28 +0200 Subject: [PATCH 304/506] Update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0981663..a59202b 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ when used in combination with [tmux](https://tmux.github.io). * Lua scripting support for automation * Run script manually or automatically at connect (once/always/never) * Simple expect/send like functionality with support for regular expressions - * Manipulate port control lines (useful for microcontroller reset/boot etc.) + * Manipulate port modem lines (useful for microcontroller reset/boot etc.) * Send files via x/y-modem protocol * Search for serial devices * Man page documentation @@ -400,7 +400,7 @@ color = 12 [esp32] device = /dev/serial/by-id/usb-0403_6014-if00-port0 -script = high(DTR); low(RTS); msleep(100); low(DTR); high(RTS); msleep(100); low(RTS) +script = set{DTR=high,RTS=low}; msleep(100); set{DTR=low,RTS=high}; msleep(100); set{RTS=low} script-run = once color = 13 From c16a2a1f94e37e654177b35a09a8fb6bf789ff42 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 29 Apr 2024 16:05:14 +0200 Subject: [PATCH 305/506] Update README --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index a59202b..8ba76cd 100644 --- a/README.md +++ b/README.md @@ -262,6 +262,11 @@ Pipe data to the serial device: $ 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 +``` + Pipe command to serial device and wait for line response within 1 second: ``` $ echo "*IDN?" | tio /dev/ttyACM0 --script "expect('\r\n', 1000)" --mute From 5e3722a10e1af6526c5aa9eeb0d2c6239be07e2a Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 29 Apr 2024 16:09:37 +0200 Subject: [PATCH 306/506] Update man page --- 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 4b76cfa..9ef4c73 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -726,7 +726,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 "high(DTR); low(RTS); msleep(100); toggle(DTR)" --script-run once /dev/ttyUSB0 +$ tio --script "set{DTR=high,RTS=low}; msleep(100); set{RTS=toggle}" --script-run once /dev/ttyUSB0 .TP Automatically log in to connected OS: From a698799a7db06f0354912544ce9aa6bb39d81553 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 29 Apr 2024 16:22:01 +0200 Subject: [PATCH 307/506] Update man page --- man/tio.1.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/man/tio.1.in b/man/tio.1.in index 9ef4c73..b305d07 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -691,6 +691,11 @@ expect -i $uart "prompt> " .fi .RE +.TP +It is also possible to use the expect/send script functionality to e.g. automate logins: + +$ tio --script 'expect("login: "); send("root\\n"); expect("Password: "); send("root\\n")' /dev/ttyUSB0 + .TP Redirect device I/O to network file socket for remote TTY sharing: From d8fb141bc4c4cc719c8b6e866d52590dee3f06c5 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 29 Apr 2024 16:47:00 +0200 Subject: [PATCH 308/506] Update lua read() description --- README.md | 2 ++ man/tio.1.in | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index 8ba76cd..1a4e0b7 100644 --- a/README.md +++ b/README.md @@ -348,6 +348,8 @@ read(size, timeout) 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/man/tio.1.in b/man/tio.1.in index b305d07..7dd7bc2 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -457,6 +457,8 @@ 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. + .IP "\fBset{line=state, ...}" Set state of one or multiple tty modem lines. From 8ead9337d1b010913df304565540db2578e7a566 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Mon, 29 Apr 2024 19:27:45 +0200 Subject: [PATCH 309/506] Enable extended pattern matching So that the exclude options can also work as include using special pattern syntax. For example, to only include /dev/ttyUSB* devices simply do: $ tio --exclude-devices=!(/dev/ttyUSB*) --list See the man page of fnmatch() for all available extended pattern options. --- src/misc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/misc.c b/src/misc.c index 1d1acea..082488a 100644 --- a/src/misc.c +++ b/src/misc.c @@ -19,6 +19,7 @@ * 02110-1301, USA. */ +#define _GNU_SOURCE // For FNM_EXTMATCH #include "config.h" #include #include @@ -197,7 +198,7 @@ bool match_patterns(const char *string, const char *patterns) while (pattern != NULL) { // Check if the string matches the current pattern - if (fnmatch(pattern, string, 0) == 0) + if (fnmatch(pattern, string, FNM_EXTMATCH) == 0) { free(patterns_copy); return true; From b763f1289b7ea8d9c836306ab6a2aa64d02e7a71 Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Mon, 29 Apr 2024 14:01:32 -0400 Subject: [PATCH 310/506] fix: add macos build patch for `fs_get_creation_time` Signed-off-by: Rui Chen --- src/fs.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/fs.c b/src/fs.c index fa989d4..a84bd27 100644 --- a/src/fs.c +++ b/src/fs.c @@ -181,19 +181,27 @@ char* fs_search_directory(const char *dir_path, const char *dirname) // Function to return creation time of file double fs_get_creation_time(const char *path) { - struct statx stx; - int fd = open(path, O_RDONLY); - if (fd == -1) +#if defined(__APPLE__) || defined(__MACH__) + // Use stat on macOS to access creation time + struct stat st; + + if (stat(path, &st) != 0) { - // Error return -1; } - int ret = statx(fd, "", AT_EMPTY_PATH, STATX_ALL, &stx); - if (ret == -1) + return st.st_birthtimespec.tv_sec + st.st_birthtimespec.tv_nsec / 1e9; +#else + struct statx stx; + int fd = open(path, O_RDONLY); + if (fd == -1) + { + return -1; + } + + if (statx(fd, "", AT_EMPTY_PATH, STATX_ALL, &stx) != 0) { - // Error close(fd); return -1; } @@ -202,4 +210,5 @@ double fs_get_creation_time(const char *path) close(fd); return stx.stx_btime.tv_sec + stx.stx_btime.tv_nsec / 1e9; +#endif } From 054326454b022f183b319f35b776cfaef30186b0 Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Mon, 29 Apr 2024 21:10:04 -0400 Subject: [PATCH 311/506] feat: add macos workflow Signed-off-by: Rui Chen --- .github/workflows/macos.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/macos.yml diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 0000000..fc9bf1a --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,30 @@ +name: macOS build + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build: + runs-on: macos-latest + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Install dependencies + run: | + brew install meson ninja pkg-config glib inih lua + + - name: Build + run: | + meson setup build + meson compile -C build --verbose + meson install -C build --verbose From 3b77eb35cf7e51aa28ffb6568dfcfe201730a653 Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Mon, 29 Apr 2024 21:13:22 -0400 Subject: [PATCH 312/506] fix: add build patch for `FNM_EXTMATCH` run into the following build failure ``` cc -Isrc/tio.p -Isrc -I../src -I/opt/homebrew/Cellar/glib/2.80.0_2/include/glib-2.0 -I/opt/homebrew/Cellar/glib/2.80.0_2/lib/glib-2.0/include -I/opt/homebrew/opt/gettext/include -I/opt/homebrew/Cellar/pcre2/10.43/include -I/opt/homebrew/Cellar/inih/58/include -I/opt/homebrew/include/lua -fdiagnostics-color=always -Wall -Winvalid-pch -Wextra -std=gnu99 -O3 -Wno-unused-result -DHAVE_IOSSIOSPEED -MD -MQ src/tio.p/misc.c.o -MF src/tio.p/misc.c.o.d -o src/tio.p/misc.c.o -c ../src/misc.c ../src/misc.c:201:38: error: use of undeclared identifier 'FNM_EXTMATCH' if (fnmatch(pattern, string, FNM_EXTMATCH) == 0) ^ 1 error generated. ``` Signed-off-by: Rui Chen --- src/misc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/misc.c b/src/misc.c index 082488a..091675a 100644 --- a/src/misc.c +++ b/src/misc.c @@ -198,7 +198,11 @@ bool match_patterns(const char *string, const char *patterns) while (pattern != NULL) { // Check if the string matches the current pattern - if (fnmatch(pattern, string, FNM_EXTMATCH) == 0) + #ifdef FNM_EXTMATCH + if (fnmatch(pattern, string, FNM_EXTMATCH) == 0) + #else + if (fnmatch(pattern, string, 0) == 0) + #endif { free(patterns_copy); return true; From f5703ff107ae582e1bc128fa08a52de5629a6da4 Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Mon, 29 Apr 2024 21:15:04 -0400 Subject: [PATCH 313/506] remove verbose for `meson install` and use system glib and pkg-config Signed-off-by: Rui Chen --- .github/workflows/macos.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index fc9bf1a..499f545 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -21,10 +21,10 @@ jobs: - name: Install dependencies run: | - brew install meson ninja pkg-config glib inih lua + brew install meson ninja inih lua - name: Build run: | meson setup build meson compile -C build --verbose - meson install -C build --verbose + meson install -C build From 22f030ebb89b39514453de61c72ee0a47f5cdd01 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 30 Apr 2024 10:32:36 +0200 Subject: [PATCH 314/506] Add github workflow for Ubuntu build --- .github/workflows/ubuntu.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/ubuntu.yml diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml new file mode 100644 index 0000000..5975190 --- /dev/null +++ b/.github/workflows/ubuntu.yml @@ -0,0 +1,30 @@ +name: Ubuntu build + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Install dependencies + run: | + sudo apt-get install -y bash-completion git meson libinih-dev liblua5.2-dev libglib2.0-dev + + - name: Build + run: | + meson setup build --prefix $HOME/opt/tio + meson compile -C build --verbose + meson install -C build From 68d3b845b2a3b3f21c2809571484f3527f97b566 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 30 Apr 2024 13:38:56 +0200 Subject: [PATCH 315/506] Remove CircleCI Replaced with github workflow CI. --- .circleci/config.yml | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 43e399a..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,27 +0,0 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/2.0/configuration-reference -version: 2.1 - -# Define a job to be invoked later in a workflow. -# See: https://circleci.com/docs/2.0/configuration-reference/#jobs -jobs: - build-tio: - # Specify the execution environment. You can specify an image from Dockerhub or use one of our Convenience Images from CircleCI's Developer Hub. - # See: https://circleci.com/docs/2.0/configuration-reference/#docker-machine-macos-windows-executor - docker: - - image: cimg/base:edge - # Add steps to the job - # See: https://circleci.com/docs/2.0/configuration-reference/#steps - steps: - - checkout - - run: sudo apt-get -qq update - - run: sudo apt-get install -y bash-completion git meson libinih-dev liblua5.2-dev libglib2.0-dev - - run: git clone https://github.com/tio/tio.git - - run: cd tio && meson build --prefix $HOME/test/tio && ninja -C build install - -# Invoke jobs via workflows -# See: https://circleci.com/docs/2.0/configuration-reference/#workflows -workflows: - build-tio-workflow: - jobs: - - build-tio From 65c5a068d8f91235cbb8eeb7f34811869063652c Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 2 May 2024 17:21:10 +0200 Subject: [PATCH 316/506] Replace inih with glib key file parser 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 parsing has been cleaned up, resulting in better and stricter configuration file and option value checks. Compared to old, configuration files now requires any default configurations to be put in a group/section named [default]. Configuration file keywords such as "enable", "disable", "on", "off", "yes", "no", "0", "1" have been retired. Now only "true" and "false" apply to boolean configuration options. This is done to simplify things and avoid any confusion. The pattern option feature has been reworked so now the user can now access the full match string and any matching subexpression using the %mN syntax. For example: [usb devices] pattern = usb([0-9]*) device = /dev/ttyUSB%m1 Then when using tio: $ tio usb12 %m0 = 'usb12' // Full match string %m1 = 12 // First match subexpression Which results in device = /dev/ttyUSB12 --- README.md | 8 +- examples/config/config | 23 +- man/tio.1.in | 4 +- src/alert.c | 38 -- src/alert.h | 2 - src/configfile.c | 847 +++++++++++++++++++++------------------ src/configfile.h | 9 + src/error.c | 6 + src/error.h | 5 + src/log.c | 2 +- src/main.c | 3 + src/meson.build | 3 - src/misc.c | 16 - src/misc.h | 3 - src/options.c | 446 ++++++++++++++++----- src/options.h | 66 +-- src/print.h | 16 +- src/rs485.c | 2 +- src/timestamp.c | 57 --- src/timestamp.h | 3 +- src/tty.c | 113 +++--- src/tty.h | 16 + subprojects/libinih.wrap | 4 - 23 files changed, 969 insertions(+), 723 deletions(-) delete mode 100644 subprojects/libinih.wrap diff --git a/README.md b/README.md index 1a4e0b7..2846449 100644 --- a/README.md +++ b/README.md @@ -385,7 +385,7 @@ Example configuration file: ``` # Defaults -baudrate = 9600 +baudrate = 115200 databits = 8 parity = none stopbits = 1 @@ -393,15 +393,15 @@ color = 10 [rpi3] device = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 -baudrate = 115200 -no-reconnect = enable -log = enable +no-reconnect = true +log = true log-file = rpi3.log line-pulse-duration = DTR=200,RTS=150 color = 11 [svf2] device = /dev/ttyUSB0 +baudrate = 9600 script = expect("login: "); send("root\n"); expect("Password: "); send("root\n") color = 12 diff --git a/examples/config/config b/examples/config/config index 4e0de75..df32b8d 100644 --- a/examples/config/config +++ b/examples/config/config @@ -15,22 +15,22 @@ databits = 8 flow = none stopbits = 1 parity = none -prefix-ctrl-key = t output-delay = 0 output-line-delay = 0 auto-connect = direct -no-reconnect = disable +no-reconnect = false +local-echo = false input-mode = normal output-mode = normal -timestamp = disable -log = disable -log-append = disable -log-strip = disable -local-echo = disable +timestamp = false +log = false +log-append = false +log-strip = false color = bold -rs-485 = disable +rs-485 = false alert = none script-run = always +prefix-ctrl-key = t # Configuration profiles @@ -50,9 +50,9 @@ color = 10 [tincan] baudrate = 9600 device = /dev/serial/by-id/usb-TinCanTools_Flyswatter2_FS20000-if00-port0 -log = enable +log = true log-file = tincan.log -log-strip = enable +log-strip = true color = 11 [usb] @@ -62,7 +62,7 @@ color = 12 [rs-485-device] device = /dev/ttyUSB0 -rs-485 = enable +rs-485 = true rs-485-config = RTS_ON_SEND=1,RTS_AFTER_SEND=1,RTS_DELAY_BEFORE_SEND=60,RTS_DELAY_AFTER_SEND=80,RX_DURING_TX color = 13 @@ -71,4 +71,3 @@ device = /dev/ttyUSB0 color = 14 script = high(DTR); low(RTS); msleep(100); low(DTR); high(RTS); msleep(100); low(RTS) script-run = always - diff --git a/man/tio.1.in b/man/tio.1.in index 7dd7bc2..93840c0 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -623,7 +623,7 @@ regular expressions: .eo [usb device] pattern = usb([0-9]*) -device = /dev/ttyUSB%s +device = /dev/ttyUSB%m1 baudrate = 115200 .ec .fi @@ -635,7 +635,7 @@ Activate the configuration profile by pattern match: $ tio usb12 .TP -Which is equivalent to: +Which becomes equivalent to: $ tio -b 115200 /dev/ttyUSB12 diff --git a/src/alert.c b/src/alert.c index e2f90a4..f9ed3ec 100644 --- a/src/alert.c +++ b/src/alert.c @@ -29,29 +29,6 @@ #include "print.h" #include "options.h" -alert_t alert_option_parse(const char *arg) -{ - alert_t alert = option.alert; // Default - - if (arg != NULL) - { - if (strcmp(arg, "none") == 0) - { - return ALERT_NONE; - } - else if (strcmp(arg, "bell") == 0) - { - return ALERT_BELL; - } - else if (strcmp(arg, "blink") == 0) - { - return ALERT_BLINK; - } - } - - return alert; -} - void blink_background(void) { // Turn on reverse video @@ -109,18 +86,3 @@ void alert_disconnect(void) break; } } - -const char *alert_state_to_string(alert_t state) -{ - switch (state) - { - case ALERT_NONE: - return "none"; - case ALERT_BELL: - return "bell"; - case ALERT_BLINK: - return "blink"; - default: - return "Unknown"; - } -} diff --git a/src/alert.h b/src/alert.h index d7b66f5..91258c9 100644 --- a/src/alert.h +++ b/src/alert.h @@ -29,7 +29,5 @@ typedef enum ALERT_END, } alert_t; -alert_t alert_option_parse(const char *arg); void alert_connect(void); void alert_disconnect(void); -const char *alert_state_to_string(alert_t state); diff --git a/src/configfile.c b/src/configfile.c index 0357b53..ac66e9b 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -32,10 +32,11 @@ #include #include #include +#include #include #include #include -#include +#include #include "options.h" #include "configfile.h" #include "misc.h" @@ -46,505 +47,555 @@ #include "timestamp.h" #include "alert.h" -struct config_t +#define CONFIG_GROUP_NAME_DEFAULT "default" + +struct config_t config = {}; + +static void config_get_string(GKeyFile *key_file, gchar *group, gchar *key, char **dest, char *allowed_string, ...) { - const char *user; + (void)dest; + GError *error = NULL; + bool mismatch = true; - char *path; - char *section_name; - char *match; - - char *target; - char *flow; - char *parity; - char *log_filename; - char *socket; - char *map; - char *script; - char *script_filename; - bool script_run; - char *exclude_devices; - char *exclude_drivers; - char *exclude_tids; -}; - -static struct config_t c; - -static int get_match(const char *input, const char *pattern, char **match) -{ - int ret; - int len = 0; - regex_t re; - regmatch_t m[2]; - char err[128]; - - /* compile a regex with the pattern */ - ret = regcomp(&re, pattern, REG_EXTENDED); - if (ret) + gchar *string = g_key_file_get_string(key_file, group, key, &error); + if (error != NULL) { - regerror(ret, &re, err, sizeof(err)); - tio_error_printf("Regex failure: %s", err); - return ret; - } - - /* try to match on input */ - ret = regexec(&re, input, 2, m, 0); - if (!ret) - { - len = m[1].rm_eo - m[1].rm_so; - } - - regfree(&re); - - if (len) - { - asprintf(match, "%s", &input[m[1].rm_so]); - } - - return len; -} - -static bool read_boolean(const char *value, const char *name) -{ - const char *true_values[] = { "true", "enable", "on", "yes", "1", NULL }; - const char *false_values[] = { "false", "disable", "off", "no", "0", NULL }; - - for (int i = 0; true_values[i] != NULL; i++) - if (strcmp(value, true_values[i]) == 0) - return true; - - for (int i = 0; false_values[i] != NULL; i++) - if (strcmp(value, false_values[i]) == 0) - return false; - - tio_error_printf("Invalid value '%s' for option '%s' in configuration file", - value, name); - exit(EXIT_FAILURE); -} - -static long read_integer(const char *value, const char *name, long min_value, long max_value) -{ - errno = 0; - char *endptr; - long result = strtol(value, &endptr, 10); - - if (errno || endptr == value || *endptr != '\0' || result < min_value || result > max_value) - { - tio_error_printf("Invalid value '%s' for option '%s' in configuration file", - value, name); + if (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND) + { + // Key not found - ignore key + g_error_free(error); + return; + } + tio_error_print("%s: %s", config.path, error->message); + g_error_free(error); exit(EXIT_FAILURE); } - return result; + va_list args; + const char* current_arg = allowed_string; + va_start(args, allowed_string); + + if (current_arg == NULL) + { + mismatch = false; + } + + // Iterate through variable arguments + while (current_arg != NULL) + { + if (strcmp(string, current_arg) == 0) + { + mismatch = false; + break; + } + current_arg = va_arg(args, const char *); + } + + if (mismatch) + { + tio_error_print("%s: Invalid %s value '%s' in %s profile", config.path, key, string, group); + exit(EXIT_FAILURE); + } + + va_end(args); + + *dest = string; } -/** - * data_handler() - walk config file to load parameters matching user input - * - * INIH handler used to get all parameters from a given section - */ -static int data_handler(void *user, const char *section, const char *name, - const char *value) +static void config_get_integer(GKeyFile *key_file, gchar *group, gchar *key, int *dest, int min, int max) { - UNUSED(user); + (void)dest; + GError *error = NULL; - // If section matches current section being parsed - if (!strcmp(section, c.section_name)) + int value = g_key_file_get_integer(key_file, group, key, &error); + if (error != NULL) { - // Set configuration parameter if found - if (!strcmp(name, "device") || !strcmp(name, "tty")) + if (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND) { - asprintf(&c.target, value, c.match); - option.target = c.target; + // Key not found - ignore key + g_error_free(error); + return; } - else if (!strcmp(name, "baudrate")) - { - option.baudrate = read_integer(value, name, 0, LONG_MAX); - } - else if (!strcmp(name, "databits")) - { - option.databits = read_integer(value, name, 5, 8); - } - else if (!strcmp(name, "flow")) - { - asprintf(&c.flow, "%s", value); - option.flow = c.flow; - } - else if (!strcmp(name, "stopbits")) - { - option.stopbits = read_integer(value, name, 1, 2); - } - else if (!strcmp(name, "parity")) - { - asprintf(&c.parity, "%s", value); - option.parity = c.parity; - } - else if (!strcmp(name, "output-delay")) - { - option.output_delay = read_integer(value, name, 0, LONG_MAX); - } - else if (!strcmp(name, "output-line-delay")) - { - option.output_line_delay = read_integer(value, name, 0, LONG_MAX); - } - else if (!strcmp(name, "line-pulse-duration")) - { - line_pulse_duration_option_parse(value); - } - else if (!strcmp(name, "no-reconnect")) - { - option.no_reconnect = read_boolean(value, name); - } - else if (!strcmp(name, "auto-connect")) - { - option.auto_connect = auto_connect_option_parse(value); - } - else if (!strcmp(name, "log")) - { - option.log = read_boolean(value, name); - } - else if (!strcmp(name, "log-file")) - { - asprintf(&c.log_filename, "%s", value); - option.log_filename = c.log_filename; - } - else if (!strcmp(name, "log-append")) - { - option.log_append = read_boolean(value, name); - } - else if (!strcmp(name, "log-strip")) - { - option.log_strip = read_boolean(value, name); - } - else if (!strcmp(name, "local-echo")) - { - option.local_echo = read_boolean(value, name); - } - else if (!strcmp(name, "input-mode")) - { - option.input_mode = input_mode_option_parse(value); - } - else if (!strcmp(name, "output-mode")) - { - option.output_mode = output_mode_option_parse(value); - } - else if (!strcmp(name, "timestamp")) - { - option.timestamp = read_boolean(value, name) ? - TIMESTAMP_24HOUR : TIMESTAMP_NONE; - } - else if (!strcmp(name, "timestamp-format")) - { - option.timestamp = timestamp_option_parse(value); - } - else if (!strcmp(name, "timestamp-timeout")) - { - option.timestamp_timeout = read_integer(value, name, 0, LONG_MAX); - } - else if (!strcmp(name, "map")) - { - asprintf(&c.map, "%s", value); - option.map = c.map; - } - else if (!strcmp(name, "color")) - { - if (!strcmp(value, "list")) - { - // Ignore - return 0; - } - else if (!strcmp(value, "none")) - { - option.color = -1; // No color - return 0; - } - else if (!strcmp(value, "bold")) - { - option.color = 256; // Bold - return 0; - } + tio_error_print("%s: %s", config.path, error->message); + g_error_free(error); + exit(EXIT_FAILURE); + } - option.color = atoi(value); - if ((option.color < 0) || (option.color > 255)) - { - option.color = -1; // No color - } - } - else if (!strcmp(name, "socket")) + if ((value < min) || (value > max)) + { + tio_error_print("%s: Invalid %s value '%d' in %s profile", config.path, key, value, group); + exit(EXIT_FAILURE); + } + + *dest = value; +} + +static void config_get_bool(GKeyFile *key_file, gchar *group, gchar *key, bool *dest) +{ + (void)dest; + GError *error = NULL; + + bool value = g_key_file_get_boolean(key_file, group, key, &error); + if (error != NULL) + { + if (error->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND) { - asprintf(&c.socket, "%s", value); - option.socket = c.socket; + // Key not found - ignore key + g_error_free(error); + return; } - else if (!strcmp(name, "prefix-ctrl-key")) + tio_error_print("%s: %s", config.path, error->message); + g_error_free(error); + exit(EXIT_FAILURE); + } + + *dest = value; +} + +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); + config_get_integer(key_file, group, "databits", &option.databits, 5, 8); + config_get_string(key_file, group, "flow", &string, "none", "hard", "soft", NULL); + if (string != NULL) + { + option_parse_flow(string, &option.flow); + g_free((void *)string); + string = NULL; + } + config_get_integer(key_file, group, "stopbits", &option.stopbits, 1, 2); + config_get_string(key_file, group, "parity", &string, "odd", "even", "none", "mark", "space", NULL); + if (string != NULL) + { + option_parse_parity(string, &option.parity); + g_free((void *)string); + string = NULL; + } + + config_get_integer(key_file, group, "output-delay", &option.output_delay, 0, INT_MAX); + config_get_integer(key_file, group, "output-line-delay", &option.output_line_delay, 0, INT_MAX); + config_get_string(key_file, group, "line-pulse-duration", &string, NULL); + if (string != NULL) + { + option_parse_line_pulse_duration(string); + g_free((void *)string); + string = NULL; + } + config_get_string(key_file, group, "auto-connect", &string, "new", "latest", "direct", NULL); + if (string != NULL) + { + option_parse_auto_connect(string, &option.auto_connect); + g_free((void *)string); + string = NULL; + } + config_get_string(key_file, group, "exclude-devices", &option.exclude_devices, NULL); + 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_string(key_file, group, "input-mode", &string, "normal", "hex", "line", NULL); + if (string != NULL) + { + option_parse_input_mode(string, &option.input_mode); + g_free((void *)string); + string = NULL; + } + config_get_string(key_file, group, "output-mode", &string, NULL); + if (string != NULL) + { + option_parse_output_mode(string, &option.output_mode); + g_free((void *)string); + string = NULL; + } + config_get_bool(key_file, group, "timestamp", &boolean); + if (boolean == true) + { + option.timestamp = TIMESTAMP_24HOUR; + } + config_get_string(key_file, group, "timestamp-format", &string, "24hour", "24hour-start", "24hour-delta", "iso8601", 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); + 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, "color", &string, NULL); + if (string != NULL) + { + if (strcmp(string, "list") == 0) { - if (!strcmp(value, "none")) - { - option.prefix_enabled = false; - } - else if (ctrl_key_code(value[0]) > 0) - { - option.prefix_code = ctrl_key_code(value[0]); - option.prefix_key = value[0]; - } + // Ignore } - else if (!strcmp(name, "rs-485")) + else if (strcmp(string, "none") == 0) { - option.rs485 = read_boolean(value, name); + option.color = -1; // No color } - else if (!strcmp(name, "rs-485-config")) + else if (strcmp(string, "bold") == 0) { - rs485_parse_config(value); - } - else if (!strcmp(name, "alert")) - { - option.alert = alert_option_parse(value); - } - else if (!strcmp(name, "mute")) - { - option.mute = read_boolean(value, name); - } - else if (!strcmp(name, "pattern")) - { - // Do nothing - } - else if (!strcmp(name, "script")) - { - asprintf(&c.script, "%s", value); - option.script = c.script; - } - else if (!strcmp(name, "script-file")) - { - asprintf(&c.script_filename, "%s", value); - option.script_filename = c.script_filename; - } - else if (!strcmp(name, "script-run")) - { - option.script_run = script_run_option_parse(value); - } - else if (!strcmp(name, "exclude-devices")) - { - c.exclude_devices = strdup(value); - option.exclude_devices = c.exclude_devices; - } - else if (!strcmp(name, "exclude-drivers")) - { - c.exclude_drivers = strdup(value); - option.exclude_drivers = c.exclude_drivers; - } - else if (!strcmp(name, "exclude-tids")) - { - c.exclude_tids = strdup(value); - option.exclude_tids = c.exclude_tids; + option.color = 256; // Bold } else { - tio_warning_printf("Unknown option '%s' in configuration file, ignored", name); + option.color = atoi(string); + if ((option.color < 0) || (option.color > 255)) + { + tio_error_print("%s: Invalid color value in %s profile", config.path, group); + exit(EXIT_FAILURE); + } } + g_free((void *)string); + string = NULL; + } + config_get_string(key_file, group, "socket", &option.socket, NULL); + config_get_bool(key_file, group, "rs-485", &option.rs485); + config_get_string(key_file, group, "rs-385-config", &string, NULL); + if (string != NULL) + { + rs485_parse_config(string); + g_free((void *)string); + string = NULL; + } + config_get_string(key_file, group, "alert", &string, "bell", "blink", "none", NULL); + if (string != NULL) + { + option_parse_alert(string, &option.alert); + g_free((void *)string); + string = NULL; + } + config_get_bool(key_file, group, "mute", &option.mute); + config_get_string(key_file, group, "script", &option.script, NULL); + config_get_string(key_file, group, "script-file", &option.script_filename, NULL); + config_get_string(key_file, group, "script-run", &string, NULL); + if (string != NULL) + { + option_parse_script_run(string, &option.script_run); + g_free((void *)string); + string = NULL; + } + config_get_string(key_file, group, "prefix-ctrl-key", &string, NULL); + if (string != NULL) + { + if (strcmp(string, "none") == 0) + { + option.prefix_enabled = false; + } + else if (strlen(string) >= 2) + { + tio_error_print("%s: Invalid prefix-ctrl-key value in %s profile", config.path, group); + exit(EXIT_FAILURE); + } + else if (ctrl_key_code(string[0]) > 0) + { + option.prefix_enabled = true; + option.prefix_code = ctrl_key_code(string[0]); + option.prefix_key = string[0]; + } + else + { + tio_error_print("%s: Invalid prefix-ctrl-key value in %s profile", config.path, group); + exit(EXIT_FAILURE); + } + g_free((void *)string); + string = NULL; } - - return 0; } -/** - * section_pattern_search_handler() - walk config file to find section matching user input - * - * INIH handler used to resolve the section matching the user's input. - * This will look for the pattern element of each section and try to match it - * with the user input. - */ -static int section_pattern_search_handler(void *user, const char *section, const char *varname, - const char *varval) -{ - UNUSED(user); - - if (strcmp(varname, "pattern")) - return 0; - - if (!strcmp(varval, c.user)) - { - /* pattern matches as plain text */ - asprintf(&c.section_name, "%s", section); - } - else if (get_match(c.user, varval, &c.match) > 0) - { - /* pattern matches as regex */ - asprintf(&c.section_name, "%s", section); - } - - return 0; -} - -/** - * section_pattern_search_handler() - walk config file to find section matching user input - * - * INIH handler used to resolve the section matching the user's input. - * This will try to match the user input against a section with the name of the user input. - */ -static int section_name_search_handler(void *user, const char *section, const char *varname, - const char *varval) -{ - UNUSED(user); - UNUSED(varname); - UNUSED(varval); - - if (!strcmp(section, c.user)) - { - /* section name matches as plain text */ - asprintf(&c.section_name, "%s", section); - } - - return 0; -} - -static int section_name_print_handler(void *user, const char *section, const char *varname, - const char *varval) -{ - UNUSED(user); - UNUSED(varname); - UNUSED(varval); - - static char *section_previous = ""; - - if (strcmp(section, section_previous) != 0) - { - printf("%s ", section); - section_previous = strdup(section); - } - - return 0; -} - -static int resolve_config_file(void) +static int config_file_resolve(void) { char *xdg = getenv("XDG_CONFIG_HOME"); if (xdg) { - if (asprintf(&c.path, "%s/tio/config", xdg) != -1) + if (asprintf(&config.path, "%s/tio/config", xdg) != -1) { - if (access(c.path, F_OK) == 0) + if (access(config.path, F_OK) == 0) { return 0; } - free(c.path); + free(config.path); } } char *home = getenv("HOME"); if (home) { - if (asprintf(&c.path, "%s/.config/tio/config", home) != -1) + if (asprintf(&config.path, "%s/.config/tio/config", home) != -1) { - if (access(c.path, F_OK) == 0) + if (access(config.path, F_OK) == 0) { return 0; } - free(c.path); + free(config.path); } - if (asprintf(&c.path, "%s/.tioconfig", home) != -1) + if (asprintf(&config.path, "%s/.tioconfig", home) != -1) { - if (access(c.path, F_OK) == 0) + if (access(config.path, F_OK) == 0) { return 0; } - free(c.path); + free(config.path); } } - c.path = NULL; + config.path = NULL; return -EINVAL; } void config_file_show_profiles(void) { - memset(&c, 0, sizeof(struct config_t)); + memset(&config, 0, sizeof(struct config_t)); // Find config file - if (resolve_config_file() != 0) + if (config_file_resolve() != 0) { // None found - stop parsing return; } - ini_parse(c.path, section_name_print_handler, NULL); + GKeyFile *keyfile; + GError *error = NULL; + + keyfile = g_key_file_new(); + + if (!g_key_file_load_from_file(keyfile, config.path, G_KEY_FILE_NONE, &error)) + { + tio_error_print("Failure loading file: %s", error->message); + g_error_free(error); + return; + } + + // Get all group names + gsize num_groups; + gchar **group = g_key_file_get_groups(keyfile, &num_groups); + + for (gsize i = 0; i < num_groups; i++) + { + // Skip default group + if (strcmp(group[i], CONFIG_GROUP_NAME_DEFAULT) == 0) + { + continue; + } + } + + g_strfreev(group); + g_key_file_free(keyfile); +} + +static void replace_substring(char *str, const char *substr, const char *replacement) +{ + char *pos = strstr(str, substr); + if (pos != NULL) + { + int substrLen = strlen(substr); + int replacementLen = strlen(replacement); + memmove(pos + replacementLen, pos + substrLen, strlen(pos + substrLen) + 1); + memcpy(pos, replacement, replacementLen); + } +} + +static char *match_and_replace(const char *str, const char *pattern, char *device) +{ + char replacement_str[PATH_MAX] = {}; + char m_key[13] = {}; + regex_t regex; + + assert(str != NULL); + assert(pattern != NULL); + assert(device != NULL); + + char *string = malloc(strlen(device) + PATH_MAX); + if (string == NULL) + { + 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 + * return the new formed device string. + * + * Note: %m0 = Full match expression. + * %m1 = First subexpression + * %m2 = Second subexpression + * %m3 = etc.. + */ + + if (regcomp(®ex, pattern, REG_EXTENDED) != 0) + { + // Failure to compile regular expression + return NULL; + } + + regmatch_t matches[regex.re_nsub + 1]; + int status = regexec(®ex, str, regex.re_nsub + 1, matches, REG_EXTENDED); + if (status == 0) + { + tio_debug_printf("Full match: "); + int j = 0; + for (int i = matches[0].rm_so; i < matches[0].rm_eo; i++) + { + tio_debug_printf_raw("%c", str[i]); + replacement_str[j++] = str[i]; + } + replacement_str[j] = '\0'; + replace_substring(string, "%m0", replacement_str); + tio_debug_printf_raw("\n"); + + for (int i = 1; i < ((int)regex.re_nsub + 1) && matches[i].rm_so != -1; i++) + { + tio_debug_printf("Subexpression %d match: ", i); + int k = 0; + for (int j = matches[i].rm_so; j < matches[i].rm_eo; j++) + { + tio_debug_printf_raw("%c", str[j]); + replacement_str[k++] = str[j]; + } + replacement_str[k] = '\0'; + sprintf(m_key, "%%m%d", i); + replace_substring(string, m_key, replacement_str); + tio_debug_printf_raw("\n"); + } + } + else if (status == REG_NOMATCH) + { + goto error; + } + else + { + char error_message[100]; + regerror(status, ®ex, error_message, sizeof(error_message)); + tio_error_print("Regex match failed: %s", error_message); + goto error; + } + + regfree(®ex); + return string; + +error: + regfree(®ex); + return NULL; } void config_file_parse(void) { - int ret; - - memset(&c, 0, sizeof(struct config_t)); - // Find config file - if (resolve_config_file() != 0) + if (config_file_resolve() != 0) { // None found - stop parsing return; } - // Set user input which may be tty device or profile or tid - c.user = option.target; - - if (!c.user) + if (option.target == NULL) { return; } - // Parse default (unnamed) settings - asprintf(&c.section_name, "%s", ""); - ret = ini_parse(c.path, data_handler, NULL); - if (ret < 0) + GKeyFile *keyfile = g_key_file_new(); + GError *error = NULL; + + if (g_key_file_load_from_file(keyfile, config.path, G_KEY_FILE_NONE, &error) == false) { - tio_error_printf("Unable to parse configuration file (%d)", ret); + tio_error_print("Failure loading file %s: %s", config.path, error->message); + g_error_free(error); exit(EXIT_FAILURE); } - free(c.section_name); - c.section_name = NULL; - // Find matching section - ret = ini_parse(c.path, section_pattern_search_handler, NULL); - if (!c.section_name) + // Parse default group/section + if (g_key_file_has_group(keyfile, CONFIG_GROUP_NAME_DEFAULT)) { - ret = ini_parse(c.path, section_name_search_handler, NULL); - if (!c.section_name) + config_parse_keys(keyfile, CONFIG_GROUP_NAME_DEFAULT); + } + + // Parse target + if (g_key_file_has_group(keyfile, option.target)) + { + config.active_group = strdup(option.target); + config_parse_keys(keyfile, option.target); + } + else + { + // Find group by pattern + gsize num_groups; + gchar **group = g_key_file_get_groups(keyfile, &num_groups); + + for (gsize i = 0; i < num_groups; i++) { - tio_debug_printf("Unable to match user input to configuration section (%d)", ret); - return; + // Skip default group + if (strcmp(group[i], CONFIG_GROUP_NAME_DEFAULT) == 0) + { + continue; + } + + // Lookup 'pattern' key + GError *error = NULL; + gchar *pattern = g_key_file_get_string(keyfile, group[i], "pattern", &error); + if (error != NULL) + { + g_error_free(error); + continue; + } + + // Lookup 'device' key + gchar *device = g_key_file_get_string(keyfile, group[i], "device", &error); + if (error != NULL) + { + g_error_free(error); + continue; + } + + // Match pattern against target and replace any sub expression + // matches (%mN) in device string and return resulting string + // representing the new pattern based string. + config.device = match_and_replace(option.target, pattern, device); + if (config.device != NULL) + { + // Match found - save device + char *device = strdup(config.device); + + // Parse found group + config_parse_keys(keyfile, group[i]); + + // Update configuration + config.active_group = strdup(group[i]); + config.device = device; + + break; + } } + + g_strfreev(group); } - // Parse settings of found section (profile) - ret = ini_parse(c.path, data_handler, NULL); - if (ret < 0) - { - tio_error_printf("Unable to parse configuration file (%d)", ret); - exit(EXIT_FAILURE); - } + g_key_file_free(keyfile); atexit(&config_exit); } void config_exit(void) { - free(c.target); - free(c.flow); - free(c.parity); - free(c.log_filename); - free(c.map); - - free(c.match); - free(c.section_name); - free(c.path); + free(config.active_group); + free(config.path); + free(config.device); } void config_file_print(void) { - if (c.path != NULL) + if (config.path != NULL) { - tio_printf(" Active configuration file: %s", c.path); - if (c.section_name != NULL) + tio_printf(" Active configuration file: %s", config.path); + if (config.active_group != NULL) { - tio_printf(" Active configuration profile: %s", c.section_name); + tio_printf(" Active configuration profile: %s", config.active_group); } } } diff --git a/src/configfile.h b/src/configfile.h index 554df3c..7b7a3a9 100644 --- a/src/configfile.h +++ b/src/configfile.h @@ -22,6 +22,15 @@ #pragma once +struct config_t +{ + char *path; + char *active_group; + char *device; +}; + +extern struct config_t config; + void config_file_print(void); void config_file_parse(void); void config_exit(void); diff --git a/src/error.c b/src/error.c index 9cff900..2973fbb 100644 --- a/src/error.c +++ b/src/error.c @@ -35,6 +35,12 @@ static char error[2][1000]; static bool in_session = false; +bool error_normal = true; + +void switch_error_output_mode(void) +{ + error_normal = false; +} void error_enter_session_mode(void) { diff --git a/src/error.h b/src/error.h index ed81ba3..9a257fc 100644 --- a/src/error.h +++ b/src/error.h @@ -21,6 +21,10 @@ #pragma once +#include + +extern bool error_normal; + #define TIO_SUCCESS 0 #define TIO_ERROR 1 @@ -28,3 +32,4 @@ void tio_error_printf(const char *format, ...); void tio_error_printf_silent(const char *format, ...); void error_exit(void); void error_enter_session_mode(void); +void switch_error_output_mode(void); diff --git a/src/log.c b/src/log.c index 0a2df34..c593213 100644 --- a/src/log.c +++ b/src/log.c @@ -75,7 +75,7 @@ int log_open(const char *filename) // If using 'new' or 'latest' autoconnect strategy we simply use strategy // name to name autogenerated log name as device names may vary asprintf(&automatic_filename, "tio_%s_%s.log", - auto_connect_state_to_string(option.auto_connect), + option_auto_connect_state_to_string(option.auto_connect), date_time()); } diff --git a/src/main.c b/src/main.c index 269995e..304258e 100644 --- a/src/main.c +++ b/src/main.c @@ -71,6 +71,9 @@ int main(int argc, char *argv[]) interactive_mode = false; } + /* Switch error output format */ + switch_error_output_mode(); + /* Configure output terminal */ if (isatty(fileno(stdout))) { diff --git a/src/meson.build b/src/meson.build index 297777e..3b3790b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -37,9 +37,6 @@ endif tio_dep = [ dependency('threads', required: true), dependency('glib-2.0', required: true), - dependency('inih', required: true, - fallback : ['libinih', 'inih_dep'], - default_options: ['default_library=static', 'distro_install=false']), lua_dep ] diff --git a/src/misc.c b/src/misc.c index 091675a..630671e 100644 --- a/src/misc.c +++ b/src/misc.c @@ -54,22 +54,6 @@ void delay(long ms) nanosleep(&ts, NULL); } -long string_to_long(char *string) -{ - long result; - char *end_token; - - errno = 0; - result = strtol(string, &end_token, 10); - if ((errno != 0) || (*end_token != 0)) - { - printf("Error: Invalid digit\n"); - exit(EXIT_FAILURE); - } - - return result; -} - int ctrl_key_code(unsigned char key) { if ((key >= 'a') && (key <= 'z')) diff --git a/src/misc.h b/src/misc.h index 25be622..edbb511 100644 --- a/src/misc.h +++ b/src/misc.h @@ -27,10 +27,7 @@ #define UNUSED(expr) do { (void)(expr); } while (0) void delay(long ms); -long string_to_long(char *string); int ctrl_key_code(unsigned char key); -void alert_connect(void); -void alert_disconnect(void); bool regex_match(const char *string, const char *pattern); unsigned long djb2_hash(const unsigned char *str); char *base62_encode(unsigned long num); diff --git a/src/options.c b/src/options.c index 65082b6..15bacb9 100644 --- a/src/options.c +++ b/src/options.c @@ -20,6 +20,7 @@ */ #include "config.h" +#include #include #include #include @@ -75,9 +76,9 @@ struct option_t option = .target = "", .baudrate = 115200, .databits = 8, - .flow = "none", + .flow = FLOW_NONE, .stopbits = 1, - .parity = "none", + .parity = PARITY_NONE, .output_delay = 0, .output_line_delay = 0, .dtr_pulse_duration = 100, @@ -121,7 +122,7 @@ struct option_t option = .vt100 = false, }; -void print_help(char *argv[]) +void option_print_help(char *argv[]) { UNUSED(argv); @@ -173,7 +174,270 @@ void print_help(char *argv[]) printf("See the man page for more details.\n"); } -const char *auto_connect_state_to_string(auto_connect_t strategy) +int option_string_to_integer(const char *string, int *value, const char *desc, int min, int max) +{ + int val; + char *end_token; + + errno = 0; + val = strtol(string, &end_token, 10); + if ((errno != 0) || (*end_token != 0)) + { + if (desc != NULL) + { + tio_error_print("Invalid %s '%s'", desc, string); + exit(EXIT_FAILURE); + } + else + { + tio_error_print("Invalid digit '%s'", string); + exit(EXIT_FAILURE); + } + return -1; + } + else + { + if ((val < min) || (val > max)) + { + if (desc != NULL) + { + tio_error_print("Invalid %s '%s'", desc, string); + exit(EXIT_FAILURE); + } + else + { + tio_error_print("Invalid digit '%s'", string); + exit(EXIT_FAILURE); + } + return -1; + } + else + { + *value = val; + } + } + + return 0; +} + +void option_parse_flow(const char *arg, flow_t *flow) +{ + assert(arg != NULL); + + /* Parse flow control */ + if (strcmp("hard", arg) == 0) + { + *flow = FLOW_HARD; + } + else if (strcmp("soft", arg) == 0) + { + *flow = FLOW_SOFT; + } + else if (strcmp("none", arg) == 0) + { + *flow = FLOW_NONE; + } + else + { + tio_error_print("Invalid flow control '%s'", arg); + exit(EXIT_FAILURE); + } +} + +const char *option_flow_to_string(flow_t flow) +{ + switch (flow) + { + case FLOW_NONE: + return "none"; + case FLOW_HARD: + return "hard"; + case FLOW_SOFT: + return "soft"; + default: + return "unknown"; + } +} + +void option_parse_parity(const char *arg, parity_t *parity) +{ + assert(arg != NULL); + + if (strcmp("none", arg) == 0) + { + *parity = PARITY_NONE; + } + else if (strcmp("odd", arg) == 0) + { + *parity = PARITY_ODD; + } + else if (strcmp("even", arg) == 0) + { + *parity = PARITY_EVEN; + } + else if (strcmp("mark", arg) == 0) + { + *parity = PARITY_MARK; + } + else if (strcmp("space", arg) == 0) + { + *parity = PARITY_SPACE; + } + else + { + tio_error_print("Invalid parity '%s'", arg); + exit(EXIT_FAILURE); + } +} + +const char *option_parity_to_string(parity_t parity) +{ + switch (parity) + { + case PARITY_NONE: + return "none"; + case PARITY_ODD: + return "odd"; + case PARITY_EVEN: + return "even"; + case PARITY_MARK: + return "mark"; + case PARITY_SPACE: + return "space"; + default: + return "unknown"; + } +} + +void option_parse_color(const char *arg, int *color) +{ + int value; + + assert(arg != NULL); + + if (strcmp(optarg, "list") == 0) + { + // Print available color codes + printf("Available color codes:\n"); + for (int i=0; i<=255; i++) + { + printf(" \e[1;38;5;%dmThis is color code %d\e[0m\n", i, i); + } + exit(EXIT_SUCCESS); + } + else if (strcmp(arg, "none") == 0) + { + *color = -1; // No color + } + else if (strcmp(arg, "bold") == 0) + { + *color = 256; // Bold + } + else + { + if (option_string_to_integer(arg, &value, "color code", 0, 255) == 0) + { + *color = value; + } + } +} + +void option_parse_alert(const char *arg, alert_t *alert) +{ + assert(arg != NULL); + + if (strcmp(arg, "none") == 0) + { + *alert = ALERT_NONE; + } + else if (strcmp(arg, "bell") == 0) + { + *alert = ALERT_BELL; + } + else if (strcmp(arg, "blink") == 0) + { + *alert = ALERT_BLINK; + } + else + { + tio_error_print("Invalid alert '%s'", arg); + exit(EXIT_FAILURE); + } +} + +const char* option_timestamp_format_to_string(timestamp_t timestamp) +{ + switch (timestamp) + { + case TIMESTAMP_NONE: + return "none"; + break; + + case TIMESTAMP_24HOUR: + return "24hour"; + break; + + case TIMESTAMP_24HOUR_START: + return "24hour-start"; + break; + + case TIMESTAMP_24HOUR_DELTA: + return "24hour-delta"; + break; + + case TIMESTAMP_ISO8601: + return "iso8601"; + break; + + default: + return "unknown"; + break; + } +} + +void option_parse_timestamp(const char *arg, timestamp_t *timestamp) +{ + assert(arg != NULL); + + if (strcmp(arg, "24hour") == 0) + { + *timestamp = TIMESTAMP_24HOUR; + } + else if (strcmp(arg, "24hour-start") == 0) + { + *timestamp = TIMESTAMP_24HOUR_START; + } + else if (strcmp(arg, "24hour-delta") == 0) + { + *timestamp = TIMESTAMP_24HOUR_DELTA; + } + else if (strcmp(arg, "iso8601") == 0) + { + *timestamp = TIMESTAMP_ISO8601; + } + else + { + tio_error_print("Invalid timestamp '%s'", arg); + exit(EXIT_FAILURE); + } +} + +const char *option_alert_state_to_string(alert_t state) +{ + switch (state) + { + case ALERT_NONE: + return "none"; + case ALERT_BELL: + return "bell"; + case ALERT_BLINK: + return "blink"; + default: + return "unknown"; + } +} + +const char *option_auto_connect_state_to_string(auto_connect_t strategy) { switch (strategy) { @@ -184,39 +448,44 @@ const char *auto_connect_state_to_string(auto_connect_t strategy) case AUTO_CONNECT_LATEST: return "latest"; default: - return "Unknown"; + return "unknown"; } } -auto_connect_t auto_connect_option_parse(const char *arg) +void option_parse_auto_connect(const char *arg, auto_connect_t *auto_connect) { - auto_connect_t auto_connect = option.auto_connect; // Default + assert(arg != NULL); if (arg != NULL) { if (strcmp(arg, "direct") == 0) { - return AUTO_CONNECT_DIRECT; + *auto_connect = AUTO_CONNECT_DIRECT; } else if (strcmp(arg, "new") == 0) { - return AUTO_CONNECT_NEW; + *auto_connect = AUTO_CONNECT_NEW; } else if (strcmp(arg, "latest") == 0) { - return AUTO_CONNECT_LATEST; + *auto_connect = AUTO_CONNECT_LATEST; + } + else + { + tio_error_print("Invalid auto-connect strategy '%s'", arg); + exit(EXIT_FAILURE); } } - - return auto_connect; } -void line_pulse_duration_option_parse(const char *arg) +void option_parse_line_pulse_duration(const char *arg) { bool token_found = true; char *token = NULL; char *buffer = strdup(arg); + assert(arg != NULL); + while (token_found == true) { if (token == NULL) @@ -259,6 +528,11 @@ void line_pulse_duration_option_parse(const char *arg) { option.ri_pulse_duration = value; } + else + { + tio_error_print("Invalid line '%s'", keyname); + exit(EXIT_FAILURE); + } } else { @@ -274,7 +548,7 @@ void line_pulse_duration_option_parse(const char *arg) } // Function to parse the input string -int parse_hexN_string(const char *input_string) +int option_parse_hexN_string(const char *input_string) { regmatch_t match[2]; // One for entire match, one for the optional N int n_value = 0; @@ -285,7 +559,7 @@ int parse_hexN_string(const char *input_string) ret = regcomp(®ex, "^hex([0-9]+)?$", REG_EXTENDED); if (ret) { - tio_error_printf("Could not compile regex\n"); + tio_error_print("Could not compile regex"); exit(EXIT_FAILURE); } @@ -319,7 +593,7 @@ int parse_hexN_string(const char *input_string) { char msgbuf[100]; regerror(ret, ®ex, msgbuf, sizeof(msgbuf)); - tio_error_printf("Regex match failed: %s\n", msgbuf); + tio_error_print("Regex match failed: %s", msgbuf); exit(EXIT_FAILURE); } @@ -328,48 +602,52 @@ int parse_hexN_string(const char *input_string) return n_value; } -input_mode_t input_mode_option_parse(const char *arg) +void option_parse_input_mode(const char *arg, input_mode_t *mode) { + assert(arg != NULL); + if (strcmp("normal", arg) == 0) { - return INPUT_MODE_NORMAL; + *mode = INPUT_MODE_NORMAL; } else if (strcmp("hex", arg) == 0) { - return INPUT_MODE_HEX; + *mode = INPUT_MODE_HEX; } else if (strcmp("line", arg) == 0) { - return INPUT_MODE_LINE; + *mode = INPUT_MODE_LINE; } else { - tio_error_printf("Invalid input mode option"); + tio_error_print("Invalid input mode '%s'", arg); exit(EXIT_FAILURE); } } -output_mode_t output_mode_option_parse(const char *arg) +void option_parse_output_mode(const char *arg, output_mode_t *mode) { int n = 0; + assert(arg != NULL); + if (strcmp("normal", arg) == 0) { - return OUTPUT_MODE_NORMAL; + *mode = OUTPUT_MODE_NORMAL; } - else if ((n = parse_hexN_string(arg)) != -1) + else if ((n = option_parse_hexN_string(arg)) != -1) { option.hex_n_value = n; - return OUTPUT_MODE_HEX; + *mode = OUTPUT_MODE_HEX; } else { - tio_error_printf("Invalid output mode option"); + tio_error_print("Invalid output mode '%s'", arg); exit(EXIT_FAILURE); } } -const char *input_mode_by_string(input_mode_t mode) +const char *option_input_mode_to_string(input_mode_t mode) { switch (mode) { @@ -386,7 +664,7 @@ const char *input_mode_by_string(input_mode_t mode) return NULL; } -const char *output_mode_by_string(output_mode_t mode) +const char *option_output_mode_to_string(output_mode_t mode) { switch (mode) { @@ -401,23 +679,25 @@ const char *output_mode_by_string(output_mode_t mode) return NULL; } -script_run_t script_run_option_parse(const char *arg) +void option_parse_script_run(const char *arg, script_run_t *script_run) { + assert(arg != NULL); + if (strcmp("once", arg) == 0) { - return SCRIPT_RUN_ONCE; + *script_run = SCRIPT_RUN_ONCE; } else if (strcmp("always", arg) == 0) { - return SCRIPT_RUN_ALWAYS; + *script_run = SCRIPT_RUN_ALWAYS; } else if (strcmp("never", arg) == 0) { - return SCRIPT_RUN_NEVER; + *script_run = SCRIPT_RUN_NEVER; } else { - tio_error_printf("Invalid script run option"); + tio_error_print("Invalid script run option '%s'", arg); exit(EXIT_FAILURE); } } @@ -427,25 +707,25 @@ void options_print() tio_printf(" Device: %s", device_name); tio_printf(" Baudrate: %u", option.baudrate); tio_printf(" Databits: %d", option.databits); - tio_printf(" Flow: %s", option.flow); + tio_printf(" Flow: %s", option_flow_to_string(option.flow)); tio_printf(" Stopbits: %d", option.stopbits); - tio_printf(" Parity: %s", option.parity); - tio_printf(" Local echo: %s", option.local_echo ? "enabled" : "disabled"); - tio_printf(" Timestamp: %s", timestamp_state_to_string(option.timestamp)); + tio_printf(" Parity: %s", option_parity_to_string(option.parity)); + tio_printf(" Local echo: %s", option.local_echo ? "true" : "false"); + tio_printf(" Timestamp: %s", option_timestamp_format_to_string(option.timestamp)); tio_printf(" Timestamp timeout: %u", option.timestamp_timeout); tio_printf(" Output delay: %d", option.output_delay); tio_printf(" Output line delay: %d", option.output_line_delay); - tio_printf(" Automatic connect strategy: %s", auto_connect_state_to_string(option.auto_connect)); - tio_printf(" Automatic reconnect: %s", option.no_reconnect ? "disabled" : "enabled"); + tio_printf(" Automatic connect strategy: %s", option_auto_connect_state_to_string(option.auto_connect)); + tio_printf(" Automatic reconnect: %s", option.no_reconnect ? "true" : "false"); tio_printf(" Pulse duration: DTR=%d RTS=%d CTS=%d DSR=%d DCD=%d RI=%d", option.dtr_pulse_duration, option.rts_pulse_duration, option.cts_pulse_duration, option.dsr_pulse_duration, option.dcd_pulse_duration, option.ri_pulse_duration); - tio_printf(" Input mode: %s", input_mode_by_string(option.input_mode)); - tio_printf(" Output mode: %s", output_mode_by_string(option.output_mode)); - tio_printf(" Alert: %s", alert_state_to_string(option.alert)); + 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); @@ -457,8 +737,8 @@ void options_print() { tio_printf(" Log file directory: %s", option.log_directory); } - tio_printf(" Log append: %s", option.log_append ? "enabled" : "disabled"); - tio_printf(" Log strip: %s", option.log_strip ? "enabled" : "disabled"); + tio_printf(" Log append: %s", option.log_append ? "true" : "false"); + tio_printf(" Log strip: %s", option.log_strip ? "true" : "false"); } if (option.socket) { @@ -477,7 +757,7 @@ void options_parse(int argc, char *argv[]) if (argc == 1) { - print_help(argv); + option_print_help(argv); exit(EXIT_SUCCESS); } @@ -563,39 +843,39 @@ void options_parse(int argc, char *argv[]) break; case 'b': - option.baudrate = string_to_long(optarg); + option_string_to_integer(optarg, &option.baudrate, "baudrate", 0, INT_MAX); break; case 'd': - option.databits = string_to_long(optarg); + option_string_to_integer(optarg, &option.databits, "databits", 5, 8); break; case 'f': - option.flow = optarg; + option_parse_flow(optarg, &option.flow); break; case 's': - option.stopbits = string_to_long(optarg); + option_string_to_integer(optarg, &option.stopbits, "stopbits", 1, 2); break; case 'p': - option.parity = optarg; + option_parse_parity(optarg, &option.parity); break; case 'o': - option.output_delay = string_to_long(optarg); + option_string_to_integer(optarg, &option.output_delay, "output delay", 0, INT_MAX); break; case 'O': - option.output_line_delay = string_to_long(optarg); + option_string_to_integer(optarg, &option.output_line_delay, "output line delay", 0, INT_MAX); break; case OPT_LINE_PULSE_DURATION: - line_pulse_duration_option_parse(optarg); + option_parse_line_pulse_duration(optarg); break; case 'a': - option.auto_connect = auto_connect_option_parse(optarg); + option_parse_auto_connect(optarg, &option.auto_connect); break; case OPT_EXCLUDE_DEVICES: @@ -623,11 +903,11 @@ void options_parse(int argc, char *argv[]) break; case OPT_TIMESTAMP_FORMAT: - option.timestamp = timestamp_option_parse(optarg); + option_parse_timestamp(optarg, &option.timestamp); break; case OPT_TIMESTAMP_TIMEOUT: - option.timestamp_timeout = string_to_long(optarg); + option_string_to_integer(optarg, &option.timestamp_timeout, "timestamp timeout", 0, INT_MAX); break; case 'L': @@ -664,41 +944,15 @@ void options_parse(int argc, char *argv[]) break; case 'c': - if (!strcmp(optarg, "list")) - { - // Print available color codes - printf("Available color codes:\n"); - for (int i=0; i<=255; i++) - { - printf(" \e[1;38;5;%dmThis is color code %d\e[0m\n", i, i); - } - exit(EXIT_SUCCESS); - } - else if (!strcmp(optarg, "none")) - { - option.color = -1; // No color - break; - } - else if (!strcmp(optarg, "bold")) - { - option.color = 256; // Bold - break; - } - - option.color = string_to_long(optarg); - if ((option.color < 0) || (option.color > 255)) - { - tio_error_printf("Invalid color code"); - exit(EXIT_FAILURE); - } + option_parse_color(optarg, &option.color); break; case OPT_INPUT_MODE: - option.input_mode = input_mode_option_parse(optarg); + option_parse_input_mode(optarg, &option.input_mode); break; case OPT_OUTPUT_MODE: - option.output_mode = output_mode_option_parse(optarg); + option_parse_output_mode(optarg, &option.output_mode); break; case OPT_RS485: @@ -710,7 +964,7 @@ void options_parse(int argc, char *argv[]) break; case OPT_ALERT: - option.alert = alert_option_parse(optarg); + option_parse_alert(optarg, &option.alert); break; case OPT_MUTE: @@ -726,7 +980,7 @@ void options_parse(int argc, char *argv[]) break; case OPT_SCRIPT_RUN: - option.script_run = script_run_option_parse(optarg); + option_parse_script_run(optarg, &option.script_run); break; case 'v': @@ -735,7 +989,7 @@ void options_parse(int argc, char *argv[]) break; case 'h': - print_help(argv); + option_print_help(argv); exit(EXIT_SUCCESS); break; @@ -775,7 +1029,7 @@ void options_parse(int argc, char *argv[]) if (strlen(option.target) == 0) { - tio_error_printf("Missing tty device, profile or topology ID"); + tio_error_print("Missing tty device, profile or topology ID"); exit(EXIT_FAILURE); } @@ -794,9 +1048,6 @@ void options_parse(int argc, char *argv[]) void options_parse_final(int argc, char *argv[]) { - /* Preserve target which may have been set by configuration file */ - const char *target = option.target; - /* Do 2nd pass to override settings set by configuration file */ optind = 1; // Reset option index to restart scanning of argv options_parse(argc, argv); @@ -804,16 +1055,13 @@ void options_parse_final(int argc, char *argv[]) #ifdef __CYGWIN__ unsigned char portnum; char *tty_win; - if ( ((strncmp("COM", target, 3) == 0) - || (strncmp("com", target, 3) == 0) ) - && (sscanf(target + 3, "%hhu", &portnum) == 1) + if ( ((strncmp("COM", option.target, 3) == 0) + || (strncmp("com", option.target, 3) == 0) ) + && (sscanf(option.target + 3, "%hhu", &portnum) == 1) && (portnum > 0) ) { asprintf(&tty_win, "/dev/ttyS%hhu", portnum - 1); - target = tty_win; + option.target = tty_win; } #endif - - /* Restore target */ - option.target = target; } diff --git a/src/options.h b/src/options.h index 016978d..31e807a 100644 --- a/src/options.h +++ b/src/options.h @@ -49,20 +49,20 @@ typedef enum /* Options */ struct option_t { - const char *target; - unsigned int baudrate; + char *target; + int baudrate; int databits; - char *flow; + flow_t flow; int stopbits; - char *parity; + parity_t parity; int output_delay; int output_line_delay; - unsigned int dtr_pulse_duration; - unsigned int rts_pulse_duration; - unsigned int cts_pulse_duration; - unsigned int dsr_pulse_duration; - unsigned int dcd_pulse_duration; - unsigned int ri_pulse_duration; + int dtr_pulse_duration; + int rts_pulse_duration; + int cts_pulse_duration; + int dsr_pulse_duration; + int dcd_pulse_duration; + int ri_pulse_duration; bool no_reconnect; auto_connect_t auto_connect; bool log; @@ -70,15 +70,15 @@ struct option_t bool log_strip; bool local_echo; timestamp_t timestamp; - const char *log_filename; - const char *log_directory; - const char *map; - const char *socket; + char *log_filename; + char *log_directory; + char *map; + char *socket; int color; input_mode_t input_mode; output_mode_t output_mode; - unsigned char prefix_code; - unsigned char prefix_key; + char prefix_code; + char prefix_key; bool prefix_enabled; bool mute; bool rs485; @@ -87,13 +87,13 @@ struct option_t int32_t rs485_delay_rts_after_send; alert_t alert; bool complete_profiles; - const char *script; - const char *script_filename; + char *script; + char *script_filename; script_run_t script_run; - unsigned int timestamp_timeout; - const char *exclude_devices; - const char *exclude_drivers; - const char *exclude_tids; + int timestamp_timeout; + char *exclude_devices; + char *exclude_drivers; + char *exclude_tids; int hex_n_value; bool vt100; }; @@ -104,10 +104,20 @@ void options_print(); void options_parse(int argc, char *argv[]); void options_parse_final(int argc, char *argv[]); -void line_pulse_duration_option_parse(const char *arg); -script_run_t script_run_option_parse(const char *arg); +int option_string_to_integer(const char *string, int *value, const char *desc, int min, int max); -input_mode_t input_mode_option_parse(const char *arg); -output_mode_t output_mode_option_parse(const char *arg); -auto_connect_t auto_connect_option_parse(const char *arg); -const char *auto_connect_state_to_string(auto_connect_t strategy); +void option_parse_flow(const char *arg, flow_t *flow); +void option_parse_parity(const char *arg, parity_t *parity); + +void option_parse_output_mode(const char *arg, output_mode_t *mode); +void option_parse_input_mode(const char *arg, input_mode_t *mode); + +void option_parse_line_pulse_duration(const char *arg); +void option_parse_script_run(const char *arg, script_run_t *script_run); +void option_parse_alert(const char *arg, alert_t *alert); + +void option_parse_auto_connect(const char *arg, auto_connect_t *auto_connect); +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); diff --git a/src/print.h b/src/print.h index dd65393..4a41b32 100644 --- a/src/print.h +++ b/src/print.h @@ -87,10 +87,18 @@ extern char ansi_format[]; { \ if (print_tainted) \ putchar('\n'); \ - if (option.color < 0) \ - fprintf (stdout, "\r[%s] Error: " format "\r\n", timestamp_current_time(), ## args); \ - else \ - ansi_printf("[%s] Error: " format, timestamp_current_time(), ## args); \ + if (option.color < 0) { \ + if (error_normal) \ + fprintf (stdout, "Error: " format "\n", ## args); \ + else \ + fprintf (stdout, "\r[%s] Error: " format "\r\n", timestamp_current_time(), ## args); \ + } \ + else { \ + if (error_normal) \ + { ansi_printf("Error: " format, ## args); }\ + else \ + { ansi_printf("[%s] Error: " format, timestamp_current_time(), ## args); }\ + } \ print_tainted = false; \ } \ } diff --git a/src/rs485.c b/src/rs485.c index c1b06db..30b7dc8 100644 --- a/src/rs485.c +++ b/src/rs485.c @@ -128,7 +128,7 @@ void rs485_print_config(void) tio_printf(" RTS_AFTER_SEND: %s", (rs485_config.flags & SER_RS485_RTS_AFTER_SEND) ? "high" : "low"); tio_printf(" RTS_DELAY_BEFORE_SEND = %d", rs485_config.delay_rts_before_send); tio_printf(" RTS_DELAY_AFTER_SEND = %d", rs485_config.delay_rts_after_send); - tio_printf(" RX_DURING_TX: %s", (rs485_config.flags & SER_RS485_RX_DURING_TX) ? "enabled" : "disabled"); + tio_printf(" RX_DURING_TX: %s", (rs485_config.flags & SER_RS485_RX_DURING_TX) ? "true" : "false"); } int rs485_mode_enable(int fd) diff --git a/src/timestamp.c b/src/timestamp.c index 19ea70e..338ff87 100644 --- a/src/timestamp.c +++ b/src/timestamp.c @@ -93,60 +93,3 @@ char *timestamp_current_time(void) return (len < TIME_STRING_SIZE_MAX) ? time_string : NULL; } - -const char* timestamp_state_to_string(timestamp_t timestamp) -{ - switch (timestamp) - { - case TIMESTAMP_NONE: - return "disabled"; - break; - - case TIMESTAMP_24HOUR: - return "24hour"; - break; - - case TIMESTAMP_24HOUR_START: - return "24hour-start"; - break; - - case TIMESTAMP_24HOUR_DELTA: - return "24hour-delta"; - break; - - case TIMESTAMP_ISO8601: - return "iso8601"; - break; - - default: - return "unknown"; - break; - } -} - -timestamp_t timestamp_option_parse(const char *arg) -{ - timestamp_t timestamp = TIMESTAMP_24HOUR; // Default - - if (arg != NULL) - { - if (strcmp(arg, "24hour") == 0) - { - return TIMESTAMP_24HOUR; - } - else if (strcmp(arg, "24hour-start") == 0) - { - return TIMESTAMP_24HOUR_START; - } - else if (strcmp(arg, "24hour-delta") == 0) - { - return TIMESTAMP_24HOUR_DELTA; - } - else if (strcmp(arg, "iso8601") == 0) - { - return TIMESTAMP_ISO8601; - } - } - - return timestamp; -} diff --git a/src/timestamp.h b/src/timestamp.h index e36660a..32d129d 100644 --- a/src/timestamp.h +++ b/src/timestamp.h @@ -32,5 +32,4 @@ typedef enum } timestamp_t; char *timestamp_current_time(void); -const char* timestamp_state_to_string(timestamp_t timestamp); -timestamp_t timestamp_option_parse(const char *arg); + diff --git a/src/tty.c b/src/tty.c index c8f7d7b..79bae34 100644 --- a/src/tty.c +++ b/src/tty.c @@ -158,7 +158,7 @@ bool map_ign_cr = false; char key_hit = 0xff; -const char* device_name; +const char* device_name = NULL; GList *device_list = NULL; static struct termios tio, tio_old, stdout_new, stdout_old, stdin_new, stdin_old; static unsigned long rx_total = 0, tx_total = 0; @@ -1154,25 +1154,26 @@ void tty_configure(void) } /* Set flow control */ - if (strcmp("hard", option.flow) == 0) + switch (option.flow) { - tio.c_cflag |= CRTSCTS; - tio.c_iflag &= ~(IXON | IXOFF | IXANY); - } - else if (strcmp("soft", option.flow) == 0) - { - tio.c_cflag &= ~CRTSCTS; - tio.c_iflag |= IXON | IXOFF; - } - else if (strcmp("none", option.flow) == 0) - { - tio.c_cflag &= ~CRTSCTS; - tio.c_iflag &= ~(IXON | IXOFF | IXANY); - } - else - { - tio_error_printf("Invalid flow control"); - exit(EXIT_FAILURE); + case FLOW_NONE: + tio.c_cflag &= ~CRTSCTS; + tio.c_iflag &= ~(IXON | IXOFF | IXANY); + break; + + case FLOW_HARD: + tio.c_cflag |= CRTSCTS; + tio.c_iflag &= ~(IXON | IXOFF | IXANY); + break; + + case FLOW_SOFT: + tio.c_cflag &= ~CRTSCTS; + tio.c_iflag |= IXON | IXOFF; + break; + + default: + tio_error_printf("Invalid flow control"); + exit(EXIT_FAILURE); } /* Set stopbits */ @@ -1190,36 +1191,37 @@ void tty_configure(void) } /* Set parity */ - if (strcmp("odd", option.parity) == 0) + switch (option.parity) { - tio.c_cflag |= PARENB; - tio.c_cflag |= PARODD; - } - else if (strcmp("even", option.parity) == 0) - { - tio.c_cflag |= PARENB; - tio.c_cflag &= ~PARODD; - } - else if (strcmp("none", option.parity) == 0) - { - tio.c_cflag &= ~PARENB; - } - else if ( strcmp("mark", option.parity) == 0) - { - tio.c_cflag |= PARENB; - tio.c_cflag |= PARODD; - tio.c_cflag |= CMSPAR; - } - else if ( strcmp("space", option.parity) == 0) - { - tio.c_cflag |= PARENB; - tio.c_cflag &= ~PARODD; - tio.c_cflag |= CMSPAR; - } - else - { - tio_error_printf("Invalid parity"); - exit(EXIT_FAILURE); + case PARITY_NONE: + tio.c_cflag &= ~PARENB; + break; + + case PARITY_ODD: + tio.c_cflag |= PARENB; + tio.c_cflag |= PARODD; + break; + + case PARITY_EVEN: + tio.c_cflag |= PARENB; + tio.c_cflag &= ~PARODD; + break; + + case PARITY_MARK: + tio.c_cflag |= PARENB; + tio.c_cflag |= PARODD; + tio.c_cflag |= CMSPAR; + break; + + case PARITY_SPACE: + tio.c_cflag |= PARENB; + tio.c_cflag &= ~PARODD; + tio.c_cflag |= CMSPAR; + break; + + default: + tio_error_printf("Invalid parity"); + exit(EXIT_FAILURE); } /* Control, input, output, local modes for tty device */ @@ -1809,7 +1811,14 @@ void tty_search(void) return; case AUTO_CONNECT_DIRECT: - if (strlen(option.target) == TOPOLOGY_ID_SIZE) + if (config.device != NULL) + { + // Prioritize any found pattern first + device_name = config.device; + + return; + } + else if (strlen(option.target) == TOPOLOGY_ID_SIZE) { // Potential topology ID detected -> trigger device search tty_search_for_serial_devices(); @@ -1829,6 +1838,12 @@ void tty_search(void) } } + if (config.device != NULL) + { + device_name = config.device; + break; + } + // Fallback to using tty device provided via cmdline target device_name = option.target; break; diff --git a/src/tty.h b/src/tty.h index b142034..4a8619f 100644 --- a/src/tty.h +++ b/src/tty.h @@ -29,6 +29,22 @@ #define TOPOLOGY_ID_SIZE 4 +typedef enum +{ + FLOW_NONE, + FLOW_HARD, + FLOW_SOFT, +} flow_t; + +typedef enum +{ + PARITY_NONE, + PARITY_ODD, + PARITY_EVEN, + PARITY_MARK, + PARITY_SPACE, +} parity_t; + typedef enum { AUTO_CONNECT_DIRECT, diff --git a/subprojects/libinih.wrap b/subprojects/libinih.wrap deleted file mode 100644 index fc5f690..0000000 --- a/subprojects/libinih.wrap +++ /dev/null @@ -1,4 +0,0 @@ -[wrap-git] -directory=libinih -url=https://github.com/benhoyt/inih.git -revision=r58 From 17bb6edfd29b19c52f0dc63f8e22b3d1f04ae686 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 2 May 2024 18:52:52 +0200 Subject: [PATCH 317/506] Bump version --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 79c8a67..05b949b 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('tio', 'c', - version : '3.0', + version : '3.1', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] From 15ba034ce5707d48cd27996f0dc33a6fc8e6d7e9 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 2 May 2024 19:38:14 +0200 Subject: [PATCH 318/506] Fix submenu response when invalid key hit --- src/tty.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tty.c b/src/tty.c index 79bae34..783f706 100644 --- a/src/tty.c +++ b/src/tty.c @@ -676,7 +676,7 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tty_line_poke(device_fd, TIOCM_RI, line_mode, option.ri_pulse_duration); break; default: - tio_warning_printf("Invalid line number"); + tio_error_print("Invalid line number"); break; } break; @@ -705,6 +705,10 @@ void handle_command_sequence(char input_char, char *output_char, bool *forward) tio_printf("%s", xymodem_send(device_fd, line, XMODEM_CRC) < 0 ? "Aborted" : "Done"); } break; + + default: + tio_error_print("Invalid protocol option"); + break; } break; } From 22b4f451eafba2fc50fe639e5f0b7179c2e791c3 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 2 May 2024 20:41:46 +0200 Subject: [PATCH 319/506] Update pattern matching example --- README.md | 4 ++-- examples/config/config | 4 ++-- man/tio.1.in | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2846449..17e989a 100644 --- a/README.md +++ b/README.md @@ -412,8 +412,8 @@ script-run = once color = 13 [usb devices] -pattern = usb([0-9]*) -device = /dev/ttyUSB%s +pattern = ^usb([0-9]*) +device = /dev/ttyUSB%m1 color = 14 ``` diff --git a/examples/config/config b/examples/config/config index df32b8d..1d18e89 100644 --- a/examples/config/config +++ b/examples/config/config @@ -56,8 +56,8 @@ log-strip = true color = 11 [usb] -pattern = usb([0-9]*) -device = /dev/ttyUSB%s +pattern = ^usb([0-9]*) +device = /dev/ttyUSB%m1 color = 12 [rs-485-device] diff --git a/man/tio.1.in b/man/tio.1.in index 93840c0..108bd3b 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -622,7 +622,7 @@ regular expressions: .nf .eo [usb device] -pattern = usb([0-9]*) +pattern = ^usb([0-9]*) device = /dev/ttyUSB%m1 baudrate = 115200 .ec From 68c78222e16b2ec646e4d0a0e4ab0efec22855c2 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 2 May 2024 21:50:40 +0200 Subject: [PATCH 320/506] Fix hexN output mode --- src/tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tty.c b/src/tty.c index 783f706..f2d123e 100644 --- a/src/tty.c +++ b/src/tty.c @@ -2324,6 +2324,7 @@ int tty_connect(void) if (first_) { // Do nothing + first_ = false; } else { @@ -2334,7 +2335,6 @@ int tty_connect(void) { log_putc('\n'); } - first_ = false; } } } From 0e9dbcbc77674304d8a6a6dc3c1d023687a8beeb Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 2 May 2024 22:30:39 +0200 Subject: [PATCH 321/506] Replace use of stat() with fstat() For better security. --- src/tty.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/tty.c b/src/tty.c index f2d123e..96b62ad 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1332,7 +1332,13 @@ static bool is_serial_device(const char *format, ...) return false; } - if (stat(filename, &st) != 0) + fd = open(filename, O_RDONLY | O_NONBLOCK | O_NOCTTY); + if (fd == -1) + { + return false; + } + + if (fstat(fd, &st) == -1) { return false; } @@ -1343,12 +1349,6 @@ static bool is_serial_device(const char *format, ...) return false; } - fd = open(filename, O_RDONLY | O_NONBLOCK | O_NOCTTY); - if (fd == -1) - { - return false; - } - // Make sure it is a tty status = isatty(fd); if (status == 0) From 22bcfdc29f15217d46544b89b0e24c09b01a4853 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Thu, 2 May 2024 23:44:26 +0200 Subject: [PATCH 322/506] Remove inih dependency from CI builds --- .github/workflows/macos.yml | 2 +- .github/workflows/ubuntu.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 499f545..558dbaf 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -21,7 +21,7 @@ jobs: - name: Install dependencies run: | - brew install meson ninja inih lua + brew install meson ninja lua - name: Build run: | diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 5975190..a72e3d3 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -21,7 +21,7 @@ jobs: - name: Install dependencies run: | - sudo apt-get install -y bash-completion git meson libinih-dev liblua5.2-dev libglib2.0-dev + sudo apt-get install -y bash-completion git meson liblua5.2-dev libglib2.0-dev - name: Build run: | From 7aa2d3fee29689103292cbbbc5ed2845351c7412 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 02:11:57 +0200 Subject: [PATCH 323/506] Fix profile autocompletion --- src/configfile.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/configfile.c b/src/configfile.c index ac66e9b..d26234c 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -385,6 +385,7 @@ void config_file_show_profiles(void) { continue; } + printf("%s ", group[i]); } g_strfreev(group); From 62a4a93dec9a0dfba2e6d41934427d18cd985b41 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 08:39:58 +0200 Subject: [PATCH 324/506] Fix potential buffer overflow in match_and_replace() --- src/configfile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/configfile.c b/src/configfile.c index d26234c..22cf225 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -407,7 +407,7 @@ static void replace_substring(char *str, const char *substr, const char *replace static char *match_and_replace(const char *str, const char *pattern, char *device) { char replacement_str[PATH_MAX] = {}; - char m_key[13] = {}; + char m_key[14] = {}; regex_t regex; assert(str != NULL); From 04dfa682c9ea913696838351464441c47a78314b Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 09:19:21 +0200 Subject: [PATCH 325/506] Fix use of invalid flag with regexec() --- src/configfile.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/configfile.c b/src/configfile.c index 22cf225..9572852 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -417,6 +417,7 @@ static char *match_and_replace(const char *str, const char *pattern, char *devic char *string = malloc(strlen(device) + PATH_MAX); if (string == NULL) { + tio_debug_printf("Failure allocating string memory\n"); return NULL; } strcpy(string, device); @@ -434,11 +435,12 @@ static char *match_and_replace(const char *str, const char *pattern, char *devic if (regcomp(®ex, pattern, REG_EXTENDED) != 0) { // Failure to compile regular expression - return NULL; + tio_error_print("Failure compiling regular expression '%s'\n", pattern); + exit(EXIT_FAILURE); } regmatch_t matches[regex.re_nsub + 1]; - int status = regexec(®ex, str, regex.re_nsub + 1, matches, REG_EXTENDED); + int status = regexec(®ex, str, regex.re_nsub + 1, matches, 0); if (status == 0) { tio_debug_printf("Full match: "); @@ -469,13 +471,14 @@ static char *match_and_replace(const char *str, const char *pattern, char *devic } else if (status == REG_NOMATCH) { + tio_debug_printf("No regex match\n"); goto error; } else { char error_message[100]; regerror(status, ®ex, error_message, sizeof(error_message)); - tio_error_print("Regex match failed: %s", error_message); + tio_debug_printf("Regex match failed: %s", error_message); goto error; } From eae7f8f8d7e7c11d6deebb4e95217bba0ea46842 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 10:02:44 +0200 Subject: [PATCH 326/506] Update NEWS --- NEWS | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 8a4739e..3862f25 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,90 @@ -=== tio v3.0 === +=== tio v3.1 === + + + +Changes since tio v3.0: + + * Fix use of invalid flag with regexec() + + * Fix potential buffer overflow in match_and_replace() + + * Fix profile autocompletion + + * Remove inih dependency from CI builds + + * Replace use of stat() with fstat() + + For better security. + + * Fix hexN output mode + + * Update pattern matching example + + * Fix submenu response when invalid key hit + + * Replace inih with glib key file parser + + 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 + parsing has been cleaned up, resulting in better and stricter + configuration file and option value checks. + + Compared to old, configuration files now requires any default + configurations to be put in a group/section named [default]. + + Configuration file keywords such as "enable", "disable", "on", + "off", "yes", "no", "0", "1" have been retired. Now only "true" and + "false" apply to boolean configuration options. This is done to simplify + things and avoid any confusion. + + The pattern option feature has been reworked so now the user can now + access the full match string and any matching subexpression using the + %mN syntax. + + For example: + + [usb devices] + pattern = usb([0-9]*) + device = /dev/ttyUSB%m1 + + Then when using tio: + $ tio usb12 + + %m0 = 'usb12' // Full match string + %m1 = 12 // First match subexpression + + Which results in device = /dev/ttyUSB12 + + * Remove CircleCI + + Replaced with github workflow CI. + + * Add github workflow for Ubuntu build + + * Enable extended pattern matching + + So that the exclude options can also work as include using special + pattern syntax. + + For example, to only include /dev/ttyUSB* devices simply do: + + $ tio --exclude-devices=!(/dev/ttyUSB*) --list + + See the man page of fnmatch() for all available extended pattern + options. + + * Update lua read() description + +Rui Chen: + + * fix: add build patch for `FNM_EXTMATCH` + + * feat: add macos workflow + + * fix: add macos build patch for `fs_get_creation_time` From 60caede5dd7f9de633c830e1cb7486ec7239cb03 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 10:32:22 +0200 Subject: [PATCH 327/506] Add support for using TID as device in config file --- src/tty.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/tty.c b/src/tty.c index 96b62ad..05e9ad6 100644 --- a/src/tty.c +++ b/src/tty.c @@ -1817,12 +1817,17 @@ void tty_search(void) case AUTO_CONNECT_DIRECT: if (config.device != NULL) { - // Prioritize any found pattern first + // Prioritize any device result of the configuration file first + // Meaning a pattern or section/group have been matched the cmdline target. device_name = config.device; - - return; } - else if (strlen(option.target) == TOPOLOGY_ID_SIZE) + else + { + // Fallback to use the target direcly + device_name = option.target; + } + + if (strlen(device_name) == TOPOLOGY_ID_SIZE) { // Potential topology ID detected -> trigger device search tty_search_for_serial_devices(); @@ -1832,7 +1837,7 @@ void tty_search(void) { device = (device_t *) iter->data; - if (strcmp(device->tid, option.target) == 0) + if (strcmp(device->tid, device_name) == 0) { // Topology ID match found -> use corresponding device name device_name = device->path; @@ -1841,15 +1846,6 @@ void tty_search(void) } } } - - if (config.device != NULL) - { - device_name = config.device; - break; - } - - // Fallback to using tty device provided via cmdline target - device_name = option.target; break; default: From a53a4f44de6b82d42e2956ef3619a27919c97863 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 10:36:14 +0200 Subject: [PATCH 328/506] Update NEWS --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 3862f25..5c92209 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ Changes since tio v3.0: + * Add support for using TID as device in config file + * Fix use of invalid flag with regexec() * Fix potential buffer overflow in match_and_replace() From 3b3fca2e8b3bf96e91f15c902ca0d8147ee68c25 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 10:45:49 +0200 Subject: [PATCH 329/506] Always message when saving log file --- src/log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/log.c b/src/log.c index c593213..9fa0414 100644 --- a/src/log.c +++ b/src/log.c @@ -231,6 +231,7 @@ void log_close(void) if (fp != NULL) { fclose(fp); + tio_printf("Saved log to file %s", log_filename); fp = NULL; log_filename = NULL; } @@ -240,7 +241,6 @@ void log_exit(void) { if ((option.log) && (log_filename != NULL)) { - tio_printf("Saved log to file %s", log_filename); log_close(); } } From e9c96c5456e1139ad56482ff9c0f6055244aaa5f Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 10:47:01 +0200 Subject: [PATCH 330/506] Update NEWS --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 5c92209..913ec16 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ Changes since tio v3.0: + * Always message when saving log file + * Add support for using TID as device in config file * Fix use of invalid flag with regexec() From 31647a934c25f301c983b2bbe2e6ce931c457948 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 11:29:28 +0200 Subject: [PATCH 331/506] List available profiles in --list output --- man/tio.1.in | 5 +++-- src/configfile.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ src/configfile.h | 1 + src/options.c | 4 +++- 4 files changed, 62 insertions(+), 3 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 108bd3b..e4cd066 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -165,7 +165,7 @@ Default value is 200. .TP .BR \-l ", " \-\-list -List available serial devices. +List available targets (serial devices, TIDs, configuration profiles). .TP .BR \-L ", " \-\-log @@ -173,7 +173,8 @@ List available serial devices. Enable log to file. The log file will be automatically named using the following format -tio_DEVICE_YYYY-MM-DDTHH:MM:SS.log. +tio_TARGET_YYYY-MM-DDTHH:MM:SS.log. Target being the command line target such +as tty-device, tid, or configuration profile. The filename can be manually set using the \-\-log-file option. diff --git a/src/configfile.c b/src/configfile.c index 9572852..db45146 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -603,3 +603,58 @@ void config_file_print(void) } } } + +void config_list_targets(void) +{ + memset(&config, 0, sizeof(struct config_t)); + + // Find config file + if (config_file_resolve() != 0) + { + // None found + return; + } + + GKeyFile *keyfile; + GError *error = NULL; + + keyfile = g_key_file_new(); + + if (!g_key_file_load_from_file(keyfile, config.path, G_KEY_FILE_NONE, &error)) + { + tio_error_print("Failure loading file: %s", error->message); + g_error_free(error); + return; + } + + // Get all group names + gsize num_groups; + gchar **group = g_key_file_get_groups(keyfile, &num_groups); + + if (num_groups == 0) + { + return; + } + + printf("\nConfiguration profiles\n"); + printf("--------------------------------------------------------------------------------\n"); + + int j = 1; + for (gsize i = 0; i < num_groups; i++) + { + // Skip default group + if (strcmp(group[i], CONFIG_GROUP_NAME_DEFAULT) == 0) + { + continue; + } + printf("%-20s ", group[i]); + if (j++ % 4 == 0) + { + putchar('\n'); + } + } + putchar('\n'); + + g_strfreev(group); + g_key_file_free(keyfile); +} diff --git a/src/configfile.h b/src/configfile.h index 7b7a3a9..2cb9f15 100644 --- a/src/configfile.h +++ b/src/configfile.h @@ -35,3 +35,4 @@ void config_file_print(void); void config_file_parse(void); void config_exit(void); void config_file_show_profiles(void); +void config_list_targets(void); diff --git a/src/options.c b/src/options.c index 15bacb9..c0316ce 100644 --- a/src/options.c +++ b/src/options.c @@ -42,6 +42,7 @@ #include "alert.h" #include "log.h" #include "script.h" +#include "configfile.h" #define HEX_N_VALUE_MAX 4096 @@ -150,7 +151,7 @@ void option_print_help(char *argv[]) printf(" -t, --timestamp Enable line timestamp\n"); printf(" --timestamp-format Set timestamp format (default: 24hour)\n"); printf(" --timestamp-timeout Set timestamp timeout (default: 200)\n"); - printf(" -l, --list List available serial devices\n"); + printf(" -l, --list List available serial devices, TIDs, and profiles\n"); printf(" -L, --log Enable log to file\n"); printf(" --log-file Set log filename\n"); printf(" --log-directory Set log directory path for automatic named logs\n"); @@ -916,6 +917,7 @@ void options_parse(int argc, char *argv[]) case 'l': list_serial_devices(); + config_list_targets(); exit(EXIT_SUCCESS); break; From adafa00b87f57d092ae71ecd7db3aa40b6a3b30a Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 11:38:00 +0200 Subject: [PATCH 332/506] Update NEWS --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 913ec16..d7a7146 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ Changes since tio v3.0: + * List available profiles in --list output + * Always message when saving log file * Add support for using TID as device in config file From 5ec33f5d4db710b933c524d298b9521dd84b3524 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 11:57:26 +0200 Subject: [PATCH 333/506] Update doc --- README.md | 2 +- examples/config/config | 2 +- man/tio.1.in | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 17e989a..a54b021 100644 --- a/README.md +++ b/README.md @@ -411,7 +411,7 @@ script = set{DTR=high,RTS=low}; msleep(100); set{DTR=low,RTS=high}; msleep(100); script-run = once color = 13 -[usb devices] +[usb-devices] pattern = ^usb([0-9]*) device = /dev/ttyUSB%m1 color = 14 diff --git a/examples/config/config b/examples/config/config index 1d18e89..5665ee3 100644 --- a/examples/config/config +++ b/examples/config/config @@ -55,7 +55,7 @@ log-file = tincan.log log-strip = true color = 11 -[usb] +[usb-devices] pattern = ^usb([0-9]*) device = /dev/ttyUSB%m1 color = 12 diff --git a/man/tio.1.in b/man/tio.1.in index e4cd066..f138483 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -622,7 +622,7 @@ regular expressions: .RS .nf .eo -[usb device] +[usb-devices] pattern = ^usb([0-9]*) device = /dev/ttyUSB%m1 baudrate = 115200 From ec8f63f06d24e57bc0c5e1f8657ceb46d191f474 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 14:12:47 +0200 Subject: [PATCH 334/506] Improve --list feature on non-linux platform --- src/fs.c | 46 ++++++++++++++------ src/tty.c | 125 ++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 132 insertions(+), 39 deletions(-) diff --git a/src/fs.c b/src/fs.c index a84bd27..76abb0c 100644 --- a/src/fs.c +++ b/src/fs.c @@ -178,21 +178,11 @@ char* fs_search_directory(const char *dir_path, const char *dirname) return NULL; } +#if defined(__linux__) + // Function to return creation time of file double fs_get_creation_time(const char *path) { - -#if defined(__APPLE__) || defined(__MACH__) - // Use stat on macOS to access creation time - struct stat st; - - if (stat(path, &st) != 0) - { - return -1; - } - - return st.st_birthtimespec.tv_sec + st.st_birthtimespec.tv_nsec / 1e9; -#else struct statx stx; int fd = open(path, O_RDONLY); if (fd == -1) @@ -210,5 +200,35 @@ double fs_get_creation_time(const char *path) close(fd); return stx.stx_btime.tv_sec + stx.stx_btime.tv_nsec / 1e9; -#endif } + +#elif defined(__APPLE__) || defined(__MACH__) + +double fs_get_creation_time(const char *path) +{ + // Use stat on macOS to access creation time + struct stat st; + + if (stat(path, &st) != 0) + { + return -1; + } + + return st.st_birthtimespec.tv_sec + st.st_birthtimespec.tv_nsec / 1e9; +} + +#else + +double fs_get_creation_time(const char *path) +{ + struct stat st; + + if (stat(path, &st) != 0) + { + return -1; + } + + return (double) st.st_ctime; +} + +#endif diff --git a/src/tty.c b/src/tty.c index 05e9ad6..8aa1606 100644 --- a/src/tty.c +++ b/src/tty.c @@ -68,24 +68,19 @@ /* tty device listing configuration */ #if defined(__linux__) -#define PATH_SERIAL_DEVICES "/dev/serial/by-id/" -#define PATH_SERIAL_DEVICES_BY_PATH "/dev/serial/by-path/" -#define PREFIX_TTY_DEVICES "" +#define PATH_SERIAL_DEVICES "/dev" +#define PATH_SERIAL_DEVICES_BY_ID "/dev/serial/by-id" +#define PATH_SERIAL_DEVICES_BY_PATH "/dev/serial/by-path" #elif defined(__FreeBSD__) -#define PATH_SERIAL_DEVICES "/dev/" -#define PREFIX_TTY_DEVICES "cua" +#define PATH_SERIAL_DEVICES "/dev" #elif defined(__APPLE__) -#define PATH_SERIAL_DEVICES "/dev/" -#define PREFIX_TTY_DEVICES "tty." +#define PATH_SERIAL_DEVICES "/dev" #elif defined(__CYGWIN__) -#define PATH_SERIAL_DEVICES "/dev/" -#define PREFIX_TTY_DEVICES "ttyS" +#define PATH_SERIAL_DEVICES "/dev" #elif defined(__HAIKU__) -#define PATH_SERIAL_DEVICES "/dev/ports/" -#define PREFIX_TTY_DEVICES "" +#define PATH_SERIAL_DEVICES "/dev/ports" #else -#define PATH_SERIAL_DEVICES "/dev/" -#define PREFIX_TTY_DEVICES "tty" +#define PATH_SERIAL_DEVICES "/dev" #endif #ifndef CMSPAR @@ -1375,7 +1370,8 @@ error: static void list_serial_devices_by_id(void) { - DIR *d = opendir(PATH_SERIAL_DEVICES); +#ifdef PATH_SERIAL_DEVICES_BY_ID + DIR *d = opendir(PATH_SERIAL_DEVICES_BY_ID); if (d) { struct dirent *dir; @@ -1387,23 +1383,20 @@ static void list_serial_devices_by_id(void) { if ((strcmp(dir->d_name, ".")) && (strcmp(dir->d_name, ".."))) { - if (!strncmp(dir->d_name, PREFIX_TTY_DEVICES, sizeof(PREFIX_TTY_DEVICES) - 1)) + if (is_serial_device("%s/%s", PATH_SERIAL_DEVICES_BY_ID, dir->d_name)) { - if (is_serial_device("%s%s", PATH_SERIAL_DEVICES, dir->d_name)) - { - printf("%s%s\n", PATH_SERIAL_DEVICES, dir->d_name); - } + printf("%s/%s\n", PATH_SERIAL_DEVICES_BY_ID, dir->d_name); } } } closedir(d); } +#endif } static void list_serial_devices_by_path(void) { #ifdef PATH_SERIAL_DEVICES_BY_PATH - DIR *d = opendir(PATH_SERIAL_DEVICES_BY_PATH); if (d) { @@ -1416,12 +1409,9 @@ static void list_serial_devices_by_path(void) { if ((strcmp(dir->d_name, ".")) && (strcmp(dir->d_name, ".."))) { - if (!strncmp(dir->d_name, "", sizeof("") - 1)) + if (is_serial_device("%s/%s", PATH_SERIAL_DEVICES_BY_PATH, dir->d_name)) { - if (is_serial_device("%s%s", PATH_SERIAL_DEVICES_BY_PATH, dir->d_name)) - { - printf("%s%s\n", PATH_SERIAL_DEVICES_BY_PATH, dir->d_name); - } + printf("%s/%s\n", PATH_SERIAL_DEVICES_BY_PATH, dir->d_name); } } } @@ -1551,6 +1541,8 @@ static void search_reset(void) device_list = NULL; } +#if defined(__linux__) + GList *tty_search_for_serial_devices(void) { DIR *dir; @@ -1566,7 +1558,6 @@ GList *tty_search_for_serial_devices(void) dir = opendir("/sys/class/tty"); if (!dir) { - // Error return NULL; } @@ -1730,6 +1721,88 @@ GList *tty_search_for_serial_devices(void) return device_list; } +#else + +GList *tty_search_for_serial_devices(void) +{ + DIR *dir; + char path[PATH_MAX] = {}; + double current_time, creation_time; + + search_reset(); + + // Open the directory containing serial devices + dir = opendir(PATH_SERIAL_DEVICES); + if (!dir) + { + return NULL; + } + + current_time = get_current_time(); + + // Iterate through each device in the subsystem directory + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) + { + // Skip . and .. entries + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + { + continue; + } + + // Construct the path to the TTY device file + snprintf(path, sizeof(path), PATH_SERIAL_DEVICES "/%s", entry->d_name); + + // Skip non serial devices + if (is_serial_device(path) == false) + { + continue; + } + + // Calculate uptime + creation_time = fs_get_creation_time(path); + double uptime = current_time - creation_time; + + // Do not add devices excluded by exclude patterns + if (match_patterns(path, option.exclude_devices)) + { + continue; + } + + // Allocate new device item for device list + device_t *device = g_new0(device_t, 1); + if (device == NULL) + { + continue; + } + + // Fill in device information + device->path = g_strdup(path); + device->tid = ""; + device->uptime = uptime; + device->driver = ""; + device->description = ""; + + // Add device information to device list + device_list = g_list_append(device_list, device); + } + + if (g_list_length(device_list) == 0) + { + // Return NULL if no serial devices found + return NULL; + } + + // Sort device list device with respect to uptime + device_list = g_list_sort(device_list, compare_uptime); + + closedir(dir); + + return device_list; +} + +#endif + void list_serial_devices(void) { tty_search_for_serial_devices(); From fe4e47219e9f2b20bd1e25b749f5fbb87b002e66 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 14:18:03 +0200 Subject: [PATCH 335/506] Update NEWS --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index d7a7146..15e52d9 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ Changes since tio v3.0: + * Improve --list feature on non-linux platform + * List available profiles in --list output * Always message when saving log file From 3e81f36dcef966b8cab89fdb818ad55458edaf29 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 14:20:57 +0200 Subject: [PATCH 336/506] Update version date --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 05b949b..45577c9 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-04-14' +version_date = '2024-05-03' # Test for dynamic baudrate configuration interface compiler = meson.get_compiler('c') From eb087713a47207b7f9691137b0c769390e00443c Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 15:26:11 +0200 Subject: [PATCH 337/506] Update README --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index a54b021..02eec93 100644 --- a/README.md +++ b/README.md @@ -191,6 +191,11 @@ By-path /dev/serial/by-path/pci-0000:00:14.0-usb-0:6.4:1.0-port0 /dev/serial/by-path/pci-0000:00:14.0-usbv2-0:6.3:1.2 /dev/serial/by-path/pci-0000:00:14.0-usb-0:6.3:1.2 + +Configuration profiles +-------------------------------------------------------------------------------- +rpi3 stm32 esp32 am64-evm +imx8mp-evk nucleo-h743zi2 usb-devices ``` It is recommended to connect serial TTY devices by ID: @@ -209,6 +214,11 @@ Note: The TID is unique and will stay the same as long as your USB serial port device plugs into the same USB topology (same ports, same hubs, etc.). This way it is possible for tio to successfully reconnect to the same device. +To connection via configuration profile simply do: +``` +tio stm32 +``` + Connect automatically to first new appearing serial device: ``` $ tio --auto-connect new From 6310a9fabc096817dd0e85badb6f29e7d1ca96bd Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 15:26:55 +0200 Subject: [PATCH 338/506] Fix alignment of profile listing --- src/configfile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/configfile.c b/src/configfile.c index db45146..a584bf1 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -647,7 +647,7 @@ void config_list_targets(void) { continue; } - printf("%-20s ", group[i]); + printf("%-19s ", group[i]); if (j++ % 4 == 0) { putchar('\n'); From 13f3bedb2f996d8b1f8946038dca516bd4a56020 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 15:28:55 +0200 Subject: [PATCH 339/506] Update README --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index 02eec93..ddaeecf 100644 --- a/README.md +++ b/README.md @@ -214,11 +214,6 @@ Note: The TID is unique and will stay the same as long as your USB serial port device plugs into the same USB topology (same ports, same hubs, etc.). This way it is possible for tio to successfully reconnect to the same device. -To connection via configuration profile simply do: -``` -tio stm32 -``` - Connect automatically to first new appearing serial device: ``` $ tio --auto-connect new From 50253a6a77e3a9b4af89c10d65b082c06d969d68 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 15:32:25 +0200 Subject: [PATCH 340/506] Update README --- README.md | 2 +- examples/config/config | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ddaeecf..14c5184 100644 --- a/README.md +++ b/README.md @@ -389,7 +389,7 @@ TTY device to connect to and other options. Example configuration file: ``` -# Defaults +[default] baudrate = 115200 databits = 8 parity = none diff --git a/examples/config/config b/examples/config/config index 5665ee3..8cc9017 100644 --- a/examples/config/config +++ b/examples/config/config @@ -9,7 +9,7 @@ # $HOME/.config/tio/config # $HOME/.tioconfig -# Defaults +[default] baudrate = 115200 databits = 8 flow = none @@ -69,5 +69,5 @@ color = 13 [esp32] device = /dev/ttyUSB0 color = 14 -script = high(DTR); low(RTS); msleep(100); low(DTR); high(RTS); msleep(100); low(RTS) +script = set{DTR=high,RTS=low}; msleep(100); set{DTR=low,RTS=high}; msleep(100); set{RTS=low} script-run = always From 05785e82b37eb4059d11197d6fbffe40c4eb2394 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 15:39:52 +0200 Subject: [PATCH 341/506] Update README --- README.md | 124 +++++++++++++++++++++++++++--------------------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 14c5184..972cd47 100644 --- a/README.md +++ b/README.md @@ -311,7 +311,68 @@ ctrl-t ? to list the available key commands. If needed, the prefix key (ctrl-t) can be remapped via configuration file. -### 3.3 Lua script API +### 3.3 Configuration file + +Options can be set via the configuration file first found in any of the +following locations in the order listed: + - $XDG_CONFIG_HOME/tio/config + - $HOME/.config/tio/config + - $HOME/.tioconfig + +The configuration file supports profiles using named sections which can be +activated via the command-line by name or pattern. A profile specifies which +TTY device to connect to and other options. + +### 3.3.1 Example + +Example configuration file: + +``` +[default] +baudrate = 115200 +databits = 8 +parity = none +stopbits = 1 +color = 10 + +[rpi3] +device = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 +no-reconnect = true +log = true +log-file = rpi3.log +line-pulse-duration = DTR=200,RTS=150 +color = 11 + +[svf2] +device = /dev/ttyUSB0 +baudrate = 9600 +script = expect("login: "); send("root\n"); expect("Password: "); send("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-run = once +color = 13 + +[usb-devices] +pattern = ^usb([0-9]*) +device = /dev/ttyUSB%m1 +color = 14 +``` + +To use a specific profile by name simply start tio like so: +``` +$ tio rpi3 +``` +Or by pattern match: +``` +$ tio usb12 +``` + +Another more elaborate configuration file example is available [here](examples/config/config). + +### 3.4 Lua script API Tio suppots Lua scripting to easily automate interaction with the tty device. @@ -372,67 +433,6 @@ exit(code) Exit with exit code. ``` -### 3.4 Configuration file - -Options can be set via the configuration file first found in any of the -following locations in the order listed: - - $XDG_CONFIG_HOME/tio/config - - $HOME/.config/tio/config - - $HOME/.tioconfig - -The configuration file supports profiles using named sections which can be -activated via the command-line by name or pattern. A profile specifies which -TTY device to connect to and other options. - -### 3.4.1 Examples - -Example configuration file: - -``` -[default] -baudrate = 115200 -databits = 8 -parity = none -stopbits = 1 -color = 10 - -[rpi3] -device = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 -no-reconnect = true -log = true -log-file = rpi3.log -line-pulse-duration = DTR=200,RTS=150 -color = 11 - -[svf2] -device = /dev/ttyUSB0 -baudrate = 9600 -script = expect("login: "); send("root\n"); expect("Password: "); send("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-run = once -color = 13 - -[usb-devices] -pattern = ^usb([0-9]*) -device = /dev/ttyUSB%m1 -color = 14 -``` - -To use a specific profile by name simply start tio like so: -``` -$ tio rpi3 -``` -Or by pattern match: -``` -$ tio usb12 -``` - -Another more elaborate configuration file example is available [here](examples/config/config). - ## 4. Installation ### 4.1 Installation using package manager (Linux) From 59940b3311f52bfd010a11f2f8f7223c21454586 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 15:45:39 +0200 Subject: [PATCH 342/506] Update README --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 972cd47..4461f48 100644 --- a/README.md +++ b/README.md @@ -211,8 +211,9 @@ topology ID (TID): $ tio bCC2 ``` Note: The TID is unique and will stay the same as long as your USB serial port -device plugs into the same USB topology (same ports, same hubs, etc.). This way -it is possible for tio to successfully reconnect to the same device. +device plugs into the same USB topology (same ports, same hubs, same +connections, etc.). This way it is possible for tio to successfully reconnect +to the same device. Connect automatically to first new appearing serial device: ``` From c88cd3c5f39c160d92c350fdc3a31316380d1824 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 18:23:53 +0200 Subject: [PATCH 343/506] Print location of configuratin file in --list output --- src/configfile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/configfile.c b/src/configfile.c index a584bf1..7e63ca2 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -636,7 +636,7 @@ void config_list_targets(void) return; } - printf("\nConfiguration profiles\n"); + printf("\nConfiguration profiles (%s)\n", config.path); printf("--------------------------------------------------------------------------------\n"); int j = 1; From ce736c267a19abc29cf00d43a2e9d109adfcfb33 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Fri, 3 May 2024 18:25:17 +0200 Subject: [PATCH 344/506] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4461f48..f3133cd 100644 --- a/README.md +++ b/README.md @@ -192,7 +192,7 @@ By-path /dev/serial/by-path/pci-0000:00:14.0-usbv2-0:6.3:1.2 /dev/serial/by-path/pci-0000:00:14.0-usb-0:6.3:1.2 -Configuration profiles +Configuration profiles (/home/lundmar/.config/tio/config) -------------------------------------------------------------------------------- rpi3 stm32 esp32 am64-evm imx8mp-evk nucleo-h743zi2 usb-devices From 242a2ea843b270f4bba786842f44e7e7b929fc27 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 4 May 2024 13:16:28 +0200 Subject: [PATCH 345/506] Fix line ending in --list output --- src/configfile.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/configfile.c b/src/configfile.c index 7e63ca2..3390b34 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -653,7 +653,10 @@ void config_list_targets(void) putchar('\n'); } } - putchar('\n'); + if ((j-1) % 4 != 0) + { + putchar('\n'); + } g_strfreev(group); g_key_file_free(keyfile); From 3a75b098d12a6359e2ebea170aaf1b8d15811513 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 4 May 2024 13:35:40 +0200 Subject: [PATCH 346/506] Add new build shields to README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f3133cd..258a9c1 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,8 @@ # tio - a serial device I/O tool -[![](https://img.shields.io/circleci/build/github/tio/tio)](https://circleci.com/github/tio/tio/tree/master) +[![](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/macos.yml?label=MacOS)](https://github.com/tio/tio/actions/workflows/macos.yml) [![](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 c2ef2fced57ee729f73747d9590d6a288da5189c Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sat, 4 May 2024 14:06:05 +0200 Subject: [PATCH 347/506] Add codefactor.io shield --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 258a9c1..462859a 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ [![](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/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) [![](https://img.shields.io/repology/repositories/tio)](https://repology.org/project/tio/versions) - ## 1. Introduction From b490233988ebe9dbc015690cd4cde9acd2e5e1e1 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Sun, 5 May 2024 21:02:28 +0200 Subject: [PATCH 348/506] Fix build on older GNU/Linux systems without statx --- src/fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fs.c b/src/fs.c index 76abb0c..1e69d53 100644 --- a/src/fs.c +++ b/src/fs.c @@ -178,7 +178,7 @@ char* fs_search_directory(const char *dir_path, const char *dirname) return NULL; } -#if defined(__linux__) +#if defined(__linux__) && defined(STATX_BTIME) // Function to return creation time of file double fs_get_creation_time(const char *path) From 68e2042fd68f83d27beda3e01a8e121c726807de Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 7 May 2024 09:12:15 +0200 Subject: [PATCH 349/506] Update man page --- 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 f138483..4ea2499 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -580,7 +580,7 @@ To change the default configuration simply set options like so: .RS .nf .eo -# Defaults +[default] baudrate = 9600 databits = 8 parity = none From 9320f54a73bcecc3bce6e5fb4ae579330b31a943 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 7 May 2024 09:19:51 +0200 Subject: [PATCH 350/506] Update plaintext man page --- man/tio.1.txt | 82 +++++++++++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/man/tio.1.txt b/man/tio.1.txt index a6851db..372debc 100644 --- a/man/tio.1.txt +++ b/man/tio.1.txt @@ -4,7 +4,7 @@ NAME tio - a serial device I/O tool SYNOPSIS - tio [] + tio [] DESCRIPTION tio is a serial device tool which features a straightforward command-line and configuration file interface to easily connect to serial TTY devices for basic I/O operations. @@ -126,13 +126,13 @@ OPTIONS -l, --list - List available serial devices. + List available targets (serial devices, TIDs, configuration profiles). -L, --log Enable log to file. - The log file will be automatically named using the following format tio_DEVICE_YYYY-MM-DDTHH:MM:SS.log. + The log file will be automatically named using the following format tio_TARGET_YYYY-MM-DDTHH:MM:SS.log. Target being the command line target such as tty-device, tid, or configuration profile. The filename can be manually set using the --log-file option. @@ -192,9 +192,13 @@ OPTIONS Default value is "normal". - --output-mode normal|hex + --output-mode normal|hex|hexN - Set output mode. In hex mode each incoming byte is printed out as a 1 byte hex value. + Set output mode. + + In hex mode each incoming byte is printed out as a 1 byte hex value. + + In hexN mode, N is a number less than or equal to 4096 which defines how many hex values will be printed before a line break. Default value is "normal". @@ -335,13 +339,15 @@ SCRIPT API 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 invalid regular expression. + 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. 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. @@ -354,17 +360,19 @@ SCRIPT API Returns nil if no serial devices are found. - exit(code) - Exit with exit code. + read(size, timeout) + Read from serial device. If timeout is 0 or not provided it will wait forever until data is ready to read. - high(line) - Set tty line high. + Returns number of bytes read on success, 0 on timeout, or -1 on error. - low(line) - Set tty line low. + On success, returns read string as second return value. - toggle(line) - Toggle the tty line. + 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. sleep(seconds) Sleep for seconds. @@ -372,16 +380,8 @@ SCRIPT API msleep(ms) Sleep for miliseconds. - config_high(line) - Set tty line state configuration to high. - - config_low(line) - Set tty line state configuration to low. - - apply_config() - Apply tty line state configuration. Using the line state configuration API instead of high()/low() will help to make the lines physically switch as simultaneously as possible. This may solve timing issues on some platforms. - - Note: Line can be any of DTR, RTS, CTS, DSR, CD, RI + exit(code) + Exit with exit code. CONFIGURATION FILE Options can be set via configuration file using the INI format. tio uses the configuration file first found in the following locations in the order listed: @@ -392,9 +392,9 @@ CONFIGURATION FILE $HOME/.tioconfig - Labels can be used to group settings into named sub-configurations which can be activated from the command-line when starting tio. + Labels can be used to group settings into named configuration profiles which can be activated from the command-line when starting tio. - tio will try to match the user input to a sub-configuration by name or by pattern to get the TTY device and other options. + tio will try to match the user input to a configuration profile by name or by pattern to get the TTY device and other options. Options without any label change the default options. @@ -471,7 +471,7 @@ CONFIGURATION FILE CONFIGURATION FILE EXAMPLES To change the default configuration simply set options like so: - # Defaults + [default] baudrate = 9600 databits = 8 parity = none @@ -479,14 +479,14 @@ CONFIGURATION FILE EXAMPLES color = 10 line-pulse-duration = DTR=200,RTS=400 - Named sub-configurations can be added via labels: + Named configuration profiles can be added via labels: [rpi3] device = /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 baudrate = 115200 color = 11 - Activate the sub-configuration by name: + Activate the configuration profile by name: $ tio rpi3 @@ -494,22 +494,24 @@ CONFIGURATION FILE EXAMPLES $ tio -b 115200 -c 11 /dev/serial/by-id/usb-FTDI_TTL232R-3V3_FTGQVXBL-if00-port0 - A sub-configuration 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 device] - pattern = usb([0-9]*) - device = /dev/ttyUSB%s + [usb-devices] + pattern = ^usb([0-9]*) + device = /dev/ttyUSB%m1 baudrate = 115200 - Activate the sub-configuration by pattern match: + Activate the configuration profile by pattern match: $ tio usb12 - Which is equivalent to: + Which becomes equivalent to: $ tio -b 115200 /dev/ttyUSB12 - It is also possible to combine use of sub-configuration 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 @@ -551,6 +553,10 @@ 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: + + $ tio --script 'expect("login: "); send("root\n"); expect("Password: "); send("root\n")' /dev/ttyUSB0 + Redirect device I/O to network file socket for remote TTY sharing: $ tio --socket inet:4444 /dev/ttyUSB0 @@ -577,7 +583,7 @@ EXAMPLES Manipulate DTR and RTS lines upon first connect to reset connected microcontroller: - $ tio --script "high(DTR); low(RTS); msleep(100); toggle(DTR)" --script-run once /dev/ttyUSB0 + $ tio --script "set{DTR=high,RTS=low}; msleep(100); set{RTS=toggle}" --script-run once /dev/ttyUSB0 Automatically log in to connected OS: @@ -589,4 +595,4 @@ WEBSITE AUTHOR Maintained by Martin Lund . -tio 2.9 2024-04-14 tio(1) +tio 3.1 2024-05-03 tio(1) From 873bd6973dd08ebb8a0fc24417e1bfa0990eefee Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 7 May 2024 09:50:07 +0200 Subject: [PATCH 351/506] Fix shadow variables --- src/configfile.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/configfile.c b/src/configfile.c index 3390b34..5d43b61 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -353,6 +353,9 @@ static int config_file_resolve(void) void config_file_show_profiles(void) { + GKeyFile *keyfile; + GError *error = NULL; + memset(&config, 0, sizeof(struct config_t)); // Find config file @@ -362,9 +365,6 @@ void config_file_show_profiles(void) return; } - GKeyFile *keyfile; - GError *error = NULL; - keyfile = g_key_file_new(); if (!g_key_file_load_from_file(keyfile, config.path, G_KEY_FILE_NONE, &error)) @@ -458,10 +458,10 @@ static char *match_and_replace(const char *str, const char *pattern, char *devic { tio_debug_printf("Subexpression %d match: ", i); int k = 0; - for (int j = matches[i].rm_so; j < matches[i].rm_eo; j++) + for (int l = matches[i].rm_so; l < matches[i].rm_eo; l++) { - tio_debug_printf_raw("%c", str[j]); - replacement_str[k++] = str[j]; + tio_debug_printf_raw("%c", str[l]); + replacement_str[k++] = str[l]; } replacement_str[k] = '\0'; sprintf(m_key, "%%m%d", i); @@ -541,11 +541,11 @@ void config_file_parse(void) } // Lookup 'pattern' key - GError *error = NULL; gchar *pattern = g_key_file_get_string(keyfile, group[i], "pattern", &error); if (error != NULL) { g_error_free(error); + error = NULL; continue; } @@ -554,6 +554,7 @@ void config_file_parse(void) if (error != NULL) { g_error_free(error); + error = NULL; continue; } @@ -564,14 +565,14 @@ void config_file_parse(void) if (config.device != NULL) { // Match found - save device - char *device = strdup(config.device); + device = strdup(config.device); - // Parse found group + // Parse found group (may replace config.device) config_parse_keys(keyfile, group[i]); // Update configuration config.active_group = strdup(group[i]); - config.device = device; + config.device = device; // Restore new device string break; } From 545d473220892a6ca7a4ec9b3ed1141923858732 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 7 May 2024 14:21:43 +0200 Subject: [PATCH 352/506] Make sure all error output is directed to stderr --- src/print.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/print.h b/src/print.h index 4a41b32..038655c 100644 --- a/src/print.h +++ b/src/print.h @@ -89,15 +89,15 @@ extern char ansi_format[]; putchar('\n'); \ if (option.color < 0) { \ if (error_normal) \ - fprintf (stdout, "Error: " format "\n", ## args); \ + fprintf (stderr, "Error: " format "\n", ## args); \ else \ - fprintf (stdout, "\r[%s] Error: " format "\r\n", timestamp_current_time(), ## args); \ + fprintf (stderr, "\r[%s] Error: " format "\r\n", timestamp_current_time(), ## args); \ } \ else { \ if (error_normal) \ - { ansi_printf("Error: " format, ## args); }\ + { ansi_error_printf("Error: " format, ## args); }\ else \ - { ansi_printf("[%s] Error: " format, timestamp_current_time(), ## args); }\ + { ansi_error_printf("[%s] Error: " format, timestamp_current_time(), ## args); }\ } \ print_tainted = false; \ } \ From e75e19eb0054704c953b6480234b54b76d48e107 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 7 May 2024 14:38:19 +0200 Subject: [PATCH 353/506] Add option '--exec ' for running shell command Runs shell command with I/O redirected to device. --- TODO | 10 ------- man/tio.1.in | 8 ++++++ src/bash-completion/tio.in | 5 ++++ src/configfile.c | 1 + src/misc.c | 59 ++++++++++++++++++++++++++++++++++++++ src/misc.h | 1 + src/options.c | 8 ++++++ src/options.h | 1 + src/tty.c | 6 ++++ 9 files changed, 89 insertions(+), 10 deletions(-) diff --git a/TODO b/TODO index bf36c62..d1609fa 100644 --- a/TODO +++ b/TODO @@ -19,16 +19,6 @@ key navigation left/right and insert/overwrite support. Also history browsing pressing up/down. - * Support for running external command - - Add key command e.g. 'ctrl-t r' which prompts user to run external command. - The command will be run in a process which stdin/stdout is redirected to the - serial port. - - This is the first step towards maybe also adding automatic support for - x/y/zmodem data transfer protocols by calling external programs such as - rb/sb, rx/sx, rz/sz, etc. - * Allow tio to connect to socket After some more consideration I think it makes sense to support connecting to a diff --git a/man/tio.1.in b/man/tio.1.in index 4ea2499..7e8d7b7 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -358,6 +358,11 @@ Run script on connect once, always, or never. Default value is "always". +.TP +.BR "\-\-exec \fI + +Execute shell command with I/O redirected to device + .TP .BR \-v ", " \-\-version @@ -571,6 +576,9 @@ Run script from string Run script from file .IP "\fBscript-run" Run script on connect +.IP "\fBexec" +Execute shell command with I/O redirected to device + .SH "CONFIGURATION FILE EXAMPLES" diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index 75a207c..a07e728 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -45,6 +45,7 @@ _tio() --script \ --script-file \ --script-run \ + --exec \ -v --version \ -h --help" @@ -179,6 +180,10 @@ _tio() COMPREPLY=( $(compgen -W "once always never" -- ${cur}) ) return 0 ;; + --exec) + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + ;; -v | --version) COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 diff --git a/src/configfile.c b/src/configfile.c index 5d43b61..cb73146 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -282,6 +282,7 @@ static void config_parse_keys(GKeyFile *key_file, char *group) g_free((void *)string); string = NULL; } + config_get_string(key_file, group, "exec", &option.exec, NULL); config_get_string(key_file, group, "prefix-ctrl-key", &string, NULL); if (string != NULL) { diff --git a/src/misc.c b/src/misc.c index 630671e..9342769 100644 --- a/src/misc.c +++ b/src/misc.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include "error.h" @@ -199,3 +200,61 @@ bool match_patterns(const char *string, const char *patterns) free(patterns_copy); return false; } + +// Function that forks subprocess, redirects its stdin and stdout to the +// specified filedescriptor, and runs command. +int execute_shell_command(int fd, const char *command) +{ + pid_t pid; + int status; + + // Fork a child process + pid = fork(); + if (pid == -1) + { + // Error occurred + tio_error_print("fork() failed (%s)", strerror(errno)); + exit(EXIT_FAILURE); + } + else if (pid == 0) + { + // Child process + + tio_printf("Executing shell command '%s'", command); + + // Redirect stdout and stderr to the file descriptor + if (dup2(fd, STDOUT_FILENO) == -1 || dup2(fd, STDERR_FILENO) == -1) + { + tio_error_print("dup2() failed (%s)", strerror(errno)); + exit(EXIT_FAILURE); + } + + // Execute the shell command + execl("/bin/sh", "sh", "-c", command, (char *)NULL); + + // If execlp() returns, it means an error occurred + perror("execlp"); + tio_error_print("execlp() failed (%s)", strerror(errno)); + exit(EXIT_FAILURE); + } + else + { + // Parent process + + // Wait for the child process to finish + waitpid(pid, &status, 0); + + if (WIFEXITED(status)) + { + tio_printf("Command exited with status %d", WEXITSTATUS(status)); + return WEXITSTATUS(status); + } + else + { + tio_error_printf("Child process exited abnormally\n"); + return -1; + } + } + + return 0; +} diff --git a/src/misc.h b/src/misc.h index edbb511..9f8a1d4 100644 --- a/src/misc.h +++ b/src/misc.h @@ -34,3 +34,4 @@ char *base62_encode(unsigned long num); 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); diff --git a/src/options.c b/src/options.c index c0316ce..0eefe7e 100644 --- a/src/options.c +++ b/src/options.c @@ -69,6 +69,7 @@ enum opt_t OPT_EXCLUDE_DEVICES, OPT_EXCLUDE_DRIVERS, OPT_EXCLUDE_TIDS, + OPT_EXEC, }; /* Default options */ @@ -121,6 +122,7 @@ struct option_t option = .exclude_tids = NULL, .hex_n_value = 0, .vt100 = false, + .exec = NULL, }; void option_print_help(char *argv[]) @@ -167,6 +169,7 @@ void option_print_help(char *argv[]) printf(" --script Run script from string\n"); 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(" -v, --version Display version\n"); printf(" -h, --help Display help\n"); printf("\n"); @@ -815,6 +818,7 @@ void options_parse(int argc, char *argv[]) {"script", required_argument, 0, OPT_SCRIPT }, {"script-file", required_argument, 0, OPT_SCRIPT_FILE }, {"script-run", required_argument, 0, OPT_SCRIPT_RUN }, + {"exec", required_argument, 0, OPT_EXEC }, {"version", no_argument, 0, 'v' }, {"help", no_argument, 0, 'h' }, {"complete-profiles", no_argument, 0, OPT_COMPLETE_PROFILES }, @@ -985,6 +989,10 @@ void options_parse(int argc, char *argv[]) option_parse_script_run(optarg, &option.script_run); break; + case OPT_EXEC: + option.exec = optarg; + break; + case 'v': printf("tio v%s\n", VERSION); exit(EXIT_SUCCESS); diff --git a/src/options.h b/src/options.h index 31e807a..ee73b8e 100644 --- a/src/options.h +++ b/src/options.h @@ -96,6 +96,7 @@ struct option_t char *exclude_tids; int hex_n_value; bool vt100; + char *exec; }; extern struct option_t option; diff --git a/src/tty.c b/src/tty.c index 8aa1606..3a3d80a 100644 --- a/src/tty.c +++ b/src/tty.c @@ -2277,6 +2277,12 @@ int tty_connect(void) exit(EXIT_SUCCESS); } + if (option.exec != NULL) + { + int status = execute_shell_command(device_fd, option.exec); + exit(status); + } + /* Input loop */ while (true) { From 016c81291e0618e315ce0bb3e4e77a69e1dc3475 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 7 May 2024 14:48:57 +0200 Subject: [PATCH 354/506] Clean up completion script --- src/bash-completion/tio.in | 72 -------------------------------------- 1 file changed, 72 deletions(-) diff --git a/src/bash-completion/tio.in b/src/bash-completion/tio.in index a07e728..3a3e7f6 100644 --- a/src/bash-completion/tio.in +++ b/src/bash-completion/tio.in @@ -80,62 +80,18 @@ _tio() COMPREPLY=( $(compgen -W "1 10 100" -- ${cur}) ) return 0 ;; - --line-pulse-duration) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - ;; -a | --auto-connect) COMPREPLY=( $(compgen -W "new latest none" -- ${cur}) ) return 0 ;; - -n | --no-reconnect) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - ;; - -e | --local-echo) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - ;; - -l | --log) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - ;; - --log-file) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - ;; - --log-directory) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - ;; - --log-append) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - ;; - --log-strip) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - ;; -m | --map) COMPREPLY=( $(compgen -W "ICRNL IGNCR INLCR IFFESCC INLCRNL OCRNL ODELBS ONLCRNL MSB2LSB" -- ${cur}) ) return 0 ;; - -t | --timestamp) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - ;; --timestamp-format) COMPREPLY=( $(compgen -W "24hour 24hour-start 24hour-delta iso8601" -- ${cur}) ) return 0 ;; - --timestamp-timeout) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - ;; - -L | --list) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - ;; -c | --color) COMPREPLY=( $(compgen -W "$(seq 0 255) none list" -- ${cur}) ) return 0 @@ -152,10 +108,6 @@ _tio() COMPREPLY=( $(compgen -W "normal hex" -- ${cur}) ) return 0 ;; - --rs-485) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - ;; --rs-485-config) COMPREPLY=( $(compgen -W "RTS_ON_SEND RTS_AFTER_SEND RTS_DELAY_BEFORE_SEND RTS_DELAY_AFTER_SEND RX_DURING_TX" -- ${cur}) ) return 0 @@ -164,34 +116,10 @@ _tio() COMPREPLY=( $(compgen -W "none bell blink" -- ${cur}) ) return 0 ;; - --mute) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - ;; - --script) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - ;; - --script-file) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - ;; --script-run) COMPREPLY=( $(compgen -W "once always never" -- ${cur}) ) return 0 ;; - --exec) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - ;; - -v | --version) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - ;; - -h | --help) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - ;; *) ;; esac From 9066523229285bad456e87c7135b11277f33a0c2 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 7 May 2024 19:08:10 +0200 Subject: [PATCH 355/506] Do not print error when using --list with broken config file --- src/configfile.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/configfile.c b/src/configfile.c index cb73146..cc3013d 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -624,7 +624,6 @@ void config_list_targets(void) if (!g_key_file_load_from_file(keyfile, config.path, G_KEY_FILE_NONE, &error)) { - tio_error_print("Failure loading file: %s", error->message); g_error_free(error); return; } From c2f910ffe34761b037f75d08e13b424745aec9bb Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Tue, 7 May 2024 21:49:23 +0200 Subject: [PATCH 356/506] Bump version --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 45577c9..cd6ddfd 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('tio', 'c', - version : '3.1', + version : '3.2', license : [ 'GPL-2'], meson_version : '>= 0.53.2', default_options : [ 'warning_level=2', 'buildtype=release', 'c_std=gnu99' ] From 86f1b3881ddc0ec27dab7de52c1b6b1de0205685 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 8 May 2024 10:31:32 +0200 Subject: [PATCH 357/506] 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 3a3d80a..f999a77 100644 --- a/src/tty.c +++ b/src/tty.c @@ -2279,7 +2279,7 @@ int tty_connect(void) if (option.exec != NULL) { - int status = execute_shell_command(device_fd, option.exec); + status = execute_shell_command(device_fd, option.exec); exit(status); } From f8253636068e27b2228dea37568716ecf6475464 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 8 May 2024 13:00:44 +0200 Subject: [PATCH 358/506] Update NEWS --- NEWS | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 15e52d9..76a1fd7 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,31 @@ -=== tio v3.1 === +=== tio v3.2 === + + + +Changes since tio v3.1: + + * Do not print error when using --list with broken config file + + * Clean up completion script + + * Add option '--exec ' for running shell command + + Runs shell command with I/O redirected to device. + + * Make sure all error output is directed to stderr + + * Fix shadow variables + + * Update man page + + * Fix build on older GNU/Linux systems without statx + + * Fix line ending in --list output + + * Print location of configuratin file in --list output + + * Fix alignment of profile listing From ce70f431131f260276ef87f61c770bf8b90cb85d Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 8 May 2024 13:04:08 +0200 Subject: [PATCH 359/506] Update man page --- man/tio.1.in | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 7e8d7b7..2e5259a 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -703,7 +703,7 @@ expect -i $uart "prompt> " .RE .TP -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 @@ -744,11 +744,6 @@ Manipulate DTR and RTS lines upon first connect to reset connected microcontroll $ tio --script "set{DTR=high,RTS=low}; msleep(100); set{RTS=toggle}" --script-run once /dev/ttyUSB0 -.TP -Automatically log in to connected OS: - -$ tio --script "expect('password:'); send('my_password\\n')" /dev/ttyUSB0 - .SH "WEBSITE" .PP Visit https://tio.github.io From 2d17624ddbda4d78d35cc00892a59288a735bd94 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 8 May 2024 13:05:47 +0200 Subject: [PATCH 360/506] Update version date --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index cd6ddfd..08eb5fd 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-03' +version_date = '2024-05-08' # Test for dynamic baudrate configuration interface compiler = meson.get_compiler('c') From 241ff93bf4a0e502eb47469044300acbe6d971ac Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 8 May 2024 13:37:05 +0200 Subject: [PATCH 361/506] 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 362/506] 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 363/506] 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 364/506] 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 365/506] 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 366/506] 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 367/506] 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 368/506] 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 369/506] 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 370/506] 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 371/506] 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 372/506] 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 373/506] 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 374/506] 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 375/506] 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 376/506] 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 377/506] 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 378/506] 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 379/506] 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 380/506] 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 381/506] 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 382/506] 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 383/506] 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 384/506] 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 385/506] 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 386/506] 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 387/506] 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 388/506] 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 389/506] 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 390/506] 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 391/506] 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 392/506] 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 393/506] 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 394/506] 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 395/506] 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 396/506] 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 397/506] 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 398/506] 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 399/506] 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 400/506] 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 401/506] 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 402/506] 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 403/506] 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 404/506] 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 405/506] 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 406/506] 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 407/506] 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 408/506] 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 409/506] 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 410/506] 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 411/506] 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 412/506] 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 413/506] 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 414/506] 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 415/506] 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 416/506] 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 417/506] 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 418/506] 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 419/506] 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 420/506] 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 421/506] 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 422/506] 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 423/506] 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 424/506] 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 425/506] 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 426/506] 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 427/506] 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 428/506] 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 429/506] 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 430/506] 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 431/506] 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 432/506] 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 433/506] 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 434/506] 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 435/506] 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 436/506] 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 437/506] 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 438/506] 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 439/506] 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 440/506] 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 441/506] 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 442/506] 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 443/506] 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 444/506] 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 445/506] 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 446/506] 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 447/506] 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 448/506] 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 449/506] 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 450/506] 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 451/506] 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 452/506] 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 453/506] 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 454/506] 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 455/506] 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 456/506] 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 457/506] 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 458/506] + 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 459/506] 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 460/506] 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 461/506] 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 462/506] 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 463/506] 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 464/506] 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 465/506] 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 466/506] 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 467/506] 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 468/506] 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 469/506] 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 470/506] 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 471/506] 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 472/506] 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 473/506] 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 474/506] 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 475/506] 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 476/506] 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 477/506] 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 478/506] 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 479/506] 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 480/506] 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 481/506] 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 482/506] 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 483/506] 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 484/506] 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 485/506] 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 486/506] 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 487/506] 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 488/506] 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 489/506] 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 490/506] 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 491/506] - 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 492/506] 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 493/506] 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 494/506] 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 495/506] 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 496/506] 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 497/506] 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 498/506] 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 499/506] 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 500/506] 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 501/506] 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 502/506] 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 503/506] 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 504/506] 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 505/506] 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 506/506] 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' ] )