From 2fff4d36d023e90d30bdf3dc416be39495ffab44 Mon Sep 17 00:00:00 2001 From: Martin Lund Date: Wed, 10 Apr 2024 14:39:42 +0200 Subject: [PATCH] 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)) {