From 7fc8e278ed14a908b6c6e66254cad019c83745b1 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 | 26 ++++++++- src/include/tio/options.h | 4 ++ src/options.c | 16 +++++- src/tty.c | 108 ++++++++++++++++++++++++++++++-------- 4 files changed, 131 insertions(+), 23 deletions(-) diff --git a/man/tio.1 b/man/tio.1 index cdb0295..7e22587 100644 --- a/man/tio.1 +++ b/man/tio.1 @@ -1,4 +1,4 @@ -.TH "tio" "1" "June 2018" +.TH "tio" "1" "July 2021" .SH "NAME" tio \- a simple TTY terminal I/O application @@ -56,6 +56,11 @@ option is provided, tio will exit if the device is not present or an established Enable local echo. +.TP +.BR \-t ", " "\-\-timestamp + +Prefix each new line with a timestamp. + .TP .BR \-l ", " "\-\-log " \fI @@ -86,10 +91,21 @@ 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 \-v ", " \-\-version Display program version. + .TP .BR \-h ", " \-\-help @@ -126,6 +142,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 "EXAMPLES" .TP Typical use is without options. For example: diff --git a/src/include/tio/options.h b/src/include/tio/options.h index 1ae1974..e6b933c 100644 --- a/src/include/tio/options.h +++ b/src/include/tio/options.h @@ -27,6 +27,8 @@ #include #include +#define OPT_NEWLINE_IN_HEX 1000 // "short" option for --newline-in-hex + /* Options */ struct option_t { @@ -41,6 +43,8 @@ struct option_t bool log; bool local_echo; bool timestamp; + bool hex_mode; + bool newline_in_hex; const char *log_filename; const char *map; }; diff --git a/src/options.c b/src/options.c index c88582a..dd62bb5 100644 --- a/src/options.c +++ b/src/options.c @@ -47,6 +47,8 @@ struct option_t option = false, // No log false, // No local echo false, // No timestamp + false, // Not starting in hex mode + false, // No newlines in hex mode "", // Log filename "" // Map string }; @@ -67,6 +69,8 @@ void print_help(char *argv[]) printf(" -t, --timestamp Prefix each new line with a timestamp\n"); printf(" -l, --log Log to file\n"); printf(" -m, --map Map special characters\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"); @@ -117,6 +121,8 @@ void parse_options(int argc, char *argv[]) {"timestamp", no_argument, 0, 't'}, {"log", required_argument, 0, 'l'}, {"map", required_argument, 0, 'm'}, + {"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 } @@ -126,7 +132,7 @@ void parse_options(int argc, char *argv[]) int option_index = 0; /* Parse argument using getopt_long */ - c = getopt_long(argc, argv, "b:d:f:s:p:o:netl:m:vh", long_options, &option_index); + c = getopt_long(argc, argv, "b:d:f:s:p:o:netl:m:xvh", long_options, &option_index); /* Detect the end of the options */ if (c == -1) @@ -188,6 +194,14 @@ void parse_options(int argc, char *argv[]) case 'm': option.map = optarg; 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); diff --git a/src/tty.c b/src/tty.c index 067179a..ba3a5d3 100644 --- a/src/tty.c +++ b/src/tty.c @@ -60,6 +60,8 @@ 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; #define tio_printf(format, args...) \ { \ @@ -68,10 +70,58 @@ static bool map_o_del_bs = false; tainted = false; \ } +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 print_hex(char c) { - - if ((c == '\n') || (c == '\r')) + if (((c == '\n') || (c == '\r')) && option.newline_in_hex) printf("%c", c); else printf("%02x ", (unsigned char) c); @@ -601,16 +651,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 */ @@ -659,6 +699,19 @@ int tty_connect(void) if (option.timestamp) next_timestamp = time(NULL); + + 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) @@ -759,6 +812,7 @@ int tty_connect(void) goto error_read; } } + if (FD_ISSET(STDIN_FILENO, &rdfs)) { bool forward = true; @@ -781,6 +835,13 @@ 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'; @@ -799,16 +860,21 @@ int tty_connect(void) tx_total++; delay(option.output_delay); } + + 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); - /* 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);