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.
This commit is contained in:
g0mb4 2021-07-19 11:32:00 +02:00
parent 0f0279bd3f
commit 7fc8e278ed
4 changed files with 131 additions and 23 deletions

View file

@ -1,4 +1,4 @@
.TH "tio" "1" "June 2018" .TH "tio" "1" "July 2021"
.SH "NAME" .SH "NAME"
tio \- a simple TTY terminal I/O application 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. Enable local echo.
.TP
.BR \-t ", " "\-\-timestamp
Prefix each new line with a timestamp.
.TP .TP
.BR \-l ", " "\-\-log " \fI<filename> .BR \-l ", " "\-\-log " \fI<filename>
@ -86,10 +91,21 @@ Map NL to CR-NL on output.
If defining more than one flag, the flags must be comma separated. If defining more than one flag, the flags must be comma separated.
.RE .RE
.TP
.BR \-x ", " \-\-hex
Start in hexadecimal mode.
.TP
.BR \-\-newline-in-hex
Interpret new line characters ('\\r', '\\n') in hexadecimal mode.
.TP .TP
.BR \-v ", " \-\-version .BR \-v ", " \-\-version
Display program version. Display program version.
.TP .TP
.BR \-h ", " \-\-help .BR \-h ", " \-\-help
@ -126,6 +142,14 @@ Toggle RTS
.IP "\fBctrl-t v" .IP "\fBctrl-t v"
Show version 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" .SH "EXAMPLES"
.TP .TP
Typical use is without options. For example: Typical use is without options. For example:

View file

@ -27,6 +27,8 @@
#include <termios.h> #include <termios.h>
#include <sys/param.h> #include <sys/param.h>
#define OPT_NEWLINE_IN_HEX 1000 // "short" option for --newline-in-hex
/* Options */ /* Options */
struct option_t struct option_t
{ {
@ -41,6 +43,8 @@ struct option_t
bool log; bool log;
bool local_echo; bool local_echo;
bool timestamp; bool timestamp;
bool hex_mode;
bool newline_in_hex;
const char *log_filename; const char *log_filename;
const char *map; const char *map;
}; };

View file

@ -47,6 +47,8 @@ struct option_t option =
false, // No log false, // No log
false, // No local echo false, // No local echo
false, // No timestamp false, // No timestamp
false, // Not starting in hex mode
false, // No newlines in hex mode
"", // Log filename "", // Log filename
"" // Map string "" // Map string
}; };
@ -67,6 +69,8 @@ void print_help(char *argv[])
printf(" -t, --timestamp Prefix each new line with a timestamp\n"); printf(" -t, --timestamp Prefix each new line with a timestamp\n");
printf(" -l, --log <filename> Log to file\n"); printf(" -l, --log <filename> Log to file\n");
printf(" -m, --map <flags> Map special characters\n"); printf(" -m, --map <flags> 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(" -v, --version Display version\n");
printf(" -h, --help Display help\n"); printf(" -h, --help Display help\n");
printf("\n"); printf("\n");
@ -117,6 +121,8 @@ void parse_options(int argc, char *argv[])
{"timestamp", no_argument, 0, 't'}, {"timestamp", no_argument, 0, 't'},
{"log", required_argument, 0, 'l'}, {"log", required_argument, 0, 'l'},
{"map", required_argument, 0, 'm'}, {"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'}, {"version", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'}, {"help", no_argument, 0, 'h'},
{0, 0, 0, 0 } {0, 0, 0, 0 }
@ -126,7 +132,7 @@ void parse_options(int argc, char *argv[])
int option_index = 0; int option_index = 0;
/* Parse argument using getopt_long */ /* 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 */ /* Detect the end of the options */
if (c == -1) if (c == -1)
@ -188,6 +194,14 @@ void parse_options(int argc, char *argv[])
case 'm': case 'm':
option.map = optarg; option.map = optarg;
break; break;
case 'x':
option.hex_mode = true;
break;
case OPT_NEWLINE_IN_HEX:
option.newline_in_hex = true;
break;
case 'v': case 'v':
printf("tio v%s\n", VERSION); printf("tio v%s\n", VERSION);

108
src/tty.c
View file

@ -60,6 +60,8 @@ static bool map_i_nl_crnl = false;
static bool map_o_cr_nl = false; static bool map_o_cr_nl = false;
static bool map_o_nl_crnl = false; static bool map_o_nl_crnl = false;
static bool map_o_del_bs = 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...) \ #define tio_printf(format, args...) \
{ \ { \
@ -68,10 +70,58 @@ static bool map_o_del_bs = false;
tainted = 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) static void print_hex(char c)
{ {
if (((c == '\n') || (c == '\r')) && option.newline_in_hex)
if ((c == '\n') || (c == '\r'))
printf("%c", c); printf("%c", c);
else else
printf("%02x ", (unsigned char) c); printf("%02x ", (unsigned char) c);
@ -601,16 +651,6 @@ void tty_restore(void)
tty_disconnect(); 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) int tty_connect(void)
{ {
fd_set rdfs; /* Read file descriptor set */ fd_set rdfs; /* Read file descriptor set */
@ -659,6 +699,19 @@ int tty_connect(void)
if (option.timestamp) if (option.timestamp)
next_timestamp = time(NULL); 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 */ /* Save current port settings */
if (tcgetattr(fd, &tio_old) < 0) if (tcgetattr(fd, &tio_old) < 0)
@ -759,6 +812,7 @@ int tty_connect(void)
goto error_read; goto error_read;
} }
} }
if (FD_ISSET(STDIN_FILENO, &rdfs)) if (FD_ISSET(STDIN_FILENO, &rdfs))
{ {
bool forward = true; bool forward = true;
@ -781,6 +835,13 @@ int tty_connect(void)
if (forward) 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 */ /* Map output character */
if ((output_char == 127) && (map_o_del_bs)) if ((output_char == 127) && (map_o_del_bs))
output_char = '\b'; output_char = '\b';
@ -799,16 +860,21 @@ int tty_connect(void)
tx_total++; tx_total++;
delay(option.output_delay); 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 */ /* Update transmit statistics */
optional_local_echo(output_char); tx_total++;
status = write(fd, &output_char, 1); }
if (status < 0)
warning_printf("Could not write to tty device");
fsync(fd);
/* Update transmit statistics */
tx_total++;
/* Insert output delay */ /* Insert output delay */
delay(option.output_delay); delay(option.output_delay);