From 0b55981e529f5173ddc574d95c29e037bc5e08f4 Mon Sep 17 00:00:00 2001 From: g0mb4 Date: Mon, 19 Jul 2021 11:32:00 +0200 Subject: [PATCH] Extended hexadecimal mode. While in hex mode (ctrl-t h) you can output hexadecimal values. E.g.: to send 0x0A you have to type 0A (always 2 characters). Added option -x, --hex to start in hexadecimal mode. Added option --newline-in-hex to interpret newline characters in hex mode. This is disabled by default, because, in my opinion, hex stream is fundamentally different from text, so a "new line" is meaningless in this context. --- man/tio.1.in | 18 +++++++++ src/options.c | 16 +++++++- src/options.h | 4 ++ src/print.c | 2 +- src/tty.c | 108 +++++++++++++++++++++++++++++++++++++++++--------- 5 files changed, 128 insertions(+), 20 deletions(-) diff --git a/man/tio.1.in b/man/tio.1.in index 5668ccd..b21ab03 100644 --- a/man/tio.1.in +++ b/man/tio.1.in @@ -117,6 +117,16 @@ Map NL to CR-NL on output. If defining more than one flag, the flags must be comma separated. .RE +.TP +.BR \-x ", " \-\-hex + +Start in hexadecimal mode. + +.TP +.BR \-\-newline-in-hex + +Interpret new line characters ('\\r', '\\n') in hexadecimal mode. + .TP .BR \-c ", " "\-\-color " \fI @@ -180,6 +190,14 @@ Toggle RTS .IP "\fBctrl-t v" Show version +.SH "HEXADECIMAL MODE" +.TP +In hexadecimal mode each incoming byte is printed out as a hexadecimal value. +.TP +By default there is \fBno new line\fR in this mode, but it can be turned on using the \fB--newline-in-hex\fR option. +.TP +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 "CONFIGURATION" .PP .TP 16n diff --git a/src/options.c b/src/options.c index 543509f..f420a3c 100644 --- a/src/options.c +++ b/src/options.c @@ -61,6 +61,8 @@ struct option_t option = .socket = NULL, .map = "", .color = -1, + .hex_mode = false, + .newline_in_hex = false, }; void print_help(char *argv[]) @@ -84,6 +86,8 @@ void print_help(char *argv[]) printf(" -m, --map Map special characters\n"); printf(" -c, --color Colorize tio text\n"); printf(" -S, --socket Listen on socket\n"); + printf(" -x, --hex Start in hexadecimal mode\n"); + printf(" --newline-in-hex Interpret new line characters in hex mode\n"); printf(" -v, --version Display version\n"); printf(" -h, --help Display help\n"); printf("\n"); @@ -195,6 +199,8 @@ void options_parse(int argc, char *argv[]) {"socket", required_argument, 0, 'S' }, {"map", required_argument, 0, 'm' }, {"color", required_argument, 0, 'c' }, + {"hex", no_argument, 0, 'x' }, + {"newline-in-hex", no_argument, 0, OPT_NEWLINE_IN_HEX }, {"version", no_argument, 0, 'v' }, {"help", no_argument, 0, 'h' }, {0, 0, 0, 0 } @@ -204,7 +210,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:netLlS:m:c:vh", long_options, &option_index); + c = getopt_long(argc, argv, "b:d:f:s:p:o:netLlS:m:c:xvh", long_options, &option_index); /* Detect the end of the options */ if (c == -1) @@ -301,6 +307,14 @@ void options_parse(int argc, char *argv[]) } break; + case 'x': + option.hex_mode = true; + break; + + case OPT_NEWLINE_IN_HEX: + option.newline_in_hex = true; + break; + case 'v': printf("tio v%s\n", VERSION); printf("Copyright (c) 2014-2022 Martin Lund\n"); diff --git a/src/options.h b/src/options.h index 27e9d0c..9a854a7 100644 --- a/src/options.h +++ b/src/options.h @@ -37,6 +37,8 @@ enum timestamp_t const char* timestamp_token(enum timestamp_t timestamp); enum timestamp_t timestamp_option_parse(const char *arg); +#define OPT_NEWLINE_IN_HEX 1000 // "short" option for --newline-in-hex + /* Options */ struct option_t { @@ -56,6 +58,8 @@ struct option_t const char *map; const char *socket; int color; + bool hex_mode; + bool newline_in_hex; }; extern struct option_t option; diff --git a/src/print.c b/src/print.c index 88ba881..05fa9f7 100644 --- a/src/print.c +++ b/src/print.c @@ -29,7 +29,7 @@ char ansi_format[30]; void print_hex(char c) { - if ((c == '\n') || (c == '\r')) + if (((c == '\n') || (c == '\r')) && option.newline_in_hex) { printf("%c", c); } diff --git a/src/tty.c b/src/tty.c index 1e1e217..e77c7aa 100644 --- a/src/tty.c +++ b/src/tty.c @@ -74,7 +74,60 @@ 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 char hex_chars[2]; +static unsigned char hex_char_index = 0; +static void optional_local_echo(char c) +{ + if (!option.local_echo) + return; + print(c); + fflush(stdout); + if (option.log) + log_write(c); +} + +inline static bool is_valid_hex(char c) +{ + return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); +} + +inline static unsigned char char_to_nibble(char c) +{ + if(c >= '0' && c <= '9'){ + return c - '0'; + } else if(c >= 'a' && c <= 'f'){ + return c - 'a' + 10; + } else if(c >= 'A' && c <= 'F'){ + return c - 'A' + 10; + } else { + return 0; + } +} + +static void output_hex(char c) +{ + hex_chars[hex_char_index++] = c; + + if (hex_char_index == 2) + { + 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 = write(fd, &hex_value, 1); + if (status < 0) + { + warning_printf("Could not write to tty device"); + } else + { + tx_total++; + } + + fsync(fd); + } +} static void toggle_line(const char *line_name, int mask) { @@ -585,16 +638,6 @@ void tty_restore(void) tty_disconnect(); } -static void optional_local_echo(char c) -{ - if (!option.local_echo) - return; - print(c); - fflush(stdout); - if (option.log) - log_write(c); -} - int tty_connect(void) { fd_set rdfs; /* Read file descriptor set */ @@ -644,6 +687,19 @@ int tty_connect(void) if (option.timestamp) next_timestamp = true; + if (option.hex_mode) + { + print = print_hex; + print_mode = HEX; + tio_printf("Switched to hexadecimal mode"); + } + else + { + print = print_normal; + print_mode = NORMAL; + tio_printf("Switched to normal mode"); + } + /* Save current port settings */ if (tcgetattr(fd, &tio_old) < 0) goto error_tcgetattr; @@ -800,6 +856,15 @@ int tty_connect(void) if (forward) { + if (print_mode == HEX) + { + if (!is_valid_hex(input_char)) + { + warning_printf("Invalid hex character: '%c' (0x%02x)", input_char, input_char); + continue; + } + } + /* Map output character */ if ((output_char == 127) && (map_o_del_bs)) output_char = '\b'; @@ -820,15 +885,22 @@ int tty_connect(void) delay(option.output_delay); } else { - /* Send output to tty device */ - optional_local_echo(output_char); - status = write(fd, &output_char, 1); - if (status < 0) - warning_printf("Could not write to tty device"); - fsync(fd); + if (print_mode == HEX) + { + output_hex(output_char); + } + else + { + /* Send output to tty device */ + optional_local_echo(output_char); + status = write(fd, &output_char, 1); + if (status < 0) + warning_printf("Could not write to tty device"); + fsync(fd); - /* Update transmit statistics */ - tx_total++; + /* Update transmit statistics */ + tx_total++; + } /* Insert output delay */ delay(option.output_delay);